]> git.lizzy.rs Git - BoundingBoxOutlineReloaded.git/commitdiff
Move stuff about a bit
authorirtimaled <irtimaled@gmail.com>
Thu, 28 Dec 2017 09:27:26 +0000 (01:27 -0800)
committerirtimaled <irtimaled@gmail.com>
Sat, 30 Dec 2017 03:48:26 +0000 (19:48 -0800)
36 files changed:
java/com/irtimaled/bbor/BoundingBox.java [deleted file]
java/com/irtimaled/bbor/BoundingBoxCache.java [deleted file]
java/com/irtimaled/bbor/BoundingBoxOutlineReloaded.java [deleted file]
java/com/irtimaled/bbor/BoundingBoxSlimeChunk.java [deleted file]
java/com/irtimaled/bbor/BoundingBoxStructure.java [deleted file]
java/com/irtimaled/bbor/BoundingBoxVillage.java [deleted file]
java/com/irtimaled/bbor/BoundingBoxWorldSpawn.java [deleted file]
java/com/irtimaled/bbor/ClientProxy.java [deleted file]
java/com/irtimaled/bbor/CommonProxy.java [deleted file]
java/com/irtimaled/bbor/ConfigManager.java [deleted file]
java/com/irtimaled/bbor/DimensionProcessor.java [deleted file]
java/com/irtimaled/bbor/IEventHandler.java [deleted file]
java/com/irtimaled/bbor/StructureType.java [deleted file]
java/com/irtimaled/bbor/WorldData.java [deleted file]
java/com/irtimaled/bbor/client/BoundingBoxOutlineReloaded.java [new file with mode: 0644]
java/com/irtimaled/bbor/client/ClientProxy.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/BoundingBoxCache.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/CommonProxy.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/DimensionProcessor.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/IEventHandler.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/StructureType.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/models/BoundingBox.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/models/BoundingBoxSlimeChunk.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/models/BoundingBoxStructure.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/models/BoundingBoxVillage.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/models/BoundingBoxWorldSpawn.java [new file with mode: 0644]
java/com/irtimaled/bbor/common/models/WorldData.java [new file with mode: 0644]
java/com/irtimaled/bbor/config/ConfigManager.java [new file with mode: 0644]
java/com/irtimaled/bbor/forge/ForgeClientProxy.java
java/com/irtimaled/bbor/forge/ForgeCommonProxy.java
java/com/irtimaled/bbor/forge/ForgeMod.java
java/com/irtimaled/bbor/forge/messages/AddBoundingBoxMessage.java
java/com/irtimaled/bbor/forge/messages/BoundingBoxDeserializer.java
java/com/irtimaled/bbor/forge/messages/BoundingBoxSerializer.java
java/com/irtimaled/bbor/forge/messages/InitializeClientMessage.java
java/com/irtimaled/bbor/forge/messages/RemoveBoundingBoxMessage.java

