diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBiped.java b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBiped.java
index 6b9b0d62e..c881e28bb 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBiped.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBiped.java
@@ -34,6 +34,7 @@ public abstract class MobRendererBiped<T extends Mob> extends MobRenderer<T> {
         final BoneTransform leftArm = model.getTransform("leftArm");
         final BoneTransform rightLeg = model.getTransform("rightLeg");
         final BoneTransform leftLeg = model.getTransform("leftLeg");
+        final BoneTransform torso = model.getTransform("torso");
         final BoneTransform waist = model.getTransform("waist");
         final BoneTransform head = model.getTransform("head");
 
@@ -42,7 +43,7 @@ public abstract class MobRendererBiped<T extends Mob> extends MobRenderer<T> {
         final float headPitch = getHeadPitch(entity, partialTick);
         final float limbSwing = getLimbSwing(entity, partialTick);
         final float limbYaw = getLimbYaw(entity, partialTick);
-        final float limbPitch = getLimbPitch(entity, partialTick);
+        float limbPitch = getLimbPitch(entity, partialTick);
         final float swingProgress = entity.getSwingProgress(partialTick);
 
         head.rotX = headPitch;
@@ -51,6 +52,7 @@ public abstract class MobRendererBiped<T extends Mob> extends MobRenderer<T> {
             final double rotX = Math.cos(limbSwing / 2 * 0.6662F) * 2.0F * limbYaw * 0.125F - 0.65f;
             rightArm.rotX = rotX;
             leftArm.rotX = rotX;
+            limbPitch = 180;
         } else {
             rightArm.rotX = Math.cos(limbSwing * 0.6662F + Math.PI) * 2.0F * limbYaw * 0.5F;
             leftArm.rotX = Math.cos(limbSwing * 0.6662F) * 2.0F * limbYaw * 0.5F;
@@ -77,43 +79,50 @@ public abstract class MobRendererBiped<T extends Mob> extends MobRenderer<T> {
             }
 
         }
-        if (entity instanceof IItemHolding && ((IItemHolding) entity).isLeftHanded()) {
-            waist.rotY = Math.sin(Math.sqrt(swingProgress) * Math.PI * -2.0F) * 0.2F;
+        if (entity.getHeldObject() != null) {
+            final double rotX = Math.sin(Math.sqrt(swingProgress) * Math.PI * -2.0F) * 0.4F;
+            rightArm.rotX += rotX;
+            leftArm.rotX += rotX;
+        }
+        else if (entity instanceof IItemHolding && ((IItemHolding) entity).isLeftHanded()) {
+            torso.rotY = Math.sin(Math.sqrt(swingProgress) * Math.PI * -2.0F) * 0.2F;
 
             float f6 = 1.0F - swingProgress;
             f6 *= f6;
             f6 *= f6;
             f6 = 1.0F - f6;
             final double f7 = Math.sin(f6 * Math.PI);
-            final double f8 = Math.sin(swingProgress * Math.PI) * -(Math.toRadians(head.rotX) - 0.7F) * 0.75F;
+            final double f8 = Math.sin(swingProgress * Math.PI) * -(Math.toRadians(headPitch) - 0.7F) * 0.75F;
 
             leftArm.rotX -= f7 * 1.2D + f8;
-            leftArm.rotY += waist.rotY * 2.0F;
+            leftArm.rotY += torso.rotY * 2.0F;
             leftArm.rotZ = Math.sin(swingProgress * Math.PI) * 0.4F;
         } else {
-            waist.rotY = Math.sin(Math.sqrt(swingProgress) * Math.PI * 2.0F) * 0.2F;
+            torso.rotY = Math.sin(Math.sqrt(swingProgress) * Math.PI * 2.0F) * 0.2F;
 
             float f6 = 1.0F - swingProgress;
             f6 *= f6;
             f6 *= f6;
             f6 = 1.0F - f6;
             final double f7 = Math.sin(f6 * Math.PI);
-            final double f8 = Math.sin(swingProgress * Math.PI) * -(Math.toRadians(head.rotX) - 0.7F) * 0.75F;
+            final double f8 = Math.sin(swingProgress * Math.PI) * -(Math.toRadians(headPitch) - 0.7F) * 0.75F;
 
             rightArm.rotX -= f7 * 1.2D + f8;
-            rightArm.rotY += waist.rotY * 2.0F;
+            rightArm.rotY += torso.rotY * 2.0F;
             rightArm.rotZ = Math.sin(swingProgress * Math.PI) * -0.4F;
         }
 
         if(entity.isSneaking())
         {
-            waist.rotX = 0.5F;
-            leftLeg.rotX -= 0.5F;
-            rightLeg.rotX -= 0.5F;
-            head.rotX -= 0.5F;
+            leftLeg.posZ += 5;
+            rightLeg.posZ += 5;
+            head.posY -= 2;
+            torso.posY -= 2;
+            torso.rotX = 0.5F;
+
 
-            rightArm.rotX -= 0.4F;
-            leftArm.rotX -= 0.4F;
+            rightArm.rotX -= 0.2F;
+            leftArm.rotX -= 0.2F;
             if (entity.getHeldObject() != null) {
                 rightArm.rotX -= 0.4f;
                 leftArm.rotX -= 0.4f;
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBipedArmored.java b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBipedArmored.java
index 546de215a..8cc98bc95 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBipedArmored.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererBipedArmored.java
@@ -105,7 +105,8 @@ public abstract class MobRendererBipedArmored<T extends Mob & IArmorWearing<Huma
             if (!(stack.getItem() instanceof IArmorItem)) {
                 GL11.glPushMatrix();
                 GL11.glEnable(GL11.GL_CULL_FACE);
-                GL11.glScalef(-0.6f, -0.6f, 0.6f);
+                GL11.glScalef(-16, 16, 16);
+                GL11.glScalef(0.6f, 0.6f, 0.6f);
                 GL11.glTranslatef(0, 7/16f, 0);
                 ItemModelDispatcher.getInstance().getDispatch(stack).render(Tessellator.instance, null, stack, DisplayPos.HEAD, true, 1, LightmapHelper.isLightmapEnabled() ? 1f : entity.getBrightness(partialTick), 1f, partialTick, true);
                 GL11.glDisable(GL11.GL_CULL_FACE);
diff --git a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererPlayer.java b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererPlayer.java
index 2a77e8ebb..5c5ae9313 100644
--- a/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererPlayer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/entity/MobRendererPlayer.java
@@ -252,7 +252,14 @@ public class MobRendererPlayer extends MobRendererBipedArmored<Player>
         if (player.getHeldObject() != null) {
             final float walkSpeed = player.walkAnimSpeedO + (player.walkAnimSpeed - player.walkAnimSpeedO) * partialTick;
             final float walkProgress = player.walkAnimPos - player.walkAnimSpeed * (1.0F - partialTick);
-            GL11.glTranslatef(0, MathHelper.cos(walkProgress/2 * 2/3f)/26f * walkSpeed + 0.1f, 0);
+            final float swingProgress = player.getSwingProgress(partialTick);
+            final float swing = MathHelper.sin(MathHelper.sqrt_float(swingProgress) * MathHelper.PI * -2.0F) * 0.4F;
+            GL11.glTranslatef(0, (MathHelper.cos(walkProgress/2 * 2/3f + MathHelper.PI)/12f * walkSpeed + 0.1f - swing/4f) * ENTITY_RENDER_SCALE, 0);
+            GL11.glTranslatef(0, 1.25f * ENTITY_RENDER_SCALE, 0);
+            if (player.isSneaking()) {
+                GL11.glTranslatef(0, 0, 4);
+            }
+            GL11.glScalef(-ENTITY_RENDER_SCALE, -ENTITY_RENDER_SCALE, ENTITY_RENDER_SCALE);
             drawHeldObject(player, partialTick);
         }
     }
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
index 6a77c86eb..9754a6056 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
@@ -99,7 +99,7 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
 
         GL11.glPushMatrix();
         final float scale2 = 0.2666667f * scale;
-         GL11.glTranslatef(0.0F, 20 * scale, 1.1f * scale);
+         GL11.glTranslatef(0.0F, height * scale, 1.1f * scale);
          GL11.glScalef(scale2, -scale2, scale2);
          GL11.glDepthMask(false);
         int color = Colors.allSignColors[tileEntity.getColor().id].getARGB();
diff --git a/game/client/src/main/resources/assets/minecraft/models/entity/pigzombie/pigzombie.geo.json b/game/client/src/main/resources/assets/minecraft/models/entity/pigzombie/pigzombie.geo.json
index 5ad4f03a7..1cd0c524d 100644
--- a/game/client/src/main/resources/assets/minecraft/models/entity/pigzombie/pigzombie.geo.json
+++ b/game/client/src/main/resources/assets/minecraft/models/entity/pigzombie/pigzombie.geo.json
@@ -12,8 +12,13 @@
 				"pivot": [0, 12, 0]
 			},
 			{
-				"name": "body",
+				"name": "torso",
 				"parent": "waist",
+				"pivot": [0, 24, 0]
+			},
+			{
+				"name": "body",
+				"parent": "torso",
 				"pivot": [0, 24, 0],
 				"cubes": [
 					{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
@@ -29,19 +34,9 @@
 				"parent": "body",
 				"pivot": [0, 24, 0]
 			},
-			{
-				"name": "head",
-				"parent": "waist",
-				"pivot": [0, 24, 0],
-				"cubes": [
-					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]},
-					{"origin": [-2, 25, -5], "size": [4, 3, 1], "uv": [24, 0]},
-					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
-				]
-			},
 			{
 				"name": "rightArm",
-				"parent": "waist",
+				"parent": "torso",
 				"pivot": [-5, 22, 0],
 				"cubes": [
 					{"origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]}
@@ -60,7 +55,7 @@
 			},
 			{
 				"name": "leftArm",
-				"parent": "waist",
+				"parent": "torso",
 				"pivot": [5, 22, 0],
 				"mirror": true,
 				"cubes": [
@@ -79,6 +74,16 @@
 				"pivot": [6, 12, -2],
 				"rotation": [-90, 180, 0]
 			},
+			{
+				"name": "head",
+				"parent": "waist",
+				"pivot": [0, 24, 0],
+				"cubes": [
+					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]},
+					{"origin": [-2, 25, -5], "size": [4, 3, 1], "uv": [24, 0]},
+					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
+				]
+			},
 			{
 				"name": "rightLeg",
 				"parent": "waist",
diff --git a/game/client/src/main/resources/assets/minecraft/models/entity/player/player.geo.json b/game/client/src/main/resources/assets/minecraft/models/entity/player/player.geo.json
index 436285126..848b8387d 100644
--- a/game/client/src/main/resources/assets/minecraft/models/entity/player/player.geo.json
+++ b/game/client/src/main/resources/assets/minecraft/models/entity/player/player.geo.json
@@ -72,6 +72,11 @@
 				{
 					"name": "body",
 					"parent": "waist",
+					"pivot": [0, 24, 0]
+				},
+				{
+					"name": "torso",
+					"parent": "body",
 					"pivot": [0, 24, 0],
 					"cubes": [
 						{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
@@ -79,31 +84,15 @@
 				},
 				{
 					"name": "jacket",
-					"parent": "body",
+					"parent": "torso",
 					"pivot": [0, 18, 0],
 					"cubes": [
 						{"origin": [-4, 12, -2], "size": [8, 12, 4], "inflate": 0.25, "uv": [16, 32]}
 					]
 				},
-				{
-					"name": "head",
-					"parent": "body",
-					"pivot": [0, 24, 0],
-					"cubes": [
-						{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
-					]
-				},
-				{
-					"name": "hat",
-					"parent": "head",
-					"pivot": [0, 28, 0],
-					"cubes": [
-						{"origin": [-4, 24, -4], "size": [8, 8, 8], "inflate": 0.25, "uv": [32, 0]}
-					]
-				},
 				{
 					"name": "rightArm",
-					"parent": "body",
+					"parent": "torso",
 					"pivot": [-5, 22, 0],
 					"cubes": [
 						{"origin": [-7, 12, -2], "size": [3, 12, 4], "uv": [40, 16]}
@@ -125,7 +114,7 @@
 				},
 				{
 					"name": "leftArm",
-					"parent": "body",
+					"parent": "torso",
 					"pivot": [5, 22, 0],
 					"cubes": [
 						{"origin": [4, 12, -2], "size": [3, 12, 4], "uv": [32, 48]}
@@ -145,6 +134,27 @@
 					"pivot": [5.5, 12, -2],
 					"rotation": [-90, 180, 0]
 				},
+				{
+					"name": "cape",
+					"parent": "torso",
+					"pivot": [0, 24, 2.5]
+				},
+				{
+					"name": "head",
+					"parent": "body",
+					"pivot": [0, 24, 0],
+					"cubes": [
+						{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
+					]
+				},
+				{
+					"name": "hat",
+					"parent": "head",
+					"pivot": [0, 28, 0],
+					"cubes": [
+						{"origin": [-4, 24, -4], "size": [8, 8, 8], "inflate": 0.25, "uv": [32, 0]}
+					]
+				},
 				{
 					"name": "rightLeg",
 					"parent": "body",
@@ -176,11 +186,6 @@
 					"cubes": [
 						{"origin": [0, 0, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [0, 48]}
 					]
-				},
-				{
-					"name": "cape",
-					"parent": "body",
-					"pivot": [0, 24, 2.5]
 				}
 			]
 		},
@@ -255,6 +260,11 @@
 				{
 					"name": "body",
 					"parent": "waist",
+					"pivot": [0, 24, 0]
+				},
+				{
+					"name": "torso",
+					"parent": "body",
 					"pivot": [0, 24, 0],
 					"cubes": [
 						{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
@@ -262,31 +272,15 @@
 				},
 				{
 					"name": "jacket",
-					"parent": "body",
+					"parent": "torso",
 					"pivot": [0, 18, 0],
 					"cubes": [
 						{"origin": [-4, 12, -2], "size": [8, 12, 4], "inflate": 0.25, "uv": [16, 32]}
 					]
 				},
-				{
-					"name": "head",
-					"parent": "body",
-					"pivot": [0, 24, 0],
-					"cubes": [
-						{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
-					]
-				},
-				{
-					"name": "hat",
-					"parent": "head",
-					"pivot": [0, 28, 0],
-					"cubes": [
-						{"origin": [-4, 24, -4], "size": [8, 8, 8], "inflate": 0.25, "uv": [32, 0]}
-					]
-				},
 				{
 					"name": "rightArm",
-					"parent": "body",
+					"parent": "torso",
 					"pivot": [-5, 22, 0],
 					"cubes": [
 						{"origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]}
@@ -308,7 +302,7 @@
 				},
 				{
 					"name": "leftArm",
-					"parent": "body",
+					"parent": "torso",
 					"pivot": [5, 22, 0],
 					"cubes": [
 						{"origin": [4, 12, -2], "size": [4, 12, 4], "uv": [32, 48]}
@@ -328,6 +322,27 @@
 					"pivot": [6, 12, -2],
 					"rotation": [-90, 180, 0]
 				},
+				{
+					"name": "cape",
+					"parent": "torso",
+					"pivot": [0, 24, 2.5]
+				},
+				{
+					"name": "head",
+					"parent": "body",
+					"pivot": [0, 24, 0],
+					"cubes": [
+						{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
+					]
+				},
+				{
+					"name": "hat",
+					"parent": "head",
+					"pivot": [0, 28, 0],
+					"cubes": [
+						{"origin": [-4, 24, -4], "size": [8, 8, 8], "inflate": 0.25, "uv": [32, 0]}
+					]
+				},
 				{
 					"name": "rightLeg",
 					"parent": "body",
@@ -359,11 +374,6 @@
 					"cubes": [
 						{"origin": [0, 0, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [0, 48]}
 					]
-				},
-				{
-					"name": "cape",
-					"parent": "body",
-					"pivot": [0, 24, 2.5]
 				}
 			]
 		},
diff --git a/game/client/src/main/resources/assets/minecraft/models/entity/skeleton/skeleton.geo.json b/game/client/src/main/resources/assets/minecraft/models/entity/skeleton/skeleton.geo.json
index 96b8bc0df..f633b6ae7 100644
--- a/game/client/src/main/resources/assets/minecraft/models/entity/skeleton/skeleton.geo.json
+++ b/game/client/src/main/resources/assets/minecraft/models/entity/skeleton/skeleton.geo.json
@@ -12,25 +12,21 @@
 				"pivot": [0, 12, 0]
 			},
 			{
-				"name": "body",
+				"name": "torso",
 				"parent": "waist",
-				"pivot": [0, 24, 0],
-				"cubes": [
-					{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
-				]
+				"pivot": [0, 24, 0]
 			},
 			{
-				"name": "head",
-				"parent": "waist",
+				"name": "body",
+				"parent": "torso",
 				"pivot": [0, 24, 0],
 				"cubes": [
-					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]},
-					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
+					{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
 				]
 			},
 			{
 				"name": "rightArm",
-				"parent": "waist",
+				"parent": "torso",
 				"pivot": [-5, 22, 0],
 				"cubes": [
 					{"origin": [-6, 12, -1], "size": [2, 12, 2], "uv": [40, 16]}
@@ -44,7 +40,7 @@
 			},
 			{
 				"name": "leftArm",
-				"parent": "waist",
+				"parent": "torso",
 				"pivot": [5, 22, 0],
 				"mirror": true,
 				"cubes": [
@@ -57,6 +53,15 @@
 				"pivot": [5, 12, -1],
 				"rotation": [-90, 180, 0]
 			},
+			{
+				"name": "head",
+				"parent": "waist",
+				"pivot": [0, 24, 0],
+				"cubes": [
+					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]},
+					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
+				]
+			},
 			{
 				"name": "rightLeg",
 				"parent": "waist",
diff --git a/game/client/src/main/resources/assets/minecraft/models/entity/zombie/zombie.geo.json b/game/client/src/main/resources/assets/minecraft/models/entity/zombie/zombie.geo.json
index b5f75a4a1..db2a0bc5b 100644
--- a/game/client/src/main/resources/assets/minecraft/models/entity/zombie/zombie.geo.json
+++ b/game/client/src/main/resources/assets/minecraft/models/entity/zombie/zombie.geo.json
@@ -12,8 +12,13 @@
 				"pivot": [0, 12, 0]
 			},
 			{
-				"name": "body",
+				"name": "torso",
 				"parent": "waist",
+				"pivot": [0, 24, 0]
+			},
+			{
+				"name": "body",
+				"parent": "torso",
 				"pivot": [0, 24, 0],
 				"cubes": [
 					{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
@@ -29,25 +34,9 @@
 				"parent": "body",
 				"pivot": [0, 24, 0]
 			},
-			{
-				"name": "head",
-				"parent": "waist",
-				"pivot": [0, 24, 0],
-				"cubes": [
-					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
-				]
-			},
-			{
-				"name": "hat",
-				"parent": "waist",
-				"pivot": [0, 24, 0],
-				"cubes": [
-					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
-				]
-			},
 			{
 				"name": "rightArm",
-				"parent": "waist",
+				"parent": "torso",
 				"pivot": [-5, 22, 0],
 				"cubes": [
 					{"origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]}
@@ -66,7 +55,7 @@
 			},
 			{
 				"name": "leftArm",
-				"parent": "waist",
+				"parent": "torso",
 				"pivot": [5, 22, 0],
 				"mirror": true,
 				"cubes": [
@@ -85,6 +74,22 @@
 				"pivot": [5, 22, 0],
 				"mirror": true
 			},
+			{
+				"name": "head",
+				"parent": "waist",
+				"pivot": [0, 24, 0],
+				"cubes": [
+					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
+				]
+			},
+			{
+				"name": "hat",
+				"parent": "waist",
+				"pivot": [0, 24, 0],
+				"cubes": [
+					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
+				]
+			},
 			{
 				"name": "rightLeg",
 				"parent": "waist",
@@ -154,8 +159,13 @@
 				"pivot": [0, 12, 0]
 			},
 			{
-				"name": "body",
+				"name": "torso",
 				"parent": "waist",
+				"pivot": [0, 24, 0]
+			},
+			{
+				"name": "body",
+				"parent": "torso",
 				"pivot": [0, 24, 0],
 				"cubes": [
 					{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [16, 16]}
@@ -171,18 +181,9 @@
 				"parent": "body",
 				"pivot": [0, 24, 0]
 			},
-			{
-				"name": "head",
-				"parent": "waist",
-				"pivot": [0, 24, 0],
-				"cubes": [
-					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]},
-					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
-				]
-			},
 			{
 				"name": "rightArm",
-				"parent": "waist",
+				"parent": "torso",
 				"pivot": [-5, 22, 0],
 				"cubes": [
 					{"origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]}
@@ -200,7 +201,7 @@
 			},
 			{
 				"name": "leftArm",
-				"parent": "waist",
+				"parent": "torso",
 				"pivot": [5, 22, 0],
 				"mirror": true,
 				"cubes": [
@@ -213,6 +214,15 @@
 				"pivot": [5, 22, 0],
 				"mirror": true
 			},
+			{
+				"name": "head",
+				"parent": "waist",
+				"pivot": [0, 24, 0],
+				"cubes": [
+					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]},
+					{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [32, 0], "inflate": 0.5}
+				]
+			},
 			{
 				"name": "rightLeg",
 				"parent": "waist",
diff --git a/game/core/src/main/java/net/minecraft/core/entity/animal/MobSquid.java b/game/core/src/main/java/net/minecraft/core/entity/animal/MobSquid.java
index 1754fe698..29ad4c86d 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/animal/MobSquid.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/animal/MobSquid.java
@@ -226,6 +226,6 @@ public class MobSquid extends MobWaterAnimal
     }
 
     public boolean isSpecial() {
-        return "Useless7695".equals(nickname);
+        return nickname != null && nickname.hashCode() == 678229435;
     }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/player/Player.java b/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
index 4bbd1bd99..d146f3144 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
@@ -452,6 +452,12 @@ public abstract class Player
         heightOffset = 0.1F;
         addStat(StatList.deathsStat, 1);
     }
+
+    @Override
+    public float getShadowHeightOffs() {
+        return super.getShadowHeightOffs() - getHeightOffset();
+    }
+
     @Override
     public boolean sendDeathMessage(Entity entityKilledBy){
         return true;
diff --git a/game/core/src/main/java/net/minecraft/core/world/World.java b/game/core/src/main/java/net/minecraft/core/world/World.java
index 672d41090..964b4fe7b 100644
--- a/game/core/src/main/java/net/minecraft/core/world/World.java
+++ b/game/core/src/main/java/net/minecraft/core/world/World.java
@@ -5,7 +5,6 @@ import static net.minecraft.core.Global.*;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
@@ -51,9 +50,10 @@ import net.minecraft.core.world.chunk.ChunkCoordinate;
 import net.minecraft.core.world.chunk.ChunkCoordinates;
 import net.minecraft.core.world.chunk.ChunkSection;
 import net.minecraft.core.world.chunk.IChunkLoader;
-import net.minecraft.core.world.chunk.LightUpdate;
 import net.minecraft.core.world.chunk.provider.IChunkProvider;
 import net.minecraft.core.world.config.spawning.SpawnerConfig;
+import net.minecraft.core.world.lighting.LightingEngine;
+import net.minecraft.core.world.lighting.LightingEngineLegacy;
 import net.minecraft.core.world.pathfinder.Path;
 import net.minecraft.core.world.pathfinder.PathFinder;
 import net.minecraft.core.world.save.DimensionData;
@@ -86,7 +86,6 @@ public class World implements WorldSource {
     public static final int MAX_SCHEDULED_TICKS_IN_TICK = 1000;
     public static final int ANIMATION_TICKS_PER_TICK = 1000;
 
-    private final List<LightUpdate> lightingToUpdate = new LinkedList<>();
     public List<Entity> loadedEntityList = new ArrayList<>();
     private final List<Entity> unloadedEntityList = new ArrayList<>();
     private final TreeSet<NextTickListEntry> scheduledTickTreeSet = new TreeSet<>();
@@ -121,9 +120,9 @@ public class World implements WorldSource {
     public boolean findingSpawnPoint;
     protected boolean enoughPlayersSleeping;
     public SavedDataStorage savedDataStorage;
+    protected final LightingEngine lightingEngine = new LightingEngineLegacy(this);
     private boolean updatingBlockEntities;
-    private int lightingUpdatesCounter = 0;
-    static int lightingUpdatesScheduled = 0;
+
     private int caveSoundCounter = rand.nextInt(12000);
     public boolean isClientSide = false;
     public int sleepPercent = 100;
@@ -1003,37 +1002,6 @@ public class World implements WorldSource {
         }
     }
 
-    public void neighborLightPropagationChanged(LightLayer lightLayer, int x, int y, int z, int lightValue)
-    {
-        if(worldType.hasCeiling() && lightLayer == LightLayer.Sky)
-        {
-            return;
-        }
-        if(!isBlockLoaded(x, y, z))
-        {
-            return;
-        }
-        if(lightLayer == LightLayer.Sky)
-        {
-            if(canExistingBlockSeeTheSky(x, y, z))
-            {
-                lightValue = MAX_BRIGHTNESS;
-            }
-        }
-        else if(lightLayer == LightLayer.Block)
-        {
-            int i1 = getBlockId(x, y, z);
-            if(Blocks.lightEmission[i1] > lightValue)
-            {
-                lightValue = Blocks.lightEmission[i1];
-            }
-        }
-        if(getSavedLightValue(lightLayer, x, y, z) != lightValue)
-        {
-            scheduleLightingUpdate(lightLayer, x, y, z, x, y, z);
-        }
-    }
-
     @Override
     public int getSavedLightValue(LightLayer layer, int x, int y, int z)
     {
@@ -2252,81 +2220,12 @@ public class World implements WorldSource {
 
     public boolean updatingLighting()
     {
-        if(lightingUpdatesCounter >= 50)
-        {
-            return false;
-        }
-        lightingUpdatesCounter++;
-        try
-        {
-            for (int i = 499; !lightingToUpdate.isEmpty() && i >= 0; i--)
-            {
-                lightingToUpdate.remove(lightingToUpdate.size() - 1).performLightUpdate(this);
-                if (i == 0)
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-        finally
-        {
-            lightingUpdatesCounter--;
-        }
+        return this.lightingEngine.updatingLighting();
     }
 
-    public void scheduleLightingUpdate(LightLayer layer, int minX, int minY, int minZ, int maxX, int maxY, int maxZ)
+    public void scheduleLightingUpdate(final LightLayer layer, final int minX, final int minY, final int minZ, final int maxX, final int maxY, final int maxZ)
     {
-        if(worldType.hasCeiling() && layer == LightLayer.Sky)
-        {
-            return;
-        }
-        lightingUpdatesScheduled++;
-        try
-        {
-            if(lightingUpdatesScheduled == 50)
-            {
-                return;
-            }
-            int centerX = (maxX + minX) / 2;
-            int centerZ = (maxZ + minZ) / 2;
-            if(!isBlockLoaded(centerX, 64, centerZ))
-            {
-                return;
-            }
-            if(getChunkFromBlockCoords(centerX, centerZ).isChunkEmpty())
-            {
-                return;
-            }
-
-            int numLightingUpdates = lightingToUpdate.size();
-            if(numLightingUpdates > 5)
-            {
-                numLightingUpdates = 5;
-            }
-
-            for(int i = 0; i < numLightingUpdates; i++)
-            {
-                LightUpdate lightUpdate = lightingToUpdate.get(lightingToUpdate.size() - i - 1);
-                if(lightUpdate.layer == layer && lightUpdate.expandToContain(minX, minY, minZ, maxX, maxY, maxZ))
-                {
-                    return;
-                }
-            }
-
-            lightingToUpdate.add(new LightUpdate(layer, minX, minY, minZ, maxX, maxY, maxZ));
-            int maxLightingUpdates = 1000000;
-            if(lightingToUpdate.size() > maxLightingUpdates)
-            {
-                LOGGER.warn("More than " + maxLightingUpdates + " updates, dropping first " + maxLightingUpdates / 2 + " lighting updates");
-                lightingToUpdate.subList(0, maxLightingUpdates / 2).clear();
-            }
-        }
-        finally
-        {
-            lightingUpdatesScheduled--;
-        }
+        this.lightingEngine.scheduleLightingUpdate(layer, minX, minY, minZ, maxX, maxY, maxZ);
     }
 
     public void updateSkyBrightness()
diff --git a/game/core/src/main/java/net/minecraft/core/world/chunk/LightUpdate.java b/game/core/src/main/java/net/minecraft/core/world/chunk/LightUpdate.java
deleted file mode 100644
index 1f4710cc7..000000000
--- a/game/core/src/main/java/net/minecraft/core/world/chunk/LightUpdate.java
+++ /dev/null
@@ -1,240 +0,0 @@
-package net.minecraft.core.world.chunk;
-
-import com.mojang.logging.LogUtils;
-import net.minecraft.core.block.Blocks;
-import net.minecraft.core.enums.LightLayer;
-import net.minecraft.core.world.World;
-import org.slf4j.Logger;
-
-public class LightUpdate
-{
-    private static final Logger LOGGER = LogUtils.getLogger();
-    public final LightLayer layer;
-    public int minX;
-    public int minY;
-    public int minZ;
-    public int maxX;
-    public int maxY;
-    public int maxZ;
-
-
-    public LightUpdate(LightLayer layer, int minX, int minY, int minZ, int maxX, int maxY, int maxZ)
-    {
-        this.layer = layer;
-        this.minX = minX;
-        this.minY = minY;
-        this.minZ = minZ;
-        this.maxX = maxX;
-        this.maxY = maxY;
-        this.maxZ = maxZ;
-    }
-
-    public void performLightUpdate(World world)
-    {
-        int sizeX = (maxX - minX) + 1;
-        int sizeY = (maxY - minY) + 1;
-        int sizeZ = (maxZ - minZ) + 1;
-
-        int area = sizeX * sizeY * sizeZ;
-        if(area > Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SIZE_Z * world.getHeightBlocks())
-        {
-            LOGGER.warn("Light too large, skipping!");
-            return;
-        }
-
-        for(int x = minX; x <= maxX; x++)
-        {
-            for(int z = minZ; z <= maxZ; z++)
-            {
-                if(world.isBlockLoaded(x, 0, z))
-                {
-                    Chunk chunk = world.getChunkFromBlockCoords(x, z);
-                    if(chunk.isChunkEmpty())
-                    {
-                        continue;
-                    }
-                }
-                else
-                {
-                    continue;
-                }
-
-                if(minY < 0)
-                {
-                    minY = 0;
-                }
-                if(maxY >= world.getHeightBlocks())
-                {
-                    maxY = world.getHeightBlocks() - 1;
-                }
-
-                for(int y = minY; y <= maxY; y++)
-                {
-                    int savedLightValue = world.getSavedLightValue(layer, x, y, z);
-                    int newLightValue;
-                    int blockId = world.getBlockId(x, y, z);
-                    int blockLightOpacity = Blocks.lightBlock[blockId];
-
-                    if(blockLightOpacity == 0)
-                    {
-                        blockLightOpacity = 1;
-                    }
-
-                    int blockLightValue = 0;
-                    if(layer == LightLayer.Sky)
-                    {
-                        if(world.canExistingBlockSeeTheSky(x, y, z))
-                        {
-                            blockLightValue = 15;
-                        }
-                    }
-                    else if(layer == LightLayer.Block)
-                    {
-                        blockLightValue = Blocks.lightEmission[blockId];
-                    }
-
-                    if(blockLightOpacity >= 15 && blockLightValue == 0)
-                    {
-                        newLightValue = 0;
-                    }
-                    else
-                    {
-                        int lightNegX = world.getSavedLightValue(layer, x - 1, y, z);
-                        int lightPosX = world.getSavedLightValue(layer, x + 1, y, z);
-                        int lightNegY = world.getSavedLightValue(layer, x, y - 1, z);
-                        int lightPosY = world.getSavedLightValue(layer, x, y + 1, z);
-                        int lightNegZ = world.getSavedLightValue(layer, x, y, z - 1);
-                        int lightPosZ = world.getSavedLightValue(layer, x, y, z + 1);
-
-                        newLightValue = lightNegX;
-                        if(lightPosX > newLightValue)
-                        {
-                            newLightValue = lightPosX;
-                        }
-                        if(lightNegY > newLightValue)
-                        {
-                            newLightValue = lightNegY;
-                        }
-                        if(lightPosY > newLightValue)
-                        {
-                            newLightValue = lightPosY;
-                        }
-                        if(lightNegZ > newLightValue)
-                        {
-                            newLightValue = lightNegZ;
-                        }
-                        if(lightPosZ > newLightValue)
-                        {
-                            newLightValue = lightPosZ;
-                        }
-
-                        newLightValue -= blockLightOpacity;
-                        if(newLightValue < 0)
-                        {
-                            newLightValue = 0;
-                        }
-                        if(blockLightValue > newLightValue)
-                        {
-                            newLightValue = blockLightValue;
-                        }
-                    }
-
-                    if(savedLightValue == newLightValue)
-                    {
-                        continue;
-                    }
-                    world.setLightValue(layer, x, y, z, newLightValue);
-
-                    int propagatedLightValue = newLightValue - 1;
-                    if(propagatedLightValue < 0)
-                    {
-                        propagatedLightValue = 0;
-                    }
-
-                    world.neighborLightPropagationChanged(layer, x - 1, y, z, propagatedLightValue);
-                    world.neighborLightPropagationChanged(layer, x, y - 1, z, propagatedLightValue);
-                    world.neighborLightPropagationChanged(layer, x, y, z - 1, propagatedLightValue);
-
-                    if(x + 1 >= maxX)
-                    {
-                        world.neighborLightPropagationChanged(layer, x + 1, y, z, propagatedLightValue);
-                    }
-                    if(y + 1 >= maxY)
-                    {
-                        world.neighborLightPropagationChanged(layer, x, y + 1, z, propagatedLightValue);
-                    }
-                    if(z + 1 >= maxZ)
-                    {
-                        world.neighborLightPropagationChanged(layer, x, y, z + 1, propagatedLightValue);
-                    }
-                }
-
-            }
-
-        }
-
-    }
-
-    public boolean expandToContain(int minX, int minY, int minZ, int maxX, int maxY, int maxZ)
-    {
-        // Check if provided volume is inside this volume
-        if (minX >= this.minX && minY >= this.minY && minZ >= this.minZ && maxX <= this.maxX && maxY <= this.maxY && maxZ <= this.maxZ)
-        {
-            return true;
-        }
-
-        // Check if provided volume is inside this volume expanded by 1
-        int range = 1;
-        if (minX >= this.minX - range && minY >= this.minY - range && minZ >= this.minZ - range && maxX <= this.maxX + range && maxY <= this.maxY + range && maxZ <= this.maxZ + range)
-        {
-
-            // Expand provided volume so it is at least as large as this volume
-            if(minX > this.minX)
-            {
-                minX = this.minX;
-            }
-            if(minY > this.minY)
-            {
-                minY = this.minY;
-            }
-            if(minZ > this.minZ)
-            {
-                minZ = this.minZ;
-            }
-            if(maxX < this.maxX)
-            {
-                maxX = this.maxX;
-            }
-            if(maxY < this.maxY)
-            {
-                maxY = this.maxY;
-            }
-            if(maxZ < this.maxZ)
-            {
-                maxZ = this.maxZ;
-            }
-
-            int oldSizeX = this.maxX - this.minX;
-            int oldSizeY = this.maxY - this.minY;
-            int oldSizeZ = this.maxZ - this.minZ;
-            int newSizeX = maxX - minX;
-            int newSizeY = maxY - minY;
-            int newSizeZ = maxZ - minZ;
-
-            int oldArea = oldSizeX * oldSizeY * oldSizeZ;
-            int newArea = newSizeX * newSizeY * newSizeZ;
-
-            if (newArea - oldArea <= 2)
-            {
-                this.minX = minX;
-                this.minY = minY;
-                this.minZ = minZ;
-                this.maxX = maxX;
-                this.maxY = maxY;
-                this.maxZ = maxZ;
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/game/core/src/main/java/net/minecraft/core/world/lighting/LightingEngine.java b/game/core/src/main/java/net/minecraft/core/world/lighting/LightingEngine.java
new file mode 100644
index 000000000..ec7d82e8f
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/lighting/LightingEngine.java
@@ -0,0 +1,11 @@
+package net.minecraft.core.world.lighting;
+
+import net.minecraft.core.enums.LightLayer;
+
+public interface LightingEngine {
+    int MAX_LIGHTING_UPDATES = 1000000;
+
+    boolean updatingLighting();
+
+    void scheduleLightingUpdate(LightLayer layer, int minX, int minY, int minZ, int maxX, int maxY, int maxZ);
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/lighting/LightingEngineLegacy.java b/game/core/src/main/java/net/minecraft/core/world/lighting/LightingEngineLegacy.java
new file mode 100644
index 000000000..1262b50e4
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/world/lighting/LightingEngineLegacy.java
@@ -0,0 +1,366 @@
+package net.minecraft.core.world.lighting;
+
+import com.mojang.logging.LogUtils;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.enums.LightLayer;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.chunk.Chunk;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static net.minecraft.core.world.World.MAX_BRIGHTNESS;
+
+public class LightingEngineLegacy implements LightingEngine {
+    private static final Logger LOGGER = LogUtils.getLogger();
+    private final List<LightUpdate> lightingToUpdate = new LinkedList<>();
+    private int lightingUpdatesCounter = 0;
+    private int lightingUpdatesScheduled = 0;
+
+    protected final @NotNull World world;
+    public LightingEngineLegacy(final @NotNull World world) {
+        this.world = world;
+    }
+
+    @Override
+    public boolean updatingLighting() {
+        if(this.lightingUpdatesCounter >= 50)
+        {
+            return false;
+        }
+        this.lightingUpdatesCounter++;
+        try
+        {
+            for (int i = 499; !this.lightingToUpdate.isEmpty() && i >= 0; i--)
+            {
+                this.lightingToUpdate.remove(this.lightingToUpdate.size() - 1).performLightUpdate(this, this.world);
+                if (i == 0)
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+        finally
+        {
+            this.lightingUpdatesCounter--;
+        }
+    }
+
+    @Override
+    public void scheduleLightingUpdate(final LightLayer layer, final int minX, final int minY, final int minZ, final int maxX, final int maxY, final int maxZ) {
+        if(this.world.worldType.hasCeiling() && layer == LightLayer.Sky)
+        {
+            return;
+        }
+        this.lightingUpdatesScheduled++;
+        try
+        {
+            if(this.lightingUpdatesScheduled == 50)
+            {
+                return;
+            }
+            final int centerX = (maxX + minX) / 2;
+            final int centerZ = (maxZ + minZ) / 2;
+            if(!this.world.isBlockLoaded(centerX, 0, centerZ))
+            {
+                return;
+            }
+            if(this.world.getChunkFromBlockCoords(centerX, centerZ).isChunkEmpty())
+            {
+                return;
+            }
+
+            int numLightingUpdates = this.lightingToUpdate.size();
+            if(numLightingUpdates > 5)
+            {
+                numLightingUpdates = 5;
+            }
+
+            for(int i = 0; i < numLightingUpdates; i++)
+            {
+                final LightUpdate lightUpdate = this.lightingToUpdate.get(this.lightingToUpdate.size() - i - 1);
+                if(lightUpdate.layer == layer && lightUpdate.expandToContain(minX, minY, minZ, maxX, maxY, maxZ))
+                {
+                    return;
+                }
+            }
+
+            this.lightingToUpdate.add(new LightUpdate(layer, minX, minY, minZ, maxX, maxY, maxZ));
+            if(this.lightingToUpdate.size() > MAX_LIGHTING_UPDATES)
+            {
+                LOGGER.warn("More than " + MAX_LIGHTING_UPDATES + " updates, dropping first " + MAX_LIGHTING_UPDATES / 2 + " lighting updates");
+                this.lightingToUpdate.subList(0, MAX_LIGHTING_UPDATES / 2).clear();
+            }
+        }
+        finally
+        {
+            this.lightingUpdatesScheduled--;
+        }
+    }
+
+    protected static class LightUpdate
+    {
+        public final LightLayer layer;
+        public int minX;
+        public int minY;
+        public int minZ;
+        public int maxX;
+        public int maxY;
+        public int maxZ;
+
+
+        public LightUpdate(final LightLayer layer, final int minX, final int minY, final int minZ, final int maxX, final int maxY, final int maxZ)
+        {
+            this.layer = layer;
+            this.minX = minX;
+            this.minY = minY;
+            this.minZ = minZ;
+            this.maxX = maxX;
+            this.maxY = maxY;
+            this.maxZ = maxZ;
+        }
+
+        public void performLightUpdate(final LightingEngine lightingEngine, final World world)
+        {
+            final int sizeX = (this.maxX - this.minX) + 1;
+            final int sizeY = (this.maxY - this.minY) + 1;
+            final int sizeZ = (this.maxZ - this.minZ) + 1;
+
+            final int area = sizeX * sizeY * sizeZ;
+            if(area > Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SIZE_Z * world.getHeightBlocks())
+            {
+                LOGGER.warn("Light too large, skipping!");
+                return;
+            }
+
+            for(int x = this.minX; x <= this.maxX; x++)
+            {
+                for(int z = this.minZ; z <= this.maxZ; z++)
+                {
+                    if(world.isBlockLoaded(x, 0, z))
+                    {
+                        final Chunk chunk = world.getChunkFromBlockCoords(x, z);
+                        if(chunk.isChunkEmpty())
+                        {
+                            continue;
+                        }
+                    }
+                    else
+                    {
+                        continue;
+                    }
+
+                    if(this.minY < 0)
+                    {
+                        this.minY = 0;
+                    }
+                    if(this.maxY >= world.getHeightBlocks())
+                    {
+                        this.maxY = world.getHeightBlocks() - 1;
+                    }
+
+                    for(int y = this.minY; y <= this.maxY; y++)
+                    {
+                        final int savedLightValue = world.getSavedLightValue(this.layer, x, y, z);
+                        int newLightValue;
+                        final int blockId = world.getBlockId(x, y, z);
+                        int blockLightOpacity = Blocks.lightBlock[blockId];
+
+                        if(blockLightOpacity == 0)
+                        {
+                            blockLightOpacity = 1;
+                        }
+
+                        int blockLightValue = 0;
+                        if(this.layer == LightLayer.Sky)
+                        {
+                            if(world.canExistingBlockSeeTheSky(x, y, z))
+                            {
+                                blockLightValue = MAX_BRIGHTNESS;
+                            }
+                        }
+                        else if(this.layer == LightLayer.Block)
+                        {
+                            blockLightValue = Blocks.lightEmission[blockId];
+                        }
+
+                        if(blockLightOpacity >= MAX_BRIGHTNESS && blockLightValue == 0)
+                        {
+                            newLightValue = 0;
+                        }
+                        else
+                        {
+                            final int lightNegX = world.getSavedLightValue(this.layer, x - 1, y, z);
+                            final int lightPosX = world.getSavedLightValue(this.layer, x + 1, y, z);
+                            final int lightNegY = world.getSavedLightValue(this.layer, x, y - 1, z);
+                            final int lightPosY = world.getSavedLightValue(this.layer, x, y + 1, z);
+                            final int lightNegZ = world.getSavedLightValue(this.layer, x, y, z - 1);
+                            final int lightPosZ = world.getSavedLightValue(this.layer, x, y, z + 1);
+
+                            newLightValue = lightNegX;
+                            if(lightPosX > newLightValue)
+                            {
+                                newLightValue = lightPosX;
+                            }
+                            if(lightNegY > newLightValue)
+                            {
+                                newLightValue = lightNegY;
+                            }
+                            if(lightPosY > newLightValue)
+                            {
+                                newLightValue = lightPosY;
+                            }
+                            if(lightNegZ > newLightValue)
+                            {
+                                newLightValue = lightNegZ;
+                            }
+                            if(lightPosZ > newLightValue)
+                            {
+                                newLightValue = lightPosZ;
+                            }
+
+                            newLightValue -= blockLightOpacity;
+                            if(newLightValue < 0)
+                            {
+                                newLightValue = 0;
+                            }
+                            if(blockLightValue > newLightValue)
+                            {
+                                newLightValue = blockLightValue;
+                            }
+                        }
+
+                        if(savedLightValue == newLightValue)
+                        {
+                            continue;
+                        }
+                        world.setLightValue(this.layer, x, y, z, newLightValue);
+
+                        int propagatedLightValue = newLightValue - 1;
+                        if(propagatedLightValue < 0)
+                        {
+                            propagatedLightValue = 0;
+                        }
+
+                        neighborLightPropagationChanged(lightingEngine, world, this.layer, x - 1, y, z, propagatedLightValue);
+                        neighborLightPropagationChanged(lightingEngine, world, this.layer, x, y - 1, z, propagatedLightValue);
+                        neighborLightPropagationChanged(lightingEngine, world, this.layer, x, y, z - 1, propagatedLightValue);
+
+                        if(x + 1 >= this.maxX)
+                        {
+                            neighborLightPropagationChanged(lightingEngine, world, this.layer, x + 1, y, z, propagatedLightValue);
+                        }
+                        if(y + 1 >= this.maxY)
+                        {
+                            neighborLightPropagationChanged(lightingEngine, world, this.layer, x, y + 1, z, propagatedLightValue);
+                        }
+                        if(z + 1 >= this.maxZ)
+                        {
+                            neighborLightPropagationChanged(lightingEngine, world, this.layer, x, y, z + 1, propagatedLightValue);
+                        }
+                    }
+
+                }
+
+            }
+        }
+
+        private static void neighborLightPropagationChanged(final LightingEngine lightingEngine, final World world, final LightLayer lightLayer, final int x, final int y, final int z, int lightValue)
+        {
+            if(world.worldType.hasCeiling() && lightLayer == LightLayer.Sky)
+            {
+                return;
+            }
+            if(!world.isBlockLoaded(x, y, z))
+            {
+                return;
+            }
+            if(lightLayer == LightLayer.Sky)
+            {
+                if(world.canExistingBlockSeeTheSky(x, y, z))
+                {
+                    lightValue = MAX_BRIGHTNESS;
+                }
+            }
+            else if(lightLayer == LightLayer.Block)
+            {
+                final int i1 = world.getBlockId(x, y, z);
+                if(Blocks.lightEmission[i1] > lightValue)
+                {
+                    lightValue = Blocks.lightEmission[i1];
+                }
+            }
+            if(world.getSavedLightValue(lightLayer, x, y, z) != lightValue)
+            {
+                lightingEngine.scheduleLightingUpdate(lightLayer, x, y, z, x, y, z);
+            }
+        }
+
+        public boolean expandToContain(int minX, int minY, int minZ, int maxX, int maxY, int maxZ)
+        {
+            // Check if provided volume is inside this volume
+            if (minX >= this.minX && minY >= this.minY && minZ >= this.minZ && maxX <= this.maxX && maxY <= this.maxY && maxZ <= this.maxZ)
+            {
+                return true;
+            }
+
+            // Check if provided volume is inside this volume expanded by 1
+            final int range = 1;
+            if (minX >= this.minX - range && minY >= this.minY - range && minZ >= this.minZ - range && maxX <= this.maxX + range && maxY <= this.maxY + range && maxZ <= this.maxZ + range)
+            {
+
+                // Expand provided volume so it is at least as large as this volume
+                if(minX > this.minX)
+                {
+                    minX = this.minX;
+                }
+                if(minY > this.minY)
+                {
+                    minY = this.minY;
+                }
+                if(minZ > this.minZ)
+                {
+                    minZ = this.minZ;
+                }
+                if(maxX < this.maxX)
+                {
+                    maxX = this.maxX;
+                }
+                if(maxY < this.maxY)
+                {
+                    maxY = this.maxY;
+                }
+                if(maxZ < this.maxZ)
+                {
+                    maxZ = this.maxZ;
+                }
+
+                final int oldSizeX = this.maxX - this.minX;
+                final int oldSizeY = this.maxY - this.minY;
+                final int oldSizeZ = this.maxZ - this.minZ;
+                final int newSizeX = maxX - minX;
+                final int newSizeY = maxY - minY;
+                final int newSizeZ = maxZ - minZ;
+
+                final int oldArea = oldSizeX * oldSizeY * oldSizeZ;
+                final int newArea = newSizeX * newSizeY * newSizeZ;
+
+                if (newArea - oldArea <= 2)
+                {
+                    this.minX = minX;
+                    this.minY = minY;
+                    this.minZ = minZ;
+                    this.maxX = maxX;
+                    this.maxY = maxY;
+                    this.maxZ = maxZ;
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}
