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 6d099f075..39aa080a1 100644
--- a/game/client/src/main/java/net/minecraft/client/Minecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/Minecraft.java
@@ -32,6 +32,7 @@ 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.net.handler.PacketHandlerClient;
+import net.minecraft.client.render.TileEntityRenderDispatcher;
 import net.minecraft.client.render.block.model.BlockModelGrass;
 import net.minecraft.client.render.font.FontRenderer;
 import net.minecraft.client.render.font.FontRendererDefault;
@@ -74,6 +75,7 @@ import net.minecraft.core.net.command.commands.CommandTestFor;
 import net.minecraft.core.net.command.commands.CommandTime;
 import net.minecraft.core.net.command.commands.CommandWeather;
 import net.minecraft.core.net.command.util.CommandHelper;
+import net.minecraft.core.util.collection.NamespaceID;
 import net.minecraft.core.util.helper.AES;
 import net.minecraft.core.util.helper.DyeColor;
 import net.minecraft.core.util.helper.LoggedPrintStream;
@@ -515,14 +517,14 @@ public class Minecraft
         
         if(gameSettings.enableMultiDrawRendering.value) {
             if(OpenGLHelper.gl20) {
-            	System.out.println("Using MultiDraw terrain renderer");
+            	LOGGER.info("Using MultiDraw terrain renderer");
             	terrainRenderer = new TerrainRendererMultiDraw(this);
             }else {
-            	System.out.println("MultiDraw not supported, using legacy terrain renderer");
+            	LOGGER.info("MultiDraw not supported, using legacy terrain renderer");
             	terrainRenderer = new TerrainRendererLegacy(this);
             }
         }else {
-        	System.out.println("MultiDraw disabled, using legacy terrain renderer");
+        	LOGGER.info("MultiDraw disabled, using legacy terrain renderer");
         	terrainRenderer = new TerrainRendererLegacy(this);
         }
         
@@ -1013,7 +1015,8 @@ public class Minecraft
                     Time.tick();
                     AABB.initializePool();
                     Vec3.initializePool();
-                    
+                    NamespaceID.initializePool();
+
                     if(gameWindow.shouldClose()) shutdown();
                     if(isGamePaused && currentWorld != null && !(currentScreen instanceof ScreenModelViewer))
                     {
@@ -1194,6 +1197,7 @@ public class Minecraft
             System.gc();
             AABB.deinitializePool();
             Vec3.deinitializePool();
+            NamespaceID.deinitializePool();
         }
         catch(Throwable ignored) { }
         try
@@ -1806,13 +1810,9 @@ public class Minecraft
             if(!isGamePaused)
             {
                 worldRenderer.updateRenderer();
-            }
-            if(!isGamePaused)
-            {
                 renderGlobal.updateClouds();
-            }
-            if(!isGamePaused)
-            {
+                TileEntityRenderDispatcher.instance.tick();
+
                 Debug.change("entity");
                 if(currentWorld.lightningFlicker > 0)
                 {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenFontTest.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenFontTest.java
index 9c5823e46..75492bed0 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenFontTest.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenFontTest.java
@@ -14,7 +14,7 @@ import java.util.Map;
 
 public class ScreenFontTest extends Screen{
     private static final char[] chars = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
-    private static final @NotNull NamespaceID testId = new NamespaceID("test", "value");
+    private static final @NotNull NamespaceID testId = NamespaceID.getPermanent("test", "value");
     private static final @NotNull StringBuilder builder = new StringBuilder();
     public double scroll = 0;
     public ScreenFontTest(final @Nullable Screen parent) {
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 9fbd5a4c3..21b47a2e2 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
@@ -44,7 +44,7 @@ public class ScreenMobPicker
         List<String> prettyMonsters = new ArrayList<>();
         for (NamespaceID monster : monsters) {
             StringBuilder sb = new StringBuilder();
-            for (char c : monster.value.toCharArray()) {
+            for (char c : monster.value().toCharArray()) {
                 if (Character.isUpperCase(c)) {
                     sb.append(' ');
                 }
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 922125d27..806789c46 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
@@ -38,7 +38,7 @@ public class ScreenWandMobPicker extends Screen {
         List<String> prettyMonsters = new ArrayList<>();
         for (NamespaceID monster : monsters) {
             StringBuilder sb = new StringBuilder();
-            for (char c : monster.value.toCharArray()) {
+            for (char c : monster.value().toCharArray()) {
                 if (Character.isUpperCase(c)) {
                     sb.append(' ');
                 }
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 a8d2302f6..014b8c692 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
@@ -65,6 +65,15 @@ public abstract class MobInfoRegistry {
                 new MobDrop(new ItemStack(Items.FEATHER_CHICKEN), 0.66f, 1, 2)
             }
         );
+        register(MobDeer.class,
+                "guidebook.section.mob.deer.name",
+                "guidebook.section.mob.deer.desc",
+                10,
+                10,
+                new MobDrop[] {
+                        new MobDrop(new ItemStack(Items.FOOD_VENISON_RAW), 1.0F, 1, 2)
+                }
+        );
         register(MobSquid.class,
             "guidebook.section.mob.squid.name",
             "guidebook.section.mob.squid.desc",
diff --git a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/ViewerCategoryBlock.java b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/ViewerCategoryBlock.java
index 26deddd45..0be3ae05f 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/ViewerCategoryBlock.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/modelviewer/categories/ViewerCategoryBlock.java
@@ -245,22 +245,22 @@ public class ViewerCategoryBlock extends ModelViewerCategory<ViewerCategoryBlock
                 if (Block.hasLogicClass(block, IPainted.class)) {
                     for (DyeColor color : DyeColor.blockOrderedColors()) {
                         int newMeta = meta | ((IPainted)block.getLogic()).toMetadata(color);
-                        renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value + "_" + color.colorID + ".png"), id, newMeta, rot, zoom));
+                        renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value() + "_" + color.colorID + ".png"), id, newMeta, rot, zoom));
                     }
                 } else if (Block.hasLogicClass(block, BlockLogicFlowerStackable.class)) {
                     for (int i = 0; i <= BlockLogicFlowerStackable.MAX_STACK_COUNT; i++) {
-                        renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value + "_" + i + ".png"), id, BlockLogicFlowerStackable.setStackCount(meta, i), rot, zoom));
+                        renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value() + "_" + i + ".png"), id, BlockLogicFlowerStackable.setStackCount(meta, i), rot, zoom));
                     }
                 } else if (Block.hasLogicClass(block, BlockLogicCropsWheat.class)) {
                     for (int i = 0; i <= BlockLogicCropsWheat.MAX_GROWTH_STATE; i++) {
-                        renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value + "_" + i + ".png"), id, meta | i, rot, zoom));
+                        renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value() + "_" + i + ".png"), id, meta | i, rot, zoom));
                     }
                 } else if (Block.hasLogicClass(block, BlockLogicCropsPumpkin.class)) {
                     for (int i = 0; i < 5; i++) {
-                        renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value + "_" + i + ".png"), id, meta | i, rot, zoom));
+                        renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value() + "_" + i + ".png"), id, meta | i, rot, zoom));
                     }
                 } else {
-                    renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value + ".png"), id, meta, rot, zoom));
+                    renderInfos.add(new BlockRenderInfo(new File(outputDir, block.namespaceId().value() + ".png"), id, meta, rot, zoom));
                 }
             }
             id = ScreenModelViewer.cycleBlockId(id, 1);
diff --git a/game/client/src/main/java/net/minecraft/client/option/GameSettings.java b/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
index edb5f4137..d05b0c3ac 100644
--- a/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
+++ b/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
@@ -134,7 +134,7 @@ public class GameSettings {
 	public OptionBoolean alwaysShowDescriptions = new OptionBoolean(this, "alwaysShowDescriptions", false);
 	public OptionBoolean turnWithVehicle = new OptionBoolean(this, "turnWithVehicle", true);
 	public OptionBoolean disableCrashHandler = new OptionBoolean(this, "disableCrashHandler", false);
-	public OptionBoolean enableMultiDrawRendering = new OptionBoolean(this, "enableMultiDrawRendering", false);
+	public OptionBoolean enableMultiDrawRendering = new OptionBoolean(this, "enableMultiDrawRendering", true);
 
 	public OptionBoolean enableDevResourceLoading = new OptionBoolean(this, "enableDevResourceLoading", false);
 	public OptionBoolean downloadSoundfiles = new OptionBoolean(this, "downloadSoundfiles", true);
diff --git a/game/client/src/main/java/net/minecraft/client/render/EntityRenderDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/EntityRenderDispatcher.java
index fdd9b2918..ca64d9f7a 100644
--- a/game/client/src/main/java/net/minecraft/client/render/EntityRenderDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/EntityRenderDispatcher.java
@@ -11,11 +11,7 @@ import net.minecraft.core.entity.EntityItem;
 import net.minecraft.core.entity.EntityLightning;
 import net.minecraft.core.entity.EntityPainting;
 import net.minecraft.core.entity.Mob;
-import net.minecraft.core.entity.animal.MobButterfly;
-import net.minecraft.core.entity.animal.MobChicken;
-import net.minecraft.core.entity.animal.MobFireflyCluster;
-import net.minecraft.core.entity.animal.MobWolf;
-import net.minecraft.core.entity.animal.MobSquid;
+import net.minecraft.core.entity.animal.*;
 import net.minecraft.core.entity.monster.*;
 import net.minecraft.core.entity.projectile.ProjectileCannonball;
 import net.minecraft.core.entity.projectile.ProjectileEgg;
@@ -35,9 +31,6 @@ import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityFishingBobber;
 import net.minecraft.core.entity.EntityFallingBlock;
 import net.minecraft.core.entity.EntityPrimedTNT;
-import net.minecraft.core.entity.animal.MobCow;
-import net.minecraft.core.entity.animal.MobPig;
-import net.minecraft.core.entity.animal.MobSheep;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.entity.projectile.ProjectileArrow;
 import net.minecraft.core.entity.projectile.ProjectilePebble;
@@ -113,6 +106,7 @@ public final class EntityRenderDispatcher
         assignRenderer(ProjectileCannonball.class, new EntityRendererCannonball());
         assignRenderer(ProjectilePebble.class, new EntityRendererSprite<>(Items.AMMO_PEBBLE));
         assignRenderer(MobButterfly.class, new MobRendererButterfly());
+        assignRenderer(MobDeer.class, new MobRendererDeer(0.7F));
     }
 
     private <T extends Entity> void assignRenderer(final @NotNull Class<? extends T> clazz, final @NotNull EntityRenderer<T> renderer) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
index 08720bee3..c0ef84e18 100644
--- a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
+++ b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
@@ -18,6 +18,7 @@ import net.minecraft.client.render.terrain.ChunkRenderer;
 import net.minecraft.client.render.texture.stitcher.IconCoordinate;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.client.render.tessellator.Tessellator;
+import net.minecraft.client.render.tileentity.TileEntityRenderer;
 import net.minecraft.client.render.worldtype.WorldTypeFX;
 import net.minecraft.client.render.worldtype.WorldTypeFXDispatcher;
 import net.minecraft.client.world.WorldClient;
@@ -516,9 +517,9 @@ public class RenderGlobal
             }
         }
 
-        for (TileEntity tileEntity : tileEntities)
-        {
-            TileEntityRenderDispatcher.instance.renderTileEntity(tessellator, tileEntity, partialTick);
+        for (int i = 0, tileEntitiesSize = tileEntities.size(); i < tileEntitiesSize; i++) {
+            TileEntity tileEntity = tileEntities.get(i);
+            TileEntityRenderDispatcher.instance.renderTileEntity(tessellator, camera, tileEntity, partialTick);
         }
     }
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/TextureManager.java b/game/client/src/main/java/net/minecraft/client/render/TextureManager.java
index 47fca8990..66940ee52 100644
--- a/game/client/src/main/java/net/minecraft/client/render/TextureManager.java
+++ b/game/client/src/main/java/net/minecraft/client/render/TextureManager.java
@@ -139,7 +139,7 @@ public class TextureManager {
 	}
 	protected void addDynamicTextureOverride(DynamicTexture texture, boolean override) {
 		if (!override && dynamicTextures.containsKey(texture.targetTexture.namespaceId)) return;
-		dynamicTextures.put(texture.targetTexture.namespaceId.namespace + ":" + TextureRegistry.stitcherMapReverse.get(texture.targetTexture.parentAtlas) + "/" + texture.targetTexture.namespaceId.value, texture);
+		dynamicTextures.put(texture.targetTexture.namespaceId.namespace() + ":" + TextureRegistry.stitcherMapReverse.get(texture.targetTexture.parentAtlas) + "/" + texture.targetTexture.namespaceId.value(), texture);
 	}
 
 	private void loadCustomAnimations() {
diff --git a/game/client/src/main/java/net/minecraft/client/render/TileEntityRenderDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/TileEntityRenderDispatcher.java
index 7719805c6..afaff32da 100644
--- a/game/client/src/main/java/net/minecraft/client/render/TileEntityRenderDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/TileEntityRenderDispatcher.java
@@ -113,27 +113,30 @@ public final class TileEntityRenderDispatcher
         this.viewLerpPosZ = camera.getZ(partialTick);
     }
 
-    public <T extends TileEntity> void renderTileEntity(final @NotNull Tessellator tessellator, final @NotNull T tileEntity, final float partialTick)
+    public <T extends TileEntity> void renderTileEntity(final @NotNull Tessellator tessellator, final ICamera camera, final @NotNull T tileEntity, final float partialTick)
     {
-        if(tileEntity.getDistanceFrom(this.viewLerpPosX, this.viewLerpPosY, this.viewLerpPosZ) < 4096D)
-        {
-            assert this.world != null;
-            final float brightness;
-            if(LightmapHelper.isLightmapEnabled()) {
-            	LightmapHelper.setLightmapCoord(this.world.getLightmapCoord(tileEntity.x, tileEntity.y, tileEntity.z, 0));
-                brightness = 1.0f;
-            }else if(Global.accessor.isFullbrightEnabled()) {
-                brightness = 1.0f;
-            }else {
-                brightness = this.world.getLightBrightness(tileEntity.x, tileEntity.y, tileEntity.z);
-            }
-            GL11.glColor3f(brightness, brightness, brightness);
-            renderTileEntity(tessellator,
-                    tileEntity,
-                    (double)tileEntity.x - renderPosX,
-                    (double)tileEntity.y - renderPosY,
-                    (double)tileEntity.z - renderPosZ, partialTick);
+        final TileEntityRenderer<T> renderer = TileEntityRenderDispatcher.instance.getRenderer(tileEntity);
+
+        if (renderer == null) return;
+        if(tileEntity.getDistanceFrom(this.viewLerpPosX, this.viewLerpPosY, this.viewLerpPosZ) > 4096D) return;
+        if (!renderer.isVisible(tileEntity, camera, partialTick)) return;
+
+        assert this.world != null;
+        final float brightness;
+        if(LightmapHelper.isLightmapEnabled()) {
+            LightmapHelper.setLightmapCoord(this.world.getLightmapCoord(tileEntity.x, tileEntity.y, tileEntity.z, 0));
+            brightness = 1.0f;
+        }else if(Global.accessor.isFullbrightEnabled()) {
+            brightness = 1.0f;
+        }else {
+            brightness = this.world.getLightBrightness(tileEntity.x, tileEntity.y, tileEntity.z);
         }
+        GL11.glColor3f(brightness, brightness, brightness);
+        renderer.doRender(tessellator,
+            tileEntity,
+            (double)tileEntity.x - renderPosX,
+            (double)tileEntity.y - renderPosY,
+            (double)tileEntity.z - renderPosZ, partialTick);
     }
 
     public <T extends TileEntity> void renderTileEntity(final @NotNull Tessellator tessellator, final @NotNull T tileEntity, final double x, final double y, final double z, final float partialTick)
@@ -145,6 +148,13 @@ public final class TileEntityRenderDispatcher
         }
     }
 
