fixing up gridded data cell manager
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-11-13 21:08:32 -05:00
parent 755db7ba72
commit 9a20a64d5b
14 changed files with 205 additions and 82 deletions

View File

@ -1016,6 +1016,10 @@ Ability to replacing a player's entity
Fix shadows on main shader
Button to swap between player entity and editor entity
Fix physics being disabled on editing a level
Fix entity repositioning bugs + Enable recursive logic
Fix hitbox manager handling of destruction
Fix gridded data cell manager player + entity position handling and refactor to simplify logic
Add debug options for entities to reposition on server

View File

@ -0,0 +1,33 @@
package electrosphere.client.ui.menu.debug.entity;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import imgui.ImGui;
/**
* An entire tab of debug actions
*/
public class ImGuiEntityDebugActions {
/**
* Debug actions view
*/
protected static void drawDebugActions(boolean show, Entity detailViewEntity){
if(show && ImGui.collapsingHeader("Debug Actions")){
ImGui.indent();
if(detailViewEntity != null){
if(ImGui.button("Teleport to player")){
if(Globals.clientSceneWrapper.containsServerId(detailViewEntity.getId())){
ServerEntityUtils.repositionEntity(detailViewEntity, EntityUtils.getPosition(Globals.playerEntity));
}
}
}
ImGui.unindent();
}
}
}

View File

