Physics fixes

This commit is contained in:
austin 2023-07-14 22:10:38 -04:00
parent ceb5a5aaf8
commit c71f4fecad
28 changed files with 636 additions and 501 deletions

3
buildNumber.properties Normal file
View File

@ -0,0 +1,3 @@
#maven.buildNumber.plugin properties file
#Fri Jul 14 20:48:01 EDT 2023
buildNumber=4

View File

@ -13,6 +13,14 @@
<joml.version>1.9.19</joml.version> <joml.version>1.9.19</joml.version>
</properties> </properties>
<!-- Used for build number plugin because it LITERALLY WONT LET YOU NOT HAVE SCM-->
<scm>
<connection>scm:svn:http://127.0.0.1/dummy</connection>
<developerConnection>scm:svn:https://127.0.0.1/dummy</developerConnection>
<tag>HEAD</tag>
<url>http://127.0.0.1/dummy</url>
</scm>
<dependencies> <dependencies>
<!-- generic LWJGL runtimes --> <!-- generic LWJGL runtimes -->
<!--License: BSD--> <!--License: BSD-->

1
saves/default/chunk.map Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -49,10 +49,10 @@ import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.collidable.Impulse; import electrosphere.entity.state.collidable.Impulse;
import electrosphere.entity.types.hitbox.HitboxData; 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 { public class CollisionEngine {
@ -85,58 +85,6 @@ public class CollisionEngine {
world = OdeHelper.createWorld(); world = OdeHelper.createWorld();
space = OdeHelper.createHashSpace(); space = OdeHelper.createHashSpace();
contactgroup = OdeHelper.createJointGroup(); 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(){ public void listBodyPositions(){
for(DBody body : bodies){ for(DBody body : bodies){
System.out.println(body); LoggerInterface.loggerEngine.INFO("" + body);
System.out.println(PhysicsUtils.getRigidBodyPosition(body)); LoggerInterface.loggerEngine.INFO("" + PhysicsUtils.getRigidBodyPosition(body));
} }
} }
@ -740,5 +688,31 @@ public class CollisionEngine {
body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation));
spaceLock.release(); 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;
}
} }

View File

@ -18,7 +18,6 @@ import org.ode4j.ode.DMass;
import org.ode4j.ode.DPlane; import org.ode4j.ode.DPlane;
import org.ode4j.ode.DSphere; import org.ode4j.ode.DSphere;
import org.ode4j.ode.DTriMesh; import org.ode4j.ode.DTriMesh;
import org.ode4j.ode.OdeHelper;
import electrosphere.collision.collidable.Collidable; import electrosphere.collision.collidable.Collidable;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
@ -105,20 +104,13 @@ public class PhysicsUtils {
} }
DBody body = OdeHelper.createBody(null);
DTriMesh triMesh = collisionEngine.createTrimeshGeom(vertices,indices); DTriMesh triMesh = collisionEngine.createTrimeshGeom(vertices,indices);
triMesh.setBody(body);
DBody body = collisionEngine.createDBody(triMesh);
// terrainRigidBody.setFriction(1f);
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(body, new Collidable(terrain,Collidable.TYPE_TERRAIN)); 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); terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, body);
return body; return body;
@ -200,7 +192,7 @@ public class PhysicsUtils {
*/ */
public static DBody generateRigidBodyFromAIScene(CollisionEngine collisionEngine, AIScene scene){ public static DBody generateRigidBodyFromAIScene(CollisionEngine collisionEngine, AIScene scene){
DBody body = OdeHelper.createBody(collisionEngine.getDWorld()); DBody body = collisionEngine.createDBody(null);
PointerBuffer meshesBuffer = scene.mMeshes(); PointerBuffer meshesBuffer = scene.mMeshes();
while(meshesBuffer.hasRemaining()){ while(meshesBuffer.hasRemaining()){
@ -244,14 +236,6 @@ public class PhysicsUtils {
return body; 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 * Gets the current position of a rigid body as a joml vector
* @param body The dbody * @param body The dbody
@ -308,17 +292,6 @@ public class PhysicsUtils {
collisionEngine.setBodyTransform(body, position, rotation); 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 //The width of a plane rigid body
//It's really a box under the hood //It's really a box under the hood
static final double PLANE_WIDTH = 0.3; static final double PLANE_WIDTH = 0.3;

View File

@ -1011,7 +1011,7 @@ public class ControlHandler {
void setAlwaysOnDebugControls(){ void setAlwaysOnDebugControls(){
alwaysOnDebugControlList.add(controls.get(DEBUG_OPEN_DEBUG_MENU)); alwaysOnDebugControlList.add(controls.get(DEBUG_OPEN_DEBUG_MENU));
controls.get(DEBUG_OPEN_DEBUG_MENU).setOnPress(new ControlMethod(){public void execute(){ 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 mainMenuWindow = new Window(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
Window mainMenuInGame = MenuGeneratorsDebug.createTopLevelDebugMenu(); Window mainMenuInGame = MenuGeneratorsDebug.createTopLevelDebugMenu();
// mainMenuWindow.addChild(mainMenuInGame); // mainMenuWindow.addChild(mainMenuInGame);

View File

@ -17,6 +17,7 @@ import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.renderer.ui.Window; import electrosphere.renderer.ui.Window;
import electrosphere.server.saves.SaveUtils; import electrosphere.server.saves.SaveUtils;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.manager.ServerTerrainManager;
import electrosphere.util.FileUtils; import electrosphere.util.FileUtils;
import electrosphere.controls.ControlHandler; import electrosphere.controls.ControlHandler;
@ -31,7 +32,7 @@ public class DebugSPWorldLoading {
WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu()); WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu());
loadingWindow.setVisible(true); 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")){ if(!SaveUtils.getSaves().contains("random_sp_world")){
// //
//the juicy server GENERATION part //the juicy server GENERATION part
@ -95,32 +96,4 @@ public class DebugSPWorldLoading {
ClientLoading.loadClientWorld(); 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();
}
} }

View File

@ -41,6 +41,7 @@ import electrosphere.server.datacell.Realm;
import electrosphere.server.saves.SaveUtils; import electrosphere.server.saves.SaveUtils;
import electrosphere.server.simulation.MacroSimulation; import electrosphere.server.simulation.MacroSimulation;
import electrosphere.server.simulation.MicroSimulation; import electrosphere.server.simulation.MicroSimulation;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.manager.ServerTerrainManager;
/** /**
@ -60,7 +61,7 @@ public class LoadingUtils {
Actually initialize the terrain manager Actually initialize the terrain manager
*/ */
float randomDampener = 0.0f; //0.25f; 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.RUN_SERVER){
if(Globals.userSettings.gameplayGenerateWorld()){ if(Globals.userSettings.gameplayGenerateWorld()){
Globals.serverTerrainManager.generate(); Globals.serverTerrainManager.generate();

View File

@ -3,6 +3,7 @@ package electrosphere.engine.loadingthreads;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.server.saves.SaveUtils; import electrosphere.server.saves.SaveUtils;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.manager.ServerTerrainManager;
public class ServerLoading { public class ServerLoading {
@ -15,7 +16,7 @@ public class ServerLoading {
// } // }
//TODO: Globals.serverTerrainManager = new ServerTerrainManager(2000,50,100,randomDampener,0); //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); SaveUtils.loadSave(Globals.currentSaveName);
// LoadingUtils.initTerrainDataCellManager(); // LoadingUtils.initTerrainDataCellManager();
//TODO: set spawnpoint //TODO: set spawnpoint

View File

@ -201,8 +201,12 @@ public class ClientCollidableTree implements BehaviorTree {
//update collision engine of this thing's position //update collision engine of this thing's position
CollidableTemplate template = (CollidableTemplate)parent.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE); CollidableTemplate template = (CollidableTemplate)parent.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE);
body.setPosition(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()); PhysicsUtils.setRigidBodyTransform(
body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); Globals.clientSceneWrapper.getCollisionEngine(),
new Vector3d(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()),
rotation,
body
);
} }
/** /**

View File

@ -207,9 +207,12 @@ public class ServerCollidableTree implements BehaviorTree {
//update collision engine of this thing's position //update collision engine of this thing's position
CollidableTemplate template = (CollidableTemplate)parent.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE); CollidableTemplate template = (CollidableTemplate)parent.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE);
body.setPosition(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()); PhysicsUtils.setRigidBodyTransform(
body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation)); parentRealm.getCollisionEngine(),
new Vector3d(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()),
rotation,
body
);
} }

View File

@ -181,9 +181,6 @@ public class ClientGravityTree implements BehaviorTree {
// hitFraction = 1; // hitFraction = 1;
// } // }
collidable.addImpulse(new Impulse(new Vector3d(0,-1,0), new Vector3d(0,0,0), new Vector3d(position), gravityDif,"gravity")); 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; break;
case NOT_ACTIVE: case NOT_ACTIVE:

View File

@ -175,9 +175,6 @@ public class ServerGravityTree implements BehaviorTree {
// hitFraction = 1; // hitFraction = 1;
// } // }
collidable.addImpulse(new Impulse(new Vector3d(0,-1,0), new Vector3d(0,0,0), new Vector3d(position), gravityDif,"gravity")); 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; break;
case NOT_ACTIVE: case NOT_ACTIVE:

View File

@ -99,27 +99,25 @@ public class GroundMovementTree implements BehaviorTree {
this.facing = facing; this.facing = facing;
state = MovementTreeState.STARTUP; state = MovementTreeState.STARTUP;
//if we aren't the server, alert the server we intend to walk forward //if we aren't the server, alert the server we intend to walk forward
if(!Globals.RUN_SERVER){ Vector3d position = EntityUtils.getPosition(parent);
Vector3d position = EntityUtils.getPosition(parent); Quaterniond rotation = EntityUtils.getRotation(parent);
Quaterniond rotation = EntityUtils.getRotation(parent); Vector3d facingVector = CreatureUtils.getFacingVector(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent); float velocity = CreatureUtils.getVelocity(parent);
float velocity = CreatureUtils.getVelocity(parent); Globals.clientConnection.queueOutgoingMessage(
Globals.clientConnection.queueOutgoingMessage( EntityMessage.constructmoveUpdateMessage(
EntityMessage.constructmoveUpdateMessage( Globals.clientSceneWrapper.mapClientToServerId(parent.getId()),
parent.getId(), Main.getCurrentFrame(),
Main.getCurrentFrame(), position.x,
position.x, position.y,
position.y, position.z,
position.z, rotation.x,
rotation.x, rotation.y,
rotation.y, rotation.z,
rotation.z, rotation.w,
rotation.w, velocity,
velocity, 0 //magic number corresponding to state startup
0 //magic number corresponding to state startup )
) );
);
}
} }
} }
@ -131,27 +129,25 @@ public class GroundMovementTree implements BehaviorTree {
public void slowdown(){ public void slowdown(){
state = MovementTreeState.SLOWDOWN; state = MovementTreeState.SLOWDOWN;
//if we aren't the server, alert the server we intend to slow down //if we aren't the server, alert the server we intend to slow down
if(!Globals.RUN_SERVER){ Vector3d position = EntityUtils.getPosition(parent);
Vector3d position = EntityUtils.getPosition(parent); Quaterniond rotation = EntityUtils.getRotation(parent);
Quaterniond rotation = EntityUtils.getRotation(parent); Vector3d facingVector = CreatureUtils.getFacingVector(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent); float velocity = CreatureUtils.getVelocity(parent);
float velocity = CreatureUtils.getVelocity(parent); Globals.clientConnection.queueOutgoingMessage(
Globals.clientConnection.queueOutgoingMessage( EntityMessage.constructmoveUpdateMessage(
EntityMessage.constructmoveUpdateMessage( Globals.clientSceneWrapper.mapClientToServerId(parent.getId()),
parent.getId(), Main.getCurrentFrame(),
Main.getCurrentFrame(), position.x,
position.x, position.y,
position.y, position.z,
position.z, rotation.x,
rotation.x, rotation.y,
rotation.y, rotation.z,
rotation.z, rotation.w,
rotation.w, velocity,
velocity, 2 //magic number corresponding to state slowdown
2 //magic number corresponding to state slowdown )
) );
);
}
} }
public void simulate(float deltaTime){ public void simulate(float deltaTime){
@ -233,15 +229,14 @@ public class GroundMovementTree implements BehaviorTree {
// System.out.println(EntityUtils.getEntityPosition(parent)); // System.out.println(EntityUtils.getEntityPosition(parent));
// System.out.println(message.getpositionX() + " " + message.getpositionY() + " " + message.getpositionZ()); // 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 //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){
if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD){ EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ());
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){
} 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));
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 //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;
} }
break; break;
@ -274,14 +269,6 @@ public class GroundMovementTree implements BehaviorTree {
state = MovementTreeState.MOVE; state = MovementTreeState.MOVE;
} }
CreatureUtils.setVelocity(parent, velocity); 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 // //actually update
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement")); collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition); // position.set(newPosition);
@ -308,14 +295,6 @@ public class GroundMovementTree implements BehaviorTree {
velocity = maxNaturalVelocity; velocity = maxNaturalVelocity;
CreatureUtils.setVelocity(parent, velocity); 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")); collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition); // position.set(newPosition);
rotation.set(movementQuaternion); rotation.set(movementQuaternion);
@ -350,13 +329,6 @@ public class GroundMovementTree implements BehaviorTree {
} }
} }
CreatureUtils.setVelocity(parent, velocity); 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")); collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition); // position.set(newPosition);
rotation.set(movementQuaternion); rotation.set(movementQuaternion);
@ -365,14 +337,6 @@ public class GroundMovementTree implements BehaviorTree {
break; break;
case IDLE: 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; break;
} }
} }

View File

@ -99,44 +99,11 @@ public class ServerGroundMovementTree implements BehaviorTree {
this.facing = facing; this.facing = facing;
state = MovementTreeState.STARTUP; state = MovementTreeState.STARTUP;
//if we aren't the server, alert the server we intend to walk forward //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); Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent); Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent); Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent); float velocity = CreatureUtils.getVelocity(parent);
Globals.clientConnection.queueOutgoingMessage( DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage( EntityMessage.constructmoveUpdateMessage(
parent.getId(), parent.getId(),
Main.getCurrentFrame(), Main.getCurrentFrame(),
@ -148,12 +115,41 @@ public class ServerGroundMovementTree implements BehaviorTree {
rotation.z, rotation.z,
rotation.w, rotation.w,
velocity, 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){ public void simulate(float deltaTime){
float velocity = CreatureUtils.getVelocity(parent); float velocity = CreatureUtils.getVelocity(parent);
float acceleration = CreatureUtils.getAcceleration(parent); float acceleration = CreatureUtils.getAcceleration(parent);
@ -201,11 +197,11 @@ public class ServerGroundMovementTree implements BehaviorTree {
long updateTime = message.gettime(); long updateTime = message.gettime();
// System.out.println("MOVE to " + message.getX() + " " + message.getY() + " " + message.getZ()); // System.out.println("MOVE to " + message.getX() + " " + message.getY() + " " + message.getZ());
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case MOVE: case MOVE: {
break; } break;
case SETFACING: case SETFACING:
break; break;
case MOVEUPDATE: case MOVEUPDATE: {
if(updateTime > lastUpdateTime){ if(updateTime > lastUpdateTime){
lastUpdateTime = updateTime; lastUpdateTime = updateTime;
switch(message.gettreeState()){ switch(message.gettreeState()){
@ -230,10 +226,11 @@ public class ServerGroundMovementTree implements BehaviorTree {
break; break;
} }
//we want to always update the server facing vector with where the client says they're facing //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; } break;
default: default:
break; break;
} }
@ -263,14 +260,6 @@ public class ServerGroundMovementTree implements BehaviorTree {
state = MovementTreeState.MOVE; state = MovementTreeState.MOVE;
} }
CreatureUtils.setVelocity(parent, velocity); 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 // //actually update
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement")); collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition); // position.set(newPosition);
@ -312,14 +301,6 @@ public class ServerGroundMovementTree implements BehaviorTree {
velocity = maxNaturalVelocity; velocity = maxNaturalVelocity;
CreatureUtils.setVelocity(parent, velocity); 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")); collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition); // position.set(newPosition);
rotation.set(movementQuaternion); rotation.set(movementQuaternion);
@ -369,13 +350,6 @@ public class ServerGroundMovementTree implements BehaviorTree {
} }
} }
CreatureUtils.setVelocity(parent, velocity); 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")); collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition); // position.set(newPosition);
rotation.set(movementQuaternion); rotation.set(movementQuaternion);
@ -399,14 +373,6 @@ public class ServerGroundMovementTree implements BehaviorTree {
); );
break; break;
case IDLE: 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; break;
} }
} }

View File

@ -40,6 +40,7 @@ import electrosphere.renderer.ui.events.NavigationEvent;
import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.renderer.ui.form.FormElement; import electrosphere.renderer.ui.form.FormElement;
import electrosphere.server.saves.SaveUtils; import electrosphere.server.saves.SaveUtils;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.manager.ServerTerrainManager;
/** /**
@ -130,7 +131,7 @@ public class MenuGenerators {
//create save dir //create save dir
SaveUtils.createOrOverwriteSave(saveName); SaveUtils.createOrOverwriteSave(saveName);
//create and save terrain //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.generate();
terrainManager.save(SaveUtils.deriveSaveDirectoryPath(saveName)); terrainManager.save(SaveUtils.deriveSaveDirectoryPath(saveName));
WindowUtils.replaceMainMenuContents(MenuGenerators.createWorldSelectMenu()); WindowUtils.replaceMainMenuContents(MenuGenerators.createWorldSelectMenu());

View File

@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import org.joml.Vector3d; 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 //these are going to be the natural ground grid of data cells, but we're going to have more than this
Map<String,ServerDataCell> groundDataCells = new HashMap<String,ServerDataCell>(); Map<String,ServerDataCell> groundDataCells = new HashMap<String,ServerDataCell>();
Map<ServerDataCell,Vector3i> cellPositionMap = new HashMap<ServerDataCell,Vector3i>(); Map<ServerDataCell,Vector3i> cellPositionMap = new HashMap<ServerDataCell,Vector3i>();
//Map of server data cell to the number of frames said cell has had no players
Map<ServerDataCell,Integer> cellPlayerlessFrameMap = new HashMap<ServerDataCell,Integer>();
//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 //loaded cells
Semaphore loadedCellsLock = new Semaphore(1); Semaphore loadedCellsLock = new Semaphore(1);
Set<ServerDataCell> loadedCells; Set<ServerDataCell> loadedCells;
@ -55,7 +60,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
*/ */
public void init(ServerWorldData data){ public void init(ServerWorldData data){
discreteWorldSize = data.getWorldSizeDiscrete(); discreteWorldSize = data.getWorldSizeDiscrete();
loadedCells = new HashSet<ServerDataCell>(); loadedCells = new CopyOnWriteArraySet<ServerDataCell>();
} }
/** /**
@ -87,6 +92,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//add to loaded cells //add to loaded cells
loadedCellsLock.acquireUninterruptibly(); loadedCellsLock.acquireUninterruptibly();
loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos))); loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
loadedCellsLock.release(); loadedCellsLock.release();
//generate/handle content for new server data cell //generate/handle content for new server data cell
@ -108,9 +114,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
int playerSimulationRadius = player.getSimulationRadius(); int playerSimulationRadius = player.getSimulationRadius();
Vector3i oldPosition = player.getWorldPos(); Vector3i oldPosition = player.getWorldPos();
player.setWorldPos(newPosition); player.setWorldPos(newPosition);
// System.out.println("=======" + "SET" + newX + " " + newY + " FROM " + oldX + " " + oldY + "========"); // System.out.println("=======" + "SET" + newX + " " + newY + " FROM " + oldX + " " + oldY + "========");
// int removals = 0; int removals = 0;
// int additions = 0; int additions = 0;
for(int x = oldPosition.x - playerSimulationRadius; x < oldPosition.x + playerSimulationRadius + 1; x++){ 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 y = oldPosition.y - playerSimulationRadius; y < oldPosition.y + playerSimulationRadius + 1; y++){
for(int z = oldPosition.z - playerSimulationRadius; z < oldPosition.z + playerSimulationRadius + 1; z++){ 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 //add to loaded cells
loadedCellsLock.acquireUninterruptibly(); loadedCellsLock.acquireUninterruptibly();
loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos))); loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
loadedCellsLock.release(); loadedCellsLock.release();
//add player //add player
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(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 //TODO: improve to make have less performance impact
for(ServerDataCell cell : loadedCells){ for(ServerDataCell cell : loadedCells){
if(cell.getPlayers().size() < 1){ if(cell.getPlayers().size() < 1){
System.out.println("Unload cell"); int frameCount = cellPlayerlessFrameMap.get(cell) + 1;
toCleanQueue.add(cell); 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){ for(ServerDataCell cell : toCleanQueue){
parent.deregisterCell(cell); parent.deregisterCell(cell);
loadedCells.remove(cell);
String key = getServerDataCellKey(getCellWorldPosition(cell));
groundDataCells.remove(key);
} }
toCleanQueue.clear(); toCleanQueue.clear();
} }
@ -280,6 +298,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//add to loaded cells //add to loaded cells
loadedCellsLock.acquireUninterruptibly(); loadedCellsLock.acquireUninterruptibly();
loadedCells.add(groundDataCells.get(getServerDataCellKey(worldPos))); loadedCells.add(groundDataCells.get(getServerDataCellKey(worldPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(worldPos)),0);
loadedCellsLock.release(); loadedCellsLock.release();
} }
return groundDataCells.get(getServerDataCellKey(worldPos)); return groundDataCells.get(getServerDataCellKey(worldPos));
@ -314,6 +333,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
Globals.microSimulation.simulate(cell, parent.getHitboxManager()); Globals.microSimulation.simulate(cell, parent.getHitboxManager());
} }
loadedCellsLock.release(); loadedCellsLock.release();
updatePlayerPositions();
} }

View File

@ -6,6 +6,8 @@ import electrosphere.engine.Globals;
import electrosphere.game.server.world.ServerWorldData; import electrosphere.game.server.world.ServerWorldData;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.server.db.DatabaseUtils; 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.server.terrain.manager.ServerTerrainManager;
import electrosphere.util.FileUtils; import electrosphere.util.FileUtils;
@ -106,7 +108,7 @@ public class SaveUtils {
} }
public static boolean loadTerrainAndCreateWorldData(String currentSaveName){ 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); SaveUtils.loadTerrainAndDB(currentSaveName);
Globals.serverWorldData = ServerWorldData.createGameWorld(Globals.serverTerrainManager); Globals.serverWorldData = ServerWorldData.createGameWorld(Globals.serverTerrainManager);
Globals.realmManager.createGriddedRealm(Globals.serverWorldData); Globals.realmManager.createGriddedRealm(Globals.serverWorldData);

View File

@ -89,21 +89,12 @@ public class MicroSimulation {
// CollidableTree tree = CollidableTree.getCollidableTree(currentCollidable); // CollidableTree tree = CollidableTree.getCollidableTree(currentCollidable);
// tree.simulate(Main.deltaFrames); // tree.simulate(Main.deltaFrames);
// } // }
//targeting crosshair
if(Globals.RUN_CLIENT){
Crosshair.checkTargetable();
Crosshair.updateTargetCrosshairPosition();
}
//simulate behavior trees //simulate behavior trees
dataCell.getScene().simulateBehaviorTrees(Main.deltaFrames); dataCell.getScene().simulateBehaviorTrees(Main.deltaFrames);
//sum collidable impulses //sum collidable impulses
for(Entity collidable : dataCell.getScene().getEntitiesWithTag(EntityTags.COLLIDABLE)){ for(Entity collidable : dataCell.getScene().getEntitiesWithTag(EntityTags.COLLIDABLE)){
ServerCollidableTree.getServerCollidableTree(collidable).simulate(Main.deltaFrames); ServerCollidableTree.getServerCollidableTree(collidable).simulate(Main.deltaFrames);
} }
//delete all client side entities that aren't in visible chunks
if(Globals.clientEntityCullingManager != null){
Globals.clientEntityCullingManager.clearOutOfBoundsEntities();
}
} }
} }

View File

@ -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<String,String> 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;
}
}

View File

@ -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<String,String> worldPosFileMap = new HashMap<String,String>();
/**
* 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<String,String>();
}
}
/**
* 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();
}
}
}

View File

@ -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
}
}

View File

@ -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<String, float[][]> heightmapCache = new ConcurrentHashMap<String, float[][]>();
/**
* 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;
}
}
}

View File

@ -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);
}

View File

@ -29,38 +29,6 @@ public class ServerTerrainChunk {
this.weights = weights; this.weights = weights;
this.values = values; 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() { public int getWorldX() {
return worldX; return worldX;

View File

@ -4,7 +4,10 @@ import com.google.gson.Gson;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.game.terrain.processing.TerrainInterpolator; 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.continentphase.TerrainGenerator;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.models.TerrainModel; import electrosphere.server.terrain.models.TerrainModel;
import electrosphere.server.terrain.models.TerrainModification; import electrosphere.server.terrain.models.TerrainModification;
import electrosphere.util.FileUtils; import electrosphere.util.FileUtils;
@ -38,36 +41,52 @@ public class ServerTerrainManager {
long seed; long seed;
//The model of the terrain this manager is managing
TerrainModel model; 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 //While we incur a penalty with converting ints -> string, think this will
//offset regenerating the array every time we want a new one //offset regenerating the array every time we want a new one
int cacheSize = 500; int cacheSize = 500;
Map<String, ServerTerrainChunk> chunkCache; Map<String, ServerTerrainChunk> chunkCache;
List<String> chunkCacheContents; List<String> chunkCacheContents;
//cache for the bicubic interpolated chunks //The map of chunk position <-> file on disk containing chunk data
//don't need to interpolate each time a new chunk is created ChunkDiskMap chunkDiskMap = null;
//This should eventually be removed as terrain generation becomes more complicated than a heightmap
Map<String, float[][]> heightmapCache = new ConcurrentHashMap<>(); //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.worldSizeDiscrete = worldSizeDiscrete;
this.verticalInterpolationRatio = verticalInterpolationRatio; this.verticalInterpolationRatio = verticalInterpolationRatio;
this.chunkCache = new ConcurrentHashMap<String, ServerTerrainChunk>(); this.chunkCache = new ConcurrentHashMap<String, ServerTerrainChunk>();
this.chunkCacheContents = new CopyOnWriteArrayList<String>(); this.chunkCacheContents = new CopyOnWriteArrayList<String>();
this.interpolationRandomDampener = interpolationRandomDampener; this.interpolationRandomDampener = interpolationRandomDampener;
this.seed = seed; this.seed = seed;
this.chunkGenerator = chunkGenerator;
} }
ServerTerrainManager(){ ServerTerrainManager(){
} }
/**
* Constructs an arena terrain manager
* @return The arena terrain manager
*/
public static ServerTerrainManager constructArenaTerrainManager(){ public static ServerTerrainManager constructArenaTerrainManager(){
ServerTerrainManager rVal = new ServerTerrainManager(); ServerTerrainManager rVal = new ServerTerrainManager();
rVal.worldSizeDiscrete = 2; rVal.worldSizeDiscrete = 2;
@ -75,16 +94,22 @@ public class ServerTerrainManager {
rVal.chunkCache = new ConcurrentHashMap<String, ServerTerrainChunk>(); rVal.chunkCache = new ConcurrentHashMap<String, ServerTerrainChunk>();
rVal.chunkCacheContents = new CopyOnWriteArrayList<String>(); rVal.chunkCacheContents = new CopyOnWriteArrayList<String>();
rVal.interpolationRandomDampener = 0.0f; rVal.interpolationRandomDampener = 0.0f;
rVal.chunkGenerator = new ArenaChunkGenerator();
return rVal; return rVal;
} }
/**
* Generates a terrain model for the manager
*/
public void generate(){ public void generate(){
TerrainGenerator terrainGen = new TerrainGenerator(); TerrainGenerator terrainGen = new TerrainGenerator();
terrainGen.setInterpolationRatio(worldSizeDiscrete/200); terrainGen.setInterpolationRatio(worldSizeDiscrete/200);
terrainGen.setVerticalInterpolationRatio(verticalInterpolationRatio); terrainGen.setVerticalInterpolationRatio(verticalInterpolationRatio);
terrainGen.setRandomSeed(seed); terrainGen.setRandomSeed(seed);
model = terrainGen.generateModel(); model = terrainGen.generateModel();
this.chunkGenerator.setModel(model);
model.setInterpolationRandomDampener(interpolationRandomDampener); model.setInterpolationRandomDampener(interpolationRandomDampener);
this.chunkDiskMap = new ChunkDiskMap();
} }
/** /**
@ -100,10 +125,19 @@ public class ServerTerrainManager {
floatView.flip(); floatView.flip();
FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array()); FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array());
FileUtils.serializeObjectToSavePath(saveName, "./terrain.json", model); 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){ public void load(String saveName){
//load terrain model
model = FileUtils.loadObjectFromSavePath(saveName, "./terrain.json", TerrainModel.class); model = FileUtils.loadObjectFromSavePath(saveName, "./terrain.json", TerrainModel.class);
chunkGenerator.setModel(model);
byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./terrain.dat"); byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./terrain.dat");
ByteBuffer buffer = ByteBuffer.wrap(data); ByteBuffer buffer = ByteBuffer.wrap(data);
FloatBuffer floatView = buffer.asFloatBuffer(); FloatBuffer floatView = buffer.asFloatBuffer();
@ -114,6 +148,9 @@ public class ServerTerrainManager {
} }
} }
model.setElevationArray(elevation); model.setElevationArray(elevation);
//load chunk disk map
chunkDiskMap = new ChunkDiskMap();
chunkDiskMap.init(saveName);
} }
public float[][] getTerrainAtChunk(int x, int y){ 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() { public TerrainModel getModel() {
return model; 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){ public String getKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ; return worldX + "_" + worldY + "_" + worldZ;
} }
@ -172,77 +220,32 @@ public class ServerTerrainManager {
* @return The ServerTerrainChunk * @return The ServerTerrainChunk
*/ */
public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){ public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){
if(model != null){ //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING String key = getKey(worldX,worldY,worldZ);
String key = getKey(worldX,worldY,worldZ); ServerTerrainChunk returnedChunk = null;
ServerTerrainChunk returnedChunk; if(chunkCache.containsKey(key)){
if(chunkCache.containsKey(key)){ chunkCacheContents.remove(key);
chunkCacheContents.remove(key); chunkCacheContents.add(0, key);
chunkCacheContents.add(0, key); returnedChunk = chunkCache.get(key);
returnedChunk = chunkCache.get(key); return returnedChunk;
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;
}
} else { } else {
//THIS FIRES IF THERE IS AN ARENA WORLD RUNNING if(chunkCacheContents.size() > cacheSize){
String key = getKey(worldX, worldY, worldZ); String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1);
ServerTerrainChunk returnedChunk; chunkCache.remove(oldChunk);
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;
} }
} //pull from disk if it exists
} if(chunkDiskMap != null){
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){
private float[][] getHeightmap(int worldX, int worldZ){ returnedChunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ);
String key = worldX + "_" + worldZ; }
if(heightmapCache.containsKey(key)){ }
return heightmapCache.get(key); //generate if it does not exist
} else { if(returnedChunk == null){
float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ); returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ);
float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk( }
macroValues, chunkCache.put(key, returnedChunk);
model.getDynamicInterpolationRatio() chunkCacheContents.add(key);
); return returnedChunk;
heightmapCache.put(key,heightmap);
return heightmap;
} }
} }

View File

@ -193,6 +193,14 @@ public class FileUtils {
return Files.readString(targetFile.toPath()); return Files.readString(targetFile.toPath());
} }
/**
* Loads an object from a save folder
* @param <T> 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>T loadObjectFromSavePath(String saveName, String pathName, Class<T> className){ public static <T>T loadObjectFromSavePath(String saveName, String pathName, Class<T> className){
T rVal = null; T rVal = null;
String sanitizedFilePath = sanitizeFilePath(pathName); String sanitizedFilePath = sanitizeFilePath(pathName);
@ -204,6 +212,12 @@ public class FileUtils {
return rVal; 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){ public static void serializeObjectToSavePath(String saveName, String pathName, Object object){
String sanitizedFilePath = sanitizeFilePath(pathName); String sanitizedFilePath = sanitizeFilePath(pathName);
try { try {
@ -248,11 +262,11 @@ public class FileUtils {
/** /**
* Checks if a directory exists * Checks if a directory exists
* @param directoryName * @param fileName
* @return true if directory exists, false otherwise * @return true if directory exists, false otherwise
*/ */
public static boolean checkFileExists(String directoryName){ public static boolean checkFileExists(String fileName){
File targetDir = new File(sanitizeFilePath(directoryName)); File targetDir = new File(sanitizeFilePath(fileName));
if(targetDir.exists()){ if(targetDir.exists()){
return true; return true;
} else { } else {

View File

@ -1,6 +1,7 @@
package electrosphere.util.worldviewer; package electrosphere.util.worldviewer;
import electrosphere.server.simulation.MacroSimulation; import electrosphere.server.simulation.MacroSimulation;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.manager.ServerTerrainManager;
import electrosphere.server.terrain.models.TerrainModel; import electrosphere.server.terrain.models.TerrainModel;
@ -19,7 +20,7 @@ public class TerrainViewer {
TerrainModel terrainModel; 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(); terrainManager.generate();
terrainModel = terrainManager.getModel(); terrainModel = terrainManager.getModel();