+    public void tick() {
+        for (final TileEntityRenderer<?> renderer : this.renderers.values()) {
+            if (renderer == null) continue;
+            renderer.tick();
+        }
+    }
+
     public @NotNull FontRenderer getFont()
     {
         return this.font;
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelFurnace.java b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelFurnace.java
index 592b70f97..64a4a8a16 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelFurnace.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelFurnace.java
@@ -28,7 +28,7 @@ public class BlockModelFurnace<T extends BlockLogic>
             if (container != null) {
                 boolean hasOutput = container.getItem(2) != null;
                 if (hasOutput) {
-                    return TextureRegistry.getTexture(originalFront.namespaceId.namespace + ":block/" + originalFront.namespaceId.value + "_filled");
+                    return TextureRegistry.getTexture(originalFront.namespaceId.namespace() + ":block/" + originalFront.namespaceId.value() + "_filled");
                 }
             }
             return originalFront;
diff --git a/game/client/src/main/java/net/minecraft/client/render/dynamictexture/DynamicTextureFireflyJar.java b/game/client/src/main/java/net/minecraft/client/render/dynamictexture/DynamicTextureFireflyJar.java
index 56d171632..a975e53cc 100644
--- a/game/client/src/main/java/net/minecraft/client/render/dynamictexture/DynamicTextureFireflyJar.java
+++ b/game/client/src/main/java/net/minecraft/client/render/dynamictexture/DynamicTextureFireflyJar.java
@@ -14,7 +14,7 @@ public class DynamicTextureFireflyJar extends DynamicTexture {
         super(texture);
 		BufferedImage image = texture.getImageStorage();
 		if(image == null) {
-			throw new TexturePackException("Could not load image!");
+			throw new TexturePackException("Could not load image from texture'" + texture.namespaceId + "'!");
 		}
         texture.setDimension(image.getWidth(), image.getHeight());
         this.originalData = new byte[texture.getArea() * 4];
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererLightningBolt.java b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererLightningBolt.java
index e77c83682..75c6ff44a 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererLightningBolt.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererLightningBolt.java
@@ -114,5 +114,6 @@ public class EntityRendererLightningBolt extends EntityRenderer<EntityLightning>
         GL11.glDisable(GL11.GL_BLEND);
         GL11.glEnable(GL11.GL_LIGHTING);
         GL11.glEnable(GL11.GL_TEXTURE_2D);
+        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
     }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererPainting.java b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererPainting.java
index 00cddbbf7..8814440d8 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererPainting.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/EntityRendererPainting.java
@@ -150,8 +150,7 @@ public class EntityRendererPainting extends EntityRenderer<EntityPainting>
             GL11.glEnable(GL11.GL_BLEND);
             GL11.glBlendFunc(770, 771);
             GL11.glColor4f(1f, 1f, 1f, 1f);
-            NamespaceID id = EntityPainting.getBorder(borderStack);
-            bindTexture(String.format("/assets/%s/textures/art/borders/%s.png", id.namespace, id.value));
+            bindTexture(EntityPainting.getBorder(borderStack));
             renderBorder(entity, entity.art.sizeX, entity.art.sizeY);
             GL11.glDisable(GL11.GL_BLEND);
         }
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBipedArmored.java b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBipedArmored.java
index 8cc98bc95..72bc573ce 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBipedArmored.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBipedArmored.java
@@ -39,7 +39,7 @@ public abstract class MobRendererBipedArmored<T extends Mob & IArmorWearing<Huma
         } else if (item instanceof IArmorItem) {
             final IArmorItem armorItem = (IArmorItem) item;
             if (armorItem.getArmorMaterial() == null) return null;
-            bindTexture(String.format("/assets/%s/textures/armor/%s_%d.png", armorItem.getArmorMaterial().identifier.namespace, armorItem.getArmorMaterial().identifier.value, (layer != 3 ? 1 : 2)));
+            bindTexture(String.format("/assets/%s/textures/armor/%s_%d.png", armorItem.getArmorMaterial().identifier.namespace(), armorItem.getArmorMaterial().identifier.value(), (layer != 3 ? 1 : 2)));
         } else {
             return null;
         }
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererDeer.java b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererDeer.java
new file mode 100644
index 000000000..d82bf3db4
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererDeer.java
@@ -0,0 +1,44 @@
+package net.minecraft.client.render.entity;
+
+import net.minecraft.core.entity.animal.MobDeer;
+import net.minecraft.core.util.helper.MathHelper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.lwjgl.opengl.GL11;
+import org.useless.dragonfly.models.entity.BoneTransform;
+import org.useless.dragonfly.models.entity.StaticEntityModel;
+
+public class MobRendererDeer extends MobRenderer<MobDeer> {
+
+    public MobRendererDeer(final float shadowSize) {
+        super(shadowSize);
+    }
+
+    @Override
+    protected @Nullable StaticEntityModel getAndSetupModelForLayer(@NotNull final MobDeer entity, final float brightness, final float partialTick, final int layer) {
+        final StaticEntityModel model = getModel("main");
+        model.resetBones();
+
+        final BoneTransform neck = model.getTransform("neck");
+        final BoneTransform head = model.getTransform("head");
+        final BoneTransform leg0 = model.getTransform("leg0");
+        final BoneTransform leg1 = model.getTransform("leg1");
+        final BoneTransform leg2 = model.getTransform("leg2");
+        final BoneTransform leg3 = model.getTransform("leg3");
+
+        final float bodyYaw = getBodyYaw(entity, partialTick);
+        final float headYaw = getHeadYaw(entity, partialTick) - bodyYaw;
+        final float headPitch = getHeadPitch(entity, partialTick);
+        final float limbSwing = getLimbSwing(entity, partialTick);
+        final float limbYaw = getLimbYaw(entity, partialTick);
+
+        neck.rotX = headPitch;
+        head.rotY = headYaw;
+        leg0.rotX = MathHelper.cos(limbSwing * 0.6662F) * 1.4F * limbYaw;
+        leg1.rotX = MathHelper.cos(limbSwing * 0.6662F + MathHelper.PI) * 1.4F * limbYaw;
+        leg2.rotX = MathHelper.cos(limbSwing * 0.6662F + MathHelper.PI) * 1.4F * limbYaw;
+        leg3.rotX = MathHelper.cos(limbSwing * 0.6662F) * 1.4F * limbYaw;
+
+        return model;
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererWolf.java b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererWolf.java
index e6f234536..f4f239602 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererWolf.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererWolf.java
@@ -26,7 +26,7 @@ public class MobRendererWolf extends MobRenderer<MobWolf> {
         if (layer == 1) {
             final ArmorMaterial material = entity.getArmorMaterial();
             if (material == null) return null;
-            bindTexture(String.format("/assets/%s/textures/armor/wolf/%s.png", material.identifier.namespace, material.identifier.value));
+            bindTexture(String.format("/assets/%s/textures/armor/wolf/%s.png", material.identifier.namespace(), material.identifier.value()));
             model = getModel("armor");
         } else {
             model = getModel("main");
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java b/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
index 02809c47e..5974a14e2 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
@@ -105,7 +105,7 @@ public class FontRendererDefault extends FontRenderer {
                                 break;
                             }
                             case 'r': {
-                                this.currentConfig = Integer.toUnsignedLong(SF.getColor(baseConfig));
+                                this.currentConfig = Integer.toUnsignedLong(SF.getColor(baseConfig)) | (baseConfig & SF.OUTLINE);
                                 break;
                             }
                             case '+': {
@@ -153,12 +153,14 @@ public class FontRendererDefault extends FontRenderer {
         }
     }
 
-    protected double renderCharInternal(@NotNull final Font font, @NotNull final Tessellator tessellator, final char c, final double x, final double y, final double z, final int color, final boolean shadow) {
+    protected double renderCharInternal(@NotNull final Font font, @NotNull final Tessellator tessellator, final char c, final double x, final double y, final double z, final int color, boolean shadow) {
         final int rgb = color & 0xFFFFFF;
         final int shadowRGB = (color & 0xFCFCFC) >> 2; // Divide RGB by 4
         final int a = (color & 0xFF_00_00_00) != 0 ? (color >> 24) & 0xFF : 0xFF;
         tessellator.startDrawingQuads();
 
+        shadow &= !SF.isOutlined(this.currentConfig);
+
         final double px = 1;
         final double px2 = px * 2;
 
@@ -174,6 +176,16 @@ public class FontRendererDefault extends FontRenderer {
                 if (SF.isBold(this.currentConfig)) font.renderChar(tessellator, randomChar, x + px2, y + px, z, SF.isItalic(this.currentConfig));
             }
 
+            if (SF.isOutlined(this.currentConfig)) {
+                tessellator.setColorRGBA_I((rgb & 0xFCFCFC) >> 2, a);
+                final double width = SF.isBold(this.currentConfig) ? 2.05 : 1.05;
+                for (double _x = -1; _x <= width; _x += 1/3d) {
+                    for (double _y = -1; _y <= 1.05; _y += 1/3d) {
+                        font.renderChar(tessellator, randomChar, x + _x, y + _y, z, SF.isItalic(this.currentConfig));
+                    }
+                }
+            }
+
             tessellator.setColorRGBA_I(rgb, a);
             font.renderChar(tessellator, randomChar, x, y, z, SF.isItalic(this.currentConfig));
             if (SF.isBold(this.currentConfig)) {
@@ -188,6 +200,16 @@ public class FontRendererDefault extends FontRenderer {
                 if (SF.isBold(this.currentConfig)) font.renderChar(tessellator, c, x + px2, y + px, z, SF.isItalic(this.currentConfig));
             }
 
+            if (SF.isOutlined(this.currentConfig)) {
+                tessellator.setColorRGBA_I((rgb & 0xFCFCFC) >> 2, a);
+                final double width = SF.isBold(this.currentConfig) ? 2.05 : 1.05;
+                for (double _x = -1; _x <= width; _x += 1/3d) {
+                    for (double _y = -1; _y <= 1.05; _y += 1/3d) {
+                        font.renderChar(tessellator, c, x + _x, y + _y, z, SF.isItalic(this.currentConfig));
+                    }
+                }
+            }
+
             tessellator.setColorRGBA_I(rgb, a);
             shift = font.renderChar(tessellator, c, x, y, z, SF.isItalic(this.currentConfig));
             if (SF.isBold(this.currentConfig)) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/SF.java b/game/client/src/main/java/net/minecraft/client/render/font/SF.java
index b2751647c..ed72d833d 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/SF.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/SF.java
@@ -10,6 +10,7 @@ public final class SF {
     public static final long STRIKETHROUGH = (0b0000_0100L << 32);
     public static final long BOLD = (0b0000_1000L << 32);
     public static final long OBFUSCATED = (0b0001_0000L << 32);
+    public static final long OUTLINE = (0b0010_0000L << 32);
 
     private SF() {}
 
@@ -53,6 +54,14 @@ public final class SF {
         return (config & OBFUSCATED) != 0;
     }
 
+    public static long setOutlined(final long config) {
+        return config | OUTLINE;
+    }
+
+    public static boolean isOutlined(final long config) {
+        return (config & OUTLINE) != 0;
+    }
+
     public static long setColor(final long config, final int color) {
         return (config & ~MASK_COLOR) | color & MASK_COLOR;
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelDispatcher.java
index 5d8fd8a28..bda5a26b6 100644
--- a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelDispatcher.java
@@ -417,6 +417,9 @@ public class ItemModelDispatcher extends Dispatcher<Item, ItemModel> {
 
         addDispatch(new ItemModelSignPainted(Items.SIGN_PAINTED));
 
+        addDispatch(new ItemModelStandard(Items.FOOD_VENISON_RAW, "minecraft"));
+        addDispatch(new ItemModelStandard(Items.FOOD_VENISON_COOKED, "minecraft"));
+
         for (int i = 0; i < Blocks.blocksList.length; i++) {
             if (Blocks.blocksList[i] == null) continue;
             Item item = Blocks.blocksList[i].asItem();
diff --git a/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/AtlasStitcher.java b/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/AtlasStitcher.java
index 2a4977d1e..de7977032 100644
--- a/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/AtlasStitcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/AtlasStitcher.java
@@ -51,13 +51,13 @@ public class AtlasStitcher extends Texture {
     protected IconCoordinate getTexture(@NotNull NamespaceID sourceId, @NotNull NamespaceID id){
         if (textureMap.containsKey(id)) return textureMap.get(id);
 
-
-        IconCoordinate iconCoordinate = new IconCoordinate(this, sourceId, id, getSourceImagePath(id));
+        id.makePermanent();
+        IconCoordinate iconCoordinate = new IconCoordinate(this, sourceId.makePermanent(), id, getSourceImagePath(id));
         textureMap.put(id, iconCoordinate);
         return iconCoordinate;
     }
     public String getSourceImagePath(@NotNull NamespaceID id){
-        return String.format("%s/%s/%s/%s.png", rootAssetPath, id.namespace, directoryPath, id.value);
+        return String.format("%s/%s/%s/%s.png", rootAssetPath, id.namespace(), directoryPath, id.value());
     }
 
     public void preinit() {
diff --git a/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/TextureRegistry.java b/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/TextureRegistry.java
index a50d1c083..0afc1a740 100644
--- a/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/TextureRegistry.java
+++ b/game/client/src/main/java/net/minecraft/client/render/texture/stitcher/TextureRegistry.java
@@ -53,41 +53,31 @@ public class TextureRegistry {
 
     public static @NotNull IconCoordinate getTexture(@NotNull String key){
         try {
-            return getTexture(new NamespaceID(key));
+            return getTexture(NamespaceID.getTemp(key));
         } catch (HardIllegalArgumentException e) {
             throw new RuntimeException(e);
         }
     }
     public static @NotNull IconCoordinate getTexture(@NotNull NamespaceID id){
-        String[] split2 = id.value.split("/");
-        AtlasStitcher atlas = stitcherMap.get(split2[0]);
-        if (atlas == null) throw new IllegalArgumentException("Failed to find atlas '" + split2[0] + "'!");
+        final int slashIndex = id.value().indexOf("/");
+        final AtlasStitcher atlas = stitcherMap.get(id.value().substring(0, slashIndex));
+        if (atlas == null) throw new IllegalArgumentException("Failed to find atlas '" + id.value().substring(0, slashIndex) + "'!");
 
-        StringBuilder target = new StringBuilder(split2[1]);
-        for (int i = 2; i < split2.length; i++) {
-            target.append("/").append(split2[i]);
-        }
-
-        return atlas.getTexture(id, new NamespaceID(id.namespace, target.toString()));
+        return atlas.getTexture(id, NamespaceID.getTemp(id.namespace(), id.value().substring(slashIndex + 1)));
     }
     public static boolean hasTexture(@NotNull String key) {
         try {
-            return hasTexture(new NamespaceID(key));
+            return hasTexture(NamespaceID.getTemp(key));
         } catch (HardIllegalArgumentException e) {
             throw new RuntimeException(e);
         }
     }
     public static boolean hasTexture(@NotNull NamespaceID id){
-        String[] split2 = id.value.split("/");
-        AtlasStitcher atlas = stitcherMap.get(split2[0]);
-        if (atlas == null) throw new IllegalArgumentException("Failed to find atlas '" + split2[0] + "'!");
+        final int slashIndex = id.value().indexOf("/");
+        final AtlasStitcher atlas = stitcherMap.get(id.value().substring(0, slashIndex));
+        if (atlas == null) throw new IllegalArgumentException("Failed to find atlas '" + id.value().substring(0, slashIndex) + "'!");
 
-        StringBuilder target = new StringBuilder(split2[1]);
-        for (int i = 2; i < split2.length; i++) {
-            target.append("/").append(split2[i]);
-        }
-
-        return atlas.textureMap.containsKey(new NamespaceID(id.namespace, target.toString()));
+        return atlas.textureMap.containsKey(NamespaceID.getTemp(id.namespace(), id.value().substring(slashIndex + 1)));
     }
     public static AtlasStitcher register(String id, AtlasStitcher atlas){
         stitcherMap.put(id, atlas);
@@ -113,8 +103,9 @@ public class TextureRegistry {
 //        System.out.println("Printing " + path);
         for (String s : files) {
 //            System.out.println(s);
-            NamespaceID id = new NamespaceID(namespace, s.replace(".png", "").replace("//", "/"));
-            atlas.getTexture(new NamespaceID(id.namespace, stitcherMapReverse.get(atlas) + "/" + id.value), id);
+
+            NamespaceID id = NamespaceID.getTemp(namespace, s.replace(".png", "").replace("//", "/"));
+            atlas.getTexture(NamespaceID.getTemp(id.namespace(), stitcherMapReverse.get(atlas) + "/" + id.value()), id);
         }
     }
 
@@ -193,7 +184,7 @@ public class TextureRegistry {
                         final IconCoordinate coordinate = entry.getValue();
                         writer.append(Integer.toString(coordinate.iconX)).append(",").append(Integer.toString(coordinate.iconY));
                         writer.append("=");
-                        writer.append(entry.getKey().value);
+                        writer.append(entry.getKey().value());
                         writer.append(" <").append(Integer.toString(coordinate.width)).append(",").append(Integer.toString(coordinate.height)).append(">");
                         writer.append("\n");
                     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRenderer.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRenderer.java
index be25210ee..e7100d7f0 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRenderer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRenderer.java
@@ -2,6 +2,7 @@ package net.minecraft.client.render.tileentity;
 
 import net.minecraft.client.render.TextureManager;
 import net.minecraft.client.render.TileEntityRenderDispatcher;
+import net.minecraft.client.render.camera.ICamera;
 import net.minecraft.client.render.font.FontRenderer;
 import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.core.block.entity.TileEntity;
@@ -44,6 +45,10 @@ public abstract class TileEntityRenderer<T extends TileEntity>
     {
     }
 
+    public void tick() {
+
+    }
+
     protected @NotNull StaticEntityModel getModel(@NotNull final String localId) {
         StaticEntityModel model = this.modelMap.get(localId);
         if (model != null) {
@@ -69,4 +74,8 @@ public abstract class TileEntityRenderer<T extends TileEntity>
     {
         return this.renderDispatcher.getFont();
     }
+
+    public boolean isVisible(@NotNull final T tileEntity, @NotNull final ICamera camera, final float partialTick) {
+        return true;
+    }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
index 183cec6de..cdc633120 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
@@ -1,8 +1,11 @@
 package net.minecraft.client.render.tileentity;
 
+import net.minecraft.client.GLAllocation;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.render.LightmapHelper;
+import net.minecraft.client.render.camera.ICamera;
 import net.minecraft.client.render.font.FontRenderer;
+import net.minecraft.client.render.font.SF;
 import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.client.render.texture.stitcher.IconCoordinate;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
@@ -16,17 +19,24 @@ import net.minecraft.core.enums.EnumSignPicture;
 import net.minecraft.core.util.helper.Color;
 import net.minecraft.core.util.helper.DyeColor;
 import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.util.phys.AABB;
+import net.minecraft.core.world.World;
 import org.jetbrains.annotations.NotNull;
 import org.lwjgl.opengl.GL11;
 import org.lwjgl.opengl.GL12;
 import org.useless.dragonfly.models.entity.StaticEntityModel;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
 public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
 {
 
     private final Minecraft mc = Minecraft.getMinecraft();
     private final @NotNull StringBuilder builder = new StringBuilder();
     private final String[] signColorTextures = new String[16];
+    private final BufferedTextMeshRenderer textMeshRenderer = new BufferedTextMeshRenderer();
 
     public TileEntityRendererSign() {
         super();
@@ -91,11 +101,11 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
 //            colorSign = 0xa57319;
             colorSign = 0x695e43;
         }
+        GL11.glDisable(GL11.GL_BLEND);
         model.render(t);
+        GL11.glEnable(GL11.GL_BLEND);
         GL11.glPopMatrix();
 
-
-
         // Render overlay
         GL11.glPushMatrix();
         GL11.glEnable(GL11.GL_BLEND);
@@ -103,7 +113,7 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
         GL11.glNormal3f(0, 0, 1);
         GL11.glDepthMask(false);
         final EnumSignPicture picture = tileEntity.getPicture();
-        drawTexturedModalRect(1, 0.5, colorSign,TextureRegistry.getTexture(picture.getTextureKey()));
+        if (picture != EnumSignPicture.NONE) drawTexturedModalRect(1, 0.5, colorSign,TextureRegistry.getTexture(picture.getTextureKey()));
         GL11.glPopMatrix();
 
         // Render text
@@ -113,7 +123,6 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
         if (tileEntity.isGlowing()) {
             lightLevel = 1f;
             lightOffset = 96f;
-            GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
             if(LightmapHelper.isLightmapEnabled()) {
                 LightmapHelper.setLightmapCoord(LightmapHelper.getLightmapCoord(15, 15));
             }
@@ -121,38 +130,44 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
 
         GL11.glPushMatrix();
         final float scale2 = 0.2666667f * scale;
-         GL11.glTranslatef(0.0F, height * scale, 1.1f * scale);
-         GL11.glScalef(scale2, -scale2, scale2);
-         GL11.glDepthMask(false);
-         int color = Colors.allSignColors[tileEntity.getColor().id].getARGB();
+        GL11.glTranslatef(0.0F, height * scale, 1.1f * scale);
+        GL11.glScalef(scale2, -scale2, scale2);
+        GL11.glDepthMask(false);
+        int color = Colors.allSignColors[tileEntity.getColor().id].getARGB();
         final int r = (int) MathHelper.clamp((Color.redFromInt(color) * lightLevel + lightOffset), 0, 255);
         final int g = (int)MathHelper.clamp((Color.greenFromInt(color) * lightLevel + lightOffset), 0, 255);
         final int b = (int)MathHelper.clamp((Color.blueFromInt(color) * lightLevel + lightOffset), 0, 255);
-        final int borderR = (int) MathHelper.clamp((Color.redFromInt(color) * lightLevel - 127), 0, 255);
-        final int borderG = (int) MathHelper.clamp((Color.greenFromInt(color) * lightLevel - 127), 0, 255);
-        final int borderB = (int) MathHelper.clamp((Color.blueFromInt(color) * lightLevel - 127), 0, 255);
         color = Color.intToIntARGB(0, r, g, b);
-        int borderColor = Color.intToIntARGB(0, borderR, borderG, borderB);
-        for(int line = 0; line < tileEntity.signText.length; line++)
-        {
-            this.builder.setLength(0);
-            if(line == tileEntity.lineBeingEdited)
-            {
-                this.builder.append("§+§f> §-").append(tileEntity.signText[line]).append("§+§f <§-");
-            } else {
-                this.builder.append(tileEntity.signText[line]);
-            }
-            int _x = -sr.stringWidth(this.builder) / 2;
-            int _y = line * 10 - tileEntity.signText.length * 5;
-            if (tileEntity.isGlowing()) {
-                int _x2 = -sr.stringWidth(tileEntity.signText[line]) / 2;
-                for (float i = -1; i <= 1; i += 1/3f) {
-                    for (float j = -1; j <= 1; j += 1/3f) {
-                        sr.renderDouble(t, tileEntity.signText[line], _x2 + i, _y + j).setColor(borderColor).call();
-                    }
-                }
-            }
-            sr.render(t, this.builder, _x, _y).setColor(color).call();
+
+        CharSequence line1 = tileEntity.signText[0];
+        CharSequence line2 = tileEntity.signText[1];
+        CharSequence line3 = tileEntity.signText[2];
+        CharSequence line4 = tileEntity.signText[3];
+
+        switch (tileEntity.lineBeingEdited) {
+            case 0:
+                this.builder.setLength(0);
+                line1 = this.builder.append("§+§f> §-").append(line1).append("§+§f <§-");
+                break;
+            case 1:
+                this.builder.setLength(0);
+                line2 = this.builder.append("§+§f> §-").append(line2).append("§+§f <§-");
+                break;
+            case 2:
+                this.builder.setLength(0);
+                line3 = this.builder.append("§+§f> §-").append(line3).append("§+§f <§-");
+                break;
+            case 3:
+                this.builder.setLength(0);
+                line4 = this.builder.append("§+§f> §-").append(line4).append("§+§f <§-");
+                break;
+        }
+        final int _x = 0;
+        final int _y = - tileEntity.signText.length * 5;
+        if (tileEntity.isGlowing() && mc.gameSettings.fancyGraphics.value >= 1) {
+            this.textMeshRenderer.render(sr, t, line1, line2, line3, line4, _x, _y, SF.setOutlined(SF.setColor(0, color)));
+        } else {
+            this.textMeshRenderer.render(sr,t, line1, line2, line3, line4, _x, _y, SF.setColor(0, color));
         }
         GL11.glPopMatrix();
 
@@ -175,4 +190,151 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
         tessellator.draw();
     }
 
+    @Override
+    public void tick() {
+        this.textMeshRenderer.tick();
+    }
+
+    @Override
+    public void onWorldChanged(final World world) {
+        this.textMeshRenderer.flushCaches();
+    }
+
+    @Override
+    public boolean isVisible(@NotNull final TileEntitySign tileEntity, final @NotNull ICamera camera, final float partialTick) {
+        return camera.getFrustum().isVisible(AABB.getTemporaryBB(tileEntity.x, tileEntity.y, tileEntity.z, tileEntity.x + 1, tileEntity.y + 1, tileEntity.z + 1), partialTick);
+    }
+
+    public static class BufferedTextMeshRenderer {
+        private static final long MAX_NON_RENDERED_TIME = 120 * 1000;
+        private final @NotNull List<@NotNull Integer> freeDisplayLists = new ArrayList<>();
+        private final @NotNull List<@NotNull MeshEntry> bufferedMeshes = new ArrayList<>();
+
+        private final @NotNull MeshEntry referenceEntry = new MeshEntry("", "", "", "", 0);
+
+        public void tick() {
+            final Iterator<MeshEntry> iterator = this.bufferedMeshes.iterator();
+            while (iterator.hasNext()) {
+                final MeshEntry entry = iterator.next();
+                if (System.currentTimeMillis() - entry.lastUse > MAX_NON_RENDERED_TIME) {
+                    assert entry.list != -1;
+                    this.freeDisplayLists.add(entry.list);
+                    iterator.remove();
+                }
+            }
+        }
+
+        public void flushCaches() {
+            for (MeshEntry entry : bufferedMeshes) {
+                GLAllocation.deleteDisplayList(entry.list);
+            }
+            bufferedMeshes.clear();
+            for (Integer list : freeDisplayLists) {
+                GLAllocation.deleteDisplayList(list);
+            }
+            freeDisplayLists.clear();
+        }
+
+        public void render(@NotNull final FontRenderer sr,
+                           @NotNull final Tessellator t,
+                           @NotNull final CharSequence line1,
+                           @NotNull final CharSequence line2,
+                           @NotNull final CharSequence line3,
+                           @NotNull final CharSequence line4,
+                           final int x, final int y, final long config) {
+            GL11.glPushMatrix();
+            GL11.glTranslatef(x, y, 0);
+            renderInternal(sr, t, line1, line2, line3, line4, config);
+            GL11.glPopMatrix();
+        }
+
+        private void renderInternal(@NotNull final FontRenderer sr,
+                                    @NotNull final Tessellator t,
+                                    @NotNull final CharSequence line1,
+                                    @NotNull final CharSequence line2,
+                                    @NotNull final CharSequence line3,
+                                    @NotNull final CharSequence line4,
+                                    final long config) {
+            this.referenceEntry.line1 = line1.toString();
+            this.referenceEntry.line2 = line2.toString();
+            this.referenceEntry.line3 = line3.toString();
+            this.referenceEntry.line4 = line4.toString();
+            this.referenceEntry.config = config;
+
+            MeshEntry entry = null;
+            final int size = this.bufferedMeshes.size();
+            for (int i = 0; i < size; i++) {
+                final MeshEntry e = this.bufferedMeshes.get(i);
+                if (this.referenceEntry.equals(e)) {
+                    entry = e;
+                    break;
+                }
+            }
+
+            if (entry != null) {
+                entry.lastUse = System.currentTimeMillis();
+                GL11.glCallList(entry.list);
+            } else {
+                entry = new MeshEntry(line1, line2, line3, line4, config);
+                entry.lastUse = System.currentTimeMillis();
+                entry.list = getDisplayList();
+
+                GL11.glNewList(entry.list, GL11.GL_COMPILE);
+                sr.render(t, line1, -sr.stringWidth(line1) / 2, 0).setConfig(config).call();
+                sr.render(t, line2, -sr.stringWidth(line2) / 2, 10).setConfig(config).call();
+                sr.render(t, line3, -sr.stringWidth(line3) / 2, 20).setConfig(config).call();
+                sr.render(t, line4, -sr.stringWidth(line4) / 2, 30).setConfig(config).call();
+                GL11.glEndList();
+                GL11.glCallList(entry.list);
+
+                this.bufferedMeshes.add(entry);
+            }
+
+        }
+
+        private int getDisplayList() {
+            if (this.freeDisplayLists.isEmpty()) {
+                return GLAllocation.generateDisplayLists(1);
+            } else {
+                return this.freeDisplayLists.remove(0);
+            }
+        }
+
+        public static class MeshEntry {
+            public long config;
+            public @NotNull String line1;
+            public @NotNull String line2;
+            public @NotNull String line3;
+            public @NotNull String line4;
+
+            public long lastUse = 0;
+            public int list = -1;
+
+            public MeshEntry(final @NotNull CharSequence line1, final @NotNull CharSequence line2, final @NotNull CharSequence line3, final @NotNull CharSequence line4, final long config) {
+                this.config = config;
+                this.line1 = line1.toString();
+                this.line2 = line2.toString();
+                this.line3 = line3.toString();
+                this.line4 = line4.toString();
+            }
+
+            @Override
+            public boolean equals(final Object o) {
+                if (o == null || getClass() != o.getClass()) return false;
+
+                final MeshEntry entry = (MeshEntry) o;
+                return this.config == entry.config && this.line1.equals(entry.line1) && this.line2.equals(entry.line2) && this.line3.equals(entry.line3) && this.line4.equals(entry.line4);
+            }
+
+            @Override
+            public int hashCode() {
+                int result = Long.hashCode(this.config);
+                result = 31 * result + this.line1.hashCode();
+                result = 31 * result + this.line2.hashCode();
+                result = 31 * result + this.line3.hashCode();
+                result = 31 * result + this.line4.hashCode();
+                return result;
+            }
+        }
+    }
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererStatue.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererStatue.java
index bf794ec4c..b95e9e87b 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererStatue.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererStatue.java
@@ -123,7 +123,7 @@ public class TileEntityRendererStatue extends TileEntityRenderer<TileEntityStatu
             } else if (item instanceof IArmorItem) {
                 final IArmorItem armorItem = (IArmorItem) item;
                 if (armorItem.getArmorMaterial() == null) return false;
-                bindTexture(String.format("/assets/%s/textures/armor/%s_%d.png", armorItem.getArmorMaterial().identifier.namespace, armorItem.getArmorMaterial().identifier.value, (layer != 2 ? 1 : 2)));
+                bindTexture(String.format("/assets/%s/textures/armor/%s_%d.png", armorItem.getArmorMaterial().identifier.namespace(), armorItem.getArmorMaterial().identifier.value(), (layer != 2 ? 1 : 2)));
             } else {
                 return false;
             }
diff --git a/game/client/src/main/java/net/minecraft/client/sound/SoundRepository.java b/game/client/src/main/java/net/minecraft/client/sound/SoundRepository.java
index ead8db828..37fd7f76e 100644
--- a/game/client/src/main/java/net/minecraft/client/sound/SoundRepository.java
+++ b/game/client/src/main/java/net/minecraft/client/sound/SoundRepository.java
@@ -1,14 +1,17 @@
 package net.minecraft.client.sound;
 
+import com.mojang.logging.LogUtils;
 import net.minecraft.client.Minecraft;
 import net.minecraft.core.data.registry.Registry;
 import net.minecraft.core.util.collection.NamespaceID;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
 
 import java.util.*;
 
 public class SoundRepository extends Registry<NamedSoundRepository> {
+    private static final Logger LOGGER = LogUtils.getLogger();
     protected static Minecraft mc = null;
     public static SoundRepository SOUNDS = null;
     public static Set<String> namespaces = new HashSet<>();
@@ -32,15 +35,15 @@ public class SoundRepository extends Registry<NamedSoundRepository> {
 
     public @Nullable SoundEvent getSoundEvent(@Nullable NamespaceID namespaceID) {
         if (namespaceID == null) return null;
-        NamedSoundRepository soundRepository = getItem(namespaceID.namespace);
+        NamedSoundRepository soundRepository = getItem(namespaceID.namespace());
         if (soundRepository == null) {
-            if (!cachedRepositoryErrors.contains(namespaceID.namespace)) {
-                System.err.println("Could not find sound repository under namespace '" + namespaceID.namespace + "'!");
-                cachedRepositoryErrors.add(namespaceID.namespace);
+            if (!cachedRepositoryErrors.contains(namespaceID.namespace())) {
+                LOGGER.error("Could not find sound repository under namespace '{}'!", namespaceID.namespace());
+                cachedRepositoryErrors.add(namespaceID.namespace());
             }
             return null;
         }
-        return soundRepository.getItem(namespaceID.value);
+        return soundRepository.getItem(namespaceID.value());
     }
 
     public @Nullable SoundEntry getSoundEntry(@Nullable String key) {
@@ -53,8 +56,8 @@ public class SoundRepository extends Registry<NamedSoundRepository> {
         SoundEvent event = getSoundEvent(namespaceID);
         if (event == null) {
             if (!cachedEntryErrors.contains(namespaceID)) {
-                System.err.println("Could not find sound event in namespace '" + namespaceID.namespace + "' matching key '" + namespaceID.value + "'!");
-                cachedEntryErrors.add(namespaceID);
+                LOGGER.error("Could not find sound event in namespace '{}' matching key '{}'!", namespaceID.namespace(), namespaceID.value());
+                cachedEntryErrors.add(namespaceID.makePermanent());
             }
             return null;
         }
@@ -101,7 +104,7 @@ public class SoundRepository extends Registry<NamedSoundRepository> {
             value = split[1];
             if (namespace.isEmpty()) throw new IllegalArgumentException("Invalid key '" + key + "', Namespace must not be empty!");
         }
-        return new NamespaceID(namespace, value);
+        return NamespaceID.getTemp(namespace, value);
     }
 
     public static void initialize(Minecraft mc) {
diff --git a/game/client/src/main/java/org/useless/dragonfly/data/block/mojang/BlockModelMojangData.java b/game/client/src/main/java/org/useless/dragonfly/data/block/mojang/BlockModelMojangData.java
index bf31e01a2..affc2f3fa 100644
--- a/game/client/src/main/java/org/useless/dragonfly/data/block/mojang/BlockModelMojangData.java
+++ b/game/client/src/main/java/org/useless/dragonfly/data/block/mojang/BlockModelMojangData.java
@@ -112,7 +112,7 @@ public class BlockModelMojangData implements BlockModelData {
         @Nullable final Map<@NotNull Direction, @NotNull Integer> particleIndices,
         @Nullable final List<@NotNull Element> elements)
     {
-        this.modelId = id;
+        this.modelId = id.makePermanent();
         this.parent = parent;
         if (parent != null) {
             final BlockModelMojangData parentData = BlockModelMojangData.Cache.loadModelData(texturePackList, parent);
@@ -253,7 +253,7 @@ public class BlockModelMojangData implements BlockModelData {
                 }
             }
             try {
-                return new BlockModelMojangData(texturePackList, new NamespaceID(id), this.parent, this.ambientOcclusion, this.renderLayer, this.displayPosMap, this.textures, this.particleIndices, elementList);
+                return new BlockModelMojangData(texturePackList, NamespaceID.getPermanent(id), this.parent, this.ambientOcclusion, this.renderLayer, this.displayPosMap, this.textures, this.particleIndices, elementList);
             } catch (final HardIllegalArgumentException e) {
                 throw new IllegalArgumentException(e);
             }
@@ -311,7 +311,7 @@ public class BlockModelMojangData implements BlockModelData {
         public static @Nullable BlockModelMojangData loadModelData(@NotNull final TexturePackList texturePackList, @NotNull final NamespaceID id) {
             final String s = id.toString();
             if (modelDataCache.containsKey(s)) return modelDataCache.get(s);
-            final BlockModelMojangData.Builder data = loadFromStream(texturePackList.getResourceAsStream(String.format(RESOURCE_PATH, id.namespace, id.value)));
+            final BlockModelMojangData.Builder data = loadFromStream(texturePackList.getResourceAsStream(String.format(RESOURCE_PATH, id.namespace(), id.value())));
             if (data == null) {
                 LOGGER.error("Could not locate block model data for id '{}'!", id);
                 return null;
diff --git a/game/client/src/main/java/org/useless/dragonfly/data/entity/mojang/EntityGeometryMojangData.java b/game/client/src/main/java/org/useless/dragonfly/data/entity/mojang/EntityGeometryMojangData.java
index 69312810e..83a9f9efc 100644
--- a/game/client/src/main/java/org/useless/dragonfly/data/entity/mojang/EntityGeometryMojangData.java
+++ b/game/client/src/main/java/org/useless/dragonfly/data/entity/mojang/EntityGeometryMojangData.java
@@ -291,7 +291,7 @@ public class EntityGeometryMojangData implements EntityModelData {
                         for (final Map.Entry<String, JsonElement> entry : renderersObj.entrySet()) {
                             final NamespaceID id;
                             try {
-                                id = new NamespaceID(entry.getKey());
+                                id = NamespaceID.getPermanent(entry.getKey());
                             } catch (final HardIllegalArgumentException exception) {
                                 LOGGER.error("Id '{}' is malformed!", entry.getKey(), exception);
                                 continue;
diff --git a/game/client/src/main/resources/assets/minecraft/models/entity/deer/deer.geo.json b/game/client/src/main/resources/assets/minecraft/models/entity/deer/deer.geo.json
new file mode 100644
index 000000000..fac15ebfb
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/models/entity/deer/deer.geo.json
@@ -0,0 +1,73 @@
+{
+	"format_version": "1.12.0",
+	"minecraft:geometry": [
+		{
+			"description": {
+				"identifier": "geometry.deer",
+				"texture_width": 64,
+				"texture_height": 32,
+				"visible_bounds_width": 3,
+				"visible_bounds_height": 3.5,
+				"visible_bounds_offset": [0, 1.25, 0]
+			},
+			"bones": [
+				{
+					"name": "neck",
+					"pivot": [0, 12, -6.5],
+					"rotation": [12.5, 0, 0],
+					"cubes": [
+						{"origin": [-1.5, 10, -8.5], "size": [3, 7, 4], "inflate": -0.1, "uv": [11, 14]}
+					]
+				},
+				{
+					"name": "head",
+					"parent": "neck",
+					"pivot": [0, 16.9, -6.5],
+					"cubes": [
+						{"origin": [-2, 16.9, -11.5], "size": [4, 4, 7], "uv": [42, 0]},
+						{"origin": [0.5, 20.9, -5.5], "size": [8, 5, 0], "uv": [4, 4], "mirror": true},
+						{"origin": [2, 19.9, -6], "size": [2, 1, 1], "uv": [25, 4], "mirror": true},
+						{"origin": [-4, 19.9, -6], "size": [2, 1, 1], "uv": [25, 4]},
+						{"origin": [-8.5, 20.9, -5.5], "size": [8, 5, 0], "uv": [4, 4]}
+					]
+				},
+				{
+					"name": "body",
+					"pivot": [1, 11, -0.5],
+					"cubes": [
+						{"origin": [-3, 9, -7.5], "size": [6, 6, 15], "uv": [20, 11]},
+						{"origin": [-1, 13, 6.5], "size": [2, 3, 2], "uv": [27, 21]}
+					]
+				},
+				{
+					"name": "leg0",
+					"pivot": [-1, 9, 4.5],
+					"cubes": [
+						{"origin": [-2.55, 0, 4.5], "size": [2, 9, 2], "uv": [0, 21]}
+					]
+				},
+				{
+					"name": "leg1",
+					"pivot": [3, 9, 4.5],
+					"cubes": [
+						{"origin": [0.65, 0, 4.5], "size": [2, 9, 2], "uv": [0, 21], "mirror": true}
+					]
+				},
+				{
+					"name": "leg2",
+					"pivot": [-1, 9, -6.5],
+					"cubes": [
+						{"origin": [-2.55, 0, -6.5], "size": [2, 9, 2], "uv": [0, 21]}
+					]
+				},
+				{
+					"name": "leg3",
+					"pivot": [3, 9, -6.5],
+					"cubes": [
+						{"origin": [0.65, 0, -6.5], "size": [2, 9, 2], "uv": [0, 21], "mirror": true}
+					]
+				}
+			]
+		}
+	]
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/models/entity/models.json b/game/client/src/main/resources/assets/minecraft/models/entity/models.json
index 416b19095..95688a92f 100644
--- a/game/client/src/main/resources/assets/minecraft/models/entity/models.json
+++ b/game/client/src/main/resources/assets/minecraft/models/entity/models.json
@@ -7,6 +7,7 @@
         "/assets/minecraft/models/entity/chicken/chicken.geo.json",
         "/assets/minecraft/models/entity/cow/cow.geo.json",
         "/assets/minecraft/models/entity/wolf/wolf.geo.json",
+        "/assets/minecraft/models/entity/deer/deer.geo.json",
         "/assets/minecraft/models/entity/skeleton/skeleton.geo.json",
         "/assets/minecraft/models/entity/zombie/zombie.geo.json",
         "/assets/minecraft/models/entity/pigman/pigman.geo.json",
@@ -49,6 +50,9 @@
         "minecraft:chicken": {
             "main":"geometry.chicken"
         },
+        "minecraft:deer": {
+            "main":"geometry.deer"
+        },
         "minecraft:creeper": {
             "main":"geometry.creeper"
         },
diff --git a/game/client/src/main/resources/assets/minecraft/textures/entity/deer/0.png b/game/client/src/main/resources/assets/minecraft/textures/entity/deer/0.png
new file mode 100644
index 000000000..f2e7eb4b7
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/entity/deer/0.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/entity/deer/1.png b/game/client/src/main/resources/assets/minecraft/textures/entity/deer/1.png
new file mode 100644
index 000000000..351d82664
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/entity/deer/1.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/entity/deer/2.png b/game/client/src/main/resources/assets/minecraft/textures/entity/deer/2.png
new file mode 100644
index 000000000..7bf4e6366
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/entity/deer/2.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/entity/deer/variants.json b/game/client/src/main/resources/assets/minecraft/textures/entity/deer/variants.json
new file mode 100644
index 000000000..c2d7d0a05
--- /dev/null
+++ b/game/client/src/main/resources/assets/minecraft/textures/entity/deer/variants.json
@@ -0,0 +1,3 @@
+{
+    "skins": 3
+}
\ No newline at end of file
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/food_venison_cooked.png b/game/client/src/main/resources/assets/minecraft/textures/item/food_venison_cooked.png
new file mode 100644
index 000000000..0083d8409
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/food_venison_cooked.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/item/food_venison_raw.png b/game/client/src/main/resources/assets/minecraft/textures/item/food_venison_raw.png
new file mode 100644
index 000000000..eb5af2b99
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/item/food_venison_raw.png differ
diff --git a/game/client/src/main/resources/credits.txt b/game/client/src/main/resources/credits.txt
index ea57ae3f0..1dc4b5375 100644
--- a/game/client/src/main/resources/credits.txt
+++ b/game/client/src/main/resources/credits.txt
@@ -38,6 +38,7 @@ Snowman64
 Tocinin
 Vivian (@vivianfox.)
 Zaraz7
+ph
 
 !Thank you for playing
 !Better than Adventure!
\ No newline at end of file
diff --git a/game/core/src/main/java/net/minecraft/core/achievement/Achievement.java b/game/core/src/main/java/net/minecraft/core/achievement/Achievement.java
index c8246ab29..26c36de60 100644
--- a/game/core/src/main/java/net/minecraft/core/achievement/Achievement.java
+++ b/game/core/src/main/java/net/minecraft/core/achievement/Achievement.java
@@ -25,7 +25,7 @@ public class Achievement extends Stat
 
     public Achievement(NamespaceID id, String name, int iconItemId, Achievement parent)
     {
-        super(new NamespaceID(id.namespace, "achievement/" + id.value), "achievement." + name);
+        super(NamespaceID.getPermanent(id.namespace(), "achievement/" + id.value()), "achievement." + name);
         this.iconItemId = iconItemId;
         this.description = "achievement." + name;
 
diff --git a/game/core/src/main/java/net/minecraft/core/achievement/Achievements.java b/game/core/src/main/java/net/minecraft/core/achievement/Achievements.java
index badffbf21..ab9f2d21a 100644
--- a/game/core/src/main/java/net/minecraft/core/achievement/Achievements.java
+++ b/game/core/src/main/java/net/minecraft/core/achievement/Achievements.java
@@ -11,142 +11,142 @@ public abstract class Achievements
 {
     public static List<Achievement> achievementList = new ArrayList<>();
     public static Achievement OPEN_INVENTORY =
-        new Achievement(new NamespaceID("minecraft", "open_inventory"), "openInventory", Items.BOOK, null)
+        new Achievement(NamespaceID.getPermanent("minecraft", "open_inventory"), "openInventory", Items.BOOK, null)
         .setClientsideAchievement().registerAchievement();
     public static Achievement MINE_WOOD =
-        new Achievement(new NamespaceID("minecraft", "mine_wood"), "mineWood", Blocks.LOG_OAK, OPEN_INVENTORY)
+        new Achievement(NamespaceID.getPermanent("minecraft", "mine_wood"), "mineWood", Blocks.LOG_OAK, OPEN_INVENTORY)
             .registerAchievement();
     public static Achievement BUILD_WORKBENCH =
-        new Achievement(new NamespaceID("minecraft", "build_workbench"), "buildWorkBench", Blocks.WORKBENCH, MINE_WOOD)
+        new Achievement(NamespaceID.getPermanent("minecraft", "build_workbench"), "buildWorkBench", Blocks.WORKBENCH, MINE_WOOD)
             .registerAchievement();
     public static Achievement BUILD_PICKAXE =
-        new Achievement(new NamespaceID("minecraft", "build_pickaxe"), "buildPickaxe", Items.TOOL_PICKAXE_WOOD, BUILD_WORKBENCH)
+        new Achievement(NamespaceID.getPermanent("minecraft", "build_pickaxe"), "buildPickaxe", Items.TOOL_PICKAXE_WOOD, BUILD_WORKBENCH)
             .registerAchievement();
     public static Achievement BUILD_FURNACE =
-        new Achievement(new NamespaceID("minecraft", "build_furnace"), "buildFurnace", Blocks.FURNACE_STONE_ACTIVE, BUILD_PICKAXE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "build_furnace"), "buildFurnace", Blocks.FURNACE_STONE_ACTIVE, BUILD_PICKAXE)
             .registerAchievement();
     public static Achievement ACQUIRE_IRON =
-        new Achievement(new NamespaceID("minecraft", "acquire_iron"), "acquireIron", Items.INGOT_IRON, BUILD_FURNACE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "acquire_iron"), "acquireIron", Items.INGOT_IRON, BUILD_FURNACE)
             .registerAchievement();
     public static Achievement GET_DIAMONDS =
-        new Achievement(new NamespaceID("minecraft", "get_diamonds"), "getDiamonds", Items.DIAMOND, ACQUIRE_IRON)
+        new Achievement(NamespaceID.getPermanent("minecraft", "get_diamonds"), "getDiamonds", Items.DIAMOND, ACQUIRE_IRON)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement BUILD_HOE =
-        new Achievement(new NamespaceID("minecraft", "build_hoe"), "buildHoe", Items.TOOL_HOE_WOOD, BUILD_WORKBENCH)
+        new Achievement(NamespaceID.getPermanent("minecraft", "build_hoe"), "buildHoe", Items.TOOL_HOE_WOOD, BUILD_WORKBENCH)
             .registerAchievement();
     public static Achievement MAKE_BREAD =
-        new Achievement(new NamespaceID("minecraft", "make_bread"), "makeBread", Items.FOOD_BREAD, BUILD_HOE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "make_bread"), "makeBread", Items.FOOD_BREAD, BUILD_HOE)
             .registerAchievement();
     public static Achievement BAKE_CAKE =
-        new Achievement(new NamespaceID("minecraft", "bake_cake"), "bakeCake", Items.FOOD_CAKE, BUILD_HOE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "bake_cake"), "bakeCake", Items.FOOD_CAKE, BUILD_HOE)
             .registerAchievement();
     public static Achievement CRAFT_ICECREAM =
-        new Achievement(new NamespaceID("minecraft", "craft_icecream"), "craftIcecream", Items.BUCKET_ICECREAM, BUILD_HOE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "craft_icecream"), "craftIcecream", Items.BUCKET_ICECREAM, BUILD_HOE)
             .registerAchievement();
     public static Achievement CRAFT_PUMPKIN_PIE =
-        new Achievement(new NamespaceID("minecraft", "craft_pumpkin_pie"), "craftPumpkinPie", Items.FOOD_PUMPKIN_PIE, BUILD_HOE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "craft_pumpkin_pie"), "craftPumpkinPie", Items.FOOD_PUMPKIN_PIE, BUILD_HOE)
             .registerAchievement();
     public static Achievement BUILD_BETTER_PICKAXE =
-        new Achievement(new NamespaceID("minecraft", "build_better_pickaxe"), "buildBetterPickaxe", Items.TOOL_PICKAXE_STONE, BUILD_PICKAXE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "build_better_pickaxe"), "buildBetterPickaxe", Items.TOOL_PICKAXE_STONE, BUILD_PICKAXE)
             .registerAchievement();
     public static Achievement COOK_FISH =
-        new Achievement(new NamespaceID("minecraft", "cook_fish"), "cookFish", Items.FOOD_FISH_COOKED, BUILD_FURNACE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "cook_fish"), "cookFish", Items.FOOD_FISH_COOKED, BUILD_FURNACE)
             .registerAchievement();
     public static Achievement ON_A_RAIL =
-        new Achievement(new NamespaceID("minecraft", "on_a_rail"), "onARail", Blocks.RAIL, ACQUIRE_IRON)
+        new Achievement(NamespaceID.getPermanent("minecraft", "on_a_rail"), "onARail", Blocks.RAIL, ACQUIRE_IRON)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement BUILD_SWORD =
-        new Achievement(new NamespaceID("minecraft", "build_sword"), "buildSword", Items.TOOL_SWORD_WOOD, BUILD_WORKBENCH)
+        new Achievement(NamespaceID.getPermanent("minecraft", "build_sword"), "buildSword", Items.TOOL_SWORD_WOOD, BUILD_WORKBENCH)
             .registerAchievement();
     public static Achievement ALL_ARMOR_TYPES =
-        new Achievement(new NamespaceID("minecraft", "all_armor_types"), "allArmorTypes", Items.ARMOR_CHESTPLATE_STEEL, null)
+        new Achievement(NamespaceID.getPermanent("minecraft", "all_armor_types"), "allArmorTypes", Items.ARMOR_CHESTPLATE_STEEL, null)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement KILL_ENEMY =
-        new Achievement(new NamespaceID("minecraft", "kill_enemy"), "killEnemy", Items.BONE, BUILD_SWORD)
+        new Achievement(NamespaceID.getPermanent("minecraft", "kill_enemy"), "killEnemy", Items.BONE, BUILD_SWORD)
             .registerAchievement();
     public static Achievement KILL_COW =
-        new Achievement(new NamespaceID("minecraft", "kill_cow"), "killCow", Items.LEATHER, BUILD_SWORD)
+        new Achievement(NamespaceID.getPermanent("minecraft", "kill_cow"), "killCow", Items.LEATHER, BUILD_SWORD)
             .registerAchievement();
     public static Achievement FLY_PIG =
-        new Achievement(new NamespaceID("minecraft", "fly_pig"), "flyPig", Items.SADDLE, KILL_COW)
+        new Achievement(NamespaceID.getPermanent("minecraft", "fly_pig"), "flyPig", Items.SADDLE, KILL_COW)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement GET_SENSED =
-        new Achievement(new NamespaceID("minecraft", "get_sensed"), "getSensed", Blocks.MOTION_SENSOR_ACTIVE, KILL_ENEMY)
+        new Achievement(NamespaceID.getPermanent("minecraft", "get_sensed"), "getSensed", Blocks.MOTION_SENSOR_ACTIVE, KILL_ENEMY)
             .registerAchievement();
     public static Achievement CRAFT_HANDCANNON =
-        new Achievement(new NamespaceID("minecraft", "craft_hand_cannon"), "craftHandCannon", Items.HANDCANNON_UNLOADED, BUILD_BETTER_PICKAXE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "craft_hand_cannon"), "craftHandCannon", Items.HANDCANNON_UNLOADED, BUILD_BETTER_PICKAXE)
             .registerAchievement();
     public static Achievement COLLECT_STONE =
-        new Achievement(new NamespaceID("minecraft", "collect_stone"), "collectStone", Blocks.COBBLE_LIMESTONE, BUILD_PICKAXE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "collect_stone"), "collectStone", Blocks.COBBLE_LIMESTONE, BUILD_PICKAXE)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement CRUSH_BLOCKS =
-        new Achievement(new NamespaceID("minecraft", "crush_blocks"), "crushBlocks", Blocks.ICE, BUILD_BETTER_PICKAXE)
+        new Achievement(NamespaceID.getPermanent("minecraft", "crush_blocks"), "crushBlocks", Blocks.ICE, BUILD_BETTER_PICKAXE)
             .registerAchievement();
     public static Achievement REPAIR_ARMOR =
-        new Achievement(new NamespaceID("minecraft", "repair_armor"), "repairArmor", Items.CHAINLINK, KILL_ENEMY)
+        new Achievement(NamespaceID.getPermanent("minecraft", "repair_armor"), "repairArmor", Items.CHAINLINK, KILL_ENEMY)
             .registerAchievement();
     public static Achievement GET_CHAINMAIL =
-        new Achievement(new NamespaceID("minecraft", "get_chainmail"), "getChainmail", Items.ARMOR_HELMET_CHAINMAIL, REPAIR_ARMOR)
+        new Achievement(NamespaceID.getPermanent("minecraft", "get_chainmail"), "getChainmail", Items.ARMOR_HELMET_CHAINMAIL, REPAIR_ARMOR)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement OPEN_GUIDEBOOK =
-        new Achievement(new NamespaceID("minecraft", "open_guidebook"), "openGuidebook", Blocks.BOOKSHELF_PLANKS_OAK, OPEN_INVENTORY)
+        new Achievement(NamespaceID.getPermanent("minecraft", "open_guidebook"), "openGuidebook", Blocks.BOOKSHELF_PLANKS_OAK, OPEN_INVENTORY)
             .setClientsideAchievement()
             .registerAchievement();
     public static Achievement CAUGHT_EM_ALL =
-        new Achievement(new NamespaceID("minecraft", "caught_em_all"), "caughtEmAll", Items.LANTERN_FIREFLY_GREEN, BUILD_WORKBENCH)
+        new Achievement(NamespaceID.getPermanent("minecraft", "caught_em_all"), "caughtEmAll", Items.LANTERN_FIREFLY_GREEN, BUILD_WORKBENCH)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement ALL_MUSIC_DISCS =
-        new Achievement(new NamespaceID("minecraft", "all_music_discs"), "allMusicDiscs", Items.RECORD_STAL, null)
+        new Achievement(NamespaceID.getPermanent("minecraft", "all_music_discs"), "allMusicDiscs", Items.RECORD_STAL, null)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement TRIPLE_HIT =
-        new Achievement(new NamespaceID("minecraft", "triple_hit"), "tripleHit", Items.AMMO_ARROW_GOLD, KILL_ENEMY)
+        new Achievement(NamespaceID.getPermanent("minecraft", "triple_hit"), "tripleHit", Items.AMMO_ARROW_GOLD, KILL_ENEMY)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
 
 
     // Nether achievements
     public static Achievement ENTER_NETHER =
-        new Achievement(new NamespaceID("minecraft", "enter_nether"), "enterNether", Blocks.OBSIDIAN, null)
+        new Achievement(NamespaceID.getPermanent("minecraft", "enter_nether"), "enterNether", Blocks.OBSIDIAN, null)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement GET_NETHERCOAL =
-        new Achievement(new NamespaceID("minecraft", "get_nethercoal"), "getNethercoal", Items.NETHERCOAL, ENTER_NETHER)
+        new Achievement(NamespaceID.getPermanent("minecraft", "get_nethercoal"), "getNethercoal", Items.NETHERCOAL, ENTER_NETHER)
             .setType(Achievement.TYPE_NORMAL)
             .registerAchievement();
     public static Achievement LIGHT_SIGN =
-        new Achievement(new NamespaceID("minecraft", "light_sign"), "lightSign", Items.DUST_GLOWSTONE, ENTER_NETHER)
+        new Achievement(NamespaceID.getPermanent("minecraft", "light_sign"), "lightSign", Items.DUST_GLOWSTONE, ENTER_NETHER)
             .setType(Achievement.TYPE_NORMAL)
             .registerAchievement();
     public static Achievement SWIM_NETHER =
-        new Achievement(new NamespaceID("minecraft", "swim_nether"), "swimNether", Blocks.ICE, ENTER_NETHER)
+        new Achievement(NamespaceID.getPermanent("minecraft", "swim_nether"), "swimNether", Blocks.ICE, ENTER_NETHER)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement HIT_FIREBALL =
-        new Achievement(new NamespaceID("minecraft", "hit_fireball"), "hitFireball", Items.AMMO_FIREBALL, ENTER_NETHER)
+        new Achievement(NamespaceID.getPermanent("minecraft", "hit_fireball"), "hitFireball", Items.AMMO_FIREBALL, ENTER_NETHER)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
     public static Achievement SLEEP_NETHER =
-        new Achievement(new NamespaceID("minecraft", "sleep_nether"), "sleepNether", Items.BED, ENTER_NETHER)
+        new Achievement(NamespaceID.getPermanent("minecraft", "sleep_nether"), "sleepNether", Items.BED, ENTER_NETHER)
             .setType(Achievement.TYPE_SPECIAL)
             .registerAchievement();
 //    public static Achievement ENTER_WARRENS =
-//        new Achievement(new NamespaceID("minecraft", "enter_warrens"), "enterWarrens", Blocks.BEDROCK, ENTER_NETHER)
+//        new Achievement(NamespaceID.getPermanentNamespaceID("minecraft", "enter_warrens"), "enterWarrens", Blocks.BEDROCK, ENTER_NETHER)
 //            .setType(Achievement.TYPE_NORMAL)
 //            .registerAchievement();
     public static Achievement MOST_WANTED =
-        new Achievement(new NamespaceID("minecraft", "most_wanted"), "mostWanted", Items.TOOL_SWORD_GOLD, ENTER_NETHER)
+        new Achievement(NamespaceID.getPermanent("minecraft", "most_wanted"), "mostWanted", Items.TOOL_SWORD_GOLD, ENTER_NETHER)
             .setType(Achievement.TYPE_NORMAL)
             .registerAchievement();
     public static Achievement FAST_TRAVEL = // TODO
-        new Achievement(new NamespaceID("minecraft", "fast_travel"), "fastTravel", Blocks.GRASS, ENTER_NETHER)
+        new Achievement(NamespaceID.getPermanent("minecraft", "fast_travel"), "fastTravel", Blocks.GRASS, ENTER_NETHER)
             .setType(Achievement.TYPE_NORMAL)
             .registerAchievement();
 
diff --git a/game/core/src/main/java/net/minecraft/core/achievement/stat/Stat.java b/game/core/src/main/java/net/minecraft/core/achievement/stat/Stat.java
index 54c25704e..a7d3f0f31 100644
--- a/game/core/src/main/java/net/minecraft/core/achievement/stat/Stat.java
+++ b/game/core/src/main/java/net/minecraft/core/achievement/stat/Stat.java
@@ -25,7 +25,7 @@ public class Stat
     public Stat(NamespaceID id, String key, StatValueFormatter statType)
     {
         clientside = false;
-        statId = id;
+        statId = id.makePermanent();
         statKey = key;
         this.statType = statType;
     }
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 b64152d60..ef3e4c5c3 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
@@ -34,51 +34,51 @@ public abstract class StatList
     public static final String STAT_MINED = "stat_mined";
 
     public static Stat startGameStat =
-        new StatBasic(new NamespaceID("minecraft", "start_game"), "stat.startGame").setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "start_game"), "stat.startGame").setClientside().registerStat();
     public static Stat createWorldStat =
-        new StatBasic(new NamespaceID("minecraft", "create_world"), "stat.createWorld").setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "create_world"), "stat.createWorld").setClientside().registerStat();
     public static Stat loadWorldStat =
-        new StatBasic(new NamespaceID("minecraft", "load_world"), "stat.loadWorld").setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "load_world"), "stat.loadWorld").setClientside().registerStat();
     public static Stat joinMultiplayerStat =
-        new StatBasic(new NamespaceID("minecraft", "join_multiplayer"), "stat.joinMultiplayer").setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "join_multiplayer"), "stat.joinMultiplayer").setClientside().registerStat();
     public static Stat leaveGameStat =
-        new StatBasic(new NamespaceID("minecraft", "leave_game"), "stat.leaveGame").setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "leave_game"), "stat.leaveGame").setClientside().registerStat();
     public static Stat minutesPlayedStat =
-        new StatBasic(new NamespaceID("minecraft", "playtime"), "stat.playOneMinute", Stat.statTypeTime).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "playtime"), "stat.playOneMinute", Stat.statTypeTime).setClientside().registerStat();
     public static Stat distanceWalkedStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_walk"), "stat.walkOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_walk"), "stat.walkOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat distanceSwumStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_swim"), "stat.swimOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_swim"), "stat.swimOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat distanceFallenStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_fall"), "stat.fallOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_fall"), "stat.fallOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat distanceClimbedStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_climb"), "stat.climbOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_climb"), "stat.climbOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat distanceFlownStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_fly"), "stat.flyOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_fly"), "stat.flyOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat distanceDoveStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_dive"), "stat.diveOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_dive"), "stat.diveOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat distanceByMinecartStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_minecart"), "stat.minecartOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_minecart"), "stat.minecartOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat distanceByBoatStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_boat"), "stat.boatOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_boat"), "stat.boatOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat distanceByPigStat =
-        new StatBasic(new NamespaceID("minecraft", "travel_pig"), "stat.pigOneCm", Stat.statTypeDistance).setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "travel_pig"), "stat.pigOneCm", Stat.statTypeDistance).setClientside().registerStat();
     public static Stat jumpStat =
-        new StatBasic(new NamespaceID("minecraft", "jump"), "stat.jump").setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "jump"), "stat.jump").setClientside().registerStat();
     public static Stat dropStat =
-        new StatBasic(new NamespaceID("minecraft", "drop"), "stat.drop").setClientside().registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "drop"), "stat.drop").setClientside().registerStat();
     public static Stat damageDealtStat =
-        new StatBasic(new NamespaceID("minecraft", "damage_dealt"), "stat.damageDealt").registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "damage_dealt"), "stat.damageDealt").registerStat();
     public static Stat damageTakenStat =
-        new StatBasic(new NamespaceID("minecraft", "damage_taken"), "stat.damageTaken").registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "damage_taken"), "stat.damageTaken").registerStat();
     public static Stat deathsStat =
-        new StatBasic(new NamespaceID("minecraft", "deaths"), "stat.deaths").registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "deaths"), "stat.deaths").registerStat();
     public static Stat mobKillsStat =
