diff --git a/build.gradle.kts b/build.gradle.kts
index 6afe1dc86..9b61f98b1 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -26,22 +26,3 @@ ext["lwjglNatives"] = Pair(
             throw Error("Unrecognized or unsupported platform. Please set \"lwjglNatives\" manually")
     }
 }
-
-plugins {
-    id("java")
-}
-
-
-
-val genJavadoc by tasks.registering(Javadoc::class) {
-    val exposedProjects = listOf(
-        ":game:core",
-        ":game:client",
-        ":game:server",
-    )
-    val sourceSets = exposedProjects.map { project(it).sourceSets["main"] }
-    classpath = files(sourceSets.map { it.compileClasspath })
-    setSource(exposedProjects.map { project(it).sourceSets["main"].allJava })
-    setDestinationDir(file("${layout.buildDirectory.get()}/docs/javadoc"))
-    print(destinationDir)
-}
\ No newline at end of file
diff --git a/game/client/src/main/java/net/betterthanadventure/utils/BiomeFinder.java b/game/client/src/main/java/net/betterthanadventure/utils/BiomeFinder.java
index ee424dc59..bba31dcd5 100644
--- a/game/client/src/main/java/net/betterthanadventure/utils/BiomeFinder.java
+++ b/game/client/src/main/java/net/betterthanadventure/utils/BiomeFinder.java
@@ -22,8 +22,9 @@ import net.minecraft.core.world.pos.ChunkTilePos;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.save.LevelStorage;
 import net.minecraft.core.world.save.SaveFormatBase;
-import net.minecraft.client.world.save.SaveHandlerClientMP;
+import net.minecraft.core.world.save.LevelStorageDummy;
 import net.minecraft.core.world.save.mcregion.SaveFormat19134;
+import net.minecraft.core.world.saveddata.SavedDataStorage;
 import net.minecraft.core.world.type.WorldType;
 import net.minecraft.core.world.type.WorldTypes;
 import org.jetbrains.annotations.NotNull;
@@ -493,12 +494,12 @@ public class BiomeFinder implements MinecraftAccessor {
 		BiomeProvider biomeProvider;
 		World world;
 		if (worldFolder == null){
-			biomeProvider = worldType.createBiomeProvider(new World(new SaveHandlerClientMP(), "", dimension, worldType, seed));
+			biomeProvider = worldType.createBiomeProvider(new World(new SavedDataStorage(new LevelStorageDummy()), "", seed, dimension, worldType));
 			world = null;
 		} else {
 			SaveFormatBase saveFormat = new SaveFormat19134(worldFolder.getParentFile());
-			LevelStorage saveHandler = saveFormat.getSaveHandler(worldFolder.getName(), false);
-			world = new World(saveHandler, saveHandler.getLevelData().getWorldName(), saveHandler.getLevelData().getRandomSeed(), null, null, null);
+			LevelStorage saveHandler = saveFormat.getLevelStorage(worldFolder.getName(), false);
+			world = new World(saveHandler, saveHandler.getLevelData().getWorldName(), saveHandler.getLevelData().getRandomSeed(), Dimension.OVERWORLD, null);
 			biomeProvider = worldType.createBiomeProvider(world);
 		}
 		allBiomes.clear();
diff --git a/game/client/src/main/java/net/minecraft/client/Minecraft.java b/game/client/src/main/java/net/minecraft/client/Minecraft.java
index 433dc9acd..5be459f2d 100644
--- a/game/client/src/main/java/net/minecraft/client/Minecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/Minecraft.java
@@ -98,7 +98,7 @@ import net.minecraft.client.world.WorldClientMP;
 import net.minecraft.client.world.chunk.provider.ChunkProviderDynamic;
 import net.minecraft.client.world.chunk.provider.ChunkProviderStatic;
 import net.minecraft.client.world.chunk.provider.ChunkProviderThreadedClient;
-import net.minecraft.client.world.save.SaveHandlerClientSP;
+import net.minecraft.client.world.save.LevelStorageClientSP;
 import net.minecraft.core.Global;
 import net.minecraft.core.MinecraftAccessor;
 import net.minecraft.core.MinecraftError;
@@ -157,6 +157,7 @@ import net.minecraft.core.net.command.util.CommandHelper;
 import net.minecraft.core.player.Session;
 import net.minecraft.core.player.gamemode.Gamemode;
 import net.minecraft.core.player.gamemode.Gamemodes;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
 import net.minecraft.core.util.debug.Debug;
 import net.minecraft.core.util.helper.Axis;
 import net.minecraft.core.util.helper.Color;
@@ -235,7 +236,7 @@ public class Minecraft
     public boolean hasCrashed;
     public UnexpectedThrowable lastCrash;
     public final Timer timer;
-    public WorldClient currentWorld;
+    public @Nullable WorldClient currentWorld;
     public RenderGlobal renderGlobal;
     public PlayerLocal thePlayer;
     public ICamera activeCamera;
@@ -516,7 +517,7 @@ public class Minecraft
         this.resolution.setScale(GameSettings.GUI_SCALE.value);
 
         SaveFormatBase handlerBase = new SaveFormat19134(new File(this.mcDataDir, "saves"));
-        handlerBase.factory = SaveHandlerClientSP::new;
+        handlerBase.factory = LevelStorageClientSP::new;
         this.saveFormat = handlerBase;
         I18n.initialize(GameSettings.LANGUAGE.value);
 
@@ -1799,10 +1800,10 @@ public class Minecraft
         if (GameSettings.KEY_HOT_BAR_SWITCH.isPressEvent(currentInputDevice)) {
             if (!this.thePlayer.inventory.currentSlotLocked()) {
                 if (shift) {
-                    this.thePlayer.setHotbarOffset((this.thePlayer.inventory.getHotbarOffset() + 27) % (36));
+                    this.thePlayer.setHotbarOffset((this.thePlayer.inventory.getHotbarOffset() + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE)) % ContainerInventory.MAIN_INVENTORY_SIZE);
                     this.hotbarSwapAnimationProgress = -HOTBAR_SWAP_ANIMATION_LENGTH_TICKS;
                 } else {
-                    this.thePlayer.setHotbarOffset((this.thePlayer.inventory.getHotbarOffset() + 9) % (36));
+                    this.thePlayer.setHotbarOffset((this.thePlayer.inventory.getHotbarOffset() + ContainerInventory.HOTBAR_SIZE) % ContainerInventory.MAIN_INVENTORY_SIZE);
                     this.hotbarSwapAnimationProgress = HOTBAR_SWAP_ANIMATION_LENGTH_TICKS;
                 }
                 this.thePlayer.inventory.changeCurrentSlot(0);
@@ -1987,14 +1988,14 @@ public class Minecraft
             }
             if (controller.digitalPad.up.pressedThisFrame()) {
                 if (!this.thePlayer.inventory.currentSlotLocked()) {
-                    this.thePlayer.setHotbarOffset((this.thePlayer.inventory.getHotbarOffset() + 27) % (36));
+                    this.thePlayer.setHotbarOffset((this.thePlayer.inventory.getHotbarOffset() + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE)) % ContainerInventory.MAIN_INVENTORY_SIZE);
                     this.hotbarSwapAnimationProgress = -HOTBAR_SWAP_ANIMATION_LENGTH_TICKS;
                     this.thePlayer.inventory.changeCurrentSlot(0);
                 }
             }
             if (controller.digitalPad.down.pressedThisFrame()) {
                 if (!this.thePlayer.inventory.currentSlotLocked()) {
-                    this.thePlayer.setHotbarOffset((this.thePlayer.inventory.getHotbarOffset() + 9) % (36));
+                    this.thePlayer.setHotbarOffset((this.thePlayer.inventory.getHotbarOffset() + ContainerInventory.HOTBAR_SIZE) % ContainerInventory.MAIN_INVENTORY_SIZE);
                     this.hotbarSwapAnimationProgress = HOTBAR_SWAP_ANIMATION_LENGTH_TICKS;
                     this.thePlayer.inventory.changeCurrentSlot(0);
                 }
@@ -2044,13 +2045,11 @@ public class Minecraft
     }
 
     public void createAndStartWorld(final @NotNull WorldConfiguration worldConfiguration) {
-        final @NotNull LevelStorage levelStorage = this.saveFormat.getSaveHandler(worldConfiguration.getFolderName(this.saveFormat), false);
+        final @NotNull LevelStorage levelStorage = this.saveFormat.getLevelStorage(worldConfiguration.getFolderName(this.saveFormat), false);
         for (final @NotNull Dimension dimension : Dimension.getDimensionList().values()) {
             final @Nullable WorldType worldType = worldConfiguration.getWorldTypeGroup().get(dimension);
-            if (worldType != null) {
-                final @NotNull DimensionData data = new DimensionData(worldType);
-                levelStorage.saveDimensionData(dimension.id, data);
-            }
+            final @NotNull DimensionData data = new DimensionData(worldType);
+            levelStorage.saveDimensionData(dimension.id, data);
         }
 
         final @NotNull WorldClient world = new WorldClient(
@@ -2058,7 +2057,7 @@ public class Minecraft
             worldConfiguration.getFinalWorldName(),
             worldConfiguration.getFinalNumericSeed(),
             Dimension.OVERWORLD,
-            null, worldConfiguration.getStartingBiome()
+            worldConfiguration.getStartingBiome()
         );
         world.getLevelData().setCheatsEnabled(worldConfiguration.getCheatsEnabled());
         world.getLevelData().getGameRules().setValues(worldConfiguration.getGameRuleCollection());
@@ -2094,14 +2093,16 @@ public class Minecraft
             convertSaveVersion(worldSaveVersion, worldDirName);
         } else {
             I18n i18n = I18n.getInstance();
-            LevelStorage saveHandler = this.saveFormat.getSaveHandler(worldDirName, false);
-            Dimension dimension = null;
+            LevelStorage saveHandler = this.saveFormat.getLevelStorage(worldDirName, false);
             CompoundTag tag = saveHandler.getPlayerData(this.session.username, this.session.uuid);
+            final @NotNull Dimension dimension;
             if (tag != null) {
                 dimension = Dimension.getDimensionList().get(tag.getInteger("Dimension"));
+            } else {
+                dimension = Dimension.OVERWORLD;
             }
 
-            WorldClient world = new WorldClient(saveHandler, null, 0L, dimension, null, null);
+            WorldClient world = new WorldClient(saveHandler, null, 0L, dimension, null);
             this.statsCounter.add(StatList.loadWorldStat, 1);
             this.statsCounter.add(StatList.startGameStat, 1);
             changeWorld(world, i18n.translateKey("gui.loading.label.loading"));
@@ -2178,23 +2179,21 @@ public class Minecraft
         this.sndManager.stopMusic();
         if (this.currentWorld != null) {
             this.currentWorld.saveWorldIndirectly(this.loadingScreen);
-            if (this.currentWorld.saveHandler != null) {
-                IChunkLoader loader = this.currentWorld.saveHandler.getChunkLoader(this.currentWorld.dimension);
-                if (loader != null) {
-                    while (loader.isSaving()) {
-                        try {
-                            Thread.sleep(1);
-                        } catch (InterruptedException e) {
-                            throw new RuntimeException(e);
-                        }
+            IChunkLoader loader = this.currentWorld.getLevelStorage().getChunkLoader(this.currentWorld.dimension);
+            if (loader != null) {
+                while (loader.isSaving()) {
+                    try {
+                        Thread.sleep(1);
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
                     }
                 }
-                this.currentWorld.getChunkProvider().unloadAllChunks();
-                this.currentWorld.onUnload();
-                this.currentWorld.close();
-                if (player != null) {
-                    this.currentWorld.saveHandler.save(player);
-                }
+            }
+            this.currentWorld.getChunkProvider().unloadAllChunks();
+            this.currentWorld.onUnload();
+            this.currentWorld.close();
+            if (player != null) {
+                this.currentWorld.getLevelStorage().save(player);
             }
         }
         this.currentWorld = world;
@@ -2281,7 +2280,7 @@ public class Minecraft
 
                     levelDataTag.putInt("version", toWorldFormat.getSaveVersion());
 
-                    LevelStorage saveHandler = toWorldFormat.getSaveHandler(worldDirName, false);
+                    LevelStorage saveHandler = toWorldFormat.getLevelStorage(worldDirName, false);
                     saveHandler.saveLevelDataRaw(levelDataTag);
 
                     startWorld(worldDirName);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenMainMenu.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenMainMenu.java
index e832d4503..dfb8c090a 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenMainMenu.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenMainMenu.java
@@ -69,8 +69,6 @@ public class ScreenMainMenu extends Screen {
     private int logoClickCounter = 0;
     private int indexToDraw = 0;
     private int codeProgress = 0;
-    private boolean showBuildTime = false;
-    private final String buildTimeString = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(Version.BUILD_TIME));
 
     static {
         loadLogo();
@@ -279,11 +277,6 @@ public class ScreenMainMenu extends Screen {
                 this.logoClickCounter++;
             }
         }
-        if (my >= 2 && my <= 12) {
-            if (mx >= 2 && mx <= 40) {
-                this.showBuildTime = !this.showBuildTime;
-            }
-        }
     }
 
     @Override
@@ -320,11 +313,7 @@ public class ScreenMainMenu extends Screen {
         drawStringCenteredShadow(this.fontRenderer, this.splashText, 0, -8, 0xffff00);
         GLRenderer.popFrame();
         int versionTextColor = GameSettings.ALPHA_MENU.value || !this.mc.mainMenuBackground.isEnabled() ? 0x505050 : 0xFFFFFF;
-        if (this.showBuildTime) {
-            drawStringShadow(this.fontRenderer, this.buildTimeString, 2, 2, versionTextColor);
-        } else {
-            drawStringShadow(this.fontRenderer, "Better than Adventure! " + Minecraft.VERSION, 2, 2, versionTextColor);
-        }
+        drawStringShadow(this.fontRenderer, "Better than Adventure! " + Minecraft.VERSION, 2, 2, versionTextColor);
         String disclaimer1 = i18n.translateKey("gui.main_menu.label.disclaimer.1");
         drawStringShadow(this.fontRenderer, disclaimer1, this.width - MathHelper.ceil(this.fontRenderer.stringWidthDouble(disclaimer1)) - 2, this.height - 20, 0xffffff);
         String disclaimer2 = i18n.translateKey("gui.main_menu.label.disclaimer.2");
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenSelectWorld.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenSelectWorld.java
index be2276fa4..2cc84dcf1 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenSelectWorld.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenSelectWorld.java
@@ -92,7 +92,7 @@ public class ScreenSelectWorld extends Screen {
         this.saveList = saveFormat.getSaveFileList();
         this.userTags = new HashMap<>();
         for (SaveFile file : this.saveList) {
-            LevelStorage levelStorage = saveFormat.getSaveHandler(file.getFileName(), false);
+            LevelStorage levelStorage = saveFormat.getLevelStorage(file.getFileName(), false);
             LevelData data = levelStorage.getLevelData();
             if (data != null && data.getLastPlayerUUID() != null) {
                 this.userTags.put(file, levelStorage.getPlayerData(null, data.getLastPlayerUUID()));
diff --git a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenDispenser.java b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenDispenser.java
index 27c5669b9..30fb7369f 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenDispenser.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenDispenser.java
@@ -3,13 +3,13 @@ package net.minecraft.client.gui.container;
 import net.minecraft.client.render.renderer.GLRenderer;
 import net.minecraft.core.block.entity.TileEntityDispenser;
 import net.minecraft.core.lang.I18n;
-import net.minecraft.core.player.inventory.menu.MenuTrap;
+import net.minecraft.core.player.inventory.menu.MenuDispenser;
 import net.minecraft.core.player.inventory.container.ContainerInventory;
 
 public class ScreenDispenser extends ScreenContainerAbstract {
 
     public ScreenDispenser(ContainerInventory inventoryplayer, TileEntityDispenser tileentitydispenser) {
-        super(new MenuTrap(inventoryplayer, tileentitydispenser));
+        super(new MenuDispenser(inventoryplayer, tileentitydispenser));
     }
 
     @Override
diff --git a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenFlagEditor.java b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenFlagEditor.java
index 799065300..3712b8a1a 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenFlagEditor.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenFlagEditor.java
@@ -10,6 +10,7 @@ import net.minecraft.client.render.renderer.GLRenderer;
 import net.minecraft.client.render.renderer.State;
 import net.minecraft.core.block.entity.TileEntityFlag;
 import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
 import org.jetbrains.annotations.NotNull;
 import org.lwjgl.input.Keyboard;
 
@@ -131,7 +132,7 @@ public class ScreenFlagEditor
         super.mouseClicked(mx, my, buttonNum);
 
         // Do not draw with no color selected
-        if (this.flagEntity.getItem(36 + this.selectedColor) == null) {
+        if (this.flagEntity.getItem(ContainerInventory.MAIN_INVENTORY_SIZE + this.selectedColor) == null) {
             return;
         }
 
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngameDoom.java b/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngameDoom.java
index 360bb0453..32c731878 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngameDoom.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngameDoom.java
@@ -13,6 +13,7 @@ import net.minecraft.core.block.Blocks;
 import net.minecraft.core.enums.HumanArmorShape;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.net.command.TextFormatting;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
 
 public class HudIngameDoom
     extends HudIngame {
@@ -218,7 +219,7 @@ public class HudIngameDoom
         int x = width / 2 + 76;
         int y = height - 36;
 
-        int selectedHotbar = ((this.mc.thePlayer.inventory.getHotbarOffset() + 27) % 36) / 9;
+        int selectedHotbar = ((this.mc.thePlayer.inventory.getHotbarOffset() + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE)) % ContainerInventory.MAIN_INVENTORY_SIZE) / ContainerInventory.HOTBAR_SIZE;
 
         drawTexturedModalRect(x, y + 9 * selectedHotbar, 0, 129, 6, 6);
     }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHotbar.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHotbar.java
index 612ca2ed0..ad3d114a1 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHotbar.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHotbar.java
@@ -68,7 +68,7 @@ public class HudComponentHotbar
         hud.drawGuiIcon(x + 5, y, 182, 22, TextureRegistry.getTexture("minecraft:gui/hud/hotbar"));
 
         // Render hotbar switcher and dots
-        int selectedHotbar = ((inv.getHotbarOffset() + 27) % 36) / 9;
+        int selectedHotbar = ((inv.getHotbarOffset() + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE)) % ContainerInventory.MAIN_INVENTORY_SIZE) / ContainerInventory.HOTBAR_SIZE;
         IconCoordinate coordinate = TextureRegistry.getTexture("minecraft:gui/hud/hotbar_selector" + selectedHotbar);
         hud.drawGuiIcon(x, y, 6, 22, coordinate);
 
@@ -76,8 +76,8 @@ public class HudComponentHotbar
         GLRenderer.enableState(State.DEPTH_TEST);
         GLRenderer.enableState(State.BLEND);
         Lighting.enableInventoryLight();
-        int lastHotbarStart = (mc.thePlayer.inventory.getHotbarOffset() + 27) % 36;
-        int nextHotbarStart = (mc.thePlayer.inventory.getHotbarOffset() + 9) % 36;
+        int lastHotbarStart = (mc.thePlayer.inventory.getHotbarOffset() + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE)) % ContainerInventory.MAIN_INVENTORY_SIZE;
+        int nextHotbarStart = (mc.thePlayer.inventory.getHotbarOffset() + ContainerInventory.HOTBAR_SIZE) % ContainerInventory.MAIN_INVENTORY_SIZE;
 
         // Render hotbars being switched if appropriate
         if (mc.hotbarSwapAnimationProgress != 0.0f) {
@@ -86,7 +86,7 @@ public class HudComponentHotbar
             if (mc.hotbarSwapAnimationProgress > 0) {
 
                 // Draw last hotbar
-                for (int itemIndex = 0; itemIndex < 9; itemIndex++) {
+                for (int itemIndex = 0; itemIndex < ContainerInventory.HOTBAR_SIZE; itemIndex++) {
                     int itemX = x + 8 + itemIndex * 20;
                     int itemY = y + 3 - 22 + (int) (22 * (mc.hotbarSwapAnimationProgress - partialTick) / HOTBAR_SWAP_ANIMATION_LENGTH_TICKS);
                     renderInventorySlot(mc, itemIndex + lastHotbarStart, itemX, itemY, partialTick);
@@ -94,14 +94,14 @@ public class HudComponentHotbar
 
                 // Render hotbar items
                 GLRenderer.setColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
-                for (int itemIndex = 0; itemIndex < 9; itemIndex++) {
+                for (int itemIndex = 0; itemIndex < ContainerInventory.HOTBAR_SIZE; itemIndex++) {
                     int itemX = x + 8 + itemIndex * 20;
                     int itemY = y + 3 + (int) (22 * (mc.hotbarSwapAnimationProgress - partialTick) / HOTBAR_SWAP_ANIMATION_LENGTH_TICKS);
                     renderInventorySlot(mc, itemIndex + mc.thePlayer.inventory.getHotbarOffset(), itemX, itemY, partialTick);
                 }
             } else {
                 // Draw next hotbar
-                for (int itemIndex = 0; itemIndex < 9; itemIndex++) {
+                for (int itemIndex = 0; itemIndex < ContainerInventory.HOTBAR_SIZE; itemIndex++) {
                     int itemX = x + 8 + itemIndex * 20;
                     int itemY = y + 3 + 22 + (int) (22 * (mc.hotbarSwapAnimationProgress + partialTick) / HOTBAR_SWAP_ANIMATION_LENGTH_TICKS);
                     renderInventorySlot(mc, itemIndex + nextHotbarStart, itemX, itemY, partialTick);
@@ -109,7 +109,7 @@ public class HudComponentHotbar
 
                 // Render hotbar items
                 GLRenderer.setColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
-                for (int itemIndex = 0; itemIndex < 9; itemIndex++) {
+                for (int itemIndex = 0; itemIndex < ContainerInventory.HOTBAR_SIZE; itemIndex++) {
                     int itemX = x + 8 + itemIndex * 20;
                     int itemY = y + 3 + (int) (22 * (mc.hotbarSwapAnimationProgress + partialTick) / HOTBAR_SWAP_ANIMATION_LENGTH_TICKS);
                     renderInventorySlot(mc, itemIndex + mc.thePlayer.inventory.getHotbarOffset(), itemX, itemY, partialTick);
@@ -120,7 +120,7 @@ public class HudComponentHotbar
         } else {
             // Render hotbar items
             GLRenderer.setColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
-            for (int itemIndex = 0; itemIndex < 9; itemIndex++) {
+            for (int itemIndex = 0; itemIndex < ContainerInventory.HOTBAR_SIZE; itemIndex++) {
                 int itemX = x + 8 + itemIndex * 20;
                 int itemY = y + 3;
                 renderInventorySlot(mc, itemIndex + mc.thePlayer.inventory.getHotbarOffset(), itemX, itemY, partialTick);
@@ -135,7 +135,7 @@ public class HudComponentHotbar
         GLRenderer.setColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
 
         // Render selector
-        hud.drawGuiIcon((x + 4) + (inv.getCurrentSlot() % 9) * 20, y - 1, 24, 24, TextureRegistry.getTexture(mc.thePlayer.getHeldObject() == null ? "minecraft:gui/hud/hotbar_selection" : "minecraft:gui/hud/hotbar_selection_locked"));
+        hud.drawGuiIcon((x + 4) + (inv.getCurrentSlot() % ContainerInventory.HOTBAR_SIZE) * 20, y - 1, 24, 24, TextureRegistry.getTexture(mc.thePlayer.getHeldObject() == null ? "minecraft:gui/hud/hotbar_selection" : "minecraft:gui/hud/hotbar_selection_locked"));
 
         GLRenderer.pushFrame();
         GLRenderer.modelM4f().translate(0, 0, 1f);
@@ -170,7 +170,7 @@ public class HudComponentHotbar
 
         // Render hotbar items
         GLRenderer.setColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
-        for (int itemIndex = 0; itemIndex < 9; itemIndex++) {
+        for (int itemIndex = 0; itemIndex < ContainerInventory.HOTBAR_SIZE; itemIndex++) {
             int itemX = x + 8 + itemIndex * 20;
             int itemY = y + 3;
             renderItem(mc, PREVIEW_ITEMS[itemIndex], itemX, itemY);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java b/game/client/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
index 5299c2583..11b27bd85 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
@@ -142,55 +142,52 @@ public abstract class OptionsPages {
         );
 
         VIDEO = register(new OptionsPage("gui.options.page.video.title", new ItemStack(Items.FLAG))
-                .withComponent(new OptionsCategory("gui.options.page.video.category.graphics")
+                .withComponent(new OptionsCategory("gui.options.page.video.category.display")
                     .withComponent(new BooleanOptionComponent(GameSettings.FULLSCREEN))
                     .withComponent(new BooleanOptionComponent(GameSettings.BORDERLESS_FULLSCREEN))
+                    .withComponent(new FloatOptionComponent(GameSettings.GAMMA))
+                    .withComponent(new BooleanOptionComponent(GameSettings.ENABLE_VSYNC))
+                    .withComponent(new FramerateOptionComponent(GameSettings.CUSTOM_FPS_LIMIT))
                     .withComponent(new ToggleableOptionComponent<>(GameSettings.RENDER_SCALE))
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.RENDER_DISTANCE))
-                    .withComponent(new BooleanOptionComponent(GameSettings.AMBIENT_OCCLUSION))
-                    .withComponent(new BooleanOptionComponent(GameSettings.CONNECTED_GLASS))
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.MIPMAP_LEVELS).applyOnRelease())
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.MIPMAP_TYPE).applyOnRelease())
                     .withComponent(new ToggleableOptionComponent<>(GameSettings.COLORBLINDNESS_FIX))
                 )
-                .withComponent(new OptionsCategory("gui.options.page.video.category.performance")
-                        .withComponent(new BooleanOptionComponent(GameSettings.ENABLE_VSYNC))
-                        .withComponent(new FramerateOptionComponent(GameSettings.CUSTOM_FPS_LIMIT))
-//                .withComponent(new BooleanOptionComponent(GameSettings.advancedOpenGL))
-                )
-                .withComponent(new OptionsCategory("gui.options.page.video.category.post_processing")
-                    .withComponent(new FloatOptionComponent(GameSettings.GAMMA))
-                    .withComponent(new FloatOptionComponent(GameSettings.COLOR_CORRECTION))
-                    .withComponent(new FloatOptionComponent(GameSettings.FXAA))
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.BLOOM))
-                    .withComponent(new BooleanOptionComponent(GameSettings.HEAT_HAZE))
-                )
                 .withComponent(new OptionsCategory("gui.options.page.video.category.visuals")
+                    .withComponent(new BooleanOptionComponent(GameSettings.AMBIENT_OCCLUSION))
                     .withComponent(new ToggleableOptionComponent<>(GameSettings.LIGHTMAP_STYLE))
                     .withComponent(new FloatOptionComponent(GameSettings.BRIGHTNESS))
-                    .withComponent(new BooleanOptionComponent(GameSettings.FOV_MODIFICATION))
-                    .withComponent(new BooleanOptionComponent(GameSettings.SCREEN_MOTION_EFFECTS))
+                    .withComponent(new BooleanOptionComponent(GameSettings.CONNECTED_GLASS))
                     .withComponent(new BooleanOptionComponent(GameSettings.FOG))
                     .withComponent(new BooleanOptionComponent(GameSettings.BIOME_WATER))
-                    .withComponent(new FloatOptionComponent(GameSettings.ENTITY_RENDER_DISTANCE, 0.1f, 3f))
-                    .withComponent(new BooleanOptionComponent(GameSettings.ENTITY_SHADOWS))
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.TEXT_OUTLINE_QUALITY))
                     .withComponent(new BooleanOptionComponent(GameSettings.CLOUDS))
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.CLOUD_QUALITY))
                     .withComponent(new FloatOptionComponent(GameSettings.CLOUD_HEIGHT, -0.5f, 1f))
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.LEAVES_QUALITY).applyOnRelease())
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.PARTICLES_QUALITY))
-                    .withComponent(new ToggleableOptionComponent<>(GameSettings.PARTICLES_CAP))
-                    .withComponent(new FloatOptionComponent(GameSettings.PARTICLE_RENDER_DISTANCE, 0.1f, 3f))
                     .withComponent(new BooleanOptionComponent(GameSettings.TREE_SHADOWS))
                     .withComponent(new BooleanOptionComponent(GameSettings.VIGNETTE))
                     .withComponent(new BooleanOptionComponent(GameSettings.ITEMS_3D))
                     .withComponent(new BooleanOptionComponent(GameSettings.AURORA))
                     .withComponent(new BooleanOptionComponent(GameSettings.SLIME_PARTICLES))
                     .withComponent(new FloatOptionComponent(GameSettings.BLOCK_OUTLINE_WIDTH))
+                )
+                .withComponent(new OptionsCategory("gui.options.page.video.category.post_processing")
+                    .withComponent(new FloatOptionComponent(GameSettings.COLOR_CORRECTION))
+                    .withComponent(new FloatOptionComponent(GameSettings.FXAA))
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.BLOOM))
+                    .withComponent(new BooleanOptionComponent(GameSettings.HEAT_HAZE))
+                )
+                .withComponent(new OptionsCategory("gui.options.page.video.category.performance")
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.RENDER_DISTANCE))
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.MIPMAP_LEVELS).applyOnRelease())
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.MIPMAP_TYPE).applyOnRelease())
                     .withComponent(new BooleanOptionComponent(GameSettings.TRANSPARENCY_DEPTH_CHECK))
                     .withComponent(new BooleanOptionComponent(GameSettings.FRUSTUM_CULLING))
                     .withComponent(new ToggleableOptionComponent<>(GameSettings.CHUNK_REBUILDS_PER_FRAME))
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.LEAVES_QUALITY).applyOnRelease())
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.PARTICLES_QUALITY))
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.PARTICLES_CAP))
+                    .withComponent(new FloatOptionComponent(GameSettings.PARTICLE_RENDER_DISTANCE, 0.1f, 3f))
+                    .withComponent(new FloatOptionComponent(GameSettings.ENTITY_RENDER_DISTANCE, 0.1f, 3f))
+                    .withComponent(new BooleanOptionComponent(GameSettings.ENTITY_SHADOWS))
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.CLOUD_QUALITY))
+                    .withComponent(new ToggleableOptionComponent<>(GameSettings.TEXT_OUTLINE_QUALITY))
                 )
         );
 
diff --git a/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java b/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
index 8b6e38fc4..bc37df58f 100644
--- a/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
+++ b/game/client/src/main/java/net/minecraft/client/net/handler/PacketHandlerClient.java
@@ -52,6 +52,7 @@ import net.minecraft.core.net.handler.PacketHandler;
 import net.minecraft.core.net.packet.*;
 import net.minecraft.core.player.gamemode.Gamemode;
 import net.minecraft.core.player.gamemode.Gamemodes;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
 import net.minecraft.core.player.inventory.menu.MenuAbstract;
 import net.minecraft.core.player.inventory.container.ContainerSimple;
 import net.minecraft.core.sound.SoundTypes;
@@ -68,6 +69,7 @@ import net.minecraft.core.world.data.SynchedEntityData;
 import net.minecraft.core.world.pos.ChunkPos;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.save.LevelData;
+import net.minecraft.core.world.save.LevelStorageDummy;
 import net.minecraft.core.world.saveddata.SavedDataStorage;
 import net.minecraft.core.world.weather.Weather;
 import net.minecraft.core.world.weather.Weathers;
