async pathfinding
Some checks are pending
studiorailgun/Renderer/pipeline/head Build queued...

This commit is contained in:
austin 2025-05-04 16:31:46 -04:00
parent 6339181aec
commit abcf4761df
13 changed files with 176 additions and 11 deletions

View File

@ -1664,6 +1664,7 @@ Blocks factor into voxel pathfinding
Debugging pathfinding code
New AI behaviors
- Will explore for resources if local ones aren't available
Async pathfinding

View File

@ -77,11 +77,13 @@ public class ImGuiAI {
if(ImGui.collapsingHeader("Statuses")){
for(AI ai : Globals.aiManager.getAIList()){
ImGui.indent();
if(ImGui.collapsingHeader(ai.getParent().getId() + " - " + ai.getStatus())){
if(ImGui.button("Draw current pathing")){
throw new Error("Unsupported currently!");
}
}
ImGui.unindent();
}
}

View File

@ -728,6 +728,7 @@ public class Globals {
Globals.clientSynchronizationManager = new ClientSynchronizationManager();
Globals.server = null;
Globals.serverSynchronizationManager = new ServerSynchronizationManager();
Globals.aiManager.shutdown();
if(Globals.realmManager != null){
Globals.realmManager.reset();
}
@ -739,6 +740,7 @@ public class Globals {
*/
public static void resetGlobals(){
Globals.unloadScene();
Globals.aiManager.shutdown();
//
//Actual globals to destroy
Globals.assetManager = null;
@ -757,6 +759,7 @@ public class Globals {
Globals.clientSynchronizationManager = null;
Globals.server = null;
Globals.serverSynchronizationManager = null;
Globals.aiManager = null;
Globals.playerManager = null;
Globals.javaPID = null;
Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true;

View File

@ -10,6 +10,7 @@ import electrosphere.entity.Entity;
import electrosphere.game.data.creature.type.ai.AITreeData;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.ai.services.NearbyEntityService;
import electrosphere.server.ai.services.PathfindingService;
import electrosphere.server.ai.services.TimerService;
/**
@ -47,6 +48,11 @@ public class AIManager {
*/
NearbyEntityService nearbyEntityService = new NearbyEntityService();
/**
* Service for performing pathfinding
*/
PathfindingService pathfindingService = new PathfindingService();
/**
* The random of the ai
*/
@ -156,6 +162,14 @@ public class AIManager {
return nearbyEntityService;
}
/**
* Gets the pathfinding service
* @return The pathfinding service
*/
public PathfindingService getPathfindingService(){
return this.pathfindingService;
}
/**
* Gets the ai manager's random
* @return The random
@ -163,5 +177,14 @@ public class AIManager {
public Random getRandom(){
return random;
}
/**
* Shuts down the ai manager
*/
public void shutdown(){
this.pathfindingService.shutdown();
this.nearbyEntityService.shutdown();
this.timerService.shutdown();
}
}

View File

@ -1,7 +1,5 @@
package electrosphere.server.ai.nodes.plan;
import java.util.List;
import org.joml.Vector3d;
import electrosphere.engine.Globals;
@ -52,7 +50,7 @@ public class PathfindingNode implements AITreeNode {
//make sure that the solved pathfinding data is for the point we want
if(PathfindingNode.hasPathfindingData(blackboard)){
PathingProgressiveData pathingProgressiveData = PathfindingNode.getPathfindingData(blackboard);
Vector3d actualPoint = pathingProgressiveData.getPoints().get(pathingProgressiveData.getPoints().size() - 1);
Vector3d actualPoint = pathingProgressiveData.getGoal();
Object targetRaw = blackboard.get(this.targetEntityKey);
Vector3d targetPos = null;
if(targetRaw == null){
@ -95,9 +93,7 @@ public class PathfindingNode implements AITreeNode {
Vector3d entityPos = EntityUtils.getPosition(entity);
List<Vector3d> path = pathfindingManager.findPath(entityPos, targetPos);
path.add(targetPos);
PathingProgressiveData pathingProgressiveData = new PathingProgressiveData(path);
PathingProgressiveData pathingProgressiveData = pathfindingManager.findPathAsync(entityPos, targetPos);
PathfindingNode.setPathfindingData(blackboard, pathingProgressiveData);
}
@ -105,9 +101,15 @@ public class PathfindingNode implements AITreeNode {
throw new Error("Failed to find path! Unhandled");
}
//check if the path has been found
PathingProgressiveData pathingProgressiveData = PathfindingNode.getPathfindingData(blackboard);
if(!pathingProgressiveData.isReady()){
return AITreeNodeResult.RUNNING;
}
Vector3d entityPos = EntityUtils.getPosition(entity);
PathingProgressiveData pathingProgressiveData = PathfindingNode.getPathfindingData(blackboard);
Vector3d currentPathPos = null;
if(pathingProgressiveData.getCurrentPoint() < pathingProgressiveData.getPoints().size()){
currentPathPos = pathingProgressiveData.getPoints().get(pathingProgressiveData.getCurrentPoint());

View File

@ -10,4 +10,9 @@ public interface AIService {
*/
public void exec();
/**
* Shuts down the service
*/
public void shutdown();
}

View File

@ -61,4 +61,8 @@ public class NearbyEntityService implements AIService {
return blackboard.has(BlackboardKeys.NEARBY_ENTITIES);
}
@Override
public void shutdown() {
}
}

View File

@ -0,0 +1,58 @@
package electrosphere.server.ai.services;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.joml.Vector3d;
import electrosphere.server.datacell.interfaces.VoxelCellManager;
import electrosphere.server.pathfinding.recast.PathingProgressiveData;
import electrosphere.server.pathfinding.voxel.VoxelPathfinder;
/**
* Service for performing pathfinding
*/
public class PathfindingService implements AIService {
/**
* The executor service
*/
ExecutorService executorService;
/**
* Queues a pathfinding job
* @param start The start point
* @param end The end point
* @param pathfinder The pathfinder object
* @param voxelCellManager The voxel cell manager
* @return The object that will eventually hold the pathfinding data
*/
public PathingProgressiveData queuePathfinding(Vector3d start, Vector3d end, VoxelPathfinder pathfinder, VoxelCellManager voxelCellManager){
PathingProgressiveData rVal = new PathingProgressiveData(end);
if(executorService == null){
executorService = Executors.newFixedThreadPool(2);
}
executorService.submit(() -> {
List<Vector3d> points = pathfinder.findPath(voxelCellManager, start, end, VoxelPathfinder.DEFAULT_MAX_COST);
points.add(end);
rVal.setPoints(points);
rVal.setReady(true);
});
return rVal;
}
@Override
public void exec() {
//No synchronous logic required yet
}
@Override
public void shutdown() {
if(executorService != null){
executorService.shutdownNow();
}
executorService = null;
}
}

View File

@ -93,5 +93,9 @@ public class TimerService implements AIService {
}
timerMap.put(timerId,frameCount);
}
@Override
public void shutdown() {
}
}

View File

@ -10,6 +10,7 @@ import electrosphere.server.ai.nodes.checks.spatial.TargetRangeCheckNode;
import electrosphere.server.ai.nodes.meta.DataDeleteNode;
import electrosphere.server.ai.nodes.meta.collections.SelectorNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode;
import electrosphere.server.ai.nodes.meta.decorators.RunnerNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
import electrosphere.server.ai.nodes.plan.PathfindingNode;
@ -47,7 +48,9 @@ public class MoveToTree {
//not in range of target, keep moving towards it
new SequenceNode(
new PublishStatusNode("Thinking about pathing"),
PathfindingNode.createPathEntity(targetKey),
new PublishStatusNode("Moving"),
new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT),
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))
)
@ -76,7 +79,9 @@ public class MoveToTree {
//not in range of target, keep moving towards it
new SequenceNode(
new PublishStatusNode("Thinking about pathing"),
PathfindingNode.createPathEntity(targetKey),
new PublishStatusNode("Moving"),
new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT),
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))
)

View File

@ -37,6 +37,7 @@ import electrosphere.server.datacell.interfaces.VoxelCellManager;
import electrosphere.server.datacell.physics.PhysicsDataCell;
import electrosphere.server.entity.ServerContentManager;
import electrosphere.server.entity.serialization.ContentSerialization;
import electrosphere.server.pathfinding.recast.PathingProgressiveData;
import electrosphere.server.pathfinding.voxel.VoxelPathfinder;
import electrosphere.server.physics.block.manager.ServerBlockManager;
import electrosphere.server.physics.fluid.manager.ServerFluidChunk;
@ -1140,4 +1141,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
return serverTerrainManager.getChunk(worldX, worldY, worldZ, ServerChunkCache.STRIDE_FULL_RES);
}
@Override
public PathingProgressiveData findPathAsync(Vector3d start, Vector3d end) {
return Globals.aiManager.getPathfindingService().queuePathfinding(start, end, this.pathfinder, this);
}
}