-        new StatBasic(new NamespaceID("minecraft", "kills_mob"), "stat.mobKills").registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "kills_mob"), "stat.mobKills").registerStat();
     public static Stat playerKillsStat =
-        new StatBasic(new NamespaceID("minecraft", "kills_player"), "stat.playerKills").registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "kills_player"), "stat.playerKills").registerStat();
     public static Stat fishCaughtStat =
-        new StatBasic(new NamespaceID("minecraft", "caught_fish"), "stat.fishCaught").registerStat();
+        new StatBasic(NamespaceID.getPermanent("minecraft", "caught_fish"), "stat.fishCaught").registerStat();
 
     public static Map<NamespaceID, Stat> mobEncounterStats;
 
@@ -142,7 +142,7 @@ public abstract class StatList
         mobEncounterStats = new HashMap<>();
         for (Map.Entry<NamespaceID, Class<? extends Entity>> entry : EntityDispatcher.idToClassMap.entrySet())
         {
-            mobEncounterStats.put(entry.getKey(), new StatMob(new NamespaceID("minecraft", "encounter_" + entry.getKey().toString().replace(":", "_")), "stat.encounterMob", entry.getKey()).registerStat());
+            mobEncounterStats.put(entry.getKey(), new StatMob(NamespaceID.getPermanent("minecraft", "encounter_" + entry.getKey().toString().replace(":", "_")), "stat.encounterMob", entry.getKey()).registerStat());
         }
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/achievement/stat/StatsCounter.java b/game/core/src/main/java/net/minecraft/core/achievement/stat/StatsCounter.java
index 206539c86..4c56e4622 100644
--- a/game/core/src/main/java/net/minecraft/core/achievement/stat/StatsCounter.java
+++ b/game/core/src/main/java/net/minecraft/core/achievement/stat/StatsCounter.java
@@ -160,7 +160,7 @@ public class StatsCounter {
 				Stat stat;
 				try {
 					if (statId.contains(":")) {
-						stat = StatList.getStat(new NamespaceID(statId));
+						stat = StatList.getStat(NamespaceID.getTemp(statId));
 					} else {
 						stat = StatList.StatConverter.getStatFromDeprecatedID(Integer.parseInt(statId));
 					}
diff --git a/game/core/src/main/java/net/minecraft/core/block/Block.java b/game/core/src/main/java/net/minecraft/core/block/Block.java
index 5a6044ada..d79b3b7dc 100644
--- a/game/core/src/main/java/net/minecraft/core/block/Block.java
+++ b/game/core/src/main/java/net/minecraft/core/block/Block.java
@@ -67,7 +67,7 @@ public final class Block<T extends BlockLogic>
     Supplier<@NotNull IItemConvertible> statParent = null;
 
     Block(int id, @NotNull String translationKey, @NotNull String namespaceId, @NotNull BlockLogicSupplier<T> logicSupplier) {
-        this.namespaceID = setupNamespaceId(namespaceId);
+        this.namespaceID = setupNamespaceId(namespaceId).makePermanent();
         this.key = setupTranslationKey(translationKey);
         this.id = setupNumericId(id);
 
@@ -112,7 +112,7 @@ public final class Block<T extends BlockLogic>
 //            namespaceId = parts[0] + ":" + "block/" + parts[1];
 //        }
         try {
-            return new NamespaceID(namespaceId);
+            return NamespaceID.getPermanent(namespaceId);
         } catch (HardIllegalArgumentException e) {
             throw new RuntimeException(e);
         }
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispatcher.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispatcher.java
index a5a326a4d..94c334efb 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispatcher.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispatcher.java
@@ -20,6 +20,7 @@ public class TileEntityDispatcher {
         if(idToClassMap.containsKey(id)) {
             throw new IllegalArgumentException("Duplicate id: " + id);
         }
+        id.makePermanent();
 
         idToClassMap.put(id, entityClass);
         stringToClassMap.put(id.toString(), entityClass);
@@ -68,24 +69,24 @@ public class TileEntityDispatcher {
     public static void init() {
         if (hasInit) return;
         hasInit = true;
-        addMapping(TileEntityFurnace.class, new NamespaceID("minecraft", "furnace"));
-        addMapping(TileEntityChest.class, new NamespaceID("minecraft", "chest"));
-        addMapping(TileEntityJukebox.class, new NamespaceID("minecraft", "jukebox"));
-        addMapping(TileEntityDispenser.class, new NamespaceID("minecraft", "dispenser"));
-        addMapping(TileEntityActivator.class, new NamespaceID("minecraft", "activator"));
-        addMapping(TileEntitySign.class, new NamespaceID("minecraft", "sign"));
-        addMapping(TileEntityMobSpawner.class, new NamespaceID("minecraft", "mob_spawner"));
-        addMapping(TileEntityMovingPistonBlock.class, new NamespaceID("minecraft", "piston_moving"));
-        addMapping(TileEntityFurnaceBlast.class, new NamespaceID("minecraft", "furnace_blast"));
-        addMapping(TileEntitySensor.class, new NamespaceID("minecraft", "sensor"));
-        addMapping(TileEntityTrommel.class, new NamespaceID("minecraft", "trommel"));
-        addMapping(TileEntityBasket.class, new NamespaceID("minecraft", "basket"));
-        addMapping(TileEntityFlag.class, new NamespaceID("minecraft", "flag"));
-        addMapping(TileEntitySeat.class, new NamespaceID("minecraft", "seat"));
-        addMapping(TileEntityFlowerJar.class, new NamespaceID("minecraft", "jar_flower"));
-        addMapping(TileEntityJarButterfly.class, new NamespaceID("minecraft", "jar_butterfly"));
-        addMapping(TileEntityMeshGold.class, new NamespaceID("minecraft", "mesh_gold"));
-        addMapping(TileEntityStatue.class, new NamespaceID("minecraft", "statue_stone"));
+        addMapping(TileEntityFurnace.class, NamespaceID.getPermanent("minecraft", "furnace"));
+        addMapping(TileEntityChest.class, NamespaceID.getPermanent("minecraft", "chest"));
+        addMapping(TileEntityJukebox.class, NamespaceID.getPermanent("minecraft", "jukebox"));
+        addMapping(TileEntityDispenser.class, NamespaceID.getPermanent("minecraft", "dispenser"));
+        addMapping(TileEntityActivator.class, NamespaceID.getPermanent("minecraft", "activator"));
+        addMapping(TileEntitySign.class, NamespaceID.getPermanent("minecraft", "sign"));
+        addMapping(TileEntityMobSpawner.class, NamespaceID.getPermanent("minecraft", "mob_spawner"));
+        addMapping(TileEntityMovingPistonBlock.class, NamespaceID.getPermanent("minecraft", "piston_moving"));
+        addMapping(TileEntityFurnaceBlast.class, NamespaceID.getPermanent("minecraft", "furnace_blast"));
+        addMapping(TileEntitySensor.class, NamespaceID.getPermanent("minecraft", "sensor"));
+        addMapping(TileEntityTrommel.class, NamespaceID.getPermanent("minecraft", "trommel"));
+        addMapping(TileEntityBasket.class, NamespaceID.getPermanent("minecraft", "basket"));
+        addMapping(TileEntityFlag.class, NamespaceID.getPermanent("minecraft", "flag"));
+        addMapping(TileEntitySeat.class, NamespaceID.getPermanent("minecraft", "seat"));
+        addMapping(TileEntityFlowerJar.class, NamespaceID.getPermanent("minecraft", "jar_flower"));
+        addMapping(TileEntityJarButterfly.class, NamespaceID.getPermanent("minecraft", "jar_butterfly"));
+        addMapping(TileEntityMeshGold.class, NamespaceID.getPermanent("minecraft", "mesh_gold"));
+        addMapping(TileEntityStatue.class, NamespaceID.getPermanent("minecraft", "statue_stone"));
     }
 
     @Deprecated
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
index 943dbdf37..1313923c2 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
@@ -189,7 +189,7 @@ public class TileEntityFurnace extends TileEntity
             	}
         	}
         	
-            if(currentBurnTime == 0 && canSmelt())
+            if(currentBurnTime == 0 && furnaceItemStacks[1] != null && canSmelt())
             {
                 maxBurnTime = currentBurnTime = getBurnTimeFromItem(furnaceItemStacks[1]);
                 if(currentBurnTime > 0)
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySign.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySign.java
index 67bca0761..ad833a99a 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySign.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySign.java
@@ -77,16 +77,9 @@ public class TileEntitySign extends TileEntity
         this.owner = player.uuid;
     }
 
-    public EnumSignPicture getPicture()
+    public @Nullable EnumSignPicture getPicture()
     {
-        if (selectedPicture < 0) return null;
-
-        for (EnumSignPicture picture : EnumSignPicture.values())
-        {
-            if (picture.getId() == selectedPicture) return picture;
-        }
-
-        return null;
+        return EnumSignPicture.fromId(selectedPicture);
     }
 
     public void setPicture(EnumSignPicture picture)
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/ItemStackJsonAdapter.java b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/ItemStackJsonAdapter.java
index 321ff8109..08628163d 100644
--- a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/ItemStackJsonAdapter.java
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/ItemStackJsonAdapter.java
@@ -52,7 +52,7 @@ public class ItemStackJsonAdapter implements JsonDeserializer<ItemStack>, JsonSe
                 item = Item.itemsList[e.getAsInt()];
             } catch (NumberFormatException ignored) { // NumberFormatException thrown if element is not actually convertible to a number
                 try {
-                    item = Item.itemsMap.get(new NamespaceID(e.getAsString()));
+                    item = Item.itemsMap.get(NamespaceID.getTemp(e.getAsString()));
                 } catch (HardIllegalArgumentException ex) {
                     throw new JsonParseException(ex);
                 }
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 45b628142..047631c48 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
@@ -1584,7 +1584,7 @@ public abstract class Entity
             return translator.translateKey(EntityDispatcher.nameKeyForClass(entity.getClass()));
         }
         NamespaceID dispatcherId = EntityDispatcher.idForClass(entity.getClass());
-        if (dispatcherId != null) return dispatcherId.value;
+        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 803f01fe1..74a64f93d 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
@@ -43,7 +43,7 @@ public final class EntityDispatcher
         if (classToIdMap.containsKey(entityClass)) {
             throw new IllegalArgumentException("EntityDispatcher already contains an assignment for class " + entityClass.getSimpleName() + " with namespace " + classToIdMap.get(entityClass) + "!");
         }
-
+        namespaceID.makePermanent();
         classToIdMap.put(entityClass, namespaceID);
         idToClassMap.put(namespaceID, entityClass);
         stringIdToClassMap.put(namespaceID.toString(), entityClass);
@@ -179,49 +179,50 @@ public final class EntityDispatcher
         hasInit = true;
 
         // Misc
-        addMapping(EntityItem.class, new NamespaceID("minecraft", "item"));
-        addMapping(EntityPainting.class, new NamespaceID("minecraft", "painting"));
-        addMapping(EntityLightning.class, new NamespaceID("minecraft", "lightning"));
-        addMapping(EntityFallingBlock.class, new NamespaceID("minecraft", "falling_block"));
-        addMapping(EntityMinecart.class, new NamespaceID("minecraft", "minecart"));
-        addMapping(EntityBoat.class, new NamespaceID("minecraft", "boat"));
-        addMapping(EntityPrimedTNT.class, new NamespaceID("minecraft", "primed_tnt"));
+        addMapping(EntityItem.class, NamespaceID.getPermanent("minecraft", "item"));
+        addMapping(EntityPainting.class, NamespaceID.getPermanent("minecraft", "painting"));
+        addMapping(EntityLightning.class, NamespaceID.getPermanent("minecraft", "lightning"));
+        addMapping(EntityFallingBlock.class, NamespaceID.getPermanent("minecraft", "falling_block"));
+        addMapping(EntityMinecart.class, NamespaceID.getPermanent("minecraft", "minecart"));
+        addMapping(EntityBoat.class, NamespaceID.getPermanent("minecraft", "boat"));
+        addMapping(EntityPrimedTNT.class, NamespaceID.getPermanent("minecraft", "primed_tnt"));
 
         // Projectiles
-        addMapping(ProjectilePebble.class, new NamespaceID("minecraft", "pebble"));
-        addMapping(ProjectileArrow.class, new NamespaceID("minecraft", "arrow"));
-        addMapping(ProjectileArrowGolden.class, new NamespaceID("minecraft", "arrow_golden"));
-        addMapping(ProjectileArrowPurple.class, new NamespaceID("minecraft", "arrow_purple"));
-        addMapping(ProjectileSnowball.class, new NamespaceID("minecraft", "snowball"));
-        addMapping(ProjectileFireball.class, new NamespaceID("minecraft", "fireball"));
-        addMapping(ProjectileCannonball.class, new NamespaceID("minecraft", "cannonball"));
-
-        addMapping(Mob.class, new NamespaceID("minecraft", "mob")); // Abstract class, shouldn't ever be instantiated
-        addMapping(MobHuman.class, new NamespaceID("minecraft", "human"));
-        addMapping(Player.class, new NamespaceID("minecraft", "player"));
+        addMapping(ProjectilePebble.class, NamespaceID.getPermanent("minecraft", "pebble"));
+        addMapping(ProjectileArrow.class, NamespaceID.getPermanent("minecraft", "arrow"));
+        addMapping(ProjectileArrowGolden.class, NamespaceID.getPermanent("minecraft", "arrow_golden"));
+        addMapping(ProjectileArrowPurple.class, NamespaceID.getPermanent("minecraft", "arrow_purple"));
+        addMapping(ProjectileSnowball.class, NamespaceID.getPermanent("minecraft", "snowball"));
+        addMapping(ProjectileFireball.class, NamespaceID.getPermanent("minecraft", "fireball"));
+        addMapping(ProjectileCannonball.class, NamespaceID.getPermanent("minecraft", "cannonball"));
+
+        addMapping(Mob.class, NamespaceID.getPermanent("minecraft", "mob")); // Abstract class, shouldn't ever be instantiated
+        addMapping(MobHuman.class, NamespaceID.getPermanent("minecraft", "human"));
+        addMapping(Player.class, NamespaceID.getPermanent("minecraft", "player"));
 
         // Hostile
-        addMapping(MobZombie.class, new NamespaceID("minecraft", "zombie"), "guidebook.section.mob.zombie.name");
-        addMapping(MobZombieArmored.class, new NamespaceID("minecraft", "zombie_armored"), "guidebook.section.mob.armored_zombie.name");
-        addMapping(MobZombiePig.class, new NamespaceID("minecraft", "zombie_pigman"), "guidebook.section.mob.zombie_pigman.name");
-        addMapping(MobCreeper.class, new NamespaceID("minecraft", "creeper"), "guidebook.section.mob.creeper.name");
-        addMapping(MobSkeleton.class, new NamespaceID("minecraft", "skeleton"), "guidebook.section.mob.skeleton.name");
-        addMapping(MobSpider.class, new NamespaceID("minecraft", "spider"), "guidebook.section.mob.spider.name");
-        addMapping(MobGiant.class, new NamespaceID("minecraft", "giant"));
-        addMapping(MobSlime.class, new NamespaceID("minecraft", "slime"), "guidebook.section.mob.slime.name");
-        addMapping(MobGhast.class, new NamespaceID("minecraft", "ghast"), "guidebook.section.mob.ghast.name");
-        addMapping(MobSnowman.class, new NamespaceID("minecraft", "snowman"), "guidebook.section.mob.snowman.name");
+        addMapping(MobZombie.class, NamespaceID.getPermanent("minecraft", "zombie"), "guidebook.section.mob.zombie.name");
+        addMapping(MobZombieArmored.class, NamespaceID.getPermanent("minecraft", "zombie_armored"), "guidebook.section.mob.armored_zombie.name");
+        addMapping(MobZombiePig.class, NamespaceID.getPermanent("minecraft", "zombie_pigman"), "guidebook.section.mob.zombie_pigman.name");
+        addMapping(MobCreeper.class, NamespaceID.getPermanent("minecraft", "creeper"), "guidebook.section.mob.creeper.name");
+        addMapping(MobSkeleton.class, NamespaceID.getPermanent("minecraft", "skeleton"), "guidebook.section.mob.skeleton.name");
+        addMapping(MobSpider.class, NamespaceID.getPermanent("minecraft", "spider"), "guidebook.section.mob.spider.name");
+        addMapping(MobGiant.class, NamespaceID.getPermanent("minecraft", "giant"));
+        addMapping(MobSlime.class, NamespaceID.getPermanent("minecraft", "slime"), "guidebook.section.mob.slime.name");
+        addMapping(MobGhast.class, NamespaceID.getPermanent("minecraft", "ghast"), "guidebook.section.mob.ghast.name");
+        addMapping(MobSnowman.class, NamespaceID.getPermanent("minecraft", "snowman"), "guidebook.section.mob.snowman.name");
 
         // Passive
-        addMapping(MobScorpion.class, new NamespaceID("minecraft", "scorpion"));
-        addMapping(MobPig.class, new NamespaceID("minecraft", "pig"), "guidebook.section.mob.pig.name");
-        addMapping(MobSheep.class, new NamespaceID("minecraft", "sheep"), "guidebook.section.mob.sheep.name");
-        addMapping(MobCow.class, new NamespaceID("minecraft", "cow"), "guidebook.section.mob.cow.name");
-        addMapping(MobChicken.class, new NamespaceID("minecraft", "chicken"), "guidebook.section.mob.chicken.name");
-        addMapping(MobSquid.class, new NamespaceID("minecraft", "squid"), "guidebook.section.mob.squid.name");
-        addMapping(MobWolf.class, new NamespaceID("minecraft", "wolf"), "guidebook.section.mob.wolf.name");
-        addMapping(MobFireflyCluster.class, new NamespaceID("minecraft", "firefly_cluster"), "guidebook.section.mob.firefly_cluster.name");
-        addMapping(MobButterfly.class, new NamespaceID("minecraft", "butterfly"), "guidebook.section.mob.butterfly.name");
+        addMapping(MobScorpion.class, NamespaceID.getPermanent("minecraft", "scorpion"));
+        addMapping(MobPig.class, NamespaceID.getPermanent("minecraft", "pig"), "guidebook.section.mob.pig.name");
+        addMapping(MobSheep.class, NamespaceID.getPermanent("minecraft", "sheep"), "guidebook.section.mob.sheep.name");
+        addMapping(MobCow.class, NamespaceID.getPermanent("minecraft", "cow"), "guidebook.section.mob.cow.name");
+        addMapping(MobChicken.class, NamespaceID.getPermanent("minecraft", "chicken"), "guidebook.section.mob.chicken.name");
+        addMapping(MobSquid.class, NamespaceID.getPermanent("minecraft", "squid"), "guidebook.section.mob.squid.name");
+        addMapping(MobWolf.class, NamespaceID.getPermanent("minecraft", "wolf"), "guidebook.section.mob.wolf.name");
+        addMapping(MobFireflyCluster.class, NamespaceID.getPermanent("minecraft", "firefly_cluster"), "guidebook.section.mob.firefly_cluster.name");
+        addMapping(MobButterfly.class, NamespaceID.getPermanent("minecraft", "butterfly"), "guidebook.section.mob.butterfly.name");
+        addMapping(MobDeer.class, NamespaceID.getPermanent("minecraft", "deer"), "guidebook.section.mob.deer.name");
     }
 
     @Deprecated
diff --git a/game/core/src/main/java/net/minecraft/core/entity/EntityPainting.java b/game/core/src/main/java/net/minecraft/core/entity/EntityPainting.java
index 2bc03d683..ed9e9b0f7 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/EntityPainting.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/EntityPainting.java
@@ -372,21 +372,21 @@ public class EntityPainting extends Entity
             entityData.set(DATA_ITEM_METADATA_ID, (byte) 0);
         }
     }
