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();