diff --git a/build.gradle.kts b/build.gradle.kts
index 4f9c18d05..c343d02fe 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -173,6 +173,7 @@ task("runServer", JavaExec::class) {
     group = "bta"
     mainClass.set("net.minecraft.server.MinecraftServer")
     classpath = sourceSets["main"].runtimeClasspath
+    jvmArgs = listOf("-ea")
 }
 
 task("runClient", JavaExec::class) {
@@ -181,7 +182,9 @@ task("runClient", JavaExec::class) {
     mainClass.set("net.minecraft.client.Minecraft")
     classpath = sourceSets["main"].runtimeClasspath
     if (arrayOf("Mac OS X", "Darwin").any { System.getProperty("os.name").startsWith(it) }) {
-        jvmArgs = listOf("-XstartOnFirstThread", "-Djava.awt.headless=true")
+        jvmArgs = listOf("-XstartOnFirstThread", "-Djava.awt.headless=true", "-ea")
+    } else {
+        jvmArgs = listOf("-ea")
     }
 
     systemProperty("java.library.path", layout.buildDirectory.dir("natives").get().asFile.absolutePath)
diff --git a/src/common/resources/lang/en_US/strings.lang b/src/common/resources/lang/en_US/strings.lang
index 577dc8aba..5543ac3bb 100644
--- a/src/common/resources/lang/en_US/strings.lang
+++ b/src/common/resources/lang/en_US/strings.lang
@@ -152,4 +152,22 @@ flag.copied=Flag pattern copied.
 
 wand.wrongmode=You must be in Creative mode to use this item!
 
-save.data.missing.data=Level Metadata Missing!
\ No newline at end of file
+save.data.missing.data=Level Metadata Missing!
+
+unit.day=Day
+
+direction.up=Up
+direction.down=Down
+direction.north=North
+direction.south=South
+direction.west=West
+direction.east=East
+direction.none=None
+
+side.top=Top
+side.bottom=Bottom
+side.north=North
+side.south=South
+side.west=West
+side.east=East
+side.none=None
\ No newline at end of file
diff --git a/src/main/java/com/b100/utils/ParserCollection.java b/src/main/java/com/b100/utils/ParserCollection.java
index 8cfdc8345..8afb8e14a 100644
--- a/src/main/java/com/b100/utils/ParserCollection.java
+++ b/src/main/java/com/b100/utils/ParserCollection.java
@@ -17,7 +17,7 @@ public class ParserCollection<E>{
 				try{
 					return parser.parse(object);
 				}catch (Exception e) {
-					throw new RuntimeException("Error parsing Object: "+object.getClass().getName()+" "+object, e);
+					throw new RuntimeException("Error parsing Object: "+object.getClass().getName()+" '"+object + "'", e);
 				}
 			}
 		}
diff --git a/src/main/java/com/mojang/nbt/CorruptedNBTException.java b/src/main/java/com/mojang/nbt/CorruptedNBTException.java
new file mode 100644
index 000000000..7d15d5f34
--- /dev/null
+++ b/src/main/java/com/mojang/nbt/CorruptedNBTException.java
@@ -0,0 +1,13 @@
+package com.mojang.nbt;
+
+import java.io.IOException;
+
+public class CorruptedNBTException extends IOException {
+    public CorruptedNBTException() {
+        super();
+    }
+
+    public CorruptedNBTException(String message) {
+        super(message);
+    }
+}
diff --git a/src/main/java/com/mojang/nbt/Tag.java b/src/main/java/com/mojang/nbt/Tag.java
index cf6184e0b..93a358bc4 100644
--- a/src/main/java/com/mojang/nbt/Tag.java
+++ b/src/main/java/com/mojang/nbt/Tag.java
@@ -214,7 +214,7 @@ public abstract class Tag<T>
         case 12:
         	return new DoubleArrayTag();
         }