-    private static final HashMap<String, NamespaceID> borderMaterialMap = new HashMap<>();
+    private static final HashMap<String, String> borderMaterialMap = new HashMap<>();
     public static void addBorder(ItemStack stack, NamespaceID imagePath){
-        borderMaterialMap.put(stack.itemID + "|" + stack.getMetadata(), imagePath);
+        borderMaterialMap.put(stack.itemID + "|" + stack.getMetadata(), String.format("/assets/%s/textures/art/borders/%s.png", imagePath.namespace(), imagePath.value()));
     }
-    public static NamespaceID getBorder(ItemStack stack){
+    public static String getBorder(ItemStack stack){
         return borderMaterialMap.get(stack.itemID + "|" + stack.getMetadata());
     }
     static {
-        addBorder(Items.INGOT_IRON.getDefaultStack(), new NamespaceID("minecraft", "border_iron"));
-        addBorder(Items.INGOT_GOLD.getDefaultStack(), new NamespaceID("minecraft", "border_gold"));
-        addBorder(Items.INGOT_STEEL.getDefaultStack(), new NamespaceID("minecraft", "border_steel"));
-        addBorder(new ItemStack(Items.DYE, 1, 4), new NamespaceID("minecraft", "border_lapis"));
-        addBorder(Items.DIAMOND.getDefaultStack(), new NamespaceID("minecraft", "border_diamond"));
-        addBorder(Items.DUST_REDSTONE.getDefaultStack(), new NamespaceID("minecraft", "border_redstone"));
-        addBorder(Items.OLIVINE.getDefaultStack(), new NamespaceID("minecraft", "border_olivine"));
-        addBorder(Items.QUARTZ.getDefaultStack(), new NamespaceID("minecraft", "border_quartz"));
+        addBorder(Items.INGOT_IRON.getDefaultStack(), NamespaceID.getPermanent("minecraft", "border_iron"));
+        addBorder(Items.INGOT_GOLD.getDefaultStack(), NamespaceID.getPermanent("minecraft", "border_gold"));
+        addBorder(Items.INGOT_STEEL.getDefaultStack(), NamespaceID.getPermanent("minecraft", "border_steel"));
+        addBorder(new ItemStack(Items.DYE, 1, 4), NamespaceID.getPermanent("minecraft", "border_lapis"));
+        addBorder(Items.DIAMOND.getDefaultStack(), NamespaceID.getPermanent("minecraft", "border_diamond"));
+        addBorder(Items.DUST_REDSTONE.getDefaultStack(), NamespaceID.getPermanent("minecraft", "border_redstone"));
+        addBorder(Items.OLIVINE.getDefaultStack(), NamespaceID.getPermanent("minecraft", "border_olivine"));
+        addBorder(Items.QUARTZ.getDefaultStack(), NamespaceID.getPermanent("minecraft", "border_quartz"));
     }
 }
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 d58874bc8..ba8615728 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
@@ -100,7 +100,7 @@ public abstract class Mob extends Entity
     protected @NotNull String basePath = "/assets/minecraft/textures/entity/char/";
     protected @NotNull String defaultTexture = "/assets/minecraft/textures/entity/char/0.png";
     protected @NotNull String variantJsonPath = "/assets/minecraft/textures/entity/char/variant.json";