View File

@ -4,6 +4,8 @@ import java.util.List;
import org.joml.Vector3d;
import electrosphere.server.pathfinding.recast.PathingProgressiveData;
/**
* Performs pathfinding
*/
@ -17,4 +19,12 @@ public interface PathfindingManager {
*/
public List<Vector3d> findPath(Vector3d start, Vector3d end);
/**
* Solves a path
* @param start The start point
* @param end The end point
* @return The path if it exists, null otherwise
*/
public PathingProgressiveData findPathAsync(Vector3d start, Vector3d end);
}

View File

@ -20,11 +20,22 @@ public class PathingProgressiveData {
int currentPoint;
/**
* Constructor
* @param points The points for the path
* The goal position
*/
public PathingProgressiveData(List<Vector3d> points){
this.points = points;
Vector3d goal;
/**
* Tracks whether this data is ready to be used or not
*/
boolean ready = false;
/**
* Constructor
* @param goal The goal point
*/
public PathingProgressiveData(Vector3d goal){
this.goal = goal;
this.currentPoint = 0;
}
@ -60,6 +71,37 @@ public class PathingProgressiveData {
this.currentPoint = currentPoint;
}
/**
* Gets the goal point
* @return The goal point
*/
public Vector3d getGoal() {
return goal;
}
/**
* Sets the goal point
* @param goal The goal point
*/
public void setGoal(Vector3d goal) {
this.goal = goal;
}
/**
* Gets whether this data is ready or not
* @return true if it is ready, false otherwise
*/
public boolean isReady() {
return ready;
}
/**
* Sets the ready status of this data
* @param ready true if the data is ready, false otherwise
*/
public void setReady(boolean ready) {
this.ready = ready;
}
}