town road nav graph construction
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-30 13:27:54 -04:00
parent cde8f35887
commit 4b1efb2bcf
5 changed files with 127 additions and 59 deletions

View File

@ -2074,6 +2074,7 @@ Sprinting/physics work
(05/30/2025) (05/30/2025)
Reorganizing macro classes Reorganizing macro classes
Start work on macro pathfinding storage Start work on macro pathfinding storage
Town constructs nav graph of road nodes

View File

@ -19,6 +19,7 @@ import electrosphere.server.macro.civilization.town.Town;
import electrosphere.server.macro.region.MacroRegion; import electrosphere.server.macro.region.MacroRegion;
import electrosphere.server.macro.spatial.MacroAreaObject; import electrosphere.server.macro.spatial.MacroAreaObject;
import electrosphere.server.macro.spatial.MacroObject; import electrosphere.server.macro.spatial.MacroObject;
import electrosphere.server.macro.spatial.path.MacroPathCache;
import electrosphere.server.macro.structure.VirtualStructure; import electrosphere.server.macro.structure.VirtualStructure;
import electrosphere.util.FileUtils; import electrosphere.util.FileUtils;
import electrosphere.util.annotation.Exclude; import electrosphere.util.annotation.Exclude;
@ -34,35 +35,30 @@ import org.joml.Vector3d;
*/ */
public class MacroData { public class MacroData {
/**
* The maximum number of attempts to try placing something
*/
static final int MAX_PLACEMENT_ATTEMPTS = 50;
/** /**
* List of races * List of races
*/ */
List<Race> races = new LinkedList<Race>(); private List<Race> races = new LinkedList<Race>();
/** /**
* List of civilizations * List of civilizations
*/ */
List<Civilization> civilizations = new LinkedList<Civilization>(); private List<Civilization> civilizations = new LinkedList<Civilization>();
/** /**
* List of towns * List of towns
*/ */
List<Town> towns = new LinkedList<Town>(); private List<Town> towns = new LinkedList<Town>();
/** /**
* List of structures * List of structures
*/ */
List<VirtualStructure> structures = new LinkedList<VirtualStructure>(); private List<VirtualStructure> structures = new LinkedList<VirtualStructure>();
/** /**
* The list of all regions * The list of all regions
*/ */
List<MacroRegion> regions = new LinkedList<MacroRegion>(); private List<MacroRegion> regions = new LinkedList<MacroRegion>();
/** /**
* Maps structure id -> structure * Maps structure id -> structure
@ -79,7 +75,12 @@ public class MacroData {
/** /**
* List of roads * List of roads
*/ */
List<Road> roads = new LinkedList<Road>(); private List<Road> roads = new LinkedList<Road>();
/**
* The pathing cache
*/
private MacroPathCache pathingCache = new MacroPathCache();
/** /**
* Generates a world * Generates a world
@ -89,15 +90,6 @@ public class MacroData {
public static MacroData generateWorld(long seed, ServerWorldData serverWorldData){ public static MacroData generateWorld(long seed, ServerWorldData serverWorldData){
Random random = new Random(seed); Random random = new Random(seed);
MacroData rVal = new MacroData(); MacroData rVal = new MacroData();
//generate initial dieties
// int numDieties = 3 + Math.abs(random.nextInt()) % 7;
// for(int i = 0; i < numDieties; i++){
// Character diety = generateInitialDiety(random.nextLong());
// rVal.initialDieties.add(diety);
// rVal.characters.add(diety);
// CharacterService.createCharacter(null, i);
// }
//generate initial races //generate initial races
@ -120,19 +112,6 @@ public class MacroData {
//init civilizations //init civilizations
CivilizationGenerator.generate(serverWorldData, rVal, Globals.gameConfigCurrent); CivilizationGenerator.generate(serverWorldData, rVal, Globals.gameConfigCurrent);
//add a test character
// Character testChar = new Character();
// testChar.setPos(new Vector3d(ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 3, 32769))));
// Race.setRace(testChar, Race.create("human", "human"));
// rVal.characters.add(testChar);
//add a test character
// Vector3d structPos = ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 0, 32770));
// double elevationAtStruct = serverWorldData.getServerTerrainManager().getElevation(32774, 32770, 0, 0);
// structPos.y = elevationAtStruct;
// Structure struct = Structure.createStructure(Globals.gameConfigCurrent.getStructureData().getType("test1"),structPos);
// rVal.structures.add(struct);
//spawn initial characters in each race //spawn initial characters in each race
//find initial positions to place characters at per race //find initial positions to place characters at per race
//generate initial characters //generate initial characters
@ -188,6 +167,9 @@ public class MacroData {
for(MacroRegion region : this.regions){ for(MacroRegion region : this.regions){
this.idRegionMap.put(region.getId(),region); this.idRegionMap.put(region.getId(),region);
} }
if(this.pathingCache != null){
this.pathingCache.reconstruct();
}
} }
/** /**
@ -197,22 +179,6 @@ public class MacroData {
public void save(String saveName){ public void save(String saveName){
FileUtils.serializeObjectToSavePath(saveName, "./macro.json", this); FileUtils.serializeObjectToSavePath(saveName, "./macro.json", this);
} }
// /**
// * Generates an initial diety
// * @param seed The seed
// * @return The character for the diety
// */
// static Character generateInitialDiety(long seed){
// Character rVal = new Character();
// Diety diety = Diety.generateDiety(seed);
// CharacterUtils.addDiety(rVal, diety);
// return rVal;
// }
/** /**
* Gets the list of civilizations * Gets the list of civilizations
@ -422,5 +388,13 @@ public class MacroData {
blockers.addAll(this.regions); blockers.addAll(this.regions);
return blockers; return blockers;
} }
/**
* Gets the path cache in the macro data
* @return The macro pathing cache
*/
public MacroPathCache getPathCache(){
return this.pathingCache;
}
} }