@@ -100,7 +102,7 @@ public class PacketHandlerClient extends PacketHandler {
         throws IOException {
         this.disconnected = false;
         this.started = false;
-        this.savedDataStorage = new SavedDataStorage(null);
+        this.savedDataStorage = new SavedDataStorage(new LevelStorageDummy());
         this.rand = new Random();
         this.mc = minecraft;
         Socket socket = new Socket(InetAddress.getByName(host), port);
@@ -842,7 +844,7 @@ public class PacketHandlerClient extends PacketHandler {
     public void handleContainerSetSlot(@NotNull PacketContainerSetSlot packetContainerSetSlot) {
         if (packetContainerSetSlot.windowId == -1) {
             this.mc.thePlayer.inventory.setHeldItemStack(packetContainerSetSlot.myItemStack);
-        } else if (packetContainerSetSlot.windowId == 0 && packetContainerSetSlot.itemSlot >= 36 && packetContainerSetSlot.itemSlot < 45) {
+        } else if (packetContainerSetSlot.windowId == 0 && packetContainerSetSlot.itemSlot >= ContainerInventory.MAIN_INVENTORY_SIZE && packetContainerSetSlot.itemSlot < ContainerInventory.MAIN_INVENTORY_SIZE + ContainerInventory.HOTBAR_SIZE) {
             ItemStack itemstack = this.mc.thePlayer.inventoryMenu.getSlot(packetContainerSetSlot.itemSlot).getItemStack();
             if (packetContainerSetSlot.myItemStack != null && (itemstack == null || itemstack.stackSize < packetContainerSetSlot.myItemStack.stackSize)) {
                 packetContainerSetSlot.myItemStack.animationsToGo = 5;
diff --git a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
index 66a3c52f8..07e442446 100644
--- a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
+++ b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
@@ -50,7 +50,7 @@ import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.BoundingVolume;
 import net.minecraft.core.util.phys.HitResult;
-import net.minecraft.core.world.AuroraProvider;
+import net.minecraft.client.world.AuroraProvider;
 import net.minecraft.core.world.LevelListener;
 import net.minecraft.core.world.pos.ChunkPos;
 import net.minecraft.core.world.pos.TilePos;
@@ -85,6 +85,8 @@ public class RenderGlobal
 
     public static final int RENDER_CHUNK_SIZE = 16;
 
+    private static final @NotNull AuroraProvider AURORA_PROVIDER = new AuroraProvider();
+
     public List<TileEntity> renderableTileEntities = new ObjectArrayList<>();
     private WorldClient world;
     private final TextureManager textureManager;
@@ -213,20 +215,20 @@ public class RenderGlobal
         return tessellator.record(GL41.glGenVertexArrays(), GL41.glGenBuffers());
     }
 
-    public void setWorld(final WorldClient world) {
+    public void setWorld(final @NotNull WorldClient world) {
         if (this.world != null) {
             this.world.removeListener(this);
         }
+
         this.xOld = -9999D;
         this.yOld = -9999D;
         this.zOld = -9999D;
         this.world = world;
         this.auroraBrightness = 0.0f;
         this.targetAuroraBrightness = Float.NaN;
-        if (world != null) {
-            world.addListener(this);
-            allChanged();
-        }
+        world.addListener(this);
+        AURORA_PROVIDER.init(world);
+        this.allChanged();
     }
 
     public void allChanged() {
@@ -311,7 +313,6 @@ public class RenderGlobal
 
         // Draw pieces
         double time = this.world.getWorldTime();
-        AuroraProvider aurora = this.world.auroraProvider;
 
         double radius = 512;
         long worldTime = this.world.getWorldTime();
@@ -329,8 +330,8 @@ public class RenderGlobal
                 distanceSqPercentage = 1f - distanceSqPercentage;
 
                 if (0.3f * distanceSqPercentage < 0.01f) continue;
-                double sampleO = aurora.getAuroraPresence(posXO, posZO, worldTime, celestialAngleO);
-                double sample = aurora.getAuroraPresence(posX, posZ, worldTime + 1, celestialAngle);
+                double sampleO = AURORA_PROVIDER.getAuroraPresence(posXO, posZO, worldTime, celestialAngleO);
+                double sample = AURORA_PROVIDER.getAuroraPresence(posX, posZ, worldTime + 1, celestialAngle);
                 if (sample * 0.3f * distanceSqPercentage < 0.01f) continue;
 
                 double atan = org.joml.Math.atan2(z, x);
@@ -343,18 +344,18 @@ public class RenderGlobal
                         double zOff = j * 8;
 
                         double aurX = (x + xOff) * scale;
-                        double aurY = (auroraY * scale  * scale) + aurora.getAuroraHeightOffset((posX + xOff), (posZ + zOff), time + 1);
+                        double aurY = (auroraY * scale  * scale) + AURORA_PROVIDER.getAuroraHeightOffset((posX + xOff), (posZ + zOff), time + 1);
                         double aurZ = (z + zOff) * scale;
 
-                        double aurYO = (auroraY * scale  * scale) + aurora.getAuroraHeightOffset((posX + xOff), (posZ + zOff), time);
+                        double aurYO = (auroraY * scale  * scale) + AURORA_PROVIDER.getAuroraHeightOffset((posX + xOff), (posZ + zOff), time);
 
-                        float r = aurora.getAuroraRedOffset((posX + xOff), (posZ + zOff), time + 1);
-                        float g = aurora.getAuroraGreenOffset((posX + xOff), (posZ + zOff), time + 1);
-                        float b = aurora.getAuroraBlueOffset((posX + xOff), (posZ + zOff), time + 1);
+                        float r = AURORA_PROVIDER.getAuroraRedOffset((posX + xOff), (posZ + zOff), time + 1);
+                        float g = AURORA_PROVIDER.getAuroraGreenOffset((posX + xOff), (posZ + zOff), time + 1);
+                        float b = AURORA_PROVIDER.getAuroraBlueOffset((posX + xOff), (posZ + zOff), time + 1);
 
-                        float ro = aurora.getAuroraRedOffset((posXO + xOff), (posZO + zOff), time);
-                        float go = aurora.getAuroraGreenOffset((posXO + xOff), (posZO + zOff), time);
-                        float bo = aurora.getAuroraBlueOffset((posXO + xOff), (posZO + zOff), time);
+                        float ro = AURORA_PROVIDER.getAuroraRedOffset((posXO + xOff), (posZO + zOff), time);
+                        float go = AURORA_PROVIDER.getAuroraGreenOffset((posXO + xOff), (posZO + zOff), time);
+                        float bo = AURORA_PROVIDER.getAuroraBlueOffset((posXO + xOff), (posZO + zOff), time);
 
                         renderAuroraBeam(this.tessellatorAurora,
                             aurX, aurY, aurZ,
@@ -1140,7 +1141,7 @@ public class RenderGlobal
                 this.toAdd[i] = null;
                 break;
             }
-            if (chunkx.rebuild(false)) {
+            if (chunkx.rebuild(GameSettings.KEY_LOAD_CHUNKS_QUICKLY.isPressed())) {
                 chunkx.setDirty(false);
                 this.dirtyChunks.remove(chunkx);
             }
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
index babac5c11..07f27b380 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
@@ -120,6 +120,9 @@ public final class BlockModelDispatcher
     public void reload() {
         BlockModelMojangData.Cache.resetCache();
         this.dispatches.clear();
+
+        addDispatch(new BlockModelEmpty<>(Blocks.AIR));
+
         addDispatch(new BlockModelGeneric<>(Blocks.STONE, loadDataModel("minecraft:block/stone")));
         addDispatch(new BlockModelGeneric<>(Blocks.BASALT, loadDataModel("minecraft:block/basalt")));
         addDispatch(new BlockModelGeneric<>(Blocks.LIMESTONE, loadDataModel("minecraft:block/limestone")));
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/generic/BlockModelGenericLayer.java b/game/client/src/main/java/net/minecraft/client/render/block/model/generic/BlockModelGenericLayer.java
index 6bdc97a76..92c4b7d8b 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/model/generic/BlockModelGenericLayer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/generic/BlockModelGenericLayer.java
@@ -4,7 +4,12 @@ import net.minecraft.client.render.block.model.BlockModelDispatcher;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.BlockLogic;
 import net.minecraft.core.block.BlockLogicLayerBase;
+import net.minecraft.core.util.helper.Direction;
+import net.minecraft.core.world.WorldSource;
+import net.minecraft.core.world.pos.TilePos;
+import net.minecraft.core.world.pos.TilePosc;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.useless.dragonfly.models.block.StaticBlockModel;
 
 public class BlockModelGenericLayer<T extends BlockLogic> extends BlockModelGeneric<T> {
@@ -21,4 +26,20 @@ public class BlockModelGenericLayer<T extends BlockLogic> extends BlockModelGene
     public @NotNull StaticBlockModel getModelFromData(int data) {
         return this.models[(data & BlockLogicLayerBase.MASK_HEIGHT) % this.models.length];
     }
+
+    private final @NotNull TilePos queryPos = new TilePos();
+    @Override
+    public boolean cullSide(@NotNull WorldSource worldSource, @NotNull TilePosc blockPos, @Nullable Direction direction) {
+        if (direction != null) {
+            TilePos shifted = blockPos.add(direction, this.queryPos);
+            if (worldSource.getBlockType(shifted) == this.block) {
+                int dataOther = worldSource.getBlockData(shifted);
+                int dataSelf = worldSource.getBlockData(blockPos);
+                return dataSelf <= dataOther || worldSource.isBlockOpaqueCube(shifted);
+            } else {
+                return worldSource.isBlockOpaqueCube(shifted);
+            }
+        }
+        return false;
+    }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererPainting.java b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererPainting.java
index 97d384fdf..d1f86242e 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererPainting.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererPainting.java
@@ -10,6 +10,7 @@ import net.minecraft.core.entity.EntityPainting;
 import net.minecraft.core.enums.ArtType;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.world.pos.TilePos;
 import org.jetbrains.annotations.NotNull;
 import org.joml.Math;
 
@@ -19,7 +20,6 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
     private static final IconCoordinate paintingBack = TextureRegistry.getTexture("minecraft:art/backing");
 
     public EntityRendererPainting() {
-        this.rand = new Random();
     }
 
     private void renderArt(TessellatorGeneral tessellator, EntityPainting entity, int sizeX, int sizeY, IconCoordinate iconCoordinate) {
@@ -32,13 +32,13 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
         final int tilesX = sizeX / 16;
         final int tilesY = sizeY / 16;
 
+        tessellator.startDrawingQuads();
         for (int x = 0; x < tilesX; x++) {
             for (int y = 0; y < tilesY; y++) {
                 final float maxX = negCenterX + (float) ((x + 1) * 16);
                 final float minX = negCenterX + (float) (x * 16);
                 final float maxY = negCenterY + (float) ((y + 1) * 16);
                 final float minY = negCenterY + (float) (y * 16);
-                setupPaintingBrightness(entity, (maxX + minX) / 2.0F, (maxY + minY) / 2.0F);
                 final double frontMinU = iconCoordinate.getSubIconU((tilesX - x) / (sizeX / 16f));
                 final double frontMaxU = iconCoordinate.getSubIconU((tilesX - x - 1) / (sizeX / 16f));
                 final double frontMinV = iconCoordinate.getSubIconV((tilesY - y) / (sizeY / 16f));
@@ -47,7 +47,8 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
                 final double backMaxU = paintingBack.getIconUMax();
                 final double backMinV = paintingBack.getIconVMin();
                 final double backMaxV = paintingBack.getIconVMax();
-                tessellator.startDrawingQuads();
+
+                tessellator.setLightmapCoord1i(getPaintingLightIndex(entity, (maxX + minX) / 2.0F, (maxY + minY) / 2.0F));
                 tessellator.setNormal(0.0F, 0.0F, -1F);
                 tessellator.addVertexWithUV(maxX, minY, minZ, frontMaxU, frontMinV);
                 tessellator.addVertexWithUV(minX, minY, minZ, frontMinU, frontMinV);
@@ -91,14 +92,14 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
                     tessellator.addVertexWithUV(minX, minY, minZ, backMaxU, backMaxV);
                     tessellator.addVertexWithUV(minX, minY, maxZ, subU, backMaxV);
                 }
-                tessellator.draw();
             }
 
         }
-
+        tessellator.draw();
     }
+    private final @NotNull TilePos queryPos = new TilePos();
 
-    private void setupPaintingBrightness(EntityPainting entity, float centerX, float centerY) {
+    private int getPaintingLightIndex(@NotNull EntityPainting entity, float centerX, float centerY) {
         int worldX = MathHelper.floor(entity.x);
         int worldY = MathHelper.floor(entity.y + (double) (centerY / 16F));
         int worldZ = MathHelper.floor(entity.z);
@@ -114,13 +115,12 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
         if (entity.direction == 3) {
             worldZ = MathHelper.floor(entity.z + (double) (centerX / 16F));
         }
-        GLRenderer.setLightmapCoord1i(entity.world.getLightmapCoord(worldX, worldY, worldZ, entity.world.getBlockLightValue(worldX, worldY, worldZ)));
-        GLRenderer.setColor3f(1f, 1f, 1f);
+        this.queryPos.set(worldX, worldY, worldZ);
+        return entity.world.getSavedLightIndex(this.queryPos);
     }
 
     @Override
     public void render(@NotNull TessellatorGeneral tessellator, @NotNull EntityPainting entity, double x, double y, double z, float yaw, float partialTick) {
-        this.rand.setSeed(187L);
         GLRenderer.pushFrame();
         GLRenderer.modelM4f().translate((float) x, (float) y, (float) z);
         GLRenderer.modelM4f().rotateY(Math.toRadians(yaw));
@@ -147,6 +147,9 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
         float maxZ = 0.5f;
         int width = sizeX / 16;
         int height = sizeY / 16;
+
+        TessellatorGeneral tessellator = GLRenderer.getTessellator();
+        tessellator.startDrawingQuads();
         for (int x = 0; x < width; ++x) {
             for (int y = 0; y < height; ++y) {
                 float maxX = negCenterX + (float) ((x + 1) * 16);
@@ -167,8 +170,6 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
                     minY -= 0.1f;
                 }
 
-                this.setupPaintingBrightness(entity, (maxX + minX) / 2.0f, (maxY + minY) / 2.0f);
-
                 int index = getIndex(width - x - 1, height - y - 1, width, height);
 
                 float u = (index % 4) / 4f;
@@ -179,8 +180,7 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
                 float minV = v + 0.25f;
                 float maxV = v;
 
-                TessellatorGeneral tessellator = GLRenderer.getTessellator();
-                tessellator.startDrawingQuads();
+                tessellator.setLightmapCoord1i(getPaintingLightIndex(entity, (maxX + minX) / 2.0f, (maxY + minY) / 2.0f));
                 tessellator.setNormal(0.0f, 0.0f, 1.0f);
                 tessellator.addVertexWithUV(maxX, minY, minZ, maxU, minV);
                 tessellator.addVertexWithUV(minX, minY, minZ, minU, minV);
@@ -238,12 +238,13 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
                     tessellator.addVertexWithUV(maxX, minY, minZ, minU, maxV);
                     tessellator.addVertexWithUV(maxX, maxY, minZ, minU, minV);
                 }
-
-                tessellator.draw();
             }
         }
+        tessellator.draw();
     }
 
+
+
     private int getIndex(int x, int y, int width, int height) {
         if (width == 1 && height == 1) { // 1x1
             return 15;
@@ -292,6 +293,4 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting> {
         }
         return 5;
     }
-
-    private final Random rand;
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/Font.java b/game/client/src/main/java/net/minecraft/client/render/font/Font.java
index 316a08e2a..d6d153e44 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/Font.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/Font.java
@@ -24,5 +24,5 @@ public interface Font {
 
     int fontHeight();
 
-    double renderChar(TessellatorFont tessellator, char c, double x, double y, double z, boolean italics);
+    double renderChar(TessellatorFont tessellator, char c, double x, double y, double z);
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java b/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
index 52c3b02cb..33aae017e 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
@@ -3,27 +3,27 @@ package net.minecraft.client.render.font;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.render.tessellator.TessellatorFont;
 import net.minecraft.client.render.texture.Texture;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
 import org.jetbrains.annotations.NotNull;
 
 public class FontDefault extends FontFile {
+
     public FontDefault(@NotNull final String key) {
         super(key, "/assets/minecraft/textures/gui/font/default/font_%02X.png", "/assets/minecraft/textures/gui/font/default/glyph_sizes.bin");
     }
 
     @Override
     public boolean canRenderChar(final char c) {
-        return c == ' ' || (!DEFAULT_BLACKLIST.contains(c) && ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c) >= 0 && textureForChar(c) != 0);
+        return c == ' ' || (ALLOWED_CHARACTER_INDICES[c] >= 0 && textureForChar(c) != 0);
 //        return false;
     }
 
     @Override
     public int textureForChar(final char c) {
-        final int fontIndex = ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c);
+        final int fontIndex = ALLOWED_CHARACTER_INDICES[c];
         if (fontIndex < 0) return 0;
         final int index = fontIndex / GLYPHS_PER_TEXTURE;
         if (this.texHandleCache[index] != TEXTURE_UNASSIGNED) return this.texHandleCache[index];
-        final Texture texture = Minecraft.getMinecraft().textureManager.loadTextureNoDefault(String.format(this.textureFormat, index));
+        final Texture texture = Minecraft.getMinecraft().textureManager.loadTextureNoDefault(this.textureLocationTable[index]);
         if (texture == null) {
             this.texHandleCache[index] = 0;
             return 0;
@@ -35,7 +35,7 @@ public class FontDefault extends FontFile {
     @Override
     public double charWidthDouble(final char c) {
         if (c == ' ') return 4;
-        final int fontIndex = ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c);
+        final int fontIndex = ALLOWED_CHARACTER_INDICES[c];
         if (fontIndex < 0) return 0;
 
         final int upper = this.charWidth[fontIndex] >> 4;
@@ -47,13 +47,11 @@ public class FontDefault extends FontFile {
     }
 
     @Override
-    public double renderChar(final TessellatorFont tessellator, final char c, final double x, final double y, final double z, final boolean italics) {
+    public double renderChar(final TessellatorFont tessellator, final char c, final double x, final double y, final double z) {
         if (c == ' ') return 4;
-        final int fontIndex = ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c);
+        final int fontIndex = ALLOWED_CHARACTER_INDICES[c];
         if (fontIndex < 0) return 0;
 
-        final int shift = italics ? ITALICS_SHIFT : 0;
-
         final int upper = this.charWidth[fontIndex] >> 4;
         final int lower = this.charWidth[fontIndex] & 0x0F;
         final int fUpper = upper;
@@ -62,13 +60,10 @@ public class FontDefault extends FontFile {
 
         final int u = (fontIndex % 16 * 16) + fUpper;
         final int v = (fontIndex & 0xff) / 16 * 16;
-        final int uWidth = fLower - fUpper;
-        final int vHeight = 16;
-
-        final double width = uWidth/2d;
-        final double height = vHeight/2d;
+        final int width = fLower - fUpper;
+        final int height = 16;
 
-        tessellator.addChar(x, y, width, height, shift, u, v, uWidth, vHeight);
+        tessellator.addChar(x, y, width, height, u, v);
 
         return (fLower - fUpper)/2d + 1;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java b/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
index a99be0772..88ff595ac 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
@@ -23,6 +23,15 @@ public class FontFile implements Font {
     static {
         DEFAULT_BLACKLIST.addAll(Arrays.asList('\uff0c', '\u3002', '\u3001', '\u3010', '\u3011', '\uff1b', '\u2019', '\u2018', '\u201c', '\u201d', '\uff1f', '\uff01', '\u00b7'));
     }
+    public static final int[] ALLOWED_CHARACTER_INDICES = new int[Character.MAX_VALUE];
+    static {
+        for (char c = 0; c < ALLOWED_CHARACTER_INDICES.length; c++) {
+            ALLOWED_CHARACTER_INDICES[c] = ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c);
+        }
+        for (var c : DEFAULT_BLACKLIST) {
+            ALLOWED_CHARACTER_INDICES[c] = -1;
+        }
+    }
 
     protected static final int ITALICS_SHIFT = 1;
     protected static final int GLYPHS_PER_TEXTURE = 256;
@@ -35,6 +44,7 @@ public class FontFile implements Font {
     protected final @NotNull String glyphSizesPath;
 
     protected final @NotNull String key;
+    protected final @NotNull String @NotNull [] textureLocationTable = new String[Character.MAX_VALUE / GLYPHS_PER_TEXTURE];
 
     public FontFile(@NotNull final String key, @NotNull final String texturesFormat, @NotNull final String glyphSizesPath) {
         this.key = key;
@@ -42,6 +52,10 @@ public class FontFile implements Font {
         this.glyphSizesPath = glyphSizesPath;
         this.charWidth = new byte[Character.MAX_VALUE + 1];
         this.texHandleCache = new int[256];
+
+        for (int i = 0; i < this.textureLocationTable.length; i++) {
+            this.textureLocationTable[i] = String.format(this.textureFormat, i);
+        }
     }
 
     @Override
@@ -64,7 +78,7 @@ public class FontFile implements Font {
     public int textureForChar(final char c) {
         final int index = c / GLYPHS_PER_TEXTURE;
         if (this.texHandleCache[index] != TEXTURE_UNASSIGNED) return this.texHandleCache[index];
-        final Texture texture = Minecraft.getMinecraft().textureManager.loadTextureNoDefault(String.format(this.textureFormat, index));
+        final Texture texture = Minecraft.getMinecraft().textureManager.loadTextureNoDefault(this.textureLocationTable[index]);
         if (texture == null) {
             this.texHandleCache[index] = 0;
             return 0;
@@ -110,9 +124,8 @@ public class FontFile implements Font {
     }
 
     @Override
-    public double renderChar(final TessellatorFont tessellator, final char c, final double x, final double y, final double z, final boolean italics) {
+    public double renderChar(final TessellatorFont tessellator, final char c, final double x, final double y, final double z) {
         if (c == ' ') return 4;
-        final int shift = italics ? ITALICS_SHIFT : 0;
 
         final int upper = this.charWidth[c] >> 4;
         final int lower = this.charWidth[c] & 0x0F;
@@ -122,13 +135,10 @@ public class FontFile implements Font {
 
         final int u = (c % 16 * 16) + fUpper;
         final int v = (c & 255) / 16 * 16;
-        final int uWidth = fLower - fUpper;
-        final int vHeight = 16;
-
-        final double width = uWidth/2d;
-        final double height = vHeight/2d;
+        final int width = fLower - fUpper;
+        final int height = 16;
 
-        tessellator.addChar(x, y, width, height, shift, u, v, uWidth, vHeight);
+        tessellator.addChar(x, y, width, height, u, v);
 
         return (fLower - fUpper)/2d + 1;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java b/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
index d7b56d405..086a3df02 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
@@ -2,7 +2,6 @@ package net.minecraft.client.render.font;
 
 import net.minecraft.client.render.tessellator.TessellatorFont;
 import net.minecraft.client.render.texturepack.TexturePackList;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.Arrays;
@@ -22,7 +21,7 @@ public class FontMonospace extends FontFile{
 
     @Override
     public boolean canRenderChar(final char c) {
-        return !DEFAULT_BLACKLIST.contains(c) && ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c) != -1 && (c == ' ' || textureForChar(c) != 0);
+        return ALLOWED_CHARACTER_INDICES[c] != -1 && (c == ' ' || textureForChar(c) != 0);
     }
 
     @Override
@@ -31,10 +30,9 @@ public class FontMonospace extends FontFile{
     }
 
     @Override
-    public double renderChar(final TessellatorFont tessellator, final char c, final double x, final double y, final double z, final boolean italics) {
+    public double renderChar(final TessellatorFont tessellator, final char c, final double x, final double y, final double z) {
         if (c != ' ') {
-            final int shift = italics ? ITALICS_SHIFT : 0;
-            final int fontIndex = ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c);
+            final int fontIndex = ALLOWED_CHARACTER_INDICES[c];
             if (fontIndex < 0) return 0;
 
             final int upper = this.charWidth[fontIndex] >> 4;
@@ -43,13 +41,10 @@ public class FontMonospace extends FontFile{
             final int fLower = (lower + 1);
             final int u = (fontIndex % 16 * 16) + fUpper;
             final int v = (fontIndex & 255) / 16 * 16;
-            final int uWidth = fLower - fUpper;
-            final int vHeight = 16;
+            final int width = fLower - fUpper;
+            final int height = 16;
 
-            final double width = uWidth/2d;
-            final double height = vHeight/2d;
-
-            tessellator.addChar(x, y, width, height, shift, u, v, uWidth, vHeight);
+            tessellator.addChar(x, y, width, height, u, v);
         }
         return MONOSPACE_WIDTH / 2d + 1;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java b/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
index 71e019730..2aaed2c6b 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
@@ -2,19 +2,12 @@ package net.minecraft.client.render.font;
 
 import net.minecraft.client.option.GameSettings;
 import net.minecraft.client.render.renderer.BlendFactor;
-import net.minecraft.client.render.renderer.DrawMode;
 import net.minecraft.client.render.renderer.GLRenderer;
 import net.minecraft.client.render.renderer.Shaders;
 import net.minecraft.client.render.renderer.State;
-import net.minecraft.client.render.tessellator.RenderBuffer;
-import net.minecraft.client.render.tessellator.Tessellator;
-import net.minecraft.client.render.tessellator.TessellatorBase;
-import net.minecraft.client.render.tessellator.TessellatorColor;
 import net.minecraft.client.render.tessellator.TessellatorFont;
 import net.minecraft.client.util.helper.Colors;
 import net.minecraft.core.net.command.TextFormatting;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
-import net.minecraft.core.util.helper.LightIndexHelper;
 import net.minecraft.core.util.helper.MathHelper;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -51,15 +44,13 @@ public class FontRendererDefault extends FontRenderer {
         this.random = new Random();
     }
 
-    protected TessellatorFont baseStringTess;
-    protected TessellatorDecorations backgroundStringTess;
+    protected TessellatorFont tessellatorFont;
     @Override
     public void init() {
-        this.baseStringTess = new TessellatorFont("FontRenderDefault Base Tessellator", 0x40000);
-        this.backgroundStringTess = new TessellatorDecorations("FontRenderDefault Deco Tessellator", 0x8000);
+        this.tessellatorFont = new TessellatorFont("FontRenderDefault Base Tessellator", 0x40000);
     }
 
-    private static final StringBuilder renderStringInternal_colorValue = new StringBuilder();
+    private final StringBuilder renderStringInternal_colorValue = new StringBuilder();
     protected void renderStringInternal(@NotNull final Font font, final @NotNull CharSequence chars, final double x, final double y, final double z, final boolean shadow, final long baseConfig, final double @Nullable [] xPosBuffer, final double @Nullable [] yPosBuffer, final long @Nullable [] configBuffer) {
         double currX = x;
 
@@ -71,7 +62,7 @@ public class FontRendererDefault extends FontRenderer {
                 final char cNext = chars.charAt(i + 1);
                 if (cNext == '<') {
                     int offAmount = 1;
-                    renderStringInternal_colorValue.setLength(0);
+                    this.renderStringInternal_colorValue.setLength(0);
                     boolean ended = false;
                     for (int j = 0; j < 8; j++) { // Maximum of 8 chars allowed in argb color code
                         offAmount++;
@@ -80,12 +71,12 @@ public class FontRendererDefault extends FontRenderer {
                             ended = true;
                             break;
                         }
-                        renderStringInternal_colorValue.append(pChar);
+                        this.renderStringInternal_colorValue.append(pChar);
                     }
 
                     if (ended){
                         long colorInt = 0xFFFFFF;
-                        try { colorInt = decodeHexToLong(renderStringInternal_colorValue); } catch (final NumberFormatException ignored){}
+                        try { colorInt = decodeHexToLong(this.renderStringInternal_colorValue); } catch (final NumberFormatException ignored){}
 
                         this.currentConfig = SF.setColor(this.currentConfig, (colorInt & 0xFF_00_00_00L) == 0 ? (int) (colorInt & 0x00_FF_FF_FFL) | (SF.getColor(baseConfig) & 0xFF_00_00_00) : ((int)colorInt));
                         //noinspection AssignmentToForLoopParameter
@@ -150,18 +141,8 @@ public class FontRendererDefault extends FontRenderer {
             }
             final Font charFont = font.canRenderChar(c) ? font : this.fallbackFont;
 
-            final int tex = charFont.textureForChar(c);
-            if (this.currentTex != tex) {
-                this.currentTex = tex;
-                Shaders.FONT.bind();
-                this.baseStringTess.draw(); // Render mesh on texture change
-                GL41.glBindTexture(GL41.GL_TEXTURE_2D, tex);
-                this.baseStringTess.startDrawing();
-            }
-
-            double off = renderCharInternal(charFont, this.baseStringTess, c, currX, y, z, SF.getColor(this.currentConfig), shadow);
-            renderCharInternalDecorations(charFont, this.backgroundStringTess, c, currX, y, z, SF.getColor(this.currentConfig), shadow);
-            currX += off;
+            this.tessellatorFont.setTexture(charFont.textureForChar(c));
+            currX += renderCharInternal(charFont, this.tessellatorFont, c, currX, y, z, shadow);
         }
 
         if (xPosBuffer != null) {
@@ -175,118 +156,29 @@ public class FontRendererDefault extends FontRenderer {
         }
     }
 
-    protected double renderCharInternal(@NotNull final Font font, @NotNull final TessellatorFont tessellator, final char c, final double x, final double y, final double z, final int color, boolean shadow) {
-        final int rgb = color & 0xFFFFFF;
-        final int shadowRGB = (color & 0xFCFCFC) >> 2; // Divide RGB by 4
-        final int a = (color & 0xFF_00_00_00) != 0 ? (color >> 24) & 0xFF : 0xFF;
-
-        shadow &= !SF.isOutlined(this.currentConfig);
+    public @NotNull TessellatorFont getTessellator() {
+        return this.tessellatorFont;
+    }
 
-        final double px = 1;
-        final double px2 = px * 2;
+    public void renderStringRaw(@NotNull final Font font, final @NotNull CharSequence chars, final double x, final double y, final double z, final boolean shadow, final long baseConfig) {
+        this.currentConfig = baseConfig;
+        renderStringInternal(font, chars, x, y, z, shadow, baseConfig, null, null, null);
+    }
 
+    protected double renderCharInternal(@NotNull final Font font, @NotNull final TessellatorFont tessellator, final char c, final double x, final double y, final double z, boolean shadow) {
+        tessellator.setShadow(shadow);
+        tessellator.setConfig(this.currentConfig);
         double shift;
-        final int fontIndex = ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c);
+        final int fontIndex = FontDefault.ALLOWED_CHARACTER_INDICES[c];
         if (SF.isObfuscated(this.currentConfig) && c != ' ' && fontIndex > 0) {
             shift = font.charWidth(c);
             final char randomChar = font.getObfuscatedChar(c, this.random);
-
-            if (shadow) {
-                tessellator.setColor2i(shadowRGB, a);
-                font.renderChar(tessellator, randomChar, x + px, y + px, z, SF.isItalic(this.currentConfig));
-                if (SF.isBold(this.currentConfig)) font.renderChar(tessellator, randomChar, x + px2, y + px, z, SF.isItalic(this.currentConfig));
-            }
-
-            if (SF.isOutlined(this.currentConfig)) {
-                tessellator.setColor2i((rgb & 0xFCFCFC) >> 2, a);
-                final double width = SF.isBold(this.currentConfig) ? 2.05 : 1.05;
-                for (double _x = -1; _x <= width; _x += 1/3d) {
-                    for (double _y = -1; _y <= 1.05; _y += 1/3d) {
-                        font.renderChar(tessellator, randomChar, x + _x, y + _y, z, SF.isItalic(this.currentConfig));
-                    }
-                }
-            }
-
-            tessellator.setColor2i(rgb, a);
-            font.renderChar(tessellator, randomChar, x, y, z, SF.isItalic(this.currentConfig));
-            if (SF.isBold(this.currentConfig)) {
-                font.renderChar(tessellator, randomChar, x + px, y, z, SF.isItalic(this.currentConfig));
-                shift += px;
-            }
+            font.renderChar(tessellator, randomChar, x, y, z);
 
         } else {
-            if (shadow) {
-                tessellator.setColor2i(shadowRGB, a);
-                font.renderChar(tessellator, c, x + px, y + px, z, SF.isItalic(this.currentConfig));
-                if (SF.isBold(this.currentConfig)) font.renderChar(tessellator, c, x + px2, y + px, z, SF.isItalic(this.currentConfig));
-            }
-
-            if (SF.isOutlined(this.currentConfig)) {
-                tessellator.setColor2i((rgb & 0xFCFCFC) >> 2, a);
-                final double width = SF.isBold(this.currentConfig) ? 2.05 : 1.05;
-                for (double _x = -1; _x <= width; _x += 1/3d) {
-                    for (double _y = -1; _y <= 1.05; _y += 1/3d) {
-                        font.renderChar(tessellator, c, x + _x, y + _y, z, SF.isItalic(this.currentConfig));
-                    }
-                }
-            }
-
-            tessellator.setColor2i(rgb, a);
-            shift = font.renderChar(tessellator, c, x, y, z, SF.isItalic(this.currentConfig));
-            if (SF.isBold(this.currentConfig)) {
-                font.renderChar(tessellator, c, x + px, y, z, SF.isItalic(this.currentConfig));
-                shift += px;
-            }
-        }
-        return shift;
-    }
-
-    protected void renderCharInternalDecorations(@NotNull final Font font, final @NotNull TessellatorDecorations tessellator, final char c, final double x, final double y, final double z, final int color, boolean shadow) {
-        final int rgb = color & 0xFFFFFF;
-        final int shadowRGB = (color & 0xFCFCFC) >> 2; // Divide RGB by 4
-        final int a = (color & 0xFF_00_00_00) != 0 ? (color >> 24) & 0xFF : 0xFF;
-
-        boolean strikethrough = SF.isStrikethrough(this.currentConfig);
-        boolean underline = SF.isUnderline(this.currentConfig);
-        if (!strikethrough && !underline) return;
-
-        shadow &= !SF.isOutlined(this.currentConfig);
-
-        final double px = 1;
-        final double px2 = px * 2;
-
-        final int fontHeight = font.fontHeight();
-        final double shift = SF.isBold(this.currentConfig) ? font.charWidth(c) + px : font.charWidth(c);
-
-        if (strikethrough) {
-            if (shadow) {
-                tessellator.setColor2i(shadowRGB, a);
-                tessellator.addVertex(x + px,        y + px + fontHeight / 2d,     0.0D);
-                tessellator.addVertex(x + px + shift,y + px + fontHeight / 2d,     0.0D);
-                tessellator.addVertex(x + px + shift,y + px + fontHeight / 2d - px,0.0D);
-                tessellator.addVertex(x + px,        y + px + fontHeight / 2d - px,0.0D);
-            }
-            tessellator.setColor2i(rgb, a);
-            tessellator.addVertex(x,        y + fontHeight / 2d,     0.0D);
-            tessellator.addVertex(x + shift,y + fontHeight / 2d,     0.0D);
-            tessellator.addVertex(x + shift,y + fontHeight / 2d - px,0.0D);
-            tessellator.addVertex(x,        y + fontHeight / 2d - px,0.0D);
-        }
-
-        if (underline) {
-            if (shadow) {
-                tessellator.setColor2i(shadowRGB, a);
-                tessellator.addVertex(x,             y + px2 + fontHeight,      0.0D);
-                tessellator.addVertex(x + px + shift,y + px2 + fontHeight,      0.0D);
-                tessellator.addVertex(x + px + shift,y + px2 + fontHeight - px, 0.0D);
-                tessellator.addVertex(x,             y + px2 + fontHeight - px, 0.0D);
-            }
-            tessellator.setColor2i(rgb, a);
-            tessellator.addVertex(x - px,    y + px + fontHeight,      0.0D);
-            tessellator.addVertex(x + shift, y + px + fontHeight,      0.0D);
-            tessellator.addVertex(x + shift, y + px + fontHeight - px, 0.0D);
-            tessellator.addVertex(x - px,    y + px + fontHeight - px, 0.0D);
+            shift = font.renderChar(tessellator, c, x, y, z);
         }
+        return shift + (SF.isBold(this.currentConfig) ? 1 : 0);
     }
 
     @Override
@@ -300,17 +192,13 @@ public class FontRendererDefault extends FontRenderer {
         GLRenderer.globalSetUniforms(Shaders.FONT);
         Shaders.FONT.bind();
         GL41.glUniform1f(Shaders.FONT.getUniform("uZ"), (float) z);
-        GLRenderer.globalSetUniforms(Shaders.COLOR);
 
-        this.baseStringTess.startDrawing();
-        this.backgroundStringTess.startDrawingQuads();
+        this.tessellatorFont.startDrawing();
 
         renderStringInternal(font, chars, x, y, z, shadow, baseConfig, xPosBuffer, yPosBuffer, configBuffer);
 
         Shaders.FONT.bind();
-        this.baseStringTess.draw();
-        Shaders.COLOR.bind();
-        this.backgroundStringTess.draw();
+        this.tessellatorFont.draw();
 
         GLRenderer.popFrame();
     }
@@ -344,10 +232,7 @@ public class FontRendererDefault extends FontRenderer {
         GLRenderer.globalSetUniforms(Shaders.FONT);
         Shaders.FONT.bind();
         GL41.glUniform1f(Shaders.FONT.getUniform("uZ"), (float) z);
-        GLRenderer.globalSetUniforms(Shaders.COLOR);
-
-        this.baseStringTess.startDrawing();
-        this.backgroundStringTess.startDrawingQuads();
+        this.tessellatorFont.startDrawing();
 
         double currY = y;
         switch (alignment) {
@@ -377,9 +262,7 @@ public class FontRendererDefault extends FontRenderer {
             }
         }
         Shaders.FONT.bind();
-        this.baseStringTess.draw();
-        Shaders.COLOR.bind();
-        this.backgroundStringTess.draw();
+        this.tessellatorFont.draw();
         GLRenderer.popFrame();
 
         if (xPosBuffer != null) {
@@ -407,10 +290,8 @@ public class FontRendererDefault extends FontRenderer {
         GLRenderer.globalSetUniforms(Shaders.FONT);
         Shaders.FONT.bind();
         GL41.glUniform1f(Shaders.FONT.getUniform("uZ"), (float) z);
-        GLRenderer.globalSetUniforms(Shaders.COLOR);
 
-        this.baseStringTess.startDrawing();
-        this.backgroundStringTess.startDrawingQuads();
+        this.tessellatorFont.startDrawing();
 
         int currY = y;
         switch (alignment) {
@@ -440,9 +321,7 @@ public class FontRendererDefault extends FontRenderer {
             }
         }
         Shaders.FONT.bind();
-        this.baseStringTess.draw();
-        Shaders.COLOR.bind();
-        this.backgroundStringTess.draw();
+        this.tessellatorFont.draw();
         GLRenderer.popFrame();
 
         if (xPosBuffer != null) {
@@ -474,9 +353,7 @@ public class FontRendererDefault extends FontRenderer {
                 GLRenderer.globalSetUniforms(Shaders.FONT);
                 Shaders.FONT.bind();
                 GL41.glUniform1f(Shaders.FONT.getUniform("uZ"), (float) z);
-                GLRenderer.globalSetUniforms(Shaders.COLOR);
-                this.baseStringTess.startDrawing();
-                this.backgroundStringTess.startDrawingQuads();
+                this.tessellatorFont.startDrawing();
                 for (int i = 0; i < lines.size(); i++) {
                     final String line = lines.get(i);
                     renderStringInternal(font, line, x, currY, z, shadow, baseConfig, null, null, this.localConfigBuffer);
@@ -484,9 +361,7 @@ public class FontRendererDefault extends FontRenderer {
                     if ((currY - y) > maxHeight) break;
                 }
                 Shaders.FONT.bind();
-                this.baseStringTess.draw();
-                Shaders.COLOR.bind();
-                this.backgroundStringTess.draw();
+                this.tessellatorFont.draw();
                 GLRenderer.popFrame();
                 break;
             }
@@ -497,9 +372,7 @@ public class FontRendererDefault extends FontRenderer {
                 GLRenderer.globalSetUniforms(Shaders.FONT);
                 Shaders.FONT.bind();
                 GL41.glUniform1f(Shaders.FONT.getUniform("uZ"), (float) z);
-                GLRenderer.globalSetUniforms(Shaders.COLOR);
-                this.baseStringTess.startDrawing();
-                this.backgroundStringTess.startDrawingQuads();
+                this.tessellatorFont.startDrawing();
                 for (int i = 0; i < lines.size(); i++) {
                     final String line = lines.get(i);
                     renderStringInternal(font, line, x + (maxWidth - stringWidthDouble(line)), currY, z, shadow, baseConfig, null, null, this.localConfigBuffer);
@@ -507,9 +380,7 @@ public class FontRendererDefault extends FontRenderer {
                     if ((currY - y) > maxHeight) break;
                 }
                 Shaders.FONT.bind();
-                this.baseStringTess.draw();
-                Shaders.COLOR.bind();
-                this.backgroundStringTess.draw();
+                this.tessellatorFont.draw();
                 GLRenderer.popFrame();
                 break;
             }
@@ -540,8 +411,8 @@ public class FontRendererDefault extends FontRenderer {
         renderConstrainedDoubleImpl(font, chars, x, y, z, shadow, baseConfig, maxWidth, maxHeight, alignment, xPosBuffer, yPosBuffer, configBuffer);
     }
 
-    private static final StringBuilder splitLineIntoConstrainedLine_lineBuilder = new StringBuilder();
-    private static final StringBuilder splitLineIntoConstrainedLine_wordBuilder = new StringBuilder();
+    private final StringBuilder splitLineIntoConstrainedLine_lineBuilder = new StringBuilder();
+    private final StringBuilder splitLineIntoConstrainedLine_wordBuilder = new StringBuilder();
     protected void splitLineIntoConstrainedLine(final @NotNull Font font, final @NotNull CharSequence line, final double maxWidth, final @NotNull List<String> output) {
         final double dashWidth = font.charWidthDouble('-');
         final double spaceWidth = font.charWidthDouble(' ');
@@ -549,26 +420,26 @@ public class FontRendererDefault extends FontRenderer {
         double currLineWidth = 0;
         double currWordWidth = 0;
 
-        splitLineIntoConstrainedLine_lineBuilder.setLength(0);
-        splitLineIntoConstrainedLine_wordBuilder.setLength(0);
+        this.splitLineIntoConstrainedLine_lineBuilder.setLength(0);
+        this.splitLineIntoConstrainedLine_wordBuilder.setLength(0);
 
         final int length = line.length();
         for (int i = 0; i < length; i++) {
             final char c = line.charAt(i);
 
             if (c == '\n') {
-                splitLineIntoConstrainedLine_lineBuilder.append(splitLineIntoConstrainedLine_wordBuilder);
-                splitLineIntoConstrainedLine_wordBuilder.setLength(0);
+                this.splitLineIntoConstrainedLine_lineBuilder.append(this.splitLineIntoConstrainedLine_wordBuilder);
+                this.splitLineIntoConstrainedLine_wordBuilder.setLength(0);
                 currWordWidth = 0;
 
-                output.add(splitLineIntoConstrainedLine_lineBuilder.toString());
-                splitLineIntoConstrainedLine_lineBuilder.setLength(0);
+                output.add(this.splitLineIntoConstrainedLine_lineBuilder.toString());
+                this.splitLineIntoConstrainedLine_lineBuilder.setLength(0);
                 currLineWidth = 0;
                 continue;
             }
 
             if (c == '\t') {
-                splitLineIntoConstrainedLine_wordBuilder.append("   ");
+                this.splitLineIntoConstrainedLine_wordBuilder.append("   ");
                 currWordWidth += spaceWidth * 3;
                 continue;
             }
@@ -588,14 +459,14 @@ public class FontRendererDefault extends FontRenderer {
 
                     if (ended){
                         // Add all entire rgb format symbols to word and skip index to next char after
-                        splitLineIntoConstrainedLine_wordBuilder.append(line, i, i + offAmount + 1);
+                        this.splitLineIntoConstrainedLine_wordBuilder.append(line, i, i + offAmount + 1);
                         //noinspection AssignmentToForLoopParameter
                         i += offAmount;
                         continue;
                     }
                 } else if (TextFormatting.FORMAT_CHARS.indexOf(cN) != -1) {
                     // Add format symbols then skip to index after
-                    splitLineIntoConstrainedLine_wordBuilder.append(line, i, i + 2);
+                    this.splitLineIntoConstrainedLine_wordBuilder.append(line, i, i + 2);
                     //noinspection AssignmentToForLoopParameter
                     i++;
                     continue;
@@ -604,15 +475,15 @@ public class FontRendererDefault extends FontRenderer {
 
             final double charWidth = font.canRenderChar(c) ? font.charWidthDouble(c) : this.fallbackFont.charWidthDouble(c);
             if (currWordWidth + charWidth + dashWidth > maxWidth) {
-                splitLineIntoConstrainedLine_wordBuilder.append('-');
-                splitLineIntoConstrainedLine_lineBuilder.append(splitLineIntoConstrainedLine_wordBuilder);
+                this.splitLineIntoConstrainedLine_wordBuilder.append('-');
+                this.splitLineIntoConstrainedLine_lineBuilder.append(this.splitLineIntoConstrainedLine_wordBuilder);
 
-                splitLineIntoConstrainedLine_wordBuilder.setLength(0);
-                splitLineIntoConstrainedLine_wordBuilder.append(c);
+                this.splitLineIntoConstrainedLine_wordBuilder.setLength(0);
+                this.splitLineIntoConstrainedLine_wordBuilder.append(c);
                 currWordWidth = charWidth;
 
-                output.add(splitLineIntoConstrainedLine_lineBuilder.toString().trim());
-                splitLineIntoConstrainedLine_lineBuilder.setLength(0);
+                output.add(this.splitLineIntoConstrainedLine_lineBuilder.toString().trim());
+                this.splitLineIntoConstrainedLine_lineBuilder.setLength(0);
                 currLineWidth = 0;
                 continue;
             }
@@ -621,29 +492,29 @@ public class FontRendererDefault extends FontRenderer {
                 /*if (c == ' ') {
                     splitLineIntoConstrainedLine_lineBuilder.append(splitLineIntoConstrainedLine_wordBuilder);
                 } else */{
-                    splitLineIntoConstrainedLine_wordBuilder.append(c);
+                    this.splitLineIntoConstrainedLine_wordBuilder.append(c);
                     currWordWidth += charWidth;
                 }
-                output.add(splitLineIntoConstrainedLine_lineBuilder.toString().trim());
-                splitLineIntoConstrainedLine_lineBuilder.setLength(0);
+                output.add(this.splitLineIntoConstrainedLine_lineBuilder.toString().trim());
+                this.splitLineIntoConstrainedLine_lineBuilder.setLength(0);
                 currLineWidth = 0;
                 continue;
             }
 
             if (c == ' ') {
-                splitLineIntoConstrainedLine_lineBuilder.append(splitLineIntoConstrainedLine_wordBuilder).append(' ');
+                this.splitLineIntoConstrainedLine_lineBuilder.append(this.splitLineIntoConstrainedLine_wordBuilder).append(' ');
                 currLineWidth += currWordWidth + spaceWidth;
 
-                splitLineIntoConstrainedLine_wordBuilder.setLength(0);
+                this.splitLineIntoConstrainedLine_wordBuilder.setLength(0);
                 currWordWidth = 0;
                 continue;
             }
 
-            splitLineIntoConstrainedLine_wordBuilder.append(c);
+            this.splitLineIntoConstrainedLine_wordBuilder.append(c);
             currWordWidth += charWidth;
         }
-        splitLineIntoConstrainedLine_lineBuilder.append(splitLineIntoConstrainedLine_wordBuilder);
-        final String remainder = splitLineIntoConstrainedLine_lineBuilder.toString().trim();
+        this.splitLineIntoConstrainedLine_lineBuilder.append(this.splitLineIntoConstrainedLine_wordBuilder);
+        final String remainder = this.splitLineIntoConstrainedLine_lineBuilder.toString().trim();
         if (!remainder.isEmpty()) {
             output.add(remainder);
         }
@@ -779,130 +650,4 @@ public class FontRendererDefault extends FontRenderer {
         return value;
     }
 
-    protected static class TessellatorDecorations extends TessellatorBase implements TessellatorColor {
-        private static final int VERT_SIZE = Float.BYTES * 3 + 4;
-        private boolean lockColor = false;
-
-        private int color;
-
-        private final int vao;
-        private final int vbo;
-
-        private int quadVertCount = 0;
-
-        public TessellatorDecorations(@NotNull String name, int initialCapacity) {
-            super(name, initialCapacity);
-            this.vao = GL41.glGenVertexArrays();
-            this.vbo = GL41.glGenBuffers();
-
-            GL41.glBindVertexArray(this.vao);
-            GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, this.vbo);
-
-            GL41.glVertexAttribPointer(0, 3, GL41.GL_FLOAT, false, VERT_SIZE, 0);
-            GL41.glVertexAttribPointer(1, 4, GL41.GL_UNSIGNED_BYTE, true, VERT_SIZE, Float.BYTES * 3);
-            GL41.glVertexAttrib2f(2, 0f, 0f);
-            GL41.glVertexAttrib1f(3, LightIndexHelper.lightIndex2i(15, 15));
-            GL41.glVertexAttrib3f(4, 0, -1f, 0);
-
-            GL41.glEnableVertexAttribArray(0);
-            GL41.glEnableVertexAttribArray(1);
-            GL41.glBindVertexArray(0);
-        }
-
-        @Override
-        public void startDrawing(@NotNull DrawMode drawMode) {
-            super.startDrawing(drawMode);
-
-            this.lockColor = false;
-            this.quadVertCount = 0;
-            if (Tessellator.QUAD_EMULATION && drawMode == DrawMode.QUADS) {
-                this.drawMode = DrawMode.TRIANGLES;
-            }
-            setColor1i(0xFFFFFFFF);
-        }
-
-        @Override
-        public void addVertex(double x, double y, double z) {
-            checkIsDrawing();
-            checkCanFit(64);
-
-            this.data.putFloat((float) (this.offsetX + x));
-            this.data.putFloat((float) (this.offsetY + y));
-            this.data.putFloat((float) (this.offsetZ + z));
-
-            this.data.putInt(this.color);
-            this.vertexCount++;
-
-            if (Tessellator.QUAD_EMULATION && this.quadVertCount % 4 == 2) {
-                checkCanFit(128);
-                this.data.put(this.data.position(), this.data, this.data.position() - VERT_SIZE * 3, VERT_SIZE);
-                this.data.put(this.data.position() + VERT_SIZE, this.data, this.data.position() - VERT_SIZE, VERT_SIZE);
-                this.data.position(this.data.position() + VERT_SIZE * 2);
-                this.vertexCount += 2;
-            }
-
-            this.quadVertCount++;
-        }
-
-        @Override
-        public void draw() {
-            if(!this.drawing) {
-                throw new IllegalStateException(this.name + " already drawing!");
-            }
-
-            this.drawing = false;
-
-            if (this.vertexCount == 0) return;
-
-            GL41.glBindVertexArray(this.vao);
-            GL41.glVertexAttrib1f(3, GLRenderer.getLightIndex());
-
-            GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, this.vbo);
-            GL41.glBufferData(GL41.GL_ARRAY_BUFFER, this.data.flip(), GL41.GL_STREAM_DRAW);
-
-            GL41.glDrawArrays(this.drawMode.cap, 0, this.vertexCount);
-            GL41.glBindVertexArray(0);
-        }
-
-        @Override
-        public @NotNull RenderBuffer record(int vao, int vbo) {
-            if(!this.drawing) {
-                throw new IllegalStateException(this.name + " already drawing!");
-            }
-
-            this.drawing = false;
-
-            GL41.glBindVertexArray(this.vao);
-            GL41.glVertexAttrib1f(3, GLRenderer.getLightIndex());
-
-            GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, this.vbo);
-            GL41.glBufferData(GL41.GL_ARRAY_BUFFER, this.data.flip(), GL41.GL_STATIC_DRAW);
-
-            GL41.glDrawArrays(this.drawMode.cap, 0, this.vertexCount);
-            GL41.glBindVertexArray(0);
-
-            return new RenderBuffer(vao, vbo, 0, this.vertexCount, this.drawMode);
-        }
-
-        @Override
-        public void setColor1i(int argb) {
-            checkIsDrawing();
-            if (this.lockColor) {
-                return;
-            }
-
-            int a = (argb >> 24) & 0xFF;
-            int r = (argb >> 16) & 0xFF;
-            int g = (argb >> 8) & 0xFF;
-            int b = argb & 0xFF;
-
-            this.color = a << 24 | b << 16 | g << 8 | r;
-        }
-
-        @Override
-        public void lockColor() {
-            checkIsDrawing();
-            this.lockColor = true;
-        }
-    }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorFont.java b/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorFont.java
index a8ca6f9b0..f2ca1c8a2 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorFont.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorFont.java
@@ -1,8 +1,10 @@
 package net.minecraft.client.render.tessellator;
 
 import com.mojang.logging.LogUtils;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
 import net.minecraft.client.render.renderer.DrawMode;
 import net.minecraft.client.render.renderer.GLRenderer;
+import net.minecraft.client.render.renderer.Shaders;
 import net.minecraft.core.util.helper.LightIndexHelper;
 import org.jetbrains.annotations.NotNull;
 import org.lwjgl.BufferUtils;
@@ -15,13 +17,18 @@ public class TessellatorFont {
     private static final Logger LOGGER = LogUtils.getLogger();
     private boolean drawing = false;
 
-    private int color;
+    private int texture = -1;
+    private long config;
+    private boolean shadow;
+
+    private final IntArrayList renderRanges = new IntArrayList(16 * 3);
 
     private final int vao;
     private final int vbo;
 
     private final @NotNull String name;
     private ByteBuffer data;
+    private int start;
     private int vertexCount;
 
     public TessellatorFont(@NotNull String name, int initialCapacity) {
@@ -34,22 +41,20 @@ public class TessellatorFont {
         GL41.glBindVertexArray(this.vao);
         GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, this.vbo);
 
-        int vertSize = 4 * Float.BYTES + 9;
+        int vertSize = 2 * Float.BYTES + 9;
         int offset = 0;
-        GL41.glVertexAttribPointer(0, 2, GL41.GL_FLOAT, false,          vertSize, offset); offset += Float.BYTES * 2; // Pos XY
-        GL41.glVertexAttribPointer(1, 2, GL41.GL_FLOAT, false,          vertSize, offset); offset += Float.BYTES * 2; // Width Height
-        GL41.glVertexAttribPointer(2, 1, GL41.GL_BYTE, false,          vertSize, offset); offset += 1; // Shift
-        GL41.glVertexAttribPointer(3, 4, GL41.GL_UNSIGNED_BYTE, true,   vertSize, offset); offset += 4;               // Color
-        GL41.glVertexAttribPointer(4, 2, GL41.GL_UNSIGNED_BYTE, false,   vertSize, offset); offset += 2; // UV
-        GL41.glVertexAttribPointer(5, 2, GL41.GL_UNSIGNED_BYTE, false,   vertSize, offset); offset += 2; // UV Sizes
-        GL41.glVertexAttrib1f(6, LightIndexHelper.lightIndex2i(15, 15));
+        GL41.glVertexAttribPointer(0, 2, GL41.GL_FLOAT, false, vertSize, offset); offset += Float.BYTES * 2; // Pos XY
+        GL41.glVertexAttribPointer(1, 2, GL41.GL_UNSIGNED_BYTE, false, vertSize, offset); offset += 2; // Width Height
+        GL41.glVertexAttribPointer(2, 4, GL41.GL_UNSIGNED_BYTE, true, vertSize, offset); offset += 4; // Color
+        GL41.glVertexAttribPointer(3, 2, GL41.GL_UNSIGNED_BYTE, false, vertSize, offset); offset += 2; // UV
+        GL41.glVertexAttribPointer(4, 1, GL41.GL_UNSIGNED_BYTE, false, vertSize, offset); offset += 1; // Config
+        GL41.glVertexAttrib1f(5, LightIndexHelper.lightIndex2i(15, 15));
 
         GL41.glEnableVertexAttribArray(0);
         GL41.glEnableVertexAttribArray(1);
         GL41.glEnableVertexAttribArray(2);
         GL41.glEnableVertexAttribArray(3);
         GL41.glEnableVertexAttribArray(4);
-        GL41.glEnableVertexAttribArray(5);
         GL41.glBindVertexArray(0);
     }
 
@@ -60,31 +65,42 @@ public class TessellatorFont {
         }
 
         this.drawing = true;
+        this.start = 0;
         this.vertexCount = 0;
+        this.texture = -1;
+        this.renderRanges.clear();
         this.data.clear();
 
-        setColor1i(0xFFFFFFFF);
+        this.setConfig(0xFFFFFFFFL);
     }
 
 
-    public void addChar(double x, double y, double width, double height, double shift, double u, double v, double uSize, double vSize) {
+    public void addChar(double x, double y, int width, int height, int u, int v) {
         checkIsDrawing();
         checkCanFit(64);
 
         this.data.putFloat((float) x);
         this.data.putFloat((float) y);
 
-        this.data.putFloat((float) width);
-        this.data.putFloat((float) height);
+        this.data.put((byte) width);
+        this.data.put((byte) height);
 
-        this.data.put((byte) shift);
 
-        this.data.putInt(this.color);
+        long color = (this.config & 0xFFFFFFFFL);
+        byte a = (byte) ((color & 0xFF000000L) >> 24);
+        byte r = (byte) ((color & 0xFF0000L) >> 16);
+        byte g = (byte) ((color & 0xFF00L) >> 8);
+        byte b = (byte) ((color & 0xFFL));
+        if (a == 0) {
+            a = (byte) 0xFF;
+        }
+        this.data.put(r).put(g).put(b).put(a);
 
         this.data.put((byte) u);
         this.data.put((byte) v);
-        this.data.put((byte) uSize);
-        this.data.put((byte) vSize);
+
+        byte packedConfig = (byte) ((this.config >> 32) | (this.shadow ? 0b1000_0000 : 0));
+        this.data.put(packedConfig);
 
         this.vertexCount++;
     }
@@ -95,33 +111,91 @@ public class TessellatorFont {
         }
         this.drawing = false;
 
-        if (this.vertexCount == 0) return;
-
         GL41.glBindVertexArray(this.vao);
         GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, this.vbo);
 
-        GL41.glVertexAttrib1f(6, GLRenderer.getLightIndex());
+        GL41.glVertexAttrib1f(5, GLRenderer.getLightIndex());
         GL41.glBufferData(GL41.GL_ARRAY_BUFFER, this.data.flip(), GL41.GL_STREAM_DRAW);
 
-        GL41.glDrawArrays(DrawMode.POINTS.cap, 0, this.vertexCount);
+        for (int i = 0, size = this.renderRanges.size(); i < size; i += 3) {
+            int texture = this.renderRanges.getInt(i);
+            int start = this.renderRanges.getInt(i + 1);
+            int count = this.renderRanges.getInt(i + 2);
+            GL41.glBindTexture(GL41.GL_TEXTURE_2D, texture);
+            GL41.glDrawArrays(DrawMode.POINTS.cap, start, count);
+        }
+
+        if (this.texture != -1) {
+            GL41.glBindTexture(GL41.GL_TEXTURE_2D, this.texture);
+            GL41.glDrawArrays(DrawMode.POINTS.cap, this.start, this.vertexCount);
+        }
+
         GL41.glBindVertexArray(0);
     }
 
-    public void setColor1i(int argb) {
-        checkIsDrawing();
+    public @NotNull FontRenderBuffer record(int vao, int vbo) {
+        if(!this.drawing) {
+            throw new IllegalStateException(this.name + " not drawing!");
+        }
+        this.drawing = false;
+
+        GL41.glBindVertexArray(vao);
+        GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, vbo);
+
+        int vertSize = 2 * Float.BYTES + 9;
+        int offset = 0;
+        GL41.glVertexAttribPointer(0, 2, GL41.GL_FLOAT, false, vertSize, offset); offset += Float.BYTES * 2; // Pos XY
+        GL41.glVertexAttribPointer(1, 2, GL41.GL_UNSIGNED_BYTE, false, vertSize, offset); offset += 2; // Width Height
+        GL41.glVertexAttribPointer(2, 4, GL41.GL_UNSIGNED_BYTE, true, vertSize, offset); offset += 4; // Color
+        GL41.glVertexAttribPointer(3, 2, GL41.GL_UNSIGNED_BYTE, false, vertSize, offset); offset += 2; // UV
+        GL41.glVertexAttribPointer(4, 1, GL41.GL_UNSIGNED_BYTE, false, vertSize, offset); offset += 1; // Config
+        GL41.glVertexAttrib1f(5, LightIndexHelper.lightIndex2i(15, 15));
+
+        GL41.glEnableVertexAttribArray(0);
+        GL41.glEnableVertexAttribArray(1);
+        GL41.glEnableVertexAttribArray(2);
+        GL41.glEnableVertexAttribArray(3);
+        GL41.glEnableVertexAttribArray(4);
 
-        int a = (argb >> 24) & 0xFF;
-        int r = (argb >> 16) & 0xFF;
-        int g = (argb >> 8) & 0xFF;
-        int b = argb & 0xFF;
+        GL41.glBufferData(GL41.GL_ARRAY_BUFFER, this.data.flip(), GL41.GL_STREAM_DRAW);
+        GL41.glBindVertexArray(0);
+
+        boolean lastValid = this.texture != -1;
+        int[] metadata = new int[this.renderRanges.size() + (lastValid ? 3 : 0)];
+        for (int i = 0, size = this.renderRanges.size(); i < size; i++) {
+            metadata[i] = this.renderRanges.getInt(i);
+        }
+
+        if (lastValid) {
+            metadata[metadata.length - 3] = this.texture;
+            metadata[metadata.length - 2] = this.start;
+            metadata[metadata.length - 1] = this.vertexCount;
+        }
 
-        this.color = a << 24 | b << 16 | g << 8 | r;
+        return new FontRenderBuffer(vao, vbo, metadata);
     }
 
-    public void setColor2i(int rgb, int a) {
-        setColor1i((rgb & 0xFFFFFF) | (a << 24) & 0xFF000000);
+    public void setShadow(boolean shadow) {
+        checkIsDrawing();
+        this.shadow = shadow;
     }
 
+    public void setConfig(long config) {
+        checkIsDrawing();
+        this.config = config;
+    }
+
+    public void setTexture(int texture) {
+        checkIsDrawing();
+        if (this.texture != texture && this.texture != -1) {
+            this.renderRanges.add(this.texture);
+            this.renderRanges.add(this.start);
+            this.renderRanges.add(this.vertexCount);
+            this.start += this.vertexCount;
+            this.vertexCount = 0;
+        }
+        this.texture = texture;
+    }
 
     public void checkIsDrawing() {
         if (!this.drawing) {
@@ -141,4 +215,32 @@ public class TessellatorFont {
             this.data = newBuffer;
         }
     }
+
+    public record FontRenderBuffer(int vao, int vbo, int[] metadata) {
+        public void delete() {
+            GL41.glDeleteVertexArrays(this.vao);
+            GL41.glDeleteBuffers(this.vbo);
+        }
+
+        public void render() {
+            GLRenderer.globalSetUniforms(Shaders.FONT);
+            Shaders.FONT.bind();
+            GL41.glUniform1f(Shaders.FONT.getUniform("uZ"), 0);
+
+            GL41.glBindVertexArray(this.vao);
+            GL41.glBindBuffer(GL41.GL_ARRAY_BUFFER, this.vbo);
+            GL41.glVertexAttrib1f(5, GLRenderer.getLightIndex());
+
+            for (int i = 0, size = this.metadata.length; i < size; i += 3) {
+                int texture = this.metadata[i];
+                int start = this.metadata[i + 1];
+                int count = this.metadata[i + 2];
+                GL41.glBindTexture(GL41.GL_TEXTURE_2D, texture);
+                GL41.glDrawArrays(DrawMode.POINTS.cap, start, count);
+            }
+
+
+            GL41.glBindVertexArray(0);
+        }
+    }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorTerrainImpl.java b/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorTerrainImpl.java
index 2c72be94e..d898f2e42 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorTerrainImpl.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tessellator/TessellatorTerrainImpl.java
@@ -11,8 +11,8 @@ import org.lwjgl.opengl.GL41;
 import java.nio.ByteBuffer;
 
 public class TessellatorTerrainImpl implements TessellatorGeneral {
-    private final float[] quadPoses = new float[3 * 4];
-    private final float[] quadUVs = new float[2 * 4];
+    private final double[] quadPoses = new double[3 * 4];
+    private final double[] quadUVs = new double[2 * 4];
     private final byte[] quadNormals = new byte[3 * 4];
     private final byte[] quadLightMap = new byte[4];
     private final byte[] quadShade = new byte[4];
@@ -80,9 +80,9 @@ public class TessellatorTerrainImpl implements TessellatorGeneral {
         this.checkIsDrawing();
 
         int index = this.quadVertCount % 4;
-        this.quadPoses[index * 3] = (float) (this.offsetX + x);
-        this.quadPoses[index * 3 + 1] = (float) (this.offsetY + y);
-        this.quadPoses[index * 3 + 2] = (float) (this.offsetZ + z);
+        this.quadPoses[index * 3] = this.offsetX + x;
+        this.quadPoses[index * 3 + 1] = this.offsetY + y;
+        this.quadPoses[index * 3 + 2] = this.offsetZ + z;
 
         this.quadColors[index] = this.color;
 
@@ -102,9 +102,9 @@ public class TessellatorTerrainImpl implements TessellatorGeneral {
             this.data.resizeIfNeeded(128);
             ByteBuffer buffer = this.data.getBuffer();
 
-            float spX = this.quadPoses[0];
-            float spY = this.quadPoses[1];
-            float spZ = this.quadPoses[2];
+            double spX = this.quadPoses[0];
+            double spY = this.quadPoses[1];
+            double spZ = this.quadPoses[2];
 
             // Average normals for quad
             byte nX = (byte) ((this.quadNormals[0] + this.quadNormals[3] + this.quadNormals[6] + this.quadNormals[9])/4);
@@ -113,22 +113,22 @@ public class TessellatorTerrainImpl implements TessellatorGeneral {
 
             int color = this.quadColors[0];
 
-            buffer.putFloat(spX).putFloat(spY).putFloat(spZ);
+            buffer.putFloat((float) spX).putFloat((float) spY).putFloat((float) spZ);
             buffer.put((byte) Color.blueFromInt(color)).put((byte) Color.greenFromInt(color)).put((byte) Color.redFromInt(color));
             buffer.put(nX).put(nY).put(nZ);
 
             buffer.putShort((short) (this.quadUVs[0] * 0xFFFF)).putShort((short) (this.quadUVs[1] * 0xFFFF));
             buffer.put(this.quadShade[0]).put(this.quadLightMap[0]);
 
-            buffer.putShort(MathHelper.toHalfFloat(this.quadPoses[3] - spX)).putShort(MathHelper.toHalfFloat(this.quadPoses[4] - spY)).putShort(MathHelper.toHalfFloat(this.quadPoses[5] - spZ));
+            buffer.putShort(MathHelper.toHalfFloat((float) (this.quadPoses[3] - spX))).putShort(MathHelper.toHalfFloat((float) (this.quadPoses[4] - spY))).putShort(MathHelper.toHalfFloat((float) (this.quadPoses[5] - spZ)));
             buffer.putShort((short) (this.quadUVs[2] * 0xFFFF)).putShort((short) (this.quadUVs[3] * 0xFFFF));
             buffer.put(this.quadShade[1]).put(this.quadLightMap[1]);
 
-            buffer.putShort(MathHelper.toHalfFloat(this.quadPoses[6] - spX)).putShort(MathHelper.toHalfFloat(this.quadPoses[7] - spY)).putShort(MathHelper.toHalfFloat(this.quadPoses[8] - spZ));
+            buffer.putShort(MathHelper.toHalfFloat((float) (this.quadPoses[6] - spX))).putShort(MathHelper.toHalfFloat((float) (this.quadPoses[7] - spY))).putShort(MathHelper.toHalfFloat((float) (this.quadPoses[8] - spZ)));
             buffer.putShort((short) (this.quadUVs[4] * 0xFFFF)).putShort((short) (this.quadUVs[5] * 0xFFFF));
             buffer.put(this.quadShade[2]).put(this.quadLightMap[2]);
 
-            buffer.putShort(MathHelper.toHalfFloat(this.quadPoses[9] - spX)).putShort(MathHelper.toHalfFloat(this.quadPoses[10] - spY)).putShort(MathHelper.toHalfFloat(this.quadPoses[11] - spZ));
+            buffer.putShort(MathHelper.toHalfFloat((float) (this.quadPoses[9] - spX))).putShort(MathHelper.toHalfFloat((float) (this.quadPoses[10] - spY))).putShort(MathHelper.toHalfFloat((float) (this.quadPoses[11] - spZ)));
             buffer.putShort((short) (this.quadUVs[6] * 0xFFFF)).putShort((short) (this.quadUVs[7] * 0xFFFF));
             buffer.put(this.quadShade[3]).put(this.quadLightMap[3]);
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlowerJar.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlowerJar.java
index d1351e425..50f8675f0 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlowerJar.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererFlowerJar.java
@@ -18,19 +18,17 @@ public class TileEntityRendererFlowerJar extends TileEntityRenderer<TileEntityFl
     public void doRender(TessellatorGeneral tessellator, TileEntityFlowerJar tileEntity, double x, double y, double z, float renderPartialTicks) {
         GLRenderer.pushFrame();
         Block<?> block = Blocks.getBlock(tileEntity.flowerInPot);
-        if (block != null) {
-            GLRenderer.modelM4f().translate((float) x, (float) y, (float) z);
-            TextureRegistry.worldAtlas.bind();
-            float scaleValue = 0.75F;
-            GLRenderer.modelM4f().scale(scaleValue, scaleValue, scaleValue);
-            GLRenderer.modelM4f().translate(0.66F, 0.75F, 0.66F);
-            GLRenderer.enableState(State.BLEND);
-            GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
-            Lighting.disable();
-            BlockModelDispatcher.getInstance().getDispatch(block).renderStandalone(tessellator, tileEntity.flowerData, tileEntity.worldObj.getLightmapCoord(tileEntity.x, tileEntity.y, tileEntity.z, 0));
-            Lighting.enableLight();
-            GLRenderer.disableState(State.BLEND);
-        }
+        GLRenderer.modelM4f().translate((float) x, (float) y, (float) z);
+        TextureRegistry.worldAtlas.bind();
+        float scaleValue = 0.75F;
+        GLRenderer.modelM4f().scale(scaleValue, scaleValue, scaleValue);
+        GLRenderer.modelM4f().translate(0.66F, 0.75F, 0.66F);
+        GLRenderer.enableState(State.BLEND);
+        GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
+        Lighting.disable();
+        BlockModelDispatcher.getInstance().getDispatch(block).renderStandalone(tessellator, tileEntity.flowerData, tileEntity.worldObj.getLightmapCoord(tileEntity.x, tileEntity.y, tileEntity.z, 0));
+        Lighting.enableLight();
+        GLRenderer.disableState(State.BLEND);
         GLRenderer.popFrame();
     }
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
index f1469c18b..aac3c8ada 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMovingPiston.java
@@ -40,7 +40,7 @@ public class TileEntityRendererMovingPiston extends TileEntityRenderer<TileEntit
             }
             TilePos tileEntPos = new TilePos(tileEntity.x, tileEntity.y, tileEntity.z);
             TextureRegistry.worldAtlas.bind();
-            GLRenderer.setShader(Shaders.TERRAIN);
+            GLRenderer.setShader(Shaders.ITEM);
             GLRenderer.setBlendFunc(BlendFactor.SRC_ALPHA, BlendFactor.ONE_MINUS_SRC_ALPHA);
             GLRenderer.enableState(State.BLEND);
             GLRenderer.disableState(State.CULL_FACE);
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
index a6787c30d..18ba368f6 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
@@ -1,15 +1,15 @@
 package net.minecraft.client.render.tileentity;
 
-import it.unimi.dsi.fastutil.ints.IntArrayList;
-import it.unimi.dsi.fastutil.ints.IntList;
-import net.minecraft.client.Minecraft;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
 import net.minecraft.client.option.GameSettings;
 import net.minecraft.client.option.enums.TextOutlineQuality;
 import net.minecraft.client.render.camera.ICamera;
-import net.minecraft.client.render.font.FontRenderer;
+import net.minecraft.client.render.font.Font;
+import net.minecraft.client.render.font.FontRendererDefault;
 import net.minecraft.client.render.font.SF;
 import net.minecraft.client.render.renderer.GLRenderer;
 import net.minecraft.client.render.renderer.State;
+import net.minecraft.client.render.tessellator.TessellatorFont;
 import net.minecraft.client.render.tessellator.TessellatorGeneral;
 import net.minecraft.client.render.texture.stitcher.IconCoordinate;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
@@ -24,17 +24,18 @@ import net.minecraft.core.util.helper.Color;
 import net.minecraft.core.util.helper.DyeColor;
 import net.minecraft.core.util.helper.MathHelper;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.joml.Math;
 import org.joml.primitives.AABBd;
+import org.lwjgl.opengl.GL41;
 import org.useless.dragonfly.models.entity.StaticEntityModel;
 
-import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.List;
+import java.util.Map;
 
 public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
 
-    private final Minecraft mc = Minecraft.getMinecraft();
+    private FontRendererDefault fontRenderer = null;
     private final @NotNull StringBuilder builder = new StringBuilder();
     private final String[] signColorTextures = new String[16];
     private final BufferedTextMeshRenderer textMeshRenderer = new BufferedTextMeshRenderer();
@@ -52,6 +53,10 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
         if (block == null || !(block.getLogic() instanceof BlockLogicSign sign))
             return; // Check to make sure the block for the entity is present before rendering, some edge cases have the tile entity loaded before the block data and can cause a crash
 
+        if (this.fontRenderer == null) {
+            this.fontRenderer = new FontRendererDefault();
+            this.fontRenderer.init();
+        }
         GLRenderer.pushFrame();
 
         final StaticEntityModel model;
@@ -108,7 +113,6 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
         GLRenderer.popFrame();
 
         // Render text
-        final FontRenderer sr = getFont();
         float lightOffset = 0f;
         if (tileEntity.isGlowing()) {
             lightOffset = 96f;
@@ -152,9 +156,10 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
         final int _x = 0;
         final int _y = -tileEntity.signText.length * 5;
         if (tileEntity.isGlowing() && GameSettings.TEXT_OUTLINE_QUALITY.value == TextOutlineQuality.FANCY) {
-            this.textMeshRenderer.render(sr, t, line1, line2, line3, line4, _x, _y, SF.setOutlined(SF.setColor(0, color)));
+            GLRenderer.setLightmapCoord2i(15, 15);
+            this.textMeshRenderer.render(this.fontRenderer, line1, line2, line3, line4, _x, _y, SF.setOutlined(SF.setColor(0, color)));
         } else {
-            this.textMeshRenderer.render(sr, t, line1, line2, line3, line4, _x, _y, SF.setColor(0, color));
+            this.textMeshRenderer.render(this.fontRenderer, line1, line2, line3, line4, _x, _y, SF.setColor(0, color));
         }
         GLRenderer.popFrame();
 
@@ -187,25 +192,22 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
 
     public static class BufferedTextMeshRenderer {
         private static final long MAX_NON_RENDERED_TIME = 120 * 1000;
-        private final @NotNull IntList freeDisplayLists = new IntArrayList();
-        private final @NotNull List<@NotNull MeshEntry> bufferedMeshes = new ArrayList<>();
+        private final @NotNull Map<@NotNull MeshEntry, @NotNull MeshEntry> bufferedMeshes = new Object2ObjectOpenHashMap<>();
 
         private final @NotNull MeshEntry referenceEntry = new MeshEntry("", "", "", "", 0);
 
         public void tick() {
-            final Iterator<MeshEntry> iterator = this.bufferedMeshes.iterator();
+            final Iterator<MeshEntry> iterator = this.bufferedMeshes.values().iterator();
             while (iterator.hasNext()) {
                 final MeshEntry entry = iterator.next();
                 if (System.currentTimeMillis() - entry.lastUse > MAX_NON_RENDERED_TIME) {
-                    assert entry.list != -1;
-                    this.freeDisplayLists.add(entry.list);
                     iterator.remove();
+                    entry.buffer.delete();
                 }
             }
         }
 
-        public void render(@NotNull final FontRenderer sr,
-                           @NotNull final TessellatorGeneral t,
+        public void render(@NotNull final FontRendererDefault sr,
                            @NotNull final CharSequence line1,
                            @NotNull final CharSequence line2,
                            @NotNull final CharSequence line3,
@@ -213,12 +215,11 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
                            final int x, final int y, final long config) {
             GLRenderer.pushFrame();
             GLRenderer.modelM4f().translate(x, y, 0);
-            renderInternal(sr, t, line1, line2, line3, line4, config);
+            renderInternal(sr, line1, line2, line3, line4, config);
             GLRenderer.popFrame();
         }
 
-        private void renderInternal(@NotNull final FontRenderer sr, // TODO Rework text caching!
-                                    @NotNull final TessellatorGeneral t,
+        private void renderInternal(@NotNull final FontRendererDefault sr,
                                     @NotNull final CharSequence line1,
                                     @NotNull final CharSequence line2,
                                     @NotNull final CharSequence line3,
@@ -230,10 +231,29 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
             this.referenceEntry.line4 = line4.toString();
             this.referenceEntry.config = config;
 
-            sr.render(line1, -sr.stringWidth(line1) / 2, 0).setConfig(config).call();
-            sr.render(line2, -sr.stringWidth(line2) / 2, 10).setConfig(config).call();
-            sr.render(line3, -sr.stringWidth(line3) / 2, 20).setConfig(config).call();
-            sr.render(line4, -sr.stringWidth(line4) / 2, 30).setConfig(config).call();
+            TessellatorFont.FontRenderBuffer buffer;
+            if (this.bufferedMeshes.containsKey(this.referenceEntry)) {
+                MeshEntry entry = this.bufferedMeshes.get(this.referenceEntry);
+                entry.lastUse = System.currentTimeMillis();
+                buffer = entry.buffer;
+            } else {
+                Font font = sr.getFont();
+                sr.getTessellator().startDrawing();
+                sr.renderStringRaw(font, line1, -sr.stringWidth(line1) / 2, 0, 0, false, config);
+                sr.renderStringRaw(font, line2, -sr.stringWidth(line2) / 2, 10, 0, false, config);
+                sr.renderStringRaw(font, line3, -sr.stringWidth(line3) / 2, 20, 0, false, config);
+                sr.renderStringRaw(font, line4, -sr.stringWidth(line4) / 2, 30, 0, false, config);
+                buffer = sr.getTessellator().record(GL41.glGenVertexArrays(), GL41.glGenBuffers());
+                MeshEntry entry = new MeshEntry(line1, line2, line3, line4, config);
+                entry.buffer = buffer;
+                entry.lastUse = System.currentTimeMillis();
+                MeshEntry last = this.bufferedMeshes.put(entry, entry);
+                if (last != null) {
+                    last.buffer.delete();
+                }
+            }
+
+            buffer.render();
         }
 
         public static class MeshEntry {
@@ -244,7 +264,7 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign> {
             public @NotNull String line4;
 
             public long lastUse = 0;
-            public int list = -1;
+            public @Nullable TessellatorFont.FontRenderBuffer buffer = null;
 
             public MeshEntry(final @NotNull CharSequence line1, final @NotNull CharSequence line2, final @NotNull CharSequence line3, final @NotNull CharSequence line4, final long config) {
                 this.config = config;
diff --git a/game/client/src/main/java/net/minecraft/client/world/AuroraProvider.java b/game/client/src/main/java/net/minecraft/client/world/AuroraProvider.java
new file mode 100644
index 000000000..b5cca7313
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/world/AuroraProvider.java
@@ -0,0 +1,108 @@
+package net.minecraft.client.world;
+
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.noise.ImprovedPerlinNoise;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Random;
+
+public class AuroraProvider {
+    private @Nullable ImprovedPerlinNoise noise = null;
+    private long lastSeed = -1;
+
+    public AuroraProvider() { }
+
+    public void init(final @NotNull World world) {
+        if (this.noise == null || world.getRandomSeed() != this.lastSeed) {
+            this.noise = new ImprovedPerlinNoise(new Random(world.getRandomSeed()));
+            this.lastSeed = world.getRandomSeed();
+        }
+    }
+
+    public double getAuroraPresence(final double x, final double z, final long worldTime, float celestialAngle) {
+        assert this.noise != null;
+
+        final double auroraLevel = 0.0f;
+        final double auroraRadius = 0.03f;
+
+        final double mult;
+        if (celestialAngle < 0.25f || celestialAngle > 0.75f) {
+            return 0.0f;
+        } else {
+            // celestialAngle from 0.25f to 0.75f
+            celestialAngle = (celestialAngle - 0.25f) * 2;
+            // celestialAngle from 0.0f to 1.0f
+            celestialAngle = (celestialAngle * 2) - 1f;
+            // celestialAngle from -1.0f to 1.0f
+            celestialAngle = Math.abs(celestialAngle);
+            // celestialAngle from 1.0f to 0.0f to 1.0f
+            celestialAngle = -celestialAngle + 1.0f;
+            // celestialAngle from 0.0f to 1.0f to 0.0f
+            mult = celestialAngle;
+        }
+
+        double sample = this.noise.getValue(x / 1000d, worldTime / 5000d, z / 1000d);
+        sample += this.noise.getValue(x / 30d, worldTime / 50d, z / 30d) * 0.05d;
+        if (sample < auroraLevel - auroraRadius || sample > auroraLevel + auroraRadius) {
+            return 0.0f;
+        } else {
+            // Range = auroraLevel - auroraRadius to auroraLevel + auroraRadius
+            sample -= auroraLevel;
+            // Range = -auroraRadius to auroraRadius
+            sample /= auroraRadius;
+            // Range = -1.0 to 1.0
+            sample = Math.abs(sample);
+            // Range = 1.0 to 0.0 to 1.0
+            sample = -sample;
+            // Range = -1.0 to 0.0 to -1.0
+            sample += 1.0d;
+            // Range = 0.0 to 1.0 to 0.0
+            return sample * mult;
+        }
+    }
+
+    public double getAuroraHeightOffset(final double x, final double z, final double worldTime) {
+        assert this.noise != null;
+
+        return this.noise.getValue(x / 100d, worldTime / 5000d, z / 100d) * 25d;
+    }
+
+    public float getAuroraRedOffset(final double x, final double z, final double worldTime) {
+        assert this.noise != null;
+
+        double sample = this.noise.getValue(x / 500d, worldTime / 5000d + 10000, z / 500d);
+        double sample2 = this.noise.getValue(x / 200d, worldTime / 5000d, z / 200d);
+        if (sample < 0.0f) sample = 0.0f;
+        if (sample > 0.25f) sample = 0.25f;
+        if (sample2 < 0.0f) sample2 = 0.0f;
+        if (sample2 > 0.1f) sample2 = 0.1f;
+
+
+        return (float) (sample * 3 + sample2 * 2);
+    }
+
+    public float getAuroraGreenOffset(final double x, final double z, final double worldTime) {
+        assert this.noise != null;
+
+        double sample = this.noise.getValue(x / 500d, worldTime / 5000d + 10000d, z / 500d);
+        double sample2 = this.noise.getValue(x / 500d, worldTime / 5000d, z / 500d);
+        if (sample < 0) sample = 0;
+        if (sample > 0.125f) sample = 0.125f;
+        if (sample2 < 0.0f) sample2 = 0.0f;
+        if (sample2 > 0.25f) sample2 = 0.25f;
+
+
+        return (float) (sample * 4 - sample2 * 2);
+    }
+
+    public float getAuroraBlueOffset(final double x, final double z, final double worldTime) {
+        assert this.noise != null;
+
+        double sample = this.noise.getValue(x / 500d, worldTime / 5000d, z / 500d);
+        if (sample < 0.0f) sample = 0.0f;
+        if (sample > 0.25f) sample = 0.25f;
+
+        return (float) sample;
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/world/WorldClient.java b/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
index b61b6d726..bc7b6482a 100644
--- a/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
+++ b/game/client/src/main/java/net/minecraft/client/world/WorldClient.java
@@ -1,20 +1,19 @@
 package net.minecraft.client.world;
 
 import net.minecraft.client.option.GameSettings;
-import net.minecraft.client.render.LightmapHelper;
 import net.minecraft.client.render.camera.ICamera;
 import net.minecraft.client.render.colorizer.Colorizers;
 import net.minecraft.client.render.worldtype.WorldTypeFXDispatcher;
 import net.minecraft.client.world.chunk.provider.ChunkProviderViewer;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.util.helper.MathHelper;
-import net.minecraft.core.world.AuroraProvider;
 import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.biome.Biome;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.save.LevelData;
 import net.minecraft.core.world.save.LevelStorage;
+import net.minecraft.core.world.saveddata.SavedDataStorage;
 import net.minecraft.core.world.season.SeasonManager;
 import net.minecraft.core.world.season.Seasons;
 import net.minecraft.core.world.type.WorldType;
@@ -30,22 +29,35 @@ import org.joml.Vector3fc;
 public class WorldClient extends World {
     public static final WorldClient EMPTY_WORLD = new WorldClient();
 
-    public WorldClient(LevelStorage saveHandler, String name, long seed, Dimension dimension, WorldType worldType, @Nullable Biome startingBiome) {
-        super(saveHandler, name, seed, dimension, worldType, startingBiome);
+    public WorldClient(
+        final @NotNull LevelStorage levelStorage,
+        final @Nullable String name,
+        final long seed,
+        final @NotNull Dimension dimension,
+        final @Nullable Biome startingBiome
+    ) {
+        super(levelStorage, name, seed, dimension, startingBiome);
     }
 
-    public WorldClient(World world, Dimension dimension) {
+    public WorldClient(
+        final @NotNull World world,
+        final @NotNull Dimension dimension
+    ) {
         super(world, dimension);
     }
 
-    public WorldClient(LevelStorage saveHandler, String name, Dimension dimension, WorldType worldType, long seed) {
-        super(saveHandler, name, dimension, worldType, seed);
+    public WorldClient(
+        final @NotNull SavedDataStorage savedDataStorage,
+        final @NotNull String name,
+        final long seed,
+        final @NotNull Dimension dimension,
+        final @NotNull WorldType worldType
+    ) {
+        super(savedDataStorage, name, seed, dimension, worldType);
     }
 
     public WorldClient() {
         this.dimension = Dimension.OVERWORLD;
-        this.saveHandler = null;
-        this.levelData = new LevelData(0, "modelviewer");
         this.worldType = WorldTypes.DEBUG;
         this.chunkProvider = new ChunkProviderViewer(this);
         this.seasonManager = SeasonManager.fromConfig(this, this.worldType.getSeasonConfig());
@@ -53,8 +65,6 @@ public class WorldClient extends World {
 
         this.weatherManager = new WeatherManager(this);
 
-        this.auroraProvider = new AuroraProvider(this, 0);
-
         this.biomeProvider = this.worldType.createBiomeProvider(this);
     }
 
diff --git a/game/client/src/main/java/net/minecraft/client/world/WorldClientMP.java b/game/client/src/main/java/net/minecraft/client/world/WorldClientMP.java
index 7388c1042..1cd0a5b1c 100644
--- a/game/client/src/main/java/net/minecraft/client/world/WorldClientMP.java
+++ b/game/client/src/main/java/net/minecraft/client/world/WorldClientMP.java
@@ -13,7 +13,7 @@ import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.chunk.Chunk;
 import net.minecraft.core.world.chunk.ChunkCoordinates;
 import net.minecraft.core.world.chunk.provider.IChunkProvider;
-import net.minecraft.client.world.save.SaveHandlerClientMP;
+import net.minecraft.core.world.save.LevelStorageDummy;
 import net.minecraft.core.world.pos.ChunkPos;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.pos.TilePosc;
@@ -32,21 +32,32 @@ public class WorldClientMP extends WorldClient {
     private final Set<Entity> forced;
     private final Set<Entity> reEntries;
 
-    public WorldClientMP(PacketHandlerClient packetHandlerClient, long seed, int dimensionId, int worldTypeId) {
-        super(new SaveHandlerClientMP(), "MpServer", Dimension.getDimensionList().get(dimensionId), Registries.WORLD_TYPES.getItemByNumericId(worldTypeId), seed);
+    public WorldClientMP(
+        final @NotNull PacketHandlerClient packetHandler,
+        final long seed,
+        final int dimensionId,
+        final int worldTypeId
+    ) {
+        super(
+            packetHandler.savedDataStorage,
+            "MpServer",
+            seed,
+            Dimension.getDimensionList().get(dimensionId),
+            Registries.WORLD_TYPES.getItemByNumericId(worldTypeId)
+        );
+
         this.positionTypesList = new LinkedList<>();
         this.entitiesById = new Int2ObjectOpenHashMap<>();
         this.forced = new HashSet<>();
         this.reEntries = new HashSet<>();
-        this.sendQueue = packetHandlerClient;
+        this.sendQueue = packetHandler;
         setSpawnPoint(new ChunkCoordinates(8, 64, 8));
-        this.savedDataStorage = packetHandlerClient.savedDataStorage;
     }
 
     @Override
     public void tick() {
         if (getGameRuleValue(GameRules.DO_DAY_CYCLE)) {
-            this.levelData.setWorldTime(this.levelData.getWorldTime() + 1L);
+            this.getLevelData().setWorldTime(this.getLevelData().getWorldTime() + 1L);
         }
         for (int k = 0; k < 10 && !this.reEntries.isEmpty(); k++) {
             Entity entity = this.reEntries.iterator().next();
diff --git a/game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientSP.java b/game/client/src/main/java/net/minecraft/client/world/save/LevelStorageClientSP.java
similarity index 87%
rename from game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientSP.java
rename to game/client/src/main/java/net/minecraft/client/world/save/LevelStorageClientSP.java
index ccccb4233..2d4ff4e49 100644
--- a/game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientSP.java
+++ b/game/client/src/main/java/net/minecraft/client/world/save/LevelStorageClientSP.java
@@ -7,17 +7,17 @@ import net.minecraft.core.world.chunk.IChunkLoader;
 import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.save.ISaveFormat;
 import net.minecraft.core.world.save.LevelData;
-import net.minecraft.core.world.save.SaveHandlerBase;
+import net.minecraft.core.world.save.LevelStorageBase;
 import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
 import java.util.List;
 
-public class SaveHandlerClientSP
-        extends SaveHandlerBase
+public class LevelStorageClientSP
+        extends LevelStorageBase
 {
 
-    public SaveHandlerClientSP(ISaveFormat saveFormat, File savesDir, String worldDirName)
+    public LevelStorageClientSP(ISaveFormat saveFormat, File savesDir, String worldDirName)
     {
         super(saveFormat, savesDir, worldDirName);
     }
diff --git a/game/client/src/main/java/org/useless/dragonfly/models/block/mojang/StaticBlockModelMojang.java b/game/client/src/main/java/org/useless/dragonfly/models/block/mojang/StaticBlockModelMojang.java
index efffcdb57..594e0819a 100644
--- a/game/client/src/main/java/org/useless/dragonfly/models/block/mojang/StaticBlockModelMojang.java
+++ b/game/client/src/main/java/org/useless/dragonfly/models/block/mojang/StaticBlockModelMojang.java
@@ -373,21 +373,6 @@ public class StaticBlockModelMojang implements StaticBlockModel {
                     lightTL = lightBL = lightBR = lightTR = getBrightness(dirX2, dirY2, dirZ2, minBrightness);
                 }
 
-                float colorRedBottomLeft = r;
-                float colorRedBottomRight = colorRedBottomLeft;
-                float colorRedTopRight = colorRedBottomLeft;
-                float colorRedTopLeft = colorRedBottomLeft;
-
-                float colorGreenBottomLeft = g;
-                float colorGreenBottomRight = colorGreenBottomLeft;
-                float colorGreenTopRight = colorGreenBottomLeft;
-                float colorGreenTopLeft = colorGreenBottomLeft;
-
-                float colorBlueBottomLeft = b;
-                float colorBlueBottomRight = colorBlueBottomLeft;
-                float colorBlueTopRight = colorBlueBottomLeft;
-                float colorBlueTopLeft = colorBlueBottomLeft;
-
                 final float tl = topP * lightTL + (1 - topP) * lightBL;
                 final float tr = topP * lightTR + (1 - topP) * lightBR;
                 final float bl = botP * lightTL + (1 - botP) * lightBL;
@@ -420,7 +405,7 @@ public class StaticBlockModelMojang implements StaticBlockModel {
                 }
                 Vector2fc uvsTL = element.vertexUvs[offset + VERTEX_TOP_LEFT];
                 if (uvlock) uvsTL = rotateUV(uvsTL, rotX, rotY, rotZ, element.directions[face], this.vertUV);
-                tessellator.setColorOpaque3f(colorRedTopLeft, colorGreenTopLeft, colorBlueTopLeft);
+                tessellator.setColorOpaque3f(r, g, b);
                 tessellator.setLightmapCoord1i(lightIndexTopLeft);
                 tessellator.setShade1i((int) (ltl * 0xFF));
                 tessellator.addVertexWithUV(
@@ -440,7 +425,7 @@ public class StaticBlockModelMojang implements StaticBlockModel {
                 }
                 Vector2fc uvsBL = element.vertexUvs[offset + VERTEX_BOTTOM_LEFT];
                 if (uvlock) uvsBL = rotateUV(uvsBL, rotX, rotY, rotZ, element.directions[face], this.vertUV);
-                tessellator.setColorOpaque3f(colorRedBottomLeft, colorGreenBottomLeft, colorBlueBottomLeft);
+                tessellator.setColorOpaque3f(r, g, b);
                 tessellator.setLightmapCoord1i(lightIndexBottomLeft);
                 tessellator.setShade1i((int) (lbl * 0xFF));
                 tessellator.addVertexWithUV(
@@ -460,7 +445,7 @@ public class StaticBlockModelMojang implements StaticBlockModel {
                 }
                 Vector2fc uvsBR = element.vertexUvs[offset + VERTEX_BOTTOM_RIGHT];
                 if (uvlock) uvsBR = rotateUV(uvsBR, rotX, rotY, rotZ, element.directions[face], this.vertUV);
-                tessellator.setColorOpaque3f(colorRedBottomRight, colorGreenBottomRight, colorBlueBottomRight);
+                tessellator.setColorOpaque3f(r, g, b);
                 tessellator.setLightmapCoord1i(lightIndexBottomRight);
                 tessellator.setShade1i((int) (lbr * 0xFF));
                 tessellator.addVertexWithUV(
@@ -480,7 +465,7 @@ public class StaticBlockModelMojang implements StaticBlockModel {
                 }
                 Vector2fc uvsTR = element.vertexUvs[offset + VERTEX_TOP_RIGHT];
                 if (uvlock) uvsTR = rotateUV(uvsTR, rotX, rotY, rotZ, element.directions[face], this.vertUV);
-                tessellator.setColorOpaque3f(colorRedTopRight, colorGreenTopRight, colorBlueTopRight);
+                tessellator.setColorOpaque3f(r, g, b);
                 tessellator.setLightmapCoord1i(lightIndexTopRight);
                 tessellator.setShade1i((int) (ltr * 0xFF));
                 tessellator.addVertexWithUV(
diff --git a/game/client/src/main/resources/shaders/font.fsh b/game/client/src/main/resources/shaders/font.fsh
index d80227c35..229c9771e 100644
--- a/game/client/src/main/resources/shaders/font.fsh
+++ b/game/client/src/main/resources/shaders/font.fsh
@@ -2,8 +2,11 @@
 out vec4 FragColor;
 
 in vec4 Color;
+in vec2 UV;
 in vec2 TexCoord;
 in vec2 Lightmap;
+in vec2 Size;
+in float Config;
 
 uniform float uAlphaTest;
 uniform vec4 uColor;
@@ -11,8 +14,65 @@ uniform vec4 uColor;
 uniform sampler2D colortex;
 uniform sampler2D lighttex;
 
+bool sampleInside(vec2 tex) {
+    vec2 max = UV + (Size/128);
+    return tex.x > UV.x && tex.y > UV.y && tex.x < max.x && tex.y < max.y;
+}
+
+vec4 colorAtPoint(vec2 point) {
+    uint config = uint(Config + 0.01);
+    vec4 tint = texture(lighttex, Lightmap) * uColor;
+
+    vec4 color = vec4(0, 0, 0, 0);
+    if (sampleInside(point)) {
+        color = texture(colortex, point) * Color * tint;
+    }
+    vec2 left = point - vec2(1, 0)/128;
+    if ((config & 0x08u) != 0 && sampleInside(left) && length(color) == 0) {
+        color = texture(colortex, left) * Color * tint;
+    }
+    vec2 max = UV + (Size/128);
+    if ((config & 0x02u) != 0 && point.y > max.y && point.y < (max.y + 1.0/128)) {
+        color = Color * tint;
+    }
+    vec2 mid = UV + Size/256;
+    if ((config & 0x04u) != 0 && point.y > (mid.y - 1.0/128) && point.y < mid.y) {
+        color = Color * tint;
+    }
+
+    return color;
+}
+
+
+bool canReplace(vec2 tex) {
+    return length(colorAtPoint(tex)) != 0;
+}
+
+
 void main() {
-    FragColor = texture(colortex, TexCoord) * texture(lighttex, Lightmap) * Color * uColor;
+    uint config = uint(Config + 0.01);
+    vec4 tint = texture(lighttex, Lightmap) * uColor;
+    FragColor = colorAtPoint(TexCoord);
+    if (length(FragColor) == 0) {
+        if ((config & 0x20u) != 0) {
+            vec2 upLeft = TexCoord + vec2(-1, -1)/128;
+            vec2 up = TexCoord + vec2(0, -1)/128;
+            vec2 upRight = TexCoord + vec2(1, -1)/128;
+            vec2 left = TexCoord + vec2(-1, 0)/128;
+            vec2 right = TexCoord + vec2(1, 0)/128;
+            vec2 downLeft = TexCoord + vec2(-1, 1)/128;
+            vec2 down = TexCoord + vec2(0, 1)/128;
+            vec2 downRight = TexCoord + vec2(1, 1)/128;
+            if (canReplace(upLeft) || canReplace(up) || canReplace(upRight) || canReplace(left) || canReplace(right) || canReplace(downLeft) || canReplace(down) || canReplace(downRight)) {
+                FragColor = vec4(Color.rgb/4, Color.a) * tint;
+            }
+        } else if ((config & 0x80u) != 0) {
+            vec2 upLeft = TexCoord - vec2(1, 1)/128;
+            if (canReplace(upLeft)) {
+                FragColor = vec4(Color.rgb/4, Color.a) * tint;
+            }
+        }
+    }
     if (FragColor.a < uAlphaTest) {
         discard;
     }
diff --git a/game/client/src/main/resources/shaders/font.gsh b/game/client/src/main/resources/shaders/font.gsh
index c3c377ebc..f036b47c1 100644
--- a/game/client/src/main/resources/shaders/font.gsh
+++ b/game/client/src/main/resources/shaders/font.gsh
@@ -4,16 +4,18 @@ layout (triangle_strip, max_vertices = 4) out;
 
 in VS_OUT {
     vec2 size;
-    float shift;
     vec4 color;
     vec2 uv;
-    vec2 uvSizes;
     vec2 lightmap;
+    float config;
 } gs_in[];
 
 out vec4 Color;
+out vec2 UV;
 out vec2 TexCoord;
 out vec2 Lightmap;
+out vec2 Size;
+out float Config;
 
 uniform mat4 model;
 layout (std140) uniform Matrices {
@@ -24,23 +26,28 @@ layout (std140) uniform Matrices {
 } matrices;
 
 void main() {
+    UV = gs_in[0].uv;
+    Size = gs_in[0].size;
+    Config = gs_in[0].config;
     Color = gs_in[0].color;
     Lightmap = gs_in[0].lightmap;
 
-    TexCoord = gs_in[0].uv + vec2(0, gs_in[0].uvSizes.y);
-    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(-gs_in[0].shift,  gs_in[0].size.y, 0.0, 0.0));
+    float shift = uint(gs_in[0].config) & 0x01u;
+
+    TexCoord = gs_in[0].uv + vec2(-1, gs_in[0].size.y + 2)/128;
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(-shift - 1,  gs_in[0].size.y + 2, 0.0, 0.0));
     EmitVertex();
 
-    TexCoord = gs_in[0].uv + vec2(gs_in[0].uvSizes.x, gs_in[0].uvSizes.y);
-    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(gs_in[0].size.x - gs_in[0].shift,  gs_in[0].size.y, 0.0, 0.0));
+    TexCoord = gs_in[0].uv + vec2(gs_in[0].size.x + 2, gs_in[0].size.y + 2)/128;
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(gs_in[0].size.x - shift + 2,  gs_in[0].size.y + 2, 0.0, 0.0));
     EmitVertex();
 
-    TexCoord = gs_in[0].uv + vec2(0, 0);
-    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(gs_in[0].shift, 0, 0.0, 0.0));
+    TexCoord = gs_in[0].uv + vec2(-1, -1)/128;
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(shift - 1, - 1, 0.0, 0.0));
     EmitVertex();
 
-    TexCoord = gs_in[0].uv + vec2(gs_in[0].uvSizes.x, 0);
-    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(gs_in[0].size.x + gs_in[0].shift, 0, 0.0, 0.0));
+    TexCoord = gs_in[0].uv + vec2(gs_in[0].size.x + 2, -1)/128;
+    gl_Position = matrices.projection * matrices.view * model * (gl_in[0].gl_Position + vec4(gs_in[0].size.x + shift + 2, -1, 0.0, 0.0));
     EmitVertex();
 
     EndPrimitive();
diff --git a/game/client/src/main/resources/shaders/font.vsh b/game/client/src/main/resources/shaders/font.vsh
index 573276518..12aa80801 100644
--- a/game/client/src/main/resources/shaders/font.vsh
+++ b/game/client/src/main/resources/shaders/font.vsh
@@ -1,19 +1,17 @@
 #version 410 core
 layout (location = 0) in vec2 aPos;
 layout (location = 1) in vec2 aSize;
-layout (location = 2) in float aShift;
-layout (location = 3) in vec4 aColor;
-layout (location = 4) in vec2 aUv;
-layout (location = 5) in vec2 aUvSizes;
-layout (location = 6) in float aLightmap;
+layout (location = 2) in vec4 aColor;
+layout (location = 3) in vec2 aUV;
+layout (location = 4) in float aConfig;
+layout (location = 5) in float aLightmap;
 
 out VS_OUT {
     vec2 size;
-    float shift;
     vec4 color;
     vec2 uv;
-    vec2 uvSizes;
     vec2 lightmap;
+    float config;
 } vs_out;
 
 uniform float uZ;
@@ -26,10 +24,9 @@ vec2 unpackLightCoord() {
 void main() {
     gl_Position = vec4(aPos, uZ, 1);
 
-    vs_out.size = aSize;
-    vs_out.shift = aShift;
+    vs_out.size = aSize/2;
     vs_out.color = aColor;
-    vs_out.uv = aUv/256.0;
-    vs_out.uvSizes = aUvSizes/256.0;
+    vs_out.uv = aUV/256.0;
     vs_out.lightmap = unpackLightCoord();
+    vs_out.config = aConfig;
 }
diff --git a/game/client/src/main/resources/shaders/terrain.fsh b/game/client/src/main/resources/shaders/terrain.fsh
index e4d7c4da6..b1e31ee04 100644
--- a/game/client/src/main/resources/shaders/terrain.fsh
+++ b/game/client/src/main/resources/shaders/terrain.fsh
@@ -78,7 +78,7 @@ void main() {
 
     vec4 c = vec4(mix(vec3(1, 1, 1), Color.rgb, maskColor.r), 1) * uColor;
 
-    vec4 postColor = texColor * c * Shade * lightColor;
+    vec4 postColor = texColor * c * vec4(Shade, Shade, Shade, 1) * lightColor;
     vec4 litColor = vec4(postColor.rgb * clamp(Light, 0, 1), postColor.a);
 //    vec4 litColor = vec4(abs(Norm), 1f);
 
diff --git a/game/client/src/main/resources/shaders/terrain.gsh b/game/client/src/main/resources/shaders/terrain.gsh
index 41418cffa..d275532da 100644
--- a/game/client/src/main/resources/shaders/terrain.gsh
+++ b/game/client/src/main/resources/shaders/terrain.gsh
@@ -2,6 +2,9 @@
 layout (points) in;
 layout (triangle_strip, max_vertices = 4) out;
 
+#define EXPAND_POS 1.0005
+#define EXPAND_UV 0.995
+
 in VS_OUT {
     vec3 sourcePos;
     vec3 sourceColor;
@@ -75,30 +78,30 @@ void main() {
 
     Color = vec4(gs_in[0].sourceColor, 1);
     Shade = unpackShade(gs_in[0].V1LightShade);
-    TexCoord = expand(gs_in[0].V1UV, uvCenter, 0.995f);
+    TexCoord = expand(gs_in[0].V1UV, uvCenter, EXPAND_UV);
     LightMapCoord = unpackLightCoord(gs_in[0].V1LightShade);
-    gl_Position = matrices.projection * matrices.view * model * vec4(expand(gs_in[0].sourcePos, posCenter, 1.0005f), 1);
+    gl_Position = matrices.projection * matrices.view * model * vec4(expand(gs_in[0].sourcePos, posCenter, EXPAND_POS), 1);
     EmitVertex();
 
     Color = vec4(gs_in[0].sourceColor, 1);
     Shade = unpackShade(gs_in[0].V2LightShade);
-    TexCoord = expand(gs_in[0].V2UV, uvCenter, 0.995f);
+    TexCoord = expand(gs_in[0].V2UV, uvCenter, EXPAND_UV);
     LightMapCoord = unpackLightCoord(gs_in[0].V2LightShade);
-    gl_Position = matrices.projection * matrices.view * model * vec4(expand(gs_in[0].sourcePos + gs_in[0].V2PosOffset, posCenter, 1.0005f), 1);
+    gl_Position = matrices.projection * matrices.view * model * vec4(expand(gs_in[0].sourcePos + gs_in[0].V2PosOffset, posCenter, EXPAND_POS), 1);
     EmitVertex();
 
     Color = vec4(gs_in[0].sourceColor, 1);
     Shade = unpackShade(gs_in[0].V4LightShade);
-    TexCoord = expand(gs_in[0].V4UV, uvCenter, 0.995f);
+    TexCoord = expand(gs_in[0].V4UV, uvCenter, EXPAND_UV);
     LightMapCoord = unpackLightCoord(gs_in[0].V4LightShade);
-    gl_Position = matrices.projection * matrices.view * model * vec4(expand(gs_in[0].sourcePos + gs_in[0].V4PosOffset, posCenter, 1.0005f), 1);
+    gl_Position = matrices.projection * matrices.view * model * vec4(expand(gs_in[0].sourcePos + gs_in[0].V4PosOffset, posCenter, EXPAND_POS), 1);
     EmitVertex();
 
     Color = vec4(gs_in[0].sourceColor, 1);
     Shade = unpackShade(gs_in[0].V3LightShade);
-    TexCoord = expand(gs_in[0].V3UV, uvCenter, 0.995f);
+    TexCoord = expand(gs_in[0].V3UV, uvCenter, EXPAND_UV);
     LightMapCoord = unpackLightCoord(gs_in[0].V3LightShade);
-    gl_Position = matrices.projection * matrices.view * model * vec4(expand(gs_in[0].sourcePos + gs_in[0].V3PosOffset, posCenter, 1.0005f), 1);
+    gl_Position = matrices.projection * matrices.view * model * vec4(expand(gs_in[0].sourcePos + gs_in[0].V3PosOffset, posCenter, EXPAND_POS), 1);
     EmitVertex();
 
     EndPrimitive();
diff --git a/game/client/src/test/java/TestBlockModelDispatcher.java b/game/client/src/test/java/TestBlockModelDispatcher.java
index 9130ef2aa..71a402c01 100644
--- a/game/client/src/test/java/TestBlockModelDispatcher.java
+++ b/game/client/src/test/java/TestBlockModelDispatcher.java
@@ -1,3 +1,4 @@
+import net.minecraft.client.Minecraft;
 import net.minecraft.client.render.block.model.BlockModelDispatcher;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
@@ -10,6 +11,11 @@ public class TestBlockModelDispatcher {
     @DisplayName("Verify block models")
     public void veryBlockModels() {
         Blocks.init();
+
+        // Models require mc reference
+        //noinspection DataFlowIssue
+        new Minecraft(Minecraft.getAppDir("minecraft-bta"), null);
+
         for (Block<?> block : Blocks.blockMap.values()) {
             Assertions.assertTrue(BlockModelDispatcher.getInstance().hasDispatch(block), () -> "Block '" + block.namespaceId() + "' is not assigned a block model!");
         }
diff --git a/game/core/build.gradle.kts b/game/core/build.gradle.kts
index d374f8e0e..ccc59f52f 100644
--- a/game/core/build.gradle.kts
+++ b/game/core/build.gradle.kts
@@ -67,7 +67,6 @@ val versionGenerator by tasks.registering {
 
                     public final class Version {
                         public static final String VERSION = "$version";
-                        public static final long BUILD_TIME = ${Date().time}L;
                         public static final int ASSET_PACK_FORMAT = $assetPackFormat;
                         public static final BuildChannel BUILD_CHANNEL = $buildChannel;
                         public static final int MP_PROTOCOL_VERSION = ${genProtocol(version, buildChannel)};
diff --git a/game/core/src/main/java/net/minecraft/core/Global.java b/game/core/src/main/java/net/minecraft/core/Global.java
index e8ea1ada8..9dceda8e8 100644
--- a/game/core/src/main/java/net/minecraft/core/Global.java
+++ b/game/core/src/main/java/net/minecraft/core/Global.java
@@ -4,10 +4,9 @@ import org.jetbrains.annotations.NotNull;
 
 public class Global
 {
-	public static final int TICKS_PER_SECOND = 20;
+	public static final int TICKS_PER_SECOND = 21;
 	public static final int DAY_LENGTH_TICKS = 24000;
 	public static final @NotNull String VERSION = Version.BUILD_CHANNEL.modifyVersionString(Version.VERSION);
-	public static final long BUILD_TIME = Version.BUILD_TIME;
 	public static final @NotNull BuildChannel BUILD_CHANNEL = Version.BUILD_CHANNEL;
 	public static final int CURRENT_SAVE_VERSION = 19134;
 	public static final int CURRENT_CHUNK_VERSION = 3;
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFlag.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFlag.java
index 8d3c3a8d7..05e21f11c 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFlag.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFlag.java
@@ -12,6 +12,7 @@ import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.net.packet.Packet;
 import net.minecraft.core.net.packet.PacketTileEntityData;
 import net.minecraft.core.player.inventory.container.Container;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
 import net.minecraft.core.util.helper.UUIDHelper;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -187,7 +188,6 @@ public class TileEntityFlag extends TileEntity
 
     @Override
     public @Nullable ItemStack getItem(int slot) {
-        slot -= 36;
         if (slot < 0 || slot >= 3) return null;
         
         return items[slot];
@@ -195,7 +195,6 @@ public class TileEntityFlag extends TileEntity
 
     @Override
     public @Nullable ItemStack removeItem(int slot, int takeAmount) {
-        slot -= 36;
         if (items[slot] != null)
         {
             if (items[slot].stackSize <= takeAmount)
@@ -232,7 +231,6 @@ public class TileEntityFlag extends TileEntity
     @Override
     public void setItem(int slot, @Nullable ItemStack stack)
     {
-        slot -= 36; // This seems so wrong and will almost certainly cause an issue at some point
         items[slot] = stack;
         itemsChanges();
         setChanged();
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
index e3b96734f..974385c5e 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
@@ -29,16 +29,20 @@ import java.util.Random;
 public class TileEntityFurnace extends TileEntity
         implements Container
 {
-    private final Random random;
+    public static final int SLOT_INGREDIENT = 0;
+    public static final int SLOT_FUEL = 1;
+    public static final int SLOT_RESULT = 2;
+
+    private final Random random = new Random();
+
+    protected ItemStack[] furnaceItemStacks = new ItemStack[3];
+    public int maxBurnTime = 0;
+    public int currentCookTime = 0;
+    public int maxCookTime = 0;
+    public int currentBurnTime = 200;
 
     public TileEntityFurnace()
     {
-        random = new Random();
-        furnaceItemStacks = new ItemStack[3];
-        currentBurnTime = 0;
-        maxBurnTime = 0;
-        currentCookTime = 0;
-        maxCookTime = 200;
     }
 
     @Override
@@ -62,7 +66,7 @@ public class TileEntityFurnace extends TileEntity
             {
                 ItemStack itemstack = furnaceItemStacks[slot];
                 furnaceItemStacks[slot] = null;
-                if (worldObj != null && slot == 2) {
+                if (worldObj != null && slot == SLOT_RESULT) {
                     worldObj.markBlockNeedsUpdate(x, y, z);
                 }
                 return itemstack;
@@ -71,7 +75,7 @@ public class TileEntityFurnace extends TileEntity
             if(furnaceItemStacks[slot].stackSize <= 0)
             {
                 furnaceItemStacks[slot] = null;
-                if (worldObj != null && slot == 2) {
+                if (worldObj != null && slot == SLOT_RESULT) {
                     worldObj.markBlockNeedsUpdate(x, y, z);
                 }
             }
@@ -90,7 +94,7 @@ public class TileEntityFurnace extends TileEntity
         {
             stack.stackSize = getMaxStackSize();
         }
-        if (worldObj != null && slot == 2 && stack == null) {
+        if (worldObj != null && slot == SLOT_RESULT && stack == null) {
             worldObj.markBlockNeedsUpdate(x, y, z);
         }
     }
@@ -180,34 +184,34 @@ public class TileEntityFurnace extends TileEntity
         if(worldObj == null || !worldObj.isClientSide)
         {
         	if(worldObj == null || worldObj.getBlockId(x, y, z) == Blocks.FURNACE_STONE_IDLE.id()) {
-            	if(currentBurnTime == 0 && furnaceItemStacks[0] == null) {
-            		if(furnaceItemStacks[1] != null && furnaceItemStacks[1].itemID == Blocks.COBBLE_NETHERRACK.id()) {
-            			furnaceItemStacks[1].stackSize--;
-            			if(furnaceItemStacks[1].stackSize <= 0) furnaceItemStacks[1] = null;
+            	if(currentBurnTime == 0 && furnaceItemStacks[SLOT_INGREDIENT] == null) {
+            		if(furnaceItemStacks[SLOT_FUEL] != null && furnaceItemStacks[SLOT_FUEL].itemID == Blocks.COBBLE_NETHERRACK.id()) {
+            			furnaceItemStacks[SLOT_FUEL].stackSize--;
+            			if(furnaceItemStacks[SLOT_FUEL].stackSize <= 0) furnaceItemStacks[SLOT_FUEL] = null;
             			updateFurnace(true);
             			furnaceUpdated = true;
             		}
             	}
         	}
         	
-            if(currentBurnTime == 0 && furnaceItemStacks[1] != null && canSmelt())
+            if(currentBurnTime == 0 && furnaceItemStacks[SLOT_FUEL] != null && canSmelt())
             {
-                maxBurnTime = currentBurnTime = getBurnTimeFromItem(furnaceItemStacks[1]);
+                maxBurnTime = currentBurnTime = getBurnTimeFromItem(furnaceItemStacks[SLOT_FUEL]);
                 if(currentBurnTime > 0)
                 {
                     furnaceUpdated = true;
-                    if(furnaceItemStacks[1] != null)
+                    if(furnaceItemStacks[SLOT_FUEL] != null)
                     {
-                        if (furnaceItemStacks[1].getItem() == Items.BUCKET_LAVA)
+                        if (furnaceItemStacks[SLOT_FUEL].getItem() == Items.BUCKET_LAVA)
                         {
-                            furnaceItemStacks[1] = new ItemStack(Items.BUCKET);
+                            furnaceItemStacks[SLOT_FUEL] = new ItemStack(Items.BUCKET);
                         }
                         else
                         {
-                            furnaceItemStacks[1].stackSize--;
-                            if(furnaceItemStacks[1].stackSize <= 0)
+                            furnaceItemStacks[SLOT_FUEL].stackSize--;
+                            if(furnaceItemStacks[SLOT_FUEL].stackSize <= 0)
                             {
-                                furnaceItemStacks[1] = null;
+                                furnaceItemStacks[SLOT_FUEL] = null;
                             }
                         }
                     }
@@ -240,7 +244,7 @@ public class TileEntityFurnace extends TileEntity
 
     private boolean canSmelt()
     {
-        if(furnaceItemStacks[0] == null)
+        if(furnaceItemStacks[SLOT_INGREDIENT] == null)
         {
             return false;
         }
@@ -257,19 +261,19 @@ public class TileEntityFurnace extends TileEntity
         {
             return false;
         }
-        if(furnaceItemStacks[2] == null)
+        if(furnaceItemStacks[SLOT_RESULT] == null)
         {
             return true;
         }
-        if(!furnaceItemStacks[2].isItemEqual(itemstack))
+        if(!furnaceItemStacks[SLOT_RESULT].isItemEqual(itemstack))
         {
             return false;
         }
-        if(furnaceItemStacks[2].stackSize < getMaxStackSize() && furnaceItemStacks[2].stackSize < furnaceItemStacks[2].getMaxStackSize())
+        if(furnaceItemStacks[SLOT_RESULT].stackSize < getMaxStackSize() && furnaceItemStacks[2].stackSize < furnaceItemStacks[SLOT_RESULT].getMaxStackSize())
         {
             return true;
         }
-        return furnaceItemStacks[2].stackSize < itemstack.getMaxStackSize();
+        return furnaceItemStacks[SLOT_RESULT].stackSize < itemstack.getMaxStackSize();
     }
 
     public void smeltItem()
@@ -282,25 +286,25 @@ public class TileEntityFurnace extends TileEntity
         ItemStack itemstack = null;
         for (RecipeEntryFurnace recipeEntryBase : list) {
             if(recipeEntryBase != null){
-                if(recipeEntryBase.matches(furnaceItemStacks[0])){
+                if(recipeEntryBase.matches(furnaceItemStacks[SLOT_INGREDIENT])){
                     itemstack = recipeEntryBase.getOutput();
                 }
             }
         }
-        boolean wasEmpty = furnaceItemStacks[2] == null;
-        if(furnaceItemStacks[2] == null && itemstack != null)
+        boolean wasEmpty = furnaceItemStacks[SLOT_RESULT] == null;
+        if(furnaceItemStacks[SLOT_RESULT] == null && itemstack != null)
         {
-            furnaceItemStacks[2] = itemstack.copy();
-        } else if(furnaceItemStacks[2] != null && itemstack != null && furnaceItemStacks[2].itemID == itemstack.itemID)
+            furnaceItemStacks[SLOT_RESULT] = itemstack.copy();
+        } else if(furnaceItemStacks[SLOT_RESULT] != null && itemstack != null && furnaceItemStacks[SLOT_RESULT].itemID == itemstack.itemID)
         {
-            furnaceItemStacks[2].stackSize += itemstack.stackSize;
+            furnaceItemStacks[SLOT_RESULT].stackSize += itemstack.stackSize;
         }
-        furnaceItemStacks[0].stackSize--;
-        if(furnaceItemStacks[0].stackSize <= 0)
+        furnaceItemStacks[SLOT_INGREDIENT].stackSize--;
+        if(furnaceItemStacks[SLOT_INGREDIENT].stackSize <= 0)
         {
-            furnaceItemStacks[0] = null;
+            furnaceItemStacks[SLOT_INGREDIENT] = null;
         }
-        if (worldObj != null && wasEmpty && furnaceItemStacks[2] != null) {
+        if (worldObj != null && wasEmpty && furnaceItemStacks[SLOT_RESULT] != null) {
             worldObj.markBlockNeedsUpdate(x, y, z);
         }
     }
@@ -372,7 +376,7 @@ public class TileEntityFurnace extends TileEntity
 
     @Override
     public Packet getDescriptionPacket() {
-        if (furnaceItemStacks[2] != null) {
+        if (furnaceItemStacks[SLOT_RESULT] != null) {
             return new PacketTileEntityData(this);
         }
         return null;
@@ -406,10 +410,4 @@ public class TileEntityFurnace extends TileEntity
     public CarriedBlock getCarriedEntry(World world, Entity holder, Block<?> currentBlock, int currentMeta) {
         return super.getCarriedEntry(world, holder, currentBlock, (currentMeta & ~0b111) | 0b010 /*Reset rotation, primarily to make it render in hand properly*/);
     }
-
-    protected ItemStack[] furnaceItemStacks;
-    public int maxBurnTime;
-    public int currentCookTime;
-    public int maxCookTime;
-    public int currentBurnTime;
 }
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
index a79f03de4..afa3343fe 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
@@ -38,19 +38,26 @@ import java.util.Random;
 public class TileEntityTrommel extends TileEntity
         implements Container
 {
-    public static final int FUEL_SLOT_INDEX = 4;
+    private static final Logger LOGGER = LogUtils.getLogger();
+
+    public static final int @NotNull [] SLOTS_INGREDIENT = { 0, 1, 2, 3 };
+    public static final int SLOT_FUEL = 4;
+
+    private static final int MAX_BURN_TIME = 50;
+
+    private final Random random = new Random();
+
+    private ItemStack[] itemStacks = new ItemStack[5];
+
+    public int burnTime = 0;
+    public int currentItemBurnTime = 0;
+    public int itemPopTime = 0;
+    private int nextToSieve = 0;
 
     public TileEntityTrommel()
     {
-        itemStacks = new ItemStack[5];
-        burnTime = 0;
-        currentItemBurnTime = 0;
-        itemPopTime = 0;
-        maxBurnTime = 50;
-        random = new Random();
-        nextToSieve = 1;
 //        for (ItemStack i : trommellableItems){
-//            simulateTrommelDrops(64 , i);
+//            simulateTrommelDrops(64, i);
 //        }
     }
 
@@ -122,7 +129,7 @@ public class TileEntityTrommel extends TileEntity
 
         burnTime = compoundTag.getShort("BurnTime");
         itemPopTime = compoundTag.getShort("CookTime");
-        currentItemBurnTime = getItemBurnTime(itemStacks[FUEL_SLOT_INDEX]);
+        currentItemBurnTime = getItemBurnTime(itemStacks[SLOT_FUEL]);
     }
 
     @Override
@@ -153,14 +160,14 @@ public class TileEntityTrommel extends TileEntity
 
     public float getCookProgressPercent(int i)
     {
-        return ((float)itemPopTime / maxBurnTime) *i;
+        return ((float)itemPopTime / MAX_BURN_TIME) *i;
     }
 
     public int getBurnTimeRemainingScaled(int i)
     {
         if(currentItemBurnTime == 0)
         {
-            currentItemBurnTime = maxBurnTime;
+            currentItemBurnTime = MAX_BURN_TIME;
         }
         return (burnTime * i) / currentItemBurnTime;
     }
@@ -174,8 +181,9 @@ public class TileEntityTrommel extends TileEntity
 	public void tick()
     {
         boolean isClient = worldObj == null ? carriedBlock.holder.world.isClientSide : worldObj.isClientSide;
-        if(nextToSieve > 4)
-            nextToSieve = 1;
+        if(nextToSieve >= SLOTS_INGREDIENT.length) {
+            nextToSieve = 0;
+        }
         boolean isBurning = burnTime > 0;
         boolean flag1 = false;
         if(isBurning)
@@ -185,10 +193,12 @@ public class TileEntityTrommel extends TileEntity
         if(!isClient)
         {
         	if(worldObj == null || worldObj.getBlockId(x, y, z) == Blocks.TROMMEL_IDLE.id()) {
-            	if(currentItemBurnTime == 0 && itemStacks[0] == null && itemStacks[1] == null && itemStacks[2] == null && itemStacks[3] == null) {
-            		if(itemStacks[FUEL_SLOT_INDEX] != null && itemStacks[FUEL_SLOT_INDEX].itemID == Blocks.COBBLE_NETHERRACK.id()) {
-            			itemStacks[FUEL_SLOT_INDEX].stackSize--;
-            			if(itemStacks[FUEL_SLOT_INDEX].stackSize <= 0) itemStacks[FUEL_SLOT_INDEX] = null;
+            	if(currentItemBurnTime == 0 && itemStacks[SLOTS_INGREDIENT[0]] == null && itemStacks[SLOTS_INGREDIENT[1]] == null && itemStacks[SLOTS_INGREDIENT[2]] == null && itemStacks[SLOTS_INGREDIENT[3]] == null) {
+            		if(itemStacks[SLOT_FUEL] != null && itemStacks[SLOT_FUEL].itemID == Blocks.COBBLE_NETHERRACK.id()) {
+            			itemStacks[SLOT_FUEL].stackSize--;
+            			if(itemStacks[SLOT_FUEL].stackSize <= 0) {
+                            itemStacks[SLOT_FUEL] = null;
+                        }
             			if (worldObj != null) {
                             BlockLogicTrommel.updateTrommelBlockState(worldObj, new TilePos(x, y, z), true);
                         } else if (carriedBlock != null) {
@@ -198,36 +208,37 @@ public class TileEntityTrommel extends TileEntity
                     }
             	}
         	}
-            if(!canProduce(nextToSieve))
-                nextToSieve = (nextToSieve+1)%4;
-            if(burnTime == 0 && canProduce(nextToSieve))
+            if(!canProduce(SLOTS_INGREDIENT[nextToSieve])) {
+                nextToSieve = (nextToSieve + 1) % SLOTS_INGREDIENT.length;
+            }
+            if(burnTime == 0 && canProduce(SLOTS_INGREDIENT[nextToSieve]))
             {
-                currentItemBurnTime = burnTime = getItemBurnTime(itemStacks[FUEL_SLOT_INDEX]);
+                currentItemBurnTime = burnTime = getItemBurnTime(itemStacks[SLOT_FUEL]);
                 if(burnTime > 0)
                 {
                     flag1 = true;
-                    if(itemStacks[FUEL_SLOT_INDEX] != null)
+                    if(itemStacks[SLOT_FUEL] != null)
                     {
-                    	if(itemStacks[FUEL_SLOT_INDEX].getItem() == Items.BUCKET_LAVA) {
-                    		itemStacks[FUEL_SLOT_INDEX] = new ItemStack(Items.BUCKET);
+                    	if(itemStacks[SLOT_FUEL].getItem() == Items.BUCKET_LAVA) {
+                    		itemStacks[SLOT_FUEL] = new ItemStack(Items.BUCKET);
                     	}else {
-                            itemStacks[FUEL_SLOT_INDEX].stackSize--;
-                            if(itemStacks[FUEL_SLOT_INDEX].stackSize <= 0)
+                            itemStacks[SLOT_FUEL].stackSize--;
+                            if(itemStacks[SLOT_FUEL].stackSize <= 0)
                             {
-                                itemStacks[FUEL_SLOT_INDEX] = null;
+                                itemStacks[SLOT_FUEL] = null;
                             }
                     	}
                     }
                 }
             }
-            if(isBurning() && canProduce(nextToSieve))
+            if(isBurning() && canProduce(SLOTS_INGREDIENT[nextToSieve]))
             {
                 itemPopTime++;
-                if(itemPopTime >= maxBurnTime)
+                if(itemPopTime >= MAX_BURN_TIME)
                 {
                     itemPopTime = 0;
-                    sieveItem(nextToSieve);
-                    nextToSieve = (nextToSieve+1)%4;
+                    sieveItem(SLOTS_INGREDIENT[nextToSieve]);
+                    nextToSieve = (nextToSieve + 1) % SLOTS_INGREDIENT.length;
                     flag1 = true;
                 }
             } else
@@ -551,14 +562,4 @@ public class TileEntityTrommel extends TileEntity
     public CarriedBlock getCarriedEntry(World world, Entity holder, Block<?> currentBlock, int currentMeta) {
         return super.getCarriedEntry(world, holder, currentBlock, (currentMeta & ~0b111) | 0b010 /*Reset rotation, primarily to make it render in hand properly*/);
     }
-
-    private static final Logger LOGGER = LogUtils.getLogger();
-    private ItemStack[] itemStacks;
-
-    public int burnTime;
-    public int currentItemBurnTime;
-    public int itemPopTime;
-    private final int maxBurnTime;
-    private int nextToSieve;
-    private final Random random;
 }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/EntityPainting.java b/game/core/src/main/java/net/minecraft/core/entity/EntityPainting.java
index 1e70d2221..6cbeeb3fa 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/EntityPainting.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/EntityPainting.java
@@ -14,6 +14,7 @@ import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.material.Material;
 import net.minecraft.core.util.helper.DamageType;
 import org.jetbrains.annotations.NotNull;
+import org.joml.Vector3dc;
 
 import java.util.*;
 
@@ -34,6 +35,7 @@ public class EntityPainting extends Entity {
         this.direction = 0;
         this.heightOffset = 0.0F;
         this.art = ArtType.Kebab;
+        this.viewScale /= 2;
         setSize(0.5F, 0.5F);
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/entity/player/Player.java b/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
index cc857a6c3..c72f07452 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
@@ -1509,7 +1509,7 @@ public abstract class Player
         //Find item with lowest stack size in inventory
         int itemSlot = -1;
         int stackSize = -1;
-        for (int i = 0; i < 36; i++) {
+        for (int i = 0; i < ContainerInventory.MAIN_INVENTORY_SIZE; i++) {
             ItemStack stack = this.inventory.getItem(i);
             if (stack != null && stack.itemID == selectItem.itemID && stack.getMetadata() == selectItem.getMetadata() && stack.getData().equals(selectItem.getData())) {
                 if (stackSize == -1 || stack.stackSize < stackSize) {
@@ -1522,7 +1522,7 @@ public abstract class Player
         if (itemSlot == -1) {
             if (getGamemode() == Gamemodes.CREATIVE) {
                 int emptySlot = -1;
-                for (int i = 0; i < 36; i++) {
+                for (int i = 0; i < ContainerInventory.MAIN_INVENTORY_SIZE; i++) {
                     if (this.inventory.getItem(i) == null) {
                         emptySlot = i;
                         break;
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemDye.java b/game/core/src/main/java/net/minecraft/core/item/ItemDye.java
index e8844b635..79e60efb6 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemDye.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemDye.java
@@ -63,7 +63,7 @@ public class ItemDye extends Item {
                 if (!world.isClientSide) {
                     if (world.getBlockType(blockPos.up(queryPos)).lightBlock() <= 2) {
                         @NotNull Block<?> grass = Blocks.GRASS;
-                        if (world.dimensionData.getWorldType() == WorldTypes.OVERWORLD_RETRO) {
+                        if (world.getDimensionData().getWorldType().isRetro()) {
                             grass = Blocks.GRASS_RETRO;
                         }
                         world.setBlockTypeNotify(blockPos, grass);
diff --git a/game/core/src/main/java/net/minecraft/core/lang/text/ChainText.java b/game/core/src/main/java/net/minecraft/core/lang/text/ChainText.java
index ea357434f..cf2127fbd 100644
--- a/game/core/src/main/java/net/minecraft/core/lang/text/ChainText.java
+++ b/game/core/src/main/java/net/minecraft/core/lang/text/ChainText.java
@@ -1,46 +1,46 @@
 package net.minecraft.core.lang.text;
 
 import net.minecraft.core.net.command.TextFormatting;
+import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-public class ChainText
-    extends Text {
-    private final List<Text> chain = new ArrayList<>();
+public class ChainText extends Text {
+    private final @NotNull List<@NotNull Text> chain = new ArrayList<>();
 
-    ChainText(Text... chain) {
+    public ChainText(final @NotNull Text @NotNull ... chain) {
         this.chain.addAll(Arrays.asList(chain));
     }
 
     @Override
-    void toString(StringBuilder sb) {
-        for (Text text : chain) {
+    void toString(final @NotNull StringBuilder sb) {
+        for (final @NotNull Text text : this.chain) {
             text.toString(sb);
         }
     }
 
     @Override
-    public Text trans(String languageKey) {
+    public @NotNull Text trans(final @NotNull String languageKey) {
         this.chain.add(new TranslatableText(languageKey));
         return this;
     }
 
     @Override
-    public Text func(FunctionText.TextGenerator generator) {
+    public @NotNull Text func(final FunctionText.@NotNull TextGenerator generator) {
         this.chain.add(new FunctionText(generator));
         return this;
     }
 
     @Override
-    public Text lit(String literal) {
+    public @NotNull Text lit(final @NotNull String literal) {
         this.chain.add(new LiteralText(literal));
         return this;
     }
 
     @Override
-    public Text fmt(TextFormatting formatting, Text text) {
+    public @NotNull Text fmt(final @NotNull TextFormatting formatting, final @NotNull Text text) {
         this.chain.add(new FormattedText(formatting, text));
         return this;
     }
diff --git a/game/core/src/main/java/net/minecraft/core/lang/text/FormattedText.java b/game/core/src/main/java/net/minecraft/core/lang/text/FormattedText.java
index 50e3dbc2c..3bd803569 100644
--- a/game/core/src/main/java/net/minecraft/core/lang/text/FormattedText.java
+++ b/game/core/src/main/java/net/minecraft/core/lang/text/FormattedText.java
@@ -1,21 +1,21 @@
 package net.minecraft.core.lang.text;
 
 import net.minecraft.core.net.command.TextFormatting;
+import org.jetbrains.annotations.NotNull;
 
-public class FormattedText
-    extends Text {
-    private final TextFormatting formatting;
-    private final Text text;
+public class FormattedText extends Text {
+    private final @NotNull TextFormatting formatting;
+    private final @NotNull Text text;
 
-    FormattedText(TextFormatting formatting, Text text) {
+    public FormattedText(final @NotNull TextFormatting formatting, final @NotNull Text text) {
         this.formatting = formatting;
         this.text = text;
     }
 
     @Override
-    void toString(StringBuilder sb) {
-        sb.append(formatting.toString());
-        text.toString(sb);
+    void toString(final @NotNull StringBuilder sb) {
+        sb.append(this.formatting);
+        this.text.toString(sb);
         sb.append(TextFormatting.RESET);
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/lang/text/FunctionText.java b/game/core/src/main/java/net/minecraft/core/lang/text/FunctionText.java
index 3454a98ff..aab2f1e44 100644
--- a/game/core/src/main/java/net/minecraft/core/lang/text/FunctionText.java
+++ b/game/core/src/main/java/net/minecraft/core/lang/text/FunctionText.java
@@ -1,19 +1,20 @@
 package net.minecraft.core.lang.text;
 
-public class FunctionText
-    extends Text {
-    private final TextGenerator generator;
+import org.jetbrains.annotations.NotNull;
 
-    FunctionText(TextGenerator generator) {
+public class FunctionText extends Text {
+    private final @NotNull TextGenerator generator;
+
+    public FunctionText(final @NotNull TextGenerator generator) {
         this.generator = generator;
     }
 
     @Override
-    void toString(StringBuilder sb) {
-        sb.append(generator.getString());
+    void toString(final @NotNull StringBuilder sb) {
+        this.generator.getText().toString(sb);
     }
 
     public interface TextGenerator {
-        String getString();
+        @NotNull Text getText();
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/lang/text/IntoText.java b/game/core/src/main/java/net/minecraft/core/lang/text/IntoText.java
new file mode 100644
index 000000000..49f23b7e7
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/lang/text/IntoText.java
@@ -0,0 +1,7 @@
+package net.minecraft.core.lang.text;
+
+import org.jetbrains.annotations.NotNull;
+
+public interface IntoText {
+    @NotNull Text intoText();
+}
diff --git a/game/core/src/main/java/net/minecraft/core/lang/text/LiteralText.java b/game/core/src/main/java/net/minecraft/core/lang/text/LiteralText.java
index bba248fa7..459bab9d8 100644
--- a/game/core/src/main/java/net/minecraft/core/lang/text/LiteralText.java
+++ b/game/core/src/main/java/net/minecraft/core/lang/text/LiteralText.java
@@ -1,15 +1,16 @@
 package net.minecraft.core.lang.text;
 
-public class LiteralText
-    extends Text {
-    private final String literal;
+import org.jetbrains.annotations.NotNull;
 
-    LiteralText(String literal) {
+public class LiteralText extends Text {
+    private final @NotNull String literal;
+
+    public LiteralText(final @NotNull String literal) {
         this.literal = literal;
     }
 
     @Override
-    void toString(StringBuilder sb) {
-        sb.append(literal);
+    void toString(final @NotNull StringBuilder sb) {
+        sb.append(this.literal);
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/lang/text/Text.java b/game/core/src/main/java/net/minecraft/core/lang/text/Text.java
index d5dfcc64e..a87d79dce 100644
--- a/game/core/src/main/java/net/minecraft/core/lang/text/Text.java
+++ b/game/core/src/main/java/net/minecraft/core/lang/text/Text.java
@@ -1,38 +1,40 @@
 package net.minecraft.core.lang.text;
 
 import net.minecraft.core.net.command.TextFormatting;
+import org.jetbrains.annotations.NotNull;
 
-public abstract class Text {
-    public static Text text() {
+public abstract class Text implements IntoText {
+    public static @NotNull Text text() {
         return new ChainText();
     }
 
     @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        toString(sb);
+    public @NotNull String toString() {
+        final @NotNull StringBuilder sb = new StringBuilder();
+        this.toString(sb);
         return sb.toString();
     }
 
-    abstract void toString(StringBuilder sb);
+    @Override
+    public @NotNull Text intoText() {
+        return this;
+    }
 
-//    public int width(Font font) { // Commented out due to being unused and referencing the client package
-//        return font.getStringWidth(this.toString());
-//    }
+    abstract void toString(@NotNull StringBuilder sb);
 
-    public Text trans(String languageKey) {
+    public @NotNull Text trans(final @NotNull String languageKey) {
         return new ChainText(this, new TranslatableText(languageKey));
     }
 
-    public Text func(FunctionText.TextGenerator generator) {
+    public @NotNull Text func(final FunctionText.@NotNull TextGenerator generator) {
         return new ChainText(this, new FunctionText(generator));
     }
 
-    public Text lit(String literal) {
+    public @NotNull Text lit(final @NotNull String literal) {
         return new ChainText(this, new LiteralText(literal));
     }
 
-    public Text fmt(TextFormatting formatting, Text text) {
+    public @NotNull Text fmt(final @NotNull TextFormatting formatting, final @NotNull Text text) {
         return new ChainText(this, new FormattedText(formatting, text));
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/lang/text/TranslatableText.java b/game/core/src/main/java/net/minecraft/core/lang/text/TranslatableText.java
index b3da433ad..2f72d58ff 100644
--- a/game/core/src/main/java/net/minecraft/core/lang/text/TranslatableText.java
+++ b/game/core/src/main/java/net/minecraft/core/lang/text/TranslatableText.java
@@ -1,18 +1,17 @@
 package net.minecraft.core.lang.text;
 
 import net.minecraft.core.lang.I18n;
+import org.jetbrains.annotations.NotNull;
 
-public class TranslatableText
-    extends Text {
-    private final String languageKey;
+public class TranslatableText extends Text {
+    private final @NotNull String languageKey;
 
-    TranslatableText(String languageKey) {
+    public TranslatableText(final @NotNull String languageKey) {
         this.languageKey = languageKey;
     }
 
     @Override
-    void toString(StringBuilder sb) {
-        sb.append(I18n.getInstance().translateKey(languageKey));
+    void toString(final @NotNull StringBuilder sb) {
+        sb.append(I18n.getInstance().translateKey(this.languageKey));
     }
-
 }
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/container/ContainerInventory.java b/game/core/src/main/java/net/minecraft/core/player/inventory/container/ContainerInventory.java
index ba98d5c82..1123be4bc 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/container/ContainerInventory.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/container/ContainerInventory.java
@@ -7,6 +7,7 @@ import com.mojang.nbt.tags.ListTag;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.enums.HumanArmorShape;
 import net.minecraft.core.enums.IArmorShape;
 import net.minecraft.core.item.IArmorItem;
 import net.minecraft.core.item.Item;
@@ -22,7 +23,7 @@ import java.util.Objects;
 public class ContainerInventory
     implements Container {
     public static final int MAIN_INVENTORY_SIZE = 36;
-    public static final int ARMOR_INVENTORY_SIZE = 4;
+    public static final int ARMOR_INVENTORY_SIZE = HumanArmorShape.values().length;
     public static final int HOTBAR_SIZE = 9;
 
     private static final int NBT_ARMOR_SLOT_OFFSET = 100;
@@ -334,7 +335,7 @@ public class ContainerInventory
 
     @Override
     public int getContainerSize() {
-        return this.mainInventory.length + 4;
+        return MAIN_INVENTORY_SIZE + ARMOR_INVENTORY_SIZE;
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuAbstract.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuAbstract.java
index 041b7f075..a79170070 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuAbstract.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuAbstract.java
@@ -25,6 +25,9 @@ import org.slf4j.Logger;
 public abstract class MenuAbstract
 {
 	private static final Logger LOGGER = LogUtils.getLogger();
+
+	public static final int NUM_COLUMNS = 9;
+
 	public List<ItemStack> lastSlots;
 	public List<Slot> slots;
 	public int containerId;
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuActivator.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuActivator.java
index 4495cfc63..ac4831969 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuActivator.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuActivator.java
@@ -7,6 +7,7 @@ import net.minecraft.core.crafting.ContainerListener;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.player.inventory.container.Container;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
 import net.minecraft.core.player.inventory.slot.Slot;
 import org.jetbrains.annotations.NotNull;
 
@@ -14,11 +15,14 @@ public class MenuActivator extends MenuAbstract {
 	public static final int ID_SELECTOR = 0;
 	public static final int ID_LOCKED_SLOTS = 1;
 
-	public TileEntityActivator activator;
+	public static final int ACTIVATOR_INVENTORY_SIZE = 9;
+
+	public static final int ACTIVATOR_SLOTS_START = 0;
+	public static final int INVENTORY_SLOTS_START = ACTIVATOR_SLOTS_START + ACTIVATOR_INVENTORY_SIZE;
+	public static final int HOTBAR_SLOTS_START = INVENTORY_SLOTS_START + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE);
 
-	private final int activatorSlotsStart;
-	private final int inventorySlotsStart;
-	private final int hotbarSlotsStart;
+
+	public TileEntityActivator activator;
 
 	private int selectorSlot;
 	private int lockedSlots;
@@ -29,19 +33,15 @@ public class MenuActivator extends MenuAbstract {
 			addSlot(new Slot(activator, i, 8 + i * 18, 35));
 		}
 
-		for (int j = 0; j < 3; j++) {
-			for (int i1 = 0; i1 < 9; i1++) {
-				addSlot(new Slot(container, i1 + j * 9 + 9, 8 + i1 * 18, 84 + j * 18));
+		for (int y = 0; y < (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) / NUM_COLUMNS; y++) {
+			for (int x = 0; x < NUM_COLUMNS; x++) {
+				addSlot(new Slot(container, x + y * NUM_COLUMNS + ContainerInventory.HOTBAR_SIZE, 8 + x * 18, 84 + y * 18));
 			}
 		}
 
-		for (int k = 0; k < 9; k++) {
-			addSlot(new Slot(container, k, 8 + k * 18, 142));
+		for (int i = 0; i < ContainerInventory.HOTBAR_SIZE; i++) {
+			addSlot(new Slot(container, i, 8 + i * 18, 142));
 		}
-		
-		activatorSlotsStart = 0;
-		inventorySlotsStart = 9;
-		hotbarSlotsStart = 36;
 
 		selectorSlot = 0;
 		lockedSlots = 0;
@@ -87,21 +87,21 @@ public class MenuActivator extends MenuAbstract {
 
 	@Override
 	public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= activatorSlotsStart && slot.index < inventorySlotsStart) {
-			return getSlots(activatorSlotsStart, 9, false);
+		if(slot.index >= ACTIVATOR_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
+			return getSlots(ACTIVATOR_SLOTS_START, ACTIVATOR_INVENTORY_SIZE, false);
 		}
 		
 		if(action == InventoryAction.MOVE_ALL) {
-			if(slot.index >= inventorySlotsStart && slot.index < hotbarSlotsStart) {
-				return getSlots(inventorySlotsStart, 27, false);
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
+				return getSlots(INVENTORY_SLOTS_START, (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE), false);
 			}
-			if(slot.index >= hotbarSlotsStart) {
-				return getSlots(hotbarSlotsStart, 9, false);
+			if(slot.index >= HOTBAR_SLOTS_START) {
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false);
 			}
 		}
 		if(action == InventoryAction.MOVE_SIMILAR) {
-			if(slot.index >= inventorySlotsStart) {
-				return getSlots(inventorySlotsStart, 36, false);
+			if(slot.index >= INVENTORY_SLOTS_START) {
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 			}
 		}
 		return null;
@@ -109,10 +109,10 @@ public class MenuActivator extends MenuAbstract {
 
 	@Override
 	public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= activatorSlotsStart && slot.index < inventorySlotsStart) {
-			return getSlots(inventorySlotsStart, 36, false);
+		if(slot.index >= ACTIVATOR_INVENTORY_SIZE && slot.index < INVENTORY_SLOTS_START) {
+			return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 		}else {
-			return getSlots(activatorSlotsStart, 9, false);
+			return getSlots(ACTIVATOR_SLOTS_START, ACTIVATOR_INVENTORY_SIZE, false);
 		}
 	}
 }
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuContainer.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuContainer.java
index 2eab6553e..b89ba7873 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuContainer.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuContainer.java
@@ -4,34 +4,34 @@ import it.unimi.dsi.fastutil.ints.IntList;
 import net.minecraft.core.InventoryAction;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.player.inventory.container.Container;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
 import net.minecraft.core.player.inventory.slot.Slot;
 import org.jetbrains.annotations.NotNull;
 
 public class MenuContainer extends MenuAbstract {
-	
 	private Container container;
 	private int numberOfRows;
 	
 	public MenuContainer(Container playerContainer, Container container) {
 		this.container = container;
-		numberOfRows = container.getContainerSize() / 9;
-		int i = (numberOfRows - 4) * 18;
-		for (int j = 0; j < numberOfRows; j++) {
-			for (int i1 = 0; i1 < 9; i1++) {
-				addSlot(new Slot(container, i1 + j * 9, 8 + i1 * 18, 18 + j * 18));
+		numberOfRows = container.getContainerSize() / NUM_COLUMNS;
+		int playerInventoryYOffset = (numberOfRows - 4) * 18;
+		for (int y = 0; y < numberOfRows; y++) {
+			for (int x = 0; x < NUM_COLUMNS; x++) {
+				addSlot(new Slot(container, x + y * NUM_COLUMNS, 8 + x * 18, 18 + y * 18));
 			}
 
 		}
 
-		for (int k = 0; k < 3; k++) {
-			for (int j1 = 0; j1 < 9; j1++) {
-				addSlot(new Slot(playerContainer, j1 + k * 9 + 9, 8 + j1 * 18, 103 + k * 18 + i));
+		for (int y = 0; y < (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) / NUM_COLUMNS; y++) {
+			for (int x = 0; x < NUM_COLUMNS; x++) {
+				addSlot(new Slot(playerContainer, x + y * NUM_COLUMNS + ContainerInventory.HOTBAR_SIZE, 8 + x * 18, 103 + y * 18 + playerInventoryYOffset));
 			}
 
 		}
 
-		for (int l = 0; l < 9; l++) {
-			addSlot(new Slot(playerContainer, l, 8 + l * 18, 161 + i));
+		for (int i = 0; i < ContainerInventory.HOTBAR_SIZE; i++) {
+			addSlot(new Slot(playerContainer, i, 8 + i * 18, 161 + playerInventoryYOffset));
 		}
 
 	}
@@ -43,7 +43,7 @@ public class MenuContainer extends MenuAbstract {
 	
 	@Override
 	public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		int chestSize = numberOfRows * 9;
+		int chestSize = numberOfRows * NUM_COLUMNS;
 		
 		if(slot.index >= 0 && slot.index < chestSize) {
 			// Chest inventory
@@ -51,18 +51,18 @@ public class MenuContainer extends MenuAbstract {
 		}
 		
 		if(action == InventoryAction.MOVE_ALL) {
-			if(slot.index >= chestSize && slot.index < chestSize + 27) {
+			if(slot.index >= chestSize && slot.index < chestSize + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE)) {
 				// Main ContainerInventory
-				return getSlots(chestSize, 27, false);
+				return getSlots(chestSize, (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE), false);
 			}
-			if(slot.index >= chestSize + 27 && slot.index < chestSize + 36) {
+			if(slot.index >= chestSize + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) && slot.index < chestSize + ContainerInventory.MAIN_INVENTORY_SIZE) {
 				// Hotbar
-				return getSlots(chestSize + 27, 9, false);
+				return getSlots(chestSize + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE), ContainerInventory.HOTBAR_SIZE, false);
 			}
 		}else {
-			if(slot.index >= chestSize && slot.index < chestSize + 36) {
+			if(slot.index >= chestSize && slot.index < chestSize + ContainerInventory.MAIN_INVENTORY_SIZE) {
 				// Full ContainerInventory
-				return getSlots(chestSize, 36, false);
+				return getSlots(chestSize, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 			}
 		}
 		
@@ -71,11 +71,11 @@ public class MenuContainer extends MenuAbstract {
 
 	@Override
 	public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		int chestSize = numberOfRows * 9;
+		int chestSize = numberOfRows * NUM_COLUMNS;
 		
 		if(slot.index < chestSize) {
 			// Clicked chest
-			return getSlots(chestSize, 36, true); // ContainerInventory
+			return getSlots(chestSize, ContainerInventory.MAIN_INVENTORY_SIZE, true); // ContainerInventory
 		}else {
 			// Clicked ContainerInventory
 			return getSlots  (0, chestSize, false); // Chest
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuCrafting.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuCrafting.java
index dc27d2b6b..a1ba6266b 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuCrafting.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuCrafting.java
@@ -18,33 +18,44 @@ import net.minecraft.core.world.pos.TilePosc;
 import org.jetbrains.annotations.NotNull;
 
 public class MenuCrafting extends MenuAbstract {
+    private static final int NUM_CRAFTING_COLUMNS = 3;
+    private static final int NUM_CRAFTING_ROWS = 3;
+    private static final int NUM_RESULT_SLOTS = 1;
+
+    private static final int RESULT_SLOTS_START = 0;
+    private static final int CRAFTING_SLOTS_START = RESULT_SLOTS_START + NUM_RESULT_SLOTS;
+    private static final int INVENTORY_SLOTS_START = CRAFTING_SLOTS_START + (NUM_CRAFTING_COLUMNS * NUM_CRAFTING_ROWS);
+    private static final int HOTBAR_SLOTS_START = INVENTORY_SLOTS_START + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE);
+
     public final @NotNull ContainerCrafting craftSlots;
     public final @NotNull Container resultSlots;
     private final @NotNull World world;
     private final @NotNull TilePos tilePos;
 
     public MenuCrafting(@NotNull ContainerInventory inventory, @NotNull World world, @NotNull TilePosc tilePos) {
-        this.craftSlots = new ContainerCrafting(this, 3, 3);
+        this.craftSlots = new ContainerCrafting(this, NUM_CRAFTING_COLUMNS, NUM_CRAFTING_ROWS);
         this.resultSlots = new ContainerResult();
         this.world = world;
         this.tilePos = new TilePos(tilePos);
+
         addSlot(new SlotResult(inventory.player, this.craftSlots, this.resultSlots, 0, 124, 35));
-        for (int l = 0; l < 3; l++) {
-            for (int k1 = 0; k1 < 3; k1++) {
-                addSlot(new Slot(this.craftSlots, k1 + l * 3, 30 + k1 * 18, 17 + l * 18));
+
+        for (int y = 0; y < NUM_CRAFTING_ROWS; y++) {
+            for (int x = 0; x < NUM_CRAFTING_COLUMNS; x++) {
+                addSlot(new Slot(this.craftSlots, x + y * NUM_CRAFTING_COLUMNS, 30 + x * 18, 17 + y * 18));
             }
 
         }
 
-        for (int i1 = 0; i1 < 3; i1++) {
-            for (int l1 = 0; l1 < 9; l1++) {
-                addSlot(new Slot(inventory, l1 + i1 * 9 + 9, 8 + l1 * 18, 84 + i1 * 18));
+        for (int y = 0; y < (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) / NUM_COLUMNS; y++) {
+            for (int x = 0; x < 9; x++) {
+                addSlot(new Slot(inventory, x + y * NUM_COLUMNS + ContainerInventory.HOTBAR_SIZE, 8 + x * 18, 84 + y * 18));
             }
 
         }
 
-        for (int j1 = 0; j1 < 9; j1++) {
-            addSlot(new Slot(inventory, j1, 8 + j1 * 18, 142));
+        for (int i = 0; i < ContainerInventory.HOTBAR_SIZE; i++) {
+            addSlot(new Slot(inventory, i, 8 + i * 18, 142));
         }
 
         slotsChanged(this.craftSlots);
@@ -90,27 +101,27 @@ public class MenuCrafting extends MenuAbstract {
 
     @Override
     public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-        if (slot.index == 0) {
+        if (slot.index >= RESULT_SLOTS_START && slot.index < CRAFTING_SLOTS_START) {
             // Craft Result
-            return getSlots(0, 1, false);
+            return getSlots(RESULT_SLOTS_START, NUM_RESULT_SLOTS, false);
         }
-        if (slot.index >= 1 && slot.index < 9) {
+        if (slot.index >= CRAFTING_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
             // Crafting Grid
-            return getSlots(1, 9, false);
+            return getSlots(CRAFTING_SLOTS_START, NUM_CRAFTING_COLUMNS * NUM_CRAFTING_ROWS, false);
         }
         if (action == InventoryAction.MOVE_SIMILAR) {
-            if (slot.index >= 10 && slot.index <= 45) {
+            if (slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
                 // Full ContainerInventory
-                return getSlots(10, 36, false);
+                return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
             }
         } else {
-            if (slot.index >= 10 && slot.index <= 36) {
+            if (slot.index >= INVENTORY_SLOTS_START && slot.index <= HOTBAR_SLOTS_START) {
                 // Main ContainerInventory
-                return getSlots(10, 27, false);
+                return getSlots(INVENTORY_SLOTS_START, (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE), false);
             }
-            if (slot.index >= 37 && slot.index <= 45) {
+            if (slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
                 // Hotbar
-                return getSlots(37, 9, false);
+                return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false);
             }
         }
         return null;
@@ -118,27 +129,27 @@ public class MenuCrafting extends MenuAbstract {
 
     @Override
     public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-        if (slot.index >= 10 && slot.index <= 45) {
+        if (slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
             // Full ContainerInventory
             if (target == 1) {
-                return getSlots(1, 9, false); // To Crafting Grid
+                return getSlots(CRAFTING_SLOTS_START, NUM_CRAFTING_COLUMNS * NUM_CRAFTING_ROWS, false); // To Crafting Grid
             }
-            if (slot.index >= 10 && slot.index <= 36) {
+            if (slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
                 // Main ContainerInventory
-                return getSlots(37, 9, false); // To Hotbar
+                return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false); // To Hotbar
             }
-            if (slot.index >= 37 && slot.index <= 45) {
+            if (slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
                 // Hotbar
-                return getSlots(10, 27, false); // To Main ContainerInventory
+                return getSlots(INVENTORY_SLOTS_START, (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE), false); // To Main ContainerInventory
             }
         } else {
             // Crafting grid or result
-            if (slot.index == 0) {
+            if (slot.index == RESULT_SLOTS_START) {
                 // Crafting Result
-                return getSlots(10, 36, true);
+                return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, true);
             } else {
                 // Crafting Grid
-                return getSlots(10, 36, false);
+                return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
             }
         }
 
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuDispenser.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuDispenser.java
new file mode 100644
index 000000000..3f6df7cb3
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuDispenser.java
@@ -0,0 +1,79 @@
+package net.minecraft.core.player.inventory.menu;
+
+import it.unimi.dsi.fastutil.ints.IntList;
+import net.minecraft.core.InventoryAction;
+import net.minecraft.core.block.entity.TileEntityDispenser;
+import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.player.inventory.container.Container;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
+import net.minecraft.core.player.inventory.slot.Slot;
+import org.jetbrains.annotations.NotNull;
+
+public class MenuDispenser extends MenuAbstract {
+
+
+	private static final int NUM_DISPENSER_COLUMNS = 3;
+	private static final int NUM_DISPENSER_ROWS = 3;
+
+	private static final int DISPENSER_SLOTS_START = 0;
+	private static final int INVENTORY_SLOTS_START = DISPENSER_SLOTS_START + (NUM_DISPENSER_COLUMNS * NUM_DISPENSER_ROWS);
+	private static final int HOTBAR_SLOTS_START = INVENTORY_SLOTS_START + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE);
+
+	public TileEntityDispenser dispenser;
+
+	public MenuDispenser(Container container, TileEntityDispenser dispenser) {
+		this.dispenser = dispenser;
+		for (int y = 0; y < NUM_DISPENSER_ROWS; y++) {
+			for (int x = 0; x < NUM_DISPENSER_COLUMNS; x++) {
+				addSlot(new Slot(dispenser, x + y * NUM_DISPENSER_COLUMNS, 62 + x * 18, 17 + y * 18));
+			}
+
+		}
+
+		for (int y = 0; y < (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) / NUM_COLUMNS; y++) {
+			for (int x = 0; x < NUM_COLUMNS; x++) {
+				addSlot(new Slot(container, x + y * NUM_COLUMNS + ContainerInventory.HOTBAR_SIZE, 8 + x * 18, 84 + y * 18));
+			}
+		}
+
+		for (int i = 0; i < ContainerInventory.HOTBAR_SIZE; i++) {
+			addSlot(new Slot(container, i, 8 + i * 18, 142));
+		}
+	}
+	
+	@Override
+	public boolean stillValid(@NotNull Player player) {
+		return dispenser.stillValid(player);
+	}
+
+	@Override
+	public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
+		if(slot.index >= DISPENSER_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
+			return getSlots(DISPENSER_SLOTS_START, NUM_DISPENSER_COLUMNS * NUM_DISPENSER_ROWS, false);
+		}
+		
+		if(action == InventoryAction.MOVE_ALL) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false);
+			}
+			if(slot.index >= HOTBAR_SLOTS_START) {
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false);
+			}
+		}
+		if(action == InventoryAction.MOVE_SIMILAR) {
+			if(slot.index >= INVENTORY_SLOTS_START) {
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
+		if(slot.index >= DISPENSER_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
+			return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
+		}else {
+			return getSlots(DISPENSER_SLOTS_START, NUM_DISPENSER_COLUMNS * NUM_DISPENSER_ROWS, false);
+		}
+	}
+}
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFlag.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFlag.java
index ca8ba176d..393fc9003 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFlag.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFlag.java
@@ -5,30 +5,37 @@ import net.minecraft.core.InventoryAction;
 import net.minecraft.core.block.entity.TileEntityFlag;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.player.inventory.container.Container;
+import net.minecraft.core.player.inventory.container.ContainerInventory;
 import net.minecraft.core.player.inventory.slot.Slot;
 import net.minecraft.core.player.inventory.slot.SlotDye;
 import org.jetbrains.annotations.NotNull;
 
 public class MenuFlag extends MenuAbstract {
+	private static final int NUM_DYE_SLOTS = 3;
+
+	private static final int DYE_SLOTS_START = 0;
+	private static final int INVENTORY_SLOTS_START = DYE_SLOTS_START + NUM_DYE_SLOTS;
+	private static final int HOTBAR_SLOTS_START = INVENTORY_SLOTS_START + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE);
+
 	public TileEntityFlag flag;
 
 	public MenuFlag(Container inv, TileEntityFlag flag) {
 		this.flag = flag;
 
 		// Dye slots
-		addSlot(new SlotDye(flag, 36, 129, 10));
-		addSlot(new SlotDye(flag, 37, 129, 32));
-		addSlot(new SlotDye(flag, 38, 129, 54));
+		for (int i = 0; i < NUM_DYE_SLOTS; i++) {
+			addSlot(new SlotDye(flag, i, 129, 10 + 22 * i));
+		}
 
 		// Player inv slots
-		for (int y = 0; y < 3; y++) {
-			for (int x = 0; x < 9; x++) {
-				addSlot(new Slot(inv, x + y * 9 + 9, 8 + x * 18, 113 + y * 18));
+		for (int y = 0; y < (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) / NUM_COLUMNS; y++) {
+			for (int x = 0; x < NUM_COLUMNS; x++) {
+				addSlot(new Slot(inv, x + y * NUM_COLUMNS + ContainerInventory.HOTBAR_SIZE, 8 + x * 18, 113 + y * 18));
 			}
 		}
 
 		// Player hotbar
-		for (int i = 0; i < 9; i++) {
+		for (int i = 0; i < ContainerInventory.HOTBAR_SIZE; i++) {
 			addSlot(new Slot(inv, i, 8 + i * 18, 171));
 		}
 	}
@@ -40,21 +47,21 @@ public class MenuFlag extends MenuAbstract {
 	 */
 	@Override
 	public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= 0 && slot.index <= 2) {
+		if(slot.index >= DYE_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
 			// Dye Slots
-			return getSlots(0, 3, false);
+			return getSlots(DYE_SLOTS_START, NUM_DYE_SLOTS, false);
 		}
 		if(action == InventoryAction.MOVE_SIMILAR) {
 			// Full ContainerInventory
-			return getSlots(3, 36, false);
+			return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 		}
 		if(action == InventoryAction.MOVE_ALL) {
-			if(slot.index < 27) {
+			if(slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(3, 27, false);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false);
 			}else {
 				// Hotbar
-				return getSlots(30, 9, false);
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false);
 			}
 		}
 		return null;
@@ -62,11 +69,11 @@ public class MenuFlag extends MenuAbstract {
 
 	@Override
 	public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= 0 && slot.index <= 2) {
+		if(slot.index >= DYE_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
 			// Dye Slots
-			return getSlots(3, 36, false); // Into ContainerInventory
+			return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false); // Into ContainerInventory
 		}
-		return getSlots(0, 3, false); // Dye Slots
+		return getSlots(DYE_SLOTS_START, NUM_DYE_SLOTS, false); // Dye Slots
 	}
 
 	@Override
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFurnace.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFurnace.java
index 26ce3aa02..201ccc0c3 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFurnace.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFurnace.java
@@ -16,30 +16,36 @@ public class MenuFurnace extends MenuAbstract {
 	public static final int ID_MAX_COOK_TIME = 2;
 	public static final int ID_MAX_BURN_TIME = 3;
 
+	private static final int NUM_INGREDIENT_SLOTS = 1;
+	private static final int NUM_FUEL_SLOTS = 1;
+	private static final int NUM_RESULT_SLOTS = 1;
+
+	private static final int INGREDIENT_SLOTS_START = 0;
+	private static final int FUEL_SLOTS_START = INGREDIENT_SLOTS_START + NUM_INGREDIENT_SLOTS;
+	private static final int RESULT_SLOTS_START = FUEL_SLOTS_START + NUM_FUEL_SLOTS;
+	private static final int INVENTORY_SLOTS_START = RESULT_SLOTS_START + NUM_RESULT_SLOTS;
+	private static final int HOTBAR_SLOTS_START = INVENTORY_SLOTS_START + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE);
+
 	public TileEntityFurnace furnace;
-	private int currentCookTime;
-	private int currentBurnTime;
-	private int itemBurnTime;
-	private int itemCookTime;
+	private int currentCookTime = 0;
+	private int currentBurnTime = 0;
+	private int itemBurnTime = 0;
+	private int itemCookTime = 0;
 
 	public MenuFurnace(ContainerInventory inventory, TileEntityFurnace tileEntity) {
-		currentCookTime = 0;
-		currentBurnTime = 0;
-		itemCookTime = 0;
-		itemBurnTime = 0;
 		furnace = tileEntity;
-		addSlot(new Slot(tileEntity, 0, 56, 17));
-		addSlot(new Slot(tileEntity, 1, 56, 53));
-		addSlot(new SlotFurnace(inventory.player, tileEntity, 2, 116, 35));
-		for (int i = 0; i < 3; i++) {
-			for (int k = 0; k < 9; k++) {
-				addSlot(new Slot(inventory, k + i * 9 + 9, 8 + k * 18, 84 + i * 18));
-			}
+		addSlot(new Slot(tileEntity, TileEntityFurnace.SLOT_INGREDIENT, 56, 17));
+		addSlot(new Slot(tileEntity, TileEntityFurnace.SLOT_FUEL, 56, 53));
+		addSlot(new SlotFurnace(inventory.player, tileEntity, TileEntityFurnace.SLOT_RESULT, 116, 35));
 
+		for (int y = 0; y < (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) / NUM_COLUMNS; y++) {
+			for (int x = 0; x < NUM_COLUMNS; x++) {
+				addSlot(new Slot(inventory, x + y * NUM_COLUMNS + ContainerInventory.HOTBAR_SIZE, 8 + x * 18, 84 + y * 18));
+			}
 		}
 
-		for (int j = 0; j < 9; j++) {
-			addSlot(new Slot(inventory, j, 8 + j * 18, 142));
+		for (int i = 0; i < ContainerInventory.HOTBAR_SIZE; i++) {
+			addSlot(new Slot(inventory, i, 8 + i * 18, 142));
 		}
 
 	}
@@ -101,53 +107,61 @@ public class MenuFurnace extends MenuAbstract {
 
 	@Override
 	public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= 0 && slot.index <= 3) return getSlots(slot.index, 1, false);
+		if (slot.index >= INGREDIENT_SLOTS_START && slot.index < FUEL_SLOTS_START) {
+            return getSlots(INGREDIENT_SLOTS_START, NUM_INGREDIENT_SLOTS, false);
+        }
+		if (slot.index >= FUEL_SLOTS_START && slot.index < RESULT_SLOTS_START) {
+			return getSlots(FUEL_SLOTS_START, NUM_FUEL_SLOTS, false);
+		}
+		if (slot.index >= RESULT_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
+			return getSlots(RESULT_SLOTS_START, NUM_RESULT_SLOTS, false);
+		}
 
 		if(action == InventoryAction.MOVE_ALL) {
-			if(slot.index >= 3 && slot.index <= 30) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(3, 27, false);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false);
 			}
-			if(slot.index >= 30 && slot.index <= 38) {
+			if(slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Hotbar
-				return getSlots(30, 9, false);
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false);
 			}
 		}
-		if(slot.index >= 3 && slot.index <= 38) {
+		if(slot.index >= INVENTORY_SLOTS_START && slot.index < INVENTORY_SLOTS_START + ContainerInventory.MAIN_INVENTORY_SIZE) {
 			// Full ContainerInventory
-			return getSlots(3, 36, false);
+			return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 		}
 		return null;
 	}
 
 	@Override
 	public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= 3 && slot.index <= 39) {
+		if(slot.index >= INVENTORY_SLOTS_START && slot.index <= INVENTORY_SLOTS_START + ContainerInventory.MAIN_INVENTORY_SIZE) {
 			// Full ContainerInventory
 			if(action != InventoryAction.MOVE_ALL) {
 				if(target == 1) {
-					return getSlots(0, 1, false); // Into Ingredient Slot
+					return getSlots(INGREDIENT_SLOTS_START, NUM_INGREDIENT_SLOTS, false); // Into Ingredient Slot
 				}
 				if(target == 2) {
-					return getSlots(1, 1, false); // Into Fuel Slot
+					return getSlots(FUEL_SLOTS_START, NUM_FUEL_SLOTS, false); // Into Fuel Slot
 				}
 			}
-			if(slot.index >= 3 && slot.index <= 29) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(30, 9, false); // Into Hotbar
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false); // Into Hotbar
 			}
-			if(slot.index >= 31 && slot.index <= 38) {
+			if(slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Hotbar
-				return getSlots(3, 27, false); // Into Main ContainerInventory
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false); // Into Main ContainerInventory
 			}
 		}
-		if(slot.index >= 0 && slot.index <= 2) {
-			if(slot.index == 2) {
+		if(slot.index >= INGREDIENT_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
+			if(slot.index == RESULT_SLOTS_START) {
 				// Output
-				return getSlots(3, 36, true);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, true);
 			}else {
 				// Input and Fuel
-				return getSlots(3, 36, false);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 			}
 		}
 
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFurnaceBlast.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFurnaceBlast.java
index 833b062c0..1d2bf9fa3 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFurnaceBlast.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuFurnaceBlast.java
@@ -16,17 +16,24 @@ public class MenuFurnaceBlast extends MenuAbstract {
 	public static final int ID_MAX_COOK_TIME = 2;
 	public static final int ID_MAX_BURN_TIME = 3;
 
+	private static final int NUM_INGREDIENT_SLOTS = 2;
+	private static final int NUM_FUEL_SLOTS = 1;
+	private static final int NUM_RESULT_SLOTS = 1;
+
+	private static final int INGREDIENT_SLOTS_START = 0;
+	private static final int FUEL_SLOTS_START = INGREDIENT_SLOTS_START + NUM_INGREDIENT_SLOTS;
+	private static final int RESULT_SLOTS_START = FUEL_SLOTS_START + NUM_FUEL_SLOTS;
+	private static final int INVENTORY_SLOTS_START = RESULT_SLOTS_START + NUM_RESULT_SLOTS;
+	private static final int HOTBAR_SLOTS_START = INVENTORY_SLOTS_START + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE);
+
+
 	public TileEntityFurnaceBlast furnace;
-	private int currentCookTime;
-	private int currentBurnTime;
-	private int itemBurnTime;
-	private int itemCookTime;
+	private int currentCookTime = 0;
+	private int currentBurnTime = 0;
+	private int itemBurnTime = 0;
+	private int itemCookTime = 0;
 
 	public MenuFurnaceBlast(final ContainerInventory inventory, final TileEntityFurnaceBlast tileEntity) {
-        this.currentCookTime = 0;
-        this.currentBurnTime = 0;
-        this.itemCookTime = 0;
-        this.itemBurnTime = 0;
         this.furnace = tileEntity;
 
 		addSlot(new Slot(tileEntity, TileEntityFurnaceBlast.SLOT_LEFT, 46, 17));
@@ -34,14 +41,14 @@ public class MenuFurnaceBlast extends MenuAbstract {
 		addSlot(new Slot(tileEntity, TileEntityFurnaceBlast.SLOT_FUEL, 56, 53));
 		addSlot(new SlotFurnace(inventory.player, tileEntity, TileEntityFurnaceBlast.SLOT_RESULT, 116, 35));
 
-		for (int row = 0; row < 3; row++) {
-			for (int col = 0; col < 9; col++) {
-				addSlot(new Slot(inventory, col + row * 9 + 9, 8 + col * 18, 84 + row * 18));
+		for (int y = 0; y < (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) / NUM_COLUMNS; y++) {
+			for (int x = 0; x < NUM_COLUMNS; x++) {
+				addSlot(new Slot(inventory, x + y * NUM_COLUMNS + ContainerInventory.HOTBAR_SIZE, 8 + x * 18, 84 + y * 18));
 			}
 
 		}
 
-		for (int i = 0; i < 9; i++) {
+		for (int i = 0; i < ContainerInventory.HOTBAR_SIZE; i++) {
 			addSlot(new Slot(inventory, i, 8 + i * 18, 142));
 		}
 	}
@@ -104,53 +111,61 @@ public class MenuFurnaceBlast extends MenuAbstract {
 
 	@Override
 	public IntList getMoveSlots(final @NotNull InventoryAction action, final @NotNull Slot slot, final int target, final Player player) {
-		if(slot.index >= 0 && slot.index < 4) return getSlots(slot.index, 1, false);
+		if (slot.index >= INGREDIENT_SLOTS_START && slot.index < FUEL_SLOTS_START) {
+			return getSlots(INGREDIENT_SLOTS_START, NUM_INGREDIENT_SLOTS, false);
+		}
+		if (slot.index >= FUEL_SLOTS_START && slot.index < RESULT_SLOTS_START) {
+			return getSlots(FUEL_SLOTS_START, NUM_FUEL_SLOTS, false);
+		}
+		if (slot.index >= RESULT_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
+			return getSlots(RESULT_SLOTS_START, NUM_RESULT_SLOTS, false);
+		}
 
 		if(action == InventoryAction.MOVE_ALL) {
-			if(slot.index >= 4 && slot.index < 31) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(4, 27, false);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false);
 			}
-			if(slot.index >= 31 && slot.index < 40) {
+			if(slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Hotbar
-				return getSlots(31, 9, false);
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false);
 			}
 		}
-		if(slot.index >= 4 && slot.index < 40) {
+		if(slot.index >= INVENTORY_SLOTS_START && slot.index < INVENTORY_SLOTS_START + ContainerInventory.MAIN_INVENTORY_SIZE) {
 			// Full ContainerInventory
-			return getSlots(4, 36, false);
+			return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 		}
 		return null;
 	}
 
 	@Override
 	public IntList getTargetSlots(final @NotNull InventoryAction action, final @NotNull Slot slot, final int target, final Player player) {
-		if(slot.index >= 4 && slot.index < 40) {
+		if(slot.index >= INVENTORY_SLOTS_START && slot.index <= INVENTORY_SLOTS_START + ContainerInventory.MAIN_INVENTORY_SIZE) {
 			// Full ContainerInventory
 			if(action != InventoryAction.MOVE_ALL) {
 				if(target == 1) {
-					return getSlots(0, 2, false); // Into Ingredient Slots
+					return getSlots(INGREDIENT_SLOTS_START, NUM_INGREDIENT_SLOTS, false); // Into Ingredient Slot
 				}
 				if(target == 2) {
-					return getSlots(2, 1, false); // Into Fuel Slot
+					return getSlots(FUEL_SLOTS_START, NUM_FUEL_SLOTS, false); // Into Fuel Slot
 				}
 			}
-			if(slot.index >= 4 && slot.index < 31) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(31, 9, false); // Into Hotbar
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false); // Into Hotbar
 			}
-			if(slot.index >= 31 && slot.index < 40) {
+			if(slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Hotbar
-				return getSlots(3, 27, false); // Into Main ContainerInventory
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false); // Into Main ContainerInventory
 			}
 		}
-		if(slot.index >= 0 && slot.index < 4) {
-			if(slot.index == 3) {
+		if(slot.index >= INGREDIENT_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
+			if(slot.index == RESULT_SLOTS_START) {
 				// Output
-				return getSlots(3, 36, true);
-			} else {
-				// Inputs and Fuel
-				return getSlots(3, 36, false);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, true);
+			}else {
+				// Input and Fuel
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 			}
 		}
 
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventory.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventory.java
index bb72be543..fb649ac49 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventory.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventory.java
@@ -1,5 +1,6 @@
 package net.minecraft.core.player.inventory.menu;
 
+import com.mojang.nbt.tags.CompoundTag;
 import it.unimi.dsi.fastutil.ints.IntList;
 import net.minecraft.core.InventoryAction;
 import net.minecraft.core.data.registry.Registries;
@@ -16,34 +17,45 @@ import net.minecraft.core.player.inventory.slot.SlotResult;
 import org.jetbrains.annotations.NotNull;
 
 public class MenuInventory extends MenuAbstract {
+	private static final int NUM_CRAFTING_COLUMNS = 2;
+	private static final int NUM_CRAFTING_ROWS = 2;
+	private static final int NUM_RESULT_SLOTS = 1;
+
+	private static final int RESULT_SLOTS_START = 0;
+	private static final int CRAFTING_SLOTS_START = RESULT_SLOTS_START + NUM_RESULT_SLOTS;
+	private static final int ARMOR_SLOTS_START = CRAFTING_SLOTS_START + (NUM_CRAFTING_COLUMNS * NUM_CRAFTING_ROWS);
+	private static final int INVENTORY_SLOTS_START = ARMOR_SLOTS_START + ContainerInventory.ARMOR_INVENTORY_SIZE;
+	private static final int HOTBAR_SLOTS_START = INVENTORY_SLOTS_START + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE);
 
 	public ContainerCrafting craftSlots;
 	public Container resultSlots;
 	public ContainerInventory inventory;
 
 	public MenuInventory(ContainerInventory inventory) {
-		craftSlots = new ContainerCrafting(this, 2, 2);
-		resultSlots = new ContainerResult();
 		this.inventory = inventory;
+
+		craftSlots = new ContainerCrafting(this, NUM_CRAFTING_COLUMNS, NUM_CRAFTING_ROWS);
+		resultSlots = new ContainerResult();
+
 		addSlot(new SlotResult(inventory.player, craftSlots, resultSlots, 0, 144, 36));
 		
-		for (int i = 0; i < 2; i++) {
-			for (int j = 0; j < 2; j++) {
-				addSlot(new Slot(craftSlots, j + i * 2, 88 + j * 18, 26 + i * 18));
+		for (int y = 0; y < NUM_CRAFTING_ROWS; y++) {
+			for (int x = 0; x < NUM_CRAFTING_COLUMNS; x++) {
+				addSlot(new Slot(craftSlots, x + y * NUM_CRAFTING_COLUMNS, 88 + x * 18, 26 + y * 18));
 			}
 		}
 		
-		for (int i = 0; i < HumanArmorShape.values().length; i++) {
-			addSlot(new SlotArmor(this, inventory, inventory.getContainerSize() - 1 - i, 8, 8 + i * 18, HumanArmorShape.values()[HumanArmorShape.values().length - (i + 1)]));
+		for (int i = 0; i < ContainerInventory.ARMOR_INVENTORY_SIZE; i++) {
+			addSlot(new SlotArmor(this, inventory, ContainerInventory.MAIN_INVENTORY_SIZE + i, 8, 8 + i * 18, HumanArmorShape.values()[ContainerInventory.ARMOR_INVENTORY_SIZE - (i + 1)]));
 		}
 		
-		for (int i = 0; i < 3; i++) {
-			for (int k1 = 0; k1 < 9; k1++) {
-				addSlot(new Slot(inventory, k1 + (i + 1) * 9, 8 + k1 * 18, 84 + i * 18));
+		for (int y = 0; y < (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE) / NUM_COLUMNS; y++) {
+			for (int x = 0; x < NUM_COLUMNS; x++) {
+				addSlot(new Slot(inventory, x + y * NUM_COLUMNS + ContainerInventory.HOTBAR_SIZE, 8 + x * 18, 84 + y * 18));
 			}
 		}
 		
-		for (int i = 0; i < 9; i++) {
+		for (int i = 0; i < ContainerInventory.HOTBAR_SIZE; i++) {
 			addSlot(new Slot(inventory, i, 8 + i * 18, 142));
 		}
 
@@ -59,7 +71,7 @@ public class MenuInventory extends MenuAbstract {
 	public void onCraftGuiClosed(@NotNull Player player) {
 		super.onCraftGuiClosed(player);
 		boolean insert = false;
-		for (int i = 0; i < 4; i++) {
+		for (int i = 0; i < NUM_CRAFTING_COLUMNS * NUM_CRAFTING_ROWS; i++) {
 			ItemStack itemstack = craftSlots.getItem(i);
 			if(itemstack != null) {
 				craftSlots.setItem(i, null);
@@ -87,31 +99,31 @@ public class MenuInventory extends MenuAbstract {
 
 	@Override
 	public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index == 0) {
+		if(slot.index >= RESULT_SLOTS_START && slot.index < CRAFTING_SLOTS_START) {
 			// Craft Result
-			return getSlots(0, 1, false);
+			return getSlots(RESULT_SLOTS_START, NUM_RESULT_SLOTS, false);
 		}
-		if(slot.index >= 1 && slot.index <= 4) {
+		if(slot.index >= CRAFTING_SLOTS_START && slot.index < ARMOR_SLOTS_START) {
     		// Crafting Grid
-			return getSlots(1, 4, false);
+			return getSlots(CRAFTING_SLOTS_START, NUM_CRAFTING_COLUMNS * NUM_CRAFTING_ROWS, false);
 		}
-		if(slot.index >= 5 && slot.index <= 8) {
+		if(slot.index >= ARMOR_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
 			// Armor
-			return getSlots(5, 4, false);
+			return getSlots(ARMOR_SLOTS_START, ContainerInventory.ARMOR_INVENTORY_SIZE, false);
 		}
 		if(action == InventoryAction.MOVE_SIMILAR) {
-			if(slot.index >= 9 && slot.index <= 44) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Full ContainerInventory
-				return getSlots(9, 36, false);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 			}	
 		}else {
-			if(slot.index >= 9 && slot.index <= 35) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(9, 27, false);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false);
 			}
-			if(slot.index >= 36 && slot.index <= 44) {
+			if(slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Hotbar
-				return getSlots(36, 9, false);
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false);
 			}
 		}
 		return null;
@@ -119,29 +131,29 @@ public class MenuInventory extends MenuAbstract {
 	
 	@Override
 	public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= 9 && slot.index <= 44) {
+		if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 			// Full ContainerInventory
 			if(target == 1) {
-				return getSlots(1, 4, false); // Crafting
+				return getSlots(CRAFTING_SLOTS_START, NUM_CRAFTING_COLUMNS * NUM_CRAFTING_ROWS, false); // Crafting
 			}
 			if(target == 2) {
-				return getSlots(5, 4, false); // Armor
+				return getSlots(ARMOR_SLOTS_START, ContainerInventory.ARMOR_INVENTORY_SIZE, false); // Armor
 			}
 			
-			if(slot.index < 36) {
+			if(slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(36, 9, false); // Hotbar
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false); // Hotbar
 			}else {
 				// Hotbar
-				return getSlots(9, 27, false); // Main ContainerInventory
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false); // Main ContainerInventory
 			}
 		}else {
-			if(slot.index == 0) {
+			if(slot.index == RESULT_SLOTS_START) {
 				// Crafting Result
-				return getSlots(9, 36, true); // Full ContainerInventory, Last First
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, true); // Full ContainerInventory, Last First
 			}else {
 				// Crafting Grid and Armor
-				return getSlots(9, 36, false); // Full ContainerInventory
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false); // Full ContainerInventory
 			}
 			
 		}
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
index c46bf0af7..e75bfa955 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuInventoryCreative.java
@@ -22,7 +22,7 @@ public class MenuInventoryCreative extends MenuInventory
 	public int page = 0;
 	public int maxPage;
 	protected int creativeSlotsStart;
-	protected List<ItemStack> searchedItems = new ArrayList<ItemStack>();
+	protected List<ItemStack> searchedItems = new ArrayList<>();
 	public String searchText = "";
 
 	public MenuInventoryCreative(ContainerInventory inventoryplayer)
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuTrap.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuTrap.java
deleted file mode 100644
index c4c92412d..000000000
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuTrap.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package net.minecraft.core.player.inventory.menu;
-
-import it.unimi.dsi.fastutil.ints.IntList;
-import net.minecraft.core.InventoryAction;
-import net.minecraft.core.block.entity.TileEntityDispenser;
-import net.minecraft.core.entity.player.Player;
-import net.minecraft.core.player.inventory.container.Container;
-import net.minecraft.core.player.inventory.slot.Slot;
-import org.jetbrains.annotations.NotNull;
-
-public class MenuTrap extends MenuAbstract {
-
-	public TileEntityDispenser trap;
-	
-	private int dispenserSlotsStart;
-	private int inventorySlotsStart;
-	private int hotbarSlotsStart;
-
-	public MenuTrap(Container container, TileEntityDispenser trap) {
-		this.trap = trap;
-		for (int i = 0; i < 3; i++) {
-			for (int l = 0; l < 3; l++) {
-				addSlot(new Slot(trap, l + i * 3, 62 + l * 18, 17 + i * 18));
-			}
-
-		}
-
-		for (int j = 0; j < 3; j++) {
-			for (int i1 = 0; i1 < 9; i1++) {
-				addSlot(new Slot(container, i1 + j * 9 + 9, 8 + i1 * 18, 84 + j * 18));
-			}
-		}
-
-		for (int k = 0; k < 9; k++) {
-			addSlot(new Slot(container, k, 8 + k * 18, 142));
-		}
-		
-		dispenserSlotsStart = 0;
-		inventorySlotsStart = 9;
-		hotbarSlotsStart = 36;
-	}
-	
-	@Override
-	public boolean stillValid(@NotNull Player player) {
-		return trap.stillValid(player);
-	}
-
-	@Override
-	public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= dispenserSlotsStart && slot.index < inventorySlotsStart) {
-			return getSlots(dispenserSlotsStart, 9, false);
-		}
-		
-		if(action == InventoryAction.MOVE_ALL) {
-			if(slot.index >= inventorySlotsStart && slot.index < hotbarSlotsStart) {
-				return getSlots(inventorySlotsStart, 27, false);
-			}
-			if(slot.index >= hotbarSlotsStart) {
-				return getSlots(hotbarSlotsStart, 9, false);
-			}
-		}
-		if(action == InventoryAction.MOVE_SIMILAR) {
-			if(slot.index >= inventorySlotsStart) {
-				return getSlots(inventorySlotsStart, 36, false);
-			}
-		}
-		return null;
-	}
-
-	@Override
-	public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= dispenserSlotsStart && slot.index < inventorySlotsStart) {
-			return getSlots(inventorySlotsStart, 36, false);
-		}else {
-			return getSlots(dispenserSlotsStart, 9, false);
-		}
-	}
-}
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuTrommel.java b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuTrommel.java
index bb4777307..05a3ef5ea 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuTrommel.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/menu/MenuTrommel.java
@@ -11,6 +11,13 @@ import net.minecraft.core.player.inventory.slot.Slot;
 import org.jetbrains.annotations.NotNull;
 
 public class MenuTrommel extends MenuAbstract {
+	private static final int NUM_INGREDIENT_SLOTS = 4;
+	private static final int NUM_FUEL_SLOTS = 1;
+
+	private static final int INGREDIENT_SLOTS_START = 0;
+	private static final int FUEL_SLOTS_START = INGREDIENT_SLOTS_START + NUM_INGREDIENT_SLOTS;
+	private static final int INVENTORY_SLOTS_START = FUEL_SLOTS_START + NUM_FUEL_SLOTS;
+	private static final int HOTBAR_SLOTS_START = INVENTORY_SLOTS_START + (ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE);
 
 	private TileEntityTrommel trommel;
 	private int cookTime = 0;
@@ -87,26 +94,26 @@ public class MenuTrommel extends MenuAbstract {
 
 	@Override
 	public IntList getMoveSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= 0 && slot.index <= 3) {
-			return getSlots(0, 4, false);
+		if(slot.index >= INGREDIENT_SLOTS_START && slot.index < FUEL_SLOTS_START) {
+			return getSlots(INGREDIENT_SLOTS_START, NUM_INGREDIENT_SLOTS, false);
 		}
-		if(slot.index == 4) {
-			return getSlots(4, 1, false);
+		if(slot.index >= FUEL_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
+			return getSlots(FUEL_SLOTS_START, NUM_FUEL_SLOTS, false);
 		}
 		if(action == InventoryAction.MOVE_ALL) {
-			if(slot.index >= 5 && slot.index <= 31) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(5, 27, false);
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false);
 			}
-			if(slot.index >= 32 && slot.index <= 40) {
+			if(slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Hotbar
-				return getSlots(32, 9, false);
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false);
 			}
 		}
 		if(action == InventoryAction.MOVE_SIMILAR) {
-			if(slot.index >= 5 && slot.index <= 40) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Full ContainerInventory
-				return getSlots(5, 36, false);
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false);
 			}
 		}
 		return null;
@@ -114,25 +121,25 @@ public class MenuTrommel extends MenuAbstract {
 
 	@Override
 	public IntList getTargetSlots(@NotNull InventoryAction action, @NotNull Slot slot, int target, Player player) {
-		if(slot.index >= 0 && slot.index <= 4) {
+		if(slot.index >= INGREDIENT_SLOTS_START && slot.index < INVENTORY_SLOTS_START) {
 			// Item and Fuel Slots
-			return getSlots(5, 36, false); // Into ContainerInventory
+			return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE, false); // Into ContainerInventory
 		}
-		if(slot.index >= 5 && slot.index <= 40) {
+		if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 			if(target == 1) {
-				return getSlots(0, 4, false); // Into Item Slots
+				return getSlots(INGREDIENT_SLOTS_START, NUM_INGREDIENT_SLOTS, false); // Into Item Slots
 			}
 			if(target == 2) {
-				return getSlots(4, 1, false); // Into Fuel Slot
+				return getSlots(FUEL_SLOTS_START, NUM_FUEL_SLOTS, false); // Into Fuel Slot
 			}
 			
-			if(slot.index >= 5 && slot.index <= 31) {
+			if(slot.index >= INVENTORY_SLOTS_START && slot.index < HOTBAR_SLOTS_START) {
 				// Main ContainerInventory
-				return getSlots(32, 9, false); // Into hotbar
+				return getSlots(HOTBAR_SLOTS_START, ContainerInventory.HOTBAR_SIZE, false); // Into hotbar
 			}
-			if(slot.index >= 32 && slot.index <= 40) {
+			if(slot.index >= HOTBAR_SLOTS_START && slot.index < HOTBAR_SLOTS_START + ContainerInventory.HOTBAR_SIZE) {
 				// Hotbar
-				return getSlots(5, 27, false); // Into ContainerInventory
+				return getSlots(INVENTORY_SLOTS_START, ContainerInventory.MAIN_INVENTORY_SIZE - ContainerInventory.HOTBAR_SIZE, false); // Into ContainerInventory
 			}
 		}
 		return null;
diff --git a/game/core/src/main/java/net/minecraft/core/world/AuroraProvider.java b/game/core/src/main/java/net/minecraft/core/world/AuroraProvider.java
deleted file mode 100644
index 347d007f5..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/AuroraProvider.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package net.minecraft.core.world;
-
-import net.minecraft.core.world.noise.ImprovedPerlinNoise;
-
-import java.util.Random;
-
-public class AuroraProvider
-{
-    private final ImprovedPerlinNoise noise;
-    private final World world;
-
-    public AuroraProvider(World world, long seed)
-    {
-        this.world = world;
-        noise = new ImprovedPerlinNoise(new Random(seed));
-    }
-
-    public double getAuroraPresence(double x, double z, long worldTime, float celestialAngle)
-    {
-        double auroraLevel = 0.0f;
-        double auroraRadius = 0.03f;
-
-        double mult;
-        if (celestialAngle < 0.25f || celestialAngle > 0.75f)
-        {
-            return 0.0f;
-        }
-        else
-        {
-            // celestialAngle from 0.25f to 0.75f
-            celestialAngle = (celestialAngle - 0.25f) * 2;
-            // celestialAngle from 0.0f to 1.0f
-            celestialAngle = (celestialAngle * 2) - 1f;
-            // celestialAngle from -1.0f to 1.0f
-            celestialAngle = Math.abs(celestialAngle);
-            // celestialAngle from 1.0f to 0.0f to 1.0f
-            celestialAngle = -celestialAngle + 1.0f;
-            // celestialAngle from 0.0f to 1.0f to 0.0f
-            mult = celestialAngle;
-        }
-
-        double sample = noise.getValue(x / 1000d, worldTime / 5000d, z / 1000d);
-        sample += noise.getValue(x / 30d, worldTime / 50d, z / 30d) * 0.05d;
-        if (sample < auroraLevel - auroraRadius || sample > auroraLevel + auroraRadius)
-        {
-            return 0.0f;
-        }
-        else
-        {
-            // Range = auroraLevel - auroraRadius to auroraLevel + auroraRadius
-            sample -= auroraLevel;
-            // Range = -auroraRadius to auroraRadius
-            sample /= auroraRadius;
-            // Range = -1.0 to 1.0
-            sample = Math.abs(sample);
-            // Range = 1.0 to 0.0 to 1.0
-            sample = -sample;
-            // Range = -1.0 to 0.0 to -1.0
-            sample += 1.0d;
-            // Range = 0.0 to 1.0 to 0.0
-            return sample * mult;
-        }
-    }
-
-    public double getAuroraHeightOffset(double x, double z, double worldTime)
-    {
-        return noise.getValue(x / 100d, worldTime / 5000d, z / 100d) * 25d;
-    }
-
-    public float getAuroraRedOffset(double x, double z, double worldTime)
-    {
-        double sample = noise.getValue(x / 500d, worldTime / 5000d + 10000, z / 500d);
-        double sample2 = noise.getValue(x / 200d, worldTime / 5000d, z / 200d);
-        if (sample < 0.0f) sample = 0.0f;
-        if (sample > 0.25f) sample = 0.25f;
-        if (sample2 < 0.0f) sample2 = 0.0f;
-        if (sample2 > 0.1f) sample2 = 0.1f;
-
-
-        return (float) (sample * 3 + sample2 * 2);
-    }
-
-    public float getAuroraGreenOffset(double x, double z, double worldTime)
-    {
-        double sample = noise.getValue(x / 500d, worldTime / 5000d + 10000d, z / 500d);
-        double sample2 = noise.getValue(x / 500d, worldTime / 5000d, z / 500d);
-        if (sample < 0) sample = 0;
-        if (sample > 0.125f) sample = 0.125f;
-        if (sample2 < 0.0f) sample2 = 0.0f;
-        if (sample2 > 0.25f) sample2 = 0.25f;
-
-
-        return (float) (sample * 4 - sample2 * 2);
-    }
-
-    public float getAuroraBlueOffset(double x, double z, double worldTime)
-    {
-        double sample = noise.getValue(x / 500d, worldTime / 5000d, z / 500d);
-        if (sample < 0.0f) sample = 0.0f;
-        if (sample > 0.25f) sample = 0.25f;
-
-        return (float) sample;
-    }
-}
diff --git a/game/core/src/main/java/net/minecraft/core/world/Dimension.java b/game/core/src/main/java/net/minecraft/core/world/Dimension.java
index 7df0f373a..3ec417331 100644
--- a/game/core/src/main/java/net/minecraft/core/world/Dimension.java
+++ b/game/core/src/main/java/net/minecraft/core/world/Dimension.java
@@ -13,20 +13,16 @@ import net.minecraft.core.world.type.WorldTypes;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
 public class Dimension
 {
 	public int id;
 	public final String languageKey;
 	public final Dimension homeDim;
-	public final WorldType defaultWorldType;
+	public final @NotNull WorldType defaultWorldType;
 	public final float worldScale;
 	public final @Nullable Block<? extends BlockLogicPortal> portalBlock;
 	
-	public Dimension(String languageKey, Dimension homeDim, float worldScale, @Nullable Block<? extends BlockLogicPortal> portalBlock, WorldType defaultWorldType)
+	public Dimension(String languageKey, Dimension homeDim, float worldScale, @Nullable Block<? extends BlockLogicPortal> portalBlock, @NotNull WorldType defaultWorldType)
 	{
 		this.languageKey = languageKey;
 		this.homeDim = homeDim;
@@ -39,9 +35,9 @@ public class Dimension
 	{
 		if (world.dimension == this)
 		{
-			return world.dimensionData;
+			return world.getDimensionData();
 		}
-		DimensionData dimensionData = world.saveHandler.getDimensionData(this.id);
+		DimensionData dimensionData = world.getLevelStorage().getDimensionData(this.id);
 		if (dimensionData == null) dimensionData = new DimensionData(this.defaultWorldType);
 		
 		return dimensionData;
diff --git a/game/core/src/main/java/net/minecraft/core/world/World.java b/game/core/src/main/java/net/minecraft/core/world/World.java
index 827166fbb..b347c3cf2 100644
--- a/game/core/src/main/java/net/minecraft/core/world/World.java
+++ b/game/core/src/main/java/net/minecraft/core/world/World.java
@@ -52,14 +52,12 @@ import net.minecraft.core.world.pos.ChunkPos;
 import net.minecraft.core.world.pos.ChunkTilePos;
 import net.minecraft.core.world.pos.TilePos;
 import net.minecraft.core.world.pos.TilePosc;
-import net.minecraft.core.world.save.DimensionData;
-import net.minecraft.core.world.save.LevelData;
-import net.minecraft.core.world.save.LevelStorage;
-import net.minecraft.core.world.save.PlayerIO;
+import net.minecraft.core.world.save.*;
 import net.minecraft.core.world.saveddata.SavedData;
 import net.minecraft.core.world.saveddata.SavedDataStorage;
 import net.minecraft.core.world.season.SeasonManager;
 import net.minecraft.core.world.type.WorldType;
+import net.minecraft.core.world.type.WorldTypeGroups;
 import net.minecraft.core.world.weather.IPrecipitation;
 import net.minecraft.core.world.weather.Weather;
 import net.minecraft.core.world.weather.WeatherManager;
@@ -124,16 +122,16 @@ public class World implements MutableWorldSource {
     public boolean noNeighborUpdate = false;
     private long lockTimestamp = System.currentTimeMillis();
     public final @NotNull Random rand = new Random();
-    public boolean isNewWorld = false;
+    public final boolean isNewWorld;
     public Dimension dimension;
     public WorldType worldType;
     public IChunkProvider chunkProvider;
-    public LevelStorage saveHandler;
-    protected LevelData levelData;
-    public DimensionData dimensionData;
+    private final @NotNull LevelStorage levelStorage;
+    private final @NotNull LevelData levelData;
+    private final @NotNull DimensionData dimensionData;
     public boolean findingSpawnPoint;
     protected boolean enoughPlayersSleeping;
-    public SavedDataStorage savedDataStorage;
+    public final @NotNull SavedDataStorage savedDataStorage;
     protected final LightingEngine lightingEngine = new LightingEngineLegacy(this);
     private boolean updatingTileEntities;
 
@@ -144,110 +142,78 @@ public class World implements MutableWorldSource {
     public int dayCountLastTick;
 
     public BiomeProvider biomeProvider;
-    public AuroraProvider auroraProvider;
     public SeasonManager seasonManager;
     public WeatherManager weatherManager;
 
-    private CommandManager commandManager;
-    private final @NotNull WireHandler wireHandler;
+    private final @NotNull CommandManager commandManager = new CommandManager(isServer);
+    private final @NotNull WireHandler wireHandler = new WireHandler(this);
 
     //Usually called when a world is loaded or generated (Singleplayer & Server)
-    public World(@NotNull LevelStorage saveHandler, @NotNull String name, long seed, @Nullable Dimension dimension, @Nullable WorldType worldType, @Nullable Biome startingBiome) {
-        this.saveHandler = saveHandler;
-        this.savedDataStorage = new SavedDataStorage(saveHandler);
-        this.levelData = saveHandler.getLevelData();
-
-        this.isNewWorld = this.levelData == null;
-
-        // Attempt to determine the correct dimension for this world
-        if (dimension != null) {
-            // Provided dimension
-            this.dimension = dimension;
-        } else if (this.levelData != null) {
-            // Dimension from level data
-            this.dimension = Dimension.getDimensionList().get(this.levelData.getDimension());
-        } else {
-            // Default dimension
-            this.dimension = Dimension.getDimensionList().get(0);
-        }
+    public World(
+        final @NotNull LevelStorage levelStorage,
+        final @Nullable String name,
+        final long seed,
+        final @NotNull Dimension dimension,
+        final @Nullable Biome startingBiome
+    ) {
+        this.levelStorage = levelStorage;
+        this.savedDataStorage = new SavedDataStorage(levelStorage);
 
-        this.dimensionData = saveHandler.getDimensionData(this.dimension.id);
+        final @Nullable LevelData levelData = levelStorage.getLevelData();
+        this.isNewWorld = levelData == null;
+        this.levelData = Objects.requireNonNullElseGet(levelData, () -> new LevelData(seed, name));
 
-        // Attempt to determine the correct world type for this world
-        if (worldType != null) {
-            // Provided world type
-            this.worldType = worldType;
-        } else if (this.dimensionData != null) {
-            // World type from dimension data
-            this.worldType = this.dimensionData.getWorldType();
-        } else {
-            // Default world type
-            this.worldType = this.dimension.defaultWorldType;
-        }
+        // Provided dimension
+        this.dimension = dimension;
+        this.dimensionData = Objects.requireNonNullElseGet(levelStorage.getDimensionData(this.dimension.id), () -> new DimensionData(this.dimension.defaultWorldType));
 
-        boolean isNewWorld = false;
-        if (this.levelData == null) {
-            this.levelData = new LevelData(seed, name);
-            isNewWorld = true;
-        }
+        // World type from dimension data
+        this.worldType = this.dimensionData.getWorldType();
 
-        if (this.dimensionData == null) {
-            this.dimensionData = new DimensionData(this.worldType);
-            this.weatherManager = new WeatherManager(this);
-        } else {
-            this.weatherManager = new WeatherManager(this,
-                this.dimensionData.getCurrentWeather(),
-                this.dimensionData.getNextWeather(),
-                this.dimensionData.getWeatherDuration(),
-                this.dimensionData.getWeatherPower()
-            );
-        }
+        this.weatherManager = new WeatherManager(this,
+            this.dimensionData.getCurrentWeather(),
+            this.dimensionData.getNextWeather(),
+            this.dimensionData.getWeatherDuration(),
+            this.dimensionData.getWeatherPower()
+        );
 
         this.chunkProvider = createChunkProvider();
 
-        this.auroraProvider = new AuroraProvider(this, this.levelData.getRandomSeed());
-
         this.seasonManager = SeasonManager.fromConfig(this, this.worldType.getSeasonConfig());
 
         this.biomeProvider = this.worldType.createBiomeProvider(this);
 
-        if (isNewWorld) {
+        if (this.isNewWorld) {
             this.worldType.onWorldCreation(this);
             getInitialSpawnLocation(startingBiome);
         }
         updateSkyBrightness();
 
-        this.commandManager = new CommandManager(isServer);
         this.commandManager.init();
-
-        this.wireHandler = new WireHandler(this);
     }
 
     //Usually called when going through portals
-    public World(World world, Dimension dimension) {
-        this.lockTimestamp = world.lockTimestamp;
-        this.saveHandler = world.saveHandler;
+    public World(final @NotNull World parent, final @NotNull Dimension dimension) {
+        this.lockTimestamp = parent.lockTimestamp;
+        this.levelStorage = parent.levelStorage;
+        this.dimension = dimension;
 
-        this.levelData = new LevelData(world.levelData);
+        this.levelData = new LevelData(parent.levelData);
+        this.dimensionData = Objects.requireNonNullElseGet(parent.levelStorage.getDimensionData(this.dimension.id), () -> new DimensionData(this.dimension.defaultWorldType));
 
-        this.dimensionData = world.saveHandler.getDimensionData(dimension.id);
-        if (this.dimensionData == null) {
-            this.dimensionData = new DimensionData(dimension.defaultWorldType);
-            this.weatherManager = new WeatherManager(this);
-        } else {
-            this.weatherManager = new WeatherManager(this,
-                this.dimensionData.getCurrentWeather(),
-                this.dimensionData.getNextWeather(),
-                this.dimensionData.getWeatherDuration(),
-                this.dimensionData.getWeatherPower()
-            );
-        }
+        this.weatherManager = new WeatherManager(this,
+            this.dimensionData.getCurrentWeather(),
+            this.dimensionData.getNextWeather(),
+            this.dimensionData.getWeatherDuration(),
+            this.dimensionData.getWeatherPower()
+        );
+
+        this.isNewWorld = false;
 
         // Set up info from dimension data
         this.worldType = this.dimensionData.getWorldType();
 
-        this.savedDataStorage = new SavedDataStorage(this.saveHandler);
-        this.dimension = dimension;
+        this.savedDataStorage = new SavedDataStorage(this.levelStorage);
         this.chunkProvider = createChunkProvider();
         this.seasonManager = SeasonManager.fromConfig(this, this.worldType.getSeasonConfig());
 
@@ -255,40 +221,50 @@ public class World implements MutableWorldSource {
 
         updateSkyBrightness();
 
-        this.auroraProvider = new AuroraProvider(this, this.getRandomSeed());
-
-        this.commandManager = new CommandManager(isServer);
         this.commandManager.init();
-
-        this.wireHandler = new WireHandler(this);
     }
 
     //Usually called when joining a server
-    public World(LevelStorage saveHandler, String name, Dimension dimension, WorldType worldType, long seed) {
-        this.saveHandler = saveHandler;
+    public World(
+        final @NotNull SavedDataStorage savedDataStorage,
+        final @NotNull String name,
+        final long seed,
+        final @NotNull Dimension dimension,
+        final @NotNull WorldType worldType
+    ) {
+        this.levelStorage = savedDataStorage.getLevelStorage();
+        this.savedDataStorage = savedDataStorage;
+
         this.levelData = new LevelData(seed, name);
+        this.isNewWorld = false;
+
+        // Provided dimension
         this.dimension = dimension;
         this.dimensionData = new DimensionData(worldType);
+
+        // Provided world type
         this.worldType = worldType;
-        this.savedDataStorage = new SavedDataStorage(saveHandler);
-        this.chunkProvider = createChunkProvider();
-        this.seasonManager = SeasonManager.fromConfig(this, this.worldType.getSeasonConfig());
-        updateSkyBrightness();
 
         this.weatherManager = new WeatherManager(this);
 
-        this.auroraProvider = new AuroraProvider(this, this.levelData.getRandomSeed());
+        this.chunkProvider = createChunkProvider();
+
+        this.seasonManager = SeasonManager.fromConfig(this, this.worldType.getSeasonConfig());
 
         this.biomeProvider = this.worldType.createBiomeProvider(this);
 
-        this.commandManager = new CommandManager(isServer);
-        this.commandManager.init();
+        this.updateSkyBrightness();
 
-        this.wireHandler = new WireHandler(this);
+        this.commandManager.init();
     }
 
     public World() {
-        this.wireHandler = new WireHandler(this);
+        this.levelStorage = new LevelStorageDummy();
+        this.savedDataStorage = new SavedDataStorage(this.levelStorage);
+
+        this.isNewWorld = false;
+        this.levelData = new LevelData(0, "Dummy World");
+        this.dimensionData = new DimensionData(WorldTypeGroups.DEFAULT.get(Dimension.OVERWORLD));
     }
 
     public <T> T getGameRuleValue(GameRule<T> gameRule) {
@@ -317,7 +293,7 @@ public class World implements MutableWorldSource {
     }
 
     protected IChunkProvider createChunkProvider() {
-        IChunkLoader chunkLoader = this.saveHandler.getChunkLoader(this.dimension);
+        IChunkLoader chunkLoader = this.levelStorage.getChunkLoader(this.dimension);
         return accessor.createChunkProvider(this, chunkLoader);
     }
 
@@ -342,7 +318,7 @@ public class World implements MutableWorldSource {
     public void spawnPlayerWithLoadedChunks(Player player, boolean respawning) {
         try {
             if (!respawning) {
-                PlayerIO playerIO = this.saveHandler.getPlayerFileData();
+                PlayerIO playerIO = this.levelStorage.getPlayerFileData();
                 if (playerIO != null) {
                     playerIO.load(player);
                 }
@@ -381,8 +357,8 @@ public class World implements MutableWorldSource {
 
     private void saveWorldData() {
         checkSessionLock();
-        this.saveHandler.saveLevelDataAndPlayerData(this.levelData, this.players);
-        this.saveHandler.saveDimensionData(this.dimension.id, this.dimensionData);
+        this.levelStorage.saveLevelDataAndPlayerData(this.levelData, this.players);
+        this.levelStorage.saveDimensionData(this.dimension.id, this.dimensionData);
         this.savedDataStorage.save();
     }
 
@@ -2578,7 +2554,7 @@ public class World implements MutableWorldSource {
     }
 
     public void checkSessionLock() {
-        this.saveHandler.checkSessionLock();
+        this.levelStorage.checkSessionLock();
     }
 
     public void setWorldTime(long l) {
@@ -2713,14 +2689,18 @@ public class World implements MutableWorldSource {
         this.triggerEvent(new TilePos(x, y, z), index, data);
     }
 
-    public LevelStorage getSaveHandler() {
-        return this.saveHandler;
+    public @NotNull LevelStorage getLevelStorage() {
+        return this.levelStorage;
     }
 
-    public LevelData getLevelData() {
+    public @NotNull LevelData getLevelData() {
         return this.levelData;
     }
 
+    public @NotNull DimensionData getDimensionData() {
+        return this.dimensionData;
+    }
+
     public void updateEnoughPlayersSleepingFlag(Player player) {
         this.enoughPlayersSleeping = false;
         if (!this.players.isEmpty()) {
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/DimensionData.java b/game/core/src/main/java/net/minecraft/core/world/save/DimensionData.java
index 9f2cd1e1d..76d24531b 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/DimensionData.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/DimensionData.java
@@ -10,15 +10,17 @@ import net.minecraft.core.world.type.WorldType;
 import net.minecraft.core.world.type.WorldTypes;
 import net.minecraft.core.world.weather.Weather;
 import net.minecraft.core.world.weather.Weathers;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.util.Objects;
 
 public class DimensionData
 {
-    private String worldType;
+    private @NotNull String worldType;
     private int weatherCurrentId = -1;
     private int weatherNextId = -1;
     private long weatherDuration;
@@ -127,9 +129,9 @@ public class DimensionData
         weatherPower = weatherTag.getFloat("Power");
     }
 
-    public WorldType getWorldType()
+    public @NotNull WorldType getWorldType()
     {
-        return Registries.WORLD_TYPES.getItem(worldType);
+        return Objects.requireNonNull(Registries.WORLD_TYPES.getItem(this.worldType));
     }
 
     public void setWorldType(WorldType type)
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/ISaveFormat.java b/game/core/src/main/java/net/minecraft/core/world/save/ISaveFormat.java
index 1aba0dcde..1ef4e0acc 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/ISaveFormat.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/ISaveFormat.java
@@ -29,7 +29,7 @@ public interface ISaveFormat
      * @param isMultiplayer Whether the world is being loaded for use on a multiplayer server.
      * @return The created save handler.
      */
-    LevelStorage getSaveHandler(String worldDirName, boolean isMultiplayer);
+    LevelStorage getLevelStorage(String worldDirName, boolean isMultiplayer);
 
     /**
      * Fetches a list containing the save files present in the user's save directory.
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerBase.java b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorageBase.java
similarity index 97%
rename from game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerBase.java
rename to game/core/src/main/java/net/minecraft/core/world/save/LevelStorageBase.java
index e33df5c6a..c0153e7ce 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerBase.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorageBase.java
@@ -21,7 +21,7 @@ import java.nio.file.Files;
 import java.util.List;
 import java.util.UUID;
 
-public abstract class SaveHandlerBase implements LevelStorage
+public abstract class LevelStorageBase implements LevelStorage
 {
 	private static final Logger LOGGER = LogUtils.getLogger();
 	protected final String worldDirName;
@@ -31,7 +31,7 @@ public abstract class SaveHandlerBase implements LevelStorage
 	protected final long now = System.currentTimeMillis();
 	protected final ISaveFormat saveFormat;
 
-	public SaveHandlerBase(ISaveFormat saveFormat, File savesDir, String worldDirName)
+	public LevelStorageBase(ISaveFormat saveFormat, File savesDir, String worldDirName)
 	{
 		this.worldDirName = worldDirName;
 		this.saveFormat = saveFormat;
@@ -319,6 +319,6 @@ public abstract class SaveHandlerBase implements LevelStorage
 
 	@FunctionalInterface
 	public static interface SaveHandlerFactory {
-		@NotNull SaveHandlerBase apply(ISaveFormat saveFormat, File savesDir, String worldDirName);
+		@NotNull LevelStorageBase apply(ISaveFormat saveFormat, File savesDir, String worldDirName);
 	}
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerCore.java b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorageCore.java
similarity index 85%
rename from game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerCore.java
rename to game/core/src/main/java/net/minecraft/core/world/save/LevelStorageCore.java
index bb4f31318..ea71bcfee 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerCore.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorageCore.java
@@ -9,11 +9,11 @@ import org.jetbrains.annotations.NotNull;
 import java.io.File;
 import java.util.List;
 
-public class SaveHandlerCore
-        extends SaveHandlerBase
+public class LevelStorageCore
+        extends LevelStorageBase
 {
 
-    public SaveHandlerCore(ISaveFormat saveFormat, File savesDir, String worldDirName)
+    public LevelStorageCore(ISaveFormat saveFormat, File savesDir, String worldDirName)
     {
         super(saveFormat, savesDir, worldDirName);
     }
diff --git a/game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientMP.java b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorageDummy.java
similarity index 93%
rename from game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientMP.java
rename to game/core/src/main/java/net/minecraft/core/world/save/LevelStorageDummy.java
index b34d11805..0f667e959 100644
--- a/game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientMP.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorageDummy.java
@@ -1,4 +1,4 @@
-package net.minecraft.client.world.save;
+package net.minecraft.core.world.save;
 
 import com.mojang.nbt.tags.CompoundTag;
 import net.minecraft.core.entity.player.Player;
@@ -15,10 +15,9 @@ import java.io.File;
 import java.util.List;
 import java.util.UUID;
 
-public class SaveHandlerClientMP
-		implements LevelStorage
+public class LevelStorageDummy implements LevelStorage
 {
-	public SaveHandlerClientMP()
+	public LevelStorageDummy()
 	{
 		
 	}
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerServer.java b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorageServer.java
similarity index 88%
rename from game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerServer.java
rename to game/core/src/main/java/net/minecraft/core/world/save/LevelStorageServer.java
index eb2f09484..c57d949e8 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/SaveHandlerServer.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorageServer.java
@@ -14,13 +14,13 @@ import java.io.File;
 import java.util.List;
 
 
-public class SaveHandlerServer
-        extends SaveHandlerBase
+public class LevelStorageServer
+        extends LevelStorageBase
         implements PlayerIO
 {
     private static final Logger LOGGER = LogUtils.getLogger();
 
-    public SaveHandlerServer(ISaveFormat saveFormat, File savesDir, String worldDirName, boolean isMultiplayer)
+    public LevelStorageServer(ISaveFormat saveFormat, File savesDir, String worldDirName, boolean isMultiplayer)
     {
         super(saveFormat, savesDir, worldDirName);
     }
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/SaveFormatBase.java b/game/core/src/main/java/net/minecraft/core/world/save/SaveFormatBase.java
index d479aba76..f6915c744 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/SaveFormatBase.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/SaveFormatBase.java
@@ -22,7 +22,7 @@ public abstract class SaveFormatBase
     private static final Logger LOGGER = LogUtils.getLogger();
     protected final File savesDir;
 
-    public @NotNull SaveHandlerBase.SaveHandlerFactory factory = SaveHandlerCore::new;
+    public @NotNull LevelStorageBase.SaveHandlerFactory factory = LevelStorageCore::new;
 
     public SaveFormatBase(File savesDir)
     {
@@ -218,7 +218,7 @@ public abstract class SaveFormatBase
     }
 
     @Override
-    public LevelStorage getSaveHandler(String worldDirName, boolean isMultiplayer)
+    public LevelStorage getLevelStorage(String worldDirName, boolean isMultiplayer)
     {
         return factory.apply(this, savesDir, worldDirName);
     }
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/conversion/SaveConverter19133To19134.java b/game/core/src/main/java/net/minecraft/core/world/save/conversion/SaveConverter19133To19134.java
index 89ca0dda9..516e62308 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/conversion/SaveConverter19133To19134.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/conversion/SaveConverter19133To19134.java
@@ -77,7 +77,7 @@ public class SaveConverter19133To19134
                 {
                     dimensionData = new DimensionData(dim.defaultWorldType);
                 }
-                toFormat.getSaveHandler(worldDirName, false).saveDimensionData(dim.id, dimensionData);
+                toFormat.getLevelStorage(worldDirName, false).saveDimensionData(dim.id, dimensionData);
             }
         }
         else return;
diff --git a/game/core/src/main/java/net/minecraft/core/world/saveddata/SavedDataStorage.java b/game/core/src/main/java/net/minecraft/core/world/saveddata/SavedDataStorage.java
index f2bb30c14..768844743 100644
--- a/game/core/src/main/java/net/minecraft/core/world/saveddata/SavedDataStorage.java
+++ b/game/core/src/main/java/net/minecraft/core/world/saveddata/SavedDataStorage.java
@@ -8,6 +8,7 @@ import com.mojang.nbt.NbtIo;
 import it.unimi.dsi.fastutil.objects.Object2ShortMap;
 import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap;
 import net.minecraft.core.world.save.LevelStorage;
+import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 
 import java.io.*;
@@ -17,12 +18,12 @@ import java.util.*;
 public class SavedDataStorage
 {
     private static final Logger LOGGER = LogUtils.getLogger();
-    private final LevelStorage storage;
+    private final @NotNull LevelStorage storage;
     private final Map<Serializable, SavedData> cache;
     private final List<SavedData> loadedData;
     private final Object2ShortMap<String> idCounts;
 
-    public SavedDataStorage(LevelStorage storage)
+    public SavedDataStorage(final @NotNull LevelStorage storage)
     {
         cache = new HashMap<>();
         loadedData = new ArrayList<>();
@@ -31,6 +32,10 @@ public class SavedDataStorage
         loadMetadata();
     }
 
+    public @NotNull LevelStorage getLevelStorage() {
+        return this.storage;
+    }
+
     public SavedData load(Class<? extends SavedData> savedDataClass, String id)
     {
         SavedData savedData = cache.get(id);
@@ -38,31 +43,21 @@ public class SavedDataStorage
         {
             return savedData;
         }
-        if(storage != null)
-        {
-            try
-            {
-                File file = storage.getDataFile(id);
-                if(file != null && file.exists())
-                {
-                    try
-                    {
-                        savedData = savedDataClass.getConstructor(String.class).newInstance(id);
-                    }
-                    catch(Exception exception1)
-                    {
-                        throw new RuntimeException("Failed to instantiate " + savedDataClass.toString(), exception1);
-                    }
-                    InputStream fis = Files.newInputStream(file.toPath());
-                    CompoundTag tag = NbtIo.readCompressed(fis);
-                    fis.close();
-                    savedData.load(tag.getCompound("data"));
+        try {
+            File file = storage.getDataFile(id);
+            if (file != null && file.exists()) {
+                try {
+                    savedData = savedDataClass.getConstructor(String.class).newInstance(id);
+                } catch (Exception exception1) {
+                    throw new RuntimeException("Failed to instantiate " + savedDataClass.toString(), exception1);
                 }
+                InputStream fis = Files.newInputStream(file.toPath());
+                CompoundTag tag = NbtIo.readCompressed(fis);
+                fis.close();
+                savedData.load(tag.getCompound("data"));
             }
-            catch(Exception e)
-            {
-                LOGGER.error("Exception loading saved data for id '{}'!", id, e);
-            }
+        } catch (Exception e) {
+            LOGGER.error("Exception loading saved data for id '{}'!", id, e);
         }
         if(savedData != null)
         {
@@ -101,10 +96,6 @@ public class SavedDataStorage
 
     private void save(SavedData savedData)
     {
-        if(storage == null)
-        {
-            return;
-        }
         try
         {
             File file = storage.getDataFile(savedData.id);
@@ -130,10 +121,6 @@ public class SavedDataStorage
         try
         {
             idCounts.clear();
-            if(storage == null)
-            {
-                return;
-            }
             File file = storage.getDataFile("idcounts");
             if(file != null && file.exists())
             {
@@ -160,10 +147,6 @@ public class SavedDataStorage
     {
         short metadata = (short) (idCounts.getOrDefault(type, (short) -1) + 1);
         idCounts.put(type, metadata);
-        if(storage == null)
-        {
-            return metadata;
-        }
         try
         {
             File file = storage.getDataFile("idcounts");
diff --git a/game/core/src/main/java/net/minecraft/core/world/season/Season.java b/game/core/src/main/java/net/minecraft/core/world/season/Season.java
index c6401a185..c7cbfc2ba 100644
--- a/game/core/src/main/java/net/minecraft/core/world/season/Season.java
+++ b/game/core/src/main/java/net/minecraft/core/world/season/Season.java
@@ -2,13 +2,14 @@ package net.minecraft.core.world.season;
 
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.world.weather.Weather;
+import org.jetbrains.annotations.NotNull;
 
 import java.util.*;
 
 public abstract class Season
 {
-	private final String id;
-	private final String languageKey;
+	private final @NotNull String id;
+	private final @NotNull String languageKey;
 
 	public List<Weather> allowedWeathers = new ArrayList<>();
 	public Map<Weather, Float> weatherProbability = new HashMap<>();
@@ -23,13 +24,13 @@ public abstract class Season
 	public float saplingDropFactor = 1.0f;
 	public float mobSpawnRateModifier = 1.0f;
 	
-	public Season(String id)
+	public Season(final @NotNull String id)
 	{
 		this.id = id;
 		this.languageKey = "season." + id;
 	}
 	
-	public Season(String id, boolean hasFallingLeaves, boolean hasDeeperSnow)
+	public Season(final @NotNull String id, boolean hasFallingLeaves, boolean hasDeeperSnow)
 	{
 		this(id);
 		this.hasFallingLeaves = hasFallingLeaves;
@@ -41,7 +42,11 @@ public abstract class Season
 		return id;
 	}
 
-	public String getTranslatedName()
+	public @NotNull String getLanguageKey() {
+		return this.languageKey;
+	}
+
+	public @NotNull String getTranslatedName()
 	{
 		return I18n.getInstance().translateKey(languageKey + ".name");
 	}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/ContainerSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/ContainerSettingNode.java
new file mode 100644
index 000000000..7f84c0905
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/ContainerSettingNode.java
@@ -0,0 +1,33 @@
+package net.minecraft.core.world.settings;
+
+import org.jetbrains.annotations.NotNull;
+
+public class ContainerSettingNode implements SettingNode {
+    private final @NotNull SettingNodeChildrenProvider childrenProvider;
+    private @NotNull SettingNodeVisibilityListener visibilityListener = SettingNodeVisibilityListener::alwaysVisible;
+
+    public ContainerSettingNode(final @NotNull SettingNodeChildrenProvider childrenProvider) {
+        this.childrenProvider = childrenProvider;
+    }
+
+    public final @NotNull ContainerSettingNode withVisibilityListener(final @NotNull SettingNodeVisibilityListener visibilityListener) {
+        this.visibilityListener = visibilityListener;
+
+        return this;
+    }
+
+    @Override
+    public @NotNull SettingNodeChildrenProvider getChildrenProvider() {
+        return this.childrenProvider;
+    }
+
+    @Override
+    public boolean isVisible(final @NotNull WorldConfiguration worldConfiguration) {
+        return this.visibilityListener.isVisible(worldConfiguration);
+    }
+
+    @Override
+    public boolean areChildrenVisible(final @NotNull WorldConfiguration worldConfiguration) {
+        return this.visibilityListener.isVisible(worldConfiguration);
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/DefaultSettingNodes.java b/game/core/src/main/java/net/minecraft/core/world/settings/DefaultSettingNodes.java
new file mode 100644
index 000000000..80b4b8650
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/DefaultSettingNodes.java
@@ -0,0 +1,31 @@
+package net.minecraft.core.world.settings;
+
+import net.minecraft.core.lang.text.TranslatableText;
+import net.minecraft.core.world.season.Seasons;
+import net.minecraft.core.world.settings.impl.SeasonSettingNode;
+import org.jetbrains.annotations.NotNull;
+
+public final class DefaultSettingNodes {
+    private DefaultSettingNodes() throws IllegalAccessException {
+        throw new IllegalAccessException("No DefaultSettingNodes for you!");
+    }
+
+    private static final @NotNull SettingNode ROOT_NODE;
+    static {
+        ROOT_NODE = new ContainerSettingNode(
+            new ListSettingNodeChildrenProvider()
+                .withChildren(
+                    new SeasonSettingNode("minecraft:world_settings/single_season", new TranslatableText("world_settings.single_season.name"), SeasonSettingNode.DISABLED)
+                        .withValue(new SeasonSettingNode.SeasonEntry(Seasons.OVERWORLD_SPRING))
+                        .withValue(new SeasonSettingNode.SeasonEntry(Seasons.OVERWORLD_SUMMER))
+                        .withValue(new SeasonSettingNode.SeasonEntry(Seasons.OVERWORLD_FALL))
+                        .withValue(new SeasonSettingNode.SeasonEntry(Seasons.OVERWORLD_WINTER))
+                        .withValue(new SeasonSettingNode.SeasonEntry(Seasons.OVERWORLD_WINTER_ENDLESS))
+                )
+        );
+    }
+
+    public static @NotNull SettingNode getRootNode() {
+        return ROOT_NODE;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/EnumValueGenericSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/EnumValueGenericSettingNode.java
new file mode 100644
index 000000000..1a5e312d4
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/EnumValueGenericSettingNode.java
@@ -0,0 +1,11 @@
+package net.minecraft.core.world.settings;
+
+import net.minecraft.core.lang.text.IntoText;
+import net.minecraft.core.lang.text.Text;
+import org.jetbrains.annotations.NotNull;
+
+public class EnumValueGenericSettingNode<T extends Enum<T> & IntoText> extends ValueGenericSettingNode<T> {
+    public EnumValueGenericSettingNode(final @NotNull String key, final @NotNull Text name, final @NotNull T value) {
+        super(key, name, value);
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/GenericSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/GenericSettingNode.java
new file mode 100644
index 000000000..58cabe811
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/GenericSettingNode.java
@@ -0,0 +1,55 @@
+package net.minecraft.core.world.settings;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class GenericSettingNode implements KeyedSettingNode {
+    private final @NotNull String key;
+    private @Nullable SettingNodeChildrenProvider childrenProvider = null;
+    private @NotNull SettingNodeVisibilityListener selfVisibilityListener = SettingNodeVisibilityListener::alwaysVisible;
+    private @NotNull SettingNodeVisibilityListener childrenVisibilityListener = SettingNodeVisibilityListener::alwaysVisible;
+
+    public GenericSettingNode(final @NotNull String key) {
+        this.key = key;
+
+        KeyedSettingNode.checkKeyIsUnique(key);
+    }
+
+    @Override
+    public @NotNull String getKey() {
+        return this.key;
+    }
+
+    @Override
+    public @Nullable SettingNodeChildrenProvider getChildrenProvider() {
+        return this.childrenProvider;
+    }
+
+    public final @NotNull GenericSettingNode withChildrenProvider(final @NotNull SettingNodeChildrenProvider childrenProvider) {
+        this.childrenProvider = childrenProvider;
+
+        return this;
+    }
+
+    @Override
+    public boolean isVisible(final @NotNull WorldConfiguration worldConfiguration) {
+        return this.selfVisibilityListener.isVisible(worldConfiguration);
+    }
+
+    public final @NotNull GenericSettingNode withVisibilityListener(final @NotNull SettingNodeVisibilityListener visibilityListener) {
+        this.selfVisibilityListener = visibilityListener;
+
+        return this;
+    }
+
+    @Override
+    public boolean areChildrenVisible(final @NotNull WorldConfiguration worldConfiguration) {
+        return this.isVisible(worldConfiguration) && this.childrenVisibilityListener.isVisible(worldConfiguration);
+    }
+
+    public final @NotNull GenericSettingNode withChildrenVisibilityListener(final @NotNull SettingNodeVisibilityListener visibilityListener) {
+        this.childrenVisibilityListener = visibilityListener;
+
+        return this;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/KeyedSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/KeyedSettingNode.java
new file mode 100644
index 000000000..1237e1b4e
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/KeyedSettingNode.java
@@ -0,0 +1,20 @@
+package net.minecraft.core.world.settings;
+
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Set;
+
+public interface KeyedSettingNode extends SettingNode {
+    @NotNull Set<@NotNull String> SETTING_KEYS = new ObjectOpenHashSet<>();
+
+    static void checkKeyIsUnique(final @NotNull String key) {
+        if (SETTING_KEYS.contains(key)) {
+            throw new IllegalStateException("Key already exists!");
+        }
+
+        SETTING_KEYS.add(key);
+    }
+
+    @NotNull String getKey();
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/ListSettingNodeChildrenProvider.java b/game/core/src/main/java/net/minecraft/core/world/settings/ListSettingNodeChildrenProvider.java
new file mode 100644
index 000000000..77dbd7164
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/ListSettingNodeChildrenProvider.java
@@ -0,0 +1,48 @@
+package net.minecraft.core.world.settings;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class ListSettingNodeChildrenProvider implements SettingNodeChildrenProvider {
+    private final @NotNull List<@NotNull SettingNode> children = new ObjectArrayList<>();
+    private final @NotNull Map<@NotNull String, @NotNull KeyedSettingNode> keyedChildren = new Object2ObjectOpenHashMap<>();
+
+    @Override
+    public int length(final @NotNull WorldConfiguration worldConfiguration) {
+        return this.children.size();
+    }
+
+    @Override
+    public @NotNull Iterator<@NotNull SettingNode> getChildren(final @NotNull WorldConfiguration worldConfiguration) {
+        return this.children.iterator();
+    }
+
+    @Override
+    public @Nullable KeyedSettingNode getChildByKey(final @NotNull String key, final @NotNull WorldConfiguration worldConfiguration) {
+        return this.keyedChildren.get(key);
+    }
+
+    public final @NotNull ListSettingNodeChildrenProvider withChild(final @NotNull SettingNode settingNode) {
+        this.children.add(settingNode);
+        if (settingNode instanceof final @NotNull KeyedSettingNode keyed) {
+            this.keyedChildren.put(keyed.getKey(), keyed);
+        }
+
+        return this;
+    }
+
+    public final @NotNull ListSettingNodeChildrenProvider withChildren(final @NotNull SettingNode @NotNull ... settingNodes) {
+        for (final @NotNull SettingNode settingNode : settingNodes) {
+            this.withChild(settingNode);
+        }
+
+        return this;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/ListValueGenericSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/ListValueGenericSettingNode.java
new file mode 100644
index 000000000..e2f31436f
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/ListValueGenericSettingNode.java
@@ -0,0 +1,28 @@
+package net.minecraft.core.world.settings;
+
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import net.minecraft.core.lang.text.IntoText;
+import net.minecraft.core.lang.text.Text;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class ListValueGenericSettingNode<T extends IntoText> extends ValueGenericSettingNode<T> {
+    private final @NotNull List<@NotNull T> values = new ObjectArrayList<>();
+
+    public ListValueGenericSettingNode(final @NotNull String key, final @NotNull Text name, final @NotNull T defaultValue) {
+        super(key, name, defaultValue);
+
+        this.values.add(defaultValue);
+    }
+
+    public @NotNull List<T> getValues() {
+        return this.values;
+    }
+
+    public @NotNull ListValueGenericSettingNode<T> withValue(final @NotNull T value) {
+        this.values.add(value);
+
+        return this;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/NamedSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/NamedSettingNode.java
new file mode 100644
index 000000000..652dbf059
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/NamedSettingNode.java
@@ -0,0 +1,8 @@
+package net.minecraft.core.world.settings;
+
+import net.minecraft.core.lang.text.Text;
+import org.jetbrains.annotations.NotNull;
+
+public interface NamedSettingNode extends SettingNode {
+    @NotNull Text getName();
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/PercentageValueGenericSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/PercentageValueGenericSettingNode.java
new file mode 100644
index 000000000..61ce58557
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/PercentageValueGenericSettingNode.java
@@ -0,0 +1,26 @@
+package net.minecraft.core.world.settings;
+
+import net.minecraft.core.lang.text.LiteralText;
+import net.minecraft.core.lang.text.Text;
+import org.jetbrains.annotations.NotNull;
+
+public class PercentageValueGenericSettingNode extends ValueGenericSettingNode<@NotNull Float> implements RangeValueSettingNode<@NotNull Float> {
+    public PercentageValueGenericSettingNode(final @NotNull String key, final @NotNull Text name, final float defaultValue) {
+        super(key, name, defaultValue);
+    }
+
+    @Override
+    public @NotNull Float getMinimum(final @NotNull WorldConfiguration worldConfiguration) {
+        return 0.0f;
+    }
+
+    @Override
+    public @NotNull Float getMaximum(final @NotNull WorldConfiguration worldConfiguration) {
+        return 1.0f;
+    }
+
+    @Override
+    public @NotNull Text getValueText(final @NotNull Float value) {
+        return new LiteralText((int)(value * 100.0) + "%");
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/RangeValueSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/RangeValueSettingNode.java
new file mode 100644
index 000000000..324ae2e89
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/RangeValueSettingNode.java
@@ -0,0 +1,8 @@
+package net.minecraft.core.world.settings;
+
+import org.jetbrains.annotations.NotNull;
+
+public interface RangeValueSettingNode<T> extends ValueSettingNode<T> {
+    @NotNull T getMinimum(final @NotNull WorldConfiguration worldConfiguration);
+    @NotNull T getMaximum(final @NotNull WorldConfiguration worldConfiguration);
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/SettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNode.java
new file mode 100644
index 000000000..b7811ee47
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNode.java
@@ -0,0 +1,34 @@
+package net.minecraft.core.world.settings;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Iterator;
+
+public interface SettingNode {
+    @Nullable SettingNodeChildrenProvider getChildrenProvider();
+
+    boolean isVisible(final @NotNull WorldConfiguration worldConfiguration);
+
+    boolean areChildrenVisible(final @NotNull WorldConfiguration worldConfiguration);
+
+    default @Nullable KeyedSettingNode getChildByKey(final @NotNull String key, final @NotNull WorldConfiguration worldConfiguration) {
+        final @Nullable SettingNodeChildrenProvider childrenProvider = this.getChildrenProvider();
+        if (childrenProvider != null) {
+            final @Nullable KeyedSettingNode myChild = childrenProvider.getChildByKey(key, worldConfiguration);
+            if (myChild != null) {
+                return myChild;
+            }
+            
+            for (final @NotNull Iterator<@NotNull SettingNode> it = childrenProvider.getChildren(worldConfiguration); it.hasNext(); ) {
+                final @NotNull SettingNode child = it.next();
+                final @Nullable KeyedSettingNode childsChild = child.getChildByKey(key, worldConfiguration); // recursion here
+                if (childsChild != null) {
+                    return childsChild;
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeChildrenProvider.java b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeChildrenProvider.java
new file mode 100644
index 000000000..c5794c923
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeChildrenProvider.java
@@ -0,0 +1,14 @@
+package net.minecraft.core.world.settings;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Iterator;
+
+public interface SettingNodeChildrenProvider {
+    int length(final @NotNull WorldConfiguration worldConfiguration);
+
+    @NotNull Iterator<@NotNull SettingNode> getChildren(final @NotNull WorldConfiguration worldConfiguration);
+
+    @Nullable KeyedSettingNode getChildByKey(final @NotNull String key, final @NotNull WorldConfiguration worldConfiguration);
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeList.java b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeList.java
new file mode 100644
index 000000000..a7fb4c0f2
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeList.java
@@ -0,0 +1,4 @@
+package net.minecraft.core.world.settings;
+
+public class SettingNodeList {
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeVisibilityListener.java b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeVisibilityListener.java
new file mode 100644
index 000000000..d62f53896
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodeVisibilityListener.java
@@ -0,0 +1,17 @@
+package net.minecraft.core.world.settings;
+
+import org.jetbrains.annotations.NotNull;
+
+public interface SettingNodeVisibilityListener {
+    boolean isVisible(final @NotNull WorldConfiguration worldConfiguration);
+
+    @SuppressWarnings("unused")
+    static boolean alwaysVisible(final @NotNull WorldConfiguration worldConfiguration) {
+        return true;
+    }
+
+    @SuppressWarnings("unused")
+    static boolean neverVisible(final @NotNull WorldConfiguration worldConfiguration) {
+        return false;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodesValues.java b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodesValues.java
new file mode 100644
index 000000000..558498999
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/SettingNodesValues.java
@@ -0,0 +1,78 @@
+package net.minecraft.core.world.settings;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+public class SettingNodesValues {
+    private final @NotNull SettingNode rootNode;
+    private final @NotNull Map<@NotNull ValueSettingNode<?>, @NotNull Object> keyedNodeValues = new Object2ObjectOpenHashMap<>();
+    private final @NotNull Map<@NotNull String, @NotNull ValueSettingNode<?>> nodesByKey = new Object2ObjectOpenHashMap<>();
+
+    public SettingNodesValues(final @NotNull SettingNode rootNode) {
+        this.rootNode = rootNode;
+    }
+
+    public @NotNull SettingNode getRootNode() {
+        return this.rootNode;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> @NotNull T getNodeValue(final @NotNull ValueSettingNode<T> node) {
+        final @Nullable T value = (T) this.keyedNodeValues.get(node);
+        if (value != null) {
+            return value;
+        }
+
+        final @NotNull T defaultValue = node.getDefaultValue();
+        this.keyedNodeValues.put(node, node.getDefaultValue());
+        this.nodesByKey.put(node.getKey(), node);
+
+        return defaultValue;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> @NotNull T getNodeValue(final @NotNull String key, final @NotNull Class<T> valueClass, final @NotNull WorldConfiguration worldConfiguration) {
+        @Nullable ValueSettingNode<?> node = this.nodesByKey.get(key);
+        if (node == null) {
+            final @Nullable KeyedSettingNode keyedSettingNode = this.rootNode.getChildByKey(key, worldConfiguration);
+            if (keyedSettingNode instanceof final @NotNull ValueSettingNode<?> nodeByKeyFromRoot) {
+                node = nodeByKeyFromRoot;
+            } else {
+                throw new IllegalArgumentException("Node does not have a value!");
+            }
+        }
+
+        if (!(valueClass.isAssignableFrom(node.getValueClass()))) {
+            throw new IllegalArgumentException("Node type " + node.getValueClass().getSimpleName() + " does not equal requested type " + valueClass.getSimpleName() + "!");
+        }
+
+        return (T) this.getNodeValue(node);
+    }
+
+    public <T> void setNodeValue(final @NotNull ValueSettingNode<T> node, final @NotNull T value) {
+        this.keyedNodeValues.put(node, value);
+        this.nodesByKey.put(node.getKey(), node);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> void setNodeValue(final @NotNull String key, final @NotNull T value, final @NotNull WorldConfiguration worldConfiguration) {
+        @Nullable ValueSettingNode<?> node = this.nodesByKey.get(key);
+        if (node == null) {
+            final @Nullable KeyedSettingNode keyedSettingNode = this.rootNode.getChildByKey(key, worldConfiguration);
+            if (keyedSettingNode instanceof final @NotNull ValueSettingNode<?> nodeByKeyFromRoot) {
+                node = nodeByKeyFromRoot;
+            } else {
+                throw new IllegalArgumentException("Node does not have a value!");
+            }
+        }
+
+        if (!(value.getClass().isAssignableFrom(node.getValueClass()))) {
+            throw new IllegalArgumentException("Node type " + node.getValueClass().getSimpleName() + " does not equal requested type " + value.getClass().getSimpleName() + "!");
+        }
+
+        this.setNodeValue((ValueSettingNode<T>) node, value);
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/ValueGenericSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/ValueGenericSettingNode.java
new file mode 100644
index 000000000..6ec1dc0a2
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/ValueGenericSettingNode.java
@@ -0,0 +1,45 @@
+package net.minecraft.core.world.settings;
+
+import net.minecraft.core.lang.text.IntoText;
+import net.minecraft.core.lang.text.LiteralText;
+import net.minecraft.core.lang.text.Text;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+public class ValueGenericSettingNode<T> extends GenericSettingNode implements ValueSettingNode<T> {
+    private final @NotNull Text name;
+    private final @NotNull T value;
+
+    public ValueGenericSettingNode(final @NotNull String key, final @NotNull Text name, final @NotNull T defaultValue) {
+        super(key);
+
+        this.name = name;
+        this.value = defaultValue;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public @NotNull Class<@NotNull T> getValueClass() {
+        return (Class<@NotNull T>) this.value.getClass();
+    }
+
+    @Override
+    public final @NotNull T getDefaultValue() {
+        return this.value;
+    }
+
+    @Override
+    public @NotNull Text getValueText(final @NotNull T value) {
+        if (value instanceof final @NotNull IntoText intoText) {
+            return intoText.intoText();
+        } else {
+            return new LiteralText(Objects.requireNonNull(value.toString()));
+        }
+    }
+
+    @Override
+    public @NotNull Text getName() {
+        return this.name;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/ValueSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/ValueSettingNode.java
new file mode 100644
index 000000000..ea2c3e7d0
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/ValueSettingNode.java
@@ -0,0 +1,12 @@
+package net.minecraft.core.world.settings;
+
+import net.minecraft.core.lang.text.Text;
+import org.jetbrains.annotations.NotNull;
+
+public interface ValueSettingNode<T> extends SettingNode, NamedSettingNode, KeyedSettingNode {
+    @NotNull Class<@NotNull T> getValueClass();
+
+    @NotNull T getDefaultValue();
+
+    @NotNull Text getValueText(final @NotNull T value);
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java b/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java
index c3f680d41..9f2cdfb7b 100644
--- a/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java
@@ -24,7 +24,8 @@ public class WorldConfiguration {
     private @NotNull WorldTypeGroups.Group worldTypeGroup = WorldTypeGroups.DEFAULT;
     private @Nullable Biome startingBiome = null;
     private final @NotNull GameRuleCollection gameRuleCollection = new GameRuleCollection();
-    private final SpawnerConfig spawnerConfig = new SpawnerConfig();
+    private final @NotNull SpawnerConfig spawnerConfig = new SpawnerConfig();
+    private final @NotNull SettingNodesValues settingNodesValues = new SettingNodesValues(DefaultSettingNodes.getRootNode());
 
     public WorldConfiguration() {
     }
@@ -118,6 +119,10 @@ public class WorldConfiguration {
         return this.startingBiome;
     }
 
+    public @NotNull SettingNodesValues getSettingNodesValues() {
+        return this.settingNodesValues;
+    }
+
     public @NotNull String getFolderName(final @NotNull ISaveFormat saveFormat) {
         final char @NotNull [] ac = ChatAllowedCharacters.ALLOWED_CHARACTERS_ARRAY;
         @NotNull String folderName = this.worldName.trim();
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/impl/SeasonSettingNode.java b/game/core/src/main/java/net/minecraft/core/world/settings/impl/SeasonSettingNode.java
new file mode 100644
index 000000000..a0457deb2
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/impl/SeasonSettingNode.java
@@ -0,0 +1,38 @@
+package net.minecraft.core.world.settings.impl;
+
+import net.minecraft.core.lang.text.IntoText;
+import net.minecraft.core.lang.text.Text;
+import net.minecraft.core.lang.text.TranslatableText;
+import net.minecraft.core.world.season.Season;
+import net.minecraft.core.world.settings.ListValueGenericSettingNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class SeasonSettingNode extends ListValueGenericSettingNode<SeasonSettingNode.SeasonEntry> {
+    public static class SeasonEntry implements IntoText {
+        private final @NotNull Text text;
+
+        public final @Nullable Season season;
+
+        private SeasonEntry() {
+            this.season = null;
+            this.text = new TranslatableText("world_settings.single_season.disabled");
+        }
+
+        public SeasonEntry(final @NotNull Season season) {
+            this.season = season;
+            this.text = new TranslatableText(season.getLanguageKey());
+        }
+
+        @Override
+        public @NotNull Text intoText() {
+            return this.text;
+        }
+    }
+
+    public static final @NotNull SeasonEntry DISABLED = new SeasonEntry();
+
+    public SeasonSettingNode(final @NotNull String key, final @NotNull Text name, final @NotNull SeasonEntry defaultValue) {
+        super(key, name, defaultValue);
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/weather/WeatherManager.java b/game/core/src/main/java/net/minecraft/core/world/weather/WeatherManager.java
index ec6ac3dcc..12ac246e3 100644
--- a/game/core/src/main/java/net/minecraft/core/world/weather/WeatherManager.java
+++ b/game/core/src/main/java/net/minecraft/core/world/weather/WeatherManager.java
@@ -3,7 +3,9 @@ package net.minecraft.core.world.weather;
 import net.minecraft.core.Global;
 import net.minecraft.core.data.gamerule.GameRules;
 import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.World;
+import net.minecraft.core.world.save.DimensionData;
 import net.minecraft.core.world.season.Season;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -160,12 +162,12 @@ public class WeatherManager {
             this.weatherDuration = getRandomWeatherDuration();
         }
 
-        this.world.dimensionData.setCurrentWeather(this.currentWeather);
-        this.world.dimensionData.setNextWeather(this.nextWeather);
-
-        this.world.dimensionData.setWeatherDuration(this.weatherDuration);
-        this.world.dimensionData.setWeatherIntensity(this.weatherIntensity);
-        this.world.dimensionData.setWeatherPower(this.weatherPower);
+        final @NotNull DimensionData dimensionData = this.world.getDimensionData();
+        dimensionData.setCurrentWeather(this.currentWeather);
+        dimensionData.setNextWeather(this.nextWeather);
+        dimensionData.setWeatherDuration(this.weatherDuration);
+        dimensionData.setWeatherIntensity(this.weatherIntensity);
+        dimensionData.setWeatherPower(this.weatherPower);
 
         return true;
     }
diff --git a/game/core/src/main/resources/lang/en_US/gui.lang b/game/core/src/main/resources/lang/en_US/gui.lang
index c688f7e61..3edc9f344 100644
--- a/game/core/src/main/resources/lang/en_US/gui.lang
+++ b/game/core/src/main/resources/lang/en_US/gui.lang
@@ -229,10 +229,10 @@ gui.options.page.accessibility.title=Accessibility
 gui.options.page.accessibility.category.accessibility=Accessibility
 
 gui.options.page.video.title=Video
-gui.options.page.video.category.graphics=Graphics
-gui.options.page.video.category.performance=Performance
-gui.options.page.video.category.post_processing=Post-Processing
+gui.options.page.video.category.display=Display
 gui.options.page.video.category.visuals=Visuals
+gui.options.page.video.category.post_processing=Post-Processing
+gui.options.page.video.category.performance=Performance
 
 gui.options.page.controls.title=Controls
 gui.options.page.controls.button.controller_setup=Controller Setup...
diff --git a/game/server/src/main/java/net/minecraft/server/MinecraftServer.java b/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
index 30570490f..81f0ab46d 100644
--- a/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -78,7 +78,7 @@ import net.minecraft.core.world.save.LevelData;
 import net.minecraft.core.world.save.LevelStorage;
 import net.minecraft.core.world.save.SaveConverters;
 import net.minecraft.core.world.save.SaveFormats;
-import net.minecraft.core.world.save.SaveHandlerServer;
+import net.minecraft.core.world.save.LevelStorageServer;
 import net.minecraft.core.world.save.mcregion.SaveFormat19134;
 import net.minecraft.core.world.type.WorldType;
 import net.minecraft.core.world.type.WorldTypes;
@@ -106,9 +106,7 @@ import net.minecraft.server.net.command.commands.CommandWhoIs;
 import net.minecraft.server.world.ConvertProgressUpdater;
 import net.minecraft.server.world.WorldManager;
 import net.minecraft.server.world.WorldServer;
-import net.minecraft.server.world.WorldServerMulti;
 import net.minecraft.server.world.chunk.provider.ChunkProviderServer;
-import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 
@@ -519,7 +517,7 @@ public class MinecraftServer
 
                     levelDataTag.putInt("version", toWorldFormat.getSaveVersion());
 
-                    LevelStorage saveHandler = toWorldFormat.getSaveHandler(worldDirName, false);
+                    LevelStorage saveHandler = toWorldFormat.getLevelStorage(worldDirName, false);
                     saveHandler.saveLevelDataRaw(levelDataTag);
                 }
             }
@@ -531,19 +529,18 @@ public class MinecraftServer
     {
         convertWorld(saveFormat, worldDirName);
         dimensionWorlds = new Int2ObjectArrayMap<>();
-        SaveHandlerServer saveHandler = new SaveHandlerServer(saveFormat, new File("."), worldDirName, true);
+        LevelStorageServer saveHandler = new LevelStorageServer(saveFormat, new File("."), worldDirName, true);
         for (Int2ObjectMap.Entry<Dimension> entry : Dimension.getDimensionList().int2ObjectEntrySet())
         {
             int dimId = entry.getIntKey();
-            Dimension dimension = entry.getValue();
             WorldServer worldServer;
             if (dimId == 0)
             {
-                worldServer = new WorldServer(this, saveHandler, worldDirName, dimId, defaultWorldType, l);
+                worldServer = new WorldServer(this, saveHandler, worldDirName, dimId, l);
             }
             else
             {
-                worldServer = new WorldServerMulti(this, saveHandler, worldDirName, dimId, dimension.defaultWorldType, l, dimensionWorlds.get(0));
+                worldServer = new WorldServer(this, dimensionWorlds.get(0), dimId);
             }
             dimensionWorlds.put(dimId, worldServer);
             worldServer.addListener(new WorldManager(this, dimensionWorlds.get(dimId)));
diff --git a/game/server/src/main/java/net/minecraft/server/entity/player/PlayerServer.java b/game/server/src/main/java/net/minecraft/server/entity/player/PlayerServer.java
index a3ea0fdd1..7410b9e8e 100644
--- a/game/server/src/main/java/net/minecraft/server/entity/player/PlayerServer.java
+++ b/game/server/src/main/java/net/minecraft/server/entity/player/PlayerServer.java
@@ -549,7 +549,7 @@ public class PlayerServer extends Player implements ContainerListener {
         getNextWindowId();
         playerNetServerHandler.sendPacket(new PacketContainerOpen(currentWindowId, PacketContainerOpen.TYPE_DISPENSER, tileEntity.getNameTranslationKey(), tileEntity.getContainerSize()));
         containerMenu.onCraftGuiClosed(this);
-        containerMenu = new MenuTrap(inventory, tileEntity);
+        containerMenu = new MenuDispenser(inventory, tileEntity);
         containerMenu.containerId = currentWindowId;
         containerMenu.addSlotListener(this);
     }
diff --git a/game/server/src/main/java/net/minecraft/server/net/PlayerList.java b/game/server/src/main/java/net/minecraft/server/net/PlayerList.java
index 8a6a5ab67..1c6416ea0 100644
--- a/game/server/src/main/java/net/minecraft/server/net/PlayerList.java
+++ b/game/server/src/main/java/net/minecraft/server/net/PlayerList.java
@@ -109,7 +109,7 @@ public class PlayerList
 
     public void setPlayerManager(Int2ObjectMap<WorldServer> worldServerMap)
     {
-        playerIo = worldServerMap.get(0).getSaveHandler().getPlayerFileData();
+        playerIo = worldServerMap.get(0).getLevelStorage().getPlayerFileData();
     }
 
     public void syncPlayerDimension(PlayerServer player)
diff --git a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerLogin.java b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerLogin.java
index 71d88812f..6eb917c79 100644
--- a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerLogin.java
+++ b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerLogin.java
@@ -183,7 +183,18 @@ public class PacketHandlerLogin extends PacketHandler
             PacketHandlerServer packetHandler = new PacketHandlerServer(mcServer, netManager, player);
             try
             {
-                packetHandler.sendPacket(new PacketLogin("", new UUID(0, 0), player.id, worldserver.getRandomSeed(), (byte) worldserver.dimension.id, (byte) Registries.WORLD_TYPES.getNumericIdOfItem(worldserver.dimensionData.getWorldType()), NetworkManager.PACKET_DELAY, RSA.getPublicKey(RSA.RSAKeyChain.getPublic())));
+                packetHandler.sendPacket(
+                    new PacketLogin(
+                    "",
+                        new UUID(0, 0),
+                        player.id,
+                        worldserver.getRandomSeed(),
+                        (byte) worldserver.dimension.id,
+                        (byte) Registries.WORLD_TYPES.getNumericIdOfItem(worldserver.getDimensionData().getWorldType()),
+                        NetworkManager.PACKET_DELAY,
+                        RSA.getPublicKey(RSA.RSAKeyChain.getPublic())
+                    )
+                );
 
                 Key aesKey = AES.generateKey();
                 packetHandler.sendPacket(new PacketAESSendKey(RSA.encrypt(AES.getKey(aesKey), RSA.getPublicKey(loginPacket.publicKey))));
diff --git a/game/server/src/main/java/net/minecraft/server/world/WorldServer.java b/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
index 07cb884d0..7eb8556cb 100644
--- a/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
+++ b/game/server/src/main/java/net/minecraft/server/world/WorldServer.java
@@ -42,18 +42,36 @@ public class WorldServer extends World
     private long lastWeatherSend = System.currentTimeMillis();
 
 
-    public WorldServer(MinecraftServer minecraftserver, LevelStorage isavehandler, String name, int dimensionId, WorldType worldType, long seed)
-    {
-        super(isavehandler, name, seed, Dimension.getDimensionList().get(dimensionId), worldType, null);
+    public WorldServer(
+        MinecraftServer minecraftServer,
+        LevelStorage isavehandler,
+        String name,
+        int dimensionId,
+        long seed
+    ) {
+        super(isavehandler, name, seed, Dimension.getDimensionList().get(dimensionId), null);
+
+        entityIntHashMap = new Int2ObjectOpenHashMap<>();
+        mcServer = minecraftServer;
+        getLevelData().setLastPlayerUUID(null);
+    }
+
+    public WorldServer(
+        MinecraftServer minecraftServer,
+        WorldServer parent,
+        int dimensionId
+    ) {
+        super(parent, Dimension.getDimensionList().get(dimensionId));
+
         entityIntHashMap = new Int2ObjectOpenHashMap<>();
-        mcServer = minecraftserver;
+        mcServer = minecraftServer;
         getLevelData().setLastPlayerUUID(null);
     }
 
     @Override
     protected IChunkProvider createChunkProvider()
     {
-        IChunkLoader ichunkloader = saveHandler.getChunkLoader(dimension);
+        IChunkLoader ichunkloader = this.getLevelStorage().getChunkLoader(dimension);
         this.chunkProviderServer = (ChunkProviderServer) Global.accessor.createChunkProvider(this, ichunkloader);
         return chunkProviderServer;
     }
@@ -76,8 +94,8 @@ public class WorldServer extends World
     @Override
     public boolean canMineBlock(final @NotNull Player player, final @NotNull TilePosc tilePos) {
         if (this.mcServer.spawnProtectionRange <= 0 || this.mcServer.playerList.isOp(player.uuid)) return super.canMineBlock(player, tilePos);
-        final int dx = (int)MathHelper.abs(tilePos.x() - this.levelData.getSpawnX());
-        final int dz = (int)MathHelper.abs(tilePos.z() - this.levelData.getSpawnZ());
+        final int dx = (int)MathHelper.abs(tilePos.x() - this.getLevelData().getSpawnX());
+        final int dz = (int)MathHelper.abs(tilePos.z() - this.getLevelData().getSpawnZ());
         return Math.max(dx, dz) > this.mcServer.spawnProtectionRange;
     }
 
@@ -122,8 +140,8 @@ public class WorldServer extends World
                     }
                 }
 
-                long timePlusOneDay = levelData.getWorldTime() + Global.DAY_LENGTH_TICKS;
-                levelData.setWorldTime(timePlusOneDay - timePlusOneDay % Global.DAY_LENGTH_TICKS + worldType.getSunriseTick(this) + Global.DAY_LENGTH_TICKS / 24);
+                long timePlusOneDay = getLevelData().getWorldTime() + Global.DAY_LENGTH_TICKS;
+                getLevelData().setWorldTime(timePlusOneDay - timePlusOneDay % Global.DAY_LENGTH_TICKS + worldType.getSunriseTick(this) + Global.DAY_LENGTH_TICKS / 24);
                 wakeUpAllPlayers();
             }
         }
@@ -227,7 +245,7 @@ public class WorldServer extends World
 
     public void checkLock()
     {
-        saveHandler.checkSessionLock();
+        this.getLevelStorage().checkSessionLock();
     }
     @Override
     public void tick() {
diff --git a/game/server/src/main/java/net/minecraft/server/world/WorldServerMulti.java b/game/server/src/main/java/net/minecraft/server/world/WorldServerMulti.java
deleted file mode 100644
index d31ade613..000000000
--- a/game/server/src/main/java/net/minecraft/server/world/WorldServerMulti.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package net.minecraft.server.world;
-
-import net.minecraft.core.world.type.WorldType;
-import net.minecraft.server.MinecraftServer;
-import net.minecraft.core.world.save.LevelStorage;
-
-public class WorldServerMulti extends WorldServer
-{
-
-    public WorldServerMulti(MinecraftServer minecraftserver, LevelStorage isavehandler, String name, int dimensionId, WorldType worldType, long seed, WorldServer worldserver)
-    {
-        super(minecraftserver, isavehandler, name, dimensionId, worldType, seed);
-        savedDataStorage = worldserver.savedDataStorage;
-    }
-}
diff --git a/gradle.properties b/gradle.properties
index cecfe8135..37ab4b219 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,6 @@
 org.gradle.parallel = true
 org.gradle.daemon = true
+org.gradle.caching = true
 
 bta.group = net.minecraft
 bta.version = 7.4
diff --git a/util/datagen/build.gradle.kts b/util/datagen/build.gradle.kts
index 9f0a275e2..49736ad3d 100644
--- a/util/datagen/build.gradle.kts
+++ b/util/datagen/build.gradle.kts
@@ -31,8 +31,8 @@ val runDatagen by tasks.registering(JavaExec::class) {
     args = listOf("--output", recipesDir.toString())
 }
 
-val datagenJar by tasks.registering(Jar::class) {
-    group = "bta.generator"
+tasks.named("jar", Jar::class) {
+    duplicatesStrategy = DuplicatesStrategy.INCLUDE;
     from(sourceSets["generated"].resources)
 }
 //endregion
@@ -46,5 +46,5 @@ sourceSets {
 }
 
 artifacts {
-    add("output", datagenJar)
+    add("output", tasks.getAt("jar"))
 }
\ No newline at end of file
diff --git a/util/seed-viewer/build.gradle.kts b/util/seed-viewer/build.gradle.kts
index 0a64b8598..0d1f2e98f 100644
--- a/util/seed-viewer/build.gradle.kts
+++ b/util/seed-viewer/build.gradle.kts
@@ -54,7 +54,6 @@ val versionGenerator by tasks.registering {
                     
                     final class Version {
                         public static final String VERSION = "$version";
-                        public static final long BUILD_TIME = ${Date().time}L;
                     }
                 """.trimIndent()
         )
