package electrosphere.server.block.manager; import electrosphere.client.block.BlockChunkCache; import electrosphere.client.block.BlockChunkData; import electrosphere.engine.Globals; import electrosphere.game.server.world.ServerWorldData; import electrosphere.server.block.diskmap.ServerBlockChunkDiskMap; import electrosphere.util.annotation.Exclude; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; import org.joml.Vector3i; /** * Provides an interface for the server to query information about block chunks */ public class ServerBlockManager { /** * The number of threads for chunk generation */ public static final int GENERATION_THREAD_POOL_SIZE = 2; /** * The parent world data */ ServerWorldData parent; /** * The cache of chunks */ @Exclude BlockChunkCache chunkCache = new BlockChunkCache(); //The map of chunk position <-> file on disk containing chunk data ServerBlockChunkDiskMap chunkDiskMap = null; /** * The threadpool for chunk generation */ @Exclude static final ExecutorService chunkExecutorService = Executors.newFixedThreadPool(GENERATION_THREAD_POOL_SIZE); /** * Constructor */ public ServerBlockManager( ServerWorldData parent ){ this.parent = parent; } ServerBlockManager(){ } /** * Inits the chunk disk map */ public void generate(){ this.chunkDiskMap = ServerBlockChunkDiskMap.init(); } /** * Saves the block cache backing this manager to a save file * @param saveName The name of the save */ public void save(String saveName){ //for each chunk, save via disk map for(BlockChunkData chunk : this.chunkCache.getContents()){ chunkDiskMap.saveToDisk(chunk); } //save disk map itself if(chunkDiskMap != null){ chunkDiskMap.save(); } } /** * Loads a block manager from a save file * @param saveName The name of the save */ public void load(String saveName){ //load chunk disk map chunkDiskMap = ServerBlockChunkDiskMap.init(saveName); } /** * Evicts all cached chunks */ public void evictAll(){ this.chunkCache.clear(); } /** * Performs logic once a server chunk is available * @param worldX The world x position * @param worldY The world y position * @param worldZ The world z position * @return The BlockChunkData */ public BlockChunkData getChunk(int worldX, int worldY, int worldZ){ Globals.profiler.beginAggregateCpuSample("ServerBlockManager.getChunk"); //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING BlockChunkData returnedChunk = null; if(chunkCache.containsChunk(worldX,worldY,worldZ,BlockChunkData.LOD_FULL_RES)){ returnedChunk = chunkCache.get(worldX,worldY,worldZ, BlockChunkData.LOD_FULL_RES); } else { //pull from disk if it exists if(chunkDiskMap != null){ if(chunkDiskMap.containsBlocksAtPosition(worldX, worldY, worldZ)){ returnedChunk = chunkDiskMap.getBlockChunk(worldX, worldY, worldZ); } } //generate if it does not exist if(returnedChunk == null){ returnedChunk = BlockChunkData.allocate(); returnedChunk.setWorldX(worldX); returnedChunk.setWorldY(worldY); returnedChunk.setWorldZ(worldZ); returnedChunk.setHomogenousValue(0); if(worldX == 0 && worldY == 0 && worldZ == 0){ returnedChunk.setHomogenousValue(BlockChunkData.NOT_HOMOGENOUS); for(int x = 3; x < 16; x++){ for(int y = 8; y < 16; y++){ for(int z = 3; z < 16; z++){ returnedChunk.setType(x, y, z, 1); } } } } } this.chunkCache.add(worldX, worldY, worldZ, BlockChunkData.LOD_FULL_RES, returnedChunk); } Globals.profiler.endCpuSample(); return returnedChunk; } /** * Performs logic once a server chunk is available * @param worldX The world x position * @param worldY The world y position * @param worldZ The world z position * @param stride The stride of the data * @param onLoad The logic to run once the chunk is available */ public void getChunkAsync(int worldX, int worldY, int worldZ, int stride, Consumer onLoad){ Globals.profiler.beginAggregateCpuSample("ServerBlockManager.getChunkAsync"); chunkExecutorService.submit(new ServerBlockChunkGenerationThread(chunkDiskMap, chunkCache, worldX, worldY, worldZ, stride, onLoad)); Globals.profiler.endCpuSample(); } /** * Saves a given position's chunk to disk. * Uses the current global save name * @param position The position to save */ public void savePositionToDisk(Vector3i position){ if(chunkDiskMap != null){ chunkDiskMap.saveToDisk(getChunk(position.x, position.y, position.z)); } } /** * Applies an edit to a block at a given location * @param worldPos The world coordinates of the chunk to modify * @param voxelPos The voxel coordinates of the voxel to modify * @param type The type of block * @param metadata The metadata of the block */ public void editBlockAtLocationToValue(Vector3i worldPos, Vector3i voxelPos, short type, short metadata){ if(chunkCache.containsChunk(worldPos.x,worldPos.y,worldPos.z,BlockChunkData.LOD_FULL_RES)){ BlockChunkData chunk = chunkCache.get(worldPos.x,worldPos.y,worldPos.z, BlockChunkData.LOD_FULL_RES); chunk.setType(voxelPos.x, voxelPos.y, voxelPos.z, type); chunk.setType(voxelPos.x, voxelPos.y, voxelPos.z, metadata); } } /** * Sets the parent world data of this manager * @param serverWorldData The parent world data */ public void setParent(ServerWorldData serverWorldData){ this.parent = serverWorldData; } /** * Closes the generation threadpool */ public void closeThreads(){ chunkExecutorService.shutdownNow(); } }