|
@@ -0,0 +1,374 @@
|
|
|
|
+package com.gmail.nossr50.util.blockmeta;
|
|
|
|
+
|
|
|
|
+import java.io.File;
|
|
|
|
+import java.io.InputStream;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.io.ObjectInputStream;
|
|
|
|
+import java.io.ObjectOutputStream;
|
|
|
|
+import java.io.OutputStream;
|
|
|
|
+import java.lang.Integer;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.Iterator;
|
|
|
|
+import java.util.UUID;
|
|
|
|
+
|
|
|
|
+import org.bukkit.Bukkit;
|
|
|
|
+import org.bukkit.World;
|
|
|
|
+import org.bukkit.block.Block;
|
|
|
|
+
|
|
|
|
+import com.gmail.nossr50.mcMMO;
|
|
|
|
+import com.gmail.nossr50.runnables.ChunkletUnloader;
|
|
|
|
+
|
|
|
|
+import org.getspout.spoutapi.chunkstore.SimpleRegionFile;
|
|
|
|
+
|
|
|
|
+public class HashChunkManager implements ChunkManager {
|
|
|
|
+ private HashMap<UUID, HashMap<Long, SimpleRegionFile>> regionFiles = new HashMap<UUID, HashMap<Long, SimpleRegionFile>>();
|
|
|
|
+ public HashMap<String, ChunkStore> store = new HashMap<String, ChunkStore>();
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void closeAll() {
|
|
|
|
+ for (UUID uid : regionFiles.keySet()) {
|
|
|
|
+ HashMap<Long, SimpleRegionFile> worldRegions = regionFiles.get(uid);
|
|
|
|
+ Iterator<SimpleRegionFile> itr = worldRegions.values().iterator();
|
|
|
|
+ while (itr.hasNext()) {
|
|
|
|
+ SimpleRegionFile rf = itr.next();
|
|
|
|
+ if (rf != null) {
|
|
|
|
+ rf.close();
|
|
|
|
+ itr.remove();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ regionFiles.clear();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public ChunkStore readChunkStore(World world, int x, int z) throws IOException {
|
|
|
|
+ SimpleRegionFile rf = getSimpleRegionFile(world, x, z);
|
|
|
|
+ InputStream in = rf.getInputStream(x, z);
|
|
|
|
+ if (in == null) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ ObjectInputStream objectStream = new ObjectInputStream(in);
|
|
|
|
+ try {
|
|
|
|
+ Object o = objectStream.readObject();
|
|
|
|
+ if (o instanceof ChunkStore) {
|
|
|
|
+ return (ChunkStore) o;
|
|
|
|
+ } else {
|
|
|
|
+ throw new RuntimeException("Wrong class type read for chunk meta data for " + x + ", " + z);
|
|
|
|
+ }
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ // Assume the format changed
|
|
|
|
+ return null;
|
|
|
|
+ //throw new RuntimeException("Unable to process chunk meta data for " + x + ", " + z, e);
|
|
|
|
+ } catch (ClassNotFoundException e) {
|
|
|
|
+ // Assume the format changed
|
|
|
|
+ //System.out.println("[SpoutPlugin] is Unable to find serialized class for " + x + ", " + z + ", " + e.getMessage());
|
|
|
|
+ return null;
|
|
|
|
+ //throw new RuntimeException("Unable to find serialized class for " + x + ", " + z, e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void writeChunkStore(World world, int x, int z, ChunkStore data) {
|
|
|
|
+ if (!data.isDirty()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ try {
|
|
|
|
+ SimpleRegionFile rf = getSimpleRegionFile(world, x, z);
|
|
|
|
+ ObjectOutputStream objectStream = new ObjectOutputStream(rf.getOutputStream(x, z));
|
|
|
|
+ objectStream.writeObject(data);
|
|
|
|
+ objectStream.flush();
|
|
|
|
+ objectStream.close();
|
|
|
|
+ data.setDirty(false);
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ throw new RuntimeException("Unable to write chunk meta data for " + x + ", " + z, e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void closeChunkStore(World world, int x, int z) {
|
|
|
|
+ SimpleRegionFile rf = getSimpleRegionFile(world, x, z);
|
|
|
|
+ if (rf != null) {
|
|
|
|
+ rf.close();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private SimpleRegionFile getSimpleRegionFile(World world, int x, int z) {
|
|
|
|
+ File directory = new File(world.getWorldFolder(), "mcmmo_regions");
|
|
|
|
+
|
|
|
|
+ directory.mkdirs();
|
|
|
|
+
|
|
|
|
+ UUID key = world.getUID();
|
|
|
|
+
|
|
|
|
+ HashMap<Long, SimpleRegionFile> worldRegions = regionFiles.get(key);
|
|
|
|
+
|
|
|
|
+ if (worldRegions == null) {
|
|
|
|
+ worldRegions = new HashMap<Long, SimpleRegionFile>();
|
|
|
|
+ regionFiles.put(key, worldRegions);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int rx = x >> 5;
|
|
|
|
+ int rz = z >> 5;
|
|
|
|
+
|
|
|
|
+ long key2 = (((long) rx) << 32) | (((long) rz) & 0xFFFFFFFFL);
|
|
|
|
+
|
|
|
|
+ SimpleRegionFile regionFile = worldRegions.get(key2);
|
|
|
|
+
|
|
|
|
+ if (regionFile == null) {
|
|
|
|
+ File file = new File(directory, "mcmmo_" + rx + "_" + rz + "_.mcm");
|
|
|
|
+ regionFile = new SimpleRegionFile(file, rx, rz);
|
|
|
|
+ worldRegions.put(key2, regionFile);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return regionFile;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void loadChunklet(int cx, int cy, int cz, World world) {
|
|
|
|
+ loadChunk(cx, cz, world);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void unloadChunklet(int cx, int cy, int cz, World world) {
|
|
|
|
+ unloadChunk(cx, cz, world);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void loadChunk(int cx, int cz, World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if(store.containsKey(world.getName() + "," + cx + "," + cz))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ ChunkStore in = null;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ in = readChunkStore(world, cx, cz);
|
|
|
|
+ }
|
|
|
|
+ catch(Exception e) {}
|
|
|
|
+
|
|
|
|
+ if(in != null) {
|
|
|
|
+ store.put(world.getName() + "," + cx + "," + cz, in);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void unloadChunk(int cx, int cz, World world) {
|
|
|
|
+ saveChunk(cx, cz, world);
|
|
|
|
+
|
|
|
|
+ if(store.containsKey(world.getName() + "," + cx + "," + cz)) {
|
|
|
|
+ store.remove(world.getName() + "," + cx + "," + cz);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void saveChunk(int cx, int cz, World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if(store.containsKey(world.getName() + "," + cx + "," + cz)) {
|
|
|
|
+ ChunkStore out = store.get(world.getName() + "," + cx + "," + cz);
|
|
|
|
+
|
|
|
|
+ if(!out.isDirty())
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ writeChunkStore(world, cx, cz, out);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean isChunkLoaded(int cx, int cz, World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return store.containsKey(world.getName() + "," + cx + "," + cz);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void chunkLoaded(int cx, int cz, World world) {}
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void chunkUnloaded(int cx, int cz, World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ ChunkletUnloader.addToList(cx, cx, world);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void saveWorld(World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ closeAll();
|
|
|
|
+ String worldName = world.getName();
|
|
|
|
+
|
|
|
|
+ for(String key : store.keySet()) {
|
|
|
|
+ String[] info = key.split(",");
|
|
|
|
+ if(worldName.equals(info[0])) {
|
|
|
|
+ int cx = 0;
|
|
|
|
+ int cz = 0;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ cx = Integer.parseInt(info[1]);
|
|
|
|
+ cz = Integer.parseInt(info[2]);
|
|
|
|
+ }
|
|
|
|
+ catch(Exception e) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ saveChunk(cx, cz, world);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void unloadWorld(World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ closeAll();
|
|
|
|
+ String worldName = world.getName();
|
|
|
|
+
|
|
|
|
+ for(String key : store.keySet()) {
|
|
|
|
+ String[] info = key.split(",");
|
|
|
|
+ if(worldName.equals(info[0])) {
|
|
|
|
+ int cx = 0;
|
|
|
|
+ int cz = 0;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ cx = Integer.parseInt(info[1]);
|
|
|
|
+ cz = Integer.parseInt(info[2]);
|
|
|
|
+ }
|
|
|
|
+ catch(Exception e) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ unloadChunk(cx, cz, world);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void loadWorld(World world) {}
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void saveAll() {
|
|
|
|
+ closeAll();
|
|
|
|
+
|
|
|
|
+ for(World world : Bukkit.getWorlds()) {
|
|
|
|
+ saveWorld(world);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void unloadAll() {
|
|
|
|
+ closeAll();
|
|
|
|
+
|
|
|
|
+ for(World world : Bukkit.getWorlds()) {
|
|
|
|
+ unloadWorld(world);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean isTrue(int x, int y, int z, World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ int cx = x / 16;
|
|
|
|
+ int cz = z / 16;
|
|
|
|
+ String key = world.getName() + "," + cx + "," + cz;
|
|
|
|
+
|
|
|
|
+ if (!store.containsKey(key)) {
|
|
|
|
+ loadChunk(cx, cz, world);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!store.containsKey(key)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ChunkStore check = store.get(key);
|
|
|
|
+ int ix = Math.abs(x) % 16;
|
|
|
|
+ int iz = Math.abs(z) % 16;
|
|
|
|
+
|
|
|
|
+ return check.isTrue(ix, y, iz);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean isTrue(Block block) {
|
|
|
|
+ if(block == null)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return isTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setTrue(int x, int y, int z, World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ int cx = x / 16;
|
|
|
|
+ int cz = z / 16;
|
|
|
|
+
|
|
|
|
+ int ix = Math.abs(x) % 16;
|
|
|
|
+ int iz = Math.abs(z) % 16;
|
|
|
|
+
|
|
|
|
+ String key = world.getName() + "," + cx + "," + cz;
|
|
|
|
+
|
|
|
|
+ if (!store.containsKey(key)) {
|
|
|
|
+ loadChunk(cx, cz, world);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ChunkStore cStore = store.get(key);
|
|
|
|
+
|
|
|
|
+ if (cStore == null) {
|
|
|
|
+ cStore = ChunkStoreFactory.getChunkStore(world, cx, cz);
|
|
|
|
+ store.put(key, cStore);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cStore.setTrue(ix, y, iz);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setTrue(Block block) {
|
|
|
|
+ if(block == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ setTrue(block.getX(), block.getY(), block.getZ(), block.getWorld());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setFalse(int x, int y, int z, World world) {
|
|
|
|
+ if(world == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ int cx = x / 16;
|
|
|
|
+ int cz = z / 16;
|
|
|
|
+
|
|
|
|
+ int ix = Math.abs(x) % 16;
|
|
|
|
+ int iz = Math.abs(z) % 16;
|
|
|
|
+
|
|
|
|
+ String key = world.getName() + "," + cx + "," + cz;
|
|
|
|
+
|
|
|
|
+ if (!store.containsKey(key)) {
|
|
|
|
+ loadChunk(cx, cz, world);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ChunkStore cStore = store.get(key);
|
|
|
|
+
|
|
|
|
+ if (cStore == null) {
|
|
|
|
+ return; //No need to make a store for something we will be setting to false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cStore.setFalse(ix, y, iz);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setFalse(Block block) {
|
|
|
|
+ if(block == null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ setFalse(block.getX(), block.getY(), block.getZ(), block.getWorld());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void cleanUp() {}
|
|
|
|
+}
|