Physics fixes
This commit is contained in:
parent
ceb5a5aaf8
commit
c71f4fecad
3
buildNumber.properties
Normal file
3
buildNumber.properties
Normal file
@ -0,0 +1,3 @@
|
||||
#maven.buildNumber.plugin properties file
|
||||
#Fri Jul 14 20:48:01 EDT 2023
|
||||
buildNumber=4
|
||||
8
pom.xml
8
pom.xml
@ -13,6 +13,14 @@
|
||||
<joml.version>1.9.19</joml.version>
|
||||
</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>
|
||||
<!-- generic LWJGL runtimes -->
|
||||
<!--License: BSD-->
|
||||
|
||||
1
saves/default/chunk.map
Normal file
1
saves/default/chunk.map
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -741,4 +689,30 @@ public class CollisionEngine {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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<String,ServerDataCell> groundDataCells = new HashMap<String,ServerDataCell>();
|
||||
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
|
||||
Semaphore loadedCellsLock = new Semaphore(1);
|
||||
Set<ServerDataCell> loadedCells;
|
||||
@ -55,7 +60,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
*/
|
||||
public void init(ServerWorldData data){
|
||||
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
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -30,38 +30,6 @@ public class ServerTerrainChunk {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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<String, ServerTerrainChunk> chunkCache;
|
||||
List<String> 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<String, float[][]> 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<String, ServerTerrainChunk>();
|
||||
this.chunkCacheContents = new CopyOnWriteArrayList<String>();
|
||||
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<String, ServerTerrainChunk>();
|
||||
rVal.chunkCacheContents = new CopyOnWriteArrayList<String>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -193,6 +193,14 @@ public class FileUtils {
|
||||
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){
|
||||
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 {
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user