Updating modified block storage to a region-style format.

This commit is contained in:
Glitchfinder
2012-11-04 13:36:57 -08:00
parent d532164cd6
commit eb5ac80714
19 changed files with 1669 additions and 11 deletions

View File

@ -9,6 +9,7 @@ public class HiddenConfig {
private static String fileName;
private static YamlConfiguration config;
private static boolean chunkletsEnabled;
private static int conversionRate;
public HiddenConfig(String fileName) {
HiddenConfig.fileName = fileName;
@ -27,10 +28,15 @@ public class HiddenConfig {
if (mcMMO.p.getResource(fileName) != null) {
config = YamlConfiguration.loadConfiguration(mcMMO.p.getResource(fileName));
chunkletsEnabled = config.getBoolean("Options.Chunklets", true);
conversionRate = config.getInt("Options.ConversionRate", 1);
}
}
public boolean getChunkletsEnabled() {
return chunkletsEnabled;
}
public int getConversionRate() {
return conversionRate;
}
}

View File

@ -1,23 +1,43 @@
package com.gmail.nossr50.listeners;
import java.io.File;
import java.util.ArrayList;
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.WorldInitEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.World;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.runnables.BlockStoreConversionMain;
import com.gmail.nossr50.util.blockmeta.ChunkletStore;
import com.gmail.nossr50.util.blockmeta.PrimitiveChunkletStore;
import com.gmail.nossr50.util.blockmeta.PrimitiveExChunkletStore;
import com.gmail.nossr50.util.blockmeta.PrimitiveChunkStore;
import com.gmail.nossr50.util.blockmeta.HashChunkletManager;
import com.gmail.nossr50.util.blockmeta.HashChunkManager;
public class WorldListener implements Listener {
ArrayList<BlockStoreConversionMain> converters = new ArrayList<BlockStoreConversionMain>();
@EventHandler
public void onWorldLoad(WorldLoadEvent event) {
public void onWorldInit(WorldInitEvent event) {
File dataDir = new File(event.getWorld().getWorldFolder(), "mcmmo_data");
if(!dataDir.exists()) {
dataDir.mkdir();
return;
}
if(mcMMO.p == null)
return;
mcMMO.p.getLogger().info("Converting block storage for " + event.getWorld().getName() + " to a new format.");
BlockStoreConversionMain converter = new BlockStoreConversionMain(event.getWorld());
converter.run();
converters.add(converter);
}
@EventHandler
@ -34,4 +54,98 @@ public class WorldListener implements Listener {
public void onChunkUnload(ChunkUnloadEvent event) {
mcMMO.placeStore.chunkUnloaded(event.getChunk().getX(), event.getChunk().getZ(), event.getWorld());
}
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
File dataDir = new File(event.getChunk().getWorld().getWorldFolder(), "mcmmo_data");
if(!dataDir.exists() || !dataDir.isDirectory()) {
return;
}
World world = event.getChunk().getWorld();
int cx = event.getChunk().getX();
int cz = event.getChunk().getZ();
HashChunkletManager manager = new HashChunkletManager();
HashChunkManager newManager = (HashChunkManager) mcMMO.p.placeStore;
manager.loadChunk(cx, cz, world);
for(int y = 0; y < 4; y++) {
String chunkletName = world.getName() + "," + cx + "," + cz + "," + y;
ChunkletStore tempChunklet = manager.store.get(chunkletName);
PrimitiveChunkletStore primitiveChunklet = null;
PrimitiveExChunkletStore primitiveExChunklet = null;
if(tempChunklet instanceof PrimitiveChunkletStore)
primitiveChunklet = (PrimitiveChunkletStore) tempChunklet;
else if(tempChunklet instanceof PrimitiveExChunkletStore)
primitiveExChunklet = (PrimitiveExChunkletStore) tempChunklet;
if(tempChunklet == null) {
continue;
} else {
String chunkName = world.getName() + "," + cx + "," + cz;
PrimitiveChunkStore cChunk = (PrimitiveChunkStore) newManager.store.get(chunkName);
if(cChunk != null) {
int xPos = cx * 16;
int zPos = cz * 16;
for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) {
int cxPos = xPos + x;
int czPos = zPos + z;
for(int y2 = (64 * y); y2 < (64 * y + 64); y2++) {
if(!manager.isTrue(cxPos, y2, czPos, world))
continue;
newManager.setTrue(cxPos, y2, czPos, world);
}
}
}
continue;
}
newManager.setTrue(cx * 16, 0, cz * 16, world);
newManager.setFalse(cx * 16, 0, cz * 16, world);
cChunk = (PrimitiveChunkStore) newManager.store.get(chunkName);
for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) {
boolean[] oldArray;
if(primitiveChunklet != null)
oldArray = primitiveChunklet.store[x][z];
if(primitiveExChunklet != null)
oldArray = primitiveExChunklet.store[x][z];
else
return;
boolean[] newArray = cChunk.store[x][z];
System.arraycopy(oldArray, 0, newArray, (y * 64), 64);
}
}
}
}
manager.unloadChunk(cx, cz, world);
newManager.unloadChunk(cx, cz, world);
File cxDir = new File(dataDir, "" + cx);
if(!cxDir.exists()) return;
File czDir = new File(cxDir, "" + cz);
if(!czDir.exists()) return;
for(File yFile : czDir.listFiles()) {
if(!yFile.exists())
continue;
yFile.delete();
}
if(czDir.listFiles().length <= 0)
czDir.delete();
if(cxDir.listFiles().length <= 0)
cxDir.delete();
if(dataDir.listFiles().length <= 0)
dataDir.delete();
}
}

