client-server physics synchronization
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-08-11 19:31:43 -04:00
parent 1cc4924c3b
commit ee15dd2366
28 changed files with 759 additions and 105 deletions

View File

@ -11,9 +11,6 @@
- Block SFX
+ rearchitecture
Ability to synchronize state of synchronized variables on client joining scene
ie, send currentBlockVariant when client connects
this is probably going to involve massive netcode arch to send all synchronized variables for each entity
+ fix the vibes
Attack animation feels slow

View File

@ -535,6 +535,7 @@ Ability to serialize/deserialize a creature with equipped items
(08/11/2024)
Sending initial synchronized state on player connect to chunk
Pass at client-server physics synchronization
# TODO

View File

@ -49,6 +49,54 @@
"name" : "rotationW",
"type" : "FIXED_DOUBLE"
},
{
"name" : "linVelX",
"type" : "FIXED_DOUBLE"
},
{
"name" : "linVelY",
"type" : "FIXED_DOUBLE"
},
{
"name" : "linVelZ",
"type" : "FIXED_DOUBLE"
},
{
"name" : "angVelX",
"type" : "FIXED_DOUBLE"
},
{
"name" : "angVelY",
"type" : "FIXED_DOUBLE"
},
{
"name" : "angVelZ",
"type" : "FIXED_DOUBLE"
},
{
"name" : "linForceX",
"type" : "FIXED_DOUBLE"
},
{
"name" : "linForceY",
"type" : "FIXED_DOUBLE"
},
{
"name" : "linForceZ",
"type" : "FIXED_DOUBLE"
},
{
"name" : "angForceX",
"type" : "FIXED_DOUBLE"
},
{
"name" : "angForceY",
"type" : "FIXED_DOUBLE"
},
{
"name" : "angForceZ",
"type" : "FIXED_DOUBLE"
},
{
"name" : "yaw",
"type" : "FIXED_DOUBLE"
@ -263,6 +311,33 @@
"yaw",
"pitch"
]
},
{
"messageName" : "syncPhysics",
"description" : "Synchronizes server physics state to client",
"data" : [
"entityID",
"time",
"positionX",
"positionY",
"positionZ",
"rotationX",
"rotationY",
"rotationZ",
"rotationW",
"linVelX",
"linVelY",
"linVelZ",
"angVelX",
"angVelY",
"angVelZ",
"linForceX",
"linForceY",
"linForceZ",
"angForceX",
"angForceY",
"angForceZ"
]
}

View File

@ -1,7 +1,6 @@
package electrosphere.client.sim;
import org.joml.Vector3d;
import org.joml.Vector3f;
import electrosphere.client.fluid.manager.ClientFluidManager;
import electrosphere.client.instancing.InstanceUpdater;

View File

