Renderer/src/main/java/electrosphere/server/terrain/diskmap/ChunkDiskMap.java
austin 3777a9a37c
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
server side terrain management refactoring
2024-11-23 21:46:13 -05:00

246 lines
9.0 KiB
Java

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<String,String> worldPosFileMap;
/**
* Locks the chunk disk map for thread safety
*/
@Exclude
ReentrantLock lock = new ReentrantLock();
/**
* Constructor
*/
private ChunkDiskMap(){
worldPosFileMap = new HashMap<String,String>();
}
/**
* 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();
}
}