Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct synthetic parameter handling in inner classes #98

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

zml2008
Copy link
Collaborator

@zml2008 zml2008 commented May 27, 2021

Broken out of #96

It appears that a lot of inner classes have gained a Signature attribute, which causes generic signature processing to activate. This exposed some issues in its handling of synthetic constructor parameters, which I think I've taken care of enough to correctly

(not sure how these changes would react if someone disabled enum handling though)

Comparisons (via VanillaGradle)

1.16.5
diff -r -u3 -N a/net/minecraft/world/entity/animal/Wolf.java b/net/minecraft/world/entity/animal/Wolf.java
--- a/net/minecraft/world/entity/animal/Wolf.java	2021-04-25 19:34:12.000000000 -0700
+++ b/net/minecraft/world/entity/animal/Wolf.java	2021-05-26 20:44:06.000000000 -0700
@@ -89,7 +89,7 @@
     protected void registerGoals() {
         this.goalSelector.addGoal(1, new FloatGoal(this));
         this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
-        this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal(this, Llama.class, 24.0F, 1.5D, 1.5D));
+        this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5D, 1.5D));
         this.goalSelector.addGoal(4, new LeapAtTargetGoal(this, 0.4F));
         this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0D, true));
         this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0D, 10.0F, 2.0F, false));

note: There are some new warnings in the decompiler output here, which appear to be classes where PG has stripped the constructor. This is due to the change to now calculate synthetic parameter masks for inner classes that are marked static. This change is required because starting with 21w19a, Mojang's tooling has corrupted Minecraft's inner class metadata and made some non-static inner classes be marked as static.

The warnings:

pool-2-thread-3: WARN:  Nested class net/minecraft/world/level/levelgen/feature/configurations/OreConfiguration$Predicates has no constructor!
pool-2-thread-14: WARN:  Nested class net/minecraft/nbt/IntTag$Cache has no constructor!
pool-2-thread-9: WARN:  Nested class net/minecraft/nbt/LongTag$Cache has no constructor!
pool-2-thread-10: WARN:  Nested class net/minecraft/nbt/ByteTag$Cache has no constructor!
pool-2-thread-8: WARN:  Nested class net/minecraft/util/FastColor$ARGB32 has no constructor!
pool-2-thread-5: WARN:  Nested class net/minecraft/nbt/ShortTag$Cache has no constructor!
pool-2-thread-15: WARN:  Nested class net/minecraft/data/worldgen/Features$Configs has no constructor!
pool-2-thread-15: WARN:  Nested class net/minecraft/data/worldgen/Features$Decorators has no constructor!
pool-2-thread-15: WARN:  Nested class net/minecraft/data/worldgen/Features$States has no constructor!
pool-2-thread-9: WARN:  Nested class net/minecraft/client/renderer/FaceInfo$Constants has no constructor!
21w19a
diff -r -u3 -N a/net/minecraft/client/gui/components/CycleButton.java b/net/minecraft/client/gui/components/CycleButton.java
--- a/net/minecraft/client/gui/components/CycleButton.java	2021-05-12 08:33:10.000000000 -0700
+++ b/net/minecraft/client/gui/components/CycleButton.java	2021-05-26 20:45:18.000000000 -0700
@@ -215,11 +215,30 @@
         List<T> getDefaultList();
 
         static <T> CycleButton.ValueListSupplier<T> create(List<T> param0) {
-            // $FF: Couldn't be decompiled
+            final List<T> var0 = ImmutableList.copyOf(param0);
+            return new CycleButton.ValueListSupplier<T>() {
+                public List<T> getSelectedList() {
+                    return var0;
+                }
+
+                public List<T> getDefaultList() {
+                    return var0;
+                }
+            };
         }
 
         static <T> CycleButton.ValueListSupplier<T> create(final BooleanSupplier param0, List<T> param1, List<T> param2) {
-            // $FF: Couldn't be decompiled
+            final List<T> var0 = ImmutableList.copyOf(param1);
+            final List<T> var1 = ImmutableList.copyOf(param2);
+            return new CycleButton.ValueListSupplier<T>() {
+                public List<T> getSelectedList() {
+                    return param0.getAsBoolean() ? var1 : var0;
+                }
+
+                public List<T> getDefaultList() {
+                    return var0;
+                }
+            };
         }
     }
 }
diff -r -u3 -N a/net/minecraft/client/gui/components/MultiLineLabel.java b/net/minecraft/client/gui/components/MultiLineLabel.java
--- a/net/minecraft/client/gui/components/MultiLineLabel.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/client/gui/components/MultiLineLabel.java	2021-05-26 20:45:18.000000000 -0700
@@ -4,6 +4,7 @@
 import com.mojang.blaze3d.vertex.PoseStack;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import net.minecraft.client.gui.Font;
 import net.minecraft.network.chat.Component;
 import net.minecraft.network.chat.FormattedText;
@@ -52,7 +53,49 @@
     }
 
     static MultiLineLabel createFixed(final Font param0, final List<MultiLineLabel.TextWithWidth> param1) {
-        // $FF: Couldn't be decompiled
+        return param1.isEmpty() ? EMPTY : new MultiLineLabel() {
+            public int renderCentered(PoseStack param0x, int param1x, int param2) {
+                Objects.requireNonNull(param0);
+                return this.renderCentered(param0, param1, param2, 9, 16777215);
+            }
+
+            public int renderCentered(PoseStack param0x, int param1x, int param2, int param3, int param4) {
+                int var0 = param2;
+
+                for(MultiLineLabel.TextWithWidth var1 : param1) {
+                    param0.drawShadow(param0, var1.text, (float)(param1 - var1.width / 2), (float)var0, param4);
+                    var0 += param3;
+                }
+
+                return var0;
+            }
+
+            public int renderLeftAligned(PoseStack param0x, int param1x, int param2, int param3, int param4) {
+                int var0 = param2;
+
+                for(MultiLineLabel.TextWithWidth var1 : param1) {
+                    param0.drawShadow(param0, var1.text, (float)param1, (float)var0, param4);
+                    var0 += param3;
+                }
+
+                return var0;
+            }
+
+            public int renderLeftAlignedNoShadow(PoseStack param0x, int param1x, int param2, int param3, int param4) {
+                int var0 = param2;
+
+                for(MultiLineLabel.TextWithWidth var1 : param1) {
+                    param0.draw(param0, var1.text, (float)param1, (float)var0, param4);
+                    var0 += param3;
+                }
+
+                return var0;
+            }
+
+            public int getLineCount() {
+                return param1.size();
+            }
+        };
     }
 
     int renderCentered(PoseStack var1, int var2, int var3);
diff -r -u3 -N a/net/minecraft/client/gui/components/toasts/ToastComponent.java b/net/minecraft/client/gui/components/toasts/ToastComponent.java
--- a/net/minecraft/client/gui/components/toasts/ToastComponent.java	2021-05-12 08:33:04.000000000 -0700
+++ b/net/minecraft/client/gui/components/toasts/ToastComponent.java	2021-05-26 20:45:18.000000000 -0700
@@ -33,7 +33,7 @@
                 }
 
                 if (this.visible[var0] == null && !this.queued.isEmpty()) {
-                    this.visible[var0] = new ToastComponent.ToastInstance(this.queued.removeFirst());
+                    this.visible[var0] = new ToastComponent.ToastInstance<>(this.queued.removeFirst());
                 }
             }
 
diff -r -u3 -N a/net/minecraft/client/gui/screens/inventory/BeaconScreen.java b/net/minecraft/client/gui/screens/inventory/BeaconScreen.java
--- a/net/minecraft/client/gui/screens/inventory/BeaconScreen.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/client/gui/screens/inventory/BeaconScreen.java	2021-05-26 20:45:18.000000000 -0700
@@ -17,6 +17,7 @@
 import net.minecraft.world.effect.MobEffect;
 import net.minecraft.world.effect.MobEffects;
 import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.inventory.AbstractContainerMenu;
 import net.minecraft.world.inventory.BeaconMenu;
 import net.minecraft.world.inventory.ContainerListener;
 import net.minecraft.world.item.ItemStack;
