diff --git a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java index 6f1f59cfd..94f517dfd 100644 --- a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java @@ -59,16 +59,16 @@ public class BlockListener implements Listener { BlockFace direction = event.getDirection(); for (Block b : blocks) { - if (b.hasMetadata("mcmmoPlacedBlock")) { - b.getRelative(direction).setMetadata("mcmmoNeedsTracking", new FixedMetadataValue(plugin, true)); - b.removeMetadata("mcmmoPlacedBlock", plugin); + if (mcMMO.placeStore.isTrue(b)) { + mcMMO.placeStore.setTrue(b.getRelative(direction)); + mcMMO.placeStore.setFalse(b); } } for (Block b : blocks) { - if (b.getRelative(direction).hasMetadata("mcmmoNeedsTracking")) { - b.getRelative(direction).setMetadata("mcmmoPlacedBlock", new FixedMetadataValue(plugin, true)); - b.getRelative(direction).removeMetadata("mcmmoNeedsTracking", plugin); + if (mcMMO.placeStore.isTrue(b.getRelative(direction))) { + mcMMO.placeStore.setTrue(b.getRelative(direction)); + mcMMO.placeStore.setFalse(b); } } } @@ -82,9 +82,9 @@ public class BlockListener implements Listener { public void onBlockPistonRetract(BlockPistonRetractEvent event) { Block block = event.getRetractLocation().getBlock(); - if (block.hasMetadata("mcmmoPlacedBlock")) { - block.removeMetadata("mcmmoPlacedBlock", plugin); - event.getBlock().getRelative(event.getDirection()).setMetadata("mcmmoPlacedBlock", new FixedMetadataValue(plugin, true)); + if (mcMMO.placeStore.isTrue(block)) { + mcMMO.placeStore.setFalse(block); + mcMMO.placeStore.setTrue(event.getBlock().getRelative(event.getDirection())); } } @@ -108,7 +108,7 @@ public class BlockListener implements Listener { } else { Block newLocation = block.getRelative(0, y + 1, 0); - newLocation.setMetadata("mcmmoPlacedBlock", new FixedMetadataValue(plugin, true)); + mcMMO.placeStore.setTrue(newLocation); break; } } @@ -116,7 +116,7 @@ public class BlockListener implements Listener { /* Check if the blocks placed should be monitored so they do not give out XP in the future */ if (BlockChecks.shouldBeWatched(mat)) { - block.setMetadata("mcmmoPlacedBlock", new FixedMetadataValue(plugin, true)); + mcMMO.placeStore.setTrue(block); } if (id == Config.getInstance().getRepairAnvilId() && Config.getInstance().getRepairAnvilMessagesEnabled()) { @@ -193,7 +193,7 @@ public class BlockListener implements Listener { * EXCAVATION */ - if (BlockChecks.canBeGigaDrillBroken(mat) && Permissions.getInstance().excavation(player) && !block.hasMetadata("mcmmoPlacedBlock")) { + if (BlockChecks.canBeGigaDrillBroken(mat) && Permissions.getInstance().excavation(player) && !mcMMO.placeStore.isTrue(block)) { if (Config.getInstance().getExcavationRequiresTool() && ItemChecks.isShovel(inhand)) { Excavation.excavationProcCheck(block, player); } @@ -203,8 +203,8 @@ public class BlockListener implements Listener { } //Remove metadata when broken - if (block.hasMetadata("mcmmoPlacedBlock") && BlockChecks.shouldBeWatched(mat)) { - block.removeMetadata("mcmmoPlacedBlock", plugin); + if (mcMMO.placeStore.isTrue(block) && BlockChecks.shouldBeWatched(mat)) { + mcMMO.placeStore.setFalse(block); } } diff --git a/src/main/java/com/gmail/nossr50/listeners/WorldListener.java b/src/main/java/com/gmail/nossr50/listeners/WorldListener.java new file mode 100644 index 000000000..507ceeea4 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/listeners/WorldListener.java @@ -0,0 +1,43 @@ +package com.gmail.nossr50.listeners; + +import java.io.File; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldSaveEvent; +import org.bukkit.event.world.WorldUnloadEvent; + +import com.gmail.nossr50.mcMMO; + +public class WorldListener implements Listener { + @EventHandler + public void onWorldLoad(WorldLoadEvent event) { + File dataDir = new File(event.getWorld().getWorldFolder(), "mcmmo_data"); + if(!dataDir.exists()) { + dataDir.mkdir(); + } + } + + @EventHandler + public void onWorldUnload(WorldUnloadEvent event) { + mcMMO.placeStore.unloadWorld(event.getWorld()); + } + + @EventHandler + public void onWorldSave(WorldSaveEvent event) { + mcMMO.placeStore.saveWorld(event.getWorld()); + } + + @EventHandler + public void onChunkLoad(ChunkLoadEvent event) { + mcMMO.placeStore.chunkLoaded(event.getChunk().getX(), event.getChunk().getZ(), event.getChunk().getWorld()); + } + + @EventHandler + public void onChunkUnload(ChunkUnloadEvent event) { + mcMMO.placeStore.chunkUnloaded(event.getChunk().getX(), event.getChunk().getZ(), event.getChunk().getWorld()); + } +} diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index acb177f8f..e61d5f938 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -14,10 +14,13 @@ import com.gmail.nossr50.util.Database; import com.gmail.nossr50.util.Leaderboard; import com.gmail.nossr50.util.Metrics; import com.gmail.nossr50.util.Users; +import com.gmail.nossr50.util.blockmeta.ChunkletManager; +import com.gmail.nossr50.util.blockmeta.HashChunkletManager; import com.gmail.nossr50.listeners.BlockListener; import com.gmail.nossr50.listeners.EntityListener; import com.gmail.nossr50.listeners.HardcoreListener; import com.gmail.nossr50.listeners.PlayerListener; +import com.gmail.nossr50.listeners.WorldListener; import com.gmail.nossr50.locale.LocaleLoader; import net.shatteredlands.shatt.backup.ZipLibrary; @@ -41,6 +44,7 @@ public class mcMMO extends JavaPlugin { private final PlayerListener playerListener = new PlayerListener(this); private final BlockListener blockListener = new BlockListener(this); private final EntityListener entityListener = new EntityListener(this); + private final WorldListener worldListener = new WorldListener(); private final HardcoreListener hardcoreListener = new HardcoreListener(); public HashMap aliasMap = new HashMap(); //Alias - Command @@ -50,6 +54,8 @@ public class mcMMO extends JavaPlugin { public static Database database; public static mcMMO p; + public static ChunkletManager placeStore = new HashChunkletManager(); + /* Jar Stuff */ public File mcmmo; @@ -84,6 +90,7 @@ public class mcMMO extends JavaPlugin { pm.registerEvents(playerListener, this); pm.registerEvents(blockListener, this); pm.registerEvents(entityListener, this); + pm.registerEvents(worldListener, this); if (configInstance.getHardcoreEnabled()) { pm.registerEvents(hardcoreListener, this); @@ -186,6 +193,12 @@ public class mcMMO extends JavaPlugin { getServer().getScheduler().cancelTasks(this); //This removes our tasks + //Save our metadata + placeStore.saveAll(); + + //Cleanup empty metadata stores + placeStore.cleanUp(); + //Remove other tasks BEFORE starting the Backup, or we just cancel it straight away. try { ZipLibrary.mcMMObackup(); diff --git a/src/main/java/com/gmail/nossr50/skills/gathering/BlastMining.java b/src/main/java/com/gmail/nossr50/skills/gathering/BlastMining.java index bb5518028..5eaff7324 100644 --- a/src/main/java/com/gmail/nossr50/skills/gathering/BlastMining.java +++ b/src/main/java/com/gmail/nossr50/skills/gathering/BlastMining.java @@ -52,7 +52,7 @@ public class BlastMining { blocksDropped.add(temp); Mining.miningDrops(temp); - if (!temp.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(temp)) { for (int i = 1 ; i < extraDrops ; i++) { blocksDropped.add(temp); Mining.miningDrops(temp); @@ -159,7 +159,7 @@ public class BlastMining { } for (Block block : xp) { - if (!block.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(block)) { Mining.miningXP(player, block); } } diff --git a/src/main/java/com/gmail/nossr50/skills/gathering/Excavation.java b/src/main/java/com/gmail/nossr50/skills/gathering/Excavation.java index 2150069b3..34a418c83 100644 --- a/src/main/java/com/gmail/nossr50/skills/gathering/Excavation.java +++ b/src/main/java/com/gmail/nossr50/skills/gathering/Excavation.java @@ -112,7 +112,7 @@ public class Excavation { public static void gigaDrillBreaker(Player player, Block block) { Skills.abilityDurabilityLoss(player.getItemInHand(), Config.getInstance().getAbilityToolDamage()); - if (!block.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(block)) { FakePlayerAnimationEvent armswing = new FakePlayerAnimationEvent(player); mcMMO.p.getServer().getPluginManager().callEvent(armswing); diff --git a/src/main/java/com/gmail/nossr50/skills/gathering/Herbalism.java b/src/main/java/com/gmail/nossr50/skills/gathering/Herbalism.java index 14b78c866..0fb285e05 100644 --- a/src/main/java/com/gmail/nossr50/skills/gathering/Herbalism.java +++ b/src/main/java/com/gmail/nossr50/skills/gathering/Herbalism.java @@ -89,7 +89,7 @@ public class Herbalism { switch (type) { case BROWN_MUSHROOM: case RED_MUSHROOM: - if (!block.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(block)) { mat = Material.getMaterial(id); xp = Config.getInstance().getHerbalismXPMushrooms(); } @@ -100,7 +100,7 @@ public class Herbalism { Block b = block.getRelative(0, y, 0); if (b.getType().equals(Material.CACTUS)) { mat = Material.CACTUS; - if (!b.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(b)) { if (herbLevel > MAX_BONUS_LEVEL || random.nextInt(1000) <= herbLevel) { catciDrops++; } @@ -122,7 +122,7 @@ public class Herbalism { break; case MELON_BLOCK: - if (!block.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(block)) { mat = Material.MELON; xp = Config.getInstance().getHerbalismXPMelon(); } @@ -137,7 +137,7 @@ public class Herbalism { case PUMPKIN: case JACK_O_LANTERN: - if (!block.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(block)) { mat = Material.getMaterial(id); xp = Config.getInstance().getHerbalismXPPumpkin(); } @@ -145,7 +145,7 @@ public class Herbalism { case RED_ROSE: case YELLOW_FLOWER: - if (!block.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(block)) { mat = Material.getMaterial(id); xp = Config.getInstance().getHerbalismXPFlowers(); } @@ -156,7 +156,7 @@ public class Herbalism { Block b = block.getRelative(0, y, 0); if (b.getType().equals(Material.SUGAR_CANE_BLOCK)) { mat = Material.SUGAR_CANE; - if (!b.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(b)) { if (herbLevel > MAX_BONUS_LEVEL || random.nextInt(1000) <= herbLevel) { caneDrops++; } @@ -167,14 +167,14 @@ public class Herbalism { break; case VINE: - if (!block.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(block)) { mat = type; xp = Config.getInstance().getHerbalismXPVines(); } break; case WATER_LILY: - if (!block.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(block)) { mat = type; xp = Config.getInstance().getHerbalismXPLilyPads(); } diff --git a/src/main/java/com/gmail/nossr50/skills/gathering/Mining.java b/src/main/java/com/gmail/nossr50/skills/gathering/Mining.java index 7e7dc6c05..524f93410 100644 --- a/src/main/java/com/gmail/nossr50/skills/gathering/Mining.java +++ b/src/main/java/com/gmail/nossr50/skills/gathering/Mining.java @@ -212,7 +212,7 @@ public class Mining { * @param block The block being broken */ public static void miningBlockCheck(Player player, Block block) { - if (block.hasMetadata("mcmmoPlacedBlock") || player.getItemInHand().containsEnchantment(Enchantment.SILK_TOUCH)) { + if (mcMMO.placeStore.isTrue(block) || player.getItemInHand().containsEnchantment(Enchantment.SILK_TOUCH)) { return; } @@ -272,7 +272,7 @@ public class Mining { case NETHERRACK: case SANDSTONE: case STONE: - if (block.hasMetadata("mcmmoPlacedBlock")) { + if (mcMMO.placeStore.isTrue(block)) { return; } diff --git a/src/main/java/com/gmail/nossr50/skills/gathering/WoodCutting.java b/src/main/java/com/gmail/nossr50/skills/gathering/WoodCutting.java index ca2c52e23..48d6e5329 100644 --- a/src/main/java/com/gmail/nossr50/skills/gathering/WoodCutting.java +++ b/src/main/java/com/gmail/nossr50/skills/gathering/WoodCutting.java @@ -114,7 +114,7 @@ public class WoodCutting { break; } - if (!x.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(x)) { WoodCutting.woodCuttingProcCheck(player, x); switch (species) { @@ -188,7 +188,7 @@ public class WoodCutting { Block zNegative = currentBlock.getRelative(0, 0, -1); Block yPositive = currentBlock.getRelative(0, 1, 0); - if (!currentBlock.hasMetadata("mcmmoPlacedBlock")) { + if (!mcMMO.placeStore.isTrue(currentBlock)) { if (!isTooAggressive(currentBlock, xPositive) && BlockChecks.treeFellerCompatible(xPositive.getType()) && !toBeFelled.contains(xPositive)) { processTreeFelling(xPositive, toBeFelled); } @@ -207,7 +207,7 @@ public class WoodCutting { } if (BlockChecks.treeFellerCompatible(yPositive.getType())) { - if(!currentBlock.hasMetadata("mcmmoPlacedBlock") && !toBeFelled.contains(yPositive)) { + if(!mcMMO.placeStore.isTrue(currentBlock) && !toBeFelled.contains(yPositive)) { processTreeFelling(yPositive, toBeFelled); } } @@ -296,7 +296,7 @@ public class WoodCutting { int xp = 0; TreeSpecies species = TreeSpecies.getByData(block.getData()); - if (block.hasMetadata("mcmmoPlacedBlock")) { + if (mcMMO.placeStore.isTrue(block)) { return; } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletManager.java new file mode 100644 index 000000000..267af41ac --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletManager.java @@ -0,0 +1,106 @@ +package com.gmail.nossr50.util.blockmeta; + +import org.bukkit.World; +import org.bukkit.block.Block; + +public interface ChunkletManager { + /** + * Informs the ChunkletManager a chunk is loaded, it should load appropriate data + * + * @param cx Chunk X coordiate that is loaded + * @param cz Chunk Z coordiate that is loaded + * @param world World that the chunk was loaded in + */ + public void chunkLoaded(int cx, int cz, World world); + + /** + * Informs the ChunkletManager a chunk is unloaded, it should unload and save appropriate data + * + * @param cx Chunk X coordiate that is unloaded + * @param cz Chunk Z coordiate that is unloaded + * @param world World that the chunk was unloaded in + */ + public void chunkUnloaded(int cx, int cz, World world); + + /** + * Save all ChunkletStores related to the given world + * + * @param world World to save + */ + public void saveWorld(World world); + + /** + * Unload all ChunkletStores from memory related to the given world after saving them + * + * @param world World to unload + */ + public void unloadWorld(World world); + + /** + * Save all ChunkletStores + */ + public void saveAll(); + + /** + * Unload all ChunkletStores after saving them + */ + public void unloadAll(); + + /** + * Check to see if a given location is set to true + * + * @param x X coordinate to check + * @param y Y coordinate to check + * @param z Z coordinate to check + * @param world World to check in + * @return true if the given location is set to true, false if otherwise + */ + public boolean isTrue(int x, int y, int z, World world); + + /** + * Check to see if a given block location is set to true + * + * @param block Block location to check + * @return true if the given block location is set to true, false if otherwise + */ + public boolean isTrue(Block block); + + /** + * Set a given location to true, should create stores as necessary if the location does not exist + * + * @param x X coordinate to set + * @param y Y coordinate to set + * @param z Z coordinate to set + * @param world World to set in + */ + public void setTrue(int x, int y, int z, World world); + + /** + * Set a given block location to true, should create stores as necessary if the location does not exist + * + * @param block Block location to set + */ + public void setTrue(Block block); + + /** + * Set a given location to false, should not create stores if one does not exist for the given location + * + * @param x X coordinate to set + * @param y Y coordinate to set + * @param z Z coordinate to set + * @param world World to set in + */ + public void setFalse(int x, int y, int z, World world); + + /** + * Set a given block location to false, should not create stores if one does not exist for the given location + * + * @param block Block location to set + */ + public void setFalse(Block block); + + /** + * Delete any ChunkletStores that are empty + */ + public void cleanUp(); +} diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStore.java new file mode 100644 index 000000000..fb8b0536a --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStore.java @@ -0,0 +1,36 @@ +package com.gmail.nossr50.util.blockmeta; + +import java.io.Serializable; + +public interface ChunkletStore extends Serializable { + /** + * @param x x coordinate in current chunklet + * @param y y coordinate in current chunklet + * @param z z coordinate in current chunklet + * @return true if the value is true at the given coordinates, false if otherwise + */ + public boolean isTrue(int x, int y, int z); + + /** + * Set the value to true at the given coordinates + * + * @param x x coordinate in current chunklet + * @param y y coordinate in current chunklet + * @param z z coordinate in current chunklet + */ + public void setTrue(int x, int y, int z); + + /** + * Set the value to false at the given coordinates + * + * @param x x coordinate in current chunklet + * @param y y coordinate in current chunklet + * @param z z coordinate in current chunklet + */ + public void setFalse(int x, int y, int z); + + /** + * @return true if all values in the chunklet are false, false if otherwise + */ + public boolean isEmpty(); +} diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkletManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkletManager.java new file mode 100644 index 000000000..ee225be73 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkletManager.java @@ -0,0 +1,227 @@ +package com.gmail.nossr50.util.blockmeta; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.block.Block; + +public class HashChunkletManager implements ChunkletManager { + private HashMap store = new HashMap(); + + public void chunkLoaded(int cx, int cz, World world) { + File dataDir = new File(world.getWorldFolder(), "mcmmo_data"); + File cxDir = new File(dataDir, "" + cx); + if(!cxDir.exists()) return; + File czDir = new File(cxDir, "" + cz); + if(!czDir.exists()) return; + + for(int y = 1; y <= 4; y++) { + File yFile = new File(czDir, "" + y); + if(!yFile.exists()) { + continue; + } else { + ChunkletStore in = deserializeChunkletStore(yFile); + if(in != null) { + store.put(world.getName() + "," + cx + "," + cz + "," + y, in); + } + } + } + } + + public void chunkUnloaded(int cx, int cz, World world) { + boolean found = false; + + for(String key : store.keySet()) { + if(key.startsWith(world.getName() + "," + cx + "," + cz)) found = true; + } + + if(!found) return; + + File dataDir = new File(world.getWorldFolder(), "mcmmo_data"); + File cxDir = new File(dataDir, "" + cx); + if(!cxDir.exists()) cxDir.mkdir(); + File czDir = new File(cxDir, "" + cz); + if(!czDir.exists()) czDir.mkdir(); + + for(int y = 1; y <= 4; y++) { + File yFile = new File(czDir, "" + y); + if(store.containsKey(world.getName() + "," + cx + "," + cz + "," + y)) { + ChunkletStore out = store.get(world.getName() + "," + cx + "," + cz + "," + y); + serializeChunkletStore(out, yFile); + } + } + } + + public void saveWorld(World world) { + String worldName = world.getName(); + File dataDir = new File(world.getWorldFolder(), "mcmmo_data"); + + for(String key : store.keySet()) { + String[] info = key.split(","); + if(worldName.equals(info[0])) { + File cxDir = new File(dataDir, "" + info[1]); + if(!cxDir.exists()) cxDir.mkdir(); + File czDir = new File(cxDir, "" + info[2]); + if(!czDir.exists()) czDir.mkdir(); + + File yFile = new File(czDir, "" + info[3]); + serializeChunkletStore(store.get(key), yFile); + } + } + } + + public void unloadWorld(World world) { + saveWorld(world); + + String worldName = world.getName(); + + for(String key : store.keySet()) { + String tempWorldName = key.split(",")[0]; + if(tempWorldName.equals(worldName)) { + store.remove(key); + } + } + } + + public void saveAll() { + for(World world : Bukkit.getWorlds()) { + saveWorld(world); + } + } + + public void unloadAll() { + saveAll(); + for(World world : Bukkit.getWorlds()) { + unloadWorld(world); + } + } + + public boolean isTrue(int x, int y, int z, World world) { + int cx = x / 16; + int cz = z / 16; + int cy = y / 64; + if(!store.containsKey(world.getName() + "," + cx + "," + cz + "," + cy)) return false; + + ChunkletStore check = store.get(world.getName() + "," + cx + "," + cz + "," + cy); + int ix = Math.abs(x) % 16; + int iz = Math.abs(z) % 16; + int iy = Math.abs(y) % 64; + + return check.isTrue(ix, iy, iz); + } + + public boolean isTrue(Block block) { + return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld()); + } + + public void setTrue(int x, int y, int z, World world) { + int cx = x / 16; + int cz = z / 16; + int cy = y / 64; + + int ix = Math.abs(x) % 16; + int iz = Math.abs(z) % 16; + int iy = Math.abs(y) % 64; + + ChunkletStore cStore; + if(!store.containsKey(world.getName() + "," + cx + "," + cz + "," + cy)) { + cStore = new PrimitiveChunkletStore(); + store.put(world.getName() + "," + cx + "," + cz + "," + cy, cStore); + } + + cStore = store.get(world.getName() + "," + cx + "," + cz + "," + cy); + cStore.setTrue(ix, iy, iz); + } + + public void setTrue(Block block) { + setTrue(block.getX(), block.getY(), block.getZ(), block.getWorld()); + } + + public void setFalse(int x, int y, int z, World world) { + int cx = x / 16; + int cz = z / 16; + int cy = y / 64; + + int ix = Math.abs(x) % 16; + int iz = Math.abs(z) % 16; + int iy = Math.abs(y) % 64; + + ChunkletStore cStore; + if(!store.containsKey(world.getName() + "," + cx + "," + cz + "," + cy)) { + return; // No need to make a store for something we will be setting to false + } + + cStore = store.get(world.getName() + "," + cx + "," + cz + "," + cy); + cStore.setFalse(ix, iy, iz); + } + + public void setFalse(Block block) { + setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld()); + } + + public void cleanUp() { + for(String key : store.keySet()) { + if(store.get(key).isEmpty()) { + String[] info = key.split(","); + File dataDir = new File(Bukkit.getWorld(info[0]).getWorldFolder(), "mcmmo_data"); + + File cxDir = new File(dataDir, "" + info[1]); + if(!cxDir.exists()) continue; + File czDir = new File(cxDir, "" + info[2]); + if(!czDir.exists()) continue; + + File yFile = new File(czDir, "" + info[3]); + yFile.delete(); + + //Delete empty directories + if(czDir.list().length == 0) czDir.delete(); + if(cxDir.list().length == 0) cxDir.delete(); + } + } + } + + /** + * @param cStore ChunkletStore to save + * @param location Where on the disk to put it + */ + private void serializeChunkletStore(ChunkletStore cStore, File location) { + try { + FileOutputStream fileOut = new FileOutputStream(location); + ObjectOutputStream objOut = new ObjectOutputStream(fileOut); + objOut.writeObject(cStore); + objOut.close(); + fileOut.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /** + * @param location Where on the disk to read from + * @return ChunkletStore from the specified location + */ + private ChunkletStore deserializeChunkletStore(File location) { + ChunkletStore storeIn = null; + + try { + FileInputStream fileIn = new FileInputStream(location); + ObjectInputStream objIn = new ObjectInputStream(fileIn); + storeIn = (ChunkletStore) objIn.readObject(); + objIn.close(); + fileIn.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } catch (ClassNotFoundException ex) { + ex.printStackTrace(); + } + + return storeIn; + } +} diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveChunkletStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveChunkletStore.java new file mode 100644 index 000000000..64d9492f0 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveChunkletStore.java @@ -0,0 +1,31 @@ +package com.gmail.nossr50.util.blockmeta; + +public class PrimitiveChunkletStore implements ChunkletStore { + private static final long serialVersionUID = -3453078050608607478L; + + /** X, Z, Y */ + private boolean[][][] store = new boolean[16][16][64]; + + public boolean isTrue(int x, int y, int z) { + return store[x][z][y]; + } + + public void setTrue(int x, int y, int z) { + store[x][z][y] = true; + } + + public void setFalse(int x, int y, int z) { + store[x][z][y] = false; + } + + public boolean isEmpty() { + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + for(int y = 0; y < 64; y++) { + if(store[x][z][y]) return false; + } + } + } + return true; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index b0cb57881..e40665b87 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -8,6 +8,7 @@ website: http://forums.bukkit.org/threads/rpg-mech-mcmmo-v1-3-05-rpg-addiction-r main: com.gmail.nossr50.mcMMO softdepend: [Spout] +load: STARTUP commands: mchud: