client hitbox body destruction based on dist
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-04-04 17:44:52 -04:00
parent 86c867fe31
commit bf35da4529
7 changed files with 322 additions and 138 deletions

View File

@ -1436,6 +1436,7 @@ Unarmed combat
GriddedDataCellTrackingData created GriddedDataCellTrackingData created
Reduce allocations in GriddedDataCellManager methods that loop cells Reduce allocations in GriddedDataCellManager methods that loop cells
Implement max distance for queueing for simulation Implement max distance for queueing for simulation
Client hitbox body destruction based on distance from player (for performance)

View File

@ -777,6 +777,50 @@ public class CollisionEngine {
} }
} }
/**
* Destroys a body + collidable pair
* @param body The body
* @param collidable The collidable
*/
protected void destroyPhysicsPair(DBody body, Collidable collidable){
spaceLock.lock();
this.deregisterCollisionObject(body, collidable);
this.destroyDBody(body);
spaceLock.unlock();
}
/**
* Disables a body
* @param body The body
*/
protected void disable(DBody body){
spaceLock.lock();
body.disable();
spaceLock.unlock();
}
/**
* Enables a body
* @param body The body
*/
protected void enable(DBody body){
spaceLock.lock();
body.enable();
spaceLock.unlock();
}
/**
* Checks if a body is enabled
* @param body The body
* @return true if it is enabled, false otherwise
*/
protected boolean isEnabled(DBody body){
spaceLock.lock();
boolean rVal = body.isEnabled();
spaceLock.unlock();
return rVal;
}
/** /**
* Creates a trimesh from a given set of vertices and indices * Creates a trimesh from a given set of vertices and indices
* @param verts The vertices * @param verts The vertices

View File

@ -8,6 +8,7 @@ import org.ode4j.math.DQuaternion;
import org.ode4j.math.DQuaternionC; import org.ode4j.math.DQuaternionC;
import org.ode4j.ode.DBody; import org.ode4j.ode.DBody;
import electrosphere.collision.collidable.Collidable;
import electrosphere.game.data.collidable.CollidableTemplate; import electrosphere.game.data.collidable.CollidableTemplate;
/** /**
@ -120,5 +121,42 @@ public class PhysicsUtils {
collisionEngine.destroyDBody(body); collisionEngine.destroyDBody(body);
} }
/**
* Destroys a body + collidable pair
* @param body The body
* @param collidable The collidable
*/
public static void destroyPhysicsPair(CollisionEngine collisionEngine, DBody body, Collidable collidable){
collisionEngine.destroyPhysicsPair(body, collidable);
}
/**
* Disables a body
* @param collisionEngine The collision engine
* @param body The body
*/
public static void disableBody(CollisionEngine collisionEngine, DBody body){
collisionEngine.disable(body);
}
/**
* Enables a body
* @param collisionEngine The collision engine
* @param body The body
*/
public static void enableBody(CollisionEngine collisionEngine, DBody body){
collisionEngine.enable(body);
}
/**
* Checks if a body is enabled
* @param collisionEngine The collision engine
* @param body The body
* @return true if the body is enabled, false otherwise
*/
public static boolean isBodyEnabled(CollisionEngine collisionEngine, DBody body){
return collisionEngine.isEnabled(body);
}
} }

View File