@@ -36,7 +37,19 @@
     MobEffect secondary;
 
     public BeaconScreen(final BeaconMenu param0, Inventory param1, Component param2) {
-        // $FF: Couldn't be decompiled
+        super(param0, param1, param2);
+        this.imageWidth = 230;
+        this.imageHeight = 219;
+        param0.addSlotListener(new ContainerListener() {
+            public void slotChanged(AbstractContainerMenu param0x, int param1, ItemStack param2) {
+            }
+
+            public void dataChanged(AbstractContainerMenu param0x, int param1, int param2) {
+                BeaconScreen.this.primary = param0.getPrimaryEffect();
+                BeaconScreen.this.secondary = param0.getSecondaryEffect();
+                BeaconScreen.this.initPowerButtons = true;
+            }
+        });
     }
 
     protected void init() {
diff -r -u3 -N a/net/minecraft/client/gui/screens/worldselection/EditGameRulesScreen.java b/net/minecraft/client/gui/screens/worldselection/EditGameRulesScreen.java
--- a/net/minecraft/client/gui/screens/worldselection/EditGameRulesScreen.java	2021-05-12 08:32:52.000000000 -0700
+++ b/net/minecraft/client/gui/screens/worldselection/EditGameRulesScreen.java	2021-05-26 20:45:18.000000000 -0700
@@ -6,12 +6,15 @@
 import com.google.common.collect.Sets;
 import com.google.common.collect.ImmutableList.Builder;
 import com.mojang.blaze3d.vertex.PoseStack;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Consumer;
 import javax.annotation.Nullable;
+import net.minecraft.ChatFormatting;
 import net.minecraft.client.gui.GuiComponent;
 import net.minecraft.client.gui.components.Button;
 import net.minecraft.client.gui.components.ContainerObjectSelectionList;
@@ -19,8 +22,10 @@
 import net.minecraft.client.gui.components.EditBox;
 import net.minecraft.client.gui.components.events.GuiEventListener;
 import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.client.resources.language.I18n;
 import net.minecraft.network.chat.CommonComponents;
 import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.TextComponent;
 import net.minecraft.network.chat.TranslatableComponent;
 import net.minecraft.server.MinecraftServer;
 import net.minecraft.util.FormattedCharSequence;
@@ -199,7 +204,46 @@
     @OnlyIn(Dist.CLIENT)
     public class RuleList extends ContainerObjectSelectionList<EditGameRulesScreen.RuleEntry> {
         public RuleList(final GameRules param1) {
-            // $FF: Couldn't be decompiled
+            super(EditGameRulesScreen.this.minecraft, EditGameRulesScreen.this.width, EditGameRulesScreen.this.height, 43, EditGameRulesScreen.this.height - 32, 24);
+            final Map<GameRules.Category, Map<GameRules.Key<?>, EditGameRulesScreen.RuleEntry>> var0 = Maps.newHashMap();
+            GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() {
+                public void visitBoolean(GameRules.Key<GameRules.BooleanValue> param0, GameRules.Type<GameRules.BooleanValue> param1x) {
+                    this.addEntry(param0, (param0x, param1xx, param2, param3) -> EditGameRulesScreen.this.new BooleanRuleEntry(param0x, param1xx, param2, param3));
+                }
+
+                public void visitInteger(GameRules.Key<GameRules.IntegerValue> param0, GameRules.Type<GameRules.IntegerValue> param1x) {
+                    this.addEntry(param0, (param0x, param1xx, param2, param3) -> EditGameRulesScreen.this.new IntegerRuleEntry(param0x, param1xx, param2, param3));
+                }
+
+                private <T extends GameRules.Value<T>> void addEntry(GameRules.Key<T> param0, EditGameRulesScreen.EntryFactory<T> param1x) {
+                    Component var0 = new TranslatableComponent(param0.getDescriptionId());
+                    Component var1 = (new TextComponent(param0.getId())).withStyle(ChatFormatting.YELLOW);
+                    T var2 = param1.getRule(param0);
+                    String var3 = var2.serialize();
+                    Component var4 = (new TranslatableComponent("editGamerule.default", new TextComponent(var3))).withStyle(ChatFormatting.GRAY);
+                    String var5 = param0.getDescriptionId() + ".description";
+                    List<FormattedCharSequence> var8;
+                    String var9;
+                    if (I18n.exists(var5)) {
+                        Builder<FormattedCharSequence> var6 = ImmutableList.<FormattedCharSequence>builder().add(var1.getVisualOrderText());
+                        Component var7 = new TranslatableComponent(var5);
+                        List var10000 = EditGameRulesScreen.this.font.split(var7, 150);
+                        Objects.requireNonNull(var6);
+                        var10000.forEach(var6::add);
+                        var8 = var6.add(var4.getVisualOrderText()).build();
+                        var9 = var7.getString() + "\n" + var4.getString();
+                    } else {
+                        var8 = ImmutableList.of(var1.getVisualOrderText(), var4.getVisualOrderText());
+                        var9 = var4.getString();
+                    }
+
+                    var0.computeIfAbsent(param0.getCategory(), (param0x) -> Maps.newHashMap()).put(param0, param1.create(var0, var8, var9, var2));
+                }
+            });
+            var0.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach((param0x) -> {
+                this.addEntry(EditGameRulesScreen.this.new CategoryRuleEntry((new TranslatableComponent(param0x.getKey().getDescriptionId())).withStyle(new ChatFormatting[]{ChatFormatting.BOLD, ChatFormatting.YELLOW})));
+                param0x.getValue().entrySet().stream().sorted(Map.Entry.comparingByKey(Comparator.comparing(GameRules.Key::getId))).forEach((param0xx) -> this.addEntry(param0xx.getValue()));
+            });
         }
 
         public void render(PoseStack param0, int param1, int param2, float param3) {
diff -r -u3 -N a/net/minecraft/client/multiplayer/ServerStatusPinger.java b/net/minecraft/client/multiplayer/ServerStatusPinger.java
--- a/net/minecraft/client/multiplayer/ServerStatusPinger.java	2021-05-12 08:33:10.000000000 -0700
+++ b/net/minecraft/client/multiplayer/ServerStatusPinger.java	2021-05-26 20:45:18.000000000 -0700
@@ -3,6 +3,7 @@
 import com.google.common.base.Splitter;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import com.mojang.authlib.GameProfile;
 import io.netty.bootstrap.Bootstrap;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
@@ -20,20 +21,30 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import net.minecraft.ChatFormatting;
+import net.minecraft.Util;
 import net.minecraft.client.gui.screens.ConnectScreen;
 import net.minecraft.client.multiplayer.resolver.ResolvedServerAddress;
 import net.minecraft.client.multiplayer.resolver.ServerAddress;
 import net.minecraft.client.multiplayer.resolver.ServerNameResolver;
 import net.minecraft.network.Connection;
+import net.minecraft.network.ConnectionProtocol;
 import net.minecraft.network.chat.Component;
 import net.minecraft.network.chat.TextComponent;
 import net.minecraft.network.chat.TranslatableComponent;
+import net.minecraft.network.protocol.handshake.ClientIntentionPacket;
 import net.minecraft.network.protocol.status.ClientStatusPacketListener;
+import net.minecraft.network.protocol.status.ClientboundPongResponsePacket;
+import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket;
+import net.minecraft.network.protocol.status.ServerStatus;
+import net.minecraft.network.protocol.status.ServerboundPingRequestPacket;
+import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket;
 import net.minecraft.util.Mth;
 import net.minecraftforge.api.distmarker.Dist;
 import net.minecraftforge.api.distmarker.OnlyIn;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -45,7 +56,109 @@
     private final List<Connection> connections = Collections.synchronizedList(Lists.newArrayList());
 
     public void pingServer(final ServerData param0, final Runnable param1) throws UnknownHostException {
-        // $FF: Couldn't be decompiled
+        ServerAddress var0 = ServerAddress.parseString(param0.ip);
+        Optional<InetSocketAddress> var1 = ServerNameResolver.DEFAULT.resolveAddress(var0).map(ResolvedServerAddress::asInetSocketAddress);
+        if (!var1.isPresent()) {
+            this.onPingFailed(ConnectScreen.UNKNOWN_HOST_MESSAGE, param0);
+        } else {
+            final InetSocketAddress var2 = var1.get();
+            final Connection var3 = Connection.connectToServer(var2, false);
+            this.connections.add(var3);
+            param0.motd = new TranslatableComponent("multiplayer.status.pinging");
+            param0.ping = -1L;
+            param0.playerList = null;
+            var3.setListener(new ClientStatusPacketListener() {
+                private boolean success;
+                private boolean receivedPing;
+                private long pingStart;
+
+                public void handleStatusResponse(ClientboundStatusResponsePacket param0x) {
+                    if (this.receivedPing) {
+                        var3.disconnect(new TranslatableComponent("multiplayer.status.unrequested"));
+                    } else {
+                        this.receivedPing = true;
+                        ServerStatus var0 = param0.getStatus();
+                        if (var0.getDescription() != null) {
+                            param0.motd = var0.getDescription();
+                        } else {
+                            param0.motd = TextComponent.EMPTY;
+                        }
+
+                        if (var0.getVersion() != null) {
+                            param0.version = new TextComponent(var0.getVersion().getName());
+                            param0.protocol = var0.getVersion().getProtocol();
+                        } else {
+                            param0.version = new TranslatableComponent("multiplayer.status.old");
+                            param0.protocol = 0;
+                        }
+
+                        if (var0.getPlayers() != null) {
+                            param0.status = ServerStatusPinger.formatPlayerCount(var0.getPlayers().getNumPlayers(), var0.getPlayers().getMaxPlayers());
+                            List<Component> var1 = Lists.newArrayList();
+                            if (ArrayUtils.isNotEmpty(var0.getPlayers().getSample())) {
+                                for(GameProfile var2 : var0.getPlayers().getSample()) {
+                                    var1.add(new TextComponent(var2.getName()));
+                                }
+
+                                if (var0.getPlayers().getSample().length < var0.getPlayers().getNumPlayers()) {
+                                    var1.add(new TranslatableComponent("multiplayer.status.and_more", var0.getPlayers().getNumPlayers() - var0.getPlayers().getSample().length));
+                                }
+
+                                param0.playerList = var1;
+                            }
+                        } else {
+                            param0.status = (new TranslatableComponent("multiplayer.status.unknown")).withStyle(ChatFormatting.DARK_GRAY);
+                        }
+
+                        String var3 = null;
+                        if (var0.getFavicon() != null) {
+                            String var4 = var0.getFavicon();
+                            if (var4.startsWith("data:image/png;base64,")) {
+                                var3 = var4.substring("data:image/png;base64,".length());
+                            } else {
+                                ServerStatusPinger.LOGGER.error("Invalid server icon (unknown format)");
+                            }
+                        }
+
+                        if (!Objects.equals(var3, param0.getIconB64())) {
+                            param0.setIconB64(var3);
+                            param1.run();
+                        }
+
+                        this.pingStart = Util.getMillis();
+                        var3.send(new ServerboundPingRequestPacket(this.pingStart));
+                        this.success = true;
+                    }
+                }
+
+                public void handlePongResponse(ClientboundPongResponsePacket param0x) {
+                    long var0 = this.pingStart;
+                    long var1 = Util.getMillis();
+                    param0.ping = var1 - var0;
+                    var3.disconnect(new TranslatableComponent("multiplayer.status.finished"));
+                }
+
+                public void onDisconnect(Component param0x) {
+                    if (!this.success) {
+                        ServerStatusPinger.this.onPingFailed(param0, param0);
+                        ServerStatusPinger.this.pingLegacyServer(var2, param0);
+                    }
+
+                }
+
+                public Connection getConnection() {
+                    return var3;
+                }
+            });
+
+            try {
+                var3.send(new ClientIntentionPacket(var0.getHost(), var0.getPort(), ConnectionProtocol.STATUS));
+                var3.send(new ServerboundStatusRequestPacket());
+            } catch (Throwable var8) {
+                LOGGER.error(var8);
+            }
+
+        }
     }
 
     void onPingFailed(Component param0, ServerData param1) {
diff -r -u3 -N a/net/minecraft/client/Options.java b/net/minecraft/client/Options.java
--- a/net/minecraft/client/Options.java	2021-05-12 08:33:00.000000000 -0700
+++ b/net/minecraft/client/Options.java	2021-05-26 20:45:18.000000000 -0700
@@ -1,6 +1,7 @@
 package net.minecraft.client;
 
 import com.google.common.base.Charsets;
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -302,7 +303,151 @@
     }
 
     public void load() {
-        // $FF: Couldn't be decompiled
+        try {
+            if (!this.optionsFile.exists()) {
+                return;
+            }
+
+            this.sourceVolumes.clear();
+            CompoundTag var0 = new CompoundTag();
+            BufferedReader var1 = Files.newReader(this.optionsFile, Charsets.UTF_8);
+
+            try {
+                var1.lines().forEach((param1) -> {
+                    try {
+                        Iterator<String> var0x = OPTION_SPLITTER.split(param1).iterator();
+                        var0.putString(var0x.next(), var0x.next());
+                    } catch (Exception var3) {
+                        LOGGER.warn("Skipping bad option: {}", (Object)param1);
+                    }
+
+                });
+            } catch (Throwable var6) {
+                if (var1 != null) {
+                    try {
+                        var1.close();
+                    } catch (Throwable var5) {
+                        var6.addSuppressed(var5);
+                    }
+                }
+
+                throw var6;
+            }
+
+            if (var1 != null) {
+                var1.close();
+            }
+
+            final CompoundTag var2 = this.dataFix(var0);
+            if (!var2.contains("graphicsMode") && var2.contains("fancyGraphics")) {
+                if (isTrue(var2.getString("fancyGraphics"))) {
+                    this.graphicsMode = GraphicsStatus.FANCY;
+                } else {
+                    this.graphicsMode = GraphicsStatus.FAST;
+                }
+            }
+
+            this.processOptions(new Options.FieldAccess() {
+                @Nullable
+                private String getValueOrNull(String param0) {
+                    return var2.contains(param0) ? var2.getString(param0) : null;
+                }
+
+                public int process(String param0, int param1) {
+                    String var0 = this.getValueOrNull(param0);
+                    if (var0 != null) {
+                        try {
+                            return Integer.parseInt(var0);
+                        } catch (NumberFormatException var5) {
+                            Options.LOGGER.warn("Invalid integer value for option {} = {}", param0, var0, var5);
+                        }
+                    }
+
+                    return param1;
+                }
+
+                public boolean process(String param0, boolean param1) {
+                    String var0 = this.getValueOrNull(param0);
+                    return var0 != null ? Options.isTrue(var0) : param1;
+                }
+
+                public String process(String param0, String param1) {
+                    return MoreObjects.firstNonNull(this.getValueOrNull(param0), param1);
+                }
+
+                public double process(String param0, double param1) {
+                    String var0 = this.getValueOrNull(param0);
+                    if (var0 != null) {
+                        if (Options.isTrue(var0)) {
+                            return 1.0D;
+                        }
+
+                        if (Options.isFalse(var0)) {
+                            return 0.0D;
+                        }
+
+                        try {
+                            return Double.parseDouble(var0);
+                        } catch (NumberFormatException var6) {
+                            Options.LOGGER.warn("Invalid floating point value for option {} = {}", param0, var0, var6);
+                        }
+                    }
+
+                    return param1;
+                }
+
+                public float process(String param0, float param1) {
+                    String var0 = this.getValueOrNull(param0);
+                    if (var0 != null) {
+                        if (Options.isTrue(var0)) {
+                            return 1.0F;
+                        }
+
+                        if (Options.isFalse(var0)) {
+                            return 0.0F;
+                        }
+
+                        try {
+                            return Float.parseFloat(var0);
+                        } catch (NumberFormatException var5) {
+                            Options.LOGGER.warn("Invalid floating point value for option {} = {}", param0, var0, var5);
+                        }
+                    }
+
+                    return param1;
+                }
+
+                public <T> T process(String param0, T param1, Function<String, T> param2, Function<T, String> param3) {
+                    String var0 = this.getValueOrNull(param0);
+                    return (T)(var0 == null ? param1 : param2.apply(var0));
+                }
+
+                public <T> T process(String param0, T param1, IntFunction<T> param2, ToIntFunction<T> param3) {
+                    String var0 = this.getValueOrNull(param0);
+                    if (var0 != null) {
+                        try {
+                            return param2.apply(Integer.parseInt(var0));
+                        } catch (Exception var7) {
+                            Options.LOGGER.warn("Invalid integer value for option {} = {}", param0, var0, var7);
+                        }
+                    }
+
+                    return param1;
+                }
+            });
+            if (var2.contains("fullscreenResolution")) {
+                this.fullscreenVideoModeString = var2.getString("fullscreenResolution");
+            }
+
+            if (this.minecraft.getWindow() != null) {
+                this.minecraft.getWindow().setFramerateLimit(this.framerateLimit);
+            }
+
+            KeyMapping.resetMapping();
+        } catch (Exception var7) {
+            LOGGER.error("Failed to load options", (Throwable)var7);
+        }
+
     }
 
     static boolean isTrue(String param0) {
@@ -325,7 +470,78 @@
     }
 
     public void save() {
-        // $FF: Couldn't be decompiled
+        try {
+            final PrintWriter var0 = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.optionsFile), StandardCharsets.UTF_8));
+
+            try {
+                var0.println("version:" + SharedConstants.getCurrentVersion().getWorldVersion());
+                this.processOptions(new Options.FieldAccess() {
+                    public void writePrefix(String param0) {
+                        var0.print(param0);
+                        var0.print(':');
+                    }
+
+                    public int process(String param0, int param1) {
+                        this.writePrefix(param0);
+                        var0.println(param1);
+                        return param1;
+                    }
+
+                    public boolean process(String param0, boolean param1) {
+                        this.writePrefix(param0);
+                        var0.println(param1);
+                        return param1;
+                    }
+
+                    public String process(String param0, String param1) {
+                        this.writePrefix(param0);
+                        var0.println(param1);
+                        return param1;
+                    }
+
+                    public double process(String param0, double param1) {
+                        this.writePrefix(param0);
+                        var0.println(param1);
+                        return param1;
+                    }
+
+                    public float process(String param0, float param1) {
+                        this.writePrefix(param0);
+                        var0.println(param1);
+                        return param1;
+                    }
+
+                    public <T> T process(String param0, T param1, Function<String, T> param2, Function<T, String> param3) {
+                        this.writePrefix(param0);
+                        var0.println(param3.apply(param1));
+                        return param1;
+                    }
+
+                    public <T> T process(String param0, T param1, IntFunction<T> param2, ToIntFunction<T> param3) {
+                        this.writePrefix(param0);
+                        var0.println(param3.applyAsInt(param1));
+                        return param1;
+                    }
+                });
+                if (this.minecraft.getWindow().getPreferredFullscreenVideoMode().isPresent()) {
+                    var0.println("fullscreenResolution:" + this.minecraft.getWindow().getPreferredFullscreenVideoMode().get().write());
+                }
+            } catch (Throwable var5) {
+                try {
+                    var0.close();
+                } catch (Throwable var4) {
+                    var5.addSuppressed(var4);
+                }
+
+                throw var5;
+            }
+
+            var0.close();
+        } catch (Exception var6) {
+            LOGGER.error("Failed to save options", (Throwable)var6);
+        }
+
+        this.broadcastOptions();
     }
 
     public float getSoundSourceVolume(SoundSource param0) {
diff -r -u3 -N a/net/minecraft/client/searchtree/SuffixArray.java b/net/minecraft/client/searchtree/SuffixArray.java
--- a/net/minecraft/client/searchtree/SuffixArray.java	2021-05-12 08:32:52.000000000 -0700
+++ b/net/minecraft/client/searchtree/SuffixArray.java	2021-05-26 20:45:18.000000000 -0700
@@ -2,12 +2,13 @@
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.Arrays;
+import it.unimi.dsi.fastutil.Swapper;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntComparator;
 import it.unimi.dsi.fastutil.ints.IntList;
 import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
 import it.unimi.dsi.fastutil.ints.IntSet;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -48,7 +49,73 @@
     }
 
     public void generate() {
-        // $FF: Couldn't be decompiled
+        int var0 = this.chars.size();
+        int[] var1 = new int[var0];
+        final int[] var2 = new int[var0];
+        final int[] var3 = new int[var0];
+        int[] var4 = new int[var0];
+        IntComparator var5 = new IntComparator() {
+            public int compare(int param0, int param1) {
+                return var2[param0] == var2[param1] ? Integer.compare(var3[param0], var3[param1]) : Integer.compare(var2[param0], var2[param1]);
+            }
+
+            public int compare(Integer param0, Integer param1) {
+                return this.compare(param0.intValue(), param1.intValue());
+            }
+        };
+        Swapper var6 = (param3, param4) -> {
+            if (param3 != param4) {
+                int var0x = var2[param3];
+                var2[param3] = var2[param4];
+                var2[param4] = var0x;
+                var0x = var3[param3];
+                var3[param3] = var3[param4];
+                var3[param4] = var0x;
+                var0x = var4[param3];
+                var4[param3] = var4[param4];
+                var4[param4] = var0x;
+            }
+
+        };
+
+        for(int var7 = 0; var7 < var0; ++var7) {
+            var1[var7] = this.chars.getInt(var7);
+        }
+
+        int var8 = 1;
+
+        for(int var9 = Math.min(var0, this.maxStringLength); var8 * 2 < var9; var8 *= 2) {
+            for(int var10 = 0; var10 < var0; var4[var10] = var10++) {
+                var2[var10] = var1[var10];
+                var3[var10] = var10 + var8 < var0 ? var1[var10 + var8] : -2;
+            }
+
+            Arrays.quickSort(0, var0, var5, var6);
+
+            for(int var11 = 0; var11 < var0; ++var11) {
+                if (var11 > 0 && var2[var11] == var2[var11 - 1] && var3[var11] == var3[var11 - 1]) {
+                    var1[var4[var11]] = var1[var4[var11 - 1]];
+                } else {
+                    var1[var4[var11]] = var11;
+                }
+            }
+        }
+
+        IntList var12 = this.suffixToT;
+        IntList var13 = this.offsets;
+        this.suffixToT = new IntArrayList(var12.size());
+        this.offsets = new IntArrayList(var13.size());
+
+        for(int var14 = 0; var14 < var0; ++var14) {
+            int var15 = var4[var14];
+            this.suffixToT.add(var12.getInt(var15));
+            this.offsets.add(var13.getInt(var15));
+        }
+
+        if (DEBUG_ARRAY) {
+            this.print();
+        }
+
     }
 
     private void print() {
@@ -149,7 +216,7 @@
             }
 
             int[] var11 = var9.toIntArray();
