town population spawning
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-24 15:52:49 -04:00
parent 84b4c11199
commit 5be0a197c3
14 changed files with 178 additions and 49 deletions

View File

@ -1952,6 +1952,8 @@ Block chunks properly stride
(05/24/2025)
Work on rotating structures
Towns spawn a population of characters when they are max-res'd
Hitbox synchronization work

View File

@ -860,7 +860,7 @@ public class CollisionEngine {
DRay ray = OdeHelper.createRay(space, length);
ray.set(start.x - this.floatingOrigin.x, start.y - this.floatingOrigin.y, start.z - this.floatingOrigin.z, unitDir.x, unitDir.y, unitDir.z);
//collide
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, typeMask);
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, geomPointerMap, typeMask);
rayCastCallback.setLength(length);
space.collide2(space, data, rayCastCallback);
//destroy ray
@ -884,7 +884,7 @@ public class CollisionEngine {
DRay ray = OdeHelper.createRay(space, length);
ray.set(start.x - this.floatingOrigin.x, start.y - this.floatingOrigin.y, start.z - this.floatingOrigin.z, unitDir.x, unitDir.y, unitDir.z);
//collide
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, null);
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, geomPointerMap, null);
rayCastCallback.setLength(length);
space.collide2(ray, data, rayCastCallback);
//destroy ray
@ -908,7 +908,7 @@ public class CollisionEngine {
DRay ray = OdeHelper.createRay(space, length);
ray.set(start.x - this.floatingOrigin.x, start.y - this.floatingOrigin.y, start.z - this.floatingOrigin.z, unitDir.x, unitDir.y, unitDir.z);
//collide
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, typeMask);
RayCastCallbackData data = new RayCastCallbackData(bodyPointerMap, geomPointerMap, typeMask);
rayCastCallback.setLength(length);
space.collide2(ray, data, rayCastCallback);
//destroy ray
@ -1474,6 +1474,20 @@ public class CollisionEngine {
geom.setBody(body);
}
/**
* Locks the ode library
*/
public static void lockOde(){
spaceLock.lock();
}
/**
* Unlocks the ode library
*/
public static void unlockOde(){
spaceLock.unlock();
}
/**
* Gets the status of the collision engine
* @return The status of the collision engine

View File

@ -72,6 +72,13 @@ void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) {
Collidable collidable1 = rayCastData.bodyEntityMap.get(b1);
Collidable collidable2 = rayCastData.bodyEntityMap.get(b2);
if(collidable1 == null){
collidable1 = rayCastData.geomEntityMap.get(o1);
}
if(collidable2 == null){
collidable2 = rayCastData.geomEntityMap.get(o2);
}
//don't self cast -- should work on both server and client
if(collidable1 != null && collidable1.getParent() == Globals.clientState.playerEntity){
@ -151,6 +158,11 @@ void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) {
*/
Map<DBody,Collidable> bodyEntityMap;
/**
* The map of ode DGeom -> collidable
*/
Map<DGeom,Collidable> geomEntityMap;
/**
* The mask of collidable types to filter collisions by. Can be null.
*/
@ -169,10 +181,12 @@ void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) {
/**
* Constructor
* @param bodyEntityMap The map of ode DBody -> collidable
* @param geomEntityMap The map of ode DGeom -> collidable
* @param collidableTypeMask The mask of collidable types to filter collisions by. Can be null.
*/
public RayCastCallbackData(Map<DBody,Collidable> bodyEntityMap,List<String> collidableTypeMask){
public RayCastCallbackData(Map<DBody,Collidable> bodyEntityMap, Map<DGeom,Collidable> geomEntityMap, List<String> collidableTypeMask){
this.bodyEntityMap = bodyEntityMap;
this.geomEntityMap = geomEntityMap;
this.collidableTypeMask = collidableTypeMask;
}
}

View File

@ -18,22 +18,17 @@ public class HitboxManager {
/**
* The list of all hitboxes
*/
List<HitboxCollectionState> hitboxes = new LinkedList<HitboxCollectionState>();
private List<HitboxCollectionState> hitboxes = new LinkedList<HitboxCollectionState>();
/**
* The collision engine for the hitbox manager
*/
CollisionEngine collisionEngine;
private CollisionEngine collisionEngine;
/**
* Lock for hitbox collections
*/
ReentrantLock lock = new ReentrantLock();
/**
* An incrementer for hitbox IDs
*/
long idIncrementer = 0;
private ReentrantLock lock = new ReentrantLock();
/**
* Constructor
@ -51,7 +46,6 @@ public class HitboxManager {
public void registerHitbox(HitboxCollectionState hitbox){
lock.lock();
hitboxes.add(hitbox);
idIncrementer++;
lock.unlock();
}

View File

@ -206,6 +206,7 @@ public class HitboxCollectionState {
*/
private void createBody(){
CollisionEngine collisionEngine = this.manager.getCollisionEngine();
CollisionEngine.lockOde();
//create the shapes
for(HitboxData hitboxDataRaw : this.rawData){
DGeom geom = null;
@ -284,6 +285,7 @@ public class HitboxCollectionState {
//register collidable with collision engine
this.collidable = new Collidable(this.parent, Collidable.TYPE_OBJECT, true);
collisionEngine.registerCollisionObject(this.body, this.collidable);
CollisionEngine.unlockOde();
}
/**

View File

@ -11,6 +11,7 @@ import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.state.server.ServerCharacterData;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.datacell.ServerWorldData;
@ -30,9 +31,9 @@ import electrosphere.server.macro.spatial.MacroObject;
public class ServerContentManager {
/**
* Maximum amount of time to wait
* Maximum amount of time to wait for macro data to generate
*/
public static final int MAX_TIME_TO_WAIT = 100;
public static final int MAX_TIME_TO_WAIT = 10000;
/**
* controls whether the manager should generate content on loading a new scene
@ -170,15 +171,19 @@ public class ServerContentManager {
* @param object The object
*/
public void spawnMacroObject(Realm realm, MacroObject object){
if(object instanceof Character && Race.hasRace((Character)object)){
Race race = Race.getRace((Character)object);
String creatureName = race.getAssociatedCreature();
if(creatureName == null){
throw new Error("Creature name not defined! " + ((Character)object).getId());
if(object instanceof Character){
if(Race.hasRace((Character)object)){
Race race = Race.getRace((Character)object);
String creatureName = race.getAssociatedCreature();
if(creatureName == null){
throw new Error("Creature name not defined! " + ((Character)object).getId());
}
//place macro object
Entity characterEntity = CreatureUtils.serverSpawnBasicCreature(realm, object.getPos(), creatureName, null);
ServerCharacterData.attachServerCharacterData(characterEntity, (Character)object);
} else {
LoggerInterface.loggerEngine.WARNING("No race defined!");
}
//place macro object
Entity characterEntity = CreatureUtils.serverSpawnBasicCreature(realm, object.getPos(), creatureName, null);
ServerCharacterData.attachServerCharacterData(characterEntity, (Character)object);
}
}

View File

@ -1,7 +1,9 @@
package electrosphere.server.macro;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
@ -18,6 +20,7 @@ import electrosphere.server.macro.spatial.MacroObject;
import electrosphere.server.macro.structure.VirtualStructure;
import electrosphere.server.macro.town.Town;
import electrosphere.util.FileUtils;
import electrosphere.util.annotation.Exclude;
import electrosphere.util.math.GeomUtils;
import java.util.Random;
@ -55,6 +58,12 @@ public class MacroData {
*/
List<VirtualStructure> structures = new LinkedList<VirtualStructure>();
/**
* Maps structure id -> structure
*/
@Exclude
private Map<Integer,VirtualStructure> idStructMap = new HashMap<Integer,VirtualStructure>();
/**
* List of roads
*/
@ -157,6 +166,15 @@ public class MacroData {
return rVal;
}
/**
* Rebuilds datastructures for the macro data
*/
public void rebuildDatastructures(){
for(VirtualStructure struct : this.structures){
this.idStructMap.put(struct.getId(),struct);
}
}
/**
* Saves this macro data to a save path
* @param saveName The name of the save
@ -276,12 +294,7 @@ public class MacroData {
* @return The structure if it exists, null otherwise
*/
public VirtualStructure getStructure(int id){
for(VirtualStructure struct : structures){
if(struct.getId() == id){
return struct;
}
}
return null;
return this.idStructMap.get(id);
}
/**
@ -291,6 +304,7 @@ public class MacroData {
public void addStructure(VirtualStructure structure){
structure.setId(structures.size());
structures.add(structure);
this.idStructMap.put(structure.getId(),structure);
}
/**

View File

@ -33,6 +33,9 @@ public class MacroDataLoader {
structure.setFab(fab);
}
//rebuild datastructures
rVal.rebuildDatastructures();
return rVal;
}

View File

@ -5,6 +5,7 @@ import org.joml.Vector3d;
import electrosphere.server.datacell.Realm;
import electrosphere.server.macro.town.Town;
import electrosphere.server.macro.town.TownLayout;
import electrosphere.server.macro.town.TownPopulator;
/**
* Updates macro data as a player comes into range of it
@ -33,6 +34,8 @@ public class MacroDataUpdater {
Vector3d townPos = town.getPos();
if(townPos.distance(playerPos) < TOWN_GENERATION_DIST){
TownLayout.layoutTown(realm, macroData, town);
TownPopulator.populateTown(realm, macroData, town);
town.setResolution(Town.TOWN_RES_MAX);
}
}
}

View File

@ -1,5 +1,12 @@
package electrosphere.server.macro.structure;
import org.joml.AABBd;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import electrosphere.client.block.BlockChunkData;
import electrosphere.controls.cursor.CursorState;
/**
* Utility functions for dealing with structures on the server
*/
@ -44,4 +51,48 @@ public class VirtualStructureUtils {
return true;
}
/**
* Samples a virtual structure's fab
* @param struct The structure
* @param currRealPos The real position to sample from
* @return The type if it is in the structure's bounds, 0 otherwise
*/
public static short getSample(VirtualStructure struct, Vector3d currRealPos){
AABBd aabb = struct.getAABB();
Vector3d localBlockPos = new Vector3d(
Math.round((currRealPos.x - aabb.minX) / BlockChunkData.BLOCK_SIZE_MULTIPLIER),
Math.round((currRealPos.y - aabb.minY) / BlockChunkData.BLOCK_SIZE_MULTIPLIER),
Math.round((currRealPos.z - aabb.minZ) / BlockChunkData.BLOCK_SIZE_MULTIPLIER)
);
Quaterniond rotationQuat = CursorState.getBlockRotation(struct.getRotation());
rotationQuat.transform(localBlockPos);
Vector3d dimVec = new Vector3d(struct.getFab().getDimensions());
rotationQuat.transform(dimVec);
dimVec.absolute();
if(localBlockPos.x < 0){
localBlockPos.x = dimVec.x + localBlockPos.x;
}
if(localBlockPos.y < 0){
localBlockPos.y = dimVec.y + localBlockPos.y;
}
if(localBlockPos.z < 0){
localBlockPos.z = dimVec.z + localBlockPos.z;
}
int finalX = Math.round((float)localBlockPos.x);
int finalY = Math.round((float)localBlockPos.y);
int finalZ = Math.round((float)localBlockPos.z);
if(
finalX >= 0 &&
finalY >= 0 &&
finalZ >= 0 &&
finalX < struct.getFab().getDimensions().x &&
finalY < struct.getFab().getDimensions().y &&
finalZ < struct.getFab().getDimensions().z
){
return struct.getFab().getType(finalX,finalY,finalZ);
}
return 0;
}
}

View File

@ -213,23 +213,8 @@ public class TownLayout {
closedSet.add(openHash);
}
// System.out.println("Structure count: " + town.getStructures(macroData).size());
town.setResolution(Town.TOWN_RES_MAX);
}
// /**
// * Generates structures along a road
// * @param realm The realm
// * @param town The town
// * @param road The road
// * @param allowedStructures The list of allowed structure types
// */
// private static void generateStructuresAlongRoad(Realm realm, Town town, Road road, List<StructureData> allowedStructures){
// TownLayout.generateStructuresAlongRoad(realm, town, road.getPoint1(), road.getPoint2(), road.getRadius(), allowedStructures);
// }
/**
* Generates structures along a road
* @param realm The realm

View File

@ -0,0 +1,40 @@
package electrosphere.server.macro.town;
import java.util.List;
import org.joml.Vector3d;
import electrosphere.engine.Globals;
import electrosphere.entity.types.EntityTypes.EntityType;
import electrosphere.entity.types.creature.ObjectTemplate;
import electrosphere.server.datacell.Realm;
import electrosphere.server.macro.MacroData;
import electrosphere.server.macro.character.Character;
import electrosphere.server.macro.character.CharacterUtils;
import electrosphere.server.macro.race.Race;
import electrosphere.server.macro.structure.VirtualStructure;
import electrosphere.server.service.CharacterService;
/**
* Creates the population of a town
*/
public class TownPopulator {
/**
* Populates a town with characters
* @param macroData The macro data
* @param town The town
*/
public static void populateTown(Realm realm, MacroData macroData, Town town){
List<VirtualStructure> structs = town.getStructures(macroData);
for(VirtualStructure struct : structs){
ObjectTemplate template = ObjectTemplate.create(EntityType.CREATURE, "human");
Character chara = Globals.serverState.characterService.createCharacter(template, CharacterService.NO_PLAYER);
Race.setRace(chara, Globals.gameConfigCurrent.getRaceMap().getRace("human"));
CharacterUtils.addShelter(chara, struct);
chara.setPos(new Vector3d(struct.getPos()).add(1,1,1));
}
System.out.println("Total characters: " + Globals.serverState.characterService.getAllCharacters().size());
}
}

View File

@ -138,10 +138,12 @@ public class StructureRepairUtils {
//check existing blocks
BlockChunkData blockChunkData = griddedDataCellManager.getBlocksAtPosition(chunkPos);
short existingBlockType = blockChunkData.getType(blockPos.x, blockPos.y, blockPos.z);
short desiredType = fab.getType(x, y, z);
if(existingBlockType != desiredType){
return true;
if(blockChunkData != null){
short existingBlockType = blockChunkData.getType(blockPos.x, blockPos.y, blockPos.z);
short desiredType = fab.getType(x, y, z);
if(existingBlockType != desiredType){
return true;
}
}
}
}

View File

@ -33,7 +33,7 @@ public class CharacterService extends SignalServiceImpl {
/**
* Playerid id for a playerless character
*/
public static final int NO_PLAYER = 0;
public static final int NO_PLAYER = -1;
/**
* Map that stores the characters currently loaded into memory