@ -30,7 +30,9 @@ public class HitboxManager {
*/ */
ReentrantLock lock = new ReentrantLock(); ReentrantLock lock = new ReentrantLock();
//an id incrementer for hitboxes /**
* An incrementer for hitbox IDs
*/
long idIncrementer = 0; long idIncrementer = 0;
/** /**

View File

@ -17,28 +17,37 @@ import electrosphere.entity.btree.BehaviorTree;
*/ */
public class ClientCollidableTree implements BehaviorTree { public class ClientCollidableTree implements BehaviorTree {
/**
* The parent entity of this tree
*/
Entity parent; Entity parent;
//the ode body for this collidable tree
/**
* The body for this tree
*/
DBody body; DBody body;
/**
* The collidable for this tree
*/
Collidable collidable; Collidable collidable;
boolean applyImpulses = true; /**
* Constructor
* @param e The entity
* @param collidable The collidable
* @param body The body
*/
public ClientCollidableTree(Entity e, Collidable collidable, DBody body){ public ClientCollidableTree(Entity e, Collidable collidable, DBody body){
parent = e; parent = e;
this.collidable = collidable; this.collidable = collidable;
this.body = body; this.body = body;
} }
public ClientCollidableTree(Entity e, Collidable collidable, DBody body, boolean applyImpulses){
parent = e;
this.collidable = collidable;
this.body = body;
this.applyImpulses = applyImpulses;
}
static int incrementer = 0;
/**
* Simulates the component
*/
public void simulate(float deltaTime){ public void simulate(float deltaTime){
Vector3d position = EntityUtils.getPosition(parent); Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent); Quaterniond rotation = EntityUtils.getRotation(parent);
@ -68,10 +77,20 @@ public class ClientCollidableTree implements BehaviorTree {
this.collidable = collidable; this.collidable = collidable;
} }
/**
* Checks if the entity has a collidable tree
* @param e The entity
* @return true if the entity contains a collidable tree, false otherwise
*/
public static boolean hasClientCollidableTree(Entity e){ public static boolean hasClientCollidableTree(Entity e){
return e.containsKey(EntityDataStrings.CLIENT_COLLIDABLE_TREE); return e.containsKey(EntityDataStrings.CLIENT_COLLIDABLE_TREE);
} }
/**
* Gets the collidable tree on the entity
* @param e The entity
* @return The tree
*/
public static ClientCollidableTree getClientCollidableTree(Entity e){ public static ClientCollidableTree getClientCollidableTree(Entity e){
return (ClientCollidableTree)e.getData(EntityDataStrings.CLIENT_COLLIDABLE_TREE); return (ClientCollidableTree)e.getData(EntityDataStrings.CLIENT_COLLIDABLE_TREE);
} }

View File

@ -54,22 +54,49 @@ public class HitboxCollectionState {
SOUR, //less damage SOUR, //less damage
} }
//the parent entity of the hitbox state /**
* Distance after which hitboxes are disabled on the client
*/
public static final double CLIENT_DISABLE_DISTANCE = 40;
/**
* Distance within which hitboxes are enabled on the client
*/
public static final double CLIENT_ENABLE_DISTANCE = 35;
/**
* The parent entity of the hitbox state
*/
Entity parent; Entity parent;
//the body that contains all the hitbox shapes /**
* The body that contains all the hitbox shapes
*/
DBody body; DBody body;
//The collidable associated with the body /**
* The collidable associated with the body
*/
Collidable collidable; Collidable collidable;
//the list of all geoms in the collection state /**
* The list of all geoms in the collection state
*/
List<DGeom> geoms = new LinkedList<DGeom>(); List<DGeom> geoms = new LinkedList<DGeom>();
//the map of bone -> list of states of individual shapes attached to that bone
/**
* The map of bone -> list of states of individual shapes attached to that bone
*/
Map<String,List<HitboxState>> boneHitboxMap = new HashMap<String,List<HitboxState>>(); Map<String,List<HitboxState>> boneHitboxMap = new HashMap<String,List<HitboxState>>();
//map of hitbox state -> geometry
/**
* Map of hitbox state -> geometry
*/
Map<HitboxState,DGeom> stateGeomMap = new HashMap<HitboxState,DGeom>(); Map<HitboxState,DGeom> stateGeomMap = new HashMap<HitboxState,DGeom>();
//the map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision
/**
* The map of geometry -> hitbox shape status, useful for finding data about a given hitbox during collision
*/
Map<DGeom,HitboxState> geomStateMap = new HashMap<DGeom,HitboxState>(); Map<DGeom,HitboxState> geomStateMap = new HashMap<DGeom,HitboxState>();
/** /**
@ -82,19 +109,34 @@ public class HitboxCollectionState {
*/ */
List<HitboxState> nonBoneHitboxes = new LinkedList<HitboxState>(); List<HitboxState> nonBoneHitboxes = new LinkedList<HitboxState>();
//callback to provide a position for the hitbox each frame /**
* Callback to provide a position for the hitbox each frame
*/
HitboxPositionCallback positionCallback; HitboxPositionCallback positionCallback;
//controls whether the hitbox state is active or not /**
* The raw data to pattern hitboxes off of
*/
List<HitboxData> rawData;
/**
* Controls whether the hitbox state is active or not
*/
boolean active = true; boolean active = true;
//controls whether active hitboxes should be overwritten with block boxes /**
* Controls whether active hitboxes should be overwritten with block boxes
*/
boolean blockOverride = false; boolean blockOverride = false;
//the associated manager /**
* The associated manager
*/
HitboxManager manager; HitboxManager manager;
//controls whether this hitbox collection thinks its on the server or client /**
* Controls whether this hitbox collection thinks its on the server or client
*/
boolean isServer = true; boolean isServer = true;
@ -107,94 +149,15 @@ public class HitboxCollectionState {
*/ */
public static HitboxCollectionState attachHitboxState(HitboxManager manager, boolean isServer, Entity entity, List<HitboxData> hitboxListRaw){ public static HitboxCollectionState attachHitboxState(HitboxManager manager, boolean isServer, Entity entity, List<HitboxData> hitboxListRaw){
HitboxCollectionState rVal = new HitboxCollectionState(); HitboxCollectionState rVal = new HitboxCollectionState();
rVal.rawData = hitboxListRaw;
rVal.manager = manager;
rVal.isServer = isServer; rVal.isServer = isServer;
//create the shapes
for(HitboxData hitboxDataRaw : hitboxListRaw){
DGeom geom = null;
HitboxType type = HitboxType.HIT;
HitboxShapeType shapeType = HitboxShapeType.SPHERE;
//
//Get the type as an enum
//
switch(hitboxDataRaw.getType()){
case HitboxData.HITBOX_TYPE_HIT: {
type = HitboxType.HIT;
shapeType = HitboxShapeType.SPHERE;
geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT);
} break;
case HitboxData.HITBOX_TYPE_HURT: {
type = HitboxType.HURT;
shapeType = HitboxShapeType.SPHERE;
geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT);
} break;
case HitboxData.HITBOX_TYPE_HIT_CONNECTED: {
type = HitboxType.HIT;
shapeType = HitboxShapeType.CAPSULE;
geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT);
} break;
case HitboxData.HITBOX_TYPE_HURT_CONNECTED: {
type = HitboxType.HURT;
shapeType = HitboxShapeType.CAPSULE;
geom = CollisionBodyCreation.createShapeSphere(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT);
} break;
case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: {
type = HitboxType.HURT;
shapeType = HitboxShapeType.STATIC_CAPSULE;
geom = CollisionBodyCreation.createCapsuleShape(manager.getCollisionEngine(), hitboxDataRaw.getRadius(), hitboxDataRaw.getLength(), Collidable.TYPE_OBJECT_BIT);
} break;
}
//
//Get the subtype as an enum
//
HitboxSubtype subType;
String subTypeRaw = hitboxDataRaw.getSubType();
if(subTypeRaw == null){
subTypeRaw = HitboxData.HITBOX_SUBTYPE_REUGLAR;
}
switch(subTypeRaw){
case HitboxData.HITBOX_SUBTYPE_SWEET: {
subType = HitboxSubtype.SWEET;
} break;
case HitboxData.HITBOX_SUBTYPE_REUGLAR: {
subType = HitboxSubtype.REGULAR;
} break;
case HitboxData.HITBOX_SUBTYPE_SOUR: {
subType = HitboxSubtype.SOUR;
} break;
default: {
subType = HitboxSubtype.REGULAR;
} break;
}
//create the state for the individual shape
HitboxState state = new HitboxState(hitboxDataRaw.getBone(), hitboxDataRaw, type, subType, shapeType, false);
//add to structures
if(hitboxDataRaw.getBone() != null){
rVal.addHitbox(hitboxDataRaw.getBone(),state);
} else {
rVal.addHitbox(state);
}
rVal.registerGeom(geom,state);
}
//create body with all the shapes
DGeom[] geomArray = rVal.geoms.toArray(new DGeom[rVal.geoms.size()]);
rVal.body = CollisionBodyCreation.createBodyWithShapes(manager.getCollisionEngine(), geomArray);
CollisionBodyCreation.setKinematic(manager.getCollisionEngine(), rVal.body);
//register collidable with collision engine
Collidable collidable = new Collidable(entity, Collidable.TYPE_OBJECT, true);
manager.getCollisionEngine().registerCollisionObject(rVal.body, collidable);
//attach //attach
entity.putData(EntityDataStrings.HITBOX_DATA, rVal); entity.putData(EntityDataStrings.HITBOX_DATA, rVal);
rVal.parent = entity; rVal.parent = entity;
//register //register
rVal.createBody();
manager.registerHitbox(rVal); manager.registerHitbox(rVal);
rVal.manager = manager;
return rVal; return rVal;
} }
@ -238,6 +201,110 @@ public class HitboxCollectionState {
// return rVal; // return rVal;
} }
/**
* Creates the body
*/
private void createBody(){
CollisionEngine collisionEngine = this.manager.getCollisionEngine();
//create the shapes
for(HitboxData hitboxDataRaw : this.rawData){
DGeom geom = null;
HitboxType type = HitboxType.HIT;
HitboxShapeType shapeType = HitboxShapeType.SPHERE;
//
//Get the type as an enum
//
switch(hitboxDataRaw.getType()){
case HitboxData.HITBOX_TYPE_HIT: {
type = HitboxType.HIT;
shapeType = HitboxShapeType.SPHERE;
geom = CollisionBodyCreation.createShapeSphere(collisionEngine, hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT);
} break;
case HitboxData.HITBOX_TYPE_HURT: {
type = HitboxType.HURT;
shapeType = HitboxShapeType.SPHERE;
geom = CollisionBodyCreation.createShapeSphere(collisionEngine, hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT);
} break;
case HitboxData.HITBOX_TYPE_HIT_CONNECTED: {
type = HitboxType.HIT;
shapeType = HitboxShapeType.CAPSULE;
geom = CollisionBodyCreation.createShapeSphere(collisionEngine, hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT);
} break;
case HitboxData.HITBOX_TYPE_HURT_CONNECTED: {
type = HitboxType.HURT;
shapeType = HitboxShapeType.CAPSULE;
geom = CollisionBodyCreation.createShapeSphere(collisionEngine, hitboxDataRaw.getRadius(), Collidable.TYPE_OBJECT_BIT);
} break;
case HitboxData.HITBOX_TYPE_STATIC_CAPSULE: {
type = HitboxType.HURT;
shapeType = HitboxShapeType.STATIC_CAPSULE;
geom = CollisionBodyCreation.createCapsuleShape(collisionEngine, hitboxDataRaw.getRadius(), hitboxDataRaw.getLength(), Collidable.TYPE_OBJECT_BIT);
} break;
}
//
//Get the subtype as an enum
//
HitboxSubtype subType;
String subTypeRaw = hitboxDataRaw.getSubType();
if(subTypeRaw == null){
subTypeRaw = HitboxData.HITBOX_SUBTYPE_REUGLAR;
}
switch(subTypeRaw){
case HitboxData.HITBOX_SUBTYPE_SWEET: {
subType = HitboxSubtype.SWEET;
} break;
case HitboxData.HITBOX_SUBTYPE_REUGLAR: {
subType = HitboxSubtype.REGULAR;
} break;
case HitboxData.HITBOX_SUBTYPE_SOUR: {
subType = HitboxSubtype.SOUR;
} break;
default: {
subType = HitboxSubtype.REGULAR;
} break;
}
//create the state for the individual shape
HitboxState state = new HitboxState(hitboxDataRaw.getBone(), hitboxDataRaw, type, subType, shapeType, false);
//add to structures
if(hitboxDataRaw.getBone() != null){
this.addHitbox(hitboxDataRaw.getBone(),state);
} else {
this.addHitbox(state);
}
this.registerGeom(geom,state);
}
//create body with all the shapes
DGeom[] geomArray = this.geoms.toArray(new DGeom[this.geoms.size()]);
this.body = CollisionBodyCreation.createBodyWithShapes(collisionEngine, geomArray);
CollisionBodyCreation.setKinematic(collisionEngine, this.body);
//register collidable with collision engine
this.collidable = new Collidable(this.parent, Collidable.TYPE_OBJECT, true);
collisionEngine.registerCollisionObject(this.body, this.collidable);
}
/**
* Destroys the body
*/
private void destroyBody(){
//destroy body
if(this.body != null){
PhysicsUtils.destroyPhysicsPair(this.manager.getCollisionEngine(), this.body, this.collidable);
//clear tracking structures
this.geoms.clear();
this.boneHitboxMap.clear();
this.stateGeomMap.clear();
this.geomStateMap.clear();
this.allStates.clear();
this.nonBoneHitboxes.clear();
this.body = null;
this.collidable = null;
}
}
/** /**
* Clears the collision status of all shapes * Clears the collision status of all shapes
*/ */
@ -255,6 +322,16 @@ public class HitboxCollectionState {
if(parent != null && !isServer && EntityUtils.getActor(parent) != null){ if(parent != null && !isServer && EntityUtils.getActor(parent) != null){
if(!this.geoms.isEmpty()){ if(!this.geoms.isEmpty()){
Vector3d entityPosition = EntityUtils.getPosition(parent); Vector3d entityPosition = EntityUtils.getPosition(parent);
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
double distanceToPlayer = entityPosition.distance(playerPos);
if(distanceToPlayer > CLIENT_DISABLE_DISTANCE){
if(this.body != null){
this.destroyBody();
}
} else if(distanceToPlayer < CLIENT_ENABLE_DISTANCE) {
if(this.body == null){
this.createBody();
}
PhysicsUtils.setRigidBodyTransform(collisionEngine, entityPosition, new Quaterniond(), body); PhysicsUtils.setRigidBodyTransform(collisionEngine, entityPosition, new Quaterniond(), body);
for(String boneName : this.boneHitboxMap.keySet()){ for(String boneName : this.boneHitboxMap.keySet()){
if(EntityUtils.getActor(parent).containsBone(boneName)){ if(EntityUtils.getActor(parent).containsBone(boneName)){
@ -290,6 +367,7 @@ public class HitboxCollectionState {
} }
} }
} }
}
//update bone-attached hitboxes on server //update bone-attached hitboxes on server
} else if(parent != null && isServer && EntityUtils.getPoseActor(parent) != null){ } else if(parent != null && isServer && EntityUtils.getPoseActor(parent) != null){
@ -506,6 +584,9 @@ public class HitboxCollectionState {
* @return the hitbox state if it exists * @return the hitbox state if it exists
*/ */
public static HitboxCollectionState getHitboxState(Entity entity){ public static HitboxCollectionState getHitboxState(Entity entity){
if(!entity.containsKey(EntityDataStrings.HITBOX_DATA)){
return null;
}
return (HitboxCollectionState)entity.getData(EntityDataStrings.HITBOX_DATA); return (HitboxCollectionState)entity.getData(EntityDataStrings.HITBOX_DATA);
} }
@ -549,7 +630,7 @@ public class HitboxCollectionState {
} else { } else {
engine = Globals.clientSceneWrapper.getHitboxManager().getCollisionEngine(); engine = Globals.clientSceneWrapper.getHitboxManager().getCollisionEngine();
} }
if(engine != null){ if(engine != null && body != null){
PhysicsUtils.destroyBody(engine, body); PhysicsUtils.destroyBody(engine, body);
} }
} }

View File

@ -361,7 +361,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
public boolean updatePlayerPositions(){ public boolean updatePlayerPositions(){
Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions - Reset chunk distances"); Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions - Reset chunk distances");
loadedCellsLock.lock(); loadedCellsLock.lock();
Globals.profiler.beginCpuSample("GriddedDataCellManager.updatePlayerPositions - Remove from old cells");
for(ServerDataCell cell : this.groundDataCells.values()){ for(ServerDataCell cell : this.groundDataCells.values()){
GriddedDataCellTrackingData trackingData = this.cellTrackingMap.get(cell); GriddedDataCellTrackingData trackingData = this.cellTrackingMap.get(cell);
trackingData.setClosestPlayer(GriddedDataCellTrackingData.REALLY_LARGE_DISTANCE); trackingData.setClosestPlayer(GriddedDataCellTrackingData.REALLY_LARGE_DISTANCE);