View File

@ -14,6 +14,7 @@ import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.World;
import com.gmail.nossr50.commands.general.AddlevelsCommand;
import com.gmail.nossr50.commands.general.AddxpCommand;
@ -78,8 +79,9 @@ import com.gmail.nossr50.util.Leaderboard;
import com.gmail.nossr50.util.Metrics;
import com.gmail.nossr50.util.Metrics.Graph;
import com.gmail.nossr50.util.Users;
import com.gmail.nossr50.util.blockmeta.ChunkletManager;
import com.gmail.nossr50.util.blockmeta.ChunkletManagerFactory;
import com.gmail.nossr50.util.blockmeta.ChunkManager;
import com.gmail.nossr50.util.blockmeta.ChunkManagerFactory;
public class mcMMO extends JavaPlugin {
@ -95,7 +97,7 @@ public class mcMMO extends JavaPlugin {
private static Database database;
public static mcMMO p;
public static ChunkletManager placeStore;
public static ChunkManager placeStore;
public static RepairManager repairManager;
/* Jar Stuff */
@ -223,7 +225,7 @@ public class mcMMO extends JavaPlugin {
}
// Get our ChunkletManager
placeStore = ChunkletManagerFactory.getChunkletManager();
placeStore = ChunkManagerFactory.getChunkManager();
}
/**

View File

@ -0,0 +1,90 @@
package com.gmail.nossr50.runnables;
import java.io.File;
import java.lang.Runnable;
import org.bukkit.scheduler.BukkitScheduler;
import com.gmail.nossr50.config.HiddenConfig;
import com.gmail.nossr50.mcMMO;
public class BlockStoreConversionMain implements Runnable {
private int taskID, i;
private org.bukkit.World world;
BukkitScheduler scheduler;
File dataDir;
File[] xDirs;
BlockStoreConversionXDirectory[] converters;
public BlockStoreConversionMain(org.bukkit.World world) {
this.taskID = -1;
this.world = world;
this.scheduler = mcMMO.p.getServer().getScheduler();
this.dataDir = new File(this.world.getWorldFolder(), "mcmmo_data");
this.converters = new BlockStoreConversionXDirectory[HiddenConfig.getInstance().getConversionRate()];
}
public void start() {
if(this.taskID >= 0)
return;
this.taskID = this.scheduler.scheduleSyncDelayedTask(mcMMO.p, this, 1);
return;
}
public void run() {
if(!this.dataDir.exists()) {
softStop();
return;
}
if(!this.dataDir.isDirectory()) {
this.dataDir.delete();
softStop();
return;
}
if(this.dataDir.listFiles().length <= 0) {
this.dataDir.delete();
softStop();
return;
}
this.xDirs = this.dataDir.listFiles();
for (this.i = 0; (this.i < HiddenConfig.getInstance().getConversionRate()) && (this.i < this.xDirs.length); this.i++) {
if(this.converters[this.i] == null)
this.converters[this.i] = new BlockStoreConversionXDirectory();
this.converters[this.i].start(this.world, this.xDirs[this.i]);
}
softStop();
}
public void stop() {
if(this.taskID < 0)
return;
this.scheduler.cancelTask(this.taskID);
this.taskID = -1;
}
public void softStop() {
stop();
if(this.dataDir.exists() || this.dataDir.isDirectory()) {
start();
return;
}
mcMMO.p.getLogger().info("Finished converting the storage for " + world.getName() + ".");
this.dataDir = null;
this.xDirs = null;
this.world = null;
this.scheduler = null;
this.converters = null;
return;
}
}

View File

@ -0,0 +1,79 @@
package com.gmail.nossr50.runnables;
import java.io.File;
import java.lang.Runnable;
import org.bukkit.scheduler.BukkitScheduler;
import com.gmail.nossr50.config.HiddenConfig;
import com.gmail.nossr50.mcMMO;
public class BlockStoreConversionXDirectory implements Runnable {
private int taskID, i;
private org.bukkit.World world;
BukkitScheduler scheduler;
File dataDir;
File[] zDirs;
BlockStoreConversionZDirectory[] converters;
public BlockStoreConversionXDirectory() {
this.taskID = -1;
}
public void start(org.bukkit.World world, File dataDir) {
this.world = world;
this.scheduler = mcMMO.p.getServer().getScheduler();
this.converters = new BlockStoreConversionZDirectory[HiddenConfig.getInstance().getConversionRate()];
this.dataDir = dataDir;
if(this.taskID >= 0)
return;
this.taskID = this.scheduler.scheduleSyncDelayedTask(mcMMO.p, this, 1);
return;
}
public void run() {
if(!this.dataDir.exists()) {
stop();
return;
}
if(!this.dataDir.isDirectory()) {
this.dataDir.delete();
stop();
return;
}
if(this.dataDir.listFiles().length <= 0) {
this.dataDir.delete();
stop();
return;
}
this.zDirs = this.dataDir.listFiles();
for (this.i = 0; (this.i < HiddenConfig.getInstance().getConversionRate()) && (this.i < this.zDirs.length); this.i++) {
if(this.converters[this.i] == null)
this.converters[this.i] = new BlockStoreConversionZDirectory();
this.converters[this.i].start(this.world, this.dataDir, this.zDirs[this.i]);
}
stop();
}
public void stop() {
if(this.taskID < 0)
return;
this.scheduler.cancelTask(this.taskID);
this.taskID = -1;
this.dataDir = null;
this.zDirs = null;
this.world = null;
this.scheduler = null;
this.converters = null;
}
}

View File

@ -0,0 +1,154 @@
package com.gmail.nossr50.runnables;
import java.io.File;
import java.lang.Runnable;
import java.lang.String;
import org.bukkit.scheduler.BukkitScheduler;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.blockmeta.PrimitiveExChunkletStore;
import com.gmail.nossr50.util.blockmeta.PrimitiveChunkStore;
import com.gmail.nossr50.util.blockmeta.HashChunkletManager;
import com.gmail.nossr50.util.blockmeta.HashChunkManager;
public class BlockStoreConversionZDirectory implements Runnable {
private int taskID, cx, cz, x, y, z, y2, xPos, zPos, cxPos, czPos;
private String cxs, czs, chunkletName, chunkName;
private org.bukkit.World world;
private BukkitScheduler scheduler;
private File xDir, dataDir;
private HashChunkletManager manager;
private HashChunkManager newManager;
private PrimitiveExChunkletStore currentChunklet;
private PrimitiveChunkStore currentChunk;
private boolean[] oldArray, newArray;
public BlockStoreConversionZDirectory() {
this.taskID = -1;
}
public void start(org.bukkit.World world, File xDir, File dataDir) {
this.world = world;
this.scheduler = mcMMO.p.getServer().getScheduler();
this.manager = new HashChunkletManager();
this.newManager = (HashChunkManager) mcMMO.p.placeStore;
this.dataDir = dataDir;
this.xDir = xDir;
if(this.taskID >= 0)
return;
this.taskID = this.scheduler.scheduleSyncDelayedTask(mcMMO.p, this, 1);
return;
}
public void run() {
if(!this.dataDir.exists()) {
stop();
return;
}
if(!this.dataDir.isDirectory()) {
this.dataDir.delete();
stop();
return;
}
if(this.dataDir.listFiles().length <= 0) {
this.dataDir.delete();
stop();
return;
}
this.cxs = this.xDir.getName();
this.czs = this.dataDir.getName();
this.cx = 0;
this.cz = 0;
try {
this.cx = Integer.parseInt(this.cxs);
this.cz = Integer.parseInt(this.czs);
}
catch(Exception e) {
this.dataDir.delete();
stop();
return;
}
this.manager.loadChunk(this.cx, this.cz, this.world);
for(this.y = 0; this.y < 4; this.y++) {
this.chunkletName = this.world.getName() + "," + this.cx + "," + this.cz + "," + this.y;
this.currentChunklet = (PrimitiveExChunkletStore) this.manager.store.get(this.chunkletName);
if(this.currentChunklet == null) {
continue;
} else {
this.chunkName = this.world.getName() + "," + this.cx + "," + this.cz;
this.currentChunk = (PrimitiveChunkStore) this.newManager.store.get(this.chunkName);
if(this.currentChunk != null) {
this.xPos = this.cx * 16;
this.zPos = this.cz * 16;
for(this.x = 0; this.x < 16; this.x++) {
for(this.z = 0; this.z < 16; this.z++) {
this.cxPos = this.xPos + this.x;
this.czPos = this.zPos + this.z;
for(this.y2 = (64 * this.y); this.y2 < (64 * this.y + 64); this.y2++) {
if(!this.manager.isTrue(this.cxPos, this.y2, this.czPos, this.world))
continue;
this.newManager.setTrue(this.cxPos, this.y2, this.czPos, this.world);
}
}
}
continue;
}
this.newManager.setTrue(this.cx * 16, 0, this.cz * 16, this.world);
this.newManager.setFalse(this.cx * 16, 0, this.cz * 16, this.world);
this.currentChunk = (PrimitiveChunkStore) this.newManager.store.get(this.chunkName);
for(this.x = 0; this.x < 16; this.x++) {
for(this.z = 0; this.z < 16; this.z++) {
this.oldArray = this.currentChunklet.store[x][z];
this.newArray = this.currentChunk.store[x][z];
System.arraycopy(this.oldArray, 0, this.newArray, (this.y * 64), 64);
}
}
}
}
this.manager.unloadChunk(this.cx, this.cz, this.world);
this.newManager.unloadChunk(this.cx, this.cz, this.world);
for(File yFile : dataDir.listFiles()) {
if(!yFile.exists())
continue;
yFile.delete();
}
stop();
}
public void stop() {
if(this.taskID < 0)
return;
this.scheduler.cancelTask(taskID);
this.taskID = -1;
this.cxs = null;
this.czs = null;
this.chunkletName = null;
this.chunkName = null;
this.manager = null;
this.xDir = null;
this.dataDir = null;
this.currentChunklet = null;
this.currentChunk = null;
}
}

View File

@ -0,0 +1,168 @@
package com.gmail.nossr50.util.blockmeta;
import java.io.IOException;
import org.bukkit.World;
import org.bukkit.block.Block;
public interface ChunkManager {
public void closeAll();
public ChunkStore readChunkStore(World world, int x, int z) throws IOException;
public void writeChunkStore(World world, int x, int z, ChunkStore data);
public void closeChunkStore(World world, int x, int z);
/**
* Loads a specific chunklet
*
* @param cx Chunklet X coordinate that needs to be loaded
* @param cy Chunklet Y coordinate that needs to be loaded
* @param cz Chunklet Z coordinate that needs to be loaded
* @param world World that the chunklet needs to be loaded in
*/
public void loadChunklet(int cx, int cy, int cz, World world);
/**
* Unload a specific chunklet
*
* @param cx Chunklet X coordinate that needs to be unloaded
* @param cy Chunklet Y coordinate that needs to be unloaded
* @param cz Chunklet Z coordinate that needs to be unloaded
* @param world World that the chunklet needs to be unloaded from
*/
public void unloadChunklet(int cx, int cy, int cz, World world);
/**
* Load a given Chunk's Chunklet data
*
* @param cx Chunk X coordinate that is to be loaded
* @param cz Chunk Z coordinate that is to be loaded
* @param world World that the Chunk is in
*/
public void loadChunk(int cx, int cz, World world);
/**
* Unload a given Chunk's Chunklet data
*
* @param cx Chunk X coordinate that is to be unloaded
* @param cz Chunk Z coordinate that is to be unloaded
* @param world World that the Chunk is in
*/
public void unloadChunk(int cx, int cz, World world);
/**
* Saves a given Chunk's Chunklet data
*
* @param cx Chunk X coordinate that is to be saved
* @param cz Chunk Z coordinate that is to be saved
* @param world World that the Chunk is in
*/
public void saveChunk(int cx, int cz, World world);
public boolean isChunkLoaded(int cx, int cz, World world);
/**
* Informs the ChunkletManager a chunk is loaded
*
* @param cx Chunk X coordinate that is loaded
* @param cz Chunk Z coordinate 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
*
* @param cx Chunk X coordinate that is unloaded
* @param cz Chunk Z coordinate 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);
/**
* Load all ChunkletStores from all loaded chunks from this world into memory
*
* @param world World to load
*/
public void loadWorld(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();
}

View File

@ -0,0 +1,15 @@
package com.gmail.nossr50.util.blockmeta;
import com.gmail.nossr50.config.HiddenConfig;
public class ChunkManagerFactory {
public static ChunkManager getChunkManager() {
HiddenConfig hConfig = HiddenConfig.getInstance();
if(hConfig.getChunkletsEnabled()) {
return new HashChunkManager();
} else {
return new NullChunkManager();
}
}
}

View File

@ -0,0 +1,72 @@
package com.gmail.nossr50.util.blockmeta;
import java.io.Serializable;
/**
* A ChunkStore should be responsible for a 16x16xWorldHeight area of data
*/
public interface ChunkStore extends Serializable {
/**
* Checks the chunk's save state
*
* @return true if the has been modified since it was last saved
*/
public boolean isDirty();
/**
* Checks the chunk's save state
*
* @param dirty the save state of the current chunk
*/
public void setDirty(boolean dirty);
/**
* Checks the chunk's x coordinate
*
* @return the chunk's x coordinate.
*/
public int getChunkX();
/**
* Checks the chunk's z coordinate
*
* @return the chunk's z coordinate.
*/
public int getChunkZ();
/**
* Checks the value 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
* @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();
/**
* Set all values in this ChunkletStore to the values from another provided ChunkletStore
*
* @param otherStore Another ChunkletStore that this one should copy all data from
*/
public void copyFrom(ChunkletStore otherStore);
}

View File

@ -0,0 +1,10 @@
package com.gmail.nossr50.util.blockmeta;
import org.bukkit.World;
public class ChunkStoreFactory {
protected static ChunkStore getChunkStore(World world, int x, int z) {
// TODO: Add in loading from config what type of store we want.
return new PrimitiveChunkStore(world, x, z);
}
}

View File

@ -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() {}
}

