From ee15dd23667aa7a5b1c6d1cc843577626d14aadf Mon Sep 17 00:00:00 2001 From: austin Date: Sun, 11 Aug 2024 19:31:43 -0400 Subject: [PATCH] client-server physics synchronization --- docs/src/progress/currenttarget.md | 3 - docs/src/progress/renderertodo.md | 1 + net/entity.json | 75 +++++ .../client/sim/ClientSimulation.java | 1 - .../collision/CollisionEngine.java | 38 ++- .../collision/CollisionWorldData.java | 23 -- .../collision/PhysicsEntityUtils.java | 22 +- .../electrosphere/collision/PhysicsUtils.java | 14 + .../entity/EntityDataStrings.java | 6 + .../entity/ServerEntityUtils.java | 8 + .../entity/scene/SceneLoader.java | 4 - .../state/inventory/InventoryUtils.java | 13 +- .../groundmove/ClientGroundMovementTree.java | 9 +- .../physicssync/ClientPhysicsSyncTree.java | 118 ++++++++ .../physicssync/ServerPhysicsSyncTree.java | 129 +++++++++ .../entity/types/creature/CreatureUtils.java | 6 + .../entity/types/item/ItemUtils.java | 28 +- .../menu/debug/ImGuiEntityMacros.java | 34 ++- .../net/client/protocol/EntityProtocol.java | 22 +- .../net/parser/net/message/EntityMessage.java | 260 ++++++++++++++++++ .../parser/net/message/NetworkMessage.java | 5 + .../net/parser/net/message/TypeBytes.java | 2 + .../net/server/protocol/EntityProtocol.java | 1 + .../server/ai/creature/Blocker.java | 4 +- .../character/PlayerCharacterCreation.java | 16 +- .../datacell/utils/DataCellSearchUtils.java | 11 +- .../utils/ServerBehaviorTreeUtils.java | 3 +- .../electrosphere/util/math/MathUtils.java | 8 + 28 files changed, 759 insertions(+), 105 deletions(-) create mode 100644 src/main/java/electrosphere/entity/state/physicssync/ClientPhysicsSyncTree.java create mode 100644 src/main/java/electrosphere/entity/state/physicssync/ServerPhysicsSyncTree.java 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 { //Messages to ignore when complaining about messages that have nonexistant entity associated - static List spawnMessageTypes = Arrays.asList(new EntityMessageType[]{ + static List 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 { 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 { 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 { // // 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 { case KILL: //to be implemented throw new UnsupportedOperationException(); - default: - //unused + case STARTATTACK: + //silently ignore break; } Globals.profiler.endCpuSample(); diff --git a/src/main/java/electrosphere/net/parser/net/message/EntityMessage.java b/src/main/java/electrosphere/net/parser/net/message/EntityMessage.java index f1491dce..973797d3 100644 --- a/src/main/java/electrosphere/net/parser/net/message/EntityMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/EntityMessage.java @@ -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; } diff --git a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java index 2b3f4cdc..a39f6c26 100644 --- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java @@ -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: diff --git a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java index 93f4e5cf..8112ac86 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java +++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java @@ -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 */ diff --git a/src/main/java/electrosphere/net/server/protocol/EntityProtocol.java b/src/main/java/electrosphere/net/server/protocol/EntityProtocol.java index da435919..9510e9b0 100644 --- a/src/main/java/electrosphere/net/server/protocol/EntityProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/EntityProtocol.java @@ -65,6 +65,7 @@ public class EntityProtocol implements ServerProtocolTemplate { case SPAWNFOLIAGESEED: case SPAWNITEM: case SPAWNOBJECT: + case SYNCPHYSICS: //silently ignore break; } diff --git a/src/main/java/electrosphere/server/ai/creature/Blocker.java b/src/main/java/electrosphere/server/ai/creature/Blocker.java index 734d7509..892deddf 100644 --- a/src/main/java/electrosphere/server/ai/creature/Blocker.java +++ b/src/main/java/electrosphere/server/ai/creature/Blocker.java @@ -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); } } diff --git a/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java b/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java index 40e8ef74..92a98a4b 100644 --- a/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java +++ b/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java @@ -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!")); + } } /** diff --git a/src/main/java/electrosphere/server/datacell/utils/DataCellSearchUtils.java b/src/main/java/electrosphere/server/datacell/utils/DataCellSearchUtils.java index 2a7b5aee..90dc498f 100644 --- a/src/main/java/electrosphere/server/datacell/utils/DataCellSearchUtils.java +++ b/src/main/java/electrosphere/server/datacell/utils/DataCellSearchUtils.java @@ -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); } /** diff --git a/src/main/java/electrosphere/server/datacell/utils/ServerBehaviorTreeUtils.java b/src/main/java/electrosphere/server/datacell/utils/ServerBehaviorTreeUtils.java index 06ba0856..d8e0f336 100644 --- a/src/main/java/electrosphere/server/datacell/utils/ServerBehaviorTreeUtils.java +++ b/src/main/java/electrosphere/server/datacell/utils/ServerBehaviorTreeUtils.java @@ -35,8 +35,9 @@ public class ServerBehaviorTreeUtils { Set 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); } /** diff --git a/src/main/java/electrosphere/util/math/MathUtils.java b/src/main/java/electrosphere/util/math/MathUtils.java index 9494bd1b..979e5f07 100644 --- a/src/main/java/electrosphere/util/math/MathUtils.java +++ b/src/main/java/electrosphere/util/math/MathUtils.java @@ -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