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 bd0ea8cdb..6f50c6fa1 100644
--- a/game/client/src/main/java/net/minecraft/client/Minecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/Minecraft.java
@@ -32,7 +32,8 @@ import net.minecraft.client.gui.error.ScreenOutOfMemory;
 import net.minecraft.client.gui.options.data.OptionsPages;
 import net.minecraft.client.gui.toasts.GuiElementToastsHud;
 import net.minecraft.client.holiday.Holidays;
-import net.minecraft.client.util.debug.GLFWLoggerCallback;
+import net.minecraft.client.player.controller.PlayerControllerSP;
+import net.minecraft.client.world.save.SaveHandlerClientSP;
 import net.minecraft.core.net.CertificateHelper;
 import net.minecraft.client.net.handler.PacketHandlerClient;
 import net.minecraft.client.render.TileEntityRenderDispatcher;
@@ -84,6 +85,9 @@ import net.minecraft.core.util.helper.Color;
 import net.minecraft.core.util.helper.DyeColor;
 import net.minecraft.core.util.helper.LoggedPrintStream;
 import net.minecraft.core.util.helper.UUIDHelper;
+import net.minecraft.core.world.save.SaveFormatBase;
+import net.minecraft.core.world.save.SaveHandlerBase;
+import net.minecraft.core.world.settings.WorldConfiguration;
 import net.minecraft.core.world.type.WorldType;
 import net.minecraft.core.world.type.WorldTypeGroups;
 
@@ -98,7 +102,6 @@ import org.jetbrains.annotations.Nullable;
 
 import org.lwjgl.LWJGLException;
 import org.lwjgl.glfw.GLFW;
-import org.lwjgl.glfw.GLFWErrorCallback;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
 import org.lwjgl.opengl.GL11;
@@ -291,7 +294,7 @@ public class Minecraft
      * Do not display the ingame crash handler until this is set to true
      */
     private boolean gameStarted = false;
-    private int mojrand = new Random(System.currentTimeMillis()).nextInt(200);
+    private final int mojrand = new Random(System.currentTimeMillis()).nextInt(200);
     public TerrainRenderer terrainRenderer;
 
     private static final TimeKeeper STARTUP_TIKE_KEEPER = new TimeKeeper();
@@ -357,7 +360,7 @@ public class Minecraft
                     try {
                         Thread.sleep(2147483647L);
                     }
-                    catch (final InterruptedException ex) {}
+                    catch (final InterruptedException ignored) {}
                 }
             }
         };
@@ -516,7 +519,9 @@ public class Minecraft
 
         resolution.setScale(gameSettings.guiScale.value);
 
-		saveFormat = new SaveFormat19134(new File(mcDataDir, "saves"));
+        SaveFormatBase handlerBase = new SaveFormat19134(new File(mcDataDir, "saves"));
+        handlerBase.factory = SaveHandlerClientSP::new;
+		saveFormat = handlerBase;
 		I18n.initialize(gameSettings.language.value);
 
 		if (!gameSettings.startInFullscreen.value) {
@@ -955,33 +960,30 @@ public class Minecraft
 
 	public void shutdownMinecraftApplet() {
 		// This could be called very early if the game crashes, so everything could be null!
-		try {
-			try {
-				if(gameSettings != null) {
-					gameSettings.saveOptions();
-				}
-			}catch (Exception e) {
-                LOGGER.error("Exception occurred while saving settings! Some settings may be lost!", e);
-			}
-			if(statsCounter != null) {
-                statsCounter.fetchStats();
-				statsCounter.saveStats();
-			}
-            LOGGER.info("Stopping!");
-			try {
-				changeWorld(null);
-			}catch (Throwable ignored) {
-			}
-			try {
-				GLAllocation.deleteTexturesAndDisplayLists();
-			}catch (Throwable ignored) {
-			}
-			if(sndManager != null) {
-				sndManager.destroy();
-			}
-		}finally {
-		}
-		System.gc();
+        try {
+            if(gameSettings != null) {
+                gameSettings.saveOptions();
+            }
+        } catch (Exception e) {
+            LOGGER.error("Exception occurred while saving settings! Some settings may be lost!", e);
+        }
+        if(statsCounter != null) {
+            statsCounter.fetchStats();
+            statsCounter.saveStats();
+        }
+        LOGGER.info("Stopping!");
+        try {
+            changeWorld(null);
+        }catch (Throwable ignored) {
+        }
+        try {
+            GLAllocation.deleteTexturesAndDisplayLists();
+        }catch (Throwable ignored) {
+        }
+        if(sndManager != null) {
+            sndManager.destroy();
+        }
+        System.gc();
 		if(!hasCrashed) {
 			System.exit(0);
 		}
@@ -1002,43 +1004,33 @@ public class Minecraft
         {
             long currentTime = System.currentTimeMillis();
             int fpsCounter = 0;
-            do
-            {
-                if(!running)
-                {
-                    break;
-                }
+            while (running) {
+                try {
+                    Debug.reset();
+                    Debug.push("update");    // 1
+                    Debug.push("misc");        // 2
 
-                try
-                {
-                	Debug.reset();
-                	Debug.push("update");	// 1
-                	Debug.push("misc");		// 2
-                	
-					long tickStartTime = System.currentTimeMillis();
+                    long tickStartTime = System.currentTimeMillis();
                     Time.tick();
                     ObjectPool.resetAllPools();
 
-                    if(gameWindow.shouldClose()) shutdown();
-                    if(isGamePaused && currentWorld != null && !(currentScreen instanceof ScreenModelViewer))
-                    {
+                    if (gameWindow.shouldClose()) shutdown();
+                    if (isGamePaused && currentWorld != null && !(currentScreen instanceof ScreenModelViewer)) {
                         float temp = timer.partialTicks;
                         timer.advanceTime();
                         timer.partialTicks = temp;
-                    } else
-                    {
+                    } else {
                         timer.advanceTime();
                     }
                     if (!doPartialTick) timer.partialTicks = 1f;
 
                     mouseInput.update();
-                    if(controllerInput != null && currentScreen != null)
-                    {
+                    if (controllerInput != null && currentScreen != null) {
                         controllerInput.moveCursor();
                     }
-                    
-                    Debug.pop();			// 2
-                	Debug.change("tick");	// 1
+
+                    Debug.pop();            // 2
+                    Debug.change("tick");    // 1
                     if (currentWorld instanceof WorldClientMP) {
                         ((WorldClientMP) currentWorld).processPackets();
                     }
@@ -1047,25 +1039,21 @@ public class Minecraft
                         this.currentScreen.tick();
                     }
 
-                    for(int tick = 0; tick < timer.frames; tick++)
-                    {
+                    for (int tick = 0; tick < timer.frames; tick++) {
                         ticksRan++;
-                        try
-                        {
+                        try {
                             runTick();
                             continue;
-                        }
-                        catch(MinecraftException e)
-                        {
+                        } catch (MinecraftException e) {
                             currentWorld = null;
                             LOGGER.error("MinecraftException occurred while attempting to run tick, exiting world!", e);
                         }
                         changeWorld(null);
                         displayScreen(new ScreenConflictWarning());
                     }
-                    
+
                     Debug.change("update"); // 1
-                    Debug.push("misc");		// 2
+                    Debug.push("misc");        // 2
 
                     OpenGLHelper.checkError("pre render");
                     BlockModelGrass.fancyGrass = gameSettings.fancyGraphics.value == 1;
@@ -1073,9 +1061,8 @@ public class Minecraft
                     BlockLogicLeavesBase.enableTreeShadowing = gameSettings.treeShadows.value;
                     sndManager.updateListener(thePlayer, timer.partialTicks);
                     glEnable(GL_TEXTURE_2D);
-                    if(currentWorld != null)
-                    {
-                    	Debug.change("updatelight");
+                    if (currentWorld != null) {
+                        Debug.change("updatelight");
                         try {
                             currentWorld.updatingLighting();
                         } catch (Throwable t) {
@@ -1083,18 +1070,17 @@ public class Minecraft
                         }
                         Debug.change("misc");
                     }
-                    
-                    
+
+
                     Debug.change("misc"); // 2
-                    
-                    if(thePlayer != null && thePlayer.isInWall())
-                    {
+
+                    if (thePlayer != null && thePlayer.isInWall()) {
                         gameSettings.thirdPersonView.value = 0;
                     }
 
                     Debug.change("displayupdate"); // 2
                     gameWindow.update();
-                    
+
                     Debug.change("render"); // 2
                     Debug.push("post"); // 3
 
@@ -1102,21 +1088,19 @@ public class Minecraft
                     glEnable(GL_ALPHA_TEST);
 
                     Debug.change("misc");  // 3
-                    if(!skipRenderWorld)
-                    {
-                        if(playerController != null)
-                        {
+                    if (!skipRenderWorld) {
+                        if (playerController != null) {
                             playerController.render(timer.partialTicks);
                         }
                         worldRenderer.updateCameraAndRender(timer.partialTicks);
                     }
-                    
-                    if(gameSettings.pauseOnLostFocus.value) {
-                        if(!gameWindow.isFocused()) {
-                            if(System.currentTimeMillis() > lastFocusTime + 250) {
+
+                    if (gameSettings.pauseOnLostFocus.value) {
+                        if (!gameWindow.isFocused()) {
+                            if (System.currentTimeMillis() > lastFocusTime + 250) {
                                 displayInGameMenu();
                             }
-                        }else {
+                        } else {
                             lastFocusTime = System.currentTimeMillis();
                         }
                     }
@@ -1124,9 +1108,9 @@ public class Minecraft
 
                     switch (downloadResourcesThread.downloading) {
                         case DOWNLOADING: {
-                            double b = java.lang.Math.sin((System.currentTimeMillis() / 1000.0) * 4);
+                            double b = Math.sin((System.currentTimeMillis() / 1000.0) * 4);
                             b = b * 0.2 + 0.8;
-                            int ib = (int) java.lang.Math.round(b * 255);
+                            int ib = (int) Math.round(b * 255);
                             int color = 0xFF000000 | (ib << 16) | (ib << 8) | (ib);
 
                             int barColor = Color.doubleToIntARGB(1, 0.5 * b, 1.0 * b, 0.5 * b);
@@ -1168,31 +1152,30 @@ public class Minecraft
 
                     Debug.change("post");  // 3
                     renderer.endRenderGame(timer.partialTicks);
-                    
-                    Debug.pop();	// 3
-                    
-                    
+
+                    Debug.pop();    // 3
+
+
                     Debug.change("misc");
-                    
-                    if(gameWindow.wasResized()) {
+
+                    if (gameWindow.wasResized()) {
                         resize();
                     }
                     OpenGLHelper.checkError("post render");
                     fpsCounter++;
                     isGamePaused = !isMultiplayerWorld() && currentScreen != null && currentScreen.isPauseScreen();
-                    while(System.currentTimeMillis() >= currentTime + 1000L)
-                    {
+                    while (System.currentTimeMillis() >= currentTime + 1000L) {
                         debug = fpsCounter + " fps, " + ChunkRenderer.updates + " chunk updates";
                         debugFPS = fpsCounter + " fps";
                         ChunkRenderer.updates = 0;
                         currentTime += 1000L;
                         fpsCounter = 0;
                     }
-					if(currentWorld == null) {
-						long now = System.currentTimeMillis();
-						// Limit FPS in menu
-						long sleepTime = (1000 / 60 - (now - tickStartTime)) - 1;
-						if(sleepTime > 0) {
+                    if (currentWorld == null) {
+                        long now = System.currentTimeMillis();
+                        // Limit FPS in menu
+                        long sleepTime = (1000 / 60 - (now - tickStartTime)) - 1;
+                        if (sleepTime > 0) {
                             long currentSleep = 0;
                             while (currentSleep < sleepTime - 1) {
                                 Thread.sleep(1L);
@@ -1205,31 +1188,25 @@ public class Minecraft
                                     this.currentScreen.tick();
                                 }
                             }
-						}
-					}
-					
-					Debug.pop();	// 2
-					Debug.pop();	// 1
-					if(gameSettings.showFrameTimes.value) {
-						DebugRender.render(font);
-					}
-					
+                        }
+                    }
+
+                    Debug.pop();    // 2
+                    Debug.pop();    // 1
+                    if (gameSettings.showFrameTimes.value) {
+                        DebugRender.render(font);
+                    }
+
                     screenshotListener();
-                }
-                catch(MinecraftException minecraftexception)
-                {
+                } catch (MinecraftException minecraftexception) {
                     currentWorld = null;
                     changeWorld(null);
                     displayScreen(new ScreenConflictWarning());
-                }
-                catch(OutOfMemoryError outofmemoryerror)
-                {
+                } catch (OutOfMemoryError outofmemoryerror) {
                     freeUpMemory();
                     displayScreen(new ScreenOutOfMemory());
                     System.gc();
-                }
-                catch(Throwable throwable)
-                {
+                } catch (Throwable throwable) {
                     if (currentWorld != null) {
                         try {
                             changeWorld(null);
@@ -1238,9 +1215,9 @@ public class Minecraft
                         }
                     }
                     freeUpMemory();
-                    onMinecraftCrash(new UnexpectedThrowable("Unexpected error", throwable));	
+                    onMinecraftCrash(new UnexpectedThrowable("Unexpected error", throwable));
                 }
-            } while(true);
+            }
         }
         catch(MinecraftError ignored) { }
         finally {
@@ -1343,9 +1320,9 @@ public class Minecraft
         }
     }
 
-    private void mineBlocks(int i, boolean flag)
+    private void mineBlocks(int mouseButton, boolean leftClickDown)
     {
-        if(flag && objectMouseOver != null && objectMouseOver.hitType == HitResult.HitType.TILE && i == 0)
+        if(leftClickDown && objectMouseOver != null && objectMouseOver.hitType == HitResult.HitType.TILE && mouseButton == 0)
         {
             int blockX = objectMouseOver.x;
             int blockY = objectMouseOver.y;
@@ -1364,7 +1341,7 @@ public class Minecraft
         }
         else
         {
-            playerController.stopDestroyBlock(flag);
+            playerController.stopDestroyBlock(leftClickDown);
         }
     }
 
@@ -1379,7 +1356,7 @@ public class Minecraft
                 if (clickType == 1) {
                     if (thePlayer.xRot < 45) {break bridge;}
                     List<AABB> cubes = thePlayer.world.getCubes(thePlayer, thePlayer.bb.cloneMove(0, -1.0, 0.0));
-                    if (cubes.size() < 1){break bridge;}
+                    if (cubes.isEmpty()){break bridge;}
                     AABB cube = cubes.get(0);
                     if (cube == null){break bridge;}
                     int blockX = MathHelper.floor(thePlayer.x);
@@ -2198,6 +2175,7 @@ public class Minecraft
     }
     
     public void handleControllerInput() {
+        assert controllerInput != null;
         try{
             controllerInput.update();
         }catch (Exception e) {
@@ -2296,12 +2274,39 @@ public class Minecraft
         return currentWorld != null && currentWorld.isClientSide;
     }
 
-    public void startWorld(String worldDirName, String worldName, long seed)
-    {
-        startWorld(worldDirName, worldName, seed, null);
+    public void createAndStartWorld(final @NotNull WorldConfiguration worldConfiguration) {
+        final @NotNull LevelStorage levelStorage = this.saveFormat.getSaveHandler(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 WorldClient world = new WorldClient(
+                levelStorage,
+                worldConfiguration.getFinalWorldName(),
+                worldConfiguration.getFinalNumericSeed(),
+                Dimension.OVERWORLD,
+                null
+        );
+        world.getLevelData().setCheatsEnabled(worldConfiguration.getCheatsEnabled());
+
+        this.statsCounter.add(StatList.createWorldStat, 1);
+        this.statsCounter.add(StatList.startGameStat, 1);
+
+        this.playerController = new PlayerControllerSP(this);
+
+        changeWorld(world, I18n.getInstance().translateKey("gui.loading.label.generating"));
+
+        this.thePlayer.setGamemode(worldConfiguration.getGamemode());
+        if (worldConfiguration.getGamemode() == Gamemode.adventure) {
+            this.thePlayer.inventory.insertItem(Items.TOOL_AXE_WOOD.getDefaultStack(), true);
+        }
     }
 
-    public void startWorld(String worldDirName, String worldName, long seed, WorldTypeGroups.Group worldTypeGroup)
+    public void startWorld(String worldDirName)
     {
         changeWorld(null);
         System.gc();
@@ -2320,38 +2325,22 @@ public class Minecraft
 
         if(worldSaveVersion < CURRENT_SAVE_VERSION)
         {
-            convertSaveVersion(worldSaveVersion, worldDirName, worldName);
+            convertSaveVersion(worldSaveVersion, worldDirName);
         }
         else
         {
             I18n i18n = I18n.getInstance();
             LevelStorage saveHandler = saveFormat.getSaveHandler(worldDirName, false);
-            if (worldTypeGroup != null) {
-                for (Dimension dimension : Dimension.getDimensionList().values()) {
-                    WorldType worldType = worldTypeGroup.get(dimension);
-                    if (worldType != null) {
-                        DimensionData data = saveHandler.getDimensionData(dimension.id);
-                        if (data == null) {
-                            data = new DimensionData(worldType);
-                        } else {
-                            data.setWorldType(worldType);
-                        }
-                        saveHandler.saveDimensionData(dimension.id, data);
-                    }
-                }
-            }
-            WorldClient world = new WorldClient(saveHandler, worldName, seed, null, null);
-            if(world.isNewWorld)
-            {
-                statsCounter.add(StatList.createWorldStat, 1);
-                statsCounter.add(StatList.startGameStat, 1);
-                changeWorld(world, i18n.translateKey("gui.loading.label.generating"));
-            } else
-            {
-                statsCounter.add(StatList.loadWorldStat, 1);
-                statsCounter.add(StatList.startGameStat, 1);
-                changeWorld(world, i18n.translateKey("gui.loading.label.loading"));
+            Dimension dimension = null;
+            CompoundTag tag = saveHandler.getPlayerData(session.username, session.uuid);
+            if (tag != null) {
+                dimension = Dimension.getDimensionList().get(tag.getInteger("Dimension"));
             }
+
+            WorldClient world = new WorldClient(saveHandler, null, 0L, dimension, null);
+            statsCounter.add(StatList.loadWorldStat, 1);
+            statsCounter.add(StatList.startGameStat, 1);
+            changeWorld(world, i18n.translateKey("gui.loading.label.loading"));
         }
     }
 
@@ -2416,7 +2405,7 @@ public class Minecraft
         changeWorld(world, loadingTitle, null);
     }
 
-    public void changeWorld(WorldClient world, String loadingTitle, Player player)
+    public void changeWorld(WorldClient world, String loadingTitle, @Nullable Player player)
     {
     	mainMenuBackground.deleteTextures();
     	gameSettings.showCollisionBoxes.value = false;
@@ -2449,6 +2438,9 @@ public class Minecraft
                 }
                 currentWorld.getChunkProvider().unloadAllChunks();
                 currentWorld.onUnload();
+                if (player != null) {
+                    currentWorld.saveHandler.save(player);
+                }
             }
         }
         currentWorld = world;
@@ -2462,7 +2454,7 @@ public class Minecraft
             {
                 if(player == null)
                 {
-                    thePlayer = (PlayerLocal)world.findSubclassOf(PlayerLocal.class);
+                    thePlayer = null;
                 }
             }
             else if(thePlayer != null)
@@ -2494,7 +2486,7 @@ public class Minecraft
             int i = MathHelper.floor_float((int)thePlayer.x) >> 4;
             int j = MathHelper.floor_float((int)thePlayer.z) >> 4;
             ichunkprovider.setCurrentChunkOver(i, j);
-            world.spawnPlayerWithLoadedChunks(thePlayer);
+            world.spawnPlayerWithLoadedChunks(thePlayer, false);
             if(world.isNewWorld)
             {
                 world.saveWorldIndirectly(loadingScreen);
@@ -2513,7 +2505,7 @@ public class Minecraft
         worldRenderer.lightmapHelper.setup();
     }
 
-    private void convertSaveVersion(int fromVersion, String worldDirName, String worldName)
+    private void convertSaveVersion(int fromVersion, String worldDirName)
     {
         ISaveFormat fromWorldFormat = SaveFormats.createSaveFormat(fromVersion, new File(mcDataDir, "saves"));
 
@@ -2553,7 +2545,7 @@ public class Minecraft
                     LevelStorage saveHandler = toWorldFormat.getSaveHandler(worldDirName, false);
                     saveHandler.saveLevelDataRaw(levelDataTag);
 
-                    startWorld(worldDirName, worldName, 0L);
+                    startWorld(worldDirName);
                 }
             }
         }
@@ -2679,7 +2671,7 @@ public class Minecraft
         }
         thePlayer.setLastDeathCoordinate(lastDeathCoordinates);
         playerController.initPlayer(thePlayer);
-        currentWorld.spawnPlayerWithLoadedChunks(thePlayer);
+        currentWorld.spawnPlayerWithLoadedChunks(thePlayer, true);
         thePlayer.input = new PlayerInput(this);
         thePlayer.id = j;
         thePlayer.animate4();
@@ -2720,11 +2712,9 @@ public class Minecraft
 
         sessionId = sessionId == null ? "" : sessionId;
         if (uuid == null || !UUIDHelper.isUUID(uuid)) {
-            if (username != null) {
-                UUID nameUUID = UUIDHelper.getUUIDFromName(username);
-                if (nameUUID != null) {
-                    uuid = nameUUID.toString();
-                }
+            UUID nameUUID = UUIDHelper.getUUIDFromName(username);
+            if (nameUUID != null) {
+                uuid = nameUUID.toString();
             }
             if (uuid == null) {
                 uuid = "00000000-0000-0000-0000-000000000000";
@@ -2750,7 +2740,7 @@ public class Minecraft
         ImageIO.setUseCache(false); // Speeds up boot time
         checkLwjglVersion();
         if (isClientASteamDeck()) {
-            System.out.println("Client is a Steam Deck!");
+            LOGGER.info("Client is a Steam Deck!");
         }
 
         String playerName = null;
@@ -2770,7 +2760,7 @@ public class Minecraft
                         playerName = args[pointer + 1];
                         pointer++;
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        LOGGER.error("", e);
                     }
                     pointer++;
                     break;
@@ -2780,7 +2770,7 @@ public class Minecraft
                         sessionId = args[pointer + 1];
                         pointer++;
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        LOGGER.error("", e);
                     }
                     pointer++;
                     break;
@@ -2790,7 +2780,7 @@ public class Minecraft
                         uuid = args[pointer + 1];
                         pointer++;
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        LOGGER.error("", e);
                     }
                     pointer++;
                     break;
@@ -2800,7 +2790,7 @@ public class Minecraft
                         gameDirectory = new File(args[pointer + 1]);
                         pointer++;
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        LOGGER.error("", e);
                     }
                     pointer++;
                     break;
@@ -2810,7 +2800,7 @@ public class Minecraft
                         server = args[pointer + 1];
                         pointer++;
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        LOGGER.error("", e);
                     }
                     pointer++;
                     break;
@@ -2820,7 +2810,7 @@ public class Minecraft
                         port = Integer.parseInt(args[pointer + 1]);
                         pointer++;
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        LOGGER.error("", e);
                     }
                     pointer++;
                     break;
@@ -2830,7 +2820,7 @@ public class Minecraft
                         width = Integer.parseInt(args[pointer + 1]);
                         pointer++;
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        LOGGER.error("", e);
                     }
                     pointer++;
                     break;
@@ -2840,7 +2830,7 @@ public class Minecraft
                         height = Integer.parseInt(args[pointer + 1]);
                         pointer++;
                     } catch (Exception e) {
-                        e.printStackTrace();
+                        LOGGER.error("", e);
                     }
                     pointer++;
                     break;
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ClickableLabelElement.java b/game/client/src/main/java/net/minecraft/client/gui/ClickableLabelElement.java
index 868eabf83..8f833c0d6 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ClickableLabelElement.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ClickableLabelElement.java
@@ -1,28 +1,45 @@
 package net.minecraft.client.gui;
 
 import net.minecraft.client.Minecraft;
+import net.minecraft.client.render.texture.stitcher.IconCoordinate;
+import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.client.render.window.CursorShape;
 import net.minecraft.core.util.helper.MathHelper;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.opengl.GL11;
 
-public class ClickableLabelElement extends ButtonElement
-{
+import static net.minecraft.client.render.OpenGLHelper.glColor1i;
+
+public class ClickableLabelElement extends ButtonElement {
     private int textColor;
     private int bgColor;
     private boolean shadow = true;
+    private final @Nullable IconCoordinate icon;
 
-    public ClickableLabelElement(int id, int xPosition, int yPosition, String s, int textColor, int bgColor)
-    {
+    public ClickableLabelElement(int id, int xPosition, int yPosition, String s, int textColor, int bgColor, @Nullable String icon) {
         super(id, xPosition, yPosition, s);
+
         this.textColor = textColor;
         this.bgColor = bgColor;
-        height = 12;
-        width = 0;
+        this.height = 12;
+        this.width = 0;
+        if (icon != null) {
+            this.icon = TextureRegistry.getTexture(icon);
+        } else {
+            this.icon = null;
+        }
+    }
+
+    public ClickableLabelElement(int id, int xPosition, int yPosition, String s, int textColor, int bgColor) {
+        this(id, xPosition, yPosition, s, textColor, bgColor, null);
+    }
+
+    public ClickableLabelElement(int id, int xPosition, int yPosition, String s, int textColor, @Nullable String icon) {
+        this(id, xPosition, yPosition, s, textColor, 0x7F000000, icon);
     }
 
-    public ClickableLabelElement(int id, int xPosition, int yPosition, String s, int textColor)
-    {
-        this(id, xPosition, yPosition, s, textColor, 0x7F000000);
+    public ClickableLabelElement(int id, int xPosition, int yPosition, String s, int textColor) {
+        this(id, xPosition, yPosition, s, textColor, null);
     }
 
     public int getTextColor() {
@@ -50,10 +67,8 @@ public class ClickableLabelElement extends ButtonElement
     }
 
     @Override
-    public void drawButton(Minecraft mc, int mouseX, int mouseY)
-    {
-        if(!visible)
-        {
+    public void drawButton(Minecraft mc, int mouseX, int mouseY) {
+        if (!visible) {
             return;
         }
 
@@ -62,15 +77,54 @@ public class ClickableLabelElement extends ButtonElement
             mc.currentScreen.setDesiredCursor(CursorShape.HAND);
         }
 
-        GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
-        width = MathHelper.ceil(mc.font.stringWidthDouble(displayString)) + 4;
+        if (this.icon != null) {
+            if (hovered && enabled) {
+                this.width = 2 + this.icon.width + 4 + MathHelper.ceil(mc.font.stringWidthDouble(displayString)) + 2;
+            } else {
+                this.width = 2 + this.icon.width + 2;
+            }
+        } else {
+            width = MathHelper.ceil(mc.font.stringWidthDouble(displayString)) + 4;
+        }
+
         drawRect(xPosition, yPosition, xPosition + width, yPosition + height, enabled ? (hovered ? 0x7F8080C0 : bgColor) : 0x00000000);
-        if (enabled && (shadow || hovered)) {
-            drawStringShadow(mc.font, displayString, xPosition + 2, yPosition + 2, hovered ? 0xFFFFA0 : textColor);
+
+        if (this.icon != null) {
+            if (hovered) {
+                if (shadow) {
+                    glColor1i(makeShadow(0xFFFFFFA0));
+                    drawGuiIcon(xPosition + 3, yPosition + 2, icon.width, icon.height, icon);
+                    glColor1i(0xFFFFFFA0);
+                    drawGuiIcon(xPosition + 2, yPosition + 1, icon.width, icon.height, icon);
+                    drawStringShadow(mc.font, displayString, xPosition + 4 + icon.width + 2, yPosition + 2, 0xFFFFFFA0);
+                } else {
+                    glColor1i(0xFFFFFFA0);
+                    drawGuiIcon(xPosition + 2, yPosition + 1, icon.width, icon.height, icon);
+                    drawStringNoShadow(mc.font, displayString, xPosition + 4 + icon.width + 2, yPosition + 2, 0xFFFFFFA0);
+                }
+            } else {
+                if (shadow) {
+                    glColor1i(makeShadow(textColor));
+                    drawGuiIcon(xPosition + 3, yPosition + 2, icon.width, icon.height, icon);
+                    glColor1i(textColor);
+                    drawGuiIcon(xPosition + 2, yPosition + 1, icon.width, icon.height, icon);
+                } else {
+                    glColor1i(textColor);
+                    drawGuiIcon(xPosition + 2, yPosition + 1, icon.width, icon.height, icon);
+                }
+            }
         } else {
-            drawStringNoShadow(mc.font, displayString, xPosition + 2, yPosition + 2, 0x7F7F7F);
+            if (enabled && (shadow || hovered)) {
+                drawStringShadow(mc.font, displayString, xPosition + 4, yPosition + 2, hovered ? 0xFFFFFFA0 : textColor);
+            } else {
+                drawStringNoShadow(mc.font, displayString, xPosition + 4, yPosition + 2, 0xFF7F7F7F);
+            }
         }
+
         mouseDragged(mc, mouseX, mouseY);
     }
 
+    private static int makeShadow(int argb) {
+        return ((argb & 0xFCFCFC) >> 2) | (argb & 0xFF000000);
+    }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenCreateWorld.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenCreateWorld.java
index 463be32ef..36795cfd2 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenCreateWorld.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenCreateWorld.java
@@ -8,21 +8,17 @@ import net.minecraft.core.item.Items;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.net.command.TextFormatting;
 import net.minecraft.core.player.gamemode.Gamemode;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
-import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.Dimension;
-import net.minecraft.core.world.save.ISaveFormat;
+import net.minecraft.core.world.settings.WorldConfiguration;
 import net.minecraft.core.world.type.WorldTypeGroups;
 import org.lwjgl.input.Keyboard;
 
 import java.util.List;
-import java.util.Random;
 
 public class ScreenCreateWorld extends Screen {
     private int overrideSpawnX;
     private int overrideSpawnY;
     private int overrideSpawnZ;
-    private Screen textField;
     private TextFieldElement textboxWorldName;
     private TextFieldElement textboxSeed;
     private ButtonElement createWorldButton;
@@ -31,15 +27,11 @@ public class ScreenCreateWorld extends Screen {
     private ButtonElement worldTypeButton;
     private ButtonElement buttonRandomWorldName;
     private ButtonElement buttonRandomSeed;
-    private String folderName;
     private boolean createClicked;
-    private Gamemode selectedGamemode = Gamemode.survival;
-    private boolean cheatsEnabled = false;
-    private WorldTypeGroups.Group worldTypeGroup = WorldTypeGroups.DEFAULT;
-    private long seed = (new Random()).nextLong();
+    private final WorldConfiguration worldConfiguration = new WorldConfiguration();
 
-    public ScreenCreateWorld(Screen guiscreen) {
-        textField = guiscreen;
+    public ScreenCreateWorld(Screen parent) {
+        super(parent);
     }
 
     @Override
@@ -54,44 +46,21 @@ public class ScreenCreateWorld extends Screen {
         Keyboard.enableRepeatEvents(true);
         buttons.clear();
         buttons.add(createWorldButton = new ButtonElement(0, width / 2 - 100, height / 4 + 104 + 12,
-            i18n.translateKey("gui.create_world.button.create_new_world")));
+                i18n.translateKey("gui.create_world.button.create_new_world")));
         buttons.add(new ButtonElement(1, width / 2 - 100, height / 4 + 128 + 12,
-            i18n.translateKey("gui.create_world.button.cancel")));
+                i18n.translateKey("gui.create_world.button.cancel")));
         textboxWorldName = new TextFieldElement(this, fontRenderer, width / 2 - 100, 60, 200 - 24, 20,
-            "", i18n.translateKey("gui.create_world.label.new_world"));
+                "", i18n.translateKey("gui.create_world.label.new_world"));
         textboxWorldName.setMaxStringLength(32);
-        textboxSeed = new TextFieldElement(this, fontRenderer, width / 2 - 100, 100, 200 - 24, 20, "", Long.toString(seed));
+        textboxSeed = new TextFieldElement(this, fontRenderer, width / 2 - 100, 100, 200 - 24, 20, "", Long.toString(this.worldConfiguration.getNumericSeed()));
         buttons.add(gamemodeButton = new ButtonElement(2, width / 2 - 100, 100 + 20 + 4, 98, 20, ""));
         buttons.add(cheatsButton = new ButtonElement(3, width / 2 - 100 + 102, 100 + 20 + 4, 98, 20, ""));
         buttons.add(worldTypeButton = new ButtonElement(4, width / 2 - 100, 100 + 20 + 20 + 4 + 4, ""));
         buttons.add(buttonRandomWorldName = new ButtonElement(5, width / 2 + 100 - 20, 60, 20, 20, "")
-            .setTextures("minecraft:gui/misc/button_random", "minecraft:gui/misc/button_random_highlighted", "minecraft:gui/misc/button_random"));
+                .setTextures("minecraft:gui/misc/button_random", "minecraft:gui/misc/button_random_highlighted", "minecraft:gui/misc/button_random"));
         buttons.add(buttonRandomSeed = new ButtonElement(6, width / 2 + 100 - 20, 100, 20, 20, "")
-            .setTextures("minecraft:gui/misc/button_random", "minecraft:gui/misc/button_random_highlighted", "minecraft:gui/misc/button_random"));
-        updateButtons();
-        updateFolderName();
-    }
-
-    private void updateFolderName() {
-        folderName = textboxWorldName.getText().trim();
-        char[] ac = ChatAllowedCharacters.ALLOWED_CHARACTERS_ARRAY;
-        for (char c : ac) {
-            folderName = folderName.replace(c, '_');
-        }
-
-        if (MathHelper.stringNullOrLengthZero(folderName)) {
-            folderName = "New World";
-        }
-        folderName = generateUnusedFolderName(mc.getSaveFormat(), folderName);
-    }
-
-    public static String generateUnusedFolderName(ISaveFormat isaveformat, String s) {
-        int count = 1;
-        String newString = s;
-        for (; isaveformat.getLevelData(newString) != null; count++) {
-            newString = s + " (" + count + ")";
-        }
-        return newString;
+                .setTextures("minecraft:gui/misc/button_random", "minecraft:gui/misc/button_random_highlighted", "minecraft:gui/misc/button_random"));
+        syncButtons();
     }
 
     @Override
@@ -105,48 +74,25 @@ public class ScreenCreateWorld extends Screen {
             return;
         }
         if (button.id == 1) {
-            mc.displayScreen(textField);
+            mc.displayScreen(parentScreen);
         } else if (button.id == 0) {
             mc.displayScreen(null);
             if (createClicked) {
                 return;
             }
             createClicked = true;
-            long seed = this.seed;
-            String seedText = textboxSeed.getText();
-            if (!MathHelper.stringNullOrLengthZero(seedText)) {
-                try {
-                    long parsedSeed = Long.parseLong(seedText);
-                    if (parsedSeed != 0L) {
-                        seed = parsedSeed;
-                    }
-                } catch (NumberFormatException e) {
-                    seed = seedText.hashCode();
-                }
-            }
-            mc.playerController = new PlayerControllerSP(mc);
-            String worldName = textboxWorldName.getText();
-            if (worldName.isEmpty()) {
-                worldName = I18n.getInstance().translateKey("gui.create_world.label.new_world");
-            }
-            mc.startWorld(folderName, worldName, seed, worldTypeGroup);
-            mc.thePlayer.setGamemode(selectedGamemode);
-            if (selectedGamemode == Gamemode.adventure) {
-                mc.thePlayer.inventory.insertItem(Items.TOOL_AXE_WOOD.getDefaultStack(), true);
-            }
-            if (mc.worldPresetName != null) mc.thePlayer.setPos(overrideSpawnX, overrideSpawnY, overrideSpawnZ);
-            mc.currentWorld.getLevelData().setCheatsEnabled(cheatsEnabled);
+
+            this.mc.createAndStartWorld(this.worldConfiguration);
+
             mc.displayScreen(null);
         } else if (button == gamemodeButton) {
-            I18n i18n = I18n.getInstance();
             List<Gamemode> gamemodeList = Gamemode.getPublicGamemodes();
-            selectedGamemode = gamemodeList.get((gamemodeList.indexOf(selectedGamemode) + 1) % gamemodeList.size());
-            cheatsEnabled = selectedGamemode == Gamemode.creative;
-            updateButtons();
+            this.worldConfiguration.setGamemode(gamemodeList.get((gamemodeList.indexOf(this.worldConfiguration.getGamemode()) + 1) % gamemodeList.size()));
+            this.worldConfiguration.setCheatsEnabled(this.worldConfiguration.getGamemode() == Gamemode.creative);
+            syncButtons();
         } else if (button == cheatsButton) {
-            I18n i18n = I18n.getInstance();
-            cheatsEnabled = !cheatsEnabled;
-            updateButtons();
+            this.worldConfiguration.setCheatsEnabled(!this.worldConfiguration.getCheatsEnabled());
+            syncButtons();
         } else if (button == worldTypeButton) {
             I18n i18n = I18n.getInstance();
             String[] keys = new String[WorldTypeGroups.GROUPS.size()];
@@ -154,37 +100,45 @@ public class ScreenCreateWorld extends Screen {
             for (int i = 0; i < keys.length; i++) {
                 keys[i] = i18n.translateKey(WorldTypeGroups.GROUPS.get(i).get(Dimension.OVERWORLD).getLanguageKey() + ".name");
                 descs[i] = i18n.translateKey(WorldTypeGroups.GROUPS.get(i).get(Dimension.OVERWORLD).getLanguageKey() + ".desc") + "\n" +
-                    TextFormatting.GRAY + Registries.WORLD_TYPES.getKey(WorldTypeGroups.GROUPS.get(i).get(Dimension.OVERWORLD));
+                        TextFormatting.GRAY + Registries.WORLD_TYPES.getKey(WorldTypeGroups.GROUPS.get(i).get(Dimension.OVERWORLD));
             }
 
             PopupScreen popup = new PopupBuilder(this, 150)
-                .closeOnEsc(0)
-                .closeOnClickOut(0)
-                .withLabel("gui.create_world.label.select_world_type")
-                .withList("worldTypeList", 140, keys, descs, WorldTypeGroups.GROUPS.indexOf(worldTypeGroup), true)
-                .withOnCloseListener((statusCode, results) -> {
-                    if (statusCode == 1) {
-                        worldTypeGroup = WorldTypeGroups.GROUPS.get((Integer) results.get("worldTypeList"));
-                        updateButtons();
-                    }
-                })
-                .build();
+                    .closeOnEsc(0)
+                    .closeOnClickOut(0)
+                    .withLabel("gui.create_world.label.select_world_type")
+                    .withList("worldTypeList", 140, keys, descs, WorldTypeGroups.GROUPS.indexOf(this.worldConfiguration.getWorldTypeGroup()), true)
+                    .withOnCloseListener((statusCode, results) -> {
+                        if (statusCode == 1) {
+                            this.worldConfiguration.setWorldTypeGroup(WorldTypeGroups.GROUPS.get((Integer) results.get("worldTypeList")));
+                            syncButtons();
+                        }
+                    })
+                    .build();
             mc.displayScreen(popup);
         } else if (button == buttonRandomWorldName) {
             textboxWorldName.setText(I18n.getInstance().getCurrentLanguage().getRandomWorldName());
-            updateFolderName();
+            syncConfig();
         } else if (button == buttonRandomSeed) {
-            this.seed = (new Random()).nextLong();
-            textboxSeed.setPlaceholder(Long.toString(seed));
+            this.worldConfiguration.regenNumericSeed();
+            textboxSeed.setPlaceholder(Long.toString(this.worldConfiguration.getNumericSeed()));
             textboxSeed.setText("");
+            syncConfig();
         }
     }
 
-    private void updateButtons() {
+    private void syncButtons() {
         I18n i18n = I18n.getInstance();
-        gamemodeButton.displayString = i18n.translateKeyAndFormat("gui.create_world.button.gamemode", i18n.translateKey(selectedGamemode.getLanguageKey() + ".name"));
-        cheatsButton.displayString = i18n.translateKeyAndFormat("gui.create_world.button.cheats", i18n.translateKey(cheatsEnabled ? "gui.create_world.button.cheats.on" : "gui.create_world.button.cheats.off"));
-        worldTypeButton.displayString = i18n.translateKeyAndFormat("gui.create_world.button.world_type", i18n.translateKey(worldTypeGroup.get(Dimension.OVERWORLD).getLanguageKey() + ".name"));
+        this.textboxWorldName.setText(this.worldConfiguration.getWorldName());
+        this.textboxSeed.setText(this.worldConfiguration.getStringSeed());
+        gamemodeButton.displayString = i18n.translateKeyAndFormat("gui.create_world.button.gamemode", i18n.translateKey(this.worldConfiguration.getGamemode().getLanguageKey() + ".name"));
+        cheatsButton.displayString = i18n.translateKeyAndFormat("gui.create_world.button.cheats", i18n.translateKey(this.worldConfiguration.getCheatsEnabled() ? "gui.create_world.button.cheats.on" : "gui.create_world.button.cheats.off"));
+        worldTypeButton.displayString = i18n.translateKeyAndFormat("gui.create_world.button.world_type", i18n.translateKey(this.worldConfiguration.getWorldTypeGroup().get(Dimension.OVERWORLD).getLanguageKey() + ".name"));
+    }
+
+    private void syncConfig() {
+        this.worldConfiguration.setWorldName(this.textboxWorldName.getText());
+        this.worldConfiguration.setStringSeed(this.textboxSeed.getText());
     }
 
     @Override
@@ -192,13 +146,14 @@ public class ScreenCreateWorld extends Screen {
         if (eventKey != Keyboard.KEY_BACK) super.keyPressed(eventCharacter, eventKey, mx, my);
         if (textboxWorldName.isFocused) {
             textboxWorldName.textboxKeyTyped(eventCharacter, eventKey);
+            syncConfig();
         } else if (textboxSeed.isFocused) {
             textboxSeed.textboxKeyTyped(eventCharacter, eventKey);
+            syncConfig();
         }
         if (eventCharacter == '\r') {
             buttonClicked(buttons.get(0));
         }
-        updateFolderName();
     }
 
     @Override
@@ -210,11 +165,11 @@ public class ScreenCreateWorld extends Screen {
 
     @Override
     public void render(int mx, int my, float partialTick) {
-        I18n stringtranslate = I18n.getInstance();
+        I18n i18n = I18n.getInstance();
         renderBackground();
-        drawStringCenteredShadow(fontRenderer, stringtranslate.translateKey("gui.create_world.label.title"), width / 2, 20, 0xffffff);
-        drawStringShadow(fontRenderer, stringtranslate.translateKey("gui.create_world.label.world_name"), width / 2 - 100, 47, 0xa0a0a0);
-        drawStringShadow(fontRenderer, stringtranslate.translateKey("gui.create_world.label.seed"), width / 2 - 100, 88, 0xa0a0a0);
+        drawStringCenteredShadow(fontRenderer, i18n.translateKey("gui.create_world.label.title"), width / 2, 20, 0xffffff);
+        drawStringShadow(fontRenderer, i18n.translateKey("gui.create_world.label.world_name"), width / 2 - 100, 47, 0xa0a0a0);
+        drawStringShadow(fontRenderer, i18n.translateKey("gui.create_world.label.seed"), width / 2 - 100, 88, 0xa0a0a0);
         textboxWorldName.drawTextBox();
         textboxWorldName.updateCursor(mc, mx, my);
         textboxSeed.drawTextBox();
@@ -237,10 +192,9 @@ public class ScreenCreateWorld extends Screen {
         overrideSpawnX = spawnX;
         overrideSpawnY = spawnY;
         overrideSpawnZ = spawnZ;
-        textboxWorldName.setText(name);
-        textboxSeed.setText(seed);
-        updateFolderName();
-        selectedGamemode = Gamemode.creative;
+        this.worldConfiguration.setWorldName(name);
+        this.worldConfiguration.setStringSeed(seed);
+        this.worldConfiguration.setGamemode(Gamemode.creative);
         buttonClicked(createWorldButton);
     }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenHudDesigner.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenHudDesigner.java
index 511d5a750..52a06c8ca 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenHudDesigner.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenHudDesigner.java
@@ -70,8 +70,8 @@ public class ScreenHudDesigner
                 if (componentUnderMouse instanceof HudComponentMovable) {
                     Layout currentLayout = componentUnderMouse.getLayout();
                     ComponentAnchor anchor = getClosestAnchor(
-                        currentLayout.getComponentX(mc, componentUnderMouse, width),
-                        currentLayout.getComponentY(mc, componentUnderMouse, height - BOTTOM_SPACE),
+                        currentLayout.getComponentX(componentUnderMouse, width),
+                        currentLayout.getComponentY(componentUnderMouse, height - BOTTOM_SPACE),
                         componentUnderMouse, mx, my);
                     Layout layout = new LayoutAbsolute(0, 0, anchor);
                     ((HudComponentMovable) componentUnderMouse).setLayout(layout);
@@ -92,8 +92,8 @@ public class ScreenHudDesigner
     private void updateHeldComponent(int mouseX, int mouseY) {
         if (heldComponent == null) return;
 
-        int xSize = heldComponent.getXSize(mc);
-        int ySize = heldComponent.getYSize(mc);
+        int xSize = heldComponent.getXSize();
+        int ySize = heldComponent.getYSize();
         int xOffset = heldComponent.getAnchorX(heldAnchor);
         int yOffset = heldComponent.getAnchorY(heldAnchor);
         int newX = MathHelper.clamp(mouseX, xOffset, width - (xSize - xOffset));
@@ -111,7 +111,7 @@ public class ScreenHudDesigner
         boolean didSnap = false;
         for (HudComponent component : HudComponents.INSTANCE.getComponents()) {
             if (component == heldComponent) continue;
-            if (!component.isEnabled(mc)) continue;
+            if (!component.isEnabled()) continue;
             Layout otherLayout = component.getLayout();
             boolean isNested = false;
             Layout nestedLayout = otherLayout;
@@ -132,8 +132,8 @@ public class ScreenHudDesigner
             }
             if (isNested) continue;
 
-            int x = otherLayout.getComponentX(mc, component, width);
-            int y = otherLayout.getComponentY(mc, component, height - BOTTOM_SPACE);
+            int x = otherLayout.getComponentX(component, width);
+            int y = otherLayout.getComponentY(component, height - BOTTOM_SPACE);
 
             // Get mouse distance
             int mouseDistance = (int) Math.sqrt(Math.pow(mouseX - newX, 2) + Math.pow(mouseY - newY, 2));
@@ -293,15 +293,15 @@ public class ScreenHudDesigner
         }
         for (HudComponent component : HudComponents.INSTANCE.getComponents()) {
             if (component == heldComponent) continue;
-            if (!component.isEnabled(mc)) continue;
+            if (!component.isEnabled()) continue;
             Layout layout = component.getLayout();
-            component.renderPreview(mc, this, layout, width, height - BOTTOM_SPACE);
+            component.renderPreview(this, layout, width, height - BOTTOM_SPACE);
             if (heldComponent == null) {
                 if (!componentsUnderMouse.isEmpty() && component == lastComponent) {
-                    int x = layout.getComponentX(mc, component, width);
-                    int y = layout.getComponentY(mc, component, height - BOTTOM_SPACE);
-                    int xSize = component.getXSize(mc);
-                    int ySize = component.getYSize(mc);
+                    int x = layout.getComponentX(component, width);
+                    int y = layout.getComponentY(component, height - BOTTOM_SPACE);
+                    int xSize = component.getXSize();
+                    int ySize = component.getYSize();
                     drawBox(x, y, x + xSize, y + ySize, 0xff00ff00, 1);
                     ComponentAnchor anchor = getClosestAnchor(x, y, component, mouseX, mouseY);
                     int xAnchor = component.getAnchorX(anchor);
@@ -313,11 +313,11 @@ public class ScreenHudDesigner
         }
         if (heldComponent != null) {
             Layout layout = heldComponent.getLayout();
-            heldComponent.renderPreview(mc, this, layout, width, height - BOTTOM_SPACE);
-            int x = layout.getComponentX(mc, heldComponent, width);
-            int y = layout.getComponentY(mc, heldComponent, height - BOTTOM_SPACE);
-            int xSize = heldComponent.getXSize(mc);
-            int ySize = heldComponent.getYSize(mc);
+            heldComponent.renderPreview(this, layout, width, height - BOTTOM_SPACE);
+            int x = layout.getComponentX(heldComponent, width);
+            int y = layout.getComponentY(heldComponent, height - BOTTOM_SPACE);
+            int xSize = heldComponent.getXSize();
+            int ySize = heldComponent.getYSize();
             int xAnchor = heldComponent.getAnchorX(heldAnchor);
             int yAnchor = heldComponent.getAnchorY(heldAnchor);
             drawBox(x, y, x + xSize, y + ySize, 0xffff0000, 1);
@@ -325,8 +325,8 @@ public class ScreenHudDesigner
         }
         if (snappedComponent != null && snappedAnchor != null) {
             Layout layout = snappedComponent.getLayout();
-            int x = layout.getComponentX(mc, snappedComponent, width);
-            int y = layout.getComponentY(mc, snappedComponent, height - BOTTOM_SPACE);
+            int x = layout.getComponentX(snappedComponent, width);
+            int y = layout.getComponentY(snappedComponent, height - BOTTOM_SPACE);
             int xAnchor = snappedComponent.getAnchorX(snappedAnchor);
             int yAnchor = snappedComponent.getAnchorY(snappedAnchor);
             drawAnchor(x, y, xAnchor, yAnchor, snappedAnchor);
@@ -379,12 +379,12 @@ public class ScreenHudDesigner
     private void updateComponentsUnderMouse(int mouseX, int mouseY) {
         componentsUnderMouse.clear();
         for (HudComponent component : HudComponents.INSTANCE.getComponents()) {
-            if (!component.isEnabled(mc)) continue;
+            if (!component.isEnabled()) continue;
             Layout layout = component.getLayout();
-            int cx = layout.getComponentX(mc, component, width);
-            int cy = layout.getComponentY(mc, component, height - BOTTOM_SPACE);
-            int cXSize = component.getXSize(mc);
-            int cYSize = component.getYSize(mc);
+            int cx = layout.getComponentX(component, width);
+            int cy = layout.getComponentY(component, height - BOTTOM_SPACE);
+            int cXSize = component.getXSize();
+            int cYSize = component.getYSize();
             if (mouseX >= cx && mouseX < cx + cXSize && mouseY >= cy && mouseY < cy + cYSize) {
                 componentsUnderMouse.add(component);
             }
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 a3936a5b1..c67571377 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
@@ -67,8 +67,7 @@ public class ScreenMainMenu extends Screen
     private int codeProgress = 0;
     private boolean showBuildTime = false;
     private String buildTimeString = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(Version.BUILD_TIME));
-    private int snowfallTimer = 0;
-    
+
     static {
     	loadLogo();
     }
@@ -178,10 +177,10 @@ public class ScreenMainMenu extends Screen
             buttons.add(new ButtonElement(4, width / 2 + 2, i + 72 + 12, 98, 20, i18n.translateKey("gui.main_menu.button.quit_game")));
         }
         int d = 1; // Move away from the edge of the screen to prevent unintentional activation because the cursor defaults to 0, 0
-        buttons.add(new ClickableLabelElement(5, d, height - 48 - d, i18n.translateKey("gui.main_menu.button.credits"), 0x9F9F9F));
-        buttons.add(new ClickableLabelElement(6, d, height - 36 - d, i18n.translateKey("gui.main_menu.button.website"), 0x5AA938));
-        buttons.add(new ClickableLabelElement(7, d, height - 24 - d, i18n.translateKey("gui.main_menu.button.discord"), 0x5865F2));
-        buttons.add(new ClickableLabelElement(8, d, height - 12 - d, i18n.translateKey("gui.main_menu.button.youtube"), 0xFF0000));
+        buttons.add(new ClickableLabelElement(5, d, height - 48 - d, i18n.translateKey("gui.main_menu.button.credits"), 0xFF9F9F9F, "minecraft:gui/screen/main_menu/icon_credits"));
+        buttons.add(new ClickableLabelElement(6, d, height - 36 - d, i18n.translateKey("gui.main_menu.button.website"), 0xFF5AA938, "minecraft:gui/screen/main_menu/icon_website"));
+        buttons.add(new ClickableLabelElement(7, d, height - 24 - d, i18n.translateKey("gui.main_menu.button.discord"), 0xFF5865F2, "minecraft:gui/screen/main_menu/icon_discord"));
+        buttons.add(new ClickableLabelElement(8, d, height - 12 - d, i18n.translateKey("gui.main_menu.button.youtube"), 0xFFFF0000, "minecraft:gui/screen/main_menu/icon_youtube"));
         buttons.add(new ButtonElement(9, width / 2 + 104, i + 72 + 12, 20, 20, "")
             .setTextures("minecraft:gui/misc/button_language", "minecraft:gui/misc/button_language_highlighted", "minecraft:gui/misc/button_language"));
         ButtonElement debugButton = new ButtonElement(10, width - 8, height - 8, 8, 8, "");
@@ -460,11 +459,6 @@ public class ScreenMainMenu extends Screen
         GL11.glEnable(GL11.GL_CULL_FACE);
     }
 
-
-    static Random getRandom() {
-        return rand;
-    }
-    
     public static void loadLogo() {
     	try {
     		InputStream inputStream = ScreenMainMenu.class.getResourceAsStream("/assets/minecraft/textures/gui/title/retrologo.png");
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenMobPicker.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenMobPicker.java
index 21b47a2e2..74d3cf598 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenMobPicker.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenMobPicker.java
@@ -34,10 +34,10 @@ public class ScreenMobPicker
     @Override
     public void init() {
         List<NamespaceID> monsters = new ArrayList<>();
-        for (Class<? extends Entity> clazz : EntityDispatcher.classToIdMap.keySet()) {
-            if (clazz == MobFireflyCluster.class) continue;
-            if (Mob.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
-                monsters.add(EntityDispatcher.classToIdMap.get(clazz));
+        for (EntityDispatcher.EntityDispatcherEntry<?> entry : EntityDispatcher.getInstance().entries) {
+            if (entry.entityClass == MobFireflyCluster.class) continue;
+            if (Mob.class.isAssignableFrom(entry.entityClass) && !Modifier.isAbstract(entry.entityClass.getModifiers())) {
+                monsters.add(entry.namespaceID);
             }
         }
         Collections.sort(monsters);
@@ -68,7 +68,7 @@ public class ScreenMobPicker
                         }
                         spawner.setMobId(mobId);
                     } else {
-                        mc.getSendQueue().addToSendQueue(new PacketSetMobSpawner(x, y, z, EntityDispatcher.classForId(mobId)));
+                        mc.getSendQueue().addToSendQueue(new PacketSetMobSpawner(x, y, z, EntityDispatcher.getInstance().entryForId(mobId)));
                     }
                 }
                 shouldClose = true;
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 e4b9000c0..1a55fed5e 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
@@ -1,5 +1,6 @@
 package net.minecraft.client.gui;
 
+import com.mojang.nbt.tags.CompoundTag;
 import net.minecraft.client.gui.popup.PopupScreen;
 import net.minecraft.client.gui.popup.PopupBuilder;
 import net.minecraft.client.render.tessellator.Tessellator;
@@ -11,16 +12,21 @@ import net.minecraft.core.world.save.ISaveFormat;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.client.player.controller.PlayerControllerSP;
 import net.minecraft.core.world.save.LevelData;
+import net.minecraft.core.world.save.LevelStorage;
 import net.minecraft.core.world.save.SaveFile;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.world.type.WorldType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.input.Keyboard;
 
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class ScreenSelectWorld extends Screen
 {
@@ -29,6 +35,7 @@ public class ScreenSelectWorld extends Screen
     private boolean selected;
     private int selectedWorld;
     private List<SaveFile> saveList;
+    private Map<SaveFile, @Nullable CompoundTag> userTags;
     private WorldSlot worldSlotContainer;
     private String stringWorld;
     private String stringConversion;
@@ -81,8 +88,18 @@ public class ScreenSelectWorld extends Screen
 
     private void loadSaves()
     {
-        ISaveFormat isaveformat = mc.getSaveFormat();
-        saveList = isaveformat.getSaveFileList();
+        ISaveFormat saveFormat = mc.getSaveFormat();
+        saveList = saveFormat.getSaveFileList();
+        userTags = new HashMap<>();
+        for (SaveFile file : saveList) {
+            LevelStorage levelStorage = saveFormat.getSaveHandler(file.getFileName(), false);
+            LevelData data = levelStorage.getLevelData();
+            if (data != null && data.getLastPlayerUUID() != null) {
+                userTags.put(file, levelStorage.getPlayerData(null, data.getLastPlayerUUID()));
+            } else {
+                userTags.put(file, levelStorage.getPlayerData(mc.session.username, mc.session.uuid));
+            }
+        }
         Collections.sort(saveList);
         if (selectedWorld >= saveList.size())
         {
@@ -98,7 +115,7 @@ public class ScreenSelectWorld extends Screen
     protected String getSaveName(int i)
     {
         String s = saveList.get(i).getDisplayName();
-        if(s == null || MathHelper.stringNullOrLengthZero(s))
+        if(MathHelper.stringNullOrLengthZero(s))
         {
             I18n i18n = I18n.getInstance();
             s = i18n.translateKeyAndFormat(stringWorld, i + 1);
@@ -166,25 +183,19 @@ public class ScreenSelectWorld extends Screen
     @Override
     protected void buttonClicked(ButtonElement button)
     {
-        if(!button.enabled)
-        {
+        if(!button.enabled) {
             return;
         }
-        if(button.id == 2)
-        {
+        if(button.id == 2) {
             tryDeleteWorld(false);
         }
-        else if(button.id == 1)
-        {
+        else if(button.id == 1) {
             selectWorld(selectedWorld);
         }
-        else if(button.id == 3)
-        {
+        else if(button.id == 3) {
             mc.displayScreen(new ScreenCreateWorld(this));
         }
-        else if(button.id == 6)
-        {
-            I18n i18n = I18n.getInstance();
+        else if(button.id == 6) {
             PopupScreen popup = new PopupBuilder(this, 140)
                     .closeOnEsc(0)
                     .closeOnEnter(1)
@@ -231,7 +242,7 @@ public class ScreenSelectWorld extends Screen
         {
             s = "World" + i;
         }
-        mc.startWorld(s, getSaveName(i), 0L);
+        mc.startWorld(s);
         mc.displayScreen(null);
     }
 
@@ -262,8 +273,10 @@ public class ScreenSelectWorld extends Screen
 
     class WorldSlot extends ScrolledSelectionList
     {
-        public WorldSlot()
-        {
+        private final @NotNull StringBuilder nameBuilder = new StringBuilder();
+        private final @NotNull StringBuilder descBuilder1 = new StringBuilder();
+        private final @NotNull StringBuilder descBuilder2 = new StringBuilder();
+        public WorldSlot() {
             super(mc, width, height, 32, height - 64, 36);
         }
 
@@ -307,24 +320,39 @@ public class ScreenSelectWorld extends Screen
         }
 
         @Override
-        protected void renderItem(int index, int x, int y, int height, Tessellator tessellator)
+        protected void renderItem(int index, int x, int y, int height, @NotNull Tessellator t)
         {
+            nameBuilder.setLength(0);
+            descBuilder1.setLength(0);
+            descBuilder2.setLength(0);
+
             I18n i18n = I18n.getInstance();
             SaveFile saveFile = saveList.get(index);
-            String s = saveFile.getDisplayName();
-            if (saveFile.isCorrupted()) s = TextFormatting.RED + s;
-            if(s == null || MathHelper.stringNullOrLengthZero(s))
-            {
-                s = i18n.translateKeyAndFormat(stringWorld, index + 1);
+
+            CompoundTag userTag = userTags.get(saveFile);
+            Gamemode gamemode;
+            if (userTag != null) {
+                gamemode = Gamemode.gamemodesList[userTag.getInteger("Gamemode")];
+            } else {
+                gamemode = Gamemode.survival;
             }
-            String s1 = saveFile.getFileName();
-            s1 = s1 + " (" + dateFormatter.format(new Date(saveFile.getLastTimePlayed()));
-            long l1 = saveFile.getSizeOnDisk();
-            s1 = s1 + ", " + (float) (((l1 / 1024L) * 100L) / 1024L) / 100F + " MB)";
-            String s2 = "";
+
+            final String saveName = saveFile.getDisplayName();
+            if (saveFile.isCorrupted()) nameBuilder.append(TextFormatting.RED);
+            if(MathHelper.stringNullOrLengthZero(saveName)) {
+                nameBuilder.append(i18n.translateKeyAndFormat(stringWorld, index + 1));
+            } else {
+                nameBuilder.append(saveName);
+            }
+
+            long sizeBytes = saveFile.getSizeOnDisk();
+            descBuilder1.append(saveFile.getFileName());
+            descBuilder1.append(" (").append(dateFormatter.format(new Date(saveFile.getLastTimePlayed())));
+            descBuilder1.append(", ").append((float) (((sizeBytes / 1024L) * 100L) / 1024L) / 100F).append(" MB)");
+
             if(saveFile.getNeedsConversion())
             {
-                s2 = stringConversion + " " + s2;
+                descBuilder2.append(stringConversion).append(" ");
             }
             else
             {
@@ -332,22 +360,22 @@ public class ScreenSelectWorld extends Screen
                 LevelData ld = saveFile.getLevelData();
                 DimensionData dd = saveFile.getDimensionData().get(0);
                 if (dd == null){
-                    s2 += TextFormatting.RED + st.translateKey("gui.select_world.label.dimension_corrupted");
+                    descBuilder2.append(TextFormatting.RED).append(st.translateKey("gui.select_world.label.dimension_corrupted"));
                 } else {
-                    s2 += st.translateKey(Gamemode.gamemodesList[ld.getGamemode()].getLanguageKey() + ".name");
-                    if (ld.getCheatsEnabled()) s2 += " | " + st.translateKey("gui.select_world.label.cheats");
+                    descBuilder2.append(st.translateKey(gamemode.getLanguageKey() + ".name"));
+                    if (ld != null && ld.getCheatsEnabled()) descBuilder2.append(" | ").append(st.translateKey("gui.select_world.label.cheats"));
                     WorldType worldType = dd.getWorldType();
                     if (worldType != null) {
-                        s2 += " | " + st.translateKey(dd.getWorldType().getLanguageKey() + ".name");
+                        descBuilder2.append(" | ").append(st.translateKey(dd.getWorldType().getLanguageKey() + ".name"));
                     } else {
-                        s2 += " | " + TextFormatting.RED + st.translateKey("gui.select_world.label.unknown");
+                        descBuilder2.append(" | ").append(TextFormatting.RED).append(st.translateKey("gui.select_world.label.unknown"));
                     }
                 }
             }
 
-            drawStringShadow(fontRenderer, s, x + 2, y + 1, 0xffffff);
-            drawStringShadow(fontRenderer, s1, x + 2, y + 12, 0x808080);
-            drawStringShadow(fontRenderer, s2, x + 2, y + 12 + 10, 0x808080);
+            drawStringShadow(fontRenderer, nameBuilder, x + 2, y + 1, 0xffffff);
+            drawStringShadow(fontRenderer, descBuilder1, x + 2, y + 12, 0x808080);
+            drawStringShadow(fontRenderer, descBuilder2, x + 2, y + 12 + 10, 0x808080);
         }
     }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenWandMobPicker.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenWandMobPicker.java
index d79342dfc..a39fa5a99 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenWandMobPicker.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenWandMobPicker.java
@@ -29,9 +29,9 @@ public class ScreenWandMobPicker extends Screen {
     @Override
     public void init() {
         List<NamespaceID> monsters = new ArrayList<>();
-        for (Class<? extends Entity> clazz : EntityDispatcher.classToIdMap.keySet()) {
-            if (Mob.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
-                monsters.add(EntityDispatcher.classToIdMap.get(clazz));
+        for (EntityDispatcher.EntityDispatcherEntry<?> entry : EntityDispatcher.getInstance().entries) {
+            if (Mob.class.isAssignableFrom(entry.entityClass) && !Modifier.isAbstract(entry.entityClass.getModifiers())) {
+                monsters.add(entry.namespaceID);
             }
         }
         Collections.sort(monsters);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/createworld/ButtonComponent.java b/game/client/src/main/java/net/minecraft/client/gui/createworld/ButtonComponent.java
deleted file mode 100644
index 083cadd5f..000000000
--- a/game/client/src/main/java/net/minecraft/client/gui/createworld/ButtonComponent.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package net.minecraft.client.gui.createworld;
-
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.gui.ButtonElement;
-import net.minecraft.client.gui.paged.PageComponent;
-import net.minecraft.client.render.tessellator.Tessellator;
-import net.minecraft.core.lang.I18n;
-import net.minecraft.core.sound.SoundCategory;
-import org.lwjgl.opengl.GL11;
-
-import static org.lwjgl.opengl.GL11.*;
-
-public abstract class ButtonComponent
-    implements PageComponent
-{
-    protected static final Minecraft mc = Minecraft.getMinecraft();
-    protected static final int COMPONENT_SPACING = 2;
-    private static final int BUTTON_WIDTH = 100;
-
-    private final String translationKey;
-
-    protected final ButtonElement resetButton;
-
-    public ButtonComponent(String translationKey)
-    {
-        this.translationKey = translationKey;
-        this.resetButton = new ButtonElement(0, 0, 0, 20, 20, "")
-            .setTextures("minecraft:gui/misc/icon_reset", "minecraft:gui/misc/icon_reset_highlighted", "minecraft:gui/misc/icon_reset");
-    }
-
-    public abstract void resetValue();
-
-    public abstract boolean isDefault();
-
-    @Override
-    public int getHeight()
-    {
-        return COMPONENT_SPACING + 20 + COMPONENT_SPACING;
-    }
-
-    @Override
-    public void render(int x, int y, int width, int relativeMouseX, int relativeMouseY)
-    {
-        String translated = I18n.getInstance().translateKey(translationKey);
-        int textColor = 0xFFFFFFFF;
-        if (relativeMouseX >= 0 && relativeMouseX <= width && relativeMouseY >= COMPONENT_SPACING && relativeMouseY <= getHeight() - COMPONENT_SPACING) {
-            textColor = 0xFFFFFFA0;
-        }
-        mc.font.render(Tessellator.instance, translated, x, y + (getHeight() / 2) - 4).setShadow().setColor(textColor).call();
-        resetButton.enabled = !isDefault();
-        renderButton(x, y, width - BUTTON_WIDTH, COMPONENT_SPACING, BUTTON_WIDTH, 20, relativeMouseX, relativeMouseY);
-        int stringWidth = mc.font.stringWidth(translated);
-        int lineMaxX = x + width - BUTTON_WIDTH - 8;
-        if (resetButton.enabled) lineMaxX -= 20;
-        drawRect(x + stringWidth + 8, y + getHeight() / 2, lineMaxX, y + getHeight() / 2 + 1, 0x5F7F7F7F);
-    }
-
-    @Override
-    public void onMouseClick(int mouseButton, int x, int y, int width, int relativeMouseX, int relativeMouseY)
-    {
-        if (relativeMouseX >= width - BUTTON_WIDTH && relativeMouseX <= width && relativeMouseY >= COMPONENT_SPACING && relativeMouseY <= COMPONENT_SPACING + 20) {
-            mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F);
-            buttonClicked(mouseButton, x, y, width, getHeight(), relativeMouseX - (width - BUTTON_WIDTH), relativeMouseY - COMPONENT_SPACING);
-        } else if (resetButton.enabled && relativeMouseX >= width - BUTTON_WIDTH - 20 && relativeMouseX <= width - BUTTON_WIDTH && relativeMouseY >= COMPONENT_SPACING && relativeMouseY <= COMPONENT_SPACING + 20) {
-            mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F);
-            resetValue();
-        }
-    }
-
-    @Override
-    public void onMouseMove(int x, int y, int width, int relativeMouseX, int relativeMouseY) {
-        buttonDragged(x, y, width, getHeight(), relativeMouseX - (width - BUTTON_WIDTH), relativeMouseY - COMPONENT_SPACING);
-    }
-
-    @Override
-    public void onMouseRelease(int mouseButton, int x, int y, int width, int relativeMouseX, int relativeMouseY) {
-        buttonReleased(mouseButton, x, y, width, getHeight(), relativeMouseX - (width - BUTTON_WIDTH), relativeMouseY - COMPONENT_SPACING);
-    }
-
-    protected abstract void buttonClicked(int mouseButton, int x, int y, int width, int height, int relativeMouseX, int relativeMouseY);
-
-    protected void buttonDragged(int x, int y, int width, int height, int relativeMouseX, int relativeMouseY) { }
-
-    protected void buttonReleased(int mouseButton, int x, int y, int width, int height, int relativeMouseX, int relativeMouseY) { }
-
-    @Override
-    public void onKeyPress(int keyCode, char character) { }
-
-    @Override
-    public boolean matchesSearchTerm(String term)
-    {
-        return I18n.getInstance().translateKey(translationKey).toLowerCase().contains(term.toLowerCase());
-    }
-
-    protected void renderButton(int x, int y, int relativeButtonX, int relativeButtonY, int buttonWidth, int buttonHeight, int relativeMouseX, int relativeMouseY) {
-        if (resetButton.enabled) {
-            resetButton.xPosition = x + relativeButtonX - 20;
-            resetButton.yPosition = y + relativeButtonY;
-
-            resetButton.drawButton(mc, x + relativeMouseX, y + relativeMouseY);
-        }
-    }
-
-    protected void drawRect(int minX, int minY, int maxX, int maxY, int argb)
-    {
-        Tessellator tessellator = Tessellator.instance;
-        GL11.glEnable(GL_BLEND);
-        GL11.glDisable(GL_TEXTURE_2D);
-        GL11.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-        tessellator.startDrawingQuads();
-        tessellator.setColorRGBA_I(argb & 0x00FFFFFF, argb >> 24 & 0xff);
-        tessellator.addVertex(minX, maxY, 0.0D);
-        tessellator.addVertex(maxX, maxY, 0.0D);
-        tessellator.addVertex(maxX, minY, 0.0D);
-        tessellator.addVertex(minX, minY, 0.0D);
-        tessellator.draw();
-        GL11.glEnable(GL_TEXTURE_2D);
-        GL11.glDisable(GL_BLEND);
-    }
-}
diff --git a/game/client/src/main/java/net/minecraft/client/gui/createworld/CreateWorldPageRegistry.java b/game/client/src/main/java/net/minecraft/client/gui/createworld/CreateWorldPageRegistry.java
deleted file mode 100644
index 14e163597..000000000
--- a/game/client/src/main/java/net/minecraft/client/gui/createworld/CreateWorldPageRegistry.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package net.minecraft.client.gui.createworld;
-
-import net.minecraft.client.gui.paged.Page;
-import net.minecraft.client.gui.paged.PageRegistry;
-import net.minecraft.core.item.Items;
-import net.minecraft.core.item.ItemStack;
-
-public class CreateWorldPageRegistry
-    extends PageRegistry {
-    private static final PageRegistry INSTANCE = new PageRegistry();
-
-    public static PageRegistry getInstance() {
-        return INSTANCE;
-    }
-
-    public static final Page GENERAL = INSTANCE.register(new Page("gui.create_world.pages.general.title", new ItemStack(Items.MAP)))
-        .withComponent(new TextFieldComponent("gui.create_world.pages.general.label.world_name"))
-        .withComponent(new TextFieldComponent("gui.create_world.pages.general.label.world_seed"));
-    public static final Page WORLD_SETTINGS = INSTANCE.register(new Page("gui.create_world.pages.world_settings.title", new ItemStack(Items.DUST_REDSTONE)));
-    public static final Page GAME_RULES = INSTANCE.register(new Page("gui.create_world.pages.game_rules.title", new ItemStack(Items.BOOK)));
-}
diff --git a/game/client/src/main/java/net/minecraft/client/gui/createworld/ScreenCreateWorld.java b/game/client/src/main/java/net/minecraft/client/gui/createworld/ScreenCreateWorld.java
deleted file mode 100644
index cc31cced6..000000000
--- a/game/client/src/main/java/net/minecraft/client/gui/createworld/ScreenCreateWorld.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package net.minecraft.client.gui.createworld;
-
-import net.minecraft.client.gui.Screen;
-import net.minecraft.client.gui.paged.ScreenPaged;
-
-public class ScreenCreateWorld
-    extends ScreenPaged {
-    public ScreenCreateWorld(Screen parent) {
-        super(
-            parent,
-            CreateWorldPageRegistry.getInstance(),
-            CreateWorldPageRegistry.GENERAL,
-            "gui.create_world.label.title",
-            new String[] {
-                "gui.create_world.button.cancel",
-                "gui.create_world.button.create_world",
-            }
-        );
-    }
-}
diff --git a/game/client/src/main/java/net/minecraft/client/gui/createworld/TextFieldComponent.java b/game/client/src/main/java/net/minecraft/client/gui/createworld/TextFieldComponent.java
deleted file mode 100644
index ca8ffc6ab..000000000
--- a/game/client/src/main/java/net/minecraft/client/gui/createworld/TextFieldComponent.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package net.minecraft.client.gui.createworld;
-
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.gui.TextFieldElement;
-import net.minecraft.client.gui.paged.CallbackComponent;
-import net.minecraft.client.gui.paged.OnValueChangedCallback;
-import net.minecraft.client.render.tessellator.Tessellator;
-import net.minecraft.core.lang.I18n;
-import org.lwjgl.input.Keyboard;
-import org.lwjgl.opengl.GL11;
-
-import static org.lwjgl.opengl.GL11.*;
-
-public class TextFieldComponent
-    implements CallbackComponent<String> {
-    protected static final Minecraft mc = Minecraft.getMinecraft();
-    protected static final int COMPONENT_SPACING = 2;
-    private static final int BUTTON_WIDTH = 150;
-
-    private final String translationKey;
-
-    private final TextFieldElement textField;
-
-    private OnValueChangedCallback<String> callback = null;
-
-    public TextFieldComponent(String translationKey)
-    {
-        this.translationKey = translationKey;
-
-        this.textField = new TextFieldElement(
-            null,
-            mc.font,
-            0, 0,
-            BUTTON_WIDTH,
-            18,
-            "",
-            ""
-        );
-    }
-
-    @Override
-    public int getHeight()
-    {
-        return COMPONENT_SPACING + 20 + COMPONENT_SPACING;
-    }
-
-    @Override
-    public void render(int x, int y, int width, int relativeMouseX, int relativeMouseY)
-    {
-        String translated = I18n.getInstance().translateKey(translationKey);
-        int textColor = 0xFFFFFFFF;
-        if (relativeMouseX >= 0 && relativeMouseX <= width && relativeMouseY >= COMPONENT_SPACING && relativeMouseY <= getHeight() - COMPONENT_SPACING) {
-            textColor = 0xFFFFFFA0;
-        }
-        mc.font.render(Tessellator.instance, translated, x, y + (getHeight() / 2) - 4).setShadow().setColor(textColor).call();
-        int stringWidth = mc.font.stringWidth(translated);
-        int lineMaxX = x + width - BUTTON_WIDTH - 8;
-        drawRect(x + stringWidth + 8, y + getHeight() / 2, lineMaxX, y + getHeight() / 2 + 1, 0x5F7F7F7F);
-
-        textField.xPosition = x + width - BUTTON_WIDTH;
-        textField.yPosition = y + COMPONENT_SPACING + 1;
-        textField.drawTextBox();
-        textField.updateCursor(mc, x + relativeMouseX, y + relativeMouseY);
-    }
-
-    @Override
-    public void onMouseClick(int mouseButton, int x, int y, int width, int relativeMouseX, int relativeMouseY)
-    {
-        boolean mouseInside = relativeMouseX >= width - BUTTON_WIDTH && relativeMouseX <= width && relativeMouseY >= COMPONENT_SPACING && relativeMouseY <= COMPONENT_SPACING + 20;
-        if (mouseInside) {
-            textField.setFocused(true);
-            Keyboard.enableRepeatEvents(true);
-        } else {
-            textField.setFocused(false);
-            Keyboard.enableRepeatEvents(false);
-        }
-    }
-
-    @Override
-    public void onMouseMove(int x, int y, int width, int relativeMouseX, int relativeMouseY) {
-    }
-
-    @Override
-    public void onMouseRelease(int mouseButton, int x, int y, int width, int relativeMouseX, int relativeMouseY) {
-    }
-
-    @Override
-    public void onKeyPress(int keyCode, char character) {
-        if (textField.isFocused) {
-            textField.textboxKeyTyped(character, keyCode);
-        }
-    }
-
-    @Override
-    public boolean matchesSearchTerm(String term)
-    {
-        return I18n.getInstance().translateKey(translationKey).toLowerCase().contains(term.toLowerCase());
-    }
-
-    protected void drawRect(int minX, int minY, int maxX, int maxY, int argb)
-    {
-        Tessellator tessellator = Tessellator.instance;
-        GL11.glEnable(GL_BLEND);
-        GL11.glDisable(GL_TEXTURE_2D);
-        GL11.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-        tessellator.startDrawingQuads();
-        tessellator.setColorRGBA_I(argb & 0x00FFFFFF, argb >> 24 & 0xff);
-        tessellator.addVertex(minX, maxY, 0.0D);
-        tessellator.addVertex(maxX, maxY, 0.0D);
-        tessellator.addVertex(maxX, minY, 0.0D);
-        tessellator.addVertex(minX, minY, 0.0D);
-        tessellator.draw();
-        GL11.glEnable(GL_TEXTURE_2D);
-        GL11.glDisable(GL_BLEND);
-    }
-
-    @Override
-    public CallbackComponent<String> withOnValueChangedCallback(OnValueChangedCallback<String> callback) {
-        this.callback = callback;
-        return this;
-    }
-}
diff --git a/game/client/src/main/java/net/minecraft/client/gui/createworld/WorldConfig.java b/game/client/src/main/java/net/minecraft/client/gui/createworld/WorldConfig.java
deleted file mode 100644
index 79816a569..000000000
--- a/game/client/src/main/java/net/minecraft/client/gui/createworld/WorldConfig.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package net.minecraft.client.gui.createworld;
-
-import net.minecraft.core.player.gamemode.Gamemode;
-import net.minecraft.core.util.helper.MathHelper;
-
-import java.util.Random;
-
-public class WorldConfig {
-    private String name;
-    private long seed;
-    private Gamemode gamemode;
-    private boolean cheatsEnabled;
-
-    public WorldConfig(String name) {
-        this.name = name;
-        this.seed = (new Random()).nextLong();
-        this.gamemode = Gamemode.survival;
-        this.cheatsEnabled = false;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public long getSeed() {
-        return seed;
-    }
-
-    public void setSeed(long seed) {
-        this.seed = seed;
-    }
-
-    public void setSeed(String seed) {
-        if(!MathHelper.stringNullOrLengthZero(seed))
-        {
-            try
-            {
-                long parsedSeed = Long.parseLong(seed);
-                if(parsedSeed != 0L)
-                {
-                    this.seed = parsedSeed;
-                }
-            }
-            catch(NumberFormatException e)
-            {
-                this.seed = seed.hashCode();
-            }
-        }
-    }
-
-    public Gamemode getGamemode() {
-        return gamemode;
-    }
-
-    public void setGamemode(Gamemode gamemode) {
-        this.gamemode = gamemode;
-    }
-
-    public boolean isCheatsEnabled() {
-        return cheatsEnabled;
-    }
-
-    public void setCheatsEnabled(boolean cheatsEnabled) {
-        this.cheatsEnabled = cheatsEnabled;
-    }
-}
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/ScreenGuidebook.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/ScreenGuidebook.java
index 3a866bc28..eaf4fcf2f 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/ScreenGuidebook.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/ScreenGuidebook.java
@@ -551,9 +551,9 @@ public class ScreenGuidebook
             int fgColor = section.getForegroundColor();
             ItemStack icon = section.getTabIcon();
 
-            OpenGLHelper.glColor1i(bgColor);
+            OpenGLHelper.glColor1i(0xFF000000 | bgColor);
             drawTexturedModalRect(x + xo, y, u, v, WIDTH, HEIGHT);
-            OpenGLHelper.glColor1i(fgColor);
+            OpenGLHelper.glColor1i(0xFF000000 | fgColor);
             drawTexturedModalRect(x + xo, y, u, v + HEIGHT, WIDTH, HEIGHT);
 
             if (icon != null) {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookPageMob.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookPageMob.java
index 892b461cc..0aeff3f7c 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookPageMob.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookPageMob.java
@@ -45,14 +45,14 @@ public class GuidebookPageMob
 
 
 
-    public GuidebookPageMob(GuidebookSection section, Class<? extends Entity> mobClass, MobInfoRegistry.MobInfo mobInfo) {
+    public GuidebookPageMob(GuidebookSection section, Class<? extends Mob> mobClass, MobInfoRegistry.MobInfo mobInfo) {
         super(section);
 
         this.mobClass = mobClass;
         this.mobInfo = mobInfo;
         Mob example = null;
         try {
-            example = (Mob) mobClass.getConstructor(World.class).newInstance(WorldClient.EMPTY_WORLD);
+            example = EntityDispatcher.getInstance().createEntityInWorld(mobClass, WorldClient.EMPTY_WORLD);
         } catch (Exception e) {
             LOGGER.error("Failed to create example instance of mob '{}'!", mobClass.getSimpleName(), e);
         }
@@ -70,7 +70,8 @@ public class GuidebookPageMob
             }
         }
 
-        discovered = mc.statsCounter.readStat(StatList.mobEncounterStats.get(EntityDispatcher.idForClass(mobClass))) > 0;
+        EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(mobClass);
+        discovered = mc.statsCounter.readStat(StatList.mobEncounterStats.get(entry != null ? entry.namespaceID : null)) > 0;
     }
 
     private static String[] createDescLines(FontRenderer sr, String languageKey) {
@@ -109,7 +110,8 @@ public class GuidebookPageMob
 
     @Override
     protected void renderForeground(TextureManager re, FontRenderer sr, int x, int y, int mouseX, int mouseY, float partialTicks) {
-        discovered = mc.statsCounter.readStat(StatList.mobEncounterStats.get(EntityDispatcher.idForClass(mobClass))) > 0;
+        EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(mobClass);
+        discovered = mc.statsCounter.readStat(StatList.mobEncounterStats.get(entry != null ? entry.namespaceID : null)) > 0;
         if (mc.thePlayer.getGamemode() == Gamemode.creative || !mc.gameSettings.hideUndiscoveredItems.value) discovered = true;
 
         Lighting.disable();
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookSectionMob.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookSectionMob.java
index 544ce699a..75bb57e40 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookSectionMob.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookSectionMob.java
@@ -8,6 +8,7 @@ import net.minecraft.core.data.registry.recipe.SearchQuery;
 import net.minecraft.core.achievement.stat.StatList;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityDispatcher;
+import net.minecraft.core.entity.Mob;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.lang.I18n;
@@ -26,7 +27,7 @@ public class GuidebookSectionMob
     public GuidebookSectionMob() {
         super("guidebook.section.mob", new ItemStack(Items.BONE), 0x66962B, 0xC29B1C);
 
-        for (Class<? extends Entity> e : MobInfoRegistry.getMobClassList()) {
+        for (Class<? extends Mob> e : MobInfoRegistry.getMobClassList()) {
             MobInfoRegistry.MobInfo mobInfo = MobInfoRegistry.getMobInfo(e);
             GuidebookPageMob page = new GuidebookPageMob(this, e, mobInfo);
             pages.add(page);
@@ -48,7 +49,7 @@ public class GuidebookSectionMob
     public List<GuidebookPage> searchPages(SearchQuery query) {
         ArrayList<GuidebookPage> filtered = new ArrayList<>();
         if(filteredPages == null || !Objects.equals(filteredPages.getLeft(), query.rawQuery)) {
-            for (Class<? extends Entity> e : MobInfoRegistry.getMobClassList()) {
+            for (Class<? extends Mob> e : MobInfoRegistry.getMobClassList()) {
                 MobInfoRegistry.MobInfo mobInfo = MobInfoRegistry.getMobInfo(e);
                 if(query.strict){
                     if (!I18n.getInstance().translateKey(mobInfo.getNameTranslationKey()).equalsIgnoreCase(query.query.getRight())){
@@ -88,7 +89,8 @@ public class GuidebookSectionMob
         @Override
         public String getName() {
         	Minecraft mc = Minecraft.getMinecraft();
-            boolean discovered = mc.statsCounter.readStat(StatList.mobEncounterStats.get(EntityDispatcher.idForClass(mobInfo.getEntityClass()))) > 0;
+            EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(mobInfo.getEntityClass());
+            boolean discovered = mc.statsCounter.readStat(StatList.mobEncounterStats.get(entry != null ? entry.namespaceID : null)) > 0;
             if (mc.thePlayer.getGamemode() == Gamemode.creative) discovered = true;
 
             if (discovered) {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/MobInfoRegistry.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/MobInfoRegistry.java
index 014b8c692..5a3d58c88 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/MobInfoRegistry.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/MobInfoRegistry.java
@@ -4,6 +4,7 @@ import net.minecraft.client.render.EntityRenderDispatcher;
 import net.minecraft.client.render.entity.EntityRenderer;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.entity.Entity;
+import net.minecraft.core.entity.Mob;
 import net.minecraft.core.entity.animal.*;
 import net.minecraft.core.entity.monster.*;
 import net.minecraft.core.item.Items;
@@ -12,15 +13,15 @@ import net.minecraft.core.item.ItemStack;
 import java.util.*;
 
 public abstract class MobInfoRegistry {
-    private static final Map<Class<? extends Entity>, MobInfo> mobInfoMap = new HashMap<>();
-    private static final List<Class<? extends Entity>> mobClassList = new ArrayList<>();
+    private static final Map<Class<? extends Mob>, MobInfo> mobInfoMap = new HashMap<>();
+    private static final List<Class<? extends Mob>> mobClassList = new ArrayList<>();
 
-    public static void register(Class<? extends Entity> entityClass, String nameTranslationKey, String descriptionTranslationKey, int health, int score, MobDrop[] drops) {
+    public static void register(Class<? extends Mob> entityClass, String nameTranslationKey, String descriptionTranslationKey, int health, int score, MobDrop[] drops) {
         mobClassList.add(entityClass);
         mobInfoMap.put(entityClass, new MobInfo(entityClass, nameTranslationKey, descriptionTranslationKey, health, score, drops));
     }
 
-    public static List<Class<? extends Entity>> getMobClassList() {
+    public static List<Class<? extends Mob>> getMobClassList() {
         return Collections.unmodifiableList(mobClassList);
     }
 
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngame.java b/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngame.java
index 3f18e2a5e..8edf01461 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngame.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/HudIngame.java
@@ -119,8 +119,8 @@ public class HudIngame extends Gui
         }
 
         for (final HudComponent component : HudComponents.INSTANCE.getComponents()) {
-            if (component.isVisible(this.mc)) {
-                component.render(this.mc, this, width - sp * 2, height - sp * 2, partialTicks);
+            if (component.isVisible()) {
+                component.render(this, width - sp * 2, height - sp * 2, partialTicks);
             }
         }
 
@@ -145,41 +145,28 @@ public class HudIngame extends Gui
         }
 
         //Text Overlays
-        if (mc.gameSettings.immersiveMode.drawOverlays())
-        {
+        if (mc.gameSettings.immersiveMode.drawOverlays()) {
             drawRecordPlayingOverlay(width, height - sp, partialTicks);
 
-            int tsp = 2 + sp;
-            int line = 0;
-            int lineHeight = 10;
-
             boolean clock = false;
             boolean compass = false;
             boolean rotaryCalendar = false;
 
-            if (mc.thePlayer.getGamemode() == Gamemode.creative)
-            {
+            if (mc.thePlayer.getGamemode() == Gamemode.creative) {
                 clock = true;
                 compass = true;
                 rotaryCalendar = true;
-            }
-            else
-            {
-                for (int iinv = 0; iinv < mc.thePlayer.inventory.getContainerSize(); iinv++)
-                {
+            } else {
+                for (int iinv = 0; iinv < mc.thePlayer.inventory.getContainerSize(); iinv++) {
                     ItemStack item = mc.thePlayer.inventory.getItem(iinv);
-                    if (item != null)
-                    {
-                        if (item.itemID == Items.TOOL_CLOCK.id)
-                        {
+                    if (item != null) {
+                        if (item.itemID == Items.TOOL_CLOCK.id) {
                             clock = true;
                         }
-                        if (item.itemID == Items.TOOL_COMPASS.id)
-                        {
+                        if (item.itemID == Items.TOOL_COMPASS.id) {
                             compass = true;
                         }
-                        if (item.itemID == Items.TOOL_CALENDAR.id)
-                        {
+                        if (item.itemID == Items.TOOL_CALENDAR.id) {
                             rotaryCalendar = true;
                         }
                     }
@@ -189,8 +176,7 @@ public class HudIngame extends Gui
             // Show Player List
             if (!mc.gameSettings.showDebugScreen.value) drawPlayerList(width, height, sp);
 
-            if (mc.gameSettings.showDebugScreen.value)
-            {
+            if (mc.gameSettings.showDebugScreen.value) {
                 glPushMatrix();
                 glEnable(GL_BLEND);
                 glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
@@ -220,49 +206,49 @@ public class HudIngame extends Gui
                 drawDebugScreenLineRight(GL11.glGetString(GL11.GL_VERSION));
                 drawDebugScreenLineRight("Resolution: " + mc.gameWindow.getWidthPixels() + "x" + mc.gameWindow.getHeightPixels());
                 drawDebugScreenLineRight("Render Resolution: " + mc.renderer.getRenderWidth() + "x" + mc.renderer.getRenderHeight());
-                
+
                 drawDebugScreenLine("Better than Adventure! " + Minecraft.VERSION);
                 drawDebugScreenLine(mc.debug);
-                if(mc.terrainRenderer instanceof TerrainRendererMultiDraw) {
-                	TerrainRendererMultiDraw terrainRenderer = (TerrainRendererMultiDraw) mc.terrainRenderer;
-                	
-                	int regionCount = terrainRenderer.regionBuffer.size();
+                if (mc.terrainRenderer instanceof TerrainRendererMultiDraw) {
+                    TerrainRendererMultiDraw terrainRenderer = (TerrainRendererMultiDraw) mc.terrainRenderer;
+
+                    int regionCount = terrainRenderer.regionBuffer.size();
 
                     int playerX = MathHelper.floor(mc.thePlayer.x);
                     int playerZ = MathHelper.floor(mc.thePlayer.z);
 
-                	
-                	List<RenderRegion> allRegions = terrainRenderer.regionBuffer.getAllRegions();
+
+                    List<RenderRegion> allRegions = terrainRenderer.regionBuffer.getAllRegions();
                     int totalEntries = 0;
                     int visibleEntries = 0;
-                	for(int i=0; i < allRegions.size(); i++) {
+                    for (int i = 0; i < allRegions.size(); i++) {
                         RenderRegion region = allRegions.get(i);
-                        for(int j=0; j < region.renderLists.length; j++) {
-                        	RenderList renderList = region.renderLists[j];
-                        	if(renderList == null) {
-                        		continue;
-                        	}
-                        	totalEntries += renderList.size();
-                        	visibleEntries += renderList.visibleEntryCount();
+                        for (int j = 0; j < region.renderLists.length; j++) {
+                            RenderList renderList = region.renderLists[j];
+                            if (renderList == null) {
+                                continue;
+                            }
+                            totalEntries += renderList.size();
+                            visibleEntries += renderList.visibleEntryCount();
                         }
-                	}
-                	
-                	drawDebugScreenLine("R: " + regionCount + " D: " + terrainRenderer.getTotalDrawCalls() + " T: " + visibleEntries + "/" + totalEntries + " VBO: " + terrainRenderer.vertexBuffer.entryCount());
-                	
-                	RenderRegion currentRegion = terrainRenderer.getRenderRegionAtBlockPos(playerX, playerZ);
+                    }
+
+                    drawDebugScreenLine("R: " + regionCount + " D: " + terrainRenderer.getTotalDrawCalls() + " T: " + visibleEntries + "/" + totalEntries + " VBO: " + terrainRenderer.vertexBuffer.entryCount());
+
+                    RenderRegion currentRegion = terrainRenderer.getRenderRegionAtBlockPos(playerX, playerZ);
                     totalEntries = 0;
                     visibleEntries = 0;
-                    for(int j=0; j < currentRegion.renderLists.length; j++) {
-                    	RenderList renderList = currentRegion.renderLists[j];
-                    	if(renderList == null) {
-                    		continue;
-                    	}
-                    	totalEntries += renderList.size();
-                    	visibleEntries += renderList.visibleEntryCount();
+                    for (int j = 0; j < currentRegion.renderLists.length; j++) {
+                        RenderList renderList = currentRegion.renderLists[j];
+                        if (renderList == null) {
+                            continue;
+                        }
+                        totalEntries += renderList.size();
+                        visibleEntries += renderList.visibleEntryCount();
                     }
-                	drawDebugScreenLine("CR: " + currentRegion + " E: " + visibleEntries + "/" + totalEntries);
+                    drawDebugScreenLine("CR: " + currentRegion + " E: " + visibleEntries + "/" + totalEntries);
 
-                }else {
+                } else {
                     drawDebugScreenLine(mc.getRendererDebugInfo());
                 }
                 drawDebugScreenLine(mc.getEntityDebugInfoString());
@@ -272,28 +258,24 @@ public class HudIngame extends Gui
                 debugOffset += space;
                 debugOffsetRight += space;
 
-                if (compass)
-                {
+                if (compass) {
                     drawDebugScreenLine("X: " + mc.thePlayer.x);
                     drawDebugScreenLine("Y: " + mc.thePlayer.y);
                     drawDebugScreenLine("Z: " + mc.thePlayer.z);
-                    
+
                     int chunkX = MathHelper.floor(mc.thePlayer.x) >> 4;
-                	int chunkZ = MathHelper.floor(mc.thePlayer.z) >> 4;
-                    
-            		drawDebugScreenLine("Chunk: " + chunkX + ", " + chunkZ);
-            		
+                    int chunkZ = MathHelper.floor(mc.thePlayer.z) >> 4;
+
+                    drawDebugScreenLine("Chunk: " + chunkX + ", " + chunkZ);
+
                     int yaw = DynamicTexture.pmod(java.lang.Math.round(mc.thePlayer.yRot), 360);
                     int pitch = java.lang.Math.round(mc.thePlayer.xRot);
                     drawDebugScreenLine("Facing: " + yaw + "°, " + pitch + "° (" + I18n.getInstance().translateKey(getFacingDirection(mc.thePlayer).getTranslationKey()) + ")");
-                }
-                else
-                {
+                } else {
                     drawDebugScreenLine("Hold a compass for more information");
                 }
 
-                if (clock)
-                {
+                if (clock) {
                     if (!compass) debugOffset += space;
 
                     int playerX = MathHelper.floor(mc.thePlayer.x);
@@ -309,48 +291,42 @@ public class HudIngame extends Gui
 
                 debugOffset += space;
 
-                if (clock)
-                {
-                    drawDebugScreenLine("Time: " + getTimeString() + " (Day " + (mc.currentWorld.getWorldTime() / Global.DAY_LENGTH_TICKS) + ")");
-                }
-                else
-                {
+                if (clock) {
+                    drawDebugScreenLine("Time: " + getTimeString(mc) + " (Day " + (mc.currentWorld.getWorldTime() / Global.DAY_LENGTH_TICKS) + ")");
+                } else {
                     drawDebugScreenLine("Hold a clock for more information");
                 }
 
                 debugOffset += space;
 
-                if (rotaryCalendar)
-                {
+                if (rotaryCalendar) {
                     Season season = mc.currentWorld.getSeasonManager().getCurrentSeason();
                     String seasonString = season != null ? season.getTranslatedName() : "None";
                     Weather weather = mc.currentWorld.getCurrentWeather();
                     String weatherString = weather != null ? stringtranslate.translateKey(weather.getNameLanguageKey()) : "None";
-                    
+
                     int blockX = MathHelper.floor(mc.thePlayer.x);
-					int blockY = MathHelper.floor(mc.thePlayer.y);
-					int blockZ = MathHelper.floor(mc.thePlayer.z);
-                    
+                    int blockY = MathHelper.floor(mc.thePlayer.y);
+                    int blockZ = MathHelper.floor(mc.thePlayer.z);
+
                     drawDebugScreenLine("Season: " + seasonString);
                     drawDebugScreenLine("Weather: " + weatherString + " (" + (mc.currentWorld.weatherManager.getWeatherDuration() / 20) + "s I: " + percentRound(mc.currentWorld.weatherManager.getWeatherIntensity()) + "% P: " + percentRound(mc.currentWorld.weatherManager.getWeatherPower()) + "%)");
                     drawDebugScreenLine("Biome: " + Registries.BIOMES.getKey(mc.currentWorld.getBlockBiome(blockX, blockY, blockZ)));
-                    
+
                     float temperature = (float) floor100(mc.currentWorld.getBlockTemperature(blockX, blockZ));
                     float humidity = (float) floor100(mc.currentWorld.getBlockHumidity(blockX, blockZ));
-                    
+
                     drawDebugScreenLine("Temperature: " + temperature + " Humidity: " + humidity);
-                    
+
                     WindProvider windProvider = mc.currentWorld.worldType.getWindManager();
-                    if(windProvider != null) {
-                    	float direction = (float) floor100(windProvider.getWindDirection(mc.currentWorld, 0.0f, 500.0f, 0.0f));
-                    	float intensity = (float) floor100(windProvider.getWindIntensity(mc.currentWorld, 0.0f, 500.0f, 0.0f));
+                    if (windProvider != null) {
+                        float direction = (float) floor100(windProvider.getWindDirection(mc.currentWorld, 0.0f, 500.0f, 0.0f));
+                        float intensity = (float) floor100(windProvider.getWindIntensity(mc.currentWorld, 0.0f, 500.0f, 0.0f));
 
                         int yaw = DynamicTexture.pmod(java.lang.Math.round((direction * 360.0f) + 270.0f), 360);
-                    	drawDebugScreenLine("Wind: " + yaw + "°, " + java.lang.Math.round(intensity * 100.0f) + "%");
+                        drawDebugScreenLine("Wind: " + yaw + "°, " + java.lang.Math.round(intensity * 100.0f) + "%");
                     }
-                }
-                else
-                {
+                } else {
                     drawDebugScreenLine("Hold a rotary calendar for more information");
                 }
 
@@ -363,23 +339,18 @@ public class HudIngame extends Gui
                 drawDebugScreenLine("CTRL + F3: Show Frametimes");
                 drawDebugScreenLine("F3 + Q: Show Keyboard Shortcuts");
 
-                if (mc.objectMouseOver != null && mc.objectMouseOver.entity == null)
-                {
+                if (mc.objectMouseOver != null && mc.objectMouseOver.entity == null) {
                     debugOffset += space;
 
                     Block<?> block = Blocks.blocksList[mc.currentWorld.getBlockId(mc.objectMouseOver.x, mc.objectMouseOver.y, mc.objectMouseOver.z)];
-                    if (block.id() > 0 && block.id() < Blocks.blocksList.length)
-                    {
+                    if (block.id() > 0 && block.id() < Blocks.blocksList.length) {
                         drawDebugScreenLineRight("Looking at:");
 
                         String name;
                         int meta = mc.currentWorld.getBlockMetadata(mc.objectMouseOver.x, mc.objectMouseOver.y, mc.objectMouseOver.z);
-                        try
-                        {
+                        try {
                             name = block.asItem().getTranslatedName(new ItemStack(block, 1, meta));
-                        }
-                        catch (Exception e)
-                        {
+                        } catch (Exception e) {
                             name = "<ERROR>";
                         }
 
@@ -389,19 +360,17 @@ public class HudIngame extends Gui
                         drawDebugScreenLineRight(block.getLanguageKey(meta));
                         drawDebugScreenLineRight(id);
 
-                        if (compass)
-                        {
+                        if (compass) {
                             drawDebugScreenLineRight("[" + mc.objectMouseOver.x + ", " + mc.objectMouseOver.y + ", " + mc.objectMouseOver.z + "]");
                         }
 
                         drawDebugScreenLineRight("Harvest: " + mc.thePlayer.canHarvestBlock(block));
 
-                        if (clock)
-                        {
+                        if (clock) {
                             int lightX = mc.objectMouseOver.x;
                             int lightY = mc.objectMouseOver.y;
                             int lightZ = mc.objectMouseOver.z;
-                            
+
                             Side side = mc.objectMouseOver.side;
                             lightX += side.getOffsetX();
                             lightY += side.getOffsetY();
@@ -413,10 +382,8 @@ public class HudIngame extends Gui
                         drawDebugScreenLineRight("Signal: " + mc.currentWorld.getSignal(mc.objectMouseOver.x, mc.objectMouseOver.y, mc.objectMouseOver.z, mc.objectMouseOver.side));
                         drawDebugScreenLineRight("Direct signal: " + mc.currentWorld.getDirectSignal(mc.objectMouseOver.x, mc.objectMouseOver.y, mc.objectMouseOver.z, mc.objectMouseOver.side));
 
-                        for (Tag<Block<?>> tag : BlockTags.TAG_LIST)
-                        {
-                            if (tag.appliesTo(block))
-                            {
+                        for (Tag<Block<?>> tag : BlockTags.TAG_LIST) {
+                            if (tag.appliesTo(block)) {
                                 drawDebugScreenLineRight(tag.getName());
                             }
                         }
@@ -424,177 +391,6 @@ public class HudIngame extends Gui
                 }
                 glPopMatrix();
             }
-            if (!mc.gameSettings.showDebugScreen.value)
-            {
-                if (mc.gameSettings.versionInOverlay.value) {
-                    int color;
-                    if (mc.gameSettings.overlayMode.value == 2)
-                    {
-                        color = 0xffff60;
-                    } else {
-                        color = 0xFFFFFF;
-                    }
-                    drawStringShadow(fontRenderer, "Better than Adventure! ", tsp, tsp + lineHeight * line, color);
-                    drawStringShadow(fontRenderer, Global.VERSION, fontRenderer.getLastEndingX(), tsp + lineHeight * line++, 0xffffff);
-                }
-                if (mc.gameSettings.fpsInOverlay.value)
-                {
-                    drawStringShadow(fontRenderer, mc.debugFPS, tsp, tsp + lineHeight * line++, 0xffffff);
-                }
-                if (compass)
-                {
-                    if (mc.gameSettings.overlayShowCoords.value)
-                    {
-                        if (mc.gameSettings.overlayMode.value == 0)
-                        {
-                            drawStringShadow(fontRenderer, "x: " + floor10(mc.thePlayer.x) + " y: " + floor10(mc.thePlayer.y) + " z: " + floor10(mc.thePlayer.z), tsp, tsp + lineHeight * line++, 0xffffff);
-                        }
-                        if (mc.gameSettings.overlayMode.value == 1)
-                        {
-                            drawStringShadow(fontRenderer, "x: " + floor10(mc.thePlayer.x), tsp, tsp + lineHeight * line++, 0xffffff);
-                            drawStringShadow(fontRenderer, "y: " + floor10(mc.thePlayer.y), tsp, tsp + lineHeight * line++, 0xffffff);
-                            drawStringShadow(fontRenderer, "z: " + floor10(mc.thePlayer.z), tsp, tsp + lineHeight * line++, 0xffffff);
-                        }
-                        if (mc.gameSettings.overlayMode.value == 2)
-                        {
-                            drawStringShadow(fontRenderer, "x: " + floor10(mc.thePlayer.x), tsp, tsp + lineHeight * line++, 0xff8080);
-                            drawStringShadow(fontRenderer, "y: " + floor10(mc.thePlayer.y), tsp, tsp + lineHeight * line++, 0x80ff80);
-                            drawStringShadow(fontRenderer, "z: " + floor10(mc.thePlayer.z), tsp, tsp + lineHeight * line++, 0x8080ff);
-                        }
-                    }
-                    if (mc.gameSettings.overlayShowDirection.value)
-                    {
-                        drawStringShadow(fontRenderer, I18n.getInstance().translateKey(getFacingDirection(mc.thePlayer).getTranslationKey()), tsp, tsp + lineHeight * line++, 0xffffff);
-                    }
-                }
-                if (clock)
-                {
-                    if (mc.gameSettings.overlayShowTime.value)
-                    {
-                        if (mc.gameSettings.overlayMode.value == 0)
-                        {
-                            this.drawStringShadow(fontRenderer, String.format("%s %s %d", getTimeString(), I18n.getInstance().translateKey("unit.day"),mc.currentWorld.getWorldTime() / Global.DAY_LENGTH_TICKS), tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xffff80 : 0xffffff);
-                        }
-                        if (mc.gameSettings.overlayMode.value > 0)
-                        {
-                            this.drawStringShadow(fontRenderer, getTimeString(), tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xffff80 : 0xffffff);
-                            this.drawStringShadow(fontRenderer, String.format("%s %d", I18n.getInstance().translateKey("unit.day"),mc.currentWorld.getWorldTime() / Global.DAY_LENGTH_TICKS), tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xffff80 : 0xffffff);
-                        }
-                    }
-                }
-                if (rotaryCalendar)
-                {
-                    if (mc.gameSettings.overlayShowSeason.value)
-                    {
-                        Season season = mc.currentWorld.getSeasonManager().getCurrentSeason();
-                        if (season != null)
-                        {
-                            this.drawStringShadow(fontRenderer, season.getTranslatedName(), tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0x80ffff : 0xffffff);
-                        }
-                    }
-                    if (mc.gameSettings.overlayShowWeather.value)
-                    {
-                        Weather weather = mc.currentWorld.getCurrentWeather();
-                        if (weather != null && weather != mc.currentWorld.worldType.getDefaultWeather())
-                        {
-                            this.drawStringShadow(fontRenderer, stringtranslate.translateKey(weather.getNameLanguageKey()), tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xff80ff : 0xffffff);
-                        }
-                    }
-					if(mc.gameSettings.overlayShowBiome.value)
-					{
-						int blockX = MathHelper.floor(mc.thePlayer.x);
-						int blockY = MathHelper.floor(mc.thePlayer.y);
-						int blockZ = MathHelper.floor(mc.thePlayer.z);
-						String key = mc.currentWorld.getBlockBiome(blockX, blockY, blockZ).translationKey;
-						drawStringShadow(fontRenderer, stringtranslate.translateKey(key), tsp, tsp + lineHeight * line++, 0xffffff);
-					}
-                }
-            }
-        }
-
-        if (mc.gameSettings.immersiveMode.drawOverlays() && mc.gameSettings.armorDurabilityOverlay.value)
-        {
-            for (int i = mc.thePlayer.getNumArmorSlots() - 1; i >= 0; i--)
-            {
-                ItemStack stack = mc.thePlayer.getItemInArmorSlot(HumanArmorShape.values()[i]);
-
-                if (stack != null)
-                {
-                    int x = 2 + sp;
-                    int y = height - sp - 64 + i * 16;
-//                    ItemModelDispatcher.getInstance().getDispatch(stack).renderItemIntoGui(Tessellator.instance, font, mc.textureManager, stack, x, y, 1.0f);
-                    ItemModelDispatcher.getInstance().getDispatch(stack).renderGui(Tessellator.instance, null, stack, x, y, 1f, 1.0f, partialTicks);
-
-                    if (stack.isItemStackDamageable())
-                    {
-                        float durability = (stack.getMaxDamage() - stack.getMetadata()) / (float) stack.getMaxDamage();
-
-                        int l = (int) (durability * 255);
-                        final int i1 = 255 - l << 16 | l << 8;
-
-                        glDisable(GL_BLEND);
-                        glDisable(GL_LIGHTING);
-                        drawStringShadow(this.fontRenderer, String.valueOf((stack.getMaxDamage() - stack.getMetadata()) + 1), x + 20, y + 4, i1);
-                    }
-                }
-            }
-            glDisable(GL_BLEND);
-            glDisable(GL_LIGHTING);
-        }
-
-        if (mc.gameSettings.immersiveMode.drawOverlays() && mc.gameSettings.heldItemCountOverlay.value)
-        {
-            ItemStack heldItemStack = mc.thePlayer.inventory.getCurrentItem();
-            if (heldItemStack != null)
-            {
-                int x = width / 2 + 96;
-                int y = height - sp - 19;
-
-
-                boolean renderItem;
-
-                if (heldItemStack.isItemStackDamageable())
-                {
-                    renderItem = true;
-                    float durability = (heldItemStack.getMaxDamage() - heldItemStack.getMetadata()) / (float) heldItemStack.getMaxDamage();
-                    
-                    final int color = Color.HSBtoRGB(durability/3F, 1f, 1f);
-                    int remainingDurability = (heldItemStack.getMaxDamage() - heldItemStack.getMetadata()) + 1;
-                    drawStringShadow(this.fontRenderer, "" + remainingDurability, x + 18, y + 4, color);
-                }
-                else
-                {
-                    int itemCount = 0;
-                    int stackCount = 0;
-                    for (int i = 0; i < mc.thePlayer.inventory.getContainerSize(); i++)
-                    {
-                        ItemStack itemStack = mc.thePlayer.inventory.getItem(i);
-                        if (itemStack != null && itemStack.itemID == heldItemStack.itemID && itemStack.getMetadata() == heldItemStack.getMetadata())
-                        {
-                            itemCount += itemStack.stackSize;
-                            stackCount++;
-                        }
-                    }
-                    renderItem = stackCount >= 1;
-
-                    if (renderItem)
-                    {
-                        drawStringShadow(this.fontRenderer, "" + itemCount, x + 18, y + 4, 0xffffff);
-                    }
-                }
-
-                if (renderItem)
-                {
-                    GL11.glEnable(GL11.GL_DEPTH_TEST);
-                    GL11.glDisable(3042 /* GL_BLEND */);
-                    GL11.glEnable(32826 /* GL_RESCALE_NORMAL_EXT */);
-                    Lighting.enableInventoryLight();
-//                    ItemModelDispatcher.getInstance().getDispatch(heldItemStack).renderItemIntoGui(Tessellator.instance, font, mc.textureManager, heldItemStack, x, y, 1.0f);
-                    ItemModelDispatcher.getInstance().getDispatch(heldItemStack).renderGui(Tessellator.instance, null, heldItemStack, x, y, 1f, 1f, partialTicks);
-                    Lighting.disable();
-                }
-            }
-
         }
 
         if (mc.gameSettings.immersiveMode.drawChat())
@@ -858,9 +654,9 @@ public class HudIngame extends Gui
                 final byte xPad = 2;
                 final int h = MathHelper.ceil( this.fontRenderer.heightOfConstrainedChars(message, MAX_CHAT_WIDTH));
                 y -= (h + 2);
-                drawRect(xPad, y - sp - 1, xPad + MAX_CHAT_WIDTH, y + h - sp + 1, alpha / 2 << 24);
+                drawRect(xPad + sp, y - sp - 1, xPad + MAX_CHAT_WIDTH, y + h - sp + 1, alpha / 2 << 24);
                 GL11.glEnable(GL_BLEND);
-                this.fontRenderer.renderWidthConstrained(Tessellator.instance, message, xPad, y, MAX_CHAT_WIDTH).setShadow().setColor(0xFFFFFF + (alpha << 24)).call();
+                this.fontRenderer.renderWidthConstrained(Tessellator.instance, message, xPad + sp, y - sp, MAX_CHAT_WIDTH).setShadow().setColor(0xFFFFFF + (alpha << 24)).call();
             }
         }
 
@@ -869,7 +665,7 @@ public class HudIngame extends Gui
         GL11.glDisable(3042 /* GL_BLEND */);
     }
 
-    public String getTimeString()
+    public static String getTimeString(Minecraft mc)
     {
         long worldTime = mc.currentWorld.getWorldTime();
         int dayLength = Global.DAY_LENGTH_TICKS;
@@ -1004,13 +800,6 @@ public class HudIngame extends Gui
         colorString = true;
     }
 
-    public void addChatMessageTranslate(String s)
-    {
-        I18n i18n = I18n.getInstance();
-        String result = i18n.translateKey(s);
-        addChatMessage(result);
-    }
-
     public static Direction getFacingDirection(Player player)
     {
         return directions[(MathHelper.floor((double) ((player.yRot * 4F) / 360F) + 0.5D) + 2) & 3];
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponent.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponent.java
index d4f529d98..6646a1f0d 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponent.java
@@ -9,6 +9,8 @@ import net.minecraft.client.gui.hud.component.layout.Layout;
 import net.minecraft.client.gui.hud.component.layout.LayoutSnap;
 
 public abstract class HudComponent {
+    protected static final Minecraft mc = Minecraft.getMinecraft();
+
     private final String key;
     private final int xSize;
     private final int ySize;
@@ -22,9 +24,9 @@ public abstract class HudComponent {
         this.layout = layout;
     }
 
-    public abstract boolean isVisible(Minecraft mc);
+    public abstract boolean isVisible();
 
-    public boolean isEnabled(Minecraft mc) {
+    public boolean isEnabled() {
         return true;
     }
 
@@ -32,31 +34,31 @@ public abstract class HudComponent {
         return key;
     }
 
-    public int getXSize(Minecraft mc) {
-        if (!(mc.currentScreen instanceof ScreenHudDesigner) && !isVisible(mc)) return 0;
+    public int getXSize() {
+        if (!isEnabled() || (!(mc.currentScreen instanceof ScreenHudDesigner) && !isVisible())) return 0;
         return xSize;
     }
 
-    public int getYSize(Minecraft mc) {
-        if (!(mc.currentScreen instanceof ScreenHudDesigner) && !isVisible(mc)) return 0;
+    public int getYSize() {
+        if (!isEnabled() || (!(mc.currentScreen instanceof ScreenHudDesigner) && !isVisible())) return 0;
         return ySize;
     }
 
     public int getAnchorX(ComponentAnchor anchor) {
-        return (int) (anchor.xPosition * this.xSize);
+        return (int) (anchor.xPosition * this.getXSize());
     }
 
     public int getAnchorY(ComponentAnchor anchor) {
-        return (int) (anchor.yPosition * this.ySize);
+        return (int) (anchor.yPosition * this.getYSize());
     }
 
     public Layout getLayout() {
         return layout;
     }
 
-    public abstract void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick);
+    public abstract void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick);
 
-    public abstract void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen);
+    public abstract void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen);
 
     public final String toSettingsString() {
         String layoutString;
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentArmorBar.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentArmorBar.java
index bc0e9d198..43db58db0 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentArmorBar.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentArmorBar.java
@@ -21,14 +21,14 @@ public class HudComponentArmorBar
     }
 
     @Override
-    public boolean isVisible(Minecraft mc) {
+    public boolean isVisible() {
         return mc.playerController.canHurtPlayer() && mc.gameSettings.immersiveMode.drawHotbar() && mc.thePlayer.getPlayerProtectionAmount() > 0;
     }
 
     @Override
-    public void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
@@ -38,7 +38,7 @@ public class HudComponentArmorBar
         for (int i = 0; i < 10; i++) {
             if (armorValue > 0)
             {
-                int xArmor = x + getXSize(mc) - i * 8 - 9;
+                int xArmor = x + getXSize() - i * 8 - 9;
                 if (i * 2 + 1 < armorValue)
                 {
                     // Full armor icon
@@ -59,9 +59,9 @@ public class HudComponentArmorBar
     }
 
     @Override
-    public void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
-        int x = layout.getComponentX(mc, this, xSizeScreen);
-        int y = layout.getComponentY(mc, this, ySizeScreen);
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = layout.getComponentX(this, xSizeScreen);
+        int y = layout.getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
@@ -69,7 +69,7 @@ public class HudComponentArmorBar
 
         int armorValue = 11;
         for (int i = 0; i < 10; i++) {
-            int xArmor = x + getXSize(mc) - i * 8 - 9;
+            int xArmor = x + getXSize() - i * 8 - 9;
             if (i * 2 + 1 < armorValue)
             {
                 // Full armor icon
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentArmorDurability.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentArmorDurability.java
new file mode 100644
index 000000000..701149499
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentArmorDurability.java
@@ -0,0 +1,93 @@
+package net.minecraft.client.gui.hud.component;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.hud.HudIngame;
+import net.minecraft.client.gui.hud.component.layout.Layout;
+import net.minecraft.client.render.item.model.ItemModelDispatcher;
+import net.minecraft.client.render.tessellator.Tessellator;
+import net.minecraft.core.enums.HumanArmorShape;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.Items;
+
+import static org.lwjgl.opengl.GL11.*;
+
+public class HudComponentArmorDurability
+    extends HudComponentMovable
+{
+    private static final ItemStack[] PREVIEW_STACKS = new ItemStack[] {
+            Items.ARMOR_HELMET_DIAMOND.getDefaultStack(),
+            Items.ARMOR_CHESTPLATE_DIAMOND.getDefaultStack(),
+            Items.ARMOR_LEGGINGS_DIAMOND.getDefaultStack(),
+            Items.ARMOR_BOOTS_DIAMOND.getDefaultStack(),
+    };
+
+    public HudComponentArmorDurability(String key, Layout layout) {
+        super(key, 64, 64, layout);
+    }
+
+    @Override
+    public boolean isVisible() {
+        return mc.gameSettings.immersiveMode.drawOverlays() && mc.gameSettings.armorDurabilityOverlay.value;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mc.gameSettings.armorDurabilityOverlay.value;
+    }
+
+    @Override
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
+
+        for (int i = mc.thePlayer.getNumArmorSlots() - 1; i >= 0; i--)
+        {
+            ItemStack stack = mc.thePlayer.getItemInArmorSlot(HumanArmorShape.values()[i]);
+
+            if (stack != null)
+            {
+                int xx = x + 2;
+                int yy = y + i * 16;
+                ItemModelDispatcher.getInstance().getDispatch(stack).renderGui(Tessellator.instance, null, stack, xx, yy, 1f, 1.0f, partialTick);
+                glDisable(GL_LIGHTING);
+
+                if (stack.isItemStackDamageable())
+                {
+                    float durability = (stack.getMaxDamage() - stack.getMetadata()) / (float) stack.getMaxDamage();
+
+                    int l = (int) (durability * 255);
+                    final int i1 = 255 - l << 16 | l << 8;
+
+                    mc.font.render(Tessellator.instance, String.valueOf((stack.getMaxDamage() - stack.getMetadata()) + 1), xx + 20, yy + 4).setShadow().setColor(i1).call();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
+
+        for (int i = PREVIEW_STACKS.length - 1; i >= 0; i--)
+        {
+            ItemStack stack = PREVIEW_STACKS[i];
+
+            int xx = x + 2;
+            int yy = y + i * 16;
+            ItemModelDispatcher.getInstance().getDispatch(stack).renderGui(Tessellator.instance, null, stack, xx, yy, 1f, 1.0f, 0.0f);
+            glDisable(GL_LIGHTING);
+
+            if (stack.isItemStackDamageable())
+            {
+                float durability = (stack.getMaxDamage() - stack.getMetadata()) / (float) stack.getMaxDamage();
+
+                int l = (int) (durability * 255);
+                final int i1 = 255 - l << 16 | l << 8;
+
+                mc.font.render(Tessellator.instance, String.valueOf((stack.getMaxDamage() - stack.getMetadata()) + 1), xx + 20, yy + 4).setShadow().setColor(i1).call();
+            }
+        }
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentCrosshair.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentCrosshair.java
index 58fa0ec12..12d145ef3 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentCrosshair.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentCrosshair.java
@@ -14,14 +14,14 @@ public class HudComponentCrosshair
     }
 
     @Override
-    public boolean isVisible(Minecraft mc) {
+    public boolean isVisible() {
         return mc.gameSettings.immersiveMode.drawCrosshair() && mc.gameSettings.thirdPersonView.value == 0;
     }
 
     @Override
-    public void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
 
         // Setup crosshair texture
         GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
@@ -34,9 +34,9 @@ public class HudComponentCrosshair
     }
 
     @Override
-    public void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
-        int x = layout.getComponentX(mc, this, xSizeScreen);
-        int y = layout.getComponentY(mc, this, ySizeScreen);
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = layout.getComponentX(this, xSizeScreen);
+        int y = layout.getComponentY(this, ySizeScreen);
 
         // Setup crosshair texture
         GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentFireBar.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentFireBar.java
index b1736f267..89ea81875 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentFireBar.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentFireBar.java
@@ -14,14 +14,14 @@ public class HudComponentFireBar
     }
 
     @Override
-    public boolean isVisible(Minecraft mc) {
+    public boolean isVisible() {
         return mc.gameSettings.immersiveMode.drawHotbar() && mc.playerController.canHurtPlayer() && !mc.thePlayer.getGamemode().isPlayerInvulnerable() && mc.thePlayer.remainingFireTicks > 0;
     }
 
     @Override
-    public void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
@@ -36,9 +36,9 @@ public class HudComponentFireBar
     }
 
     @Override
-    public void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
-        int x = layout.getComponentX(mc, this, xSizeScreen);
-        int y = layout.getComponentY(mc, this, ySizeScreen);
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = layout.getComponentX(this, xSizeScreen);
+        int y = layout.getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHealthBar.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHealthBar.java
index 4551275df..8eb71cb96 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHealthBar.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHealthBar.java
@@ -26,14 +26,14 @@ public class HudComponentHealthBar
     }
 
     @Override
-    public boolean isVisible(Minecraft mc) {
+    public boolean isVisible() {
         return mc.playerController.canHurtPlayer() && !mc.thePlayer.getGamemode().isPlayerInvulnerable() && mc.gameSettings.immersiveMode.drawHotbar();
     }
 
     @Override
-    public void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
@@ -118,9 +118,9 @@ public class HudComponentHealthBar
     }
 
     @Override
-    public void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
-        int x = layout.getComponentX(mc, this, xSizeScreen);
-        int y = layout.getComponentY(mc, this, ySizeScreen);
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = layout.getComponentX(this, xSizeScreen);
+        int y = layout.getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHeldItemCount.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHeldItemCount.java
new file mode 100644
index 000000000..ad85cf818
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentHeldItemCount.java
@@ -0,0 +1,111 @@
+package net.minecraft.client.gui.hud.component;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.hud.HudIngame;
+import net.minecraft.client.gui.hud.component.layout.Layout;
+import net.minecraft.client.render.Lighting;
+import net.minecraft.client.render.item.model.ItemModelDispatcher;
+import net.minecraft.client.render.tessellator.Tessellator;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.item.ItemStack;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL12;
+
+import java.awt.*;
+
+public class HudComponentHeldItemCount
+    extends HudComponentMovable
+{
+    private static final ItemStack PREVIEW_ITEM = new ItemStack(Blocks.COBBLE_BASALT, 53);
+
+    public HudComponentHeldItemCount(String key, Layout layout) {
+        super(key, 53, 22, layout);
+    }
+
+    @Override
+    public boolean isVisible() {
+        return mc.gameSettings.immersiveMode.drawOverlays() && mc.gameSettings.heldItemCountOverlay.value;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mc.gameSettings.heldItemCountOverlay.value;
+    }
+
+    @Override
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
+
+        int xx = x + 3;
+        int yy = y + 3;
+
+        ItemStack heldItemStack = mc.thePlayer.inventory.getCurrentItem();
+        if (heldItemStack != null)
+        {
+            boolean renderItem;
+
+            if (heldItemStack.isItemStackDamageable())
+            {
+                renderItem = true;
+                float durability = (heldItemStack.getMaxDamage() - heldItemStack.getMetadata()) / (float) heldItemStack.getMaxDamage();
+
+                final int color = 0xFF000000 + Color.HSBtoRGB(durability/3F, 1f, 1f);
+                int remainingDurability = (heldItemStack.getMaxDamage() - heldItemStack.getMetadata()) + 1;
+                mc.font.render(Tessellator.instance, remainingDurability, xx + 18, yy + 4).setShadow().setColor(color).call();
+            }
+            else
+            {
+                int itemCount = 0;
+                int stackCount = 0;
+                for (int i = 0; i < mc.thePlayer.inventory.getContainerSize(); i++)
+                {
+                    ItemStack itemStack = mc.thePlayer.inventory.getItem(i);
+                    if (itemStack != null && itemStack.itemID == heldItemStack.itemID && itemStack.getMetadata() == heldItemStack.getMetadata())
+                    {
+                        itemCount += itemStack.stackSize;
+                        stackCount++;
+                    }
+                }
+                renderItem = stackCount >= 1;
+
+                if (renderItem)
+                {
+                    mc.font.render(Tessellator.instance, itemCount, xx + 18, yy + 4).setShadow().setColor(0xFFFFFFFF).call();
+                }
+            }
+
+            if (renderItem)
+            {
+                GL11.glEnable(GL11.GL_DEPTH_TEST);
+                GL11.glDisable(GL11.GL_BLEND);
+                GL11.glEnable(GL12.GL_RESCALE_NORMAL);
+                Lighting.enableInventoryLight();
+                ItemModelDispatcher.getInstance().getDispatch(heldItemStack).renderGui(Tessellator.instance, null, heldItemStack, xx, yy, 1f, 1f, partialTick);
+                Lighting.disable();
+            }
+        }
+    }
+
+    @Override
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
+
+        int xx = x + 3;
+        int yy = y + 3;
+
+        ItemStack heldItemStack = PREVIEW_ITEM;
+        int itemCount = heldItemStack.stackSize;
+
+        mc.font.render(Tessellator.instance, itemCount, xx + 18, yy + 4).setShadow().setColor(0xFFFFFFFF).call();
+
+        GL11.glEnable(GL11.GL_DEPTH_TEST);
+        GL11.glDisable(GL11.GL_BLEND);
+        GL11.glEnable(GL12.GL_RESCALE_NORMAL);
+        Lighting.enableInventoryLight();
+        ItemModelDispatcher.getInstance().getDispatch(heldItemStack).renderGui(Tessellator.instance, null, heldItemStack, xx, yy, 1f, 1f, 0.0f);
+        Lighting.disable();
+    }
+}
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 0264d808b..6f86881da 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
@@ -49,14 +49,14 @@ public class HudComponentHotbar
     }
 
     @Override
-    public boolean isVisible(Minecraft mc) {
+    public boolean isVisible() {
         return mc.gameSettings.immersiveMode.drawHotbar();
     }
 
     @Override
-    public void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
 
         ContainerInventory inv = mc.thePlayer.inventory;
 
@@ -145,9 +145,9 @@ public class HudComponentHotbar
     }
 
     @Override
-    public void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
-        int x = layout.getComponentX(mc, this, xSizeScreen);
-        int y = layout.getComponentY(mc, this, ySizeScreen);
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = layout.getComponentX(this, xSizeScreen);
+        int y = layout.getComponentY(this, ySizeScreen);
 
         // Setup hotbar texture
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentInfoOverlay.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentInfoOverlay.java
new file mode 100644
index 000000000..df83b1868
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentInfoOverlay.java
@@ -0,0 +1,270 @@
+package net.minecraft.client.gui.hud.component;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.hud.HudIngame;
+import net.minecraft.client.gui.hud.component.layout.Layout;
+import net.minecraft.client.gui.hud.component.layout.LayoutAbsolute;
+import net.minecraft.client.gui.hud.component.layout.LayoutSnap;
+import net.minecraft.client.render.tessellator.Tessellator;
+import net.minecraft.core.Global;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.Items;
+import net.minecraft.core.lang.I18n;
+import net.minecraft.core.net.command.TextFormatting;
+import net.minecraft.core.player.gamemode.Gamemode;
+import net.minecraft.core.util.helper.Direction;
+import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.world.biome.Biomes;
+import net.minecraft.core.world.season.Season;
+import net.minecraft.core.world.season.Seasons;
+import net.minecraft.core.world.weather.Weather;
+import net.minecraft.core.world.weather.Weathers;
+
+import static net.minecraft.core.util.helper.Utils.floor10;
+
+public class HudComponentInfoOverlay
+    extends HudComponentMovable
+{
+    public HudComponentInfoOverlay(String key, Layout layout) {
+        super(key, 130, 112, layout);
+    }
+
+    @Override
+    public boolean isVisible() {
+        return mc.gameSettings.immersiveMode.drawOverlays() && !mc.gameSettings.showDebugScreen.value;
+    }
+
+    private void drawString(String string, int x, int y, int argb, boolean rightAligned) {
+        int xOffset = -2;
+        if (rightAligned) {
+            xOffset = mc.font.stringWidth(string) + 2 - this.getXSize();
+        }
+
+        mc.font.render(Tessellator.instance, string, x - xOffset, y).setShadow().setColor(argb).call();
+    }
+
+    @Override
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
+
+        boolean rightAligned = false;
+        if (this.getLayout() instanceof LayoutAbsolute) {
+            final LayoutAbsolute layout = (LayoutAbsolute) this.getLayout();
+            final ComponentAnchor anchor = layout.getAnchor();
+            if (anchor == ComponentAnchor.TOP_RIGHT || anchor == ComponentAnchor.CENTER_RIGHT || anchor == ComponentAnchor.BOTTOM_RIGHT) {
+                rightAligned = true;
+            }
+        } else if (this.getLayout() instanceof LayoutSnap) {
+            final LayoutSnap layout = (LayoutSnap) this.getLayout();
+            final ComponentAnchor anchor = layout.getAnchor();
+            if (anchor == ComponentAnchor.TOP_RIGHT || anchor == ComponentAnchor.CENTER_RIGHT || anchor == ComponentAnchor.BOTTOM_RIGHT) {
+                rightAligned = true;
+            }
+        }
+
+        boolean clock = false;
+        boolean compass = false;
+        boolean rotaryCalendar = false;
+
+        if (mc.thePlayer.getGamemode() == Gamemode.creative)
+        {
+            clock = true;
+            compass = true;
+            rotaryCalendar = true;
+        }
+        else
+        {
+            for (int iinv = 0; iinv < mc.thePlayer.inventory.getContainerSize(); iinv++)
+            {
+                ItemStack item = mc.thePlayer.inventory.getItem(iinv);
+                if (item != null)
+                {
+                    if (item.itemID == Items.TOOL_CLOCK.id)
+                    {
+                        clock = true;
+                    }
+                    if (item.itemID == Items.TOOL_COMPASS.id)
+                    {
+                        compass = true;
+                    }
+                    if (item.itemID == Items.TOOL_CALENDAR.id)
+                    {
+                        rotaryCalendar = true;
+                    }
+                }
+            }
+        }
+
+        int tsp = 2;
+        int line = 0;
+        int lineHeight = 10;
+
+        if (mc.gameSettings.versionInOverlay.value) {
+            int color;
+            if (mc.gameSettings.overlayMode.value == 2)
+            {
+                color = 0xFFFFFF60;
+            } else {
+                color = 0xFFFFFFFF;
+            }
+            drawString("Better than Adventure! " + TextFormatting.WHITE + Global.VERSION, x, y + tsp + lineHeight * line++, color, rightAligned);
+        }
+        if (mc.gameSettings.fpsInOverlay.value)
+        {
+            drawString(mc.debugFPS, x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+        }
+        if (compass)
+        {
+            if (mc.gameSettings.overlayShowCoords.value)
+            {
+                if (mc.gameSettings.overlayMode.value == 0)
+                {
+                    drawString("x: " + floor10(mc.thePlayer.x) + " y: " + floor10(mc.thePlayer.y) + " z: " + floor10(mc.thePlayer.z), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+                }
+                if (mc.gameSettings.overlayMode.value == 1)
+                {
+                    drawString( "x: " + floor10(mc.thePlayer.x), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+                    drawString( "y: " + floor10(mc.thePlayer.y), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+                    drawString( "z: " + floor10(mc.thePlayer.z), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+                }
+                if (mc.gameSettings.overlayMode.value == 2)
+                {
+                    drawString("x: " + floor10(mc.thePlayer.x), x, y + tsp + lineHeight * line++, 0xFFFF8080, rightAligned);
+                    drawString("y: " + floor10(mc.thePlayer.y), x, y + tsp + lineHeight * line++, 0xFF80FF80, rightAligned);
+                    drawString("z: " + floor10(mc.thePlayer.z), x, y + tsp + lineHeight * line++, 0xFF8080FF, rightAligned);
+                }
+            }
+            if (mc.gameSettings.overlayShowDirection.value)
+            {
+                drawString(I18n.getInstance().translateKey(HudIngame.getFacingDirection(mc.thePlayer).getTranslationKey()), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+            }
+        }
+        if (clock)
+        {
+            if (mc.gameSettings.overlayShowTime.value)
+            {
+                if (mc.gameSettings.overlayMode.value == 0)
+                {
+                    drawString(String.format("%s %s %d", HudIngame.getTimeString(mc), I18n.getInstance().translateKey("unit.day"),mc.currentWorld.getWorldTime() / Global.DAY_LENGTH_TICKS), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFFFFFF80 : 0xFFFFFFFF, rightAligned);
+                }
+                if (mc.gameSettings.overlayMode.value > 0)
+                {
+                    drawString(HudIngame.getTimeString(mc), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFFFFFF80 : 0xFFFFFFFF, rightAligned);
+                    drawString(String.format("%s %d", I18n.getInstance().translateKey("unit.day"), mc.currentWorld.getWorldTime() / Global.DAY_LENGTH_TICKS), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFFFFFF80 : 0xFFFFFFFF, rightAligned);
+                }
+            }
+        }
+        if (rotaryCalendar)
+        {
+            if (mc.gameSettings.overlayShowSeason.value)
+            {
+                Season season = mc.currentWorld.getSeasonManager().getCurrentSeason();
+                if (season != null)
+                {
+                    drawString(season.getTranslatedName(), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFF80FFFF : 0xFFFFFFFF, rightAligned);
+                }
+            }
+            if (mc.gameSettings.overlayShowWeather.value)
+            {
+                Weather weather = mc.currentWorld.getCurrentWeather();
+                if (weather != null && weather != mc.currentWorld.worldType.getDefaultWeather())
+                {
+                    drawString(I18n.getInstance().translateKey(weather.getNameLanguageKey()), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFFFF80FF : 0xFFFFFFFF, rightAligned);
+                }
+            }
+            if(mc.gameSettings.overlayShowBiome.value)
+            {
+                int blockX = MathHelper.floor(mc.thePlayer.x);
+                int blockY = MathHelper.floor(mc.thePlayer.y);
+                int blockZ = MathHelper.floor(mc.thePlayer.z);
+                String key = mc.currentWorld.getBlockBiome(blockX, blockY, blockZ).translationKey;
+                drawString(I18n.getInstance().translateKey(key), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+            }
+        }
+    }
+
+    @Override
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
+
+        boolean rightAligned = false;
+        if (layout instanceof LayoutAbsolute) {
+            final LayoutAbsolute l = (LayoutAbsolute) this.getLayout();
+            final ComponentAnchor anchor = l.getAnchor();
+            if (anchor == ComponentAnchor.TOP_RIGHT || anchor == ComponentAnchor.CENTER_RIGHT || anchor == ComponentAnchor.BOTTOM_RIGHT) {
+                rightAligned = true;
+            }
+        } else if (layout instanceof LayoutSnap) {
+            final LayoutSnap l = (LayoutSnap) this.getLayout();
+            final ComponentAnchor anchor = l.getAnchor();
+            if (anchor == ComponentAnchor.TOP_RIGHT || anchor == ComponentAnchor.CENTER_RIGHT || anchor == ComponentAnchor.BOTTOM_RIGHT) {
+                rightAligned = true;
+            }
+        }
+
+        int tsp = 2;
+        int line = 0;
+        int lineHeight = 10;
+
+        { // FPS
+            drawString("60 fps", x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+        }
+        { // Compass
+            {
+                final double px = 372.853;
+                final double py = 64.355;
+                final double pz = 38472.333;
+
+                if (mc.gameSettings.overlayMode.value == 0)
+                {
+                    drawString("x: " + floor10(px) + " y: " + floor10(py) + " z: " + floor10(pz), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+                }
+                if (mc.gameSettings.overlayMode.value == 1)
+                {
+                    drawString("x: " + floor10(px), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+                    drawString("y: " + floor10(py), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+                    drawString("z: " + floor10(pz), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+                }
+                if (mc.gameSettings.overlayMode.value == 2)
+                {
+                    drawString("x: " + floor10(px), x, y + tsp + lineHeight * line++, 0xFFFF8080, rightAligned);
+                    drawString("y: " + floor10(py), x, y + tsp + lineHeight * line++, 0xFF80FF80, rightAligned);
+                    drawString("z: " + floor10(pz), x, y + tsp + lineHeight * line++, 0xFF8080FF, rightAligned);
+                }
+            }
+            {
+                drawString(I18n.getInstance().translateKey(Direction.NORTH.getTranslationKey()), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+            }
+        }
+        {
+            {
+                if (mc.gameSettings.overlayMode.value == 0)
+                {
+                    drawString(String.format("%s %s %d", "8:32", I18n.getInstance().translateKey("unit.day"), 500), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFFFFFF80 : 0xFFFFFFFF, rightAligned);
+                }
+                if (mc.gameSettings.overlayMode.value > 0)
+                {
+                    drawString("8:32", x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFFFFFF80 : 0xFFFFFFFF, rightAligned);
+                    drawString(String.format("%s %d", I18n.getInstance().translateKey("unit.day"), 500), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFFFFFF80 : 0xFFFFFFFF, rightAligned);
+                }
+            }
+        }
+        {
+            {
+                Season season = Seasons.OVERWORLD_SUMMER;
+                drawString(season.getTranslatedName(), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFF80FFFF : 0xFFFFFFFF, rightAligned);
+            }
+            {
+                Weather weather = Weathers.OVERWORLD_FOG;
+                drawString(I18n.getInstance().translateKey(weather.getNameLanguageKey()), x, y + tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xFFFF80FF : 0xFFFFFFFF, rightAligned);
+            }
+            {
+                String key = Biomes.OVERWORLD_GRASSLANDS.translationKey;
+                drawString(I18n.getInstance().translateKey(key), x, y + tsp + lineHeight * line++, 0xFFFFFFFF, rightAligned);
+            }
+        }
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentLog.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentLog.java
index 42d081191..d25b23c9f 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentLog.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentLog.java
@@ -64,17 +64,17 @@ public class HudComponentLog extends HudComponentMovable {
     }
 
     @Override
-    public boolean isEnabled(final @NotNull Minecraft mc) {
+    public boolean isEnabled() {
         return mc.gameSettings.developerMode.value;
     }
 
     @Override
-    public boolean isVisible(final @NotNull Minecraft mc) {
+    public boolean isVisible() {
         return mc.gameSettings.developerMode.value;
     }
 
     @Override
-    public void render(final @NotNull Minecraft mc, final @NotNull HudIngame hud, final int xSizeScreen, final int ySizeScreen, final float partialTick) {
+    public void render(final @NotNull HudIngame hud, final int xSizeScreen, final int ySizeScreen, final float partialTick) {
         if (!this.callbackAdded) {
             LogMessageReceiver.addCallback((message) -> {
                 if (message.getMessage().startsWith("[CHAT] ")) return;
@@ -108,8 +108,8 @@ public class HudComponentLog extends HudComponentMovable {
             callbackAdded = true;
         }
 
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
 
         // Remove dead entries
         for (int i = 0; i < this.logEntries.size(); i++) {
@@ -137,9 +137,9 @@ public class HudComponentLog extends HudComponentMovable {
     }
 
     @Override
-    public void renderPreview(final @NotNull Minecraft mc, final @NotNull Gui gui, final @NotNull Layout layout, final int xSizeScreen, final int ySizeScreen) {
-        int x = layout.getComponentX(mc, this, xSizeScreen);
-        int y = layout.getComponentY(mc, this, ySizeScreen);
+    public void renderPreview(final @NotNull Gui gui, final @NotNull Layout layout, final int xSizeScreen, final int ySizeScreen) {
+        int x = layout.getComponentX(this, xSizeScreen);
+        int y = layout.getComponentY(this, ySizeScreen);
 
         final @NotNull String @NotNull[] lines = {
             this.preludes.get(LogMessage.Level.DEBUG) + "This is a debug line!",
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentOxygenBar.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentOxygenBar.java
index e100af5b8..87e372230 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentOxygenBar.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentOxygenBar.java
@@ -20,14 +20,14 @@ public class HudComponentOxygenBar
     }
 
     @Override
-    public boolean isVisible(Minecraft mc) {
+    public boolean isVisible() {
         return mc.gameSettings.immersiveMode.drawHotbar() && mc.playerController.canHurtPlayer() && !mc.thePlayer.getGamemode().isPlayerInvulnerable() && mc.thePlayer.isUnderLiquid(Material.water);
     }
 
     @Override
-    public void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
@@ -49,9 +49,9 @@ public class HudComponentOxygenBar
     }
 
     @Override
-    public void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
-        int x = layout.getComponentX(mc, this, xSizeScreen);
-        int y = layout.getComponentY(mc, this, ySizeScreen);
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = layout.getComponentX(this, xSizeScreen);
+        int y = layout.getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentRotationLock.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentRotationLock.java
index 654d26164..a34afcf59 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentRotationLock.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentRotationLock.java
@@ -16,18 +16,18 @@ public class HudComponentRotationLock
     }
 
     @Override
-    public boolean isVisible(Minecraft mc) {
+    public boolean isVisible() {
         return mc.thePlayer.rotationLock != null && mc.thePlayer.rotationLock != Direction.NONE && mc.gameSettings.immersiveMode.drawOverlays();
     }
 
     @Override
-    public void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
         GL11.glDisable(GL11.GL_BLEND);
 
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
 
         String prefix = mc.gameSettings.rotationOverlayMode.value % 2 == 1 ? "arrow" : "cube";
 
@@ -86,9 +86,9 @@ public class HudComponentRotationLock
     }
 
     @Override
-    public void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
-        int x = layout.getComponentX(mc, this, xSizeScreen);
-        int y = layout.getComponentY(mc, this, ySizeScreen);
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+        int x = layout.getComponentX(this, xSizeScreen);
+        int y = layout.getComponentY(this, ySizeScreen);
 
         // Setup rendering
         GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // Reset color
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HubComponentSubtitles.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentSubtitles.java
similarity index 81%
rename from game/client/src/main/java/net/minecraft/client/gui/hud/component/HubComponentSubtitles.java
rename to game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentSubtitles.java
index be8a27ca2..b9dbb41fd 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HubComponentSubtitles.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentSubtitles.java
@@ -17,36 +17,41 @@ import static org.lwjgl.opengl.GL11.GL_BLEND;
 import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
 import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
 
-public class HubComponentSubtitles extends HudComponentMovable {
-    public HubComponentSubtitles(String key, int xSize, int ySize, Layout layout) {
+public class HudComponentSubtitles extends HudComponentMovable {
+    public HudComponentSubtitles(String key, int xSize, int ySize, Layout layout) {
         super(key, xSize, ySize, layout);
     }
 
     @Override
-    public boolean isVisible(Minecraft mc) {
+    public boolean isVisible() {
         return mc.gameSettings.immersiveMode.drawOverlays() && mc.gameSettings.subtitles.value;
     }
 
     @Override
-    public void render(Minecraft mc, HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
-        drawChat(mc, hud, new ArrayList<>(mc.subtitleTracker.entryMap.values()), xSizeScreen, ySizeScreen, partialTick);
+    public boolean isEnabled() {
+        return mc.gameSettings.subtitles.value;
     }
 
     @Override
-    public void renderPreview(Minecraft mc, Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
+    public void render(HudIngame hud, int xSizeScreen, int ySizeScreen, float partialTick) {
+        drawChat(hud, new ArrayList<>(mc.subtitleTracker.entryMap.values()), xSizeScreen, ySizeScreen, partialTick);
+    }
+
+    @Override
+    public void renderPreview(Gui gui, Layout layout, int xSizeScreen, int ySizeScreen) {
         List<SubtitleTracker.SubtitleEntry> example = new ArrayList<>();
         example.add(new SubtitleTracker.SubtitleEntry(SoundRepository.SOUNDS.getSoundEvent("mob.wolf.whine"), 0, 0, 0));
         example.add(new SubtitleTracker.SubtitleEntry(SoundRepository.SOUNDS.getSoundEvent("random.drr"), 0, 0, 0));
         example.add(new SubtitleTracker.SubtitleEntry(SoundRepository.SOUNDS.getSoundEvent("random.bow"), 0, 0, 0));
-        drawChat(mc, gui, example, xSizeScreen, ySizeScreen, 0);
+        drawChat(gui, example, xSizeScreen, ySizeScreen, 0);
     }
 
-    public void drawChat(Minecraft mc, Gui gui, List<SubtitleTracker.SubtitleEntry> subtitleEntries, int xSizeScreen, int ySizeScreen, float partialTick)
+    public void drawChat(Gui gui, List<SubtitleTracker.SubtitleEntry> subtitleEntries, int xSizeScreen, int ySizeScreen, float partialTick)
     {
-        int x = this.getLayout().getComponentX(mc, this, xSizeScreen);
-        int y = this.getLayout().getComponentY(mc, this, ySizeScreen);
-        int width = getXSize(mc);
-        int height = getYSize(mc);
+        int x = this.getLayout().getComponentX(this, xSizeScreen);
+        int y = this.getLayout().getComponentY(this, ySizeScreen);
+        int width = getXSize();
+        int height = getYSize();
         int xRight = x + width;
         int yBottom = y + height;
         int messagesToShow = height/10;
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponents.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponents.java
index 4573a52df..8dc127e0c 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponents.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponents.java
@@ -65,6 +65,9 @@ public class HudComponents {
     public static final HudComponent OXYGEN_BAR = register(new HudComponentOxygenBar("oxygen_bar", new LayoutSnap(HEALTH_BAR, ComponentAnchor.TOP_LEFT, ComponentAnchor.BOTTOM_LEFT)));
     public static final HudComponent FIRE_BAR = register(new HudComponentFireBar("fire_bar", new LayoutSnap(OXYGEN_BAR, ComponentAnchor.TOP_LEFT, ComponentAnchor.BOTTOM_LEFT)));
     public static final HudComponent ROTATION_LOCK = register(new HudComponentRotationLock("rotation_lock", new LayoutSnap(CROSSHAIR, ComponentAnchor.CENTER_RIGHT, ComponentAnchor.CENTER_LEFT)));
-    public static final HudComponent SUBTITLES = register(new HubComponentSubtitles("subtitles_box", 100, 50, new LayoutAbsolute(1.0f, 0.75f, ComponentAnchor.BOTTOM_RIGHT)));
+    public static final HudComponent SUBTITLES = register(new HudComponentSubtitles("subtitles_box", 100, 50, new LayoutAbsolute(1.0f, 0.75f, ComponentAnchor.BOTTOM_RIGHT)));
     public static final HudComponent LOG = register(new HudComponentLog("log", new LayoutAbsolute(1.0f, 0.0f, ComponentAnchor.TOP_RIGHT)));
+    public static final HudComponent ARMOR_DURABILITY = register(new HudComponentArmorDurability("armor_durability", new LayoutAbsolute(0.0f, 1.0f, ComponentAnchor.BOTTOM_LEFT)));
+    public static final HudComponent HELD_ITEM_COUNT = register(new HudComponentHeldItemCount("held_item_count", new LayoutSnap(HOTBAR, ComponentAnchor.CENTER_RIGHT, ComponentAnchor.CENTER_LEFT)));
+    public static final HudComponent INFO_OVERLAY = register(new HudComponentInfoOverlay("info_overlay", new LayoutAbsolute(0.0f, 0.0f, ComponentAnchor.TOP_LEFT)));
 }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/Layout.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/Layout.java
index d040060ee..9fe05c4e7 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/Layout.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/Layout.java
@@ -4,8 +4,10 @@ import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.hud.component.HudComponent;
 
 public abstract class Layout {
-    public abstract int getComponentX(Minecraft mc, HudComponent component, int xSizeScreen);
-    public abstract int getComponentY(Minecraft mc, HudComponent component, int ySizeScreen);
+    protected static final Minecraft mc = Minecraft.getMinecraft();
+
+    public abstract int getComponentX(HudComponent component, int xSizeScreen);
+    public abstract int getComponentY(HudComponent component, int ySizeScreen);
     public abstract Layout copy();
     public abstract String toSettingsString();
     public abstract void fromSettingsString(String settings);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/LayoutAbsolute.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/LayoutAbsolute.java
index 4ab7094a6..95b769d4a 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/LayoutAbsolute.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/LayoutAbsolute.java
@@ -41,13 +41,13 @@ public class LayoutAbsolute
     }
 
     @Override
-    public int getComponentX(Minecraft mc, HudComponent component, int xSizeScreen) {
+    public int getComponentX(HudComponent component, int xSizeScreen) {
         int sp = (int) (mc.gameSettings.screenPadding.value * mc.resolution.getScaledHeightScreenCoords() / 8);
         return (int) (xPosition * xSizeScreen) - component.getAnchorX(anchor) + sp;
     }
 
     @Override
-    public int getComponentY(Minecraft mc, HudComponent component, int ySizeScreen) {
+    public int getComponentY(HudComponent component, int ySizeScreen) {
         int sp = (int) (mc.gameSettings.screenPadding.value * mc.resolution.getScaledHeightScreenCoords() / 8);
         return (int) (yPosition * ySizeScreen) - component.getAnchorY(anchor) + sp;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/LayoutSnap.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/LayoutSnap.java
index d42c7cb01..034cdcbcf 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/LayoutSnap.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/layout/LayoutSnap.java
@@ -42,21 +42,21 @@ public class LayoutSnap
     }
 
     @Override
-    public int getComponentX(Minecraft mc, HudComponent component, int xSizeScreen) {
+    public int getComponentX(HudComponent component, int xSizeScreen) {
         if (parent == null) {
             int sp = (int) (mc.gameSettings.screenPadding.value * mc.resolution.getScaledHeightScreenCoords() / 8);
             return (int) (xSizeScreen * parentAnchor.xPosition) - component.getAnchorX(anchor) + sp;
         }
-        return parent.getLayout().getComponentX(mc, parent, xSizeScreen) - component.getAnchorX(anchor) + parent.getAnchorX(parentAnchor);
+        return parent.getLayout().getComponentX(parent, xSizeScreen) - component.getAnchorX(anchor) + parent.getAnchorX(parentAnchor);
     }
 
     @Override
-    public int getComponentY(Minecraft mc, HudComponent component, int ySizeScreen) {
+    public int getComponentY( HudComponent component, int ySizeScreen) {
         if (parent == null) {
             int sp = (int) (mc.gameSettings.screenPadding.value * mc.resolution.getScaledHeightScreenCoords() / 8);
             return (int) (ySizeScreen * parentAnchor.yPosition) - component.getAnchorY(anchor) + sp;
         }
-        return parent.getLayout().getComponentY(mc, parent, ySizeScreen) - component.getAnchorY(anchor) + parent.getAnchorY(parentAnchor);
+        return parent.getLayout().getComponentY(parent, ySizeScreen) - component.getAnchorY(anchor) + parent.getAnchorY(parentAnchor);
     }
 
     @Override
diff --git a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/ViewerCategoryEntity.java b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/ViewerCategoryEntity.java
index 6054a0186..865e141e0 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/ViewerCategoryEntity.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/ViewerCategoryEntity.java
@@ -45,7 +45,7 @@ public class ViewerCategoryEntity extends ModelViewerCategory<ModelViewerCategor
         createEntries();
     }
     public void createEntries(){
-        for (NamespaceID id : EntityDispatcher.idToClassMap.keySet()){
+        for (NamespaceID id : EntityDispatcher.getInstance().idToEntryMap.keySet()){
             try{
                 addEntry(new EntityEntryDispatcher(id));
             } catch (Exception ignore){}
@@ -84,7 +84,7 @@ public class ViewerCategoryEntity extends ModelViewerCategory<ModelViewerCategor
     public String getCurrentDisplayInfo(I18n translator) {
         String title = "None";
         if (displayEntity != null){
-            title = EntityDispatcher.idForClass(displayEntity.getClass()).toString();
+            title = String.valueOf(displayEntity.getDispatcherId());
         }
         return translator.translateKeyAndFormat("model.category.entity.info", title);
     }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/block/BlockEntryMobSpawner.java b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/block/BlockEntryMobSpawner.java
index c9c7209e7..5ddb23ef6 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/block/BlockEntryMobSpawner.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/block/BlockEntryMobSpawner.java
@@ -39,7 +39,7 @@ public class BlockEntryMobSpawner extends BlockEntry{
     @Override
     public List<ButtonElement> getEntryButtons(Minecraft mc, Screen parentScreen, World world, int meta) {
         List<String> ids = new ArrayList<>();
-        for (NamespaceID id : EntityDispatcher.idToClassMap.keySet()) {
+        for (NamespaceID id : EntityDispatcher.getInstance().idToEntryMap.keySet()) {
             ids.add(id.toString());
         }
         ListSelectorButtonElement<String> dispatcherIds = new ListSelectorButtonElement<String>(-1, -120, 0, 120, 20, I18n.getInstance().translateKey("model.category.block.spawner.button.id"), ids, "minecraft:pig") {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/entity/EntityEntryDispatcher.java b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/entity/EntityEntryDispatcher.java
index 8449297ee..5feae79f4 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/entity/EntityEntryDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/entries/entity/EntityEntryDispatcher.java
@@ -27,7 +27,7 @@ public class EntityEntryDispatcher extends EntityEntry<Entity> {
 
     @Override
     public Entity getEntityInstance(Minecraft mc, World world) {
-        return EntityDispatcher.createEntityInWorld(id, mc.currentWorld);
+        return EntityDispatcher.getInstance().createEntityInWorld(id, mc.currentWorld);
     }
 
     @Override
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 f599adb45..7e630527c 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
@@ -67,7 +67,6 @@ import net.minecraft.core.world.chunk.ChunkCoordinates;
 import net.minecraft.core.world.data.SynchedEntityData;
 import net.minecraft.core.world.save.LevelData;
 import net.minecraft.core.world.saveddata.SavedDataStorage;
-import net.minecraft.core.world.saveddata.maps.ItemMapSavedData;
 import net.minecraft.core.world.weather.Weather;
 import net.minecraft.core.world.weather.Weathers;
 import org.slf4j.Logger;
@@ -409,7 +408,7 @@ public class PacketHandlerClient extends PacketHandler
             if (tileentity instanceof TileEntityMobSpawner)
             {
                 TileEntityMobSpawner tileEntityMobSpawner = (TileEntityMobSpawner) tileentity;
-                NamespaceID namespaceID = EntityDispatcher.idForClass(setMobSpawnerPacket.entityClass);
+                NamespaceID namespaceID = setMobSpawnerPacket.dispatcherEntry != null ? setMobSpawnerPacket.dispatcherEntry.namespaceID : null;
                 tileEntityMobSpawner.setMobId(namespaceID == null ? "none" : namespaceID.toString());
 
                 tileEntityMobSpawner.setChanged();
@@ -750,7 +749,7 @@ public class PacketHandlerClient extends PacketHandler
         double d2 = (double) addMobPacket.z / 32D;
         float f = (float) (addMobPacket.yaw * 360) / 256F;
         float f1 = (float) (addMobPacket.pitch * 360) / 256F;
-        Mob mob = (Mob) EntityDispatcher.createEntity(addMobPacket.type, mc.currentWorld);
+        Mob mob = (Mob) EntityDispatcher.getInstance().createEntity(addMobPacket.type, mc.currentWorld);
         mob.serverPosX = addMobPacket.x;
         mob.serverPosY = addMobPacket.y;
         mob.serverPosZ = addMobPacket.z;
@@ -1229,7 +1228,7 @@ public class PacketHandlerClient extends PacketHandler
         if (syncIDsPacket.destinationId == PacketSyncIDs.DESTINATION_SOUND_IDS) {
             SoundTypes.setSoundIds(syncIDsPacket.mapping);
         } else if (syncIDsPacket.destinationId == PacketSyncIDs.DESTINATION_ENTITY_IDS) {
-            EntityDispatcher.setNumericIds(syncIDsPacket.mapping);
+            EntityDispatcher.getInstance().setNumericIds(syncIDsPacket.mapping);
         }
     }
     
diff --git a/game/client/src/main/java/net/minecraft/client/render/OpenGLHelper.java b/game/client/src/main/java/net/minecraft/client/render/OpenGLHelper.java
index 582835f6d..fd0a4bd93 100644
--- a/game/client/src/main/java/net/minecraft/client/render/OpenGLHelper.java
+++ b/game/client/src/main/java/net/minecraft/client/render/OpenGLHelper.java
@@ -88,11 +88,11 @@ public class OpenGLHelper {
 		return "Unknown Error";
 	}
 
-	public static void glColor1i(int rgb) {
-		float r = (float)(rgb >> 16 & 0xff) / 255F;
-		float g = (float)(rgb >> 8 & 0xff) / 255F;
-		float b = (float)(rgb & 0xff) / 255F;
-		glColor3f(r, g, b);
+	public static void glColor1i(int argb) {
+		float a = (float)(argb >> 24 & 0xff) / 255F;
+		float r = (float)(argb >> 16 & 0xff) / 255F;
+		float g = (float)(argb >> 8 & 0xff) / 255F;
+		float b = (float)(argb & 0xff) / 255F;
+		glColor4f(r, g, b, a);
 	}
-	
 }
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 b3ac76ce1..e6acd9aa6 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
@@ -82,6 +82,9 @@ public final class BlockModelDispatcher
         addDispatch(new BlockModelStandard<>(Blocks.COBBLE_LIMESTONE).setAllTextures(BLOCK_TEXTURES, "minecraft:block/cobbled_limestone"));
         addDispatch(new BlockModelStandard<>(Blocks.COBBLE_GRANITE).setAllTextures(BLOCK_TEXTURES, "minecraft:block/cobbled_granite"));
         addDispatch(new BlockModelStandard<>(Blocks.COBBLE_PERMAFROST).setAllTextures(BLOCK_TEXTURES, "minecraft:block/cobbled_permafrost"));
+        addDispatch(new BlockModelStandard<>(Blocks.COBBLE_BASALT_MOSSY).setAllTextures(BLOCK_TEXTURES, "minecraft:block/cobbled_basalt_mossy"));
+        addDispatch(new BlockModelStandard<>(Blocks.COBBLE_LIMESTONE_MOSSY).setAllTextures(BLOCK_TEXTURES, "minecraft:block/cobbled_limestone_mossy"));
+        addDispatch(new BlockModelStandard<>(Blocks.COBBLE_GRANITE_MOSSY).setAllTextures(BLOCK_TEXTURES, "minecraft:block/cobbled_granite_mossy"));
 
         addDispatch(new BlockModelStandard<>(Blocks.STONE_POLISHED)
             .setTex(BLOCK_TEXTURES, "minecraft:block/polished_stone_top", S_TB)
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRenderer.java b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRenderer.java
index f9337ba3f..b8d393c62 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRenderer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRenderer.java
@@ -73,7 +73,8 @@ public abstract class EntityRenderer<T extends Entity>
         if (model != null) {
             return model;
         }
-        final NamespaceID id = EntityDispatcher.idForClass(this.appliedClass);
+        EntityDispatcher.EntityDispatcherEntry entry = EntityDispatcher.getInstance().entryForClass(this.appliedClass);
+        final NamespaceID id = entry != null ? entry.namespaceID : null;
         if (id != null) {
             model = EntityGeometryMojangData.Cache.getModelForEntity(id, localId);
         } else {
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMobSpawner.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMobSpawner.java
index 51560ab2d..c3b23b9bf 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMobSpawner.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererMobSpawner.java
@@ -34,7 +34,7 @@ public class TileEntityRendererMobSpawner extends TileEntityRenderer<TileEntityM
         Entity entity = entityHashMap.get(mobSpawner.getMobId());
         if(entity == null)
         {
-            entity = EntityDispatcher.createEntityInWorld(mobSpawner.getMobId(), WorldClient.EMPTY_WORLD);
+            entity = EntityDispatcher.getInstance().createEntityInWorld(mobSpawner.getMobId(), WorldClient.EMPTY_WORLD);
             entityHashMap.put(mobSpawner.getMobId(), entity);
         }
         if(entity != null)
diff --git a/game/client/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java b/game/client/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
index 0b1aa4873..a090dbae0 100644
--- a/game/client/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
+++ b/game/client/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
@@ -25,7 +25,9 @@ import com.mojang.logging.LogUtils;
 import net.minecraft.client.render.OpenGLHelper;
 import net.minecraft.client.util.debug.GLFWLoggerCallback;
 import net.minecraft.client.util.debug.OpenGLLoggerCallback;
+import net.minecraft.core.BuildChannel;
 import net.minecraft.core.Global;
+import net.minecraft.core.Version;
 import net.minecraft.core.util.helper.Color;
 import org.jetbrains.annotations.Nullable;
 import org.lwjgl.BufferUtils;
@@ -82,8 +84,12 @@ public class GameWindowGLFW implements GameWindow {
     public void init(Minecraft minecraft, int width, int height) {
         mc = minecraft;
 
+        boolean debug = (mc.gameSettings.developerMode.value || Version.BUILD_CHANNEL == BuildChannel.DEVELOPMENT);
+
         LOGGER.info("Initializing GLFW...");
-        GLFWErrorCallback.create(new GLFWLoggerCallback()).set();
+        if (debug) {
+            GLFWErrorCallback.create(new GLFWLoggerCallback()).set();
+        }
         if (!glfwInit()) {
             throw new RuntimeException("GLFW initialization failed!");
         }
@@ -156,7 +162,7 @@ public class GameWindowGLFW implements GameWindow {
         // OpenGL setup
         glfwMakeContextCurrent(window);
         GLCapabilities capabilities = GL.createCapabilities();
-        if (capabilities.OpenGL43) {
+        if (capabilities.OpenGL43 && debug) {
             GL11.glEnable(GL43.GL_DEBUG_OUTPUT);
             GL43.glDebugMessageCallback(new OpenGLLoggerCallback(), NULL);
             LOGGER.info("GL4.3 supported, enabling GL Error logging");
diff --git a/game/client/src/main/java/net/minecraft/client/util/debug/OpenGLLoggerCallback.java b/game/client/src/main/java/net/minecraft/client/util/debug/OpenGLLoggerCallback.java
index 30c2bea28..c40b2d017 100644
--- a/game/client/src/main/java/net/minecraft/client/util/debug/OpenGLLoggerCallback.java
+++ b/game/client/src/main/java/net/minecraft/client/util/debug/OpenGLLoggerCallback.java
@@ -1,12 +1,10 @@
 package net.minecraft.client.util.debug;
 
-import com.mojang.logging.LogUtils;
 import org.jetbrains.annotations.NotNull;
 import org.lwjgl.opengl.ARBFramebufferObject;
 import org.lwjgl.opengl.GL11;
 import org.lwjgl.opengl.GL43;
 import org.lwjgl.opengl.GLDebugMessageCallbackI;
-import org.lwjgl.opengl.OpenGLException;
 import org.lwjgl.system.MemoryUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
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 07dcacda2..1b491f4a0 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
@@ -5,6 +5,7 @@ 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.util.phys.Vec3;
 import net.minecraft.core.world.AuroraProvider;
@@ -51,6 +52,12 @@ public class WorldClient extends World {
         biomeProvider = this.worldType.createBiomeProvider(this);
     }
 
+    @Override
+    public void spawnPlayerWithLoadedChunks(Player player, boolean respawning) {
+        super.spawnPlayerWithLoadedChunks(player, respawning);
+        getLevelData().setLastPlayerUUID(player.uuid);
+    }
+
     public Vec3 getDimensionColor(ICamera camera, float partialTick)
     {
         float f1 = getCelestialAngle(partialTick);
diff --git a/game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientMP.java b/game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientMP.java
index ad4411534..b34d11805 100644
--- a/game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientMP.java
+++ b/game/client/src/main/java/net/minecraft/client/world/save/SaveHandlerClientMP.java
@@ -1,16 +1,19 @@
 package net.minecraft.client.world.save;
 
-import net.minecraft.core.entity.player.Player;
 import com.mojang.nbt.tags.CompoundTag;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.world.chunk.IChunkLoader;
 import net.minecraft.core.world.Dimension;
 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 org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 import java.util.List;
+import java.util.UUID;
 
 public class SaveHandlerClientMP
 		implements LevelStorage
@@ -57,7 +60,7 @@ public class SaveHandlerClientMP
 	}
 
 	@Override
-	public void saveLevelDataAndPlayerData(LevelData levelData, List<Player> playerList)
+	public void saveLevelDataAndPlayerData(@NotNull LevelData levelData, @NotNull List<Player> playerList)
 	{
 		
 	}
@@ -98,4 +101,18 @@ public class SaveHandlerClientMP
 		return null;
 	}
 
+	@Override
+	public @Nullable CompoundTag getPlayerData(@Nullable String username, @NotNull UUID uuid) {
+		return null;
+	}
+
+	@Override
+	public void save(@NotNull Player player) {
+
+	}
+
+	@Override
+	public void load(@NotNull Player player) {
+
+	}
 }
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/SaveHandlerClientSP.java
index 05a1d79ab..ccccb4233 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/SaveHandlerClientSP.java
@@ -1,5 +1,6 @@
 package net.minecraft.client.world.save;
 
+import com.mojang.nbt.tags.CompoundTag;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.world.chunk.ChunkLoaderRegionAsync;
 import net.minecraft.core.world.chunk.IChunkLoader;
@@ -7,6 +8,7 @@ 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 org.jetbrains.annotations.NotNull;
 
 import java.io.File;
 import java.util.List;
@@ -15,11 +17,24 @@ public class SaveHandlerClientSP
         extends SaveHandlerBase
 {
 
-    public SaveHandlerClientSP(ISaveFormat saveFormat, File savesDir, String worldDirName, boolean isMultiplayer)
+    public SaveHandlerClientSP(ISaveFormat saveFormat, File savesDir, String worldDirName)
     {
-        super(saveFormat, savesDir, worldDirName, isMultiplayer);
+        super(saveFormat, savesDir, worldDirName);
     }
-    
+
+    @Override
+    public void load(@NotNull Player player) {
+        LevelData data = getLevelData();
+        if (data != null && data.getLastPlayerUUID() != null) {
+            CompoundTag lastPlayerTag = getPlayerData(null, data.getLastPlayerUUID());
+            if (lastPlayerTag != null) {
+                player.load(lastPlayerTag);
+                return;
+            }
+        }
+        super.load(player);
+    }
+
     @Override
     public IChunkLoader getChunkLoader(Dimension dimension)
     {
@@ -30,7 +45,7 @@ public class SaveHandlerClientSP
     }
 
     @Override
-    public void saveLevelDataAndPlayerData(LevelData levelData, List<Player> playerList)
+    public void saveLevelDataAndPlayerData(@NotNull LevelData levelData, @NotNull List<Player> playerList)
     {
         levelData.setSaveVersion(saveFormat.getSaveVersion());
         super.saveLevelDataAndPlayerData(levelData, playerList);
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_basalt_mossy.png b/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_basalt_mossy.png
new file mode 100644
index 000000000..3c9d02479
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_basalt_mossy.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_granite_mossy.png b/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_granite_mossy.png
new file mode 100644
index 000000000..804666571
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_granite_mossy.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_limestone_mossy.png b/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_limestone_mossy.png
new file mode 100644
index 000000000..acb950c01
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/cobbled_limestone_mossy.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_credits.png b/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_credits.png
new file mode 100644
index 000000000..e18552a89
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_credits.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_discord.png b/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_discord.png
new file mode 100644
index 000000000..74052dfb3
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_discord.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_website.png b/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_website.png
new file mode 100644
index 000000000..b0c7fc626
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_website.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_youtube.png b/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_youtube.png
new file mode 100644
index 000000000..8d79786ed
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/gui/sprites/screen/main_menu/icon_youtube.png differ
diff --git a/game/core/src/main/java/net/minecraft/core/achievement/stat/StatList.java b/game/core/src/main/java/net/minecraft/core/achievement/stat/StatList.java
index 12e308c1e..37b867c95 100644
--- a/game/core/src/main/java/net/minecraft/core/achievement/stat/StatList.java
+++ b/game/core/src/main/java/net/minecraft/core/achievement/stat/StatList.java
@@ -144,9 +144,9 @@ public abstract class StatList
         }
 
         mobEncounterStats = new HashMap<>();
-        for (Map.Entry<NamespaceID, Class<? extends Entity>> entry : EntityDispatcher.idToClassMap.entrySet())
+        for (EntityDispatcher.EntityDispatcherEntry<?> entry : EntityDispatcher.getInstance().entries)
         {
-            mobEncounterStats.put(entry.getKey(), new StatMob(NamespaceID.fromPool("minecraft", "encounter_" + entry.getKey().toString().replace(":", "_")), "stat.encounterMob", entry.getKey()).registerStat());
+            mobEncounterStats.put(entry.namespaceID, new StatMob(NamespaceID.fromPool("minecraft", "encounter_" + entry.namespaceID.toString().replace(":", "_")), "stat.encounterMob", entry.namespaceID).registerStat());
         }
     }
 
@@ -252,7 +252,8 @@ public abstract class StatList
             if (OFFSET_MOB <= id && id < OFFSET_MOB + OFFSET_OFFSET) {
                 @SuppressWarnings("deprecation") // This is converting old stats that used the legacy ids numbers
                 Class<? extends Entity> c = EntityDispatcher.Legacy.getClassFromID(id - OFFSET_MOB);
-                NamespaceID entityID = EntityDispatcher.idForClass(c);
+                EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(c);
+                NamespaceID entityID = entry != null ? entry.namespaceID : null;
                 return mobEncounterStats.get(entityID);
             }
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMoss.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMoss.java
index 5cb8453d7..f85515d06 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMoss.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMoss.java
@@ -94,6 +94,9 @@ public class BlockLogicMoss extends BlockLogic implements IBonemealable {
 		stoneToMossMap.put(Blocks.LIMESTONE, Blocks.MOSS_LIMESTONE);
 		stoneToMossMap.put(Blocks.BASALT, Blocks.MOSS_BASALT);
 		stoneToMossMap.put(Blocks.COBBLE_STONE, Blocks.COBBLE_STONE_MOSSY);
+		stoneToMossMap.put(Blocks.COBBLE_BASALT, Blocks.COBBLE_BASALT_MOSSY);
+		stoneToMossMap.put(Blocks.COBBLE_LIMESTONE, Blocks.COBBLE_LIMESTONE_MOSSY);
+		stoneToMossMap.put(Blocks.COBBLE_GRANITE, Blocks.COBBLE_GRANITE_MOSSY);
 		stoneToMossMap.put(Blocks.BRICK_STONE_POLISHED, Blocks.BRICK_STONE_POLISHED_MOSSY);
 		stoneToMossMap.put(Blocks.LOG_OAK, Blocks.LOG_OAK_MOSSY);
 		
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicWireRedstone.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicWireRedstone.java
index bbff8d921..a88ced793 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicWireRedstone.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicWireRedstone.java
@@ -20,6 +20,10 @@ import java.util.Random;
 import java.util.Set;
 
 public class BlockLogicWireRedstone extends BlockLogic {
+    public static final int SIGNAL_MIN = 0;
+    public static final int SIGNAL_MAX = 15;
+    public static final int SIGNAL_STEP = 1;
+
     public static final int MASK_DIRECTION = 0xF0;
     public static final int MASK_POWER = 0x0F;
     private boolean shouldSignal = true;
diff --git a/game/core/src/main/java/net/minecraft/core/block/Blocks.java b/game/core/src/main/java/net/minecraft/core/block/Blocks.java
index ce453c96d..09d260d1b 100644
--- a/game/core/src/main/java/net/minecraft/core/block/Blocks.java
+++ b/game/core/src/main/java/net/minecraft/core/block/Blocks.java
@@ -82,28 +82,15 @@ public final class Blocks {
     public static final Block<?> COBBLE_PERMAFROST = register("cobble.permafrost", "minecraft:block/cobble_permafrost", 15, (b) -> new BlockLogicCobble(b, Material.permafrost, () -> Blocks.GRAVEL))
         .withSound(BlockSounds.PERMAFROST).withHardness(2.0F).withBlastResistance(10F)
         .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-
-    public static final Block<?> STONE_POLISHED = register("stone.polished", "minecraft:block/stone_polished", 860, (b) -> new BlockLogic(b, Material.stone))
-        .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final Block<?> GRANITE_POLISHED = register("granite.polished", "minecraft:block/granite_polished", 861, (b) -> new BlockLogic(b, Material.granite))
-        .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final Block<?> LIMESTONE_POLISHED = register("limestone.polished", "minecraft:block/limestone_polished", 862, (b) -> new BlockLogic(b, Material.limestone))
-        .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final Block<?> BASALT_POLISHED = register("basalt.polished", "minecraft:block/basalt_polished", 863, (b) -> new BlockLogic(b, Material.basalt))
-        .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final Block<?> SLATE_POLISHED = register("slate.polished", "minecraft:block/slate_polished", 864, (b) -> new BlockLogic(b, Material.slate))
-        .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final Block<?> PERMAFROST_POLISHED = register("permafrost.polished", "minecraft:block/permafrost_polished", 865, (b) -> new BlockLogic(b, Material.permafrost))
-        .withSound(BlockSounds.PERMAFROST).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
-    public static final Block<?> NETHERRACK_POLISHED = register("netherrack.polished", "minecraft:block/netherrack_polished", 866, (b) -> new BlockLogic(b, Material.netherrack))
-        .withSound(BlockSounds.STONE).withHardness(0.4F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final Block<?> COBBLE_BASALT_MOSSY = register("cobble.basalt.mossy", "minecraft:block/cobble_basalt_mossy", 16, (b) -> new BlockLogicCobble(b, Material.stone, () -> Blocks.GRAVEL))
+        .withSound(BlockSounds.STONE).withHardness(2.0F).withBlastResistance(10F)
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.GROWS_FLOWERS);
+    public static final Block<?> COBBLE_LIMESTONE_MOSSY = register("cobble.limestone.mossy", "minecraft:block/cobble_limestone_mossy", 17, (b) -> new BlockLogicCobble(b, Material.stone, () -> Blocks.GRAVEL))
+        .withSound(BlockSounds.STONE).withHardness(2.0F).withBlastResistance(10F)
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.GROWS_FLOWERS);
+    public static final Block<?> COBBLE_GRANITE_MOSSY = register("cobble.granite.mossy", "minecraft:block/cobble_granite_mossy", 18, (b) -> new BlockLogicCobble(b, Material.stone, () -> Blocks.GRAVEL))
+        .withSound(BlockSounds.STONE).withHardness(2.0F).withBlastResistance(10F)
+        .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.GROWS_FLOWERS);
 
     public static final Block<?> PILLAR_MARBLE = register("pillar.marble", "minecraft:block/pillar_marble", 20, (b) -> new BlockLogicAxisAligned(b, Material.marble))
         .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
@@ -1156,6 +1143,28 @@ public final class Blocks {
         .setBlockItem(ItemBlockLamp::new)
         .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_PICKAXE);
 
+    public static final Block<?> STONE_POLISHED = register("stone.polished", "minecraft:block/stone_polished", 860, (b) -> new BlockLogic(b, Material.stone))
+            .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
+            .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final Block<?> GRANITE_POLISHED = register("granite.polished", "minecraft:block/granite_polished", 861, (b) -> new BlockLogic(b, Material.granite))
+            .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
+            .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final Block<?> LIMESTONE_POLISHED = register("limestone.polished", "minecraft:block/limestone_polished", 862, (b) -> new BlockLogic(b, Material.limestone))
+            .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
+            .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final Block<?> BASALT_POLISHED = register("basalt.polished", "minecraft:block/basalt_polished", 863, (b) -> new BlockLogic(b, Material.basalt))
+            .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
+            .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final Block<?> SLATE_POLISHED = register("slate.polished", "minecraft:block/slate_polished", 864, (b) -> new BlockLogic(b, Material.slate))
+            .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
+            .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final Block<?> PERMAFROST_POLISHED = register("permafrost.polished", "minecraft:block/permafrost_polished", 865, (b) -> new BlockLogic(b, Material.permafrost))
+            .withSound(BlockSounds.PERMAFROST).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
+            .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+    public static final Block<?> NETHERRACK_POLISHED = register("netherrack.polished", "minecraft:block/netherrack_polished", 866, (b) -> new BlockLogic(b, Material.netherrack))
+            .withSound(BlockSounds.STONE).withHardness(0.4F).withDisabledNeighborNotifyOnMetadataChange()
+            .withTags(BlockTags.MINEABLE_BY_PICKAXE);
+
     public static final Block<?> LANTERN_FIREFLY_GREEN = register("lantern.firefly.green", "minecraft:block/lantern_firefly_green", 870,
         (b) -> new BlockLogicLanternFirefly(b, MobFireflyCluster.FireflyColor.GREEN, () -> Items.LANTERN_FIREFLY_GREEN))
         .withSound(BlockSounds.GLASS).withHardness(0.1F).withLightEmission(0.9375F).withDisabledNeighborNotifyOnMetadataChange().setStatParent(() -> Items.LANTERN_FIREFLY_GREEN)
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMobSpawner.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMobSpawner.java
index 8531e895a..09fbeb354 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMobSpawner.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityMobSpawner.java
@@ -41,7 +41,8 @@ public class TileEntityMobSpawner extends TileEntity
     	}
         try {
             if (EntityDispatcher.Legacy.isLegacyKey(mobId)) {
-                this.mobId = EntityDispatcher.idForClass(EntityDispatcher.Legacy.getClassFromEncodeID(mobId)).toString();
+                EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(EntityDispatcher.Legacy.getClassFromEncodeID(mobId));
+                this.mobId = entry != null ? entry.stringID : null;
             }
         } catch (Exception e) {
             LOGGER.error("Failed to convert legacy mobspawner id!", e);
@@ -96,7 +97,7 @@ public class TileEntityMobSpawner extends TileEntity
             byte byte0 = 4;
             for(int i = 0; i < byte0; i++)
             {
-                Mob mob = (Mob) EntityDispatcher.createEntityInWorld(mobId, worldObj);
+                Mob mob = (Mob) EntityDispatcher.getInstance().createEntityInWorld(mobId, worldObj);
 
                 if(mob == null)
                 {
@@ -125,7 +126,7 @@ public class TileEntityMobSpawner extends TileEntity
                     double d5 = (double) z + 0.5D + ((double)worldObj.rand.nextFloat() - 0.5D) * 2D;
                     worldObj.spawnParticle("smoke", d1, d3, d5, 0.0D, 0.0D, 0.0D, 0);
                     worldObj.spawnParticle("flame", d1, d3, d5, 0.0D, 0.0D, 0.0D, 0);
-                    worldObj.playSoundAtEntity(null, mob, "mob.ghast.fireball", 0.025F, 0.75F);
+                    worldObj.playSoundAtEntity(null, mob, "tile.mobspawner.spawn", 0.025F, 0.75F);
                 }
 
                 mob.spawnExplosionParticle();
@@ -139,7 +140,7 @@ public class TileEntityMobSpawner extends TileEntity
     @Override
 	public Packet getDescriptionPacket()
     {
-        return new PacketSetMobSpawner(x, y, z, EntityDispatcher.classForId(mobId));
+        return new PacketSetMobSpawner(x, y, z, EntityDispatcher.getInstance().entryForId(mobId));
     }
     
     public int countNearbySpawners() {
diff --git a/game/core/src/main/java/net/minecraft/core/current/util/BlockPos.java b/game/core/src/main/java/net/minecraft/core/current/util/BlockPos.java
index c79344c05..00cdfc831 100644
--- a/game/core/src/main/java/net/minecraft/core/current/util/BlockPos.java
+++ b/game/core/src/main/java/net/minecraft/core/current/util/BlockPos.java
@@ -1,5 +1,7 @@
 package net.minecraft.core.current.util;
 
+import net.minecraft.core.util.helper.Direction;
+
 public class BlockPos {
 
 	public final int x;
@@ -32,7 +34,7 @@ public class BlockPos {
 	}
 
 	public BlockPos offset(Direction dir, int dist) {
-		return offset(dist * dir.offsetX, dist * dir.offsetY, dist * dir.offsetZ);
+		return offset(dist * dir.getOffsetX(), dist * dir.getOffsetY(), dist * dir.getOffsetZ());
 	}
 
 	public BlockPos offset(int offsetX, int offsetY, int offsetZ) {
diff --git a/game/core/src/main/java/net/minecraft/core/current/util/BlockState.java b/game/core/src/main/java/net/minecraft/core/current/util/BlockState.java
index 1e87b735c..0c41ed3f4 100644
--- a/game/core/src/main/java/net/minecraft/core/current/util/BlockState.java
+++ b/game/core/src/main/java/net/minecraft/core/current/util/BlockState.java
@@ -4,7 +4,7 @@ package net.minecraft.core.current.util;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.enums.EnumDropCause;
-import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.world.World;
 
 public class BlockState {
@@ -78,11 +78,11 @@ public class BlockState {
 	}
 
 	public boolean hasSignal(World world, BlockPos pos, Direction dir) {
-		return block.getSignal(world, pos.x, pos.y, pos.z, Side.getSideById(dir.index));
+		return block.getSignal(world, pos.x, pos.y, pos.z, dir.getSide());
 	}
 
 	public boolean hasDirectSignal(World world, BlockPos pos, Direction dir) {
-		return block.getDirectSignal(world, pos.x, pos.y, pos.z, Side.getSideById(dir.index));
+		return block.getDirectSignal(world, pos.x, pos.y, pos.z, dir.getSide());
 	}
 
 	public boolean canSurvive(World world, BlockPos pos) {
diff --git a/game/core/src/main/java/net/minecraft/core/current/util/BlockUtil.java b/game/core/src/main/java/net/minecraft/core/current/util/BlockUtil.java
deleted file mode 100644
index 8fc442573..000000000
--- a/game/core/src/main/java/net/minecraft/core/current/util/BlockUtil.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.minecraft.core.current.util;
-
-public class BlockUtil {
-
-	public static final int FLAG_UPDATE_CLIENTS = 0b10;
-
-}
diff --git a/game/core/src/main/java/net/minecraft/core/current/util/Direction.java b/game/core/src/main/java/net/minecraft/core/current/util/Direction.java
deleted file mode 100644
index 59884135b..000000000
--- a/game/core/src/main/java/net/minecraft/core/current/util/Direction.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package net.minecraft.core.current.util;
-
-public enum Direction {
-
-	DOWN(0, 1, 0, -1, 0),
-	UP(1, 0, 0, 1, 0),
-	NORTH(2, 3, 0, 0, -1),
-	SOUTH(3, 2, 0, 0, 1),
-	WEST(4, 5, -1, 0, 0),
-	EAST(5, 4, 1, 0, 0);
-
-	public static final Direction[] ALL;
-
-	static {
-
-		ALL = new Direction[values().length];
-
-		for (Direction dir : values()) {
-			ALL[dir.index] = dir;
-		}
-	}
-
-	public final int index;
-	public final int oppositeIndex;
-	public final int offsetX;
-	public final int offsetY;
-	public final int offsetZ;
-
-	private Direction(int index, int opposite, int offsetX, int offsetY, int offsetZ) {
-		this.index = index;
-		this.oppositeIndex = opposite;
-		this.offsetX = offsetX;
-		this.offsetY = offsetY;
-		this.offsetZ = offsetZ;
-	}
-
-	public static Direction fromIndex(int index) {
-		if (index < 0 || index >= ALL.length) {
-			return null;
-		}
-
-		return ALL[index];
-	}
-
-	public Direction getOpposite() {
-		return ALL[oppositeIndex];
-	}
-}
diff --git a/game/core/src/main/java/net/minecraft/core/current/util/Mth.java b/game/core/src/main/java/net/minecraft/core/current/util/Mth.java
deleted file mode 100644
index 992f9470f..000000000
--- a/game/core/src/main/java/net/minecraft/core/current/util/Mth.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.minecraft.core.current.util;
-
-public class Mth {
-	public static int clamp(int i, int min, int max) {
-		return i <= min ? min : Math.min(i, max);
-	}
-}
diff --git a/game/core/src/main/java/net/minecraft/core/current/util/Redstone.java b/game/core/src/main/java/net/minecraft/core/current/util/Redstone.java
deleted file mode 100644
index ec470d758..000000000
--- a/game/core/src/main/java/net/minecraft/core/current/util/Redstone.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.minecraft.core.current.util;
-
-public class Redstone {
-	public static final int SIGNAL_MIN = 0;
-	public static final int SIGNAL_MAX = 15;
-	public static final int SIGNAL_STEP = 1;
-}
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/Node.java b/game/core/src/main/java/net/minecraft/core/current/wire/Node.java
index 1159096fc..c85086c80 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/Node.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/Node.java
@@ -1,8 +1,8 @@
 package net.minecraft.core.current.wire;
 
+import net.minecraft.core.current.wire.WireHandler.Directions;
 import net.minecraft.core.current.util.BlockPos;
 import net.minecraft.core.current.util.BlockState;
-import net.minecraft.core.current.wire.WireHandler.Directions;
 import net.minecraft.core.world.World;
 import net.minecraft.core.block.Blocks;
 
@@ -14,7 +14,7 @@ import java.util.Arrays;
  *
  * @author Space Walker
  */
-public class Node {
+class Node {
 
 	// flags that encode the Node type
 	private static final int CONDUCTOR = 0b01;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/PriorityQueue.java b/game/core/src/main/java/net/minecraft/core/current/wire/PriorityQueue.java
index 32000d8b1..5f38cce11 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/PriorityQueue.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/PriorityQueue.java
@@ -1,14 +1,15 @@
 package net.minecraft.core.current.wire;
 
-import net.minecraft.core.current.util.Redstone;
+import net.minecraft.core.block.BlockLogicWireRedstone;
+import org.jetbrains.annotations.NotNull;
 
 import java.util.AbstractQueue;
 import java.util.Arrays;
 import java.util.Iterator;
 
-public class PriorityQueue extends AbstractQueue<Node> {
+class PriorityQueue extends AbstractQueue<Node> {
 
-	private static final int OFFSET = -Redstone.SIGNAL_MIN;
+	private static final int OFFSET = -BlockLogicWireRedstone.SIGNAL_MIN;
 
 	/** The last node for each priority value. */
 	private final Node[] tails;
@@ -19,7 +20,7 @@ public class PriorityQueue extends AbstractQueue<Node> {
 	private int size;
 
 	PriorityQueue() {
-		this.tails = new Node[(Redstone.SIGNAL_MAX + OFFSET) + 1];
+		this.tails = new Node[(BlockLogicWireRedstone.SIGNAL_MAX + OFFSET) + 1];
 	}
 
 	@Override
@@ -98,7 +99,7 @@ public class PriorityQueue extends AbstractQueue<Node> {
 	}
 
 	@Override
-	public Iterator<Node> iterator() {
+	public @NotNull Iterator<Node> iterator() {
 		throw new UnsupportedOperationException();
 	}
 
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/SimpleQueue.java b/game/core/src/main/java/net/minecraft/core/current/wire/SimpleQueue.java
index 405861d9c..0fcef5f66 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/SimpleQueue.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/SimpleQueue.java
@@ -3,7 +3,7 @@ package net.minecraft.core.current.wire;
 import java.util.AbstractQueue;
 import java.util.Iterator;
 
-public class SimpleQueue extends AbstractQueue<WireNode> {
+class SimpleQueue extends AbstractQueue<WireNode> {
 
 	private WireNode head;
 	private WireNode tail;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireConnection.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireConnection.java
index 86c6fa7bd..b2bb579e0 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireConnection.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireConnection.java
@@ -7,7 +7,7 @@ package net.minecraft.core.current.wire;
  * 
  * @author Space Walker
  */
-public class WireConnection {
+class WireConnection {
 
 	/** The connected wire. */
 	final WireNode wire;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireConnectionManager.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireConnectionManager.java
index 85f14e5c9..a1309f169 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireConnectionManager.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireConnectionManager.java
@@ -6,7 +6,7 @@ import net.minecraft.core.current.wire.WireHandler.NodeProvider;
 import java.util.Arrays;
 import java.util.function.Consumer;
 
-public class WireConnectionManager {
+class WireConnectionManager {
 
 	/** The owner of these connections. */
 	final WireNode owner;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireHandler.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireHandler.java
index d72950e5b..7e9ed8c90 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireHandler.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireHandler.java
@@ -1,11 +1,11 @@
 package net.minecraft.core.current.wire;
 
-import net.minecraft.core.current.util.BlockPos;
-import net.minecraft.core.current.util.BlockState;
-import net.minecraft.core.current.util.Direction;
-import net.minecraft.core.current.util.Redstone;
+import net.minecraft.core.block.BlockLogicWireRedstone;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
+import net.minecraft.core.current.util.BlockPos;
+import net.minecraft.core.current.util.BlockState;
+import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.world.World;
 
 import java.util.HashMap;
@@ -255,9 +255,9 @@ public class WireHandler {
 	 */
 	static final int[] DEFAULT_CARDINAL_UPDATE_ORDER = CARDINAL_UPDATE_ORDERS[0];
 
-	private static final int POWER_MIN = Redstone.SIGNAL_MIN;
-	private static final int POWER_MAX = Redstone.SIGNAL_MAX;
-	private static final int POWER_STEP = Redstone.SIGNAL_STEP;
+	private static final int POWER_MIN = BlockLogicWireRedstone.SIGNAL_MIN;
+	private static final int POWER_MAX = BlockLogicWireRedstone.SIGNAL_MAX;
+	private static final int POWER_STEP = BlockLogicWireRedstone.SIGNAL_STEP;
 
 	// If Vanilla will ever multi-thread the ticking of worlds, there should
 	// be only one WireHandler per world, in case redstone updates in multiple
@@ -745,7 +745,7 @@ public class WireHandler {
 			return;
 		}
 
-		wire.externalPower = hasExternalPower(wire) ? Redstone.SIGNAL_MAX : Redstone.SIGNAL_MIN;
+		wire.externalPower = hasExternalPower(wire) ? BlockLogicWireRedstone.SIGNAL_MAX : BlockLogicWireRedstone.SIGNAL_MIN;
 
 		if (wire.externalPower > wire.virtualPower) {
 			wire.virtualPower = wire.externalPower;
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
index 69790e1f7..a323dc284 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
@@ -2,11 +2,10 @@ package net.minecraft.core.current.wire;
 
 import com.mojang.logging.LogUtils;
 import net.minecraft.core.block.BlockLogicWireRedstone;
+import net.minecraft.core.block.Blocks;
 import net.minecraft.core.current.util.BlockPos;
 import net.minecraft.core.current.util.BlockState;
-import net.minecraft.core.current.util.Mth;
-import net.minecraft.core.current.util.Redstone;
-import net.minecraft.core.block.Blocks;
+import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.World;
 import org.slf4j.Logger;
 
@@ -17,7 +16,7 @@ import org.slf4j.Logger;
  *
  * @author Space Walker
  */
-public class WireNode extends Node {
+class WireNode extends Node {
 	private static final Logger LOGGER = LogUtils.getLogger();
 
 	final WireConnectionManager connections;
@@ -69,7 +68,7 @@ public class WireNode extends Node {
 
 	@Override
 	int priority() {
-		return Mth.clamp(virtualPower, Redstone.SIGNAL_MIN, Redstone.SIGNAL_MAX);
+		return MathHelper.clamp(virtualPower, BlockLogicWireRedstone.SIGNAL_MIN, BlockLogicWireRedstone.SIGNAL_MAX);
 	}
 
 	@Override
@@ -119,7 +118,7 @@ public class WireNode extends Node {
 			return true;
 		}
 
-		currentPower = Mth.clamp(virtualPower, Redstone.SIGNAL_MIN, Redstone.SIGNAL_MAX);
+		currentPower = MathHelper.clamp(virtualPower, BlockLogicWireRedstone.SIGNAL_MIN, BlockLogicWireRedstone.SIGNAL_MAX);
 		state = state.set(currentPower | direction);
 
 		return WorldHelper.setWireState(world, pos, state);
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WorldHelper.java b/game/core/src/main/java/net/minecraft/core/current/wire/WorldHelper.java
index 939a59990..37d1c3e0f 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WorldHelper.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WorldHelper.java
@@ -4,7 +4,7 @@ import net.minecraft.core.current.util.BlockPos;
 import net.minecraft.core.current.util.BlockState;
 import net.minecraft.core.world.World;
 
-public class WorldHelper {
+class WorldHelper {
 
 	private static final int Y_MIN = 0;
 
diff --git a/game/core/src/main/java/net/minecraft/core/entity/Entity.java b/game/core/src/main/java/net/minecraft/core/entity/Entity.java
index 659f3516a..067fb5261 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/Entity.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/Entity.java
@@ -1091,7 +1091,7 @@ public abstract class Entity
 
     public boolean save(@NotNull CompoundTag tag)
     {
-        @Nullable NamespaceID s = getEncodeId();
+        @Nullable NamespaceID s = getDispatcherId();
         if(removed || s == null)
         {
             return false;
@@ -1153,9 +1153,13 @@ public abstract class Entity
         readAdditionalSaveData(tag);
     }
 
-    protected final @Nullable NamespaceID getEncodeId()
+    public final @Nullable NamespaceID getDispatcherId()
     {
-        return EntityDispatcher.idForClass(this.getClass());
+        EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(this.getClass());
+        if (entry != null) {
+            return entry.namespaceID;
+        }
+        return null;
     }
 
     public abstract void readAdditionalSaveData(@NotNull CompoundTag tag);
@@ -1604,11 +1608,13 @@ public abstract class Entity
         if (entity instanceof Player){
             return ((Player) entity).getDisplayName();
         }
-        if (EntityDispatcher.nameKeyForClass(entity.getClass()) != null){
-            return translator.translateKey(EntityDispatcher.nameKeyForClass(entity.getClass()));
+        EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(entity.getClass());
+        if (entry != null) {
+            if (entry.nameKey != null){
+                return translator.translateKey(entry.nameKey);
+            }
+            return entry.namespaceID.value();
         }
-        NamespaceID dispatcherId = EntityDispatcher.idForClass(entity.getClass());
-        if (dispatcherId != null) return dispatcherId.value();
         return entity.getClass().getSimpleName();
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/EntityDispatcher.java b/game/core/src/main/java/net/minecraft/core/entity/EntityDispatcher.java
index a6ae82946..247764890 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/EntityDispatcher.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/EntityDispatcher.java
@@ -3,12 +3,12 @@ package net.minecraft.core.entity;
 import com.mojang.logging.LogUtils;
 import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
-import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
-import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
 import net.minecraft.core.entity.animal.*;
+import net.minecraft.core.entity.factories.EntityDontUseFactory;
+import net.minecraft.core.entity.factories.EntityFactory;
 import net.minecraft.core.entity.monster.*;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.entity.projectile.*;
@@ -21,89 +21,160 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 public final class EntityDispatcher
 {
     private static final Logger LOGGER = LogUtils.getLogger();
 
-    private static int mappingCount = 0;
+    private static final @NotNull EntityDispatcher INSTANCE = new EntityDispatcher();
 
-    public static final Map<Class<? extends Entity>, NamespaceID> classToIdMap = new HashMap<>();
-    public static final Map<NamespaceID, Class<? extends Entity>> idToClassMap = new HashMap<>();
-    public static final Map<String, Class<? extends Entity>> stringIdToClassMap = new HashMap<>();
-
-    private static final Int2ObjectMap<Class<? extends Entity>> numericIdToClassMap = new Int2ObjectOpenHashMap<>();
-    private static final Int2ObjectMap<String> numericIdToStringIdMap = new Int2ObjectOpenHashMap<>();
-    private static final Object2IntMap<Class<? extends Entity>> classToNumericIdMap = new Object2IntOpenHashMap<>();
-
-    private static final Map<Class<? extends Entity>, String> classToNameKeyMap = new HashMap<>();
-
-    private EntityDispatcher() {}
-
-    public static void addMapping(@NotNull Class<? extends Entity> entityClass, @NotNull NamespaceID namespaceID) {
-        addMapping(entityClass, namespaceID, null);
+    public static @NotNull EntityDispatcher getInstance() {
+        return INSTANCE;
     }
 
-    public static void addMapping(@NotNull Class<? extends Entity> entityClass, @NotNull NamespaceID namespaceID, @Nullable String nameKey) {
-        if (classToIdMap.containsKey(entityClass)) {
-            throw new IllegalArgumentException("EntityDispatcher already contains an assignment for class " + entityClass.getSimpleName() + " with namespace " + classToIdMap.get(entityClass) + "!");
-        }
-        final @NotNull NamespaceID unpooledNamespaceID = namespaceID.copyUnpooled();
-
-        classToIdMap.put(entityClass, unpooledNamespaceID);
-        idToClassMap.put(unpooledNamespaceID, entityClass);
-        stringIdToClassMap.put(unpooledNamespaceID.toString(), entityClass);
-
-        int numericId = mappingCount++;
-        numericIdToClassMap.put(numericId, entityClass);
-        numericIdToStringIdMap.put(numericId, unpooledNamespaceID.toString());
-        classToNumericIdMap.put(entityClass, numericId);
-
-        if (nameKey != null) {
-            classToNameKeyMap.put(entityClass, nameKey);
-        }
-    }
+    private int mappingCount = 0;
+
+    public final List<@NotNull EntityDispatcherEntry<?>> entries = new ArrayList<>();
+    public final Map<@NotNull NamespaceID, @NotNull EntityDispatcherEntry<?>> idToEntryMap = new HashMap<>();
+    public final Map<@NotNull String, @NotNull EntityDispatcherEntry<?>> stringIdToEntryMap = new HashMap<>();
+    public final Map<@NotNull Class<?>, @NotNull EntityDispatcherEntry<?>> classToEntryMap = new HashMap<>();
+    private final Int2ObjectMap<@NotNull EntityDispatcherEntry<?>> numericIdToEntryMap = new Int2ObjectOpenHashMap<>();
+    private final Int2ObjectMap<@NotNull String> numericIdToStringIdMap = new Int2ObjectOpenHashMap<>();
+
+    // Misc
+    public final EntityFactory<EntityItem> FACTORY_ITEM;
+    public final EntityFactory<EntityPainting> FACTORY_PAINTING;
+    public final EntityFactory<EntityLightning> FACTORY_LIGHTNING;
+    public final EntityFactory<EntityFallingBlock> FACTORY_FALLING_BLOCK;
+    public final EntityFactory<EntityMinecart> FACTORY_MINECART;
+    public final EntityFactory<EntityBoat> FACTORY_BOAT;
+    public final EntityFactory<EntityPrimedTNT> FACTORY_PRIMED_TNT;
+
+    // Projectiles
+    public final EntityFactory<ProjectilePebble> FACTORY_PROJECTILE_PEBBLE;
+    public final EntityFactory<ProjectileEgg> FACTORY_PROJECTILE_EGG;
+    public final EntityFactory<ProjectileArrow> FACTORY_PROJECTILE_ARROW;
+    public final EntityFactory<ProjectileArrowGolden> FACTORY_PROJECTILE_ARROW_GOLDEN;
+    public final EntityFactory<ProjectileArrowPurple> FACTORY_PROJECTILE_ARROW_PURPLE;
+    public final EntityFactory<ProjectileSnowball> FACTORY_PROJECTILE_SNOWBALL;
+    public final EntityFactory<ProjectileFireball> FACTORY_PROJECTILE_FIREBALL;
+    public final EntityFactory<ProjectileCannonball> FACTORY_PROJECTILE_CANNONBALL;
+
+    public final EntityFactory<Mob> FACTORY_MOB;
+    public final EntityFactory<MobHuman> FACTORY_HUMAN;
+    public final EntityFactory<Player> FACTORY_PLAYER;
+
+    // Hostile
+    public final EntityFactory<MobZombie> FACTORY_ZOMBIE;
+    public final EntityFactory<MobZombieArmored> FACTORY_ZOMBIE_ARMORED;
+    public final EntityFactory<MobZombiePig> FACTORY_ZOMBIE_PIG;
+    public final EntityFactory<MobCreeper> FACTORY_CREEPER;
+    public final EntityFactory<MobSkeleton> FACTORY_SKELETON;
+    public final EntityFactory<MobSpider> FACTORY_SPIDER;
+    public final EntityFactory<MobGiant> FACTORY_GIANT;
+    public final EntityFactory<MobSlime> FACTORY_SLIME;
+    public final EntityFactory<MobGhast> FACTORY_GHAST;
+    public final EntityFactory<MobSnowman> FACTORY_SNOWMAN;
+
+    // Passive
+    public final EntityFactory<MobScorpion> FACTORY_SCORPION;
+    public final EntityFactory<MobPig> FACTORY_PIG;
+    public final EntityFactory<MobSheep> FACTORY_SHEEP;
+    public final EntityFactory<MobCow> FACTORY_COW;
+    public final EntityFactory<MobChicken> FACTORY_CHICKEN;
+    public final EntityFactory<MobSquid> FACTORY_SQUID;
+    public final EntityFactory<MobWolf> FACTORY_WOLF;
+    public final EntityFactory<MobFireflyCluster> FACTORY_FIREFLY_CLUSTER;
+    public final EntityFactory<MobButterfly> FACTORY_BUTTERFLY;
+    public final EntityFactory<MobDeer> FACTORY_DEER;
+
+    private EntityDispatcher() {
+        // Misc
+        FACTORY_ITEM = addMapping(EntityItem.class, NamespaceID.fromPool("minecraft", "item"), EntityItem::new);
+        FACTORY_PAINTING = addMapping(EntityPainting.class, NamespaceID.fromPool("minecraft", "painting"), EntityPainting::new);
+        FACTORY_LIGHTNING = addMapping(EntityLightning.class, NamespaceID.fromPool("minecraft", "lightning"), EntityLightning::new);
+        FACTORY_FALLING_BLOCK = addMapping(EntityFallingBlock.class, NamespaceID.fromPool("minecraft", "falling_block"), EntityFallingBlock::new);
+        FACTORY_MINECART = addMapping(EntityMinecart.class, NamespaceID.fromPool("minecraft", "minecart"), EntityMinecart::new);
+        FACTORY_BOAT = addMapping(EntityBoat.class, NamespaceID.fromPool("minecraft", "boat"), EntityBoat::new);
+        FACTORY_PRIMED_TNT = addMapping(EntityPrimedTNT.class, NamespaceID.fromPool("minecraft", "primed_tnt"), EntityPrimedTNT::new);
 
-    public static @Nullable Class<? extends Entity> classForId(@NotNull NamespaceID id) {
-        return idToClassMap.get(id);
-    }
+        // Projectiles
+        FACTORY_PROJECTILE_PEBBLE = addMapping(ProjectilePebble.class, NamespaceID.fromPool("minecraft", "pebble"), ProjectilePebble::new);
+        FACTORY_PROJECTILE_EGG = addMapping(ProjectileEgg.class, NamespaceID.fromPool("minecraft", "egg"), ProjectileEgg::new);
+        FACTORY_PROJECTILE_ARROW = addMapping(ProjectileArrow.class, NamespaceID.fromPool("minecraft", "arrow"), ProjectileArrow::new);
+        FACTORY_PROJECTILE_ARROW_GOLDEN = addMapping(ProjectileArrowGolden.class, NamespaceID.fromPool("minecraft", "arrow_golden"), ProjectileArrowGolden::new);
+        FACTORY_PROJECTILE_ARROW_PURPLE = addMapping(ProjectileArrowPurple.class, NamespaceID.fromPool("minecraft", "arrow_purple"), ProjectileArrowPurple::new);
+        FACTORY_PROJECTILE_SNOWBALL = addMapping(ProjectileSnowball.class, NamespaceID.fromPool("minecraft", "snowball"), ProjectileSnowball::new);
+        FACTORY_PROJECTILE_FIREBALL = addMapping(ProjectileFireball.class, NamespaceID.fromPool("minecraft", "fireball"), ProjectileFireball::new);
+        FACTORY_PROJECTILE_CANNONBALL = addMapping(ProjectileCannonball.class, NamespaceID.fromPool("minecraft", "cannonball"), ProjectileCannonball::new);
+
+        FACTORY_MOB = addMapping(Mob.class, NamespaceID.fromPool("minecraft", "mob"), new EntityDontUseFactory<>()); // Abstract class, shouldn't ever be instantiated
+        FACTORY_HUMAN = addMapping(MobHuman.class, NamespaceID.fromPool("minecraft", "human"), MobHuman::new);
+        FACTORY_PLAYER = addMapping(Player.class, NamespaceID.fromPool("minecraft", "player"), new EntityDontUseFactory<>());
 
-    public static @Nullable Class<? extends Entity> classForId(@NotNull String id) {
-        return stringIdToClassMap.get(id);
-    }
+        // Hostile
+        FACTORY_ZOMBIE = addMapping(MobZombie.class, NamespaceID.fromPool("minecraft", "zombie"), MobZombie::new, "guidebook.section.mob.zombie.name");
+        FACTORY_ZOMBIE_ARMORED = addMapping(MobZombieArmored.class, NamespaceID.fromPool("minecraft", "zombie_armored"), MobZombieArmored::new, "guidebook.section.mob.armored_zombie.name");
+        FACTORY_ZOMBIE_PIG = addMapping(MobZombiePig.class, NamespaceID.fromPool("minecraft", "zombie_pigman"), MobZombiePig::new, "guidebook.section.mob.zombie_pigman.name");
+        FACTORY_CREEPER = addMapping(MobCreeper.class, NamespaceID.fromPool("minecraft", "creeper"), MobCreeper::new, "guidebook.section.mob.creeper.name");
+        FACTORY_SKELETON = addMapping(MobSkeleton.class, NamespaceID.fromPool("minecraft", "skeleton"), MobSkeleton::new, "guidebook.section.mob.skeleton.name");
+        FACTORY_SPIDER = addMapping(MobSpider.class, NamespaceID.fromPool("minecraft", "spider"), MobSpider::new, "guidebook.section.mob.spider.name");
+        FACTORY_GIANT = addMapping(MobGiant.class, NamespaceID.fromPool("minecraft", "giant"), MobGiant::new);
+        FACTORY_SLIME = addMapping(MobSlime.class, NamespaceID.fromPool("minecraft", "slime"), MobSlime::new, "guidebook.section.mob.slime.name");
+        FACTORY_GHAST = addMapping(MobGhast.class, NamespaceID.fromPool("minecraft", "ghast"), MobGhast::new, "guidebook.section.mob.ghast.name");
+        FACTORY_SNOWMAN = addMapping(MobSnowman.class, NamespaceID.fromPool("minecraft", "snowman"), MobSnowman::new, "guidebook.section.mob.snowman.name");
 
-    public static @Nullable NamespaceID idForClass(Class<? extends Entity> entityClass) {
-        return classToIdMap.get(entityClass);
+        // Passive
+        FACTORY_SCORPION = addMapping(MobScorpion.class, NamespaceID.fromPool("minecraft", "scorpion"), MobScorpion::new);
+        FACTORY_PIG = addMapping(MobPig.class, NamespaceID.fromPool("minecraft", "pig"), MobPig::new, "guidebook.section.mob.pig.name");
+        FACTORY_SHEEP = addMapping(MobSheep.class, NamespaceID.fromPool("minecraft", "sheep"), MobSheep::new, "guidebook.section.mob.sheep.name");
+        FACTORY_COW = addMapping(MobCow.class, NamespaceID.fromPool("minecraft", "cow"), MobCow::new, "guidebook.section.mob.cow.name");
+        FACTORY_CHICKEN = addMapping(MobChicken.class, NamespaceID.fromPool("minecraft", "chicken"), MobChicken::new, "guidebook.section.mob.chicken.name");
+        FACTORY_SQUID = addMapping(MobSquid.class, NamespaceID.fromPool("minecraft", "squid"), MobSquid::new, "guidebook.section.mob.squid.name");
+        FACTORY_WOLF = addMapping(MobWolf.class, NamespaceID.fromPool("minecraft", "wolf"), MobWolf::new, "guidebook.section.mob.wolf.name");
+        FACTORY_FIREFLY_CLUSTER = addMapping(MobFireflyCluster.class, NamespaceID.fromPool("minecraft", "firefly_cluster"), MobFireflyCluster::new, "guidebook.section.mob.firefly_cluster.name");
+        FACTORY_BUTTERFLY = addMapping(MobButterfly.class, NamespaceID.fromPool("minecraft", "butterfly"), MobButterfly::new, "guidebook.section.mob.butterfly.name");
+        FACTORY_DEER = addMapping(MobDeer.class, NamespaceID.fromPool("minecraft", "deer"), MobDeer::new, "guidebook.section.mob.deer.name");
     }
 
-    public static @Nullable Class<? extends Entity> classForNumericId(int id) {
-        return numericIdToClassMap.get(id);
+    public <T extends Entity> @NotNull EntityFactory<T> addMapping(@NotNull Class<T> entityClass, @NotNull NamespaceID namespaceID, @NotNull EntityFactory<T> factory) {
+        return addMapping(entityClass, namespaceID, factory, null);
     }
 
-    public static @Nullable String nameKeyForClass(Class<? extends Entity> entityClass) {
-        return classToNameKeyMap.get(entityClass);
-    }
+    public <T extends Entity> @NotNull EntityFactory<T> addMapping(@NotNull Class<T> entityClass, @NotNull NamespaceID namespaceID, @NotNull EntityFactory<T> factory, @Nullable String nameKey) {
+        if (classToEntryMap.containsKey(entityClass)) {
+            throw new IllegalArgumentException("EntityDispatcher already contains an assignment for class " + entityClass.getSimpleName() + " with namespace " + classToEntryMap.get(entityClass).namespaceID + "!");
+        }
+        final @NotNull NamespaceID unpooledNamespaceID = namespaceID.copyUnpooled();
 
-    public static int numericIdForClass(Class<? extends Entity> entityClass) {
-        return classToNumericIdMap.getOrDefault(entityClass, -1);
+        int numericId = mappingCount++;
+        EntityDispatcherEntry<T> entry = new EntityDispatcherEntry<>(namespaceID, numericId, entityClass, factory, nameKey);
+        entries.add(entry);
+        idToEntryMap.put(entry.namespaceID, entry);
+        stringIdToEntryMap.put(entry.stringID, entry);
+        classToEntryMap.put(entry.entityClass, entry);
+        numericIdToEntryMap.put(entry.numericID, entry);
+
+        return factory;
     }
 
-    public static void setNumericIds(final @NotNull Int2ObjectMap<String> mapping) {
-        numericIdToClassMap.clear();
+    public void setNumericIds(final @NotNull Int2ObjectMap<String> mapping) {
+        numericIdToEntryMap.clear();
         numericIdToStringIdMap.clear();
-        classToNumericIdMap.clear();
         for (final Int2ObjectMap.Entry<String> entry : mapping.int2ObjectEntrySet()) {
-            final Class<? extends Entity> entityClass = classForId(entry.getValue());
-            if (entityClass == null) {
-                LOGGER.error("Could not find entityClass assigned to namespace id {}! Skipping!", entry.getValue());
+            EntityDispatcherEntry<?> e = stringIdToEntryMap.get(entry.getValue());
+            if (e == null) {
+                LOGGER.error("Could not find entity dispatcher entry assigned to namespace id {}! Skipping!", entry.getValue());
                 continue;
             }
 
-            numericIdToClassMap.put(entry.getIntKey(), entityClass);
+            e.updateID(entry.getIntKey());
+            numericIdToEntryMap.put(entry.getIntKey(), e);
             numericIdToStringIdMap.put(entry.getIntKey(), entry.getValue());
-            classToNumericIdMap.put(entityClass, entry.getIntKey());
 
             if (entry.getIntKey() >= mappingCount) {
                 mappingCount = entry.getIntKey() + 1;
@@ -111,45 +182,67 @@ public final class EntityDispatcher
         }
     }
 
-    public static Int2ObjectMap<String> getEntityIds() {
+    public @Nullable EntityDispatcher.EntityDispatcherEntry<?> entryForId(@NotNull NamespaceID id) {
+        return idToEntryMap.get(id);
+    }
+
+    public @Nullable EntityDispatcher.EntityDispatcherEntry<?> entryForId(@NotNull String id) {
+        return stringIdToEntryMap.get(id);
+    }
+
+    public @Nullable EntityDispatcher.EntityDispatcherEntry<?> entryForNumericId(int id) {
+        return numericIdToEntryMap.get(id);
+    }
+
+    public @Nullable <T extends Entity> EntityDispatcher.EntityDispatcherEntry<T> entryForClass(@Nullable Class<T> entityClass) {
+        return (EntityDispatcherEntry<T>) classToEntryMap.get(entityClass);
+    }
+
+    public @NotNull Int2ObjectMap<String> getEntityIds() {
         return numericIdToStringIdMap;
     }
 
-    public static @Nullable Entity createEntityInWorld(@Nullable final String id, @NotNull final World world)
+    public @Nullable Entity createEntityInWorld(@Nullable final String id, @NotNull final World world)
     {
         if (id == null) return null;
+        EntityDispatcherEntry<?> entry;
         if (Legacy.isLegacyKey(id)) { // Handle entity creation via legacy id
-            return createEntityInWorld(Legacy.getClassFromEncodeID(id), world);
-        } else {
-            Class<? extends Entity> entityClass = classForId(id);
-            if (entityClass == null) {
+            entry = entryForClass(Legacy.getClassFromEncodeID(id));
+            if (entry != null) {
+                return entry.factory.createEntity(world);
+            } else {
                 LOGGER.warn("EntityDispatcher could not find entity class for id '{}'!", id);
                 return null;
             }
-            return createEntityInWorld(entityClass, world);
+        } else {
+            entry = entryForId(id);
         }
+        if (entry != null) {
+            return entry.factory.createEntity(world);
+        }
+        LOGGER.warn("EntityDispatcher could not find entity class for id '{}'!", id);
+        return null;
     }
 
-    public static @Nullable Entity createEntityInWorld(@NotNull final NamespaceID id, @NotNull final World world) {
-        return createEntityInWorld(classForId(id), world);
+    public @Nullable Entity createEntityInWorld(@NotNull final NamespaceID id, @NotNull final World world) {
+        EntityDispatcherEntry<?> entry = entryForId(id);
+        if (entry != null) {
+            return entry.factory.createEntity(world);
+        }
+        LOGGER.warn("EntityDispatcher could not find entity class for id '{}'!", id);
+        return null;
     }
 
-    public static @Nullable Entity createEntityInWorld(@Nullable final Class<? extends Entity> clazz, @NotNull final World world) {
-        Entity entity = null;
-        try {
-            if(clazz != null) {
-                entity = clazz.getConstructor(World.class).newInstance(world);
-            } else {
-                return null;
-            }
-        }
-        catch(Exception exception) {
-            LOGGER.error("Failed to instance for class '{}'!", clazz.getSimpleName(), exception);
+    public @Nullable <T extends Entity> T createEntityInWorld(@NotNull Class<T> entityClass, @NotNull final World world) {
+        EntityDispatcherEntry<T> entry = entryForClass(entityClass);
+        if (entry != null) {
+            return entry.factory.createEntity(world);
         }
-        return entity;
+        LOGGER.warn("EntityDispatcher could not find entity class for class '{}'!", entityClass.getSimpleName());
+        return null;
     }
 
-    public static Entity createEntityFromNBT(CompoundTag compoundTag, World world)
+    public @Nullable Entity createEntityFromNBT(@NotNull CompoundTag compoundTag, @NotNull World world)
     {
         String id = compoundTag.getStringOrDefault("id", "UNKNOWN");
         Entity entity = createEntityInWorld(id, world);
@@ -162,76 +255,39 @@ public final class EntityDispatcher
         return entity;
     }
 
-    public static @Nullable Entity createEntity(int id, World world)
+    public @Nullable Entity createEntity(int id, @NotNull World world)
     {
-        @Nullable Entity entity = null;
-        try
-        {
-            Class<? extends Entity> clazz = classForNumericId(id);
-            if(clazz != null) {
-                entity = clazz.getConstructor(new Class[] {World.class}).newInstance(world);
-            } else {
-                LOGGER.error("No entity class assign to id {}!", id);
-            }
+        EntityDispatcherEntry<?> entry = entryForNumericId(id);
+        if(entry != null) {
+            return entry.factory.createEntity(world);
         }
-        catch(Exception exception)
-        {
-            LOGGER.error("Failed to instance entity id '{}'!", id, exception);
-        }
-        return entity;
+        LOGGER.error("No entity class assign to id {}!", id);
+        return null;
     }
 
-    private static boolean hasInit = false;
-    public static void init() {
-        if (hasInit) return;
-        hasInit = true;
 
-        // Misc
-        addMapping(EntityItem.class, NamespaceID.fromPool("minecraft", "item"));
-        addMapping(EntityPainting.class, NamespaceID.fromPool("minecraft", "painting"));
-        addMapping(EntityLightning.class, NamespaceID.fromPool("minecraft", "lightning"));
-        addMapping(EntityFallingBlock.class, NamespaceID.fromPool("minecraft", "falling_block"));
-        addMapping(EntityMinecart.class, NamespaceID.fromPool("minecraft", "minecart"));
-        addMapping(EntityBoat.class, NamespaceID.fromPool("minecraft", "boat"));
-        addMapping(EntityPrimedTNT.class, NamespaceID.fromPool("minecraft", "primed_tnt"));
+    public static void init() {}
 
-        // Projectiles
-        addMapping(ProjectilePebble.class, NamespaceID.fromPool("minecraft", "pebble"));
-        addMapping(ProjectileEgg.class, NamespaceID.fromPool("minecraft", "egg"));
-        addMapping(ProjectileArrow.class, NamespaceID.fromPool("minecraft", "arrow"));
-        addMapping(ProjectileArrowGolden.class, NamespaceID.fromPool("minecraft", "arrow_golden"));
-        addMapping(ProjectileArrowPurple.class, NamespaceID.fromPool("minecraft", "arrow_purple"));
-        addMapping(ProjectileSnowball.class, NamespaceID.fromPool("minecraft", "snowball"));
-        addMapping(ProjectileFireball.class, NamespaceID.fromPool("minecraft", "fireball"));
-        addMapping(ProjectileCannonball.class, NamespaceID.fromPool("minecraft", "cannonball"));
-
-        addMapping(Mob.class, NamespaceID.fromPool("minecraft", "mob")); // Abstract class, shouldn't ever be instantiated
-        addMapping(MobHuman.class, NamespaceID.fromPool("minecraft", "human"));
-        addMapping(Player.class, NamespaceID.fromPool("minecraft", "player"));
+    public static class EntityDispatcherEntry<T extends Entity> {
+        public final @NotNull NamespaceID namespaceID;
+        public final @NotNull String stringID;
+        public int numericID;
+        public final @NotNull EntityFactory<T> factory;
+        public final @NotNull Class<T> entityClass;
+        public final @Nullable String nameKey;
 
-        // Hostile
-        addMapping(MobZombie.class, NamespaceID.fromPool("minecraft", "zombie"), "guidebook.section.mob.zombie.name");
-        addMapping(MobZombieArmored.class, NamespaceID.fromPool("minecraft", "zombie_armored"), "guidebook.section.mob.armored_zombie.name");
-        addMapping(MobZombiePig.class, NamespaceID.fromPool("minecraft", "zombie_pigman"), "guidebook.section.mob.zombie_pigman.name");
-        addMapping(MobCreeper.class, NamespaceID.fromPool("minecraft", "creeper"), "guidebook.section.mob.creeper.name");
-        addMapping(MobSkeleton.class, NamespaceID.fromPool("minecraft", "skeleton"), "guidebook.section.mob.skeleton.name");
-        addMapping(MobSpider.class, NamespaceID.fromPool("minecraft", "spider"), "guidebook.section.mob.spider.name");
-        addMapping(MobGiant.class, NamespaceID.fromPool("minecraft", "giant"));
-        addMapping(MobSlime.class, NamespaceID.fromPool("minecraft", "slime"), "guidebook.section.mob.slime.name");
-        addMapping(MobGhast.class, NamespaceID.fromPool("minecraft", "ghast"), "guidebook.section.mob.ghast.name");
-        addMapping(MobSnowman.class, NamespaceID.fromPool("minecraft", "snowman"), "guidebook.section.mob.snowman.name");
+        public EntityDispatcherEntry(@NotNull NamespaceID namespaceID, int numericID, @NotNull Class<T> entityClass, @NotNull EntityFactory<T> factory, @Nullable String nameKey) {
+            this.namespaceID = namespaceID;
+            this.stringID = namespaceID.toString();
+            this.numericID = numericID;
+            this.factory = factory;
+            this.entityClass = entityClass;
+            this.nameKey = nameKey;
+        }
 
-        // Passive
-        addMapping(MobScorpion.class, NamespaceID.fromPool("minecraft", "scorpion"));
-        addMapping(MobPig.class, NamespaceID.fromPool("minecraft", "pig"), "guidebook.section.mob.pig.name");
-        addMapping(MobSheep.class, NamespaceID.fromPool("minecraft", "sheep"), "guidebook.section.mob.sheep.name");
-        addMapping(MobCow.class, NamespaceID.fromPool("minecraft", "cow"), "guidebook.section.mob.cow.name");
-        addMapping(MobChicken.class, NamespaceID.fromPool("minecraft", "chicken"), "guidebook.section.mob.chicken.name");
-        addMapping(MobSquid.class, NamespaceID.fromPool("minecraft", "squid"), "guidebook.section.mob.squid.name");
-        addMapping(MobWolf.class, NamespaceID.fromPool("minecraft", "wolf"), "guidebook.section.mob.wolf.name");
-        addMapping(MobFireflyCluster.class, NamespaceID.fromPool("minecraft", "firefly_cluster"), "guidebook.section.mob.firefly_cluster.name");
-        addMapping(MobButterfly.class, NamespaceID.fromPool("minecraft", "butterfly"), "guidebook.section.mob.butterfly.name");
-        addMapping(MobDeer.class, NamespaceID.fromPool("minecraft", "deer"), "guidebook.section.mob.deer.name");
+        public void updateID(int newNumericID) {
+            this.numericID = newNumericID;
+        }
     }
 
     @Deprecated
diff --git a/game/core/src/main/java/net/minecraft/core/entity/EntityLightning.java b/game/core/src/main/java/net/minecraft/core/entity/EntityLightning.java
index b6235fd3e..26aada370 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/EntityLightning.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/EntityLightning.java
@@ -1,5 +1,6 @@
 package net.minecraft.core.entity;
 
+import net.minecraft.core.entity.animal.MobWolf;
 import net.minecraft.core.enums.Difficulty;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.sound.SoundCategory;
@@ -39,6 +40,12 @@ public class EntityLightning extends Entity
         {
             world.playSoundEffect(null, SoundCategory.WEATHER_SOUNDS, x, y, z, "ambient.weather.thunder", 10000F, 0.8F + random.nextFloat() * 0.2F);
             world.playSoundEffect(null, SoundCategory.WEATHER_SOUNDS, x, y, z, "random.explode", 2.0F, 0.5F + random.nextFloat() * 0.2F);
+            for (int i = 0; i < world.loadedEntityList.size(); i++) {
+                Entity e = world.loadedEntityList.get(i);
+                if (e instanceof MobWolf) {
+                    ((MobWolf) e).whine();
+                }
+            }
         }
         life--;
         if(life < 0)
diff --git a/game/core/src/main/java/net/minecraft/core/entity/Mob.java b/game/core/src/main/java/net/minecraft/core/entity/Mob.java
index c4de84b39..eebfdd8fe 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/Mob.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/Mob.java
@@ -60,7 +60,7 @@ public abstract class Mob extends Entity
     public int prevHealth;
     public int bonusHealth;
     public int prevBonusHealth;
-    private int livingSoundTime;
+    protected int livingSoundTime;
     public int hurtTime;
     public int maxHurtTime;
     public float attackedAtYaw;
diff --git a/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java b/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
index 3d5829f10..1c1cb3b3e 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
@@ -10,7 +10,6 @@ import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.item.material.ArmorMaterial;
 import net.minecraft.core.util.helper.UUIDHelper;
-import net.minecraft.core.util.collection.NamespaceID;
 import net.minecraft.core.util.helper.MathHelper;
 import com.mojang.nbt.tags.CompoundTag;
 import net.minecraft.core.world.pathfinder.Path;
@@ -262,19 +261,26 @@ public class MobWolf
         }
         if(random.nextInt(3) == 0)
         {
-            if(isWolfTamed() && getHealth() < getMaxHealth()/2)
+            if((isWolfTamed() && getHealth() < getMaxHealth()/2))
             {
                 return "mob.wolf.whine";
             } else
             {
                 return "mob.wolf.panting";
             }
-        } else
-        {
+        } else {
             return "mob.wolf.bark";
         }
     }
 
+    public void whine() {
+        if(!world.isClientSide)
+        {
+            livingSoundTime = -getAmbientSoundInterval();
+            world.playSoundAtEntity(null, this, "mob.wolf.whine", getSoundVolume(), (random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F + getPitchModifier());
+        }
+    }
+
     @Override
 	protected String getHurtSound()
     {
diff --git a/game/core/src/main/java/net/minecraft/core/entity/factories/EntityDontUseFactory.java b/game/core/src/main/java/net/minecraft/core/entity/factories/EntityDontUseFactory.java
new file mode 100644
index 000000000..5bc69fda8
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/entity/factories/EntityDontUseFactory.java
@@ -0,0 +1,12 @@
+package net.minecraft.core.entity.factories;
+
+import net.minecraft.core.entity.Entity;
+import net.minecraft.core.world.World;
+import org.jetbrains.annotations.NotNull;
+
+public class EntityDontUseFactory<T extends Entity> implements EntityFactory<T> {
+    @Override
+    public T createEntity(@NotNull World world) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/entity/factories/EntityFactory.java b/game/core/src/main/java/net/minecraft/core/entity/factories/EntityFactory.java
new file mode 100644
index 000000000..1e35c8372
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/entity/factories/EntityFactory.java
@@ -0,0 +1,9 @@
+package net.minecraft.core.entity.factories;
+
+import net.minecraft.core.entity.Entity;
+import net.minecraft.core.world.World;
+import org.jetbrains.annotations.NotNull;
+
+public interface EntityFactory<T extends Entity> {
+    T createEntity(@NotNull World world);
+}
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/ArmorBag.java b/game/core/src/main/java/net/minecraft/core/entity/monster/ArmorBag.java
new file mode 100644
index 000000000..085a3175d
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/ArmorBag.java
@@ -0,0 +1,90 @@
+package net.minecraft.core.entity.monster;
+
+import net.minecraft.core.enums.IArmorShape;
+import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemArmor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ArmorBag<T extends IArmorShape> {
+    private class ArmorBagEntry {
+        public final @Nullable ItemArmor<T> armorItem;
+        public final int chance;
+        public final float maxHeight;
+
+        public ArmorBagEntry(final @Nullable ItemArmor<T> armorItem, final int chance, final float maxHeight) {
+            this.armorItem = armorItem;
+            this.chance = chance;
+            this.maxHeight = maxHeight;
+        }
+    }
+
+    private final @NotNull List<@NotNull ArmorBagEntry> entries = new ArrayList<>();
+
+    private final @NotNull T armorShape;
+
+    public ArmorBag(final @NotNull T armorShape) {
+        this.armorShape = armorShape;
+    }
+
+    public @NotNull T getArmorShape() {
+        return this.armorShape;
+    }
+
+    public @NotNull ArmorBag<T> addEntry(final @Nullable Item item, final int chance, final float maxHeight) {
+        if (item != null && !(item instanceof ItemArmor<?>)) {
+            throw new IllegalArgumentException("Illegal non-armor item for bag!");
+        }
+        final @Nullable ItemArmor<T> armorItem = (ItemArmor<T>) item;
+
+        if (armorItem != null && this.armorShape != armorItem.getArmorShape()) {
+            throw new IllegalArgumentException("Illegal armor item shape for bag!");
+        }
+        if (chance == 0) {
+            throw new IllegalArgumentException("Item chance cannot be 0!");
+        }
+        if (maxHeight == 0) {
+            throw new IllegalArgumentException("Max height for item cannot be 0!");
+        }
+
+        this.entries.add(new ArmorBagEntry(armorItem, chance, maxHeight));
+
+        return this;
+    }
+
+    public @Nullable ItemArmor<T> getRandomItem(final @NotNull Random rand, final int worldHeight, final int mobHeight) {
+        if (this.entries.isEmpty()) {
+            throw new RuntimeException("Tried to pull armor item from empty bag!");
+        }
+
+        final float mobHeightPercentage = (float) mobHeight / (float) worldHeight;
+
+        int sum = 0;
+        for (final @NotNull ArmorBagEntry entry : this.entries) {
+            if (mobHeightPercentage < entry.maxHeight) {
+                sum += entry.chance;
+            }
+        }
+        if (sum == 0) {
+            return null;
+        }
+
+        final int randVal = rand.nextInt(sum);
+
+        int randSum = 0;
+        for (final @NotNull ArmorBagEntry entry : this.entries) {
+            if (mobHeightPercentage < entry.maxHeight) {
+                randSum += entry.chance;
+                if (randVal < randSum) {
+                    return entry.armorItem;
+                }
+            }
+        }
+
+        throw new IllegalStateException("Should never not find an item in the bag!");
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/ArmorBags.java b/game/core/src/main/java/net/minecraft/core/entity/monster/ArmorBags.java
new file mode 100644
index 000000000..6296e9515
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/ArmorBags.java
@@ -0,0 +1,82 @@
+package net.minecraft.core.entity.monster;
+
+import net.minecraft.core.enums.HumanArmorShape;
+import net.minecraft.core.enums.IArmorShape;
+import net.minecraft.core.item.Items;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+
+public abstract class ArmorBags {
+    public static final @NotNull HashMap<Class<? extends MobMonsterArmored<?>>, HashMap<?, ArmorBag<?>>> ARMOR_BAGS = new HashMap<>();
+
+    public static final @NotNull HashMap<@NotNull HumanArmorShape, @NotNull ArmorBag<@NotNull HumanArmorShape>> ZOMBIE_ARMOR_BAGS = register(MobZombie.class, new HashMap<>());
+    static {
+        ZOMBIE_ARMOR_BAGS.put(HumanArmorShape.HEAD,
+                new ArmorBag<>(HumanArmorShape.HEAD)
+                        .addEntry(null, 100, 1.0f)
+                        .addEntry(Items.ARMOR_HELMET_LEATHER, 30, 0.8f)
+                        .addEntry(Items.ARMOR_HELMET_IRON, 10, 0.5f)
+                        .addEntry(Items.ARMOR_HELMET_DIAMOND, 5, 0.125f)
+                        .addEntry(Items.ARMOR_HELMET_STEEL, 1, 0.125f));
+        ZOMBIE_ARMOR_BAGS.put(HumanArmorShape.CHEST,
+                new ArmorBag<>(HumanArmorShape.CHEST)
+                        .addEntry(null, 100, 1.0f)
+                        .addEntry(Items.ARMOR_CHESTPLATE_LEATHER, 30, 0.8f)
+                        .addEntry(Items.ARMOR_CHESTPLATE_IRON, 10, 0.5f)
+                        .addEntry(Items.ARMOR_CHESTPLATE_DIAMOND, 5, 0.125f)
+                        .addEntry(Items.ARMOR_CHESTPLATE_STEEL, 1, 0.125f));
+        ZOMBIE_ARMOR_BAGS.put(HumanArmorShape.LEGS,
+                new ArmorBag<>(HumanArmorShape.LEGS)
+                        .addEntry(null, 100, 1.0f)
+                        .addEntry(Items.ARMOR_LEGGINGS_LEATHER, 30, 0.8f)
+                        .addEntry(Items.ARMOR_LEGGINGS_IRON, 10, 0.5f)
+                        .addEntry(Items.ARMOR_LEGGINGS_DIAMOND, 5, 0.125f)
+                        .addEntry(Items.ARMOR_LEGGINGS_STEEL, 1, 0.125f));
+        ZOMBIE_ARMOR_BAGS.put(HumanArmorShape.BOOTS,
+                new ArmorBag<>(HumanArmorShape.BOOTS)
+                        .addEntry(null, 100, 1.0f)
+                        .addEntry(Items.ARMOR_BOOTS_LEATHER, 30, 0.8f)
+                        .addEntry(Items.ARMOR_BOOTS_IRON, 10, 0.5f)
+                        .addEntry(Items.ARMOR_BOOTS_DIAMOND, 5, 0.125f)
+                        .addEntry(Items.ARMOR_BOOTS_STEEL, 1, 0.125f));
+    }
+
+    public static final @NotNull HashMap<@NotNull HumanArmorShape, @NotNull ArmorBag<@NotNull HumanArmorShape>> SKELETON_ARMOR_BAGS = register(MobSkeleton.class, new HashMap<>());
+    static {
+        SKELETON_ARMOR_BAGS.put(HumanArmorShape.HEAD,
+                new ArmorBag<>(HumanArmorShape.HEAD)
+                        .addEntry(null, 100, 1.0f)
+                        .addEntry(Items.ARMOR_HELMET_LEATHER, 30, 0.8f)
+                        .addEntry(Items.ARMOR_HELMET_IRON, 10, 0.5f)
+                        .addEntry(Items.ARMOR_HELMET_DIAMOND, 5, 0.125f)
+                        .addEntry(Items.ARMOR_HELMET_STEEL, 1, 0.125f));
+        SKELETON_ARMOR_BAGS.put(HumanArmorShape.CHEST,
+                new ArmorBag<>(HumanArmorShape.CHEST)
+                        .addEntry(null, 100, 1.0f)
+                        .addEntry(Items.ARMOR_CHESTPLATE_LEATHER, 30, 0.8f)
+                        .addEntry(Items.ARMOR_CHESTPLATE_IRON, 10, 0.5f)
+                        .addEntry(Items.ARMOR_CHESTPLATE_DIAMOND, 5, 0.125f)
+                        .addEntry(Items.ARMOR_CHESTPLATE_STEEL, 1, 0.125f));
+        SKELETON_ARMOR_BAGS.put(HumanArmorShape.LEGS,
+                new ArmorBag<>(HumanArmorShape.LEGS)
+                        .addEntry(null, 100, 1.0f)
+                        .addEntry(Items.ARMOR_LEGGINGS_LEATHER, 30, 0.8f)
+                        .addEntry(Items.ARMOR_LEGGINGS_IRON, 10, 0.5f)
+                        .addEntry(Items.ARMOR_LEGGINGS_DIAMOND, 5, 0.125f)
+                        .addEntry(Items.ARMOR_LEGGINGS_STEEL, 1, 0.125f));
+        SKELETON_ARMOR_BAGS.put(HumanArmorShape.BOOTS,
+                new ArmorBag<>(HumanArmorShape.BOOTS)
+                        .addEntry(null, 100, 1.0f)
+                        .addEntry(Items.ARMOR_BOOTS_LEATHER, 30, 0.8f)
+                        .addEntry(Items.ARMOR_BOOTS_IRON, 10, 0.5f)
+                        .addEntry(Items.ARMOR_BOOTS_DIAMOND, 5, 0.125f)
+                        .addEntry(Items.ARMOR_BOOTS_STEEL, 1, 0.125f));
+    }
+
+    public static <S extends IArmorShape> @NotNull HashMap<@NotNull S, @NotNull ArmorBag<S>> register(final @NotNull Class<? extends MobMonsterArmored<S>> clazz, final @NotNull HashMap<@NotNull S, @NotNull ArmorBag<S>> armorBag) {
+        ARMOR_BAGS.put(clazz, (HashMap) armorBag);
+
+        return armorBag;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/MobMonsterArmored.java b/game/core/src/main/java/net/minecraft/core/entity/monster/MobMonsterArmored.java
index c5c1f6400..4be57e0d0 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/monster/MobMonsterArmored.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/MobMonsterArmored.java
@@ -7,12 +7,16 @@ import com.mojang.nbt.tags.Tag;
 import net.minecraft.core.entity.IArmorWearing;
 import net.minecraft.core.entity.IItemHolding;
 import net.minecraft.core.enums.IArmorShape;
+import net.minecraft.core.item.ItemArmor;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.DamageType;
 import net.minecraft.core.world.World;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.HashMap;
+import java.util.Random;
+
 public abstract class MobMonsterArmored<T extends IArmorShape>
     extends MobMonster
     implements IArmorWearing<T>, IItemHolding {
@@ -25,10 +29,37 @@ public abstract class MobMonsterArmored<T extends IArmorShape>
         super(world);
     }
 
+    /**
+     * Spawns one or more random armor pieces on the mob, with rarity based on height - lower mobs will be tougher
+     */
+    private void doRandomArmorSpawn(final @NotNull Random rand, final HashMap<?, ArmorBag<?>> armorBags) {
+        final int slots = this.getNumArmorSlots();
+        for (int i = 0; i < slots; i++) {
+            final @Nullable T slot = this.getArmorSlotByIndex(i);
+            if (slot != null && armorBags.containsKey(slot)) {
+                final @NotNull ArmorBag<?> armorBag = armorBags.get(slot);
+                if (armorBag.getArmorShape() != slot) {
+                    throw new RuntimeException("armorBags is malformed!");
+                }
+
+                final @Nullable ItemArmor<?> armor = armorBag.getRandomItem(rand, this.world.getHeightBlocks(), (int) Math.floor(this.y));
+                if (armor != null) {
+                    final @NotNull ItemStack armorStack = new ItemStack(armor);
+                    this.setItemInArmorSlot(slot, armorStack);
+                }
+            }
+        }
+    }
+
     @Override
     public void spawnInit() {
         super.spawnInit();
         setSharedFlag(FLAG_LEFT_HANDED, this.random.nextInt(10) == 0);
+
+        if (ArmorBags.ARMOR_BAGS.containsKey(this.getClass())) {
+            final HashMap<?, ArmorBag<?>> armorBags = ArmorBags.ARMOR_BAGS.get(this.getClass());
+            this.doRandomArmorSpawn(this.world.rand, armorBags);
+        }
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombie.java b/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombie.java
index 2c5b60abe..028c70296 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombie.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombie.java
@@ -10,6 +10,8 @@ import net.minecraft.core.world.weather.Weathers;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.HashMap;
+
 public class MobZombie extends MobMonsterArmored<HumanArmorShape>
 {
     public MobZombie(final @NotNull World world) {
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 2a0280a1b..8d227861f 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
@@ -469,7 +469,7 @@ public abstract class Player
     private void collideWithPlayer(Entity entity)
     {
         entity.playerTouch(this);
-        addStat(StatList.mobEncounterStats.get(EntityDispatcher.idForClass(entity.getClass())), 1);
+        addStat(StatList.mobEncounterStats.get(entity.getDispatcherId()), 1);
     }
 
     public int getScore()
@@ -988,7 +988,7 @@ public abstract class Player
         }
         addStat(StatList.damageTakenStat, damage);
         if (attacker != null) {
-            addStat(StatList.mobEncounterStats.get(EntityDispatcher.idForClass(attacker.getClass())), 1);
+            addStat(StatList.mobEncounterStats.get(attacker.getDispatcherId()), 1);
         }
 
         return super.hurt(attacker, damage, type);
@@ -1180,7 +1180,7 @@ public abstract class Player
                 addStat(StatList.damageDealtStat, i);
             }
         }
-        addStat(StatList.mobEncounterStats.get(EntityDispatcher.idForClass(entity.getClass())), 1);
+        addStat(StatList.mobEncounterStats.get(entity.getDispatcherId()), 1);
     }
 
     public void respawnPlayer()
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemWandSpawner.java b/game/core/src/main/java/net/minecraft/core/item/ItemWandSpawner.java
index 18a402a33..1ee476b3e 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemWandSpawner.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemWandSpawner.java
@@ -52,7 +52,7 @@ public class ItemWandSpawner extends Item implements IDispensable
         return id;
     }
     public void spawnEntity(ItemStack itemStack, World world, double x, double y, double z){
-        Entity entity = EntityDispatcher.createEntityInWorld(getEntityId(itemStack), world);
+        Entity entity = EntityDispatcher.getInstance().createEntityInWorld(getEntityId(itemStack), world);
         if (entity != null){
             entity.setPos(x, y, z);
             entity.spawnInit();
diff --git a/game/core/src/main/java/net/minecraft/core/lang/I18n.java b/game/core/src/main/java/net/minecraft/core/lang/I18n.java
index 1358620a9..0ed43a171 100644
--- a/game/core/src/main/java/net/minecraft/core/lang/I18n.java
+++ b/game/core/src/main/java/net/minecraft/core/lang/I18n.java
@@ -23,7 +23,8 @@ public class I18n {
         INSTANCE = new I18n(languageCode);
     }
 
-    public static @Nullable I18n getInstance() {
+    public static @NotNull I18n getInstance() {
+        assert INSTANCE != null : "I18n must be initialized before an instance is valid!";
         return INSTANCE;
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/arguments/ArgumentTypeEntitySummon.java b/game/core/src/main/java/net/minecraft/core/net/command/arguments/ArgumentTypeEntitySummon.java
index ceb3e209f..143e09f9e 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/arguments/ArgumentTypeEntitySummon.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/arguments/ArgumentTypeEntitySummon.java
@@ -37,9 +37,9 @@ public class ArgumentTypeEntitySummon implements ArgumentType<Class<? extends En
         }
         String string = builder.toString();
 
-        for (NamespaceID entityId : EntityDispatcher.idToClassMap.keySet()) {
-            if (CommandHelper.matchesNamespaceId(entityId, string)) {
-                return EntityDispatcher.classForId(entityId);
+        for (EntityDispatcher.EntityDispatcherEntry<?> entry : EntityDispatcher.getInstance().entries) {
+            if (CommandHelper.matchesNamespaceId(entry.namespaceID, string)) {
+                return entry.entityClass;
             }
         }
         throw new CommandSyntaxException(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(), () -> I18n.getInstance().translateKey("command.argument_types.entity_summon.invalid_entity"));
@@ -48,8 +48,8 @@ public class ArgumentTypeEntitySummon implements ArgumentType<Class<? extends En
     @Override
     public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
         String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
-        for (NamespaceID entityId : EntityDispatcher.idToClassMap.keySet()) {
-            CommandHelper.getStringToSuggest(entityId.toString().toLowerCase(Locale.ROOT), remaining).ifPresent(builder::suggest);
+        for (EntityDispatcher.EntityDispatcherEntry<?> entry : EntityDispatcher.getInstance().entries) {
+            CommandHelper.getStringToSuggest(entry.stringID.toLowerCase(Locale.ROOT), remaining).ifPresent(builder::suggest);
         }
         return builder.buildFuture();
     }
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandHeal.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandHeal.java
index de47f6969..58cf57ddb 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandHeal.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandHeal.java
@@ -29,14 +29,10 @@ public class CommandHeal implements CommandManager.CommandRegistry {
                                     int entitiesAffected = 0;
                                     for (Entity entity : entities) {
                                         if (entity instanceof Mob) {
-                                            int maxHealth = 20;
-                                            try {
-                                                maxHealth = ((Mob)entity.getClass().getConstructor(World.class).newInstance(c.getSource().getWorld())).getMaxHealth();
-                                            } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException ignored) {
-                                            }
-                                            int originalHealth = ((Mob)entity).getHealth();
-                                            ((Mob)entity).setHealthRaw(MathHelper.clamp(((Mob)entity).getHealth() + amount, 0, maxHealth));
-                                            if (((Mob)entity).getHealth() != originalHealth) ++entitiesAffected;
+                                            Mob mob = (Mob)entity;
+                                            int originalHealth = mob.getHealth();
+                                            mob.setHealthRaw(MathHelper.clamp(mob.getHealth() + amount, 0, mob.getMaxHealth()));
+                                            if (mob.getHealth() != originalHealth) ++entitiesAffected;
                                         }
                                     }
                                     c.getSource().sendTranslatableMessage("command.commands.heal.success_" + (entitiesAffected == 1 ? "single" : "multiple"), entitiesAffected);
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandMobSpawning.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandMobSpawning.java
index edcb3581b..ea132ffa7 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandMobSpawning.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandMobSpawning.java
@@ -28,7 +28,7 @@ public class CommandMobSpawning implements CommandManager.CommandRegistry {
                 ArgumentBuilderLiteral.<CommandSource>literal("mobspawning")
                     .requires(CommandSource::hasAdmin)
                     .then(ArgumentBuilderLiteral.<CommandSource>literal("list").executes(c -> {
-                        Collection<NamespaceID> ids = EntityDispatcher.idToClassMap.keySet();
+                        Collection<NamespaceID> ids = EntityDispatcher.getInstance().idToEntryMap.keySet();
                         World world = c.getSource().getWorld();
                         SpawnerConfig config = world.getSpawnerConfig();
                         for (NamespaceID s : ids) {
@@ -46,7 +46,8 @@ public class CommandMobSpawning implements CommandManager.CommandRegistry {
                                     World world = c.getSource().getWorld();
                                     SpawnerConfig config = world.getSpawnerConfig();
 
-                                    NamespaceID mobId = EntityDispatcher.idForClass(c.getArgument("entity", Class.class));
+                                    EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(c.getArgument("entity", Class.class));
+                                    NamespaceID mobId = entry != null ? entry.namespaceID : null;
                                     boolean value = c.getArgument("value", Boolean.class);
                                     config.setMobSpawn(mobId, value);
                                     c.getSource().sendTranslatableMessage("command.commands.mobspawning.set", mobId, value);
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandSummon.java b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandSummon.java
index de5bf0160..895f529c8 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandSummon.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/commands/CommandSummon.java
@@ -7,6 +7,7 @@ import com.mojang.brigadier.builder.ArgumentBuilderLiteral;
 import com.mojang.brigadier.builder.ArgumentBuilderRequired;
 import com.mojang.brigadier.context.CommandContext;
 import net.minecraft.core.entity.Entity;
+import net.minecraft.core.entity.EntityDispatcher;
 import net.minecraft.core.net.command.CommandManager;
 import net.minecraft.core.util.phys.Vec3;
 import net.minecraft.core.world.World;
@@ -63,14 +64,12 @@ public class CommandSummon implements CommandManager.CommandRegistry {
     @SuppressWarnings({"SameParameterValue", "unchecked"})
     private static Entity summonEntityAt(CommandContext<CommandSource> c, double x, double y, double z, float yaw, float pitch) {
         Class<? extends Entity> entityClass = c.getArgument("entity", Class.class);
-        Entity entity;
-        try {
-            entity = entityClass.getConstructor(World.class).newInstance(c.getSource().getWorld());
-        } catch (Exception e) {
-            throw new RuntimeException(e);
+        Entity entity = EntityDispatcher.getInstance().createEntityInWorld(entityClass, c.getSource().getWorld());
+        if (entity == null) {
+            throw new RuntimeException("Could not create entity for '" + String.valueOf(entityClass) + "'");
         }
-        entity.spawnInit();
         entity.moveTo(x, y, z, yaw, pitch);
+        entity.spawnInit();
         c.getSource().getWorld().entityJoinedWorld(entity);
         return entity;
     }
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/helpers/EntitySelectorOptions.java b/game/core/src/main/java/net/minecraft/core/net/command/helpers/EntitySelectorOptions.java
index 5bca2e9ec..0f11eb454 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/helpers/EntitySelectorOptions.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/helpers/EntitySelectorOptions.java
@@ -115,7 +115,7 @@ public class EntitySelectorOptions {
                 }
                 CommandHelper.suggest("!Player", builder);
                 CommandHelper.suggest("Player", builder);
-                for (NamespaceID key : EntityDispatcher.idToClassMap.keySet()) {
+                for (NamespaceID key : EntityDispatcher.getInstance().idToEntryMap.keySet()) {
                     if (!(key.toString().startsWith(string) || (key.namespace().equals("minecraft") && key.value().startsWith(string)))) continue;
                     CommandHelper.suggest("!" + key, builder);
                     if (invert) continue;
@@ -148,10 +148,12 @@ public class EntitySelectorOptions {
             if (type.equals("Player")) {
                 parser.setLimitToType(Player.class);
             } else {
-                if (EntityDispatcher.classForId(type) != null) {
-                    parser.setLimitToType(EntityDispatcher.classForId(type));
-                } else if (EntityDispatcher.classForId("minecraft:" + type) != null) {
-                    parser.setLimitToType(EntityDispatcher.classForId("minecraft:" + type));
+                EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForId(type);
+                EntityDispatcher.EntityDispatcherEntry<?> entry2 = EntityDispatcher.getInstance().entryForId("minecraft:" + type);
+                if (entry != null) {
+                    parser.setLimitToType(entry.entityClass);
+                } else if (entry2 != null) {
+                    parser.setLimitToType(entry2.entityClass);
                 } else {
                     parser.getReader().setCursor(cursor);
                     throw UNKNOWN_ENTITY_TYPE.createWithContext(parser.getReader(), type);
diff --git a/game/core/src/main/java/net/minecraft/core/net/packet/PacketAddMob.java b/game/core/src/main/java/net/minecraft/core/net/packet/PacketAddMob.java
index 0724cb7b7..3a9e44c1f 100644
--- a/game/core/src/main/java/net/minecraft/core/net/packet/PacketAddMob.java
+++ b/game/core/src/main/java/net/minecraft/core/net/packet/PacketAddMob.java
@@ -31,7 +31,7 @@ public class PacketAddMob extends Packet
     public PacketAddMob(Mob mob)
     {
         id = mob.id;
-        type = (short) EntityDispatcher.numericIdForClass(mob.getClass());
+        type = (short) EntityDispatcher.getInstance().entryForClass(mob.getClass()).numericID;
         x = MathHelper.floor(mob.x * 32D);
         y = MathHelper.floor(mob.y * 32D);
         z = MathHelper.floor(mob.z * 32D);
diff --git a/game/core/src/main/java/net/minecraft/core/net/packet/PacketSetMobSpawner.java b/game/core/src/main/java/net/minecraft/core/net/packet/PacketSetMobSpawner.java
index 0f9784a70..ac4cf849f 100644
--- a/game/core/src/main/java/net/minecraft/core/net/packet/PacketSetMobSpawner.java
+++ b/game/core/src/main/java/net/minecraft/core/net/packet/PacketSetMobSpawner.java
@@ -1,6 +1,5 @@
 package net.minecraft.core.net.packet;
 
-import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityDispatcher;
 import net.minecraft.core.net.handler.PacketHandler;
 import org.jetbrains.annotations.Nullable;
@@ -13,18 +12,18 @@ public class PacketSetMobSpawner extends Packet {
     public int xPosition;
     public int yPosition;
     public int zPosition;
-    public @Nullable Class<? extends Entity> entityClass;
+    public @Nullable EntityDispatcher.EntityDispatcherEntry<?> dispatcherEntry;
 
     public PacketSetMobSpawner() {
         isChunkDataPacket = true;
     }
 
-    public PacketSetMobSpawner(int x, int y, int z, @Nullable Class<? extends Entity> entityClass) {
+    public PacketSetMobSpawner(int x, int y, int z, @Nullable EntityDispatcher.EntityDispatcherEntry<?> dispatcherEntry) {
         isChunkDataPacket = true;
         this.xPosition = x;
         this.yPosition = y;
         this.zPosition = z;
-        this.entityClass = entityClass;
+        this.dispatcherEntry = dispatcherEntry;
     }
 
     @Override
@@ -34,9 +33,9 @@ public class PacketSetMobSpawner extends Packet {
         zPosition = dis.readInt();
         int type = dis.readShort() & 0xFFFF;
         if (type == 0xFFFF) {
-            entityClass = null;
+            dispatcherEntry = null;
         } else {
-            entityClass = EntityDispatcher.classForNumericId(type);
+            dispatcherEntry = EntityDispatcher.getInstance().entryForNumericId(type);
         }
     }
 
@@ -45,7 +44,11 @@ public class PacketSetMobSpawner extends Packet {
         dos.writeInt(xPosition);
         dos.writeShort(yPosition);
         dos.writeInt(zPosition);
-        dos.writeShort(entityClass == null ? 0xFFFF : EntityDispatcher.numericIdForClass(entityClass));
+        if (dispatcherEntry != null) {
+            dos.writeShort(dispatcherEntry.numericID);
+        } else {
+            dos.writeShort(0xFFFF);
+        }
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/util/helper/MathHelper.java b/game/core/src/main/java/net/minecraft/core/util/helper/MathHelper.java
index ec7b6ad36..5729f3a75 100644
--- a/game/core/src/main/java/net/minecraft/core/util/helper/MathHelper.java
+++ b/game/core/src/main/java/net/minecraft/core/util/helper/MathHelper.java
@@ -1,6 +1,8 @@
 package net.minecraft.core.util.helper;
 
 
+import org.jetbrains.annotations.Nullable;
+
 public class MathHelper
 {
 
@@ -122,9 +124,9 @@ public class MathHelper
 		return MathHelper.lerp(val0, val1, index - index0);
 	}
 
-    public static boolean stringNullOrLengthZero(String s)
+    public static boolean stringNullOrLengthZero(@Nullable String s)
     {
-        return s == null || s.length() == 0;
+        return s == null || s.isEmpty();
     }
     public static int ceilInt(int num, int denom){
         return (int) Math.ceil(((float)num/denom));
diff --git a/game/core/src/main/java/net/minecraft/core/world/Explosion.java b/game/core/src/main/java/net/minecraft/core/world/Explosion.java
index efaa9e170..e74c5c8be 100644
--- a/game/core/src/main/java/net/minecraft/core/world/Explosion.java
+++ b/game/core/src/main/java/net/minecraft/core/world/Explosion.java
@@ -4,6 +4,7 @@ import net.minecraft.core.block.Block;
 import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.data.gamerule.GameRules;
 import net.minecraft.core.entity.Entity;
+import net.minecraft.core.entity.IArmorWearing;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.sound.SoundCategory;
@@ -167,6 +168,10 @@ public class Explosion {
                 double d13 = (1.0D - d4) * d12;
                 entity.hurt(exploder, (int) (((d13 * d13 + d13) / 2D) * 8D * (double) explosionSize2 + 1.0D), DamageType.BLAST);
                 double flingForce = d13 * 2;
+                if (entity instanceof IArmorWearing) {
+                    float proc = 1 - (((IArmorWearing<?>) entity).getTotalProtectionAmount(DamageType.BLAST) / 2f);
+                    flingForce *= proc;
+                }
                 entity.fling(xComp * flingForce, yComp * flingForce, zComp * flingForce, 1);
             }
         }
diff --git a/game/core/src/main/java/net/minecraft/core/world/ExplosionCannonball.java b/game/core/src/main/java/net/minecraft/core/world/ExplosionCannonball.java
index 329f6a6f6..c64ed88a3 100644
--- a/game/core/src/main/java/net/minecraft/core/world/ExplosionCannonball.java
+++ b/game/core/src/main/java/net/minecraft/core/world/ExplosionCannonball.java
@@ -2,6 +2,7 @@ package net.minecraft.core.world;
 
 import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.entity.Entity;
+import net.minecraft.core.entity.IArmorWearing;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.sound.SoundCategory;
 import net.minecraft.core.util.helper.MathHelper;
@@ -94,17 +95,22 @@ public class ExplosionCannonball extends Explosion{
             double d4 = entity.distanceTo(explosionX, explosionY, explosionZ) / (double)explosionSize;
             if(d4 <= 1.0D)
             {
-                double d6 = entity.x - explosionX;
-                double d8 = entity.y - explosionY;
-                double d10 = entity.z - explosionZ;
-                double d11 = MathHelper.sqrt(d6 * d6 + d8 * d8 + d10 * d10);
-                d6 *= 2.5;
-                d8 /= d11;
-                d10 *= 2.5;
+                double xComp = entity.x - explosionX;
+                double yComp = entity.y - explosionY;
+                double zComp = entity.z - explosionZ;
+                double d11 = MathHelper.sqrt(xComp * xComp + yComp * yComp + zComp * zComp);
+                xComp *= 2.5;
+                yComp /= d11;
+                yComp += 0.5f;
+                zComp *= 2.5;
                 double d12 = worldObj.getSeenPercent(vec3, entity.bb);
-                double d13 = (1.0D - d4) * d12;
-                entity.hurt(exploder, (int)(((d13 * d13 + d13) / 2D) * 8D * (double)explosionSize + 1.0D), DamageType.BLAST);
-                entity.fling(d6 * d13, (d8 * d13) + 0.5F, d10 * d13, 1);
+                double flingForce = (1.0D - d4) * d12;
+                entity.hurt(exploder, (int)(((flingForce * flingForce + flingForce) / 2D) * 8D * (double)explosionSize + 1.0D), DamageType.BLAST);
+                if (entity instanceof IArmorWearing && entity != exploder) {
+                    float proc = 1 - (((IArmorWearing<?>) entity).getTotalProtectionAmount(DamageType.BLAST) / 2f);
+                    flingForce *= proc;
+                }
+                entity.fling(xComp * flingForce, yComp * flingForce, zComp * flingForce, 1);
             }
         }
 
diff --git a/game/core/src/main/java/net/minecraft/core/world/SpawnerMobs.java b/game/core/src/main/java/net/minecraft/core/world/SpawnerMobs.java
index aa187ae6f..7dfac1349 100644
--- a/game/core/src/main/java/net/minecraft/core/world/SpawnerMobs.java
+++ b/game/core/src/main/java/net/minecraft/core/world/SpawnerMobs.java
@@ -4,6 +4,7 @@ import com.mojang.logging.LogUtils;
 import net.minecraft.core.block.BlockLogicBed;
 import net.minecraft.core.block.material.Material;
 import net.minecraft.core.data.gamerule.GameRules;
+import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityDispatcher;
 import net.minecraft.core.entity.Mob;
 import net.minecraft.core.entity.SpawnListEntry;
@@ -32,12 +33,12 @@ import java.util.Set;
 public final class SpawnerMobs
 {
     private static final Logger LOGGER = LogUtils.getLogger();
-    private static final Set<ChunkCoordinate> eligibleChunksForSpawning = new HashSet<>();
-    private static final Class<?>[] nightSpawnEntities;
+    private static final Set<ChunkCoordinate> ELIGIBLE_CHUNKS_FOR_SPAWNING = new HashSet<>();
+    private static final Class<? extends Mob>[] NIGHT_SPAWN_ENTITIES;
 
     static
     {
-        nightSpawnEntities = new Class[] {
+        NIGHT_SPAWN_ENTITIES = new Class[] {
             MobSpider.class, MobZombie.class, MobSkeleton.class, MobZombieArmored.class
         };
     }
@@ -57,7 +58,7 @@ public final class SpawnerMobs
     public static int performSpawning(World world, SpawnerConfig spawnerConfig)
     {
         if(!spawnerConfig.canHostileSpawn(world) && !spawnerConfig.canPassiveSpawn(world)) { return 0; }
-        eligibleChunksForSpawning.clear();
+        ELIGIBLE_CHUNKS_FOR_SPAWNING.clear();
 
         for (Player player : world.players){
             final int playerChunkX = MathHelper.floor(player.x / (double) Chunk.CHUNK_SIZE_X);
@@ -67,7 +68,7 @@ public final class SpawnerMobs
             for(int dx = -spawnRadius; dx <= spawnRadius; ++dx) {
                 for(int dz = -spawnRadius; dz <= spawnRadius; ++dz) {
                     if(world.isChunkLoaded(dx + playerChunkX, dz + playerChunkZ)) {
-                        eligibleChunksForSpawning.add(new ChunkCoordinate(dx + playerChunkX, dz + playerChunkZ));
+                        ELIGIBLE_CHUNKS_FOR_SPAWNING.add(new ChunkCoordinate(dx + playerChunkX, dz + playerChunkZ));
                     }
                 }
             }
@@ -87,10 +88,10 @@ public final class SpawnerMobs
 
             if (creatureType.isPeaceful() && !spawnerConfig.canPassiveSpawn(world)) continue;
             if (!creatureType.isPeaceful() && !spawnerConfig.canHostileSpawn(world)) continue;
-            if (world.countEntities(creatureType.getBaseClass()) > ((creatureType.getMaxCreaturesPerChunk() * eligibleChunksForSpawning.size()) / 256))
+            if (world.countEntities(creatureType.getBaseClass()) > ((creatureType.getMaxCreaturesPerChunk() * ELIGIBLE_CHUNKS_FOR_SPAWNING.size()) / 256))
                 continue;
 
-            chunkLoop: for (ChunkCoordinate chunk : eligibleChunksForSpawning) {
+            chunkLoop: for (ChunkCoordinate chunk : ELIGIBLE_CHUNKS_FOR_SPAWNING) {
                 boolean checkSpawnDist = false;
                 for (ChunkCoordinate c : spawnChunks){
                     if (c.equals(chunk)){
@@ -127,7 +128,8 @@ public final class SpawnerMobs
                     }
                 }
 
-                if (!spawnerConfig.canMobSpawn(EntityDispatcher.idForClass(spawnListEntry.entityClass))) continue;
+                EntityDispatcher.EntityDispatcherEntry<?> entry = EntityDispatcher.getInstance().entryForClass(spawnListEntry.entityClass);
+                if (entry == null || !spawnerConfig.canMobSpawn(entry.namespaceID)) continue;
 
                 final ChunkPosition pos = getRandomSpawningPointInChunk(world, chunk.x, chunk.z);
                 final int x = pos.x;
@@ -166,7 +168,7 @@ public final class SpawnerMobs
 
                         Mob mobToSpawn;
                         try {
-                            mobToSpawn = (Mob) spawnListEntry.entityClass.getConstructor(new Class[]{World.class}).newInstance(new Object[]{world});
+                            mobToSpawn = (Mob) EntityDispatcher.getInstance().createEntityInWorld(spawnListEntry.entityClass, world);
                         } catch (Exception e) {
                             LOGGER.error("Error spawning entity class '{}' class missing default constructor for World! Skipping spawn attempt!", spawnListEntry.entityClass.getSimpleName(), e);
                             return totalSpawned;
@@ -213,7 +215,7 @@ public final class SpawnerMobs
             return false;
         }
 
-        Class<?>[] entityClasses = nightSpawnEntities;
+        Class<? extends Mob>[] entityClasses = NIGHT_SPAWN_ENTITIES;
         if (entityClasses != null && entityClasses.length != 0) {
             boolean willSpawnEntity = false;
             PathFinder pathFinder = new PathFinder(world);
@@ -249,9 +251,9 @@ public final class SpawnerMobs
                     float entityZ = z + 0.5f;
 
                     Mob entity;
-                    Class<?> eClass = entityClasses[world.rand.nextInt(entityClasses.length)];
+                    Class<? extends Mob> eClass = entityClasses[world.rand.nextInt(entityClasses.length)];
                     try {
-                        entity = (Mob) eClass.getConstructor(World.class).newInstance(world);
+                        entity = EntityDispatcher.getInstance().createEntityInWorld(eClass, world);
                     } catch (Exception e) {
                         LOGGER.error("Exception instancing class '{}'!", eClass.getSimpleName(), e);
                         return false;
@@ -301,7 +303,7 @@ public final class SpawnerMobs
                 continue;
             }
 
-			Class<?>[] entityClasses = nightSpawnEntities;
+			Class<? extends Mob>[] entityClasses = NIGHT_SPAWN_ENTITIES;
 			if (entityClasses != null && entityClasses.length != 0)
             {
 				boolean spawnedEntityForPlayer = false;
@@ -340,10 +342,10 @@ public final class SpawnerMobs
 						float entityZ = z + 0.5f;
 
 						Mob entity;
-                        Class<?> eClass= entityClasses[world.rand.nextInt(entityClasses.length)];
+                        Class<? extends Mob> eClass= entityClasses[world.rand.nextInt(entityClasses.length)];
 						try
                         {
-							entity = (Mob) eClass.getConstructor(World.class).newInstance(world);
+							entity = EntityDispatcher.getInstance().createEntityInWorld(eClass, world);
 						}
                         catch (Exception e)
                         {
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 83f15db1f..6e39f17b4 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
@@ -12,9 +12,7 @@ import java.util.TreeSet;
 import java.util.UUID;
 
 import com.mojang.logging.LogUtils;
-import com.mojang.nbt.tags.CompoundTag;
 
-import net.minecraft.core.block.BlockLogicOreRedstone;
 import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.current.wire.WireHandler;
 import net.minecraft.core.entity.EntityItem;
@@ -61,6 +59,7 @@ import net.minecraft.core.world.pathfinder.PathFinder;
 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.saveddata.SavedData;
 import net.minecraft.core.world.saveddata.SavedDataStorage;
 import net.minecraft.core.world.season.SeasonManager;
@@ -193,10 +192,6 @@ public class World implements WorldSource {
             levelData = new LevelData(seed, name);
             isNewWorld = true;
         }
-        else
-        {
-            levelData.setWorldName(name);
-        }
 
         if (dimensionData == null)
         {
@@ -215,7 +210,7 @@ public class World implements WorldSource {
 
         chunkProvider = createChunkProvider();
 
-        auroraProvider = new AuroraProvider(this, seed);
+        auroraProvider = new AuroraProvider(this, this.levelData.getRandomSeed());
 
         seasonManager = SeasonManager.fromConfig(this, this.worldType.getSeasonConfig());
 
@@ -293,7 +288,7 @@ public class World implements WorldSource {
 
         this.weatherManager = new WeatherManager(this);
 
-        auroraProvider = new AuroraProvider(this, seed);
+        auroraProvider = new AuroraProvider(this, this.levelData.getRandomSeed());
 
         biomeProvider = this.worldType.createBiomeProvider(this);
 
@@ -362,15 +357,15 @@ public class World implements WorldSource {
         return getBlockId(x, y, z);
     }
 
-    public void spawnPlayerWithLoadedChunks(Player player)
+    public void spawnPlayerWithLoadedChunks(Player player, boolean respawning)
     {
         try
         {
-            CompoundTag nbttagcompound = levelData.getPlayerNBTTagCompound();
-            if(nbttagcompound != null)
-            {
-                player.load(nbttagcompound);
-                levelData.setPlayerNBTTagCompound(null);
+            if (!respawning) {
+                PlayerIO playerIO = saveHandler.getPlayerFileData();
+                if (playerIO != null) {
+                    playerIO.load(player);
+                }
             }
             int chunkX = MathHelper.floor_float((int)player.x) / Chunk.CHUNK_SIZE_X;
             int chunkZ = MathHelper.floor_float((int)player.z) / Chunk.CHUNK_SIZE_Z;
@@ -2132,11 +2127,6 @@ public class World implements WorldSource {
         }
     }
 
-    public Entity findSubclassOf(Class<? extends Entity> clazz) // this is what the b1.2 mappings called this
-    {
-        return null;
-    }
-
     public @Nullable Entity getEntityByID(int id)
     {
         for(int i = 0; i < loadedEntityList.size(); i++)
diff --git a/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkLoaderLegacy.java b/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkLoaderLegacy.java
index 0450575ff..4b0154c8b 100644
--- a/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkLoaderLegacy.java
+++ b/game/core/src/main/java/net/minecraft/core/world/chunk/ChunkLoaderLegacy.java
@@ -286,7 +286,7 @@ public class ChunkLoaderLegacy
                 if (entityTagBase instanceof CompoundTag)
                 {
                     CompoundTag entityTag = (CompoundTag) entityTagBase;
-                    Entity entity = EntityDispatcher.createEntityFromNBT(entityTag, world);
+                    Entity entity = EntityDispatcher.getInstance().createEntityFromNBT(entityTag, world);
                     chunk.hasEntities = true;
                     if (entity != null)
                     {
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/overworld/ChunkDecoratorOverworld.java b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/overworld/ChunkDecoratorOverworld.java
index 7195f0743..8ccca75ff 100644
--- a/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/overworld/ChunkDecoratorOverworld.java
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/chunk/perlin/overworld/ChunkDecoratorOverworld.java
@@ -131,10 +131,29 @@ public class ChunkDecoratorOverworld
             int j5 = x + rand.nextInt(16) + 8;
             int k8 = minY + rand.nextInt(rangeY);
             int j11 = z + rand.nextInt(16) + 8;
-            if (rand.nextInt(2) == 0) {
+
+            int design = rand.nextInt(2);
+            if (design == 1) {
+                int id = world.getBlockId(j5, k8, j11);
+                if (id == Blocks.LIMESTONE.id()) {
+                    design = 2;
+                } else if (id == Blocks.GRANITE.id()) {
+                    design = 3;
+                } else if (id == Blocks.BASALT.id()) {
+                    design = 4;
+                }
+            }
+
+            if (design == 0) {
                 new WorldFeatureDungeon(Blocks.BRICK_CLAY.id(), Blocks.BRICK_CLAY.id(), null).place(world, rand, j5, k8, j11);
-            } else {
+            } else if (design == 1) {
                 new WorldFeatureDungeon(Blocks.COBBLE_STONE.id(), Blocks.COBBLE_STONE_MOSSY.id(), null).place(world, rand, j5, k8, j11);
+            } else if (design == 2) {
+                new WorldFeatureDungeon(Blocks.COBBLE_LIMESTONE.id(), Blocks.COBBLE_LIMESTONE_MOSSY.id(), null).place(world, rand, j5, k8, j11);
+            } else if (design == 3) {
+                new WorldFeatureDungeon(Blocks.COBBLE_GRANITE.id(), Blocks.COBBLE_GRANITE_MOSSY.id(), null).place(world, rand, j5, k8, j11);
+            } else if (design == 4) {
+                new WorldFeatureDungeon(Blocks.COBBLE_BASALT.id(), Blocks.COBBLE_BASALT_MOSSY.id(), null).place(world, rand, j5, k8, j11);
             }
         }
 
diff --git a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureLabyrinth.java b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureLabyrinth.java
index f652ee87d..c02f98658 100644
--- a/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureLabyrinth.java
+++ b/game/core/src/main/java/net/minecraft/core/world/generate/feature/WorldFeatureLabyrinth.java
@@ -61,7 +61,7 @@ public class WorldFeatureLabyrinth extends WorldFeature
             brickBlockB = Blocks.BRICK_SANDSTONE.id();
             slabBlock = Blocks.SLAB_SANDSTONE.id();
         }
-        if (biome == Biomes.OVERWORLD_TAIGA || biome == Biomes.OVERWORLD_TUNDRA || biome == Biomes.OVERWORLD_GLACIER)
+        else if (biome == Biomes.OVERWORLD_TAIGA || biome == Biomes.OVERWORLD_TUNDRA || biome == Biomes.OVERWORLD_GLACIER)
         {
             wallBlockA = Blocks.PERMAFROST.id();
             wallBlockB = Blocks.COBBLE_PERMAFROST.id();
@@ -69,6 +69,40 @@ public class WorldFeatureLabyrinth extends WorldFeature
             brickBlockB = Blocks.BRICK_PERMAFROST.id();
             isCold = true;
         }
+        else
+        {
+            int design = 0;
+            final int id = world.getBlockId(x, y - 5, z);
+            if (id == Blocks.LIMESTONE.id()) {
+                design = 1;
+            } else if (id == Blocks.GRANITE.id()) {
+                design = 2;
+            } else if (id == Blocks.BASALT.id()) {
+                design = 3;
+            }
+
+            if (design == 1) // Limestone
+            {
+                wallBlockA = Blocks.COBBLE_LIMESTONE.id();
+                wallBlockB = Blocks.COBBLE_LIMESTONE_MOSSY.id();
+                brickBlockA = brickBlockB = Blocks.BRICK_LIMESTONE.id();
+                slabBlock = Blocks.SLAB_LIMESTONE_POLISHED.id();
+            }
+            else if (design == 2) // Granite
+            {
+                wallBlockA = Blocks.COBBLE_GRANITE.id();
+                wallBlockB = Blocks.COBBLE_GRANITE_MOSSY.id();
+                brickBlockA = brickBlockB = Blocks.BRICK_GRANITE.id();
+                slabBlock = Blocks.SLAB_GRANITE_POLISHED.id();
+            }
+            else if (design == 3) // Basalt
+            {
+                wallBlockA = Blocks.COBBLE_BASALT.id();
+                wallBlockB = Blocks.COBBLE_BASALT_MOSSY.id();
+                brickBlockA = brickBlockB = Blocks.BRICK_BASALT.id();
+                slabBlock = Blocks.SLAB_BASALT_POLISHED.id();
+            }
+        }
 
         this.chestLoot = new WeightedRandomBag<>();
         chestLoot.addEntry(new WeightedRandomLootObject(Items.INGOT_IRON.getDefaultStack(), 1, 6), 100);
@@ -509,7 +543,7 @@ public class WorldFeatureLabyrinth extends WorldFeature
                                 if (x == blockX || z == blockZ) {
                                     world.setBlockWithNotify(x, y, z, isCold ? Blocks.OBSIDIAN.id() : Blocks.FLUID_LAVA_FLOWING.id());
                                 } else {
-                                    world.setBlockWithNotify(x, y, z, isCold ? Blocks.PERMAFROST_POLISHED.id() : Blocks.STONE_POLISHED.id());
+                                    world.setBlockWithNotify(x, y, z, slabBlock);
                                 }
                             else if (random.nextInt(5) == 0)
                                 world.setBlockWithNotify(x, y, z, wallBlockB);
@@ -543,9 +577,9 @@ public class WorldFeatureLabyrinth extends WorldFeature
         for (int x = blockX - 1; x <= blockX + 1; x++) {
             for (int z = blockZ - 1; z <= blockZ + 1; z++) {
                 if (x == blockX && z == blockZ) {
-                    world.setBlockAndMetadataWithNotify(x, blockY - 1, z, isCold ? Blocks.SLAB_PERMAFROST_POLISHED.id() : Blocks.SLAB_STONE_POLISHED.id(), 1);
+                    world.setBlockAndMetadataWithNotify(x, blockY - 1, z, slabBlock, 1);
                 } else {
-                    world.setBlockWithNotify(x, blockY - 1, z, isCold ? Blocks.SLAB_PERMAFROST_POLISHED.id() : Blocks.SLAB_STONE_POLISHED.id());
+                    world.setBlockWithNotify(x, blockY - 1, z, slabBlock);
                 }
             }
         }
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/LevelData.java b/game/core/src/main/java/net/minecraft/core/world/save/LevelData.java
index b1e00bbfd..73b7bca1d 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/LevelData.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/LevelData.java
@@ -3,17 +3,17 @@ package net.minecraft.core.world.save;
 import com.mojang.logging.LogUtils;
 import net.minecraft.core.enums.Difficulty;
 import net.minecraft.core.data.gamerule.GameRuleCollection;
-import net.minecraft.core.entity.player.Player;
 import com.mojang.nbt.tags.CompoundTag;
 import com.mojang.nbt.NbtIo;
 import net.minecraft.core.world.config.spawning.SpawnerConfig;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
-import java.util.List;
+import java.util.UUID;
 
 public class LevelData
 {
@@ -25,9 +25,13 @@ public class LevelData
     private long worldTime;
     private long lastTimePlayed;
     private long sizeOnDisk;
+    @Deprecated
     private CompoundTag playerTag;
+    @Deprecated
     private int dimension;
+    @Deprecated
     private int gamemode;
+    private @Nullable UUID lastPlayerUUID;
     private @NotNull String levelName = "DEFAULT NAME";
     private int saveVersion;
     private boolean cheatsEnabled;
@@ -78,6 +82,7 @@ public class LevelData
         spawnerConfig = levelData.getSpawnerConfig();
         difficulty = levelData.difficulty;
         difficultyLocked = levelData.difficultyLocked;
+        lastPlayerUUID = levelData.lastPlayerUUID;
     }
 
     public LevelData(File worldDir)
@@ -126,30 +131,12 @@ public class LevelData
 
     public CompoundTag getNBTTagCompound()
     {
-        CompoundTag nbttagcompound = new CompoundTag();
-        updateTagCompound(nbttagcompound, playerTag);
-        return nbttagcompound;
+        CompoundTag tag = new CompoundTag();
+        updateTagCompound(tag);
+        return tag;
     }
 
-    public CompoundTag getNBTTagCompoundWithPlayer(List<Player> list)
-    {
-        CompoundTag nbttagcompound = new CompoundTag();
-        Player entityplayer = null;
-        CompoundTag nbttagcompound1 = null;
-        if(!list.isEmpty())
-        {
-            entityplayer = list.get(0);
-        }
-        if(entityplayer != null)
-        {
-            nbttagcompound1 = new CompoundTag();
-            entityplayer.saveWithoutId(nbttagcompound1);
-        }
-        updateTagCompound(nbttagcompound, nbttagcompound1);
-        return nbttagcompound;
-    }
-
-    private void updateTagCompound(CompoundTag levelTag, CompoundTag playerTag)
+    private void updateTagCompound(CompoundTag levelTag)
     {
         levelTag.putLong("RandomSeed", randomSeed);
         levelTag.putInt("SpawnX", spawnX);
@@ -163,15 +150,15 @@ public class LevelData
         levelTag.putBoolean("CheatsEnabled", cheatsEnabled);
         levelTag.putInt("Difficulty", difficulty.id());
         levelTag.putBoolean("DifficultyLock", difficultyLocked);
-        if(playerTag != null)
-        {
-            levelTag.putCompound("Player", playerTag);
-        }
 
         CompoundTag gameRulesTag = new CompoundTag();
         GameRuleCollection.writeToNBT(gameRulesTag, gameRules);
         levelTag.put("GameRules", gameRulesTag);
 
+        if (lastPlayerUUID != null) {
+            levelTag.putString("LastPlayerUUID", lastPlayerUUID.toString());
+        }
+
         if (spawnerConfig != null) {
             CompoundTag spawnerConfigTag = new CompoundTag();
             spawnerConfig.writeToTag(spawnerConfigTag);
@@ -208,6 +195,9 @@ public class LevelData
         {
             gameRules = new GameRuleCollection();
         }
+        if (tag.containsKey("LastPlayerUUID")) {
+            lastPlayerUUID = UUID.fromString(tag.getString("LastPlayerUUID"));
+        }
 
         if (tag.containsKey("SpawnConfig")) {
             CompoundTag spawnConfigTag = tag.getCompound("SpawnConfig");
@@ -247,16 +237,22 @@ public class LevelData
         return sizeOnDisk;
     }
 
+    @Deprecated
     public CompoundTag getPlayerNBTTagCompound()
     {
         return playerTag;
     }
 
+    @Deprecated
     public int getDimension()
     {
         return dimension;
     }
 
+    public @Nullable UUID getLastPlayerUUID() {
+        return lastPlayerUUID;
+    }
+
     public void setSpawnX(int x)
     {
         spawnX = x;
@@ -282,9 +278,14 @@ public class LevelData
         sizeOnDisk = size;
     }
 
-    public void setPlayerNBTTagCompound(CompoundTag nbttagcompound)
+    @Deprecated
+    public void setPlayerNBTTagCompound(CompoundTag tag)
     {
-        playerTag = nbttagcompound;
+        playerTag = tag;
+    }
+
+    public void setLastPlayerUUID(@Nullable UUID lastPlayerUUID) {
+        this.lastPlayerUUID = lastPlayerUUID;
     }
 
     public void setSpawn(int x, int y, int z)
@@ -319,11 +320,13 @@ public class LevelData
         return lastTimePlayed;
     }
 
+    @Deprecated
     public int getGamemode()
     {
     	return gamemode;
     }
 
+    @Deprecated
     public void setGamemode(int gamemode)
     {
     	this.gamemode = gamemode;
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/LevelStorage.java b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorage.java
index f09593fce..a74037d40 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/LevelStorage.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/LevelStorage.java
@@ -1,13 +1,17 @@
 package net.minecraft.core.world.save;
 
-import net.minecraft.core.entity.player.Player;
 import com.mojang.nbt.tags.CompoundTag;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.world.chunk.IChunkLoader;
 import net.minecraft.core.world.Dimension;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
 import java.io.File;
 import java.util.List;
+import java.util.UUID;
 
-public interface LevelStorage
+public interface LevelStorage extends PlayerIO
 {
 	/**
 	 * Loads the level data for this save.
@@ -35,10 +39,11 @@ public interface LevelStorage
 
 	/**
 	 * Saves the level data and data for any players.
+
 	 * @param levelData The level data to save.
 	 * @param playerList A list of players whose data should be saved.
 	 */
-	void saveLevelDataAndPlayerData(LevelData levelData, List<Player> playerList);
+	void saveLevelDataAndPlayerData(@NotNull LevelData levelData, @NotNull List<Player> playerList);
 
 	/**
 	 * Saves the level data.
@@ -80,7 +85,11 @@ public interface LevelStorage
 	 */
 	void saveDimensionDataRaw(int dimensionId, CompoundTag dimensionDataTag);
 
-	PlayerIO getPlayerFileData();
+	@Nullable PlayerIO getPlayerFileData();
+
+	@Nullable CompoundTag getPlayerData(@Nullable String username, @NotNull UUID uuid);
 
 	File getDataFile(String fileName);
+
+
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/PlayerIO.java b/game/core/src/main/java/net/minecraft/core/world/save/PlayerIO.java
index 64496b8ce..296490a3c 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/PlayerIO.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/PlayerIO.java
@@ -1,11 +1,13 @@
 package net.minecraft.core.world.save;
 
 import net.minecraft.core.entity.player.Player;
+import org.jetbrains.annotations.NotNull;
 
 public interface PlayerIO
 {
 
-    void save(Player player);
+    void save(@NotNull Player player);
+
+    void load(@NotNull Player player);
 
-    void load(Player player);
 }
diff --git a/game/core/src/main/java/net/minecraft/core/world/save/SaveFile.java b/game/core/src/main/java/net/minecraft/core/world/save/SaveFile.java
index 55deba72b..3e23aa206 100644
--- a/game/core/src/main/java/net/minecraft/core/world/save/SaveFile.java
+++ b/game/core/src/main/java/net/minecraft/core/world/save/SaveFile.java
@@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.Map;
 import java.util.Objects;
 
 public class SaveFile
@@ -24,12 +23,12 @@ public class SaveFile
         this.corrupted = levelData == null || (!needsConversion && dimensionData.get(0) == null);
     }
 
-    public String getFileName()
+    public @NotNull String getFileName()
     {
         return fileName;
     }
 
-    public String getDisplayName()
+    public @NotNull String getDisplayName()
     {
         return displayName;
     }
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 0e29802b1..d479aba76 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
@@ -8,14 +8,13 @@ import net.minecraft.core.util.helper.MathHelper;
 import com.mojang.nbt.tags.CompoundTag;
 import com.mojang.nbt.NbtIo;
 import net.minecraft.core.world.Dimension;
+import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 
 import java.io.File;
 import java.nio.file.Files;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 public abstract class SaveFormatBase
     implements ISaveFormat
@@ -23,6 +22,8 @@ public abstract class SaveFormatBase
     private static final Logger LOGGER = LogUtils.getLogger();
     protected final File savesDir;
 
+    public @NotNull SaveHandlerBase.SaveHandlerFactory factory = SaveHandlerCore::new;
+
     public SaveFormatBase(File savesDir)
     {
         if(!savesDir.exists())
@@ -219,7 +220,7 @@ public abstract class SaveFormatBase
     @Override
     public LevelStorage getSaveHandler(String worldDirName, boolean isMultiplayer)
     {
-        return new SaveHandlerCore(this, savesDir, worldDirName, isMultiplayer);
+        return factory.apply(this, savesDir, worldDirName);
     }
 
     protected static void deleteFilesRecursively(File[] dirFiles)
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/SaveHandlerBase.java
index b0b2405af..e33df5c6a 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/SaveHandlerBase.java
@@ -2,20 +2,24 @@ package net.minecraft.core.world.save;
 
 import com.mojang.logging.LogUtils;
 import net.minecraft.core.MinecraftException;
-import net.minecraft.core.entity.player.Player;
 import com.mojang.nbt.tags.CompoundTag;
 import com.mojang.nbt.NbtIo;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.chunk.ChunkLoaderLegacy;
 import net.minecraft.core.world.chunk.IChunkLoader;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.util.List;
+import java.util.UUID;
 
 public abstract class SaveHandlerBase implements LevelStorage
 {
@@ -27,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, boolean isMultiplayer)
+	public SaveHandlerBase(ISaveFormat saveFormat, File savesDir, String worldDirName)
 	{
 		this.worldDirName = worldDirName;
 		this.saveFormat = saveFormat;
@@ -36,10 +40,7 @@ public abstract class SaveHandlerBase implements LevelStorage
 		playersDirectory = new File(saveDirectory, "players");
 		dataDirectory = new File(saveDirectory, "data");
 		dataDirectory.mkdirs();
-		if (isMultiplayer)
-		{
-			playersDirectory.mkdirs();
-		}
+		playersDirectory.mkdirs();
 		lockSession();
 	}
 
@@ -116,9 +117,9 @@ public abstract class SaveHandlerBase implements LevelStorage
 	}
 
 	@Override
-	public void saveLevelDataAndPlayerData(LevelData levelData, List<Player> playerList)
+	public void saveLevelDataAndPlayerData(@NotNull LevelData levelData, @NotNull List<Player> playerList)
 	{
-		CompoundTag dataTag = levelData.getNBTTagCompoundWithPlayer(playerList);
+		CompoundTag dataTag = levelData.getNBTTagCompound();
 		CompoundTag savedTag = new CompoundTag();
 		savedTag.put("Data", dataTag);
 		try
@@ -146,6 +147,14 @@ public abstract class SaveHandlerBase implements LevelStorage
 		{
 			LOGGER.error("Failed to save level data to disk!", exception);
 		}
+
+		for (Player player : playerList) {
+			try {
+				save(player);
+			} catch (Exception e) {
+				LOGGER.error("Failed to save player data for player '{}'!", player.username, e);
+			}
+		}
 	}
 
 	@Override
@@ -237,10 +246,79 @@ public abstract class SaveHandlerBase implements LevelStorage
 		return new File(dataDirectory, fileName + ".dat");
 	}
 
+	@Override
+	public void save(@NotNull Player player)
+	{
+		try
+		{
+			CompoundTag compoundTag = new CompoundTag();
+			player.saveWithoutId(compoundTag);
+			compoundTag.putInt("Format", 1);
+			File temp = new File(playersDirectory, "_tmp_.dat");
+			File save = new File(playersDirectory, player.uuid + ".dat");
+			NbtIo.writeCompressed(compoundTag, new FileOutputStream(temp));
+			if(save.exists())
+			{
+				save.delete();
+			}
+			temp.renameTo(save);
+		}
+		catch(Exception exception)
+		{
+			LOGGER.warn("Failed to save player data for " + player.username);
+		}
+	}
+
+	@Override
+	public void load(@NotNull Player player)
+	{
+		CompoundTag tag = getPlayerData(player.username, player.uuid);
+		if(tag != null)
+		{
+			player.load(tag);
+			return;
+		}
+		LevelData data;
+		if ((data = getLevelData()) != null){
+			tag = data.getPlayerNBTTagCompound();
+			if (tag != null) {
+				player.load(tag);
+			}
+		}
+	}
+
 	@Override
 	public PlayerIO getPlayerFileData()
 	{
+		return this;
+	}
+
+	public @Nullable CompoundTag getPlayerData(@Nullable String username, @NotNull UUID uuid)
+	{
+		try
+		{
+			File uuidFile = new File(playersDirectory, uuid + ".dat");
+			if(uuidFile.exists())
+			{
+				return NbtIo.readCompressed(Files.newInputStream(uuidFile.toPath()));
+			} else if (username != null) { // If the uuid file doesn't exist attempt to load the old username file
+				File nameFile = new File(playersDirectory, username + ".dat");
+				if (nameFile.exists()) {
+					CompoundTag tag = NbtIo.readCompressed(Files.newInputStream(nameFile.toPath()));
+					nameFile.renameTo(uuidFile);
+					return tag;
+				}
+			}
+		}
+		catch(Exception exception)
+		{
+            LOGGER.warn("Failed to load player data for {}", username);
+		}
 		return null;
 	}
 
+	@FunctionalInterface
+	public static interface SaveHandlerFactory {
+		@NotNull SaveHandlerBase 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/SaveHandlerCore.java
index 6e3300bd7..bb4f31318 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/SaveHandlerCore.java
@@ -4,6 +4,7 @@ import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.chunk.ChunkLoaderRegionAsync;
 import net.minecraft.core.world.chunk.IChunkLoader;
+import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
 import java.util.List;
@@ -12,9 +13,9 @@ public class SaveHandlerCore
         extends SaveHandlerBase
 {
 
-    public SaveHandlerCore(ISaveFormat saveFormat, File savesDir, String worldDirName, boolean isMultiplayer)
+    public SaveHandlerCore(ISaveFormat saveFormat, File savesDir, String worldDirName)
     {
-        super(saveFormat, savesDir, worldDirName, isMultiplayer);
+        super(saveFormat, savesDir, worldDirName);
     }
     
     @Override
@@ -27,7 +28,7 @@ public class SaveHandlerCore
     }
 
     @Override
-    public void saveLevelDataAndPlayerData(LevelData levelData, List<Player> playerList)
+    public void saveLevelDataAndPlayerData(@NotNull LevelData levelData, @NotNull List<Player> playerList)
     {
         levelData.setSaveVersion(saveFormat.getSaveVersion());
         super.saveLevelDataAndPlayerData(levelData, playerList);
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/SaveHandlerServer.java
index 4fab0e346..eb2f09484 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/SaveHandlerServer.java
@@ -4,17 +4,14 @@ import com.mojang.logging.LogUtils;
 import net.minecraft.core.entity.player.Player;
 import com.mojang.nbt.tags.CompoundTag;
 import net.minecraft.core.world.chunk.IChunkLoader;
-import com.mojang.nbt.NbtIo;
 import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.chunk.ChunkLoaderRegion;
+import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 
 
 import java.io.File;
-import java.io.FileOutputStream;
-import java.nio.file.Files;
 import java.util.List;
-import java.util.UUID;
 
 
 public class SaveHandlerServer
@@ -25,7 +22,7 @@ public class SaveHandlerServer
 
     public SaveHandlerServer(ISaveFormat saveFormat, File savesDir, String worldDirName, boolean isMultiplayer)
     {
-        super(saveFormat, savesDir, worldDirName, isMultiplayer);
+        super(saveFormat, savesDir, worldDirName);
     }
     
     @Override
@@ -38,72 +35,20 @@ public class SaveHandlerServer
     }
 
     @Override
-    public void saveLevelDataAndPlayerData(LevelData levelData, List<Player> playerList)
+    public void saveLevelDataAndPlayerData(@NotNull LevelData levelData, @NotNull List<Player> playerList)
     {
         levelData.setSaveVersion(saveFormat.getSaveVersion());
         super.saveLevelDataAndPlayerData(levelData, playerList);
     }
 
     @Override
-    public void save(Player player)
+    public void load(@NotNull Player player)
     {
-        try
+        CompoundTag tag = getPlayerData(player.username, player.uuid);
+        if(tag != null)
         {
-            CompoundTag nbttagcompound = new CompoundTag();
-            player.saveWithoutId(nbttagcompound);
-            nbttagcompound.putInt("Format", 1);
-            File temp = new File(playersDirectory, "_tmp_.dat");
-            File save = new File(playersDirectory, player.uuid + ".dat");
-            NbtIo.writeCompressed(nbttagcompound, new FileOutputStream(temp));
-            if(save.exists())
-            {
-                save.delete();
-            }
-            temp.renameTo(save);
+            player.load(tag);
         }
-        catch(Exception exception)
-        {
-            LOGGER.warn("Failed to save player data for " + player.username);
-        }
-    }
-
-    @Override
-    public void load(Player player)
-    {
-        CompoundTag nbttagcompound = getPlayerData(player.username, player.uuid);
-        if(nbttagcompound != null)
-        {
-            player.load(nbttagcompound);
-        }
-    }
-
-    @Override
-    public PlayerIO getPlayerFileData()
-    {
-        return this;
     }
 
-    private CompoundTag getPlayerData(String username, UUID uuid)
-    {
-        try
-        {
-            File uuidFile = new File(playersDirectory, uuid + ".dat");
-            if(uuidFile.exists())
-            {
-                return NbtIo.readCompressed(Files.newInputStream(uuidFile.toPath()));
-            } else { // If the uuid file doesn't exist attempt to load the old username file
-                File nameFile = new File(playersDirectory, username + ".dat");
-                if (nameFile.exists()) {
-                    CompoundTag tag = NbtIo.readCompressed(Files.newInputStream(nameFile.toPath()));
-                    nameFile.renameTo(uuidFile);
-                    return tag;
-                }
-            }
-        }
-        catch(Exception exception)
-        {
-            LOGGER.warn("Failed to load player data for " + username);
-        }
-        return null;
-    }
 }
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
new file mode 100644
index 000000000..cd6b5ea3d
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java
@@ -0,0 +1,115 @@
+package net.minecraft.core.world.settings;
+
+import net.minecraft.core.lang.I18n;
+import net.minecraft.core.player.gamemode.Gamemode;
+import net.minecraft.core.util.helper.ChatAllowedCharacters;
+import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.world.save.ISaveFormat;
+import net.minecraft.core.world.type.WorldTypeGroups;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Random;
+
+public class WorldConfiguration {
+    private @NotNull String worldName = "";
+    private long numericSeed = new Random().nextLong();
+    private @NotNull String stringSeed = "";
+    private boolean cheatsEnabled = false;
+    private @NotNull Gamemode gamemode = Gamemode.survival;
+    private @NotNull WorldTypeGroups.Group worldTypeGroup = WorldTypeGroups.DEFAULT;
+
+    public WorldConfiguration() {
+    }
+
+    public @NotNull String getWorldName() {
+        return this.worldName;
+    }
+
+    public @NotNull String getFinalWorldName() {
+        if (MathHelper.stringNullOrLengthZero(this.worldName)) {
+            return I18n.getInstance().translateKey("gui.create_world.label.new_world");
+        }
+
+        return this.worldName;
+    }
+
+    public void setWorldName(final @NotNull String worldName) {
+        this.worldName = worldName;
+    }
+
+    public long getNumericSeed() {
+        return this.numericSeed;
+    }
+
+    public long getFinalNumericSeed() {
+        if (MathHelper.stringNullOrLengthZero(this.stringSeed)) {
+            return this.numericSeed;
+        } else {
+            try {
+                final long parsedSeed = Long.parseLong(this.stringSeed);
+                if (parsedSeed != 0L) {
+                    return parsedSeed;
+                }
+            } catch (final @NotNull NumberFormatException e) {
+                return this.stringSeed.hashCode();
+            }
+        }
+
+        return this.numericSeed;
+    }
+
+    public void regenNumericSeed() {
+        this.numericSeed = new Random().nextLong();
+    }
+
+    public @NotNull String getStringSeed() {
+        return this.stringSeed;
+    }
+
+    public void setStringSeed(final @NotNull String seed) {
+        this.stringSeed = seed;
+    }
+
+    public boolean getCheatsEnabled() {
+        return this.cheatsEnabled;
+    }
+
+    public void setCheatsEnabled(final boolean cheatsEnabled) {
+        this.cheatsEnabled = cheatsEnabled;
+    }
+
+    public @NotNull Gamemode getGamemode() {
+        return this.gamemode;
+    }
+
+    public void setGamemode(final @NotNull Gamemode gamemode) {
+        this.gamemode = gamemode;
+    }
+
+    public @NotNull WorldTypeGroups.Group getWorldTypeGroup() {
+        return this.worldTypeGroup;
+    }
+
+    public void setWorldTypeGroup(final @NotNull WorldTypeGroups.Group worldTypeGroup) {
+        this.worldTypeGroup = worldTypeGroup;
+    }
+
+    public @NotNull String getFolderName(final @NotNull ISaveFormat saveFormat) {
+        final char @NotNull [] ac = ChatAllowedCharacters.ALLOWED_CHARACTERS_ARRAY;
+        @NotNull String folderName = this.worldName.trim();
+        if (MathHelper.stringNullOrLengthZero(folderName)) {
+            folderName = I18n.getInstance().translateKey("gui.create_world.label.new_world");
+        }
+
+        for (final char c : ac) {
+            folderName = folderName.replace(c, '_');
+        }
+
+        int count = 1;
+        for (; saveFormat.getLevelData(folderName) != null; count++) {
+            folderName = folderName + " (" + count + ")";
+        }
+        return folderName;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSetting.java b/game/core/src/main/java/net/minecraft/core/world/settings/WorldSetting.java
deleted file mode 100644
index 1272e1415..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSetting.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package net.minecraft.core.world.settings;
-
-public class WorldSetting<T>
-{
-    private T value;
-    private final String languageKey;
-
-    public WorldSetting(String languageKey)
-    {
-        this.languageKey = languageKey;
-    }
-
-    public T getValue()
-    {
-        return this.value;
-    }
-
-    public void setValue(T newValue)
-    {
-        this.value = newValue;
-    }
-
-    public WorldSetting<T> withDefaultValue(T value)
-    {
-        this.value = value;
-
-        return this;
-    }
-
-    public String getLanguageKey()
-    {
-        return this.languageKey;
-    }
-}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingCollection.java b/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingCollection.java
deleted file mode 100644
index afa5d5cd7..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingCollection.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package net.minecraft.core.world.settings;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-
-public class WorldSettingCollection
-{
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target(ElementType.FIELD)
-    protected @interface Setting {}
-
-    private WorldSetting<?>[] settings = null;
-
-    public WorldSetting<?>[] getSettings()
-    {
-        if (settings == null)
-        {
-            List<Field> annotatedFields = new ArrayList<>();
-            Field[] fields = this.getClass().getDeclaredFields();
-            for (Field field : fields)
-            {
-                if (field.isAnnotationPresent(Setting.class))
-                {
-                    annotatedFields.add(field);
-                }
-            }
-
-            settings = new WorldSetting[annotatedFields.size()];
-
-            for (int i = 0; i < annotatedFields.size(); i++)
-            {
-                try
-                {
-                    settings[i] = (WorldSetting<?>)annotatedFields.get(i).get(this);
-                }
-                catch (Exception ignored) { }
-            }
-        }
-
-        return settings;
-    }
-}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingEnum.java b/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingEnum.java
deleted file mode 100644
index 6f2b21ac8..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingEnum.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package net.minecraft.core.world.settings;
-
-public class WorldSettingEnum extends WorldSetting<Integer>
-{
-    private int numEnums = 0;
-    private String[] enumLanguageKeys = null;
-    private int enumIndex = -1;
-
-    public WorldSettingEnum(String languageKey)
-    {
-        super(languageKey);
-    }
-
-    @Override
-    public Integer getValue()
-    {
-        if (numEnums == 0 || enumLanguageKeys == null || enumLanguageKeys.length == 0) return null;
-
-        return enumIndex;
-    }
-
-    @Override
-    public void setValue(Integer newValue)
-    {
-        if (newValue >= 0 && newValue < numEnums && enumLanguageKeys != null && enumLanguageKeys.length != 0)
-        {
-            super.setValue(newValue);
-        }
-    }
-
-    public WorldSettingEnum withEnums(String... enums)
-    {
-        if (enums != null)
-        {
-            numEnums = enums.length;
-            enumLanguageKeys = enums;
-            enumIndex = 0;
-        }
-
-        return this;
-    }
-}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingRange.java b/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingRange.java
deleted file mode 100644
index 16f389034..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettingRange.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package net.minecraft.core.world.settings;
-
-public class WorldSettingRange<T extends Comparable<T>> extends WorldSetting<T>
-{
-    private T minValue = null;
-    private T maxValue = null;
-
-    public WorldSettingRange(String languageKey)
-    {
-        super(languageKey);
-    }
-
-    public T getMinValue()
-    {
-        return minValue;
-    }
-
-    public T getMaxValue()
-    {
-        return maxValue;
-    }
-
-    @Override
-    public void setValue(T newValue)
-    {
-        if (newValue.compareTo(minValue) < 0)
-        {
-            newValue = minValue;
-        }
-        else if (newValue.compareTo(maxValue) > 0)
-        {
-            newValue = maxValue;
-        }
-
-        super.setValue(newValue);
-    }
-
-    public WorldSettingRange<T> withRange(T minValue, T maxValue)
-    {
-        this.minValue = minValue;
-        this.maxValue = maxValue;
-
-        return this;
-    }
-}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettings.java b/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettings.java
deleted file mode 100644
index 3a4c3419b..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/settings/WorldSettings.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package net.minecraft.core.world.settings;
-
-import java.util.Random;
-
-public class WorldSettings extends WorldSettingCollection
-{
-    // Y limit variables
-    @Setting
-    public WorldSetting<Integer> minY = new WorldSettingRange<Integer>("worldSettings.yLimit.minY")
-        .withRange(0, 255)
-        .withDefaultValue(0);
-    @Setting
-    public WorldSetting<Integer> maxY = new WorldSettingRange<Integer>("worldSettings.yLimit.maxY")
-        .withRange(0, 255)
-        .withDefaultValue(128);
-
-    // Y limit with
-    public WorldSettings withYLimit(Integer minY, Integer maxY)
-    {
-        if (minY != null) this.minY.setValue(minY);
-        if (maxY != null) this.maxY.setValue(maxY);
-        return this;
-    }
-
-    // Seed
-    @Setting
-    public WorldSetting<Long> seed = new WorldSetting<Long>("worldSettings.seed")
-        .withDefaultValue((new Random()).nextLong());
-
-    // Seed with
-    public WorldSettings withSeed(Long seed)
-    {
-        if (seed != null) this.seed.setValue(seed);
-        return this;
-    }
-}
diff --git a/game/core/src/main/resources/assets/minecraft/sounds/sounds.json b/game/core/src/main/resources/assets/minecraft/sounds/sounds.json
index b7443c846..a98d95157 100644
--- a/game/core/src/main/resources/assets/minecraft/sounds/sounds.json
+++ b/game/core/src/main/resources/assets/minecraft/sounds/sounds.json
@@ -806,6 +806,15 @@
       }
     ]
   },
+  "tile.mobspawner.spawn": {
+    "subtitle": "subtitles.tile.mobspawner.spawn",
+    "sounds": [
+      {
+        "name": "mob.ghast.fireball",
+        "type": "event"
+      }
+    ]
+  },
   "record.mall": {
     "sounds": [
       {
diff --git a/game/core/src/main/resources/lang/en_US/subtitles.lang b/game/core/src/main/resources/lang/en_US/subtitles.lang
index ac0d5cdfd..665afe394 100644
--- a/game/core/src/main/resources/lang/en_US/subtitles.lang
+++ b/game/core/src/main/resources/lang/en_US/subtitles.lang
@@ -92,6 +92,7 @@ subtitles.tile.piston.in=Piston retracted
 
 subtitles.tile.tnt.fuse=TNT lit
 subtitles.tile.sensor_block.sense=Mob sensed
+subtitles.tile.mobspawner.spawn=Mob spawned
 
 subtitles.tile.dispenser.click=Dispenser click
 subtitles.tile.dispenser.eject=Dispenser eject
diff --git a/game/core/src/main/resources/lang/en_US/tile.lang b/game/core/src/main/resources/lang/en_US/tile.lang
index 0ae07fd2a..4e481d3c7 100644
--- a/game/core/src/main/resources/lang/en_US/tile.lang
+++ b/game/core/src/main/resources/lang/en_US/tile.lang
@@ -25,6 +25,12 @@ tile.cobble.granite.name=Cobbled Granite
 tile.cobble.granite.desc=Granite, smashed apart and reconstituted.
 tile.cobble.permafrost.name=Cobbled Permafrost
 tile.cobble.permafrost.desc=Permafrost, smashed apart and reconstituted.
+tile.cobble.basalt.mossy.name=Mossy Cobbled Basalt
+tile.cobble.basalt.mossy.desc=Cobbled basalt with added greenery.
+tile.cobble.limestone.mossy.name=Mossy Cobbled Limestone
+tile.cobble.limestone.mossy.desc=Cobbled limestone with added greenery.
+tile.cobble.granite.mossy.name=Mossy Cobbled Granite
+tile.cobble.granite.mossy.desc=Cobbled granite with added greenery.
 
 tile.pillar.marble.name=Marble Pillar
 tile.pillar.marble.desc=A block of marble engraved into a pillar.
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 004235a1c..1726a51bf 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
@@ -196,7 +196,7 @@ public class PacketHandlerLogin extends PacketHandler
                 player.playerNetServerHandler.sendPacket(new PacketRecipeSync(recipe,Registries.RECIPES.getAllSerializableRecipes().size()));
             }
             player.playerNetServerHandler.sendPacket(new PacketSyncIDs(PacketSyncIDs.DESTINATION_SOUND_IDS, SoundTypes.getSoundIds()));
-            player.playerNetServerHandler.sendPacket(new PacketSyncIDs(PacketSyncIDs.DESTINATION_ENTITY_IDS, EntityDispatcher.getEntityIds()));
+            player.playerNetServerHandler.sendPacket(new PacketSyncIDs(PacketSyncIDs.DESTINATION_ENTITY_IDS, EntityDispatcher.getInstance().getEntityIds()));
 
             packetHandler.sendPacket(new PacketSetSpawnPosition(chunkcoordinates.x, chunkcoordinates.y, chunkcoordinates.z));
             mcServer.playerList.setTime(player, worldserver);
diff --git a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
index d5bdbf90f..5db443aa2 100644
--- a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
+++ b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
@@ -47,8 +47,6 @@ import org.slf4j.Logger;
 
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
 
 public class PacketHandlerServer extends PacketHandler
         implements ICommandListener
@@ -104,8 +102,7 @@ public class PacketHandlerServer extends PacketHandler
             if (tileentity instanceof TileEntityMobSpawner)
             {
                 TileEntityMobSpawner tileEntityMobSpawner = (TileEntityMobSpawner) tileentity;
-                NamespaceID namespaceID = EntityDispatcher.idForClass(packet.entityClass);
-                tileEntityMobSpawner.setMobId(namespaceID == null ? "none" : namespaceID.toString());
+                tileEntityMobSpawner.setMobId(packet.dispatcherEntry == null ? "none" : packet.dispatcherEntry.stringID);
 
                 tileEntityMobSpawner.setChanged();
                 world.markBlockNeedsUpdate(packet.xPosition, packet.yPosition, packet.zPosition);
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 a973d0220..dcea78b96 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
@@ -28,6 +28,7 @@ public class WorldServer extends World
         super(isavehandler, name, seed, Dimension.getDimensionList().get(dimensionId), worldType);
         entityIntHashMap = new Int2ObjectOpenHashMap<>();
         mcServer = minecraftserver;
+        getLevelData().setLastPlayerUUID(null);
     }
 
     @Override
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/TestChunkProvider.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/TestChunkProvider.java
index 266f41298..dad2553b0 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/TestChunkProvider.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/TestChunkProvider.java
@@ -4,17 +4,17 @@ import net.minecraft.core.util.collection.NamespaceID;
 import org.useless.seedviewer.collections.ChunkLocation;
 import org.useless.seedviewer.collections.ChunkPos2D;
 import org.useless.seedviewer.collections.ChunkPos3D;
-import org.useless.seedviewer.data.Biome;
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerBiome;
+import org.useless.seedviewer.data.ViewerChunk;
 import org.useless.seedviewer.gui.ChunkProvider;
 
 public class TestChunkProvider implements ChunkProvider {
     @Override
-    public Chunk getChunk(ChunkLocation location) {
-        return new Chunk() {
+    public ViewerChunk getChunk(ChunkLocation location) {
+        return new ViewerChunk() {
             @Override
-            public Biome getBiome(ChunkPos3D pos) {
-                return new Biome() {
+            public ViewerBiome getBiome(ChunkPos3D pos) {
+                return new ViewerBiome() {
                     @Override
                     public String getName() {
                         return "Test Biome";
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTABiome.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTABiome.java
index e807a1b2a..a53b39378 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTABiome.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTABiome.java
@@ -4,9 +4,9 @@ import net.minecraft.core.data.registry.Registries;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.util.HardIllegalArgumentException;
 import net.minecraft.core.util.collection.NamespaceID;
-import org.useless.seedviewer.data.Biome;
+import org.useless.seedviewer.data.ViewerBiome;
 
-public class BTABiome implements Biome {
+public class BTABiome implements ViewerBiome {
     private final net.minecraft.core.world.biome.Biome biome;
     public BTABiome(net.minecraft.core.world.biome.Biome biome) {
         this.biome = biome;
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAChunkProvider.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAChunkProvider.java
index c6f5cc2a5..f6996d8e8 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAChunkProvider.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAChunkProvider.java
@@ -13,7 +13,7 @@ import net.minecraft.core.world.save.LevelData;
 import net.minecraft.core.world.type.WorldTypes;
 import org.useless.seedviewer.bta.worldloader.ChunkLoader;
 import org.useless.seedviewer.collections.ChunkLocation;
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerChunk;
 import org.useless.seedviewer.gui.ChunkProvider;
 import org.slf4j.Logger;
 
@@ -48,7 +48,7 @@ public class BTAChunkProvider implements ChunkProvider {
         chunkLoader = new ChunkLoader(worldFolder, 0);
     }
     @Override
-    public Chunk getChunk(ChunkLocation location) {
+    public ViewerChunk getChunk(ChunkLocation location) {
         if (chunkLoader != null) {
             try {
                 net.minecraft.core.world.chunk.Chunk c = chunkLoader.loadChunk(location);
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAComplexChunk.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAComplexChunk.java
index e4fb3beb7..519993199 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAComplexChunk.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAComplexChunk.java
@@ -8,10 +8,10 @@ import net.minecraft.core.world.biome.provider.BiomeProvider;
 import org.jetbrains.annotations.NotNull;
 import org.useless.seedviewer.collections.ChunkPos2D;
 import org.useless.seedviewer.collections.ChunkPos3D;
-import org.useless.seedviewer.data.Biome;
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerBiome;
+import org.useless.seedviewer.data.ViewerChunk;
 
-public class BTAComplexChunk implements Chunk {
+public class BTAComplexChunk implements ViewerChunk {
     private final net.minecraft.core.world.chunk.Chunk chunk;
     private final BiomeProvider provider;
     public BTAComplexChunk(@NotNull net.minecraft.core.world.chunk.Chunk chunk, BiomeProvider biomeProvider) {
@@ -19,10 +19,10 @@ public class BTAComplexChunk implements Chunk {
         this.provider = biomeProvider;
     }
     @Override
-    public Biome getBiome(ChunkPos3D pos) {
+    public ViewerBiome getBiome(ChunkPos3D pos) {
         net.minecraft.core.world.biome.Biome b = chunk.getBlockBiome(pos.x, pos.y, pos.z);
         if (b != null) return new BTABiome(b);;
-        return new BTABiome(provider.getBiome(chunk.xPosition * Chunk.CHUNK_SIZE_X + pos.x, pos.y, chunk.zPosition * Chunk.CHUNK_SIZE_Z + pos.z));
+        return new BTABiome(provider.getBiome(chunk.xPosition * ViewerChunk.CHUNK_SIZE_X + pos.x, pos.y, chunk.zPosition * ViewerChunk.CHUNK_SIZE_Z + pos.z));
     }
 
     @Override
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTASimpleChunk.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTASimpleChunk.java
index b25426067..1951905be 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTASimpleChunk.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTASimpleChunk.java
@@ -4,10 +4,10 @@ import net.minecraft.core.world.biome.provider.BiomeProvider;
 import org.useless.seedviewer.collections.ChunkLocation;
 import org.useless.seedviewer.collections.ChunkPos2D;
 import org.useless.seedviewer.collections.ChunkPos3D;
-import org.useless.seedviewer.data.Biome;
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerBiome;
+import org.useless.seedviewer.data.ViewerChunk;
 
-public class BTASimpleChunk implements Chunk {
+public class BTASimpleChunk implements ViewerChunk {
     private final ChunkLocation location;
     private final BiomeProvider provider;
     public BTASimpleChunk(ChunkLocation location, BiomeProvider biomeProvider) {
@@ -15,8 +15,8 @@ public class BTASimpleChunk implements Chunk {
         provider = biomeProvider;
     }
     @Override
-    public Biome getBiome(ChunkPos3D pos) {
-        return new BTABiome(provider.getBiome(location.x * Chunk.CHUNK_SIZE_X + pos.x, pos.y, location.z * Chunk.CHUNK_SIZE_Z + pos.z));
+    public ViewerBiome getBiome(ChunkPos3D pos) {
+        return new BTABiome(provider.getBiome(location.x * ViewerChunk.CHUNK_SIZE_X + pos.x, pos.y, location.z * ViewerChunk.CHUNK_SIZE_Z + pos.z));
     }
 
     @Override
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAWorld.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAWorld.java
index 34329fad4..f404bcfed 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAWorld.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/bta/BTAWorld.java
@@ -3,13 +3,13 @@ package org.useless.seedviewer.bta;
 import net.minecraft.core.world.save.LevelData;
 import org.jetbrains.annotations.NotNull;
 import org.useless.seedviewer.bta.worldloader.LevelDataLoader;
-import org.useless.seedviewer.data.World;
+import org.useless.seedviewer.data.ViewerWorld;
 import org.useless.seedviewer.gui.ChunkProvider;
 
 import java.io.File;
 import java.util.Objects;
 
-public class BTAWorld implements World {
+public class BTAWorld implements ViewerWorld {
     private final @NotNull File worldFolder;
     private final LevelData data;
     public BTAWorld(@NotNull File worldFolder) {
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/collections/ChunkPos2D.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/collections/ChunkPos2D.java
index 13c13f016..1681a4c1c 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/collections/ChunkPos2D.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/collections/ChunkPos2D.java
@@ -1,6 +1,6 @@
 package org.useless.seedviewer.collections;
 
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerChunk;
 
 import java.util.Objects;
 
@@ -10,16 +10,16 @@ public class ChunkPos2D {
 
     public ChunkPos2D(int x, int z) {
         if (x < 0) {
-            throw new IllegalArgumentException("X must be between in range [0, " + Chunk.CHUNK_SIZE_X + "]!");
+            throw new IllegalArgumentException("X must be between in range [0, " + ViewerChunk.CHUNK_SIZE_X + "]!");
         }
-        if (x > Chunk.CHUNK_SIZE_X) {
-            throw new IllegalArgumentException("X must be between in range [0, " + Chunk.CHUNK_SIZE_X + "]!");
+        if (x > ViewerChunk.CHUNK_SIZE_X) {
+            throw new IllegalArgumentException("X must be between in range [0, " + ViewerChunk.CHUNK_SIZE_X + "]!");
         }
         if (z < 0) {
-            throw new IllegalArgumentException("Z must be between in range [0, " + Chunk.CHUNK_SIZE_Z + "]!");
+            throw new IllegalArgumentException("Z must be between in range [0, " + ViewerChunk.CHUNK_SIZE_Z + "]!");
         }
-        if (z > Chunk.CHUNK_SIZE_Z) {
-            throw new IllegalArgumentException("Z must be between in range [0, " + Chunk.CHUNK_SIZE_Z + "]!");
+        if (z > ViewerChunk.CHUNK_SIZE_Z) {
+            throw new IllegalArgumentException("Z must be between in range [0, " + ViewerChunk.CHUNK_SIZE_Z + "]!");
         }
         this.x = x;
         this.z = z;
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/collections/ChunkPos3D.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/collections/ChunkPos3D.java
index 48100c7ab..bac228140 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/collections/ChunkPos3D.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/collections/ChunkPos3D.java
@@ -1,6 +1,6 @@
 package org.useless.seedviewer.collections;
 
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerChunk;
 
 import java.util.Objects;
 
@@ -13,10 +13,10 @@ public final class ChunkPos3D extends ChunkPos2D {
     public ChunkPos3D(int x, int y, int z) {
         super(x, z);
         if (y < 0) {
-            throw new IllegalArgumentException("Y must be between in range [0, " + Chunk.CHUNK_SIZE_Y + "]!");
+            throw new IllegalArgumentException("Y must be between in range [0, " + ViewerChunk.CHUNK_SIZE_Y + "]!");
         }
-        if (y > Chunk.CHUNK_SIZE_Y) {
-            throw new IllegalArgumentException("Y must be between in range [0, " + Chunk.CHUNK_SIZE_Y + "]!");
+        if (y > ViewerChunk.CHUNK_SIZE_Y) {
+            throw new IllegalArgumentException("Y must be between in range [0, " + ViewerChunk.CHUNK_SIZE_Y + "]!");
         }
         this.y = y;
     }
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/data/Biome.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerBiome.java
similarity index 84%
rename from util/seed-viewer/src/main/java/org/useless/seedviewer/data/Biome.java
rename to util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerBiome.java
index 5c4f56f7b..f58a827f7 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/data/Biome.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerBiome.java
@@ -2,7 +2,7 @@ package org.useless.seedviewer.data;
 
 import net.minecraft.core.util.collection.NamespaceID;
 
-public interface Biome {
+public interface ViewerBiome {
     String getName();
     NamespaceID getID();
     int getColor();
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/data/Chunk.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerChunk.java
similarity index 88%
rename from util/seed-viewer/src/main/java/org/useless/seedviewer/data/Chunk.java
rename to util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerChunk.java
index 96529440a..5ba550a6d 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/data/Chunk.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerChunk.java
@@ -4,11 +4,11 @@ import net.minecraft.core.world.chunk.ChunkSection;
 import org.useless.seedviewer.collections.ChunkPos2D;
 import org.useless.seedviewer.collections.ChunkPos3D;
 
-public interface Chunk {
+public interface ViewerChunk {
     int CHUNK_SIZE_X = net.minecraft.core.world.chunk.Chunk.CHUNK_SIZE_X;
     int CHUNK_SIZE_Y = net.minecraft.core.world.chunk.Chunk.CHUNK_SECTIONS * ChunkSection.SECTION_SIZE_Y;
     int CHUNK_SIZE_Z = net.minecraft.core.world.chunk.Chunk.CHUNK_SIZE_Z;
-    Biome getBiome(ChunkPos3D pos);
+    ViewerBiome getBiome(ChunkPos3D pos);
     int getBlockColor(ChunkPos3D pos);
     int getHeight(ChunkPos2D pos);
     int getWaterDepth(ChunkPos2D pos);
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/data/World.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerWorld.java
similarity index 88%
rename from util/seed-viewer/src/main/java/org/useless/seedviewer/data/World.java
rename to util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerWorld.java
index 0bfb5c442..ff311b26f 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/data/World.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/data/ViewerWorld.java
@@ -4,7 +4,7 @@ import org.useless.seedviewer.gui.ChunkProvider;
 
 import java.io.File;
 
-public interface World {
+public interface ViewerWorld {
     File getFolder();
     long getSeed();
     String getName();
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/ChunkProvider.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/ChunkProvider.java
index 7466f61b1..4df3aba89 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/ChunkProvider.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/ChunkProvider.java
@@ -1,8 +1,8 @@
 package org.useless.seedviewer.gui;
 
 import org.useless.seedviewer.collections.ChunkLocation;
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerChunk;
 
 public interface ChunkProvider {
-    Chunk getChunk(ChunkLocation location);
+    ViewerChunk getChunk(ChunkLocation location);
 }
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/ChunkView.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/ChunkView.java
index d9d3a667b..d4a0bd9fc 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/ChunkView.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/ChunkView.java
@@ -5,8 +5,8 @@ import org.slf4j.Logger;
 import org.useless.seedviewer.collections.ChunkLocation;
 import org.useless.seedviewer.collections.ChunkPos2D;
 import org.useless.seedviewer.collections.ChunkPos3D;
-import org.useless.seedviewer.data.Biome;
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerBiome;
+import org.useless.seedviewer.data.ViewerChunk;
 import org.useless.seedviewer.gui.components.Viewport;
 
 import java.awt.*;
@@ -20,10 +20,10 @@ public class ChunkView {
     private static final ExecutorService InitializerService = Executors.newFixedThreadPool(10);
     private static final ExecutorService PostProcessorService = Executors.newFixedThreadPool(10);
 
-    private final BufferedImage biomeMapImage = new BufferedImage(Chunk.CHUNK_SIZE_X, Chunk.CHUNK_SIZE_Z, BufferedImage.TYPE_INT_ARGB);
-    private final BufferedImage terrainMapImage = new BufferedImage(Chunk.CHUNK_SIZE_X, Chunk.CHUNK_SIZE_Z, BufferedImage.TYPE_INT_ARGB);
-    private final BufferedImage heightMapImage = new BufferedImage(Chunk.CHUNK_SIZE_X, Chunk.CHUNK_SIZE_Z, BufferedImage.TYPE_INT_ARGB);
-    private final BufferedImage waterDepthMapImage = new BufferedImage(Chunk.CHUNK_SIZE_X, Chunk.CHUNK_SIZE_Z, BufferedImage.TYPE_INT_ARGB);
+    private final BufferedImage biomeMapImage = new BufferedImage(ViewerChunk.CHUNK_SIZE_X, ViewerChunk.CHUNK_SIZE_Z, BufferedImage.TYPE_INT_ARGB);
+    private final BufferedImage terrainMapImage = new BufferedImage(ViewerChunk.CHUNK_SIZE_X, ViewerChunk.CHUNK_SIZE_Z, BufferedImage.TYPE_INT_ARGB);
+    private final BufferedImage heightMapImage = new BufferedImage(ViewerChunk.CHUNK_SIZE_X, ViewerChunk.CHUNK_SIZE_Z, BufferedImage.TYPE_INT_ARGB);
+    private final BufferedImage waterDepthMapImage = new BufferedImage(ViewerChunk.CHUNK_SIZE_X, ViewerChunk.CHUNK_SIZE_Z, BufferedImage.TYPE_INT_ARGB);
 
     private final Rectangle bounds;
     private final ChunkLocation location;
@@ -42,15 +42,15 @@ public class ChunkView {
         this.viewport = viewport;
         this.location = location;
         this.provider = provider;
-        this.bounds = new Rectangle(location.x * Chunk.CHUNK_SIZE_X, location.z * Chunk.CHUNK_SIZE_Z, Chunk.CHUNK_SIZE_X, Chunk.CHUNK_SIZE_Z);
+        this.bounds = new Rectangle(location.x * ViewerChunk.CHUNK_SIZE_X, location.z * ViewerChunk.CHUNK_SIZE_Z, ViewerChunk.CHUNK_SIZE_X, ViewerChunk.CHUNK_SIZE_Z);
 
         loadTask = InitializerService.submit(
             () -> {
                 try {
-                    Chunk chunk = provider.getChunk(location);
+                    ViewerChunk chunk = provider.getChunk(location);
 
-                    for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
-                        for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
+                    for (int x = 0; x < ViewerChunk.CHUNK_SIZE_X; x++) {
+                        for (int z = 0; z < ViewerChunk.CHUNK_SIZE_Z; z++) {
                             ChunkPos2D pos2D = new ChunkPos2D(x, z);
                             int h = chunk.getHeight(pos2D) - 1;
                             if (h < 0) h = 0;
@@ -59,7 +59,7 @@ public class ChunkView {
                             int wd = chunk.getWaterDepth(pos2D) & 0xFF;
                             waterDepthMapImage.setRGB(x, z, 0xFF_00_00_00 | wd << 16 | wd << 8 | wd);
 
-                            Biome b = chunk.getBiome(new ChunkPos3D(x, h, z));
+                            ViewerBiome b = chunk.getBiome(new ChunkPos3D(x, h, z));
                             biomeMapImage.setRGB(x, z, b.getColor() | 0x80_00_00_00);
 
                             terrainMapImage.setRGB(x, z, chunk.getBlockColor(new ChunkPos3D(x, h, z)));
@@ -88,8 +88,8 @@ public class ChunkView {
         synchronized (this) {
             processTask = PostProcessorService.submit(() -> {
                 try {
-                    for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
-                        for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
+                    for (int x = 0; x < ViewerChunk.CHUNK_SIZE_X; x++) {
+                        for (int z = 0; z < ViewerChunk.CHUNK_SIZE_Z; z++) {
                             int depth = waterDepthMapImage.getRGB(x, z) & 0xFF;
 
                             int shade;
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/components/InfoPanel.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/components/InfoPanel.java
index 544ad9860..30a7b6bdf 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/components/InfoPanel.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/components/InfoPanel.java
@@ -1,7 +1,7 @@
 package org.useless.seedviewer.gui.components;
 
 import net.minecraft.core.Global;
-import org.useless.seedviewer.data.Biome;
+import org.useless.seedviewer.data.ViewerBiome;
 import org.useless.seedviewer.gui.SeedViewer;
 
 import javax.swing.*;
@@ -60,7 +60,7 @@ public class InfoPanel extends JPanel {
         viewLabel.setSize(0, textHeight);
         seedViewer.viewport.viewX.addChangeListener(newValue -> viewLabel.setText(String.format("View: X:%.2f, Z:%.2f", seedViewer.viewport.viewX.get(), -seedViewer.viewport.viewZ.get())));
         seedViewer.viewport.viewX.addChangeListener(newValue -> {
-            Biome b = seedViewer.viewport.getHoveredBiome();
+            ViewerBiome b = seedViewer.viewport.getHoveredBiome();
             if (b == null) {
                 biomeLabel.setText("Biome: null");
             } else {
@@ -69,7 +69,7 @@ public class InfoPanel extends JPanel {
         });
         seedViewer.viewport.viewZ.addChangeListener(newValue -> viewLabel.setText(String.format("View: X:%.2f, Z:%.2f", seedViewer.viewport.viewX.get(), -seedViewer.viewport.viewZ.get())));
         seedViewer.viewport.viewZ.addChangeListener(newValue -> {
-            Biome b = seedViewer.viewport.getHoveredBiome();
+            ViewerBiome b = seedViewer.viewport.getHoveredBiome();
             if (b == null) {
                 biomeLabel.setText("Biome: null");
             } else {
diff --git a/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/components/Viewport.java b/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/components/Viewport.java
index 5647806fc..89ef13929 100644
--- a/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/components/Viewport.java
+++ b/util/seed-viewer/src/main/java/org/useless/seedviewer/gui/components/Viewport.java
@@ -8,9 +8,9 @@ import org.useless.seedviewer.collections.ChunkLocation;
 import org.useless.seedviewer.collections.ChunkPos2D;
 import org.useless.seedviewer.collections.ChunkPos3D;
 import org.useless.seedviewer.collections.ObjectWrapper;
-import org.useless.seedviewer.data.Biome;
-import org.useless.seedviewer.data.Chunk;
-import org.useless.seedviewer.data.World;
+import org.useless.seedviewer.data.ViewerBiome;
+import org.useless.seedviewer.data.ViewerChunk;
+import org.useless.seedviewer.data.ViewerWorld;
 import org.useless.seedviewer.gui.ChunkProvider;
 import org.useless.seedviewer.gui.ChunkView;
 import org.useless.seedviewer.gui.SeedViewer;
@@ -54,7 +54,7 @@ public class Viewport extends JLabel {
 
     public ChunkProvider chunkProvider = new TestChunkProvider();
     public final ObjectWrapper<@NotNull Long> seed = new ObjectWrapper<>(100L);
-    public final ObjectWrapper<@Nullable World> world = new ObjectWrapper<>(null);
+    public final ObjectWrapper<@Nullable ViewerWorld> world = new ObjectWrapper<>(null);
 
     public final ObjectWrapper<@NotNull Float> zoom = new ObjectWrapper<>(1F);
     public final ObjectWrapper<@NotNull Float> viewX = new ObjectWrapper<>(0F);
@@ -169,10 +169,10 @@ public class Viewport extends JLabel {
 
     public synchronized void addMissingViewers(Rectangle viewportBounds) {
         ChunkLocation topLeftLocation =
-            new ChunkLocation(viewportBounds.x/ Chunk.CHUNK_SIZE_X,
-                viewportBounds.y/Chunk.CHUNK_SIZE_Z);
-        int chunksX = viewportBounds.width/Chunk.CHUNK_SIZE_X;
-        int chunksZ = viewportBounds.height/Chunk.CHUNK_SIZE_Z;
+            new ChunkLocation(viewportBounds.x/ ViewerChunk.CHUNK_SIZE_X,
+                viewportBounds.y/ ViewerChunk.CHUNK_SIZE_Z);
+        int chunksX = viewportBounds.width/ ViewerChunk.CHUNK_SIZE_X;
+        int chunksZ = viewportBounds.height/ ViewerChunk.CHUNK_SIZE_Z;
 
         List<ChunkLocation> pendingLocation = new ArrayList<>();
         for (int _x = topLeftLocation.x; _x < topLeftLocation.x + chunksX; _x++) {
@@ -183,7 +183,7 @@ public class Viewport extends JLabel {
             }
         }
         pendingLocation.sort((o1, o2) -> {
-            ChunkLocation viewLoc = new ChunkLocation((int) Math.floor(viewX.get()/ Chunk.CHUNK_SIZE_X), (int) Math.floor(-viewZ.get()/ Chunk.CHUNK_SIZE_Z));
+            ChunkLocation viewLoc = new ChunkLocation((int) Math.floor(viewX.get()/ ViewerChunk.CHUNK_SIZE_X), (int) Math.floor(-viewZ.get()/ ViewerChunk.CHUNK_SIZE_Z));
             int d1;
             {
                 int dx = o1.x - viewLoc.x;
@@ -236,7 +236,7 @@ public class Viewport extends JLabel {
         final int blockZ = (int) Math.floor((-viewZ.get() - (getHeight()/(zoom.get() * 2))));
         final int widthBlocks = (int) Math.ceil((getWidth()/zoom.get()));
         final int heightBlocks = (int) Math.ceil((getHeight()/zoom.get()));
-        return new Rectangle(blockX - (Chunk.CHUNK_SIZE_X * VIEWPORT_CHUNKS_OVERSCAN), blockZ - (Chunk.CHUNK_SIZE_Z * VIEWPORT_CHUNKS_OVERSCAN), widthBlocks + Chunk.CHUNK_SIZE_X * (VIEWPORT_CHUNKS_OVERSCAN * 2 + 1), heightBlocks + Chunk.CHUNK_SIZE_Z * (VIEWPORT_CHUNKS_OVERSCAN * 2 + 1));
+        return new Rectangle(blockX - (ViewerChunk.CHUNK_SIZE_X * VIEWPORT_CHUNKS_OVERSCAN), blockZ - (ViewerChunk.CHUNK_SIZE_Z * VIEWPORT_CHUNKS_OVERSCAN), widthBlocks + ViewerChunk.CHUNK_SIZE_X * (VIEWPORT_CHUNKS_OVERSCAN * 2 + 1), heightBlocks + ViewerChunk.CHUNK_SIZE_Z * (VIEWPORT_CHUNKS_OVERSCAN * 2 + 1));
     }
 
     public synchronized void offsetZoom(float delta) {
@@ -281,10 +281,10 @@ public class Viewport extends JLabel {
     }
 
     private ChunkLocation lastHoveredLocation = null;
-    private Chunk lastHoveredChunk = null;
-    public synchronized Biome getHoveredBiome() {
-        ChunkLocation chunkLocation = new ChunkLocation((int) Math.floor(viewX.get()/ Chunk.CHUNK_SIZE_X), (int) Math.floor(-viewZ.get()/ Chunk.CHUNK_SIZE_Z));
-        Chunk chunk;
+    private ViewerChunk lastHoveredChunk = null;
+    public synchronized ViewerBiome getHoveredBiome() {
+        ChunkLocation chunkLocation = new ChunkLocation((int) Math.floor(viewX.get()/ ViewerChunk.CHUNK_SIZE_X), (int) Math.floor(-viewZ.get()/ ViewerChunk.CHUNK_SIZE_Z));
+        ViewerChunk chunk;
         if (lastHoveredLocation != null && lastHoveredChunk != null && lastHoveredLocation.equals(chunkLocation)) {
             chunk = lastHoveredChunk;
         } else {
@@ -292,7 +292,7 @@ public class Viewport extends JLabel {
             lastHoveredChunk = chunk;
             lastHoveredLocation = chunkLocation;
         }
-        ChunkPos2D c2D = new ChunkPos2D(((int) Math.floor(viewX.get())) - chunkLocation.x * Chunk.CHUNK_SIZE_X, ((int) Math.floor(-viewZ.get())) - chunkLocation.z * Chunk.CHUNK_SIZE_Z);
+        ChunkPos2D c2D = new ChunkPos2D(((int) Math.floor(viewX.get())) - chunkLocation.x * ViewerChunk.CHUNK_SIZE_X, ((int) Math.floor(-viewZ.get())) - chunkLocation.z * ViewerChunk.CHUNK_SIZE_Z);
         return chunk.getBiome(new ChunkPos3D(c2D.x, chunk.getHeight(c2D), c2D.z));
     }
 
@@ -311,13 +311,13 @@ public class Viewport extends JLabel {
             long seed = world.get() == null ? this.seed.get() : world.get().getSeed();
             for (ChunkView view : chunkViewMap.values()) {
                 if (!viewportBounds.intersects(view.getWorldBounds())) continue;
-                int blockX = view.getLocation().x * Chunk.CHUNK_SIZE_X;
-                int blockZ = view.getLocation().z * Chunk.CHUNK_SIZE_Z;
+                int blockX = view.getLocation().x * ViewerChunk.CHUNK_SIZE_X;
+                int blockZ = view.getLocation().z * ViewerChunk.CHUNK_SIZE_Z;
 
                 int subImgX = (int) Math.floor((blockX - viewX.get()) * zoom.get() + getWidth()/2d);
                 int subImgZ = (int) Math.floor((blockZ + viewZ.get()) * zoom.get() + getHeight()/2d);
-                int subImgWidth = (int) Math.floor(Chunk.CHUNK_SIZE_X * zoom.get());
-                int subImgHeight = (int) Math.floor(Chunk.CHUNK_SIZE_Z * zoom.get());
+                int subImgWidth = (int) Math.floor(ViewerChunk.CHUNK_SIZE_X * zoom.get());
+                int subImgHeight = (int) Math.floor(ViewerChunk.CHUNK_SIZE_Z * zoom.get());
                 if (showTerrain.get() && world.get() != null) {
                     g.drawImage(view.getTerrainMapImage(),
                         subImgX,
@@ -357,21 +357,21 @@ public class Viewport extends JLabel {
                 }
             }
             if (showChunkBorders.get()) {
-                int leftChunk = viewportBounds.x / Chunk.CHUNK_SIZE_X;
-                int widthChunks = viewportBounds.width / Chunk.CHUNK_SIZE_X;
-                int topChunk = viewportBounds.y / Chunk.CHUNK_SIZE_Z;
-                int heightChunks = viewportBounds.height / Chunk.CHUNK_SIZE_Z;
+                int leftChunk = viewportBounds.x / ViewerChunk.CHUNK_SIZE_X;
+                int widthChunks = viewportBounds.width / ViewerChunk.CHUNK_SIZE_X;
+                int topChunk = viewportBounds.y / ViewerChunk.CHUNK_SIZE_Z;
+                int heightChunks = viewportBounds.height / ViewerChunk.CHUNK_SIZE_Z;
 
                 Graphics gBorders = g.create();
                 gBorders.setColor(new Color(0, 0, 0, 64));
                 for (int _x = leftChunk; _x <= leftChunk + widthChunks; _x++) {
-                    float blockX = (_x * Chunk.CHUNK_SIZE_X);
+                    float blockX = (_x * ViewerChunk.CHUNK_SIZE_X);
                     int subImgX = (int) Math.floor((blockX - viewX.get()) * zoom.get() + getWidth()/2d);
 
                     gBorders.drawLine(subImgX, 0, subImgX, getHeight());
                 }
                 for (int _z = topChunk; _z < topChunk + heightChunks; _z++) {
-                    float blockZ = (_z * Chunk.CHUNK_SIZE_Z);
+                    float blockZ = (_z * ViewerChunk.CHUNK_SIZE_Z);
                     int subImgZ = (int) Math.floor((blockZ + viewZ.get()) * zoom.get() + getHeight()/2d);
 
                     gBorders.drawLine(0, subImgZ, getWidth(), subImgZ);
diff --git a/util/seed-viewer/src/test/java/collections/ChunkPosTest.java b/util/seed-viewer/src/test/java/collections/ChunkPosTest.java
index 206d4f19b..8b5755cde 100644
--- a/util/seed-viewer/src/test/java/collections/ChunkPosTest.java
+++ b/util/seed-viewer/src/test/java/collections/ChunkPosTest.java
@@ -5,14 +5,14 @@ import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 import org.useless.seedviewer.collections.ChunkPos2D;
 import org.useless.seedviewer.collections.ChunkPos3D;
-import org.useless.seedviewer.data.Chunk;
+import org.useless.seedviewer.data.ViewerChunk;
 
 public class ChunkPosTest {
     @Test
     @DisplayName("Test Valid ChunkPos2D")
     void validChunkPos2D() {
-        for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
-            for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
+        for (int x = 0; x < ViewerChunk.CHUNK_SIZE_X; x++) {
+            for (int z = 0; z < ViewerChunk.CHUNK_SIZE_Z; z++) {
                 ChunkPos2D chunkPos2D_1 = new ChunkPos2D(x, z);
                 Assertions.assertEquals(chunkPos2D_1.x, x);
                 Assertions.assertEquals(chunkPos2D_1.z, z);
@@ -42,9 +42,9 @@ public class ChunkPosTest {
     @Test
     @DisplayName("Test Valid ChunkPos3D")
     void validChunkPos3D() {
-        for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
-            for (int y = 0; y < Chunk.CHUNK_SIZE_Y; y++) {
-                for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
+        for (int x = 0; x < ViewerChunk.CHUNK_SIZE_X; x++) {
+            for (int y = 0; y < ViewerChunk.CHUNK_SIZE_Y; y++) {
+                for (int z = 0; z < ViewerChunk.CHUNK_SIZE_Z; z++) {
                     ChunkPos3D chunkPos3D_1 = new ChunkPos3D(x, y, z);
                     Assertions.assertEquals(chunkPos3D_1.x, x);
                     Assertions.assertEquals(chunkPos3D_1.y, y);
