--- a/net/minecraft/world/level/GameRules.java
+++ b/net/minecraft/world/level/GameRules.java
@@ -10,7 +10,11 @@
 import com.mojang.logging.LogUtils;
 import com.mojang.serialization.DynamicLike;
 import java.util.Comparator;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -25,68 +29,77 @@
 import org.slf4j.Logger;
 
 public class GameRules {
+
     public static final int DEFAULT_RANDOM_TICK_SPEED = 3;
     static final Logger LOGGER = LogUtils.getLogger();
-    private static final Map<GameRules.Key<?>, GameRules.Type<?>> GAME_RULE_TYPES = Maps.newTreeMap(Comparator.comparing((key) -> {
-        return key.id;
+    private static final Map<GameRules.Key<?>, GameRules.Type<?>> GAME_RULE_TYPES = Maps.newTreeMap(Comparator.comparing((gamerules_gamerulekey) -> {
+        return gamerules_gamerulekey.id;
     }));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOFIRETICK = register("doFireTick", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_MOBGRIEFING = register("mobGriefing", GameRules.Category.MOBS, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_KEEPINVENTORY = register("keepInventory", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOMOBSPAWNING = register("doMobSpawning", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOMOBLOOT = register("doMobLoot", GameRules.Category.DROPS, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOBLOCKDROPS = register("doTileDrops", GameRules.Category.DROPS, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOENTITYDROPS = register("doEntityDrops", GameRules.Category.DROPS, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_COMMANDBLOCKOUTPUT = register("commandBlockOutput", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_NATURAL_REGENERATION = register("naturalRegeneration", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DAYLIGHT = register("doDaylightCycle", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_LOGADMINCOMMANDS = register("logAdminCommands", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_SHOWDEATHMESSAGES = register("showDeathMessages", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.IntegerValue> RULE_RANDOMTICKING = register("randomTickSpeed", GameRules.Category.UPDATES, GameRules.IntegerValue.create(3));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_SENDCOMMANDFEEDBACK = register("sendCommandFeedback", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_REDUCEDDEBUGINFO = register("reducedDebugInfo", GameRules.Category.MISC, GameRules.BooleanValue.create(false, (server, rule) -> {
-        byte b = (byte)(rule.get() ? 22 : 23);
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOFIRETICK = GameRules.register("doFireTick", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_MOBGRIEFING = GameRules.register("mobGriefing", GameRules.Category.MOBS, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_KEEPINVENTORY = GameRules.register("keepInventory", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOMOBSPAWNING = GameRules.register("doMobSpawning", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOMOBLOOT = GameRules.register("doMobLoot", GameRules.Category.DROPS, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOBLOCKDROPS = GameRules.register("doTileDrops", GameRules.Category.DROPS, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOENTITYDROPS = GameRules.register("doEntityDrops", GameRules.Category.DROPS, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_COMMANDBLOCKOUTPUT = GameRules.register("commandBlockOutput", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_NATURAL_REGENERATION = GameRules.register("naturalRegeneration", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DAYLIGHT = GameRules.register("doDaylightCycle", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_LOGADMINCOMMANDS = GameRules.register("logAdminCommands", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_SHOWDEATHMESSAGES = GameRules.register("showDeathMessages", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.IntegerValue> RULE_RANDOMTICKING = GameRules.register("randomTickSpeed", GameRules.Category.UPDATES, GameRules.IntegerValue.create(3));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_SENDCOMMANDFEEDBACK = GameRules.register("sendCommandFeedback", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_REDUCEDDEBUGINFO = GameRules.register("reducedDebugInfo", GameRules.Category.MISC, GameRules.BooleanValue.create(false, (minecraftserver, gamerules_gameruleboolean) -> {
+        int i = gamerules_gameruleboolean.get() ? 22 : 23;
+        Iterator iterator = minecraftserver.players().iterator(); // Paper
 
-        for(ServerPlayer serverPlayer : server.getPlayerList().getPlayers()) {
-            serverPlayer.connection.send(new ClientboundEntityEventPacket(serverPlayer, b));
+        while (iterator.hasNext()) {
+            ServerPlayer entityplayer = (ServerPlayer) iterator.next();
+
+            entityplayer.connection.send(new ClientboundEntityEventPacket(entityplayer, (byte) i));
         }
 
     }));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_SPECTATORSGENERATECHUNKS = register("spectatorsGenerateChunks", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.IntegerValue> RULE_SPAWN_RADIUS = register("spawnRadius", GameRules.Category.PLAYER, GameRules.IntegerValue.create(10));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DISABLE_ELYTRA_MOVEMENT_CHECK = register("disableElytraMovementCheck", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false));
-    public static final GameRules.Key<GameRules.IntegerValue> RULE_MAX_ENTITY_CRAMMING = register("maxEntityCramming", GameRules.Category.MOBS, GameRules.IntegerValue.create(24));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_WEATHER_CYCLE = register("doWeatherCycle", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_LIMITED_CRAFTING = register("doLimitedCrafting", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false));
-    public static final GameRules.Key<GameRules.IntegerValue> RULE_MAX_COMMAND_CHAIN_LENGTH = register("maxCommandChainLength", GameRules.Category.MISC, GameRules.IntegerValue.create(65536));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_ANNOUNCE_ADVANCEMENTS = register("announceAdvancements", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DISABLE_RAIDS = register("disableRaids", GameRules.Category.MOBS, GameRules.BooleanValue.create(false));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOINSOMNIA = register("doInsomnia", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_IMMEDIATE_RESPAWN = register("doImmediateRespawn", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (server, rule) -> {
-        for(ServerPlayer serverPlayer : server.getPlayerList().getPlayers()) {
-            serverPlayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.IMMEDIATE_RESPAWN, rule.get() ? 1.0F : 0.0F));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_SPECTATORSGENERATECHUNKS = GameRules.register("spectatorsGenerateChunks", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.IntegerValue> RULE_SPAWN_RADIUS = GameRules.register("spawnRadius", GameRules.Category.PLAYER, GameRules.IntegerValue.create(10));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DISABLE_ELYTRA_MOVEMENT_CHECK = GameRules.register("disableElytraMovementCheck", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false));
+    public static final GameRules.Key<GameRules.IntegerValue> RULE_MAX_ENTITY_CRAMMING = GameRules.register("maxEntityCramming", GameRules.Category.MOBS, GameRules.IntegerValue.create(24));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_WEATHER_CYCLE = GameRules.register("doWeatherCycle", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_LIMITED_CRAFTING = GameRules.register("doLimitedCrafting", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false));
+    public static final GameRules.Key<GameRules.IntegerValue> RULE_MAX_COMMAND_CHAIN_LENGTH = GameRules.register("maxCommandChainLength", GameRules.Category.MISC, GameRules.IntegerValue.create(65536));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_ANNOUNCE_ADVANCEMENTS = GameRules.register("announceAdvancements", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DISABLE_RAIDS = GameRules.register("disableRaids", GameRules.Category.MOBS, GameRules.BooleanValue.create(false));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DOINSOMNIA = GameRules.register("doInsomnia", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_IMMEDIATE_RESPAWN = GameRules.register("doImmediateRespawn", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (minecraftserver, gamerules_gameruleboolean) -> {
+        Iterator iterator = minecraftserver.players().iterator(); // Paper
+
+        while (iterator.hasNext()) {
+            ServerPlayer entityplayer = (ServerPlayer) iterator.next();
+
+            entityplayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.IMMEDIATE_RESPAWN, gamerules_gameruleboolean.get() ? 1.0F : 0.0F));
         }
 
     }));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DROWNING_DAMAGE = register("drowningDamage", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_FALL_DAMAGE = register("fallDamage", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_FIRE_DAMAGE = register("fireDamage", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_FREEZE_DAMAGE = register("freezeDamage", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_PATROL_SPAWNING = register("doPatrolSpawning", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_TRADER_SPAWNING = register("doTraderSpawning", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_WARDEN_SPAWNING = register("doWardenSpawning", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_FORGIVE_DEAD_PLAYERS = register("forgiveDeadPlayers", GameRules.Category.MOBS, GameRules.BooleanValue.create(true));
-    public static final GameRules.Key<GameRules.BooleanValue> RULE_UNIVERSAL_ANGER = register("universalAnger", GameRules.Category.MOBS, GameRules.BooleanValue.create(false));
-    public static final GameRules.Key<GameRules.IntegerValue> RULE_PLAYERS_SLEEPING_PERCENTAGE = register("playersSleepingPercentage", GameRules.Category.PLAYER, GameRules.IntegerValue.create(100));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DROWNING_DAMAGE = GameRules.register("drowningDamage", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_FALL_DAMAGE = GameRules.register("fallDamage", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_FIRE_DAMAGE = GameRules.register("fireDamage", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_FREEZE_DAMAGE = GameRules.register("freezeDamage", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_PATROL_SPAWNING = GameRules.register("doPatrolSpawning", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_TRADER_SPAWNING = GameRules.register("doTraderSpawning", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_WARDEN_SPAWNING = GameRules.register("doWardenSpawning", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_FORGIVE_DEAD_PLAYERS = GameRules.register("forgiveDeadPlayers", GameRules.Category.MOBS, GameRules.BooleanValue.create(true));
+    public static final GameRules.Key<GameRules.BooleanValue> RULE_UNIVERSAL_ANGER = GameRules.register("universalAnger", GameRules.Category.MOBS, GameRules.BooleanValue.create(false));
+    public static final GameRules.Key<GameRules.IntegerValue> RULE_PLAYERS_SLEEPING_PERCENTAGE = GameRules.register("playersSleepingPercentage", GameRules.Category.PLAYER, GameRules.IntegerValue.create(100));
     private final Map<GameRules.Key<?>, GameRules.Value<?>> rules;
 
     private static <T extends GameRules.Value<T>> GameRules.Key<T> register(String name, GameRules.Category category, GameRules.Type<T> type) {
-        GameRules.Key<T> key = new GameRules.Key<>(name, category);
-        GameRules.Type<?> type2 = GAME_RULE_TYPES.put(key, type);
-        if (type2 != null) {
+        GameRules.Key<T> gamerules_gamerulekey = new GameRules.Key<>(name, category);
+        GameRules.Type<?> gamerules_gameruledefinition1 = (GameRules.Type) GameRules.GAME_RULE_TYPES.put(gamerules_gamerulekey, type);
+
+        if (gamerules_gameruledefinition1 != null) {
             throw new IllegalStateException("Duplicate game rule registration for " + name);
         } else {
-            return key;
+            return gamerules_gamerulekey;
         }
     }
 
@@ -96,8 +109,8 @@
     }
 
     public GameRules() {
-        this.rules = GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, (e) -> {
-            return e.getValue().createRule();
+        this.rules = (Map) GameRules.GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry) -> {
+            return ((GameRules.Type) entry.getValue()).createRule();
         }));
     }
 
@@ -106,70 +119,201 @@
     }
 
     public <T extends GameRules.Value<T>> T getRule(GameRules.Key<T> key) {
-        return (T)(this.rules.get(key));
+        return (T) this.rules.get(key); // CraftBukkit - decompile error
     }
 
     public CompoundTag createTag() {
-        CompoundTag compoundTag = new CompoundTag();
-        this.rules.forEach((key, rule) -> {
-            compoundTag.putString(key.id, rule.serialize());
+        CompoundTag nbttagcompound = new CompoundTag();
+
+        this.rules.forEach((gamerules_gamerulekey, gamerules_gamerulevalue) -> {
+            nbttagcompound.putString(gamerules_gamerulekey.id, gamerules_gamerulevalue.serialize());
         });
-        return compoundTag;
+        return nbttagcompound;
     }
 
     private void loadFromTag(DynamicLike<?> dynamic) {
-        this.rules.forEach((key, rule) -> {
-            dynamic.get(key.id).asString().result().ifPresent(rule::deserialize);
+        this.rules.forEach((gamerules_gamerulekey, gamerules_gamerulevalue) -> {
+            Optional<String> optional = dynamic.get(gamerules_gamerulekey.id).asString().result(); // CraftBukkit - decompile error
+
+            Objects.requireNonNull(gamerules_gamerulevalue);
+            optional.ifPresent(gamerules_gamerulevalue::deserialize);
         });
     }
 
     public GameRules copy() {
-        return new GameRules(this.rules.entrySet().stream().collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, (entry) -> {
-            return entry.getValue().copy();
+        return new GameRules((Map) this.rules.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry) -> {
+            return ((GameRules.Value) entry.getValue()).copy();
         })));
     }
 
     public static void visitGameRuleTypes(GameRules.GameRuleTypeVisitor visitor) {
-        GAME_RULE_TYPES.forEach((key, type) -> {
-            callVisitorCap(visitor, key, type);
+        GameRules.GAME_RULE_TYPES.forEach((gamerules_gamerulekey, gamerules_gameruledefinition) -> {
+            GameRules.callVisitorCap(visitor, gamerules_gamerulekey, gamerules_gameruledefinition);
         });
     }
 
     private static <T extends GameRules.Value<T>> void callVisitorCap(GameRules.GameRuleTypeVisitor consumer, GameRules.Key<?> key, GameRules.Type<?> type) {
-        consumer.visit(key, type);
-        type.callVisitor(consumer, key);
+        consumer.visit((GameRules.Key<T>) key, (GameRules.Type<T>) type); // CraftBukkit - decompile error
+        ((GameRules.Type<T>) type).callVisitor(consumer, (GameRules.Key<T>) key); // CraftBukkit - decompile error
     }
 
-    public void assignFrom(GameRules rules, @Nullable MinecraftServer server) {
-        rules.rules.keySet().forEach((key) -> {
-            this.assignCap(key, rules, server);
+    public void assignFrom(GameRules rules, @Nullable net.minecraft.server.level.ServerLevel server) { // Paper
+        rules.rules.keySet().forEach((gamerules_gamerulekey) -> {
+            this.assignCap(gamerules_gamerulekey, rules, server);
         });
     }
 
-    private <T extends GameRules.Value<T>> void assignCap(GameRules.Key<T> key, GameRules rules, @Nullable MinecraftServer server) {
-        T value = rules.getRule(key);
-        this.<T>getRule(key).setFrom(value, server);
+    private <T extends GameRules.Value<T>> void assignCap(GameRules.Key<T> key, GameRules rules, @Nullable net.minecraft.server.level.ServerLevel server) { // Paper
+        T t0 = rules.getRule(key);
+
+        this.getRule(key).setFrom(t0, server);
     }
 
     public boolean getBoolean(GameRules.Key<GameRules.BooleanValue> rule) {
-        return this.getRule(rule).get();
+        return ((GameRules.BooleanValue) this.getRule(rule)).get();
     }
 
     public int getInt(GameRules.Key<GameRules.IntegerValue> rule) {
-        return this.getRule(rule).get();
+        return ((GameRules.IntegerValue) this.getRule(rule)).get();
+    }
+
+    public static final class Key<T extends GameRules.Value<T>> {
+
+        final String id;
+        private final GameRules.Category category;
+
+        public Key(String name, GameRules.Category category) {
+            this.id = name;
+            this.category = category;
+        }
+
+        public String toString() {
+            return this.id;
+        }
+
+        public boolean equals(Object object) {
+            return this == object ? true : object instanceof GameRules.Key && ((GameRules.Key) object).id.equals(this.id);
+        }
+
+        public int hashCode() {
+            return this.id.hashCode();
+        }
+
+        public String getId() {
+            return this.id;
+        }
+
+        public String getDescriptionId() {
+            return "gamerule." + this.id;
+        }
+
+        public GameRules.Category getCategory() {
+            return this.category;
+        }
+    }
+
+    public static enum Category {
+
+        PLAYER("gamerule.category.player"), MOBS("gamerule.category.mobs"), SPAWNING("gamerule.category.spawning"), DROPS("gamerule.category.drops"), UPDATES("gamerule.category.updates"), CHAT("gamerule.category.chat"), MISC("gamerule.category.misc");
+
+        private final String descriptionId;
+
+        private Category(String s) {
+            this.descriptionId = s;
+        }
+
+        public String getDescriptionId() {
+            return this.descriptionId;
+        }
+    }
+
+    public static class Type<T extends GameRules.Value<T>> {
+
+        private final Supplier<ArgumentType<?>> argument;
+        private final Function<GameRules.Type<T>, T> constructor;
+        final BiConsumer<net.minecraft.server.level.ServerLevel, T> callback; // Paper
+        private final GameRules.VisitorCaller<T> visitorCaller;
+
+        Type(Supplier<ArgumentType<?>> argumentType, Function<GameRules.Type<T>, T> ruleFactory, BiConsumer<net.minecraft.server.level.ServerLevel, T> changeCallback, GameRules.VisitorCaller<T> ruleAcceptor) { // Paper
+            this.argument = argumentType;
+            this.constructor = ruleFactory;
+            this.callback = changeCallback;
+            this.visitorCaller = ruleAcceptor;
+        }
+
+        public RequiredArgumentBuilder<CommandSourceStack, ?> createArgument(String name) {
+            return Commands.argument(name, (ArgumentType) this.argument.get());
+        }
+
+        public T createRule() {
+            return this.constructor.apply(this); // CraftBukkit - decompile error
+        }
+
+        public void callVisitor(GameRules.GameRuleTypeVisitor consumer, GameRules.Key<T> key) {
+            this.visitorCaller.call(consumer, key, this);
+        }
+    }
+
+    public abstract static class Value<T extends GameRules.Value<T>> {
+
+        protected final GameRules.Type<T> type;
+
+        public Value(GameRules.Type<T> type) {
+            this.type = type;
+        }
+
+        protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<T> gameRuleKey); // Paper
+
+        public void setFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<T> gameRuleKey) { // Paper
+            this.updateFromArgument(context, name, gameRuleKey); // Paper
+            this.onChanged(((CommandSourceStack) context.getSource()).getLevel()); // Paper
+        }
+
+        public void onChanged(@Nullable net.minecraft.server.level.ServerLevel server) { // Paper
+            if (server != null) {
+                this.type.callback.accept(server, this.getSelf());
+            }
+
+        }
+
+        public abstract void deserialize(String value); // PAIL - private->public
+
+        public abstract String serialize();
+
+        public String toString() {
+            return this.serialize();
+        }
+
+        public abstract int getCommandResult();
+
+        protected abstract T getSelf();
+
+        protected abstract T copy();
+
+        public abstract void setFrom(T rule, @Nullable net.minecraft.server.level.ServerLevel level); // Paper
+    }
+
+    public interface GameRuleTypeVisitor {
+
+        default <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {}
+
+        default void visitBoolean(GameRules.Key<GameRules.BooleanValue> key, GameRules.Type<GameRules.BooleanValue> type) {}
+
+        default void visitInteger(GameRules.Key<GameRules.IntegerValue> key, GameRules.Type<GameRules.IntegerValue> type) {}
     }
 
     public static class BooleanValue extends GameRules.Value<GameRules.BooleanValue> {
+
         private boolean value;
 
-        static GameRules.Type<GameRules.BooleanValue> create(boolean initialValue, BiConsumer<MinecraftServer, GameRules.BooleanValue> changeCallback) {
-            return new GameRules.Type<>(BoolArgumentType::bool, (type) -> {
-                return new GameRules.BooleanValue(type, initialValue);
+        static GameRules.Type<GameRules.BooleanValue> create(boolean initialValue, BiConsumer<net.minecraft.server.level.ServerLevel, GameRules.BooleanValue> changeCallback) { // Paper
+            return new GameRules.Type<>(BoolArgumentType::bool, (gamerules_gameruledefinition) -> {
+                return new GameRules.BooleanValue(gamerules_gameruledefinition, initialValue);
             }, changeCallback, GameRules.GameRuleTypeVisitor::visitBoolean);
         }
 
         static GameRules.Type<GameRules.BooleanValue> create(boolean initialValue) {
-            return create(initialValue, (server, rule) -> {
+            return BooleanValue.create(initialValue, (minecraftserver, gamerules_gameruleboolean) -> {
             });
         }
 
@@ -179,15 +323,18 @@
         }
 
         @Override
-        protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name) {
-            this.value = BoolArgumentType.getBool(context, name);
+        protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<BooleanValue> gameRuleKey) { // Paper start
+            io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(context.getSource().getBukkitWorld(), context.getSource().getBukkitSender(), (org.bukkit.GameRule<Boolean>) org.bukkit.GameRule.getByName(gameRuleKey.toString()), String.valueOf(BoolArgumentType.getBool(context, name)));
+            if (!event.callEvent()) return;
+            this.value = Boolean.parseBoolean(event.getValue());
+            // Paper end
         }
 
         public boolean get() {
             return this.value;
         }
 
-        public void set(boolean value, @Nullable MinecraftServer server) {
+        public void set(boolean value, @Nullable net.minecraft.server.level.ServerLevel server) { // Paper
             this.value = value;
             this.onChanged(server);
         }
@@ -198,7 +345,7 @@
         }
 
         @Override
-        protected void deserialize(String value) {
+        public void deserialize(String value) { // PAIL - protected->public
             this.value = Boolean.parseBoolean(value);
         }
 
@@ -217,55 +364,24 @@
             return new GameRules.BooleanValue(this.type, this.value);
         }
 
-        @Override
-        public void setFrom(GameRules.BooleanValue rule, @Nullable MinecraftServer server) {
+        public void setFrom(GameRules.BooleanValue rule, @Nullable net.minecraft.server.level.ServerLevel server) { // Paper
             this.value = rule.value;
             this.onChanged(server);
         }
     }
 
-    public static enum Category {
-        PLAYER("gamerule.category.player"),
-        MOBS("gamerule.category.mobs"),
-        SPAWNING("gamerule.category.spawning"),
-        DROPS("gamerule.category.drops"),
-        UPDATES("gamerule.category.updates"),
-        CHAT("gamerule.category.chat"),
-        MISC("gamerule.category.misc");
-
-        private final String descriptionId;
-
-        private Category(String category) {
-            this.descriptionId = category;
-        }
-
-        public String getDescriptionId() {
-            return this.descriptionId;
-        }
-    }
-
-    public interface GameRuleTypeVisitor {
-        default <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {
-        }
-
-        default void visitBoolean(GameRules.Key<GameRules.BooleanValue> key, GameRules.Type<GameRules.BooleanValue> type) {
-        }
-
-        default void visitInteger(GameRules.Key<GameRules.IntegerValue> key, GameRules.Type<GameRules.IntegerValue> type) {
-        }
-    }
-
     public static class IntegerValue extends GameRules.Value<GameRules.IntegerValue> {
+
         private int value;
 
-        private static GameRules.Type<GameRules.IntegerValue> create(int initialValue, BiConsumer<MinecraftServer, GameRules.IntegerValue> changeCallback) {
-            return new GameRules.Type<>(IntegerArgumentType::integer, (type) -> {
-                return new GameRules.IntegerValue(type, initialValue);
+        private static GameRules.Type<GameRules.IntegerValue> create(int initialValue, BiConsumer<net.minecraft.server.level.ServerLevel, GameRules.IntegerValue> changeCallback) { // Paper
+            return new GameRules.Type<>(IntegerArgumentType::integer, (gamerules_gameruledefinition) -> {
+                return new GameRules.IntegerValue(gamerules_gameruledefinition, initialValue);
             }, changeCallback, GameRules.GameRuleTypeVisitor::visitInteger);
         }
 
         static GameRules.Type<GameRules.IntegerValue> create(int initialValue) {
-            return create(initialValue, (server, rule) -> {
+            return IntegerValue.create(initialValue, (minecraftserver, gamerules_gameruleint) -> {
             });
         }
 
@@ -275,15 +391,18 @@
         }
 
         @Override
-        protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name) {
-            this.value = IntegerArgumentType.getInteger(context, name);
+        protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<IntegerValue> gameRuleKey) { // Paper start
+            io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(context.getSource().getBukkitWorld(), context.getSource().getBukkitSender(), (org.bukkit.GameRule<Integer>) org.bukkit.GameRule.getByName(gameRuleKey.toString()), String.valueOf(IntegerArgumentType.getInteger(context, name)));
+            if (!event.callEvent()) return;
+            this.value = Integer.parseInt(event.getValue());
+            // Paper end
         }
 
         public int get() {
             return this.value;
         }
 
-        public void set(int value, @Nullable MinecraftServer server) {
+        public void set(int value, @Nullable net.minecraft.server.level.ServerLevel server) { // Paper
             this.value = value;
             this.onChanged(server);
         }
@@ -294,15 +413,15 @@
         }
 
         @Override
-        protected void deserialize(String value) {
-            this.value = safeParse(value);
+        public void deserialize(String value) { // PAIL - protected->public
+            this.value = IntegerValue.safeParse(value);
         }
 
         public boolean tryDeserialize(String input) {
             try {
                 this.value = Integer.parseInt(input);
                 return true;
-            } catch (NumberFormatException var3) {
+            } catch (NumberFormatException numberformatexception) {
                 return false;
             }
         }
@@ -311,8 +430,8 @@
             if (!input.isEmpty()) {
                 try {
                     return Integer.parseInt(input);
-                } catch (NumberFormatException var2) {
-                    GameRules.LOGGER.warn("Failed to parse integer {}", (Object)input);
+                } catch (NumberFormatException numberformatexception) {
+                    GameRules.LOGGER.warn("Failed to parse integer {}", input);
                 }
             }
 
@@ -334,120 +453,14 @@
             return new GameRules.IntegerValue(this.type, this.value);
         }
 
-        @Override
-        public void setFrom(GameRules.IntegerValue rule, @Nullable MinecraftServer server) {
+        public void setFrom(GameRules.IntegerValue rule, @Nullable net.minecraft.server.level.ServerLevel server) { // Paper
             this.value = rule.value;
             this.onChanged(server);
         }
     }
 
-    public static final class Key<T extends GameRules.Value<T>> {
-        final String id;
-        private final GameRules.Category category;
-
-        public Key(String name, GameRules.Category category) {
-            this.id = name;
-            this.category = category;
-        }
-
-        @Override
-        public String toString() {
-            return this.id;
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            if (this == object) {
-                return true;
-            } else {
-                return object instanceof GameRules.Key && ((GameRules.Key)object).id.equals(this.id);
-            }
-        }
-
-        @Override
-        public int hashCode() {
-            return this.id.hashCode();
-        }
-
-        public String getId() {
-            return this.id;
-        }
-
-        public String getDescriptionId() {
-            return "gamerule." + this.id;
-        }
-
-        public GameRules.Category getCategory() {
-            return this.category;
-        }
-    }
-
-    public static class Type<T extends GameRules.Value<T>> {
-        private final Supplier<ArgumentType<?>> argument;
-        private final Function<GameRules.Type<T>, T> constructor;
-        final BiConsumer<MinecraftServer, T> callback;
-        private final GameRules.VisitorCaller<T> visitorCaller;
-
-        Type(Supplier<ArgumentType<?>> argumentType, Function<GameRules.Type<T>, T> ruleFactory, BiConsumer<MinecraftServer, T> changeCallback, GameRules.VisitorCaller<T> ruleAcceptor) {
-            this.argument = argumentType;
-            this.constructor = ruleFactory;
-            this.callback = changeCallback;
-            this.visitorCaller = ruleAcceptor;
-        }
-
-        public RequiredArgumentBuilder<CommandSourceStack, ?> createArgument(String name) {
-            return Commands.argument(name, this.argument.get());
-        }
-
-        public T createRule() {
-            return this.constructor.apply(this);
-        }
-
-        public void callVisitor(GameRules.GameRuleTypeVisitor consumer, GameRules.Key<T> key) {
-            this.visitorCaller.call(consumer, key, this);
-        }
-    }
-
-    public abstract static class Value<T extends GameRules.Value<T>> {
-        protected final GameRules.Type<T> type;
-
-        public Value(GameRules.Type<T> type) {
-            this.type = type;
-        }
-
-        protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String name);
-
-        public void setFromArgument(CommandContext<CommandSourceStack> context, String name) {
-            this.updateFromArgument(context, name);
-            this.onChanged(context.getSource().getServer());
-        }
-
-        public void onChanged(@Nullable MinecraftServer server) {
-            if (server != null) {
-                this.type.callback.accept(server, this.getSelf());
-            }
-
-        }
-
-        protected abstract void deserialize(String value);
-
-        public abstract String serialize();
-
-        @Override
-        public String toString() {
-            return this.serialize();
-        }
-
-        public abstract int getCommandResult();
-
-        protected abstract T getSelf();
-
-        protected abstract T copy();
-
-        public abstract void setFrom(T rule, @Nullable MinecraftServer server);
-    }
+    private interface VisitorCaller<T extends GameRules.Value<T>> {
 
-    interface VisitorCaller<T extends GameRules.Value<T>> {
         void call(GameRules.GameRuleTypeVisitor consumer, GameRules.Key<T> key, GameRules.Type<T> type);
     }
 }
