diff --git a/docs/src/progress/currenttarget.md b/docs/src/progress/currenttarget.md index 595e7390..c4c35c5c 100644 --- a/docs/src/progress/currenttarget.md +++ b/docs/src/progress/currenttarget.md @@ -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 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 96ecb7a9..b05777ef 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -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 diff --git a/net/entity.json b/net/entity.json index 8e64d34e..7dfec583 100644 --- a/net/entity.json +++ b/net/entity.json @@ -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" + ] } diff --git a/src/main/java/electrosphere/client/sim/ClientSimulation.java b/src/main/java/electrosphere/client/sim/ClientSimulation.java index d2842e17..85922d1b 100644 --- a/src/main/java/electrosphere/client/sim/ClientSimulation.java +++ b/src/main/java/electrosphere/client/sim/ClientSimulation.java @@ -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; diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index b9302a8c..bdae2ffa 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -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 diff --git a/src/main/java/electrosphere/collision/CollisionWorldData.java b/src/main/java/electrosphere/collision/CollisionWorldData.java index 86778b80..33c3d00d 100644 --- a/src/main/java/electrosphere/collision/CollisionWorldData.java +++ b/src/main/java/electrosphere/collision/CollisionWorldData.java @@ -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(); diff --git a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java index 6f03d7bc..79c54e6d 100644 --- a/src/main/java/electrosphere/collision/PhysicsEntityUtils.java +++ b/src/main/java/electrosphere/collision/PhysicsEntityUtils.java @@ -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); + } } diff --git a/src/main/java/electrosphere/collision/PhysicsUtils.java b/src/main/java/electrosphere/collision/PhysicsUtils.java index f14b8651..02c54f3e 100644 --- a/src/main/java/electrosphere/collision/PhysicsUtils.java +++ b/src/main/java/electrosphere/collision/PhysicsUtils.java @@ -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 diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index 7d4faaec..b9bf2071 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -299,6 +299,12 @@ public class EntityDataStrings { * Server-specific btrees */ 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 diff --git a/src/main/java/electrosphere/entity/ServerEntityUtils.java b/src/main/java/electrosphere/entity/ServerEntityUtils.java index 21f33cef..a3d0b384 100644 --- a/src/main/java/electrosphere/entity/ServerEntityUtils.java +++ b/src/main/java/electrosphere/entity/ServerEntityUtils.java @@ -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); } diff --git a/src/main/java/electrosphere/entity/scene/SceneLoader.java b/src/main/java/electrosphere/entity/scene/SceneLoader.java index f2e19417..af044fba 100644 --- a/src/main/java/electrosphere/entity/scene/SceneLoader.java +++ b/src/main/java/electrosphere/entity/scene/SceneLoader.java @@ -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(); diff --git a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java index 6bb50605..83f3fe33 100644 --- a/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java +++ b/src/main/java/electrosphere/entity/state/inventory/InventoryUtils.java @@ -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); } diff --git a/src/main/java/electrosphere/entity/state/movement/groundmove/ClientGroundMovementTree.java b/src/main/java/electrosphere/entity/state/movement/groundmove/ClientGroundMovementTree.java index fbcb981f..80f453f1 100644 --- a/src/main/java/electrosphere/entity/state/movement/groundmove/ClientGroundMovementTree.java +++ b/src/main/java/electrosphere/entity/state/movement/groundmove/ClientGroundMovementTree.java @@ -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){ diff --git a/src/main/java/electrosphere/entity/state/physicssync/ClientPhysicsSyncTree.java b/src/main/java/electrosphere/entity/state/physicssync/ClientPhysicsSyncTree.java new file mode 100644 index 00000000..1d9d4147 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/physicssync/ClientPhysicsSyncTree.java @@ -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; + } + } + + /** + *
(initially) Automatically generated
+ *+ * Attaches this tree to the entity. + *
+ * @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; + } + + /** + *Automatically generated
+ *+ * Detatches this tree from the entity. + *
+ * @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); + } + +} + diff --git a/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java b/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java new file mode 100644 index 00000000..a841f548 --- /dev/null +++ b/src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java @@ -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 + ) + ); + } + } + + /** + *+ * Attaches this tree to the entity. + *
+ * @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; + } + + /** + *+ * Detatches this tree from the entity. + *
+ * @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); + } + +} diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java index c317f258..00d14092 100644 --- a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java +++ b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java @@ -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; } diff --git a/src/main/java/electrosphere/entity/types/item/ItemUtils.java b/src/main/java/electrosphere/entity/types/item/ItemUtils.java index 843aacf5..c096207d 100644 --- a/src/main/java/electrosphere/entity/types/item/ItemUtils.java +++ b/src/main/java/electrosphere/entity/types/item/ItemUtils.java @@ -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); } diff --git a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java index 2569c8b0..42e09006 100644 --- a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java +++ b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java @@ -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,7 +474,9 @@ public class ImGuiEntityMacros { ImGui.text("Rotation (Server): " + EntityUtils.getRotation(serverEntity)); ImGui.text("Velocity (Server): " + serverPhysicsBody.getLinearVel()); ImGui.text("Force (Server): " + serverPhysicsBody.getForce()); - ImGui.text("Move Vector (Server): " + CreatureUtils.getFacingVector(serverEntity)); + 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)); } } else if(Globals.clientSceneWrapper.containsServerId(detailViewEntity.getId())){ @@ -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)); } } } diff --git a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java index 5c1ab014..c5e0e337 100644 --- a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java @@ -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