-            Arrays.sort(var11);
+            java.util.Arrays.sort(var11);
             Set<T> var12 = Sets.newLinkedHashSet();
 
             for(int var13 : var11) {
diff -r -u3 -N a/net/minecraft/client/sounds/SoundManager.java b/net/minecraft/client/sounds/SoundManager.java
--- a/net/minecraft/client/sounds/SoundManager.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/client/sounds/SoundManager.java	2021-05-26 20:45:18.000000000 -0700
@@ -238,7 +238,60 @@
         final Map<ResourceLocation, WeighedSoundEvents> registry = Maps.newHashMap();
 
         void handleRegistration(ResourceLocation param0, SoundEventRegistration param1, ResourceManager param2) {
-            // $FF: Couldn't be decompiled
+            WeighedSoundEvents var0 = this.registry.get(param0);
+            boolean var1 = var0 == null;
+            if (var1 || param1.isReplace()) {
+                if (!var1) {
+                    SoundManager.LOGGER.debug("Replaced sound event location {}", (Object)param0);
+                }
+
+                var0 = new WeighedSoundEvents(param0, param1.getSubtitle());
+                this.registry.put(param0, var0);
+            }
+
+            for(final Sound var2 : param1.getSounds()) {
+                final ResourceLocation var3 = var2.getLocation();
+                Weighted<Sound> var5;
+                switch(var2.getType()) {
+                case FILE:
+                    if (!SoundManager.validateSoundResource(var2, param0, param2)) {
+                        continue;
+                    }
+
+                    var5 = var2;
+                    break;
+                case SOUND_EVENT:
+                    var5 = new Weighted<Sound>() {
+                        public int getWeight() {
+                            WeighedSoundEvents var0 = Preparations.this.registry.get(var3);
+                            return var0 == null ? 0 : var0.getWeight();
+                        }
+
+                        public Sound getSound() {
+                            WeighedSoundEvents var0 = Preparations.this.registry.get(var3);
+                            if (var0 == null) {
+                                return SoundManager.EMPTY_SOUND;
+                            } else {
+                                Sound var1 = var0.getSound();
+                                return new Sound(var1.getLocation().toString(), var1.getVolume() * var2.getVolume(), var1.getPitch() * var2.getPitch(), var2.getWeight(), Sound.Type.FILE, var1.shouldStream() || var2.shouldStream(), var1.shouldPreload(), var1.getAttenuationDistance());
+                            }
+                        }
+
+                        public void preloadIfRequired(SoundEngine param0) {
+                            WeighedSoundEvents var0 = Preparations.this.registry.get(var3);
+                            if (var0 != null) {
+                                var0.preloadIfRequired(param0);
+                            }
+                        }
+                    };
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown SoundEventRegistration type: " + var2.getType());
+                }
+
+                var0.addSound(var5);
+            }
+
         }
 
         public void apply(Map<ResourceLocation, WeighedSoundEvents> param0, SoundEngine param1) {
diff -r -u3 -N a/net/minecraft/client/StringSplitter.java b/net/minecraft/client/StringSplitter.java
--- a/net/minecraft/client/StringSplitter.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/client/StringSplitter.java	2021-05-26 20:45:18.000000000 -0700
@@ -115,7 +115,28 @@
     }
 
     public FormattedText headByWidth(FormattedText param0, int param1, Style param2) {
-        // $FF: Couldn't be decompiled
+        final StringSplitter.WidthLimitedCharSink var0 = new StringSplitter.WidthLimitedCharSink((float)param1);
+        return param0.visit(new FormattedText.StyledContentConsumer<FormattedText>() {
+            private final ComponentCollector collector = new ComponentCollector();
+
+            public Optional<FormattedText> accept(Style param0, String param1) {
+                var0.resetPosition();
+                if (!StringDecomposer.iterateFormatted(param1, param0, var0)) {
+                    String var0 = param1.substring(0, var0.getPosition());
+                    if (!var0.isEmpty()) {
+                        this.collector.append(FormattedText.of(var0, param0));
+                    }
+
+                    return Optional.of(this.collector.getResultOrEmpty());
+                } else {
+                    if (!param1.isEmpty()) {
+                        this.collector.append(FormattedText.of(param1, param0));
+                    }
+
+                    return Optional.empty();
+                }
+            }
+        }, param2).orElse(param0);
     }
 
     public int findLineBreak(String param0, int param1, Style param2) {
diff -r -u3 -N a/net/minecraft/commands/arguments/item/FunctionArgument.java b/net/minecraft/commands/arguments/item/FunctionArgument.java
--- a/net/minecraft/commands/arguments/item/FunctionArgument.java	2021-05-12 08:33:12.000000000 -0700
+++ b/net/minecraft/commands/arguments/item/FunctionArgument.java	2021-05-26 20:45:18.000000000 -0700
@@ -9,6 +9,7 @@
 import com.mojang.datafixers.util.Pair;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import net.minecraft.commands.CommandFunction;
 import net.minecraft.commands.CommandSourceStack;
 import net.minecraft.network.chat.TranslatableComponent;
@@ -25,7 +26,31 @@
     }
 
     public FunctionArgument.Result parse(StringReader param0) throws CommandSyntaxException {
-        // $FF: Couldn't be decompiled
+        if (param0.canRead() && param0.peek() == '#') {
+            param0.skip();
+            final ResourceLocation var0 = ResourceLocation.read(param0);
+            return new FunctionArgument.Result() {
+                public Collection<CommandFunction> create(CommandContext<CommandSourceStack> param0) throws CommandSyntaxException {
+                    Tag<CommandFunction> var0 = FunctionArgument.getFunctionTag(param0, var0);
+                    return var0.getValues();
+                }
+
+                public Pair<ResourceLocation, Either<CommandFunction, Tag<CommandFunction>>> unwrap(CommandContext<CommandSourceStack> param0) throws CommandSyntaxException {
+                    return Pair.of(var0, Either.right(FunctionArgument.getFunctionTag(param0, var0)));
+                }
+            };
+        } else {
+            final ResourceLocation var1 = ResourceLocation.read(param0);
+            return new FunctionArgument.Result() {
+                public Collection<CommandFunction> create(CommandContext<CommandSourceStack> param0) throws CommandSyntaxException {
+                    return Collections.singleton(FunctionArgument.getFunction(param0, var1));
+                }
+
+                public Pair<ResourceLocation, Either<CommandFunction, Tag<CommandFunction>>> unwrap(CommandContext<CommandSourceStack> param0) throws CommandSyntaxException {
+                    return Pair.of(var1, Either.left(FunctionArgument.getFunction(param0, var1)));
+                }
+            };
+        }
     }
 
     static CommandFunction getFunction(CommandContext<CommandSourceStack> param0, ResourceLocation param1) throws CommandSyntaxException {
diff -r -u3 -N a/net/minecraft/data/recipes/SpecialRecipeBuilder.java b/net/minecraft/data/recipes/SpecialRecipeBuilder.java
--- a/net/minecraft/data/recipes/SpecialRecipeBuilder.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/data/recipes/SpecialRecipeBuilder.java	2021-05-26 20:45:18.000000000 -0700
@@ -1,6 +1,10 @@
 package net.minecraft.data.recipes;
 
+import com.google.gson.JsonObject;
 import java.util.function.Consumer;
+import javax.annotation.Nullable;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.RecipeSerializer;
 import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
 
 public class SpecialRecipeBuilder {
@@ -15,6 +19,26 @@
     }
 
     public void save(Consumer<FinishedRecipe> param0, final String param1) {
-        // $FF: Couldn't be decompiled
+        param0.accept(new FinishedRecipe() {
+            public void serializeRecipeData(JsonObject param0) {
+            }
+
+            public RecipeSerializer<?> getType() {
+                return SpecialRecipeBuilder.this.serializer;
+            }
+
+            public ResourceLocation getId() {
+                return new ResourceLocation(param1);
+            }
+
+            @Nullable
+            public JsonObject serializeAdvancement() {
+                return null;
+            }
+
+            public ResourceLocation getAdvancementId() {
+                return new ResourceLocation("");
+            }
+        });
     }
 }
diff -r -u3 -N a/net/minecraft/gametest/framework/GameTestBatchRunner.java b/net/minecraft/gametest/framework/GameTestBatchRunner.java
--- a/net/minecraft/gametest/framework/GameTestBatchRunner.java	2021-05-12 08:33:08.000000000 -0700
+++ b/net/minecraft/gametest/framework/GameTestBatchRunner.java	2021-05-26 20:45:18.000000000 -0700
@@ -47,7 +47,42 @@
     }
 
     void runBatch(final int param0) {
-        // $FF: Couldn't be decompiled
+        if (param0 < this.batches.size()) {
+            Pair<GameTestBatch, Collection<GameTestInfo>> var0 = this.batches.get(param0);
+            final GameTestBatch var1 = var0.getFirst();
+            Collection<GameTestInfo> var2 = var0.getSecond();
+            Map<GameTestInfo, BlockPos> var3 = this.createStructuresForBatch(var2);
+            String var4 = var1.getName();
+            LOGGER.info("Running test batch '{}' ({} tests)...", var4, var2.size());
+            var1.runBeforeBatchFunction(this.level);
+            final MultipleTestTracker var5 = new MultipleTestTracker();
+            Objects.requireNonNull(var5);
+            var2.forEach(var5::addTestToTrack);
+            var5.addListener(new GameTestListener() {
+                private void testCompleted() {
+                    if (var5.isDone()) {
+                        var1.runAfterBatchFunction(GameTestBatchRunner.this.level);
+                        GameTestBatchRunner.this.runBatch(param0 + 1);
+                    }
+
+                }
+
+                public void testStructureLoaded(GameTestInfo param0x) {
+                }
+
+                public void testPassed(GameTestInfo param0x) {
+                    this.testCompleted();
+                }
+
+                public void testFailed(GameTestInfo param0x) {
+                    this.testCompleted();
+                }
+            });
+            var2.forEach((param1) -> {
+                BlockPos var0x = var3.get(param1);
+                GameTestRunner.runTest(param1, var0x, this.testTicker);
+            });
+        }
     }
 
     private Map<GameTestInfo, BlockPos> createStructuresForBatch(Collection<GameTestInfo> param0) {
diff -r -u3 -N a/net/minecraft/gametest/framework/MultipleTestTracker.java b/net/minecraft/gametest/framework/MultipleTestTracker.java
--- a/net/minecraft/gametest/framework/MultipleTestTracker.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/gametest/framework/MultipleTestTracker.java	2021-05-26 20:45:18.000000000 -0700
@@ -37,7 +37,17 @@
     }
 
     public void addFailureListener(final Consumer<GameTestInfo> param0) {
-        // $FF: Couldn't be decompiled
+        this.addListener(new GameTestListener() {
+            public void testStructureLoaded(GameTestInfo param0x) {
+            }
+
+            public void testPassed(GameTestInfo param0x) {
+            }
+
+            public void testFailed(GameTestInfo param0x) {
+                param0.accept(param0);
+            }
+        });
     }
 
     public int getFailedRequiredCount() {
diff -r -u3 -N a/net/minecraft/nbt/NbtOps.java b/net/minecraft/nbt/NbtOps.java
--- a/net/minecraft/nbt/NbtOps.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/nbt/NbtOps.java	2021-05-26 20:45:18.000000000 -0700
@@ -21,6 +21,7 @@
 import java.util.stream.IntStream;
 import java.util.stream.LongStream;
 import java.util.stream.Stream;
+import javax.annotation.Nullable;
 
 public class NbtOps implements DynamicOps<Tag> {
     public static final NbtOps INSTANCE = new NbtOps();
@@ -220,7 +221,30 @@
     }
 
     public DataResult<MapLike<Tag>> getMap(Tag param0) {
-        // $FF: Couldn't be decompiled
+        if (!(param0 instanceof CompoundTag)) {
+            return DataResult.error("Not a map: " + param0);
+        } else {
+            final CompoundTag var0 = (CompoundTag)param0;
+            return DataResult.success(new MapLike<Tag>() {
+                @Nullable
+                public Tag get(Tag param0) {
+                    return var0.get(param0.getAsString());
+                }
+
+                @Nullable
+                public Tag get(String param0) {
+                    return var0.get(param0);
+                }
+
+                public Stream<Pair<Tag, Tag>> entries() {
+                    return var0.getAllKeys().stream().map((param1) -> Pair.of(NbtOps.this.createString(param1), var0.get(param1)));
+                }
+
+                public String toString() {
+                    return "MapLike[" + var0 + "]";
+                }
+            });
+        }
     }
 
     public Tag createMap(Stream<Pair<Tag, Tag>> param0) {
diff -r -u3 -N a/net/minecraft/nbt/TagType.java b/net/minecraft/nbt/TagType.java
--- a/net/minecraft/nbt/TagType.java	2021-05-12 08:33:12.000000000 -0700
+++ b/net/minecraft/nbt/TagType.java	2021-05-26 20:45:18.000000000 -0700
@@ -15,6 +15,18 @@
     String getPrettyName();
 
     static TagType<EndTag> createInvalid(final int param0) {
-        // $FF: Couldn't be decompiled
+        return new TagType<EndTag>() {
+            public EndTag load(DataInput param0x, int param1, NbtAccounter param2) {
+                throw new IllegalArgumentException("Invalid tag id: " + param0);
+            }
+
+            public String getName() {
+                return "INVALID[" + param0 + "]";
+            }
+
+            public String getPrettyName() {
+                return "UNKNOWN_" + param0;
+            }
+        };
     }
 }
diff -r -u3 -N a/net/minecraft/network/chat/FormattedText.java b/net/minecraft/network/chat/FormattedText.java
--- a/net/minecraft/network/chat/FormattedText.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/network/chat/FormattedText.java	2021-05-26 20:45:18.000000000 -0700
@@ -34,7 +34,15 @@
     }
 
     static FormattedText of(final String param0, final Style param1) {
-        // $FF: Couldn't be decompiled
+        return new FormattedText() {
+            public <T> Optional<T> visit(FormattedText.ContentConsumer<T> param0x) {
+                return param0.accept(param0);
+            }
+
+            public <T> Optional<T> visit(FormattedText.StyledContentConsumer<T> param0x, Style param1x) {
+                return param0.accept(param1.applyTo(param1), param0);
+            }
+        };
     }
 
     static FormattedText composite(FormattedText... param0) {
diff -r -u3 -N a/net/minecraft/server/Bootstrap.java b/net/minecraft/server/Bootstrap.java
--- a/net/minecraft/server/Bootstrap.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/server/Bootstrap.java	2021-05-26 20:45:18.000000000 -0700
@@ -68,7 +68,15 @@
     }
 
     private static void checkGameruleTranslations(final Set<String> param0) {
-        // $FF: Couldn't be decompiled
+        final Language var0 = Language.getInstance();
+        GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() {
+            public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> param0x, GameRules.Type<T> param1) {
+                if (!var0.has(param0.getDescriptionId())) {
+                    param0.add(param0.getId());
+                }
+
+            }
+        });
     }
 
     public static Set<String> getMissingTranslations() {
diff -r -u3 -N a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
--- a/net/minecraft/server/level/ChunkMap.java	2021-05-12 08:33:22.000000000 -0700
+++ b/net/minecraft/server/level/ChunkMap.java	2021-05-26 20:45:18.000000000 -0700
@@ -223,7 +223,53 @@
     }
 
     private CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> getChunkRangeFuture(ChunkPos param0, int param1, IntFunction<ChunkStatus> param2) {
-        // $FF: Couldn't be decompiled
+        List<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> var0 = Lists.newArrayList();
+        int var1 = param0.x;
+        int var2 = param0.z;
+
+        for(int var3 = -param1; var3 <= param1; ++var3) {
+            for(int var4 = -param1; var4 <= param1; ++var4) {
+                int var5 = Math.max(Math.abs(var4), Math.abs(var3));
+                final ChunkPos var6 = new ChunkPos(var1 + var4, var2 + var3);
+                long var7 = var6.toLong();
+                ChunkHolder var8 = this.getUpdatingChunkIfPresent(var7);
+                if (var8 == null) {
+                    return CompletableFuture.completedFuture(Either.right(new ChunkHolder.ChunkLoadingFailure() {
+                        public String toString() {
+                            return "Unloaded " + var6;
+                        }
+                    }));
+                }
+
+                ChunkStatus var9 = param2.apply(var5);
+                CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> var10 = var8.getOrScheduleFuture(var9, this);
+                var0.add(var10);
+            }
+        }
+
+        CompletableFuture<List<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> var11 = Util.sequence(var0);
+        return var11.thenApply((param3) -> {
+            List<ChunkAccess> var0x = Lists.newArrayList();
+            int var1x = 0;
+
+            for(final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> var2x : param3) {
+                Optional<ChunkAccess> var3x = var2x.left();
+                if (!var3x.isPresent()) {
+                    final int var4x = var1x;
+                    return Either.right(new ChunkHolder.ChunkLoadingFailure() {
+                        public String toString() {
+                            int var10002 = var1 + var4x % (param1 * 2 + 1);
+                            return "Unloaded " + new ChunkPos(var10002, var2 + var4x / (param1 * 2 + 1)) + " " + var2x.right().get();
+                        }
+                    });
+                }
+
+                var0x.add(var3x.get());
+                ++var1x;
+            }
+
+            return Either.left(var0x);
+        });
     }
 
     public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareEntityTickingChunk(ChunkPos param0) {
diff -r -u3 -N a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
--- a/net/minecraft/server/MinecraftServer.java	2021-05-12 08:32:54.000000000 -0700
+++ b/net/minecraft/server/MinecraftServer.java	2021-05-26 20:45:18.000000000 -0700
@@ -1519,7 +1519,36 @@
     }
 
     private void dumpGameRules(Path param0) throws IOException {
-        // $FF: Couldn't be decompiled
+        Writer var0 = Files.newBufferedWriter(param0);
+
+        try {
+            final List<String> var1 = Lists.newArrayList();
+            final GameRules var2 = this.getGameRules();
+            GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() {
+                public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> param0, GameRules.Type<T> param1) {
+                    var1.add(String.format("%s=%s\n", param0.getId(), var2.<T>getRule(param0)));
+                }
+            });
+
+            for(String var3 : var1) {
+                var0.write(var3);
+            }
+        } catch (Throwable var8) {
+            if (var0 != null) {
+                try {
+                    var0.close();
+                } catch (Throwable var7) {
+                    var8.addSuppressed(var7);
+                }
+            }
+
+            throw var8;
+        }
+
+        if (var0 != null) {
+            var0.close();
+        }
+
     }
 
     private void dumpClasspath(Path param0) throws IOException {
diff -r -u3 -N a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java	2021-05-12 08:32:52.000000000 -0700
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java	2021-05-26 20:45:18.000000000 -0700
@@ -109,11 +109,15 @@
 import net.minecraft.world.InteractionResult;
 import net.minecraft.world.effect.MobEffects;
 import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.ExperienceOrb;
 import net.minecraft.world.entity.MoverType;
 import net.minecraft.world.entity.PlayerRideableJumping;
 import net.minecraft.world.entity.animal.horse.AbstractHorse;
+import net.minecraft.world.entity.item.ItemEntity;
 import net.minecraft.world.entity.player.ChatVisiblity;
 import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.entity.projectile.AbstractArrow;
 import net.minecraft.world.entity.vehicle.Boat;
 import net.minecraft.world.inventory.AbstractContainerMenu;
 import net.minecraft.world.inventory.AnvilMenu;
@@ -1185,7 +1189,47 @@
     }
 
     public void handleInteract(ServerboundInteractPacket param0) {
-        // $FF: Couldn't be decompiled
+        PacketUtils.ensureRunningOnSameThread(param0, this, this.player.getLevel());
+        ServerLevel var0 = this.player.getLevel();
+        final Entity var1 = param0.getTarget(var0);
+        this.player.resetLastActionTime();
+        this.player.setShiftKeyDown(param0.isUsingSecondaryAction());
+        if (var1 != null) {
+            double var2 = 36.0D;
+            if (this.player.distanceToSqr(var1) < 36.0D) {
+                param0.dispatch(new ServerboundInteractPacket.Handler() {
+                    private void performInteraction(InteractionHand param0, ServerGamePacketListenerImpl.EntityInteraction param1) {
+                        ItemStack var0 = ServerGamePacketListenerImpl.this.player.getItemInHand(param0).copy();
+                        InteractionResult var1 = param1.run(ServerGamePacketListenerImpl.this.player, var1, param0);
+                        if (var1.consumesAction()) {
+                            CriteriaTriggers.PLAYER_INTERACTED_WITH_ENTITY.trigger(ServerGamePacketListenerImpl.this.player, var0, var1);
+                            if (var1.shouldSwing()) {
+                                ServerGamePacketListenerImpl.this.player.swing(param0, true);
+                            }
+                        }
+
+                    }
+
+                    public void onInteraction(InteractionHand param0) {
+                        this.performInteraction(param0, Player::interactOn);
+                    }
+
+                    public void onInteraction(InteractionHand param0, Vec3 param1) {
+                        this.performInteraction(param0, (param1x, param2, param3) -> param2.interactAt(param1x, param1, param3));
+                    }
+
+                    public void onAttack() {
+                        if (!(var1 instanceof ItemEntity) && !(var1 instanceof ExperienceOrb) && !(var1 instanceof AbstractArrow) && var1 != ServerGamePacketListenerImpl.this.player) {
+                            ServerGamePacketListenerImpl.this.player.attack(var1);
+                        } else {
+                            ServerGamePacketListenerImpl.this.disconnect(new TranslatableComponent("multiplayer.disconnect.invalid_entity_attacked"));
+                            ServerGamePacketListenerImpl.LOGGER.warn("Player {} tried to attack an invalid entity", (Object)ServerGamePacketListenerImpl.this.player.getName().getString());
+                        }
+                    }
+                });
+            }
+        }
+
     }
 
     public void handleClientCommand(ServerboundClientCommandPacket param0) {
diff -r -u3 -N a/net/minecraft/server/packs/resources/SimpleReloadInstance.java b/net/minecraft/server/packs/resources/SimpleReloadInstance.java
--- a/net/minecraft/server/packs/resources/SimpleReloadInstance.java	2021-05-12 08:33:14.000000000 -0700
+++ b/net/minecraft/server/packs/resources/SimpleReloadInstance.java	2021-05-26 20:45:18.000000000 -0700
@@ -8,6 +8,7 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
+import net.minecraft.Util;
 import net.minecraft.util.Unit;
 import net.minecraft.util.profiling.InactiveProfiler;
 
@@ -30,7 +31,47 @@
     }
 
     protected SimpleReloadInstance(Executor param0, final Executor param1, ResourceManager param2, List<PreparableReloadListener> param3, SimpleReloadInstance.StateFactory<S> param4, CompletableFuture<Unit> param5) {
-        // $FF: Couldn't be decompiled
+        this.resourceManager = param2;
+        this.listenerCount = param3.size();
+        this.startedTaskCounter.incrementAndGet();
+        AtomicInteger var10001 = this.doneTaskCounter;
+        Objects.requireNonNull(this.doneTaskCounter);
+        param5.thenRun(var10001::incrementAndGet);
+        List<CompletableFuture<S>> var0 = Lists.newArrayList();
+        CompletableFuture<?> var1 = param5;
+        this.preparingListeners = Sets.newHashSet(param3);
+
+        for(final PreparableReloadListener var2 : param3) {
+            final CompletableFuture<?> var3 = var1;
+            CompletableFuture<S> var4 = param4.create(new PreparableReloadListener.PreparationBarrier() {
+                public <T> CompletableFuture<T> wait(T param0) {
+                    param1.execute(() -> {
+                        SimpleReloadInstance.this.preparingListeners.remove(var2);
+                        if (SimpleReloadInstance.this.preparingListeners.isEmpty()) {
+                            SimpleReloadInstance.this.allPreparations.complete(Unit.INSTANCE);
+                        }
+
+                    });
+                    return SimpleReloadInstance.this.allPreparations.thenCombine(var3, (param1x, param2) -> param0);
+                }
+            }, param2, var2, (param1x) -> {
+                this.startedTaskCounter.incrementAndGet();
+                param0.execute(() -> {
+                    param1x.run();
+                    this.doneTaskCounter.incrementAndGet();
+                });
+            }, (param1x) -> {
+                ++this.startedReloads;
+                param1.execute(() -> {
+                    param1x.run();
+                    ++this.finishedReloads;
+                });
+            });
+            var0.add(var4);
+            var1 = var4;
+        }
+
+        this.allDone = Util.sequenceFailFast(var0);
     }
 
     public CompletableFuture<Unit> done() {
diff -r -u3 -N a/net/minecraft/server/packs/VanillaPackResources.java b/net/minecraft/server/packs/VanillaPackResources.java
--- a/net/minecraft/server/packs/VanillaPackResources.java	2021-05-12 08:33:04.000000000 -0700
+++ b/net/minecraft/server/packs/VanillaPackResources.java	2021-05-26 20:45:18.000000000 -0700
@@ -7,6 +7,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UncheckedIOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -282,6 +283,43 @@
     }
 
     public Resource getResource(final ResourceLocation param0) throws IOException {
-        // $FF: Couldn't be decompiled
+        return new Resource() {
+            @Nullable
+            InputStream inputStream;
+
+            public void close() throws IOException {
+                if (this.inputStream != null) {
+                    this.inputStream.close();
+                }
+
+            }
+
+            public ResourceLocation getLocation() {
+                return param0;
+            }
+
+            public InputStream getInputStream() {
+                try {
+                    this.inputStream = VanillaPackResources.this.getResource(PackType.CLIENT_RESOURCES, param0);
+                } catch (IOException var2) {
+                    throw new UncheckedIOException("Could not get client resource from vanilla pack", var2);
+                }
+
+                return this.inputStream;
+            }
+
+            public boolean hasMetadata() {
+                return false;
+            }
+
+            @Nullable
+            public <T> T getMetadata(MetadataSectionSerializer<T> param0x) {
+                return (T)null;
+            }
+
+            public String getSourceName() {
+                return param0.toString();
+            }
+        };
     }
 }
