diff --git a/buildNumber.properties b/buildNumber.properties index d231cc53..086f0e5a 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Mon Nov 04 12:41:10 EST 2024 -buildNumber=375 +#Fri Nov 08 15:44:15 EST 2024 +buildNumber=376 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 366e3a79..5ba51648 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -954,6 +954,12 @@ Fix LOD bounding sphere calculation Hook up content generation to test generation realm Reorganize biome data +(11/08/2024) +Player and entity tracking overhaul in grid data cell manager +Add more profiling points +Height manual adjustment for content placement +Fast track client draw cell manager cell evaluation + # TODO diff --git a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java index 50b16afc..b9f96785 100644 --- a/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java +++ b/src/main/java/electrosphere/client/foliagemanager/ClientFoliageManager.java @@ -99,7 +99,7 @@ public class ClientFoliageManager { } else { chunkUpdateCache.remove(foundChunk); } - foundChunk.updateCells(false); + foundChunk.updateCells(); Globals.profiler.endCpuSample(); } @@ -144,7 +144,7 @@ public class ClientFoliageManager { public void evaluateChunk(Vector3i worldPos){ for(FoliageChunk chunk : chunkUpdateCache){ if(chunk.getWorldPos().equals(worldPos)){ - chunk.updateCells(true); + chunk.updateCells(); break; } } diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java index 7b652ab8..eee8519a 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageCell.java @@ -27,6 +27,7 @@ import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.foliage.AmbientFoliage; import electrosphere.game.data.foliage.type.FoliageType; +import electrosphere.logger.LoggerInterface; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; import electrosphere.renderer.actor.instance.TextureInstancedActor; @@ -60,6 +61,11 @@ public class FoliageCell { */ static final float SAMPLE_START_HEIGHT = 1.0f; + /** + * The ID of the air voxel + */ + static final int AIR_VOXEL_ID = 0; + /** *