-    protected @NotNull NamespaceID textureIdentifier = new NamespaceID("minecraft", "char");
+    protected @NotNull NamespaceID textureIdentifier = NamespaceID.getPermanent("minecraft", "char");
 
     public boolean isSkating = false;
 
@@ -154,8 +154,8 @@ public abstract class Mob extends Entity
     }
 
     protected final void setTextureIdentifier(final  @NotNull String namespace, final @NotNull String id) {
-        this.textureIdentifier = new NamespaceID(namespace, id);
-        this.basePath = String.format("/assets/%s/textures/entity/%s/", this.textureIdentifier.namespace, this.textureIdentifier.value);
+        this.textureIdentifier = NamespaceID.getPermanent(namespace, id);
+        this.basePath = String.format("/assets/%s/textures/entity/%s/", this.textureIdentifier.namespace(), this.textureIdentifier.value());
         this.defaultTexture = this.basePath + "0.png";
         this.variantJsonPath = this.basePath + "variants.json";
     }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/animal/MobAnimal.java b/game/core/src/main/java/net/minecraft/core/entity/animal/MobAnimal.java
index c8793e3e7..189ea6dde 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/animal/MobAnimal.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/animal/MobAnimal.java
@@ -56,7 +56,7 @@ public abstract class MobAnimal extends MobPathfinder
         if(world.getSeasonManager().getCurrentSeason() == Seasons.OVERWORLD_SPRING)
             return 8;
         if(world.getSeasonManager().getCurrentSeason() == Seasons.OVERWORLD_WINTER)