diff -r -u3 -N a/net/minecraft/server/players/OldUsersConverter.java b/net/minecraft/server/players/OldUsersConverter.java
--- a/net/minecraft/server/players/OldUsersConverter.java	2021-05-12 08:32:54.000000000 -0700
+++ b/net/minecraft/server/players/OldUsersConverter.java	2021-05-26 20:45:18.000000000 -0700
@@ -6,6 +6,7 @@
 import com.mojang.authlib.Agent;
 import com.mojang.authlib.GameProfile;
 import com.mojang.authlib.ProfileLookupCallback;
+import com.mojang.authlib.yggdrasil.ProfileNotFoundException;
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -61,7 +62,56 @@
     }
 
     public static boolean convertUserBanlist(final MinecraftServer param0) {
-        // $FF: Couldn't be decompiled
+        final UserBanList var0 = new UserBanList(PlayerList.USERBANLIST_FILE);
+        if (OLD_USERBANLIST.exists() && OLD_USERBANLIST.isFile()) {
+            if (var0.getFile().exists()) {
+                try {
+                    var0.load();
+                } catch (IOException var6) {
+                    LOGGER.warn("Could not load existing file {}", var0.getFile().getName(), var6);
+                }
+            }
+
+            try {
+                final Map<String, String[]> var2 = Maps.newHashMap();
+                readOldListFormat(OLD_USERBANLIST, var2);
+                ProfileLookupCallback var3 = new ProfileLookupCallback() {
+                    public void onProfileLookupSucceeded(GameProfile param0x) {
+                        param0.getProfileCache().add(param0);
+                        String[] var0 = var2.get(param0.getName().toLowerCase(Locale.ROOT));
+                        if (var0 == null) {
+                            OldUsersConverter.LOGGER.warn("Could not convert user banlist entry for {}", (Object)param0.getName());
+                            throw new OldUsersConverter.ConversionError("Profile not in the conversionlist");
+                        } else {
+                            Date var1 = var0.length > 1 ? OldUsersConverter.parseDate(var0[1], (Date)null) : null;
+                            String var2 = var0.length > 2 ? var0[2] : null;
+                            Date var3 = var0.length > 3 ? OldUsersConverter.parseDate(var0[3], (Date)null) : null;
+                            String var4 = var0.length > 4 ? var0[4] : null;
+                            var0.add(new UserBanListEntry(param0, var1, var2, var3, var4));
+                        }
+                    }
+
+                    public void onProfileLookupFailed(GameProfile param0x, Exception param1) {
+                        OldUsersConverter.LOGGER.warn("Could not lookup user banlist entry for {}", param0.getName(), param1);
+                        if (!(param1 instanceof ProfileNotFoundException)) {
+                            throw new OldUsersConverter.ConversionError("Could not request user " + param0.getName() + " from backend systems", param1);
+                        }
+                    }
+                };
+                lookupPlayers(param0, var2.keySet(), var3);
+                var0.save();
+                renameOldFile(OLD_USERBANLIST);
+                return true;
+            } catch (IOException var4) {
+                LOGGER.warn("Could not read old user banlist to convert it!", (Throwable)var4);
+                return false;
+            } catch (OldUsersConverter.ConversionError var51) {
+                LOGGER.error("Conversion failed, please try again later", (Throwable)var51);
+                return false;
+            }
+        } else {
+            return true;
+        }
     }
 
     public static boolean convertIpBanlist(MinecraftServer param0) {
@@ -101,20 +151,197 @@
     }
 
     public static boolean convertOpsList(final MinecraftServer param0) {
-        // $FF: Couldn't be decompiled
+        final ServerOpList var0 = new ServerOpList(PlayerList.OPLIST_FILE);
+        if (OLD_OPLIST.exists() && OLD_OPLIST.isFile()) {
+            if (var0.getFile().exists()) {
+                try {
+                    var0.load();
+                } catch (IOException var6) {
+                    LOGGER.warn("Could not load existing file {}", var0.getFile().getName(), var6);
+                }
+            }
+
+            try {
+                List<String> var2 = Files.readLines(OLD_OPLIST, StandardCharsets.UTF_8);
+                ProfileLookupCallback var3 = new ProfileLookupCallback() {
+                    public void onProfileLookupSucceeded(GameProfile param0x) {
+                        param0.getProfileCache().add(param0);
+                        var0.add(new ServerOpListEntry(param0, param0.getOperatorUserPermissionLevel(), false));
+                    }
+
+                    public void onProfileLookupFailed(GameProfile param0x, Exception param1) {
+                        OldUsersConverter.LOGGER.warn("Could not lookup oplist entry for {}", param0.getName(), param1);
+                        if (!(param1 instanceof ProfileNotFoundException)) {
+                            throw new OldUsersConverter.ConversionError("Could not request user " + param0.getName() + " from backend systems", param1);
+                        }
+                    }
+                };
+                lookupPlayers(param0, var2, var3);
+                var0.save();
+                renameOldFile(OLD_OPLIST);
+                return true;
+            } catch (IOException var4) {
+                LOGGER.warn("Could not read old oplist to convert it!", (Throwable)var4);
+                return false;
+            } catch (OldUsersConverter.ConversionError var51) {
+                LOGGER.error("Conversion failed, please try again later", (Throwable)var51);
+                return false;
+            }
+        } else {
+            return true;
+        }
     }
 
     public static boolean convertWhiteList(final MinecraftServer param0) {
-        // $FF: Couldn't be decompiled
+        final UserWhiteList var0 = new UserWhiteList(PlayerList.WHITELIST_FILE);
+        if (OLD_WHITELIST.exists() && OLD_WHITELIST.isFile()) {
+            if (var0.getFile().exists()) {
+                try {
+                    var0.load();
+                } catch (IOException var6) {
+                    LOGGER.warn("Could not load existing file {}", var0.getFile().getName(), var6);
+                }
+            }
+
+            try {
+                List<String> var2 = Files.readLines(OLD_WHITELIST, StandardCharsets.UTF_8);
+                ProfileLookupCallback var3 = new ProfileLookupCallback() {
+                    public void onProfileLookupSucceeded(GameProfile param0x) {
+                        param0.getProfileCache().add(param0);
+                        var0.add(new UserWhiteListEntry(param0));
+                    }
+
+                    public void onProfileLookupFailed(GameProfile param0x, Exception param1) {
+                        OldUsersConverter.LOGGER.warn("Could not lookup user whitelist entry for {}", param0.getName(), param1);
+                        if (!(param1 instanceof ProfileNotFoundException)) {
+                            throw new OldUsersConverter.ConversionError("Could not request user " + param0.getName() + " from backend systems", param1);
+                        }
+                    }
+                };
+                lookupPlayers(param0, var2, var3);
+                var0.save();
+                renameOldFile(OLD_WHITELIST);
+                return true;
+            } catch (IOException var4) {
+                LOGGER.warn("Could not read old whitelist to convert it!", (Throwable)var4);
+                return false;
+            } catch (OldUsersConverter.ConversionError var51) {
+                LOGGER.error("Conversion failed, please try again later", (Throwable)var51);
+                return false;
+            }
+        } else {
+            return true;
+        }
     }
 
     @Nullable
     public static UUID convertMobOwnerIfNecessary(final MinecraftServer param0, String param1) {
-        // $FF: Couldn't be decompiled
+        if (!StringUtil.isNullOrEmpty(param1) && param1.length() <= 16) {
+            GameProfile var1 = param0.getProfileCache().get(param1);
+            if (var1 != null && var1.getId() != null) {
+                return var1.getId();
+            } else if (!param0.isSingleplayer() && param0.usesAuthentication()) {
+                final List<GameProfile> var2 = Lists.newArrayList();
+                ProfileLookupCallback var3 = new ProfileLookupCallback() {
+                    public void onProfileLookupSucceeded(GameProfile param0x) {
+                        param0.getProfileCache().add(param0);
+                        var2.add(param0);
+                    }
+
+                    public void onProfileLookupFailed(GameProfile param0x, Exception param1) {
+                        OldUsersConverter.LOGGER.warn("Could not lookup user whitelist entry for {}", param0.getName(), param1);
+                    }
+                };
+                lookupPlayers(param0, Lists.newArrayList(param1), var3);
+                return !var2.isEmpty() && var2.get(0).getId() != null ? var2.get(0).getId() : null;
+            } else {
+                return Player.createPlayerUUID(new GameProfile((UUID)null, param1));
+            }
+        } else {
+            try {
+                return UUID.fromString(param1);
+            } catch (IllegalArgumentException var5) {
+                return null;
+            }
+        }
     }
 
     public static boolean convertPlayers(final DedicatedServer param0) {
-        // $FF: Couldn't be decompiled
+        final File var0 = getWorldPlayersDirectory(param0);
+        final File var1 = new File(var0.getParentFile(), "playerdata");
+        final File var2 = new File(var0.getParentFile(), "unknownplayers");
+        if (var0.exists() && var0.isDirectory()) {
+            File[] var3 = var0.listFiles();
+            List<String> var4 = Lists.newArrayList();
+
+            for(File var5 : var3) {
+                String var6 = var5.getName();
+                if (var6.toLowerCase(Locale.ROOT).endsWith(".dat")) {
+                    String var7 = var6.substring(0, var6.length() - ".dat".length());
+                    if (!var7.isEmpty()) {
+                        var4.add(var7);
+                    }
+                }
+            }
+
+            try {
+                final String[] var8 = var4.toArray(new String[var4.size()]);
+                ProfileLookupCallback var9 = new ProfileLookupCallback() {
+                    public void onProfileLookupSucceeded(GameProfile param0x) {
+                        param0.getProfileCache().add(param0);
+                        UUID var0 = param0.getId();
+                        if (var0 == null) {
+                            throw new OldUsersConverter.ConversionError("Missing UUID for user profile " + param0.getName());
+                        } else {
+                            this.movePlayerFile(var1, this.getFileNameForProfile(param0), var0.toString());
+                        }
+                    }
+
+                    public void onProfileLookupFailed(GameProfile param0x, Exception param1) {
+                        OldUsersConverter.LOGGER.warn("Could not lookup user uuid for {}", param0.getName(), param1);
+                        if (param1 instanceof ProfileNotFoundException) {
+                            String var0 = this.getFileNameForProfile(param0);
+                            this.movePlayerFile(var2, var0, var0);
+                        } else {
+                            throw new OldUsersConverter.ConversionError("Could not request user " + param0.getName() + " from backend systems", param1);
+                        }
+                    }
+
+                    private void movePlayerFile(File param0x, String param1, String param2) {
+                        File var0 = new File(var0, param1 + ".dat");
+                        File var1 = new File(param0, param2 + ".dat");
+                        OldUsersConverter.ensureDirectoryExists(param0);
+                        if (!var0.renameTo(var1)) {
+                            throw new OldUsersConverter.ConversionError("Could not convert file for " + param1);
+                        }
+                    }
+
+                    private String getFileNameForProfile(GameProfile param0x) {
+                        String var0 = null;
+
+                        for(String var1 : var8) {
+                            if (var1 != null && var1.equalsIgnoreCase(param0.getName())) {
+                                var0 = var1;
+                                break;
+                            }
+                        }
+
+                        if (var0 == null) {
+                            throw new OldUsersConverter.ConversionError("Could not find the filename for " + param0.getName() + " anymore");
+                        } else {
+                            return var0;
+                        }
+                    }
+                };
+                lookupPlayers(param0, Lists.newArrayList(var8), var9);
+                return true;
+            } catch (OldUsersConverter.ConversionError var12) {
+                LOGGER.error("Conversion failed, please try again later", (Throwable)var12);
+                return false;
+            }
+        } else {
+            return true;
+        }
     }
 
     static void ensureDirectoryExists(File param0) {
diff -r -u3 -N a/net/minecraft/tags/TagCollection.java b/net/minecraft/tags/TagCollection.java
--- a/net/minecraft/tags/TagCollection.java	2021-05-12 08:32:56.000000000 -0700
+++ b/net/minecraft/tags/TagCollection.java	2021-05-26 20:45:18.000000000 -0700
@@ -90,7 +90,23 @@
     }
 
     static <T> TagCollection<T> of(Map<ResourceLocation, Tag<T>> param0) {
-        // $FF: Couldn't be decompiled
+        final BiMap<ResourceLocation, Tag<T>> var0 = ImmutableBiMap.copyOf(param0);
+        return new TagCollection<T>() {
+            private final Tag<T> empty = SetTag.empty();
+
+            public Tag<T> getTagOrEmpty(ResourceLocation param0) {
+                return var0.getOrDefault(param0, this.empty);
+            }
+
+            @Nullable
+            public ResourceLocation getId(Tag<T> param0) {
+                return param0 instanceof Tag.Named ? ((Tag.Named)param0).getName() : var0.inverse().get(param0);
+            }
+
+            public Map<ResourceLocation, Tag<T>> getAllTags() {
+                return var0;
+            }
+        };
     }
 
     public static class NetworkPayload {
diff -r -u3 -N a/net/minecraft/tags/TagContainer.java b/net/minecraft/tags/TagContainer.java
--- a/net/minecraft/tags/TagContainer.java	2021-05-12 08:33:24.000000000 -0700
+++ b/net/minecraft/tags/TagContainer.java	2021-05-26 20:45:18.000000000 -0700
@@ -75,7 +75,19 @@
     }
 
     public Map<ResourceKey<? extends Registry<?>>, TagCollection.NetworkPayload> serializeToNetwork(final RegistryAccess param0) {
-        // $FF: Couldn't be decompiled
+        final Map<ResourceKey<? extends Registry<?>>, TagCollection.NetworkPayload> var0 = Maps.newHashMap();
+        this.getAll(new TagContainer.CollectionConsumer() {
+            public <T> void accept(ResourceKey<? extends Registry<T>> param0x, TagCollection<T> param1) {
+                Optional<? extends Registry<T>> var0 = param0.registry(param0);
+                if (var0.isPresent()) {
+                    var0.put(param0, param1.serializeToNetwork(var0.get()));
+                } else {
+                    TagContainer.LOGGER.error("Unknown registry {}", (Object)param0);
+                }
+
+            }
+        });
+        return var0;
     }
 
     public static TagContainer deserializeFromNetwork(RegistryAccess param0, Map<ResourceKey<? extends Registry<?>>, TagCollection.NetworkPayload> param1) {
diff -r -u3 -N a/net/minecraft/util/profiling/ProfilerFiller.java b/net/minecraft/util/profiling/ProfilerFiller.java
--- a/net/minecraft/util/profiling/ProfilerFiller.java	2021-05-12 08:33:06.000000000 -0700
+++ b/net/minecraft/util/profiling/ProfilerFiller.java	2021-05-26 20:45:18.000000000 -0700
@@ -24,6 +24,55 @@
     void incrementCounter(Supplier<String> var1);
 
     static ProfilerFiller tee(final ProfilerFiller param0, final ProfilerFiller param1) {
-        // $FF: Couldn't be decompiled
+        if (param0 == InactiveProfiler.INSTANCE) {
+            return param1;
+        } else {
+            return param1 == InactiveProfiler.INSTANCE ? param0 : new ProfilerFiller() {
+                public void startTick() {
+                    param0.startTick();
+                    param1.startTick();
+                }
+
+                public void endTick() {
+                    param0.endTick();
+                    param1.endTick();
+                }
+
+                public void push(String param0x) {
+                    param0.push(param0);
+                    param1.push(param0);
+                }
+
+                public void push(Supplier<String> param0x) {
+                    param0.push(param0);
+                    param1.push(param0);
+                }
+
+                public void pop() {
+                    param0.pop();
+                    param1.pop();
+                }
+
+                public void popPush(String param0x) {
+                    param0.popPush(param0);
+                    param1.popPush(param0);
+                }
+
+                public void popPush(Supplier<String> param0x) {
+                    param0.popPush(param0);
+                    param1.popPush(param0);
+                }
+
+                public void incrementCounter(String param0x) {
+                    param0.incrementCounter(param0);
+                    param1.incrementCounter(param0);
+                }
+
+                public void incrementCounter(Supplier<String> param0x) {
+                    param0.incrementCounter(param0);
+                    param1.incrementCounter(param0);
+                }
+            };
+        }
     }
 }