* Size of a single item of foliage in the texture buffer @@ -148,6 +154,11 @@ public class FoliageCell { */ static Sphered boundingSphere = new Sphered(0.5,0.5,0.5,2); + /** + * Tracks whether the cell has generated or not + */ + boolean hasGenerated = false; + /** * Inits the foliage cell data */ @@ -204,17 +215,17 @@ public class FoliageCell { */ protected void generate(){ boolean shouldGenerate = false; - //get foliage types supported - ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition,ChunkData.NO_STRIDE); - if(data == null){ + if(voxelPosition.y + 1 >= ServerTerrainChunk.CHUNK_DIMENSION){ return; } - if(voxelPosition.y + 1 >= ServerTerrainChunk.CHUNK_DIMENSION){ + ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosition,ChunkData.NO_STRIDE); + if(data == null){ return; } if(!Globals.clientDrawCellManager.isFullLOD(worldPosition)){ return; } + //get foliage types supported List foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPosition)).getAmbientFoliage(); boolean airAbove = data.getType(voxelPosition.x,voxelPosition.y+1,voxelPosition.z) == 0; if(foliageTypesSupported != null && foliageTypesSupported.size() > 0 && airAbove && scale < 3){ @@ -227,12 +238,15 @@ public class FoliageCell { //create cell and buffer ByteBuffer buffer = BufferUtils.createByteBuffer(TARGET_FOLIAGE_PER_CELL * SINGLE_FOLIAGE_DATA_SIZE_BYTES); + if(buffer.capacity() < TARGET_FOLIAGE_PER_CELL * SINGLE_FOLIAGE_DATA_SIZE_BYTES){ + LoggerInterface.loggerEngine.WARNING("Failed to allocate data for foliage cell! " + buffer.limit()); + } FloatBuffer floatBufferView = buffer.asFloatBuffer(); int drawCount = 0; for(int x = 0; x < scale; x++){ for(int y = 0; y < scale; y++){ for(int z = 0; z < scale; z++){ - drawCount = drawCount + insertBlades(x, y, z, floatBufferView, data); + drawCount = drawCount + this.insertBlades(x, y, z, floatBufferView, data); } } } @@ -255,6 +269,7 @@ public class FoliageCell { this.addEntity(grassEntity); } } + this.hasGenerated = true; } /** @@ -278,7 +293,7 @@ public class FoliageCell { List foliageTypesSupported = null; if(chunkData != null && currVoxelPos.y + 1 < ServerTerrainChunk.CHUNK_DIMENSION){ foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(chunkData.getType(currVoxelPos)).getAmbientFoliage(); - boolean airAbove = chunkData.getType(currVoxelPos.x,currVoxelPos.y+1,currVoxelPos.z) == 0; + boolean airAbove = chunkData.getType(currVoxelPos.x,currVoxelPos.y+1,currVoxelPos.z) == AIR_VOXEL_ID; if(foliageTypesSupported != null && airAbove){ shouldGenerate = true; } @@ -387,12 +402,14 @@ public class FoliageCell { offsetY = offsetY - realPosition.y; double rotVar = placementRandomizer.nextDouble() * Math.PI * 2; double rotVar2 = placementRandomizer.nextDouble(); - floatBufferView.put((float)offsetX + vX); - floatBufferView.put((float)offsetY + vY); - floatBufferView.put((float)offsetZ + vZ); - floatBufferView.put((float)rotVar); - floatBufferView.put((float)rotVar2); - rVal++; + if(floatBufferView.limit() >= floatBufferView.position() + 4){ + floatBufferView.put((float)offsetX + vX); + floatBufferView.put((float)offsetY + vY); + floatBufferView.put((float)offsetZ + vZ); + floatBufferView.put((float)rotVar); + floatBufferView.put((float)rotVar2); + rVal++; + } } } } @@ -463,6 +480,14 @@ public class FoliageCell { } } + /** + * Gets whether the cell has generated or not + * @return true if has generated, false otherwise + */ + public boolean hasGenerated(){ + return this.hasGenerated; + } + /** * SCAFFOLDING FOR BUILDING SCALE>1 CELLS AND ALSO FOR TOP LEVEL CELL CHECKING diff --git a/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java b/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java index de56289b..628c8050 100644 --- a/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java +++ b/src/main/java/electrosphere/client/foliagemanager/FoliageChunk.java @@ -39,6 +39,12 @@ public class FoliageChunk { */ boolean containsFoliageVoxel = false; + /** + * Tracks whether this foliage chunk has properly initialized or not. + * It is only considered initialized once it has been evaluated with the data present in cache. + */ + boolean initialized = false; + /** * The octree holding all the chunks to evaluate */ @@ -80,21 +86,19 @@ public class FoliageChunk { this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos,ChunkData.NO_STRIDE); // //evaluate top cells if chunk above this one exists this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0),ChunkData.NO_STRIDE); - this.updateCells(true); + this.updateCells(); Globals.profiler.endCpuSample(); } /** * Updates all cells in the chunk - * @param force true if should ignore cache, false otherwise */ - public void updateCells(boolean force){ + public void updateCells(){ Globals.profiler.beginCpuSample("FoliageChunk.updateCells"); //re-evaluate whether contains foliage voxel or not - if(force){ - this.containsFoliageVoxel = checkContainsFoliageVoxel(); - } - if(force || containsFoliageVoxel){ + boolean hasData = Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE); + boolean hasPhysics = Globals.clientDrawCellManager.hasGeneratedPhysics(worldPos.x, worldPos.y, worldPos.z); + if(containsFoliageVoxel && hasData && hasPhysics){ Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); //the sets to iterate through boolean updated = true; @@ -105,6 +109,13 @@ public class FoliageChunk { attempts++; } } + if(!hasData){ + Globals.clientTerrainManager.requestChunk(worldPos.x, worldPos.y, worldPos.z, ChunkData.NO_STRIDE); + } + if(hasData && !this.initialized) { + this.evaluateContainsFoliage(); + this.initialized = true; + } Globals.profiler.endCpuSample(); } @@ -130,6 +141,13 @@ public class FoliageChunk { return false; } + /** + * Instructs the chunk to evaluate whether it has foliage or not + */ + public void evaluateContainsFoliage(){ + this.containsFoliageVoxel = this.checkContainsFoliageVoxel(); + } + /** * Recursively update child nodes * @param node The root node @@ -137,8 +155,9 @@ public class FoliageChunk { */ private boolean recursivelyUpdateCells(ChunkTreeNode node, Vector3d playerPos){ boolean updated = false; + Globals.profiler.beginRecursiveCpuSample("FoliageChunk.recursivelyUpdateCells"); if(this.shouldSplit(playerPos, node)){ - Globals.profiler.beginCpuSample("FoliageChunk.split"); + Globals.profiler.beginAggregateCpuSample("FoliageChunk.split"); //perform op ChunkTreeNode container = chunkTree.split(node); @@ -157,7 +176,7 @@ public class FoliageChunk { Globals.profiler.endCpuSample(); updated = true; } else if(this.shouldJoin(playerPos, node)) { - // Globals.profiler.beginCpuSample("FoliageChunk.join"); + Globals.profiler.beginAggregateCpuSample("FoliageChunk.join"); //perform op ChunkTreeNode newLeaf = chunkTree.join(node); @@ -166,12 +185,12 @@ public class FoliageChunk { //do creations newLeaf.convertToLeaf(new FoliageCell(worldPos, newLeaf.getMinBound(), realPos, 5 - newLeaf.getLevel())); - // Globals.profiler.endCpuSample(); + Globals.profiler.endCpuSample(); updated = true; } else if(shouldGenerate(playerPos, node)){ - // Globals.profiler.beginCpuSample("FoliageChunk.generate"); + Globals.profiler.beginAggregateCpuSample("FoliageChunk.generate"); node.getData().generate(); - // Globals.profiler.endCpuSample(); + Globals.profiler.endCpuSample(); updated = true; } else if(!node.isLeaf()){ List> children = new LinkedList>(node.getChildren()); @@ -180,6 +199,7 @@ public class FoliageChunk { updated = childUpdate || updated; } } + Globals.profiler.endCpuSample(); return updated; } @@ -264,7 +284,7 @@ public class FoliageChunk { return node.isLeaf() && node.getData() != null && - node.getData().containedEntities.size() < 1 && + !node.getData().hasGenerated() && ( ( node.getLevel() == ChunkTree.MAX_LEVEL diff --git a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java index b84dd2c6..603f748b 100644 --- a/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java +++ b/src/main/java/electrosphere/client/terrain/cells/ClientDrawCellManager.java @@ -1,12 +1,15 @@ package electrosphere.client.terrain.cells; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.joml.Vector3d; import org.joml.Vector3i; import electrosphere.client.terrain.cells.DrawCell.DrawCellFace; +import electrosphere.collision.PhysicsEntityUtils; import electrosphere.engine.Globals; import electrosphere.entity.EntityUtils; import electrosphere.logger.LoggerInterface; @@ -90,6 +93,11 @@ public class ClientDrawCellManager { */ WorldOctTree chunkTree; + /** + * Tracks what nodes have been evaluated this frame -- used to deduplicate evaluation calls + */ + Map,Boolean> evaluationMap = new HashMap,Boolean>(); + /** * Tracks whether the cell manager updated last frame or not */ @@ -150,29 +158,40 @@ public class ClientDrawCellManager { * Updates all cells in the chunk */ public void update(){ - Globals.profiler.beginCpuSample("ClientDrawCellManager.updateCells"); + Globals.profiler.beginCpuSample("ClientDrawCellManager.update"); if(shouldUpdate && Globals.playerEntity != null){ Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); //the sets to iterate through updatedLastFrame = true; validCellCount = 0; + evaluationMap.clear(); //update all full res cells FloatingChunkTreeNode rootNode = this.chunkTree.getRoot(); - updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, HALF_RES_LOD); + Globals.profiler.beginCpuSample("ClientDrawCellManager.update - full res cells"); + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, HALF_RES_LOD); + Globals.profiler.endCpuSample(); if(!updatedLastFrame && !this.initialized){ this.initialized = true; } if(!updatedLastFrame){ - updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, QUARTER_RES_LOD); + Globals.profiler.beginCpuSample("ClientDrawCellManager.update - half res cells"); + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, QUARTER_RES_LOD); + Globals.profiler.endCpuSample(); } if(!updatedLastFrame){ - updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, EIGHTH_RES_LOD); + Globals.profiler.beginCpuSample("ClientDrawCellManager.update - quarter res cells"); + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, EIGHTH_RES_LOD); + Globals.profiler.endCpuSample(); } if(!updatedLastFrame){ - updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, SIXTEENTH_RES_LOD); + Globals.profiler.beginCpuSample("ClientDrawCellManager.update - eighth res cells"); + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, SIXTEENTH_RES_LOD); + Globals.profiler.endCpuSample(); } if(!updatedLastFrame){ - updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, ALL_RES_LOD); + Globals.profiler.beginCpuSample("ClientDrawCellManager.update - all res cells"); + updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos, evaluationMap, ALL_RES_LOD); + Globals.profiler.endCpuSample(); } } Globals.profiler.endCpuSample(); @@ -183,11 +202,15 @@ public class ClientDrawCellManager { * @param node The root node * @param playerPos The player's position * @param minLeafLod The minimum LOD required to evaluate a leaf + * @param evaluationMap Map of leaf nodes that have been evaluated this frame * @return true if there is work remaining to be done, false otherwise */ - private boolean recursivelyUpdateCells(FloatingChunkTreeNode node, Vector3d playerPos, int minLeafLod){ + private boolean recursivelyUpdateCells(FloatingChunkTreeNode node, Vector3d playerPos, Map,Boolean> evaluationMap, int minLeafLod){ Vector3d playerRealPos = EntityUtils.getPosition(Globals.playerEntity); boolean updated = false; + if(evaluationMap.containsKey(node)){ + return false; + } if(this.shouldSplit(playerPos, node)){ Globals.profiler.beginCpuSample("ClientDrawCellManager.split"); //perform op @@ -205,6 +228,7 @@ public class ClientDrawCellManager { ); DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos); child.convertToLeaf(drawCell); + evaluationMap.put(child,true); }); //update neighbors @@ -226,6 +250,7 @@ public class ClientDrawCellManager { //update neighbors this.conditionalUpdateAdjacentNodes(newLeaf, newLeaf.getLevel()); + evaluationMap.put(newLeaf,true); Globals.profiler.endCpuSample(); updated = true; @@ -240,6 +265,7 @@ public class ClientDrawCellManager { if(this.requestChunks(node, highResFaces)){ cell.setHasRequested(true); } + evaluationMap.put(node,true); Globals.profiler.endCpuSample(); updated = true; @@ -258,17 +284,21 @@ public class ClientDrawCellManager { node.getData().setHasRequested(false); } } + evaluationMap.put(node,true); Globals.profiler.endCpuSample(); updated = true; } else if(!node.isLeaf()){ this.validCellCount++; List> children = new LinkedList>(node.getChildren()); for(FloatingChunkTreeNode child : children){ - boolean childUpdate = recursivelyUpdateCells(child, playerPos, minLeafLod); + boolean childUpdate = recursivelyUpdateCells(child, playerPos, evaluationMap, minLeafLod); if(childUpdate == true){ updated = true; } } + if((this.chunkTree.getMaxLevel() - node.getLevel()) < minLeafLod){ + evaluationMap.put(node,true); + } } return updated; } @@ -398,7 +428,7 @@ public class ClientDrawCellManager { */ private void conditionalUpdateAdjacentNodes(FloatingChunkTreeNode node, int level){ //don't bother to check if it's a lowest-res chunk - if(this.chunkTree.getMaxLevel() - level > DrawCell.LOWEST_LOD){ + if(this.chunkTree.getMaxLevel() - level > ClientDrawCellManager.FULL_RES_LOD){ return; } if(node.getMinBound().x - 1 >= 0){ @@ -887,6 +917,36 @@ public class ClientDrawCellManager { return this.initialized; } + /** + * Gets the draw cell for a given world coordinate if it has been generated + * @param worldX The world x coordinate + * @param worldY The world y coordinate + * @param worldZ The world z coordinate + * @return The draw cell if it exists, null otherwise + */ + public DrawCell getDrawCell(int worldX, int worldY, int worldZ){ + FloatingChunkTreeNode node = this.chunkTree.search(new Vector3i(worldX,worldY,worldZ), false); + if(node != null){ + return node.getData(); + } + return null; + } + + /** + * Checks if physics has been generated for a given world coordinate + * @param worldX The world x coordinate + * @param worldY The world y coordinate + * @param worldZ The world z coordinate + * @return true if physics has been generated, false otherwise + */ + public boolean hasGeneratedPhysics(int worldX, int worldY, int worldZ){ + DrawCell cell = this.getDrawCell(worldX, worldY, worldZ); + if(cell != null && cell.getEntity() != null){ + return PhysicsEntityUtils.containsDBody(cell.getEntity()); + } + return false; + } + } diff --git a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java index d46a8719..9c8d2463 100644 --- a/src/main/java/electrosphere/client/terrain/cells/DrawCell.java +++ b/src/main/java/electrosphere/client/terrain/cells/DrawCell.java @@ -42,31 +42,6 @@ public class DrawCell { float[][][] weights = new float[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE]; int[][][] types = new int[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE]; - /** - * An invalid LOD level - */ - public static final int INVALID_LOD_LEVEL = -1; - - /** - * The maximum detail LOD level - */ - public static final int FULL_DETAIL_LOD = 0; - - /** - * The half detail lod level - */ - public static final int HALF_DETAIL_LOD = 1; - - /** - * The lowest available LOD - */ - public static final int LOWEST_LOD = HALF_DETAIL_LOD; - - /** - * The lod level of this draw cell - */ - int lodLevel = FULL_DETAIL_LOD; - /** * Tracks whether the draw cell has requested its chunk data or not */ @@ -139,9 +114,6 @@ public class DrawCell { } } } - if(lod == INVALID_LOD_LEVEL){ - throw new Error("Trying to generate invalid LOD"); - } modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(chunkData, lod, atlas, this.hasPolygons()); ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos(), new Quaterniond()); this.setHasGenerated(true); @@ -452,23 +424,6 @@ public class DrawCell { return true; } - - /** - * Gets the LOD level - * @return The LOD level - */ - public int getLodLevel() { - return lodLevel; - } - - /** - * Sets the LOD level - * @param lodLevel The LOD level - */ - public void setLodLevel(int lodLevel) { - this.lodLevel = lodLevel; - } - /** * Gets whether this draw cell has requested its chunk data or not * @return true if has requested, false otherwise diff --git a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java index 844c22da..e9d54391 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java @@ -42,7 +42,7 @@ public class ClientLoading { /** * The number of frames the draw cell is expected to take (minimum) to init */ - static final int DRAW_CELL_EXPECTED_MINIMUM_FRAMES_TO_INIT = 100; + static final int DRAW_CELL_EXPECTED_MINIMUM_FRAMES_TO_INIT = 10; protected static void loadCharacterServer(Object[] params){ @@ -313,7 +313,7 @@ public class ClientLoading { } } if(i < DRAW_CELL_EXPECTED_MINIMUM_FRAMES_TO_INIT){ - LoggerInterface.loggerEngine.WARNING("Probably didn't block for draw cell manager initialization!"); + LoggerInterface.loggerEngine.WARNING("Draw cell manager loaded exceptionally fast!"); } } diff --git a/src/main/java/electrosphere/engine/profiler/Profiler.java b/src/main/java/electrosphere/engine/profiler/Profiler.java index d9533338..e45bccf9 100644 --- a/src/main/java/electrosphere/engine/profiler/Profiler.java +++ b/src/main/java/electrosphere/engine/profiler/Profiler.java @@ -40,7 +40,7 @@ public class Profiler { } /** - * Begins an aggregate CPU sample (must create a regular cpu sample of same type outside function this is inside of to encapsule all calls to aggregate) + * Begins an aggregate CPU sample * @param sampleName The name of the sample */ public void beginAggregateCpuSample(String sampleName){ @@ -49,6 +49,16 @@ public class Profiler { } } + /** + * Begins an recursive CPU sample + * @param sampleName The name of the sample + */ + public void beginRecursiveCpuSample(String sampleName){ + if(PROFILE){ + Remotery.rmt_BeginCPUSample(sampleName, Remotery.RMTSF_Recursive, null); + } + } + /** * Begins a Root CPU sample (will assert if another sample is not ended before this one) * @param sampleName The name of the root sample diff --git a/src/main/java/electrosphere/entity/ServerEntityUtils.java b/src/main/java/electrosphere/entity/ServerEntityUtils.java index 5c962005..bea65a06 100644 --- a/src/main/java/electrosphere/entity/ServerEntityUtils.java +++ b/src/main/java/electrosphere/entity/ServerEntityUtils.java @@ -64,6 +64,9 @@ public class ServerEntityUtils { throw new Error("Trying to set server entity position to null!"); } Realm realm = Globals.realmManager.getEntityRealm(entity); + if(position.x < 0 || position.y < 0 || position.z < 0){ + throw new Error("Providing invalid location to reposition! " + position); + } //if server, get current server data cell ServerDataCell oldDataCell = Globals.entityDataCellMapper.getEntityDataCell(entity); ServerDataCell newDataCell = realm.getDataCellManager().getDataCellAtPoint(position); diff --git a/src/main/java/electrosphere/entity/scene/Scene.java b/src/main/java/electrosphere/entity/scene/Scene.java index b2455aa3..6ae9ca87 100644 --- a/src/main/java/electrosphere/entity/scene/Scene.java +++ b/src/main/java/electrosphere/entity/scene/Scene.java @@ -166,7 +166,7 @@ public class Scene { * Simulates all behavior trees stored in the entity manager */ public void simulateBehaviorTrees(float deltaTime){ - Globals.profiler.beginCpuSample("Scene.simulateBehaviorTrees"); + Globals.profiler.beginAggregateCpuSample("Scene.simulateBehaviorTrees"); for(BehaviorTree tree : behaviorTreeList){ tree.simulate(deltaTime); } diff --git a/src/main/java/electrosphere/entity/state/attach/AttachUtils.java b/src/main/java/electrosphere/entity/state/attach/AttachUtils.java index 29366cda..f4d5b271 100644 --- a/src/main/java/electrosphere/entity/state/attach/AttachUtils.java +++ b/src/main/java/electrosphere/entity/state/attach/AttachUtils.java @@ -47,7 +47,7 @@ public class AttachUtils { * @param cell The data cell */ public static void serverUpdateAttachedEntityPositions(ServerDataCell cell){ - Globals.profiler.beginCpuSample("AttachUtils.serverUpdateAttachedEntityPositions"); + Globals.profiler.beginAggregateCpuSample("AttachUtils.serverUpdateAttachedEntityPositions"); serverUpdateBoneAttachedEntityPositions(cell); serverUpdateNonBoneAttachments(cell); Globals.profiler.endCpuSample(); @@ -95,7 +95,7 @@ public class AttachUtils { * @param cell the data cell */ private static void serverUpdateNonBoneAttachments(ServerDataCell cell){ - Globals.profiler.beginCpuSample("AttachUtils.serverUpdateNonBoneAttachments"); + Globals.profiler.beginAggregateCpuSample("AttachUtils.serverUpdateNonBoneAttachments"); Matrix4d parentTransform = new Matrix4d().identity(); Vector3d position = new Vector3d(); Quaterniond rotation = new Quaterniond(); diff --git a/src/main/java/electrosphere/entity/state/movement/editor/ClientEditorMovementTree.java b/src/main/java/electrosphere/entity/state/movement/editor/ClientEditorMovementTree.java index 8276cc6f..4c627389 100644 --- a/src/main/java/electrosphere/entity/state/movement/editor/ClientEditorMovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/editor/ClientEditorMovementTree.java @@ -3,7 +3,6 @@ package electrosphere.entity.state.movement.editor; import electrosphere.entity.state.gravity.GravityUtils; import electrosphere.engine.Globals; -import electrosphere.entity.types.collision.CollisionObjUtils; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.game.data.creature.type.movement.EditorMovementSystem; import electrosphere.entity.Entity; @@ -259,7 +258,9 @@ public class ClientEditorMovementTree implements BehaviorTree { rotation.set(movementQuaternion); position.set(new Vector3d(position).add(new Vector3d(movementVector).mul(velocity))); if(serverEntity != null){ - ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position)); + if(position.x >= 0 && position.y >= 0 && position.z >= 0){ + ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position)); + } } GravityUtils.clientAttemptActivateGravity(parent); @@ -274,7 +275,9 @@ public class ClientEditorMovementTree implements BehaviorTree { rotation.set(movementQuaternion); position.set(new Vector3d(position).add(new Vector3d(movementVector).mul(velocity))); if(serverEntity != null){ - ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position)); + if(position.x >= 0 && position.y >= 0 && position.z >= 0){ + ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position)); + } } GravityUtils.clientAttemptActivateGravity(parent); @@ -295,7 +298,9 @@ public class ClientEditorMovementTree implements BehaviorTree { rotation.set(movementQuaternion); position.set(new Vector3d(position).add(new Vector3d(movementVector).mul(velocity))); if(serverEntity != null){ - ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position)); + if(position.x >= 0 && position.y >= 0 && position.z >= 0){ + ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position)); + } } GravityUtils.clientAttemptActivateGravity(parent); diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java index 2e237e61..cc62e734 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java @@ -3,9 +3,10 @@ package electrosphere.entity.types.terrain; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import org.joml.Quaterniond; import org.joml.Vector3d; -import electrosphere.client.terrain.cells.DrawCell; +import electrosphere.client.terrain.cells.ClientDrawCellManager; import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.client.terrain.manager.ClientTerrainManager; import electrosphere.collision.PhysicsEntityUtils; @@ -13,7 +14,10 @@ import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityDataStrings; +import electrosphere.entity.EntityUtils; import electrosphere.entity.ServerEntityUtils; +import electrosphere.entity.types.collision.CollisionObjUtils; +import electrosphere.logger.LoggerInterface; import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; @@ -27,7 +31,7 @@ public class TerrainChunk { /** * Used for generating terrain chunks */ - static ExecutorService generationService = Executors.newFixedThreadPool(2); + static final ExecutorService generationService = Executors.newFixedThreadPool(2); /** * Creates a client terrain chunk based on weights and values provided @@ -44,17 +48,22 @@ public class TerrainChunk { if(hasPolygons){ generationService.submit(() -> { TerrainChunkData data; - if(levelOfDetail == DrawCell.FULL_DETAIL_LOD){ - data = TerrainChunkModelGeneration.generateTerrainChunkData(chunkData.terrainGrid, chunkData.textureGrid); - } else { + try { data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData); - } - if(Globals.clientScene.containsEntity(rVal)){ - String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas); - EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); - if(levelOfDetail == DrawCell.FULL_DETAIL_LOD){ - PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data); + if(Globals.clientScene.containsEntity(rVal)){ + String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas); + EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); + if(levelOfDetail == ClientDrawCellManager.FULL_RES_LOD){ + PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data); + CollisionObjUtils.clientPositionCharacter(rVal, new Vector3d(EntityUtils.getPosition(rVal)), new Quaterniond()); + } + } else { + LoggerInterface.loggerEngine.WARNING("Finished generating terrain polygons; however, entity has already been deleted."); } + } catch (Error e){ + LoggerInterface.loggerEngine.ERROR(e); + } catch(Exception e){ + LoggerInterface.loggerEngine.ERROR(e); } }); } diff --git a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java index 45a1c6a2..9782670a 100644 --- a/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/TerrainProtocol.java @@ -14,6 +14,7 @@ import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.template.ClientProtocolTemplate; +import electrosphere.server.terrain.manager.ServerTerrainChunk; /** * The client protocol for handling terrain messages @@ -32,8 +33,16 @@ public class TerrainProtocol implements ClientProtocolTemplate { case RESPONSEMETADATA: Globals.clientWorldData = new ClientWorldData( //Vector3f worldMinPoint, Vector3f worldMaxPoint, int dynamicInterpolationRatio, float randomDampener, int worldDiscreteSize - new Vector3f(message.getworldMinX(),0,message.getworldMinY()), - new Vector3f(message.getworldMaxX(),32,message.getworldMaxY()), + new Vector3f( + message.getworldMinX(), + 0, + message.getworldMinY() + ), + new Vector3f( + message.getworldMaxX(), + (int)(message.getworldSizeDiscrete() * ServerTerrainChunk.CHUNK_DIMENSION), + message.getworldMaxY() + ), message.getrandomDampener(), message.getworldSizeDiscrete() ); diff --git a/src/main/java/electrosphere/server/MainServerFunctions.java b/src/main/java/electrosphere/server/MainServerFunctions.java index 3c4f665e..147d8747 100644 --- a/src/main/java/electrosphere/server/MainServerFunctions.java +++ b/src/main/java/electrosphere/server/MainServerFunctions.java @@ -12,6 +12,7 @@ public class MainServerFunctions { * Calls the main server routines that should fire each frame */ public static void simulate(){ + Globals.profiler.beginCpuSample("MainServerFunctions.simulate"); // //Cleanup disconnected clients @@ -21,12 +22,12 @@ public class MainServerFunctions { // //Synchronous player message parsing\ - Globals.profiler.beginCpuSample("Server synchronous packet parsing"); + Globals.profiler.beginCpuSample("MainServerFunctions.simulate - Server synchronous packet parsing"); if(Globals.server != null){ Globals.server.synchronousPacketHandling(); } Globals.profiler.endCpuSample(); - Globals.profiler.beginCpuSample("Server process synchronization messages"); + Globals.profiler.beginCpuSample("MainServerFunctions.simulate - Server process synchronization messages"); if(Globals.serverSynchronizationManager != null){ Globals.serverSynchronizationManager.processMessages(); } @@ -34,7 +35,7 @@ public class MainServerFunctions { // //Micro simulation (ie simulating each scene on the server) - Globals.profiler.beginCpuSample("Server micro simulation"); + Globals.profiler.beginCpuSample("MainServerFunctions.simulate - Server micro simulation"); LoggerInterface.loggerEngine.DEBUG_LOOP("Begin server micro simulation"); if(Globals.realmManager != null){ Globals.realmManager.simulate(); @@ -42,11 +43,13 @@ public class MainServerFunctions { // //Macro simulation (ie simulating the larger world macro data) - LoggerInterface.loggerEngine.DEBUG_LOOP("Server macro simulation"); + LoggerInterface.loggerEngine.DEBUG_LOOP("MainServerFunctions.simulate - Server macro simulation"); if(Globals.macroSimulation != null && Globals.macroSimulation.isReady()){ Globals.macroSimulation.simulate(); } Globals.profiler.endCpuSample(); + + Globals.profiler.endCpuSample(); } } diff --git a/src/main/java/electrosphere/server/content/ServerContentGenerator.java b/src/main/java/electrosphere/server/content/ServerContentGenerator.java index 58806514..149f7877 100644 --- a/src/main/java/electrosphere/server/content/ServerContentGenerator.java +++ b/src/main/java/electrosphere/server/content/ServerContentGenerator.java @@ -24,6 +24,12 @@ public class ServerContentGenerator { */ public static final int FOLIAGE_SEED = 0; + /** + * Adjustment applied to generated height value to approximate the height of the actual terrain. + * The voxels don't generate QUITE to the height of the heightmap, so this is applied to make the value line up better for entity placement. + */ + public static final float HEIGHT_MANUAL_ADJUSTMENT = -0.35f; + /** * Generates content for a given data cell * @param realm The realm @@ -55,7 +61,7 @@ public class ServerContentGenerator { if(foliageDescriptions != null){ for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ - double height = realm.getServerWorldData().getServerTerrainManager().getElevation(worldPos.x, worldPos.z, x, z); + double height = realm.getServerWorldData().getServerTerrainManager().getElevation(worldPos.x, worldPos.z, x, z) + HEIGHT_MANUAL_ADJUSTMENT; if( realm.getServerWorldData().convertVoxelToRealSpace(0, worldPos.y) <= height && realm.getServerWorldData().convertVoxelToRealSpace(ServerTerrainChunk.CHUNK_DIMENSION, worldPos.y) > height diff --git a/src/main/java/electrosphere/server/content/ServerContentManager.java b/src/main/java/electrosphere/server/content/ServerContentManager.java index f1822b37..6a89cfd8 100644 --- a/src/main/java/electrosphere/server/content/ServerContentManager.java +++ b/src/main/java/electrosphere/server/content/ServerContentManager.java @@ -1,7 +1,6 @@ package electrosphere.server.content; import java.util.Collection; -import java.util.List; import org.joml.Vector3i; diff --git a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java index 0e0a5f40..1801eaee 100644 --- a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java @@ -546,6 +546,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager * Calls the simulate function on all loaded cells */ public void simulate(){ + Globals.profiler.beginCpuSample("GriddedDataCellManager.simulate"); loadedCellsLock.acquireUninterruptibly(); for(ServerDataCell cell : this.groundDataCells.values()){ if(Globals.microSimulation != null && Globals.microSimulation.isReady()){ @@ -561,6 +562,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager } loadedCellsLock.release(); this.updatePlayerPositions(); + Globals.profiler.endCpuSample(); } @@ -748,11 +750,13 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager * @param worldPosition the world position */ private void rebroadcastFluidChunk(Vector3i worldPosition){ + Globals.profiler.beginAggregateCpuSample("GriddedDataCellManager.rebroadcastFluidChunk"); ServerDataCell cell = getCellAtWorldPosition(worldPosition); ServerFluidChunk chunk = getFluidChunkAtPosition(worldPosition); cell.broadcastNetworkMessage( TerrainMessage.constructupdateFluidDataMessage(worldPosition.x, worldPosition.y, worldPosition.z, TerrainProtocol.constructFluidByteBuffer(chunk).array()) ); + Globals.profiler.endCpuSample(); } @Override diff --git a/src/main/java/electrosphere/server/datacell/Realm.java b/src/main/java/electrosphere/server/datacell/Realm.java index a1af66dd..bfb1258c 100644 --- a/src/main/java/electrosphere/server/datacell/Realm.java +++ b/src/main/java/electrosphere/server/datacell/Realm.java @@ -187,6 +187,7 @@ public class Realm { * Tells the data cell manager to simulate all loaded cells */ protected void simulate(){ + Globals.profiler.beginCpuSample("Realm.simulate"); // //simulate bullet physics engine step if(Globals.RUN_PHYSICS){ @@ -209,6 +210,8 @@ public class Realm { //clear collidable impulse lists collisionEngine.clearCollidableImpulseLists(); chemistryEngine.clearCollidableImpulseLists(); + + Globals.profiler.endCpuSample(); } /** diff --git a/src/main/java/electrosphere/server/datacell/RealmManager.java b/src/main/java/electrosphere/server/datacell/RealmManager.java index 82b85346..d78ad165 100644 --- a/src/main/java/electrosphere/server/datacell/RealmManager.java +++ b/src/main/java/electrosphere/server/datacell/RealmManager.java @@ -158,9 +158,11 @@ public class RealmManager { * Simulates all realms in this manager */ public void simulate(){ + Globals.profiler.beginCpuSample("RealmManager.simulate"); for(Realm realm : realms){ realm.simulate(); } + Globals.profiler.endCpuSample(); } //TODO: !!URGENT!! come up with some mechanism to enforce this actually being called every time a player is added to a server data cell diff --git a/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java b/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java index fbacd15b..74315e0c 100644 --- a/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java +++ b/src/main/java/electrosphere/server/fluid/manager/ServerFluidManager.java @@ -1,6 +1,7 @@ package electrosphere.server.fluid.manager; +import electrosphere.engine.Globals; import electrosphere.game.server.world.ServerWorldData; import electrosphere.server.fluid.diskmap.FluidDiskMap; import electrosphere.server.fluid.generation.FluidGenerator; @@ -270,14 +271,17 @@ public class ServerFluidManager { * Simulates a chunk */ public boolean simulate(int worldX, int worldY, int worldZ){ + boolean rVal = false; + Globals.profiler.beginAggregateCpuSample("ServerFluidManager.simulate"); if(simulate){ ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ); ServerTerrainChunk terrainChunk = this.serverTerrainManager.getChunk(worldX, worldY, worldZ); if(fluidChunk != null && terrainChunk != null && this.serverFluidSimulator != null){ - return this.serverFluidSimulator.simulate(fluidChunk,terrainChunk,worldX,worldY,worldZ); + rVal = this.serverFluidSimulator.simulate(fluidChunk,terrainChunk,worldX,worldY,worldZ); } } - return false; + Globals.profiler.endCpuSample(); + return rVal; } //getter for simulate diff --git a/src/main/java/electrosphere/server/simulation/MicroSimulation.java b/src/main/java/electrosphere/server/simulation/MicroSimulation.java index f8447419..4d21e94e 100644 --- a/src/main/java/electrosphere/server/simulation/MicroSimulation.java +++ b/src/main/java/electrosphere/server/simulation/MicroSimulation.java @@ -28,6 +28,7 @@ public class MicroSimulation { * @param dataCell The data cell */ public void simulate(ServerDataCell dataCell){ + Globals.profiler.beginAggregateCpuSample("MicroSimulation.simulate"); if(dataCell.isReady()){ //simulate ai Globals.aiManager.simulate(); @@ -58,6 +59,7 @@ public class MicroSimulation { ServerCollidableTree.getServerCollidableTree(collidable).simulate((float)Globals.timekeeper.getSimFrameTime()); } } + Globals.profiler.endCpuSample(); } public boolean isReady(){ diff --git a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java index 34347ea9..dada56cd 100644 --- a/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java +++ b/src/main/java/electrosphere/server/terrain/generation/TestGenerationChunkGenerator.java @@ -225,7 +225,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator { ){ GeneratedVoxel voxel = new GeneratedVoxel(); voxel.weight = (float)(surfaceHeight - flooredSurfaceHeight) * 2 - 1; - voxel.type = 1; + voxel.type = 2; return voxel; } diff --git a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java index 7d391c54..a661302e 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java +++ b/src/main/java/electrosphere/server/terrain/manager/ChunkGenerationThread.java @@ -130,6 +130,8 @@ public class ChunkGenerationThread implements Runnable { this.onLoad.accept(chunk); } catch (Error e){ LoggerInterface.loggerEngine.ERROR(e); + } catch(Exception e){ + LoggerInterface.loggerEngine.ERROR(e); } } diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java index ee555ba4..662e63dd 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java @@ -28,7 +28,7 @@ public class ServerTerrainManager { /** * The number of threads for chunk generation */ - public static final int GENERATION_THREAD_POOL_SIZE = 1; + public static final int GENERATION_THREAD_POOL_SIZE = 2; /** * Full world discrete size @@ -76,7 +76,7 @@ public class ServerTerrainManager { * The threadpool for chunk generation */ @Exclude - ExecutorService chunkExecutorService = Executors.newFixedThreadPool(GENERATION_THREAD_POOL_SIZE); + static final ExecutorService chunkExecutorService = Executors.newFixedThreadPool(GENERATION_THREAD_POOL_SIZE); /** * Constructor @@ -225,7 +225,7 @@ public class ServerTerrainManager { * @return The ServerTerrainChunk */ public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){ - Globals.profiler.beginCpuSample("ServerTerrainManager.getChunk"); + Globals.profiler.beginAggregateCpuSample("ServerTerrainManager.getChunk"); //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING ServerTerrainChunk returnedChunk = null; if(chunkCache.containsChunk(worldX,worldY,worldZ,ChunkData.NO_STRIDE)){ @@ -256,7 +256,7 @@ public class ServerTerrainManager { * @return The ServerTerrainChunk */ public double getElevation(int worldX, int worldZ, int chunkX, int chunkZ){ - Globals.profiler.beginCpuSample("ServerTerrainManager.getChunk"); + Globals.profiler.beginAggregateCpuSample("ServerTerrainManager.getChunk"); double elevation = chunkGenerator.getElevation(worldX, worldZ, chunkX, chunkZ); Globals.profiler.endCpuSample(); return elevation; @@ -271,8 +271,8 @@ public class ServerTerrainManager { * @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.beginCpuSample("ServerTerrainManager.getChunkAsync"); - this.chunkExecutorService.submit(new ChunkGenerationThread(chunkDiskMap, chunkCache, chunkGenerator, worldX, worldY, worldZ, stride, onLoad)); + Globals.profiler.beginAggregateCpuSample("ServerTerrainManager.getChunkAsync"); + chunkExecutorService.submit(new ChunkGenerationThread(chunkDiskMap, chunkCache, chunkGenerator, worldX, worldY, worldZ, stride, onLoad)); Globals.profiler.endCpuSample(); } @@ -326,7 +326,7 @@ public class ServerTerrainManager { * Closes the generation threadpool */ public void closeThreads(){ - this.chunkExecutorService.shutdownNow(); + chunkExecutorService.shutdownNow(); } } diff --git a/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java b/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java index e98db024..177b2e89 100644 --- a/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java +++ b/src/test/java/electrosphere/client/terrain/cells/ClientDrawCellManagerTests.java @@ -42,7 +42,7 @@ public class ClientDrawCellManagerTests { FloatingChunkTreeNode node = FloatingChunkTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16)); node.convertToLeaf(DrawCell.generateTerrainCell(new Vector3i(0,0,0))); - assertFalse(manager.shouldSplit(playerPos, node)); + assertTrue(manager.shouldSplit(playerPos, node)); //cleanup Main.shutdown();