View File

@ -1,7 +1,9 @@
package electrosphere.server.macro.civilization.town; package electrosphere.server.macro.civilization.town;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.joml.AABBd; import org.joml.AABBd;
@ -19,6 +21,8 @@ import electrosphere.server.macro.character.race.Race;
import electrosphere.server.macro.civilization.Civilization; import electrosphere.server.macro.civilization.Civilization;
import electrosphere.server.macro.civilization.road.Road; import electrosphere.server.macro.civilization.road.Road;
import electrosphere.server.macro.region.MacroRegion; import electrosphere.server.macro.region.MacroRegion;
import electrosphere.server.macro.spatial.path.MacroPathCache;
import electrosphere.server.macro.spatial.path.MacroPathNode;
import electrosphere.server.macro.structure.VirtualStructure; import electrosphere.server.macro.structure.VirtualStructure;
import electrosphere.util.math.HashUtils; import electrosphere.util.math.HashUtils;
import electrosphere.util.math.VoronoiUtils; import electrosphere.util.math.VoronoiUtils;
@ -80,6 +84,8 @@ public class TownLayout {
*/ */
public static void layoutTown(Realm realm, MacroData macroData, Town town){ public static void layoutTown(Realm realm, MacroData macroData, Town town){
MacroPathCache pathCache = macroData.getPathCache();
// //
//figure out what structures we're allowed to place //figure out what structures we're allowed to place
Civilization parentCiv = town.getParent(macroData); Civilization parentCiv = town.getParent(macroData);
@ -183,6 +189,76 @@ public class TownLayout {
closedSet.add(openHash); closedSet.add(openHash);
} }
//
// Construct pathfinding nodes for roads and link them
//
//
//sets for breadth search
Map<Long,MacroPathNode> positionNodeMap = new HashMap<Long,MacroPathNode>();
openSet.clear();
closedSet.clear();
closedSet.add(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET));
openSet.add(HashUtils.hashIVec(HASH_OFFSET - 1, 0, HASH_OFFSET));
openSet.add(HashUtils.hashIVec(HASH_OFFSET + 1, 0, HASH_OFFSET));
openSet.add(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET - 1));
openSet.add(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET + 1));
//add pathing nodes for initial entries
MacroPathNode currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(centerNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET), currentPathingNode);
//up
currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(upNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET + 1), currentPathingNode);
//left
currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(leftNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET - 1, 0, HASH_OFFSET), currentPathingNode);
//right
currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(rightNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET + 1, 0, HASH_OFFSET), currentPathingNode);
//down
currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(downNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET - 1), currentPathingNode);
while(openSet.size() > 0){
long openHash = openSet.poll();
int x = HashUtils.unhashIVec(openHash, HashUtils.UNHASH_COMPONENT_X) - HASH_OFFSET;
int z = HashUtils.unhashIVec(openHash, HashUtils.UNHASH_COMPONENT_Z) - HASH_OFFSET;
scanPoint.set(townCenter).add(TOWN_LAYOUT_SCALER * x,0,TOWN_LAYOUT_SCALER * z);
currPoint = TownLayout.getTownCenter(realm, scanPoint);
currentPathingNode = positionNodeMap.get(openHash);
if(currentPathingNode == null){
throw new Error("Failed to find pathing node for hash");
}
//check below
for(int i = 0; i < 4; i++){
int oX = x + offsetX[i];
int oZ = z + offsetZ[i];
scanPoint.set(townCenter).add(TOWN_LAYOUT_SCALER * oX,0,TOWN_LAYOUT_SCALER * oZ);
nearPoint = TownLayout.getTownCenter(realm, scanPoint);
long newHash = HashUtils.hashIVec(HASH_OFFSET + oX, 0, HASH_OFFSET + oZ);
if(nearPoint.distance(townCenter) < TOWN_MAX_RADIUS){
if(!openSet.contains(newHash) && !closedSet.contains(newHash)){
openSet.add(newHash);
}
//link the neighbor to this node
MacroPathNode neighborPathingNode;
if(positionNodeMap.containsKey(newHash)){
neighborPathingNode = positionNodeMap.get(newHash);
} else {
neighborPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(nearPoint));
positionNodeMap.put(newHash,neighborPathingNode);
}
currentPathingNode.addNeighbor(neighborPathingNode);
}
}
closedSet.add(openHash);
}
// //
//Breadth search other nodes to branch outwards //Breadth search other nodes to branch outwards