@ -1,31 +1,26 @@
package electrosphere.collision;
import static org.ode4j.ode.OdeConstants.dContactBounce;
import static org.ode4j.ode.OdeConstants.dContactSoftCFM;
import static org.ode4j.ode.OdeConstants.dInfinity;
// import static org.ode4j.ode.OdeConstants.dContactBounce;
// import static org.ode4j.ode.OdeConstants.dContactSoftCFM;
// import static org.ode4j.ode.OdeConstants.dInfinity;
import static org.ode4j.ode.OdeHelper.areConnectedExcluding;
import static org.ode4j.ode.OdeMath.dCalcVectorLengthSquare3;
import static org.ode4j.ode.OdeMath.dSubtractVectors3;
import static org.ode4j.ode.internal.Common.dRecip;
// import static org.ode4j.ode.OdeMath.dCalcVectorLengthSquare3;
// import static org.ode4j.ode.OdeMath.dSubtractVectors3;
// import static org.ode4j.ode.internal.Common.dRecip;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4d;
import org.ode4j.math.DMatrix3;
import org.ode4j.math.DVector3;
import org.ode4j.math.DVector3C;
import org.ode4j.math.DVector4;
import org.ode4j.ode.DBody;
import org.ode4j.ode.DBox;
import org.ode4j.ode.DCapsule;
@ -740,6 +735,27 @@ public class CollisionEngine {
spaceLock.release();
}
/**
* Synchronizes the data on a body
* @param body The body
* @param position The position
* @param rotation The rotation
* @param linearVel The linear velocity
* @param angularVel The angular velocity
* @param linearForce The linear force
* @param angularForce The angular force
*/
protected void synchronizeData(DBody body, Vector3d position, Quaterniond rotation, Vector3d linearVel, Vector3d angularVel, Vector3d linearForce, Vector3d angularForce){
spaceLock.acquireUninterruptibly();
body.setPosition(position.x, position.y, position.z);
body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation));
body.setLinearVel(PhysicsUtils.jomlVecToOdeVec(linearVel));
body.setAngularVel(PhysicsUtils.jomlVecToOdeVec(angularVel));
body.setForce(PhysicsUtils.jomlVecToOdeVec(linearForce));
body.setTorque(PhysicsUtils.jomlVecToOdeVec(angularForce));
spaceLock.release();
}
/**
* Sets the transform on a body
* @param body The body

View File

@ -28,29 +28,6 @@ public class CollisionWorldData {
}
/**
* IF SERVER, RETURN HEIGHT
* IF CLIENT:
* IF HAS DATA,
* RETURN HEIGHT
* IF DOES NOT HAVE DATA,
* RETURN 0
* @param position
* @return
*/
// public double getElevationAtPoint(Vector3d position){
// if(clientWorldData != null){
// if(clientTerrainManager.containsHeightmapAtRealPoint(position.x, position.z)){
// return clientTerrainManager.getHeightAtPosition(position.x, position.z);
// } else {
// return 0;
// }
// } else {
// return serverTerrainManager.getHeightAtPosition(position.x, position.z);
// }
// }
public Vector3f getWorldBoundMin(){
if(clientWorldData != null){
return clientWorldData.getWorldBoundMin();

View File

@ -1,10 +1,8 @@
package electrosphere.collision;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.ode4j.ode.DBody;
import org.ode4j.ode.DGeom;
import org.ode4j.ode.DTriMesh;
@ -17,6 +15,8 @@ import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.collidable.ClientCollidableTree;
import electrosphere.entity.state.collidable.ServerCollidableTree;
import electrosphere.entity.state.physicssync.ClientPhysicsSyncTree;
import electrosphere.entity.state.physicssync.ServerPhysicsSyncTree;
import electrosphere.entity.types.terrain.TerrainChunkData;
import electrosphere.game.data.collidable.CollidableTemplate;
import electrosphere.server.datacell.Realm;
@ -33,11 +33,9 @@ public class PhysicsEntityUtils {
* @param physicsTemplate The collidable template
*/
public static void clientAttachCollidableTemplate(Entity rVal, CollidableTemplate physicsTemplate){
DBody rigidBody;
DBody rigidBody = null;
Collidable collidable;
float mass = 1.0f;
Matrix4f inertiaTensor;
Vector3f scale;
switch(physicsTemplate.getType()){
case "CYLINDER": {
rigidBody = CollisionBodyCreation.createCylinderBody(
@ -63,7 +61,6 @@ public class PhysicsEntityUtils {
rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
rVal.putData(EntityDataStrings.CLIENT_COLLIDABLE_TREE, tree);
scale = new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3());
rVal.putData(EntityDataStrings.PHYSICS_MASS, mass);
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(rigidBody, collidable);
@ -88,13 +85,16 @@ public class PhysicsEntityUtils {
rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
rVal.putData(EntityDataStrings.CLIENT_COLLIDABLE_TREE, tree);
scale = new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3());
rVal.putData(EntityDataStrings.PHYSICS_MASS, mass);
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(rigidBody, collidable);
Globals.clientScene.registerEntityToTag(rVal, EntityTags.COLLIDABLE);
} break;
}
//if we successfully attached the body, add a sync tree
if(rigidBody != null){
ClientPhysicsSyncTree.attachTree(rVal);
}
}
@ -108,8 +108,6 @@ public class PhysicsEntityUtils {
DBody rigidBody = null;
Collidable collidable;
float mass = 1.0f;
Matrix4f inertiaTensor;
Vector3f scale;
switch(physicsTemplate.getType()){
case "CYLINDER": {
rigidBody = CollisionBodyCreation.createCylinderBody(
@ -136,7 +134,6 @@ public class PhysicsEntityUtils {
rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
rVal.putData(EntityDataStrings.SERVER_COLLIDABLE_TREE, tree);
scale = new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3());
rVal.putData(EntityDataStrings.PHYSICS_MASS, mass);
realm.getCollisionEngine().registerCollisionObject(rigidBody, collidable);
@ -157,13 +154,16 @@ public class PhysicsEntityUtils {
rVal.putData(EntityDataStrings.PHYSICS_COLLIDABLE, collidable);
rVal.putData(EntityDataStrings.SERVER_COLLIDABLE_TREE, tree);
scale = new Vector3f(physicsTemplate.getDimension1(),physicsTemplate.getDimension2(),physicsTemplate.getDimension3());
rVal.putData(EntityDataStrings.PHYSICS_MASS, mass);
realm.getCollisionEngine().registerCollisionObject(rigidBody, collidable);
ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.COLLIDABLE);
} break;
}
//if we successfully attached the body, add a sync tree
if(rigidBody != null){
ServerPhysicsSyncTree.attachTree(rVal);
}
}

View File

@ -78,6 +78,20 @@ public class PhysicsUtils {
collisionEngine.setBodyTransform(body, position, rotation);
}
/**
* Synchronizes the data on a body
* @param body The body
* @param position The position
* @param rotation The rotation
* @param linearVel The linear velocity
* @param angularVel The angular velocity
* @param linearForce The linear force
* @param angularForce The angular force
*/
public static void synchronizeData(CollisionEngine collisionEngine, DBody body, Vector3d position, Quaterniond rotation, Vector3d linearVel, Vector3d angularVel, Vector3d linearForce, Vector3d angularForce){
collisionEngine.synchronizeData(body, position, rotation, linearVel, angularVel, linearForce, angularForce);
}
/**
* Sets the position + rotation + scale of a body

View File

@ -300,6 +300,12 @@ public class EntityDataStrings {
*/
public static final String TREE_SERVERPLAYERVIEWDIR = "treeServerPlayerViewDir";
/**
* Physics synchronization
*/
public static final String TREE_SERVERPHYSICSSYNCTREE = "treeServerPhysicsSyncTree";
public static final String TREE_CLIENTPHYSICSSYNCTREE = "treeClientPhysicsSyncTree";
/*
Entity categories
*/

View File

@ -3,12 +3,15 @@ package electrosphere.entity;
import org.joml.Vector3d;
import electrosphere.engine.Globals;
import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
import electrosphere.server.datacell.utils.ServerEntityTagUtils;
/**
* Entity utilities specifically for the server side
@ -88,6 +91,11 @@ public class ServerEntityUtils {
//if the entity had physics, remove them from the world
realm.getCollisionEngine().destroyEntityThatHasPhysics(entity);
}
HitboxCollectionState.destroyHitboxState(entity);
ServerBehaviorTreeUtils.deregisterEntity(entity);
Globals.realmManager.removeEntity(entity);
EntityLookupUtils.removeEntity(entity);
//deregister all behavior trees
EntityUtils.cleanUpEntity(entity);
}

View File

@ -75,7 +75,6 @@ public class SceneLoader {
realm = Globals.realmManager.createRealm();
} break;
}
ServerDataCell rVal = realm.createNewCell();
//spawn initial entities
for(EntityDescriptor descriptor : file.getEntities()){
//spawn entity somehow
@ -85,14 +84,12 @@ public class SceneLoader {
Vector3d position = new Vector3d(descriptor.posX,descriptor.posY,descriptor.posZ);
Entity newEntity = CreatureUtils.serverSpawnBasicCreature(realm, position, descriptor.subtype, null);
EntityUtils.getRotation(newEntity).set((float)descriptor.rotX, (float)descriptor.rotY, (float)descriptor.rotZ, (float)descriptor.rotW);
rVal.initializeEntityForNewPlayers(newEntity, rVal);
} break;
case EntityDescriptor.TYPE_ITEM: {
Vector3d position = new Vector3d(descriptor.posX,descriptor.posY,descriptor.posZ);
Entity newEntity = ItemUtils.serverSpawnBasicItem(realm, position, descriptor.subtype);
EntityUtils.getRotation(newEntity).set((float)descriptor.rotX, (float)descriptor.rotY, (float)descriptor.rotZ, (float)descriptor.rotW);
rVal.initializeEntityForNewPlayers(newEntity, rVal);
} break;
case EntityDescriptor.TYPE_OBJECT: {
@ -100,7 +97,6 @@ public class SceneLoader {
Entity newEntity = ObjectUtils.serverSpawnBasicObject(realm, position, descriptor.subtype);
EntityUtils.getPosition(newEntity).set(descriptor.posX,descriptor.posY,descriptor.posZ);
EntityUtils.getRotation(newEntity).set((float)descriptor.rotX, (float)descriptor.rotY, (float)descriptor.rotZ, (float)descriptor.rotW);
rVal.initializeEntityForNewPlayers(newEntity, rVal);
} break;
default:
throw new UnsupportedOperationException();

View File

@ -6,6 +6,7 @@ import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.equip.ServerEquipState;
import electrosphere.entity.state.gravity.GravityUtils;
@ -113,10 +114,10 @@ public class InventoryUtils {
//send message
controllerPlayer.addMessage(InventoryMessage.constructaddItemToInventoryMessage(inventoryItem.getId(), ItemUtils.getType(inventoryItem)));
}
//destroy the item that was left over
ItemUtils.serverDestroyInWorldItem(item);
//alert script engine
ServerScriptUtils.fireSignalOnEntity(creature, "itemPickup", item.getId(), inventoryItem.getId());
//destroy the item that was left over
ServerEntityUtils.destroyEntity(item);
return inventoryItem;
}
return null;
@ -223,8 +224,8 @@ public class InventoryUtils {
// ServerDataCell dataCell = Globals.dataCellLocationResolver.getDataCellAtPoint(oldItemPos,realWorldItem);
ServerDataCell dataCell = Globals.realmManager.getEntityRealm(realWorldItem).getEntityDataCellMapper().getEntityDataCell(realWorldItem);
//broadcast destroy item
NetworkMessage destroyMessage = InventoryMessage.constructserverCommandUnequipItemMessage(creature.getId(), inventorySlot);
dataCell.broadcastNetworkMessage(destroyMessage);
NetworkMessage unequipMessage = InventoryMessage.constructserverCommandUnequipItemMessage(creature.getId(), inventorySlot);
dataCell.broadcastNetworkMessage(unequipMessage);
}
}
}
@ -244,8 +245,8 @@ public class InventoryUtils {
//
//compose item into in-world entity
Entity inWorldItem = ItemUtils.serverSpawnBasicItem(realm,dropSpot,ItemUtils.getType(item));
//delete in container item
ItemUtils.destroyInInventoryItem(item);
//destroy the entity on server side
ServerEntityUtils.destroyEntity(item);
//activate gravity
GravityUtils.serverAttemptActivateGravity(inWorldItem);
}

View File

@ -239,7 +239,6 @@ public class ClientGroundMovementTree implements BehaviorTree {
}
Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(facingVector.x,0,facingVector.z)).normalize();
Quaterniond rotation = EntityUtils.getRotation(parent);
rotation.set(movementQuaternion);
//parse attached network messages
for(EntityMessage message : networkMessageQueue){
@ -295,6 +294,9 @@ public class ClientGroundMovementTree implements BehaviorTree {
//state machine
switch(state){
case STARTUP: {
//update rotation
rotation.set(movementQuaternion);
//play animation
String animationToPlay = determineCorrectAnimation();
if(entityActor != null){
if(!entityActor.isPlayingAnimation(animationToPlay)){
@ -329,6 +331,9 @@ public class ClientGroundMovementTree implements BehaviorTree {
GravityUtils.clientAttemptActivateGravity(parent);
} break;
case MOVE: {
//update rotation
rotation.set(movementQuaternion);
//play animation
String animationToPlay = determineCorrectAnimation();
if(entityActor != null){
if(!entityActor.isPlayingAnimation(animationToPlay)){
@ -359,6 +364,8 @@ public class ClientGroundMovementTree implements BehaviorTree {
GravityUtils.clientAttemptActivateGravity(parent);
} break;
case SLOWDOWN: {
//update rotation
rotation.set(movementQuaternion);
//run slowdown code
String animationToPlay = determineCorrectAnimation();
if(entityActor != null){

View File

@ -0,0 +1,118 @@
package electrosphere.entity.state.physicssync;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.ode4j.ode.DBody;
import electrosphere.collision.PhysicsEntityUtils;
import electrosphere.collision.PhysicsUtils;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.net.parser.net.message.EntityMessage;
/**
* Receives synchronization data from the client
*/
public class ClientPhysicsSyncTree implements BehaviorTree {
//The parent entity for the tree
Entity parent;
//The most recent message received from the client
EntityMessage latestMessage = null;
//checks if the message has been pushed to the physics engine or not
boolean hasPushesMessage = true;
/**
* Constructor
* @param parent
* @param params
*/
private ClientPhysicsSyncTree(Entity parent, Object ... params){
this.parent = parent;
}
@Override
public void simulate(float deltaTime) {
if(!hasPushesMessage && latestMessage != null){
this.hasPushesMessage = true;
Vector3d position = new Vector3d(latestMessage.getpositionX(),latestMessage.getpositionY(),latestMessage.getpositionZ());
Quaterniond rotationFromServer = new Quaterniond(latestMessage.getrotationX(),latestMessage.getrotationY(),latestMessage.getrotationZ(),latestMessage.getrotationW());
Vector3d linearVelocity = new Vector3d(latestMessage.getlinVelX(), latestMessage.getlinVelY(), latestMessage.getlinVelZ());
Vector3d angularVelocity = new Vector3d();
Vector3d linearForce = new Vector3d(latestMessage.getlinForceX(), latestMessage.getlinForceY(), latestMessage.getlinForceZ());
Vector3d angularForce = new Vector3d();
DBody body = PhysicsEntityUtils.getDBody(parent);
EntityUtils.getPosition(parent).set(position);
EntityUtils.getRotation(parent).set(rotationFromServer);
PhysicsUtils.synchronizeData(Globals.clientSceneWrapper.getCollisionEngine(), body, position, rotationFromServer, linearVelocity, angularVelocity, linearForce, angularForce);
}
}
/**
* Sets the message for this sync tree
* @param syncMessage The synchronization message received from the server
*/
public void setMessage(EntityMessage syncMessage){
if(this.latestMessage == null){
this.latestMessage = syncMessage;
this.hasPushesMessage = false;
} else if(this.latestMessage.gettime() < syncMessage.gettime()){
this.latestMessage = syncMessage;
this.hasPushesMessage = false;
}
}
/**
* <p> (initially) Automatically generated </p>
* <p>
* Attaches this tree to the entity.
* </p>
* @param entity The entity to attach to
* @param tree The behavior tree to attach
* @param params Optional parameters that will be provided to the constructor
*/
public static ClientPhysicsSyncTree attachTree(Entity parent, Object ... params){
ClientPhysicsSyncTree rVal = new ClientPhysicsSyncTree(parent,params);
//!!WARNING!! from here below should not be touched
//This was generated automatically to properly alert various systems that the btree exists and should be tracked
parent.putData(EntityDataStrings.TREE_CLIENTPHYSICSSYNCTREE, rVal);
Globals.clientSceneWrapper.getScene().registerBehaviorTree(rVal);
return rVal;
}
/**
* <p> Automatically generated </p>
* <p>
* Detatches this tree from the entity.
* </p>
* @param entity The entity to detach to
* @param tree The behavior tree to detach
*/
public static void detachTree(Entity entity, BehaviorTree tree){
}
/**
* Returns whether the entity has a physics sync tree
* @param entity The entity to check
* @return True if the entity contains a physics sync tree, false otherwise
*/
public static boolean hasTree(Entity entity){
return entity.containsKey(EntityDataStrings.TREE_CLIENTPHYSICSSYNCTREE);
}
/**
* Gets the client physics sync tree on the entity
* @param entity The entity
* @return The tree if it exists, null otherwise
*/
public static ClientPhysicsSyncTree getTree(Entity entity){
return (ClientPhysicsSyncTree)entity.getData(EntityDataStrings.TREE_CLIENTPHYSICSSYNCTREE);
}
}

View File

@ -0,0 +1,129 @@
package electrosphere.entity.state.physicssync;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.ode4j.ode.DBody;
import electrosphere.collision.PhysicsEntityUtils;
import electrosphere.collision.PhysicsUtils;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
/**
* Synchronized physics state to client
*/
public class ServerPhysicsSyncTree implements BehaviorTree {
//The parent entity for the tree
Entity parent;
//the last sent transforms
Vector3d lastSentPosition = new Vector3d();
Quaterniond lastSentRotation = new Quaterniond();
//threshold of position/rotation difference after which a sync packet is sent to client
static final double UPDATE_THRESHOLD = 0.01;
/**
* Constructor
* @param parent
* @param params
*/
private ServerPhysicsSyncTree(Entity parent, Object ... params){
this.parent = parent;
}
@Override
public void simulate(float deltaTime) {
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
DBody body = PhysicsEntityUtils.getDBody(parent);
//velocities
Vector3d linearVel = PhysicsUtils.odeVecToJomlVec(body.getLinearVel());
Vector3d angularVel = PhysicsUtils.odeVecToJomlVec(body.getAngularVel());
//forces
Vector3d linearForce = PhysicsUtils.odeVecToJomlVec(body.getForce());
Vector3d angularForce = PhysicsUtils.odeVecToJomlVec(body.getTorque());
if(position.distance(lastSentPosition) > UPDATE_THRESHOLD || 1.0 - rotation.dot(lastSentRotation) > UPDATE_THRESHOLD){
lastSentPosition.set(position);
lastSentRotation.set(rotation);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructsyncPhysicsMessage(
parent.getId(),
Globals.timekeeper.getNumberOfSimFramesElapsed(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
linearVel.x,
linearVel.y,
linearVel.z,
angularVel.x,
angularVel.y,
angularVel.z,
linearForce.x,
linearForce.y,
linearForce.z,
angularForce.x,
angularForce.y,
angularForce.z
)
);
}
}
/**
* <p>
* Attaches this tree to the entity.
* </p>
* @param entity The entity to attach to
* @param tree The behavior tree to attach
* @param params Optional parameters that will be provided to the constructor
*/
public static ServerPhysicsSyncTree attachTree(Entity parent, Object ... params){
ServerPhysicsSyncTree rVal = new ServerPhysicsSyncTree(parent,params);
//!!WARNING!! from here below should not be touched
//This was generated automatically to properly alert various systems that the btree exists and should be tracked
ServerBehaviorTreeUtils.attachBTreeToEntity(parent, rVal);
parent.putData(EntityDataStrings.TREE_SERVERPHYSICSSYNCTREE, rVal);
return rVal;
}
/**
* <p>
* Detatches this tree from the entity.
* </p>
* @param entity The entity to detach to
* @param tree The behavior tree to detach
*/
public static void detachTree(Entity entity, BehaviorTree tree){
}
/**
* Returns whether the entity has a physics sync tree
* @param entity The entity to check
* @return True if the entity contains a physics sync tree, false otherwise
*/
public static boolean hasTree(Entity entity){
return entity.containsKey(EntityDataStrings.TREE_SERVERPHYSICSSYNCTREE);
}
/**
* Gets the server physics sync tree on the entity
* @param entity The entity
* @return The tree if it exists, null otherwise
*/
public static ServerPhysicsSyncTree getTree(Entity entity){
return (ServerPhysicsSyncTree)entity.getData(EntityDataStrings.TREE_SERVERPHYSICSSYNCTREE);
}
}

View File

@ -659,6 +659,12 @@ public class CreatureUtils {
//The server initialization logic checks what type of entity this is, if this function is called prior to its type being stored
//the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(realm,rVal,position);
//error checking
if(Globals.realmManager.getEntityRealm(rVal) == null){
LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Created creature without it being assigned to a realm!"));
}
return rVal;
}

View File

@ -24,6 +24,7 @@ import electrosphere.game.data.item.type.EquipData;
import electrosphere.game.data.item.type.EquipWhitelist;
import electrosphere.game.data.item.type.Item;
import electrosphere.game.data.item.type.WeaponData;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.server.player.Player;
@ -198,6 +199,11 @@ public class ItemUtils {
//the server will not be able to synchronize it properly.
ServerEntityUtils.initiallyPositionEntity(realm,rVal,correctedPosition);
//error checking
if(Globals.realmManager.getEntityRealm(rVal) == null){
LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Created item without it being assigned to a realm!"));
}
return rVal;
}
@ -427,32 +433,10 @@ public class ItemUtils {
}
}
public static void serverDestroyInWorldItem(Entity item){
if(isItem(item)){
//destroy physics
if(item.containsKey(EntityDataStrings.PHYSICS_COLLISION_BODY) && item.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){
Realm itemRealm = Globals.realmManager.getEntityRealm(item);
//destroy physics
//this deregisters from all four & unhooks rigid bodies from the physics runtime
if(itemRealm != null){
itemRealm.getCollisionEngine().destroyEntityThatHasPhysics(item);
//destroy hitboxes
HitboxCollectionState.destroyHitboxState(item);
}
//destroy graphics
EntityUtils.cleanUpEntity(item);
}
}
}
public static String getWeaponClass(Entity item){
return (String)item.getData(EntityDataStrings.ITEM_WEAPON_CLASS);
}
public static void destroyInInventoryItem(Entity item){
EntityUtils.cleanUpEntity(item);
}
public static String getItemIcon(Entity item){
return (String)item.getData(EntityDataStrings.ITEM_ICON);
}

View File

@ -451,18 +451,20 @@ public class ImGuiEntityMacros {
if(PhysicsEntityUtils.getDBody(detailViewEntity) != null){
DBody physicsBody = PhysicsEntityUtils.getDBody(detailViewEntity);
if(physicsBody != null){
ImGui.text("Position (Server): " + EntityUtils.getPosition(detailViewEntity));
ImGui.text("Rotation (Server): " + EntityUtils.getRotation(detailViewEntity));
ImGui.text("Velocity (Server): " + physicsBody.getLinearVel());
ImGui.text("Force (Server): " + physicsBody.getForce());
ImGui.text("Move Vector (Server): " + CreatureUtils.getFacingVector(detailViewEntity));
ImGui.text("Velocity (Server): " + CreatureUtils.getVelocity(detailViewEntity));
ImGui.text("Position: " + EntityUtils.getPosition(detailViewEntity));
ImGui.text("Rotation: " + EntityUtils.getRotation(detailViewEntity));
ImGui.text("Velocity: " + physicsBody.getLinearVel());
ImGui.text("Force: " + physicsBody.getForce());
ImGui.text("Angular Velocity: " + physicsBody.getAngularVel());
ImGui.text("Torque: " + physicsBody.getTorque());
ImGui.text("Move Vector: " + CreatureUtils.getFacingVector(detailViewEntity));
ImGui.text("Velocity: " + CreatureUtils.getVelocity(detailViewEntity));
}
//synchronized data
if(Globals.clientSceneWrapper.getScene().getEntityFromId(detailViewEntity.getId()) != null){
//detailViewEntity is a client entity
//get server entity
int serverIdForClientEntity = Globals.clientSceneWrapper.mapClientToServerId(Globals.playerEntity.getId());
int serverIdForClientEntity = Globals .clientSceneWrapper.mapClientToServerId(detailViewEntity.getId());
Entity serverEntity = EntityLookupUtils.getEntityById(serverIdForClientEntity);
DBody serverPhysicsBody = PhysicsEntityUtils.getDBody(serverEntity);
if(serverPhysicsBody != null){
@ -472,6 +474,8 @@ public class ImGuiEntityMacros {
ImGui.text("Rotation (Server): " + EntityUtils.getRotation(serverEntity));
ImGui.text("Velocity (Server): " + serverPhysicsBody.getLinearVel());
ImGui.text("Force (Server): " + serverPhysicsBody.getForce());
ImGui.text("Angular Velocity: " + serverPhysicsBody.getAngularVel());
ImGui.text("Torque: " + serverPhysicsBody.getTorque());
ImGui.text("Move Vector (Server): "+ CreatureUtils.getFacingVector(serverEntity));
ImGui.text("Velocity (Server): " + CreatureUtils.getVelocity(serverEntity));
}
@ -484,12 +488,14 @@ public class ImGuiEntityMacros {
if(clientPhysicsBody != null){
ImGui.newLine();
ImGui.text("Linked client entity:");
ImGui.text("Position (Server): " + EntityUtils.getPosition(clientEntity));
ImGui.text("Rotation (Server): " + EntityUtils.getRotation(clientEntity));
ImGui.text("Velocity (Server): " + clientPhysicsBody.getLinearVel());
ImGui.text("Force (Server): " + clientPhysicsBody.getForce());
ImGui.text("Move Vector (Server): " + CreatureUtils.getFacingVector(clientEntity));
ImGui.text("Velocity (Server): " + CreatureUtils.getVelocity(clientEntity));
ImGui.text("Position (Client): " + EntityUtils.getPosition(clientEntity));
ImGui.text("Rotation (Client): " + EntityUtils.getRotation(clientEntity));
ImGui.text("Velocity (Client): " + clientPhysicsBody.getLinearVel());
ImGui.text("Force (Client): " + clientPhysicsBody.getForce());
ImGui.text("Angular Velocity: " + clientPhysicsBody.getAngularVel());
ImGui.text("Torque: " + clientPhysicsBody.getTorque());
ImGui.text("Move Vector (Client): " + CreatureUtils.getFacingVector(clientEntity));
ImGui.text("Velocity (Client): " + CreatureUtils.getVelocity(clientEntity));
}
}
}

View File

@ -13,6 +13,7 @@ import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.attack.ClientAttackTree;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.physicssync.ClientPhysicsSyncTree;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.creature.CreatureEquipData.EquippedItem;
import electrosphere.entity.types.creature.CreatureTemplate;
@ -35,12 +36,13 @@ import electrosphere.util.Utilities;
public class EntityProtocol implements ClientProtocolTemplate<EntityMessage> {
//Messages to ignore when complaining about messages that have nonexistant entity associated
static List<EntityMessageType> spawnMessageTypes = Arrays.asList(new EntityMessageType[]{
static List<EntityMessageType> idModifyingMessages = Arrays.asList(new EntityMessageType[]{
EntityMessageType.CREATE,
EntityMessageType.SPAWNCREATURE,
EntityMessageType.SPAWNFOLIAGESEED,
EntityMessageType.SPAWNITEM,
EntityMessageType.SPAWNOBJECT,
EntityMessageType.DESTROY,
});
@Override
@ -54,7 +56,7 @@ public class EntityProtocol implements ClientProtocolTemplate<EntityMessage> {
LoggerInterface.loggerNetworking.DEBUG_LOOP("Parse entity message of type " + message.getMessageSubtype());
//error check
if(Globals.clientScene != null && Globals.clientSceneWrapper.getEntityFromServerId(message.getentityID()) == null && !spawnMessageTypes.contains(message.getMessageSubtype())){
if(Globals.clientScene != null && Globals.clientSceneWrapper.getEntityFromServerId(message.getentityID()) == null && !idModifyingMessages.contains(message.getMessageSubtype())){
LoggerInterface.loggerNetworking.WARNING("Client received packet for entity that is not in the client scene!");
Globals.clientSceneWrapper.dumpTranslationLayerStatus();
Globals.clientSceneWrapper.dumpIdData(message.getentityID());
@ -180,6 +182,13 @@ public class EntityProtocol implements ClientProtocolTemplate<EntityMessage> {
ClientAttackTree.getClientAttackTree(Globals.clientSceneWrapper.getEntityFromServerId(message.getentityID())).addNetworkMessage(message);
} break;
case SYNCPHYSICS: {
Entity entity = Globals.clientSceneWrapper.getEntityFromServerId(message.getentityID());
if(entity != null && ClientPhysicsSyncTree.hasTree(entity)){
ClientPhysicsSyncTree.getTree(entity).setMessage(message);
}
} break;
//
//
@ -187,7 +196,10 @@ public class EntityProtocol implements ClientProtocolTemplate<EntityMessage> {
//
//
case DESTROY: {
EntityUtils.cleanUpEntity(Globals.clientSceneWrapper.getEntityFromServerId(message.getentityID()));
Entity entity = Globals.clientSceneWrapper.getEntityFromServerId(message.getentityID());
if(entity != null){
EntityUtils.cleanUpEntity(entity);
}
} break;
@ -200,8 +212,8 @@ public class EntityProtocol implements ClientProtocolTemplate<EntityMessage> {
case KILL:
//to be implemented
throw new UnsupportedOperationException();
default:
//unused
case STARTATTACK:
//silently ignore
break;
}
Globals.profiler.endCpuSample();

View File

@ -21,6 +21,7 @@ public class EntityMessage extends NetworkMessage {
ATTACHENTITYTOENTITY,
SPAWNFOLIAGESEED,
UPDATEENTITYVIEWDIR,
SYNCPHYSICS,
}
EntityMessageType messageType;
@ -35,6 +36,18 @@ public class EntityMessage extends NetworkMessage {
double rotationY;
double rotationZ;
double rotationW;
double linVelX;
double linVelY;
double linVelZ;
double angVelX;
double angVelY;
double angVelZ;
double linForceX;
double linForceY;
double linForceZ;
double angForceX;
double angForceY;
double angForceZ;
double yaw;
double pitch;
double velocity;
@ -151,6 +164,102 @@ public class EntityMessage extends NetworkMessage {
this.rotationW = rotationW;
}
public double getlinVelX() {
return linVelX;
}
public void setlinVelX(double linVelX) {
this.linVelX = linVelX;
}
public double getlinVelY() {
return linVelY;
}
public void setlinVelY(double linVelY) {
this.linVelY = linVelY;
}
public double getlinVelZ() {
return linVelZ;
}
public void setlinVelZ(double linVelZ) {
this.linVelZ = linVelZ;
}
public double getangVelX() {
return angVelX;
}
public void setangVelX(double angVelX) {
this.angVelX = angVelX;
}
public double getangVelY() {
return angVelY;
}
public void setangVelY(double angVelY) {
this.angVelY = angVelY;
}
public double getangVelZ() {
return angVelZ;
}
public void setangVelZ(double angVelZ) {
this.angVelZ = angVelZ;
}
public double getlinForceX() {
return linForceX;
}
public void setlinForceX(double linForceX) {
this.linForceX = linForceX;
}
public double getlinForceY() {
return linForceY;
}
public void setlinForceY(double linForceY) {
this.linForceY = linForceY;
}
public double getlinForceZ() {
return linForceZ;
}
public void setlinForceZ(double linForceZ) {
this.linForceZ = linForceZ;
}
public double getangForceX() {
return angForceX;
}
public void setangForceX(double angForceX) {
this.angForceX = angForceX;
}
public double getangForceY() {
return angForceY;
}
public void setangForceY(double angForceY) {
this.angForceY = angForceY;
}
public double getangForceZ() {
return angForceZ;
}
public void setangForceZ(double angForceZ) {
this.angForceZ = angForceZ;
}
public double getyaw() {
return yaw;
}
@ -355,6 +464,12 @@ public class EntityMessage extends NetworkMessage {
} else {
return false;
}
case TypeBytes.ENTITY_MESSAGE_TYPE_SYNCPHYSICS:
if(byteBuffer.getRemaining() >= TypeBytes.ENTITY_MESSAGE_TYPE_SYNCPHYSICS_SIZE){
return true;
} else {
return false;
}
}
return false;
}
@ -829,6 +944,60 @@ public class EntityMessage extends NetworkMessage {
return rVal;
}
public static EntityMessage parsesyncPhysicsMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.SYNCPHYSICS);
stripPacketHeader(byteBuffer);
rVal.setentityID(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.settime(ByteStreamUtils.popLongFromByteQueue(byteBuffer));
rVal.setpositionX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setpositionY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setpositionZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setrotationX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setrotationY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setrotationZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setrotationW(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setlinVelX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setlinVelY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setlinVelZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setangVelX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setangVelY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setangVelZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setlinForceX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setlinForceY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setlinForceZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setangForceX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setangForceY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
rVal.setangForceZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
return rVal;
}
public static EntityMessage constructsyncPhysicsMessage(int entityID,long time,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double rotationW,double linVelX,double linVelY,double linVelZ,double angVelX,double angVelY,double angVelZ,double linForceX,double linForceY,double linForceZ,double angForceX,double angForceY,double angForceZ){
EntityMessage rVal = new EntityMessage(EntityMessageType.SYNCPHYSICS);
rVal.setentityID(entityID);
rVal.settime(time);
rVal.setpositionX(positionX);
rVal.setpositionY(positionY);
rVal.setpositionZ(positionZ);
rVal.setrotationX(rotationX);
rVal.setrotationY(rotationY);
rVal.setrotationZ(rotationZ);
rVal.setrotationW(rotationW);
rVal.setlinVelX(linVelX);
rVal.setlinVelY(linVelY);
rVal.setlinVelZ(linVelZ);
rVal.setangVelX(angVelX);
rVal.setangVelY(angVelY);
rVal.setangVelZ(angVelZ);
rVal.setlinForceX(linForceX);
rVal.setlinForceY(linForceY);
rVal.setlinForceZ(linForceZ);
rVal.setangForceX(angForceX);
rVal.setangForceY(angForceY);
rVal.setangForceZ(angForceZ);
rVal.serialize();
return rVal;
}
@Override
void serialize(){
byte[] intValues = new byte[8];
@ -1205,6 +1374,97 @@ public class EntityMessage extends NetworkMessage {
rawBytes[26+i] = intValues[i];
}
break;
case SYNCPHYSICS:
rawBytes = new byte[2+4+8+8+8+8+8+8+8+8+8+8+8+8+8+8+8+8+8+8+8+8];
//message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_ENTITY;
//entity messaage header
rawBytes[1] = TypeBytes.ENTITY_MESSAGE_TYPE_SYNCPHYSICS;
intValues = ByteStreamUtils.serializeIntToBytes(entityID);
for(int i = 0; i < 4; i++){
rawBytes[2+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeLongToBytes(time);
for(int i = 0; i < 8; i++){
rawBytes[6+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(positionX);
for(int i = 0; i < 8; i++){
rawBytes[14+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(positionY);
for(int i = 0; i < 8; i++){
rawBytes[22+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(positionZ);
for(int i = 0; i < 8; i++){
rawBytes[30+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(rotationX);
for(int i = 0; i < 8; i++){
rawBytes[38+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(rotationY);
for(int i = 0; i < 8; i++){
rawBytes[46+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(rotationZ);
for(int i = 0; i < 8; i++){
rawBytes[54+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(rotationW);
for(int i = 0; i < 8; i++){
rawBytes[62+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(linVelX);
for(int i = 0; i < 8; i++){
rawBytes[70+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(linVelY);
for(int i = 0; i < 8; i++){
rawBytes[78+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(linVelZ);
for(int i = 0; i < 8; i++){
rawBytes[86+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(angVelX);
for(int i = 0; i < 8; i++){
rawBytes[94+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(angVelY);
for(int i = 0; i < 8; i++){
rawBytes[102+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(angVelZ);
for(int i = 0; i < 8; i++){
rawBytes[110+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(linForceX);
for(int i = 0; i < 8; i++){
rawBytes[118+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(linForceY);
for(int i = 0; i < 8; i++){
rawBytes[126+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(linForceZ);
for(int i = 0; i < 8; i++){
rawBytes[134+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(angForceX);
for(int i = 0; i < 8; i++){
rawBytes[142+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(angForceY);
for(int i = 0; i < 8; i++){
rawBytes[150+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeDoubleToBytes(angForceZ);
for(int i = 0; i < 8; i++){
rawBytes[158+i] = intValues[i];
}
break;
}
serialized = true;
}

View File

@ -107,6 +107,11 @@ COMBAT_MESSAGE,
rVal = EntityMessage.parseupdateEntityViewDirMessage(byteBuffer);
}
break;
case TypeBytes.ENTITY_MESSAGE_TYPE_SYNCPHYSICS:
if(EntityMessage.canParseMessage(byteBuffer,secondByte)){
rVal = EntityMessage.parsesyncPhysicsMessage(byteBuffer);
}
break;
}
break;
case TypeBytes.MESSAGE_TYPE_LORE:

View File

@ -31,6 +31,7 @@ Message categories
public static final byte ENTITY_MESSAGE_TYPE_ATTACHENTITYTOENTITY = 10;
public static final byte ENTITY_MESSAGE_TYPE_SPAWNFOLIAGESEED = 11;
public static final byte ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR = 12;
public static final byte ENTITY_MESSAGE_TYPE_SYNCPHYSICS = 13;
/*
Entity packet sizes
*/
@ -41,6 +42,7 @@ Message categories
public static final byte ENTITY_MESSAGE_TYPE_DESTROY_SIZE = 6;
public static final byte ENTITY_MESSAGE_TYPE_SETPROPERTY_SIZE = 22;
public static final byte ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR_SIZE = 34;
public static final short ENTITY_MESSAGE_TYPE_SYNCPHYSICS_SIZE = 166;
/*
Lore subcategories
*/

View File

@ -65,6 +65,7 @@ public class EntityProtocol implements ServerProtocolTemplate<EntityMessage> {
case SPAWNFOLIAGESEED:
case SPAWNITEM:
case SPAWNOBJECT:
case SYNCPHYSICS:
//silently ignore
break;
}

View File

@ -2,6 +2,7 @@ package electrosphere.server.ai.creature;
import java.util.Random;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import electrosphere.engine.Globals;
@ -85,7 +86,8 @@ public class Blocker implements AITree {
}
if(target != null){
Vector3d targetPos = EntityUtils.getPosition(target);
EntityUtils.getRotation(parent).set(MathUtils.calculateRotationFromPointToPoint(EntityUtils.getPosition(parent), targetPos));
Quaterniond rotation = MathUtils.calculateRotationFromPointToPoint(EntityUtils.getPosition(parent), targetPos);
EntityUtils.getRotation(parent).set(rotation);
}
}

View File

@ -24,13 +24,20 @@ public class PlayerCharacterCreation {
*/
public static void spawnPlayerCharacter(ServerConnectionHandler connectionHandler){
Player playerObject = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId());
Realm realm = Globals.realmManager.getRealms().iterator().next();
//
//get template
CreatureTemplate template = connectionHandler.getCurrentCreatureTemplate();
String raceName = template.getCreatureType();
//
//spawn entity in world
Realm realm = Globals.realmManager.getRealms().iterator().next();
Vector3d spawnPoint = realm.getSpawnPoint();
Entity newPlayerEntity = CreatureUtils.serverSpawnBasicCreature(realm,new Vector3d(spawnPoint.x,spawnPoint.y,spawnPoint.z),raceName,template);
//
//attach entity to player object
LoggerInterface.loggerEngine.INFO("Spawned entity for player. Entity id: " + newPlayerEntity.getId() + " Player id: " + playerObject.getId());
int playerCharacterId = newPlayerEntity.getId();
connectionHandler.setPlayerEntityId(playerCharacterId);
@ -45,6 +52,13 @@ public class PlayerCharacterCreation {
realm.getServerWorldData().convertRealToChunkSpace(spawnPoint.z)
));
realm.getDataCellManager().addPlayerToRealm(playerObject);
//
//error checking
Realm searchedRealm = Globals.realmManager.getEntityRealm(newPlayerEntity);
if(searchedRealm == null){
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Player entity created but not attached to a realm!"));
}
}
/**

View File

@ -6,6 +6,7 @@ import org.joml.Vector3d;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell;
@ -20,7 +21,15 @@ public class DataCellSearchUtils {
* @return The data cell the entitiy is inside of, or null if it isn't inside a data cell for some reason
*/
public static ServerDataCell getEntityDataCell(Entity entity){
return Globals.realmManager.getEntityRealm(entity).getEntityDataCellMapper().getEntityDataCell(entity);
if(entity == null){
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to get entity data cell of null!"));
}
Realm realm = Globals.realmManager.getEntityRealm(entity);
if(realm == null){
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to get entity data cell of an entity that is not assigned to a realm!"));
return null;
}
return realm.getEntityDataCellMapper().getEntityDataCell(entity);
}
/**

View File

@ -35,8 +35,9 @@ public class ServerBehaviorTreeUtils {
Set<BehaviorTree> trees = entityBTreeMap.remove(entity);
ServerDataCell currentCell = DataCellSearchUtils.getEntityDataCell(entity);
for(BehaviorTree tree : trees){
currentCell.getScene().registerBehaviorTree(tree);
currentCell.getScene().deregisterBehaviorTree(tree);
}
currentCell.getScene().deregisterEntity(entity);
}
/**

View File

@ -85,6 +85,14 @@ public class MathUtils {
return new Vector3f(0,0,1);
}
/**
* Gets the up rotation
* @return The up rotation
*/
public static Quaterniond getUpRotation(){
return MathUtils.calculateRotationFromPointToPoint(MathUtils.getOriginVector(), MathUtils.getUpVector());
}
/**
* Calculates the quaternion that rotates the origin vector to point from origin to destination