-            return 1;
+            return 0;
         return 4;
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/entity/animal/MobDeer.java b/game/core/src/main/java/net/minecraft/core/entity/animal/MobDeer.java
new file mode 100644
index 000000000..d368c873f
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/entity/animal/MobDeer.java
@@ -0,0 +1,85 @@
+package net.minecraft.core.entity.animal;
+
+import net.minecraft.core.WeightedRandomLootObject;
+import net.minecraft.core.entity.AgedMob;
+import net.minecraft.core.entity.MobAge;
+import net.minecraft.core.item.Items;
+import com.mojang.nbt.tags.CompoundTag;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.season.Seasons;
+import org.jetbrains.annotations.NotNull;
+
+public class MobDeer
+        extends MobAnimal
+        implements AgedMob
+{
+    private final @NotNull MobAge age;
+
+    public MobDeer(World world)
+    {
+        super(world);
+        setTextureIdentifier("minecraft", "deer");
+        setSize(0.9F, 1.3F);
+        mobDrops.add(new WeightedRandomLootObject(Items.FOOD_VENISON_RAW.getDefaultStack(), 1, 2));
+        this.age = MobAge.newRandom(this, MobAge.YEAR_LENGTH_DAYS * 5, 14, MobAge.YEAR_LENGTH_DAYS * 4);
+    }
+
+    @Override
+    public void onLivingUpdate() {
+        super.onLivingUpdate();
+
+        this.getMobAge().tick(this.world);
+    }
+
+    @Override
+    public @NotNull MobAge getMobAge() {
+        return this.age;
+    }
+
+    @Override
+    public void addAdditionalSaveData(@NotNull CompoundTag tag)
+    {
+        super.addAdditionalSaveData(tag);
+
+        this.age.writeTag(tag);
+    }
+
+    @Override
+    public int getMaxSpawnedInChunk()
+    {
+        if(world.getSeasonManager().getCurrentSeason() == Seasons.OVERWORLD_WINTER)
+            return 4;
+        return 0;
+    }
+
+    @Override
+    public void readAdditionalSaveData(@NotNull CompoundTag tag)
+    {
+        super.readAdditionalSaveData(tag);
+
+        this.age.readTag(tag);
+    }
+
+    //Todo: change with new sounds
+    @Override
+    public String getLivingSound()
+    {
+        return "mob.cow";
+    }
+    @Override
+    protected String getHurtSound()
+    {
+        return "mob.cowhurt";
+    }
+    @Override
+    protected String getDeathSound()
+    {
+        return "mob.cowhurt";
+    }
+
+    @Override
+    protected float getSoundVolume()
+    {
+        return 0.4F;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/enums/EnumSignPicture.java b/game/core/src/main/java/net/minecraft/core/enums/EnumSignPicture.java
index abd5d27ef..0b33796b0 100644
--- a/game/core/src/main/java/net/minecraft/core/enums/EnumSignPicture.java
+++ b/game/core/src/main/java/net/minecraft/core/enums/EnumSignPicture.java
@@ -1,5 +1,7 @@
 package net.minecraft.core.enums;
 
+import org.jetbrains.annotations.Nullable;
+
 public enum EnumSignPicture
 {
     NONE                (0,  "sign.image.none",             "minecraft:sign/none"),
@@ -48,6 +50,7 @@ public enum EnumSignPicture
     CHECK               (43, "sign.image.check",            "minecraft:sign/check"),
     EXIT                (44, "sign.image.exit",             "minecraft:sign/exit");
 
+    private static EnumSignPicture[] vals = values();
     private final int id;
     private final String languageKey;
     private final String texKey;
@@ -73,4 +76,9 @@ public enum EnumSignPicture
     {
         return texKey;
     }
+
+    public static @Nullable EnumSignPicture fromId(int id) {
+        if (id < 0 || id >= vals.length) return null;
+        return vals[id];
+    }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/item/Item.java b/game/core/src/main/java/net/minecraft/core/item/Item.java
index 742178d66..a68fd0060 100644
--- a/game/core/src/main/java/net/minecraft/core/item/Item.java
+++ b/game/core/src/main/java/net/minecraft/core/item/Item.java
@@ -60,7 +60,7 @@ public class Item implements ITaggable<Item>, IItemConvertible
         hasSubtypes = false;
         containerItem = null;
         this.id = id;
-        this.namespaceID = namespaceId;
+        this.namespaceID = namespaceId.makePermanent();
         if (itemsList[id] != null) {
             throw new IllegalArgumentException("Item slot '" + id + "' is already occupied by '" + itemsList[id] + "' when adding " + this);
         }
@@ -82,7 +82,7 @@ public class Item implements ITaggable<Item>, IItemConvertible
 
     private static NamespaceID makeNamespaceID(String string){ // This exists because java is idiotic
         try {
-            return new NamespaceID(string);
+            return NamespaceID.getPermanent(string);
         } catch (HardIllegalArgumentException e) {
             throw new IllegalArgumentException(e);
         }
@@ -98,7 +98,7 @@ public class Item implements ITaggable<Item>, IItemConvertible
                 stat = replaceItem.createStat(statID, nameKey);
             }
         } else {
-            stat = new StatItem(new NamespaceID(namespaceID.namespace, namespaceID.value + "_" + statID), nameKey, id).registerStat();
+            stat = new StatItem(NamespaceID.getPermanent(namespaceID.namespace(), namespaceID.value() + "_" + statID), nameKey, id).registerStat();
         }
 
         putStat(statID, stat);
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemSign.java b/game/core/src/main/java/net/minecraft/core/item/ItemSign.java
index 027ca73f6..b2135f0bd 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemSign.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemSign.java
@@ -2,6 +2,7 @@ package net.minecraft.core.item;
 
 
 import net.minecraft.core.block.Blocks;
+import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.block.entity.TileEntitySign;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumBlockSoundEffectType;
@@ -57,8 +58,12 @@ public class ItemSign extends Item
         TileEntitySign tileentitysign = (TileEntitySign)world.getTileEntity(blockX, blockY, blockZ);
         if(tileentitysign != null)
         {
-            tileentitysign.setOwner(player);
-            player.displaySignEditorScreen(tileentitysign);
+            if (itemstack.getData().containsKey("tileEntityData")) {
+                tileentitysign.readAdditionalData(itemstack.getData().getCompound("tileEntityData"));
+            } else {
+                tileentitysign.setOwner(player);
+                player.displaySignEditorScreen(tileentitysign);
+            }
         }
         return true;
     }
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemSignPainted.java b/game/core/src/main/java/net/minecraft/core/item/ItemSignPainted.java
index 3a00fd3ae..73e762e31 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemSignPainted.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemSignPainted.java
@@ -55,8 +55,12 @@ public class ItemSignPainted extends ItemSign{
         TileEntitySign tileentitysign = (TileEntitySign)world.getTileEntity(blockX, blockY, blockZ);
         if(tileentitysign != null)
         {
-            tileentitysign.setOwner(player);
-            player.displaySignEditorScreen(tileentitysign);
+            if (itemstack.getData().containsKey("tileEntityData")) {
+                tileentitysign.readAdditionalData(itemstack.getData().getCompound("tileEntityData"));
+            } else {
+                tileentitysign.setOwner(player);
+                player.displaySignEditorScreen(tileentitysign);
+            }
         }
         return true;
     }
diff --git a/game/core/src/main/java/net/minecraft/core/item/Items.java b/game/core/src/main/java/net/minecraft/core/item/Items.java
index 8b55d1ad4..96c21f50a 100644
--- a/game/core/src/main/java/net/minecraft/core/item/Items.java
+++ b/game/core/src/main/java/net/minecraft/core/item/Items.java
@@ -86,6 +86,8 @@ public final class Items {
     public static Item FLINT;
     public static Item FOOD_PORKCHOP_RAW;
     public static Item FOOD_PORKCHOP_COOKED;
+    public static Item FOOD_VENISON_RAW;
+    public static Item FOOD_VENISON_COOKED;
     public static Item PAINTING;
     public static Item FOOD_APPLE_GOLD;
     public static Item SIGN;
@@ -422,5 +424,7 @@ public final class Items {
         STATUE_MARBLE = new ItemStatue("statue.marble", "minecraft:item/statue_marble", 16547, Blocks.STATUE_MARBLE_LOWER, Blocks.STATUE_MARBLE_UPPER);
         STATUE_PIGMAN = new ItemStatue("statue.pigman", "minecraft:item/statue_pigman", 16548, Blocks.STATUE_PIGMAN_LOWER, Blocks.STATUE_PIGMAN_UPPER);
         SIGN_PAINTED = (new ItemSignPainted("sign.painted", "minecraft:item/sign_painted", 16549));
+        FOOD_VENISON_RAW = (new ItemFood("food.venison.raw", "minecraft:item/food_venison_raw", 16550, 3, 16, true, 4));
+        FOOD_VENISON_COOKED = (new ItemFood("food.venison.cooked", "minecraft:item/food_venison_cooked", 16551, 12, 8, true, 4));
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/item/material/ArmorMaterial.java b/game/core/src/main/java/net/minecraft/core/item/material/ArmorMaterial.java
index d3dd6792a..33b9a23ca 100644
--- a/game/core/src/main/java/net/minecraft/core/item/material/ArmorMaterial.java
+++ b/game/core/src/main/java/net/minecraft/core/item/material/ArmorMaterial.java
@@ -31,7 +31,7 @@ public class ArmorMaterial {
 	private final Map<DamageType, Float> protection = new HashMap<>();
 	
 	public ArmorMaterial(NamespaceID identifier, int durability) {
-		this.identifier = identifier;
+		this.identifier = identifier.makePermanent();
 		this.durability = durability;
 
 		ARMOR_MATERIALS.add(this);
@@ -50,37 +50,37 @@ public class ArmorMaterial {
 		return this;
 	}
 	
-	public static final ArmorMaterial LEATHER = register(new ArmorMaterial(new NamespaceID("minecraft", "cloth"), 180)
+	public static final ArmorMaterial LEATHER = register(new ArmorMaterial(NamespaceID.getPermanent("minecraft", "cloth"), 180)
 		.withProtectionPercentage(COMBAT, 20.0f)
 		.withProtectionPercentage(BLAST, 20.0f)
 		.withProtectionPercentage(FIRE, 20.0f)
 		.withProtectionPercentage(FALL, 120.0f)
 	);
-	public static final ArmorMaterial CHAINMAIL = register(new ArmorMaterial(new NamespaceID("minecraft", "chain"), 240)
+	public static final ArmorMaterial CHAINMAIL = register(new ArmorMaterial(NamespaceID.getPermanent("minecraft", "chain"), 240)
 		.withProtectionPercentage(COMBAT, 120.0f)
 		.withProtectionPercentage(BLAST, 35.0f)
 		.withProtectionPercentage(FIRE, 35.0f)
 		.withProtectionPercentage(FALL, 35.0f)
 	);
-	public static final ArmorMaterial IRON = register(new ArmorMaterial(new NamespaceID("minecraft", "iron"), 200)
+	public static final ArmorMaterial IRON = register(new ArmorMaterial(NamespaceID.getPermanent("minecraft", "iron"), 200)
 		.withProtectionPercentage(COMBAT, 45.0f)
 		.withProtectionPercentage(BLAST, 45.0f)
 		.withProtectionPercentage(FIRE, 45.0f)
 		.withProtectionPercentage(FALL, 45.0f)
 	);
-	public static final ArmorMaterial GOLD = register(new ArmorMaterial(new NamespaceID("minecraft", "gold"), 120)
+	public static final ArmorMaterial GOLD = register(new ArmorMaterial(NamespaceID.getPermanent("minecraft", "gold"), 120)
 		.withProtectionPercentage(COMBAT, 70.0f)
 		.withProtectionPercentage(BLAST, 70.0f)
 		.withProtectionPercentage(FIRE, 70.0f)
 		.withProtectionPercentage(FALL, 70.0f)
 	);
-	public static final ArmorMaterial DIAMOND = register(new ArmorMaterial(new NamespaceID("minecraft", "diamond"), 800)
+	public static final ArmorMaterial DIAMOND = register(new ArmorMaterial(NamespaceID.getPermanent("minecraft", "diamond"), 800)
 		.withProtectionPercentage(COMBAT, 66.0f)
 		.withProtectionPercentage(BLAST, 66.0f)
 		.withProtectionPercentage(FIRE, 124.0f)
 		.withProtectionPercentage(FALL, 66.0f)
 	);
-	public static final ArmorMaterial STEEL = register(new ArmorMaterial(new NamespaceID("minecraft", "steel"), 1200)
+	public static final ArmorMaterial STEEL = register(new ArmorMaterial(NamespaceID.getPermanent("minecraft", "steel"), 1200)
 		.withProtectionPercentage(COMBAT, 55.0f)
 		.withProtectionPercentage(BLAST, 150.0f)
 		.withProtectionPercentage(FIRE, 55.0f)
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/helpers/BlockArgumentParser.java b/game/core/src/main/java/net/minecraft/core/net/command/helpers/BlockArgumentParser.java
index d28681588..190b8c2bf 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/helpers/BlockArgumentParser.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/helpers/BlockArgumentParser.java
@@ -17,7 +17,7 @@ import java.util.function.Consumer;
 
 public class BlockArgumentParser extends ArgumentParser {
     private static final SimpleCommandExceptionType INVALID_BLOCK = new SimpleCommandExceptionType(() -> I18n.getInstance().translateKey("command.argument_types.block.invalid_block"));
-    public static final NamespaceID AIR_BLOCK = new NamespaceID("minecraft", "block/air");
+    public static final NamespaceID AIR_BLOCK = NamespaceID.getPermanent("minecraft", "block/air");
     private Block<?> block;
     private int metadata = 0;
     private CompoundTag tag = new CompoundTag();
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 c5dad2470..5bca2e9ec 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
@@ -116,11 +116,11 @@ public class EntitySelectorOptions {
                 CommandHelper.suggest("!Player", builder);
                 CommandHelper.suggest("Player", builder);
                 for (NamespaceID key : EntityDispatcher.idToClassMap.keySet()) {
-                    if (!(key.toString().startsWith(string) || (key.namespace.equals("minecraft") && key.value.startsWith(string)))) continue;
+                    if (!(key.toString().startsWith(string) || (key.namespace().equals("minecraft") && key.value().startsWith(string)))) continue;
                     CommandHelper.suggest("!" + key, builder);
                     if (invert) continue;
-                    if (key.namespace.equals("minecraft")) {
-                        CommandHelper.suggest(key.value, builder);
+                    if (key.namespace().equals("minecraft")) {
+                        CommandHelper.suggest(key.value(), builder);
                     }
                     CommandHelper.suggest(key.toString(), builder);
                 }
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/util/CommandHelper.java b/game/core/src/main/java/net/minecraft/core/net/command/util/CommandHelper.java
index 74575ed23..81c487f46 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/util/CommandHelper.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/util/CommandHelper.java
@@ -94,8 +94,8 @@ public class CommandHelper {
     public static boolean matchesNamespaceId(NamespaceID namespaceID, String input) {
         if (input.contains(":")) {
             return namespaceID.toString().equalsIgnoreCase(input);
-        } else if (namespaceID.namespace.equals("minecraft")) {
-            return namespaceID.value.equalsIgnoreCase(input);
+        } else if (namespaceID.namespace().equals("minecraft")) {
+            return namespaceID.value().equalsIgnoreCase(input);
         }
         return false;
     }
diff --git a/game/core/src/main/java/net/minecraft/core/net/packet/PacketStatistic.java b/game/core/src/main/java/net/minecraft/core/net/packet/PacketStatistic.java
index a71558d9b..6b76a85e4 100644
--- a/game/core/src/main/java/net/minecraft/core/net/packet/PacketStatistic.java
+++ b/game/core/src/main/java/net/minecraft/core/net/packet/PacketStatistic.java
@@ -34,7 +34,7 @@ public class PacketStatistic extends Packet
         throws IOException
     {
         try {
-            statID = new NamespaceID(readStringUTF8(dis, Integer.MAX_VALUE));
+            statID = NamespaceID.getTemp(readStringUTF8(dis, Integer.MAX_VALUE));
         } catch (HardIllegalArgumentException e) {
             statID = null;
         }
diff --git a/game/core/src/main/java/net/minecraft/core/util/collection/NamespaceID.java b/game/core/src/main/java/net/minecraft/core/util/collection/NamespaceID.java
index 2f85fc7db..06235b798 100644
--- a/game/core/src/main/java/net/minecraft/core/util/collection/NamespaceID.java
+++ b/game/core/src/main/java/net/minecraft/core/util/collection/NamespaceID.java
@@ -2,48 +2,148 @@ package net.minecraft.core.util.collection;
 
 import net.minecraft.core.util.HardIllegalArgumentException;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
-import java.util.Comparator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
 
-public final class NamespaceID implements Comparator<NamespaceID>, Comparable<NamespaceID> {
-    public static final String DEFAULT_NAMESPACE = "minecraft";
-    public final String namespace;
-    public final String value;
-    public NamespaceID(String namespace, String value) throws IllegalArgumentException {
-        if (namespace.contains(":")) throw new IllegalArgumentException(namespace + " cannot contain the ':' character!");
-        if (value.contains(":")) throw new IllegalArgumentException(value + " cannot contain the ':' character!");
+public final class NamespaceID implements Comparable<NamespaceID>, CharSequence {
+    public static final @NotNull String DEFAULT_NAMESPACE = "minecraft";
+    private static final List<NamespaceID> pool = new ArrayList<>();
+    private static int poolPointer = 0;
+
+    public static @NotNull NamespaceID getPermanent(final @NotNull String formattedKey) throws HardIllegalArgumentException {
+        return new NamespaceID(formattedKey);
+    }
+
+    public static @NotNull NamespaceID getPermanent(final @NotNull String namespace, final @NotNull String value) {
+        return new NamespaceID(namespace, value);
+    }
+
+    public static @NotNull NamespaceID getTemp(final @NotNull String formattedKey) throws HardIllegalArgumentException {
+        if(poolPointer >= pool.size())
+        {
+            pool.add(getPermanent(formattedKey));
+        }
+        return pool.get(poolPointer++).set(formattedKey);
+    }
+
+    public static @NotNull NamespaceID getTemp(final @NotNull String namespace, final @NotNull String value) {
+        if(poolPointer >= pool.size())
+        {
+            pool.add(getPermanent(namespace, value));
+        }
+        return pool.get(poolPointer++).set(namespace, value);
+    }
+
+    public static void deinitializePool()
+    {
+        pool.clear();
+        poolPointer = 0;
+    }
+
+    public static void initializePool()
+    {
+        poolPointer = 0;
+    }
+
+    private @NotNull String fullString;
+    private @Nullable String namespace;
+    private @Nullable String value;
+
+    NamespaceID(final @NotNull String formattedKey) throws HardIllegalArgumentException {
+        set(formattedKey);
+    }
+
+    NamespaceID(final @NotNull String namespace, final @NotNull String value) {
+        set(namespace, value);
+    }
+
+    @NotNull
+    private NamespaceID set(final @NotNull String formattedKey) throws HardIllegalArgumentException {
+        Objects.requireNonNull(formattedKey);
+        final int firstIndex;
+        if ((firstIndex = formattedKey.indexOf(":")) != -1 && firstIndex == formattedKey.lastIndexOf(":") && firstIndex != 0 && firstIndex != formattedKey.length() - 1) {
+            this.fullString = formattedKey;
+            this.namespace = null;
+            this.value = null;
+        } else {
+            throw new HardIllegalArgumentException("Namespace id key must have exactly 1 ':' character in format <namespace>:<value>! " + formattedKey);
+        }
+        return this;
+    }
+
+    @NotNull
+    private NamespaceID set(final @NotNull String namespace, final @NotNull String value) {
+        Objects.requireNonNull(namespace);
+        Objects.requireNonNull(value);
+        if (namespace.contains(":")) throw new IllegalArgumentException("Namespace must not contain a ':', but was provided '" + namespace + "'!");
+        if (value.contains(":")) throw new IllegalArgumentException("Value must not contain a ':', but was provided '" + value + "'!");
+        this.fullString = namespace + ":" + value;
         this.namespace = namespace;
         this.value = value;
+        return this;
     }
-    public NamespaceID(String formattedString) throws HardIllegalArgumentException {
-        if (!formattedString.contains(":")) throw new HardIllegalArgumentException("Formatted String '" + formattedString + "' missing ':' character all namespace ids must follow the format <namespace>:<value>!");
-        String[] split = formattedString.split(":", 2);
-        if (split.length != 2 || split[0].isEmpty()) throw new HardIllegalArgumentException("Formatted String '" + formattedString + "' is missing namespace! all namespace ids must follow the format <namespace>:<value>!");
-        if (split[1].isEmpty()) throw new HardIllegalArgumentException("Formatted String '" + formattedString + "' is missing value! all namespace ids must follow the format <namespace>:<value>!");
-        if (split[1].contains(":")) throw new HardIllegalArgumentException("Formatted String '" + formattedString + "' has more then 1 ':' character! all namespace ids must follow the format <namespace>:<value>!");
-        namespace = split[0];
-        value = split[1];
+
+    @NotNull
+    public NamespaceID makePermanent(){
+        if (pool.contains(this)){
+            pool.remove(this);
+            poolPointer--;
+        }
+        return this;
+    }
+
+    public @NotNull String namespace() {
+        if (this.namespace == null) {
+            this.namespace = this.fullString.substring(0, this.fullString.indexOf(":"));
+        }
+        return this.namespace;
     }
+
+    public @NotNull String value() {
+        if (this.value == null) {
+            this.value = this.fullString.substring(this.fullString.indexOf(":") + 1);
+        }
+        return this.value;
+    }
+
     @Override
-    public int hashCode(){
-        return toString().hashCode();
+    public int length() {
+        return this.fullString.length();
     }
+
     @Override
-    public boolean equals(Object other){
-        if (other == null) return false;
-        return super.equals(other) | (other.toString().equals(this.toString()));
+    public char charAt(final int index) {
+        return this.fullString.charAt(index);
     }
+
     @Override
-    public String toString(){
-        return namespace + ":" + value;
+    public @NotNull CharSequence subSequence(final int start, final int end) {
+        return this.fullString.subSequence(start, end);
     }
+
     @Override
-    public int compare(NamespaceID o1, NamespaceID o2) {
-        return o1.toString().compareTo(o2.toString());
+    public @NotNull String toString() {
+        return this.fullString;
     }
 
     @Override
     public int compareTo(@NotNull NamespaceID o) {
-        return toString().compareTo(o.toString());
+        return this.fullString.compareTo(o.fullString);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final NamespaceID that = (NamespaceID) o;
+        return this.fullString.equals(that.fullString);
+    }
+
+    @Override
+    public int hashCode() {
+        return this.fullString.hashCode();
     }
 }
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 b80086510..47ee135ca 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
@@ -176,13 +176,12 @@ public final class SpawnerMobs
                         if (!mobToSpawn.canSpawnHere()) {
                             continue;
                         }
-
-                        ++countSpawned;
-                        mobToSpawn.spawnInit();
-                        world.entityJoinedWorld(mobToSpawn);
                         if (countSpawned >= mobToSpawn.getMaxSpawnedInChunk()) {
                             continue chunkLoop;
                         }
+                        ++countSpawned;
+                        mobToSpawn.spawnInit();
+                        world.entityJoinedWorld(mobToSpawn);
                     }
                 }
                 totalSpawned += countSpawned;
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 98f67822b..d6aec1900 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
@@ -2367,6 +2367,7 @@ public class World implements WorldSource {
         {
             caveSoundCounter--;
         }
+        final int randomTicks = getGameRuleValue(GameRules.RANDOM_TICK_SPEED);
         for (ChunkCoordinate coordinate : positionsToUpdate) {
             int chunkBlockX = coordinate.x * Chunk.CHUNK_SIZE_X;
             int chunkBlockZ = coordinate.z * Chunk.CHUNK_SIZE_Z;
@@ -2400,9 +2401,9 @@ public class World implements WorldSource {
                 int weatherBlockZ = (randVal / (Chunk.CHUNK_SIZE_Z * Chunk.CHUNK_SIZE_X)) & (Chunk.CHUNK_SIZE_Z - 1);
                 getCurrentWeather().doEnvironmentUpdate(this, rand, chunkBlockX + weatherBlockX, chunkBlockZ + weatherBlockZ);
             }
-            
+
             for (int s = 0; s < Chunk.CHUNK_SECTIONS; s++) {
-                for (int i = 0; i < getGameRuleValue(GameRules.RANDOM_TICK_SPEED); i++) {
+                for (int i = 0; i < randomTicks; i++) {
                     updateLCG = updateLCG * 3 + 0x3c6ef35f;
                     int iRandVal = updateLCG >> 2;
                     int iBlockX = iRandVal & (Chunk.CHUNK_SIZE_X - 1);
diff --git a/game/core/src/main/java/net/minecraft/core/world/biome/Biome.java b/game/core/src/main/java/net/minecraft/core/world/biome/Biome.java
index ebda87dcb..2b9e3f455 100644
--- a/game/core/src/main/java/net/minecraft/core/world/biome/Biome.java
+++ b/game/core/src/main/java/net/minecraft/core/world/biome/Biome.java
@@ -1,11 +1,6 @@
 package net.minecraft.core.world.biome;
 
-import net.minecraft.core.entity.animal.MobChicken;
-import net.minecraft.core.entity.animal.MobCow;
-import net.minecraft.core.entity.animal.MobFireflyCluster;
-import net.minecraft.core.entity.animal.MobPig;
-import net.minecraft.core.entity.animal.MobSheep;
-import net.minecraft.core.entity.animal.MobSquid;
+import net.minecraft.core.entity.animal.*;
 import net.minecraft.core.entity.monster.MobCreeper;
 import net.minecraft.core.entity.monster.MobSkeleton;
 import net.minecraft.core.entity.monster.MobSlime;
@@ -58,6 +53,7 @@ public class Biome
         spawnableCreatureList.add(new SpawnListEntry(MobPig.class, 102));
         spawnableCreatureList.add(new SpawnListEntry(MobChicken.class, 102));
         spawnableCreatureList.add(new SpawnListEntry(MobCow.class, 102));
+        spawnableCreatureList.add(new SpawnListEntry(MobDeer.class, 10));
         spawnableWaterCreatureList.add(new SpawnListEntry(MobSquid.class, 10));
         spawnableAmbientCreatureList.add(new SpawnListEntry(MobFireflyCluster.class, 10));
     }
diff --git a/game/core/src/main/java/net/minecraft/core/world/config/spawning/SpawnerConfig.java b/game/core/src/main/java/net/minecraft/core/world/config/spawning/SpawnerConfig.java
index fb153d8ad..be63f9911 100644
--- a/game/core/src/main/java/net/minecraft/core/world/config/spawning/SpawnerConfig.java
+++ b/game/core/src/main/java/net/minecraft/core/world/config/spawning/SpawnerConfig.java
@@ -63,7 +63,7 @@ public class SpawnerConfig {
         CompoundTag overrides = tag.getCompound("overrides");
         for (Map.Entry<String, Tag<?>> tagEntry : overrides.getValue().entrySet()) {
             try {
-                mobSpawnOverrides.put(new NamespaceID(tagEntry.getKey()), ((ByteTag)tagEntry.getValue()).getValue() != 0);
+                mobSpawnOverrides.put(NamespaceID.getPermanent(tagEntry.getKey()), ((ByteTag)tagEntry.getValue()).getValue() != 0);
             } catch (HardIllegalArgumentException e) {
                 LOGGER.error("Failed to convert string '{}' to namespace id!", tagEntry.getKey(), e);
             }
diff --git a/game/core/src/main/resources/lang/en_US/guidebook.lang b/game/core/src/main/resources/lang/en_US/guidebook.lang
index dcbbbf22b..8d3790296 100644
--- a/game/core/src/main/resources/lang/en_US/guidebook.lang
+++ b/game/core/src/main/resources/lang/en_US/guidebook.lang
@@ -34,6 +34,8 @@ guidebook.section.mob.cow.name=Cow
 guidebook.section.mob.cow.desc=Cows are useful as a source of leather, which can be used to create armor.\nThey can be found in most biomes.
 guidebook.section.mob.chicken.name=Chicken
 guidebook.section.mob.chicken.desc=Chickens are a source of feathers, which are useful for making arrows.\nThey can be found in most biomes.
+guidebook.section.mob.deer.name=Deer
+guidebook.section.mob.deer.desc=Deer are skittish animals found in cold climates. They provide an excellent source of food but are hard to catch.
 guidebook.section.mob.squid.name=Squid
 guidebook.section.mob.squid.desc=Squids are a source of ink sacs, which can be used to dye wool.\nThey can be found in lakes and oceans.
 guidebook.section.mob.wolf.name=Wolf
diff --git a/game/core/src/main/resources/lang/en_US/item.lang b/game/core/src/main/resources/lang/en_US/item.lang
index e57bb67ee..4ddd2d9bf 100644
--- a/game/core/src/main/resources/lang/en_US/item.lang
+++ b/game/core/src/main/resources/lang/en_US/item.lang
@@ -160,6 +160,10 @@ item.food.porkchop.raw.name=Raw Porkchop
 item.food.porkchop.raw.desc=Uncooked meat from a pig.
 item.food.porkchop.cooked.name=Cooked Porkchop
 item.food.porkchop.cooked.desc=Braised meat from a pig. Smoking!
+item.food.venison.raw.name=Raw Venison
+item.food.venison.raw.desc=Uncooked meat from a pig.
+item.food.venison.cooked.name=Cooked Venison
+item.food.venison.cooked.desc=Seared steak from a deer. Delicious!
 item.painting.name=Painting
 item.painting.desc=A lovely picture to hang on your wall.
 item.food.apple.gold.name=Golden Apple
diff --git a/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java b/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java
index ee6baeaff..1f471c10a 100644
--- a/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java
+++ b/util/datagen/src/main/java/net/minecraft/datagen/SmeltingGenerator.java
@@ -29,6 +29,7 @@ public class SmeltingGenerator {
 
         furnaceRecipe("fish_raw_to_fish_cooked", Items.FOOD_FISH_RAW.getDefaultStack(), Items.FOOD_FISH_COOKED.getDefaultStack());
         furnaceRecipe("porkchop_raw_to_porkchop_cooked", Items.FOOD_PORKCHOP_RAW.getDefaultStack(), Items.FOOD_PORKCHOP_COOKED.getDefaultStack());
+        furnaceRecipe("venison_raw_to_venison_cooked", Items.FOOD_VENISON_RAW.getDefaultStack(), Items.FOOD_VENISON_COOKED.getDefaultStack());
 
         furnaceRecipe("cobble_stone_to_stone", Blocks.COBBLE_STONE.getDefaultStack(), Blocks.STONE.getDefaultStack());
         furnaceRecipe("cobble_basalt_to_basalt", Blocks.COBBLE_BASALT.getDefaultStack(), Blocks.BASALT.getDefaultStack());
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 0f597f9cd..0c6416598 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
@@ -22,7 +22,7 @@ public class TestChunkProvider implements ChunkProvider {
 
                     @Override
                     public NamespaceID getID() {
-                        return new NamespaceID("viewer", "test");
+                        return NamespaceID.getPermanent("viewer", "test");
                     }
 
                     @Override
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 e3f61e1d5..38dadc049 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
@@ -20,7 +20,7 @@ public class BTABiome implements Biome {
     @Override
     public NamespaceID getID() {
         try {
-            return new NamespaceID(Registries.BIOMES.getKey(biome));
+            return NamespaceID.getPermanent(Registries.BIOMES.getKey(biome));
         } catch (HardIllegalArgumentException e) {
             throw new IllegalArgumentException(e);
         }
diff --git a/util/seed-viewer/src/test/java/collections/NamespaceTest.java b/util/seed-viewer/src/test/java/collections/NamespaceTest.java
index 1be1c8123..29f271278 100644
--- a/util/seed-viewer/src/test/java/collections/NamespaceTest.java
+++ b/util/seed-viewer/src/test/java/collections/NamespaceTest.java
@@ -11,77 +11,77 @@ public class NamespaceTest {
     @DisplayName("Test Namespace 2String Constructor")
     void namespaceCreation1() {
         // Valid creation
-        NamespaceID namespaceID_1 = new NamespaceID("minecraft", "value1");
-        Assertions.assertEquals(namespaceID_1.namespace, "minecraft");
-        Assertions.assertEquals(namespaceID_1.value, "value1");
+        NamespaceID namespaceID_1 = NamespaceID.getPermanent("minecraft", "value1");
+        Assertions.assertEquals(namespaceID_1.namespace(), "minecraft");
+        Assertions.assertEquals(namespaceID_1.value(), "value1");
         Assertions.assertEquals(namespaceID_1.toString(), "minecraft:value1");
 
-        NamespaceID namespaceID_2 = new NamespaceID("minecraft", "value1");
-        Assertions.assertEquals(namespaceID_2.namespace, "minecraft");
-        Assertions.assertEquals(namespaceID_2.value, "value1");
+        NamespaceID namespaceID_2 = NamespaceID.getPermanent("minecraft", "value1");
+        Assertions.assertEquals(namespaceID_2.namespace(), "minecraft");
+        Assertions.assertEquals(namespaceID_2.value(), "value1");
         Assertions.assertEquals(namespaceID_2.toString(), "minecraft:value1");
 
-        Assertions.assertEquals(namespaceID_1.namespace, namespaceID_2.namespace);
-        Assertions.assertEquals(namespaceID_1.value, namespaceID_2.value);
+        Assertions.assertEquals(namespaceID_1.namespace(), namespaceID_2.namespace());
+        Assertions.assertEquals(namespaceID_1.value(), namespaceID_2.value());
         Assertions.assertEquals(namespaceID_1, namespaceID_2);
         Assertions.assertEquals(namespaceID_1.toString(), namespaceID_2.toString());
 
-        NamespaceID namespaceID_3 = new NamespaceID("bta", "value2");
-        Assertions.assertEquals(namespaceID_3.namespace, "bta");
-        Assertions.assertEquals(namespaceID_3.value, "value2");
+        NamespaceID namespaceID_3 = NamespaceID.getPermanent("bta", "value2");
+        Assertions.assertEquals(namespaceID_3.namespace(), "bta");
+        Assertions.assertEquals(namespaceID_3.value(), "value2");
         Assertions.assertEquals(namespaceID_3.toString(), "bta:value2");
 
-        Assertions.assertNotEquals(namespaceID_3.namespace, "minecraft");
-        Assertions.assertNotEquals(namespaceID_3.value, "value1");
+        Assertions.assertNotEquals(namespaceID_3.namespace(), "minecraft");
+        Assertions.assertNotEquals(namespaceID_3.value(), "value1");
         Assertions.assertNotEquals(namespaceID_3.toString(), "minecraft:value1");
 
         Assertions.assertNotEquals(namespaceID_3, namespaceID_1);
-        Assertions.assertNotEquals(namespaceID_3.value, namespaceID_1.value);
-        Assertions.assertNotEquals(namespaceID_3.namespace, namespaceID_1.namespace);
+        Assertions.assertNotEquals(namespaceID_3.value(), namespaceID_1.value());
+        Assertions.assertNotEquals(namespaceID_3.namespace(), namespaceID_1.namespace());
         Assertions.assertNotEquals(namespaceID_3.toString(), namespaceID_1.toString());
 
         Assertions.assertNotEquals(namespaceID_3, namespaceID_2);
-        Assertions.assertNotEquals(namespaceID_3.value, namespaceID_2.value);
-        Assertions.assertNotEquals(namespaceID_3.namespace, namespaceID_2.namespace);
+        Assertions.assertNotEquals(namespaceID_3.value(), namespaceID_2.value());
+        Assertions.assertNotEquals(namespaceID_3.namespace(), namespaceID_2.namespace());
         Assertions.assertNotEquals(namespaceID_3.toString(), namespaceID_2.toString());
 
         // Invalid creation
-        Assertions.assertThrowsExactly(NullPointerException.class, () -> new NamespaceID(null, null));
-        Assertions.assertThrowsExactly(NullPointerException.class, () -> new NamespaceID("minecraft", null));
-        Assertions.assertThrowsExactly(NullPointerException.class, () -> new NamespaceID(null, "value1"));
+        Assertions.assertThrowsExactly(NullPointerException.class, () -> NamespaceID.getPermanent(null, null));
+        Assertions.assertThrowsExactly(NullPointerException.class, () -> NamespaceID.getPermanent("minecraft", null));
+        Assertions.assertThrowsExactly(NullPointerException.class, () -> NamespaceID.getPermanent(null, "value1"));
 
-        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> new NamespaceID("minecraft:", "value1"));
-        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> new NamespaceID("minecraft", "value1:"));
-        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> new NamespaceID("minecraft:", "value1:"));
+        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> NamespaceID.getPermanent("minecraft:", "value1"));
+        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> NamespaceID.getPermanent("minecraft", "value1:"));
+        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> NamespaceID.getPermanent("minecraft:", "value1:"));
     }
 
     @Test
     @DisplayName("Test Namespace 1String Constructor")
     void namespaceCreation2() throws HardIllegalArgumentException {
         String formatted_1 = "minecraft:value1";
-        NamespaceID namespaceID_1 = new NamespaceID(formatted_1);
-        Assertions.assertEquals(namespaceID_1.namespace, "minecraft");
-        Assertions.assertEquals(namespaceID_1.value, "value1");
+        NamespaceID namespaceID_1 = NamespaceID.getPermanent(formatted_1);
+        Assertions.assertEquals(namespaceID_1.namespace(), "minecraft");
+        Assertions.assertEquals(namespaceID_1.value(), "value1");
         Assertions.assertEquals(namespaceID_1.toString(), formatted_1);
-        Assertions.assertEquals(namespaceID_1.namespace + ":" + namespaceID_1.value, formatted_1);
+        Assertions.assertEquals(namespaceID_1.namespace() + ":" + namespaceID_1.value(), formatted_1);
 
-        NamespaceID namespaceID_2 = new NamespaceID(formatted_1);
-        Assertions.assertEquals(namespaceID_2.namespace, "minecraft");
-        Assertions.assertEquals(namespaceID_2.value, "value1");
+        NamespaceID namespaceID_2 = NamespaceID.getPermanent(formatted_1);
+        Assertions.assertEquals(namespaceID_2.namespace(), "minecraft");
+        Assertions.assertEquals(namespaceID_2.value(), "value1");
         Assertions.assertEquals(namespaceID_2.toString(), formatted_1);
-        Assertions.assertEquals(namespaceID_2.namespace + ":" + namespaceID_2.value, formatted_1);
+        Assertions.assertEquals(namespaceID_2.namespace() + ":" + namespaceID_2.value(), formatted_1);
 
-        Assertions.assertEquals(namespaceID_1.namespace, namespaceID_2.namespace);
-        Assertions.assertEquals(namespaceID_1.value, namespaceID_2.value);
+        Assertions.assertEquals(namespaceID_1.namespace(), namespaceID_2.namespace());
+        Assertions.assertEquals(namespaceID_1.value(), namespaceID_2.value());
         Assertions.assertEquals(namespaceID_1, namespaceID_2);
         Assertions.assertEquals(namespaceID_1.toString(), namespaceID_2.toString());
 
-        Assertions.assertThrowsExactly(NullPointerException.class, () -> new NamespaceID(null));
-        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> new NamespaceID("minecraft::value1"));
-        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> new NamespaceID("minecraft:value1:"));
-        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> new NamespaceID(":value1"));
-        Assertions.assertDoesNotThrow(() -> new NamespaceID("minecraft:value"));
-        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> new NamespaceID("minecraft value"));
-        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> new NamespaceID("minecraft|value"));
+        Assertions.assertThrowsExactly(NullPointerException.class, () -> NamespaceID.getPermanent(null));
+        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> NamespaceID.getPermanent("minecraft::value1"));
+        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> NamespaceID.getPermanent("minecraft:value1:"));
+        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> NamespaceID.getPermanent(":value1"));
+        Assertions.assertDoesNotThrow(() -> NamespaceID.getPermanent("minecraft:value"));
+        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> NamespaceID.getPermanent("minecraft value"));
+        Assertions.assertThrowsExactly(HardIllegalArgumentException.class, () -> NamespaceID.getPermanent("minecraft|value"));
     }
 }