View File

@ -22,7 +22,7 @@ public class MacroPathCache {
* Map of node id -> node * Map of node id -> node
*/ */
@Exclude @Exclude
private Map<Integer,MacroPathNode> idNodeMap = new HashMap<Integer,MacroPathNode>(); private Map<Long,MacroPathNode> idNodeMap = new HashMap<Long,MacroPathNode>();
/** /**
* Reconstructs the datastructures for this cache after deserialization * Reconstructs the datastructures for this cache after deserialization
@ -46,7 +46,7 @@ public class MacroPathCache {
* @param id The id * @param id The id
* @return The corresponding node if it exists, null otherwise * @return The corresponding node if it exists, null otherwise
*/ */
public MacroPathNode getNodeById(int id){ public MacroPathNode getNodeById(long id){
return idNodeMap.get(id); return idNodeMap.get(id);
} }

View File

@ -6,6 +6,7 @@ import java.util.stream.Collectors;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.server.macro.region.MacroRegion;
import electrosphere.server.macro.structure.VirtualStructure; import electrosphere.server.macro.structure.VirtualStructure;
/** /**
@ -27,6 +28,11 @@ public class MacroPathNode {
*/ */
private static final int TYPE_VIRTUAL_STRUCTURE = 1; private static final int TYPE_VIRTUAL_STRUCTURE = 1;
/**
* A region
*/
private static final int TYPE_REGION = 2;
// //
// cost values // cost values
@ -37,6 +43,11 @@ public class MacroPathNode {
*/ */
private static final int COST_VIRTUAL_STRUCTURE = 10; private static final int COST_VIRTUAL_STRUCTURE = 10;
/**
* Cost of a region object
*/
private static final int COST_REGION = 8;
/** /**
* Cost of a road node * Cost of a road node
*/ */
@ -50,12 +61,12 @@ public class MacroPathNode {
/** /**
* The id of this node * The id of this node
*/ */
private int id; private long id;
/** /**
* The id of the object that this node corresponds to * The id of the object that this node corresponds to
*/ */
private int objectId; private long objectId;
/** /**
* The type of object at the node * The type of object at the node
@ -65,7 +76,7 @@ public class MacroPathNode {
/** /**
* The ids of neighboring path nodes * The ids of neighboring path nodes
*/ */
private List<Integer> neighborNodes = new LinkedList<Integer>(); private List<Long> neighborNodes = new LinkedList<Long>();
/** /**
* The position of the node * The position of the node
@ -102,6 +113,10 @@ public class MacroPathNode {
rVal.cost = COST_VIRTUAL_STRUCTURE; rVal.cost = COST_VIRTUAL_STRUCTURE;
rVal.objectId = structObj.getId(); rVal.objectId = structObj.getId();
rVal.objectType = TYPE_VIRTUAL_STRUCTURE; rVal.objectType = TYPE_VIRTUAL_STRUCTURE;
} else if(correspondingObject instanceof MacroRegion regionObj){
rVal.cost = COST_REGION;
rVal.objectId = regionObj.getId();
rVal.objectType = TYPE_REGION;
} else { } else {
throw new Error("Unsupported object type! " + correspondingObject); throw new Error("Unsupported object type! " + correspondingObject);
} }
@ -134,7 +149,7 @@ public class MacroPathNode {
* Gets the associated id * Gets the associated id
* @return * @return
*/ */
public int getId() { public long getId() {
return id; return id;
} }
@ -142,7 +157,7 @@ public class MacroPathNode {
* Sets the id of this path node * Sets the id of this path node
* @param id The id of this path node * @param id The id of this path node
*/ */
public void setId(int id) { protected void setId(int id) {
this.id = id; this.id = id;
} }
@ -166,7 +181,7 @@ public class MacroPathNode {
* Gets the id of the object associated with this node * Gets the id of the object associated with this node
* @return The id of the object * @return The id of the object
*/ */
public int getObjectId() { public long getObjectId() {
return objectId; return objectId;
} }
@ -184,7 +199,7 @@ public class MacroPathNode {
* @return The list of node ids * @return The list of node ids
*/ */
public List<MacroPathNode> getNeighborNodes(MacroPathCache cache) { public List<MacroPathNode> getNeighborNodes(MacroPathCache cache) {
return this.neighborNodes.stream().map((Integer id) -> cache.getNodeById(objectId)).collect(Collectors.toList()); return this.neighborNodes.stream().map((Long id) -> cache.getNodeById(objectId)).collect(Collectors.toList());
} }
/** /**
@ -192,7 +207,9 @@ public class MacroPathNode {
* @param neighbor The neighbor * @param neighbor The neighbor
*/ */
public void addNeighbor(MacroPathNode neighbor){ public void addNeighbor(MacroPathNode neighbor){
this.neighborNodes.add(neighbor.getId()); if(!this.neighborNodes.contains(neighbor.getId())){
this.neighborNodes.add(neighbor.getId());
}
} }
/** /**