/*
* This file is part of SpoutcraftPlugin.
*
* Copyright (c) 2011 SpoutcraftDev <http://spoutcraft.org//>
* SpoutcraftPlugin is licensed under the GNU Lesser General Public License.
*
* SpoutcraftPlugin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SpoutcraftPlugin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.getspout.spoutapi.chunkstore;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.getspout.spoutapi.chunkdatamanager.ChunkDataManager;
import org.getspout.spoutapi.inventory.ItemMap;
import org.getspout.spoutapi.io.store.FlatFileStore;
public class SimpleChunkDataManager implements ChunkDataManager {
private ChunkStore chunkStore = new ChunkStore();
private HashMap<UUID, TLongObjectHashMap<ChunkMetaData>> chunkMetaDataLoaded = new HashMap<UUID, TLongObjectHashMap<ChunkMetaData>>();
private HashMap<UUID, ItemMap> worldItemMaps = new HashMap<UUID, ItemMap>();
public void closeAllFiles() {
chunkStore.closeAll();
}
public ChunkMetaData loadChunk(Chunk c) {
return loadChunk(c.getWorld(), c.getX(), c.getZ());
}
public ChunkMetaData loadChunk(World world, int x, int z) {
ChunkMetaData md = getMetaData(world, x, z, true, true);
return md;
}
public boolean loadWorldChunks(World w) {
Chunk[] chunks = w.getLoadedChunks();
boolean loaded = false;
for (Chunk c : chunks) {
loaded |= loadChunk(c) != null;
}
return loaded;
}
public boolean loadAllChunks() {
List<World> worlds = Bukkit.getServer().getWorlds();
boolean loaded = false;
for (World w : worlds) {
loaded |= loadWorldChunks(w);
}
return loaded;
}
public boolean saveChunk(Chunk c) {
return saveChunk(c.getWorld(), c.getX(), c.getZ());
}
public boolean saveChunk(Chunk c, boolean remove) {
return saveChunk(c.getWorld(), c.getX(), c.getZ(), remove);
}
public boolean saveChunk(World w, int x, int z) {
ChunkMetaData md = getMetaData(w, x, z, false, false);
if (md != null) {
chunkStore.writeChunkMetaData(w, x, z, md);
return true;
} else {
return false;
}
}
public boolean saveChunk(World w, int x, int z, boolean remove) {
boolean saved = saveChunk(w, x, z);
if (saved) {
if (remove) {
final TLongObjectHashMap<ChunkMetaData> chunks = chunkMetaDataLoaded.get(w.getUID());
if (chunks == null) {
return false;
}
chunks.remove((((long) x) << 32) | (((long) z) & 0xFFFFFFFFL));
}
} else {
return false;
}
return true;
}
public boolean closeChunk(World w, int x, int z) {
ChunkMetaData md = getMetaData(w, x, z, false, false);
if (md != null) {
chunkStore.closeChunkMetaData(w, x, z);
return true;
} else {
return false;
}
}
public boolean unloadWorldChunks(World world) {
return saveWorldChunks(world, true);
}
public boolean saveWorldChunks(World world) {
return saveWorldChunks(world, false);
}
public boolean saveWorldChunks(World world, boolean unload) {
boolean unloaded = false;
synchronized (chunkMetaDataLoaded) {
TLongObjectHashMap<ChunkMetaData> worldChunks = chunkMetaDataLoaded.get(world.getUID());
if (worldChunks == null) {
return false;
}
Collection<ChunkMetaData> chunks = worldChunks.valueCollection();
for (ChunkMetaData md : chunks) {
unloaded |= saveChunk(world, md.getChunkX(), md.getChunkZ());
if (unload) {
closeChunk(world, md.getChunkX(), md.getChunkZ());
}
}
worldChunks.clear();
}
return unloaded;
}
public boolean unloadAllChunks() {
List<World> worlds = Bukkit.getServer().getWorlds();
boolean unloaded = false;
for (World world : worlds) {
unloaded |= saveWorldChunks(world);
}
return unloaded;
}
@Override
public Serializable setBlockData(String id, World world, int x, int y, int z, Serializable data) {
ChunkMetaData md = getMetaData(world, x >> 4, z >> 4, true, true);
return md.putBlockData(id, x, y, z, data);
}
@Override
public Serializable getBlockData(String id, World world, int x, int y, int z) {
ChunkMetaData md = getMetaData(world, x >> 4, z >> 4, true, false);
if (md == null) {
return null;
}
return md.getBlockData(id, x, y, z);
}
public Serializable removeBlockData(String id, World world, int x, int y, int z) {
ChunkMetaData md = getMetaData(world, x >> 4, z >> 4, true, false);
if (md == null) {
return null;
}
return md.removeBlockData(id, x, y, z);
}
@Override
public Serializable setChunkData(String id, World world, int x, int z, Serializable data) {
ChunkMetaData md = getMetaData(world, x, z, true, true);
return md.putChunkData(id, data);
}
@Override
public Serializable getChunkData(String id, World world, int x, int z) {
ChunkMetaData md = getMetaData(world, x, z, true, false);
if (md == null) {
return null;
}
return md.getChunkData(id);
}
@Override
public Serializable removeChunkData(String id, World world, int x, int z) {
ChunkMetaData md = getMetaData(world, x, z, true, false);
if (md == null) {
return null;
}
return md.removeChunkData(id);
}
@Override
public short[] getCustomBlockIds(World world, int x, int z) {
ChunkMetaData md = getMetaData(world, x, z, true, false);
if (md == null) {
return null;
}
return md.getCustomBlockIds();
}
@Override
public void setCustomBlockIds(World world, int x, int z, short[] ids) {
ChunkMetaData md = getMetaData(world, x, z, true, true);
md.setCustomBlockIds(ids);
}
@Override
public byte[] getCustomBlockData(World world, int x, int z) {
ChunkMetaData md = getMetaData(world, x, z, true, false);
if (md == null) {
return null;
}
return md.getCustomBlockData();
}
@Override
public void setCustomBlockData(World world, int x, int z, byte[] ids) {
ChunkMetaData md = getMetaData(world, x, z, true, true);
md.setCustomBlockData(ids);
}
private ChunkMetaData getMetaData(World world, int x, int z, boolean load, boolean loadOrCreate) {
long key = (((long) x) << 32) | (((long) z) & 0xFFFFFFFFL);
UUID uid = world.getUID();
ChunkMetaData md = null;
synchronized (chunkMetaDataLoaded) {
TLongObjectHashMap<ChunkMetaData> worldChunks = chunkMetaDataLoaded.get(uid);
if (worldChunks == null) {
worldChunks = new TLongObjectHashMap<ChunkMetaData>();
chunkMetaDataLoaded.put(uid, worldChunks);
}
md = worldChunks.get(key);
if (md == null && (load || loadOrCreate)) {
try {
md = chunkStore.readChunkMetaData(world, x, z);
if (md != null) {
if (!md.getWorldUID().equals(world.getUID()) || md.getChunkX() != x || md.getChunkZ() != z) {
System.err.println("Chunk data mismatch!");
System.err.println("Expected: " + world.getUID() + " " + x + " " + z);
System.err.println("Actual: " + md.getWorldUID() + " " + md.getChunkX() + " " + md.getChunkZ());
//throw new RuntimeException("Chunk meta data stored in wrong location");
}
md.setWorldItemMap(getWorldItemMap(world));
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
if (md == null && loadOrCreate) {
md = new ChunkMetaData(world.getUID(), getWorldItemMap(world), x, z);
}
if (md != null) {
worldChunks.put(key, md);
}
}
}
return md;
}
private ItemMap getWorldItemMap(World world) {
UUID uid = world.getUID();
ItemMap worldItemMap = worldItemMaps.get(uid);
if (worldItemMap == null) {
File dir = new File(world.getWorldFolder(), "spout_meta");
dir.mkdirs();
FlatFileStore<Integer> fs = new FlatFileStore<Integer>(new File(dir, "worldItemMap.txt"), Integer.class);
fs.load();
worldItemMap = new ItemMap(ItemMap.getRootMap(), fs, null);
}
worldItemMaps.put(uid, worldItemMap);
return worldItemMap;
}
public int getStringId(String string) {
return ItemMap.getRootMap().register(string);
}
@Override
public ItemMap getItemMap(World world) {
return worldItemMaps.get(world.getUID());
}
}