diff -r -u3 -N a/net/minecraft/util/StringRepresentable.java b/net/minecraft/util/StringRepresentable.java
--- a/net/minecraft/util/StringRepresentable.java	2021-05-12 08:33:28.000000000 -0700
+++ b/net/minecraft/util/StringRepresentable.java	2021-05-26 20:45:18.000000000 -0700
@@ -1,10 +1,13 @@
 package net.minecraft.util;
 
+import com.mojang.datafixers.util.Pair;
 import com.mojang.serialization.Codec;
+import com.mojang.serialization.DataResult;
 import com.mojang.serialization.DynamicOps;
 import com.mojang.serialization.Keyable;
 import java.util.Arrays;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.function.Function;
 import java.util.function.IntFunction;
 import java.util.function.Supplier;
@@ -21,7 +24,19 @@
     }
 
     static <E extends StringRepresentable> Codec<E> fromStringResolver(final ToIntFunction<E> param0, final IntFunction<E> param1, final Function<? super String, ? extends E> param2) {
-        // $FF: Couldn't be decompiled
+        return new Codec<E>() {
+            public <T> DataResult<T> encode(E param0x, DynamicOps<T> param1x, T param2x) {
+                return param1.compressMaps() ? param1.mergeToPrimitive(param2, param1.createInt(param0.applyAsInt(param0))) : param1.mergeToPrimitive(param2, param1.createString(param0.getSerializedName()));
+            }
+
+            public <T> DataResult<Pair<E, T>> decode(DynamicOps<T> param0x, T param1x) {
+                return param0.compressMaps() ? param0.getNumberValue(param1).flatMap((param1xx) -> Optional.ofNullable(param1.apply(param1xx.intValue())).map(DataResult::success).orElseGet(() -> DataResult.error("Unknown element id: " + param1xx))).map((param1xx) -> Pair.of(param1xx, param0.empty())) : param0.getStringValue(param1).flatMap((param1xx) -> Optional.ofNullable(param2.apply(param1xx)).map(DataResult::success).orElseGet(() -> DataResult.error("Unknown element name: " + param1xx))).map((param1xx) -> Pair.of(param1xx, param0.empty()));
+            }
+
+            public String toString() {
+                return "StringRepresentable[" + param0 + "]";
+            }
+        };
     }
 
     static Keyable keys(final StringRepresentable[] param0) {
diff -r -u3 -N a/net/minecraft/util/thread/ProcessorHandle.java b/net/minecraft/util/thread/ProcessorHandle.java
--- a/net/minecraft/util/thread/ProcessorHandle.java	2021-05-12 08:33:20.000000000 -0700
+++ b/net/minecraft/util/thread/ProcessorHandle.java	2021-05-26 20:45:18.000000000 -0700
@@ -35,6 +35,18 @@
     }
 
     static <Msg> ProcessorHandle<Msg> of(final String param0, final Consumer<Msg> param1) {
-        // $FF: Couldn't be decompiled
+        return new ProcessorHandle<Msg>() {
+            public String name() {
+                return param0;
+            }
+
+            public void tell(Msg param0x) {
+                param1.accept(param0);
+            }
+
+            public String toString() {
+                return param0;
+            }
+        };
     }
 }