@ -63,6 +63,7 @@ public class ImGuiEntityMacros {
private static boolean showLinkedEntitiesTab = false;//show linked entities
private static boolean showServerViewDirTab = false; //show server view dir
private static boolean showPhysicsTab = false; //show physics values
private static boolean showDebugActionsTab = false; //show debug actions
/**
* Creates the windows in this file
@ -164,6 +165,9 @@ public class ImGuiEntityMacros {
if(HitboxCollectionState.hasHitboxState(detailViewEntity) && ImGui.checkbox("Hitbox State", showHitboxTab)){
showHitboxTab = !showHitboxTab;
}
if(ImGui.checkbox("Debug Actions", showDebugActionsTab)){
showDebugActionsTab = !showDebugActionsTab;
}
ImGui.treePop();
}
ImGui.nextColumn();
@ -176,6 +180,7 @@ public class ImGuiEntityMacros {
ImGuiEntityMacros.drawLinkedEntities();
ImGuiEntityMacros.drawServerViewDir();
ImGuiEntityMacros.drawPhysicsDetails();
ImGuiEntityDebugActions.drawDebugActions(showDebugActionsTab, detailViewEntity);
ImGuiEntityMacros.drawDataView();
}
});

View File

@ -84,6 +84,11 @@ public class CollisionEngine {
* This keeps the physics simulation much more stable than it would be otherwise.
*/
public static final int PHYSICS_SIMULATION_RESOLUTION = 5;
/**
* Threshold after which the engine warns about collidable count
*/
public static final int COLLIDABLE_COUNT_WARNING_THRESHOLD = 1000;
//world data that the collision engine leverages for position correction and the like
CollisionWorldData collisionWorldData;
@ -577,9 +582,14 @@ public class CollisionEngine {
Globals.profiler.endCpuSample();
}
/**
* Registers a collision object with the server
* @param body The body
* @param collidable The corresponding collidable
*/
public void registerCollisionObject(DBody body, Collidable collidable){
spaceLock.acquireUninterruptibly();
registerPhysicsObject(body);
this.registerPhysicsObject(body);
bodyPointerMap.put(body,collidable);
collidableList.add(collidable);
spaceLock.release();
@ -688,9 +698,16 @@ public class CollisionEngine {
return data.collisionPosition;
}
/**
* Registers a body
* @param body The body
*/
public void registerPhysicsObject(DBody body){
if(!bodies.contains(body)){
bodies.add(body);
if(bodies.size() > COLLIDABLE_COUNT_WARNING_THRESHOLD){
LoggerInterface.loggerEngine.WARNING("Body count has superceded the warning threshold! " + bodies.size());
}
// OdeHelper.createBody(world);
}
}
@ -699,7 +716,7 @@ public class CollisionEngine {
* Destroys a body and all geometry under the body
* @param body The DBody to destroy
*/
private void deregisterPhysicsObject(DBody body){
protected void destroyDBody(DBody body){
spaceLock.acquireUninterruptibly();
if(bodies.contains(body)){
bodies.remove(body);
@ -734,7 +751,7 @@ public class CollisionEngine {
this.deregisterCollisionObject(rigidBody,PhysicsEntityUtils.getCollidable(e));
e.removeData(EntityDataStrings.PHYSICS_COLLISION_BODY);
if(rigidBody != null){
this.deregisterPhysicsObject(rigidBody);
this.destroyDBody(rigidBody);
}
}
if(ServerPhysicsSyncTree.hasTree(e)){
@ -1084,7 +1101,11 @@ public class CollisionEngine {
* @param geom The geometry
*/
protected void destroyGeom(DGeom geom){
spaceLock.acquireUninterruptibly();
this.space.remove(geom);
geom.DESTRUCTOR();
geom.destroy();
spaceLock.release();
}
/**

View File

@ -1,5 +1,8 @@
package electrosphere.collision;
import java.util.LinkedList;
import java.util.List;
import org.joml.Matrix4d;
import org.joml.Quaterniond;
import org.joml.Vector3d;
@ -11,6 +14,8 @@ import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.collidable.ClientCollidableTree;
import electrosphere.entity.state.collidable.ServerCollidableTree;
import electrosphere.entity.state.physicssync.ClientPhysicsSyncTree;
@ -485,6 +490,24 @@ public class PhysicsEntityUtils {
return terrainBody;
}
/**
* Repositions all active physics-scoped entities on a given realm
* @param collisionEngine The realm's collision engine
*/
public static void serverRepositionEntities(CollisionEngine collisionEngine){
List<Entity> toReposition = new LinkedList<Entity>();
for(Collidable collidable : collisionEngine.getCollidables()){
Entity entity = collidable.getParent();
DBody body = PhysicsEntityUtils.getDBody(entity);
if(body != null && body.isEnabled() && !body.isKinematic()){
toReposition.add(entity);
}
}
for(Entity parent : toReposition){
ServerEntityUtils.repositionEntity(parent,EntityUtils.getPosition(parent));
}
}
/**
* Gets the transform from parent entity position to rigid body
* @param entity The entity

View File

@ -120,5 +120,14 @@ public class PhysicsUtils {
collisionEngine.setBodyTransform(body, template, position, rotation, scale);
}
/**
* Destroys a body
* @param collisionEngine The collision engine
* @param body The body
*/
public static void destroyBody(CollisionEngine collisionEngine, DBody body){
collisionEngine.destroyDBody(body);
}
}

View File

@ -7,6 +7,7 @@ import org.joml.Vector3d;
import electrosphere.engine.Globals;
import electrosphere.entity.state.attach.AttachUtils;
import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.renderer.actor.ActorUtils;
@ -64,6 +65,7 @@ public class ClientEntityUtils {
if(Globals.clientScene != null){
Globals.clientScene.deregisterEntity(entity);
}
HitboxCollectionState.destroyHitboxState(entity,false);
if(entity == Globals.playerEntity && Globals.firstPersonEntity != null){
ClientEntityUtils.destroyEntity(Globals.firstPersonEntity);
}

View File

@ -56,6 +56,7 @@ public class EntityCreationUtils {
if(Globals.entityDataCellMapper.getEntityDataCell(rVal) == null){
Globals.entityDataCellMapper.registerEntity(rVal, cell);
ServerDataCell mapping = Globals.entityDataCellMapper.getEntityDataCell(rVal);
throw new Error("Failed to map entity to cell!");
}

View File

@ -55,7 +55,7 @@ public class ServerEntityUtils {
}
/**
* Called to reposition the creature
* Called to reposition the entity
* @param entity
* @param position
*/
@ -63,10 +63,25 @@ public class ServerEntityUtils {
if(position == null){
throw new Error("Trying to set server entity position to null!");
}
if(AttachUtils.getParent(entity) != null){
throw new Error("Trying to reposition attached entity!");
}
Realm realm = Globals.realmManager.getEntityRealm(entity);
if(position.x < 0 || position.y < 0 || position.z < 0){
throw new Error("Providing invalid location to reposition! " + position);
}
ServerEntityUtils.repositionEntityRecursive(realm, entity, position);
//reposition entity
CollisionObjUtils.serverPositionCharacter(entity, position);
}
/**
* Called to reposition the entity
* @param realm The realm containing the entity
* @param entity The entity
* @param position The new position for the entity
*/
protected static void repositionEntityRecursive(Realm realm, Entity entity, Vector3d position){
//if server, get current server data cell
ServerDataCell oldDataCell = Globals.entityDataCellMapper.getEntityDataCell(entity);
ServerDataCell newDataCell = realm.getDataCellManager().getDataCellAtPoint(position);
@ -92,8 +107,12 @@ public class ServerEntityUtils {
throw new Error("Entity not removed from scene!");
}
}
//reposition entity
CollisionObjUtils.serverPositionCharacter(entity, position);
if(AttachUtils.hasChildren(entity)){
List<Entity> children = AttachUtils.getChildrenList(entity);
for(Entity child : children){
repositionEntityRecursive(realm, child, position);
}
}
}
/**
@ -105,15 +124,6 @@ public class ServerEntityUtils {
throw new IllegalArgumentException("Trying to destroy null!");
}
//
//destroy the child entities, too
if(AttachUtils.hasChildren(entity)){
List<Entity> children = AttachUtils.getChildrenList(entity);
for(Entity child : children){
ServerEntityUtils.destroyEntity(child);
}
}
//
//get info required to destroy
Realm realm = Globals.realmManager.getEntityRealm(entity);
@ -136,7 +146,7 @@ public class ServerEntityUtils {
//
//detatch from all global tracking
HitboxCollectionState.destroyHitboxState(entity);
HitboxCollectionState.destroyHitboxState(entity,true);
Globals.realmManager.removeEntity(entity);
EntityLookupUtils.removeEntity(entity);
if(Globals.aiManager != null){
@ -146,6 +156,15 @@ public class ServerEntityUtils {
//
//deregister all behavior trees
EntityUtils.cleanUpEntity(entity);
//
//destroy the child entities, too
if(AttachUtils.hasChildren(entity)){
List<Entity> children = AttachUtils.getChildrenList(entity);
for(Entity child : children){
ServerEntityUtils.destroyEntity(child);
}
}
}
/**

View File

@ -18,6 +18,7 @@ import electrosphere.collision.PhysicsUtils;
import electrosphere.collision.collidable.Collidable;
import electrosphere.collision.hitbox.HitboxManager;
import electrosphere.collision.hitbox.HitboxUtils.HitboxPositionCallback;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
@ -26,6 +27,7 @@ import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState.Hitbo
import electrosphere.game.data.collidable.HitboxData;
import electrosphere.game.data.utils.DataFormatUtil;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.datacell.Realm;
import electrosphere.util.math.SpatialMathUtils;
/**
@ -518,16 +520,37 @@ public class HitboxCollectionState {
/**
* Destroys the hitbox state and removes it from the entity
* @param entity the entity
* @param isServer true if this is the server, false otherwise
* @return The hitbox state if it exists, null otherwise
*/
public static HitboxCollectionState destroyHitboxState(Entity entity){
public static HitboxCollectionState destroyHitboxState(Entity entity, boolean isServer){
HitboxCollectionState state = null;
if(hasHitboxState(entity)){
state = getHitboxState(entity);
if(HitboxCollectionState.hasHitboxState(entity)){
state = HitboxCollectionState.getHitboxState(entity);
state.manager.deregisterHitbox(state);
state.destroy(isServer);
}
return state;
}
/**
* Destroys the content of the state
* @param true if this is the server, false otherwise
*/
protected void destroy(boolean isServer){
CollisionEngine engine = null;
if(isServer){
Realm realm = Globals.realmManager.getEntityRealm(parent);
if(realm != null){
engine = realm.getHitboxManager().getCollisionEngine();
}
} else {
engine = Globals.clientSceneWrapper.getHitboxManager().getCollisionEngine();
}
if(engine != null){
PhysicsUtils.destroyBody(engine, body);
}
}
/**
* Gets whether the hitbox state is active or not

View File

@ -451,7 +451,7 @@ public class ItemUtils {
//this deregisters from all four & unhooks rigid bodies from the physics runtime
Globals.clientSceneWrapper.getCollisionEngine().destroyPhysics(item);
//destroy hitboxes
HitboxCollectionState.destroyHitboxState(item);
HitboxCollectionState.destroyHitboxState(item,false);
//destroy graphics
ClientEntityUtils.destroyEntity(item);
}

View File

@ -40,6 +40,9 @@ public class EntityDataCellMapper {
* @param serverDataCell The new server data cell for the entity
*/
public void updateEntityCell(Entity entity, ServerDataCell serverDataCell){
if(serverDataCell == null){
throw new Error("Passing null to cell mapper update! " + entity + " " + serverDataCell);
}
entityDataCellMap.put(entity, serverDataCell);
}

View File

@ -164,11 +164,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
for(int x = worldPos.x - playerSimulationRadius; x < worldPos.x + playerSimulationRadius + 1; x++){
for(int y = worldPos.y - playerSimulationRadius; y < worldPos.y + playerSimulationRadius + 1; y++){
for(int z = worldPos.z - playerSimulationRadius; z < worldPos.z + playerSimulationRadius + 1; z++){
if(
x >= 0 && x < this.serverWorldData.getWorldSizeDiscrete() &&
y >= 0 && y < this.serverWorldData.getWorldSizeDiscrete() &&
z >= 0 && z < this.serverWorldData.getWorldSizeDiscrete()
){
if(this.canCreateCell(x, y, z) && this.shouldContainPlayer(new Vector3i(x,y,z), worldPos, playerSimulationRadius)){
Vector3i targetPos = new Vector3i(x,y,z);
LoggerInterface.loggerEngine.DEBUG("GriddedDataCellManager: Add player to " + x + " " + y + " " + z);
loadedCellsLock.acquireUninterruptibly();
@ -199,21 +195,10 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
*/
public void movePlayer(Player player, Vector3i newPosition){
int playerSimulationRadius = player.getSimulationRadius();
Vector3i oldPosition = player.getWorldPos();
player.setWorldPos(newPosition);
for(ServerDataCell cell : this.groundDataCells.values()){
Vector3i worldPos = this.getCellWorldPosition(cell);
if(
cell.containsPlayer(player) &&
(
worldPos.x < newPosition.x - playerSimulationRadius ||
worldPos.x > newPosition.x + playerSimulationRadius ||
worldPos.y < newPosition.y - playerSimulationRadius ||
worldPos.y > newPosition.y + playerSimulationRadius ||
worldPos.z < newPosition.z - playerSimulationRadius ||
worldPos.z > newPosition.z + playerSimulationRadius
)
){
if(cell.containsPlayer(player) && !this.shouldContainPlayer(worldPos, newPosition, playerSimulationRadius)){
cell.removePlayer(player);
this.broadcastDestructionToPlayer(player, cell);
if(cell.getScene().containsEntity(player.getPlayerEntity())){
@ -230,19 +215,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
for(int x = newPosition.x - playerSimulationRadius; x < newPosition.x + playerSimulationRadius + 1; x++){
for(int y = newPosition.y - playerSimulationRadius; y < newPosition.y + playerSimulationRadius + 1; y++){
for(int z = newPosition.x - playerSimulationRadius; z < newPosition.z + playerSimulationRadius + 1; z++){
if(
x >= 0 && x < this.serverWorldData.getWorldSizeDiscrete() &&
y >= 0 && y < this.serverWorldData.getWorldSizeDiscrete() &&
z >= 0 && z < this.serverWorldData.getWorldSizeDiscrete() &&
(
x < oldPosition.x - playerSimulationRadius ||
x > oldPosition.x + playerSimulationRadius ||
y < oldPosition.y - playerSimulationRadius ||
y > oldPosition.y + playerSimulationRadius ||
z < oldPosition.z - playerSimulationRadius ||
z > oldPosition.z + playerSimulationRadius
)
){
if(this.canCreateCell(x, y, z) && this.shouldContainPlayer(new Vector3i(x, y, z), newPosition, playerSimulationRadius)){
Vector3i targetPos = new Vector3i(x,y,z);
if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){
loadedCellsLock.acquireUninterruptibly();
@ -266,6 +239,38 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
}
}
/**
* Checks if a player should be contained in a cell
* @param cellWorldPos The world position of the cell
* @param playerPos The world position of the player
* @param simRadius The simulation radius of the player
* @return true if the player should be contained in the cell, false otherwise
*/
private boolean shouldContainPlayer(Vector3i cellWorldPos, Vector3i playerPos, int simRadius){
return cellWorldPos.distance(playerPos) < simRadius;
}
/**
* Checks if a cell can be created at the position
* @param cellPos The position to check
* @return true if a cell can be created at that position, false otherwise
*/
private boolean canCreateCell(Vector3i cellPos){
return this.canCreateCell(cellPos.x, cellPos.y, cellPos.z);
}
/**
* Checks if a cell can be created at the position
* @return true if a cell can be created at that position, false otherwise
*/
private boolean canCreateCell(int x, int y, int z){
return
x >= 0 && x < this.serverWorldData.getWorldSizeDiscrete() &&
y >= 0 && y < this.serverWorldData.getWorldSizeDiscrete() &&
z >= 0 && z < this.serverWorldData.getWorldSizeDiscrete()
;
}
/**
* Broadcasts messages to player to destroy all entities in a given cell
* @param player The player
@ -314,17 +319,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//remove from cells that are out of range
for(ServerDataCell cell : this.groundDataCells.values()){
Vector3i cellWorldPos = this.getCellWorldPosition(cell);
if(
cell.containsPlayer(player) &&
(
cellWorldPos.x < newPosition.x - playerSimulationRadius ||
cellWorldPos.x > newPosition.x + playerSimulationRadius ||
cellWorldPos.y < newPosition.y - playerSimulationRadius ||
cellWorldPos.y > newPosition.y + playerSimulationRadius ||
cellWorldPos.z < newPosition.z - playerSimulationRadius ||
cellWorldPos.z > newPosition.z + playerSimulationRadius
)
){
if(cell.containsPlayer(player) && !this.shouldContainPlayer(cellWorldPos, newPosition, playerSimulationRadius)){
if(cell.getScene().containsEntity(player.getPlayerEntity())){
throw new Error("Trying to remove player from a cell that contains its entity!");
}
@ -337,11 +332,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
for(int x = newPosition.x - playerSimulationRadius + 1; x < newPosition.x + playerSimulationRadius; x++){
for(int y = newPosition.y - playerSimulationRadius + 1; y < newPosition.y + playerSimulationRadius; y++){
for(int z = newPosition.x - playerSimulationRadius + 1; z < newPosition.z + playerSimulationRadius; z++){
if(
x >= 0 && x < this.serverWorldData.getWorldSizeDiscrete() &&
y >= 0 && y < this.serverWorldData.getWorldSizeDiscrete() &&
z >= 0 && z < this.serverWorldData.getWorldSizeDiscrete()
){
if(this.canCreateCell(x,y,z) && this.shouldContainPlayer(new Vector3i(x,y,z), newPosition, playerSimulationRadius)){
Vector3i targetPos = new Vector3i(x,y,z);
if(groundDataCells.get(this.getServerDataCellKey(targetPos)) != null){
loadedCellsLock.acquireUninterruptibly();
@ -498,14 +489,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @return The data cell if created, null otherwise
*/
public ServerDataCell tryCreateCellAtPoint(Vector3i worldPos){
if(
//in bounds of array
worldPos.x >= 0 && worldPos.x < this.serverWorldData.getWorldSizeDiscrete() &&
worldPos.y >= 0 && worldPos.y < this.serverWorldData.getWorldSizeDiscrete() &&
worldPos.z >= 0 && worldPos.z < this.serverWorldData.getWorldSizeDiscrete() &&
//isn't null
groundDataCells.get(this.getServerDataCellKey(worldPos)) == null
){
if(this.canCreateCell(worldPos) && groundDataCells.get(this.getServerDataCellKey(worldPos)) == null){
loadedCellsLock.acquireUninterruptibly();
//create data cell
this.createServerDataCell(worldPos);
@ -530,14 +514,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @return The data cell if found, null otherwise
*/
public ServerDataCell getCellAtWorldPosition(Vector3i position){
if(
//in bounds of array
position.x >= 0 && position.x < this.serverWorldData.getWorldSizeDiscrete() &&
position.y >= 0 && position.y < this.serverWorldData.getWorldSizeDiscrete() &&
position.z >= 0 && position.z < this.serverWorldData.getWorldSizeDiscrete() &&
//isn't null
groundDataCells.get(this.getServerDataCellKey(position)) != null
){
if(this.canCreateCell(position) && groundDataCells.get(this.getServerDataCellKey(position)) != null){
return groundDataCells.get(this.getServerDataCellKey(position));
}
return null;
@ -563,6 +540,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
}
}
loadedCellsLock.release();
this.unloadPlayerlessChunks();
this.updatePlayerPositions();
Globals.profiler.endCpuSample();
}

View File

@ -1,6 +1,7 @@
package electrosphere.server.datacell;
import electrosphere.collision.CollisionEngine;
import electrosphere.collision.PhysicsEntityUtils;
import electrosphere.collision.hitbox.HitboxManager;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
@ -193,6 +194,7 @@ public class Realm {
if(Globals.RUN_PHYSICS){
collisionEngine.simulatePhysics((float)Globals.timekeeper.getSimFrameTime());
collisionEngine.updateDynamicObjectTransforms();
PhysicsEntityUtils.serverRepositionEntities(collisionEngine);
chemistryEngine.collide();
}
//