package electrosphere.server.terrain.diskmap; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterOutputStream; import electrosphere.client.terrain.cache.ChunkData; import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.util.FileUtils; import electrosphere.util.annotation.Exclude; /** * An interface for accessing the disk map of chunk information */ public class ChunkDiskMap { /** * The map of world position+chunk type to the file that actually houses that information */ Map worldPosFileMap; /** * Locks the chunk disk map for thread safety */ @Exclude ReentrantLock lock = new ReentrantLock(); /** * Constructor */ private ChunkDiskMap(){ worldPosFileMap = new HashMap(); } /** * Gets a key for a given chunk file based on a world coordinate * @param worldX The x component * @param worldY The y component * @param worldZ The z component * @return The key */ private static String getTerrainChunkKey(int worldX, int worldY, int worldZ){ return worldX + "_" + worldY + "_" + worldZ + "t"; } /** * Gets a key for a given chunk file based on a world coordinate * @param worldX The x component * @param worldY The y component * @param worldZ The z component * @return The key */ private static String getFluidChunkKey(int worldX, int worldY, int worldZ){ return worldX + "_" + worldY + "_" + worldZ + "f"; } /** * Initializes a diskmap based on a given save name * @param saveName The save name */ public static ChunkDiskMap init(String saveName){ ChunkDiskMap rVal = null; LoggerInterface.loggerEngine.DEBUG("INIT CHUNK MAP " + saveName); if(FileUtils.getSaveFile(saveName, "chunk.map").exists()){ rVal = FileUtils.loadObjectFromSavePath(saveName, "chunk.map", ChunkDiskMap.class); LoggerInterface.loggerEngine.DEBUG("POS FILE MAP: " + rVal.worldPosFileMap.keySet()); } else { rVal = new ChunkDiskMap(); } return rVal; } /** * Initializes a diskmap based on a given save name * @param saveName The save name */ public static ChunkDiskMap init(){ return new ChunkDiskMap(); } /** * Saves the disk map to disk */ public void save(){ FileUtils.serializeObjectToSavePath(Globals.currentSave.getName(), "chunk.map", worldPosFileMap); } /** * Checks if the map contains a given chunk position * @param worldX The x component * @param worldY The y component * @param worldZ The z component * @return True if the map contains the chunk, false otherwise */ public boolean containsTerrainAtPosition(int worldX, int worldY, int worldZ){ lock.lock(); boolean rVal = worldPosFileMap.containsKey(getTerrainChunkKey(worldX, worldY, worldZ)); lock.unlock(); return rVal; } /** * Checks if the map contains a given chunk position * @param worldX The x component * @param worldY The y component * @param worldZ The z component * @return True if the map contains the chunk, false otherwise */ public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){ lock.lock(); boolean rVal = worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ)); lock.unlock(); return rVal; } /** * Gets the server terrain chunk from disk if it exists, otherwise returns null * @param worldX The x coordinate * @param worldY The y coordinate * @param worldZ The z coordinate * @return The server terrain chunk if it exists, null otherwise */ public ServerTerrainChunk getTerrainChunk(int worldX, int worldY, int worldZ){ lock.lock(); LoggerInterface.loggerEngine.INFO("Load chunk " + worldX + " " + worldY + " " + worldZ); ServerTerrainChunk rVal = null; if(containsTerrainAtPosition(worldX, worldY, worldZ)){ //read file String fileName = worldPosFileMap.get(getTerrainChunkKey(worldX, worldY, worldZ)); byte[] rawDataCompressed = FileUtils.loadBinaryFromSavePath(Globals.currentSave.getName(), fileName); //decompress byte[] rawData = null; ByteArrayOutputStream out = new ByteArrayOutputStream(); InflaterOutputStream inflaterInputStream = new InflaterOutputStream(out); try { inflaterInputStream.write(rawDataCompressed); inflaterInputStream.flush(); inflaterInputStream.close(); rawData = out.toByteArray(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //parse if(rawData != null){ ByteBuffer buffer = ByteBuffer.wrap(rawData); FloatBuffer floatView = buffer.asFloatBuffer(); int DIM = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; float[][][] weights = new float[DIM][DIM][DIM]; int[][][] values = new int[DIM][DIM][DIM]; for(int x = 0; x < DIM; x++){ for(int y = 0; y < DIM; y++){ for(int z = 0; z < DIM; z++){ weights[x][y][z] = floatView.get(); } } } IntBuffer intView = buffer.asIntBuffer(); intView.position(DIM * DIM * DIM); int firstType = -1; boolean homogenous = true; for(int x = 0; x < DIM; x++){ for(int y = 0; y < DIM; y++){ for(int z = 0; z < DIM; z++){ values[x][y][z] = intView.get(); if(firstType == -1){ firstType = values[x][y][z]; } else if(homogenous && firstType == values[x][y][z]){ homogenous = false; } } } } rVal = new ServerTerrainChunk(worldX, worldY, worldZ, homogenous ? firstType : ChunkData.NOT_HOMOGENOUS, weights, values); } } lock.unlock(); return rVal; } /** * Saves a terrain chunk to disk * @param terrainChunk The terrain chunk */ public void saveToDisk(ServerTerrainChunk terrainChunk){ lock.lock(); LoggerInterface.loggerEngine.DEBUG("Save to disk: " + terrainChunk.getWorldX() + " " + terrainChunk.getWorldY() + " " + terrainChunk.getWorldZ()); //get the file name for this chunk String fileName = null; String chunkKey = getTerrainChunkKey(terrainChunk.getWorldX(),terrainChunk.getWorldY(),terrainChunk.getWorldZ()); if(worldPosFileMap.containsKey(chunkKey)){ fileName = worldPosFileMap.get(chunkKey); } else { fileName = chunkKey + ".dat"; } //generate binary for the file float[][][] weights = terrainChunk.getWeights(); int[][][] values = terrainChunk.getValues(); int DIM = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; ByteBuffer buffer = ByteBuffer.allocate(DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4); FloatBuffer floatView = buffer.asFloatBuffer(); for(int x = 0; x < DIM; x++){ for(int y = 0; y < DIM; y++){ for(int z = 0; z < DIM; z++){ floatView.put(weights[x][y][z]); } } } buffer.position(DIM * DIM * DIM * 4); IntBuffer intView = buffer.asIntBuffer(); for(int x = 0; x < DIM; x++){ for(int y = 0; y < DIM; y++){ for(int z = 0; z < DIM; z++){ intView.put(values[x][y][z]); } } } //compress ByteArrayOutputStream out = new ByteArrayOutputStream(); DeflaterOutputStream deflaterInputStream = new DeflaterOutputStream(out); try { deflaterInputStream.write(buffer.array()); deflaterInputStream.flush(); deflaterInputStream.close(); //write to disk FileUtils.saveBinaryToSavePath(Globals.currentSave.getName(), fileName, out.toByteArray()); //save to the map of filenames worldPosFileMap.put(chunkKey,fileName); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } lock.unlock(); } }