-        throw new IOException("Cannot read NBT Tag type for type '" + type + "'!");
+        throw new CorruptedNBTException("Cannot read NBT Tag type for type '" + type + "'!");
     }
 
     /**
diff --git a/src/main/java/net/minecraft/client/gui/container/FlagEditorScreen.java b/src/main/java/net/minecraft/client/gui/container/FlagEditorScreen.java
index 46346e755..0caf9198b 100644
--- a/src/main/java/net/minecraft/client/gui/container/FlagEditorScreen.java
+++ b/src/main/java/net/minecraft/client/gui/container/FlagEditorScreen.java
@@ -24,7 +24,7 @@ public class FlagEditorScreen
     extends AbstractContainerScreen
     implements IDrawableSurface<Byte>
 {
-    private final FlagBlockEntity tileEntity;
+    private final FlagBlockEntity flagEntity;
     private final int CANVAS_SCALE = 4;
     private final int CANVAS_WIDTH = 24;
     private final int CANVAS_HEIGHT = 16;
@@ -51,7 +51,7 @@ public class FlagEditorScreen
     public FlagEditorScreen(Player player, FlagBlockEntity flagTileEntity)
     {
         super(new FlagMenu(player.inventory, flagTileEntity));
-        this.tileEntity = flagTileEntity;
+        this.flagEntity = flagTileEntity;
         xSize = 176;
         ySize = 195;
         flagTileEntity.owner = player.uuid;
@@ -91,6 +91,11 @@ public class FlagEditorScreen
             }
             buttons.add(colorLabels[i]);
         }
+
+        buttons.add(
+            new ButtonElement(50, (this.width - this.xSize) / 2 + 12, (this.height - this.ySize) / 2 + 61, 12, 12, "")
+                .setTextures("minecraft:gui/misc/button_flag_flip", "minecraft:gui/misc/button_flag_flip_highlighted", "minecraft:gui/misc/button_flag_flip_disabled.png")
+        );
     }
 
     private void renderCanvas()
@@ -101,7 +106,7 @@ public class FlagEditorScreen
         int[] colors = new int[5];
         for (int i = 1; i < 4; i++)
         {
-            ItemStack stack = tileEntity.getItem(35 + i);
+            ItemStack stack = flagEntity.getItem(35 + i);
             if (stack != null && stack.getItem() == Items.DYE)
             {
                 colors[i] = Colors.allFlagColors[TextFormatting.get(15 - stack.getMetadata()).id].getARGB();
@@ -124,7 +129,7 @@ public class FlagEditorScreen
         super.mouseClicked(mx, my, buttonNum);
         
         // Do not draw with no color selected
-        if(tileEntity.getItem(36 + selectedColor) == null) {
+        if(flagEntity.getItem(36 + selectedColor) == null) {
         	return;
         }
         
@@ -148,6 +153,7 @@ public class FlagEditorScreen
                         color = (byte) (selectedColor + 1);
                     }
                     flagSurfaceEditor.drawLine(xInCanvas, yInCanvas, xInCanvas, yInCanvas, color, activeTool + 1);
+                    flagEntity.isDirty = true;
                 }
             }
         }
@@ -169,6 +175,7 @@ public class FlagEditorScreen
                         color = (byte) (selectedColor + 1);
                     }
                     flagSurfaceEditor.floodFill(xInCanvas, yInCanvas, color);
+                    flagEntity.isDirty = true;
                 }
             }
         }
@@ -201,6 +208,9 @@ public class FlagEditorScreen
             activeTool = button.id;
             button.enabled = false;
         }
+        if (button.id == 50) {
+            flagEntity.setFlipped(!flagEntity.getFlipped());
+        }
         if (button.id >= 100 && button.id < 103)
         {
             colorLabels[selectedColor].setTextColor(0xFF404040);
@@ -238,10 +248,12 @@ public class FlagEditorScreen
                 if(MathHelper.abs(xLast - xInCanvas) <= 1 && MathHelper.abs(yLast - yInCanvas) <= 1)
                 {
                     flagSurfaceEditor.drawLine(xInCanvas, yInCanvas, xInCanvas, yInCanvas, color, activeTool + 1);
+                    flagEntity.isDirty = true;
                 }
                 else
                 {
                     flagSurfaceEditor.drawLine(xLast, yLast, xInCanvas, yInCanvas, color, activeTool + 1);
+                    flagEntity.isDirty = true;
                 }
 
                 xLast = xInCanvas;
@@ -273,6 +285,7 @@ public class FlagEditorScreen
                 }
 
                 flagSurfaceEditor.drawRectangle(xLast, yLast, xInCanvas, yInCanvas, color);
+                flagEntity.isDirty = true;
                 this.mouseButton = -1;
             }
         }
@@ -293,6 +306,7 @@ public class FlagEditorScreen
                 }
 
                 flagSurfaceEditor.drawEllipse(xLast, yLast, xInCanvas, yInCanvas, color);
+                flagEntity.isDirty = true;
                 this.mouseButton = -1;
             }
         }
@@ -313,6 +327,7 @@ public class FlagEditorScreen
                 }
 
                 flagSurfaceEditor.drawLine(xLast, yLast, xInCanvas, yInCanvas, color, 1);
+                flagEntity.isDirty = true;
                 this.mouseButton = -1;
             }
         }
@@ -397,7 +412,7 @@ public class FlagEditorScreen
         {
     		// Flag packet needs to be sent before the close screen packet is sent!
     		if(mc.theWorld.isClientSide) {
-    			mc.getSendQueue().addToSendQueue(new CustomPayloadPacket("BTA|Flag", tileEntity.flagColors));
+    			mc.getSendQueue().addToSendQueue(new CustomPayloadPacket("BTA|Flag", flagEntity.flagColors));
     		}
             mc.thePlayer.closeScreen();
         }
@@ -432,7 +447,7 @@ public class FlagEditorScreen
             return 0;
         if(y < 0 || y >= CANVAS_HEIGHT)
             return 0;
-        return tileEntity.flagColors[x + 24 * y];
+        return flagEntity.flagColors[x + 24 * y];
     }
 
     @Override
@@ -442,7 +457,8 @@ public class FlagEditorScreen
             return false;
         if(y < 0 || y >= CANVAS_HEIGHT)
             return false;
-        tileEntity.flagColors[x + 24 * y] = value;
+        flagEntity.flagColors[x + 24 * y] = value;
+        flagEntity.isDirty = true;
         return true;
     }
 }
diff --git a/src/main/java/net/minecraft/client/gui/hud/IngameHud.java b/src/main/java/net/minecraft/client/gui/hud/IngameHud.java
index 45d7d43f4..3229148c5 100644
--- a/src/main/java/net/minecraft/client/gui/hud/IngameHud.java
+++ b/src/main/java/net/minecraft/client/gui/hud/IngameHud.java
@@ -15,6 +15,7 @@ import net.minecraft.client.gui.chat.ChatScreen;
 import net.minecraft.client.gui.Gui;
 import net.minecraft.client.gui.HeldItemTooltipElement;
 import net.minecraft.client.gui.PhotoModeScreen;
+import net.minecraft.core.util.helper.Direction;
 import org.lwjgl.opengl.GL11;
 
 import net.minecraft.client.Minecraft;
@@ -55,7 +56,7 @@ import org.slf4j.Logger;
 public class IngameHud extends Gui
 {
     private static final Logger LOGGER = LogUtils.getLogger();
-    public static final String[] directions = new String[]{"North", "East", "South", "West"};
+    public static final Direction[] directions = new Direction[]{Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
 
     public static ItemEntityRenderer itemRenderer = new ItemEntityRenderer();
     private final List<ChatLine> chatMessageList = new ArrayList<>();
@@ -277,7 +278,7 @@ public class IngameHud extends Gui
             		
                     int yaw = DynamicTexture.pmod(java.lang.Math.round(mc.thePlayer.yRot), 360);
                     int pitch = java.lang.Math.round(mc.thePlayer.xRot);
-                    drawDebugScreenLine("Facing: " + yaw + "°, " + pitch + "° (" + getFacingDirection(mc.thePlayer) + ")");
+                    drawDebugScreenLine("Facing: " + yaw + "°, " + pitch + "° (" + I18n.getInstance().translateKey(getFacingDirection(mc.thePlayer).getTranslationKey()) + ")");
                 }
                 else
                 {
@@ -446,7 +447,7 @@ public class IngameHud extends Gui
                     }
                     if (mc.gameSettings.overlayShowDirection.value)
                     {
-                        drawString(font, getFacingDirection(mc.thePlayer), tsp, tsp + lineHeight * line++, 0xffffff);
+                        drawString(font, I18n.getInstance().translateKey(getFacingDirection(mc.thePlayer).getTranslationKey()), tsp, tsp + lineHeight * line++, 0xffffff);
                     }
                 }
                 if (clock)
@@ -455,12 +456,12 @@ public class IngameHud extends Gui
                     {
                         if (mc.gameSettings.overlayMode.value == 0)
                         {
-                            this.drawString(font, getTimeString() + " Day " + mc.theWorld.getWorldTime() / Global.DAY_LENGTH_TICKS, tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xffff80 : 0xffffff);
+                            this.drawString(font, String.format("%s %s %d", getTimeString(), I18n.getInstance().translateKey("unit.day"),mc.theWorld.getWorldTime() / Global.DAY_LENGTH_TICKS), tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xffff80 : 0xffffff);
                         }
                         if (mc.gameSettings.overlayMode.value > 0)
                         {
                             this.drawString(font, getTimeString(), tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xffff80 : 0xffffff);
-                            this.drawString(font, "Day " + mc.theWorld.getWorldTime() / Global.DAY_LENGTH_TICKS, tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xffff80 : 0xffffff);
+                            this.drawString(font, String.format("%s %d", I18n.getInstance().translateKey("unit.day"),mc.theWorld.getWorldTime() / Global.DAY_LENGTH_TICKS), tsp, tsp + lineHeight * line++, mc.gameSettings.overlayMode.value == 2 ? 0xffff80 : 0xffffff);
                         }
                     }
                 }
@@ -1009,7 +1010,7 @@ public class IngameHud extends Gui
         addChatMessage(result);
     }
 
-    public static String getFacingDirection(Player player)
+    public static Direction getFacingDirection(Player player)
     {
         return directions[(MathHelper.floor((double) ((player.yRot * 4F) / 360F) + 0.5D) + 2) & 3];
     }
diff --git a/src/main/java/net/minecraft/client/render/FlagRenderer.java b/src/main/java/net/minecraft/client/render/FlagRenderer.java
index 543598f30..c7cdb60f1 100644
--- a/src/main/java/net/minecraft/client/render/FlagRenderer.java
+++ b/src/main/java/net/minecraft/client/render/FlagRenderer.java
@@ -1,43 +1,126 @@
 package net.minecraft.client.render;
 
 import net.minecraft.client.GLAllocation;
+import net.minecraft.client.Minecraft;
 import net.minecraft.core.block.entity.FlagBlockEntity;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.net.command.TextFormatting;
 import net.minecraft.client.util.helper.Colors;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public class FlagRenderer {
     public final int CANVAS_WIDTH = 24;
     public final int CANVAS_HEIGHT = 16;
 
     private final TextureManager textureManager;
-    
-    private final int texture;
+
+    private final List<Integer> freeTextures = new ArrayList<>();
+    private final List<FlagTextureEntry> flagTextures = new ArrayList<>();
+    private final Map<FlagBlockEntity, FlagTextureEntry> cache = new Hashtable<>();
     
     public FlagRenderer(TextureManager textureManager)
     {
         this.textureManager = textureManager;
-        
-        texture = textureManager.createTexture(CANVAS_WIDTH, CANVAS_HEIGHT);
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        GLAllocation.deleteTexture(texture);
+    private int tickCount = 0;
+
+    public void tick() {
+        tickCount++;
+        if (tickCount % 100 != 0) return;
+        Iterator<Map.Entry<FlagBlockEntity, FlagTextureEntry>> iterator = cache.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<FlagBlockEntity, FlagTextureEntry> e = iterator.next();
+            FlagBlockEntity flagBlockEntity = e.getKey();
+            if (flagBlockEntity.isInvalid()) {
+                // Remove reference to flag from entry
+                FlagTextureEntry entry = e.getValue();
+                assert entry.appliedFlags.contains(flagBlockEntity);
+                entry.appliedFlags.remove(flagBlockEntity);
+
+                // If flag texture is no longer referenced by any flags then free its texture
+                if (entry.appliedFlags.isEmpty()) {
+                    flagTextures.remove(entry);
+                    freeTextures.add(entry.texture);
+                }
+
+                // Remove Flag and Texture entry from cache map
+                iterator.remove();
+            }
+        }
     }
 
-    public void updateTexture(FlagBlockEntity tileEntity)
-    {
-        byte[] flagColors = tileEntity.flagColors;
-        int[] rgbFlagColors = flagDataToRGB(tileEntity, flagColors);
-        textureManager.updateTextureData(rgbFlagColors, CANVAS_WIDTH, CANVAS_HEIGHT, texture);
+    public void flushCaches() {
+
+        freeTextures.forEach(GLAllocation::deleteTexture);
+        freeTextures.clear();
+        cache.clear();
+        flagTextures.clear();
     }
+
     
-    public int getTexture()
+    public int getTexture(FlagBlockEntity flagBlockEntity)
     {
-        return texture;
+        return getEntryForFlag(flagBlockEntity).texture;
+    }
+
+    private @NotNull FlagTextureEntry getEntryForFlag(FlagBlockEntity flag) {
+        // Returned cached value if not dirty
+        if (!flag.isDirty) {
+            FlagTextureEntry e = cache.get(flag);
+            if (e != null) {
+                return e;
+            }
+        }
+
+        FlagTextureEntry returnEntry = null;
+
+        // Remove cached flag entry if it exists and find new applicable entry
+        FlagTextureEntry oldEntry = cache.remove(flag);
+
+        // Remove flag reference from old entry and delete the entry itself if it has no more references
+        if (oldEntry != null) {
+            oldEntry.appliedFlags.remove(flag);
+            if (oldEntry.appliedFlags.isEmpty()) {
+                flagTextures.remove(oldEntry);
+                freeTextures.add(oldEntry.texture);
+            }
+        }
+
+        for (int i = 0; i < flagTextures.size(); i++) {
+            FlagTextureEntry flagEntry = flagTextures.get(i);
+            if (flag.colorHash == flagEntry.colorHash && flag.activeDyes == flagEntry.activeDyes && Arrays.equals(flag.flagColors, flagEntry.colorData)) {
+                returnEntry = flagEntry;
+                break;
+            }
+        }
+        // if no entries match make a new one
+        if (returnEntry == null) {
+            int tex;
+            if (freeTextures.isEmpty()) {
+                tex = textureManager.createTexture(CANVAS_WIDTH, CANVAS_HEIGHT);
+            } else {
+                tex = freeTextures.remove(0);
+            }
+            returnEntry = new FlagTextureEntry(tex, flag.colorHash, flag.activeDyes, flag.flagColors);
+            flagTextures.add(returnEntry);
+            textureManager.updateTextureData(flagDataToRGB(flag, flag.flagColors), CANVAS_WIDTH, CANVAS_HEIGHT, tex);
+        }
+        cache.put(flag, returnEntry);
+        returnEntry.appliedFlags.add(flag);
+        flag.isDirty = false;
+        return returnEntry;
     }
 
     private int[] flagDataToRGB(FlagBlockEntity tileEntity, byte[] colorData)
@@ -75,5 +158,20 @@ public class FlagRenderer {
         return col;    
     }
 
+    private static class FlagTextureEntry {
+        public final int colorHash;
+        public final int activeDyes;
+        public final byte[] colorData;
+        public final int texture;
+        public final Set<FlagBlockEntity> appliedFlags = new HashSet<>();
+
+        public FlagTextureEntry(int texture, int colorHash, int activeDyes, byte[] colorData) {
+            this.texture = texture;
+            this.colorHash = colorHash;
+            this.activeDyes = activeDyes;
+            this.colorData = colorData;
 
+
+        }
+    }
 }
diff --git a/src/main/java/net/minecraft/client/render/Polygon.java b/src/main/java/net/minecraft/client/render/Polygon.java
index 4f0076b48..9844c9768 100644
--- a/src/main/java/net/minecraft/client/render/Polygon.java
+++ b/src/main/java/net/minecraft/client/render/Polygon.java
@@ -28,15 +28,24 @@ public class Polygon
         vertices[3] = vertices[3].remap((float)maxU / texWidth - offsetU, (float)maxV / texHeight - offsetV);
     }
 
-    public void mirror()
+    public void mirrorU()
     {
-        Vertex[] vertices = new Vertex[this.vertices.length];
-        for(int i = 0; i < this.vertices.length; i++)
-        {
-            vertices[i] = this.vertices[this.vertices.length - i - 1];
-        }
+        float minU = vertices[1].u;
+        float maxU = vertices[0].u;
+        vertices[0].u = minU;
+        vertices[1].u = maxU;
+        vertices[2].u = maxU;
+        vertices[3].u = minU;
+    }
 
-        this.vertices = vertices;
+    public void mirrorV()
+    {
+        float minV = vertices[1].v;
+        float maxV = vertices[2].v;
+        vertices[0].u = maxV;
+        vertices[1].u = maxV;
+        vertices[2].u = minV;
+        vertices[3].u = minV;
     }
 
     public void render(Tessellator tessellator, float scale)
diff --git a/src/main/java/net/minecraft/client/render/TextureManager.java b/src/main/java/net/minecraft/client/render/TextureManager.java
index 2cec7bbed..b3c41138e 100644
--- a/src/main/java/net/minecraft/client/render/TextureManager.java
+++ b/src/main/java/net/minecraft/client/render/TextureManager.java
@@ -248,14 +248,14 @@ public class TextureManager {
 		
 		byte[] bytes = new byte[w * h * 4];
 		for (int l = 0; l < imageData.length; l++) {
-			int i1 = imageData[l] >> 24 & 0xff;
-			int j1 = imageData[l] >> 16 & 0xff;
-			int k1 = imageData[l] >> 8 & 0xff;
-			int l1 = imageData[l] & 0xff;
-			bytes[l * 4] = (byte) j1;
-			bytes[l * 4 + 1] = (byte) k1;
-			bytes[l * 4 + 2] = (byte) l1;
-			bytes[l * 4 + 3] = (byte) i1;
+			int a = imageData[l] >> 24 & 0xff;
+			int r = imageData[l] >> 16 & 0xff;
+			int g = imageData[l] >> 8 & 0xff;
+			int b = imageData[l] & 0xff;
+			bytes[l * 4] 	 = (byte) r;
+			bytes[l * 4 + 1] = (byte) g;
+			bytes[l * 4 + 2] = (byte) b;
+			bytes[l * 4 + 3] = (byte) a;
 		}
 
 		buffer.clear();
diff --git a/src/main/java/net/minecraft/client/render/block/model/RailBlockModel.java b/src/main/java/net/minecraft/client/render/block/model/RailBlockModel.java
index a53bf2c8f..31dde58f5 100644
--- a/src/main/java/net/minecraft/client/render/block/model/RailBlockModel.java
+++ b/src/main/java/net/minecraft/client/render/block/model/RailBlockModel.java
@@ -15,6 +15,7 @@ public class RailBlockModel<T extends RailBlock> extends StandardBlockModel<T> {
     protected IconCoordinate railCurve = TextureRegistry.getTexture("minecraft:block/rail_turn");
     protected IconCoordinate powerActive = TextureRegistry.getTexture("minecraft:block/rail_powered_active");
     protected IconCoordinate powerActiveOverlay = TextureRegistry.getTexture("minecraft:block/rail_powered_overlay");
+    protected IconCoordinate detectorActiveOverlay = TextureRegistry.getTexture("minecraft:block/rail_detector_overlay");
 
     public RailBlockModel(Block block) {
         super(block);
@@ -118,15 +119,15 @@ public class RailBlockModel<T extends RailBlock> extends StandardBlockModel<T> {
     @Override
     public IconCoordinate getBlockOverbrightTexture(WorldSource blockAccess, int x, int y, int z, int side)
     {
-        if (block.id != Blocks.RAIL_POWERED.id || (blockAccess.getBlockMetadata(x, y, z) & 8) == 0) return null;
+        if (!block.isPowered || (blockAccess.getBlockMetadata(x, y, z) & 8) == 0) return null;
 
-        return powerActiveOverlay;
+        return block == Blocks.RAIL_POWERED ? powerActiveOverlay : detectorActiveOverlay;
     }
 
     @Override
     public IconCoordinate getBlockTextureFromSideAndMetadata(Side side, int data)
     {
-        if(block.isPowered && (data & 8) != 0)
+        if(block == Blocks.RAIL_POWERED && (data & 8) != 0)
         {
             return powerActive;
         } else if(!block.isPowered && data >= 6)
diff --git a/src/main/java/net/minecraft/client/render/blockentity/BlockEntityRenderer.java b/src/main/java/net/minecraft/client/render/blockentity/BlockEntityRenderer.java
index 887f15cf6..8131e23cd 100644
--- a/src/main/java/net/minecraft/client/render/blockentity/BlockEntityRenderer.java
+++ b/src/main/java/net/minecraft/client/render/blockentity/BlockEntityRenderer.java
@@ -32,6 +32,7 @@ public abstract class BlockEntityRenderer<T extends BlockEntity>
     {
     }
 
+
     public Font getFont()
     {
         return renderDispatcher.getFont();
diff --git a/src/main/java/net/minecraft/client/render/blockentity/FlagBlockEntityRenderer.java b/src/main/java/net/minecraft/client/render/blockentity/FlagBlockEntityRenderer.java
index 1183fc4b0..2fbce2cec 100644
--- a/src/main/java/net/minecraft/client/render/blockentity/FlagBlockEntityRenderer.java
+++ b/src/main/java/net/minecraft/client/render/blockentity/FlagBlockEntityRenderer.java
@@ -16,11 +16,13 @@ public class FlagBlockEntityRenderer extends BlockEntityRenderer<FlagBlockEntity
 
 	public final Minecraft mc = Minecraft.getMinecraft(this);
     private final FlagModel flagModel;
+    private final FlagModel flagModelMirrored;
     private FlagRenderer flagRenderer = null;
     
     public FlagBlockEntityRenderer()
     {
-        flagModel = new FlagModel();
+        flagModel = new FlagModel(false);
+        flagModelMirrored = new FlagModel(true);
     }
 
     @Override
@@ -37,15 +39,20 @@ public class FlagBlockEntityRenderer extends BlockEntityRenderer<FlagBlockEntity
         doRender(tileEntity, x, y, z, partialTick, shortPole, windDirection, windIntensity);
     }
 
-    public void doRender(FlagBlockEntity tileEntity, double x, double y, double z, float partialTick, boolean shortPole, float windDirection, float windIntensity)
+    private long lastTick = 0;
+    public void doRender(FlagBlockEntity flagBlockEntity, double x, double y, double z, float partialTick, boolean shortPole, float windDirection, float windIntensity)
     {
         if (flagRenderer == null)
         {
             flagRenderer = new FlagRenderer(renderDispatcher.textureManager);
         }
-        flagRenderer.updateTexture(tileEntity);
+
+        if (flagBlockEntity.worldObj.getWorldTime() != lastTick) {
+            lastTick = flagBlockEntity.worldObj.getWorldTime();
+            flagRenderer.tick();
+        }
         
-        float swayInterpolated = tileEntity.sway + partialTick;
+        float swayInterpolated = flagBlockEntity.sway + partialTick;
         
         float sway = (float) ((swayInterpolated / 1.5) % (2 * Math.PI));
         sway = (float) (Math.sin(sway) * windIntensity * 2.0f);
@@ -68,10 +75,14 @@ public class FlagBlockEntityRenderer extends BlockEntityRenderer<FlagBlockEntity
         GL11.glPushMatrix();
         GL11.glRotatef(windDirection + sway, 0.0f, 1.0f, 0.0f);
         flagModel.renderFlag();
-        renderDispatcher.textureManager.bindTexture(flagRenderer.getTexture());
+        renderDispatcher.textureManager.bindTexture(flagRenderer.getTexture(flagBlockEntity));
         GL11.glEnable(GL11.GL_BLEND);
         GL11.glBlendFunc(GL11.GL_ONE_MINUS_SRC_COLOR, GL11.GL_SRC_COLOR);
-        flagModel.renderFlagOverlay();
+        if (flagBlockEntity.getFlipped()) {
+            flagModelMirrored.renderFlagOverlay();
+        } else {
+            flagModel.renderFlagOverlay();
+        }
         GL11.glDisable(GL11.GL_BLEND);
         GL11.glPopMatrix();
         final float f4 = 0.01666667F * scale;
@@ -79,6 +90,12 @@ public class FlagBlockEntityRenderer extends BlockEntityRenderer<FlagBlockEntity
         GL11.glScalef(f4, -f4, f4);
         GL11.glNormal3f(0.0F, 0.0F, -1F * f4);
         GL11.glPopMatrix();
+    }
 
+    @Override
+    public void onWorldChanged(World world) {
+        if (flagRenderer != null) {
+            flagRenderer.flushCaches();
+        }
     }
 }
diff --git a/src/main/java/net/minecraft/client/render/model/Cube.java b/src/main/java/net/minecraft/client/render/model/Cube.java
index fc6be338a..1e80c0e0f 100644
--- a/src/main/java/net/minecraft/client/render/model/Cube.java
+++ b/src/main/java/net/minecraft/client/render/model/Cube.java
@@ -141,7 +141,7 @@ public class Cube
         if(mirror)
         {
             for (Polygon face : polygons) {
-                face.mirror();
+                face.mirrorU();
             }
         }
     }
@@ -283,4 +283,12 @@ public class Cube
         GL11.glEndList();
         compiled = true;
     }
+
+    public Polygon[] getPolygons() {
+        return polygons;
+    }
+
+    public Vertex[] getVertices() {
+        return vertices;
+    }
 }
diff --git a/src/main/java/net/minecraft/client/render/model/FlagModel.java b/src/main/java/net/minecraft/client/render/model/FlagModel.java
index dfe5e5634..5660ec454 100644
--- a/src/main/java/net/minecraft/client/render/model/FlagModel.java
+++ b/src/main/java/net/minecraft/client/render/model/FlagModel.java
@@ -1,5 +1,7 @@
 package net.minecraft.client.render.model;
 
+import net.minecraft.client.render.Polygon;
+
 public class FlagModel {
     public Cube flagPole;
     public Cube flagPoleShort;
@@ -7,7 +9,7 @@ public class FlagModel {
     public Cube flagOverlay;
     public Cube flagBase;
 
-    public FlagModel()
+    public FlagModel(boolean mirrored)
     {
         flagPole = new Cube(0, 0, 64, 48);
         flagPole.addBox(-1F, -22F, -1F, 2, 33, 2, 0.0F);
@@ -17,6 +19,10 @@ public class FlagModel {
         flag.addBox(1F, -21F, -0.0F, 24, 16, 0, 0.0F);
         flagOverlay = new Cube(0, 0, 24, 16);
         flagOverlay.addBox(1F, -21F, -0.0F, 24, 16, 0, 0.0F);
+        if (mirrored) {
+            Polygon[] faces = flagOverlay.getPolygons();
+            faces[5].mirrorU();
+        }
         flagBase = new Cube(0, 40, 64, 48);
         flagBase.addBox(-2F, 11F, -2F, 4, 1, 4, 0.0F);
     }
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
index 4a72e5379..8b099bb99 100644
--- a/src/main/java/net/minecraft/client/world/chunk/provider/ChunkProviderDynamic.java
+++ b/src/main/java/net/minecraft/client/world/chunk/provider/ChunkProviderDynamic.java
@@ -1,17 +1,20 @@
 package net.minecraft.client.world.chunk.provider;
 
+import com.mojang.logging.LogUtils;
 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 org.slf4j.Logger;
 
 import java.io.IOException;
 import java.util.*;
 
 public class ChunkProviderDynamic implements IChunkProvider
 {
+    private static final Logger LOGGER = LogUtils.getLogger();
     private final Set<Integer> droppedChunksSet;
     private final Chunk emptyChunk;
     public ChunkGenerator chunkGenerator;
@@ -216,16 +219,16 @@ public class ChunkProviderDynamic implements IChunkProvider
         }
         catch(IOException ioexception)
         {
-            ioexception.printStackTrace();
+            LOGGER.error("Failed to save chunk at {}, {}!", chunk.xPosition, chunk.zPosition, ioexception);
         }
     }
 
-    private Chunk loadChunk(int i, int j)
+    private Chunk loadChunk(int x, int y)
     {
         if(chunkLoader == null) return emptyChunk;
         try
         {
-            Chunk chunk = chunkLoader.loadChunk(world, i, j);
+            Chunk chunk = chunkLoader.loadChunk(world, x, y);
             if(chunk != null)
             {
                 chunk.lastSaveTime = world.getWorldTime();
@@ -234,7 +237,7 @@ public class ChunkProviderDynamic implements IChunkProvider
         }
         catch(Exception exception)
         {
-            exception.printStackTrace();
+            LOGGER.error("Failed to load chunk at {}, {}!", x, y, exception);
         }
         return emptyChunk;
     }
diff --git a/src/main/java/net/minecraft/core/block/DetectorRailBlock.java b/src/main/java/net/minecraft/core/block/DetectorRailBlock.java
index 974d0324e..37efefec6 100644
--- a/src/main/java/net/minecraft/core/block/DetectorRailBlock.java
+++ b/src/main/java/net/minecraft/core/block/DetectorRailBlock.java
@@ -52,19 +52,19 @@ public class DetectorRailBlock extends RailBlock {
 
     @Override
     public boolean getSignal(WorldSource worldSource, int x, int y, int z, Side side) {
-        return (worldSource.getBlockMetadata(x, y, z) & 8) != 0;
+        return (worldSource.getBlockMetadata(x, y, z) & 0b1000) != 0;
     }
 
     @Override
     public boolean getDirectSignal(World world, int x, int y, int z, Side side) {
-        if((world.getBlockMetadata(x, y, z) & 8) == 0) {
+        if((world.getBlockMetadata(x, y, z) & 0b1000) == 0) {
             return false;
         } else {
             return side == Side.TOP;
         }
     }
     private void checkForMinecart(World world, int x, int y, int z, int meta) {
-        boolean isPowered = (meta & 8) != 0;
+        boolean isPowered = (meta & 0b1000) != 0;
         boolean minecartPresent = false;
         float offset = 0.125f;
         List<Entity> list = world.getEntitiesWithinAABB(MinecartEntity.class, AABB.getTemporaryBB(x + offset, y + offset, z + offset, x + 1 - offset, y + 1 - offset, z + 1 - offset));
@@ -73,17 +73,17 @@ public class DetectorRailBlock extends RailBlock {
         }
         if(minecartPresent && !isPowered) {
             // Set rail to powered
-            world.setBlockMetadataWithNotify(x, y, z, meta | 8);
+            world.setBlockMetadataWithNotify(x, y, z, meta | 0b1000);
             world.notifyBlocksOfNeighborChange(x, y, z, id);
             world.notifyBlocksOfNeighborChange(x, y - 1, z, id);
-            world.markBlocksDirty(x, y, z, x, y, z);
+            world.markBlockDirty(x, y, z);
         }
         if(!minecartPresent && isPowered) {
             // Set rail to unpowered
-            world.setBlockMetadataWithNotify(x, y, z, meta & 7);
+            world.setBlockMetadataWithNotify(x, y, z, meta & 0b111);
             world.notifyBlocksOfNeighborChange(x, y, z, id);
             world.notifyBlocksOfNeighborChange(x, y - 1, z, id);
-            world.markBlocksDirty(x, y, z, x, y, z);
+            world.markBlockDirty(x, y, z);
         }
         if(minecartPresent) {
             // Schedule update if minecart in rail
diff --git a/src/main/java/net/minecraft/core/block/RailBlock.java b/src/main/java/net/minecraft/core/block/RailBlock.java
index a05f43701..74b5d5e29 100644
--- a/src/main/java/net/minecraft/core/block/RailBlock.java
+++ b/src/main/java/net/minecraft/core/block/RailBlock.java
@@ -101,7 +101,7 @@ public class RailBlock extends Block {
 
         Block block = Block.getBlock(blockId);
 
-        if(isPowered) {
+        if(this == Blocks.RAIL_POWERED) {
             boolean gettingPower =
                     world.isBlockIndirectlyGettingPowered(x, y, z) ||
                     world.isBlockIndirectlyGettingPowered(x, y + 1, z) ||
diff --git a/src/main/java/net/minecraft/core/block/entity/BlockEntity.java b/src/main/java/net/minecraft/core/block/entity/BlockEntity.java
index 00c13fa48..034d03dce 100644
--- a/src/main/java/net/minecraft/core/block/entity/BlockEntity.java
+++ b/src/main/java/net/minecraft/core/block/entity/BlockEntity.java
@@ -21,6 +21,14 @@ public class BlockEntity
 {
     private static final Logger LOGGER = LogUtils.getLogger();
 
+    private static final Map<String, Class<? extends BlockEntity>> nameToClassMap = new HashMap<>();
+    private static final Map<Class<? extends BlockEntity>, String> classToNameMap = new HashMap<>();
+    public World worldObj;
+    public int x;
+    public int y;
+    public int z;
+    protected boolean tileEntityInvalid;
+
     public BlockEntity()
     {
     }
@@ -139,14 +147,6 @@ public class BlockEntity
         tileEntityInvalid = false;
     }
 
-    private static final Map<String, Class<? extends BlockEntity>> nameToClassMap = new HashMap<>();
-    private static final Map<Class<? extends BlockEntity>, String> classToNameMap = new HashMap<>();
-    public World worldObj;
-    public int x;
-    public int y;
-    public int z;
-    protected boolean tileEntityInvalid;
-
     static 
     {
         addMapping(FurnaceBlockEntity.class, "Furnace");
diff --git a/src/main/java/net/minecraft/core/block/entity/FlagBlockEntity.java b/src/main/java/net/minecraft/core/block/entity/FlagBlockEntity.java
index 1b90a467a..1736888d0 100644
--- a/src/main/java/net/minecraft/core/block/entity/FlagBlockEntity.java
+++ b/src/main/java/net/minecraft/core/block/entity/FlagBlockEntity.java
@@ -20,8 +20,12 @@ public class FlagBlockEntity extends BlockEntity
 {
     public final int CANVAS_WIDTH = 24;
     public final int CANVAS_HEIGHT = 16;
+    public boolean isDirty = true; // Used by flag renderer to check if color data has changed at all
+    public int colorHash = 0xFFFFFFFF; // Used by flag renderer to quickly check if color data isn't the same
+    public int activeDyes = 0; // Used by flag renderer to quickly check if dye data isn't the same
     public final byte[] flagColors = new byte[CANVAS_WIDTH * CANVAS_HEIGHT];
-    
+
+    public boolean flipped = true;
     public ItemStack[] items = new ItemStack[3];
     public @Nullable UUID owner;
     
@@ -61,6 +65,14 @@ public class FlagBlockEntity extends BlockEntity
         return 15;
     }
 
+    public void setFlipped(boolean flag) {
+        this.flipped = flag;
+    }
+
+    public boolean getFlipped() {
+        return flipped;
+    }
+
     @Override
     public void writeToNBT(CompoundTag tag) {
         super.writeToNBT(tag);
@@ -72,6 +84,7 @@ public class FlagBlockEntity extends BlockEntity
         byte[] packedColors = packFlagColors(flagColors);
         tag.putByteArray("Colors", packedColors);
         UUIDHelper.writeToTag(tag, owner, "OwnerUUID");
+        tag.putBoolean("Flip", flipped);
 
         ListTag list = new ListTag();
         for (int i = 0; i < items.length; i++)
@@ -100,6 +113,11 @@ public class FlagBlockEntity extends BlockEntity
         if (packedColors == null) packedColors = new byte[(CANVAS_WIDTH * CANVAS_HEIGHT) / 4];
         byte[] unpackedColors = unpackFlagColors(packedColors);
         System.arraycopy(unpackedColors, 0, flagColors, 0, CANVAS_WIDTH * CANVAS_HEIGHT);
+        isDirty = true;
+        colorHash = 0xFFFFFFFF;
+        for (int i = 0; i < flagColors.length; i++) {
+            colorHash ^= flagColors[i] << (8 * (i & 0b11));
+        }
 
         UUID ownerUUID = UUIDHelper.readFromTag(tag, "OwnerUUID");
         if (ownerUUID == null) {
@@ -113,6 +131,8 @@ public class FlagBlockEntity extends BlockEntity
             this.owner = ownerUUID;
         }
 
+        flipped = tag.getBooleanOrDefault("Flip", false);
+
         ListTag list = tag.getList("Items");
         items = new ItemStack[getContainerSize()];
         for (int i = 0; i < list.tagCount(); i++)
@@ -124,10 +144,10 @@ public class FlagBlockEntity extends BlockEntity
                 items[slot] = ItemStack.readItemStackFromNbt(compound);
             }
         }
+        itemsChanges();
     }
 
     public void copyFlagNBT(CompoundTag tag) {
-
         byte[] packedColors = packFlagColors(flagColors);
         UUID copyOwner = UUIDHelper.readFromTag(tag, "OwnerUUID");
         if(copyOwner != null) {
@@ -135,6 +155,7 @@ public class FlagBlockEntity extends BlockEntity
         }
 
         tag.putByteArray("Colors", packedColors);
+        tag.putBoolean("Flip", flipped);
     }
 
     private byte[] packFlagColors(byte[] unpacked)
@@ -193,6 +214,7 @@ public class FlagBlockEntity extends BlockEntity
                 ItemStack stack = items[index];
                 items[index] = null;
                 setChanged();
+                itemsChanges();
                 return stack;
             }
             else
@@ -203,6 +225,7 @@ public class FlagBlockEntity extends BlockEntity
                     items[index] = null;
                 }
                 setChanged();
+                itemsChanges();
                 return splitStack;
             }
         }
@@ -220,9 +243,21 @@ public class FlagBlockEntity extends BlockEntity
     @Override
     public void setItem(int index, ItemStack stack)
     {
-        index -= 36;
+        index -= 36; // This seems so wrong and will almost certainly cause an issue at some point
         items[index] = stack;
         setChanged();
+        itemsChanges();
+    }
+
+    private void itemsChanges() {
+        activeDyes = 0;
+        for (int i = 0; i < items.length; i++) {
+            ItemStack s = items[i];
+            if (s != null && s.getItem() == Items.DYE) {
+                activeDyes |= ((s.getMetadata() + 1) & 0b11111) << 5 * i;
+            }
+        }
+        isDirty = true;
     }
 
     @Override
diff --git a/src/main/java/net/minecraft/core/entity/PaintingEntity.java b/src/main/java/net/minecraft/core/entity/PaintingEntity.java
index b587eb6ea..66ebccf92 100644
--- a/src/main/java/net/minecraft/core/entity/PaintingEntity.java
+++ b/src/main/java/net/minecraft/core/entity/PaintingEntity.java
@@ -1,12 +1,10 @@
 package net.minecraft.core.entity;
 
-import net.minecraft.core.Global;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.ArtType;
 import net.minecraft.core.enums.EnumBlockSoundEffectType;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
-import net.minecraft.core.net.packet.AddPaintingPacket;
 import net.minecraft.core.util.collection.NamespaceID;
 import net.minecraft.core.util.helper.MathHelper;
 import com.mojang.nbt.CompoundTag;
@@ -64,8 +62,8 @@ public class PaintingEntity extends Entity
     @Override
     protected void defineSynchedData()
     {
-        entityData.define(DATA_ITEM_ID, 0);
-        entityData.define(DATA_ITEM_METADATA_ID, 0);
+        entityData.define(DATA_ITEM_ID, (short)0);
+        entityData.define(DATA_ITEM_METADATA_ID, (byte)0);
     }
 
     public void setDirection(int direction)
diff --git a/src/main/java/net/minecraft/core/entity/vehicle/MinecartEntity.java b/src/main/java/net/minecraft/core/entity/vehicle/MinecartEntity.java
index bb6b48861..f4e610050 100644
--- a/src/main/java/net/minecraft/core/entity/vehicle/MinecartEntity.java
+++ b/src/main/java/net/minecraft/core/entity/vehicle/MinecartEntity.java
@@ -290,7 +290,7 @@ public class MinecartEntity extends Entity implements Container {
             y = blockY;
             boolean onPoweredPoweredRail = false;
             boolean onUnpoweredPoweredRail = false;
-            if(rail.isPowered) {
+            if(rail == Blocks.RAIL_POWERED) {
                 onPoweredPoweredRail = (world.getBlockMetadata(blockX, blockY, blockZ) & 0b1000) != 0;
                 onUnpoweredPoweredRail = !onPoweredPoweredRail;
             }
diff --git a/src/main/java/net/minecraft/core/item/FlagItem.java b/src/main/java/net/minecraft/core/item/FlagItem.java
index 4ca93387b..07d1586d6 100644
--- a/src/main/java/net/minecraft/core/item/FlagItem.java
+++ b/src/main/java/net/minecraft/core/item/FlagItem.java
@@ -74,18 +74,18 @@ public class FlagItem extends Item
         world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, Blocks.FLAG.id, 0);
         
         stack.consumeItem(entityplayer);
-        FlagBlockEntity tileentityflag = (FlagBlockEntity) world.getBlockEntity(blockX, blockY, blockZ);
-        if(tileentityflag != null)
+        FlagBlockEntity flagBlockEntity = (FlagBlockEntity) world.getBlockEntity(blockX, blockY, blockZ);
+        if(flagBlockEntity != null)
         {
             CompoundTag flagData = stack.getData().getCompoundOrDefault("FlagData", null);
             if (flagData != null)
             {
-                tileentityflag.readFlagNBT(flagData);
+                flagBlockEntity.readFlagNBT(flagData);
             }
 
-            if (!world.isClientSide && tileentityflag.owner == null)
+            if (!world.isClientSide && flagBlockEntity.owner == null)
             {
-                entityplayer.displayFlagEditorScreen(tileentityflag);
+                entityplayer.displayFlagEditorScreen(flagBlockEntity);
             }
         }
         return true;
diff --git a/src/main/java/net/minecraft/core/lang/Language.java b/src/main/java/net/minecraft/core/lang/Language.java
index e09fc852b..5587d74b6 100644
--- a/src/main/java/net/minecraft/core/lang/Language.java
+++ b/src/main/java/net/minecraft/core/lang/Language.java
@@ -4,10 +4,12 @@ import com.mojang.logging.LogUtils;
 import org.slf4j.Logger;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Enumeration;
@@ -85,22 +87,26 @@ public class Language {
                         LOGGER.error("Failed to load lang file '{}' in '{}'", entry, name, e);
                     }
                 }
-                else if (tips == null && entry.getName().equalsIgnoreCase("tips.txt"))
+                else if (tips == null && entry.getName().endsWith("tips.txt"))
                 {
-                    try (InputStream in = zipFile.getInputStream(entry); InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8))
+                    try (InputStream in = zipFile.getInputStream(entry); InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(reader))
                     {
-                        tips = Arrays.asList(reader.toString().split("\n"));
+                        tips = new ArrayList<>();
+                        String st;
+                        while ((st = bufferedReader.readLine()) != null) {
+                            tips.add(st);
+                        }
                     } catch (IOException e) {
-                        LOGGER.error("Failed to load 'tips.txt' file in '{}'", name, e);
+                        LOGGER.error("Failed to load 'tips.txt' file in '{}'", getName(), e);
                     }
                 }
-                else if (entry.getName().equalsIgnoreCase("worldnames.txt"))
+                else if (entry.getName().endsWith("worldnames.txt"))
                 {
                     try (InputStream in = zipFile.getInputStream(entry); InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(reader))
                     {
                         worldNameGenerator = new WorldNameGenerator(bufferedReader.lines().collect(Collectors.toList()));
                     } catch (IOException e) {
-                        LOGGER.error("Failed to load 'worldnames.txt' file in '{}'", name, e);
+                        LOGGER.error("Failed to load 'worldnames.txt' file in '{}'", getName(), e);
                     }
                 }
             }
@@ -112,6 +118,67 @@ public class Language {
         }
     }
 
+    /**
+     * The package-private constructor to create a language instance. To obtain an instance, use {@link LanguageSeeker#seek(String)}.
+     */
+    Language(String id,
+             String name,
+             String region,
+             List<String> credits,
+             File directory) {
+        this.id = id;
+        this.name = name;
+        this.region = region;
+        this.credits = credits;
+        this.entries = new Properties();
+
+        if (directory != null) {
+            loadFromDirectory(directory);
+        }
+    }
+
+    private void loadFromDirectory(File directory) {
+        assert directory.isDirectory() : "Directory file must actually be a directory!";
+        File[] files = directory.listFiles();
+        if (files == null) return;
+        for (int i = 0; i < files.length; i++) {
+            File currentFile = files[i];
+            if (currentFile.isDirectory()) {
+                loadFromDirectory(currentFile);
+            }
+            else if (currentFile.getPath().endsWith(".lang")) {
+                try (InputStream in = Files.newInputStream(currentFile.toPath()); InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8))
+                {
+                    entries.load(reader);
+                } catch (IOException e) {
+                    LOGGER.error("Failed to load lang file '{}' in '{}'", currentFile.getName(), name, e);
+                }
+            }
+            else if (tips == null && currentFile.getPath().endsWith("tips.txt"))
+            {
+                try (InputStream stream = Files.newInputStream(currentFile.toPath()); InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(reader))
+                {
+                    tips = new ArrayList<>();
+                    String st;
+                    while ((st = bufferedReader.readLine()) != null) {
+                        tips.add(st);
+                    }
+                } catch (IOException e) {
+                    LOGGER.error("Failed to load 'tips.txt' file in '{}'", getName(), e);
+                }
+            }
+            else if (currentFile.getPath().endsWith("worldnames.txt"))
+            {
+                try (InputStream stream = Files.newInputStream(currentFile.toPath()); InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(reader))
+                {
+                    worldNameGenerator = new WorldNameGenerator(bufferedReader.lines().collect(Collectors.toList()));
+                } catch (IOException e) {
+                    LOGGER.error("Failed to load 'worldnames.txt' file in '{}'", getName(), e);
+                }
+            }
+        }
+    }
+
     public String translateKey(String key) {
         if (key == null) return null;
         String ret = entries.getProperty(key);
@@ -178,7 +245,7 @@ public class Language {
         public static final Default INSTANCE = new Default();
 
         private Default() {
-            super("en_US", "English", "US", Arrays.asList("Mojang", "jonkadelic", "skydeckagogo"), null);
+            super("en_US", "English", "US", Arrays.asList("Mojang", "jonkadelic", "skydeckagogo"), (ZipFile) null);
 
             entries.clear();
             for (String path : I18n.getFilesInDirectory("/lang/en_US/")){
diff --git a/src/main/java/net/minecraft/core/lang/LanguageSeeker.java b/src/main/java/net/minecraft/core/lang/LanguageSeeker.java
index 6f5e53f2f..247e19e52 100644
--- a/src/main/java/net/minecraft/core/lang/LanguageSeeker.java
+++ b/src/main/java/net/minecraft/core/lang/LanguageSeeker.java
@@ -11,6 +11,7 @@ import org.slf4j.Logger;
 
 import java.io.*;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.zip.ZipFile;
@@ -64,6 +65,31 @@ public class LanguageSeeker {
                         } catch (IOException e) {
                             throw new RuntimeException(e);
                         }
+                    } else if (file.isDirectory()) {
+                        File info = new File(file, "lang_info.json");
+                        if (info.exists()) {
+                            try (InputStream stream = Files.newInputStream(info.toPath())) {
+                                String content = readInputString(stream);
+                                JsonObject json = JsonParser.instance.parseString(content);
+                                String langId = json.getString("id");
+                                String langName = json.getString("name");
+                                String langRegion = json.getString("region");
+                                JsonArray array = json.getArray("credits");
+                                List<String> langCredits = new ArrayList<>();
+                                for (JsonElement element : array) {
+                                    String string = element.getAsString().toString();
+                                    if (string != null) {
+                                        langCredits.add(string);
+                                    }
+                                }
+                                if (langId != null && langName != null && !langCredits.isEmpty() && langId.equals(id)) {
+                                    ret = new Language(id, langName, langRegion, langCredits, file);
+                                    break; // Everything goes right; Let's get out from this loop
+                                }
+                            } catch (IOException e) {
+                                LOGGER.error("Exception loading json 'lang_info.json'!", e);
+                            }
+                        }
                     }
                 }
             }
@@ -106,6 +132,28 @@ public class LanguageSeeker {
                         } catch (IOException e) {
                             LOGGER.error("Exception loading json 'lang_info.json'!", e);
                         }
+                    } else if (file.isDirectory()) {
+                        File info = new File(file, "lang_info.json");
+                        if (info.exists()) {
+                            try (InputStream stream = Files.newInputStream(info.toPath())) {
+                                String content = readInputString(stream);
+                                JsonObject json = JsonParser.instance.parseString(content);
+                                String langId = json.getString("id");
+                                String langName = json.getString("name");
+                                String langRegion = json.getString("region");
+                                JsonArray array = json.getArray("credits");
+                                List<String> langCredits = new ArrayList<>();
+                                for (JsonElement element : array) {
+                                    String string = element.getAsString().toString();
+                                    if (string != null) {
+                                        langCredits.add(string);
+                                    }
+                                }
+                                ret.add(new Language(langId, langName, langRegion, langCredits, file));
+                            } catch (IOException e) {
+                                LOGGER.error("Exception loading json 'lang_info.json'!", e);
+                            }
+                        }
                     }
                 }
             }
