diff --git a/paper-api/src/main/java/org/bukkit/GameRule.java b/paper-api/src/main/java/org/bukkit/GameRule.java
new file mode 100644
index 0000000000..958fe3eaf1
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/GameRule.java
@@ -0,0 +1,217 @@
+package org.bukkit;
+
+import com.google.common.base.Preconditions;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * GameRules dictate certain behavior within Minecraft itself
+ *
+ * For more information please visit the
+ * Minecraft
+ * Wiki
+ */
+public final class GameRule {
+
+ private static Map> gameRules = new HashMap<>();
+ // Boolean rules
+ /**
+ * Toggles the announcing of advancements.
+ */
+ public static final GameRule ANNOUNCE_ADVANCEMENTS = new GameRule<>("announceAdvancements", Boolean.class);
+
+ /**
+ * Whether command blocks should notify admins when they perform commands.
+ */
+ public static final GameRule COMMAND_BLOCK_OUTPUT = new GameRule<>("commandBlockOutput", Boolean.class);
+
+ /**
+ * Whether the server should skip checking player speed when the player is
+ * wearing elytra.
+ */
+ public static final GameRule DISABLE_ELYTRA_MOVEMENT_CHECK = new GameRule<>("disableElytraMovementCheck", Boolean.class);
+
+ /**
+ * Whether time progresses from the current moment.
+ */
+ public static final GameRule DO_DAYLIGHT_CYCLE = new GameRule<>("doDaylightCycle", Boolean.class);
+
+ /**
+ * Whether entities that are not mobs should have drops.
+ */
+ public static final GameRule DO_ENTITY_DROPS = new GameRule<>("doEntityDrops", Boolean.class);
+
+ /**
+ * Whether fire should spread and naturally extinguish.
+ */
+ public static final GameRule DO_FIRE_TICK = new GameRule<>("doFireTick", Boolean.class);
+
+ /**
+ * Whether players should only be able to craft recipes they've unlocked
+ * first.
+ */
+ public static final GameRule DO_LIMITED_CRAFTING = new GameRule<>("doLimitedCrafting", Boolean.class);
+
+ /**
+ * Whether mobs should drop items.
+ */
+ public static final GameRule DO_MOB_LOOT = new GameRule<>("doMobLoot", Boolean.class);
+
+ /**
+ * Whether mobs should naturally spawn.
+ */
+ public static final GameRule DO_MOB_SPAWNING = new GameRule<>("doMobSpawning", Boolean.class);
+
+ /**
+ * Whether blocks should have drops.
+ */
+ public static final GameRule DO_TILE_DROPS = new GameRule<>("doTileDrops", Boolean.class);
+
+ /**
+ * Whether the weather will change from the current moment.
+ */
+ public static final GameRule DO_WEATHER_CYCLE = new GameRule<>("doWeatherCycle", Boolean.class);
+
+ /**
+ * Whether the player should keep items in their inventory after death.
+ */
+ public static final GameRule KEEP_INVENTORY = new GameRule<>("keepInventory", Boolean.class);
+
+ /**
+ * Whether to log admin commands to server log.
+ */
+ public static final GameRule LOG_ADMIN_COMMANDS = new GameRule<>("logAdminCommands", Boolean.class);
+
+ /**
+ * Whether mobs can pick up items or change blocks.
+ */
+ public static final GameRule MOB_GRIEFING = new GameRule<>("mobGriefing", Boolean.class);
+
+ /**
+ * Whether players can regenerate health naturally through their hunger bar.
+ */
+ public static final GameRule NATURAL_REGENERATION = new GameRule<>("naturalRegeneration", Boolean.class);
+
+ /**
+ * Whether the debug screen shows all or reduced information.
+ */
+ public static final GameRule REDUCED_DEBUG_INFO = new GameRule<>("reducedDebugInfo", Boolean.class);
+
+ /**
+ * Whether the feedback from commands executed by a player should show up in
+ * chat. Also affects the default behavior of whether command blocks store
+ * their output text.
+ */
+ public static final GameRule SEND_COMMAND_FEEDBACK = new GameRule<>("sendCommandFeedback", Boolean.class);
+
+ /**
+ * Whether a message appears in chat when a player dies.
+ */
+ public static final GameRule SHOW_DEATH_MESSAGES = new GameRule<>("showDeathMessages", Boolean.class);
+
+ /**
+ * Whether players in spectator mode can generate chunks.
+ */
+ public static final GameRule SPECTATORS_GENERATE_CHUNKS = new GameRule<>("spectatorsGenerateChunks", Boolean.class);
+
+ // Numerical rules
+ /**
+ * How often a random block tick occurs (such as plant growth, leaf decay,
+ * etc.) per chunk section per game tick. 0 will disable random ticks,
+ * higher numbers will increase random ticks.
+ */
+ public static final GameRule RANDOM_TICK_SPEED = new GameRule<>("randomTickSpeed", Integer.class);
+
+ /**
+ * The number of blocks outward from the world spawn coordinates that a
+ * player will spawn in when first joining a server or when dying without a
+ * spawnpoint.
+ */
+ public static final GameRule SPAWN_RADIUS = new GameRule<>("spawnRadius", Integer.class);
+
+ /**
+ * The maximum number of other pushable entities a mob or player can push,
+ * before taking suffocation damage.
+ *
+ * Setting to 0 disables this rule.
+ */
+ public static final GameRule MAX_ENTITY_CRAMMING = new GameRule<>("maxEntityCramming", Integer.class);
+
+ /**
+ * Determines the number at which the chain of command blocks act as a
+ * "chain."
+ *
+ * This is the maximum amount of command blocks that can be activated in a
+ * single tick from a single chain.
+ */
+ public static final GameRule MAX_COMMAND_CHAIN_LENGTH = new GameRule<>("maxCommandChainLength", Integer.class);
+
+ // All GameRules instantiated above this for organizational purposes
+ private final String name;
+ private final Class type;
+
+ private GameRule(String name, Class clazz) {
+ Preconditions.checkNotNull(name, "GameRule name cannot be null");
+ Preconditions.checkNotNull(clazz, "GameRule type cannot be null");
+ Preconditions.checkArgument(clazz == Boolean.class || clazz == Integer.class, "Must be of type Boolean or Integer. Found %s ", clazz.getName());
+ this.name = name;
+ this.type = clazz;
+ gameRules.put(name, this);
+ }
+
+ /**
+ * Get the name of this GameRule.
+ *
+ * @return the name of this GameRule
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the type of this rule.
+ *
+ * @return the rule type; Integer or Boolean
+ */
+ public Class getType() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof GameRule)) {
+ return false;
+ }
+ GameRule> other = (GameRule>) obj;
+ return this.getName().equals(other.getName()) && this.getType() == other.getType();
+ }
+
+ @Override
+ public String toString() {
+ return "GameRule{" + "key=" + name + ", type=" + type + '}';
+ }
+
+ /**
+ * Get a {@link GameRule} by its name.
+ *
+ * @param rule the name of the GameRule
+ * @return the {@link GameRule} or null if no GameRule matches the given
+ * name
+ */
+ public static GameRule> getByName(String rule) {
+ Preconditions.checkNotNull(rule, "Rule cannot be null");
+ return gameRules.get(rule);
+ }
+
+ /**
+ * Get an immutable collection of {@link GameRule}s.
+ *
+ * @return an immutable collection containing all registered GameRules.
+ */
+ public static GameRule>[] values() {
+ return gameRules.values().toArray(new GameRule>[gameRules.size()]);
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java
index ecfdecaa27..2237039b52 100644
--- a/paper-api/src/main/java/org/bukkit/World.java
+++ b/paper-api/src/main/java/org/bukkit/World.java
@@ -1205,9 +1205,9 @@ public interface World extends PluginMessageRecipient, Metadatable {
void playSound(Location location, String sound, SoundCategory category, float volume, float pitch);
/**
- * Get existing rules
+ * Get an array containing the names of all the {@link GameRule}s.
*
- * @return An array of rules
+ * @return An array of {@link GameRule} names.
*/
public String[] getGameRules();
@@ -1218,7 +1218,9 @@ public interface World extends PluginMessageRecipient, Metadatable {
*
* @param rule Rule to look up value of
* @return String value of rule
+ * @deprecated use {@link #getGameRuleValue(GameRule)} instead
*/
+ @Deprecated
public String getGameRuleValue(String rule);
/**
@@ -1232,7 +1234,9 @@ public interface World extends PluginMessageRecipient, Metadatable {
* @param rule Rule to set
* @param value Value to set rule to
* @return True if rule was set
+ * @deprecated use {@link #setGameRule(GameRule, Object)} instead.
*/
+ @Deprecated
public boolean setGameRuleValue(String rule, String value);
/**
@@ -1243,6 +1247,35 @@ public interface World extends PluginMessageRecipient, Metadatable {
*/
public boolean isGameRule(String rule);
+ /**
+ * Get the current value for a given {@link GameRule}.
+ *
+ * @param rule the GameRule to check
+ * @param the GameRule's type
+ * @return the current value
+ */
+ public T getGameRuleValue(GameRule rule);
+
+ /**
+ * Get the default value for a given {@link GameRule}. This value is not
+ * guaranteed to match the current value.
+ *
+ * @param rule the rule to return a default value for
+ * @param the type of GameRule
+ * @return the default value
+ */
+ public T getGameRuleDefault(GameRule rule);
+
+ /**
+ * Set the given {@link GameRule}'s new value.
+ *
+ * @param rule the GameRule to update
+ * @param newValue the new value
+ * @param the value type of the GameRule
+ * @return true if the value was successfully set
+ */
+ public boolean setGameRule(GameRule rule, T newValue);
+
/**
* Gets the world border for this world.
*