View File

@ -19,7 +19,7 @@ import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.runnables.ChunkletUnloader;
public class HashChunkletManager implements ChunkletManager {
private HashMap<String, ChunkletStore> store = new HashMap<String, ChunkletStore>();
public HashMap<String, ChunkletStore> store = new HashMap<String, ChunkletStore>();
@Override
public void loadChunklet(int cx, int cy, int cz, World world) {

View File

@ -0,0 +1,89 @@
package com.gmail.nossr50.util.blockmeta;
import java.io.IOException;
import org.bukkit.World;
import org.bukkit.block.Block;
public class NullChunkManager implements ChunkManager {
@Override
public void closeAll() {}
@Override
public ChunkStore readChunkStore(World world, int x, int z) throws IOException {
return null;
}
@Override
public void writeChunkStore(World world, int x, int z, ChunkStore data) {}
@Override
public void closeChunkStore(World world, int x, int z) {}
@Override
public void loadChunklet(int cx, int cy, int cz, World world) {}
@Override
public void unloadChunklet(int cx, int cy, int cz, World world) {}
@Override
public void loadChunk(int cx, int cz, World world) {}
@Override
public void unloadChunk(int cx, int cz, World world) {}
@Override
public void saveChunk(int cx, int cz, World world) {}
@Override
public boolean isChunkLoaded(int cx, int cz, World world) {
return true;
}
@Override
public void chunkLoaded(int cx, int cz, World world) {}
@Override
public void chunkUnloaded(int cx, int cz, World world) {}
@Override
public void saveWorld(World world) {}
@Override
public void unloadWorld(World world) {}
@Override
public void loadWorld(World world) {}
@Override
public void saveAll() {}
@Override
public void unloadAll() {}
@Override
public boolean isTrue(int x, int y, int z, World world) {
return false;
}
@Override
public boolean isTrue(Block block) {
return false;
}
@Override
public void setTrue(int x, int y, int z, World world) {}
@Override
public void setTrue(Block block) {}
@Override
public void setFalse(int x, int y, int z, World world) {}
@Override
public void setFalse(Block block) {}
@Override
public void cleanUp() {}
}

View File

@ -0,0 +1,144 @@
package com.gmail.nossr50.util.blockmeta;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.UUID;
import org.bukkit.World;
import com.gmail.nossr50.mcMMO;
public class PrimitiveChunkStore implements ChunkStore {
private static final long serialVersionUID = -1L;
transient private boolean dirty = false;
/** X, Z, Y */
public boolean[][][] store;
private static final int CURRENT_VERSION = 4;
private static final int MAGIC_NUMBER = 0xEA5EDEBB;
private int cx;
private int cz;
private UUID worldUid;
transient private int worldHeight;
transient private int xBitShifts;
transient private int zBitShifts;
transient private boolean conversionNeeded;
public PrimitiveChunkStore(World world, int cx, int cz) {
this.cx = cx;
this.cz = cz;
this.worldUid = world.getUID();
this.worldHeight = world != null ? world.getMaxHeight() : 128;
this.xBitShifts = 11;
this.zBitShifts = 7;
this.store = new boolean[16][16][this.worldHeight - 1];
conversionNeeded = false;
}
@Override
public boolean isDirty() {
return dirty;
}
@Override
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
@Override
public int getChunkX() {
return cx;
}
@Override
public int getChunkZ() {
return cz;
}
@Override
public boolean isTrue(int x, int y, int z) {
return store[x][z][y];
}
@Override
public void setTrue(int x, int y, int z) {
store[x][z][y] = true;
dirty = true;
}
@Override
public void setFalse(int x, int y, int z) {
store[x][z][y] = false;
dirty = true;
}
@Override
public boolean isEmpty() {
for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) {
for(int y = 0; y < this.worldHeight; y++) {
if(store[x][z][y]) return false;
}
}
}
return true;
}
@Override
public void copyFrom(ChunkletStore otherStore) {
for(int x = 0; x < 16; x++) {
for(int z = 0; z < 16; z++) {
for(int y = 0; y < this.worldHeight; y++) {
store[x][z][y] = otherStore.isTrue(x, y, z);
}
}
}
dirty = true;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeInt(MAGIC_NUMBER);
out.writeInt(CURRENT_VERSION);
out.writeLong(worldUid.getLeastSignificantBits());
out.writeLong(worldUid.getMostSignificantBits());
out.writeInt(cx);
out.writeInt(cz);
out.writeObject(store);
dirty = false;
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
int fileVersionNumber; // Can be used to determine the format of the file
long lsb = in.readLong();
if (((int) (lsb >> 32)) == MAGIC_NUMBER) {
fileVersionNumber = (int) lsb;
lsb = in.readLong();
} else {
fileVersionNumber = 0;
}
long msb = in.readLong();
worldUid = new UUID(msb, lsb);
cx = in.readInt();
cz = in.readInt();
// Constructor is not invoked, need to set these fields
World world = mcMMO.p.getServer().getWorld(this.worldUid);
this.worldHeight = world.getMaxHeight();
this.xBitShifts = 11;
this.zBitShifts = 7;
store = (boolean[][][]) in.readObject();
if (fileVersionNumber < CURRENT_VERSION) {
dirty = true;
}
}
}

View File

@ -4,7 +4,7 @@ public class PrimitiveChunkletStore implements ChunkletStore {
private static final long serialVersionUID = -3453078050608607478L;
/** X, Z, Y */
private boolean[][][] store = new boolean[16][16][64];
public boolean[][][] store = new boolean[16][16][64];
@Override
public boolean isTrue(int x, int y, int z) {

View File

@ -9,7 +9,7 @@ public class PrimitiveExChunkletStore implements ChunkletStore, Externalizable {
private static final long serialVersionUID = 8603603827094383873L;
/** X, Z, Y */
private boolean[][][] store = new boolean[16][16][64];
public boolean[][][] store = new boolean[16][16][64];
@Override
public boolean isTrue(int x, int y, int z) {