diff --git a/build.gradle.kts b/build.gradle.kts
index 561df0df9..93a4bf8c8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -145,10 +145,6 @@ dependencies {
 
     implementation("com.github.JnCrMx:discord-game-sdk4j:v0.5.5")?.let { clientInclude(it) }
 
-    runtimeOnly("net.java.jinput:jinput-platform:2.0.5")
-    implementation("net.java.jinput:jinput:2.0.5")
-    implementation("net.java.jutils:jutils:1.0.0")
-
     implementation("objects:client:43db9b498cb67058d2e12d394e6507722e71bb45") // Minecraft client jar
 
     val log4jVersion = "2.20.0"
diff --git a/src/common/resources/lang/en_US/options.lang b/src/common/resources/lang/en_US/options.lang
index f41aabcf1..5f4904060 100644
--- a/src/common/resources/lang/en_US/options.lang
+++ b/src/common/resources/lang/en_US/options.lang
@@ -158,7 +158,6 @@ options.lightmapStyle.disabled=Disabled
 options.customLightmap=Custom Lightmap
 options.old=Old Menu
 options.lockCamera=Lock Camera
-options.disableJInput=Disable JInput
 options.disableCrashHandler=Disable Crash Handler
 options.disableResize=Disable Resize
 options.startInFullscreen=Start In Fullscreen
diff --git a/src/common/resources/lang/en_US/strings.lang b/src/common/resources/lang/en_US/strings.lang
index 269e2d9bd..a72990d37 100644
--- a/src/common/resources/lang/en_US/strings.lang
+++ b/src/common/resources/lang/en_US/strings.lang
@@ -1,5 +1,5 @@
 dimension.overworld.name=the Overworld
-dimension.hell.name=the Nether
+dimension.nether.name=the Nether
 dimension.paradise.name=Paradise
 
 season.overworld.spring.name=Spring
diff --git a/src/main/java/net/betterthanadventure/utils/BiomeFinder.java b/src/main/java/net/betterthanadventure/utils/BiomeFinder.java
index 5f3b15b42..2b74f56c4 100644
--- a/src/main/java/net/betterthanadventure/utils/BiomeFinder.java
+++ b/src/main/java/net/betterthanadventure/utils/BiomeFinder.java
@@ -1,7 +1,7 @@
 package net.betterthanadventure.utils;
 
 import net.minecraft.client.Minecraft;
-import net.minecraft.client.world.chunk.provider.ChunkProviderStatic;
+import net.minecraft.client.world.chunk.provider.ChunkProviderDynamic;
 import net.minecraft.core.Global;
 import net.minecraft.core.MinecraftAccessor;
 import net.minecraft.core.data.registry.Registries;
@@ -512,12 +512,12 @@ public class BiomeFinder implements MinecraftAccessor {
 				int z = offsetZ + j * renderScale;
 
 				Biome biome = null;
-				if (world != null && world.getChunkProvider() instanceof ChunkProviderStatic){
+				if (world != null && world.getChunkProvider() instanceof ChunkProviderDynamic){
                     try {
 						if (lastChunkX != Math.floorDiv(x, Chunk.CHUNK_SIZE_X) || lastChunkZ != Math.floorDiv(z, Chunk.CHUNK_SIZE_Z)){
 							lastChunkX = Math.floorDiv(x, Chunk.CHUNK_SIZE_X);
 							lastChunkZ = Math.floorDiv(z, Chunk.CHUNK_SIZE_Z);
-							chunk = ((ChunkProviderStatic) world.getChunkProvider()).getChunkLoader().loadChunk(world, lastChunkX, lastChunkZ);
+							chunk = ((ChunkProviderDynamic) world.getChunkProvider()).getChunkLoader().loadChunk(world, lastChunkX, lastChunkZ);
 						}
 						if (chunk != null){
 							biome = chunk.getBlockBiome(x & (Chunk.CHUNK_SIZE_X - 1), posY, z & (Chunk.CHUNK_SIZE_Z - 1));
@@ -642,7 +642,7 @@ public class BiomeFinder implements MinecraftAccessor {
 
 	@Override
 	public IChunkProvider createChunkProvider(World world, IChunkLoader chunkLoader) {
-		return new ChunkProviderStatic(world, chunkLoader, world.getWorldType().createChunkGenerator(world));
+		return new ChunkProviderDynamic(world, chunkLoader, world.getWorldType().createChunkGenerator(world));
 	}
 
 	@Override
diff --git a/src/main/java/net/minecraft/client/Minecraft.java b/src/main/java/net/minecraft/client/Minecraft.java
index 20c495e07..30270e34f 100644
--- a/src/main/java/net/minecraft/client/Minecraft.java
+++ b/src/main/java/net/minecraft/client/Minecraft.java
@@ -19,6 +19,7 @@ import java.util.Random;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 
+import net.minecraft.client.world.chunk.provider.ChunkProviderDynamic;
 import org.lwjgl.LWJGLException;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
@@ -67,7 +68,6 @@ import net.minecraft.client.option.GameSettings;
 import net.minecraft.client.option.KeyBinding;
 import net.minecraft.client.option.enums.LightmapStyle;
 import net.minecraft.client.player.controller.PlayerController;
-import net.minecraft.client.render.ChunkRenderer;
 import net.minecraft.client.render.EffectRenderer;
 import net.minecraft.client.render.EntityRenderDispatcher;
 import net.minecraft.client.render.FallbackRenderer;
@@ -89,6 +89,10 @@ import net.minecraft.client.render.colorizer.Colorizers;
 import net.minecraft.client.render.item.model.ItemModelDispatcher;
 import net.minecraft.client.render.shader.Shaders;
 import net.minecraft.client.render.shader.ShadersRenderer;
+import net.minecraft.client.render.terrain.ChunkRenderer;
+import net.minecraft.client.render.terrain.TerrainRenderer;
+import net.minecraft.client.render.terrain.TerrainRendererLegacy;
+import net.minecraft.client.render.terrain.TerrainRendererMultiDraw;
 import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.client.render.texturepack.TexturePackList;
@@ -99,7 +103,6 @@ import net.minecraft.client.util.debug.Debug;
 import net.minecraft.client.util.debug.DebugRender;
 import net.minecraft.client.util.helper.ScreenshotHelper;
 import net.minecraft.client.util.helper.Textures;
-import net.minecraft.client.world.chunk.provider.ChunkProviderStatic;
 import net.minecraft.core.Global;
 import net.minecraft.core.MinecraftAccessor;
 import net.minecraft.core.MinecraftError;
@@ -238,6 +241,7 @@ public class Minecraft
      */
     private boolean gameStarted = false;
     private int mojrand = new Random(System.currentTimeMillis()).nextInt(200);
+    public TerrainRenderer terrainRenderer;
     
     public Minecraft(GameWindow gameWindow)
     {
@@ -417,6 +421,19 @@ public class Minecraft
 		
         OpenGLHelper.testCapabilities(this);
         
+        if(gameSettings.enableMultiDrawRendering.value) {
+            if(OpenGLHelper.gl20) {
+            	System.out.println("Using MultiDraw terrain renderer");
+            	terrainRenderer = new TerrainRendererMultiDraw(this);
+            }else {
+            	System.out.println("MultiDraw not supported, using legacy terrain renderer");
+            	terrainRenderer = new TerrainRendererLegacy(this);
+            }	
+        }else {
+        	System.out.println("MultiDraw disabled, using legacy terrain renderer");
+        	terrainRenderer = new TerrainRendererLegacy(this);
+        }
+        
         if(Shaders.enableShaders) {
             setRenderer(new ShadersRenderer(this));
         }else {
@@ -452,6 +469,7 @@ public class Minecraft
             System.err.println("Input init error!");
             e.printStackTrace();
         }
+        mouseInput.setRawInput(gameSettings.rawMouseInput.value);
         System.out.println("Input done!");
         
         System.out.println("Setting up OpenGL values...");
@@ -598,7 +616,7 @@ public class Minecraft
     private void loadScreen() throws LWJGLException
     {
         System.out.println("Drawing Loading Screen...");
-        glClear(16640);
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
         glMatrixMode(GL11.GL_PROJECTION);
         glLoadIdentity();
         glOrtho(0.0D, resolution.scaledWidthExact, resolution.scaledHeightExact, 0.0D, 1000D, 3000D);
@@ -615,7 +633,7 @@ public class Minecraft
         if(mojrand == 0)
             renderEngine.loadTexture("/assets/minecraft/textures/gui/title/majonk.png").bind();
         else
-            renderEngine.loadTexture("/title/mojang.png").bind();
+            renderEngine.loadTexture("/assets/minecraft/textures/gui/title/mojang.png").bind();
         
         tessellator.startDrawingQuads();
         tessellator.setColorOpaque_I(0xffffff);
@@ -635,7 +653,7 @@ public class Minecraft
         glDisable(GL11.GL_LIGHTING);
         glDisable(GL11.GL_FOG);
         glEnable(GL11.GL_ALPHA_TEST);
-        glAlphaFunc(516, 0.1F);
+        glAlphaFunc(GL_GREATER,0.1F);
         gameWindow.update();
     }
 
@@ -670,7 +688,7 @@ public class Minecraft
     @Override
     public IChunkProvider createChunkProvider(World world, IChunkLoader chunkLoader)
     {
-        return new ChunkProviderStatic(world, chunkLoader, world.getWorldType().createChunkGenerator(world));
+        return new ChunkProviderDynamic(world, chunkLoader, world.getWorldType().createChunkGenerator(world));
     }
 
     public static File getAppDir(String s)
@@ -1313,12 +1331,12 @@ public class Minecraft
         if(thePlayer != null)
         {
             IChunkProvider chunkProvider = theWorld.getChunkProvider();
-            if(chunkProvider instanceof ChunkProviderStatic)
+            if(chunkProvider instanceof ChunkProviderDynamic)
             {
-                ChunkProviderStatic chunkproviderloadorgenerate = (ChunkProviderStatic)chunkProvider;
+                ChunkProviderDynamic chunkProviderDynamic = (ChunkProviderDynamic)chunkProvider;
                 int playerChunkX = MathHelper.floor_float((int)thePlayer.x) >> 4;
                 int playerChunkZ = MathHelper.floor_float((int)thePlayer.z) >> 4;
-                chunkproviderloadorgenerate.setCurrentChunkOver(playerChunkX, playerChunkZ);
+                chunkProviderDynamic.setCurrentChunkOver(playerChunkX, playerChunkZ);
             }
         }
         if(!isGamePaused && theWorld != null)
@@ -2212,12 +2230,12 @@ public class Minecraft
             }
             playerController.adjustPlayer(thePlayer);
             IChunkProvider ichunkprovider = world.getChunkProvider();
-            if(ichunkprovider instanceof ChunkProviderStatic)
+            if(ichunkprovider instanceof ChunkProviderDynamic)
             {
-                ChunkProviderStatic chunkproviderloadorgenerate = (ChunkProviderStatic)ichunkprovider;
+                ChunkProviderDynamic chunkProviderDynamic = (ChunkProviderDynamic)ichunkprovider;
                 int i = MathHelper.floor_float((int)thePlayer.x) >> 4;
                 int j = MathHelper.floor_float((int)thePlayer.z) >> 4;
-                chunkproviderloadorgenerate.setCurrentChunkOver(i, j);
+                chunkProviderDynamic.setCurrentChunkOver(i, j);
             }
             world.spawnPlayerWithLoadedChunks(thePlayer);
             if(world.isNewWorld)
@@ -2303,9 +2321,9 @@ public class Minecraft
             chunkcoordinates.x = (int)thePlayer.x;
             chunkcoordinates.z = (int)thePlayer.z;
         }
-        if(ichunkprovider instanceof ChunkProviderStatic)
+        if(ichunkprovider instanceof ChunkProviderDynamic)
         {
-            ChunkProviderStatic chunkProvider = (ChunkProviderStatic)ichunkprovider;
+            ChunkProviderDynamic chunkProvider = (ChunkProviderDynamic)ichunkprovider;
             chunkProvider.setCurrentChunkOver(Math.floorDiv(chunkcoordinates.x, Chunk.CHUNK_SIZE_X), Math.floorDiv(chunkcoordinates.z, Chunk.CHUNK_SIZE_Z));
         }
         for(int k = -c; k <= c; k += Chunk.CHUNK_SIZE_X)
@@ -2403,10 +2421,10 @@ public class Minecraft
             canRespawnAtBed = false;
         }
         IChunkProvider ichunkprovider = theWorld.getChunkProvider();
-        if(ichunkprovider instanceof ChunkProviderStatic)
+        if(ichunkprovider instanceof ChunkProviderDynamic)
         {
-            ChunkProviderStatic chunkproviderloadorgenerate = (ChunkProviderStatic)ichunkprovider;
-            chunkproviderloadorgenerate.setCurrentChunkOver(bedSpawnCoordinates.x >> 4, bedSpawnCoordinates.z >> 4);
+            ChunkProviderDynamic chunkProviderDynamic = (ChunkProviderDynamic)ichunkprovider;
+            chunkProviderDynamic.setCurrentChunkOver(bedSpawnCoordinates.x >> 4, bedSpawnCoordinates.z >> 4);
         }
         theWorld.getRespawnLocation();
         theWorld.updateEntityList();
diff --git a/src/main/java/net/minecraft/client/gui/GuiControllerSetup.java b/src/main/java/net/minecraft/client/gui/GuiControllerSetup.java
index 76b30f83e..0f0b320b0 100644
--- a/src/main/java/net/minecraft/client/gui/GuiControllerSetup.java
+++ b/src/main/java/net/minecraft/client/gui/GuiControllerSetup.java
@@ -54,22 +54,21 @@ public class GuiControllerSetup extends GuiScreen {
 
         ControllerInput controller = mc.controllerInput;
 
-
         if (controller != null) {
-            boolean swapJoystickAndDPad = controller.getType().getCategory() == ControllerType.Category.XBOX || controller.getType().getCategory() == ControllerType.Category.NINTENDO;
+            boolean swapJoystickAndDPad = controller.getType().getLayout() == ControllerType.Layout.XBOX || controller.getType().getLayout() == ControllerType.Layout.NINTENDO;
             int y = height / 2 - 64;
 
+            int x1 = width / 2;
+
             glDisable(GL_DEPTH_TEST);
             glDisable(GL_CULL_FACE);
             glDisable(GL_TEXTURE_2D);
             if (swapJoystickAndDPad) {
-                drawJoystick(controller.joyLeft, width / 2 - 100 + 8, y + 32 + 4 + 8);
+                drawJoystick(controller.joyLeft, x1 - 100 + 8, y + 32 + 4 + 8);
             } else {
-                drawJoystick(controller.joyLeft, width / 2 - 48, y + 96);
+                drawJoystick(controller.joyLeft, x1 - 48, y + 96);
             }
-            drawJoystick(controller.joyRight, width / 2 - 48 + 64, y + 96);
-
-            int x1 = width / 2;
+            drawJoystick(controller.joyRight, x1 - 48 + 64, y + 96);
 
             GL11.glEnable(GL_TEXTURE_2D);
             fontRenderer.drawCenteredString("(" + controller.getType().getPrintableName() + ")", width / 2, 40, 0xFFFFFF);
@@ -91,7 +90,7 @@ public class GuiControllerSetup extends GuiScreen {
             if (swapJoystickAndDPad) {
                 glyphRenderer.drawDPad(controller, x1 - 48 - 8, y + 96 - 8, controller.digitalPad.up.isPressed(), controller.digitalPad.right.isPressed(), controller.digitalPad.down.isPressed(), controller.digitalPad.left.isPressed());
             } else {
-                glyphRenderer.drawDPad(controller, x1 - 48 - 8, y + 96 - 8, controller.digitalPad.up.isPressed(), controller.digitalPad.right.isPressed(), controller.digitalPad.down.isPressed(), controller.digitalPad.left.isPressed());
+                glyphRenderer.drawDPad(controller, x1 - 100, y + 32 + 4, controller.digitalPad.up.isPressed(), controller.digitalPad.right.isPressed(), controller.digitalPad.down.isPressed(), controller.digitalPad.left.isPressed());
             }
 
         }
@@ -106,27 +105,6 @@ public class GuiControllerSetup extends GuiScreen {
         glyphRenderer.drawButton(controller, controller.buttonY, x + w * 1, y + w * 0, controller.buttonY.isPressed());
     }
 
-    private void drawDPad(ControllerInput controller, int x, int y) {
-        GL11.glEnable(GL11.GL_TEXTURE_2D);
-        mc.renderEngine.loadTexture("/assets/minecraft/textures/gui/controller/" + controller.getType().getAssetName() + ".png").bind();
-        glColor3d(1.0, 1.0, 1.0);
-        glBegin(GL_QUADS);
-
-        rectangle(x, y, 48, 48, 0, 64, 48, 48);
-
-        if (controller.digitalPad.up.isPressed())
-            rectangle(x + 18, y + 0, 12, 18, 66, 64, 12, 18);
-        if (controller.digitalPad.down.isPressed())
-            rectangle(x + 18, y + 30, 12, 18, 66, 94, 12, 18);
-        if (controller.digitalPad.left.isPressed())
-            rectangle(x + 0, y + 18, 18, 12, 48, 82, 18, 12);
-        if (controller.digitalPad.right.isPressed())
-            rectangle(x + 30, y + 18, 18, 12, 78, 82, 18, 12);
-
-        glEnd();
-
-    }
-
     @Override
     public void keyTyped(char c, int i, int mouseX, int mouseY) {
 
@@ -153,19 +131,6 @@ public class GuiControllerSetup extends GuiScreen {
         glVertex2d(x, y + h);
     }
 
-    private void rectangle(double x, double y, double w, double h, int u0, int v0, int uw, int vh) {
-        double u1 = u0 + uw;
-        double v1 = v0 + vh;
-        glTexCoord2d(u0 / 256.0, v0 / 256.0);
-        glVertex2d(x, y);
-        glTexCoord2d(u1 / 256.0, v0 / 256.0);
-        glVertex2d(x + w, y);
-        glTexCoord2d(u1 / 256.0, v1 / 256.0);
-        glVertex2d(x + w, y + h);
-        glTexCoord2d(u0 / 256.0, v1 / 256.0);
-        glVertex2d(x, y + h);
-    }
-
     @Override
     protected void buttonPressed(GuiButton button) {
         super.buttonPressed(button);
@@ -173,17 +138,17 @@ public class GuiControllerSetup extends GuiScreen {
         if (button.id == 0) {
             int index;
             if (mc.controllerInput == null) {
-                index = 0;
+                index = -1;
             } else {
                 index = Arrays.asList(InputHandler.controllers).indexOf(mc.controllerInput);
-                do {
-                    index++;
-                    if (index >= InputHandler.controllers.length) {
-                        index = -1;
-                        break;
-                    }
-                } while (InputHandler.controllers[index] == null);
             }
+            do {
+                index++;
+                if (index >= InputHandler.controllers.length) {
+                    index = -1;
+                    break;
+                }
+            } while (InputHandler.controllers[index] == null);
             if (index == -1) {
                 mc.controllerInput = null;
                 mc.setInputType(InputType.KEYBOARD);
diff --git a/src/main/java/net/minecraft/client/gui/GuiIngame.java b/src/main/java/net/minecraft/client/gui/GuiIngame.java
index c4f7123b2..35a761ce0 100644
--- a/src/main/java/net/minecraft/client/gui/GuiIngame.java
+++ b/src/main/java/net/minecraft/client/gui/GuiIngame.java
@@ -20,6 +20,9 @@ import net.minecraft.client.render.Lighting;
 import net.minecraft.client.render.dynamictexture.DynamicTexture;
 import net.minecraft.client.render.entity.ItemEntityRenderer;
 import net.minecraft.client.render.item.model.ItemModelDispatcher;
+import net.minecraft.client.render.terrain.RenderList;
+import net.minecraft.client.render.terrain.RenderRegion;
+import net.minecraft.client.render.terrain.TerrainRendererMultiDraw;
 import net.minecraft.client.render.texture.stitcher.IconCoordinate;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.client.render.tessellator.Tessellator;
@@ -206,7 +209,48 @@ public class GuiIngame extends Gui
                 
                 drawDebugScreenLine("Better than Adventure! " + Minecraft.VERSION);
                 drawDebugScreenLine(mc.debug);
-                drawDebugScreenLine(mc.getRendererDebugInfo());
+                if(mc.terrainRenderer instanceof TerrainRendererMultiDraw) {
+                	TerrainRendererMultiDraw terrainRenderer = (TerrainRendererMultiDraw) mc.terrainRenderer;
+                	
+                	int regionCount = terrainRenderer.regionBuffer.size();
+
+                    int playerX = MathHelper.floor(mc.thePlayer.x);
+                    int playerZ = MathHelper.floor(mc.thePlayer.z);
+
+                	
+                	List<RenderRegion> allRegions = terrainRenderer.regionBuffer.getAllRegions();
+                    int totalEntries = 0;
+                    int visibleEntries = 0;
+                	for(int i=0; i < allRegions.size(); i++) {
+                        RenderRegion region = allRegions.get(i);
+                        for(int j=0; j < region.renderLists.length; j++) {
+                        	RenderList renderList = region.renderLists[j];
+                        	if(renderList == null) {
+                        		continue;
+                        	}
+                        	totalEntries += renderList.size();
+                        	visibleEntries += renderList.visibleEntryCount();
+                        }
+                	}
+                	
+                	drawDebugScreenLine("R: " + regionCount + " D: " + terrainRenderer.getTotalDrawCalls() + " T: " + visibleEntries + "/" + totalEntries + " VBO: " + terrainRenderer.vertexBuffer.entryCount());
+                	
+                	RenderRegion currentRegion = terrainRenderer.getRenderRegionAtBlockPos(playerX, playerZ);
+                    totalEntries = 0;
+                    visibleEntries = 0;
+                    for(int j=0; j < currentRegion.renderLists.length; j++) {
+                    	RenderList renderList = currentRegion.renderLists[j];
+                    	if(renderList == null) {
+                    		continue;
+                    	}
+                    	totalEntries += renderList.size();
+                    	visibleEntries += renderList.visibleEntryCount();
+                    }
+                	drawDebugScreenLine("CR: " + currentRegion + " E: " + visibleEntries + "/" + totalEntries);
+
+                }else {
+                    drawDebugScreenLine(mc.getRendererDebugInfo());
+                }
                 drawDebugScreenLine(mc.getEntityDebugInfoString());
                 drawDebugScreenLine(mc.getEntityCountsInfoString());
                 drawDebugScreenLine(mc.getChunkProviderInfoString());
diff --git a/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java b/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
index 246088f51..efa2030db 100644
--- a/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
+++ b/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
@@ -258,6 +258,7 @@ public abstract class OptionsPages
             .withComponent(new KeyBindingComponent(mc.gameSettings.keyReloadChunks))
             .withComponent(new KeyBindingComponent(mc.gameSettings.keySmoothLighting))
             .withComponent(new KeyBindingComponent(mc.gameSettings.keyReleaseCursor))
+            .withComponent(new KeyBindingComponent(mc.gameSettings.keyLoadChunksQuickly))
         )
         .withComponent(new OptionsCategory("gui.options.page.debug.category.settings")
             .withComponent(new BooleanOptionComponent(mc.gameSettings.alphaMenu))
@@ -267,7 +268,6 @@ public abstract class OptionsPages
             .withComponent(new BooleanOptionComponent(mc.gameSettings.showChunkBorders))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.showCollisionBoxes))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.showItemDebugInfo))
-            .withComponent(new BooleanOptionComponent(mc.gameSettings.disableJInput))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.disableCrashHandler))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.startInFullscreen))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.disableShaders))
@@ -275,6 +275,7 @@ public abstract class OptionsPages
             .withComponent(new BooleanOptionComponent(mc.gameSettings.allowSoundOverrides))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.downloadSoundfiles))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.enableDevResourceLoading))