diff -r -u3 -N a/net/minecraft/Util.java b/net/minecraft/Util.java
--- a/net/minecraft/Util.java	2021-05-12 08:33:10.000000000 -0700
+++ b/net/minecraft/Util.java	2021-05-26 20:45:18.000000000 -0700
@@ -2,10 +2,12 @@
 
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.mojang.datafixers.DataFixUtils;
 import com.mojang.datafixers.DSL.TypeReference;
 import com.mojang.datafixers.types.Type;
+import com.mojang.datafixers.util.Pair;
 import com.mojang.serialization.DataResult;
 import it.unimi.dsi.fastutil.Hash.Strategy;
 import java.io.File;
@@ -398,7 +400,21 @@
     }
 
     private static BooleanSupplier createRenamer(final Path param0, final Path param1) {
-        // $FF: Couldn't be decompiled
+        return new BooleanSupplier() {
+            public boolean getAsBoolean() {
+                try {
+                    Files.move(param0, param1);
+                    return true;
+                } catch (IOException var2) {
+                    Util.LOGGER.error("Failed to rename", (Throwable)var2);
+                    return false;
+                }
+            }
+
+            public String toString() {
+                return "rename " + param0 + " to " + param1;
+            }
+        };
     }
 
     private static BooleanSupplier createDeleter(final Path param0) {
@@ -555,11 +571,31 @@
     }
 
     public static <T, R> Function<T, R> memoize(final Function<T, R> param0) {
-        // $FF: Couldn't be decompiled
+        return new Function<T, R>() {
+            private final Map<T, R> cache = Maps.newHashMap();
+
+            public R apply(T param0x) {
+                return this.cache.computeIfAbsent(param0, param0);
+            }
+
+            public String toString() {
+                return "memoize/1[function=" + param0 + ", size=" + this.cache.size() + "]";
+            }
+        };
     }
 
     public static <T, U, R> BiFunction<T, U, R> memoize(final BiFunction<T, U, R> param0) {
-        // $FF: Couldn't be decompiled
+        return new BiFunction<T, U, R>() {
+            private final Map<Pair<T, U>, R> cache = Maps.newHashMap();
+
+            public R apply(T param0x, U param1) {
+                return this.cache.computeIfAbsent(Pair.of(param0, param1), (param1x) -> param0.apply(param1x.getFirst(), param1x.getSecond()));
+            }
+
+            public String toString() {
+                return "memoize/2[function=" + param0 + ", size=" + this.cache.size() + "]";
+            }
+        };
     }
 
     static enum IdentityStrategy implements Strategy<Object> {
diff -r -u3 -N a/net/minecraft/world/entity/ai/behavior/ShufflingList.java b/net/minecraft/world/entity/ai/behavior/ShufflingList.java
--- a/net/minecraft/world/entity/ai/behavior/ShufflingList.java	2021-05-12 08:33:20.000000000 -0700
+++ b/net/minecraft/world/entity/ai/behavior/ShufflingList.java	2021-05-26 20:45:18.000000000 -0700
@@ -1,11 +1,15 @@
 package net.minecraft.world.entity.ai.behavior;
 
 import com.google.common.collect.Lists;
+import com.mojang.datafixers.util.Pair;
 import com.mojang.serialization.Codec;
+import com.mojang.serialization.DataResult;
 import com.mojang.serialization.Dynamic;
 import com.mojang.serialization.DynamicOps;
+import com.mojang.serialization.OptionalDynamic;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Random;
 import java.util.stream.Stream;
 
@@ -75,7 +79,19 @@
         }
 
         public static <E> Codec<ShufflingList.WeightedEntry<E>> codec(final Codec<E> param0) {
-            // $FF: Couldn't be decompiled
+            return new Codec<ShufflingList.WeightedEntry<E>>() {
+                public <T> DataResult<Pair<ShufflingList.WeightedEntry<E>, T>> decode(DynamicOps<T> param0x, T param1) {
+                    Dynamic<T> var0 = new Dynamic<>(param0, param1);
+                    OptionalDynamic var10000 = var0.get("data");
+                    Codec var10001 = param0;
+                    Objects.requireNonNull(param0);
+                    return var10000.flatMap(var10001::parse).map((param1x) -> new ShufflingList.WeightedEntry<>(param1x, var0.get("weight").asInt(1))).map((param1x) -> Pair.of(param1x, param0.empty()));
+                }
+
+                public <T> DataResult<T> encode(ShufflingList.WeightedEntry<E> param0x, DynamicOps<T> param1, T param2) {
+                    return param1.mapBuilder().add("weight", param1.createInt(param0.weight)).add("data", param0.encodeStart(param1, param0.data)).build(param2);
+                }
+            };
         }
     }
 }
