197 lines
6.4 KiB
Java
197 lines
6.4 KiB
Java
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<BlockChunkData> 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();
|
|
}
|
|
|
|
}
|