From 18add8b847c900959a77f608cdf041fb2daae4c0 Mon Sep 17 00:00:00 2001 From: asiekierka Date: Thu, 19 Mar 2015 22:01:46 +0100 Subject: [PATCH] write global zone map system to replace earlier zone planner system --- buildcraft_resources/changelog/7.0.0 | 2 + common/buildcraft/BuildCraftCore.java | 3 - common/buildcraft/BuildCraftRobotics.java | 41 ++++ common/buildcraft/robotics/BlockZonePlan.java | 20 ++ common/buildcraft/robotics/TileZonePlan.java | 109 +--------- .../robotics/gui/ContainerZonePlan.java | 7 +- common/buildcraft/robotics/map/MapChunk.java | 80 ++++++++ .../buildcraft/robotics/map/MapManager.java | 94 +++++++++ common/buildcraft/robotics/map/MapRegion.java | 62 ++++++ common/buildcraft/robotics/map/MapWorld.java | 193 ++++++++++++++++++ 10 files changed, 501 insertions(+), 110 deletions(-) create mode 100644 common/buildcraft/robotics/map/MapChunk.java create mode 100644 common/buildcraft/robotics/map/MapManager.java create mode 100644 common/buildcraft/robotics/map/MapRegion.java create mode 100644 common/buildcraft/robotics/map/MapWorld.java diff --git a/buildcraft_resources/changelog/7.0.0 b/buildcraft_resources/changelog/7.0.0 index ea2362c7..a5422aeb 100644 --- a/buildcraft_resources/changelog/7.0.0 +++ b/buildcraft_resources/changelog/7.0.0 @@ -3,6 +3,8 @@ Additions: * **Modularization** - BuildCraft is back to being modules (asie) * Blocks: * Blueprint Library renamed to Electronic Library, supports copying books - and soon other things! (asie) + * Rewritten Zone Planner map system - should have much less lag and take up less disk space! + * **Note** - all existing zone planner previews will disappear. To reload them, simply break and place the zone planner! * Items: * Paintbrush for dyeing pipes and other supported blocks (asie) * Debugger for developer use (asie) diff --git a/common/buildcraft/BuildCraftCore.java b/common/buildcraft/BuildCraftCore.java index 8159b2a8..3a727fce 100644 --- a/common/buildcraft/BuildCraftCore.java +++ b/common/buildcraft/BuildCraftCore.java @@ -227,8 +227,6 @@ public class BuildCraftCore extends BuildCraftMod { private static FloatBuffer pos = ByteBuffer.allocateDirect(3 * 4).asFloatBuffer(); - public Thread serverThread; - @Mod.EventHandler public void loadConfiguration(FMLPreInitializationEvent evt) { BCLog.initLog(); @@ -443,7 +441,6 @@ public class BuildCraftCore extends BuildCraftMod { @Mod.EventHandler public void serverStarting(FMLServerStartingEvent event) { - serverThread = Thread.currentThread(); event.registerServerCommand(new CommandBuildCraft()); } diff --git a/common/buildcraft/BuildCraftRobotics.java b/common/buildcraft/BuildCraftRobotics.java index 51d371d1..a1bd0cfb 100644 --- a/common/buildcraft/BuildCraftRobotics.java +++ b/common/buildcraft/BuildCraftRobotics.java @@ -8,6 +8,7 @@ */ package buildcraft; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -22,9 +23,13 @@ import cpw.mods.fml.common.event.FMLInitializationEvent; import cpw.mods.fml.common.event.FMLInterModComms; import cpw.mods.fml.common.event.FMLMissingMappingsEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.event.FMLServerStartingEvent; +import cpw.mods.fml.common.event.FMLServerStoppingEvent; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.registry.EntityRegistry; import cpw.mods.fml.common.registry.GameRegistry; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.MinecraftForge; import buildcraft.api.boards.RedstoneBoardRegistry; import buildcraft.api.recipes.BuildcraftRecipeRegistry; import buildcraft.api.robots.RobotManager; @@ -126,6 +131,7 @@ import buildcraft.robotics.boards.BoardRobotShovelman; import buildcraft.robotics.boards.BoardRobotShovelmanNBT; import buildcraft.robotics.boards.BoardRobotStripes; import buildcraft.robotics.boards.BoardRobotStripesNBT; +import buildcraft.robotics.map.MapManager; import buildcraft.robotics.statements.ActionRobotFilter; import buildcraft.robotics.statements.ActionRobotFilterTool; import buildcraft.robotics.statements.ActionRobotGotoStation; @@ -187,6 +193,9 @@ public class BuildCraftRobotics extends BuildCraftMod { public static List blacklistedRobots; + public static MapManager manager; + private static Thread managerThread; + @Mod.EventHandler public void preInit(FMLPreInitializationEvent evt) { new BCCreativeTab("boards"); @@ -367,6 +376,38 @@ public class BuildCraftRobotics extends BuildCraftMod { BuildcraftRecipeRegistry.integrationTable.addRecipe(new RobotIntegrationRecipe("buildcraft:robotIntegration")); } + + @Mod.EventHandler + public void serverUnload(FMLServerStoppingEvent event) { + if (managerThread != null) { + manager.stop(); + manager.saveAllWorlds(); + managerThread.interrupt(); + + MinecraftForge.EVENT_BUS.unregister(manager); + } + + managerThread = null; + manager = null; + } + + @Mod.EventHandler + public void serverLoad(FMLServerStartingEvent event) { + File f = new File(DimensionManager.getCurrentSaveRootDirectory(), "buildcraft/zonemap"); + + try { + f.mkdirs(); + } catch (Exception e) { + e.printStackTrace(); + } + + manager = new MapManager(f); + managerThread = new Thread(manager); + managerThread.start(); + + MinecraftForge.EVENT_BUS.register(manager); + } + @Mod.EventHandler public void processRequests(FMLInterModComms.IMCEvent event) { InterModComms.processIMC(event); diff --git a/common/buildcraft/robotics/BlockZonePlan.java b/common/buildcraft/robotics/BlockZonePlan.java index 964465e6..eb08da61 100755 --- a/common/buildcraft/robotics/BlockZonePlan.java +++ b/common/buildcraft/robotics/BlockZonePlan.java @@ -28,6 +28,7 @@ import buildcraft.api.events.BlockInteractionEvent; import buildcraft.core.lib.block.BlockBuildCraft; import buildcraft.core.GuiIds; import buildcraft.core.lib.utils.Utils; +import buildcraft.robotics.map.MapWorld; public class BlockZonePlan extends BlockBuildCraft { public BlockZonePlan() { @@ -54,4 +55,23 @@ public class BlockZonePlan extends BlockBuildCraft { return true; } + @Override + public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase entity, ItemStack stack) { + super.onBlockPlacedBy(world, x, y, z, entity, stack); + + if (!world.isRemote) { + int r = TileZonePlan.RESOLUTION >> 4; + + int cox = (x >> 4); + int coz = (z >> 4); + MapWorld w = BuildCraftRobotics.manager.getWorld(world); + + for (int cx = -r; cx < r; cx++) { + for (int cz = -r; cz < r; cz++) { + int dist = cx * cx + cz * cz; + w.queueChunkForUpdateIfEmpty(cox + cx, coz + cz, dist); + } + } + } + } } diff --git a/common/buildcraft/robotics/TileZonePlan.java b/common/buildcraft/robotics/TileZonePlan.java index 4fb5666d..f1182c21 100644 --- a/common/buildcraft/robotics/TileZonePlan.java +++ b/common/buildcraft/robotics/TileZonePlan.java @@ -17,6 +17,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.chunk.Chunk; +import buildcraft.BuildCraftRobotics; import buildcraft.api.core.SafeTimeTracker; import buildcraft.api.items.INamedItem; import buildcraft.core.ItemMapLocation; @@ -32,76 +33,25 @@ public class TileZonePlan extends TileBuildCraft implements IInventory { private static int RESOLUTION_CHUNKS = RESOLUTION >> 4; public int chunkStartX, chunkStartZ; - public byte[] colors = new byte[RESOLUTION * RESOLUTION]; public short progress = 0; public String mapName = ""; - private boolean scan = false; - private int chunkIt = 0; - private ZonePlan[] selectedAreas = new ZonePlan[16]; private int currentSelectedArea = 0; private SimpleInventory inv = new SimpleInventory(2, "inv", 64); - private SafeTimeTracker zonePlannerScanning = new SafeTimeTracker(5); - @Override public void initialize() { super.initialize(); - chunkStartX = (xCoord >> 4) - RESOLUTION_CHUNKS / 2; - chunkStartZ = (zCoord >> 4) - RESOLUTION_CHUNKS / 2; - if (!scan) { - chunkIt = 0; - scan = true; - } - } + int cx = (xCoord >> 4); + int cz = (zCoord >> 4); - private int[] getCoords() { - int chunkCenterX = xCoord >> 4; - int chunkCenterZ = zCoord >> 4; - - if (chunkIt == 0) { - return new int[] {chunkCenterX, chunkCenterZ}; - } - - int radius = 1; - int left = chunkIt; - - while (radius < RESOLUTION_CHUNKS / 2) { - int lineLength = radius * 2; - int perimeter = lineLength * 4; - - if (left <= perimeter) { - int chunkX = 0, chunkZ = 0; - int remained = (left - 1) % lineLength; - - if ((left - 1) / lineLength == 0) { - chunkX = chunkCenterX + radius; - chunkZ = chunkCenterZ - lineLength / 2 + remained; - } else if ((left - 1) / lineLength == 1) { - chunkX = chunkCenterX - radius; - chunkZ = chunkCenterZ - lineLength / 2 + remained + 1; - } else if ((left - 1) / lineLength == 2) { - chunkX = chunkCenterX - lineLength / 2 + remained + 1; - chunkZ = chunkCenterZ + radius; - } else { - chunkX = chunkCenterX - lineLength / 2 + remained; - chunkZ = chunkCenterZ - radius; - } - - return new int[] {chunkX, chunkZ}; - } else { - left -= perimeter; - } - - radius += 1; - } - - return new int[] {chunkCenterX, chunkCenterZ}; + chunkStartX = cx - RESOLUTION_CHUNKS / 2; + chunkStartZ = cz - RESOLUTION_CHUNKS / 2; } @Override @@ -112,19 +62,6 @@ public class TileZonePlan extends TileBuildCraft implements IInventory { return; } - if (scan && zonePlannerScanning.markTimeIfDelay(worldObj)) { - int[] coords = getCoords(); - Chunk chunk = worldObj.getChunkFromChunkCoords(coords[0], coords[1]); - loadChunk(chunk); - - if (chunkIt > RESOLUTION_CHUNKS * RESOLUTION_CHUNKS) { - scan = false; - chunkIt = 0; - } else { - chunkIt++; - } - } - if (inv.getStackInSlot(0) != null && inv.getStackInSlot(1) == null && inv.getStackInSlot(0).getItem() instanceof ItemMapLocation) { @@ -151,36 +88,9 @@ public class TileZonePlan extends TileBuildCraft implements IInventory { } } - private void loadChunk(Chunk chunk) { - for (int cx = 0; cx < 16; ++cx) { - for (int cz = 0; cz < 16; ++cz) { - int x = (chunk.xPosition << 4) + cx; - int z = (chunk.zPosition << 4) + cz; - - int y = getWorldObj().getHeightValue(x, z); - int color; - while ((color = chunk.getBlock(cx, y, cz).getMapColor(0).colorIndex) == MapColor.airColor.colorIndex) { - y--; - if (y < 0) { - break; - } - } - - int ix = x - chunkStartX * 16; - int iz = z - chunkStartZ * 16; - - colors[ix + iz * RESOLUTION] = (byte) color; - } - } - } - @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); - - nbt.setBoolean("scan", scan); - nbt.setInteger("chunkIt", chunkIt); - nbt.setByteArray("colors", colors); nbt.setString("name", mapName); NBTTagCompound invNBT = new NBTTagCompound(); @@ -200,21 +110,12 @@ public class TileZonePlan extends TileBuildCraft implements IInventory { public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); - scan = nbt.getBoolean("scan"); - chunkIt = nbt.getInteger("chunkIt"); - colors = nbt.getByteArray("colors"); mapName = nbt.getString("name"); if (mapName == null) { mapName = ""; } - if (colors.length != RESOLUTION * RESOLUTION || chunkIt >= RESOLUTION_CHUNKS * RESOLUTION_CHUNKS) { - colors = new byte[RESOLUTION * RESOLUTION]; - scan = true; - chunkIt = 0; - } - inv.readFromNBT(nbt.getCompoundTag("inv")); for (int i = 0; i < selectedAreas.length; ++i) { diff --git a/common/buildcraft/robotics/gui/ContainerZonePlan.java b/common/buildcraft/robotics/gui/ContainerZonePlan.java index 2bbfe625..adad2380 100755 --- a/common/buildcraft/robotics/gui/ContainerZonePlan.java +++ b/common/buildcraft/robotics/gui/ContainerZonePlan.java @@ -18,6 +18,7 @@ import net.minecraft.inventory.Slot; import cpw.mods.fml.relauncher.Side; import buildcraft.BuildCraftCore; +import buildcraft.BuildCraftRobotics; import buildcraft.core.lib.render.DynamicTexturePaletted; import buildcraft.core.ZonePlan; import buildcraft.core.lib.gui.BuildCraftContainer; @@ -71,8 +72,7 @@ public class ContainerZonePlan extends BuildCraftContainer implements ICommandRe public void saveArea(final int index) { BuildCraftCore.instance.sendToServer(new PacketCommand(this, "saveArea", new CommandWriter() { - public void write(ByteBuf data) { - data.writeByte(index); + public void write(ByteBuf data) {data.writeByte(index); currentAreaSelection.writeData(data); } })); @@ -129,7 +129,8 @@ public class ContainerZonePlan extends BuildCraftContainer implements ICommandRe int iz = z - (map.chunkStartZ << 4); if (ix >= 0 && iz >= 0 && ix < TileZonePlan.RESOLUTION && iz < TileZonePlan.RESOLUTION) { - textureData[i + j * width] = map.colors[ix + iz * TileZonePlan.RESOLUTION]; + textureData[i + j * width] = (byte) BuildCraftRobotics.manager.getWorld(map.getWorldObj()) + .getColor(x, z); } } } diff --git a/common/buildcraft/robotics/map/MapChunk.java b/common/buildcraft/robotics/map/MapChunk.java new file mode 100644 index 00000000..3156e535 --- /dev/null +++ b/common/buildcraft/robotics/map/MapChunk.java @@ -0,0 +1,80 @@ +package buildcraft.robotics.map; + +import net.minecraft.block.material.MapColor; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.chunk.Chunk; +import buildcraft.api.core.BCLog; + +public class MapChunk { + private static final int VERSION = 1; + + private int x, z; + private byte[] data; + + public MapChunk(int x, int y) { + this.x = x; + this.z = z; + data = new byte[256]; + } + + public MapChunk(NBTTagCompound compound) { + readFromNBT(compound); + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + public int getColor(int x, int z) { + return (int) data[((z & 15) << 4) | (x & 15)]; + } + + public void update(Chunk chunk) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + int y = chunk.getHeightValue(x, z); + int color; + + while ((color = chunk.getBlock(x, y, z).getMapColor(0).colorIndex) == MapColor.airColor.colorIndex) { + y--; + if (y < 0) { + break; + } + } + + data[(z << 4) | x] = (byte) color; + } + } + } + + public void readFromNBT(NBTTagCompound compound) { + int version = compound.getShort("version"); + if (version > MapChunk.VERSION) { + BCLog.logger.error("Unsupported MapChunk version: " + version); + return; + } + x = compound.getInteger("x"); + z = compound.getInteger("z"); + data = compound.getByteArray("data"); + if (data.length != 256) { + BCLog.logger.error("Invalid MapChunk data length: " + data.length); + data = new byte[256]; + } + } + + public void writeToNBT(NBTTagCompound compound) { + compound.setShort("version", (short) VERSION); + compound.setInteger("x", x); + compound.setInteger("z", z); + compound.setByteArray("data", data); + } + + @Override + public int hashCode() { + return 31 * x + z; + } +} diff --git a/common/buildcraft/robotics/map/MapManager.java b/common/buildcraft/robotics/map/MapManager.java new file mode 100644 index 00000000..c5b22503 --- /dev/null +++ b/common/buildcraft/robotics/map/MapManager.java @@ -0,0 +1,94 @@ +package buildcraft.robotics.map; + +import java.io.File; +import java.util.Date; +import com.google.common.collect.HashBiMap; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.ChunkEvent; + +public class MapManager implements Runnable { + private final HashBiMap worldMap = HashBiMap.create(); + private final File location; + private boolean stop = false; + + private long lastSaveTime; + + public MapManager(File location) { + this.location = location; + } + + public void stop() { + stop = true; + } + + public MapWorld getWorld(World world) { + if (world.isRemote) { + return null; + } + + if (!worldMap.containsKey(world)) { + worldMap.put(world, new MapWorld(world, location)); + } + return worldMap.get(world); + } + + @SubscribeEvent + public void chunkLoaded(ChunkEvent.Load event) { + MapWorld world = getWorld(event.getChunk().worldObj); + if (world != null) { + world.queueChunkForUpdateIfEmpty(event.getChunk().xPosition, event.getChunk().zPosition, 99999); + } + } + + @SubscribeEvent + public void blockPlaced(BlockEvent.PlaceEvent placeEvent) { + Chunk chunk = placeEvent.world.getChunkFromBlockCoords(placeEvent.x, placeEvent.z); + MapWorld world = getWorld(placeEvent.world); + if (world != null) { + world.queueChunkForUpdate(chunk.xPosition, chunk.zPosition, 512); + } + } + + @SubscribeEvent + public void blockBroken(BlockEvent.BreakEvent placeEvent) { + Chunk chunk = placeEvent.world.getChunkFromBlockCoords(placeEvent.x, placeEvent.z); + MapWorld world = getWorld(placeEvent.world); + if (world != null) { + world.queueChunkForUpdate(chunk.xPosition, chunk.zPosition, 512); + } + } + + + public void saveAllWorlds() { + for (MapWorld world : worldMap.values()) { + world.save(); + } + } + + @Override + public void run() { + lastSaveTime = (new Date()).getTime(); + + while (!stop) { + for (MapWorld world : worldMap.values()) { + world.updateChunkInQueue(); + } + + long now = (new Date()).getTime(); + + if (now - lastSaveTime > 120000) { + saveAllWorlds(); + lastSaveTime = now; + } + + try { + Thread.sleep(50 * worldMap.size()); + } catch (Exception e) { + + } + } + } +} diff --git a/common/buildcraft/robotics/map/MapRegion.java b/common/buildcraft/robotics/map/MapRegion.java new file mode 100644 index 00000000..66dc6ea6 --- /dev/null +++ b/common/buildcraft/robotics/map/MapRegion.java @@ -0,0 +1,62 @@ +package buildcraft.robotics.map; + +import gnu.trove.map.hash.TIntObjectHashMap; + +import net.minecraft.nbt.NBTTagCompound; +import buildcraft.api.core.INBTStoreable; + +public class MapRegion implements INBTStoreable { + private final TIntObjectHashMap chunks = new TIntObjectHashMap(); + private final int x, z; + + public MapRegion(int x, int z) { + this.x = x; + this.z = z; + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + public boolean hasChunk(int x, int z) { + return chunks.contains((z << 4) | x); + } + + public MapChunk getChunk(int x, int z) { + int id = (z << 4) | x; + MapChunk chunk = chunks.get(id); + if (chunk == null) { + chunk = new MapChunk(x, z); + chunks.put(id, chunk); + } + return chunk; + } + + @Override + public void readFromNBT(NBTTagCompound tag) { + chunks.clear(); + + for (int i = 0; i < 256; i++) { + if (tag.hasKey("r" + i)) { + MapChunk chunk = new MapChunk(tag.getCompoundTag("r" + i)); + chunks.put(i, chunk); + } + } + } + + @Override + public void writeToNBT(NBTTagCompound tag) { + for (int i = 0; i < 256; i++) { + MapChunk chunk = chunks.get(i); + if (chunk != null) { + NBTTagCompound chunkNBT = new NBTTagCompound(); + chunk.writeToNBT(chunkNBT); + tag.setTag("r" + i, chunkNBT); + } + } + } +} diff --git a/common/buildcraft/robotics/map/MapWorld.java b/common/buildcraft/robotics/map/MapWorld.java new file mode 100644 index 00000000..8c47271c --- /dev/null +++ b/common/buildcraft/robotics/map/MapWorld.java @@ -0,0 +1,193 @@ +package buildcraft.robotics.map; + +import gnu.trove.iterator.TLongIterator; +import gnu.trove.map.hash.TLongObjectHashMap; +import gnu.trove.set.hash.TLongHashSet; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import buildcraft.core.lib.utils.NBTUtils; + +public class MapWorld { + private final World world; + private final TLongObjectHashMap regionMap; + private final Set regionUpdateSet = new HashSet(); + private final Queue queuedChunks; + private final File location; + + private long lastForcedChunkLoad; + + private class QueuedXZ { + int x, z, p; + + QueuedXZ(int x, int z, int p) { + this.x = x; + this.z = z; + this.p = p; + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof QueuedXZ)) { + return false; + } + return ((QueuedXZ) other).x == x && ((QueuedXZ) other).z == z; + } + + @Override + public int hashCode() { + return x * 31 + z; + } + } + + public MapWorld(World world, File location) { + this.world = world; + regionMap = new TLongObjectHashMap(); + queuedChunks = new PriorityQueue(new Comparator() { + @Override + public int compare(QueuedXZ c1, QueuedXZ c2) { + return c1.p - c2.p; + } + }); + + String saveFolder = world.provider.getSaveFolder(); + if (saveFolder == null) { + saveFolder = "world"; + } + this.location = new File(location, saveFolder); + try { + this.location.mkdirs(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private long getXzId(int x, int z) { + return (x << 24) | z; + } + + private MapRegion getRegion(int x, int z) { + long id = getXzId(x, z); + MapRegion region = regionMap.get(id); + if (region == null) { + region = new MapRegion(x, z); + + // Check in the location first + File target = new File(location, "r" + x + "," + z + ".nbt"); + if (target.exists()) { + try { + FileInputStream f = new FileInputStream(target); + byte [] data = new byte [(int) target.length()]; + f.read(data); + f.close(); + + region.readFromNBT(NBTUtils.load(data)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + regionMap.put(id, region); + } + return region; + } + + private MapChunk getChunk(int x, int z) { + MapRegion region = getRegion(x >> 4, z >> 4); + return region.getChunk(x & 15, z & 15); + } + + public boolean hasChunk(int x, int z) { + MapRegion region = getRegion(x >> 4, z >> 4); + return region.hasChunk(x & 15, z & 15); + } + + public void queueChunkForUpdate(int x, int z, int priority) { + long id = getXzId(x, z); + queuedChunks.add(new QueuedXZ(x, z, priority)); + } + + public void queueChunkForUpdateIfEmpty(int x, int z, int priority) { + if (!hasChunk(x, z)) { + queueChunkForUpdate(x, z, priority); + } + } + + public void updateChunkInQueue() { + if (queuedChunks.size() == 0) { + return; + } + + QueuedXZ q = queuedChunks.remove(); + if (q == null) { + return; + } + + if (!world.getChunkProvider().chunkExists(q.x, q.z)) { + long now = (new Date()).getTime(); + if (now - lastForcedChunkLoad < 5000) { + q.p++; // Increase priority so it gets looked at later + queuedChunks.add(q); + return; + } else { + lastForcedChunkLoad = now; + } + } + + updateChunk(q.x, q.z); + } + + public void save() { + Iterator i = regionUpdateSet.iterator(); + + while (i.hasNext()) { + QueuedXZ id = i.next(); + i.remove(); + MapRegion region = regionMap.get(getXzId(id.x, id.z)); + if (region == null) { + continue; + } + + NBTTagCompound output = new NBTTagCompound(); + region.writeToNBT(output); + byte[] data = NBTUtils.save(output); + File file = new File(location, "r" + id.x + "," + id.z + ".nbt"); + + try { + FileOutputStream f = new FileOutputStream(file); + f.write(data); + f.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public int getColor(int x, int z) { + MapChunk chunk = getChunk(x >> 4, z >> 4); + return chunk.getColor(x & 15, z & 15); + } + + protected void updateChunk(int x, int z) { + MapChunk chunk = getChunk(x, z); + chunk.update(world.getChunkFromChunkCoords(x, z)); + regionUpdateSet.add(new QueuedXZ(x >> 4, z >> 4, 0)); + + // priority does not matter - see equals + queuedChunks.remove(new QueuedXZ(x, z, 0)); + } +}