+            .withComponent(new BooleanOptionComponent(mc.gameSettings.enableMultiDrawRendering))
         )
         .withComponent(new OptionsCategory("gui.options.page.debug.category.utility")
             .withComponent(new ShortcutComponent("gui.options.page.debug.button.open.model_viewer", () -> {
diff --git a/src/main/java/net/minecraft/client/input/InputHandler.java b/src/main/java/net/minecraft/client/input/InputHandler.java
index 473e3a096..b8f1c3ad2 100644
--- a/src/main/java/net/minecraft/client/input/InputHandler.java
+++ b/src/main/java/net/minecraft/client/input/InputHandler.java
@@ -1,5 +1,6 @@
 package net.minecraft.client.input;
 
+import net.minecraft.client.input.glfw.FakeGamepad;
 import org.lwjgl.glfw.GLFW;
 
 import net.minecraft.client.Minecraft;
@@ -13,21 +14,11 @@ public abstract class InputHandler {
 	
 	public static void init(Minecraft minecraft) throws Exception{
 		InputHandler.minecraft = minecraft;
-		
-        if(!minecraft.gameSettings.disableJInput.value) {
-        	try {
-        		initJInput(minecraft);
-        	}catch (Throwable e) {
-        		System.err.println("Could not initialize JInput.");
-        		e.printStackTrace();
-			}
-        }else {
-        	System.err.println("JInput is disabled!");
-        }
+
 		try {
 			initLWJGL3(minecraft);
 		} catch (Throwable e) {
-			System.err.println("Could not initialize SDL2.");
+			System.err.println("Could not initialize LWJGL3.");
 			e.printStackTrace();
 		}
 	}
@@ -79,30 +70,15 @@ public abstract class InputHandler {
 				}
 			}
 		}
+//		int[] vids = { 0x0079, 0x0079, 0x054c, 0x0079, 0x03F0, 0x057e, 0x28de, 0x28de };
+//		int[] pids = { 0x181a, 0x181b, 0x0ce6, 0x18d4, 0x0495, 0x2008, 0x1101, 0x11ff };
+//		String[] names = { "Fake PS3", "Fake PS4", "Fake PS5", "Fake X360", "Fake XONE", "Fake Switch", "Fake Steam", "Fake Steam Deck" };
+//		for (int i = 0; i < vids.length; i++) {
+//			controllers[GLFW.GLFW_JOYSTICK_1 + i] = new ControllerInput(minecraft, new FakeGamepad(GLFW.GLFW_JOYSTICK_1 + i, names[i], vids[i], pids[i]));
+//		}
 		System.out.println(found + " controllers found!");
 	}
 	
-	public static void initJInput(Minecraft minecraft) throws Throwable{
-		net.java.games.input.Controller[] devices = getControllers();
-		
-		if(devices == null) {
-			System.err.println("Could not get devices!");
-			return;
-		}
-		
-		System.out.println(devices.length + " devices found!");
-		
-		for(net.java.games.input.Controller controller : devices) {
-			if(controller.getType() == net.java.games.input.Controller.Type.MOUSE) {
-				minecraft.mouseInput.addMouse(controller);
-			}
-		}
-	}
-	
-	public static net.java.games.input.Controller[] getControllers() throws Throwable{
-		return net.java.games.input.ControllerEnvironment.getDefaultEnvironment().getControllers();
-	}
-	
 	public static ControllerInput getController(String guid) {
 		try {
 			for(ControllerInput controllerInput : controllers) {
@@ -113,13 +89,5 @@ public abstract class InputHandler {
 		}catch (Exception e) {}
 		return null;
 	}
-	
-	public static net.java.games.input.Component getComponent(net.java.games.input.Controller controller, String id) {
-		try {
-			for(net.java.games.input.Component component : controller.getComponents()) {
-				if(component.getIdentifier().toString().equalsIgnoreCase(id)) return component;
-			}
-		}catch (Exception e) {}
-		return null;
-	}
+
 }
diff --git a/src/main/java/net/minecraft/client/input/MouseInput.java b/src/main/java/net/minecraft/client/input/MouseInput.java
index 0e5346ddf..5a25e5b8a 100644
--- a/src/main/java/net/minecraft/client/input/MouseInput.java
+++ b/src/main/java/net/minecraft/client/input/MouseInput.java
@@ -3,18 +3,16 @@ package net.minecraft.client.input;
 import java.util.ArrayList;
 import java.util.List;
 
+import net.minecraft.client.render.window.GameWindowGLFW;
+import org.lwjgl.glfw.GLFW;
 import org.lwjgl.input.Mouse;
 
-import net.java.games.input.Component;
-import net.java.games.input.Controller;
 import net.minecraft.client.Minecraft;
 
 public class MouseInput {
 	
 	public final Minecraft minecraft;
-	
-	private List<MouseDevice> mouses;
-	
+
 	public double deltaX;
 	public double deltaY;
 	
@@ -33,45 +31,14 @@ public class MouseInput {
 	}
 	
 	public void update() {
-		if(minecraft.gameSettings.rawMouseInput.value && this.mouses != null && this.mouses.size() > 0) {
-			this.deltaX = 0.0;
-			this.deltaY = 0.0;
-			
-			for(int i=0; i < mouses.size(); i++) {
-				MouseDevice mouse = mouses.get(i);
-				
-				mouse.device.poll();
-				
-				this.deltaX += mouse.x.getPollData() * 0.5;
-				this.deltaY += mouse.y.getPollData() * -0.5;
-			}
-		}else {
-			this.deltaX = Mouse.getDX();
-			this.deltaY = Mouse.getDY();
-		}
+		this.deltaX = Mouse.getDX();
+		this.deltaY = Mouse.getDY();
 	}
-	
-	public void addMouse(Controller controller) {
-		if(this.mouses == null) {
-			this.mouses = new ArrayList<>();
-		}
-		this.mouses.add(new MouseDevice(controller));
-	}
-	
-	public static class MouseDevice{
-		
-		private Controller device;
-		
-		private Component x;
-		private Component y;
-		
-		public MouseDevice(Controller device) {
-			this.device = device;
-			
-			this.x = InputHandler.getComponent(device, "x");
-			this.y = InputHandler.getComponent(device, "y");
+
+	public void setRawInput(boolean enabled) {
+		if (GLFW.glfwRawMouseMotionSupported()) {
+			GLFW.glfwSetInputMode(((GameWindowGLFW) minecraft.gameWindow).window, GLFW.GLFW_RAW_MOUSE_MOTION, enabled ? 1 : 0);
 		}
-		
 	}
 	
 }
diff --git a/src/main/java/net/minecraft/client/input/controller/ControllerInput.java b/src/main/java/net/minecraft/client/input/controller/ControllerInput.java
index b9433474a..77ed4c282 100644
--- a/src/main/java/net/minecraft/client/input/controller/ControllerInput.java
+++ b/src/main/java/net/minecraft/client/input/controller/ControllerInput.java
@@ -199,7 +199,7 @@ public class ControllerInput {
                     case PS4:
                     case PS5:
                         return "L2";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "ZL";
                     default:
                         return "LT";
@@ -210,7 +210,7 @@ public class ControllerInput {
                     case PS4:
                     case PS5:
                         return "R2";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "ZR";
                     default:
                         return "RT";
@@ -228,7 +228,7 @@ public class ControllerInput {
                     case PS4:
                     case PS5:
                         return "X";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "B";
                     default:
                         return "A";
@@ -239,7 +239,7 @@ public class ControllerInput {
                     case PS4:
                     case PS5:
                         return "O";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "A";
                     default:
                         return "B";
@@ -250,7 +250,7 @@ public class ControllerInput {
                     case PS4:
                     case PS5:
                         return "□";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "Y";
                     default:
                         return "X";
@@ -261,33 +261,33 @@ public class ControllerInput {
                     case PS4:
                     case PS5:
                         return "△";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "X";
                     default:
                         return "Y";
                 }
             case GLFW.GLFW_GAMEPAD_BUTTON_BACK:
                 switch (controllerType) {
-                    case XBOXONE:
+                    case XBOX_ONE:
                         return "View";
                     case PS3:
                         return "Select";
                     case PS4:
                     case PS5:
                         return "Share";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "-";
                     default:
                         return "Back";
                 }
             case GLFW.GLFW_GAMEPAD_BUTTON_START:
                 switch (controllerType) {
-                    case XBOXONE:
+                    case XBOX_ONE:
                         return "Menu";
                     case PS4:
                     case PS5:
                         return "Options";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "+";
                     default:
                         return "Start";
@@ -298,7 +298,7 @@ public class ControllerInput {
                     case PS4:
                     case PS5:
                         return "L1";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "L";
                     default:
                         return "LB";
@@ -309,7 +309,7 @@ public class ControllerInput {
                     case PS4:
                     case PS5:
                         return "R1";
-                    case NINTENDO_SWITCH_PRO:
+                    case SWITCH:
                         return "R";
                     default:
                         return "RB";
diff --git a/src/main/java/net/minecraft/client/input/controller/ControllerType.java b/src/main/java/net/minecraft/client/input/controller/ControllerType.java
index 399cc6eae..b59abd297 100644
--- a/src/main/java/net/minecraft/client/input/controller/ControllerType.java
+++ b/src/main/java/net/minecraft/client/input/controller/ControllerType.java
@@ -1,51 +1,36 @@
 package net.minecraft.client.input.controller;
 
 import net.minecraft.client.input.glfw.GlfwGamepad;
-import java.util.Arrays;
-import java.util.List;
 
 public enum ControllerType {
-    UNKNOWN(Category.XBOX, "xbox", "UNKNOWN"),
-    XBOX360(Category.XBOX, "xbox", "XBOX 360"),
-    XBOXONE(Category.XBOX, "xbox", "XBOX ONE"),
-    PS3(Category.PLAYSTATION, "playstation", "PLAYSTATION 3"),
-    PS4(Category.PLAYSTATION, "playstation", "PLAYSTATION 4"),
-    PS5(Category.PLAYSTATION, "playstation", "PLAYSTATION 5"),
-    NINTENDO_SWITCH_PRO(Category.NINTENDO, "nintendo", "NINTENDO SWITCH PRO");
-
-    private final Category category;
+    UNKNOWN(Layout.XBOX, "xbox_360", "UNKNOWN"),
+    XBOX_360(Layout.XBOX, "xbox_360", "XBOX 360"),
+    XBOX_ONE(Layout.XBOX, "xbox_one", "XBOX ONE"),
+    PS3(Layout.PLAYSTATION, "ps3", "PLAYSTATION 3"),
+    PS4(Layout.PLAYSTATION, "ps4", "PLAYSTATION 4"),
+    PS5(Layout.PLAYSTATION, "ps5", "PLAYSTATION 5"),
+    SWITCH(Layout.NINTENDO, "switch", "NINTENDO SWITCH"),
+    STEAM(Layout.XBOX, "steam", "STEAM CONTROLLER"),
+    STEAM_DECK(Layout.STEAM_DECK, "steam_deck", "STEAM DECK"),;
+
+    private final Layout layout;
     private final String assetName;
     private final String printableName;
 
-    ControllerType(Category category, String assetName, String printableName) {
-        this.category = category;
+    ControllerType(Layout layout, String assetName, String printableName) {
+        this.layout = layout;
         this.assetName = assetName;
         this.printableName = printableName;
     }
 
     public static ControllerType fromController(GlfwGamepad controller) {
-        List<String> name = Arrays.asList(controller.getName().toLowerCase().split(" "));
-        if (name.contains("xbox") || name.contains("xinput")) {
-            if (name.contains("one")) {
-                return XBOXONE;
-            }
-            return XBOX360;
-        }
-        if ((name.contains("playstation") && name.contains("3")) || name.contains("ps3")) {
-            return PS3;
-        } else if ((name.contains("playstation") && name.contains("4")) || name.contains("ps4")) {
-            return PS4;
-        } else if ((name.contains("playstation") && name.contains("5")) || name.contains("ps5")) {
-            return PS5;
-        } else if (name.contains("nintendo") && name.contains("switch") && name.contains("pro")) {
-            return NINTENDO_SWITCH_PRO;
-        } else {
-            return UNKNOWN;
-        }
+        int vid = controller.getVendorId();
+        int pid = controller.getDeviceId();
+        return ControllerTypeDatabase.getControllerType(vid, pid);
     }
 
-    public Category getCategory() {
-        return this.category;
+    public Layout getLayout() {
+        return this.layout;
     }
 
     public String getAssetName() {
@@ -56,9 +41,10 @@ public enum ControllerType {
         return this.printableName;
     }
 
-    public enum Category {
+    public enum Layout {
         XBOX,
         PLAYSTATION,
-        NINTENDO;
+        NINTENDO,
+        STEAM_DECK
     }
 }
diff --git a/src/main/java/net/minecraft/client/input/controller/ControllerTypeDatabase.java b/src/main/java/net/minecraft/client/input/controller/ControllerTypeDatabase.java
new file mode 100644
index 000000000..d9702e1c1
--- /dev/null
+++ b/src/main/java/net/minecraft/client/input/controller/ControllerTypeDatabase.java
@@ -0,0 +1,78 @@
+package net.minecraft.client.input.controller;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class ControllerTypeDatabase {
+    private static Map<Integer, ControllerType> controllers = null;
+
+    public static ControllerType getControllerType(int vid, int pid) {
+        int combined = ((vid & 0xFFFF) << 16) | (pid & 0xFFFF);
+        if (controllers == null) {
+            init();
+        }
+        return controllers.getOrDefault(combined, ControllerType.UNKNOWN);
+    }
+
+    private static void init() {
+        controllers = new HashMap<>();
+
+        // Read controllers.csv
+        try (InputStream stream = ControllerTypeDatabase.class.getResourceAsStream("/controllers.csv")) {
+            if (stream != null) {
+                BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    if (line.startsWith("#")) {
+                        continue;
+                    }
+
+                    String[] parts = line.split(",");
+                    if (parts.length != 3) {
+                        continue;
+                    }
+
+                    if (parts[0].startsWith("0x")) {
+                        parts[0] = parts[0].substring(2);
+                    }
+                    if (parts[1].startsWith("0x")) {
+                        parts[1] = parts[1].substring(2);
+                    }
+                    int vid = Integer.parseInt(parts[0], 16);
+                    int pid = Integer.parseInt(parts[1], 16);
+                    int combined = ((vid & 0xFFFF) << 16) | (pid & 0xFFFF);
+                    controllers.put(combined, getControllerType(parts[2]));
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return;
+        }
+    }
+
+    private static ControllerType getControllerType(String id) {
+        switch (id) {
+            case "PS3Controller":
+                return ControllerType.PS3;
+            case "PS4Controller":
+                return ControllerType.PS4;
+            case "PS5Controller":
+                return ControllerType.PS5;
+            case "XBox360Controller":
+                return ControllerType.XBOX_360;
+            case "XBoxOneController":
+                return ControllerType.XBOX_ONE;
+            case "SwitchController":
+                return ControllerType.SWITCH;
+            case "SteamController":
+                return ControllerType.STEAM;
+            case "SteamDeckController":
+                return ControllerType.STEAM_DECK;
+            default:
+                return ControllerType.UNKNOWN;
+        }
+    }
+}
diff --git a/src/main/java/net/minecraft/client/input/controller/DigitalPad.java b/src/main/java/net/minecraft/client/input/controller/DigitalPad.java
index 49ea31c1b..1ca4fac56 100644
--- a/src/main/java/net/minecraft/client/input/controller/DigitalPad.java
+++ b/src/main/java/net/minecraft/client/input/controller/DigitalPad.java
@@ -1,7 +1,5 @@
 package net.minecraft.client.input.controller;
 
-import net.java.games.input.Component;
-
 public class DigitalPad {
 	
 	public IButton up;
diff --git a/src/main/java/net/minecraft/client/input/controller/IButtonListener.java b/src/main/java/net/minecraft/client/input/controller/IButtonListener.java
index 69168223d..4671abd83 100644
--- a/src/main/java/net/minecraft/client/input/controller/IButtonListener.java
+++ b/src/main/java/net/minecraft/client/input/controller/IButtonListener.java
@@ -1,7 +1,5 @@
 package net.minecraft.client.input.controller;
 
-import net.java.games.input.Component;
-
 public interface IButtonListener {
 	
 	boolean isPressed(int button);
diff --git a/src/main/java/net/minecraft/client/input/glfw/FakeGamepad.java b/src/main/java/net/minecraft/client/input/glfw/FakeGamepad.java
new file mode 100644
index 000000000..a76eaa804
--- /dev/null
+++ b/src/main/java/net/minecraft/client/input/glfw/FakeGamepad.java
@@ -0,0 +1,65 @@
+package net.minecraft.client.input.glfw;
+
+public class FakeGamepad extends GlfwGamepad {
+    private final String name;
+    private final int vid;
+    private final int pid;
+
+    public FakeGamepad(int id, String name, int vid, int pid) {
+        super(id);
+
+        this.name = name;
+        this.vid = vid;
+        this.pid = pid;
+    }
+
+    @Override
+    public String getGuid() {
+        return "00000000000000000000000000000000";
+    }
+
+    @Override
+    public int getVendorId() {
+        return vid;
+    }
+
+    @Override
+    public int getDeviceId() {
+        return pid;
+    }
+
+    @Override
+    public void poll() {
+        return;
+    }
+
+    @Override
+    public boolean isPresent() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public float getAxisValue(int axis) {
+        return 0.0F;
+    }
+
+    @Override
+    public boolean isButtonPressed(int button) {
+        return false;
+    }
+
+    @Override
+    public int getHatValue(int hat) {
+        return 0;
+    }
+
+    @Override
+    public GlfwGamepad intoGamepad() {
+        return this;
+    }
+}
diff --git a/src/main/java/net/minecraft/client/input/glfw/GlfwJoystick.java b/src/main/java/net/minecraft/client/input/glfw/GlfwJoystick.java
index 31b2401f8..06f70a47b 100644
--- a/src/main/java/net/minecraft/client/input/glfw/GlfwJoystick.java
+++ b/src/main/java/net/minecraft/client/input/glfw/GlfwJoystick.java
@@ -7,6 +7,8 @@ import java.nio.FloatBuffer;
 
 public class GlfwJoystick {
     private final int id;
+    private final int vid;
+    private final int did;
     private FloatBuffer axes = null;
     private ByteBuffer buttons = null;
     private ByteBuffer hats = null;
@@ -14,12 +16,53 @@ public class GlfwJoystick {
 
     public GlfwJoystick(int id) {
         this.id = id;
+
+        String guid = this.getGuid();
+
+        int vid = 0;
+        try {
+            String lower = guid.substring(8, 10);
+            String upper = guid.substring(10, 12);
+            vid = Integer.parseInt(upper + lower, 16);
+        } catch (Exception e) {
+            System.out.println("Controller " + id + " has weird GUID!");
+            this.vid = 0;
+            this.did = 0;
+            return;
+        }
+
+        int did = 0;
+        try {
+            String lower = guid.substring(16, 18);
+            String upper = guid.substring(18, 20);
+            did = Integer.parseInt(upper + lower, 16);
+        } catch (Exception e) {
+            System.out.println("Controller " + id + " has weird GUID!");
+            this.vid = 0;
+            this.did = 0;
+            return;
+        }
+
+        this.vid = vid;
+        this.did = did;
     }
 
     public int getId() {
         return this.id;
     }
 
+    public String getGuid() {
+        return GLFW.glfwGetJoystickGUID(this.id);
+    }
+
+    public int getVendorId() {
+        return this.vid;
+    }
+
+    public int getDeviceId() {
+        return this.did;
+    }
+
     public void poll() {
         this.axes = GLFW.glfwGetJoystickAxes(this.id);
         this.buttons = GLFW.glfwGetJoystickButtons(this.id);
diff --git a/src/main/java/net/minecraft/client/option/GameSettings.java b/src/main/java/net/minecraft/client/option/GameSettings.java
index 9a34f85c1..2e8d9ee90 100644
--- a/src/main/java/net/minecraft/client/option/GameSettings.java
+++ b/src/main/java/net/minecraft/client/option/GameSettings.java
@@ -97,7 +97,6 @@ public class GameSettings {
 	public BooleanOption showChunkBorders = new BooleanOption(this, "showChunkBorders", false);
 	public BooleanOption showCollisionBoxes = new BooleanOption(this, "showCollisionBoxes", false);
 	public BooleanOption swapCraftingButtons = new BooleanOption(this, "swapCraftingButtons", false);
-	public BooleanOption disableJInput = new BooleanOption(this, "disableJInput", false);
     public BooleanOption forceUnicodeFont = new BooleanOption(this, "forceUnicodeFont", false);
 	public EnumOption<PlacementMode> placementMode = new EnumOption<>(this, "placementMode", PlacementMode.class, PlacementMode.DEFAULT).setIsSlider(false);
 	public BooleanOption discordRichPresence = new BooleanOption(this, "discordRichPresence", false);
@@ -126,7 +125,8 @@ public class GameSettings {
 	public BooleanOption enableItemClumping = new BooleanOption(this, "enableItemClumping", false);
 	public BooleanOption alwaysShowDescriptions = new BooleanOption(this, "alwaysShowDescriptions", false);
 	public BooleanOption turnWithVehicle = new BooleanOption(this, "turnWithVehicle", true);
-	public BooleanOption disableCrashHandler = new BooleanOption(this,"disableCrashHandler",false);
+	public BooleanOption disableCrashHandler = new BooleanOption(this, "disableCrashHandler", false);
+	public BooleanOption enableMultiDrawRendering = new BooleanOption(this, "enableMultiDrawRendering", true);
 
 	public BooleanOption enableDevResourceLoading = new BooleanOption(this, "enableDevResourceLoading", false);
 	public BooleanOption downloadSoundfiles = new BooleanOption(this, "downloadSoundfiles", true);
@@ -231,6 +231,7 @@ public class GameSettings {
 	public KeyBinding keyReleaseCursor = new KeyBinding("key.debug.releasecursor").setDefault(InputDevice.keyboard, KEY_F12);
 	public KeyBinding keyShowShortcuts = new KeyBinding("key.debug.shortcuts").setDefault(InputDevice.keyboard, KEY_Q);
 	public KeyBinding keyToggleTextureAtlas = new KeyBinding("key.debug.textureAtlas");
+	public KeyBinding keyLoadChunksQuickly = new KeyBinding("key.debug.loadChunksQuickly");
 
 	public static final List<KeyBinding> keys = new ArrayList<>();
 	public static final List<Option<?>> options = new ArrayList<>();
@@ -462,6 +463,9 @@ public class GameSettings {
 		if(option == customLightmap) {
 			mc.worldRenderer.lightmapHelper.setup();
 		}
+		if(option == rawMouseInput) {
+			mc.mouseInput.setRawInput(rawMouseInput.value);
+		}
 	}
 
 	public void loadOptions() {
diff --git a/src/main/java/net/minecraft/client/render/CameraSorter.java b/src/main/java/net/minecraft/client/render/CameraSorter.java
index 1a4c4a725..5dd7cfef5 100644
--- a/src/main/java/net/minecraft/client/render/CameraSorter.java
+++ b/src/main/java/net/minecraft/client/render/CameraSorter.java
@@ -1,6 +1,7 @@
 package net.minecraft.client.render;
 
 import net.minecraft.client.render.camera.ICamera;
+import net.minecraft.client.render.terrain.ChunkRenderer;
 
 import java.util.Comparator;
 
diff --git a/src/main/java/net/minecraft/client/render/ChunkRenderer.java b/src/main/java/net/minecraft/client/render/ChunkRenderer.java
deleted file mode 100644
index 0fdef71bc..000000000
--- a/src/main/java/net/minecraft/client/render/ChunkRenderer.java
+++ /dev/null
@@ -1,298 +0,0 @@
-package net.minecraft.client.render;
-
-import net.minecraft.client.render.block.model.BlockModel;
-import net.minecraft.client.render.block.model.BlockModelDispatcher;
-import net.minecraft.client.render.camera.ICamera;
-import net.minecraft.client.render.culling.CameraFrustum;
-import net.minecraft.client.render.entity.ItemEntityRenderer;
-import net.minecraft.client.render.tessellator.Tessellator;
-import net.minecraft.core.block.Block;
-import net.minecraft.core.block.entity.BlockEntity;
-import net.minecraft.core.entity.Entity;
-import net.minecraft.core.util.helper.MathHelper;
-import net.minecraft.core.util.phys.AABB;
-import net.minecraft.core.world.World;
-import net.minecraft.core.world.chunk.Chunk;
-import net.minecraft.core.world.chunk.ChunkCache;
-import org.lwjgl.opengl.GL11;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class ChunkRenderer
-{
-    public static final int MAX_RENDER_PASSES = 2;
-
-    public World world;
-    private final int lists;
-    private static final Tessellator tessellator;
-    public static int updates = 0;
-    public int x;
-    public int y;
-    public int z;
-    public int xs;
-    public int ys;
-    public int zs;
-    public int xRender;
-    public int yRender;
-    public int zRender;
-    public int xRenderOffs;
-    public int yRenderOffs;
-    public int zRenderOffs;
-    public boolean visible;
-    public boolean[] empty;
-    public int xm;
-    public int ym;
-    public int zm;
-    public float radius;
-    public boolean dirty;
-    public AABB bb;
-    public int id;
-    public boolean occlusionVisible;
-    public boolean occlusionQuerying;
-    public int occlusionId;
-    public boolean skyLit;
-    private boolean compiled;
-    public List<BlockEntity> renderableBlockEntities;
-    private final List<BlockEntity> globalRenderableBlockEntities;
-
-    public ChunkRenderer(World world, List<BlockEntity> globalRenderableTileEntities, int x, int y, int z, int size, int lists)
-    {
-        visible = false;
-        empty = new boolean[MAX_RENDER_PASSES];
-        occlusionVisible = true;
-        compiled = false;
-        renderableBlockEntities = new ArrayList<>();
-        this.world = world;
-        globalRenderableBlockEntities = globalRenderableTileEntities;
-        xs = ys = zs = size;
-        radius = MathHelper.sqrt_float(xs * xs + ys * ys + zs * zs) / 2.0F;
-        this.lists = lists;
-        this.x = -999;
-        setPos(x, y, z);
-        dirty = false;
-    }
-
-    public void setPos(int x, int y, int z)
-    {
-        if (x != this.x || y != this.y || z != this.z)
-        {
-            reset();
-            this.x = x;
-            this.y = y;
-            this.z = z;
-            xm = x + xs / 2;
-            ym = y + ys / 2;
-            zm = z + zs / 2;
-            xRenderOffs = x & 0x3ff;
-            yRenderOffs = y;
-            zRenderOffs = z & 0x3ff;
-            xRender = x - xRenderOffs;
-            yRender = y - yRenderOffs;
-            zRender = z - zRenderOffs;
-            float f = 6F;
-            bb = AABB.getPermanentBB((float)x - f, (float)y - f, (float)z - f, (float)(x + xs) + f, (float)(y + ys) + f, (float)(z + zs) + f);
-            GL11.glNewList(lists + MAX_RENDER_PASSES, GL11.GL_COMPILE);
-            ItemEntityRenderer.renderFlatAABB(AABB.getTemporaryBB((float) xRenderOffs - f, (float) yRenderOffs - f, (float) zRenderOffs - f, (float)(xRenderOffs + xs) + f, (float)(yRenderOffs + ys) + f, (float)(zRenderOffs + zs) + f));
-            GL11.glEndList();
-            setDirty();
-        }
-    }
-
-    private void translateToPos()
-    {
-        GL11.glTranslatef(xRenderOffs, yRenderOffs, zRenderOffs);
-    }
-
-    public void rebuild()
-    {
-        if(!dirty) { return; }
-        updates++;
-        final int minX = x;
-        final int minY = y;
-        final int minZ = z;
-        final int maxX = x + xs;
-        final int maxY = y + ys;
-        final int maxZ = z + zs;
-        for(int i = 0; i < MAX_RENDER_PASSES; i++) { empty[i] = true; }
-
-        Chunk.isLit = false;
-        final Set<BlockEntity> lastSpecialTileEntities = new HashSet<>(renderableBlockEntities);
-        renderableBlockEntities.clear();
-        final int cacheRadius = 1;
-        final ChunkCache chunkcache = new ChunkCache(world, minX - cacheRadius, minY - cacheRadius, minZ - cacheRadius, maxX + cacheRadius, maxY + cacheRadius, maxZ + cacheRadius);
-        final RenderBlocks renderblocks = new RenderBlocks(chunkcache);
-        BlockModel.setRenderBlocks(renderblocks);
-        int renderPass = 0;
-        do
-        {
-            if(renderPass >= MAX_RENDER_PASSES) { break; }
-
-            boolean needsMoreRenderPasses = false;
-            boolean hasRenderedBlock = false;
-            boolean hasStartedDrawing = false;
-
-            for(int y = minY; y < maxY; y++)
-            {
-                for(int z = minZ; z < maxZ; z++)
-                {
-                    for(int x = minX; x < maxX; x++)
-                    {
-                        final int blockId = chunkcache.getBlockId(x, y, z);
-                        if(blockId <= 0) {
-                            continue;
-                        }
-                        if(!hasStartedDrawing) {
-                            hasStartedDrawing = true;
-                            GL11.glNewList(lists + renderPass, GL11.GL_COMPILE);
-                            GL11.glPushMatrix();
-                            translateToPos();
-                            final float scale = 1.000001F;
-                            GL11.glTranslatef((float)(-zs) / 2.0F, (float)(-ys) / 2.0F, (float)(-zs) / 2.0F);
-                            GL11.glScalef(scale, scale, scale);
-                            GL11.glTranslatef((float) zs / 2.0F, (float) ys / 2.0F, (float) zs / 2.0F);
-                            tessellator.startDrawingQuads();
-                            tessellator.setTranslation(-this.x, -this.y, -this.z);
-                            
-                            if(LightmapHelper.isLightmapEnabled()) {
-                            	// Make sure it's active to prevent crash if a model doesnt enable it
-                            	tessellator.setLightmapCoord(LightmapHelper.getLightmapCoord(15, 15));
-                            }
-                        }
-                        if(renderPass == 0 && Block.isEntityTile[blockId]) {
-                            final BlockEntity tileentity = chunkcache.getBlockEntity(x, y, z);
-                            if(BlockEntityRenderDispatcher.instance.hasRenderer(tileentity)) {
-                                renderableBlockEntities.add(tileentity);
-                            }
-                        }
-                        final Block block = Block.blocksList[blockId];
-                        final int blockRenderPass = block.getRenderLayer();
-                        if(blockRenderPass != renderPass) {
-                            needsMoreRenderPasses = true;
-                        }
-                        else {
-                        	if(LightmapHelper.isLightmapEnabled()) {
-                        		// Can probably be removed once all block models support the lightmap
-                        		tessellator.setLightmapCoord(LightmapHelper.getLightmapCoord(15, 0));
-                        	}
-                            
-                            final BlockModel<Block> model = BlockModelDispatcher.getInstance().getDispatch(block);
-                            hasRenderedBlock |= model.render(tessellator, x, y, z);
-                            if (model.hasOverbright()) {
-                            	renderblocks.overbright = true;
-                                hasRenderedBlock |= model.render(tessellator, x, y, z);
-                                renderblocks.overbright = false;
-                            }
-                        }
-                    }
-                }
-            }
-            if(hasStartedDrawing) {
-                tessellator.draw();
-                GL11.glPopMatrix();
-                GL11.glEndList();
-                tessellator.setTranslation(0.0D, 0.0D, 0.0D);
-            } else {
-                hasRenderedBlock = false;
-            }
-            
-            if(hasRenderedBlock) {
-                empty[renderPass] = false;
-            }            
-            if(!needsMoreRenderPasses) {
-                break;
-            }
-            
-            renderPass++;
-        } while(true);
-        HashSet<BlockEntity> newSpecialTileEntities = new HashSet<>(renderableBlockEntities);
-        newSpecialTileEntities.removeAll(lastSpecialTileEntities);
-        globalRenderableBlockEntities.addAll(newSpecialTileEntities);
-        renderableBlockEntities.forEach(lastSpecialTileEntities::remove);
-        globalRenderableBlockEntities.removeAll(lastSpecialTileEntities);
-        skyLit = Chunk.isLit;
-        compiled = true;
-    }
-
-    public float distanceToEntitySquared(Entity entity)
-    {
-        float dx = (float)(entity.x - (double) xm);
-        float dy = (float)(entity.y - (double) ym);
-        float dz = (float)(entity.z - (double) zm);
-        return dx * dx + dy * dy + dz * dz;
-    }
-
-    public float distanceToCameraSquared(ICamera camera)
-    {
-        float dx = (float)(camera.getX(1f) - xm);
-        float dy = (float)(camera.getY(1f) - ym);
-        float dz = (float)(camera.getZ(1f) - zm);
-        return dx * dx + dy * dy + dz * dz;
-    }
-
-    public void reset()
-    {
-        for(int i = 0; i < MAX_RENDER_PASSES; i++)
-        {
-            empty[i] = true;
-        }
-
-        visible = false;
-        compiled = false;
-    }
-
-    public void delete()
-    {
-        reset();
-        world = null;
-    }
-
-    public int getList(int layer)
-    {
-        if(!visible)
-        {
-            return -1;
-        }
-        if(!empty[layer])
-        {
-            return lists + layer;
-        } 
-        else
-        {
-            return -1;
-        }
-    }
-
-    public void cull(CameraFrustum frustum, float partialTick)
-    {
-        visible = frustum.isVisible(bb, partialTick);
-    }
-
-    public void renderBB()
-    {
-        GL11.glCallList(lists + MAX_RENDER_PASSES);
-    }
-
-    public boolean isEmpty()
-    {
-        if(!compiled)
-        {
-            return false;
-        } else
-        {
-            return empty[0] && empty[1];
-        }
-    }
-
-    public void setDirty()
-    {
-        dirty = true;
-    }
-
-    static 
-    {
-        tessellator = Tessellator.instance;
-    }
-}
diff --git a/src/main/java/net/minecraft/client/render/ChunkRendererComparator.java b/src/main/java/net/minecraft/client/render/ChunkRendererComparator.java
index 86185a0e9..e5291eebd 100644
--- a/src/main/java/net/minecraft/client/render/ChunkRendererComparator.java
+++ b/src/main/java/net/minecraft/client/render/ChunkRendererComparator.java
@@ -1,6 +1,7 @@
 package net.minecraft.client.render;
 
 import net.minecraft.client.render.camera.ICamera;
+import net.minecraft.client.render.terrain.ChunkRenderer;
 
 import java.util.Comparator;
 
diff --git a/src/main/java/net/minecraft/client/render/OpenGLHelper.java b/src/main/java/net/minecraft/client/render/OpenGLHelper.java
index aed0371c0..6b545d69b 100644
--- a/src/main/java/net/minecraft/client/render/OpenGLHelper.java
+++ b/src/main/java/net/minecraft/client/render/OpenGLHelper.java
@@ -19,6 +19,8 @@ public class OpenGLHelper {
 	
 	public static boolean enableOcclusionCheck = false;
 	public static boolean enableSphericalFog = false;
+	public static boolean gl20 = false;
+	public static boolean gl31 = false;
 	
     public static void testCapabilities(Minecraft minecraft) {
         System.out.println(glGetString(GL_VERSION));
@@ -33,6 +35,8 @@ public class OpenGLHelper {
         
         enableOcclusionCheck = capabilities.GL_ARB_occlusion_query;
         enableSphericalFog = capabilities.GL_NV_fog_distance;
+        gl20 = capabilities.OpenGL20;
+        gl31 = capabilities.OpenGL31;
         
 		if(minecraft.gameSettings.disableShaders.value) {
 			Shaders.enableShaders = false;
diff --git a/src/main/java/net/minecraft/client/render/RenderGlobal.java b/src/main/java/net/minecraft/client/render/RenderGlobal.java
index 0289d6990..474a729ce 100644
--- a/src/main/java/net/minecraft/client/render/RenderGlobal.java
+++ b/src/main/java/net/minecraft/client/render/RenderGlobal.java
@@ -14,6 +14,7 @@ import net.minecraft.client.render.camera.EntityCamera;
 import net.minecraft.client.render.camera.ICamera;
 import net.minecraft.client.render.colorizer.Colorizers;
 import net.minecraft.client.render.culling.CameraFrustum;
+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;
@@ -74,7 +75,7 @@ public class RenderGlobal
         }
     }
     public static final int RENDER_CHUNK_SIZE = 16;
-    public static final int RENDER_LIST_BASE_WIDTH = 64;
+    public static final int RENDER_LIST_BASE_WIDTH = 48;
 
     public RenderGlobal(Minecraft minecraft, RenderEngine renderengine)
     {
@@ -231,10 +232,6 @@ public class RenderGlobal
             }
         }
         int renderDiameterBlocks = renderDistance.chunks * 16 * 2;
-        if (renderDiameterBlocks > 400)
-        {
-            renderDiameterBlocks = 400;
-        }
 		renderChunksWide = renderDiameterBlocks / RENDER_CHUNK_SIZE + 1;
         renderChunksTall = worldObj.getHeightBlocks() / RENDER_CHUNK_SIZE;
 		renderChunksDeep =  renderDiameterBlocks / RENDER_CHUNK_SIZE + 1;
@@ -262,7 +259,7 @@ public class RenderGlobal
                 for(int z = 0; z < renderChunksDeep; z++)
                 {
                     int index = (z * renderChunksTall + y) * renderChunksWide + x;
-                    chunkRenderers[index] = new ChunkRenderer(worldObj, tileEntities, x * RENDER_CHUNK_SIZE, y * RENDER_CHUNK_SIZE, z * RENDER_CHUNK_SIZE, RENDER_CHUNK_SIZE, glRenderListBase + renderListOffset);
+                    chunkRenderers[index] = mc.terrainRenderer.createChunkRenderer(worldObj, tileEntities, x * RENDER_CHUNK_SIZE, y * RENDER_CHUNK_SIZE, z * RENDER_CHUNK_SIZE, RENDER_CHUNK_SIZE, glRenderListBase + renderListOffset);
                     if(occlusionEnabled)
                     {
                         chunkRenderers[index].occlusionId = glOcclusionQueryBase.get(chunkIndex);
@@ -486,7 +483,7 @@ public class RenderGlobal
         return "E: " + countEntitiesRendered + "/" + countEntitiesTotal + ". B: " + countEntitiesHidden + ", I: " + (countEntitiesTotal - countEntitiesHidden - countEntitiesRendered);
     }
 
-    private void markRenderersForNewPosition(int i, int j, int k)
+    public void markRenderersForNewPosition(int i, int j, int k)
     {
         i -= 8;
         j -= 8;
diff --git a/src/main/java/net/minecraft/client/render/WorldRenderer.java b/src/main/java/net/minecraft/client/render/WorldRenderer.java
index eccac0135..ca54ffae0 100644
--- a/src/main/java/net/minecraft/client/render/WorldRenderer.java
+++ b/src/main/java/net/minecraft/client/render/WorldRenderer.java
@@ -5,6 +5,7 @@ import static org.lwjgl.opengl.GL11.*;
 import java.util.List;
 import java.util.Random;
 
+import net.minecraft.client.world.chunk.provider.ChunkProviderDynamic;
 import net.minecraft.core.entity.Mob;
 import net.minecraft.core.util.phys.Vec3;
 import org.lwjgl.input.Mouse;
@@ -20,6 +21,7 @@ import net.minecraft.client.gui.modelviewer.GuiModelViewer;
 import net.minecraft.client.input.InputType;
 import net.minecraft.client.input.MouseFilter;
 import net.minecraft.client.input.PlayerInput;
+import net.minecraft.client.option.GameSettings;
 import net.minecraft.client.render.camera.CameraUtil;
 import net.minecraft.client.render.camera.EntityCamera;
 import net.minecraft.client.render.camera.EntityCameraFirstPerson;
@@ -32,7 +34,6 @@ import net.minecraft.client.render.culling.Frustum;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.client.util.debug.Debug;
-import net.minecraft.client.world.chunk.provider.ChunkProviderStatic;
 import net.minecraft.core.util.phys.HitResult;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.material.Material;
@@ -579,7 +580,7 @@ public class WorldRenderer
         }
     }
 
-    public void renderWorld(float partialTick, long updateRenderersUntil)
+    public void renderWorld(float partialTicks, long updateRenderersUntil)
     {
     	Debug.push("misc");
         Colorizers.update(false);
@@ -593,7 +594,7 @@ public class WorldRenderer
         {
             mc.activeCamera = new EntityCameraFirstPerson(mc, mc.thePlayer);
         }
-        getMouseOver(partialTick);
+        getMouseOver(partialTicks);
         Mob activeEntity = null;
         if (mc.activeCamera instanceof EntityCamera)
         {
@@ -602,70 +603,76 @@ public class WorldRenderer
 
         if (activeEntity != null)
         {
-            double viewEntityX = activeEntity.xo + (activeEntity.x - activeEntity.xo) * (double)partialTick;
-            double viewEntityZ = activeEntity.zo + (activeEntity.z - activeEntity.zo) * (double)partialTick;
+            double viewEntityX = activeEntity.xo + (activeEntity.x - activeEntity.xo) * (double)partialTicks;
+            double viewEntityZ = activeEntity.zo + (activeEntity.z - activeEntity.zo) * (double)partialTicks;
             IChunkProvider iChunkProvider = mc.theWorld.getChunkProvider();
-            if(iChunkProvider instanceof ChunkProviderStatic)
+            if(iChunkProvider instanceof ChunkProviderDynamic)
             {
-                ChunkProviderStatic chunkProviderStatic = (ChunkProviderStatic)iChunkProvider;
+                ChunkProviderDynamic ChunkProviderDynamic = (ChunkProviderDynamic)iChunkProvider;
                 int chunkX = MathHelper.floor_float((int)viewEntityX) >> 4;
                 int chunkZ = MathHelper.floor_float((int)viewEntityZ) >> 4;
-                chunkProviderStatic.setCurrentChunkOver(chunkX, chunkZ);
+                ChunkProviderDynamic.setCurrentChunkOver(chunkX, chunkZ);
             }
         }
 
-        fogManager.updateFogColor(partialTick);
+        fogManager.updateFogColor(partialTicks);
         
         Debug.change("post");
-        mc.renderer.beginRenderWorld(partialTick);
+        mc.renderer.beginRenderWorld(partialTicks);
         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
         glEnable(GL_CULL_FACE);
-        setupCameraTransform(partialTick);
+        setupCameraTransform(partialTicks);
         Frustum.getFrustum();
         Debug.change("sky");
         if(mc.gameSettings.renderDistance.value.renderSky && !(mc.currentScreen instanceof GuiModelViewer))
         {
-            fogManager.setupFog(FogManager.FOG_MODE_SKY, farPlaneDistance, partialTick);
+            fogManager.setupFog(FogManager.FOG_MODE_SKY, farPlaneDistance, partialTicks);
             glDisable(GL_FOG);
 
-            renderGlobal.drawSky(partialTick);
+            renderGlobal.drawSky(partialTicks);
         }
         
         Debug.change("misc");
         glEnable(GL_FOG);
-        fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTick);
+        fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTicks);
         if(mc.gameSettings.ambientOcclusion.value)
         {
             glShadeModel(GL11.GL_SMOOTH);
         }
         
-        mc.renderGlobal.clipRenderersByFrustum(mc.activeCamera.getFrustum(), partialTick);
+        mc.renderGlobal.clipRenderersByFrustum(mc.activeCamera.getFrustum(), partialTicks);
 
         Debug.change("aurora");
         if (mc.gameSettings.aurora.value && mc.theWorld.getWorldType().hasAurora())
         {
-            renderGlobal.renderAurora(mc.activeCamera, partialTick);
+            renderGlobal.renderAurora(mc.activeCamera, partialTicks);
         }
         Debug.change("update");
 
-        long l1;
-        do
-        {
-            if(mc.renderGlobal.updateRenderers(mc.activeCamera) || updateRenderersUntil == 0L)
+        int loops = 1;
+        if(mc.gameSettings.keyLoadChunksQuickly.isPressed()) {
+        	loops = 40;
+        }
+        for(int i=0; i < loops; i++) {
+            long l1;
+            do
             {
-                break;
-            }
-            l1 = updateRenderersUntil - System.nanoTime();
-        } while(l1 >= 0L && l1 <= nsPerSecond);
+                if(mc.renderGlobal.updateRenderers(mc.activeCamera) || updateRenderersUntil == 0L)
+                {
+                    break;
+                }
+                l1 = updateRenderersUntil - System.nanoTime();
+            } while(l1 >= 0L && l1 <= nsPerSecond);
+        }
 
-        fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTick);
+        fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTicks);
         glEnable(GL_FOG);
         TextureRegistry.blockAtlas.bind();
         Lighting.disable();
 
         Debug.change("terrain");
         lightmapHelper.enableLightmapRendering();
-        renderGlobal.sortAndRender(mc.activeCamera, 0, partialTick);
+        mc.terrainRenderer.renderSolidTerrain(partialTicks);
 
         Debug.change("entities");
         glShadeModel(GL_FLAT);
@@ -675,17 +682,17 @@ public class WorldRenderer
         {
             glDisable(GL_FOG);
         }
-        renderGlobal.renderEntities(mc.activeCamera, partialTick);
+        renderGlobal.renderEntities(mc.activeCamera, partialTicks);
         if (!mc.gameSettings.fog.value)
         {
             glEnable(GL_FOG);
         }
         
         Debug.change("particles");
-        effectRenderer.renderParticlesAtOrigin(partialTick);
+        effectRenderer.renderParticlesAtOrigin(partialTicks);
         Lighting.disable();
-        fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTick);
-        effectRenderer.renderParticles(mc.activeCamera, partialTick);
+        fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTicks);
+        effectRenderer.renderParticles(mc.activeCamera, partialTicks);
 
         lightmapHelper.disableLightmapRendering();
         
@@ -693,76 +700,58 @@ public class WorldRenderer
         if(mc.objectMouseOver != null)
         {
             glDisable(GL_ALPHA_TEST);
-            renderGlobal.drawBlockBreaking(mc.activeCamera, mc.objectMouseOver, partialTick);
-            renderGlobal.drawSelectionBox(mc.activeCamera, mc.objectMouseOver, partialTick);
+            renderGlobal.drawBlockBreaking(mc.activeCamera, mc.objectMouseOver, partialTicks);
+            renderGlobal.drawSelectionBox(mc.activeCamera, mc.objectMouseOver, partialTicks);
             glEnable(GL_ALPHA_TEST);
         }
         
         Debug.change("debugoverlays");
         if(mc.gameSettings.showCollisionBoxes.value)
         {
-        	renderGlobal.drawDebugEntityOutlines(mc.activeCamera, partialTick);
+        	renderGlobal.drawDebugEntityOutlines(mc.activeCamera, partialTicks);
         }
         if(mc.gameSettings.showChunkBorders.value)
         {
-        	renderGlobal.drawDebugChunkBorders(mc.activeCamera, partialTick);
+        	renderGlobal.drawDebugChunkBorders(mc.activeCamera, partialTicks);
         }
         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-        fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTick);
+        fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTicks);
         glEnable(GL_BLEND);
         glDisable(GL_CULL_FACE);
         TextureRegistry.blockAtlas.bind();
 
         Debug.change("water");
-        if(mc.gameSettings.ambientOcclusion.value)
-        {
-            glShadeModel(GL_SMOOTH);
-        }
-        if(mc.gameSettings.fancyGraphics.value == 1)
-        {
-            glColorMask(false, false, false, false);
-            int i1 = renderGlobal.sortAndRender(mc.activeCamera, 1, partialTick);
-            glColorMask(true, true, true, true);
-            if(i1 > 0)
-            {
-                lightmapHelper.enableLightmapRendering();
-                if (!mc.gameSettings.fog.value)
-                {
-                    glDisable(GL_FOG);
-                }
-                renderGlobal.callAllDisplayLists(1, partialTick);
-                if (!mc.gameSettings.fog.value)
-                {
-                    glEnable(GL_FOG);
-                }
-                lightmapHelper.disableLightmapRendering();
-            }
-        }
-        else
-        {
-        	lightmapHelper.enableLightmapRendering();
-            renderGlobal.sortAndRender(mc.activeCamera, 1, partialTick);
-            lightmapHelper.disableLightmapRendering();
-        }
-        glShadeModel(GL_FLAT);
+		lightmapHelper.enableLightmapRendering();
+		if(mc.gameSettings.ambientOcclusion.value) {
+			glShadeModel(GL_SMOOTH);
+		}
+		if(!mc.gameSettings.fog.value) {
+			glDisable(GL_FOG);
+		}
+        mc.terrainRenderer.renderTranslucentTerrain(partialTicks);
+		if(!mc.gameSettings.fog.value) {
+			glEnable(GL_FOG);
+		}
+		glShadeModel(GL_FLAT);
+		lightmapHelper.disableLightmapRendering();
         
         Debug.change("weather");
         glDepthMask(true);
         glEnable(GL_CULL_FACE);
         glDisable(GL_BLEND);
-        renderRainSnow(partialTick);
+        renderRainSnow(partialTicks);
         glDisable(GL_FOG);
         if(mc.gameSettings.clouds.value) {
         	Debug.change("clouds");
-            fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTick);
+            fogManager.setupFog(FogManager.FOG_MODE_NORMAL, farPlaneDistance, partialTicks);
             glEnable(GL_FOG);
-            renderGlobal.renderClouds(partialTick);
+            renderGlobal.renderClouds(partialTicks);
             glDisable(GL_FOG);
         }
 
         Debug.change("misc");
-        fogManager.setupFog(1, farPlaneDistance, partialTick);
-        renderHand(partialTick);
+        fogManager.setupFog(1, farPlaneDistance, partialTicks);
+        renderHand(partialTicks);
         
         glColorMask(true, true, true, true);
         Debug.pop();
diff --git a/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java b/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
index 342f51d47..cadad76b5 100644
--- a/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
+++ b/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
@@ -361,10 +361,10 @@ public class BlockModelDispatcher
 
         addDispatch(new StandardBlockModel<>(Blocks.WORKBENCH).withTextures("minecraft:block/workbench_top", "minecraft:block/planks_oak", "minecraft:block/workbench_front", "minecraft:block/workbench_side", "minecraft:block/workbench_front", "minecraft:block/workbench_side"));
 
-        addDispatch(new HorizontalRotationBlockModel<>(Blocks.FURNACE_STONE_IDLE).withTextures("minecraft:block/furnace_stone_top", "minecraft:block/furnace_stone_top", "minecraft:block/furnace_stone_idle_front", "minecraft:block/furnace_stone_side", "minecraft:block/furnace_stone_side", "minecraft:block/furnace_stone_side"));
-        addDispatch(new HorizontalRotationBlockModel<>(Blocks.FURNACE_STONE_ACTIVE).withTextures("minecraft:block/furnace_stone_top", "minecraft:block/furnace_stone_top", "minecraft:block/furnace_stone_active_front", "minecraft:block/furnace_stone_side", "minecraft:block/furnace_stone_side", "minecraft:block/furnace_stone_side"));
-        addDispatch(new HorizontalRotationBlockModel<>(Blocks.FURNACE_BLAST_IDLE).withTextures("minecraft:block/furnace_blast_top", "minecraft:block/furnace_blast_bottom", "minecraft:block/furnace_blast_idle_front", "minecraft:block/furnace_blast_side", "minecraft:block/furnace_blast_side", "minecraft:block/furnace_blast_side"));
-        addDispatch(new HorizontalRotationBlockModel<>(Blocks.FURNACE_BLAST_ACTIVE).withTextures("minecraft:block/furnace_blast_top", "minecraft:block/furnace_blast_bottom", "minecraft:block/furnace_blast_active_front", "minecraft:block/furnace_blast_side", "minecraft:block/furnace_blast_side", "minecraft:block/furnace_blast_side"));
+        addDispatch(new FurnaceBlockModel<>(Blocks.FURNACE_STONE_IDLE).withTextures("minecraft:block/furnace_stone_top", "minecraft:block/furnace_stone_top", "minecraft:block/furnace_stone_idle_front", "minecraft:block/furnace_stone_side", "minecraft:block/furnace_stone_side", "minecraft:block/furnace_stone_side"));
+        addDispatch(new FurnaceBlockModel<>(Blocks.FURNACE_STONE_ACTIVE).withTextures("minecraft:block/furnace_stone_top", "minecraft:block/furnace_stone_top", "minecraft:block/furnace_stone_active_front", "minecraft:block/furnace_stone_side", "minecraft:block/furnace_stone_side", "minecraft:block/furnace_stone_side"));
+        addDispatch(new FurnaceBlockModel<>(Blocks.FURNACE_BLAST_IDLE).withTextures("minecraft:block/furnace_blast_top", "minecraft:block/furnace_blast_bottom", "minecraft:block/furnace_blast_idle_front", "minecraft:block/furnace_blast_side", "minecraft:block/furnace_blast_side", "minecraft:block/furnace_blast_side"));
+        addDispatch(new FurnaceBlockModel<>(Blocks.FURNACE_BLAST_ACTIVE).withTextures("minecraft:block/furnace_blast_top", "minecraft:block/furnace_blast_bottom", "minecraft:block/furnace_blast_active_front", "minecraft:block/furnace_blast_side", "minecraft:block/furnace_blast_side", "minecraft:block/furnace_blast_side"));
 
         addDispatch(new HorizontalRotationBlockModel<>(Blocks.TROMMEL_IDLE).withTextures("minecraft:block/trommel_top", "minecraft:block/trommel_top", "minecraft:block/trommel_idle_front", "minecraft:block/trommel_side_slot", "minecraft:block/trommel_idle_front", "minecraft:block/trommel_side"));
         addDispatch(new HorizontalRotationBlockModel<>(Blocks.TROMMEL_ACTIVE).withTextures("minecraft:block/trommel_top", "minecraft:block/trommel_top", "minecraft:block/trommel_active_front", "minecraft:block/trommel_side_slot", "minecraft:block/trommel_active_back", "minecraft:block/trommel_side"));
diff --git a/src/main/java/net/minecraft/client/render/block/model/FurnaceBlockModel.java b/src/main/java/net/minecraft/client/render/block/model/FurnaceBlockModel.java
new file mode 100644
index 000000000..0324d511e
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/block/model/FurnaceBlockModel.java
@@ -0,0 +1,37 @@
+package net.minecraft.client.render.block.model;
+
+import net.minecraft.client.render.texture.stitcher.IconCoordinate;
+import net.minecraft.client.render.texture.stitcher.TextureRegistry;
+import net.minecraft.core.block.Block;
+import net.minecraft.core.player.inventory.container.Container;
+import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.util.helper.Sides;
+import net.minecraft.core.world.WorldSource;
+
+public class FurnaceBlockModel<T extends Block>
+    extends HorizontalRotationBlockModel<T> {
+
+    public FurnaceBlockModel(Block block) {
+        super(block);
+    }
+
+    @Override
+    public IconCoordinate getBlockTexture(WorldSource blockAccess, int x, int y, int z, Side side) {
+        int data = blockAccess.getBlockMetadata(x, y, z);
+        int index = Sides.orientationLookUpHorizontal[6 * Math.min(data, 5) + side.getId()];
+        if (index >= Sides.orientationLookUpHorizontal.length) return atlasIndices[0];
+        if (index == Side.NORTH.getId()) {
+            IconCoordinate originalFront = atlasIndices[index];
+            Container container = (Container) blockAccess.getBlockEntity(x, y, z);
+            if (container != null) {
+                boolean hasOutput = container.getItem(2) != null;
+                if (hasOutput) {
+                    IconCoordinate newFront = TextureRegistry.getTexture(originalFront.namespaceId.namespace + ":block/" + originalFront.namespaceId.value + "_filled");
+                    return newFront;
+                }
+            }
+            return originalFront;
+        }
+        return atlasIndices[index];
+    }
+}
diff --git a/src/main/java/net/minecraft/client/render/colorizer/Colorizer.java b/src/main/java/net/minecraft/client/render/colorizer/Colorizer.java
index 70a2fa749..f5c469ca0 100644
--- a/src/main/java/net/minecraft/client/render/colorizer/Colorizer.java
+++ b/src/main/java/net/minecraft/client/render/colorizer/Colorizer.java
@@ -9,6 +9,8 @@ import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.WorldSource;
 import net.minecraft.core.world.season.Season;
 import net.minecraft.core.world.season.Seasons;
+import net.minecraft.core.world.type.WorldType;
+import net.minecraft.core.world.type.WorldTypes;
 
 public class Colorizer {
 	
@@ -29,12 +31,26 @@ public class Colorizer {
 		}
 		
 		String path = null;
+
+		// First, check for worldtype overrides
+		WorldType worldType = minecraft.theWorld.worldType;
+		if(worldType == WorldTypes.OVERWORLD_HELL) {
+			path = "/assets/minecraft/textures/colormap/" + this.name + "/hell.png";
+			texturePack = Colorizers.findTexturePackWithFile(minecraft, path);
+		}
+		if(worldType == WorldTypes.OVERWORLD_WINTER) {
+			path = "/assets/minecraft/textures/colormap/" + this.name + "/winter.png";
+			texturePack = Colorizers.findTexturePackWithFile(minecraft, path);
+		}
+		
+		if(texturePack == null) {
+			// Check if there is a colormap for the current dimension
+			Dimension dimension = minecraft.theWorld.dimension;
+			String dimensionName = dimension.languageKey.replace('.', '_').toLowerCase();
+			path = "/assets/minecraft/textures/colormap/" + this.name + "/" + dimensionName + ".png";
+			texturePack = Colorizers.findTexturePackWithFile(minecraft, path);	
+		}
 		
-		// First, check if there is a colormap for the current dimension
-		Dimension dimension = minecraft.theWorld.dimension;
-		String dimensionName = dimension.languageKey.replace('.', '_').toLowerCase();
-		path = "/assets/minecraft/textures/colormap/" + this.name + "/" + dimensionName + ".png";
-		texturePack = Colorizers.findTexturePackWithFile(minecraft, path);
 		
 		if(texturePack == null) {
 			// If there is no colormap for the currrent dimension, check if there is a default colormap, to be used for all dimensions
@@ -60,19 +76,15 @@ public class Colorizer {
 		if(season == null || season == Seasons.NULL) {
 			return defaultColor;
 		}
-//		System.out.println("Loading colormap "+this.name+": dimension: " + dimension.languageKey + " season: " + season.getId());
 		
 		String seasonName = season.getId().replace('.', '_').toLowerCase();
 		String seasonPath = "/assets/minecraft/textures/colormap/" + this.name + "/" + seasonName + ".png";
 		
 		ColorMap seasonColor = Colorizers.loadColorData(texturePack, seasonPath);
-//		System.out.println(seasonPath + ": " + seasonColor);
 		if(seasonColor != null) {
-//			System.out.println("  Using season colors!");
 			return seasonColor;
 		}
 		
-//		System.out.println("  Using default colors!");
 		return defaultColor;
 	}
 	
diff --git a/src/main/java/net/minecraft/client/render/item/model/StandardItemModel.java b/src/main/java/net/minecraft/client/render/item/model/StandardItemModel.java
index 56d46e723..ff5bf5749 100644
--- a/src/main/java/net/minecraft/client/render/item/model/StandardItemModel.java
+++ b/src/main/java/net/minecraft/client/render/item/model/StandardItemModel.java
@@ -210,6 +210,11 @@ public class StandardItemModel extends ItemModel{
         }  else {
             GL11.glColor4f(brightness, brightness, brightness, 1.0F);
         }
+        if(LightmapHelper.isLightmapEnabled() && itemfullBright && entity != null) {
+        	int lmc = entity.getLightmapCoord(1.0f);
+        	lmc = LightmapHelper.setBlocklightValue(lmc, 15);
+        	LightmapHelper.setLightmapCoord(lmc);
+        }
         if(mc.gameSettings.items3D.value) {
             glPushMatrix();
 
diff --git a/src/main/java/net/minecraft/client/render/terrain/ChunkRenderer.java b/src/main/java/net/minecraft/client/render/terrain/ChunkRenderer.java
new file mode 100644
index 000000000..2b2cfd8b1
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/ChunkRenderer.java
@@ -0,0 +1,194 @@
+package net.minecraft.client.render.terrain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.lwjgl.opengl.GL11;
+
+import net.minecraft.client.render.RenderBlocks;
+import net.minecraft.client.render.block.model.BlockModel;
+import net.minecraft.client.render.block.model.BlockModelDispatcher;
+import net.minecraft.client.render.camera.ICamera;
+import net.minecraft.client.render.culling.CameraFrustum;
+import net.minecraft.client.render.entity.ItemEntityRenderer;
+import net.minecraft.client.render.tessellator.Tessellator;
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.entity.BlockEntity;
+import net.minecraft.core.entity.Entity;
+import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.util.phys.AABB;
+import net.minecraft.core.world.World;
+
+public abstract class ChunkRenderer
+{
+    public static final int MAX_RENDER_PASSES = 2;
+    public static int updates = 0;
+    
+    public World world;
+    protected final int lists;
+    public int posX;
+    public int posY;
+    public int posZ;
+    public int sizeX;
+    public int sizeY;
+    public int sizeZ;
+    public int xRender;
+    public int yRender;
+    public int zRender;
+    public int xRenderOffs;
+    public int yRenderOffs;
+    public int zRenderOffs;
+    public boolean visible;
+    public boolean[] empty;
+    public int xm;
+    public int ym;
+    public int zm;
+    public float radius;
+    public boolean dirty;
+    public AABB bb;
+    public int id;
+    public boolean occlusionVisible;
+    public boolean occlusionQuerying;
+    public int occlusionId;
+    public boolean skyLit;
+    protected boolean compiled;
+    public List<BlockEntity> renderableBlockEntities;
+    protected final List<BlockEntity> globalRenderableBlockEntities;
+
+    public ChunkRenderer(World world, List<BlockEntity> globalRenderableTileEntities, int x, int y, int z, int size, int lists)
+    {
+        visible = false;
+        empty = new boolean[MAX_RENDER_PASSES];
+        occlusionVisible = true;
+        compiled = false;
+        renderableBlockEntities = new ArrayList<>();
+        this.world = world;
+        globalRenderableBlockEntities = globalRenderableTileEntities;
+        sizeX = sizeY = sizeZ = size;
+        radius = MathHelper.sqrt_float(sizeX * sizeX + sizeY * sizeY + sizeZ * sizeZ) / 2.0F;
+        this.lists = lists;
+        this.posX = -999;
+        setPos(x, y, z);
+        dirty = false;
+    }
+
+    public void setPos(int x, int y, int z)
+    {
+        if (x != this.posX || y != this.posY || z != this.posZ)
+        {
+            reset();
+            this.posX = x;
+            this.posY = y;
+            this.posZ = z;
+            xm = x + sizeX / 2;
+            ym = y + sizeY / 2;
+            zm = z + sizeZ / 2;
+            xRenderOffs = x & 0x3ff;
+            yRenderOffs = y;
+            zRenderOffs = z & 0x3ff;
+            xRender = x - xRenderOffs;
+            yRender = y - yRenderOffs;
+            zRender = z - zRenderOffs;
+            float f = 6F;
+            bb = AABB.getPermanentBB((float)x - f, (float)y - f, (float)z - f, (float)(x + sizeX) + f, (float)(y + sizeY) + f, (float)(z + sizeZ) + f);
+            GL11.glNewList(lists + MAX_RENDER_PASSES, GL11.GL_COMPILE);
+            ItemEntityRenderer.renderFlatAABB(AABB.getTemporaryBB((float) xRenderOffs - f, (float) yRenderOffs - f, (float) zRenderOffs - f, (float)(xRenderOffs + sizeX) + f, (float)(yRenderOffs + sizeY) + f, (float)(zRenderOffs + sizeZ) + f));
+            GL11.glEndList();
+            setDirty();
+        }
+    }
+    
+    public abstract void rebuild();
+    
+    public boolean renderBlock(Tessellator tessellator, RenderBlocks renderBlocks, Block block, int x, int y, int z) {
+    	boolean rendered = false;
+		final BlockModel<?> model = BlockModelDispatcher.getInstance().getDispatch(block);
+		rendered |= model.render(tessellator, x, y, z);
+		if(model.hasOverbright()) {
+			renderBlocks.overbright = true;
+			rendered |= model.render(tessellator, x, y, z);
+			renderBlocks.overbright = false;
+		}
+		return rendered;
+    }
+
+    protected void translateToPos()
+    {
+        GL11.glTranslatef(xRenderOffs, yRenderOffs, zRenderOffs);
+    }
+
+    public float distanceToEntitySquared(Entity entity)
+    {
+        float dx = (float)(entity.x - (double) xm);
+        float dy = (float)(entity.y - (double) ym);
+        float dz = (float)(entity.z - (double) zm);
+        return dx * dx + dy * dy + dz * dz;
+    }
+
+    public float distanceToCameraSquared(ICamera camera)
+    {
+        float dx = (float)(camera.getX(1f) - xm);
+        float dy = (float)(camera.getY(1f) - ym);
+        float dz = (float)(camera.getZ(1f) - zm);
+        return dx * dx + dy * dy + dz * dz;
+    }
+
+    public void reset()
+    {
+        for(int i = 0; i < MAX_RENDER_PASSES; i++)
+        {
+            empty[i] = true;
+        }
+
+        visible = false;
+        compiled = false;
+    }
+
+    public void delete()
+    {
+        reset();
+        world = null;
+    }
+
+    public int getList(int layer)
+    {
+        if(!visible)
+        {
+            return -1;
+        }
+        if(!empty[layer])
+        {
+            return lists + layer;
+        } 
+        else
+        {
+            return -1;
+        }
+    }
+
+    public void cull(CameraFrustum frustum, float partialTick)
+    {
+        visible = frustum.isVisible(bb, partialTick);
+    }
+
+    public void renderBB()
+    {
+        GL11.glCallList(lists + MAX_RENDER_PASSES);
+    }
+
+    public boolean isEmpty()
+    {
+        if(!compiled)
+        {
+            return false;
+        } else
+        {
+            return empty[0] && empty[1];
+        }
+    }
+
+    public void setDirty()
+    {
+        dirty = true;
+    }
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/ChunkRendererLegacy.java b/src/main/java/net/minecraft/client/render/terrain/ChunkRendererLegacy.java
new file mode 100644
index 000000000..f1f2fb8df
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/ChunkRendererLegacy.java
@@ -0,0 +1,132 @@
+package net.minecraft.client.render.terrain;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.lwjgl.opengl.GL11;
+
+import net.minecraft.client.render.BlockEntityRenderDispatcher;
+import net.minecraft.client.render.LightmapHelper;
+import net.minecraft.client.render.RenderBlocks;
+import net.minecraft.client.render.block.model.BlockModel;
+import net.minecraft.client.render.tessellator.Tessellator;
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.entity.BlockEntity;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.chunk.Chunk;
+import net.minecraft.core.world.chunk.ChunkCache;
+
+public class ChunkRendererLegacy extends ChunkRenderer {
+
+	public ChunkRendererLegacy(World world, List<BlockEntity> globalRenderableTileEntities, int x, int y, int z, int size, int lists) {
+		super(world, globalRenderableTileEntities, x, y, z, size, lists);
+	}
+
+	@Override
+    public void rebuild()
+    {
+        if(!dirty) { return; }
+        updates++;
+        final int minX = posX;
+        final int minY = posY;
+        final int minZ = posZ;
+        final int maxX = posX + sizeX;
+        final int maxY = posY + sizeY;
+        final int maxZ = posZ + sizeZ;
+        for(int i = 0; i < MAX_RENDER_PASSES; i++) { empty[i] = true; }
+
+        Chunk.isLit = false;
+        final Set<BlockEntity> lastSpecialTileEntities = new HashSet<>(renderableBlockEntities);
+        renderableBlockEntities.clear();
+        final int cacheRadius = 1;
+        final ChunkCache chunkcache = new ChunkCache(world, minX - cacheRadius, minY - cacheRadius, minZ - cacheRadius, maxX + cacheRadius, maxY + cacheRadius, maxZ + cacheRadius);
+        final RenderBlocks renderblocks = new RenderBlocks(chunkcache);
+        BlockModel.setRenderBlocks(renderblocks);
+        int renderPass = 0;
+        Tessellator tessellator = Tessellator.instance;
+        do
+        {
+            if(renderPass >= MAX_RENDER_PASSES) { break; }
+
+            boolean needsMoreRenderPasses = false;
+            boolean hasRenderedBlock = false;
+            boolean hasStartedDrawing = false;
+
+            for(int y = minY; y < maxY; y++)
+            {
+                for(int z = minZ; z < maxZ; z++)
+                {
+                    for(int x = minX; x < maxX; x++)
+                    {
+                        final int blockId = chunkcache.getBlockId(x, y, z);
+                        if(blockId <= 0) {
+                            continue;
+                        }
+                        if(!hasStartedDrawing) {
+                            hasStartedDrawing = true;
+                            GL11.glNewList(lists + renderPass, GL11.GL_COMPILE);
+                            GL11.glPushMatrix();
+                            translateToPos();
+                            final float scale = 1.000001F;
+                            GL11.glTranslatef((float)(-sizeZ) / 2.0F, (float)(-sizeY) / 2.0F, (float)(-sizeZ) / 2.0F);
+                            GL11.glScalef(scale, scale, scale);
+                            GL11.glTranslatef((float) sizeZ / 2.0F, (float) sizeY / 2.0F, (float) sizeZ / 2.0F);
+                            tessellator.startDrawingQuads();
+                            tessellator.setTranslation(-this.posX, -this.posY, -this.posZ);
+                            
+                            if(LightmapHelper.isLightmapEnabled()) {
+                            	// Make sure it's active
+                            	tessellator.setLightmapCoord(LightmapHelper.getLightmapCoord(15, 15));
+                            }
+                        }
+                        if(renderPass == 0 && Block.isEntityTile[blockId]) {
+                            final BlockEntity tileentity = chunkcache.getBlockEntity(x, y, z);
+                            if(BlockEntityRenderDispatcher.instance.hasRenderer(tileentity)) {
+                                renderableBlockEntities.add(tileentity);
+                            }
+                        }
+                        final Block block = Block.blocksList[blockId];
+                        final int blockRenderPass = block.getRenderLayer();
+                        if(blockRenderPass != renderPass) {
+                            needsMoreRenderPasses = true;
+                        }
+                        else {
+                        	if(LightmapHelper.isLightmapEnabled()) {
+                        		// Can probably be removed once all block models support the lightmap
+                        		tessellator.setLightmapCoord(LightmapHelper.getLightmapCoord(15, 0));
+                        	}
+
+    						hasRenderedBlock |= renderBlock(tessellator, renderblocks, block, x, y, z);
+                        }
+                    }
+                }
+            }
+            if(hasStartedDrawing) {
+                tessellator.draw();
+                GL11.glPopMatrix();
+                GL11.glEndList();
+                tessellator.setTranslation(0.0D, 0.0D, 0.0D);
+            } else {
+                hasRenderedBlock = false;
+            }
+            
+            if(hasRenderedBlock) {
+                empty[renderPass] = false;
+            }            
+            if(!needsMoreRenderPasses) {
+                break;
+            }
+            
+            renderPass++;
+        } while(true);
+        HashSet<BlockEntity> newSpecialTileEntities = new HashSet<>(renderableBlockEntities);
+        newSpecialTileEntities.removeAll(lastSpecialTileEntities);
+        globalRenderableBlockEntities.addAll(newSpecialTileEntities);
+        renderableBlockEntities.forEach(lastSpecialTileEntities::remove);
+        globalRenderableBlockEntities.removeAll(lastSpecialTileEntities);
+        skyLit = Chunk.isLit;
+        compiled = true;
+    }
+
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/ChunkRendererMultiDraw.java b/src/main/java/net/minecraft/client/render/terrain/ChunkRendererMultiDraw.java
new file mode 100644
index 000000000..6dad6b056
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/ChunkRendererMultiDraw.java
@@ -0,0 +1,158 @@
+package net.minecraft.client.render.terrain;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import net.minecraft.client.render.BlockEntityRenderDispatcher;
+import net.minecraft.client.render.LightmapHelper;
+import net.minecraft.client.render.RenderBlocks;
+import net.minecraft.client.render.block.model.BlockModel;
+import net.minecraft.client.render.culling.CameraFrustum;
+import net.minecraft.client.render.terrain.VertexBuffer.Entry;
+import net.minecraft.client.render.tessellator.Tessellator;
+import net.minecraft.client.render.tessellator.TessellatorStandard;
+import net.minecraft.core.block.Block;
+import net.minecraft.core.block.entity.BlockEntity;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.chunk.Chunk;
+import net.minecraft.core.world.chunk.ChunkCache;
+
+public class ChunkRendererMultiDraw extends ChunkRenderer {
+
+	public final TerrainRendererMultiDraw terrainRenderer;
+	public final Entry[] renderListEntries;
+	
+	public ChunkRendererMultiDraw(TerrainRendererMultiDraw terrainRenderer, World world, List<BlockEntity> globalRenderableTileEntities, int x, int y, int z, int size, int lists) {
+		super(world, globalRenderableTileEntities, x, y, z, size, lists);
+		
+		if(terrainRenderer == null) {
+			throw new NullPointerException("TerrainRenderer is null!");
+		}
+		
+		this.terrainRenderer = terrainRenderer;
+		this.renderListEntries = new Entry[terrainRenderer.MAX_RENDER_PASSES];
+	}
+	
+	@Override
+	public void rebuild() {
+		if(!dirty) {
+			return;
+		}
+		
+		updates++;
+		
+		final int minX = posX;
+		final int minY = posY;
+		final int minZ = posZ;
+		
+		final int maxX = posX + sizeX;
+		final int maxY = posY + sizeY;
+		final int maxZ = posZ + sizeZ;
+		
+		final int renderPassCount = terrainRenderer.MAX_RENDER_PASSES;
+		
+		terrainRenderer.chunkDeleted(this);
+		
+		Chunk.isLit = false;
+		final Set<BlockEntity> lastSpecialTileEntities = new HashSet<>(renderableBlockEntities);
+		renderableBlockEntities.clear();
+		
+		final int cacheRadius = 1;
+		final ChunkCache chunkCache = new ChunkCache(world, minX - cacheRadius, minY - cacheRadius, minZ - cacheRadius, maxX + cacheRadius, maxY + cacheRadius, maxZ + cacheRadius);
+		final RenderBlocks renderBlocks = new RenderBlocks(chunkCache);
+		BlockModel.setRenderBlocks(renderBlocks);
+		
+		TessellatorStandard tessellator = (TessellatorStandard) Tessellator.instance;
+		for(int renderPass = 0; renderPass < renderPassCount; renderPass++) {
+			boolean needsMoreRenderPasses = false;
+			boolean hasRenderedBlock = false;
+			boolean hasStartedDrawing = false;
+
+			for(int y = minY; y < maxY; y++) {
+				for(int z = minZ; z < maxZ; z++) {
+					for(int x = minX; x < maxX; x++) {
+						final int blockId = chunkCache.getBlockId(x, y, z);
+						if(blockId <= 0) {
+							continue;
+						}
+						if(!hasStartedDrawing) {
+							hasStartedDrawing = true;
+							tessellator.startDrawingQuads();
+//							tessellator.setTranslation(-this.posX, -this.posY, -this.posZ);
+							
+							RenderRegion region = terrainRenderer.getRenderRegionAtBlockPos(posX, posZ);
+							tessellator.setTranslation(-(region.posX << terrainRenderer.REGION_SIZE), 0.0, -(region.posZ << terrainRenderer.REGION_SIZE));
+							
+							if(LightmapHelper.isLightmapEnabled()) {
+								// Make sure it's active
+								tessellator.setLightmapCoord(LightmapHelper.getLightmapCoord(15, 15));
+							}
+							tessellator.setColorRGBA(255, 255, 255, 255);
+							tessellator.setTextureUV(0.0, 0.0);
+						}
+						if(renderPass == 0 && Block.isEntityTile[blockId]) {
+							final BlockEntity tileentity = chunkCache.getBlockEntity(x, y, z);
+							if(BlockEntityRenderDispatcher.instance.hasRenderer(tileentity)) {
+								renderableBlockEntities.add(tileentity);
+							}
+						}
+						final Block block = Block.blocksList[blockId];
+						final int blockRenderPass = block.getRenderLayer();
+						
+						if(blockRenderPass != renderPass) {
+							needsMoreRenderPasses = true;
+							continue;
+						}
+						
+						hasRenderedBlock |= renderBlock(tessellator, renderBlocks, block, x, y, z);
+					}
+				}
+			}
+			if(hasStartedDrawing) {
+				terrainRenderer.chunkRendered(this, tessellator, renderPass);
+				tessellator.setTranslation(0.0, 0.0, 0.0);
+			}else {
+				hasRenderedBlock = false;
+			}
+
+			if(hasRenderedBlock) {
+				empty[renderPass] = false;
+			}
+			if(!needsMoreRenderPasses) {
+				break;
+			}
+		}
+		
+		HashSet<BlockEntity> newSpecialTileEntities = new HashSet<>(renderableBlockEntities);
+		newSpecialTileEntities.removeAll(lastSpecialTileEntities);
+		globalRenderableBlockEntities.addAll(newSpecialTileEntities);
+		renderableBlockEntities.forEach(lastSpecialTileEntities::remove);
+		globalRenderableBlockEntities.removeAll(lastSpecialTileEntities);
+		skyLit = Chunk.isLit;
+		compiled = true;
+	}
+	
+	@Override
+	public void cull(CameraFrustum frustum, float partialTick) {
+		boolean wasVisible = this.visible;
+		super.cull(frustum, partialTick);
+		if(wasVisible != visible) {
+			terrainRenderer.chunkVisibilityChanged(this);
+		}
+	}
+	
+	@Override
+	public void reset() {
+		// TerrainRenderer is null during initialization
+		if(terrainRenderer != null) {
+			terrainRenderer.chunkDeleted(this);	
+		}
+		super.reset();
+	}
+	
+	@Override
+	public String toString() {
+		return "[" + posX + "," + posY + "," + posZ + "]";
+	}
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/RegionBuffer.java b/src/main/java/net/minecraft/client/render/terrain/RegionBuffer.java
new file mode 100644
index 000000000..3da683cfd
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/RegionBuffer.java
@@ -0,0 +1,67 @@
+package net.minecraft.client.render.terrain;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RegionBuffer {
+	
+	public final int regionSizeShift;
+	
+	private final Map<Integer, RenderRegion> regionMap = new HashMap<>();
+	private final List<RenderRegion> regionList = new ArrayList<>();
+	private final List<RenderRegion> immutableRegionList = Collections.unmodifiableList(regionList);
+	
+	public RegionBuffer(int regionSizeShift) {
+		this.regionSizeShift = regionSizeShift;
+	}
+	
+	public RenderRegion getRegionAtBlockPos(int blockX, int blockZ) {
+		int x = blockX >> regionSizeShift;
+		int z = blockZ >> regionSizeShift;
+		return getRegion(x, z);
+	}
+	
+	public RenderRegion getRegion(int x, int z) {
+		int index = getIndex(x, z);
+		
+		RenderRegion region = regionMap.get(index);
+		if(region == null) {
+			region = new RenderRegion(x, z);
+			regionMap.put(index, region);
+			regionList.add(region);
+		}else {
+			if(region.posX != x || region.posZ != z) {
+				throw new RuntimeException(region + " != " + new RenderRegion(x, z));
+			}	
+		}
+		return region;
+	}
+	
+	public void remove(RenderRegion region) {
+		int index = getIndex(region.posX, region.posZ);
+		
+		RenderRegion existingRegion = regionMap.get(index);
+		if(existingRegion != region) {
+			throw new RuntimeException(region + " != " + existingRegion);
+		}
+		
+		regionMap.remove(index);
+		regionList.remove(region);
+	}
+	
+	public int getIndex(int x, int z) {
+		return ((x & 0xFFFF) << 16) | (z & 0xFFFF);
+	}
+	
+	public List<RenderRegion> getAllRegions() {
+		return immutableRegionList;
+	}
+	
+	public int size() {
+		return regionMap.size();
+	}
+	
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/client/render/terrain/RenderList.java b/src/main/java/net/minecraft/client/render/terrain/RenderList.java
new file mode 100644
index 000000000..6e428881a
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/RenderList.java
@@ -0,0 +1,160 @@
+package net.minecraft.client.render.terrain;
+
+import static org.lwjgl.opengl.GL15.*;
+
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import net.minecraft.client.render.terrain.VertexBuffer.Entry;
+import net.minecraft.core.util.helper.Buffer;
+
+/**
+ * 		Can render a lot of objects with a single call
+ * <br> All objects need to have the same {@link VertexConfig}
+ * <br> Entries can be toggled visible or invisible quickly
+ */
+public class RenderList {
+	
+	public final VertexBuffer vertexBuffer;
+	
+	private VertexConfig vertexConfig;
+	private boolean vertexConfigLocked = false;
+	
+	private IntBuffer posBuffer;
+	private IntBuffer sizeBuffer;
+	
+	private List<Entry> entryList = new ArrayList<>();
+	private Set<Entry> visibleEntries = new HashSet<>();
+	
+	private boolean visibleEntriesUpdated = true;
+	
+	public RenderList(VertexBuffer vertexBuffer, int capacity) {
+		this.vertexBuffer = vertexBuffer;
+		this.posBuffer = Buffer.createBuffer(capacity * 4).asIntBuffer();
+		this.sizeBuffer = Buffer.createBuffer(capacity * 4).asIntBuffer();
+	}
+	
+	public void draw() {
+		if(entryList.size() == 0) {
+//			System.out.println("No Entries!");
+			return;
+		}
+		
+		if(visibleEntriesUpdated) {
+			int vertexSize = vertexConfig.getVertexSize();
+			
+			posBuffer = expandIfNecessary(posBuffer, visibleEntries.size());
+			sizeBuffer = expandIfNecessary(sizeBuffer, visibleEntries.size());
+			
+			posBuffer.clear();
+			sizeBuffer.clear();
+			
+			for(Entry entry : visibleEntries) {
+				posBuffer.put(entry.position / vertexSize);
+				sizeBuffer.put(entry.size / vertexSize);
+			}
+			
+			posBuffer.flip();
+			sizeBuffer.flip();
+			
+			visibleEntriesUpdated = false;
+		}
+		
+		if(visibleEntries.size() == 0) {
+//			System.out.println("No Visible Entries!");
+			return;
+		}
+		
+//		System.out.println("Rendering " + visibleEntries.size() + " entries");
+		glMultiDrawArrays(vertexConfig.drawMode, posBuffer, sizeBuffer);
+	}
+	
+	public Entry add(VertexData vertexData, boolean visible) {
+		if(vertexData.vertexCount <= 0) {
+			return null;
+		}
+		
+		if(entryList.size() == 0 && !vertexConfigLocked) {
+			this.vertexConfig = vertexData.config.copy();
+		}else {
+			VertexConfig.compareThrow(vertexConfig, vertexData.config);
+		}
+		
+		Entry entry = vertexBuffer.addVertexData(vertexData);
+		entryList.add(entry);
+		
+		if(visible) {
+			visibleEntries.add(entry);
+			visibleEntriesUpdated = true;
+		}
+		
+		return entry;
+	}
+	
+	public void setVisible(Entry entry, boolean visible) {
+		if(entry == null) {
+			throw new NullPointerException();
+		}
+		if(visible) {
+			if(visibleEntries.add(entry)) {
+				visibleEntriesUpdated = true;
+			}
+		}else {
+			if(visibleEntries.remove(entry)) {
+				visibleEntriesUpdated = true;
+			}
+		}
+	}
+	
+	public void lockVertexConfig(VertexConfig vertexConfig) {
+		if(vertexConfig == null) {
+			throw new NullPointerException("VertexConfig cannot be null!");
+		}
+		if(entryList.size() > 0 && !this.vertexConfig.equals(vertexConfig)) {
+			VertexConfig.compareThrow(this.vertexConfig, vertexConfig);
+		}
+		this.vertexConfig = vertexConfig.copy();
+		this.vertexConfigLocked = true;
+	}
+	
+	public boolean remove(Entry entry) {
+		if(entryList.remove(entry)) {
+			vertexBuffer.removeEntry(entry);
+			visibleEntries.remove(entry);
+			visibleEntriesUpdated = true;
+			return true;
+		}
+		return false;
+	}
+	
+	public int size() {
+		return entryList.size();
+	}
+	
+	public int visibleEntryCount() {
+		return visibleEntries.size();
+	}
+	
+	public VertexConfig getVertexConfig() {
+		return vertexConfig;
+	}
+	
+	public boolean isVertexConfigLocked() {
+		return vertexConfigLocked;
+	}
+	
+	public static IntBuffer expandIfNecessary(IntBuffer buffer, int requiredCapacity) {
+		if(buffer.capacity() < requiredCapacity) {
+			int newCapacity = Math.max(buffer.capacity() * 2, requiredCapacity);
+			
+			System.out.println("Expand RenderList IntBuffer " + buffer.capacity() + " -> " + newCapacity);
+			
+			return Buffer.createBuffer(newCapacity << 2).asIntBuffer();
+		}
+		return buffer;
+	}
+
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/RenderRegion.java b/src/main/java/net/minecraft/client/render/terrain/RenderRegion.java
new file mode 100644
index 000000000..5a1d2f619
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/RenderRegion.java
@@ -0,0 +1,30 @@
+package net.minecraft.client.render.terrain;
+
+public class RenderRegion {
+	
+	public final int posX;
+	public final int posZ;
+	
+	public RenderList[] renderLists = new RenderList[2];
+	
+	public RenderRegion(int posX, int posZ) {
+		this.posX = posX;
+		this.posZ = posZ;
+	}
+	
+	public boolean hasRenderData() {
+		for(int i=0; i < renderLists.length; i++) {
+			RenderList renderList = renderLists[i];
+			if(renderList != null && renderList.size() > 0) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		return "[" + posX + "," + posZ + "]";
+	}
+	
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/client/render/terrain/TerrainRenderPass.java b/src/main/java/net/minecraft/client/render/terrain/TerrainRenderPass.java
new file mode 100644
index 000000000..c9e75e2ac
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/TerrainRenderPass.java
@@ -0,0 +1,5 @@
+package net.minecraft.client.render.terrain;
+
+public class TerrainRenderPass {
+
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/TerrainRenderer.java b/src/main/java/net/minecraft/client/render/terrain/TerrainRenderer.java
new file mode 100644
index 000000000..19ca2f610
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/TerrainRenderer.java
@@ -0,0 +1,23 @@
+package net.minecraft.client.render.terrain;
+
+import java.util.List;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.core.block.entity.BlockEntity;
+import net.minecraft.core.world.World;
+
+public abstract class TerrainRenderer {
+	
+	public final Minecraft mc;
+	
+	public TerrainRenderer(Minecraft minecraft) {
+		this.mc = minecraft;
+	}
+	
+	public abstract void renderSolidTerrain(float partialTicks);
+	
+	public abstract void renderTranslucentTerrain(float partialTicks);
+	
+	public abstract ChunkRenderer createChunkRenderer(World world, List<BlockEntity> globalRenderableTileEntities, int x, int y, int z, int size, int lists);
+
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/TerrainRendererLegacy.java b/src/main/java/net/minecraft/client/render/terrain/TerrainRendererLegacy.java
new file mode 100644
index 000000000..b1dd01cd8
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/TerrainRendererLegacy.java
@@ -0,0 +1,44 @@
+package net.minecraft.client.render.terrain;
+
+import static org.lwjgl.opengl.GL11.*;
+
+import java.util.List;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.render.RenderGlobal;
+import net.minecraft.core.block.entity.BlockEntity;
+import net.minecraft.core.world.World;
+
+public class TerrainRendererLegacy extends TerrainRenderer {
+
+	public TerrainRendererLegacy(Minecraft minecraft) {
+		super(minecraft);
+	}
+
+	@Override
+	public void renderSolidTerrain(float partialTicks) {
+        mc.renderGlobal.sortAndRender(mc.activeCamera, 0, partialTicks);
+	}
+
+	@Override
+	public void renderTranslucentTerrain(float partialTicks) {
+		RenderGlobal renderGlobal = mc.renderGlobal;
+		if(mc.gameSettings.fancyGraphics.value == 0) {
+			renderGlobal.sortAndRender(mc.activeCamera, 1, partialTicks);
+			return;
+		}
+		
+		glColorMask(false, false, false, false);
+		int renderCount = renderGlobal.sortAndRender(mc.activeCamera, 1, partialTicks);
+		glColorMask(true, true, true, true);
+		
+		if(renderCount > 0) {
+			renderGlobal.callAllDisplayLists(1, partialTicks);
+		}
+	}
+
+	@Override
+	public ChunkRenderer createChunkRenderer(World world, List<BlockEntity> globalRenderableTileEntities, int x, int y, int z, int size, int lists) {
+		return new ChunkRendererLegacy(world, globalRenderableTileEntities, x, y, z, size, lists);
+	}
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/TerrainRendererMultiDraw.java b/src/main/java/net/minecraft/client/render/terrain/TerrainRendererMultiDraw.java
new file mode 100644
index 000000000..e7f0b6cfa
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/TerrainRendererMultiDraw.java
@@ -0,0 +1,269 @@
+package net.minecraft.client.render.terrain;
+
+import static org.lwjgl.opengl.GL11.*;
+
+import java.util.List;
+
+import org.lwjgl.opengl.GL11;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.render.camera.ICamera;
+import net.minecraft.client.render.terrain.VertexBuffer.Entry;
+import net.minecraft.client.render.tessellator.TessellatorStandard;
+import net.minecraft.core.block.entity.BlockEntity;
+import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.world.World;
+
+public class TerrainRendererMultiDraw extends TerrainRenderer {
+
+	public final int VERTEX_BUFFER_INITIAL_CAPACITY = 67108864;
+	
+	public final int RENDER_LIST_INITIAL_CAPACITY = 2048;
+	
+	/**
+	 * Exponential Region Size
+	 * Lower Region Size: More Draw Calls, reduced FPS
+	 * Higher Region Size: More Visual Artifacts
+	 */
+	public final int REGION_SIZE = 9;
+	
+	public final int MAX_RENDER_PASSES = 2;
+	
+	/**
+	 * For debugging purposes
+	 */
+	public final boolean DISABLE_REGION_OFFSET = false;
+	
+	public VertexBuffer vertexBuffer;
+	public RegionBuffer regionBuffer;
+	
+	private VertexConfig terrainVertexConfig;
+	
+	int[] drawCalls = new int[MAX_RENDER_PASSES];
+	
+	private double renderPosX;
+	private double renderPosY;
+	private double renderPosZ;
+
+	private double prevSortX;
+	private double prevSortY;
+	private double prevSortZ;
+	
+	public TerrainRendererMultiDraw(Minecraft minecraft) {
+		super(minecraft);
+		
+		vertexBuffer = new VertexBuffer(VERTEX_BUFFER_INITIAL_CAPACITY);
+		regionBuffer = new RegionBuffer(REGION_SIZE);
+	}
+	
+	@Override
+	public void renderSolidTerrain(float partialTicks) {
+		for(int i=0; i < drawCalls.length; i++) {
+			drawCalls[i] = 0;
+		}
+
+		ICamera camera = mc.activeCamera;
+		renderPosX = camera.getX(partialTicks);
+		renderPosY = camera.getY(partialTicks);
+		renderPosZ = camera.getZ(partialTicks);
+
+		double dX = renderPosX - prevSortX;
+		double dY = renderPosY - prevSortY;
+		double dZ = renderPosZ - prevSortZ;
+
+		if(dX * dX + dY * dY + dZ * dZ > 16.0) {
+			prevSortX = renderPosX;
+			prevSortY = renderPosY;
+			prevSortZ = renderPosZ;
+			
+			int x = MathHelper.floor(renderPosX);
+			int y = MathHelper.floor(renderPosY);
+			int z = MathHelper.floor(renderPosZ);
+			
+			mc.renderGlobal.markRenderersForNewPosition(x, y, z);
+		}
+		
+		if(terrainVertexConfig == null) {
+			return;
+		}
+		
+		glPushMatrix();
+		glTranslated(-renderPosX, -renderPosY, -renderPosZ);
+		
+		terrainVertexConfig.enableVBO(vertexBuffer.getVbo());
+		
+		List<RenderRegion> regions = regionBuffer.getAllRegions();
+		for(int i=0; i < regions.size(); i++) {
+			RenderRegion region = regions.get(i);
+			// TODO Clear empty regions, but not like this
+//			if(!region.hasRenderData()) {
+//				regionBuffer.remove(region);
+//				i--;
+//				continue;
+//			}
+
+			RenderList renderList = region.renderLists[0];
+			if(renderList != null && renderList.size() > 0) {
+				if(!DISABLE_REGION_OFFSET) {
+					glPushMatrix();
+					glTranslated((region.posX << REGION_SIZE), 0.0, (region.posZ << REGION_SIZE));
+				}
+				
+				renderList.draw();
+				drawCalls[0]++;
+				
+				if(!DISABLE_REGION_OFFSET) {
+					glPopMatrix();
+				}
+			}
+		}
+		
+		terrainVertexConfig.disable();
+		
+		glPopMatrix();
+		
+	}
+	
+	public void renderAllLists(int renderPass) {
+		List<RenderRegion> regions = regionBuffer.getAllRegions();
+		for(int i=0; i < regions.size(); i++) {
+			RenderRegion region = regions.get(i);
+			RenderList renderList = region.renderLists[renderPass];
+			if(renderList != null && renderList.size() > 0) {
+				if(!DISABLE_REGION_OFFSET) {
+					glPushMatrix();
+					glTranslated((region.posX << REGION_SIZE), 0.0, (region.posZ << REGION_SIZE));
+				}
+				
+				renderList.draw();
+				drawCalls[renderPass]++;
+				
+				if(!DISABLE_REGION_OFFSET) {
+					glPopMatrix();
+				}
+			}
+		}
+	}
+
+	@Override
+	public void renderTranslucentTerrain(float partialTicks) {
+		if(terrainVertexConfig == null) {
+			return;
+		}
+		
+		glPushMatrix();
+		glTranslated(-renderPosX, -renderPosY, -renderPosZ);
+		
+		terrainVertexConfig.enableVBO(vertexBuffer.getVbo());
+		
+		boolean fancy = mc.gameSettings.fancyGraphics.value != 0;
+		
+		if(fancy) {
+			GL11.glColorMask(false, false, false, false);
+		}
+		
+		renderAllLists(1);
+		
+		if(fancy) {
+			GL11.glColorMask(true, true, true, true);
+			if(drawCalls[1] > 0) {
+				renderAllLists(1);
+			}
+		}
+		
+		terrainVertexConfig.disable();
+		
+		glPopMatrix();
+	}
+	
+	public void chunkRendered(ChunkRendererMultiDraw chunkRenderer, TessellatorStandard tessellator, final int renderPass) {
+		tessellator.drawing = false;
+		VertexConfig config = tessellator.data.config;
+		
+		if(terrainVertexConfig == null) {
+			this.terrainVertexConfig = config.copy();
+		}else {
+			if(!this.terrainVertexConfig.equals(tessellator.data.config)) {
+				List<RenderRegion> allRegions = regionBuffer.getAllRegions();
+				for(int i=0; i < allRegions.size(); i++) {
+					RenderRegion region = allRegions.get(i);
+					if(region.hasRenderData()) {
+						VertexConfig.compareThrow(tessellator.data.config, terrainVertexConfig);
+					}
+					regionBuffer.remove(region);
+					i--;
+				}
+				
+				this.terrainVertexConfig = config.copy();
+			}
+		}
+		
+		RenderList renderList = getOrCreateRenderList(chunkRenderer, renderPass);
+		Entry entry = renderList.add(tessellator.data, chunkRenderer.visible);
+		chunkRenderer.renderListEntries[renderPass] = entry;
+	}
+	
+	public void chunkVisibilityChanged(ChunkRendererMultiDraw chunkRenderer) {
+		RenderRegion region = getRenderRegionAtBlockPos(chunkRenderer.posX, chunkRenderer.posZ);
+		
+		for(int renderPass=0; renderPass < MAX_RENDER_PASSES; renderPass++) {
+			Entry entry = chunkRenderer.renderListEntries[renderPass];
+			if(entry == null) {
+				continue;
+			}
+			RenderList renderList = region.renderLists[renderPass];
+			if(renderList == null) {
+				throw new NullPointerException("Region " + region + " does not have a render list for render pass " + renderPass + "!");
+			}
+			renderList.setVisible(entry, chunkRenderer.visible);
+		}
+	}
+	
+	public void chunkDeleted(ChunkRendererMultiDraw chunkRenderer) {
+		RenderRegion region = getRenderRegionAtBlockPos(chunkRenderer.posX, chunkRenderer.posZ);
+		for(int renderPass=0; renderPass < MAX_RENDER_PASSES; renderPass++) {
+			Entry entry = chunkRenderer.renderListEntries[renderPass];
+			if(entry == null) {
+				continue;
+			}
+			RenderList renderList = region.renderLists[renderPass];
+			if(renderList == null) {
+				throw new NullPointerException("Region " + region + " does not have a render list for render pass " + renderPass + "!");
+			}
+			renderList.remove(entry);
+			chunkRenderer.renderListEntries[renderPass] = null;
+		}
+	}
+	
+	public RenderList getOrCreateRenderList(ChunkRendererMultiDraw chunkRenderer, final int renderPass) {
+		return getOrCreateRenderList(chunkRenderer.posX, chunkRenderer.posZ, renderPass);
+	}
+	
+	public RenderList getOrCreateRenderList(int posX, int posZ, final int renderPass) {
+		RenderRegion region = regionBuffer.getRegionAtBlockPos(posX, posZ);
+		RenderList renderList = region.renderLists[renderPass];
+		if(renderList == null) {
+			renderList = new RenderList(vertexBuffer, RENDER_LIST_INITIAL_CAPACITY);
+			region.renderLists[renderPass] = renderList;
+		}
+		return renderList;
+	}
+
+	@Override
+	public ChunkRenderer createChunkRenderer(World world, List<BlockEntity> globalRenderableTileEntities, int x, int y, int z, int size, int lists) {
+		return new ChunkRendererMultiDraw(this, world, globalRenderableTileEntities, x, y, z, size, lists);
+	}
+	
+	public RenderRegion getRenderRegionAtBlockPos(int blockX, int blockZ) {
+		return regionBuffer.getRegionAtBlockPos(blockX, blockZ);
+	}
+	
+	public int getTotalDrawCalls() {
+		int totalDrawCalls = 0;
+		for(int i=0; i < drawCalls.length; i++) {
+			totalDrawCalls += drawCalls[i];
+		}
+		return totalDrawCalls;
+	}
+	
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/VertexAttribute.java b/src/main/java/net/minecraft/client/render/terrain/VertexAttribute.java
new file mode 100644
index 000000000..6ca70afde
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/VertexAttribute.java
@@ -0,0 +1,40 @@
+package net.minecraft.client.render.terrain;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Can be used by mods
+ */
+public abstract class VertexAttribute {
+	
+	public final String name;
+	public final int id;
+	public final int type;
+	
+	public VertexAttribute(String name, int id, int type) {
+		this.name = name;
+		this.id = id;
+		this.type = type;
+	}
+	
+	public abstract void addVertex(ByteBuffer buffer);
+	
+	public abstract int getSize();
+	
+	public abstract int getTypeSize();
+	
+	public static int compare(VertexAttribute a1, VertexAttribute a2) {
+		if(a1 == a2) {
+			return 0;
+		}
+		
+		if(!a1.name.equals(a2.name)) return 1;
+		if(a1.id != a2.id) return 2;
+		if(a1.type != a2.type) return 3;
+		if(a1.getSize() != a2.getSize()) return 4;
+		
+		return 0;
+	}
+	
+
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/VertexBuffer.java b/src/main/java/net/minecraft/client/render/terrain/VertexBuffer.java
new file mode 100644
index 000000000..655877eb9
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/VertexBuffer.java
@@ -0,0 +1,206 @@
+package net.minecraft.client.render.terrain;
+
+import static org.lwjgl.opengl.GL15.*;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.lwjgl.opengl.GL31;
+
+import net.minecraft.client.render.OpenGLHelper;
+
+/**
+ * 		A large buffer that contains vertex data
+ * <br> Elements can have different vertex configurations
+ * <br> When the buffer is full, it will automatically expand
+ */
+public class VertexBuffer {
+	
+	private int vbo;
+	private long capacity;
+	
+	public List<Entry> entries = new ArrayList<>();
+	
+	private int entryHashCounter;
+	
+	public VertexBuffer(long initialCapacity) {
+		this.capacity = initialCapacity;
+		
+		vbo = glGenBuffers();
+		glBindBuffer(GL_ARRAY_BUFFER, vbo);
+		glBufferData(GL_ARRAY_BUFFER, capacity, GL_STATIC_DRAW);
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+	}
+	
+	public Entry addVertexData(VertexData vertexData) {
+		if(vertexData.vertexCount <= 0) {
+			return null;
+		}
+		
+		// If the buffer is empty, just put it at the start
+		if(entries.size() == 0) {
+			return insertAtIndex(vertexData, 0);
+		}
+		
+		int bytes = vertexData.buffer.position();
+		
+		// If there is a single element in the buffer, check if there is enough space in front of it
+		// If not put it behind the other element
+		if(entries.size() == 1) {
+			Entry entry = entries.get(0);
+			if(entry.position > bytes) {
+				return insertAtIndex(vertexData, 0);
+			}else {
+				return insertAtIndex(vertexData, 1);
+			}
+		}
+		
+		// Check between every single element for a large enough gap
+		for(int i = 0; i < entries.size() - 1; i++) {
+			Entry entry = entries.get(i);
+			Entry nextEntry = entries.get(i + 1);
+			
+			int availableSpace = nextEntry.position - (entry.position + entry.size);
+			if(availableSpace > bytes) {
+				return insertAtIndex(vertexData, i + 1);
+			}
+		}
+		
+		// Put it all the way at the end
+		return insertAtIndex(vertexData, entries.size());
+	}
+	
+	private Entry insertAtIndex(VertexData vertexData, int listIndex) {
+		int pos = 0;
+		int bytes = vertexData.buffer.position();
+		
+		if(listIndex > 0) {
+			Entry prev = entries.get(listIndex - 1);
+			pos = prev.position + prev.size;
+		}
+		
+		if(pos + bytes >= capacity) {
+			expandVBO();
+		}
+		
+		Entry entry = new Entry(pos, bytes);
+		
+		if(listIndex > 0) {
+			Entry previousEntry = entries.get(listIndex - 1);
+			if(previousEntry.position + previousEntry.size > pos) {
+				throw new RuntimeException("Entry collision: " + entry + " and " + previousEntry);
+			}
+		}
+		if(listIndex < entries.size() - 1) {
+			Entry nextEntry = entries.get(listIndex + 1);
+			if(nextEntry.position <= pos + bytes) {
+				throw new RuntimeException("Entry collision: " + entry + " and " + nextEntry);
+			}
+		}
+		
+		vertexData.buffer.flip();
+		
+		glBindBuffer(GL_ARRAY_BUFFER, vbo);
+		glBufferSubData(GL_ARRAY_BUFFER, pos, vertexData.buffer);
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+		
+		entries.add(listIndex, entry);
+		
+		return entry;
+	}
+	
+	private void expandVBO() {
+		final long prevCapacity = this.capacity;
+		long newCapacity = this.capacity * 2L;
+		if(newCapacity < capacity) {
+			// future proof
+			throw new RuntimeException();
+		}
+		
+		System.out.println("Expand VBO Buffer: " + newCapacity);
+		
+		OpenGLHelper.checkError("pre expand buffer");
+		
+		int newBuffer = glGenBuffers();
+		glBindBuffer(GL_ARRAY_BUFFER, newBuffer);
+		glBufferData(GL_ARRAY_BUFFER, newCapacity, GL_STATIC_DRAW);
+		
+		OpenGLHelper.checkError("create expand buffer");
+		
+		if(OpenGLHelper.gl31) {
+			glBindBuffer(GL31.GL_COPY_READ_BUFFER, this.vbo);
+			glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, newBuffer);
+			
+			GL31.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0, 0, this.capacity);
+			
+			glBindBuffer(GL31.GL_COPY_READ_BUFFER, 0);
+			glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, 0);	
+		}else {
+			// confirmed to work on trash hardware
+			
+			int iPrevCapacity = (int) prevCapacity;
+			int iNewCapacity = (int) newCapacity;
+
+			if(iPrevCapacity < 0) throw new RuntimeException("Previous capacity < 0");
+			if(iNewCapacity < 0) throw new RuntimeException("New capacity < 0");
+			if(iNewCapacity < iPrevCapacity) throw new RuntimeException("New capacity < previous capacity: " + iNewCapacity + " < " + iPrevCapacity);
+			
+			ByteBuffer oldData = ByteBuffer.allocateDirect(iPrevCapacity).order(ByteOrder.nativeOrder());
+			
+			glBindBuffer(GL_ARRAY_BUFFER, this.vbo);
+			glGetBufferSubData(GL_ARRAY_BUFFER, 0L, oldData);
+			
+			oldData.position(0);
+			
+			glBindBuffer(GL_ARRAY_BUFFER, newBuffer);
+			glBufferSubData(GL_ARRAY_BUFFER, 0L, oldData);
+		}
+		
+		OpenGLHelper.checkError("copy buffer");
+		
+		this.capacity = newCapacity;
+		this.vbo = newBuffer;
+	}
+	
+	public boolean removeEntry(Entry entry) {
+		return entries.remove(entry);
+	}
+	
+	public int getVbo() {
+		return vbo;
+	}
+	
+	public int entryCount() {
+		return entries.size();
+	}
+	
+	/**
+	 * A reference to some vertex data that has been stored in this buffer
+	 */
+	public class Entry {
+		
+		public final int position;
+		public final int size;
+		
+		private final int hashCode = entryHashCounter++;
+		
+		public Entry(int position, int size) {
+			this.position = position;
+			this.size = size;
+		}
+		
+		@Override
+		public String toString() {
+			return "VertexBufferEntry[position: " + position + ", size: " + size + "]";
+		}
+		
+		@Override
+		public int hashCode() {
+			return hashCode;
+		}
+		
+	}
+
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/VertexConfig.java b/src/main/java/net/minecraft/client/render/terrain/VertexConfig.java
new file mode 100644
index 000000000..71a5668a5
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/VertexConfig.java
@@ -0,0 +1,244 @@
+package net.minecraft.client.render.terrain;
+
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL13.*;
+import static org.lwjgl.opengl.GL20.*;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.b100.json.element.JsonObject;
+
+public class VertexConfig {
+	
+	public int drawMode;
+	public boolean enableColor = false;
+	public boolean enableTexture = false;
+	public boolean enableNormal = false;
+	public boolean enableLightmap = false;
+	public final List<VertexAttribute> attributes = new ArrayList<>();
+	
+	public void enableDirect(ByteBuffer buffer) {
+		int vertexSize = getVertexSize();
+		int offset = 0;
+		
+		buffer.position(offset);
+		glEnableClientState(GL_VERTEX_ARRAY);
+		glVertexPointer(3, GL_FLOAT, vertexSize, buffer);
+		offset += 12;
+		
+		if(enableColor) {
+			buffer.position(offset);
+			glColorPointer(4, GL_UNSIGNED_BYTE, vertexSize, buffer);
+			glEnableClientState(GL_COLOR_ARRAY);
+			offset += 4;
+		}else {
+			glDisableClientState(GL_COLOR_ARRAY);
+		}
+		
+		if(enableTexture) {
+			buffer.position(offset);
+			glTexCoordPointer(2, GL_FLOAT, vertexSize, buffer);
+			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+			offset += 8;
+		}else {
+			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+		}
+		
+		if(enableNormal) {
+			buffer.position(offset);
+			glNormalPointer(GL_BYTE, vertexSize, buffer);
+			glEnableClientState(GL_NORMAL_ARRAY);
+			offset += 3;
+		}else {
+			glDisableClientState(GL_NORMAL_ARRAY);
+		}
+		
+		glClientActiveTexture(GL_TEXTURE1);
+		if(enableLightmap) {
+			buffer.position(offset);
+			glTexCoordPointer(2, GL_SHORT, vertexSize, buffer);
+			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+			offset += 4;
+		}else {
+			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+		}
+		glClientActiveTexture(GL_TEXTURE0);
+		
+		for(int i=0; i < attributes.size(); i++) {
+			VertexAttribute attrib = attributes.get(i);
+			
+			buffer.position(offset);
+			glEnableVertexAttribArray(attrib.id);
+			glVertexAttribPointer(attrib.id, attrib.getSize(), attrib.type, false, vertexSize, buffer);
+			offset += attrib.getTypeSize();
+		}
+	}
+	
+	public void enableVBO(int vbo) {
+		glBindBuffer(GL_ARRAY_BUFFER, vbo);
+		
+		int vertexSize = getVertexSize();
+		int offset = 0;
+		
+		glEnableClientState(GL_VERTEX_ARRAY);
+		glVertexPointer(3, GL_FLOAT, vertexSize, offset);
+		offset += 12;
+		
+		if(enableColor) {
+			glColorPointer(4, GL_UNSIGNED_BYTE, vertexSize, offset);
+			glEnableClientState(GL_COLOR_ARRAY);
+			offset += 4;
+		}else {
+			glDisableClientState(GL_COLOR_ARRAY);
+		}
+		
+		if(enableTexture) {
+			glTexCoordPointer(2, GL_FLOAT, vertexSize, offset);
+			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+			offset += 8;
+		}else {
+			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+		}
+		
+		if(enableNormal) {
+			glNormalPointer(GL_BYTE, vertexSize, offset);
+			glEnableClientState(GL_NORMAL_ARRAY);
+			offset += 3;
+		}else {
+			glDisableClientState(GL_NORMAL_ARRAY);
+		}
+		
+		if(enableLightmap) {
+			glClientActiveTexture(GL_TEXTURE1);
+			glTexCoordPointer(2, GL_SHORT, vertexSize, offset);
+			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+			offset += 4;
+			glClientActiveTexture(GL_TEXTURE0);
+		}
+		
+		for(int i=0; i < attributes.size(); i++) {
+			VertexAttribute attrib = attributes.get(i);
+			
+			glEnableVertexAttribArray(attrib.id);
+			glVertexAttribPointer(attrib.id, attrib.getSize(), attrib.type, false, vertexSize, offset);
+			offset += attrib.getTypeSize();
+		}
+	}
+	
+	public void disable() {
+		if(enableLightmap) {
+			glClientActiveTexture(GL_TEXTURE1);
+			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+			glClientActiveTexture(GL_TEXTURE0);
+		}
+		
+		glBindBuffer(GL_ARRAY_BUFFER, 0);
+		for(int i=0; i < attributes.size(); i++) {
+			VertexAttribute attrib = attributes.get(i);
+			glDisableVertexAttribArray(attrib.id);
+		}
+	}
+	
+	public int getVertexSize() {
+		int size = 12;
+		
+		if(enableColor) size += 4;
+		if(enableTexture) size += 8;
+		if(enableNormal) size += 3;
+		if(enableLightmap) size += 4;
+		
+		for(int i=0; i < attributes.size(); i++) {
+			size += attributes.get(i).getTypeSize();
+		}
+		
+		return size;
+	}
+	
+	public VertexConfig copy() {
+		VertexConfig copy = new VertexConfig();
+		copy.drawMode = drawMode;
+		copy.enableColor = enableColor;
+		copy.enableTexture = enableTexture;
+		copy.enableNormal = enableNormal;
+		copy.enableLightmap = enableLightmap;
+		copy.attributes.addAll(attributes);
+		return copy;
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if(obj instanceof VertexConfig) {
+			return compare(this, (VertexConfig) obj) == 0;
+		}
+		return false;
+	}
+	
+	/**
+	 * Only used for debugging
+	 */
+	public JsonObject toJson() {
+		JsonObject object = new JsonObject();
+		object.set("drawMode", drawMode);
+		object.set("enableColor", enableColor);
+		object.set("enableTexcoord", enableTexture);
+		object.set("enableNormal", enableNormal);
+		object.set("enableLightmap", enableLightmap);
+		if(attributes.size() > 0) {
+			JsonObject attribsJson = new JsonObject();
+			
+			attribsJson.set("size", attributes.size());
+			
+			for(int i=0; i < attributes.size(); i++) {
+				VertexAttribute attribute = attributes.get(i);
+				JsonObject attribObject = new JsonObject();
+				
+				attribObject.set("id", attribute.id);
+				attribObject.set("type", attribute.type);
+				
+				attribsJson.set(attribute.name, attribObject);
+			}
+			
+			object.set("attribs", attribsJson);
+		}
+		return object;
+	}
+	
+	public static int compare(VertexConfig c1, VertexConfig c2) {
+		if(c1.drawMode != c2.drawMode) return 1;
+		if(c1.enableColor != c2.enableColor) return 2;
+		if(c1.enableTexture != c2.enableTexture) return 3;
+		if(c1.enableNormal != c2.enableNormal) return 4;
+		if(c1.enableLightmap != c2.enableLightmap) return 4;
+		
+		if(c1.attributes.size() != c2.attributes.size()) {
+			return 5;
+		}
+		
+		for(int i=0; i < c1.attributes.size(); i++) {
+			VertexAttribute attrib1 = c1.attributes.get(i);
+			VertexAttribute attrib2 = c2.attributes.get(i);
+			int compare = VertexAttribute.compare(attrib1, attrib2); 
+			if(compare != 0) {
+				return (6 + i) * 1000 + compare;
+			}
+		}
+		
+		return 0;
+	}
+	
+	public static void compareThrow(VertexConfig config1, VertexConfig config2) {
+		int compare = compare(config1, config2);
+		if(compare != 0) {
+			System.err.println("VertexConfig Mismatch:\n" + config1 + "\n!=\n" + config2);
+			throw new RuntimeException("VertexConfig mismatch " + compare + "!");
+		}	
+	}
+	
+	@Override
+	public String toString() {
+		return toJson().toString();
+	}
+
+}
diff --git a/src/main/java/net/minecraft/client/render/terrain/VertexData.java b/src/main/java/net/minecraft/client/render/terrain/VertexData.java
new file mode 100644
index 000000000..124c03505
--- /dev/null
+++ b/src/main/java/net/minecraft/client/render/terrain/VertexData.java
@@ -0,0 +1,36 @@
+package net.minecraft.client.render.terrain;
+
+import static org.lwjgl.opengl.GL11.*;
+
+import java.nio.ByteBuffer;
+
+import net.minecraft.core.util.helper.Buffer;
+
+public class VertexData {
+	
+	public ByteBuffer buffer;
+	
+	public VertexConfig config = new VertexConfig();
+	
+	public int vertexCount;
+	
+	public VertexData(int size) {
+		buffer = Buffer.createBuffer(size);
+	}
+	
+	public void drawAll() {
+		draw(0, vertexCount);
+	}
+	
+	public void draw(int first, int count) {
+		if(count == 0) {
+			return;
+		}
+		
+		buffer.limit(config.getVertexSize() * vertexCount);
+		config.enableDirect(buffer);
+		glDrawArrays(config.drawMode, first, count);
+		config.disable();
+	}
+
+}
diff --git a/src/main/java/net/minecraft/client/render/tessellator/TessellatorStandard.java b/src/main/java/net/minecraft/client/render/tessellator/TessellatorStandard.java
index 1110cca62..79b4fe822 100644
--- a/src/main/java/net/minecraft/client/render/tessellator/TessellatorStandard.java
+++ b/src/main/java/net/minecraft/client/render/tessellator/TessellatorStandard.java
@@ -1,27 +1,16 @@
 package net.minecraft.client.render.tessellator;
 
-import static org.lwjgl.opengl.GL11.*;
-import static org.lwjgl.opengl.GL13.*;
-
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
+import net.minecraft.client.render.terrain.VertexConfig;
+import net.minecraft.client.render.terrain.VertexData;
 import net.minecraft.core.util.helper.MathHelper;
-import org.lwjgl.opengl.GL11;
 
 public class TessellatorStandard extends TessellatorBase {
 
-	private int drawMode;
-	private boolean drawing;
-	
-	private ByteBuffer buffer;
-	
-	private boolean hasColor;
-	private boolean hasTexture;
-	private boolean hasLightmap;
-	private boolean hasNormal;
-	
-	private int vertexCount = 0;
+	public VertexData data;
+	public boolean drawing;
 	
 	private int color;
 	
@@ -41,7 +30,7 @@ public class TessellatorStandard extends TessellatorBase {
 	private boolean lockedColor = false;
 	
 	public TessellatorStandard(int bufferSize) {
-		this.buffer = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder());
+		data = new VertexData(bufferSize);
 	}
 	
 	@Override
@@ -50,18 +39,20 @@ public class TessellatorStandard extends TessellatorBase {
 			throw new IllegalStateException("Already drawing!");
 		}
 		
-		this.drawMode = drawMode;
+		VertexConfig config = data.config;
+		
+		config.drawMode = drawMode;
 		this.drawing = true;
-		this.vertexCount = 0;
+		data.vertexCount = 0;
 		
-		this.hasColor = false;
-		this.hasTexture = false;
-		this.hasLightmap = false;
-		this.hasNormal = false;
+		config.enableColor = false;
+		config.enableTexture = false;
+		config.enableLightmap = false;
+		config.enableNormal = false;
 
 		this.lockedColor = false;
 		
-		this.buffer.clear();
+		data.buffer.clear();
 	}
 
 	@Override
@@ -69,115 +60,68 @@ public class TessellatorStandard extends TessellatorBase {
 		checkIsDrawing();
 		this.drawing = false;
 		
-		if(vertexCount == 0) {
+		if(data.vertexCount == 0) {
 			return;
 		}
 		
-		int vertexSize = getVertexSize();
-		int offset = 0;
-		
-		buffer.flip();
-		
-		glEnableClientState(GL_VERTEX_ARRAY);
-		buffer.position(0);
-		glVertexPointer(3, GL_FLOAT, vertexSize, buffer);
-		offset += 12;
-
-		if(hasColor) {
-			glEnableClientState(GL_COLOR_ARRAY);
-			buffer.position(offset);
-			glColorPointer(4, GL_UNSIGNED_BYTE, vertexSize, buffer);
-			offset += 4;
-		}else {
-			glDisableClientState(GL_COLOR_ARRAY);
-		}
-		if(hasTexture) {
-			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-			buffer.position(offset);
-			glTexCoordPointer(2, GL_FLOAT, vertexSize, buffer);
-			offset += 8;
-		}else {
-			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-		}
-		if(hasLightmap) {
-			glClientActiveTexture(GL_TEXTURE1);
-			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-			buffer.position(offset);
-			glTexCoordPointer(2, GL_SHORT, vertexSize, buffer);
-			glClientActiveTexture(GL_TEXTURE0);
-			offset += 4;
-		}else {
-			glClientActiveTexture(GL_TEXTURE1);
-			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-			glClientActiveTexture(GL_TEXTURE0);
-		}
-		if(hasNormal) {
-			glEnableClientState(GL_NORMAL_ARRAY);
-			buffer.position(offset);
-			glNormalPointer(GL_BYTE, vertexSize, buffer);
-			offset += 3;
-		}else {
-			glDisableClientState(GL_NORMAL_ARRAY);
-		}
-		
-		glDrawArrays(drawMode, 0, vertexCount);
+		data.buffer.flip();
+		data.drawAll();
 	}
 	
 	public int getVertexSize() {
-		int vertexSize = 12;
-		if(hasColor) vertexSize += 4;
-		if(hasTexture) vertexSize += 8;
-		if(hasLightmap) vertexSize += 4;
-		if(hasNormal) vertexSize += 3;
-		return vertexSize;
+		return data.config.getVertexSize();
 	}
 
 	@Override
 	public void addVertex(double x, double y, double z) {
 		checkIsDrawing();
 
-		if(buffer.capacity() < buffer.position() + 64) {
-			int newSize = buffer.capacity() * 2;
+		if(data.buffer.capacity() < data.buffer.position() + 64) {
+			int newSize = data.buffer.capacity() * 2;
 			
-			System.out.println("Expanding Tessellator Buffer (" + buffer.capacity() + " -> " + newSize + ")");
+			System.out.println("Expanding Tessellator Buffer (" + data.buffer.capacity() + " -> " + newSize + ")");
 			
 			ByteBuffer newBuffer = ByteBuffer.allocateDirect(newSize).order(ByteOrder.nativeOrder());
-			this.buffer.flip();
-			newBuffer.put(buffer);
-			this.buffer = newBuffer;
+			data.buffer.flip();
+			newBuffer.put(data.buffer);
+			data.buffer = newBuffer;
 		}
 		
+		ByteBuffer buffer = data.buffer;
+		
 		buffer.putFloat((float) (offsetX + x));
 		buffer.putFloat((float) (offsetY + y));
 		buffer.putFloat((float) (offsetZ + z));
 
-		if(hasColor) {
+		VertexConfig config = data.config;
+		
+		if(config.enableColor) {
 			buffer.putInt(color);
 		}
-		if(hasTexture) {
+		if(config.enableTexture) {
 			buffer.putFloat((float) textureU);
 			buffer.putFloat((float) textureV);
 		}
-		if(hasLightmap) {
+		if(config.enableLightmap) {
 			buffer.putInt(lightmapCoord);
 		}
-		if(hasNormal) {
+		if(config.enableNormal) {
 			buffer.put(normalX);
 			buffer.put(normalY);
 			buffer.put(normalZ);
 		}
 		
-		this.vertexCount++;
+		data.vertexCount++;
 	}
 
 	@Override
 	public void setTextureUV(double u, double v) {
 		checkIsDrawing();
-		if(!hasTexture) {
-			if(vertexCount > 0) {
+		if(!data.config.enableTexture) {
+			if(data.vertexCount > 0) {
 				throw new IllegalStateException("Texture is disabled!");
 			}
-			this.hasTexture = true;
+			data.config.enableTexture = true;
 		}
 		
 		this.textureU = u;
@@ -187,11 +131,11 @@ public class TessellatorStandard extends TessellatorBase {
 	@Override
 	public void setLightmapCoord(int lightmapCoord) {
 		checkIsDrawing();
-		if(!hasLightmap) {
-			if(vertexCount > 0) {
+		if(!data.config.enableLightmap) {
+			if(data.vertexCount > 0) {
 				throw new IllegalStateException("Lightmap is disabled!");
 			}
-			this.hasLightmap = true;
+			data.config.enableLightmap = true;
 		}
 		
 		this.lightmapCoord = lightmapCoord;
@@ -203,11 +147,11 @@ public class TessellatorStandard extends TessellatorBase {
 		if(lockedColor) {
 			return;
 		}
-		if(!hasColor) {
-			if(vertexCount > 0) {
+		if(!data.config.enableColor) {
+			if(data.vertexCount > 0) {
 				throw new IllegalStateException("Color is disabled!");
 			}
-			this.hasColor = true;
+			data.config.enableColor = true;
 		}
 		
 		r = MathHelper.clamp(r, 0, 255);
@@ -221,11 +165,11 @@ public class TessellatorStandard extends TessellatorBase {
 	@Override
 	public void setNormal(float x, float y, float z) {
 		checkIsDrawing();
-		if(!hasNormal) {
-			if(vertexCount > 0) {
+		if(!data.config.enableNormal) {
+			if(data.vertexCount > 0) {
 				throw new IllegalStateException("Texture is disabled!");
 			}
-			this.hasNormal = true;
+			data.config.enableNormal = true;
 		}
 		
 		this.normalX = (byte) (x * 127.0f);
diff --git a/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java b/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
index 46eb62b1a..384e4e205 100644
--- a/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
+++ b/src/main/java/net/minecraft/client/render/window/GameWindowGLFW.java
@@ -200,7 +200,15 @@ public class GameWindowGLFW implements GameWindow {
 
 	@Override
 	public int getFrequency() {
-		return glfwGetVideoMode(window).refreshRate();
+		GLFWVidMode vidMode = glfwGetVideoMode(window);
+		if (vidMode != null) {
+			return vidMode.refreshRate();
+		} else {
+			int x = windowPosX + windowWidth / 2;
+			int y = windowPosY + windowHeight / 2;
+			MonitorInfo monitor = getMonitorAt(x, y);
+			return monitor.refreshRate;
+		}
 	}
 	
 	@Override
@@ -251,7 +259,7 @@ public class GameWindowGLFW implements GameWindow {
 		}
 	}
 	
-	class MonitorInfo {
+	public static class MonitorInfo {
 		
 		public final long handle;
 		public final int posX;
diff --git a/src/main/java/net/minecraft/client/world/chunk/provider/ChunkProviderDynamic.java b/src/main/java/net/minecraft/client/world/chunk/provider/ChunkProviderDynamic.java
new file mode 100644
index 000000000..20c98a49d
--- /dev/null
+++ b/src/main/java/net/minecraft/client/world/chunk/provider/ChunkProviderDynamic.java
@@ -0,0 +1,302 @@
+package net.minecraft.client.world.chunk.provider;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.core.world.ProgressListener;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.chunk.*;
+import net.minecraft.core.world.chunk.provider.IChunkProvider;
+import net.minecraft.core.world.generate.chunk.ChunkGenerator;
+
+import java.io.IOException;
+import java.util.*;
+
+public class ChunkProviderDynamic implements IChunkProvider
+{
+    private final Set<Integer> droppedChunksSet;
+    private final Chunk emptyChunk;
+    public ChunkGenerator chunkGenerator;
+    private final IChunkLoader chunkLoader;
+    private final Map<Integer, Chunk> chunkMap;
+    private final Map<Integer, Chunk> forceLoadedChunkMap;
+    private final List<Chunk> forceLoadedChunkList;
+    private final World world;
+    private int currentChunkX;
+    private int currentChunkZ;
+    public int forceLoadedChunksLimit = 64;
+    public int maxUnloadPerTick = 10;
+    private int lastQueriedChunkXPos;
+    private int lastQueriedChunkZPos;
+    private Chunk lastQueriedChunk;
+
+    public ChunkProviderDynamic(World world, IChunkLoader chunkLoader, ChunkGenerator chunkGenerator)
+    {
+        droppedChunksSet = new HashSet<>();
+        chunkMap = new HashMap<>();
+        forceLoadedChunkList = new ArrayList<>();
+        forceLoadedChunkMap = new HashMap<>();
+        emptyChunk = new EmptyChunk(world, 0, 0);
+        this.world = world;
+        this.chunkLoader = chunkLoader;
+        this.chunkGenerator = chunkGenerator;
+    }
+
+    @Override
+    public boolean isChunkLoaded(int chunkX, int chunkZ) {
+        int id = ChunkCoordinate.toInt(chunkX, chunkZ);
+        if(canChunkBeUnloaded(chunkX, chunkZ)) {
+            if(chunkMap.containsKey(id)) droppedChunksSet.add(id);
+            return false;
+        }
+        Chunk chunk = chunkMap.get(id);
+        return chunk == emptyChunk || (chunk != null && chunk.isAtLocation(chunkX, chunkZ));
+    }
+
+    public boolean canChunkBeUnloaded(int chunkX, int chunkZ) {
+        int id = ChunkCoordinate.toInt(chunkX, chunkZ);
+        if(forceLoadedChunkMap.containsKey(id)) return false;
+        int renderDistance = Minecraft.getMinecraft(this).gameSettings.renderDistance.value.chunks;
+        return chunkX <= currentChunkX - renderDistance || chunkZ <= currentChunkZ - renderDistance || chunkX >= currentChunkX + renderDistance || chunkZ >= currentChunkZ + renderDistance;
+    }
+
+    @Override
+    public Chunk provideChunk(int chunkX, int chunkZ) {
+        if(chunkX == lastQueriedChunkXPos && chunkZ == lastQueriedChunkZPos && lastQueriedChunk != null)
+        {
+            return lastQueriedChunk;
+        }
+        if(!world.findingSpawnPoint && canChunkBeUnloaded(chunkX, chunkZ)) return emptyChunk;
+        int id = ChunkCoordinate.toInt(chunkX, chunkZ);
+        if(!isChunkLoaded(chunkX, chunkZ)) {
+            if(chunkMap.get(id) != null){
+                Chunk chunk = chunkMap.get(id);
+                saveChunk(chunk);
+                chunk.onUnload();
+            }
+            Chunk chunk = loadChunk(chunkX, chunkZ);
+            if(chunk == null)
+            {
+                if (chunkGenerator != null)
+                {
+                    chunk = chunkGenerator.generate(chunkX, chunkZ);
+                    chunk.fixMissingBlocks();
+                }
+                else
+                {
+                    chunk = emptyChunk;
+                }
+            }
+            chunkMap.put(id, chunk);
+            chunk.onLoad();
+            if(chunk.isTerrainPopulated && isChunkLoaded(chunkX + 1, chunkZ + 1) && isChunkLoaded(chunkX, chunkZ + 1) && isChunkLoaded(chunkX + 1, chunkZ))
+            {
+                populate(this, chunkX, chunkZ);
+            }
+            if(isChunkLoaded(chunkX - 1, chunkZ) && !provideChunk(chunkX - 1, chunkZ).isTerrainPopulated && isChunkLoaded(chunkX - 1, chunkZ + 1) && isChunkLoaded(chunkX, chunkZ + 1) && isChunkLoaded(chunkX - 1, chunkZ))
+            {
+                populate(this, chunkX - 1, chunkZ);
+            }
+            if(isChunkLoaded(chunkX, chunkZ - 1) && !provideChunk(chunkX, chunkZ - 1).isTerrainPopulated && isChunkLoaded(chunkX + 1, chunkZ - 1) && isChunkLoaded(chunkX, chunkZ - 1) && isChunkLoaded(chunkX + 1, chunkZ))
+            {
+                populate(this, chunkX, chunkZ - 1);
+            }
+            if(isChunkLoaded(chunkX - 1, chunkZ - 1) && !provideChunk(chunkX - 1, chunkZ - 1).isTerrainPopulated && isChunkLoaded(chunkX - 1, chunkZ - 1) && isChunkLoaded(chunkX, chunkZ - 1) && isChunkLoaded(chunkX - 1, chunkZ))
+            {
+                populate(this, chunkX - 1, chunkZ - 1);
+            }
+            if (world.getCurrentWeather() != null)
+            {
+                world.getCurrentWeather().doChunkLoadEffect(world, chunk);
+            }
+        }
+        lastQueriedChunkXPos = chunkX;
+        lastQueriedChunkZPos = chunkZ;
+        lastQueriedChunk = chunkMap.get(id);;
+        return chunkMap.get(id);
+    }
+
+    @Override
+    public Chunk prepareChunk(int chunkX, int chunkZ) {
+        return provideChunk(chunkX, chunkZ);
+    }
+
+    @Override
+    public void regenerateChunk(int chunkX, int chunkZ)
+    {
+        int id = ChunkCoordinate.toInt(chunkX, chunkZ);
+        droppedChunksSet.remove(id);
+        chunkMap.remove(id);
+        Chunk chunk;
+        if (chunkGenerator != null)
+        {
+            chunk = chunkGenerator.generate(chunkX, chunkZ);
+            chunk.fixMissingBlocks();
+        }
+        else return;
+        chunkMap.put(id, chunk);
+        if(!chunk.isTerrainPopulated && isChunkLoaded(chunkX + 1, chunkZ + 1) && isChunkLoaded(chunkX, chunkZ + 1) && isChunkLoaded(chunkX + 1, chunkZ))
+        {
+            populate(this, chunkX, chunkZ);
+        }
+        if(isChunkLoaded(chunkX - 1, chunkZ) && !provideChunk(chunkX - 1, chunkZ).isTerrainPopulated && isChunkLoaded(chunkX - 1, chunkZ + 1) && isChunkLoaded(chunkX, chunkZ + 1) && isChunkLoaded(chunkX - 1, chunkZ))
+        {
+            populate(this, chunkX - 1, chunkZ);
+        }
+        if(isChunkLoaded(chunkX, chunkZ - 1) && !provideChunk(chunkX, chunkZ - 1).isTerrainPopulated && isChunkLoaded(chunkX + 1, chunkZ - 1) && isChunkLoaded(chunkX, chunkZ - 1) && isChunkLoaded(chunkX + 1, chunkZ))
+        {
+            populate(this, chunkX, chunkZ - 1);
+        }
+        if(isChunkLoaded(chunkX - 1, chunkZ - 1) && !provideChunk(chunkX - 1, chunkZ - 1).isTerrainPopulated && isChunkLoaded(chunkX - 1, chunkZ - 1) && isChunkLoaded(chunkX, chunkZ - 1) && isChunkLoaded(chunkX - 1, chunkZ))
+        {
+            populate(this, chunkX - 1, chunkZ - 1);
+        }
+    }
+
+    @Override
+    public void populate(IChunkProvider chunkProvider, int chunkX, int chunkZ)
+    {
+        Chunk chunk = provideChunk(chunkX, chunkZ);
+        if(!chunk.isTerrainPopulated)
+        {
+            chunk.isTerrainPopulated = true;
+            if (this.chunkGenerator != null)
+            {
+                this.chunkGenerator.decorate(chunk);
+                chunk.setChunkModified();
+            }
+        }
+    }
+
+    public boolean keepLoaded(int chunkX, int chunkZ) {
+        int id = ChunkCoordinate.toInt(chunkX, chunkZ);
+        Chunk chunk = chunkMap.get(id);
+        if(isChunkLoaded(chunkX, chunkZ) && !forceLoadedChunkMap.containsKey(id) && forceLoadedChunkList.size() < forceLoadedChunksLimit) {
+            forceLoadedChunkMap.put(id, chunk);
+            forceLoadedChunkList.add(chunk);
+            return true;
+        }
+        return false;
+    }
+
+    public boolean removeFromForceLoaded(int chunkX, int chunkZ){
+        int id = ChunkCoordinate.toInt(chunkX, chunkZ);
+        forceLoadedChunkList.remove(forceLoadedChunkMap.get(id));
+        return forceLoadedChunkMap.remove(id) != null;
+    }
+
+    @Override
+    public boolean saveChunks(boolean saveImmediately, ProgressListener progressUpdate)
+    {
+        int attempts = 0;
+        int chunksToBeSaved = 0;
+        if(progressUpdate != null)
+        {
+            for (Chunk chunk : chunkMap.values()) {
+                if(chunk != null && chunk.needsSaving(saveImmediately)) chunksToBeSaved++;
+            }
+        }
+        int progress = 0;
+        for (Chunk chunk : chunkMap.values()) {
+            if(chunk == null) continue;
+            if(!chunk.needsSaving(saveImmediately)) continue;
+            saveChunk(chunk);
+            chunk.isModified = false;
+            if(++attempts == 2 && !saveImmediately) return false;
+            if(progressUpdate != null && ++progress % 10 == 0) progressUpdate.progressStagePercentage((progress * 100) / chunksToBeSaved);
+        }
+        return true;
+    }
+
+    private void saveChunk(Chunk chunk)
+    {
+        if(chunkLoader == null) return;
+        try
+        {
+            chunk.lastSaveTime = world.getWorldTime();
+            chunkLoader.saveChunk(world, chunk);
+        }
+        catch(IOException ioexception)
+        {
+            ioexception.printStackTrace();
+        }
+    }
+
+    private Chunk loadChunk(int i, int j)
+    {
+        if(chunkLoader == null) return emptyChunk;
+        try
+        {
+            Chunk chunk = chunkLoader.loadChunk(world, i, j);
+            if(chunk != null)
+            {
+                chunk.lastSaveTime = world.getWorldTime();
+            }
+            return chunk;
+        }
+        catch(Exception exception)
+        {
+            exception.printStackTrace();
+        }
+        return emptyChunk;
+    }
+
+    @Override
+    public boolean tick() {
+        int dropped = 0;
+        for (Chunk chunk : chunkMap.values()) {
+            if(dropped >= maxUnloadPerTick) break;
+            if(!isChunkLoaded(chunk.xPosition,chunk.zPosition)){
+                break;
+            } //droppedChunksSet gets updated as side-effect
+        }
+        if(!droppedChunksSet.isEmpty()){
+            int unloaded = 0;
+            HashSet<Integer> copy = new HashSet<>(droppedChunksSet);
+            for (int i : copy) {
+                if(unloaded >= maxUnloadPerTick) break;
+                if(forceLoadedChunkMap.containsKey(i)) continue;
+                if(chunkMap.containsKey(i)){
+                    Chunk chunk = chunkMap.get(i);
+                    chunk.onUnload();
+                    saveChunk(chunk);
+                    chunkMap.remove(i);
+                    droppedChunksSet.remove(i);
+                    unloaded++;
+                } else {
+                    droppedChunksSet.remove(i);
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean canSave() {
+        return true;
+    }
+
+    public IChunkLoader getChunkLoader() {
+        return chunkLoader;
+    }
+
+    @Override
+    public String getInfoString() {
+        return "Chunks: " + chunkMap.size() + ", " + "Dropped: " + droppedChunksSet.size()+", "+"Chunkloaded: "+forceLoadedChunkMap.size()+"/"+forceLoadedChunksLimit;
+    }
+
+    @Override
+    public void unloadAllChunks() {
+        for (Chunk chunk : chunkMap.values()) {
+            if(chunk != null) chunk.onUnload();
+        }
+        chunkMap.clear();
+        droppedChunksSet.clear();
+        chunkGenerator = null;
+        System.gc();
+    }
+
+    @Override
+    public void setCurrentChunkOver(int chunkX, int chunkZ) {
+        currentChunkX = chunkX;
+        currentChunkZ = chunkZ;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/core/block/BaseLeavesBlock.java b/src/main/java/net/minecraft/core/block/BaseLeavesBlock.java
index a01d7f5e9..2d4bc69bc 100644
--- a/src/main/java/net/minecraft/core/block/BaseLeavesBlock.java
+++ b/src/main/java/net/minecraft/core/block/BaseLeavesBlock.java
@@ -1,17 +1,18 @@
 package net.minecraft.core.block;
 
+import java.util.Random;
+
 import net.minecraft.core.block.entity.BlockEntity;
 import net.minecraft.core.block.material.Material;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.item.ItemStack;
-import net.minecraft.core.item.block.LeavesBlockItem;
 import net.minecraft.core.util.helper.MathHelper;
+import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.WorldSource;
 import net.minecraft.core.world.season.Season;
 
-import java.util.Random;
-
 // Referenced classes of package net.minecraft.src:
 //            Block, IBlockAccess, Material
 
@@ -26,7 +27,6 @@ public abstract class BaseLeavesBlock extends Block
     {
         super(key, namespaceId, id, material);
         setTicking(true);
-        setBlockItem(() -> new LeavesBlockItem(this));
     }
 
     @Override
@@ -213,6 +213,11 @@ public abstract class BaseLeavesBlock extends Block
     	}
     }
     
+    @Override
+    public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced) {
+    	return LeavesLayerBlock.MASK_PERMANENT;
+    }
+    
     @Override
     public float getAmbientOcclusionStrength(WorldSource blockAccess, int x, int y, int z) {
     	return enableTreeShadowing ? 0.8f : 0.0f;
diff --git a/src/main/java/net/minecraft/core/block/Block.java b/src/main/java/net/minecraft/core/block/Block.java
index c39cf79a9..23e918c3f 100644
--- a/src/main/java/net/minecraft/core/block/Block.java
+++ b/src/main/java/net/minecraft/core/block/Block.java
@@ -1,46 +1,48 @@
 package net.minecraft.core.block;
 
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
 import com.b100.utils.Utils;
+
 import net.minecraft.core.achievement.stat.Stat;
 import net.minecraft.core.achievement.stat.StatList;
 import net.minecraft.core.block.entity.BlockEntity;
-import net.minecraft.core.block.material.MaterialColor;
-import net.minecraft.core.entity.ItemEntity;
-import net.minecraft.core.entity.Mob;
-import net.minecraft.core.entity.player.Player;
-import net.minecraft.core.item.block.BlockItem;
-import net.minecraft.core.sound.BlockSound;
-import net.minecraft.core.sound.BlockSounds;
-import net.minecraft.core.util.HardIllegalArgumentException;
-import net.minecraft.core.util.phys.HitResult;
 import net.minecraft.core.block.material.Material;
+import net.minecraft.core.block.material.MaterialColor;
 import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.data.tag.ITaggable;
 import net.minecraft.core.data.tag.Tag;
 import net.minecraft.core.entity.Entity;
+import net.minecraft.core.entity.ItemEntity;
+import net.minecraft.core.entity.Mob;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.item.IItemConvertible;
 import net.minecraft.core.item.Item;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.item.ToolShearsItem;
+import net.minecraft.core.item.block.BlockItem;
+import net.minecraft.core.sound.BlockSound;
+import net.minecraft.core.sound.BlockSounds;
+import net.minecraft.core.util.HardIllegalArgumentException;
 import net.minecraft.core.util.collection.NamespaceID;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.AABB;
 import net.minecraft.core.util.phys.BoundingVolume;
+import net.minecraft.core.util.phys.HitResult;
 import net.minecraft.core.util.phys.Vec3;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.WorldSource;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Random;
-import java.util.function.Function;
-import java.util.function.Supplier;
 
 public class Block
     implements ITaggable<Block>, IItemConvertible
@@ -696,6 +698,11 @@ public class Block
     {
         return blockMaterial.getPushReaction();
     }
+    
+    public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced)
+    {
+    	return 0;
+    }
 
     public static boolean isBuried (World world, int x, int y, int z)
     {
diff --git a/src/main/java/net/minecraft/core/block/FlowerBlock.java b/src/main/java/net/minecraft/core/block/FlowerBlock.java
index 14b31fc1d..c72f1999f 100644
--- a/src/main/java/net/minecraft/core/block/FlowerBlock.java
+++ b/src/main/java/net/minecraft/core/block/FlowerBlock.java
@@ -1,19 +1,18 @@
 package net.minecraft.core.block;
 
-import net.minecraft.core.entity.Mob;
+import java.util.Random;
+
+import net.minecraft.core.block.material.Material;
+import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.item.IBonemealable;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.AABB;
-import net.minecraft.core.block.tag.BlockTags;
-import net.minecraft.core.enums.EnumDropCause;
-import net.minecraft.core.block.material.Material;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.WorldSource;
 
-import java.util.Random;
-
 // Referenced classes of package net.minecraft.src:
 //            Block, Material, World, GrassBlock,
 //            AxisAlignedBB
@@ -44,11 +43,10 @@ public class FlowerBlock extends Block implements IBonemealable
     }
 
     @Override
-    public void onBlockPlacedByMob(World world, int x, int y, int z, Side placeSide, Mob mob, double sideHeight) {
-        if (mob instanceof Player){
-            world.setBlockMetadata(x, y, z, world.getBlockMetadata(x, y, z) | 0b1000_0000); // Set persistent flag
-        }
+    public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced) {
+    	return LeavesLayerBlock.MASK_PERMANENT;
     }
+    
     @Override
 	public boolean canPlaceBlockAt(World world, int x, int y, int z)
     {
diff --git a/src/main/java/net/minecraft/core/block/PaintedPlanksBlock.java b/src/main/java/net/minecraft/core/block/PaintedPlanksBlock.java
index 26e5c1cbf..a597b3867 100644
--- a/src/main/java/net/minecraft/core/block/PaintedPlanksBlock.java
+++ b/src/main/java/net/minecraft/core/block/PaintedPlanksBlock.java
@@ -2,19 +2,24 @@ package net.minecraft.core.block;
 
 import net.minecraft.core.block.entity.BlockEntity;
 import net.minecraft.core.block.material.Material;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 
-// Referenced classes of package net.minecraft.src:
-//            Block, Material
-
 public class PaintedPlanksBlock extends Block
 {
     public PaintedPlanksBlock(String key, String namespaceId, int id)
     {
         super(key, namespaceId, id, Material.wood);
     }
+    
+    @Override
+    public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced)
+    {
+    	return stack.getMetadata();
+    }
 
     @Override
     public ItemStack[] getBreakResult(World world, EnumDropCause dropCause, int x, int y, int z, int meta, BlockEntity blockEntity)
diff --git a/src/main/java/net/minecraft/core/block/PaintedSlabBlock.java b/src/main/java/net/minecraft/core/block/PaintedSlabBlock.java
index 3865612f6..f96d247af 100644
--- a/src/main/java/net/minecraft/core/block/PaintedSlabBlock.java
+++ b/src/main/java/net/minecraft/core/block/PaintedSlabBlock.java
@@ -1,6 +1,8 @@
 package net.minecraft.core.block;
 
 import net.minecraft.core.entity.Mob;
+import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
@@ -11,6 +13,11 @@ public class PaintedSlabBlock extends SlabBlock
 	{
 		super(modelBlock, namespaceId, id);
 	}
+	
+	@Override
+	public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced) {
+		return stack.getMetadata();
+	}
 
 	@Override
 	public void onBlockPlacedByMob(World world, int x, int y, int z, Side side, Mob mob, double sideHeight) {
diff --git a/src/main/java/net/minecraft/core/block/entity/FurnaceBlockEntity.java b/src/main/java/net/minecraft/core/block/entity/FurnaceBlockEntity.java
index 58eaeb004..c2aac8276 100644
--- a/src/main/java/net/minecraft/core/block/entity/FurnaceBlockEntity.java
+++ b/src/main/java/net/minecraft/core/block/entity/FurnaceBlockEntity.java
@@ -54,12 +54,18 @@ public class FurnaceBlockEntity extends BlockEntity
             {
                 ItemStack itemstack = furnaceItemStacks[index];
                 furnaceItemStacks[index] = null;
+                if (index == 2) {
+                    worldObj.markBlockNeedsUpdate(x, y, z);
+                }
                 return itemstack;
             }
             ItemStack itemstack1 = furnaceItemStacks[index].splitStack(takeAmount);
             if(furnaceItemStacks[index].stackSize <= 0)
             {
                 furnaceItemStacks[index] = null;
+                if (index == 2) {
+                    worldObj.markBlockNeedsUpdate(x, y, z);
+                }
             }
             return itemstack1;
         } else
@@ -76,6 +82,9 @@ public class FurnaceBlockEntity extends BlockEntity
         {
             itemstack.stackSize = getMaxStackSize();
         }
+        if (index == 2 && itemstack == null) {
+            worldObj.markBlockNeedsUpdate(x, y, z);
+        }
     }
 
     @Override
@@ -272,6 +281,7 @@ public class FurnaceBlockEntity extends BlockEntity
                 }
             }
         }
+        boolean wasEmpty = furnaceItemStacks[2] == null;
         if(furnaceItemStacks[2] == null && itemstack != null)
         {
             furnaceItemStacks[2] = itemstack.copy();
@@ -284,6 +294,9 @@ public class FurnaceBlockEntity extends BlockEntity
         {
             furnaceItemStacks[0] = null;
         }
+        if (wasEmpty && furnaceItemStacks[2] != null) {
+            worldObj.markBlockNeedsUpdate(x, y, z);
+        }
     }
 
     protected void updateFurnace(){
@@ -312,7 +325,7 @@ public class FurnaceBlockEntity extends BlockEntity
         // Do nothing
     }
 
-    protected ItemStack furnaceItemStacks[];
+    protected ItemStack[] furnaceItemStacks;
     public int maxBurnTime;
     public int currentCookTime;
     public int maxCookTime;
diff --git a/src/main/java/net/minecraft/core/block/piston/BasePistonBlock.java b/src/main/java/net/minecraft/core/block/piston/BasePistonBlock.java
index ea4582340..142107b20 100644
--- a/src/main/java/net/minecraft/core/block/piston/BasePistonBlock.java
+++ b/src/main/java/net/minecraft/core/block/piston/BasePistonBlock.java
@@ -1,13 +1,16 @@
 package net.minecraft.core.block.piston;
 
+import java.util.ArrayList;
+
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.entity.BlockEntity;
 import net.minecraft.core.block.logic.PistonDirections;
 import net.minecraft.core.block.material.Material;
 import net.minecraft.core.entity.Mob;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
-import net.minecraft.core.item.block.PistonBlockItem;
+import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.sound.SoundCategory;
 import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.util.helper.Side;
@@ -15,8 +18,6 @@ import net.minecraft.core.util.phys.AABB;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.WorldSource;
 
-import java.util.ArrayList;
-
 public class BasePistonBlock extends Block
 {
 
@@ -24,7 +25,6 @@ public class BasePistonBlock extends Block
     {
         super(key, namespaceId, id, Material.piston);
         this.isSticky = isSticky;
-        setBlockItem(() -> new PistonBlockItem(this));
         withHardness(0.5F);
     }
 
@@ -389,6 +389,11 @@ public class BasePistonBlock extends Block
 
         return true;
     }
+    
+    @Override
+    public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced) {
+    	return 7;
+    }
 
     private boolean isSticky;
     
diff --git a/src/main/java/net/minecraft/core/item/Item.java b/src/main/java/net/minecraft/core/item/Item.java
index 7bc1ce125..b474f0da8 100644
--- a/src/main/java/net/minecraft/core/item/Item.java
+++ b/src/main/java/net/minecraft/core/item/Item.java
@@ -174,12 +174,7 @@ public class Item implements ITaggable<Item>, IItemConvertible
     {
         return maxStackSize;
     }
-
-    public int getPlacedBlockMetadata(int i)
-    {
-        return 0;
-    }
-
+    
     public boolean getHasSubtypes()
     {
         return hasSubtypes;
diff --git a/src/main/java/net/minecraft/core/item/PaintingItem.java b/src/main/java/net/minecraft/core/item/PaintingItem.java
index d5bf9bcdc..3f1f78338 100644
--- a/src/main/java/net/minecraft/core/item/PaintingItem.java
+++ b/src/main/java/net/minecraft/core/item/PaintingItem.java
@@ -65,7 +65,8 @@ public class PaintingItem extends Item
             }
             world.playBlockSoundEffect(entityplayer, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, Blocks.PLANKS_OAK, EnumBlockSoundEffectType.PLACE);
             itemstack.consumeItem(entityplayer);
+            return true;
         }
-        return true;
+        return false;
     }
 }
diff --git a/src/main/java/net/minecraft/core/item/PebbleItem.java b/src/main/java/net/minecraft/core/item/PebbleItem.java
index 69ded824f..8e4a8538b 100644
--- a/src/main/java/net/minecraft/core/item/PebbleItem.java
+++ b/src/main/java/net/minecraft/core/item/PebbleItem.java
@@ -115,14 +115,13 @@ public class PebbleItem
         // Regular placement, as all else fails
         if (world.canBlockBePlacedAt(Blocks.OVERLAY_PEBBLES.id, blockX, blockY, blockZ, false, side) && world.isBlockOpaqueCube(blockX, blockY - 1, blockZ))
         {
-            final Block block = Blocks.OVERLAY_PEBBLES;
-            if (world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, Blocks.OVERLAY_PEBBLES.id, getPlacedBlockMetadata(itemstack.getMetadata())))
+            if (world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, Blocks.OVERLAY_PEBBLES.id, 0))
             {
                 Blocks.OVERLAY_PEBBLES.onBlockPlacedByMob(world, blockX, blockY, blockZ, side, entityplayer, yPlaced);
                 world.playBlockSoundEffect(entityplayer, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, Blocks.OVERLAY_PEBBLES, EnumBlockSoundEffectType.PLACE);
                 itemstack.consumeItem(entityplayer);
+				return true;
             }
-            return true;
         }
         return false;
 
diff --git a/src/main/java/net/minecraft/core/item/PlaceableItem.java b/src/main/java/net/minecraft/core/item/PlaceableItem.java
index 994e9ebc0..9cdf81b84 100644
--- a/src/main/java/net/minecraft/core/item/PlaceableItem.java
+++ b/src/main/java/net/minecraft/core/item/PlaceableItem.java
@@ -1,14 +1,11 @@
 package net.minecraft.core.item;
 
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.FluidBlock;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumBlockSoundEffectType;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 
-import static net.minecraft.core.world.LevelListener.EVENT_BLOCK_BREAK;
-
 public class PlaceableItem extends Item {
 	
 	public Block blockToPlace;
@@ -30,21 +27,15 @@ public class PlaceableItem extends Item {
 			blockY += side.getOffsetY();
 			blockZ += side.getOffsetZ();
 		}
-		Block currentBlock = world.getBlock(blockX, blockY, blockZ);
-		boolean placedInside = currentBlock != null && !(currentBlock instanceof FluidBlock);
-
 		if(blockY < 0 || blockY >= world.getHeightBlocks()) {
 			return false;
 		}
 		if(world.canBlockBePlacedAt(blockToPlace.id, blockX, blockY, blockZ, false, side) && stack.consumeItem(player)) {
-			if (placedInside && !world.isClientSide){
-				world.playSoundEffect(EVENT_BLOCK_BREAK, blockX, blockY, blockZ, world.getBlockId(blockX, blockY, blockZ));
-			}
 			if(world.setBlockWithNotify(blockX, blockY, blockZ, blockToPlace.id)) {
 				blockToPlace.onBlockPlacedByMob(world, blockX, blockY, blockZ, side, player, yPlaced);
 				world.playBlockSoundEffect(player, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, blockToPlace, EnumBlockSoundEffectType.PLACE);
+				return true;
 			}
-			return true;
 		}
 		return false;
 	}
diff --git a/src/main/java/net/minecraft/core/item/RopeItem.java b/src/main/java/net/minecraft/core/item/RopeItem.java
index f3dd0d599..afb078b92 100644
--- a/src/main/java/net/minecraft/core/item/RopeItem.java
+++ b/src/main/java/net/minecraft/core/item/RopeItem.java
@@ -1,14 +1,11 @@
 package net.minecraft.core.item;
 
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.FluidBlock;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumBlockSoundEffectType;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 
-import static net.minecraft.core.world.LevelListener.EVENT_BLOCK_BREAK;
-
 public class RopeItem extends PlaceableItem {
     public RopeItem(String name, String namespaceId, int id, Block blockToPlace) {
         super(name, namespaceId, id, blockToPlace);
@@ -38,21 +35,15 @@ public class RopeItem extends PlaceableItem {
             blockY += side.getOffsetY();
             blockZ += side.getOffsetZ();
         }
-        Block currentBlock = world.getBlock(blockX, blockY, blockZ);
-        boolean placedInside = currentBlock != null && !(currentBlock instanceof FluidBlock);
-
         if(blockY < 0 || blockY >= world.getHeightBlocks()) {
             return false;
         }
         if(world.canBlockBePlacedAt(blockToPlace.id, blockX, blockY, blockZ, false, side) && stack.consumeItem(player)) {
-            if (placedInside && !world.isClientSide){
-                world.playSoundEffect(EVENT_BLOCK_BREAK, blockX, blockY, blockZ, world.getBlockId(blockX, blockY, blockZ));
-            }
             if(world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, blockToPlace.id, placeOnSide ? 0b1000_0000 : 0)) {
                 blockToPlace.onBlockPlacedByMob(world, blockX, blockY, blockZ, side, player, yPlaced);
                 world.playBlockSoundEffect(player, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, blockToPlace, EnumBlockSoundEffectType.PLACE);
+				return true;
             }
-            return true;
         }
         return false;
     }
diff --git a/src/main/java/net/minecraft/core/item/SeedsItem.java b/src/main/java/net/minecraft/core/item/SeedsItem.java
index a81fc1920..aa6f45789 100644
--- a/src/main/java/net/minecraft/core/item/SeedsItem.java
+++ b/src/main/java/net/minecraft/core/item/SeedsItem.java
@@ -24,13 +24,13 @@ public class SeedsItem extends Item {
 			blockZ += side.getOffsetZ();
 		}
 		if(world.getBlockId(blockX, blockY - 1, blockZ) == Blocks.FARMLAND_DIRT.id && world.canPlaceInsideBlock(blockX, blockY, blockZ)) {
-			world.setBlockWithNotify(blockX, blockY, blockZ, cropsId);
-			world.playBlockSoundEffect(entityplayer, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, Block.blocksList[cropsId], EnumBlockSoundEffectType.PLACE);
-			itemstack.consumeItem(entityplayer);
-			return true;
-		} else {
-			return false;
+			if(world.setBlockWithNotify(blockX, blockY, blockZ, cropsId)) {
+				world.playBlockSoundEffect(entityplayer, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, Block.blocksList[cropsId], EnumBlockSoundEffectType.PLACE);
+				itemstack.consumeItem(entityplayer);
+				return true;
+			}
 		}
+		return false;
 	}
 
 }
diff --git a/src/main/java/net/minecraft/core/item/block/BlockItem.java b/src/main/java/net/minecraft/core/item/block/BlockItem.java
index d91d457c5..78fbdf5fc 100644
--- a/src/main/java/net/minecraft/core/item/block/BlockItem.java
+++ b/src/main/java/net/minecraft/core/item/block/BlockItem.java
@@ -1,7 +1,6 @@
 package net.minecraft.core.item.block;
 
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.FluidBlock;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumBlockSoundEffectType;
 import net.minecraft.core.item.Item;
@@ -9,8 +8,6 @@ import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 
-import static net.minecraft.core.world.LevelListener.EVENT_BLOCK_BREAK;
-
 // Referenced classes of package net.minecraft.src:
 //            Item, Block, World, ItemStack, 
 //            Material, StepSound, Player
@@ -33,35 +30,35 @@ public class BlockItem extends Item
     }
 
     @Override
-	public boolean onUseItemOnBlock(ItemStack stack, Player player, World world, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
+	public boolean onUseItemOnBlock(ItemStack stack, Player player, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced) {
 		if(stack.stackSize <= 0) {
 			return false;
 		}
-		if(!world.canPlaceInsideBlock(blockX, blockY, blockZ)) {
-			blockX += side.getOffsetX();
-			blockY += side.getOffsetY();
-			blockZ += side.getOffsetZ();
+		if(!world.canPlaceInsideBlock(x, y, z)) {
+			x += side.getOffsetX();
+			y += side.getOffsetY();
+			z += side.getOffsetZ();
 		}
-        Block currentBlock = world.getBlock(blockX, blockY, blockZ);
-        boolean placedInside = currentBlock != null && !(currentBlock instanceof FluidBlock);
-
-        if(blockY < 0 || blockY >= world.getHeightBlocks()) {
-        	return false;
-        }
-		if(world.canBlockBePlacedAt(blockID, blockX, blockY, blockZ, false, side) && stack.consumeItem(player)) {
+		if(y < 0 || y >= world.getHeightBlocks()) {
+			return false;
+		}
+		if(world.canBlockBePlacedAt(blockID, x, y, z, false, side)) {
 			Block block = Block.blocksList[blockID];
-            if (placedInside && !world.isClientSide){
-                world.playSoundEffect(EVENT_BLOCK_BREAK, blockX, blockY, blockZ, world.getBlockId(blockX, blockY, blockZ));
-            }
-			if(world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, blockID, getPlacedBlockMetadata(stack.getMetadata()))) {
-				block.onBlockPlacedByMob(world, blockX, blockY, blockZ, side, player, yPlaced);
-				world.playBlockSoundEffect(player, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, block, EnumBlockSoundEffectType.PLACE);
-
+			int meta = getPlacedBlockMetadata(player, stack, world, x, y, z, side, xPlaced, yPlaced);
+			if(world.setBlockAndMetadataWithNotify(x, y, z, blockID, meta)) {
+				stack.consumeItem(player);
+				block.onBlockPlacedByMob(world, x, y, z, side, player, yPlaced);
+				world.playBlockSoundEffect(player, x + 0.5F, y + 0.5F, z + 0.5F, block, EnumBlockSoundEffectType.PLACE);
+				return true;
 			}
-			return true;
 		}
 		return false;
 	}
+    
+    public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced)
+    {
+    	return Block.getBlock(blockID).getPlacedBlockMetadata(player, stack, world, x, y, z, side, xPlaced, yPlaced);
+    }
 
     @Override
     public String getLanguageKey(ItemStack itemstack)
diff --git a/src/main/java/net/minecraft/core/item/block/BlockItemStairsPainted.java b/src/main/java/net/minecraft/core/item/block/BlockItemStairsPainted.java
index 0e8b4d768..c275a0a64 100644
--- a/src/main/java/net/minecraft/core/item/block/BlockItemStairsPainted.java
+++ b/src/main/java/net/minecraft/core/item/block/BlockItemStairsPainted.java
@@ -1,8 +1,11 @@
 package net.minecraft.core.item.block;
 
 import net.minecraft.core.block.Block;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.DyeColor;
+import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.world.World;
 
 public class BlockItemStairsPainted extends BlockItem
 {
@@ -15,11 +18,10 @@ public class BlockItemStairsPainted extends BlockItem
     }
 
     @Override
-    public int getPlacedBlockMetadata(int i)
-    {
-        return i;
+    public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced) {
+    	return stack.getMetadata();
     }
-
+    
     @Override
     public String getLanguageKey(ItemStack itemstack) {
         return super.getKey() + "." + DyeColor.colorFromBlockMeta((itemstack.getMetadata() & 0xF0) >> 4).colorID;
diff --git a/src/main/java/net/minecraft/core/item/block/LadderBlockItem.java b/src/main/java/net/minecraft/core/item/block/LadderBlockItem.java
index f0e9a6904..00858722e 100644
--- a/src/main/java/net/minecraft/core/item/block/LadderBlockItem.java
+++ b/src/main/java/net/minecraft/core/item/block/LadderBlockItem.java
@@ -1,7 +1,6 @@
 package net.minecraft.core.item.block;
 
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.FluidBlock;
 import net.minecraft.core.block.LadderBlock;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumBlockSoundEffectType;
@@ -9,8 +8,6 @@ import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 
-import static net.minecraft.core.world.LevelListener.EVENT_BLOCK_BREAK;
-
 public class LadderBlockItem extends BlockItem {
 
 	private LadderBlock ladderBlock;
@@ -56,11 +53,7 @@ public class LadderBlockItem extends BlockItem {
 	}
 	
 	public boolean placeBlock(World world, int x, int y, int z, int meta, Player player, ItemStack stack, Side side, double sideHeight) {
-		Block currentBlock = world.getBlock(x, y, z);
 		if(world.canPlaceInsideBlock(x, y, z) && world.canBlockBePlacedAt(blockID, x, y, z, false, side) && stack.consumeItem(player)) {
-			if (currentBlock != null && !(currentBlock instanceof FluidBlock) && !world.isClientSide){
-				world.playSoundEffect(EVENT_BLOCK_BREAK, x, y, z, world.getBlockId(x, y, z));
-			}
 			if(world.setBlockAndMetadataWithNotify(x, y, z, blockID, meta)) {
 				Block.getBlock(blockID).onBlockPlacedByMob(world, x, y, z, side, player, sideHeight);
 				world.playBlockSoundEffect(player, x + 0.5f, y + 0.5f, z + 0.5f, ladderBlock, EnumBlockSoundEffectType.PLACE);
diff --git a/src/main/java/net/minecraft/core/item/block/LampBlockItem.java b/src/main/java/net/minecraft/core/item/block/LampBlockItem.java
index 7ae72fe8b..f088b21b4 100644
--- a/src/main/java/net/minecraft/core/item/block/LampBlockItem.java
+++ b/src/main/java/net/minecraft/core/item/block/LampBlockItem.java
@@ -1,9 +1,11 @@
 package net.minecraft.core.item.block;
 
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.LampBlock;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.DyeColor;
+import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.world.World;
 
 public class LampBlockItem extends BlockItem
 {
@@ -14,11 +16,10 @@ public class LampBlockItem extends BlockItem
         setMaxDamage(0);
         setHasSubtypes(true);
     }
-
+    
     @Override
-    public int getPlacedBlockMetadata(int i)
-    {
-        return i;
+    public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced) {
+    	return stack.getMetadata();
     }
 
     @Override
diff --git a/src/main/java/net/minecraft/core/item/block/LayerBlockItem.java b/src/main/java/net/minecraft/core/item/block/LayerBlockItem.java
index c18181e8b..03a60d60e 100644
--- a/src/main/java/net/minecraft/core/item/block/LayerBlockItem.java
+++ b/src/main/java/net/minecraft/core/item/block/LayerBlockItem.java
@@ -1,12 +1,12 @@
 package net.minecraft.core.item.block;
 
+import net.minecraft.core.block.BaseLayerBlock;
+
 // Referenced classes of package net.minecraft.src:
 //            Item, Block, World, ItemStack,
 //            Material, StepSound, Player
 
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.FluidBlock;
-import net.minecraft.core.block.BaseLayerBlock;
 import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumBlockSoundEffectType;
@@ -15,8 +15,6 @@ import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.AABB;
 import net.minecraft.core.world.World;
 
-import static net.minecraft.core.world.LevelListener.EVENT_BLOCK_BREAK;
-
 
 public class LayerBlockItem extends BlockItem
 {
@@ -127,18 +125,14 @@ public class LayerBlockItem extends BlockItem
 		if (world.canBlockBePlacedAt(blockID, blockX, blockY, blockZ, false, side))
 		{
 			final Block block = Block.blocksList[blockID];
-			Block currentBlock = world.getBlock(blockX, blockY, blockZ);
-			boolean placedInside = currentBlock != null && !(currentBlock instanceof FluidBlock);
-			if (world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, blockID, getPlacedBlockMetadata(itemstack.getMetadata())))
+			int placeMeta = getPlacedBlockMetadata(entityplayer, itemstack, world, blockX, blockY, blockZ, side, xPlaced, yPlaced);
+			if (world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, blockID, placeMeta))
 			{
-				if (placedInside && !world.isClientSide){
-					world.playSoundEffect(EVENT_BLOCK_BREAK, blockX, blockY, blockZ, currentBlock.id);
-				}
 				Block.blocksList[blockID].onBlockPlacedByMob(world, blockX, blockY, blockZ, null, entityplayer, yPlaced);
 				world.playBlockSoundEffect(entityplayer, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, block, EnumBlockSoundEffectType.PLACE);
 				itemstack.consumeItem(entityplayer);
+				return true;
 			}
-			return true;
 		}
 		return false;
 
diff --git a/src/main/java/net/minecraft/core/item/block/LeavesBlockItem.java b/src/main/java/net/minecraft/core/item/block/LeavesBlockItem.java
deleted file mode 100644
index ac574477f..000000000
--- a/src/main/java/net/minecraft/core/item/block/LeavesBlockItem.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.minecraft.core.item.block;
-
-import net.minecraft.core.block.BaseLeavesBlock;
-import net.minecraft.core.block.Block;
-
-public class LeavesBlockItem extends BlockItem
-{
-    public LeavesBlockItem(Block block)
-    {
-        super(block);
-    }
-
-    @Override
-    public int getPlacedBlockMetadata(int i)
-    {
-        return BaseLeavesBlock.setPermanent(super.getPlacedBlockMetadata(i), true);
-    }
-}
diff --git a/src/main/java/net/minecraft/core/item/block/LeavesLayerBlockItem.java b/src/main/java/net/minecraft/core/item/block/LeavesLayerBlockItem.java
index cc989411a..9179e3f21 100644
--- a/src/main/java/net/minecraft/core/item/block/LeavesLayerBlockItem.java
+++ b/src/main/java/net/minecraft/core/item/block/LeavesLayerBlockItem.java
@@ -1,11 +1,8 @@
-
 package net.minecraft.core.item.block;
 
-
+import net.minecraft.core.block.BaseLayerBlock;
 import net.minecraft.core.block.BaseLeavesBlock;
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.FluidBlock;
-import net.minecraft.core.block.BaseLayerBlock;
 import net.minecraft.core.block.LeavesLayerBlock;
 import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.player.Player;
@@ -15,8 +12,6 @@ import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.AABB;
 import net.minecraft.core.world.World;
 
-import static net.minecraft.core.world.LevelListener.EVENT_BLOCK_BREAK;
-
 public class LeavesLayerBlockItem extends BlockItem
 {
 	public LeavesLayerBlockItem(Block block)
@@ -126,21 +121,22 @@ public class LeavesLayerBlockItem extends BlockItem
 		if (world.canBlockBePlacedAt(blockID, blockX, blockY, blockZ, false, side))
 		{
 			final Block block = Block.blocksList[blockID];
-			Block currentBlock = world.getBlock(blockX, blockY, blockZ);
-			boolean placedInside = currentBlock != null && !(currentBlock instanceof FluidBlock);
-			if (world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, blockID, LeavesLayerBlock.setPermanent(getPlacedBlockMetadata(itemstack.getMetadata()), true)))
+			int placeMeta = getPlacedBlockMetadata(entityplayer, itemstack, world, blockX, blockY, blockZ, side, xPlaced, yPlaced);
+			if (world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, blockID, placeMeta))
 			{
-				if (placedInside && !world.isClientSide){
-					world.playSoundEffect(EVENT_BLOCK_BREAK, blockX, blockY, blockZ, currentBlock.id);
-				}
 				Block.blocksList[blockID].onBlockPlacedByMob(world, blockX, blockY, blockZ, null, entityplayer, yPlaced);
 				world.playBlockSoundEffect(entityplayer, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, block, EnumBlockSoundEffectType.PLACE);
 				itemstack.consumeItem(entityplayer);
+				return true;
 			}
-			return true;
 		}
 		return false;
 
 	}
+	
+	@Override
+	public int getPlacedBlockMetadata(Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced) {
+		return LeavesLayerBlock.MASK_PERMANENT;
+	}
 
 }
diff --git a/src/main/java/net/minecraft/core/item/block/LegacyChestBlockItem.java b/src/main/java/net/minecraft/core/item/block/LegacyChestBlockItem.java
deleted file mode 100644
index 3bf3a0c9b..000000000
--- a/src/main/java/net/minecraft/core/item/block/LegacyChestBlockItem.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package net.minecraft.core.item.block;
-
-import net.minecraft.core.block.Block;
-
-@Deprecated
-public class LegacyChestBlockItem extends BlockItem {
-
-	public LegacyChestBlockItem(Block block) {
-		super(block);
-	}
-	
-	@Override
-	public int getPlacedBlockMetadata(int i) {
-		return i;
-	}
-
-}
diff --git a/src/main/java/net/minecraft/core/item/block/PaintedBlockItem.java b/src/main/java/net/minecraft/core/item/block/PaintedBlockItem.java
index 2114d28ab..1d6c38d55 100644
--- a/src/main/java/net/minecraft/core/item/block/PaintedBlockItem.java
+++ b/src/main/java/net/minecraft/core/item/block/PaintedBlockItem.java
@@ -1,13 +1,6 @@
 package net.minecraft.core.item.block;
 
-
-// Referenced classes of package net.minecraft.src:
-//            BlockItem, Block, BlockCloth, DyeItem,
-//            ItemStack
-
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.WoolBlock;
-import net.minecraft.core.item.DyeItem;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.DyeColor;
 
@@ -23,12 +16,6 @@ public class PaintedBlockItem extends BlockItem
         this.upperMetadata = upperMetadata;
     }
 
-    @Override
-    public int getPlacedBlockMetadata(int i)
-    {
-        return i;
-    }
-
     @Override
     public String getLanguageKey(ItemStack itemstack)
     {
diff --git a/src/main/java/net/minecraft/core/item/block/PaintedSlabBlockItem.java b/src/main/java/net/minecraft/core/item/block/PaintedSlabBlockItem.java
index 072e58c9c..6ff077e1a 100644
--- a/src/main/java/net/minecraft/core/item/block/PaintedSlabBlockItem.java
+++ b/src/main/java/net/minecraft/core/item/block/PaintedSlabBlockItem.java
@@ -1,13 +1,6 @@
 package net.minecraft.core.item.block;
 
-
-// Referenced classes of package net.minecraft.src:
-//            BlockItem, Block, BlockCloth, DyeItem,
-//            ItemStack
-
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.WoolBlock;
-import net.minecraft.core.item.DyeItem;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.DyeColor;
 
@@ -23,12 +16,6 @@ public class PaintedSlabBlockItem extends SlabBlockItem
         this.upperMetadata = true;
     }
 
-    @Override
-    public int getPlacedBlockMetadata(int i)
-    {
-        return i;
-    }
-
     @Override
     public String getLanguageKey(ItemStack itemstack)
     {
diff --git a/src/main/java/net/minecraft/core/item/block/PistonBlockItem.java b/src/main/java/net/minecraft/core/item/block/PistonBlockItem.java
deleted file mode 100644
index 47d747497..000000000
--- a/src/main/java/net/minecraft/core/item/block/PistonBlockItem.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package net.minecraft.core.item.block;
-
-
-// Referenced classes of package net.minecraft.src:
-//            BlockItem
-
-import net.minecraft.core.block.Block;
-
-public class PistonBlockItem extends BlockItem
-{
-
-    public PistonBlockItem(Block block)
-    {
-        super(block);
-    }
-
-    @Override
-    public int getPlacedBlockMetadata(int i)
-    {
-        return 7;
-    }
-}
diff --git a/src/main/java/net/minecraft/core/item/block/SlabBlockItem.java b/src/main/java/net/minecraft/core/item/block/SlabBlockItem.java
index 741b98835..cba832a33 100644
--- a/src/main/java/net/minecraft/core/item/block/SlabBlockItem.java
+++ b/src/main/java/net/minecraft/core/item/block/SlabBlockItem.java
@@ -1,7 +1,6 @@
 package net.minecraft.core.item.block;
 
 import net.minecraft.core.block.Block;
-import net.minecraft.core.block.FluidBlock;
 import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumBlockSoundEffectType;
@@ -9,8 +8,6 @@ import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.AABB;
 import net.minecraft.core.world.World;
-
-import static net.minecraft.core.world.LevelListener.EVENT_BLOCK_BREAK;
 public class SlabBlockItem extends BlockItem
 {
 
@@ -88,18 +85,14 @@ public class SlabBlockItem extends BlockItem
         if(world.canBlockBePlacedAt(blockID, blockX, blockY, blockZ, false, side))
         {
             Block block = Block.blocksList[blockID];
-            Block currentBlock = world.getBlock(blockX, blockY, blockZ);
-            boolean placedInside = currentBlock != null && !(currentBlock instanceof FluidBlock);
-            if(world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, blockID, getPlacedBlockMetadata(itemstack.getMetadata())))
+            int placeMeta = getPlacedBlockMetadata(entityplayer, itemstack, world, blockX, blockY, blockZ, side, xPlaced, yPlaced);
+            if(world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, blockID, placeMeta))
             {
-                if (placedInside && !world.isClientSide){
-                    world.playSoundEffect(EVENT_BLOCK_BREAK, blockX, blockY, blockZ, currentBlock.id);
-                }
             	Block.blocksList[blockID].onBlockPlacedByMob(world, blockX, blockY, blockZ, side, entityplayer, yPlaced);
                 world.playBlockSoundEffect(entityplayer, blockX + 0.5F, blockY + 0.5F, blockZ + 0.5F, block, EnumBlockSoundEffectType.PLACE);
                 itemstack.consumeItem(entityplayer);
+				return true;
             }
-            return true;
         }
         return false;
     }
diff --git a/src/main/java/net/minecraft/core/item/tool/ShovelToolItem.java b/src/main/java/net/minecraft/core/item/tool/ShovelToolItem.java
index 9a2da802b..a19e2d050 100644
--- a/src/main/java/net/minecraft/core/item/tool/ShovelToolItem.java
+++ b/src/main/java/net/minecraft/core/item/tool/ShovelToolItem.java
@@ -41,9 +41,7 @@ public class ShovelToolItem extends ToolItem
                 itemstack.damageItem(1, entityplayer);
             }
             return true;
-        } else
-        {
-            return false;
         }
+        return false;
     }
 }
diff --git a/src/main/java/net/minecraft/core/util/helper/Buffer.java b/src/main/java/net/minecraft/core/util/helper/Buffer.java
index a4e74e29d..f6e3a2ba1 100644
--- a/src/main/java/net/minecraft/core/util/helper/Buffer.java
+++ b/src/main/java/net/minecraft/core/util/helper/Buffer.java
@@ -8,7 +8,7 @@ public abstract class Buffer {
 	
 	public static ByteBuffer buffer = createBuffer(2097152);
 	
-	private static ByteBuffer createBuffer(int size) {
+	public static ByteBuffer createBuffer(int size) {
 		return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
 	}
 	
diff --git a/src/main/java/net/minecraft/core/world/Dimension.java b/src/main/java/net/minecraft/core/world/Dimension.java
index e3f8a6bda..45cdd1625 100644
--- a/src/main/java/net/minecraft/core/world/Dimension.java
+++ b/src/main/java/net/minecraft/core/world/Dimension.java
@@ -96,7 +96,7 @@ public class Dimension
 	}
 	public static void init(){
 		overworld = new Dimension("overworld", null, 1.0f, -1, WorldTypes.OVERWORLD_DEFAULT);
-		nether = new Dimension("hell", overworld, 0.125f, Blocks.PORTAL_NETHER.id, WorldTypes.NETHER_DEFAULT);
+		nether = new Dimension("nether", overworld, 0.125f, Blocks.PORTAL_NETHER.id, WorldTypes.NETHER_DEFAULT);
 		paradise = new Dimension("paradise", overworld, 8.0f, Blocks.PORTAL_PARADISE.id, WorldTypes.PARADISE_DEFAULT);
 
 		registerDimension(0, overworld);
diff --git a/src/main/java/net/minecraft/core/world/save/conversion/ChunkConverter.java b/src/main/java/net/minecraft/core/world/save/conversion/ChunkConverter.java
index 396ad1f6c..5ed319489 100644
--- a/src/main/java/net/minecraft/core/world/save/conversion/ChunkConverter.java
+++ b/src/main/java/net/minecraft/core/world/save/conversion/ChunkConverter.java
@@ -266,7 +266,7 @@ public class ChunkConverter
 			converters[0].addMapping(new MappingSimple(88, 810)); //Block.soulsand.blockID));
 			converters[0].addMapping(new MappingSimple(89, 820)); //Block.glowstone.blockID));
 			converters[0].addMapping(new MappingSimple(90, 830)); //Block.portalNether.blockID));
-			converters[0].addMapping(new MappingSimple(91, 831)); //Block.pumpkinCarvedActive.blockID));
+			converters[0].addMapping(new MappingSimple(91, 792)); //Block.pumpkinCarvedActive.blockID));
 			converters[0].addMapping(new MappingSimple(92, 840)); //Block.cake.blockID));
 			converters[0].addMapping(new MappingSimple(93, 510)); //Block.repeaterIdle.blockID));
 			converters[0].addMapping(new MappingSimple(94, 511)); //Block.repeaterActive.blockID));
diff --git a/src/main/resources/assets/minecraft/textures/block/furnace_blast_active_front_filled.png b/src/main/resources/assets/minecraft/textures/block/furnace_blast_active_front_filled.png
new file mode 100644
index 000000000..92ba85ebb
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/block/furnace_blast_active_front_filled.png differ
diff --git a/src/main/resources/assets/minecraft/textures/block/furnace_blast_idle_front_filled.png b/src/main/resources/assets/minecraft/textures/block/furnace_blast_idle_front_filled.png
new file mode 100644
index 000000000..4d3802b06
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/block/furnace_blast_idle_front_filled.png differ
diff --git a/src/main/resources/assets/minecraft/textures/block/furnace_stone_active_front_filled.png b/src/main/resources/assets/minecraft/textures/block/furnace_stone_active_front_filled.png
new file mode 100644
index 000000000..524e8b791
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/block/furnace_stone_active_front_filled.png differ
diff --git a/src/main/resources/assets/minecraft/textures/block/furnace_stone_idle_front_filled.png b/src/main/resources/assets/minecraft/textures/block/furnace_stone_idle_front_filled.png
new file mode 100644
index 000000000..459af483c
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/block/furnace_stone_idle_front_filled.png differ
diff --git a/src/main/resources/assets/minecraft/textures/colormap/grass/hell.png b/src/main/resources/assets/minecraft/textures/colormap/grass/hell.png
new file mode 100644
index 000000000..b98fe3b97
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/colormap/grass/hell.png differ
diff --git a/src/main/resources/assets/minecraft/textures/colormap/grass/paradise_gold.png b/src/main/resources/assets/minecraft/textures/colormap/grass/paradise_gold.png
new file mode 100644
index 000000000..c240e73a9
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/colormap/grass/paradise_gold.png differ
diff --git a/src/main/resources/assets/minecraft/textures/colormap/grass/paradise_silver.png b/src/main/resources/assets/minecraft/textures/colormap/grass/paradise_silver.png
new file mode 100644
index 000000000..e0e229a2a
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/colormap/grass/paradise_silver.png differ
diff --git a/src/main/resources/assets/minecraft/textures/colormap/oak/paradise_gold.png b/src/main/resources/assets/minecraft/textures/colormap/oak/paradise_gold.png
new file mode 100644
index 000000000..be210b157
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/colormap/oak/paradise_gold.png differ
diff --git a/src/main/resources/assets/minecraft/textures/colormap/oak/paradise_silver.png b/src/main/resources/assets/minecraft/textures/colormap/oak/paradise_silver.png
new file mode 100644
index 000000000..e0e229a2a
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/colormap/oak/paradise_silver.png differ
diff --git a/src/main/resources/assets/minecraft/textures/colormap/water/nether.png b/src/main/resources/assets/minecraft/textures/colormap/water/nether.png
new file mode 100644
index 000000000..a35b7f7c8
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/colormap/water/nether.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/playstation.png b/src/main/resources/assets/minecraft/textures/gui/controller/ps3.png
similarity index 56%
rename from src/main/resources/assets/minecraft/textures/gui/controller/playstation.png
rename to src/main/resources/assets/minecraft/textures/gui/controller/ps3.png
index 6ade36536..e0123dbff 100644
Binary files a/src/main/resources/assets/minecraft/textures/gui/controller/playstation.png and b/src/main/resources/assets/minecraft/textures/gui/controller/ps3.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/ps4.png b/src/main/resources/assets/minecraft/textures/gui/controller/ps4.png
new file mode 100644
index 000000000..6d1ae1c9d
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/controller/ps4.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/ps5.png b/src/main/resources/assets/minecraft/textures/gui/controller/ps5.png
new file mode 100644
index 000000000..4c55ada85
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/controller/ps5.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/steam.png b/src/main/resources/assets/minecraft/textures/gui/controller/steam.png
new file mode 100644
index 000000000..6cd546f82
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/controller/steam.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/steam_deck.png b/src/main/resources/assets/minecraft/textures/gui/controller/steam_deck.png
new file mode 100644
index 000000000..f2eee7089
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/controller/steam_deck.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/nintendo.png b/src/main/resources/assets/minecraft/textures/gui/controller/switch.png
similarity index 100%
rename from src/main/resources/assets/minecraft/textures/gui/controller/nintendo.png
rename to src/main/resources/assets/minecraft/textures/gui/controller/switch.png
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/template.png b/src/main/resources/assets/minecraft/textures/gui/controller/template.png
new file mode 100644
index 000000000..e4815496d
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/controller/template.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/xbox_360.png b/src/main/resources/assets/minecraft/textures/gui/controller/xbox_360.png
new file mode 100644
index 000000000..4cac4a1fd
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/controller/xbox_360.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/controller/xbox.png b/src/main/resources/assets/minecraft/textures/gui/controller/xbox_one.png
similarity index 100%
rename from src/main/resources/assets/minecraft/textures/gui/controller/xbox.png
rename to src/main/resources/assets/minecraft/textures/gui/controller/xbox_one.png
diff --git a/src/main/resources/assets/minecraft/textures/gui/title/mojang.png b/src/main/resources/assets/minecraft/textures/gui/title/mojang.png
new file mode 100644
index 000000000..829cfc458
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/title/mojang.png differ
diff --git a/src/main/resources/controllers.csv b/src/main/resources/controllers.csv
new file mode 100644
index 000000000..98364fd61
--- /dev/null
+++ b/src/main/resources/controllers.csv
@@ -0,0 +1,532 @@
+0x0079,0x181a,PS3Controller,
+0x0079,0x1844,PS3Controller,
+0x044f,0xb315,PS3Controller,
+0x044f,0xd007,PS3Controller,
+0x046d,0xcad1,PS3Controller,
+0x046d,0xc24f,PS3Controller,
+0x054c,0x0268,PS3Controller,
+0x056e,0x200f,PS3Controller,
+0x056e,0x2013,PS3Controller,
+0x05b8,0x1004,PS3Controller,
+0x05b8,0x1006,PS3Controller,
+0x06a3,0xf622,PS3Controller,
+0x0738,0x3180,PS3Controller,
+0x0738,0x3250,PS3Controller,
+0x0738,0x3481,PS3Controller,
+0x0738,0x8180,PS3Controller,
+0x0738,0x8838,PS3Controller,
+0x0810,0x0001,PS3Controller,
+0x0810,0x0003,PS3Controller,
+0x0925,0x0005,PS3Controller,
+0x0925,0x8866,PS3Controller,
+0x0925,0x8888,PS3Controller,
+0x0e6f,0x0109,PS3Controller,
+0x0e6f,0x011e,PS3Controller,
+0x0e6f,0x0128,PS3Controller,
+0x0e6f,0x0214,PS3Controller,
+0x0e6f,0x1314,PS3Controller,
+0x0e6f,0x6302,PS3Controller,
+0x0e8f,0x0008,PS3Controller,
+0x0e8f,0x3075,PS3Controller,
+0x0e8f,0x310d,PS3Controller,
+0x0f0d,0x0009,PS3Controller,
+0x0f0d,0x004d,PS3Controller,
+0x0f0d,0x005f,PS3Controller,
+0x0f0d,0x006a,PS3Controller,
+0x0f0d,0x006e,PS3Controller,
+0x0f0d,0x0085,PS3Controller,
+0x0f0d,0x0086,PS3Controller,
+0x0f0d,0x0088,PS3Controller,
+0x0f30,0x1100,PS3Controller,
+0x11ff,0x3331,PS3Controller,
+0x1345,0x1000,PS3Controller,
+0x1345,0x6005,PS3Controller,
+0x146b,0x5500,PS3Controller,
+0x1a34,0x0836,PS3Controller,
+0x20bc,0x5500,PS3Controller,
+0x20d6,0x576d,PS3Controller,
+0x20d6,0xca6d,PS3Controller,
+0x2563,0x0523,PS3Controller,
+0x2563,0x0575,PS3Controller,
+0x25f0,0x83c3,PS3Controller,
+0x25f0,0xc121,PS3Controller,
+0x2c22,0x2003,PS3Controller,
+0x2c22,0x2302,PS3Controller,
+0x2c22,0x2502,PS3Controller,
+0x8380,0x0003,PS3Controller,
+0x8888,0x0308,PS3Controller,
+0x0079,0x181b,PS4Controller,
+0x046d,0xc260,PS4Controller,
+0x044f,0xd00e,PS4Controller,
+0x054c,0x05c4,PS4Controller,
+0x054c,0x05c5,PS4Controller,
+0x054c,0x09cc,PS4Controller,
+0x054c,0x0ba0,PS4Controller,
+0x0738,0x8250,PS4Controller,
+0x0738,0x8384,PS4Controller,
+0x0738,0x8480,PS4Controller,
+0x0738,0x8481,PS4Controller,
+0x0c12,0x0e10,PS4Controller,
+0x0c12,0x0e13,PS4Controller,
+0x0c12,0x0e15,PS4Controller,
+0x0c12,0x0e20,PS4Controller,
+0x0c12,0x0ef6,PS4Controller,
+0x0c12,0x1cf6,PS4Controller,
+0x0c12,0x1e10,PS4Controller,
+0x0e6f,0x0203,PS4Controller,
+0x0e6f,0x0207,PS4Controller,
+0x0e6f,0x020a,PS4Controller,
+0x0f0d,0x0055,PS4Controller,
+0x0f0d,0x005e,PS4Controller,
+0x0f0d,0x0066,PS4Controller,
+0x0f0d,0x0084,PS4Controller,
+0x0f0d,0x0087,PS4Controller,
+0x0f0d,0x008a,PS4Controller,
+0x0f0d,0x009c,PS4Controller,
+0x0f0d,0x00a0,PS4Controller,
+0x0f0d,0x00ed,PS4Controller,
+0x0f0d,0x00ee,PS4Controller,
+0x0f0d,0x011c,PS4Controller,
+0x0f0d,0x0123,PS4Controller,
+0x0f0d,0x0162,PS4Controller,
+0x0f0d,0x0164,PS4Controller,
+0x11c0,0x4001,PS4Controller,
+0x146b,0x0603,PS4Controller,
+0x146b,0x0604,PS4Controller,
+0x146b,0x0605,PS4Controller,
+0x146b,0x0606,PS4Controller,
+0x146b,0x0609,PS4Controller,
+0x146b,0x0d01,PS4Controller,
+0x146b,0x0d02,PS4Controller,
+0x146b,0x0d06,PS4Controller,
+0x146b,0x0d08,PS4Controller,
+0x146b,0x0d09,PS4Controller,
+0x146b,0x0d10,PS4Controller,
+0x146b,0x0d10,PS4Controller,
+0x146b,0x0d13,PS4Controller,
+0x146b,0x1103,PS4Controller,
+0x1532,0x0401,PS4Controller,
+0x1532,0x1000,PS4Controller,
+0x1532,0x1004,PS4Controller,
+0x1532,0x1007,PS4Controller,
+0x1532,0x1008,PS4Controller,
+0x1532,0x1009,PS4Controller,
+0x1532,0x100A,PS4Controller,
+0x1532,0x1100,PS4Controller,
+0x20d6,0x792a,PS4Controller,
+0x2c22,0x2000,PS4Controller,
+0x2c22,0x2300,PS4Controller,
+0x2c22,0x2303,PS4Controller,
+0x2c22,0x2500,PS4Controller,
+0x2c22,0x2503,PS4Controller,
+0x3285,0x0d16,PS4Controller,
+0x3285,0x0d17,PS4Controller,
+0x7545,0x0104,PS4Controller,
+0x9886,0x0025,PS4Controller,
+0x054c,0x0ce6,PS5Controller,
+0x054c,0x0df2,PS5Controller,
+0x054c,0x0e5f,PS5Controller,
+0x0e6f,0x0209,PS5Controller,
+0x0f0d,0x0163,PS5Controller,
+0x0f0d,0x0184,PS5Controller,
+0x1532,0x100b,PS5Controller,
+0x1532,0x100c,PS5Controller,
+0x1532,0x1012,PS5Controller,
+0x3285,0x0d18,PS5Controller,
+0x3285,0x0d19,PS5Controller,
+0x358a,0x0104,PS5Controller,
+0x0079,0x18d4,XBox360Controller,
+0x03eb,0xff02,XBox360Controller,
+0x044f,0xb326,XBox360Controller,
+0x045e,0x028e,XBox360Controller,
+0x045e,0x028f,XBox360Controller,
+0x045e,0x0291,XBox360Controller,
+0x045e,0x02a0,XBox360Controller,
+0x045e,0x02a1,XBox360Controller,
+0x045e,0x02a9,XBox360Controller,
+0x045e,0x0719,XBox360Controller,
+0x046d,0xc21d,XBox360Controller,
+0x046d,0xc21e,XBox360Controller,
+0x046d,0xc21f,XBox360Controller,
+0x046d,0xc242,XBox360Controller,
+0x056e,0x2004,XBox360Controller,
+0x0738,0x4716,XBox360Controller,
+0x0738,0x4718,XBox360Controller,
+0x0738,0x4726,XBox360Controller,
+0x0738,0x4728,XBox360Controller,
+0x0738,0x4736,XBox360Controller,
+0x0738,0x4738,XBox360Controller,
+0x0738,0x4740,XBox360Controller,
+0x0738,0xb726,XBox360Controller,
+0x0738,0xbeef,XBox360Controller,
+0x0738,0xcb02,XBox360Controller,
+0x0738,0xcb03,XBox360Controller,
+0x0738,0xf738,XBox360Controller,
+0x0955,0x7210,XBox360Controller,
+0x0955,0xb400,XBox360Controller,
+0x0e6f,0x0105,XBox360Controller,
+0x0e6f,0x0113,XBox360Controller,
+0x0e6f,0x011f,XBox360Controller,
+0x0e6f,0x0125,XBox360Controller,
+0x0e6f,0x0127,XBox360Controller,
+0x0e6f,0x0131,XBox360Controller,
+0x0e6f,0x0133,XBox360Controller,
+0x0e6f,0x0143,XBox360Controller,
+0x0e6f,0x0147,XBox360Controller,
+0x0e6f,0x0201,XBox360Controller,
+0x0e6f,0x0213,XBox360Controller,
+0x0e6f,0x021f,XBox360Controller,
+0x0e6f,0x0301,XBox360Controller,
+0x0e6f,0x0313,XBox360Controller,
+0x0e6f,0x0314,XBox360Controller,
+0x0e6f,0x0401,XBox360Controller,
+0x0e6f,0x0413,XBox360Controller,
+0x0e6f,0x0501,XBox360Controller,
+0x0e6f,0xf900,XBox360Controller,
+0x0f0d,0x000a,XBox360Controller,
+0x0f0d,0x000c,XBox360Controller,
+0x0f0d,0x000d,XBox360Controller,
+0x0f0d,0x0016,XBox360Controller,
+0x0f0d,0x001b,XBox360Controller,
+0x0f0d,0x008c,XBox360Controller,
+0x0f0d,0x00db,XBox360Controller,
+0x0f0d,0x011e,XBox360Controller,
+0x1038,0x1430,XBox360Controller,
+0x1038,0x1431,XBox360Controller,
+0x1038,0xb360,XBox360Controller,
+0x11c9,0x55f0,XBox360Controller,
+0x12ab,0x0004,XBox360Controller,
+0x12ab,0x0301,XBox360Controller,
+0x12ab,0x0303,XBox360Controller,
+0x1430,0x02a0,XBox360Controller,
+0x1430,0x4748,XBox360Controller,
+0x1430,0xf801,XBox360Controller,
+0x146b,0x0601,XBox360Controller,
+0x15e4,0x3f00,XBox360Controller,
+0x15e4,0x3f0a,XBox360Controller,
+0x15e4,0x3f10,XBox360Controller,
+0x162e,0xbeef,XBox360Controller,
+0x1689,0xfd00,XBox360Controller,
+0x1689,0xfd01,XBox360Controller,
+0x1689,0xfe00,XBox360Controller,
+0x1949,0x041a,XBox360Controller,
+0x1bad,0x0002,XBox360Controller,
+0x1bad,0x0003,XBox360Controller,
+0x1bad,0xf016,XBox360Controller,
+0x1bad,0xf018,XBox360Controller,
+0x1bad,0xf019,XBox360Controller,
+0x1bad,0xf021,XBox360Controller,
+0x1bad,0xf023,XBox360Controller,
+0x1bad,0xf025,XBox360Controller,
+0x1bad,0xf027,XBox360Controller,
+0x1bad,0xf028,XBox360Controller,
+0x1bad,0xf02e,XBox360Controller,
+0x1bad,0xf036,XBox360Controller,
+0x1bad,0xf038,XBox360Controller,
+0x1bad,0xf039,XBox360Controller,
+0x1bad,0xf03a,XBox360Controller,
+0x1bad,0xf03d,XBox360Controller,
+0x1bad,0xf03e,XBox360Controller,
+0x1bad,0xf03f,XBox360Controller,
+0x1bad,0xf042,XBox360Controller,
+0x1bad,0xf080,XBox360Controller,
+0x1bad,0xf501,XBox360Controller,
+0x1bad,0xf502,XBox360Controller,
+0x1bad,0xf503,XBox360Controller,
+0x1bad,0xf504,XBox360Controller,
+0x1bad,0xf505,XBox360Controller,
+0x1bad,0xf506,XBox360Controller,
+0x1bad,0xf900,XBox360Controller,
+0x1bad,0xf901,XBox360Controller,
+0x1bad,0xf902,XBox360Controller,
+0x1bad,0xf903,XBox360Controller,
+0x1bad,0xf904,XBox360Controller,
+0x1bad,0xf906,XBox360Controller,
+0x1bad,0xfa01,XBox360Controller,
+0x1bad,0xfd00,XBox360Controller,
+0x1bad,0xfd01,XBox360Controller,
+0x24c6,0x5000,XBox360Controller,
+0x24c6,0x5300,XBox360Controller,
+0x24c6,0x5303,XBox360Controller,
+0x24c6,0x530a,XBox360Controller,
+0x24c6,0x531a,XBox360Controller,
+0x24c6,0x5397,XBox360Controller,
+0x24c6,0x5500,XBox360Controller,
+0x24c6,0x5501,XBox360Controller,
+0x24c6,0x5502,XBox360Controller,
+0x24c6,0x5503,XBox360Controller,
+0x24c6,0x5506,XBox360Controller,
+0x24c6,0x550d,XBox360Controller,
+0x24c6,0x550e,XBox360Controller,
+0x24c6,0x5508,XBox360Controller,
+0x24c6,0x5510,XBox360Controller,
+0x24c6,0x5b00,XBox360Controller,
+0x24c6,0x5b02,XBox360Controller,
+0x24c6,0x5b03,XBox360Controller,
+0x24c6,0x5d04,XBox360Controller,
+0x24c6,0xfafa,XBox360Controller,
+0x24c6,0xfafb,XBox360Controller,
+0x24c6,0xfafc,XBox360Controller,
+0x24c6,0xfafd,XBox360Controller,
+0x24c6,0xfafe,XBox360Controller,
+0x03f0,0x0495,XBoxOneController,
+0x044f,0xd012,XBoxOneController,
+0x045e,0x02d1,XBoxOneController,
+0x045e,0x02dd,XBoxOneController,
+0x045e,0x02e0,XBoxOneController,
+0x045e,0x02e3,XBoxOneController,
+0x045e,0x02ea,XBoxOneController,
+0x045e,0x02fd,XBoxOneController,
+0x045e,0x02ff,XBoxOneController,
+0x045e,0x0b00,XBoxOneController,
+0x045e,0x0b05,XBoxOneController,
+0x045e,0x0b0a,XBoxOneController,
+0x045e,0x0b0c,XBoxOneController,
+0x045e,0x0b12,XBoxOneController,
+0x045e,0x0b13,XBoxOneController,
+0x045e,0x0b20,XBoxOneController,
+0x045e,0x0b21,XBoxOneController,
+0x045e,0x0b22,XBoxOneController,
+0x0738,0x4a01,XBoxOneController,
+0x0e6f,0x0139,XBoxOneController,
+0x0e6f,0x013B,XBoxOneController,
+0x0e6f,0x013a,XBoxOneController,
+0x0e6f,0x0145,XBoxOneController,
+0x0e6f,0x0146,XBoxOneController,
+0x0e6f,0x015b,XBoxOneController,
+0x0e6f,0x015c,XBoxOneController,
+0x0e6f,0x015d,XBoxOneController,
+0x0e6f,0x015f,XBoxOneController,
+0x0e6f,0x0160,XBoxOneController,
+0x0e6f,0x0161,XBoxOneController,
+0x0e6f,0x0162,XBoxOneController,
+0x0e6f,0x0163,XBoxOneController,
+0x0e6f,0x0164,XBoxOneController,
+0x0e6f,0x0165,XBoxOneController,
+0x0e6f,0x0166,XBoxOneController,
+0x0e6f,0x0167,XBoxOneController,
+0x0e6f,0x0205,XBoxOneController,
+0x0e6f,0x0206,XBoxOneController,
+0x0e6f,0x0246,XBoxOneController,
+0x0e6f,0x0261,XBoxOneController,
+0x0e6f,0x0262,XBoxOneController,
+0x0e6f,0x02a0,XBoxOneController,
+0x0e6f,0x02a1,XBoxOneController,
+0x0e6f,0x02a2,XBoxOneController,
+0x0e6f,0x02a3,XBoxOneController,
+0x0e6f,0x02a4,XBoxOneController,
+0x0e6f,0x02a5,XBoxOneController,
+0x0e6f,0x02a6,XBoxOneController,
+0x0e6f,0x02a7,XBoxOneController,
+0x0e6f,0x02a8,XBoxOneController,
+0x0e6f,0x02a9,XBoxOneController,
+0x0e6f,0x02aa,XBoxOneController,
+0x0e6f,0x02ab,XBoxOneController,
+0x0e6f,0x02ac,XBoxOneController,
+0x0e6f,0x02ad,XBoxOneController,
+0x0e6f,0x02ae,XBoxOneController,
+0x0e6f,0x02af,XBoxOneController,
+0x0e6f,0x02b0,XBoxOneController,
+0x0e6f,0x02b1,XBoxOneController,
+0x0e6f,0x02b3,XBoxOneController,
+0x0e6f,0x02b5,XBoxOneController,
+0x0e6f,0x02b6,XBoxOneController,
+0x0e6f,0x02bd,XBoxOneController,
+0x0e6f,0x02be,XBoxOneController,
+0x0e6f,0x02bf,XBoxOneController,
+0x0e6f,0x02c0,XBoxOneController,
+0x0e6f,0x02c1,XBoxOneController,
+0x0e6f,0x02c2,XBoxOneController,
+0x0e6f,0x02c3,XBoxOneController,
+0x0e6f,0x02c4,XBoxOneController,
+0x0e6f,0x02c5,XBoxOneController,
+0x0e6f,0x02c6,XBoxOneController,
+0x0e6f,0x02c7,XBoxOneController,
+0x0e6f,0x02c8,XBoxOneController,
+0x0e6f,0x02c9,XBoxOneController,
+0x0e6f,0x02ca,XBoxOneController,
+0x0e6f,0x02cb,XBoxOneController,
+0x0e6f,0x02cd,XBoxOneController,
+0x0e6f,0x02ce,XBoxOneController,
+0x0e6f,0x02cf,XBoxOneController,
+0x0e6f,0x02d5,XBoxOneController,
+0x0e6f,0x0346,XBoxOneController,
+0x0e6f,0x0446,XBoxOneController,
+0x0e6f,0x02da,XBoxOneController,
+0x0e6f,0x02d6,XBoxOneController,
+0x0e6f,0x02d9,XBoxOneController,
+0x0f0d,0x0063,XBoxOneController,
+0x0f0d,0x0067,XBoxOneController,
+0x0f0d,0x0078,XBoxOneController,
+0x0f0d,0x00c5,XBoxOneController,
+0x0f0d,0x0150,XBoxOneController,
+0x10f5,0x7009,XBoxOneController,
+0x10f5,0x7013,XBoxOneController,
+0x1532,0x0a00,XBoxOneController,
+0x1532,0x0a03,XBoxOneController,
+0x1532,0x0a14,XBoxOneController,
+0x1532,0x0a15,XBoxOneController,
+0x20d6,0x2001,XBoxOneController,
+0x20d6,0x2002,XBoxOneController,
+0x20d6,0x2003,XBoxOneController,
+0x20d6,0x2004,XBoxOneController,
+0x20d6,0x2005,XBoxOneController,
+0x20d6,0x2006,XBoxOneController,
+0x20d6,0x2009,XBoxOneController,
+0x20d6,0x200a,XBoxOneController,
+0x20d6,0x200b,XBoxOneController,
+0x20d6,0x200c,XBoxOneController,
+0x20d6,0x200d,XBoxOneController,
+0x20d6,0x200e,XBoxOneController,
+0x20d6,0x200f,XBoxOneController,
+0x20d6,0x2011,XBoxOneController,
+0x20d6,0x2012,XBoxOneController,
+0x20d6,0x2015,XBoxOneController,
+0x20d6,0x2016,XBoxOneController,
+0x20d6,0x2017,XBoxOneController,
+0x20d6,0x2018,XBoxOneController,
+0x20d6,0x2019,XBoxOneController,
+0x20d6,0x201a,XBoxOneController,
+0x20d6,0x4001,XBoxOneController,
+0x20d6,0x4002,XBoxOneController,
+0x24c6,0x541a,XBoxOneController,
+0x24c6,0x542a,XBoxOneController,
+0x24c6,0x543a,XBoxOneController,
+0x24c6,0x551a,XBoxOneController,
+0x24c6,0x561a,XBoxOneController,
+0x24c6,0x581a,XBoxOneController,
+0x24c6,0x591a,XBoxOneController,
+0x24c6,0x592a,XBoxOneController,
+0x24c6,0x791a,XBoxOneController,
+0x2dc8,0x2002,XBoxOneController,
+0x2e24,0x0652,XBoxOneController,
+0x2e24,0x1618,XBoxOneController,
+0x2e24,0x1688,XBoxOneController,
+0x146b,0x0611,XBoxOneController,
+0x0000,0x0000,XBox360Controller,
+0x045e,0x02a2,XBox360Controller,
+0x0e6f,0x1414,XBox360Controller,
+0x0e6f,0x0159,XBox360Controller,
+0x24c6,0xfaff,XBox360Controller,
+0x0f0d,0x006d,XBox360Controller,
+0x0f0d,0x00a4,XBox360Controller,
+0x0079,0x1832,XBox360Controller,
+0x0079,0x187f,XBox360Controller,
+0x0079,0x1883,XBox360Controller,
+0x03eb,0xff01,XBox360Controller,
+0x0c12,0x0ef8,XBox360Controller,
+0x046d,0x1000,XBox360Controller,
+0x1345,0x6006,XBox360Controller,
+0x056e,0x2012,XBox360Controller,
+0x146b,0x0602,XBox360Controller,
+0x0f0d,0x00ae,XBox360Controller,
+0x046d,0x0401,XBox360Controller,
+0x046d,0x0301,XBox360Controller,
+0x046d,0xcaa3,XBox360Controller,
+0x046d,0xc261,XBox360Controller,
+0x046d,0x0291,XBox360Controller,
+0x0079,0x18d3,XBox360Controller,
+0x0f0d,0x00b1,XBox360Controller,
+0x0001,0x0001,XBox360Controller,
+0x0079,0x188e,XBox360Controller,
+0x0079,0x187c,XBox360Controller,
+0x0079,0x189c,XBox360Controller,
+0x0079,0x1874,XBox360Controller,
+0x2f24,0x0050,XBoxOneController,
+0x2f24,0x002e,XBoxOneController,
+0x2f24,0x0091,XBoxOneController,
+0x1430,0x0719,XBoxOneController,
+0x0f0d,0x00ed,XBoxOneController,
+0x0f0d,0x00c0,XBoxOneController,
+0x0e6f,0x0152,XBoxOneController,
+0x0e6f,0x02a7,XBoxOneController,
+0x046d,0x1007,XBoxOneController,
+0x0e6f,0x02b8,XBoxOneController,
+0x0e6f,0x02a8,XBoxOneController,
+0x0079,0x18a1,XBoxOneController,
+0x0000,0x6686,XBoxOneController,
+0x11ff,0x0511,XBoxOneController,
+0x12ab,0x0304,XBoxOneController,
+0x1430,0x0291,XBoxOneController,
+0x1430,0x02a9,XBoxOneController,
+0x1430,0x070b,XBoxOneController,
+0x1bad,0x028e,XBoxOneController,
+0x1bad,0x02a0,XBoxOneController,
+0x1bad,0x5500,XBoxOneController,
+0x20ab,0x55ef,XBoxOneController,
+0x24c6,0x5509,XBoxOneController,
+0x2516,0x0069,XBoxOneController,
+0x25b1,0x0360,XBoxOneController,
+0x2c22,0x2203,XBoxOneController,
+0x2f24,0x0011,XBoxOneController,
+0x2f24,0x0053,XBoxOneController,
+0x2f24,0x00b7,XBoxOneController,
+0x046d,0x0000,XBoxOneController,
+0x046d,0x1004,XBoxOneController,
+0x046d,0x1008,XBoxOneController,
+0x046d,0xf301,XBoxOneController,
+0x0738,0x02a0,XBoxOneController,
+0x0738,0x7263,XBoxOneController,
+0x0738,0xb738,XBoxOneController,
+0x0738,0xcb29,XBoxOneController,
+0x0738,0xf401,XBoxOneController,
+0x0079,0x18c2,XBoxOneController,
+0x0079,0x18c8,XBoxOneController,
+0x0079,0x18cf,XBoxOneController,
+0x0c12,0x0e17,XBoxOneController,
+0x0c12,0x0e1c,XBoxOneController,
+0x0c12,0x0e22,XBoxOneController,
+0x0c12,0x0e30,XBoxOneController,
+0xd2d2,0xd2d2,XBoxOneController,
+0x0d62,0x9a1a,XBoxOneController,
+0x0d62,0x9a1b,XBoxOneController,
+0x0e00,0x0e00,XBoxOneController,
+0x0e6f,0x012a,XBoxOneController,
+0x0e6f,0x02a1,XBoxOneController,
+0x0e6f,0x02a2,XBoxOneController,
+0x0e6f,0x02a5,XBoxOneController,
+0x0e6f,0x02b2,XBoxOneController,
+0x0e6f,0x02bd,XBoxOneController,
+0x0e6f,0x02bf,XBoxOneController,
+0x0e6f,0x02c0,XBoxOneController,
+0x0e6f,0x02c6,XBoxOneController,
+0x0f0d,0x0097,XBoxOneController,
+0x0f0d,0x00ba,XBoxOneController,
+0x0f0d,0x00d8,XBoxOneController,
+0x0fff,0x02a1,XBoxOneController,
+0x045e,0x0867,XBoxOneController,
+0x16d0,0x0f3f,XBoxOneController,
+0x2f24,0x008f,XBoxOneController,
+0x0e6f,0xf501,XBoxOneController,
+0x057e,0x2008,SwitchController,
+0x057e,0x2009,SwitchController,
+0x057e,0x2017,SwitchController,
+0x057e,0x2019,SwitchController,
+0x057e,0x201e,SwitchController,
+0x0f0d,0x00c1,SwitchController,
+0x0f0d,0x0092,SwitchController,
+0x0f0d,0x00f6,SwitchController,
+0x0f0d,0x00dc,SwitchController,
+0x0e6f,0x0180,SwitchController,
+0x0e6f,0x0181,SwitchController,
+0x0e6f,0x0184,SwitchController,
+0x0e6f,0x0185,SwitchController,
+0x0e6f,0x0186,SwitchController,
+0x0e6f,0x0187,SwitchController,
+0x0e6f,0x0188,SwitchController,
+0x0f0d,0x00aa,SwitchController,
+0x20d6,0xa711,SwitchController,
+0x20d6,0xa712,SwitchController,
+0x20d6,0xa713,SwitchController,
+0x20d6,0xa714,SwitchController,
+0x20d6,0xa715,SwitchController,
+0x20d6,0xa716,SwitchController,
+0x28de,0x1101,SteamController,
+0x28de,0x1102,SteamController,
+0x28de,0x1105,SteamController,
+0x28de,0x1106,SteamController,
+0x28de,0x11ff,SteamDeckController,
+0x28de,0x1142,SteamController,
+0x28de,0x1201,SteamController,
+0x28de,0x1202,SteamController,
+0x28de,0x1205,SteamDeckController,