diff --git a/src/main/java/net/minecraft/core/util/helper/Direction.java b/src/main/java/net/minecraft/core/util/helper/Direction.java
index 39b2ca599..797bb4d81 100644
--- a/src/main/java/net/minecraft/core/util/helper/Direction.java
+++ b/src/main/java/net/minecraft/core/util/helper/Direction.java
@@ -4,13 +4,13 @@ import net.minecraft.core.entity.Mob;
 
 public enum Direction {
 	
-	NORTH(Axis.Z, 0, 0, -1),
-	EAST(Axis.X, 1, 0, 0),
-	SOUTH(Axis.Z, 0, 0, 1),
-	WEST(Axis.X, -1, 0, 0),
-	UP(Axis.Y, 0, 1, 0),
-	DOWN(Axis.Y, 0, -1, 0),
-	NONE(Axis.NONE, 0, 0, 0);
+	NORTH(Axis.Z, 0, 0, -1, "north"),
+	EAST(Axis.X, 1, 0, 0, "east"),
+	SOUTH(Axis.Z, 0, 0, 1, "south"),
+	WEST(Axis.X, -1, 0, 0, "west"),
+	UP(Axis.Y, 0, 1, 0, "up"),
+	DOWN(Axis.Y, 0, -1, 0, "down"),
+	NONE(Axis.NONE, 0, 0, 0, "none");
 	
 	public static final Direction[] directions = new Direction[6];
 	public static final Direction[] horizontalDirections = new Direction[4];
@@ -86,15 +86,17 @@ public enum Direction {
 	private final int offsetX;
 	private final int offsetY;
 	private final int offsetZ;
+	private final String translationKey;
 	
 	@Deprecated
 	public int index;
 	
-	private Direction(Axis axis, int offsetX, int offsetY, int offsetZ) {
+	Direction(Axis axis, int offsetX, int offsetY, int offsetZ, String translationKey) {
 		this.axis = axis;
 		this.offsetX = offsetX;
 		this.offsetY = offsetY;
 		this.offsetZ = offsetZ;
+		this.translationKey = "direction." + translationKey;
 	}
 	
 	public int getOffsetX() {
@@ -108,6 +110,10 @@ public enum Direction {
 	public int getOffsetZ() {
 		return offsetZ;
 	}
+
+	public String getTranslationKey() {
+		return translationKey;
+	}
 	
 	public Direction getOpposite() {
 		return opposite;
diff --git a/src/main/java/net/minecraft/core/util/helper/GetSkinUrlThread.java b/src/main/java/net/minecraft/core/util/helper/GetSkinUrlThread.java
index 195f2af64..aa690fab2 100644
--- a/src/main/java/net/minecraft/core/util/helper/GetSkinUrlThread.java
+++ b/src/main/java/net/minecraft/core/util/helper/GetSkinUrlThread.java
@@ -36,20 +36,20 @@ public class GetSkinUrlThread extends Thread{
 	public void run() {
 		String name = player.username;
 		this.uuid = player.uuid;
-		if(name == null || name.length() == 0) return;
+		if(name == null || name.isEmpty()) return;
 
 		//  Attempt to get the skin 3 times before failing
 		String string = null;
 		for (int i = 0; i < 3; i++) {
 			string = getSkinObject(name);
-			if (string != null) break;
+			if (string != null && !string.isEmpty()) break;
 			try {
 				sleep(5000);
 			} catch (InterruptedException e) {
 				break;
 			}
 		}
-		if (string == null) return;
+		if (string == null || string.isEmpty()) return;
 		
 		JsonObject object = jsonParser.parse(string);
 		JsonArray properties = object.getArray("properties");
diff --git a/src/main/java/net/minecraft/core/util/helper/Side.java b/src/main/java/net/minecraft/core/util/helper/Side.java
index 1dcdef4bb..a2c1faf97 100644
--- a/src/main/java/net/minecraft/core/util/helper/Side.java
+++ b/src/main/java/net/minecraft/core/util/helper/Side.java
@@ -2,13 +2,13 @@ package net.minecraft.core.util.helper;
 
 public enum Side {
 	
-	TOP(Axis.Y, 0, 1, 0),
-	BOTTOM(Axis.Y, 0, -1, 0),
-	NORTH(Axis.Z, 0, 0, -1),
-	EAST(Axis.X, 1, 0, 0),
-	SOUTH(Axis.Z, 0, 0, 1),
-	WEST(Axis.X, -1, 0, 0),
-	NONE(Axis.NONE, 0, 0, 0);
+	TOP(Axis.Y, 0, 1, 0, "top"),
+	BOTTOM(Axis.Y, 0, -1, 0, "bottom"),
+	NORTH(Axis.Z, 0, 0, -1, "north"),
+	EAST(Axis.X, 1, 0, 0, "east"),
+	SOUTH(Axis.Z, 0, 0, 1, "south"),
+	WEST(Axis.X, -1, 0, 0, "west"),
+	NONE(Axis.NONE, 0, 0, 0, "none");
 	
 	public static final Side posX = EAST;
 	public static final Side negX = WEST;
@@ -72,12 +72,15 @@ public enum Side {
 	private final int offsetX;
 	private final int offsetY;
 	private final int offsetZ;
+
+	private final String translationKey;
 	
-	private Side(Axis axis, int offsetX, int offsetY, int offsetZ) {
+	Side(Axis axis, int offsetX, int offsetY, int offsetZ, String translationKey) {
 		this.axis = axis;
 		this.offsetX = offsetX;
 		this.offsetY = offsetY;
 		this.offsetZ = offsetZ;
+		this.translationKey = translationKey;
 	}
 	
 	public int getOffsetX() {
diff --git a/src/main/java/net/minecraft/core/world/World.java b/src/main/java/net/minecraft/core/world/World.java
index 4412efabb..3771e770b 100644
--- a/src/main/java/net/minecraft/core/world/World.java
+++ b/src/main/java/net/minecraft/core/world/World.java
@@ -16,6 +16,7 @@ import java.util.UUID;
 import com.mojang.logging.LogUtils;
 import com.mojang.nbt.CompoundTag;
 
+import net.minecraft.core.data.registry.Registries;
 import net.minecraft.core.enums.Difficulty;
 import net.minecraft.client.render.LightmapHelper;
 import net.minecraft.client.render.camera.ICamera;
@@ -72,6 +73,7 @@ import net.minecraft.core.world.type.WorldTypes;
 import net.minecraft.core.world.weather.Weather;
 import net.minecraft.core.world.weather.WeatherManager;
 import net.minecraft.core.world.weather.Weathers;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 
@@ -87,7 +89,7 @@ public class World implements WorldSource {
     private final TreeSet<NextTickListEntry> scheduledTickTreeSet = new TreeSet<>();
     private final Set<NextTickListEntry> scheduledTickSet = new HashSet<>();
     public List<BlockEntity> loadedBlockEntityList = new ArrayList<>();
-    private final List<BlockEntity> field_30900_E = new ArrayList<>();
+    private final List<BlockEntity> queuedBlockEntityAdditions = new ArrayList<>();
     public List<Player> players = new ArrayList<>();
     public List<Entity> weatherEffects = new ArrayList<>();
     public final ArrayList<AABB> collidingBoundingBoxes = new ArrayList<>();
@@ -118,7 +120,7 @@ public class World implements WorldSource {
     public boolean findingSpawnPoint;
     protected boolean enoughPlayersSleeping;
     public SavedDataStorage savedDataStorage;
-    private boolean field_31055_L;
+    private boolean updatingBlockEntities;
     private int lightingUpdatesCounter = 0;
     public final SpawnerMobs.SpawnerConfig spawningConfig;
     static int lightingUpdatesScheduled = 0;
@@ -567,9 +569,9 @@ public class World implements WorldSource {
     }
 
     @Override
-    public Biome getBlockBiome(int x, int y, int z)
+    public @NotNull Biome getBlockBiome(int x, int y, int z)
     {
-        if(x < 0xfe17b800 || z < 0xfe17b800 || x >= 0x1e84800 || z > 0x1e84800)
+        if(x < -32000000 || z < -32000000 || x >= 32000000 || z > 32000000)
         {
             return Biomes.OVERWORLD_PLAINS;
         }
@@ -595,6 +597,8 @@ public class World implements WorldSource {
                 }
             }
 
+            assert biome != null : "Biome should never be null!";
+
             return biome;
         }
     }
@@ -1865,63 +1869,47 @@ public class World implements WorldSource {
             releaseEntitySkin(entity2);
         }
 
-        field_31055_L = true;
-        Iterator iterator = loadedBlockEntityList.iterator();
-        do
-        {
-            if(!iterator.hasNext())
-            {
-                break;
-            }
-            BlockEntity tileentity = (BlockEntity)iterator.next();
-            if(!tileentity.isInvalid() && isBlockLoaded(tileentity.x, tileentity.y, tileentity.z))
-            {
-                tileentity.tick();
+        updatingBlockEntities = true;
+        Iterator<BlockEntity> iterator = loadedBlockEntityList.iterator();
+        while (iterator.hasNext()) {
+            BlockEntity blockEntity = iterator.next();
+            if (!blockEntity.isInvalid() && isBlockLoaded(blockEntity.x, blockEntity.y, blockEntity.z)) {
+                blockEntity.tick();
             }
-            if(tileentity.isInvalid())
-            {
+            if (blockEntity.isInvalid()) {
                 iterator.remove();
-                Chunk chunk = getChunkFromChunkCoords(Math.floorDiv(tileentity.x, Chunk.CHUNK_SIZE_X), Math.floorDiv(tileentity.z, Chunk.CHUNK_SIZE_Z));
-                if(chunk != null)
-                {
-                    chunk.removeBlockEntity(tileentity.x & (Chunk.CHUNK_SIZE_X - 1), tileentity.y, tileentity.z & (Chunk.CHUNK_SIZE_Z - 1));
+                Chunk chunk = getChunkFromChunkCoords(Math.floorDiv(blockEntity.x, Chunk.CHUNK_SIZE_X), Math.floorDiv(blockEntity.z, Chunk.CHUNK_SIZE_Z));
+                if (chunk != null) {
+                    chunk.removeBlockEntity(blockEntity.x & (Chunk.CHUNK_SIZE_X - 1), blockEntity.y, blockEntity.z & (Chunk.CHUNK_SIZE_Z - 1));
                 }
             }
-        } while(true);
-        field_31055_L = false;
-        if(!field_30900_E.isEmpty())
+        }
+        updatingBlockEntities = false;
+        if(!queuedBlockEntityAdditions.isEmpty())
         {
-            Iterator iterator1 = field_30900_E.iterator();
-            do
-            {
-                if(!iterator1.hasNext())
-                {
-                    break;
-                }
-                BlockEntity tileentity1 = (BlockEntity)iterator1.next();
-                if(!tileentity1.isInvalid())
-                {
-                    if(!loadedBlockEntityList.contains(tileentity1))
-                    {
-                        loadedBlockEntityList.add(tileentity1);
+            Iterator<BlockEntity> entityIterator = queuedBlockEntityAdditions.iterator();
+            while (entityIterator.hasNext()) {
+                BlockEntity blockEntity = entityIterator.next();
+                if (!blockEntity.isInvalid()) {
+                    if (!loadedBlockEntityList.contains(blockEntity)) {
+                        loadedBlockEntityList.add(blockEntity);
                     }
-                    Chunk chunk1 = getChunkFromChunkCoords(Math.floorDiv(tileentity1.x, Chunk.CHUNK_SIZE_X), Math.floorDiv(tileentity1.z, Chunk.CHUNK_SIZE_Z));
-                    if(chunk1 != null)
-                    {
-                        chunk1.setBlockEntity(tileentity1.x & (Chunk.CHUNK_SIZE_X - 1), tileentity1.y, tileentity1.z & (Chunk.CHUNK_SIZE_Z - 1), tileentity1);
+                    Chunk chunk1 = getChunkFromChunkCoords(Math.floorDiv(blockEntity.x, Chunk.CHUNK_SIZE_X), Math.floorDiv(blockEntity.z, Chunk.CHUNK_SIZE_Z));
+                    if (chunk1 != null) {
+                        chunk1.setBlockEntity(blockEntity.x & (Chunk.CHUNK_SIZE_X - 1), blockEntity.y, blockEntity.z & (Chunk.CHUNK_SIZE_Z - 1), blockEntity);
                     }
-                    markBlockNeedsUpdate(tileentity1.x, tileentity1.y, tileentity1.z);
+                    markBlockNeedsUpdate(blockEntity.x, blockEntity.y, blockEntity.z);
                 }
-            } while(true);
-            field_30900_E.clear();
+            }
+            queuedBlockEntityAdditions.clear();
         }
     }
 
     public void addAllBlockEntities(Collection<BlockEntity> blockEntities)
     {
-        if(field_31055_L)
+        if(updatingBlockEntities)
         {
-            field_30900_E.addAll(blockEntities);
+            queuedBlockEntityAdditions.addAll(blockEntities);
         } else
         {
             loadedBlockEntityList.addAll(blockEntities);
@@ -2303,12 +2291,12 @@ public class World implements WorldSource {
     {
         if(!tileentity.isInvalid())
         {
-            if(field_31055_L)
+            if(updatingBlockEntities)
             {
                 tileentity.x = i;
                 tileentity.y = j;
                 tileentity.z = k;
-                field_30900_E.add(tileentity);
+                queuedBlockEntityAdditions.add(tileentity);
             } else
             {
                 loadedBlockEntityList.add(tileentity);
@@ -2323,16 +2311,16 @@ public class World implements WorldSource {
 
     public void removeBlockTileEntity(int i, int j, int k)
     {
-        BlockEntity tileentity = getBlockEntity(i, j, k);
+        BlockEntity blockEntity = getBlockEntity(i, j, k);
 
-        if(tileentity != null && field_31055_L)
+        if(blockEntity != null && updatingBlockEntities)
         {
-            tileentity.invalidate();
+            blockEntity.invalidate();
         } else
         {
-            if(tileentity != null)
+            if(blockEntity != null)
             {
-                loadedBlockEntityList.remove(tileentity);
+                loadedBlockEntityList.remove(blockEntity);
             }
             Chunk chunk = getChunkFromChunkCoords(Math.floorDiv(i, Chunk.CHUNK_SIZE_X), Math.floorDiv(k, Chunk.CHUNK_SIZE_Z));
             if(chunk != null)
diff --git a/src/main/java/net/minecraft/core/world/chunk/Chunk.java b/src/main/java/net/minecraft/core/world/chunk/Chunk.java
index d894a238d..fdbdec6eb 100644
--- a/src/main/java/net/minecraft/core/world/chunk/Chunk.java
+++ b/src/main/java/net/minecraft/core/world/chunk/Chunk.java
@@ -11,6 +11,7 @@ import net.minecraft.core.enums.LightLayer;
 import net.minecraft.core.world.biome.Biome;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.EntityBlock;
+import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 
 import java.util.*;
@@ -39,7 +40,7 @@ public class Chunk
     public boolean neverSave;
     public boolean hasEntities;
     public long lastSaveTime;
-    private final ChunkSection[] sections = new ChunkSection[CHUNK_SECTIONS];
+    protected final ChunkSection[] sections = new ChunkSection[CHUNK_SECTIONS];
 
     public Chunk(World world, int x, int z)
     {
@@ -353,7 +354,7 @@ public class Chunk
         isModified = true;
     }
     
-    public Biome getBlockBiome(int x, int y, int z)
+    public @Nullable Biome getBlockBiome(int x, int y, int z)
     {
         if (x < 0 || x >= CHUNK_SIZE_X || y < 0 || y >= CHUNK_SECTIONS * ChunkSection.SECTION_SIZE_Y || z < 0 || z >= CHUNK_SIZE_Z) return null;
 
diff --git a/src/main/java/net/minecraft/core/world/chunk/ChunkLoaderRegion.java b/src/main/java/net/minecraft/core/world/chunk/ChunkLoaderRegion.java
index 025affe31..afa6a2ee5 100644
--- a/src/main/java/net/minecraft/core/world/chunk/ChunkLoaderRegion.java
+++ b/src/main/java/net/minecraft/core/world/chunk/ChunkLoaderRegion.java
@@ -2,6 +2,7 @@ package net.minecraft.core.world.chunk;
 
 import com.mojang.logging.LogUtils;
 import com.mojang.nbt.CompoundTag;
+import com.mojang.nbt.CorruptedNBTException;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.save.LevelData;
 import net.minecraft.core.world.save.mcregion.RegionFileCache;
@@ -9,6 +10,7 @@ import com.mojang.nbt.NbtIo;
 import org.slf4j.Logger;
 
 import java.io.*;
+import java.util.zip.ZipException;
 
 public class ChunkLoaderRegion
     implements IChunkLoader
@@ -29,7 +31,15 @@ public class ChunkLoaderRegion
         CompoundTag tag;
         if(regionStream != null)
         {
-            tag = NbtIo.read(regionStream);
+            try {
+                tag = NbtIo.read(regionStream);
+            } catch (ZipException zipException) {
+                LOGGER.warn("Zipped data for chunk at {}, {} is too corrupted to read! Assuming unrecoverable and resetting!", x, z, zipException);
+                return null;
+            } catch (CorruptedNBTException corruptedNBTException){
+                LOGGER.warn("NBT data for chunk at {}, {} is too corrupted to read! Assuming unrecoverable and resetting!", x, z, corruptedNBTException);
+                return null;
+            }
         }
         else
         {
diff --git a/src/main/java/net/minecraft/core/world/chunk/EmptyChunk.java b/src/main/java/net/minecraft/core/world/chunk/EmptyChunk.java
index 37020d40c..280ed9359 100644
--- a/src/main/java/net/minecraft/core/world/chunk/EmptyChunk.java
+++ b/src/main/java/net/minecraft/core/world/chunk/EmptyChunk.java
@@ -20,6 +20,12 @@ public class EmptyChunk extends Chunk
     public EmptyChunk(World world, int i, int j)
     {
         super(world, i, j);
+        // TODO, find out why we have init methods that are never actually used
+        for (ChunkSection section : sections) {
+            if (section != null) {
+                section.init();
+            }
+        }
         neverSave = true;
     }
 
diff --git a/src/main/resources/assets/minecraft/textures/block/rail_detector_overlay.png b/src/main/resources/assets/minecraft/textures/block/rail_detector_overlay.png
new file mode 100644
index 000000000..51abdea39
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/block/rail_detector_overlay.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip.png b/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip.png
new file mode 100644
index 000000000..21c439b3b
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip_disabled.png b/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip_disabled.png
new file mode 100644
index 000000000..9bec96913
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip_disabled.png differ
diff --git a/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip_highlighted.png b/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip_highlighted.png
new file mode 100644
index 000000000..61a5031f0
Binary files /dev/null and b/src/main/resources/assets/minecraft/textures/gui/sprites/misc/button_flag_flip_highlighted.png differ
diff --git a/src/main/resources/credits.txt b/src/main/resources/credits.txt
index 186cd7487..bf40d4e61 100644
--- a/src/main/resources/credits.txt
+++ b/src/main/resources/credits.txt
@@ -23,6 +23,8 @@ DirtPiper
 Vivian (vivianfox.)
 Snowman64
 AndroidDr (Pedro270707)
+Bigjango
+Tocinin
 
 !Thank you for playing
 !Better than Adventure!
\ No newline at end of file