diff -r -u3 -N a/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/net/minecraft/world/entity/animal/horse/AbstractHorse.java
--- a/net/minecraft/world/entity/animal/horse/AbstractHorse.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/world/entity/animal/horse/AbstractHorse.java	2021-05-26 20:45:18.000000000 -0700
@@ -926,7 +926,21 @@
     }
 
     private SlotAccess createEquipmentSlotAccess(final int param0, final Predicate<ItemStack> param1) {
-        // $FF: Couldn't be decompiled
+        return new SlotAccess() {
+            public ItemStack get() {
+                return AbstractHorse.this.inventory.getItem(param0);
+            }
+
+            public boolean set(ItemStack param0x) {
+                if (!param1.test(param0)) {
+                    return false;
+                } else {
+                    AbstractHorse.this.inventory.setItem(param0, param0);
+                    AbstractHorse.this.updateContainerEquipment();
+                    return true;
+                }
+            }
+        };
     }
 
     public SlotAccess getSlot(int param0) {
diff -r -u3 -N a/net/minecraft/world/entity/animal/Wolf.java b/net/minecraft/world/entity/animal/Wolf.java
--- a/net/minecraft/world/entity/animal/Wolf.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/world/entity/animal/Wolf.java	2021-05-26 20:45:18.000000000 -0700
@@ -90,7 +90,7 @@
     protected void registerGoals() {
         this.goalSelector.addGoal(1, new FloatGoal(this));
         this.goalSelector.addGoal(2, new SitWhenOrderedToGoal(this));
-        this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal(this, Llama.class, 24.0F, 1.5D, 1.5D));
+        this.goalSelector.addGoal(3, new Wolf.WolfAvoidEntityGoal<>(this, Llama.class, 24.0F, 1.5D, 1.5D));
         this.goalSelector.addGoal(4, new LeapAtTargetGoal(this, 0.4F));
         this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0D, true));
         this.goalSelector.addGoal(6, new FollowOwnerGoal(this, 1.0D, 10.0F, 2.0F, false));
