From c71f4fecadf291154df87587e75b4b42fb66d3bf Mon Sep 17 00:00:00 2001 From: austin Date: Fri, 14 Jul 2023 22:10:38 -0400 Subject: [PATCH] Physics fixes --- buildNumber.properties | 3 + pom.xml | 8 + saves/default/chunk.map | 1 + .../collision/CollisionEngine.java | 84 +++---- .../electrosphere/collision/PhysicsUtils.java | 33 +-- .../controls/ControlHandler.java | 2 +- .../loadingthreads/DebugSPWorldLoading.java | 31 +-- .../engine/loadingthreads/LoadingUtils.java | 3 +- .../engine/loadingthreads/ServerLoading.java | 3 +- .../collidable/ClientCollidableTree.java | 8 +- .../collidable/ServerCollidableTree.java | 9 +- .../state/gravity/ClientGravityTree.java | 3 - .../state/gravity/ServerGravityTree.java | 3 - .../state/movement/GroundMovementTree.java | 124 ++++------- .../movement/ServerGroundMovementTree.java | 108 ++++----- .../electrosphere/menu/MenuGenerators.java | 3 +- .../datacell/GriddedDataCellManager.java | 34 ++- .../electrosphere/server/saves/SaveUtils.java | 4 +- .../server/simulation/MicroSimulation.java | 9 - .../terrain/diskcache/ChunkDiskCache.java | 92 -------- .../server/terrain/diskmap/ChunkDiskMap.java | 205 ++++++++++++++++++ .../generation/ArenaChunkGenerator.java | 43 ++++ .../generation/OverworldChunkGenerator.java | 88 ++++++++ .../generation/interfaces/ChunkGenerator.java | 26 +++ .../terrain/manager/ServerTerrainChunk.java | 32 --- .../terrain/manager/ServerTerrainManager.java | 155 ++++++------- .../java/electrosphere/util/FileUtils.java | 20 +- .../util/worldviewer/TerrainViewer.java | 3 +- 28 files changed, 636 insertions(+), 501 deletions(-) create mode 100644 buildNumber.properties create mode 100644 saves/default/chunk.map delete mode 100644 src/main/java/electrosphere/server/terrain/diskcache/ChunkDiskCache.java create mode 100644 src/main/java/electrosphere/server/terrain/diskmap/ChunkDiskMap.java create mode 100644 src/main/java/electrosphere/server/terrain/generation/ArenaChunkGenerator.java create mode 100644 src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java create mode 100644 src/main/java/electrosphere/server/terrain/generation/interfaces/ChunkGenerator.java diff --git a/buildNumber.properties b/buildNumber.properties new file mode 100644 index 00000000..96a66728 --- /dev/null +++ b/buildNumber.properties @@ -0,0 +1,3 @@ +#maven.buildNumber.plugin properties file +#Fri Jul 14 20:48:01 EDT 2023 +buildNumber=4 diff --git a/pom.xml b/pom.xml index d1fa0bbd..af5b282c 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,14 @@ 1.9.19 + + + scm:svn:http://127.0.0.1/dummy + scm:svn:https://127.0.0.1/dummy + HEAD + http://127.0.0.1/dummy + + diff --git a/saves/default/chunk.map b/saves/default/chunk.map new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/saves/default/chunk.map @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index 3c37ea6d..28dd5b6a 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -49,10 +49,10 @@ import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.collidable.Impulse; import electrosphere.entity.types.hitbox.HitboxData; +import electrosphere.logger.LoggerInterface; /** * - * TODO: https://stackoverflow.com/questions/32445679/3d-java-collision-detection-with-jbullet */ public class CollisionEngine { @@ -85,58 +85,6 @@ public class CollisionEngine { world = OdeHelper.createWorld(); space = OdeHelper.createHashSpace(); contactgroup = OdeHelper.createJointGroup(); - - -// callback = new InternalTickCallback(){ -// @Override -// public void internalTick(DynamicsWorld dw, float f) { -// Dispatcher dispatcher = dw.getDispatcher(); -// int manifoldCount = dispatcher.getNumManifolds(); -// for (int i = 0; i < manifoldCount; i++) { -// PersistentManifold manifold = dispatcher.getManifoldByIndexInternal(i); -// // The following two lines are optional. -// CollisionObject object1 = (CollisionObject)manifold.getBody0(); -// CollisionObject object2 = (CollisionObject)manifold.getBody1(); -// Collidable physicsObject1 = (Collidable)object1.getUserPointer(); -// Collidable physicsObject2 = (Collidable)object2.getUserPointer(); -// boolean hit = false; -// Vector3d normal = null; -// Vector3d localPosition1 = null; -// Vector3d localPosition2 = null; -// Vector3d worldPosA = null; -// Vector3d worldPosB = null; -// float magnitude = 0.0f; -// for (int j = 0; j < manifold.getNumContacts(); j++) { -// ManifoldPoint contactPoint = manifold.getContactPoint(j); -// if (contactPoint.getDistance() < 0.0f) { -// magnitude = -contactPoint.getDistance(); -// //linear dampen -// // magnitude = magnitude;// * (float)Math.pow(1.0f - linearDamping,deltaTime * 2); -// hit = true; -// // System.out.println(contactPoint.positionWorldOnA + " " + contactPoint.positionWorldOnB); -// normal = new Vector3d(contactPoint.normalWorldOnB.x,contactPoint.normalWorldOnB.y,contactPoint.normalWorldOnB.z); -// localPosition1 = new Vector3d(contactPoint.localPointA.x,contactPoint.localPointA.y,contactPoint.localPointA.z); -// localPosition2 = new Vector3d(contactPoint.localPointB.x,contactPoint.localPointB.y,contactPoint.localPointB.z); -// worldPosA = new Vector3d(contactPoint.positionWorldOnA.x,contactPoint.positionWorldOnA.y,contactPoint.positionWorldOnA.z); -// worldPosB = new Vector3d(contactPoint.positionWorldOnB.x,contactPoint.positionWorldOnB.y,contactPoint.positionWorldOnB.z); -// break; -// } -// } -// if (hit) { -// resolveCollision(physicsObject1,physicsObject2, new Vector3d(normal).mul(-1.0), localPosition1, worldPosA, magnitude); -// resolveCollision(physicsObject2,physicsObject1, normal, localPosition2, worldPosB, magnitude); -// // System.out.println("HIT + " + normal); -// // Collision happened between physicsObject1 and physicsObject2. Collision normal is in variable 'normal'. -// } -// } -// } -// }; -// world.setInternalTickCallback(callback, world); - - //https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=11507 - //https://www.google.com/search?client=firefox-b-1-d&q=bullet+set+position+and+check+if+collision - //https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=11399 - } @@ -457,8 +405,8 @@ public class CollisionEngine { public void listBodyPositions(){ for(DBody body : bodies){ - System.out.println(body); - System.out.println(PhysicsUtils.getRigidBodyPosition(body)); + LoggerInterface.loggerEngine.INFO("" + body); + LoggerInterface.loggerEngine.INFO("" + PhysicsUtils.getRigidBodyPosition(body)); } } @@ -740,5 +688,31 @@ public class CollisionEngine { body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); spaceLock.release(); } + + /** + * Gets the position of the body in a thread-safe way + * @param body The body to get the position of + * @return The position + */ + protected Vector3d getBodyPosition(DBody body){ + Vector3d rVal = null; + spaceLock.acquireUninterruptibly(); + rVal = PhysicsUtils.odeVecToJomlVec(body.getPosition()); + spaceLock.release(); + return rVal; + } + + /** + * Gets the rotation of the body in a thread-safe way + * @param body The body to get the rotation of + * @return The rotation + */ + protected Quaterniond getBodyRotation(DBody body){ + Quaterniond rVal = null; + spaceLock.acquireUninterruptibly(); + rVal = PhysicsUtils.odeQuatToJomlQuat(body.getQuaternion()); + spaceLock.release(); + return rVal; + } } diff --git a/src/main/java/electrosphere/collision/PhysicsUtils.java b/src/main/java/electrosphere/collision/PhysicsUtils.java index aaad2567..305541da 100644 --- a/src/main/java/electrosphere/collision/PhysicsUtils.java +++ b/src/main/java/electrosphere/collision/PhysicsUtils.java @@ -18,7 +18,6 @@ import org.ode4j.ode.DMass; import org.ode4j.ode.DPlane; import org.ode4j.ode.DSphere; import org.ode4j.ode.DTriMesh; -import org.ode4j.ode.OdeHelper; import electrosphere.collision.collidable.Collidable; import electrosphere.engine.Globals; @@ -105,20 +104,13 @@ public class PhysicsUtils { } - DBody body = OdeHelper.createBody(null); DTriMesh triMesh = collisionEngine.createTrimeshGeom(vertices,indices); - triMesh.setBody(body); - -// terrainRigidBody.setFriction(1f); + + DBody body = collisionEngine.createDBody(triMesh); Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(body, new Collidable(terrain,Collidable.TYPE_TERRAIN)); -// terrainRigidBody.getAabb(aabbMin, aabbMax); -// -// System.out.println("aabbMin: " + aabbMin + " aabbMax: " + aabbMax); - - terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, body); return body; @@ -200,7 +192,7 @@ public class PhysicsUtils { */ public static DBody generateRigidBodyFromAIScene(CollisionEngine collisionEngine, AIScene scene){ - DBody body = OdeHelper.createBody(collisionEngine.getDWorld()); + DBody body = collisionEngine.createDBody(null); PointerBuffer meshesBuffer = scene.mMeshes(); while(meshesBuffer.hasRemaining()){ @@ -244,14 +236,6 @@ public class PhysicsUtils { return body; } - /** - * Adds a test plane body to the engine scene - * @param engine The engine - */ - public static void addTestPlaneRigidBody(CollisionEngine engine){ - DPlane plane = OdeHelper.createPlane(engine.getSpace(), 0, 1, 0, 0); - } - /** * Gets the current position of a rigid body as a joml vector * @param body The dbody @@ -308,17 +292,6 @@ public class PhysicsUtils { collisionEngine.setBodyTransform(body, position, rotation); } -// public static RigidBody getUnitCylinderRigidBody(float mass){ -// CylinderShape cylinderShape = new CylinderShape(jomlToVecmathVector3f(new Vector3f(1.0f,1.0f,1.0f))); -// DefaultMotionState defaultMotionState = new DefaultMotionState(new Transform(new javax.vecmath.Matrix4f(new javax.vecmath.Quat4f(0,1,0,1),new javax.vecmath.Vector3f(0,0,0),1.0f))); -// RigidBodyConstructionInfo cylinderRigidBodyCI = new RigidBodyConstructionInfo(mass, defaultMotionState, cylinderShape); -// RigidBody cylinderRigidBody = new RigidBody(cylinderRigidBodyCI); -//// cylinderRigidBody.setMassProps(mass, PhysicsUtils.jomlToVecmathVector3f(new Vector3f(1.0f,1.0f,1.0f))); -// cylinderRigidBody.clearForces(); -// return cylinderRigidBody; -// } - - //The width of a plane rigid body //It's really a box under the hood static final double PLANE_WIDTH = 0.3; diff --git a/src/main/java/electrosphere/controls/ControlHandler.java b/src/main/java/electrosphere/controls/ControlHandler.java index 8dad1953..23980b31 100644 --- a/src/main/java/electrosphere/controls/ControlHandler.java +++ b/src/main/java/electrosphere/controls/ControlHandler.java @@ -1011,7 +1011,7 @@ public class ControlHandler { void setAlwaysOnDebugControls(){ alwaysOnDebugControlList.add(controls.get(DEBUG_OPEN_DEBUG_MENU)); controls.get(DEBUG_OPEN_DEBUG_MENU).setOnPress(new ControlMethod(){public void execute(){ - System.out.println("open debug menu"); + LoggerInterface.loggerEngine.INFO("open debug menu"); // Window mainMenuWindow = new Window(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); Window mainMenuInGame = MenuGeneratorsDebug.createTopLevelDebugMenu(); // mainMenuWindow.addChild(mainMenuInGame); diff --git a/src/main/java/electrosphere/engine/loadingthreads/DebugSPWorldLoading.java b/src/main/java/electrosphere/engine/loadingthreads/DebugSPWorldLoading.java index 99ce0155..2332cc6c 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/DebugSPWorldLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/DebugSPWorldLoading.java @@ -17,6 +17,7 @@ import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.server.ServerConnectionHandler; import electrosphere.renderer.ui.Window; import electrosphere.server.saves.SaveUtils; +import electrosphere.server.terrain.generation.OverworldChunkGenerator; import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.util.FileUtils; import electrosphere.controls.ControlHandler; @@ -31,7 +32,7 @@ public class DebugSPWorldLoading { WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu()); loadingWindow.setVisible(true); - Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0); + Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0, new OverworldChunkGenerator()); if(!SaveUtils.getSaves().contains("random_sp_world")){ // //the juicy server GENERATION part @@ -95,32 +96,4 @@ public class DebugSPWorldLoading { ClientLoading.loadClientWorld(); } - - private static void initWorldBaseGraphicalEntities(){ - /* - - Skybox - - */ - // Model skyboxModel = Globals.assetManager.fetchModel(AssetDataStrings.ASSET_STRING_SKYBOX_BASIC); - // Globals.skybox = EntityUtils.spawnDrawableEntity(AssetDataStrings.ASSET_STRING_SKYBOX_BASIC); - - - /* - - Player Camera - - */ - Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityTrackingCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,1)); - // Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityAirplaneTrackingCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,1)); - - - - /* - Targeting crosshair - */ - Crosshair.initCrossHairEntity(); - - } - } diff --git a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java index 70e9850c..ff888757 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java +++ b/src/main/java/electrosphere/engine/loadingthreads/LoadingUtils.java @@ -41,6 +41,7 @@ import electrosphere.server.datacell.Realm; import electrosphere.server.saves.SaveUtils; import electrosphere.server.simulation.MacroSimulation; import electrosphere.server.simulation.MicroSimulation; +import electrosphere.server.terrain.generation.OverworldChunkGenerator; import electrosphere.server.terrain.manager.ServerTerrainManager; /** @@ -60,7 +61,7 @@ public class LoadingUtils { Actually initialize the terrain manager */ float randomDampener = 0.0f; //0.25f; - Globals.serverTerrainManager = new ServerTerrainManager(2000,50,randomDampener,0); + Globals.serverTerrainManager = new ServerTerrainManager(2000,50,randomDampener,0,new OverworldChunkGenerator()); if(Globals.RUN_SERVER){ if(Globals.userSettings.gameplayGenerateWorld()){ Globals.serverTerrainManager.generate(); diff --git a/src/main/java/electrosphere/engine/loadingthreads/ServerLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ServerLoading.java index 370eb9d2..f4a053e4 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ServerLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ServerLoading.java @@ -3,6 +3,7 @@ package electrosphere.engine.loadingthreads; import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; import electrosphere.server.saves.SaveUtils; +import electrosphere.server.terrain.generation.OverworldChunkGenerator; import electrosphere.server.terrain.manager.ServerTerrainManager; public class ServerLoading { @@ -15,7 +16,7 @@ public class ServerLoading { // } //TODO: Globals.serverTerrainManager = new ServerTerrainManager(2000,50,100,randomDampener,0); - Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0); + Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0, new OverworldChunkGenerator()); SaveUtils.loadSave(Globals.currentSaveName); // LoadingUtils.initTerrainDataCellManager(); //TODO: set spawnpoint diff --git a/src/main/java/electrosphere/entity/state/collidable/ClientCollidableTree.java b/src/main/java/electrosphere/entity/state/collidable/ClientCollidableTree.java index 0c084ef3..13e4afd7 100644 --- a/src/main/java/electrosphere/entity/state/collidable/ClientCollidableTree.java +++ b/src/main/java/electrosphere/entity/state/collidable/ClientCollidableTree.java @@ -201,8 +201,12 @@ public class ClientCollidableTree implements BehaviorTree { //update collision engine of this thing's position CollidableTemplate template = (CollidableTemplate)parent.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE); - body.setPosition(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()); - body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); + PhysicsUtils.setRigidBodyTransform( + Globals.clientSceneWrapper.getCollisionEngine(), + new Vector3d(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()), + rotation, + body + ); } /** diff --git a/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java b/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java index d5e8aa00..21e756e9 100644 --- a/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java +++ b/src/main/java/electrosphere/entity/state/collidable/ServerCollidableTree.java @@ -207,9 +207,12 @@ public class ServerCollidableTree implements BehaviorTree { //update collision engine of this thing's position CollidableTemplate template = (CollidableTemplate)parent.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE); - body.setPosition(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()); - body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); - + PhysicsUtils.setRigidBodyTransform( + parentRealm.getCollisionEngine(), + new Vector3d(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()), + rotation, + body + ); } diff --git a/src/main/java/electrosphere/entity/state/gravity/ClientGravityTree.java b/src/main/java/electrosphere/entity/state/gravity/ClientGravityTree.java index c19fc27c..65040773 100644 --- a/src/main/java/electrosphere/entity/state/gravity/ClientGravityTree.java +++ b/src/main/java/electrosphere/entity/state/gravity/ClientGravityTree.java @@ -181,9 +181,6 @@ public class ClientGravityTree implements BehaviorTree { // hitFraction = 1; // } collidable.addImpulse(new Impulse(new Vector3d(0,-1,0), new Vector3d(0,0,0), new Vector3d(position), gravityDif,"gravity")); -// System.out.println(hitFraction); -// bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(new Vector3f((float)position.x,(float)position.y,(float)position.z)),1.0f); -// body.setWorldTransform(new electrosphere.linearmath.Transform(bodyTransformMatrix)); } break; case NOT_ACTIVE: diff --git a/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java b/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java index 541fe026..bf62128a 100644 --- a/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java +++ b/src/main/java/electrosphere/entity/state/gravity/ServerGravityTree.java @@ -175,9 +175,6 @@ public class ServerGravityTree implements BehaviorTree { // hitFraction = 1; // } collidable.addImpulse(new Impulse(new Vector3d(0,-1,0), new Vector3d(0,0,0), new Vector3d(position), gravityDif,"gravity")); -// System.out.println(hitFraction); -// bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(new Vector3f((float)position.x,(float)position.y,(float)position.z)),1.0f); -// body.setWorldTransform(new electrosphere.linearmath.Transform(bodyTransformMatrix)); } break; case NOT_ACTIVE: diff --git a/src/main/java/electrosphere/entity/state/movement/GroundMovementTree.java b/src/main/java/electrosphere/entity/state/movement/GroundMovementTree.java index 6e4b7b50..0a42cec7 100644 --- a/src/main/java/electrosphere/entity/state/movement/GroundMovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/GroundMovementTree.java @@ -99,27 +99,25 @@ public class GroundMovementTree implements BehaviorTree { this.facing = facing; state = MovementTreeState.STARTUP; //if we aren't the server, alert the server we intend to walk forward - if(!Globals.RUN_SERVER){ - Vector3d position = EntityUtils.getPosition(parent); - Quaterniond rotation = EntityUtils.getRotation(parent); - Vector3d facingVector = CreatureUtils.getFacingVector(parent); - float velocity = CreatureUtils.getVelocity(parent); - Globals.clientConnection.queueOutgoingMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - Main.getCurrentFrame(), - position.x, - position.y, - position.z, - rotation.x, - rotation.y, - rotation.z, - rotation.w, - velocity, - 0 //magic number corresponding to state startup - ) - ); - } + Vector3d position = EntityUtils.getPosition(parent); + Quaterniond rotation = EntityUtils.getRotation(parent); + Vector3d facingVector = CreatureUtils.getFacingVector(parent); + float velocity = CreatureUtils.getVelocity(parent); + Globals.clientConnection.queueOutgoingMessage( + EntityMessage.constructmoveUpdateMessage( + Globals.clientSceneWrapper.mapClientToServerId(parent.getId()), + Main.getCurrentFrame(), + position.x, + position.y, + position.z, + rotation.x, + rotation.y, + rotation.z, + rotation.w, + velocity, + 0 //magic number corresponding to state startup + ) + ); } } @@ -131,27 +129,25 @@ public class GroundMovementTree implements BehaviorTree { public void slowdown(){ state = MovementTreeState.SLOWDOWN; //if we aren't the server, alert the server we intend to slow down - if(!Globals.RUN_SERVER){ - Vector3d position = EntityUtils.getPosition(parent); - Quaterniond rotation = EntityUtils.getRotation(parent); - Vector3d facingVector = CreatureUtils.getFacingVector(parent); - float velocity = CreatureUtils.getVelocity(parent); - Globals.clientConnection.queueOutgoingMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - Main.getCurrentFrame(), - position.x, - position.y, - position.z, - rotation.x, - rotation.y, - rotation.z, - rotation.w, - velocity, - 2 //magic number corresponding to state slowdown - ) - ); - } + Vector3d position = EntityUtils.getPosition(parent); + Quaterniond rotation = EntityUtils.getRotation(parent); + Vector3d facingVector = CreatureUtils.getFacingVector(parent); + float velocity = CreatureUtils.getVelocity(parent); + Globals.clientConnection.queueOutgoingMessage( + EntityMessage.constructmoveUpdateMessage( + Globals.clientSceneWrapper.mapClientToServerId(parent.getId()), + Main.getCurrentFrame(), + position.x, + position.y, + position.z, + rotation.x, + rotation.y, + rotation.z, + rotation.w, + velocity, + 2 //magic number corresponding to state slowdown + ) + ); } public void simulate(float deltaTime){ @@ -233,15 +229,14 @@ public class GroundMovementTree implements BehaviorTree { // System.out.println(EntityUtils.getEntityPosition(parent)); // System.out.println(message.getpositionX() + " " + message.getpositionY() + " " + message.getpositionZ()); //this should only fire on the client, we don't want the server snap updating due to client position reporting - if(!Globals.RUN_SERVER){ - if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD){ - EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ()); - } else if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD){ - EntityUtils.getPosition(parent).add(new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ()).mul(SOFT_UPDATE_MULTIPLIER)); - } + if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD){ + EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ()); + } else if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD){ + EntityUtils.getPosition(parent).add(new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ()).mul(SOFT_UPDATE_MULTIPLIER)); } //we want to always update the server facing vector with where the client says they're facing - CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ())); + EntityUtils.getRotation(parent).set(message.getrotationX(),message.getrotationY(),message.getrotationZ(),message.getrotationW()); + // CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ())); break; } break; @@ -274,14 +269,6 @@ public class GroundMovementTree implements BehaviorTree { state = MovementTreeState.MOVE; } CreatureUtils.setVelocity(parent, velocity); -// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector.x,0,movementVector.z).normalize().mul(velocity))); - // EntityUtils.getRotation(parent).set(movementQuaternion); -// //move the entity -// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime)); -// //check/update if collision -// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ -// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); -// } // //actually update collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement")); // position.set(newPosition); @@ -308,14 +295,6 @@ public class GroundMovementTree implements BehaviorTree { velocity = maxNaturalVelocity; CreatureUtils.setVelocity(parent, velocity); } -// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(force)); - // EntityUtils.getRotation(parent).set(movementQuaternion); - //check if can move forward (collision engine) - //if can, move forward by entity movement stats -// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime)); -// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ -// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); -// } collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement")); // position.set(newPosition); rotation.set(movementQuaternion); @@ -350,13 +329,6 @@ public class GroundMovementTree implements BehaviorTree { } } CreatureUtils.setVelocity(parent, velocity); -// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector).mul(-1.0f).normalize().mul(velocity))); - // EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), new Vector3f((float)movementVector.x,(float)movementVector.y,(float)movementVector.z)); - //move the entity -// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime)); -// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ -// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); -// } collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement")); // position.set(newPosition); rotation.set(movementQuaternion); @@ -365,14 +337,6 @@ public class GroundMovementTree implements BehaviorTree { break; case IDLE: -// body.clearForces(); - -// if(Globals.collisionEngine.gravityCheck(Globals.commonWorldData, parent)){ -// position.set(Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData,parent,new Vector3f(position.x,position.y - 9.8f,position.z))); -// } -// position.set(new Vector3f(position.x,position.y - 0.08f,position.z)); -// bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(position),1.0f); -// body.setWorldTransform(new com.bulletphysics.linearmath.Transform(bodyTransformMatrix)); break; } } diff --git a/src/main/java/electrosphere/entity/state/movement/ServerGroundMovementTree.java b/src/main/java/electrosphere/entity/state/movement/ServerGroundMovementTree.java index d7816d40..4b24793a 100644 --- a/src/main/java/electrosphere/entity/state/movement/ServerGroundMovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/ServerGroundMovementTree.java @@ -99,44 +99,11 @@ public class ServerGroundMovementTree implements BehaviorTree { this.facing = facing; state = MovementTreeState.STARTUP; //if we aren't the server, alert the server we intend to walk forward - if(!Globals.RUN_SERVER){ - Vector3d position = EntityUtils.getPosition(parent); - Quaterniond rotation = EntityUtils.getRotation(parent); - Vector3d facingVector = CreatureUtils.getFacingVector(parent); - float velocity = CreatureUtils.getVelocity(parent); - Globals.clientConnection.queueOutgoingMessage( - EntityMessage.constructmoveUpdateMessage( - parent.getId(), - Main.getCurrentFrame(), - position.x, - position.y, - position.z, - rotation.x, - rotation.y, - rotation.z, - rotation.w, - velocity, - 0 //magic number corresponding to state startup - ) - ); - } - } - } - - public void interrupt(){ - state = MovementTreeState.IDLE; - CreatureUtils.setVelocity(parent, 0); - } - - public void slowdown(){ - state = MovementTreeState.SLOWDOWN; - //if we aren't the server, alert the server we intend to slow down - if(!Globals.RUN_SERVER){ Vector3d position = EntityUtils.getPosition(parent); Quaterniond rotation = EntityUtils.getRotation(parent); Vector3d facingVector = CreatureUtils.getFacingVector(parent); float velocity = CreatureUtils.getVelocity(parent); - Globals.clientConnection.queueOutgoingMessage( + DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage( EntityMessage.constructmoveUpdateMessage( parent.getId(), Main.getCurrentFrame(), @@ -148,12 +115,41 @@ public class ServerGroundMovementTree implements BehaviorTree { rotation.z, rotation.w, velocity, - 2 //magic number corresponding to state slowdown + 0 //magic number corresponding to state startup ) ); } } + public void interrupt(){ + state = MovementTreeState.IDLE; + CreatureUtils.setVelocity(parent, 0); + } + + public void slowdown(){ + state = MovementTreeState.SLOWDOWN; + //if we aren't the server, alert the server we intend to slow down + Vector3d position = EntityUtils.getPosition(parent); + Quaterniond rotation = EntityUtils.getRotation(parent); + Vector3d facingVector = CreatureUtils.getFacingVector(parent); + float velocity = CreatureUtils.getVelocity(parent); + DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage( + EntityMessage.constructmoveUpdateMessage( + parent.getId(), + Main.getCurrentFrame(), + position.x, + position.y, + position.z, + rotation.x, + rotation.y, + rotation.z, + rotation.w, + velocity, + 2 //magic number corresponding to state slowdown + ) + ); + } + public void simulate(float deltaTime){ float velocity = CreatureUtils.getVelocity(parent); float acceleration = CreatureUtils.getAcceleration(parent); @@ -201,11 +197,11 @@ public class ServerGroundMovementTree implements BehaviorTree { long updateTime = message.gettime(); // System.out.println("MOVE to " + message.getX() + " " + message.getY() + " " + message.getZ()); switch(message.getMessageSubtype()){ - case MOVE: - break; + case MOVE: { + } break; case SETFACING: break; - case MOVEUPDATE: + case MOVEUPDATE: { if(updateTime > lastUpdateTime){ lastUpdateTime = updateTime; switch(message.gettreeState()){ @@ -230,10 +226,11 @@ public class ServerGroundMovementTree implements BehaviorTree { break; } //we want to always update the server facing vector with where the client says they're facing - CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ())); + EntityUtils.getRotation(parent).set(message.getrotationX(),message.getrotationY(),message.getrotationZ(),message.getrotationW()); + CreatureUtils.setFacingVector(parent, new Vector3d(0,0,1).rotate(EntityUtils.getRotation(parent))); break; } - break; + } break; default: break; } @@ -263,14 +260,6 @@ public class ServerGroundMovementTree implements BehaviorTree { state = MovementTreeState.MOVE; } CreatureUtils.setVelocity(parent, velocity); -// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector.x,0,movementVector.z).normalize().mul(velocity))); - // EntityUtils.getRotation(parent).set(movementQuaternion); -// //move the entity -// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime)); -// //check/update if collision -// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ -// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); -// } // //actually update collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement")); // position.set(newPosition); @@ -312,14 +301,6 @@ public class ServerGroundMovementTree implements BehaviorTree { velocity = maxNaturalVelocity; CreatureUtils.setVelocity(parent, velocity); } -// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(force)); - // EntityUtils.getRotation(parent).set(movementQuaternion); - //check if can move forward (collision engine) - //if can, move forward by entity movement stats -// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime)); -// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ -// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); -// } collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement")); // position.set(newPosition); rotation.set(movementQuaternion); @@ -369,13 +350,6 @@ public class ServerGroundMovementTree implements BehaviorTree { } } CreatureUtils.setVelocity(parent, velocity); -// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector).mul(-1.0f).normalize().mul(velocity))); - // EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), new Vector3f((float)movementVector.x,(float)movementVector.y,(float)movementVector.z)); - //move the entity -// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime)); -// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){ -// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition); -// } collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement")); // position.set(newPosition); rotation.set(movementQuaternion); @@ -399,14 +373,6 @@ public class ServerGroundMovementTree implements BehaviorTree { ); break; case IDLE: -// body.clearForces(); - -// if(Globals.collisionEngine.gravityCheck(Globals.commonWorldData, parent)){ -// position.set(Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData,parent,new Vector3f(position.x,position.y - 9.8f,position.z))); -// } -// position.set(new Vector3f(position.x,position.y - 0.08f,position.z)); -// bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(position),1.0f); -// body.setWorldTransform(new com.bulletphysics.linearmath.Transform(bodyTransformMatrix)); break; } } diff --git a/src/main/java/electrosphere/menu/MenuGenerators.java b/src/main/java/electrosphere/menu/MenuGenerators.java index f6a237c4..5e68f0dc 100644 --- a/src/main/java/electrosphere/menu/MenuGenerators.java +++ b/src/main/java/electrosphere/menu/MenuGenerators.java @@ -40,6 +40,7 @@ import electrosphere.renderer.ui.events.NavigationEvent; import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.form.FormElement; import electrosphere.server.saves.SaveUtils; +import electrosphere.server.terrain.generation.OverworldChunkGenerator; import electrosphere.server.terrain.manager.ServerTerrainManager; /** @@ -130,7 +131,7 @@ public class MenuGenerators { //create save dir SaveUtils.createOrOverwriteSave(saveName); //create and save terrain - ServerTerrainManager terrainManager = new ServerTerrainManager(2000,50,0.0f,0); + ServerTerrainManager terrainManager = new ServerTerrainManager(2000,50,0.0f,0,new OverworldChunkGenerator()); terrainManager.generate(); terrainManager.save(SaveUtils.deriveSaveDirectoryPath(saveName)); WindowUtils.replaceMainMenuContents(MenuGenerators.createWorldSelectMenu()); diff --git a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java index 42f72589..2613c2fa 100644 --- a/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/GriddedDataCellManager.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Semaphore; import org.joml.Vector3d; @@ -29,6 +30,10 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //these are going to be the natural ground grid of data cells, but we're going to have more than this Map groundDataCells = new HashMap(); Map cellPositionMap = new HashMap(); + //Map of server data cell to the number of frames said cell has had no players + Map cellPlayerlessFrameMap = new HashMap(); + //The number of frames without players that must pass before a server data cell is unloaded + static final int UNLOAD_FRAME_THRESHOLD = 100; //loaded cells Semaphore loadedCellsLock = new Semaphore(1); Set loadedCells; @@ -55,7 +60,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager */ public void init(ServerWorldData data){ discreteWorldSize = data.getWorldSizeDiscrete(); - loadedCells = new HashSet(); + loadedCells = new CopyOnWriteArraySet(); } /** @@ -87,6 +92,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //add to loaded cells loadedCellsLock.acquireUninterruptibly(); loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos))); + cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0); loadedCellsLock.release(); //generate/handle content for new server data cell @@ -108,9 +114,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager int playerSimulationRadius = player.getSimulationRadius(); Vector3i oldPosition = player.getWorldPos(); player.setWorldPos(newPosition); -// System.out.println("=======" + "SET" + newX + " " + newY + " FROM " + oldX + " " + oldY + "========"); - // int removals = 0; - // int additions = 0; + // System.out.println("=======" + "SET" + newX + " " + newY + " FROM " + oldX + " " + oldY + "========"); + int removals = 0; + int additions = 0; for(int x = oldPosition.x - playerSimulationRadius; x < oldPosition.x + playerSimulationRadius + 1; x++){ for(int y = oldPosition.y - playerSimulationRadius; y < oldPosition.y + playerSimulationRadius + 1; y++){ for(int z = oldPosition.z - playerSimulationRadius; z < oldPosition.z + playerSimulationRadius + 1; z++){ @@ -166,6 +172,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //add to loaded cells loadedCellsLock.acquireUninterruptibly(); loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos))); + cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0); loadedCellsLock.release(); //add player groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player); @@ -178,7 +185,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager } } } -// System.out.println("removals: " + removals + "\tadditions: " + additions); + // System.out.println("removals: " + removals + "\tadditions: " + additions); } /** @@ -221,12 +228,23 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //TODO: improve to make have less performance impact for(ServerDataCell cell : loadedCells){ if(cell.getPlayers().size() < 1){ - System.out.println("Unload cell"); - toCleanQueue.add(cell); + int frameCount = cellPlayerlessFrameMap.get(cell) + 1; + cellPlayerlessFrameMap.put(cell,frameCount); + if(frameCount > UNLOAD_FRAME_THRESHOLD){ + System.out.println("Unload cell " + getCellWorldPosition(cell)); + toCleanQueue.add(cell); + } + } else { + if(cellPlayerlessFrameMap.get(cell) > 0){ + cellPlayerlessFrameMap.put(cell, 0); + } } } for(ServerDataCell cell : toCleanQueue){ parent.deregisterCell(cell); + loadedCells.remove(cell); + String key = getServerDataCellKey(getCellWorldPosition(cell)); + groundDataCells.remove(key); } toCleanQueue.clear(); } @@ -280,6 +298,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager //add to loaded cells loadedCellsLock.acquireUninterruptibly(); loadedCells.add(groundDataCells.get(getServerDataCellKey(worldPos))); + cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(worldPos)),0); loadedCellsLock.release(); } return groundDataCells.get(getServerDataCellKey(worldPos)); @@ -314,6 +333,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager Globals.microSimulation.simulate(cell, parent.getHitboxManager()); } loadedCellsLock.release(); + updatePlayerPositions(); } diff --git a/src/main/java/electrosphere/server/saves/SaveUtils.java b/src/main/java/electrosphere/server/saves/SaveUtils.java index d8f54f03..2a625bde 100644 --- a/src/main/java/electrosphere/server/saves/SaveUtils.java +++ b/src/main/java/electrosphere/server/saves/SaveUtils.java @@ -6,6 +6,8 @@ import electrosphere.engine.Globals; import electrosphere.game.server.world.ServerWorldData; import electrosphere.logger.LoggerInterface; import electrosphere.server.db.DatabaseUtils; +import electrosphere.server.terrain.generation.OverworldChunkGenerator; +import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.util.FileUtils; @@ -106,7 +108,7 @@ public class SaveUtils { } public static boolean loadTerrainAndCreateWorldData(String currentSaveName){ - Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0); + Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0,new OverworldChunkGenerator()); SaveUtils.loadTerrainAndDB(currentSaveName); Globals.serverWorldData = ServerWorldData.createGameWorld(Globals.serverTerrainManager); Globals.realmManager.createGriddedRealm(Globals.serverWorldData); diff --git a/src/main/java/electrosphere/server/simulation/MicroSimulation.java b/src/main/java/electrosphere/server/simulation/MicroSimulation.java index 155dbe06..fbc8f7b0 100644 --- a/src/main/java/electrosphere/server/simulation/MicroSimulation.java +++ b/src/main/java/electrosphere/server/simulation/MicroSimulation.java @@ -89,21 +89,12 @@ public class MicroSimulation { // CollidableTree tree = CollidableTree.getCollidableTree(currentCollidable); // tree.simulate(Main.deltaFrames); // } - //targeting crosshair - if(Globals.RUN_CLIENT){ - Crosshair.checkTargetable(); - Crosshair.updateTargetCrosshairPosition(); - } //simulate behavior trees dataCell.getScene().simulateBehaviorTrees(Main.deltaFrames); //sum collidable impulses for(Entity collidable : dataCell.getScene().getEntitiesWithTag(EntityTags.COLLIDABLE)){ ServerCollidableTree.getServerCollidableTree(collidable).simulate(Main.deltaFrames); } - //delete all client side entities that aren't in visible chunks - if(Globals.clientEntityCullingManager != null){ - Globals.clientEntityCullingManager.clearOutOfBoundsEntities(); - } } } diff --git a/src/main/java/electrosphere/server/terrain/diskcache/ChunkDiskCache.java b/src/main/java/electrosphere/server/terrain/diskcache/ChunkDiskCache.java deleted file mode 100644 index bb1ce681..00000000 --- a/src/main/java/electrosphere/server/terrain/diskcache/ChunkDiskCache.java +++ /dev/null @@ -1,92 +0,0 @@ -package electrosphere.server.terrain.diskcache; - -import java.util.Map; - -import electrosphere.net.server.Server; -import electrosphere.server.terrain.manager.ServerTerrainChunk; -import electrosphere.util.FileUtils; - -/** - * An interface for accessing the disk cache of chunk information - */ -public class ChunkDiskCache { - - //The map of world position+chunk type to the file that actually houses that information - Map worldPosFileMap; - - /** - * Constructor - */ - public ChunkDiskCache(){ - - } - - /** - * 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 diskcache based on a given save name - * @param saveName The save name - */ - public void init(String saveName){ - worldPosFileMap = FileUtils.loadObjectFromSavePath(saveName, "ChunkCache.map", Map.class); - } - - /** - * Checks if the cache contains a given chunk position - * @param worldX The x component - * @param worldY The y component - * @param worldZ The z component - * @return True if the cache contains the chunk, false otherwise - */ - public boolean containsTerrainAtPosition(int worldX, int worldY, int worldZ){ - return worldPosFileMap.containsKey(getTerrainChunkKey(worldX, worldY, worldZ)); - } - - /** - * Checks if the cache contains a given chunk position - * @param worldX The x component - * @param worldY The y component - * @param worldZ The z component - * @return True if the cache contains the chunk, false otherwise - */ - public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){ - return worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ)); - } - - /** - * 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){ - ServerTerrainChunk rVal = null; - if(containsTerrainAtPosition(worldX, worldY, worldZ)){ - String fileName = worldPosFileMap.get(getTerrainChunkKey(worldX, worldY, worldZ)); - //TODO: implement - } - return rVal; - } - -} diff --git a/src/main/java/electrosphere/server/terrain/diskmap/ChunkDiskMap.java b/src/main/java/electrosphere/server/terrain/diskmap/ChunkDiskMap.java new file mode 100644 index 00000000..0477e55d --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/diskmap/ChunkDiskMap.java @@ -0,0 +1,205 @@ +package electrosphere.server.terrain.diskmap; + +import java.io.ByteArrayOutputStream; +import java.io.File; +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.zip.DeflaterInputStream; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterOutputStream; + +import electrosphere.engine.Globals; +import electrosphere.net.server.Server; +import electrosphere.server.terrain.manager.ServerTerrainChunk; +import electrosphere.util.FileUtils; + +/** + * 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 = new HashMap(); + + /** + * Constructor + */ + public ChunkDiskMap(){ + + } + + /** + * 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 void init(String saveName){ + if(FileUtils.getSaveFile(saveName, "chunk.map").exists()){ + worldPosFileMap = FileUtils.loadObjectFromSavePath(saveName, "chunk.map", Map.class); + } else { + worldPosFileMap = new HashMap(); + } + } + + /** + * Saves the disk map to disk + */ + public void save(){ + FileUtils.serializeObjectToSavePath(Globals.currentSaveName, "chunk.map", this); + } + + /** + * 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){ + return worldPosFileMap.containsKey(getTerrainChunkKey(worldX, worldY, worldZ)); + } + + /** + * 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){ + return worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ)); + } + + /** + * 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){ + ServerTerrainChunk rVal = null; + if(containsTerrainAtPosition(worldX, worldY, worldZ)){ + //read file + String fileName = worldPosFileMap.get(getTerrainChunkKey(worldX, worldY, worldZ)); + byte[] rawDataCompressed = FileUtils.loadBinaryFromSavePath(Globals.currentSaveName, fileName); + //decompress + byte[] rawData = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + InflaterOutputStream inflaterInputStream = new InflaterOutputStream(out); + try { + inflaterInputStream.write(rawData); + 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_DIMENSION; + 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 * 4); + 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(); + } + } + } + rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); + } + } + return rVal; + } + + /** + * Saves a terrain chunk to disk + * @param terrainChunk The terrain chunk + */ + public void saveToDisk(ServerTerrainChunk terrainChunk){ + //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_DIMENSION; + 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.currentSaveName, fileName, out.toByteArray()); + //save to the map of filenames + worldPosFileMap.put(chunkKey,fileName); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/ArenaChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/ArenaChunkGenerator.java new file mode 100644 index 00000000..09e095ef --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/ArenaChunkGenerator.java @@ -0,0 +1,43 @@ +package electrosphere.server.terrain.generation; + +import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; +import electrosphere.server.terrain.manager.ServerTerrainChunk; +import electrosphere.server.terrain.models.TerrainModel; + +/** + * An arena terrain chunk generator + */ +public class ArenaChunkGenerator implements ChunkGenerator { + + @Override + public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) { + //Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap. + //Hence, width should actually be chunk dimension + 1 + float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + for(int inc = 0; inc < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; inc++){ + for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){ + for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){ + weights[weightX][inc][weightZ] = -1; + values[weightX][inc][weightZ] = 0; + } + } + } + if(worldY < 1){ + for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){ + for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){ + weights[weightX][0][weightZ] = 0.1f; + values[weightX][0][weightZ] = 2; + } + } + } + ServerTerrainChunk rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); + return rVal; + } + + @Override + public void setModel(TerrainModel model) { + //Does nothing as the arena is not based on a terrain model + } + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java new file mode 100644 index 00000000..b225f36a --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/OverworldChunkGenerator.java @@ -0,0 +1,88 @@ +package electrosphere.server.terrain.generation; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import electrosphere.game.terrain.processing.TerrainInterpolator; +import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; +import electrosphere.server.terrain.manager.ServerTerrainChunk; +import electrosphere.server.terrain.models.TerrainModel; + +/** + * Chunk generator for overworld chunks + */ +public class OverworldChunkGenerator implements ChunkGenerator { + + //The model of terrain for the overworld + TerrainModel model; + + //cache for the bicubic interpolated chunks + //don't need to interpolate each time a new chunk is created + //This should eventually be removed as terrain generation becomes more complicated than a heightmap + Map heightmapCache = new ConcurrentHashMap(); + + /** + * Constructor + */ + public OverworldChunkGenerator(){ + } + + @Override + public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) { + ServerTerrainChunk returnedChunk; + //Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap. + //Hence, width should actually be chunk dimension + 1 + float[][] heightmap = getHeightmap(worldX, worldZ); + float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; + for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){ + for(int weightY = 0; weightY < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightY++){ + for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){ + float height = heightmap[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE * worldX + weightX][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE * worldZ + weightZ]; + if(weightY < height){ + weights[weightX][weightY][weightZ] = 1; + values[weightX][weightY][weightZ] = 1; + } else if(height == 0 && weightY == 0 && worldY == 0) { + weights[weightX][weightY][weightZ] = 0.1f; + values[weightX][weightY][weightZ] = 1; + } else { + weights[weightX][weightY][weightZ] = -1; + values[weightX][weightY][weightZ] = 0; + } + } + } + } + returnedChunk = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); + return returnedChunk; + } + + @Override + /** + * Sets the terrain model for the overworld algo + */ + public void setModel(TerrainModel model){ + this.model = model; + } + + /** + * Gets a heightmap array. Either pulls it from cache if it exists or does the logic to generate it + * @param worldX The x position in world coordinates of the chunk + * @param worldZ THe z position in world coordinates of the chunk + * @return The heightmap array + */ + private float[][] getHeightmap(int worldX, int worldZ){ + String key = worldX + "_" + worldZ; + if(heightmapCache.containsKey(key)){ + return heightmapCache.get(key); + } else { + float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ); + float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk( + macroValues, + model.getDynamicInterpolationRatio() + ); + heightmapCache.put(key,heightmap); + return heightmap; + } + } + +} diff --git a/src/main/java/electrosphere/server/terrain/generation/interfaces/ChunkGenerator.java b/src/main/java/electrosphere/server/terrain/generation/interfaces/ChunkGenerator.java new file mode 100644 index 00000000..bb002835 --- /dev/null +++ b/src/main/java/electrosphere/server/terrain/generation/interfaces/ChunkGenerator.java @@ -0,0 +1,26 @@ +package electrosphere.server.terrain.generation.interfaces; + +import electrosphere.server.terrain.manager.ServerTerrainChunk; +import electrosphere.server.terrain.models.TerrainModel; + +/** + * An interface for generating chunks. Used to isolate different algorithms for getting chunks from one another. + */ +public interface ChunkGenerator { + + /** + * Generates a chunk given an x, y, and z + * @param worldX The x component + * @param worldY The y component + * @param worldZ The z component + * @return The chunk + */ + public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ); + + /** + * Sets the terrain model for the generation algorithm + * @param model The terrain model + */ + public void setModel(TerrainModel model); + +} diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java index 0469df8d..a92e97c1 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainChunk.java @@ -29,38 +29,6 @@ public class ServerTerrainChunk { this.weights = weights; this.values = values; } - - /** - * Returns an arena chunk. Should be flat land if worldY=0, otherwise all air - * @param worldX The world x position - * @param worldY The world y position - * @param worldZ The world z position - * @return The ServerTerrainChunk - */ - public static ServerTerrainChunk getArenaChunk(int worldX, int worldY, int worldZ){ - //Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap. - //Hence, width should actually be chunk dimension + 1 - float[][][] weights = new float[CHUNK_DATA_GENERATOR_SIZE][CHUNK_DATA_GENERATOR_SIZE][CHUNK_DATA_GENERATOR_SIZE]; - int[][][] values = new int[CHUNK_DATA_GENERATOR_SIZE][CHUNK_DATA_GENERATOR_SIZE][CHUNK_DATA_GENERATOR_SIZE]; - for(int inc = 0; inc < CHUNK_DATA_GENERATOR_SIZE; inc++){ - for(int weightX = 0; weightX < CHUNK_DATA_GENERATOR_SIZE; weightX++){ - for(int weightZ = 0; weightZ < CHUNK_DATA_GENERATOR_SIZE; weightZ++){ - weights[weightX][inc][weightZ] = -1; - values[weightX][inc][weightZ] = 0; - } - } - } - if(worldY < 1){ - for(int weightX = 0; weightX < CHUNK_DATA_GENERATOR_SIZE; weightX++){ - for(int weightZ = 0; weightZ < CHUNK_DATA_GENERATOR_SIZE; weightZ++){ - weights[weightX][0][weightZ] = 0.1f; - values[weightX][0][weightZ] = 2; - } - } - } - ServerTerrainChunk rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); - return rVal; - } public int getWorldX() { return worldX; diff --git a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java index dcac3cf1..d022b7e0 100644 --- a/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java +++ b/src/main/java/electrosphere/server/terrain/manager/ServerTerrainManager.java @@ -4,7 +4,10 @@ import com.google.gson.Gson; import electrosphere.engine.Globals; import electrosphere.game.terrain.processing.TerrainInterpolator; +import electrosphere.server.terrain.diskmap.ChunkDiskMap; +import electrosphere.server.terrain.generation.ArenaChunkGenerator; import electrosphere.server.terrain.generation.continentphase.TerrainGenerator; +import electrosphere.server.terrain.generation.interfaces.ChunkGenerator; import electrosphere.server.terrain.models.TerrainModel; import electrosphere.server.terrain.models.TerrainModification; import electrosphere.util.FileUtils; @@ -38,36 +41,52 @@ public class ServerTerrainManager { long seed; + //The model of the terrain this manager is managing TerrainModel model; - //Basic idea is we associate string that contains chunk x&y with elevation + //In memory cache of chunk data + //Basic idea is we associate string that contains chunk x&y&z with elevation //While we incur a penalty with converting ints -> string, think this will //offset regenerating the array every time we want a new one int cacheSize = 500; Map chunkCache; List chunkCacheContents; - //cache for the bicubic interpolated chunks - //don't need to interpolate each time a new chunk is created - //This should eventually be removed as terrain generation becomes more complicated than a heightmap - Map heightmapCache = new ConcurrentHashMap<>(); + //The map of chunk position <-> file on disk containing chunk data + ChunkDiskMap chunkDiskMap = null; + + //The generation algorithm for this terrain manager + ChunkGenerator chunkGenerator; - - public ServerTerrainManager(int worldSizeDiscrete, int verticalInterpolationRatio, float interpolationRandomDampener, long seed){ + /** + * Constructor + */ + public ServerTerrainManager( + int worldSizeDiscrete, + int verticalInterpolationRatio, + float interpolationRandomDampener, + long seed, + ChunkGenerator chunkGenerator + ){ this.worldSizeDiscrete = worldSizeDiscrete; this.verticalInterpolationRatio = verticalInterpolationRatio; this.chunkCache = new ConcurrentHashMap(); this.chunkCacheContents = new CopyOnWriteArrayList(); this.interpolationRandomDampener = interpolationRandomDampener; this.seed = seed; + this.chunkGenerator = chunkGenerator; } ServerTerrainManager(){ } + /** + * Constructs an arena terrain manager + * @return The arena terrain manager + */ public static ServerTerrainManager constructArenaTerrainManager(){ ServerTerrainManager rVal = new ServerTerrainManager(); rVal.worldSizeDiscrete = 2; @@ -75,16 +94,22 @@ public class ServerTerrainManager { rVal.chunkCache = new ConcurrentHashMap(); rVal.chunkCacheContents = new CopyOnWriteArrayList(); rVal.interpolationRandomDampener = 0.0f; + rVal.chunkGenerator = new ArenaChunkGenerator(); return rVal; } + /** + * Generates a terrain model for the manager + */ public void generate(){ TerrainGenerator terrainGen = new TerrainGenerator(); terrainGen.setInterpolationRatio(worldSizeDiscrete/200); terrainGen.setVerticalInterpolationRatio(verticalInterpolationRatio); terrainGen.setRandomSeed(seed); model = terrainGen.generateModel(); + this.chunkGenerator.setModel(model); model.setInterpolationRandomDampener(interpolationRandomDampener); + this.chunkDiskMap = new ChunkDiskMap(); } /** @@ -100,10 +125,19 @@ public class ServerTerrainManager { floatView.flip(); FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array()); FileUtils.serializeObjectToSavePath(saveName, "./terrain.json", model); + if(chunkDiskMap != null){ + chunkDiskMap.save(); + } } + /** + * Loads a terrain manager from a save file + * @param saveName The name of the save + */ public void load(String saveName){ + //load terrain model model = FileUtils.loadObjectFromSavePath(saveName, "./terrain.json", TerrainModel.class); + chunkGenerator.setModel(model); byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./terrain.dat"); ByteBuffer buffer = ByteBuffer.wrap(data); FloatBuffer floatView = buffer.asFloatBuffer(); @@ -114,6 +148,9 @@ public class ServerTerrainManager { } } model.setElevationArray(elevation); + //load chunk disk map + chunkDiskMap = new ChunkDiskMap(); + chunkDiskMap.init(saveName); } public float[][] getTerrainAtChunk(int x, int y){ @@ -156,10 +193,21 @@ public class ServerTerrainManager { } } + /** + * Gets the terrain model backing this terrain manager + * @return The terrain model + */ public TerrainModel getModel() { return model; } + /** + * Gets the key for a given world position + * @param worldX The x component + * @param worldY The y component + * @param worldZ The z component + * @return The key + */ public String getKey(int worldX, int worldY, int worldZ){ return worldX + "_" + worldY + "_" + worldZ; } @@ -172,77 +220,32 @@ public class ServerTerrainManager { * @return The ServerTerrainChunk */ public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){ - if(model != null){ - //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING - String key = getKey(worldX,worldY,worldZ); - ServerTerrainChunk returnedChunk; - if(chunkCache.containsKey(key)){ - chunkCacheContents.remove(key); - chunkCacheContents.add(0, key); - returnedChunk = chunkCache.get(key); - return returnedChunk; - } else { - //Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap. - //Hence, width should actually be chunk dimension + 1 - float[][] heightmap = getHeightmap(worldX, worldZ); - float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; - int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; - for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){ - for(int weightY = 0; weightY < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightY++){ - for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){ - float height = heightmap[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE * worldX + weightX][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE * worldZ + weightZ]; - if(weightY < height){ - weights[weightX][weightY][weightZ] = 1; - values[weightX][weightY][weightZ] = 1; - } else if(height == 0 && weightY == 0 && worldY == 0) { - weights[weightX][weightY][weightZ] = 0.1f; - values[weightX][weightY][weightZ] = 1; - } else { - weights[weightX][weightY][weightZ] = -1; - values[weightX][weightY][weightZ] = 0; - } - } - } - } - if(chunkCacheContents.size() > cacheSize){ - String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1); - chunkCache.remove(oldChunk); - } - returnedChunk = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); - chunkCache.put(key, returnedChunk); - chunkCacheContents.add(key); - return returnedChunk; - } + //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING + String key = getKey(worldX,worldY,worldZ); + ServerTerrainChunk returnedChunk = null; + if(chunkCache.containsKey(key)){ + chunkCacheContents.remove(key); + chunkCacheContents.add(0, key); + returnedChunk = chunkCache.get(key); + return returnedChunk; } else { - //THIS FIRES IF THERE IS AN ARENA WORLD RUNNING - String key = getKey(worldX, worldY, worldZ); - ServerTerrainChunk returnedChunk; - if(chunkCache.containsKey(key)){ - chunkCacheContents.remove("" + key); - chunkCacheContents.add(0, key); - returnedChunk = chunkCache.get(key); - return returnedChunk; - } else { - returnedChunk = ServerTerrainChunk.getArenaChunk(worldX, worldY, worldZ); - chunkCache.put(key, returnedChunk); - chunkCacheContents.add(key); - return returnedChunk; + if(chunkCacheContents.size() > cacheSize){ + String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1); + chunkCache.remove(oldChunk); } - } - } - - private float[][] getHeightmap(int worldX, int worldZ){ - String key = worldX + "_" + worldZ; - if(heightmapCache.containsKey(key)){ - return heightmapCache.get(key); - } else { - float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ); - float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk( - macroValues, - model.getDynamicInterpolationRatio() - ); - heightmapCache.put(key,heightmap); - return heightmap; + //pull from disk if it exists + if(chunkDiskMap != null){ + if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){ + returnedChunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ); + } + } + //generate if it does not exist + if(returnedChunk == null){ + returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ); + } + chunkCache.put(key, returnedChunk); + chunkCacheContents.add(key); + return returnedChunk; } } diff --git a/src/main/java/electrosphere/util/FileUtils.java b/src/main/java/electrosphere/util/FileUtils.java index 68a86e19..5f07bdb2 100644 --- a/src/main/java/electrosphere/util/FileUtils.java +++ b/src/main/java/electrosphere/util/FileUtils.java @@ -193,6 +193,14 @@ public class FileUtils { return Files.readString(targetFile.toPath()); } + /** + * Loads an object from a save folder + * @param The type of the object + * @param saveName The name of the save + * @param pathName The path of the file containing the object json + * @param className The class of the object + * @return The object + */ public static T loadObjectFromSavePath(String saveName, String pathName, Class className){ T rVal = null; String sanitizedFilePath = sanitizeFilePath(pathName); @@ -204,6 +212,12 @@ public class FileUtils { return rVal; } + /** + * Serializes an object to a save folder + * @param saveName The name of the save + * @param pathName The path within the save folder to the file + * @param object The object to save + */ public static void serializeObjectToSavePath(String saveName, String pathName, Object object){ String sanitizedFilePath = sanitizeFilePath(pathName); try { @@ -248,11 +262,11 @@ public class FileUtils { /** * Checks if a directory exists - * @param directoryName + * @param fileName * @return true if directory exists, false otherwise */ - public static boolean checkFileExists(String directoryName){ - File targetDir = new File(sanitizeFilePath(directoryName)); + public static boolean checkFileExists(String fileName){ + File targetDir = new File(sanitizeFilePath(fileName)); if(targetDir.exists()){ return true; } else { diff --git a/src/main/java/electrosphere/util/worldviewer/TerrainViewer.java b/src/main/java/electrosphere/util/worldviewer/TerrainViewer.java index 340de321..b97ea2e1 100644 --- a/src/main/java/electrosphere/util/worldviewer/TerrainViewer.java +++ b/src/main/java/electrosphere/util/worldviewer/TerrainViewer.java @@ -1,6 +1,7 @@ package electrosphere.util.worldviewer; import electrosphere.server.simulation.MacroSimulation; +import electrosphere.server.terrain.generation.OverworldChunkGenerator; import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.models.TerrainModel; @@ -19,7 +20,7 @@ public class TerrainViewer { TerrainModel terrainModel; - ServerTerrainManager terrainManager = new ServerTerrainManager(2000, 1000, 0.05f, new Random().nextLong()); + ServerTerrainManager terrainManager = new ServerTerrainManager(2000, 1000, 0.05f, new Random().nextLong(), new OverworldChunkGenerator()); terrainManager.generate(); terrainModel = terrainManager.getModel();