diff --git a/java/com/irtimaled/bbor/BoundingBox.java b/java/com/irtimaled/bbor/BoundingBox.java
deleted file mode 100644 (file)
index 749a727..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.util.math.AxisAlignedBB;
-import net.minecraft.util.math.BlockPos;
-
-import java.awt.*;
-
-public abstract class BoundingBox {
-    private final Color color;
-    private final BlockPos minBlockPos;
-    private final BlockPos maxBlockPos;
-
-    protected BoundingBox(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
-        this.minBlockPos = minBlockPos;
-        this.maxBlockPos = maxBlockPos;
-        this.color = color;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + minBlockPos.hashCode();
-        result = prime * result + maxBlockPos.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        BoundingBox other = (BoundingBox) obj;
-        if (!minBlockPos.equals(other.minBlockPos))
-            return false;
-        if (!maxBlockPos.equals(other.maxBlockPos))
-            return false;
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return "(" + minBlockPos.toString() + "; " + maxBlockPos.toString() + ")";
-    }
-
-    public AxisAlignedBB toAxisAlignedBB() {
-        return toAxisAlignedBB(true);
-    }
-
-    public AxisAlignedBB toAxisAlignedBB(boolean extendMaxByOne) {
-        AxisAlignedBB axisAlignedBB = new AxisAlignedBB(minBlockPos, maxBlockPos);
-        if (extendMaxByOne)
-            return axisAlignedBB.expand(1, 1, 1);
-        return axisAlignedBB;
-    }
-
-    public BlockPos getMinBlockPos() {
-        return minBlockPos;
-    }
-
-    public BlockPos getMaxBlockPos() {
-        return maxBlockPos;
-    }
-
-    public Color getColor() {
-        return color;
-    }
-}
diff --git a/java/com/irtimaled/bbor/BoundingBoxCache.java b/java/com/irtimaled/bbor/BoundingBoxCache.java
deleted file mode 100644 (file)
index 53dcde7..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.irtimaled.bbor;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class BoundingBoxCache {
-    private Map<BoundingBox, Set<BoundingBox>> cache = new ConcurrentHashMap<>();
-
-    public Map<BoundingBox, Set<BoundingBox>> getBoundingBoxes() {
-        return cache;
-    }
-
-    public synchronized void refresh() {
-    }
-
-    public void close() {
-        cache.clear();
-    }
-
-    public boolean isCached(BoundingBox key) {
-        return cache.containsKey(key);
-    }
-
-    public void addBoundingBoxes(BoundingBox key, Set<BoundingBox> boundingBoxes) {
-        cache.put(key, boundingBoxes);
-    }
-
-    public void addBoundingBox(BoundingBox key) {
-        Set<BoundingBox> boundingBoxes = new HashSet<>();
-        boundingBoxes.add(key);
-        addBoundingBoxes(key, boundingBoxes);
-    }
-
-    public void removeBoundingBox(BoundingBox key) {
-        if (cache.containsKey(key)) {
-            cache.remove(key);
-        }
-    }
-}
diff --git a/java/com/irtimaled/bbor/BoundingBoxOutlineReloaded.java b/java/com/irtimaled/bbor/BoundingBoxOutlineReloaded.java
deleted file mode 100644 (file)
index dfb4771..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.client.Minecraft;
-import net.minecraft.network.NetworkManager;
-import net.minecraft.world.World;
-import net.minecraft.world.chunk.Chunk;
-
-import java.io.File;
-
-public class BoundingBoxOutlineReloaded {
-    public static ClientProxy proxy;
-
-    public static void init() {
-        proxy = new ClientProxy();
-        proxy.init(new ConfigManager(new File(Minecraft.getMinecraft().mcDataDir, "config")));
-    }
-
-    public static void chunkLoaded(Chunk chunk) {
-        proxy.chunkLoaded(chunk);
-    }
-
-    public static void worldLoaded(World world) {
-        proxy.worldLoaded(world);
-    }
-
-    public static void keyPressed() {
-        proxy.keyPressed();
-    }
-
-    public static void render(float partialTicks) {
-        proxy.render(partialTicks);
-    }
-
-    public static void playerConnectedToServer(NetworkManager networkManager) {
-        proxy.playerConnectedToServer(networkManager);
-    }
-
-    public static void playerDisconnectedFromServer() {
-        proxy.playerDisconnectedFromServer();
-    }
-}
diff --git a/java/com/irtimaled/bbor/BoundingBoxSlimeChunk.java b/java/com/irtimaled/bbor/BoundingBoxSlimeChunk.java
deleted file mode 100644 (file)
index c8b85a0..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.ChunkPos;
-
-import java.awt.*;
-
-public class BoundingBoxSlimeChunk extends BoundingBox {
-    private BoundingBoxSlimeChunk(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
-        super(minBlockPos, maxBlockPos, color);
-    }
-
-    public static BoundingBoxSlimeChunk from(ChunkPos chunkCoordIntPair, Color color) {
-        BlockPos minBlockPos = new BlockPos(chunkCoordIntPair.getXStart(), 1, chunkCoordIntPair.getZStart());
-        BlockPos maxBlockPos = new BlockPos(chunkCoordIntPair.getXEnd(), 38, chunkCoordIntPair.getZEnd());
-        return new BoundingBoxSlimeChunk(minBlockPos, maxBlockPos, color);
-    }
-
-    public static BoundingBoxSlimeChunk from(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
-        return new BoundingBoxSlimeChunk(minBlockPos, maxBlockPos, color);
-    }
-}
diff --git a/java/com/irtimaled/bbor/BoundingBoxStructure.java b/java/com/irtimaled/bbor/BoundingBoxStructure.java
deleted file mode 100644 (file)
index cbddbd3..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.world.gen.structure.StructureBoundingBox;
-
-import java.awt.*;
-
-public class BoundingBoxStructure extends BoundingBox {
-    private BoundingBoxStructure(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
-        super(minBlockPos, maxBlockPos, color);
-    }
-
-    public static BoundingBoxStructure from(StructureBoundingBox bb, Color color) {
-        BlockPos minBlockPos = new BlockPos(bb.minX, bb.minY, bb.minZ);
-        BlockPos maxBlockPos = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
-        return new BoundingBoxStructure(minBlockPos, maxBlockPos, color);
-    }
-
-    public static BoundingBoxStructure from(int[] bb, Color color) {
-        BlockPos minBlockPos = new BlockPos(bb[0], bb[1], bb[2]);
-        BlockPos maxBlockPos = new BlockPos(bb[3], bb[4], bb[5]);
-        return new BoundingBoxStructure(minBlockPos, maxBlockPos, color);
-    }
-
-    public static BoundingBoxStructure from(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
-        return new BoundingBoxStructure(minBlockPos, maxBlockPos, color);
-    }
-}
diff --git a/java/com/irtimaled/bbor/BoundingBoxVillage.java b/java/com/irtimaled/bbor/BoundingBoxVillage.java
deleted file mode 100644 (file)
index 58ac6e3..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.util.math.BlockPos;
-
-import java.awt.*;
-import java.util.Set;
-
-public class BoundingBoxVillage extends BoundingBox {
-    private final BlockPos center;
-    private final Integer radius;
-    private final boolean spawnsIronGolems;
-    private Set<BlockPos> doors;
-    private Double centerOffsetX;
-    private Double centerOffsetZ;
-
-    protected BoundingBoxVillage(BlockPos center, Integer radius, Color color, boolean spawnsIronGolems, Set<BlockPos> doors, BlockPos minBlockPos, BlockPos maxBlockPos) {
-        super(minBlockPos, maxBlockPos, color);
-        this.center = center;
-        this.radius = radius;
-        this.spawnsIronGolems = spawnsIronGolems;
-        this.doors = doors;
-        calculateCenterOffsets(doors);
-    }
-
-    public static BoundingBoxVillage from(BlockPos center, Integer radius, int population, Set<BlockPos> doors) {
-        return from(center, radius, null, population, doors);
-    }
-
-    public static BoundingBoxVillage from(BlockPos center, Integer radius, Color color, int population, Set<BlockPos> doors) {
-        Boolean spawnsIronGolems = population >= 10 && doors.size() >= 21;
-        return from(center, radius, color, spawnsIronGolems, doors);
-    }
-
-    public static BoundingBoxVillage from(BlockPos center, Integer radius, Color color, boolean spawnsIronGolems, Set<BlockPos> doors) {
-        BlockPos minBlockPos = new BlockPos(center.getX() - radius,
-                center.getY() - 4,
-                center.getZ() - radius);
-        BlockPos maxBlockPos = new BlockPos(center.getX() + radius,
-                center.getY() + 4,
-                center.getZ() + radius);
-        if (color == null)
-            color = getNextColor();
-        return new BoundingBoxVillage(center, radius, color, spawnsIronGolems, doors, minBlockPos, maxBlockPos);
-    }
-
-    private void calculateCenterOffsets(Set<BlockPos> doors) {
-        boolean processedFirstDoor = false;
-        int minX = 0;
-        int maxX = 0;
-        int minZ = 0;
-        int maxZ = 0;
-        for (BlockPos door : doors) {
-            if (!processedFirstDoor ||
-                    (minX > door.getX()))
-                minX = door.getX();
-            if (!processedFirstDoor ||
-                    maxX < door.getX())
-                maxX = door.getX();
-            if (!processedFirstDoor ||
-                    minZ > door.getZ())
-                minZ = door.getZ();
-            if (!processedFirstDoor ||
-                    maxZ < door.getZ())
-                maxZ = door.getZ();
-
-            processedFirstDoor = true;
-        }
-        centerOffsetX = Math.abs(maxX - minX) % 2 == 0 ? 0.5 : (minX < 0 ? 0 : 1);
-        centerOffsetZ = Math.abs(maxZ - minZ) % 2 == 0 ? 0.5 : (minZ < 0 ? 0 : 1);
-    }
-
-    @Override
-    public String toString() {
-        return "(" + center.toString() + "; " + radius.toString() + ")";
-    }
-
-    public Integer getRadius() {
-        return radius;
-    }
-
-    public BlockPos getCenter() {
-        return center;
-    }
-
-    public Double getCenterOffsetX() {
-        return centerOffsetX;
-    }
-
-    public Double getCenterOffsetZ() {
-        return centerOffsetZ;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = super.hashCode();
-        for (BlockPos door : doors) {
-            result = prime * result + door.hashCode();
-        }
-        return result;
-    }
-
-    public boolean getSpawnsIronGolems() {
-        return spawnsIronGolems;
-    }
-
-    private static int colorIndex = -1;
-
-    public static Color getNextColor() {
-        ++colorIndex;
-        switch (colorIndex % 6) {
-            case 0:
-                return Color.RED;
-            case 1:
-                return Color.GREEN;
-            case 2:
-                return Color.BLUE;
-            case 3:
-                return Color.MAGENTA;
-            case 4:
-                return Color.YELLOW;
-            case 5:
-                return Color.CYAN;
-        }
-        return Color.WHITE;
-    }
-
-    public Set<BlockPos> getDoors() {
-        return doors;
-    }
-}
diff --git a/java/com/irtimaled/bbor/BoundingBoxWorldSpawn.java b/java/com/irtimaled/bbor/BoundingBoxWorldSpawn.java
deleted file mode 100644 (file)
index 3820110..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.util.math.BlockPos;
-
-import java.awt.*;
-
-public class BoundingBoxWorldSpawn extends BoundingBox {
-    protected BoundingBoxWorldSpawn(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
-        super(minBlockPos, maxBlockPos, color);
-    }
-
-    public static BoundingBoxWorldSpawn from(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
-        return new BoundingBoxWorldSpawn(minBlockPos, maxBlockPos, color);
-    }
-}
diff --git a/java/com/irtimaled/bbor/ClientProxy.java b/java/com/irtimaled/bbor/ClientProxy.java
deleted file mode 100644 (file)
index eeee8b6..0000000
+++ /dev/null
@@ -1,706 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.multiplayer.WorldClient;
-import net.minecraft.client.renderer.BufferBuilder;
-import net.minecraft.client.renderer.Tessellator;
-import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
-import net.minecraft.client.settings.KeyBinding;
-import net.minecraft.entity.player.EntityPlayer;
-import net.minecraft.nbt.CompressedStreamTools;
-import net.minecraft.nbt.NBTTagCompound;
-import net.minecraft.nbt.NBTTagList;
-import net.minecraft.network.NetworkManager;
-import net.minecraft.util.math.AxisAlignedBB;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.ChunkPos;
-import net.minecraft.util.math.MathHelper;
-import net.minecraft.world.DimensionType;
-import org.apache.commons.lang3.ArrayUtils;
-import org.lwjgl.input.Keyboard;
-import org.lwjgl.opengl.GL11;
-
-import java.awt.*;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-
-public class ClientProxy extends CommonProxy {
-    private double activeY;
-    private boolean active;
-    private boolean outerBoxOnly;
-    private KeyBinding activeHotKey;
-    private KeyBinding outerBoxOnlyHotKey;
-    private double playerX;
-    private double playerY;
-    private double playerZ;
-    private BoundingBox worldSpawnBoundingBox;
-    private BoundingBox spawnChunksBoundingBox;
-    private BoundingBox lazySpawnChunksBoundingBox;
-
-    public void keyPressed() {
-        if (activeHotKey.isPressed()) {
-            active = !active;
-            if (active)
-                activeY = playerY;
-        } else if (outerBoxOnlyHotKey.isPressed()) {
-            outerBoxOnly = !outerBoxOnly;
-        }
-    }
-
-    @Override
-    public void init(ConfigManager configManager) {
-        super.init(configManager);
-        String category = "Bounding Box Outline Reloaded";
-        activeHotKey = new KeyBinding("Toggle On/Off", Keyboard.KEY_B, category);
-        outerBoxOnlyHotKey = new KeyBinding("Toggle Display Outer Box Only", Keyboard.KEY_O, category);
-        Minecraft.getMinecraft().gameSettings.keyBindings = ArrayUtils.addAll(Minecraft.getMinecraft().gameSettings.keyBindings, activeHotKey, outerBoxOnlyHotKey);
-    }
-
-    @Override
-    public void setWorldData(WorldData worldData) {
-        worldSpawnBoundingBox = null;
-        spawnChunksBoundingBox = null;
-        lazySpawnChunksBoundingBox = null;
-        super.setWorldData(worldData);
-    }
-
-    public void render(float partialTicks) {
-        EntityPlayer entityPlayer = Minecraft.getMinecraft().player;
-        playerX = entityPlayer.lastTickPosX + (entityPlayer.posX - entityPlayer.lastTickPosX) * (double) partialTicks;
-        playerY = entityPlayer.lastTickPosY + (entityPlayer.posY - entityPlayer.lastTickPosY) * (double) partialTicks;
-        playerZ = entityPlayer.lastTickPosZ + (entityPlayer.posZ - entityPlayer.lastTickPosZ) * (double) partialTicks;
-
-        if (this.active) {
-            DimensionType dimensionType = DimensionType.getById(entityPlayer.dimension);
-            Map<BoundingBox, Set<BoundingBox>> boundingBoxes = null;
-            if (boundingBoxCacheMap.containsKey(dimensionType)) {
-                boundingBoxes = boundingBoxCacheMap.get(dimensionType).getBoundingBoxes();
-            }
-            renderBoundingBoxes(boundingBoxes, getClientBoundingBoxes(dimensionType));
-        }
-    }
-
-    public void playerConnectedToServer(NetworkManager networkManager) {
-        SocketAddress remoteAddress = networkManager.getRemoteAddress();
-        if (remoteAddress instanceof InetSocketAddress) {
-            InetSocketAddress socketAddress = (InetSocketAddress) remoteAddress;
-            loadLocalStructures(socketAddress.getHostName(), socketAddress.getPort());
-        }
-    }
-
-    private void loadLocalStructures(String host, int port) {
-        Logger.info("Looking for local structures (host:port=%s:%d)", host, port);
-        String path = String.format("BBOutlineReloaded%s%s%s%d", File.separator, host, File.separator, port);
-        File localStructuresFolder = new File(configManager.configDir, path);
-        Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
-        if (!localStructuresFolder.exists()) {
-            path = String.format("BBOutlineReloaded%s%s", File.separator, host);
-            localStructuresFolder = new File(configManager.configDir, path);
-            Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
-        }
-        if (!localStructuresFolder.exists()) {
-            path = String.format("BBOutlineReloaded%s%s,%d", File.separator, host, port);
-            localStructuresFolder = new File(configManager.configDir, path);
-            Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
-        }
-        if (!localStructuresFolder.exists()) {
-            Logger.info("No local structures folders found");
-            return;
-        }
-        loadLevelDat(localStructuresFolder);
-        loadNetherStructures(localStructuresFolder);
-        loadOverworldStructures(localStructuresFolder);
-        loadEndStructures(localStructuresFolder);
-    }
-
-    private void loadOverworldStructures(File localStructuresFolder) {
-        BoundingBoxCache cache = new BoundingBoxCache();
-        if (configManager.drawDesertTemples.getBoolean()) {
-            loadStructureNbtFile(localStructuresFolder, cache, "Temple.dat", StructureType.DesertTemple.getColor(), "TeDP");
-        }
-        if (configManager.drawJungleTemples.getBoolean()) {
-            loadStructureNbtFile(localStructuresFolder, cache, "Temple.dat", StructureType.JungleTemple.getColor(), "TeJP");
-        }
-        if (configManager.drawWitchHuts.getBoolean()) {
-            loadStructureNbtFile(localStructuresFolder, cache, "Temple.dat", StructureType.WitchHut.getColor(), "TeSH");
-        }
-        if (configManager.drawOceanMonuments.getBoolean()) {
-            loadStructureNbtFile(localStructuresFolder, cache, "Monument.dat", StructureType.OceanMonument.getColor(), "*");
-        }
-        if (configManager.drawStrongholds.getBoolean()) {
-            loadStructureNbtFile(localStructuresFolder, cache, "Stronghold.dat", StructureType.Stronghold.getColor(), "*");
-        }
-        if (configManager.drawMansions.getBoolean()) {
-            loadStructureNbtFile(localStructuresFolder, cache, "Mansion.dat", StructureType.Mansion.getColor(), "*");
-        }
-        if (configManager.drawMineShafts.getBoolean()) {
-            loadStructureNbtFile(localStructuresFolder, cache, "Mineshaft.dat", StructureType.MineShaft.getColor(), "*");
-        }
-        if (configManager.drawVillages.getBoolean()) {
-            loadVillageNbtFile(localStructuresFolder, cache, "Villages.dat");
-        }
-
-        boundingBoxCacheMap.put(DimensionType.OVERWORLD, cache);
-    }
-
-    private void loadNetherStructures(File localStructuresFolder) {
-        BoundingBoxCache cache = new BoundingBoxCache();
-        if (configManager.drawNetherFortresses.getBoolean())
-            loadStructureNbtFile(localStructuresFolder, cache, "Fortress.dat", StructureType.NetherFortress.getColor(), "*");
-        if (configManager.drawVillages.getBoolean()) {
-            loadVillageNbtFile(localStructuresFolder, cache, "villages_nether.dat");
-        }
-        boundingBoxCacheMap.put(DimensionType.NETHER, cache);
-    }
-
-    private void loadEndStructures(File localStructuresFolder) {
-        BoundingBoxCache cache = new BoundingBoxCache();
-        if (configManager.drawVillages.getBoolean()) {
-            loadVillageNbtFile(localStructuresFolder, cache, "Villages_end.dat");
-        }
-        if (configManager.drawEndCities.getBoolean()) {
-            loadStructureNbtFile(localStructuresFolder, cache, "EndCity.dat", StructureType.EndCity.getColor(), "*");
-        }
-        boundingBoxCacheMap.put(DimensionType.THE_END, cache);
-    }
-
-    private void loadVillageNbtFile(File localStructuresFolder, BoundingBoxCache cache, String fileName) {
-        File file = new File(localStructuresFolder, fileName);
-        NBTTagCompound nbt = loadNbtFile(file);
-        if (nbt == null)
-            return;
-
-        NBTTagCompound[] villages = getChildCompoundTags(nbt.getCompoundTag("data"), "Villages");
-        for (NBTTagCompound village : villages) {
-            BlockPos center = new BlockPos(village.getInteger("CX"), village.getInteger("CY"), village.getInteger("CZ"));
-            int radius = village.getInteger("Radius");
-            int population = village.getInteger("PopSize");
-            Set<BlockPos> doors = getDoors(village);
-            BoundingBox boundingBox = BoundingBoxVillage.from(center, radius, population, doors);
-            cache.addBoundingBox(boundingBox);
-        }
-
-        Logger.info("Loaded %s (%d villages)", fileName, villages.length);
-    }
-
-    private Set<BlockPos> getDoors(NBTTagCompound village) {
-        Set<BlockPos> doors = new HashSet<>();
-        for (NBTTagCompound door : getChildCompoundTags(village, "Doors")) {
-            doors.add(new BlockPos(door.getInteger("X"), door.getInteger("Y"), door.getInteger("Z")));
-        }
-        return doors;
-    }
-
-    private void loadStructureNbtFile(File localStructuresFolder, BoundingBoxCache cache, String fileName, Color color, String id) {
-        File file = new File(localStructuresFolder, fileName);
-        NBTTagCompound nbt = loadNbtFile(file);
-        if (nbt == null)
-            return;
-
-        NBTTagCompound features = nbt.getCompoundTag("data")
-                .getCompoundTag("Features");
-        int loadedStructureCount = 0;
-        for (Object key : features.getKeySet()) {
-            NBTTagCompound feature = features.getCompoundTag((String) key);
-            BoundingBox structure = BoundingBoxStructure.from(feature.getIntArray("BB"), color);
-            Set<BoundingBox> boundingBoxes = new HashSet<>();
-            NBTTagCompound[] children = getChildCompoundTags(feature, "Children");
-            for (NBTTagCompound child : children) {
-                if (id.equals(child.getString("id")) || id.equals("*"))
-                    boundingBoxes.add(BoundingBoxStructure.from(child.getIntArray("BB"), color));
-            }
-            if (boundingBoxes.size() > 0)
-                ++loadedStructureCount;
-            cache.addBoundingBoxes(structure, boundingBoxes);
-        }
-
-        Logger.info("Loaded %s (%d structures with type %s)", fileName, loadedStructureCount, id);
-    }
-
-    private NBTTagCompound[] getChildCompoundTags(NBTTagCompound parent, String key) {
-        NBTTagList tagList = parent.getTagList(key, 10);
-        NBTTagCompound[] result = new NBTTagCompound[tagList.tagCount()];
-        for (int index = 0; index < tagList.tagCount(); index++) {
-            result[index] = tagList.getCompoundTagAt(index);
-        }
-        return result;
-    }
-
-    private void loadLevelDat(File localStructuresFolder) {
-        File file = new File(localStructuresFolder, "level.dat");
-        NBTTagCompound nbt = loadNbtFile(file);
-        if (nbt == null)
-            return;
-
-        NBTTagCompound data = nbt.getCompoundTag("Data");
-        long seed = data.getLong("RandomSeed");
-        int spawnX = data.getInteger("SpawnX");
-        int spawnZ = data.getInteger("SpawnZ");
-        setWorldData(new WorldData(seed, spawnX, spawnZ));
-        Logger.info("Loaded level.dat (seed: %d, spawn: %d,%d)", worldData.getSeed(), worldData.getSpawnX(), worldData.getSpawnZ());
-    }
-
-    private NBTTagCompound loadNbtFile(File file) {
-        if (!file.exists())
-            return null;
-        try {
-            return CompressedStreamTools.readCompressed(new FileInputStream(file));
-        } catch (IOException e) {
-        }
-        return null;
-    }
-
-    public void playerDisconnectedFromServer() {
-        active = false;
-        if (configManager.keepCacheBetweenSessions.getBoolean()) return;
-        worldData = null;
-        worldSpawnBoundingBox = null;
-        spawnChunksBoundingBox = null;
-        for (BoundingBoxCache cache : boundingBoxCacheMap.values()) {
-            cache.close();
-        }
-        boundingBoxCacheMap.clear();
-    }
-
-    private void renderBoundingBoxes(Map<BoundingBox, Set<BoundingBox>> map, Set<BoundingBox> clientBoundingBoxes) {
-        if (map == null && clientBoundingBoxes == null)
-            return;
-
-        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
-        GL11.glLineWidth(2.0f);
-        GL11.glDisable(GL11.GL_TEXTURE_2D);
-        GL11.glDisable(GL11.GL_CULL_FACE);
-
-        if (configManager.alwaysVisible.getBoolean()) {
-            GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
-        }
-
-        if (map != null)
-            for (BoundingBox bb : map.keySet()) {
-                if (outerBoxOnly) {
-                    renderBoundingBoxSet(map.get(bb));
-                } else {
-                    renderBoundingBoxByType(bb);
-                }
-            }
-
-        if (clientBoundingBoxes != null)
-            renderBoundingBoxSet(clientBoundingBoxes);
-
-        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
-        GL11.glEnable(GL11.GL_CULL_FACE);
-        GL11.glEnable(GL11.GL_TEXTURE_2D);
-    }
-
-    private void renderBoundingBoxSet(Set<BoundingBox> bbList) {
-        if (bbList != null)
-            for (BoundingBox bb : bbList) {
-                renderBoundingBoxByType(bb);
-            }
-    }
-
-    private void renderBoundingBoxByType(BoundingBox bb) {
-        WorldClient world = Minecraft.getMinecraft().world;
-        if (!world.isAreaLoaded(bb.getMinBlockPos(), bb.getMaxBlockPos())) {
-            return;
-        }
-
-        if (bb instanceof BoundingBoxVillage) {
-            BoundingBoxVillage villageBB = (BoundingBoxVillage) bb;
-            if (configManager.renderVillageAsSphere.getBoolean()) {
-                renderBoundingBoxVillageAsSphere(villageBB);
-            } else {
-                renderBoundingBox(villageBB);
-            }
-            if (configManager.drawIronGolemSpawnArea.getBoolean() &&
-                    villageBB.getSpawnsIronGolems()) {
-                renderIronGolemSpawnArea(villageBB);
-            }
-            if (configManager.drawVillageDoors.getBoolean())
-                renderVillageDoors(villageBB);
-        } else if (bb instanceof BoundingBoxSlimeChunk) {
-            renderSlimeChunk((BoundingBoxSlimeChunk) bb);
-        } else if (bb instanceof BoundingBoxWorldSpawn) {
-            renderWorldSpawn((BoundingBoxWorldSpawn) bb);
-        } else {
-            renderBoundingBox(bb);
-        }
-    }
-
-    private void renderBoundingBox(BoundingBox bb) {
-        AxisAlignedBB aaBB = bb.toAxisAlignedBB();
-        Color color = bb.getColor();
-        renderCuboid(aaBB, color, fill());
-    }
-
-    private void renderWorldSpawn(BoundingBoxWorldSpawn bb) {
-        AxisAlignedBB aaBB = bb.toAxisAlignedBB(false);
-        Color color = bb.getColor();
-        double y = getMaxY(configManager.worldSpawnMaxY.getInt()) + 0.001F;
-        renderRectangle(aaBB, y, y, color, false);
-    }
-
-    private void renderSlimeChunk(BoundingBoxSlimeChunk bb) {
-        AxisAlignedBB aaBB = bb.toAxisAlignedBB();
-        Color color = bb.getColor();
-        renderCuboid(aaBB, color, fill());
-
-        double maxY = getMaxY(configManager.slimeChunkMaxY.getInt());
-        if (maxY > 39) {
-            renderRectangle(aaBB, 39, maxY, color, fill());
-        }
-    }
-
-    private double getMaxY(double configMaxY) {
-        if (configMaxY == -1) {
-            return activeY;
-        } else if ((configMaxY == 0) || (playerY < configMaxY)) {
-            return playerY;
-        }
-        return configMaxY;
-    }
-
-    private void renderRectangle(AxisAlignedBB aaBB, double minY, double maxY, Color color, Boolean fill) {
-        aaBB = new AxisAlignedBB(aaBB.minX, minY, aaBB.minZ, aaBB.maxX, maxY, aaBB.maxZ);
-        renderCuboid(aaBB, color, fill);
-    }
-
-    private boolean fill() {
-        return configManager.fill.getBoolean();
-    }
-
-    private void renderIronGolemSpawnArea(BoundingBoxVillage villageBB) {
-        BlockPos center = villageBB.getCenter();
-        AxisAlignedBB abb = new AxisAlignedBB(new BlockPos(center.getX() - 8,
-                center.getY() - 3,
-                center.getZ() - 8),
-                new BlockPos(center.getX() + 8,
-                        center.getY() + 3,
-                        center.getZ() + 8))
-                .offset(villageBB.getCenterOffsetX(), 0.0, villageBB.getCenterOffsetZ());
-
-        renderCuboid(abb, villageBB.getColor(), false);
-    }
-
-    private void renderVillageDoors(BoundingBoxVillage villageBB) {
-        OffsetPoint center = new OffsetPoint(villageBB.getCenter())
-                .add(villageBB.getCenterOffsetX(), 0.0, villageBB.getCenterOffsetZ());
-        Color color = villageBB.getColor();
-        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
-        Tessellator tessellator = Tessellator.getInstance();
-        BufferBuilder worldRenderer = tessellator.getBuffer();
-
-        int colorR = color.getRed();
-        int colorG = color.getGreen();
-        int colorB = color.getBlue();
-
-        worldRenderer.begin(GL11.GL_LINES, worldRenderer.getVertexFormat());
-        for (BlockPos door : villageBB.getDoors()) {
-            OffsetPoint point = new OffsetPoint(door).add(0.5, 0, 0.5);
-
-            worldRenderer.pos(point.getX(), point.getY(), point.getZ()).color(colorR, colorG, colorB, 255).endVertex();
-            worldRenderer.pos(center.getX(), center.getY(), center.getZ()).color(colorR, colorG, colorB, 255).endVertex();
-        }
-        tessellator.draw();
-    }
-
-    private void renderCuboid(AxisAlignedBB aaBB, Color color, boolean fill) {
-        aaBB = offsetAxisAlignedBB(aaBB);
-        if (fill) {
-            renderFilledCuboid(aaBB, color);
-        }
-        renderUnfilledCuboid(aaBB, color);
-    }
-
-    private void renderFilledCuboid(AxisAlignedBB aaBB, Color color) {
-        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
-        GL11.glEnable(GL11.GL_BLEND);
-        renderCuboid(aaBB, 30, color);
-        GL11.glDisable(GL11.GL_BLEND);
-        GL11.glEnable(GL11.GL_POLYGON_OFFSET_LINE);
-        GL11.glPolygonOffset(-1.f, -1.f);
-    }
-
-    private void renderUnfilledCuboid(AxisAlignedBB aaBB, Color color) {
-        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
-        renderCuboid(aaBB, 255, color);
-    }
-
-    private void renderCuboid(AxisAlignedBB bb, int alphaChannel, Color color) {
-        Tessellator tessellator = Tessellator.getInstance();
-        BufferBuilder worldRenderer = tessellator.getBuffer();
-
-        int colorR = color.getRed();
-        int colorG = color.getGreen();
-        int colorB = color.getBlue();
-
-        worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
-        worldRenderer.pos(bb.minX, bb.minY, bb.minZ)
-                .color(colorR, colorG, colorB, alphaChannel)
-                .endVertex();
-        worldRenderer.pos(bb.maxX, bb.minY, bb.minZ)
-                .color(colorR, colorG, colorB, alphaChannel)
-                .endVertex();
-        worldRenderer.pos(bb.maxX, bb.minY, bb.maxZ)
-                .color(colorR, colorG, colorB, alphaChannel)
-                .endVertex();
-        worldRenderer.pos(bb.minX, bb.minY, bb.maxZ)
-                .color(colorR, colorG, colorB, alphaChannel)
-                .endVertex();
-
-        if (bb.minY != bb.maxY) {
-            worldRenderer.pos(bb.minX, bb.maxY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.maxY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.maxY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.minX, bb.maxY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-
-            worldRenderer.pos(bb.minX, bb.minY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.minX, bb.maxY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.maxY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.minY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-
-            worldRenderer.pos(bb.minX, bb.minY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.minX, bb.maxY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.maxY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.minY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-
-            worldRenderer.pos(bb.minX, bb.minY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.minX, bb.minY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.minX, bb.maxY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.minX, bb.maxY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-
-            worldRenderer.pos(bb.maxX, bb.minY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.minY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.maxY, bb.maxZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-            worldRenderer.pos(bb.maxX, bb.maxY, bb.minZ)
-                    .color(colorR, colorG, colorB, alphaChannel)
-                    .endVertex();
-        }
-        tessellator.draw();
-    }
-
-    private AxisAlignedBB offsetAxisAlignedBB(AxisAlignedBB axisAlignedBB) {
-        double growXZ = 0.001F;
-        double growY = 0;
-        if (axisAlignedBB.minY != axisAlignedBB.maxY) {
-            growY = growXZ;
-        }
-        return axisAlignedBB
-                .grow(growXZ, growY, growXZ)
-                .offset(-playerX, -playerY, -playerZ);
-    }
-
-    private void renderBoundingBoxVillageAsSphere(BoundingBoxVillage bb) {
-        OffsetPoint center = new OffsetPoint(bb.getCenter())
-                .add(bb.getCenterOffsetX(), 0.0, bb.getCenterOffsetZ());
-        ;
-        int radius = bb.getRadius();
-        Color color = bb.getColor();
-        renderSphere(center, radius, color);
-    }
-
-    private void renderSphere(OffsetPoint center, double radius, Color color) {
-        GL11.glEnable(GL11.GL_POINT_SMOOTH);
-        GL11.glPointSize(2f);
-
-        Tessellator tessellator = Tessellator.getInstance();
-        BufferBuilder worldRenderer = tessellator.getBuffer();
-        worldRenderer.begin(GL11.GL_POINTS, DefaultVertexFormats.POSITION_COLOR);
-        for (OffsetPoint point : buildPoints(center, radius)) {
-            worldRenderer.pos(point.getX(), point.getY(), point.getZ())
-                    .color(color.getRed(), color.getGreen(), color.getBlue(), 255)
-                    .endVertex();
-        }
-        tessellator.draw();
-    }
-
-    private class OffsetPoint {
-        private final double x;
-        private final double y;
-        private final double z;
-
-        public OffsetPoint(double x, double y, double z) {
-            this.x = x;
-            this.y = y;
-            this.z = z;
-        }
-
-        public OffsetPoint(BlockPos blockPos) {
-            x = blockPos.getX();
-            y = blockPos.getY();
-            z = blockPos.getZ();
-        }
-
-        public double getX() {
-            return x - playerX;
-        }
-
-        public double getY() {
-            return y - playerY;
-        }
-
-        public double getZ() {
-            return z - playerZ;
-        }
-
-        public OffsetPoint add(double x, double y, double z) {
-            return new OffsetPoint(this.x + x, this.y + y, this.z + z);
-        }
-    }
-
-    private Set<OffsetPoint> buildPoints(OffsetPoint center, double radius) {
-        Set<OffsetPoint> points = new HashSet<>(1200);
-
-        double tau = 6.283185307179586D;
-        double pi = tau / 2D;
-        double segment = tau / 48D;
-
-        for (double t = 0.0D; t < tau; t += segment)
-            for (double theta = 0.0D; theta < pi; theta += segment) {
-                double dx = radius * Math.sin(t) * Math.cos(theta);
-                double dz = radius * Math.sin(t) * Math.sin(theta);
-                double dy = radius * Math.cos(t);
-
-                points.add(center.add(dx, dy, dz));
-            }
-        return points;
-    }
-
-    private Set<BoundingBox> getClientBoundingBoxes(DimensionType dimensionType) {
-        if (worldData == null || dimensionType != DimensionType.OVERWORLD) {
-            return null;
-        }
-
-        Set<BoundingBox> boundingBoxes = new HashSet<>();
-        if (configManager.drawWorldSpawn.getBoolean()) {
-            boundingBoxes.add(getWorldSpawnBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
-            boundingBoxes.add(getSpawnChunksBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
-        }
-        if (configManager.drawLazySpawnChunks.getBoolean()) {
-            boundingBoxes.add(getLazySpawnChunksBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
-        }
-        if (configManager.drawSlimeChunks.getBoolean()) {
-            boundingBoxes.addAll(this.getSlimeChunks());
-        }
-        return boundingBoxes;
-    }
-
-    private Set<BoundingBoxSlimeChunk> getSlimeChunks() {
-        Minecraft minecraft = Minecraft.getMinecraft();
-        int renderDistanceChunks = minecraft.gameSettings.renderDistanceChunks;
-        int playerChunkX = MathHelper.floor(minecraft.player.posX / 16.0D);
-        int playerChunkZ = MathHelper.floor(minecraft.player.posZ / 16.0D);
-        Set<BoundingBoxSlimeChunk> slimeChunks = new HashSet<>();
-        for (int chunkX = playerChunkX - renderDistanceChunks; chunkX <= playerChunkX + renderDistanceChunks; ++chunkX) {
-            for (int chunkZ = playerChunkZ - renderDistanceChunks; chunkZ <= playerChunkZ + renderDistanceChunks; ++chunkZ) {
-                if (isSlimeChunk(chunkX, chunkZ)) {
-                    ChunkPos chunk = new ChunkPos(chunkX, chunkZ);
-                    BlockPos minBlockPos = new BlockPos(chunk.getXStart(), 1, chunk.getZStart());
-                    BlockPos maxBlockPos = new BlockPos(chunk.getXEnd(), 38, chunk.getZEnd());
-                    if (minecraft.world.isAreaLoaded(minBlockPos, maxBlockPos)) {
-                        slimeChunks.add(BoundingBoxSlimeChunk.from(minBlockPos, maxBlockPos, Color.GREEN));
-                    }
-                }
-            }
-        }
-        return slimeChunks;
-    }
-
-    private boolean isSlimeChunk(int chunkX, int chunkZ) {
-        Random r = new Random(worldData.getSeed() +
-                (long) (chunkX * chunkX * 4987142) +
-                (long) (chunkX * 5947611) +
-                (long) (chunkZ * chunkZ) * 4392871L +
-                (long) (chunkZ * 389711) ^ 987234911L);
-        return r.nextInt(10) == 0;
-    }
-
-    private BoundingBox getSpawnChunksBoundingBox(int spawnX, int spawnZ) {
-        if (spawnChunksBoundingBox != null) {
-            return spawnChunksBoundingBox;
-        }
-        BoundingBox boundingBox = getSpawnChunksBoundingBox(spawnX, spawnZ, 12, Color.RED);
-        spawnChunksBoundingBox = boundingBox;
-        return boundingBox;
-    }
-
-    private BoundingBox getLazySpawnChunksBoundingBox(int spawnX, int spawnZ) {
-        if (lazySpawnChunksBoundingBox != null)
-            return lazySpawnChunksBoundingBox;
-
-        BoundingBox boundingBox = getSpawnChunksBoundingBox(spawnX, spawnZ, 16, Color.RED);
-        lazySpawnChunksBoundingBox = boundingBox;
-        return boundingBox;
-    }
-
-    private BoundingBox getSpawnChunksBoundingBox(int spawnX, int spawnZ, int size, Color color) {
-        double chunkSize = 16;
-        double midOffset = chunkSize * (size / 2);
-        double midX = Math.round((float) (spawnX / chunkSize)) * chunkSize;
-        double midZ = Math.round((float) (spawnZ / chunkSize)) * chunkSize;
-        BlockPos minBlockPos = new BlockPos(midX - midOffset, 0, midZ - midOffset);
-        if (spawnX / chunkSize % 0.5D == 0.0D && spawnZ / chunkSize % 0.5D == 0.0D) {
-            midX += chunkSize;
-            midZ += chunkSize;
-        }
-        BlockPos maxBlockPos = new BlockPos(midX + midOffset, 0, midZ + midOffset);
-        return BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, color);
-    }
-
-    private BoundingBox getWorldSpawnBoundingBox(int spawnX, int spawnZ) {
-        if (worldSpawnBoundingBox != null)
-            return worldSpawnBoundingBox;
-
-        BlockPos minBlockPos = new BlockPos(spawnX - 10, 0, spawnZ - 10);
-        BlockPos maxBlockPos = new BlockPos(spawnX + 10, 0, spawnZ + 10);
-
-        BoundingBox boundingBox = BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, Color.RED);
-        worldSpawnBoundingBox = boundingBox;
-        return boundingBox;
-    }
-}
diff --git a/java/com/irtimaled/bbor/CommonProxy.java b/java/com/irtimaled/bbor/CommonProxy.java
deleted file mode 100644 (file)
index 0799ca8..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.world.DimensionType;
-import net.minecraft.world.World;
-import net.minecraft.world.chunk.Chunk;
-import net.minecraft.world.chunk.IChunkProvider;
-import net.minecraft.world.gen.ChunkProviderServer;
-import net.minecraft.world.gen.IChunkGenerator;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class CommonProxy {
-    public Map<DimensionType, BoundingBoxCache> boundingBoxCacheMap = new ConcurrentHashMap<>();
-
-    public ConfigManager configManager;
-    protected WorldData worldData;
-    private IEventHandler eventHandler = null;
-
-    public void init(ConfigManager configManager) {
-        this.configManager = configManager;
-    }
-
-    public void worldLoaded(World world) {
-        IChunkProvider chunkProvider = world.getChunkProvider();
-        if (chunkProvider instanceof ChunkProviderServer) {
-            IChunkGenerator chunkGenerator = ReflectionHelper.getPrivateValue(ChunkProviderServer.class, (ChunkProviderServer) chunkProvider, IChunkGenerator.class);
-            setWorldData(new WorldData(world.getSeed(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnZ()));
-            DimensionType dimensionType = world.provider.getDimensionType();
-            Logger.info("create world dimension: %s, %s (chunkprovider: %s) (seed: %d)", dimensionType, world.getClass().toString(), chunkGenerator.getClass().toString(), worldData.getSeed());
-            boundingBoxCacheMap.put(dimensionType, new DimensionProcessor(eventHandler, configManager, world, dimensionType, chunkGenerator));
-        }
-    }
-
-    public void chunkLoaded(Chunk chunk) {
-        DimensionType dimensionType = chunk.getWorld().provider.getDimensionType();
-        if (boundingBoxCacheMap.containsKey(dimensionType)) {
-            boundingBoxCacheMap.get(dimensionType).refresh();
-        }
-    }
-
-    public WorldData getWorldData() {
-        return worldData;
-    }
-
-    public void setWorldData(WorldData worldData) {
-        this.worldData = worldData;
-    }
-
-    public void setEventHandler(IEventHandler eventHandler) {
-        this.eventHandler = eventHandler;
-    }
-}
diff --git a/java/com/irtimaled/bbor/ConfigManager.java b/java/com/irtimaled/bbor/ConfigManager.java
deleted file mode 100644 (file)
index d31d395..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.irtimaled.bbor;
-
-import com.irtimaled.bbor.config.Configuration;
-import com.irtimaled.bbor.config.Setting;
-
-import java.io.File;
-
-public class ConfigManager {
-    public final File configDir;
-
-    public Setting fill;
-    public Setting drawVillages;
-    public Setting drawDesertTemples;
-    public Setting drawJungleTemples;
-    public Setting drawWitchHuts;
-    public Setting drawStrongholds;
-    public Setting drawMineShafts;
-    public Setting drawNetherFortresses;
-    public Setting drawOceanMonuments;
-    public Setting alwaysVisible;
-    public Setting renderVillageAsSphere;
-    public Setting drawIronGolemSpawnArea;
-    public Setting drawVillageDoors;
-    public Setting drawSlimeChunks;
-    public Setting slimeChunkMaxY;
-    public Setting keepCacheBetweenSessions;
-    public Setting drawWorldSpawn;
-    public Setting worldSpawnMaxY;
-    public Setting drawLazySpawnChunks;
-    public Setting drawEndCities;
-    public Setting drawMansions;
-
-    private Configuration config;
-
-    public ConfigManager(File configDir) {
-        this.configDir = configDir;
-        config = new Configuration(new File(configDir, "BBOutlineReloaded.cfg"));
-        config.load();
-
-        fill = SetupBooleanProperty(config, "general", "fill", true, "If set to true the bounding boxes are filled. (default: true)");
-        alwaysVisible = SetupBooleanProperty(config, "general", "alwaysVisible", false, "If set to true boxes will be visible even through other blocks. (default: false)");
-        keepCacheBetweenSessions = SetupBooleanProperty(config, "general", "keepCacheBetweenSessions", false, "If set to true bounding box caches will be kept between sessions. (default: false)");
-        drawVillages = SetupBooleanProperty(config, "features", "drawVillages", true, "If set to true village bounding boxes are drawn. (default: true)");
-        renderVillageAsSphere = SetupBooleanProperty(config, "features", "renderVillageAsSphere", true, "If set to true villages will be drawn as a sphere. (default:true)");
-        drawIronGolemSpawnArea = SetupBooleanProperty(config, "features", "drawIronGolemSpawnArea", true, "If set to true the iron golem spawn area of the village will be drawn. (default:true)");
-        drawVillageDoors = SetupBooleanProperty(config, "features", "drawVillageDoors", false, "If set to true lines between the village centre and doors will be drawn. (default:false)");
-        drawDesertTemples = SetupBooleanProperty(config, "features", "drawDesertTemples", true, "If set to true desert temple bounding boxes are drawn. (default: true)");
-        drawJungleTemples = SetupBooleanProperty(config, "features", "drawJungleTemples", true, "If set to true jungle temple bounding boxes are drawn. (default: true)");
-        drawWitchHuts = SetupBooleanProperty(config, "features", "drawWitchHuts", true, "If set to true witch hut bounding boxes are drawn. (default: true)");
-        drawStrongholds = SetupBooleanProperty(config, "features", "drawStrongholds", false, "If set to true stronghold bounding boxes are drawn. (default: false)");
-        drawMineShafts = SetupBooleanProperty(config, "features", "drawMineShafts", false, "If set to true mineshaft bounding boxes are drawn. (default: false)");
-        drawNetherFortresses = SetupBooleanProperty(config, "features", "drawNetherFortresses", true, "If set to true nether fortress bounding boxes are drawn. (default: true)");
-        drawOceanMonuments = SetupBooleanProperty(config, "features", "drawOceanMonuments", true, "If set to true ocean monument bounding boxes are drawn. (default: true)");
-        drawSlimeChunks = SetupBooleanProperty(config, "features", "drawSlimeChunks", true, "If set to true slime chunks bounding boxes are drawn. (default: true)");
-        slimeChunkMaxY = SetupIntegerProperty(config, "features", "slimeChunkMaxY", -1, "The maximum top of the slime chunk bounding box. If set to -1 it will use the value when activated, if set to 0 it will always track the player's feet. (default: -1)");
-        drawWorldSpawn = SetupBooleanProperty(config, "features", "drawWorldSpawn", true, "If set to true world spawn and spawn chunks bounding boxes are drawn. (default: true)");
-        worldSpawnMaxY = SetupIntegerProperty(config, "features", "worldSpawnMaxY", -1, "The maximum top of the world spawn bounding boxes. If set to -1 it will use the value when activated, if set to 0 it will always track the players feet. (default: -1)");
-        drawLazySpawnChunks = SetupBooleanProperty(config, "features", "drawLazySpawnChunks", false, "If set to true the lazy spawn chunks bounding boxes will be drawn. (default: false)");
-        drawEndCities = SetupBooleanProperty(config, "features", "drawEndCities", true, "If set to true end city bounding boxes will be drawn. (default: true)");
-        drawMansions = SetupBooleanProperty(config, "features", "drawMansions", true, "If set to true woodland mansions will be drawn. (default: true)");
-        config.save();
-    }
-
-    private Setting SetupBooleanProperty(Configuration config, String category, String settingName, Boolean defaultValue, String comment) {
-        Setting property = config.get(category, settingName, defaultValue);
-        property.comment = comment;
-        property.set(property.getBoolean(defaultValue));
-        return property;
-    }
-
-    private Setting SetupIntegerProperty(Configuration config, String category, String settingName, int defaultValue, String comment) {
-        Setting property = config.get(category, settingName, defaultValue);
-        property.comment = comment;
-        property.set(property.getInt(defaultValue));
-        return property;
-    }
-}
diff --git a/java/com/irtimaled/bbor/DimensionProcessor.java b/java/com/irtimaled/bbor/DimensionProcessor.java
deleted file mode 100644 (file)
index 27d3f15..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.ChunkPos;
-import net.minecraft.village.Village;
-import net.minecraft.village.VillageDoorInfo;
-import net.minecraft.world.DimensionType;
-import net.minecraft.world.World;
-import net.minecraft.world.gen.ChunkGeneratorEnd;
-import net.minecraft.world.gen.ChunkGeneratorHell;
-import net.minecraft.world.gen.ChunkGeneratorOverworld;
-import net.minecraft.world.gen.IChunkGenerator;
-import net.minecraft.world.gen.structure.*;
-
-import java.awt.*;
-import java.util.*;
-import java.util.List;
-
-public class DimensionProcessor extends BoundingBoxCache {
-    private ConfigManager configManager;
-    private World world;
-    private IEventHandler eventHandler;
-
-    public DimensionProcessor(IEventHandler eventHandler, ConfigManager configManager, World world, DimensionType dimensionType, IChunkGenerator chunkGenerator) {
-        this.eventHandler = eventHandler;
-        this.configManager = configManager;
-        this.world = world;
-        this.dimensionType = dimensionType;
-        this.chunkGenerator = chunkGenerator;
-        villageCache = new HashMap<>();
-        slimeChunkCache = new HashSet<>();
-        worldSpawnCache = new HashSet<>();
-    }
-
-    private DimensionType dimensionType;
-    private IChunkGenerator chunkGenerator;
-    private Map<Integer, BoundingBoxVillage> villageCache;
-    private Set<BoundingBox> slimeChunkCache;
-    private Set<BoundingBox> worldSpawnCache;
-
-    private boolean closed = false;
-
-    @Override
-    public void close() {
-        closed = true;
-        chunkGenerator = null;
-        villageCache.clear();
-        slimeChunkCache.clear();
-        worldSpawnCache.clear();
-        super.close();
-    }
-
-    private static <T extends IChunkGenerator, R extends MapGenStructure> Collection<StructureStart> getStructures(T chunkGenerator, Class<R> generatorClass) {
-        Class<T> chunkGeneratorClass = (Class<T>) chunkGenerator.getClass();
-        R structureGenerator = ReflectionHelper.getPrivateValue(chunkGeneratorClass, chunkGenerator, generatorClass);
-        if (structureGenerator != null) {
-            Map<ChunkPos, StructureStart> structureMap = ReflectionHelper.getPrivateValue(MapGenStructure.class, structureGenerator, Map.class);
-            return structureMap.values();
-        }
-        return Collections.emptyList();
-    }
-
-    private Map<StructureType, Collection<StructureStart>> getStructures() {
-        Map<StructureType, Collection<StructureStart>> structureMap = new HashMap<>();
-        if (chunkGenerator instanceof ChunkGeneratorOverworld) {
-            if (configManager.drawDesertTemples.getBoolean()) {
-                structureMap.put(StructureType.DesertTemple, getStructuresWithComponent(getStructures(chunkGenerator, MapGenScatteredFeature.class), ComponentScatteredFeaturePieces.DesertPyramid.class));
-            }
-
-            if (configManager.drawJungleTemples.getBoolean()) {
-                structureMap.put(StructureType.JungleTemple, getStructuresWithComponent(getStructures(chunkGenerator, MapGenScatteredFeature.class), ComponentScatteredFeaturePieces.JunglePyramid.class));
-            }
-
-            if (configManager.drawWitchHuts.getBoolean()) {
-                structureMap.put(StructureType.WitchHut, getStructuresWithComponent(getStructures(chunkGenerator, MapGenScatteredFeature.class), ComponentScatteredFeaturePieces.SwampHut.class));
-            }
-
-            if (configManager.drawOceanMonuments.getBoolean()) {
-                structureMap.put(StructureType.OceanMonument, getStructures(chunkGenerator, StructureOceanMonument.class));
-            }
-
-            if (configManager.drawStrongholds.getBoolean()) {
-                structureMap.put(StructureType.Stronghold, getStructures(chunkGenerator, MapGenStronghold.class));
-            }
-
-            if (configManager.drawMansions.getBoolean()) {
-                structureMap.put(StructureType.Mansion, getStructures(chunkGenerator, WoodlandMansion.class));
-            }
-
-            if (configManager.drawMineShafts.getBoolean()) {
-                structureMap.put(StructureType.MineShaft, getStructures(chunkGenerator, MapGenMineshaft.class));
-            }
-        } else if (chunkGenerator instanceof ChunkGeneratorHell) {
-            if (configManager.drawNetherFortresses.getBoolean()) {
-                structureMap.put(StructureType.NetherFortress, getStructures(chunkGenerator, MapGenNetherBridge.class));
-            }
-        } else if (chunkGenerator instanceof ChunkGeneratorEnd) {
-            if (configManager.drawEndCities.getBoolean()) {
-                structureMap.put(StructureType.EndCity, getStructures(chunkGenerator, MapGenEndCity.class));
-            }
-        }
-
-        return structureMap;
-    }
-
-    private Collection<StructureStart> getStructuresWithComponent(Collection<StructureStart> structures, Class structureComponent) {
-        Collection<StructureStart> validStructures = new HashSet<>();
-        for (StructureStart structure : structures) {
-            if (structure.getComponents().get(0).getClass().equals(structureComponent)) {
-                validStructures.add(structure);
-            }
-        }
-        return validStructures;
-    }
-
-    @Override
-    public synchronized void refresh() {
-        if (closed) return;
-
-        Map<StructureType, Collection<StructureStart>> structureMap = getStructures();
-        for (StructureType structureType : structureMap.keySet()) {
-            Color color = structureType.getColor();
-            for (StructureStart structureStart : structureMap.get(structureType)) {
-                if (structureStart.getBoundingBox() != null) {
-                    BoundingBox boundingBox = BoundingBoxStructure.from(structureStart.getBoundingBox(), color);
-                    if (!isCached(boundingBox)) {
-                        Set<BoundingBox> structureBoundingBoxes = new HashSet<>();
-                        for (StructureComponent structureComponent : structureStart.getComponents()) {
-                            structureBoundingBoxes.add(BoundingBoxStructure.from(structureComponent.getBoundingBox(), color));
-                        }
-                        addBoundingBoxes(boundingBox, structureBoundingBoxes);
-                        Logger.info("[%s] new boundingBoxCacheMap entries: %d", dimensionType, structureBoundingBoxes.size());
-                    }
-                }
-            }
-        }
-
-        if (configManager.drawVillages.getBoolean() &&
-                world.getVillageCollection() != null) {
-            Map<Integer, BoundingBoxVillage> villageBoundingBoxes = new HashMap<>();
-            List<Village> villages = world.getVillageCollection().getVillageList();
-            for (Village village : villages) {
-                int villageId = village.hashCode();
-                BlockPos center = village.getCenter();
-                Color color = null;
-                if (villageCache.containsKey(villageId)) {
-                    BoundingBoxVillage boundingBoxVillage = villageCache.get(villageId);
-                    if (boundingBoxVillage.getCenter() == center) {
-                        villageBoundingBoxes.put(villageId, boundingBoxVillage);
-                        villageCache.remove(villageId);
-                        continue;
-                    }
-                    color = boundingBoxVillage.getColor();
-                }
-
-                Integer radius = village.getVillageRadius();
-                int population = village.getNumVillagers();
-                Set<BlockPos> doors = getDoorsFromVillage(village);
-                villageBoundingBoxes.put(villageId, BoundingBoxVillage.from(center, radius, color, population, doors));
-            }
-            processDelta(villageCache, villageBoundingBoxes);
-            villageCache = villageBoundingBoxes;
-        }
-    }
-
-    private Set<BlockPos> getDoorsFromVillage(Village village) {
-        Set<BlockPos> doors = new HashSet<>();
-        for (Object doorInfo : village.getVillageDoorInfoList()) {
-            VillageDoorInfo villageDoorInfo = (VillageDoorInfo) doorInfo;
-            doors.add(villageDoorInfo.getDoorBlockPos());
-        }
-        return doors;
-    }
-
-    private void processDelta(Map<Integer, BoundingBoxVillage> oldVillages, Map<Integer, BoundingBoxVillage> newVillages) {
-        for (BoundingBox village : oldVillages.values()) {
-            removeBoundingBox(village);
-            if (eventHandler != null) {
-                eventHandler.boundingBoxRemoved(this.dimensionType, village);
-            }
-        }
-        for (BoundingBox village : newVillages.values()) {
-            if (!isCached(village))
-                addBoundingBox(village);
-        }
-    }
-}
diff --git a/java/com/irtimaled/bbor/IEventHandler.java b/java/com/irtimaled/bbor/IEventHandler.java
deleted file mode 100644 (file)
index c6fff48..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.irtimaled.bbor;
-
-import net.minecraft.world.DimensionType;
-
-public interface IEventHandler {
-    void boundingBoxRemoved(DimensionType dimensionType, BoundingBox bb);
-}
diff --git a/java/com/irtimaled/bbor/StructureType.java b/java/com/irtimaled/bbor/StructureType.java
deleted file mode 100644 (file)
index ae4420b..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.irtimaled.bbor;
-
-import java.awt.*;
-
-public class StructureType {
-    private static final int JUNGLE_TEMPLE = 1;
-    private static final int DESERT_TEMPLE = 2;
-    private static final int WITCH_HUT = 3;
-    private static final int OCEAN_MONUMENT = 4;
-    private static final int STRONGHOLD = 5;
-    private static final int MINE_SHAFT = 6;
-    private static final int NETHER_FORTRESS = 7;
-    private static final int END_CITY = 8;
-    private static final int MANSION = 9;
-
-    public final static StructureType JungleTemple = new StructureType(JUNGLE_TEMPLE);
-    public final static StructureType DesertTemple = new StructureType(DESERT_TEMPLE);
-    public final static StructureType WitchHut = new StructureType(WITCH_HUT);
-    public final static StructureType OceanMonument = new StructureType(OCEAN_MONUMENT);
-    public final static StructureType Stronghold = new StructureType(STRONGHOLD);
-    public final static StructureType MineShaft = new StructureType(MINE_SHAFT);
-    public final static StructureType NetherFortress = new StructureType(NETHER_FORTRESS);
-    public final static StructureType EndCity = new StructureType(END_CITY);
-    public final static StructureType Mansion = new StructureType(MANSION);
-
-    private final int type;
-
-    private StructureType(int type) {
-        this.type = type;
-    }
-
-    public Color getColor() {
-        switch (type) {
-            case DESERT_TEMPLE:
-                return Color.ORANGE;
-            case JUNGLE_TEMPLE:
-                return Color.GREEN;
-            case WITCH_HUT:
-                return Color.BLUE;
-            case MINE_SHAFT:
-                return Color.LIGHT_GRAY;
-            case NETHER_FORTRESS:
-                return Color.RED;
-            case OCEAN_MONUMENT:
-                return Color.CYAN;
-            case STRONGHOLD:
-                return Color.YELLOW;
-            case END_CITY:
-                return Color.MAGENTA;
-            case MANSION:
-                return new Color(139, 69, 19);
-        }
-        return Color.WHITE;
-    }
-}
diff --git a/java/com/irtimaled/bbor/WorldData.java b/java/com/irtimaled/bbor/WorldData.java
deleted file mode 100644 (file)
index d38d363..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.irtimaled.bbor;
-
-public class WorldData {
-    private long seed;
-    private int spawnX;
-    private int spawnZ;
-
-    public WorldData(long seed, int spawnX, int spawnZ) {
-        this.seed = seed;
-        this.spawnX = spawnX;
-        this.spawnZ = spawnZ;
-    }
-
-    public long getSeed() {
-        return seed;
-    }
-
-    public int getSpawnX() {
-        return spawnX;
-    }
-
-    public int getSpawnZ() {
-        return spawnZ;
-    }
-}
diff --git a/java/com/irtimaled/bbor/client/BoundingBoxOutlineReloaded.java b/java/com/irtimaled/bbor/client/BoundingBoxOutlineReloaded.java
new file mode 100644 (file)
index 0000000..6361e30
--- /dev/null
@@ -0,0 +1,42 @@
+package com.irtimaled.bbor.client;
+
+import com.irtimaled.bbor.config.ConfigManager;
+import net.minecraft.client.Minecraft;
+import net.minecraft.network.NetworkManager;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+
+import java.io.File;
+
+public class BoundingBoxOutlineReloaded {
+    public static ClientProxy proxy;
+
+    public static void init() {
+        proxy = new ClientProxy();
+        proxy.init(new ConfigManager(new File(Minecraft.getMinecraft().mcDataDir, "config")));
+    }
+
+    public static void chunkLoaded(Chunk chunk) {
+        proxy.chunkLoaded(chunk);
+    }
+
+    public static void worldLoaded(World world) {
+        proxy.worldLoaded(world);
+    }
+
+    public static void keyPressed() {
+        proxy.keyPressed();
+    }
+
+    public static void render(float partialTicks) {
+        proxy.render(partialTicks);
+    }
+
+    public static void playerConnectedToServer(NetworkManager networkManager) {
+        proxy.playerConnectedToServer(networkManager);
+    }
+
+    public static void playerDisconnectedFromServer() {
+        proxy.playerDisconnectedFromServer();
+    }
+}
diff --git a/java/com/irtimaled/bbor/client/ClientProxy.java b/java/com/irtimaled/bbor/client/ClientProxy.java
new file mode 100644 (file)
index 0000000..1c23c8c
--- /dev/null
@@ -0,0 +1,712 @@
+package com.irtimaled.bbor.client;
+
+import com.irtimaled.bbor.Logger;
+import com.irtimaled.bbor.common.BoundingBoxCache;
+import com.irtimaled.bbor.common.CommonProxy;
+import com.irtimaled.bbor.common.StructureType;
+import com.irtimaled.bbor.common.models.*;
+import com.irtimaled.bbor.config.ConfigManager;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.WorldClient;
+import net.minecraft.client.renderer.BufferBuilder;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.nbt.CompressedStreamTools;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.network.NetworkManager;
+import net.minecraft.util.math.AxisAlignedBB;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.world.DimensionType;
+import org.apache.commons.lang3.ArrayUtils;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+public class ClientProxy extends CommonProxy {
+    private double activeY;
+    private boolean active;
+    private boolean outerBoxOnly;
+    private KeyBinding activeHotKey;
+    private KeyBinding outerBoxOnlyHotKey;
+    private double playerX;
+    private double playerY;
+    private double playerZ;
+    private BoundingBox worldSpawnBoundingBox;
+    private BoundingBox spawnChunksBoundingBox;
+    private BoundingBox lazySpawnChunksBoundingBox;
+
+    public void keyPressed() {
+        if (activeHotKey.isPressed()) {
+            active = !active;
+            if (active)
+                activeY = playerY;
+        } else if (outerBoxOnlyHotKey.isPressed()) {
+            outerBoxOnly = !outerBoxOnly;
+        }
+    }
+
+    @Override
+    public void init(ConfigManager configManager) {
+        super.init(configManager);
+        String category = "Bounding Box Outline Reloaded";
+        activeHotKey = new KeyBinding("Toggle On/Off", Keyboard.KEY_B, category);
+        outerBoxOnlyHotKey = new KeyBinding("Toggle Display Outer Box Only", Keyboard.KEY_O, category);
+        Minecraft.getMinecraft().gameSettings.keyBindings = ArrayUtils.addAll(Minecraft.getMinecraft().gameSettings.keyBindings, activeHotKey, outerBoxOnlyHotKey);
+    }
+
+    @Override
+    public void setWorldData(WorldData worldData) {
+        worldSpawnBoundingBox = null;
+        spawnChunksBoundingBox = null;
+        lazySpawnChunksBoundingBox = null;
+        super.setWorldData(worldData);
+    }
+
+    public void render(float partialTicks) {
+        EntityPlayer entityPlayer = Minecraft.getMinecraft().player;
+        playerX = entityPlayer.lastTickPosX + (entityPlayer.posX - entityPlayer.lastTickPosX) * (double) partialTicks;
+        playerY = entityPlayer.lastTickPosY + (entityPlayer.posY - entityPlayer.lastTickPosY) * (double) partialTicks;
+        playerZ = entityPlayer.lastTickPosZ + (entityPlayer.posZ - entityPlayer.lastTickPosZ) * (double) partialTicks;
+
+        if (this.active) {
+            DimensionType dimensionType = DimensionType.getById(entityPlayer.dimension);
+            Map<BoundingBox, Set<BoundingBox>> boundingBoxes = null;
+            if (boundingBoxCacheMap.containsKey(dimensionType)) {
+                boundingBoxes = boundingBoxCacheMap.get(dimensionType).getBoundingBoxes();
+            }
+            renderBoundingBoxes(boundingBoxes, getClientBoundingBoxes(dimensionType));
+        }
+    }
+
+    public void playerConnectedToServer(NetworkManager networkManager) {
+        SocketAddress remoteAddress = networkManager.getRemoteAddress();
+        if (remoteAddress instanceof InetSocketAddress) {
+            InetSocketAddress socketAddress = (InetSocketAddress) remoteAddress;
+            loadLocalStructures(socketAddress.getHostName(), socketAddress.getPort());
+        }
+    }
+
+    private void loadLocalStructures(String host, int port) {
+        Logger.info("Looking for local structures (host:port=%s:%d)", host, port);
+        String path = String.format("BBOutlineReloaded%s%s%s%d", File.separator, host, File.separator, port);
+        File localStructuresFolder = new File(configManager.configDir, path);
+        Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
+        if (!localStructuresFolder.exists()) {
+            path = String.format("BBOutlineReloaded%s%s", File.separator, host);
+            localStructuresFolder = new File(configManager.configDir, path);
+            Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
+        }
+        if (!localStructuresFolder.exists()) {
+            path = String.format("BBOutlineReloaded%s%s,%d", File.separator, host, port);
+            localStructuresFolder = new File(configManager.configDir, path);
+            Logger.info("Looking for local structures (folder=%s)", localStructuresFolder.getAbsolutePath());
+        }
+        if (!localStructuresFolder.exists()) {
+            Logger.info("No local structures folders found");
+            return;
+        }
+        loadLevelDat(localStructuresFolder);
+        loadNetherStructures(localStructuresFolder);
+        loadOverworldStructures(localStructuresFolder);
+        loadEndStructures(localStructuresFolder);
+    }
+
+    private void loadOverworldStructures(File localStructuresFolder) {
+        BoundingBoxCache cache = new BoundingBoxCache();
+        if (configManager.drawDesertTemples.getBoolean()) {
+            loadStructureNbtFile(localStructuresFolder, cache, "Temple.dat", StructureType.DesertTemple.getColor(), "TeDP");
+        }
+        if (configManager.drawJungleTemples.getBoolean()) {
+            loadStructureNbtFile(localStructuresFolder, cache, "Temple.dat", StructureType.JungleTemple.getColor(), "TeJP");
+        }
+        if (configManager.drawWitchHuts.getBoolean()) {
+            loadStructureNbtFile(localStructuresFolder, cache, "Temple.dat", StructureType.WitchHut.getColor(), "TeSH");
+        }
+        if (configManager.drawOceanMonuments.getBoolean()) {
+            loadStructureNbtFile(localStructuresFolder, cache, "Monument.dat", StructureType.OceanMonument.getColor(), "*");
+        }
+        if (configManager.drawStrongholds.getBoolean()) {
+            loadStructureNbtFile(localStructuresFolder, cache, "Stronghold.dat", StructureType.Stronghold.getColor(), "*");
+        }
+        if (configManager.drawMansions.getBoolean()) {
+            loadStructureNbtFile(localStructuresFolder, cache, "Mansion.dat", StructureType.Mansion.getColor(), "*");
+        }
+        if (configManager.drawMineShafts.getBoolean()) {
+            loadStructureNbtFile(localStructuresFolder, cache, "Mineshaft.dat", StructureType.MineShaft.getColor(), "*");
+        }
+        if (configManager.drawVillages.getBoolean()) {
+            loadVillageNbtFile(localStructuresFolder, cache, "Villages.dat");
+        }
+
+        boundingBoxCacheMap.put(DimensionType.OVERWORLD, cache);
+    }
+
+    private void loadNetherStructures(File localStructuresFolder) {
+        BoundingBoxCache cache = new BoundingBoxCache();
+        if (configManager.drawNetherFortresses.getBoolean())
+            loadStructureNbtFile(localStructuresFolder, cache, "Fortress.dat", StructureType.NetherFortress.getColor(), "*");
+        if (configManager.drawVillages.getBoolean()) {
+            loadVillageNbtFile(localStructuresFolder, cache, "villages_nether.dat");
+        }
+        boundingBoxCacheMap.put(DimensionType.NETHER, cache);
+    }
+
+    private void loadEndStructures(File localStructuresFolder) {
+        BoundingBoxCache cache = new BoundingBoxCache();
+        if (configManager.drawVillages.getBoolean()) {
+            loadVillageNbtFile(localStructuresFolder, cache, "Villages_end.dat");
+        }
+        if (configManager.drawEndCities.getBoolean()) {
+            loadStructureNbtFile(localStructuresFolder, cache, "EndCity.dat", StructureType.EndCity.getColor(), "*");
+        }
+        boundingBoxCacheMap.put(DimensionType.THE_END, cache);
+    }
+
+    private void loadVillageNbtFile(File localStructuresFolder, BoundingBoxCache cache, String fileName) {
+        File file = new File(localStructuresFolder, fileName);
+        NBTTagCompound nbt = loadNbtFile(file);
+        if (nbt == null)
+            return;
+
+        NBTTagCompound[] villages = getChildCompoundTags(nbt.getCompoundTag("data"), "Villages");
+        for (NBTTagCompound village : villages) {
+            BlockPos center = new BlockPos(village.getInteger("CX"), village.getInteger("CY"), village.getInteger("CZ"));
+            int radius = village.getInteger("Radius");
+            int population = village.getInteger("PopSize");
+            Set<BlockPos> doors = getDoors(village);
+            BoundingBox boundingBox = BoundingBoxVillage.from(center, radius, population, doors);
+            cache.addBoundingBox(boundingBox);
+        }
+
+        Logger.info("Loaded %s (%d villages)", fileName, villages.length);
+    }
+
+    private Set<BlockPos> getDoors(NBTTagCompound village) {
+        Set<BlockPos> doors = new HashSet<>();
+        for (NBTTagCompound door : getChildCompoundTags(village, "Doors")) {
+            doors.add(new BlockPos(door.getInteger("X"), door.getInteger("Y"), door.getInteger("Z")));
+        }
+        return doors;
+    }
+
+    private void loadStructureNbtFile(File localStructuresFolder, BoundingBoxCache cache, String fileName, Color color, String id) {
+        File file = new File(localStructuresFolder, fileName);
+        NBTTagCompound nbt = loadNbtFile(file);
+        if (nbt == null)
+            return;
+
+        NBTTagCompound features = nbt.getCompoundTag("data")
+                .getCompoundTag("Features");
+        int loadedStructureCount = 0;
+        for (Object key : features.getKeySet()) {
+            NBTTagCompound feature = features.getCompoundTag((String) key);
+            BoundingBox structure = BoundingBoxStructure.from(feature.getIntArray("BB"), color);
+            Set<BoundingBox> boundingBoxes = new HashSet<>();
+            NBTTagCompound[] children = getChildCompoundTags(feature, "Children");
+            for (NBTTagCompound child : children) {
+                if (id.equals(child.getString("id")) || id.equals("*"))
+                    boundingBoxes.add(BoundingBoxStructure.from(child.getIntArray("BB"), color));
+            }
+            if (boundingBoxes.size() > 0)
+                ++loadedStructureCount;
+            cache.addBoundingBoxes(structure, boundingBoxes);
+        }
+
+        Logger.info("Loaded %s (%d structures with type %s)", fileName, loadedStructureCount, id);
+    }
+
+    private NBTTagCompound[] getChildCompoundTags(NBTTagCompound parent, String key) {
+        NBTTagList tagList = parent.getTagList(key, 10);
+        NBTTagCompound[] result = new NBTTagCompound[tagList.tagCount()];
+        for (int index = 0; index < tagList.tagCount(); index++) {
+            result[index] = tagList.getCompoundTagAt(index);
+        }
+        return result;
+    }
+
+    private void loadLevelDat(File localStructuresFolder) {
+        File file = new File(localStructuresFolder, "level.dat");
+        NBTTagCompound nbt = loadNbtFile(file);
+        if (nbt == null)
+            return;
+
+        NBTTagCompound data = nbt.getCompoundTag("Data");
+        long seed = data.getLong("RandomSeed");
+        int spawnX = data.getInteger("SpawnX");
+        int spawnZ = data.getInteger("SpawnZ");
+        setWorldData(new WorldData(seed, spawnX, spawnZ));
+        Logger.info("Loaded level.dat (seed: %d, spawn: %d,%d)", worldData.getSeed(), worldData.getSpawnX(), worldData.getSpawnZ());
+    }
+
+    private NBTTagCompound loadNbtFile(File file) {
+        if (!file.exists())
+            return null;
+        try {
+            return CompressedStreamTools.readCompressed(new FileInputStream(file));
+        } catch (IOException e) {
+        }
+        return null;
+    }
+
+    public void playerDisconnectedFromServer() {
+        active = false;
+        if (configManager.keepCacheBetweenSessions.getBoolean()) return;
+        worldData = null;
+        worldSpawnBoundingBox = null;
+        spawnChunksBoundingBox = null;
+        for (BoundingBoxCache cache : boundingBoxCacheMap.values()) {
+            cache.close();
+        }
+        boundingBoxCacheMap.clear();
+    }
+
+    private void renderBoundingBoxes(Map<BoundingBox, Set<BoundingBox>> map, Set<BoundingBox> clientBoundingBoxes) {
+        if (map == null && clientBoundingBoxes == null)
+            return;
+
+        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+        GL11.glLineWidth(2.0f);
+        GL11.glDisable(GL11.GL_TEXTURE_2D);
+        GL11.glDisable(GL11.GL_CULL_FACE);
+
+        if (configManager.alwaysVisible.getBoolean()) {
+            GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
+        }
+
+        if (map != null)
+            for (BoundingBox bb : map.keySet()) {
+                if (outerBoxOnly) {
+                    renderBoundingBoxSet(map.get(bb));
+                } else {
+                    renderBoundingBoxByType(bb);
+                }
+            }
+
+        if (clientBoundingBoxes != null)
+            renderBoundingBoxSet(clientBoundingBoxes);
+
+        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
+        GL11.glEnable(GL11.GL_CULL_FACE);
+        GL11.glEnable(GL11.GL_TEXTURE_2D);
+    }
+
+    private void renderBoundingBoxSet(Set<BoundingBox> bbList) {
+        if (bbList != null)
+            for (BoundingBox bb : bbList) {
+                renderBoundingBoxByType(bb);
+            }
+    }
+
+    private void renderBoundingBoxByType(BoundingBox bb) {
+        WorldClient world = Minecraft.getMinecraft().world;
+        if (!world.isAreaLoaded(bb.getMinBlockPos(), bb.getMaxBlockPos())) {
+            return;
+        }
+
+        if (bb instanceof BoundingBoxVillage) {
+            BoundingBoxVillage villageBB = (BoundingBoxVillage) bb;
+            if (configManager.renderVillageAsSphere.getBoolean()) {
+                renderBoundingBoxVillageAsSphere(villageBB);
+            } else {
+                renderBoundingBox(villageBB);
+            }
+            if (configManager.drawIronGolemSpawnArea.getBoolean() &&
+                    villageBB.getSpawnsIronGolems()) {
+                renderIronGolemSpawnArea(villageBB);
+            }
+            if (configManager.drawVillageDoors.getBoolean())
+                renderVillageDoors(villageBB);
+        } else if (bb instanceof BoundingBoxSlimeChunk) {
+            renderSlimeChunk((BoundingBoxSlimeChunk) bb);
+        } else if (bb instanceof BoundingBoxWorldSpawn) {
+            renderWorldSpawn((BoundingBoxWorldSpawn) bb);
+        } else {
+            renderBoundingBox(bb);
+        }
+    }
+
+    private void renderBoundingBox(BoundingBox bb) {
+        AxisAlignedBB aaBB = bb.toAxisAlignedBB();
+        Color color = bb.getColor();
+        renderCuboid(aaBB, color, fill());
+    }
+
+    private void renderWorldSpawn(BoundingBoxWorldSpawn bb) {
+        AxisAlignedBB aaBB = bb.toAxisAlignedBB(false);
+        Color color = bb.getColor();
+        double y = getMaxY(configManager.worldSpawnMaxY.getInt()) + 0.001F;
+        renderRectangle(aaBB, y, y, color, false);
+    }
+
+    private void renderSlimeChunk(BoundingBoxSlimeChunk bb) {
+        AxisAlignedBB aaBB = bb.toAxisAlignedBB();
+        Color color = bb.getColor();
+        renderCuboid(aaBB, color, fill());
+
+        double maxY = getMaxY(configManager.slimeChunkMaxY.getInt());
+        if (maxY > 39) {
+            renderRectangle(aaBB, 39, maxY, color, fill());
+        }
+    }
+
+    private double getMaxY(double configMaxY) {
+        if (configMaxY == -1) {
+            return activeY;
+        } else if ((configMaxY == 0) || (playerY < configMaxY)) {
+            return playerY;
+        }
+        return configMaxY;
+    }
+
+    private void renderRectangle(AxisAlignedBB aaBB, double minY, double maxY, Color color, Boolean fill) {
+        aaBB = new AxisAlignedBB(aaBB.minX, minY, aaBB.minZ, aaBB.maxX, maxY, aaBB.maxZ);
+        renderCuboid(aaBB, color, fill);
+    }
+
+    private boolean fill() {
+        return configManager.fill.getBoolean();
+    }
+
+    private void renderIronGolemSpawnArea(BoundingBoxVillage villageBB) {
+        BlockPos center = villageBB.getCenter();
+        AxisAlignedBB abb = new AxisAlignedBB(new BlockPos(center.getX() - 8,
+                center.getY() - 3,
+                center.getZ() - 8),
+                new BlockPos(center.getX() + 8,
+                        center.getY() + 3,
+                        center.getZ() + 8))
+                .offset(villageBB.getCenterOffsetX(), 0.0, villageBB.getCenterOffsetZ());
+
+        renderCuboid(abb, villageBB.getColor(), false);
+    }
+
+    private void renderVillageDoors(BoundingBoxVillage villageBB) {
+        OffsetPoint center = new OffsetPoint(villageBB.getCenter())
+                .add(villageBB.getCenterOffsetX(), 0.0, villageBB.getCenterOffsetZ());
+        Color color = villageBB.getColor();
+        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
+        Tessellator tessellator = Tessellator.getInstance();
+        BufferBuilder worldRenderer = tessellator.getBuffer();
+
+        int colorR = color.getRed();
+        int colorG = color.getGreen();
+        int colorB = color.getBlue();
+
+        worldRenderer.begin(GL11.GL_LINES, worldRenderer.getVertexFormat());
+        for (BlockPos door : villageBB.getDoors()) {
+            OffsetPoint point = new OffsetPoint(door).add(0.5, 0, 0.5);
+
+            worldRenderer.pos(point.getX(), point.getY(), point.getZ()).color(colorR, colorG, colorB, 255).endVertex();
+            worldRenderer.pos(center.getX(), center.getY(), center.getZ()).color(colorR, colorG, colorB, 255).endVertex();
+        }
+        tessellator.draw();
+    }
+
+    private void renderCuboid(AxisAlignedBB aaBB, Color color, boolean fill) {
+        aaBB = offsetAxisAlignedBB(aaBB);
+        if (fill) {
+            renderFilledCuboid(aaBB, color);
+        }
+        renderUnfilledCuboid(aaBB, color);
+    }
+
+    private void renderFilledCuboid(AxisAlignedBB aaBB, Color color) {
+        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
+        GL11.glEnable(GL11.GL_BLEND);
+        renderCuboid(aaBB, 30, color);
+        GL11.glDisable(GL11.GL_BLEND);
+        GL11.glEnable(GL11.GL_POLYGON_OFFSET_LINE);
+        GL11.glPolygonOffset(-1.f, -1.f);
+    }
+
+    private void renderUnfilledCuboid(AxisAlignedBB aaBB, Color color) {
+        GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
+        renderCuboid(aaBB, 255, color);
+    }
+
+    private void renderCuboid(AxisAlignedBB bb, int alphaChannel, Color color) {
+        Tessellator tessellator = Tessellator.getInstance();
+        BufferBuilder worldRenderer = tessellator.getBuffer();
+
+        int colorR = color.getRed();
+        int colorG = color.getGreen();
+        int colorB = color.getBlue();
+
+        worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
+        worldRenderer.pos(bb.minX, bb.minY, bb.minZ)
+                .color(colorR, colorG, colorB, alphaChannel)
+                .endVertex();
+        worldRenderer.pos(bb.maxX, bb.minY, bb.minZ)
+                .color(colorR, colorG, colorB, alphaChannel)
+                .endVertex();
+        worldRenderer.pos(bb.maxX, bb.minY, bb.maxZ)
+                .color(colorR, colorG, colorB, alphaChannel)
+                .endVertex();
+        worldRenderer.pos(bb.minX, bb.minY, bb.maxZ)
+                .color(colorR, colorG, colorB, alphaChannel)
+                .endVertex();
+
+        if (bb.minY != bb.maxY) {
+            worldRenderer.pos(bb.minX, bb.maxY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.maxY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.maxY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.minX, bb.maxY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+
+            worldRenderer.pos(bb.minX, bb.minY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.minX, bb.maxY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.maxY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.minY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+
+            worldRenderer.pos(bb.minX, bb.minY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.minX, bb.maxY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.maxY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.minY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+
+            worldRenderer.pos(bb.minX, bb.minY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.minX, bb.minY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.minX, bb.maxY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.minX, bb.maxY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+
+            worldRenderer.pos(bb.maxX, bb.minY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.minY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.maxY, bb.maxZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+            worldRenderer.pos(bb.maxX, bb.maxY, bb.minZ)
+                    .color(colorR, colorG, colorB, alphaChannel)
+                    .endVertex();
+        }
+        tessellator.draw();
+    }
+
+    private AxisAlignedBB offsetAxisAlignedBB(AxisAlignedBB axisAlignedBB) {
+        double growXZ = 0.001F;
+        double growY = 0;
+        if (axisAlignedBB.minY != axisAlignedBB.maxY) {
+            growY = growXZ;
+        }
+        return axisAlignedBB
+                .grow(growXZ, growY, growXZ)
+                .offset(-playerX, -playerY, -playerZ);
+    }
+
+    private void renderBoundingBoxVillageAsSphere(BoundingBoxVillage bb) {
+        OffsetPoint center = new OffsetPoint(bb.getCenter())
+                .add(bb.getCenterOffsetX(), 0.0, bb.getCenterOffsetZ());
+        ;
+        int radius = bb.getRadius();
+        Color color = bb.getColor();
+        renderSphere(center, radius, color);
+    }
+
+    private void renderSphere(OffsetPoint center, double radius, Color color) {
+        GL11.glEnable(GL11.GL_POINT_SMOOTH);
+        GL11.glPointSize(2f);
+
+        Tessellator tessellator = Tessellator.getInstance();
+        BufferBuilder worldRenderer = tessellator.getBuffer();
+        worldRenderer.begin(GL11.GL_POINTS, DefaultVertexFormats.POSITION_COLOR);
+        for (OffsetPoint point : buildPoints(center, radius)) {
+            worldRenderer.pos(point.getX(), point.getY(), point.getZ())
+                    .color(color.getRed(), color.getGreen(), color.getBlue(), 255)
+                    .endVertex();
+        }
+        tessellator.draw();
+    }
+
+    private class OffsetPoint {
+        private final double x;
+        private final double y;
+        private final double z;
+
+        public OffsetPoint(double x, double y, double z) {
+            this.x = x;
+            this.y = y;
+            this.z = z;
+        }
+
+        public OffsetPoint(BlockPos blockPos) {
+            x = blockPos.getX();
+            y = blockPos.getY();
+            z = blockPos.getZ();
+        }
+
+        public double getX() {
+            return x - playerX;
+        }
+
+        public double getY() {
+            return y - playerY;
+        }
+
+        public double getZ() {
+            return z - playerZ;
+        }
+
+        public OffsetPoint add(double x, double y, double z) {
+            return new OffsetPoint(this.x + x, this.y + y, this.z + z);
+        }
+    }
+
+    private Set<OffsetPoint> buildPoints(OffsetPoint center, double radius) {
+        Set<OffsetPoint> points = new HashSet<>(1200);
+
+        double tau = 6.283185307179586D;
+        double pi = tau / 2D;
+        double segment = tau / 48D;
+
+        for (double t = 0.0D; t < tau; t += segment)
+            for (double theta = 0.0D; theta < pi; theta += segment) {
+                double dx = radius * Math.sin(t) * Math.cos(theta);
+                double dz = radius * Math.sin(t) * Math.sin(theta);
+                double dy = radius * Math.cos(t);
+
+                points.add(center.add(dx, dy, dz));
+            }
+        return points;
+    }
+
+    private Set<BoundingBox> getClientBoundingBoxes(DimensionType dimensionType) {
+        if (worldData == null || dimensionType != DimensionType.OVERWORLD) {
+            return null;
+        }
+
+        Set<BoundingBox> boundingBoxes = new HashSet<>();
+        if (configManager.drawWorldSpawn.getBoolean()) {
+            boundingBoxes.add(getWorldSpawnBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
+            boundingBoxes.add(getSpawnChunksBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
+        }
+        if (configManager.drawLazySpawnChunks.getBoolean()) {
+            boundingBoxes.add(getLazySpawnChunksBoundingBox(worldData.getSpawnX(), worldData.getSpawnZ()));
+        }
+        if (configManager.drawSlimeChunks.getBoolean()) {
+            boundingBoxes.addAll(this.getSlimeChunks());
+        }
+        return boundingBoxes;
+    }
+
+    private Set<BoundingBoxSlimeChunk> getSlimeChunks() {
+        Minecraft minecraft = Minecraft.getMinecraft();
+        int renderDistanceChunks = minecraft.gameSettings.renderDistanceChunks;
+        int playerChunkX = MathHelper.floor(minecraft.player.posX / 16.0D);
+        int playerChunkZ = MathHelper.floor(minecraft.player.posZ / 16.0D);
+        Set<BoundingBoxSlimeChunk> slimeChunks = new HashSet<>();
+        for (int chunkX = playerChunkX - renderDistanceChunks; chunkX <= playerChunkX + renderDistanceChunks; ++chunkX) {
+            for (int chunkZ = playerChunkZ - renderDistanceChunks; chunkZ <= playerChunkZ + renderDistanceChunks; ++chunkZ) {
+                if (isSlimeChunk(chunkX, chunkZ)) {
+                    ChunkPos chunk = new ChunkPos(chunkX, chunkZ);
+                    BlockPos minBlockPos = new BlockPos(chunk.getXStart(), 1, chunk.getZStart());
+                    BlockPos maxBlockPos = new BlockPos(chunk.getXEnd(), 38, chunk.getZEnd());
+                    if (minecraft.world.isAreaLoaded(minBlockPos, maxBlockPos)) {
+                        slimeChunks.add(BoundingBoxSlimeChunk.from(minBlockPos, maxBlockPos, Color.GREEN));
+                    }
+                }
+            }
+        }
+        return slimeChunks;
+    }
+
+    private boolean isSlimeChunk(int chunkX, int chunkZ) {
+        Random r = new Random(worldData.getSeed() +
+                (long) (chunkX * chunkX * 4987142) +
+                (long) (chunkX * 5947611) +
+                (long) (chunkZ * chunkZ) * 4392871L +
+                (long) (chunkZ * 389711) ^ 987234911L);
+        return r.nextInt(10) == 0;
+    }
+
+    private BoundingBox getSpawnChunksBoundingBox(int spawnX, int spawnZ) {
+        if (spawnChunksBoundingBox != null) {
+            return spawnChunksBoundingBox;
+        }
+        BoundingBox boundingBox = getSpawnChunksBoundingBox(spawnX, spawnZ, 12, Color.RED);
+        spawnChunksBoundingBox = boundingBox;
+        return boundingBox;
+    }
+
+    private BoundingBox getLazySpawnChunksBoundingBox(int spawnX, int spawnZ) {
+        if (lazySpawnChunksBoundingBox != null)
+            return lazySpawnChunksBoundingBox;
+
+        BoundingBox boundingBox = getSpawnChunksBoundingBox(spawnX, spawnZ, 16, Color.RED);
+        lazySpawnChunksBoundingBox = boundingBox;
+        return boundingBox;
+    }
+
+    private BoundingBox getSpawnChunksBoundingBox(int spawnX, int spawnZ, int size, Color color) {
+        double chunkSize = 16;
+        double midOffset = chunkSize * (size / 2);
+        double midX = Math.round((float) (spawnX / chunkSize)) * chunkSize;
+        double midZ = Math.round((float) (spawnZ / chunkSize)) * chunkSize;
+        BlockPos minBlockPos = new BlockPos(midX - midOffset, 0, midZ - midOffset);
+        if (spawnX / chunkSize % 0.5D == 0.0D && spawnZ / chunkSize % 0.5D == 0.0D) {
+            midX += chunkSize;
+            midZ += chunkSize;
+        }
+        BlockPos maxBlockPos = new BlockPos(midX + midOffset, 0, midZ + midOffset);
+        return BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, color);
+    }
+
+    private BoundingBox getWorldSpawnBoundingBox(int spawnX, int spawnZ) {
+        if (worldSpawnBoundingBox != null)
+            return worldSpawnBoundingBox;
+
+        BlockPos minBlockPos = new BlockPos(spawnX - 10, 0, spawnZ - 10);
+        BlockPos maxBlockPos = new BlockPos(spawnX + 10, 0, spawnZ + 10);
+
+        BoundingBox boundingBox = BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, Color.RED);
+        worldSpawnBoundingBox = boundingBox;
+        return boundingBox;
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/BoundingBoxCache.java b/java/com/irtimaled/bbor/common/BoundingBoxCache.java
new file mode 100644 (file)
index 0000000..f2bd78e
--- /dev/null
@@ -0,0 +1,43 @@
+package com.irtimaled.bbor.common;
+
+import com.irtimaled.bbor.common.models.BoundingBox;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class BoundingBoxCache {
+    private Map<BoundingBox, Set<BoundingBox>> cache = new ConcurrentHashMap<>();
+
+    public Map<BoundingBox, Set<BoundingBox>> getBoundingBoxes() {
+        return cache;
+    }
+
+    public synchronized void refresh() {
+    }
+
+    public void close() {
+        cache.clear();
+    }
+
+    public boolean isCached(BoundingBox key) {
+        return cache.containsKey(key);
+    }
+
+    public void addBoundingBoxes(BoundingBox key, Set<BoundingBox> boundingBoxes) {
+        cache.put(key, boundingBoxes);
+    }
+
+    public void addBoundingBox(BoundingBox key) {
+        Set<BoundingBox> boundingBoxes = new HashSet<>();
+        boundingBoxes.add(key);
+        addBoundingBoxes(key, boundingBoxes);
+    }
+
+    public void removeBoundingBox(BoundingBox key) {
+        if (cache.containsKey(key)) {
+            cache.remove(key);
+        }
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/CommonProxy.java b/java/com/irtimaled/bbor/common/CommonProxy.java
new file mode 100644 (file)
index 0000000..f53e990
--- /dev/null
@@ -0,0 +1,57 @@
+package com.irtimaled.bbor.common;
+
+import com.irtimaled.bbor.Logger;
+import com.irtimaled.bbor.ReflectionHelper;
+import com.irtimaled.bbor.common.models.WorldData;
+import com.irtimaled.bbor.config.ConfigManager;
+import net.minecraft.world.DimensionType;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.IChunkProvider;
+import net.minecraft.world.gen.ChunkProviderServer;
+import net.minecraft.world.gen.IChunkGenerator;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class CommonProxy {
+    public Map<DimensionType, BoundingBoxCache> boundingBoxCacheMap = new ConcurrentHashMap<>();
+
+    public ConfigManager configManager;
+    protected WorldData worldData;
+    private IEventHandler eventHandler = null;
+
+    public void init(ConfigManager configManager) {
+        this.configManager = configManager;
+    }
+
+    public void worldLoaded(World world) {
+        IChunkProvider chunkProvider = world.getChunkProvider();
+        if (chunkProvider instanceof ChunkProviderServer) {
+            IChunkGenerator chunkGenerator = ReflectionHelper.getPrivateValue(ChunkProviderServer.class, (ChunkProviderServer) chunkProvider, IChunkGenerator.class);
+            setWorldData(new WorldData(world.getSeed(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnZ()));
+            DimensionType dimensionType = world.provider.getDimensionType();
+            Logger.info("create world dimension: %s, %s (chunkprovider: %s) (seed: %d)", dimensionType, world.getClass().toString(), chunkGenerator.getClass().toString(), worldData.getSeed());
+            boundingBoxCacheMap.put(dimensionType, new DimensionProcessor(eventHandler, configManager, world, dimensionType, chunkGenerator));
+        }
+    }
+
+    public void chunkLoaded(Chunk chunk) {
+        DimensionType dimensionType = chunk.getWorld().provider.getDimensionType();
+        if (boundingBoxCacheMap.containsKey(dimensionType)) {
+            boundingBoxCacheMap.get(dimensionType).refresh();
+        }
+    }
+
+    public WorldData getWorldData() {
+        return worldData;
+    }
+
+    public void setWorldData(WorldData worldData) {
+        this.worldData = worldData;
+    }
+
+    public void setEventHandler(IEventHandler eventHandler) {
+        this.eventHandler = eventHandler;
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/DimensionProcessor.java b/java/com/irtimaled/bbor/common/DimensionProcessor.java
new file mode 100644 (file)
index 0000000..a0d1ccf
--- /dev/null
@@ -0,0 +1,193 @@
+package com.irtimaled.bbor.common;
+
+import com.irtimaled.bbor.Logger;
+import com.irtimaled.bbor.ReflectionHelper;
+import com.irtimaled.bbor.common.models.BoundingBox;
+import com.irtimaled.bbor.common.models.BoundingBoxStructure;
+import com.irtimaled.bbor.common.models.BoundingBoxVillage;
+import com.irtimaled.bbor.config.ConfigManager;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
+import net.minecraft.village.Village;
+import net.minecraft.village.VillageDoorInfo;
+import net.minecraft.world.DimensionType;
+import net.minecraft.world.World;
+import net.minecraft.world.gen.ChunkGeneratorEnd;
+import net.minecraft.world.gen.ChunkGeneratorHell;
+import net.minecraft.world.gen.ChunkGeneratorOverworld;
+import net.minecraft.world.gen.IChunkGenerator;
+import net.minecraft.world.gen.structure.*;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+public class DimensionProcessor extends BoundingBoxCache {
+    private ConfigManager configManager;
+    private World world;
+    private IEventHandler eventHandler;
+
+    public DimensionProcessor(IEventHandler eventHandler, ConfigManager configManager, World world, DimensionType dimensionType, IChunkGenerator chunkGenerator) {
+        this.eventHandler = eventHandler;
+        this.configManager = configManager;
+        this.world = world;
+        this.dimensionType = dimensionType;
+        this.chunkGenerator = chunkGenerator;
+        villageCache = new HashMap<>();
+        slimeChunkCache = new HashSet<>();
+        worldSpawnCache = new HashSet<>();
+    }
+
+    private DimensionType dimensionType;
+    private IChunkGenerator chunkGenerator;
+    private Map<Integer, BoundingBoxVillage> villageCache;
+    private Set<BoundingBox> slimeChunkCache;
+    private Set<BoundingBox> worldSpawnCache;
+
+    private boolean closed = false;
+
+    @Override
+    public void close() {
+        closed = true;
+        chunkGenerator = null;
+        villageCache.clear();
+        slimeChunkCache.clear();
+        worldSpawnCache.clear();
+        super.close();
+    }
+
+    private static <T extends IChunkGenerator, R extends MapGenStructure> Collection<StructureStart> getStructures(T chunkGenerator, Class<R> generatorClass) {
+        Class<T> chunkGeneratorClass = (Class<T>) chunkGenerator.getClass();
+        R structureGenerator = ReflectionHelper.getPrivateValue(chunkGeneratorClass, chunkGenerator, generatorClass);
+        if (structureGenerator != null) {
+            Map<ChunkPos, StructureStart> structureMap = ReflectionHelper.getPrivateValue(MapGenStructure.class, structureGenerator, Map.class);
+            return structureMap.values();
+        }
+        return Collections.emptyList();
+    }
+
+    private Map<StructureType, Collection<StructureStart>> getStructures() {
+        Map<StructureType, Collection<StructureStart>> structureMap = new HashMap<>();
+        if (chunkGenerator instanceof ChunkGeneratorOverworld) {
+            if (configManager.drawDesertTemples.getBoolean()) {
+                structureMap.put(StructureType.DesertTemple, getStructuresWithComponent(getStructures(chunkGenerator, MapGenScatteredFeature.class), ComponentScatteredFeaturePieces.DesertPyramid.class));
+            }
+
+            if (configManager.drawJungleTemples.getBoolean()) {
+                structureMap.put(StructureType.JungleTemple, getStructuresWithComponent(getStructures(chunkGenerator, MapGenScatteredFeature.class), ComponentScatteredFeaturePieces.JunglePyramid.class));
+            }
+
+            if (configManager.drawWitchHuts.getBoolean()) {
+                structureMap.put(StructureType.WitchHut, getStructuresWithComponent(getStructures(chunkGenerator, MapGenScatteredFeature.class), ComponentScatteredFeaturePieces.SwampHut.class));
+            }
+
+            if (configManager.drawOceanMonuments.getBoolean()) {
+                structureMap.put(StructureType.OceanMonument, getStructures(chunkGenerator, StructureOceanMonument.class));
+            }
+
+            if (configManager.drawStrongholds.getBoolean()) {
+                structureMap.put(StructureType.Stronghold, getStructures(chunkGenerator, MapGenStronghold.class));
+            }
+
+            if (configManager.drawMansions.getBoolean()) {
+                structureMap.put(StructureType.Mansion, getStructures(chunkGenerator, WoodlandMansion.class));
+            }
+
+            if (configManager.drawMineShafts.getBoolean()) {
+                structureMap.put(StructureType.MineShaft, getStructures(chunkGenerator, MapGenMineshaft.class));
+            }
+        } else if (chunkGenerator instanceof ChunkGeneratorHell) {
+            if (configManager.drawNetherFortresses.getBoolean()) {
+                structureMap.put(StructureType.NetherFortress, getStructures(chunkGenerator, MapGenNetherBridge.class));
+            }
+        } else if (chunkGenerator instanceof ChunkGeneratorEnd) {
+            if (configManager.drawEndCities.getBoolean()) {
+                structureMap.put(StructureType.EndCity, getStructures(chunkGenerator, MapGenEndCity.class));
+            }
+        }
+
+        return structureMap;
+    }
+
+    private Collection<StructureStart> getStructuresWithComponent(Collection<StructureStart> structures, Class structureComponent) {
+        Collection<StructureStart> validStructures = new HashSet<>();
+        for (StructureStart structure : structures) {
+            if (structure.getComponents().get(0).getClass().equals(structureComponent)) {
+                validStructures.add(structure);
+            }
+        }
+        return validStructures;
+    }
+
+    @Override
+    public synchronized void refresh() {
+        if (closed) return;
+
+        Map<StructureType, Collection<StructureStart>> structureMap = getStructures();
+        for (StructureType structureType : structureMap.keySet()) {
+            Color color = structureType.getColor();
+            for (StructureStart structureStart : structureMap.get(structureType)) {
+                if (structureStart.getBoundingBox() != null) {
+                    BoundingBox boundingBox = BoundingBoxStructure.from(structureStart.getBoundingBox(), color);
+                    if (!isCached(boundingBox)) {
+                        Set<BoundingBox> structureBoundingBoxes = new HashSet<>();
+                        for (StructureComponent structureComponent : structureStart.getComponents()) {
+                            structureBoundingBoxes.add(BoundingBoxStructure.from(structureComponent.getBoundingBox(), color));
+                        }
+                        addBoundingBoxes(boundingBox, structureBoundingBoxes);
+                        Logger.info("[%s] new boundingBoxCacheMap entries: %d", dimensionType, structureBoundingBoxes.size());
+                    }
+                }
+            }
+        }
+
+        if (configManager.drawVillages.getBoolean() &&
+                world.getVillageCollection() != null) {
+            Map<Integer, BoundingBoxVillage> villageBoundingBoxes = new HashMap<>();
+            List<Village> villages = world.getVillageCollection().getVillageList();
+            for (Village village : villages) {
+                int villageId = village.hashCode();
+                BlockPos center = village.getCenter();
+                Color color = null;
+                if (villageCache.containsKey(villageId)) {
+                    BoundingBoxVillage boundingBoxVillage = villageCache.get(villageId);
+                    if (boundingBoxVillage.getCenter() == center) {
+                        villageBoundingBoxes.put(villageId, boundingBoxVillage);
+                        villageCache.remove(villageId);
+                        continue;
+                    }
+                    color = boundingBoxVillage.getColor();
+                }
+
+                Integer radius = village.getVillageRadius();
+                int population = village.getNumVillagers();
+                Set<BlockPos> doors = getDoorsFromVillage(village);
+                villageBoundingBoxes.put(villageId, BoundingBoxVillage.from(center, radius, color, population, doors));
+            }
+            processDelta(villageCache, villageBoundingBoxes);
+            villageCache = villageBoundingBoxes;
+        }
+    }
+
+    private Set<BlockPos> getDoorsFromVillage(Village village) {
+        Set<BlockPos> doors = new HashSet<>();
+        for (Object doorInfo : village.getVillageDoorInfoList()) {
+            VillageDoorInfo villageDoorInfo = (VillageDoorInfo) doorInfo;
+            doors.add(villageDoorInfo.getDoorBlockPos());
+        }
+        return doors;
+    }
+
+    private void processDelta(Map<Integer, BoundingBoxVillage> oldVillages, Map<Integer, BoundingBoxVillage> newVillages) {
+        for (BoundingBox village : oldVillages.values()) {
+            removeBoundingBox(village);
+            if (eventHandler != null) {
+                eventHandler.boundingBoxRemoved(this.dimensionType, village);
+            }
+        }
+        for (BoundingBox village : newVillages.values()) {
+            if (!isCached(village))
+                addBoundingBox(village);
+        }
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/IEventHandler.java b/java/com/irtimaled/bbor/common/IEventHandler.java
new file mode 100644 (file)
index 0000000..e427ab5
--- /dev/null
@@ -0,0 +1,8 @@
+package com.irtimaled.bbor.common;
+
+import com.irtimaled.bbor.common.models.BoundingBox;
+import net.minecraft.world.DimensionType;
+
+public interface IEventHandler {
+    void boundingBoxRemoved(DimensionType dimensionType, BoundingBox bb);
+}
diff --git a/java/com/irtimaled/bbor/common/StructureType.java b/java/com/irtimaled/bbor/common/StructureType.java
new file mode 100644 (file)
index 0000000..9f7e87a
--- /dev/null
@@ -0,0 +1,55 @@
+package com.irtimaled.bbor.common;
+
+import java.awt.*;
+
+public class StructureType {
+    private static final int JUNGLE_TEMPLE = 1;
+    private static final int DESERT_TEMPLE = 2;
+    private static final int WITCH_HUT = 3;
+    private static final int OCEAN_MONUMENT = 4;
+    private static final int STRONGHOLD = 5;
+    private static final int MINE_SHAFT = 6;
+    private static final int NETHER_FORTRESS = 7;
+    private static final int END_CITY = 8;
+    private static final int MANSION = 9;
+
+    public final static StructureType JungleTemple = new StructureType(JUNGLE_TEMPLE);
+    public final static StructureType DesertTemple = new StructureType(DESERT_TEMPLE);
+    public final static StructureType WitchHut = new StructureType(WITCH_HUT);
+    public final static StructureType OceanMonument = new StructureType(OCEAN_MONUMENT);
+    public final static StructureType Stronghold = new StructureType(STRONGHOLD);
+    public final static StructureType MineShaft = new StructureType(MINE_SHAFT);
+    public final static StructureType NetherFortress = new StructureType(NETHER_FORTRESS);
+    public final static StructureType EndCity = new StructureType(END_CITY);
+    public final static StructureType Mansion = new StructureType(MANSION);
+
+    private final int type;
+
+    private StructureType(int type) {
+        this.type = type;
+    }
+
+    public Color getColor() {
+        switch (type) {
+            case DESERT_TEMPLE:
+                return Color.ORANGE;
+            case JUNGLE_TEMPLE:
+                return Color.GREEN;
+            case WITCH_HUT:
+                return Color.BLUE;
+            case MINE_SHAFT:
+                return Color.LIGHT_GRAY;
+            case NETHER_FORTRESS:
+                return Color.RED;
+            case OCEAN_MONUMENT:
+                return Color.CYAN;
+            case STRONGHOLD:
+                return Color.YELLOW;
+            case END_CITY:
+                return Color.MAGENTA;
+            case MANSION:
+                return new Color(139, 69, 19);
+        }
+        return Color.WHITE;
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/models/BoundingBox.java b/java/com/irtimaled/bbor/common/models/BoundingBox.java
new file mode 100644 (file)
index 0000000..6d322cd
--- /dev/null
@@ -0,0 +1,71 @@
+package com.irtimaled.bbor.common.models;
+
+import net.minecraft.util.math.AxisAlignedBB;
+import net.minecraft.util.math.BlockPos;
+
+import java.awt.*;
+
+public abstract class BoundingBox {
+    private final Color color;
+    private final BlockPos minBlockPos;
+    private final BlockPos maxBlockPos;
+
+    protected BoundingBox(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
+        this.minBlockPos = minBlockPos;
+        this.maxBlockPos = maxBlockPos;
+        this.color = color;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + minBlockPos.hashCode();
+        result = prime * result + maxBlockPos.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BoundingBox other = (BoundingBox) obj;
+        if (!minBlockPos.equals(other.minBlockPos))
+            return false;
+        if (!maxBlockPos.equals(other.maxBlockPos))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "(" + minBlockPos.toString() + "; " + maxBlockPos.toString() + ")";
+    }
+
+    public AxisAlignedBB toAxisAlignedBB() {
+        return toAxisAlignedBB(true);
+    }
+
+    public AxisAlignedBB toAxisAlignedBB(boolean extendMaxByOne) {
+        AxisAlignedBB axisAlignedBB = new AxisAlignedBB(minBlockPos, maxBlockPos);
+        if (extendMaxByOne)
+            return axisAlignedBB.expand(1, 1, 1);
+        return axisAlignedBB;
+    }
+
+    public BlockPos getMinBlockPos() {
+        return minBlockPos;
+    }
+
+    public BlockPos getMaxBlockPos() {
+        return maxBlockPos;
+    }
+
+    public Color getColor() {
+        return color;
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/models/BoundingBoxSlimeChunk.java b/java/com/irtimaled/bbor/common/models/BoundingBoxSlimeChunk.java
new file mode 100644 (file)
index 0000000..9966b1b
--- /dev/null
@@ -0,0 +1,22 @@
+package com.irtimaled.bbor.common.models;
+
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
+
+import java.awt.*;
+
+public class BoundingBoxSlimeChunk extends BoundingBox {
+    private BoundingBoxSlimeChunk(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
+        super(minBlockPos, maxBlockPos, color);
+    }
+
+    public static BoundingBoxSlimeChunk from(ChunkPos chunkCoordIntPair, Color color) {
+        BlockPos minBlockPos = new BlockPos(chunkCoordIntPair.getXStart(), 1, chunkCoordIntPair.getZStart());
+        BlockPos maxBlockPos = new BlockPos(chunkCoordIntPair.getXEnd(), 38, chunkCoordIntPair.getZEnd());
+        return new BoundingBoxSlimeChunk(minBlockPos, maxBlockPos, color);
+    }
+
+    public static BoundingBoxSlimeChunk from(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
+        return new BoundingBoxSlimeChunk(minBlockPos, maxBlockPos, color);
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/models/BoundingBoxStructure.java b/java/com/irtimaled/bbor/common/models/BoundingBoxStructure.java
new file mode 100644 (file)
index 0000000..97f2602
--- /dev/null
@@ -0,0 +1,28 @@
+package com.irtimaled.bbor.common.models;
+
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.gen.structure.StructureBoundingBox;
+
+import java.awt.*;
+
+public class BoundingBoxStructure extends BoundingBox {
+    private BoundingBoxStructure(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
+        super(minBlockPos, maxBlockPos, color);
+    }
+
+    public static BoundingBoxStructure from(StructureBoundingBox bb, Color color) {
+        BlockPos minBlockPos = new BlockPos(bb.minX, bb.minY, bb.minZ);
+        BlockPos maxBlockPos = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
+        return new BoundingBoxStructure(minBlockPos, maxBlockPos, color);
+    }
+
+    public static BoundingBoxStructure from(int[] bb, Color color) {
+        BlockPos minBlockPos = new BlockPos(bb[0], bb[1], bb[2]);
+        BlockPos maxBlockPos = new BlockPos(bb[3], bb[4], bb[5]);
+        return new BoundingBoxStructure(minBlockPos, maxBlockPos, color);
+    }
+
+    public static BoundingBoxStructure from(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
+        return new BoundingBoxStructure(minBlockPos, maxBlockPos, color);
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/models/BoundingBoxVillage.java b/java/com/irtimaled/bbor/common/models/BoundingBoxVillage.java
new file mode 100644 (file)
index 0000000..a2ebcfa
--- /dev/null
@@ -0,0 +1,131 @@
+package com.irtimaled.bbor.common.models;
+
+import net.minecraft.util.math.BlockPos;
+
+import java.awt.*;
+import java.util.Set;
+
+public class BoundingBoxVillage extends BoundingBox {
+    private final BlockPos center;
+    private final Integer radius;
+    private final boolean spawnsIronGolems;
+    private Set<BlockPos> doors;
+    private Double centerOffsetX;
+    private Double centerOffsetZ;
+
+    protected BoundingBoxVillage(BlockPos center, Integer radius, Color color, boolean spawnsIronGolems, Set<BlockPos> doors, BlockPos minBlockPos, BlockPos maxBlockPos) {
+        super(minBlockPos, maxBlockPos, color);
+        this.center = center;
+        this.radius = radius;
+        this.spawnsIronGolems = spawnsIronGolems;
+        this.doors = doors;
+        calculateCenterOffsets(doors);
+    }
+
+    public static BoundingBoxVillage from(BlockPos center, Integer radius, int population, Set<BlockPos> doors) {
+        return from(center, radius, null, population, doors);
+    }
+
+    public static BoundingBoxVillage from(BlockPos center, Integer radius, Color color, int population, Set<BlockPos> doors) {
+        Boolean spawnsIronGolems = population >= 10 && doors.size() >= 21;
+        return from(center, radius, color, spawnsIronGolems, doors);
+    }
+
+    public static BoundingBoxVillage from(BlockPos center, Integer radius, Color color, boolean spawnsIronGolems, Set<BlockPos> doors) {
+        BlockPos minBlockPos = new BlockPos(center.getX() - radius,
+                center.getY() - 4,
+                center.getZ() - radius);
+        BlockPos maxBlockPos = new BlockPos(center.getX() + radius,
+                center.getY() + 4,
+                center.getZ() + radius);
+        if (color == null)
+            color = getNextColor();
+        return new BoundingBoxVillage(center, radius, color, spawnsIronGolems, doors, minBlockPos, maxBlockPos);
+    }
+
+    private void calculateCenterOffsets(Set<BlockPos> doors) {
+        boolean processedFirstDoor = false;
+        int minX = 0;
+        int maxX = 0;
+        int minZ = 0;
+        int maxZ = 0;
+        for (BlockPos door : doors) {
+            if (!processedFirstDoor ||
+                    (minX > door.getX()))
+                minX = door.getX();
+            if (!processedFirstDoor ||
+                    maxX < door.getX())
+                maxX = door.getX();
+            if (!processedFirstDoor ||
+                    minZ > door.getZ())
+                minZ = door.getZ();
+            if (!processedFirstDoor ||
+                    maxZ < door.getZ())
+                maxZ = door.getZ();
+
+            processedFirstDoor = true;
+        }
+        centerOffsetX = Math.abs(maxX - minX) % 2 == 0 ? 0.5 : (minX < 0 ? 0 : 1);
+        centerOffsetZ = Math.abs(maxZ - minZ) % 2 == 0 ? 0.5 : (minZ < 0 ? 0 : 1);
+    }
+
+    @Override
+    public String toString() {
+        return "(" + center.toString() + "; " + radius.toString() + ")";
+    }
+
+    public Integer getRadius() {
+        return radius;
+    }
+
+    public BlockPos getCenter() {
+        return center;
+    }
+
+    public Double getCenterOffsetX() {
+        return centerOffsetX;
+    }
+
+    public Double getCenterOffsetZ() {
+        return centerOffsetZ;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        for (BlockPos door : doors) {
+            result = prime * result + door.hashCode();
+        }
+        return result;
+    }
+
+    public boolean getSpawnsIronGolems() {
+        return spawnsIronGolems;
+    }
+
+    private static int colorIndex = -1;
+
+    public static Color getNextColor() {
+        ++colorIndex;
+        switch (colorIndex % 6) {
+            case 0:
+                return Color.RED;
+            case 1:
+                return Color.GREEN;
+            case 2:
+                return Color.BLUE;
+            case 3:
+                return Color.MAGENTA;
+            case 4:
+                return Color.YELLOW;
+            case 5:
+                return Color.CYAN;
+        }
+        return Color.WHITE;
+    }
+
+    public Set<BlockPos> getDoors() {
+        return doors;
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/models/BoundingBoxWorldSpawn.java b/java/com/irtimaled/bbor/common/models/BoundingBoxWorldSpawn.java
new file mode 100644 (file)
index 0000000..4ea13b5
--- /dev/null
@@ -0,0 +1,15 @@
+package com.irtimaled.bbor.common.models;
+
+import net.minecraft.util.math.BlockPos;
+
+import java.awt.*;
+
+public class BoundingBoxWorldSpawn extends BoundingBox {
+    protected BoundingBoxWorldSpawn(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
+        super(minBlockPos, maxBlockPos, color);
+    }
+
+    public static BoundingBoxWorldSpawn from(BlockPos minBlockPos, BlockPos maxBlockPos, Color color) {
+        return new BoundingBoxWorldSpawn(minBlockPos, maxBlockPos, color);
+    }
+}
diff --git a/java/com/irtimaled/bbor/common/models/WorldData.java b/java/com/irtimaled/bbor/common/models/WorldData.java
new file mode 100644 (file)
index 0000000..1eb5dbb
--- /dev/null
@@ -0,0 +1,25 @@
+package com.irtimaled.bbor.common.models;
+
+public class WorldData {
+    private long seed;
+    private int spawnX;
+    private int spawnZ;
+
+    public WorldData(long seed, int spawnX, int spawnZ) {
+        this.seed = seed;
+        this.spawnX = spawnX;
+        this.spawnZ = spawnZ;
+    }
+
+    public long getSeed() {
+        return seed;
+    }
+
+    public int getSpawnX() {
+        return spawnX;
+    }
+
+    public int getSpawnZ() {
+        return spawnZ;
+    }
+}
diff --git a/java/com/irtimaled/bbor/config/ConfigManager.java b/java/com/irtimaled/bbor/config/ConfigManager.java
new file mode 100644 (file)
index 0000000..80936f7
--- /dev/null
@@ -0,0 +1,74 @@
+package com.irtimaled.bbor.config;
+
+import java.io.File;
+
+public class ConfigManager {
+    public final File configDir;
+
+    public Setting fill;
+    public Setting drawVillages;
+    public Setting drawDesertTemples;
+    public Setting drawJungleTemples;
+    public Setting drawWitchHuts;
+    public Setting drawStrongholds;
+    public Setting drawMineShafts;
+    public Setting drawNetherFortresses;
+    public Setting drawOceanMonuments;
+    public Setting alwaysVisible;
+    public Setting renderVillageAsSphere;
+    public Setting drawIronGolemSpawnArea;
+    public Setting drawVillageDoors;
+    public Setting drawSlimeChunks;
+    public Setting slimeChunkMaxY;
+    public Setting keepCacheBetweenSessions;
+    public Setting drawWorldSpawn;
+    public Setting worldSpawnMaxY;
+    public Setting drawLazySpawnChunks;
+    public Setting drawEndCities;
+    public Setting drawMansions;
+
+    private Configuration config;
+
+    public ConfigManager(File configDir) {
+        this.configDir = configDir;
+        config = new Configuration(new File(configDir, "BBOutlineReloaded.cfg"));
+        config.load();
+
+        fill = SetupBooleanProperty(config, "general", "fill", true, "If set to true the bounding boxes are filled. (default: true)");
+        alwaysVisible = SetupBooleanProperty(config, "general", "alwaysVisible", false, "If set to true boxes will be visible even through other blocks. (default: false)");
+        keepCacheBetweenSessions = SetupBooleanProperty(config, "general", "keepCacheBetweenSessions", false, "If set to true bounding box caches will be kept between sessions. (default: false)");
+        drawVillages = SetupBooleanProperty(config, "features", "drawVillages", true, "If set to true village bounding boxes are drawn. (default: true)");
+        renderVillageAsSphere = SetupBooleanProperty(config, "features", "renderVillageAsSphere", true, "If set to true villages will be drawn as a sphere. (default:true)");
+        drawIronGolemSpawnArea = SetupBooleanProperty(config, "features", "drawIronGolemSpawnArea", true, "If set to true the iron golem spawn area of the village will be drawn. (default:true)");
+        drawVillageDoors = SetupBooleanProperty(config, "features", "drawVillageDoors", false, "If set to true lines between the village centre and doors will be drawn. (default:false)");
+        drawDesertTemples = SetupBooleanProperty(config, "features", "drawDesertTemples", true, "If set to true desert temple bounding boxes are drawn. (default: true)");
+        drawJungleTemples = SetupBooleanProperty(config, "features", "drawJungleTemples", true, "If set to true jungle temple bounding boxes are drawn. (default: true)");
+        drawWitchHuts = SetupBooleanProperty(config, "features", "drawWitchHuts", true, "If set to true witch hut bounding boxes are drawn. (default: true)");
+        drawStrongholds = SetupBooleanProperty(config, "features", "drawStrongholds", false, "If set to true stronghold bounding boxes are drawn. (default: false)");
+        drawMineShafts = SetupBooleanProperty(config, "features", "drawMineShafts", false, "If set to true mineshaft bounding boxes are drawn. (default: false)");
+        drawNetherFortresses = SetupBooleanProperty(config, "features", "drawNetherFortresses", true, "If set to true nether fortress bounding boxes are drawn. (default: true)");
+        drawOceanMonuments = SetupBooleanProperty(config, "features", "drawOceanMonuments", true, "If set to true ocean monument bounding boxes are drawn. (default: true)");
+        drawSlimeChunks = SetupBooleanProperty(config, "features", "drawSlimeChunks", true, "If set to true slime chunks bounding boxes are drawn. (default: true)");
+        slimeChunkMaxY = SetupIntegerProperty(config, "features", "slimeChunkMaxY", -1, "The maximum top of the slime chunk bounding box. If set to -1 it will use the value when activated, if set to 0 it will always track the player's feet. (default: -1)");
+        drawWorldSpawn = SetupBooleanProperty(config, "features", "drawWorldSpawn", true, "If set to true world spawn and spawn chunks bounding boxes are drawn. (default: true)");
+        worldSpawnMaxY = SetupIntegerProperty(config, "features", "worldSpawnMaxY", -1, "The maximum top of the world spawn bounding boxes. If set to -1 it will use the value when activated, if set to 0 it will always track the players feet. (default: -1)");
+        drawLazySpawnChunks = SetupBooleanProperty(config, "features", "drawLazySpawnChunks", false, "If set to true the lazy spawn chunks bounding boxes will be drawn. (default: false)");
+        drawEndCities = SetupBooleanProperty(config, "features", "drawEndCities", true, "If set to true end city bounding boxes will be drawn. (default: true)");
+        drawMansions = SetupBooleanProperty(config, "features", "drawMansions", true, "If set to true woodland mansions will be drawn. (default: true)");
+        config.save();
+    }
+
+    private Setting SetupBooleanProperty(Configuration config, String category, String settingName, Boolean defaultValue, String comment) {
+        Setting property = config.get(category, settingName, defaultValue);
+        property.comment = comment;
+        property.set(property.getBoolean(defaultValue));
+        return property;
+    }
+
+    private Setting SetupIntegerProperty(Configuration config, String category, String settingName, int defaultValue, String comment) {
+        Setting property = config.get(category, settingName, defaultValue);
+        property.comment = comment;
+        property.set(property.getInt(defaultValue));
+        return property;
+    }
+}
index 764d42f01e8fadb3d91ccc0c460321cfcc13ba0b..d248b4a189d539a6da9463caab0d41fcaf1f91ff 100644 (file)
@@ -1,6 +1,6 @@
 package com.irtimaled.bbor.forge;
 
-import com.irtimaled.bbor.ClientProxy;
+import com.irtimaled.bbor.client.ClientProxy;
 import net.minecraft.client.Minecraft;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraftforge.client.event.RenderWorldLastEvent;
index c204fbf6c785ed3e4144050ab4e5185631c31c47..1d2099759ecf093bcadd42c1d314ed0935b61617 100644 (file)
@@ -1,6 +1,12 @@
 package com.irtimaled.bbor.forge;
 
-import com.irtimaled.bbor.*;
+import com.irtimaled.bbor.Logger;
+import com.irtimaled.bbor.common.BoundingBoxCache;
+import com.irtimaled.bbor.common.CommonProxy;
+import com.irtimaled.bbor.common.IEventHandler;
+import com.irtimaled.bbor.common.models.BoundingBox;
+import com.irtimaled.bbor.common.models.WorldData;
+import com.irtimaled.bbor.config.ConfigManager;
 import com.irtimaled.bbor.forge.messages.*;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
index 27824862f3965a0fdbdd1b3b3a3a14a46240cdc6..955312b463f5d63c7ecb50b43b59ef9b0cc437de 100644 (file)
@@ -1,6 +1,6 @@
 package com.irtimaled.bbor.forge;
 
-import com.irtimaled.bbor.ConfigManager;
+import com.irtimaled.bbor.config.ConfigManager;
 import net.minecraftforge.common.MinecraftForge;
 import net.minecraftforge.fml.common.Mod;
 import net.minecraftforge.fml.common.SidedProxy;
index 322fe6b97a1ed0e4050ad97f4e1204e3c4be6ed0..b76826e6c47a9dfc5bcbfec16568ea1ba3abb05f 100644 (file)
@@ -1,6 +1,6 @@
 package com.irtimaled.bbor.forge.messages;
 
-import com.irtimaled.bbor.BoundingBox;
+import com.irtimaled.bbor.common.models.BoundingBox;
 import io.netty.buffer.ByteBuf;
 import net.minecraft.world.DimensionType;
 import net.minecraftforge.fml.common.network.ByteBufUtils;
index 53f55c3de28dba1875664984a739a942d1c47862..44c4296284f63ef9109ef9efc5b2b802b7ec3d11 100644 (file)
@@ -1,8 +1,8 @@
 package com.irtimaled.bbor.forge.messages;
 
-import com.irtimaled.bbor.BoundingBox;
-import com.irtimaled.bbor.BoundingBoxStructure;
-import com.irtimaled.bbor.BoundingBoxVillage;
+import com.irtimaled.bbor.common.models.BoundingBox;
+import com.irtimaled.bbor.common.models.BoundingBoxStructure;
+import com.irtimaled.bbor.common.models.BoundingBoxVillage;
 import io.netty.buffer.ByteBuf;
 import net.minecraft.util.math.BlockPos;
 import net.minecraftforge.fml.common.network.ByteBufUtils;
index 7c0af0108a11f97e31670ed928c40c0e73c270da..7b0965006b5576e76989e8b18e61b9de773a73ba 100644 (file)
@@ -1,8 +1,8 @@
 package com.irtimaled.bbor.forge.messages;
 
-import com.irtimaled.bbor.BoundingBox;
-import com.irtimaled.bbor.BoundingBoxStructure;
-import com.irtimaled.bbor.BoundingBoxVillage;
+import com.irtimaled.bbor.common.models.BoundingBox;
+import com.irtimaled.bbor.common.models.BoundingBoxStructure;
+import com.irtimaled.bbor.common.models.BoundingBoxVillage;
 import io.netty.buffer.ByteBuf;
 import net.minecraft.util.math.BlockPos;
 import net.minecraftforge.fml.common.network.ByteBufUtils;
index 35e2de85341067dc329a6768b28590dcc750b35a..cd32a00e9aff2665cac16bf8164d67f49e5b622c 100644 (file)
@@ -1,6 +1,6 @@
 package com.irtimaled.bbor.forge.messages;
 
-import com.irtimaled.bbor.WorldData;
+import com.irtimaled.bbor.common.models.WorldData;
 import io.netty.buffer.ByteBuf;
 import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
 
index f59089c88816d57cf728be1ce869a7a79917a31d..a80fc09686a7dc933847bbf2a013afc22eb6cef9 100644 (file)
@@ -1,6 +1,6 @@
 package com.irtimaled.bbor.forge.messages;
 
-import com.irtimaled.bbor.BoundingBox;
+import com.irtimaled.bbor.common.models.BoundingBox;
 import io.netty.buffer.ByteBuf;
 import net.minecraft.world.DimensionType;
 import net.minecraftforge.fml.common.network.ByteBufUtils;