diff -r -u3 -N a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java
--- a/net/minecraft/world/entity/EntityType.java	2021-05-12 08:33:26.000000000 -0700
+++ b/net/minecraft/world/entity/EntityType.java	2021-05-26 20:45:18.000000000 -0700
@@ -500,7 +500,27 @@
     }
 
     public static Stream<Entity> loadEntitiesRecursive(final List<? extends Tag> param0, final Level param1) {
-        // $FF: Couldn't be decompiled
+        final Spliterator<? extends Tag> var0 = param0.spliterator();
+        return StreamSupport.stream(new Spliterator<Entity>() {
+            public boolean tryAdvance(Consumer<? super Entity> param0x) {
+                return var0.tryAdvance((param2) -> EntityType.loadEntityRecursive((CompoundTag)param2, param1, (param1x) -> {
+                        param0.accept(param1x);
+                        return param1x;
+                    }));
+            }
+
+            public Spliterator<Entity> trySplit() {
+                return null;
+            }
+
+            public long estimateSize() {
+                return (long)param0.size();
+            }
+
+            public int characteristics() {
+                return 1297;
+            }
+        }, false);
     }
 
     private static Optional<Entity> loadStaticEntity(CompoundTag param0, Level param1) {
diff -r -u3 -N a/net/minecraft/world/entity/SlotAccess.java b/net/minecraft/world/entity/SlotAccess.java
--- a/net/minecraft/world/entity/SlotAccess.java	2021-05-12 08:33:26.000000000 -0700
+++ b/net/minecraft/world/entity/SlotAccess.java	2021-05-26 20:45:18.000000000 -0700
@@ -16,7 +16,20 @@
     };
 
     static SlotAccess forContainer(final Container param0, final int param1, final Predicate<ItemStack> param2) {
-        // $FF: Couldn't be decompiled
+        return new SlotAccess() {
+            public ItemStack get() {
+                return param0.getItem(param1);
+            }
+
+            public boolean set(ItemStack param0x) {
+                if (!param2.test(param0)) {
+                    return false;
+                } else {
+                    param0.setItem(param1, param0);
+                    return true;
+                }
+            }
+        };
     }
 
     static SlotAccess forContainer(Container param0, int param1) {
@@ -24,7 +37,20 @@
     }
 
     static SlotAccess forEquipmentSlot(final LivingEntity param0, final EquipmentSlot param1, final Predicate<ItemStack> param2) {
-        // $FF: Couldn't be decompiled
+        return new SlotAccess() {
+            public ItemStack get() {
+                return param0.getItemBySlot(param1);
+            }
+
+            public boolean set(ItemStack param0x) {
+                if (!param2.test(param0)) {
+                    return false;
+                } else {
+                    param0.setItemSlot(param1, param0);
+                    return true;
+                }
+            }
+        };
     }
 
     static SlotAccess forEquipmentSlot(LivingEntity param0, EquipmentSlot param1) {
diff -r -u3 -N a/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
--- a/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java	2021-05-12 08:33:00.000000000 -0700
+++ b/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java	2021-05-26 20:45:18.000000000 -0700
@@ -99,7 +99,16 @@
     }
 
     public SlotAccess getSlot(final int param0) {
-        // $FF: Couldn't be decompiled
+        return param0 >= 0 && param0 < this.getContainerSize() ? new SlotAccess() {
+            public ItemStack get() {
+                return AbstractMinecartContainer.this.getItem(param0);
+            }
+
+            public boolean set(ItemStack param0x) {
+                AbstractMinecartContainer.this.setItem(param0, param0);
+                return true;
+            }
+        } : super.getSlot(param0);
     }
 
     public void setChanged() {
diff -r -u3 -N a/net/minecraft/world/inventory/ContainerLevelAccess.java b/net/minecraft/world/inventory/ContainerLevelAccess.java
--- a/net/minecraft/world/inventory/ContainerLevelAccess.java	2021-05-12 08:33:10.000000000 -0700
+++ b/net/minecraft/world/inventory/ContainerLevelAccess.java	2021-05-26 20:45:18.000000000 -0700
@@ -14,7 +14,11 @@
     };
 
     static ContainerLevelAccess create(final Level param0, final BlockPos param1) {
-        // $FF: Couldn't be decompiled
+        return new ContainerLevelAccess() {
+            public <T> Optional<T> evaluate(BiFunction<Level, BlockPos, T> param0x) {
+                return Optional.of(param0.apply(param0, param1));
+            }
+        };
     }
 
     <T> Optional<T> evaluate(BiFunction<Level, BlockPos, T> var1);
diff -r -u3 -N a/net/minecraft/world/item/crafting/RecipeType.java b/net/minecraft/world/item/crafting/RecipeType.java
--- a/net/minecraft/world/item/crafting/RecipeType.java	2021-05-12 08:32:48.000000000 -0700
+++ b/net/minecraft/world/item/crafting/RecipeType.java	2021-05-26 20:45:18.000000000 -0700
@@ -16,7 +16,11 @@
     RecipeType<UpgradeRecipe> SMITHING = register("smithing");
 
     static <T extends Recipe<?>> RecipeType<T> register(final String param0) {
-        // $FF: Couldn't be decompiled
+        return Registry.register(Registry.RECIPE_TYPE, new ResourceLocation(param0), new RecipeType<T>() {
+            public String toString() {
+                return param0;
+            }
+        });
     }
 
     default <C extends Container> Optional<T> tryMatch(Recipe<C> param0, Level param1, C param2) {
diff -r -u3 -N a/net/minecraft/world/level/block/ChestBlock.java b/net/minecraft/world/level/block/ChestBlock.java
--- a/net/minecraft/world/level/block/ChestBlock.java	2021-05-12 08:33:14.000000000 -0700
+++ b/net/minecraft/world/level/block/ChestBlock.java	2021-05-26 20:45:18.000000000 -0700
@@ -2,6 +2,7 @@
 
 import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Random;
 import java.util.function.BiPredicate;
@@ -9,6 +10,8 @@
 import javax.annotation.Nullable;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.TranslatableComponent;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.server.level.ServerLevel;
 import net.minecraft.stats.Stat;
@@ -22,8 +25,10 @@
 import net.minecraft.world.entity.LivingEntity;
 import net.minecraft.world.entity.animal.Cat;
 import net.minecraft.world.entity.monster.piglin.PiglinAi;
+import net.minecraft.world.entity.player.Inventory;
 import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.inventory.ChestMenu;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.item.context.BlockPlaceContext;
 import net.minecraft.world.level.BlockGetter;
@@ -77,7 +82,27 @@
     };
     private static final DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>> MENU_PROVIDER_COMBINER = new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>>() {
         public Optional<MenuProvider> acceptDouble(final ChestBlockEntity param0, final ChestBlockEntity param1) {
-            // $FF: Couldn't be decompiled
+            final Container var0 = new CompoundContainer(param0, param1);
+            return Optional.of(new MenuProvider() {
+                @Nullable
+                public AbstractContainerMenu createMenu(int param0x, Inventory param1x, Player param2) {
+                    if (param0.canOpen(param2) && param1.canOpen(param2)) {
+                        param0.unpackLootTable(param1.player);
+                        param1.unpackLootTable(param1.player);
+                        return ChestMenu.sixRows(param0, param1, var0);
+                    } else {
+                        return null;
+                    }
+                }
+
+                public Component getDisplayName() {
+                    if (param0.hasCustomName()) {
+                        return param0.getDisplayName();
+                    } else {
+                        return (Component)(param1.hasCustomName() ? param1.getDisplayName() : new TranslatableComponent("container.chestDouble"));
+                    }
+                }
+            });
         }
 
         public Optional<MenuProvider> acceptSingle(ChestBlockEntity param0) {
@@ -249,7 +274,22 @@
     }
 
     public static DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction> opennessCombiner(final LidBlockEntity param0) {
-        // $FF: Couldn't be decompiled
+        return new DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction>() {
+            public Float2FloatFunction acceptDouble(ChestBlockEntity param0x, ChestBlockEntity param1) {
+                return (param2) -> Math.max(param0.getOpenNess(param2), param1.getOpenNess(param2));
+            }
+
+            public Float2FloatFunction acceptSingle(ChestBlockEntity param0x) {
+                Objects.requireNonNull(param0);
+                return param0::getOpenNess;
+            }
+
+            public Float2FloatFunction acceptNone() {
+                LidBlockEntity var10000 = param0;
+                Objects.requireNonNull(param0);
+                return var10000::getOpenNess;
+            }
+        };
     }
 
     public BlockEntity newBlockEntity(BlockPos param0, BlockState param1) {
diff -r -u3 -N a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
--- a/net/minecraft/world/level/chunk/LevelChunk.java	2021-05-12 08:33:12.000000000 -0700
+++ b/net/minecraft/world/level/chunk/LevelChunk.java	2021-05-26 20:45:18.000000000 -0700
@@ -793,7 +793,7 @@
     }
 
     private <T extends BlockEntity> TickingBlockEntity createTicker(T param0, BlockEntityTicker<T> param1) {
-        return new LevelChunk.BoundTickingBlockEntity(param0, param1);
+        return new LevelChunk.BoundTickingBlockEntity<>(param0, param1);
     }
 
     class BoundTickingBlockEntity<T extends BlockEntity> implements TickingBlockEntity {
diff -r -u3 -N a/net/minecraft/world/level/entity/EntityTypeTest.java b/net/minecraft/world/level/entity/EntityTypeTest.java
--- a/net/minecraft/world/level/entity/EntityTypeTest.java	2021-05-12 08:33:02.000000000 -0700
+++ b/net/minecraft/world/level/entity/EntityTypeTest.java	2021-05-26 20:45:18.000000000 -0700
@@ -4,7 +4,16 @@
 
 public interface EntityTypeTest<B, T extends B> {
     static <B, T extends B> EntityTypeTest<B, T> forClass(final Class<T> param0) {
-        // $FF: Couldn't be decompiled
+        return new EntityTypeTest<B, T>() {
+            @Nullable
+            public T tryCast(B param0x) {
+                return (T)(param0.isInstance(param0) ? param0 : null);
+            }
+
+            public Class<? extends B> getBaseClass() {
+                return param0;
+            }
+        };
     }
 
     @Nullable
diff -r -u3 -N a/net/minecraft/world/level/levelgen/Aquifer.java b/net/minecraft/world/level/levelgen/Aquifer.java
--- a/net/minecraft/world/level/levelgen/Aquifer.java	2021-05-12 08:33:20.000000000 -0700
+++ b/net/minecraft/world/level/levelgen/Aquifer.java	2021-05-26 20:45:18.000000000 -0700
@@ -17,7 +17,19 @@
     }
 
     static Aquifer createDisabled(final int param0, final BlockState param1) {
-        // $FF: Couldn't be decompiled
+        return new Aquifer() {
+            public BlockState computeState(BaseStoneSource param0x, int param1x, int param2, int param3, double param4) {
+                if (param4 > 0.0D) {
+                    return param0.getBaseBlock(param1, param2, param3);
+                } else {
+                    return param2 >= param0 ? Blocks.AIR.defaultBlockState() : param1;
+                }
+            }
+
+            public boolean shouldScheduleFluidUpdate() {
+                return false;
+            }
+        };
     }
 
     BlockState computeState(BaseStoneSource var1, int var2, int var3, int var4, double var5);

@@ -463,7 +463,7 @@ index ebb845dff674965ff3e0e8011db33dae5270e24d..a02c40a8a8a2ad1a1888d8a5370625c2
+
+ int j = 0;
+ for (int i = start; i < lstParameters.size(); ++i) {
+ if (mask == null || mask.get(i) != null) {
+ if (mask == null || mask.get(i) == null) {
Copy link
Member

@LexManos LexManos May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh.. wonder how this ever worked. Also probably need to change isNew to isGenNew a few lines above where the mask is set so that we can make sure desc.getSignature is not null. Err.. nevermind its inside a desc.getSignature() != null if. But that should probably be changed for proper fix.

+ // However, synthetic parameters are added at the beginnings of method signatures (at least usually)
+ // so we really want to start from the beginning
+ // if it ends up that this assumption is not true, we'd have to do some sort of fuzzy matching here, or earlier
+ // on traverse multiple steps in the graph in getEnclosingVarField
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed in Discord, this assumption is incorrect.
Would need a deeper dive into the compiler to find all cases where synthetics are added to the beginning or end.

index fe372662be3fbd310f94831b47f21b92033a851c..f50e767ab81de739cad2059b5a8279fcd7cdba25 100644
--- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java
+++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java
@@ -35,6 +35,8 @@ import java.util.*;
Copy link
Member

@LexManos LexManos May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this and it does not seem to effect decompiled code at all. So i'm gunna split this out and we can discuss it in more depth later.

@LexManos
Copy link
Member

Pulled in the mask fix in e661013
We'll need to discuss the other parts more.

@SizableShrimp SizableShrimp marked this pull request as draft January 25, 2023 19:25
@SizableShrimp SizableShrimp marked this pull request as draft January 25, 2023 19:25
@SizableShrimp
Copy link
Member

Awaiting discussion & rebasing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants