hook up content generation to test gen realm
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-11-07 18:44:07 -05:00
parent afabf5595e
commit e12dd55703
28 changed files with 458 additions and 86 deletions

View File

@ -51,7 +51,20 @@
],
"surfaceGenerationParams": {
"surfaceGenTag": "hills",
"heightOffset": 10
"heightOffset": 10,
"floorVariants": [
],
"foliageDescriptions": [
{
"entityIDs": [
"pine2"
],
"regularity": 0.6,
"threshold": 0.05,
"scale": 0.5,
"priority": 1.0
}
]
}
},
{

View File

@ -4,4 +4,5 @@
- @subpage creaturetodo
- @subpage creatureanimations
- @subpage creatureideas
- @subpage creaturemechanicsideas
- @subpage creaturemechanicsideas
- @subpage foliageideas

View File

@ -0,0 +1,5 @@
@page foliageideas Foliage Ideas
"Tree" that shoots up a lantern at night time which sifts through the air to collect bugs for food.
Lantern is a glowing particle.

View File

@ -8,4 +8,9 @@ Another challenge to consider is creating teasers for the narrative peaks.
IE, if your big bad is supposed to be a dark paladin, how do you tease him in the story without making it a final encounter.
- "Visions", "Proxies", etc that aren't the real version
- For certain types of creatures (ie dragons), you can have them fly by in a way that wouldn't be easily interacted with
Yet another challenge to consider is adding blocks where side quests can become relevant
IE, you hit a quest where you ask someone for a favor (lets say they need to repair something), you now have a timer where side quests can be introduced in the interim
Another idea is having the quest pend on a building being constructed
Another idea is waiting for an npc to move to a given location, ie if a character went off on another task sometime earlier in the quest and you are now waiting to regroup with them

View File

@ -941,6 +941,19 @@ Hill Gen tweaks
(11/05/2024)
More normals fixes for terrain
(11/06/2024)
Fix server caching of terrain
Implement server side striding of chunk data
(11/07/2024)
Add quarter, eighth, and sixteenth scale chunk gen
Add network pressure capping
Add stratified updates that prioritize game chunks
Add message deduplication
Fix LOD bounding sphere calculation
Hook up content generation to test generation realm
Reorganize biome data
# TODO

View File

@ -115,19 +115,24 @@ public class ImGuiPlayerEntity {
//server pos
int serverIdForClientEntity = Globals.clientSceneWrapper.mapClientToServerId(Globals.playerEntity.getId());
Entity serverPlayerEntity = EntityLookupUtils.getEntityById(serverIdForClientEntity);
ImGui.text("Position (Server): " + EntityUtils.getPosition(serverPlayerEntity));
ImGui.text("Rotation (Server): " + EntityUtils.getRotation(serverPlayerEntity));
if(serverPlayerEntity != null){
ImGui.text("Position (Server): " + EntityUtils.getPosition(serverPlayerEntity));
ImGui.text("Rotation (Server): " + EntityUtils.getRotation(serverPlayerEntity));
//server-side physics stuff
DBody serverBody = PhysicsEntityUtils.getDBody(serverPlayerEntity);
if(serverBody != null){
ImGui.text("Velocity (Server): " + serverBody.getLinearVel());
ImGui.text("Force (Server): " + serverBody.getForce());
ImGui.text("Move Vector (Server): " + CreatureUtils.getFacingVector(serverPlayerEntity));
ImGui.text("Velocity (Server): " + CreatureUtils.getVelocity(serverPlayerEntity));
//server-side physics stuff
DBody serverBody = PhysicsEntityUtils.getDBody(serverPlayerEntity);
if(serverBody != null){
ImGui.text("Velocity (Server): " + serverBody.getLinearVel());
ImGui.text("Force (Server): " + serverBody.getForce());
ImGui.text("Move Vector (Server): " + CreatureUtils.getFacingVector(serverPlayerEntity));
ImGui.text("Velocity (Server): " + CreatureUtils.getVelocity(serverPlayerEntity));
}
ImGui.text("View yaw (Server): " + ServerPlayerViewDirTree.getTree(serverPlayerEntity).getYaw());
ImGui.text("View pitch (Server): " + ServerPlayerViewDirTree.getTree(serverPlayerEntity).getPitch());
}
if(Globals.server != null && Globals.server.getFirstConnection() != null && Globals.server.getFirstConnection().getPlayer() != null){
ImGui.text("Player Object World Pos (Server): " + Globals.server.getFirstConnection().getPlayer().getWorldPos());
}
ImGui.text("View yaw (Server): " + ServerPlayerViewDirTree.getTree(serverPlayerEntity).getYaw());
ImGui.text("View pitch (Server): " + ServerPlayerViewDirTree.getTree(serverPlayerEntity).getPitch());
}
/**

View File

@ -76,12 +76,4 @@ public class CollisionWorldData {
}
}
public float getRandomDampener(){
if(clientWorldData != null){
return clientWorldData.getRandomDampener();
} else {
return serverWorldData.getRandomDampener();
}
}
}

View File

@ -45,6 +45,8 @@ public class ChunkGenerationTestLoading {
String saveName = "generation_testing";
//delete if it exists
SaveUtils.deleteSave(saveName);
if(!SaveUtils.getSaves().contains(saveName)){
//
//the juicy server GENERATION part

View File

@ -8,6 +8,7 @@ import electrosphere.engine.Globals;
import electrosphere.entity.state.attach.AttachUtils;
import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell;
@ -57,6 +58,9 @@ public class ServerEntityUtils {
//if server, get current server data cell
ServerDataCell oldDataCell = realm.getDataCellManager().getDataCellAtPoint(EntityUtils.getPosition(entity));
ServerDataCell newDataCell = realm.getDataCellManager().getDataCellAtPoint(position);
if(oldDataCell == null){
LoggerInterface.loggerEngine.WARNING("Trying to reposition entity on server when it's former position is null!");
}
if(oldDataCell != newDataCell){
if(newDataCell != null){
ServerDataCell.moveEntityFromCellToCell(entity, oldDataCell, newDataCell);

View File

@ -58,7 +58,10 @@ public class SceneLoader {
//Content manager
//
ServerContentManager serverContentManager = null;
if(file.realmDescriptor.getType() == RealmDescriptor.REALM_DESCRIPTOR_PROCEDURAL){
if(
file.realmDescriptor.getType().matches(RealmDescriptor.REALM_DESCRIPTOR_PROCEDURAL) ||
file.realmDescriptor.getType().matches(RealmDescriptor.REALM_DESCRIPTOR_GENERATION_TESTING)
){
serverContentManager = ServerContentManager.createServerContentManager(true);
} else {
serverContentManager = ServerContentManager.createServerContentManager(false);

View File

@ -9,6 +9,7 @@ import electrosphere.game.data.creature.type.movement.EditorMovementSystem;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.attack.ClientAttackTree;
import electrosphere.entity.state.movement.fall.ClientFallTree;
@ -21,6 +22,7 @@ import electrosphere.net.synchronization.annotation.SynchronizableEnum;
import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree;
import electrosphere.net.synchronization.enums.BehaviorTreeIdEnums;
import electrosphere.renderer.anim.Animation;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.util.math.SpatialMathUtils;
import java.util.concurrent.CopyOnWriteArrayList;
@ -194,6 +196,8 @@ public class ClientEditorMovementTree implements BehaviorTree {
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float maxNaturalVelocity = EDITOR_MAX_VELOCITY;
Entity serverEntity = EntityLookupUtils.getEntityById(Globals.clientSceneWrapper.mapClientToServerId(parent.getId()));
//
//rotation update
if(this.state != MovementTreeState.IDLE){
@ -304,6 +308,9 @@ public class ClientEditorMovementTree implements BehaviorTree {
//actually update
rotation.set(movementQuaternion);
position.set(new Vector3d(position).add(new Vector3d(movementVector).mul(velocity)));
if(serverEntity != null){
ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position));
}
GravityUtils.clientAttemptActivateGravity(parent);
} break;
@ -316,6 +323,9 @@ public class ClientEditorMovementTree implements BehaviorTree {
float velocity = this.getModifiedVelocity();
rotation.set(movementQuaternion);
position.set(new Vector3d(position).add(new Vector3d(movementVector).mul(velocity)));
if(serverEntity != null){
ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position));
}
GravityUtils.clientAttemptActivateGravity(parent);
} break;
@ -334,6 +344,9 @@ public class ClientEditorMovementTree implements BehaviorTree {
}
rotation.set(movementQuaternion);
position.set(new Vector3d(position).add(new Vector3d(movementVector).mul(velocity)));
if(serverEntity != null){
ServerEntityUtils.repositionEntity(serverEntity, new Vector3d(position));
}
GravityUtils.clientAttemptActivateGravity(parent);
} break;

View File

@ -13,18 +13,63 @@ public class BiomeFoliageDescription {
List<String> entityIDs;
/**
* The frequency of this element in particular
* How regular the placement of foliage is. Low values will create very uneven foliage, while high values will place them along a grid.
*/
Double frequency;
Double regularity;
/**
* The scale of the noise used to generate this element
* The percentage of the ground to cover with foliage
*/
Double dispersion;
Double threshold;
/**
* The priority of this floor element in particular
*/
Double priority;
/**
* The scale of the noise used to place foliage
*/
Double scale;
/**
* Gets the entity ids for this foliage description
* @return The list of entity ids
*/
public List<String> getEntityIDs(){
return this.entityIDs;
}
/**
* Gets the regularity of the foliage placement
* @return The regularity
*/
public Double getRegularity(){
return regularity;
}
/**
* Gets the percentage of the ground to cover with foliage
* @return The percentage of the ground to cover with foliage
*/
public Double getThreshold(){
return threshold;
}
/**
* Gets the priority of this floor element in particular
* @return The priority of this floor element in particular
*/
public Double getPriority(){
return priority;
}
/**
* Gets the scale of the noise used to place foliage
* @return The scale of the noise used to place foliage
*/
public Double getScale(){
return scale;
}
}

View File

@ -1,5 +1,7 @@
package electrosphere.game.data.biome;
import java.util.List;
/**
* Params for the surface generation algorithm
*/
@ -15,6 +17,16 @@ public class BiomeSurfaceGenerationParams {
*/
Float heightOffset;
/**
* The different floor elements
*/
List<BiomeFloorElement> floorVariants;
/**
* The list of foliage descriptions available to this biome type
*/
List<BiomeFoliageDescription> foliageDescriptions;
/**
* Gets the tag for the generation algorithm for generating the surface
* @return The tag for the generation algorithm for generating the surface
@ -31,6 +43,22 @@ public class BiomeSurfaceGenerationParams {
return heightOffset;
}
/**
* Gets the list of floor variants
* @return The list of floor variants
*/
public List<BiomeFloorElement> getFloorVariants(){
return floorVariants;
}
/**
* Gets the list of foliage descriptions
* @return The list of foliage descriptions
*/
public List<BiomeFoliageDescription> getFoliageDescriptions(){
return foliageDescriptions;
}
}

View File

@ -147,7 +147,7 @@ public class ServerWorldData {
ServerTerrainManager serverTerrainManager = null;
ServerFluidManager serverFluidManager = null;
//TODO: Allow loading procedurally generated terrain from disk (the chunk generator is always default currently)
serverWorldData = ServerWorldData.createFixedWorldData(new Vector3d(0),new Vector3d(16 * 4 * 4));
serverWorldData = ServerWorldData.createFixedWorldData(new Vector3d(0),new Vector3d(TestGenerationChunkGenerator.GENERATOR_REALM_SIZE * ServerTerrainChunk.CHUNK_DIMENSION));
serverWorldData.worldSizeDiscrete = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE;
serverWorldData.worldSizeDiscreteVertical = TestGenerationChunkGenerator.GENERATOR_REALM_SIZE;
@ -171,6 +171,10 @@ public class ServerWorldData {
return worldMaxPoint;
}
/**
* Gets the discrete size of the world (in chunks)
* @return The discrete size of the world (in chunks)
*/
public int getWorldSizeDiscrete() {
return worldSizeDiscrete;
}
@ -179,10 +183,6 @@ public class ServerWorldData {
return dynamicInterpolationRatio;
}
public float getRandomDampener() {
return randomDampener;
}
public int convertRealToChunkSpace(double real){
return (int)Math.floor(real / ServerTerrainChunk.CHUNK_DIMENSION);
}

View File

@ -13,26 +13,69 @@ import org.joml.Vector3i;
* A client logged into the server
*/
public class Player {
ServerConnectionHandler connectionHandler;
int id;
/**
* The default server-side simulation radius in chunks
*/
public static final int DEFAULT_SIMULATION_RADIUS = 3;
/**
* Id incrementer lock
*/
static Semaphore idIncrementerLock = new Semaphore(1);
/**
* The actual incrementing id counter
*/
static int idIncrementer = 0;
/**
* The corresponding connection handler
*/
ServerConnectionHandler connectionHandler;
/**
* The id of the player
*/
int id;
/**
* The world position of this player
*/
Vector3i worldPos;
int simulationRadius = 3;
/**
* The simulation radius of this player
*/
int simulationRadius = DEFAULT_SIMULATION_RADIUS;
/**
* The player's primary entity
*/
Entity playerEntity;
/**
* Constructor
* @param connectionHandler The corresponding connection
*/
public Player(ServerConnectionHandler connectionHandler){
this.connectionHandler = connectionHandler;
id = connectionHandler.getPlayerId();
this.simulationRadius = Globals.userSettings.getGameplayPhysicsCellRadius();
}
//used for when client is creating a player object for itself
/**
* Used when initing a local connection
* @param id The id of the local connection
*/
public Player(int id){
this.id = id;
}
/**
* Gets the id of the player
* @return The player's id
*/
public int getId() {
if(connectionHandler != null){
return this.connectionHandler.getPlayerId();
@ -40,30 +83,58 @@ public class Player {
return id;
}
/**
* Adds a message that should be sent to this player
* @param message The message
*/
public void addMessage(NetworkMessage message){
connectionHandler.addMessagetoOutgoingQueue(message);
}
/**
* Gets the world position of the player
* @return The world position
*/
public Vector3i getWorldPos() {
return worldPos;
}
/**
* Sets the world position of the player
* @param worldPos The world position
*/
public void setWorldPos(Vector3i worldPos) {
this.worldPos = worldPos;
}
/**
* Gets the simulation radius of the player
* @return The simulation radius
*/
public int getSimulationRadius() {
return simulationRadius;
}
/**
* Sets the simulation radius of the player
* @param simulationRadius The simulation radius
*/
public void setSimulationRadius(int simulationRadius) {
this.simulationRadius = simulationRadius;
}
/**
* Gets the player's entity
* @return The player's entity
*/
public Entity getPlayerEntity() {
return playerEntity;
}
/**
* Sets the player's entity
* @param playerEntity The player's entity
*/
public void setPlayerEntity(Entity playerEntity) {
this.playerEntity = playerEntity;
}

View File

@ -28,7 +28,7 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
case REQUESTCHARACTERLIST:
//TODO
break;
case REQUESTCREATECHARACTER:
case REQUESTCREATECHARACTER: {
CreatureTemplate template = Utilities.deserialize(message.getdata(), CreatureTemplate.class);
if(template != null){
connectionHandler.setCreatureTemplate(Utilities.deserialize(message.getdata(), CreatureTemplate.class));
@ -36,10 +36,10 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
} else {
connectionHandler.addMessagetoOutgoingQueue(CharacterMessage.constructResponseCreateCharacterFailureMessage());
}
break;
case REQUESTSPAWNCHARACTER:
} break;
case REQUESTSPAWNCHARACTER: {
spawnEntityForClient(connectionHandler);
break;
} break;
case RESPONSECHARACTERLIST:
case RESPONSECREATECHARACTERSUCCESS:
case RESPONSECREATECHARACTERFAILURE:

View File

@ -292,7 +292,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
TerrainMessage.constructResponseMetadataMessage(
realm.getServerWorldData().getWorldSizeDiscrete(),
realm.getServerWorldData().getDynamicInterpolationRatio(),
realm.getServerWorldData().getRandomDampener(),
0,
(int)realm.getServerWorldData().getWorldBoundMin().x,
(int)realm.getServerWorldData().getWorldBoundMin().z,
(int)realm.getServerWorldData().getWorldBoundMax().x,

View File

@ -68,6 +68,8 @@ public class PlayerCharacterCreation {
int playerCharacterId = entity.getId();
serverConnectionHandler.setPlayerEntityId(playerCharacterId);
CreatureUtils.setControllerPlayerId(entity, serverConnectionHandler.getPlayerId());
Player player = serverConnectionHandler.getPlayer();
player.setPlayerEntity(entity);
//custom player btrees
addPlayerServerBTrees(entity);
}

View File

@ -0,0 +1,88 @@
package electrosphere.server.content;
import java.util.List;
import java.util.Random;
import org.joml.Vector3d;
import org.joml.Vector3i;
import electrosphere.entity.types.foliage.FoliageUtils;
import electrosphere.game.data.biome.BiomeData;
import electrosphere.game.data.biome.BiomeFoliageDescription;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import io.github.studiorailgun.NoiseUtils;
/**
* Generates content for a given datacell
*/
public class ServerContentGenerator {
/**
* The seed of the foliage
*/
public static final int FOLIAGE_SEED = 0;
/**
* Generates content for a given data cell
* @param realm The realm
* @param cell The cell
* @param worldPos The world position of the cell
* @param randomizer The randomizer
*/
public static void generateContent(Realm realm, ServerDataCell cell, Vector3i worldPos, long randomizer){
//verify we have everything for chunk content gen
if(realm.getServerWorldData() == null && realm.getServerWorldData().getServerTerrainManager() == null && realm.getServerWorldData().getServerTerrainManager().getModel() == null){
throw new Error(
"Trying to generate content for a realm that does not have a terrain model defined!\n" +
realm.getServerWorldData() + "\n" +
realm.getServerWorldData().getServerTerrainManager() + "\n" +
realm.getServerWorldData().getServerTerrainManager().getModel()
);
}
//setup
Random random = new Random(randomizer);
//generate foliage
BiomeData biome = null;
if(realm.getServerWorldData() != null && realm.getServerWorldData().getServerTerrainManager() != null && realm.getServerWorldData().getServerTerrainManager().getModel() != null){
biome = realm.getServerWorldData().getServerTerrainManager().getModel().getSurfaceBiome(worldPos.x, worldPos.z);
}
List<BiomeFoliageDescription> foliageDescriptions = biome.getSurfaceGenerationParams().getFoliageDescriptions();
if(foliageDescriptions != null){
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
double height = realm.getServerWorldData().getServerTerrainManager().getElevation(worldPos.x, worldPos.z, x, z);
if(
realm.getServerWorldData().convertVoxelToRealSpace(0, worldPos.y) <= height &&
realm.getServerWorldData().convertVoxelToRealSpace(ServerTerrainChunk.CHUNK_DIMENSION, worldPos.y) > height
){
for(BiomeFoliageDescription foliageDescription : foliageDescriptions){
double scale = foliageDescription.getScale();
double regularity = foliageDescription.getRegularity();
double threshold = foliageDescription.getThreshold();
if(NoiseUtils.relaxedPointGen(x * scale, z * scale, regularity, threshold) > 0){
String type = foliageDescription.getEntityIDs().get(random.nextInt(0,foliageDescription.getEntityIDs().size()));
FoliageUtils.serverSpawnTreeFoliage(
realm,
new Vector3d(
realm.getServerWorldData().convertVoxelToRealSpace(x, worldPos.x),
height,
realm.getServerWorldData().convertVoxelToRealSpace(z, worldPos.z)
),
type,
random.nextLong()
);
}
}
}
}
}
}
}
}

View File

@ -11,6 +11,7 @@ import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.saves.SaveUtils;
import electrosphere.util.FileUtils;
import io.github.studiorailgun.HashUtils;
public class ServerContentManager {
@ -39,7 +40,7 @@ public class ServerContentManager {
* @param worldPos The world position
* @param cell The cell
*/
public void generateContentForDataCell(Realm realm, Vector3i worldPos, ServerDataCell cell, String cellKey){
public void generateContentForDataCell(Realm realm, Vector3i worldPos, ServerDataCell cell, Long cellKey){
String fullPath = "/content/" + cellKey + ".dat";
if(generateContent){ //in other words, if not arena mode
if(FileUtils.checkSavePathExists(Globals.currentSave.getName(), fullPath)){
@ -48,8 +49,7 @@ public class ServerContentManager {
contentRaw.hydrateRawContent(realm,cell);
} else {
//else create from scratch
//UNCOMMENT THIS WHEN YOU WANT CONTENT GENERATED FOR WORLDS AGAIN
// EnvironmentGenerator.generateForest(realm, cell, worldPos, 0);
ServerContentGenerator.generateContent(realm, cell, worldPos, HashUtils.cantorHash(worldPos.x, worldPos.y, worldPos.z));
}
} else {
//just because content wasn't generated doesn't mean there isn't data saved under that key
@ -75,7 +75,7 @@ public class ServerContentManager {
* @param locationKey the location key to save under
* @param entities the list of entities to save
*/
public void saveContentToDisk(String locationKey, List<Entity> entities){
public void saveContentToDisk(Long locationKey, List<Entity> entities){
ContentSerialization serialization = ContentSerialization.constructContentSerialization(entities);
String dirPath = SaveUtils.deriveSaveDirectoryPath(Globals.currentSave.getName());
String fullPath = dirPath + "/content/" + locationKey + ".dat";

View File

@ -14,11 +14,13 @@ import org.joml.Vector3i;
import electrosphere.engine.Globals;
import electrosphere.engine.threads.LabeledThread.ThreadLabel;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.server.ServerPlayerViewDirTree;
import electrosphere.game.server.world.ServerWorldData;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.player.Player;
import electrosphere.net.server.protocol.TerrainProtocol;
@ -29,6 +31,8 @@ import electrosphere.server.datacell.physics.PhysicsDataCell;
import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.fluid.manager.ServerFluidManager;
import electrosphere.server.terrain.manager.ServerTerrainManager;
import electrosphere.server.terrain.models.TerrainModel;
import io.github.studiorailgun.HashUtils;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
/**
@ -44,7 +48,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
/**
* The max grid size allowed
*/
public static final int MAX_GRID_SIZE = 2000 * 1024;
public static final int MAX_GRID_SIZE = TerrainModel.MAX_MACRO_DATA_SIZE * TerrainModel.DEFAULT_MACRO_DATA_SCALE * ServerTerrainChunk.CHUNK_DIMENSION;
/**
* Tracks whether this manager has been flagged to unload cells or not
@ -52,7 +56,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
boolean unloadCells = true;
//these are going to be the natural ground grid of data cells, but we're going to have more than this
Map<String,ServerDataCell> groundDataCells = new HashMap<String,ServerDataCell>();
Map<Long,ServerDataCell> groundDataCells = new HashMap<Long,ServerDataCell>();
Map<ServerDataCell,Vector3i> cellPositionMap = new HashMap<ServerDataCell,Vector3i>();
//Map of server data cell to the number of frames said cell has had no players
Map<ServerDataCell,Integer> cellPlayerlessFrameMap = new HashMap<ServerDataCell,Integer>();
@ -77,7 +81,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
/**
* Map of world position key -> physics cell
*/
Map<String,PhysicsDataCell> posPhysicsMap = new HashMap<String,PhysicsDataCell>();
Map<Long,PhysicsDataCell> posPhysicsMap = new HashMap<Long,PhysicsDataCell>();
/**
* Constructor
@ -140,9 +144,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
loadedCellsLock.acquireUninterruptibly();
loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
loadedCellsLock.release();
//generate/handle content for new server data cell
loadedCellsLock.release();
//add player
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player);
}
@ -181,11 +183,14 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
)
){
Vector3i targetPos = new Vector3i(x,y,z);
if(groundDataCells.get(getServerDataCellKey(targetPos)) != null){
if(groundDataCells.get(getServerDataCellKey(targetPos)).containsPlayer(player)){
// removals++;
groundDataCells.get(getServerDataCellKey(targetPos)).removePlayer(player);
}
Long key = this.getServerDataCellKey(targetPos);
if(
groundDataCells.get(key) != null &&
groundDataCells.get(key).containsPlayer(player)
){
// removals++;
groundDataCells.get(key).removePlayer(player);
this.broadcastDestructionToPlayer(player, groundDataCells.get(key));
}
}
}
@ -235,11 +240,22 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
// System.out.println("removals: " + removals + "\tadditions: " + additions);
}
/**
* Broadcasts messages to player to destroy all entities in a given cell
* @param player The player
* @param cell The cell
*/
private void broadcastDestructionToPlayer(Player player, ServerDataCell cell){
for(Entity entity : cell.getScene().getEntityList()){
player.addMessage(EntityMessage.constructDestroyMessage(entity.getId()));
}
}
/**
* Creates physics entities when new data cell being created
*/
private void createTerrainPhysicsEntities(Vector3i worldPos){
String key = this.getServerDataCellKey(worldPos);
Long key = this.getServerDataCellKey(worldPos);
if(posPhysicsMap.containsKey(key)){
PhysicsDataCell cell = posPhysicsMap.get(key);
cell.retireCell();
@ -300,13 +316,16 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
parent.deregisterCell(cell);
loadedCells.remove(cell);
Vector3i worldPos = getCellWorldPosition(cell);
String key = getServerDataCellKey(worldPos);
Long key = getServerDataCellKey(worldPos);
groundDataCells.remove(key);
//offload all entities in cell to chunk file
serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList());
//clear all entities in cell
for(Entity entity : cell.getScene().getEntityList()){
ClientEntityUtils.destroyEntity(entity);
if(ServerPlayerViewDirTree.hasTree(entity)){
throw new Error("Trying to unload a player's entity! " + entity + " " + worldPos);
}
ServerEntityUtils.destroyEntity(entity);
}
//save terrain to disk
serverTerrainManager.savePositionToDisk(worldPos);
@ -334,13 +353,13 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
parent.deregisterCell(cell);
loadedCells.remove(cell);
Vector3i worldPos = getCellWorldPosition(cell);
String key = getServerDataCellKey(worldPos);
Long key = getServerDataCellKey(worldPos);
groundDataCells.remove(key);
//offload all entities in cell to chunk file
serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList());
//clear all entities in cell
for(Entity entity : cell.getScene().getEntityList()){
ClientEntityUtils.destroyEntity(entity);
ServerEntityUtils.destroyEntity(entity);
}
}
this.serverTerrainManager.evictAll();
@ -399,21 +418,25 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
worldPos.y >= 0 && worldPos.y < this.serverWorldData.getWorldSizeDiscrete() &&
worldPos.z >= 0 && worldPos.z < this.serverWorldData.getWorldSizeDiscrete() &&
//isn't null
groundDataCells.get(getServerDataCellKey(worldPos)) == null
groundDataCells.get(this.getServerDataCellKey(worldPos)) == null
){
//create data cell
createServerDataCell(worldPos);
this.createServerDataCell(worldPos);
//generates physics for the cell in a dedicated thread then finally registers
runPhysicsGenerationThread(worldPos);
this.runPhysicsGenerationThread(worldPos);
//add to loaded cells
loadedCellsLock.acquireUninterruptibly();
loadedCells.add(groundDataCells.get(getServerDataCellKey(worldPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(worldPos)),0);
loadedCells.add(groundDataCells.get(this.getServerDataCellKey(worldPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(this.getServerDataCellKey(worldPos)),0);
loadedCellsLock.release();
} else {
LoggerInterface.loggerEngine.WARNING("Trying to create data cell outside world bounds! " + worldPos);
} else if(groundDataCells.get(this.getServerDataCellKey(worldPos)) == null) {
LoggerInterface.loggerEngine.WARNING(
"Trying to create data cell outside world bounds!\n" +
worldPos + "\n" +
this.serverWorldData.getWorldSizeDiscrete()
);
}
return groundDataCells.get(getServerDataCellKey(worldPos));
return groundDataCells.get(this.getServerDataCellKey(worldPos));
}
/**
@ -428,9 +451,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
position.y >= 0 && position.y < this.serverWorldData.getWorldSizeDiscrete() &&
position.z >= 0 && position.z < this.serverWorldData.getWorldSizeDiscrete() &&
//isn't null
groundDataCells.get(getServerDataCellKey(position)) != null
groundDataCells.get(this.getServerDataCellKey(position)) != null
){
return groundDataCells.get(getServerDataCellKey(position));
return groundDataCells.get(this.getServerDataCellKey(position));
}
return null;
}
@ -486,7 +509,11 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//create physics entities
createTerrainPhysicsEntities(worldPos);
//set ready
groundDataCells.get(getServerDataCellKey(worldPos)).setReady(true);
if(groundDataCells.get(getServerDataCellKey(worldPos)) != null){
groundDataCells.get(getServerDataCellKey(worldPos)).setReady(true);
} else {
LoggerInterface.loggerEngine.WARNING("Finished generating physics for server cell, but cell is null!");
}
}
});
groundDataCells.get(getServerDataCellKey(worldPos)).setReady(false);
@ -498,8 +525,8 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
* @param worldPos The position in world coordinates of the server data cell
* @return The server data cell if it exists, otherwise null
*/
private String getServerDataCellKey(Vector3i worldPos){
return worldPos.x + "_" + worldPos.y + "_" + worldPos.z;
private Long getServerDataCellKey(Vector3i worldPos){
return (long)HashUtils.cantorHash(worldPos.x, worldPos.y, worldPos.z);
}
/**
@ -509,7 +536,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
*/
private ServerDataCell createServerDataCell(Vector3i worldPos){
ServerDataCell rVal = parent.createNewCell();
String cellKey = getServerDataCellKey(worldPos);
Long cellKey = getServerDataCellKey(worldPos);
groundDataCells.put(cellKey,rVal);
LoggerInterface.loggerEngine.DEBUG("Create server data cell with key " + cellKey);
cellPositionMap.put(rVal,new Vector3i(worldPos));
@ -648,7 +675,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
@Override
public void save(String saveName) {
for(ServerDataCell cell : loadedCells){
String key = this.getServerDataCellKey(this.getCellWorldPosition(cell));
Long key = this.getServerDataCellKey(this.getCellWorldPosition(cell));
//offload all entities in cell to chunk file
serverContentManager.saveContentToDisk(key, cell.getScene().getEntityList());
}

View File

@ -75,7 +75,9 @@ public class ServerBehaviorTreeUtils {
Set<BehaviorTree> trees = entityBTreeMap.get(entity);
ServerDataCell newCell = DataCellSearchUtils.getEntityDataCell(entity);
for(BehaviorTree tree : trees){
oldCell.getScene().deregisterBehaviorTree(tree);
if(oldCell != null){
oldCell.getScene().deregisterBehaviorTree(tree);
}
newCell.getScene().registerBehaviorTree(tree);
}
}

View File

@ -47,6 +47,11 @@ public class DefaultChunkGenerator implements ChunkGenerator {
return rVal;
}
@Override
public double getElevation(int worldX, int worldZ, int chunkX, int chunkZ){
return 0.1;
}
@Override
public void setModel(TerrainModel model) {
//Does nothing as the arena is not based on a terrain model

View File

@ -56,6 +56,12 @@ public class OverworldChunkGenerator implements ChunkGenerator {
return returnedChunk;
}
@Override
public double getElevation(int worldX, int worldZ, int chunkX, int chunkZ){
float[][] heightmap = getHeightmap(worldX, worldZ);
return heightmap[chunkX][chunkZ];
}
@Override
/**
* Sets the terrain model for the overworld algo

View File

@ -74,7 +74,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
//actual generation algo
//biome of the current chunk
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldY, worldZ);
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ);
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag());
@ -126,6 +126,23 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
return rVal;
}
@Override
public double getElevation(int worldX, int worldZ, int chunkX, int chunkZ){
BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldZ);
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
}
double rVal = heightmapGen.getHeight(
this.terrainModel.getSeed(),
this.serverWorldData.convertVoxelToRealSpace(chunkX, worldX),
this.serverWorldData.convertVoxelToRealSpace(chunkZ, worldZ)
);
return rVal;
}
@Override
public void setModel(TerrainModel model) {
this.terrainModel = model;

View File

@ -18,6 +18,16 @@ public interface ChunkGenerator {
*/
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ, int stride);
/**
* Gets the elevation at a given 2d coordinate
* @param worldX The world x coordinate
* @param worldZ The world z coordinate
* @param chunkX The chunk x coordinate
* @param chunkZ The chunk z coordinate
* @return The elevation at that specific coordinate
*/
public double getElevation(int worldX, int worldZ, int chunkX, int chunkZ);
/**
* Sets the terrain model for the generation algorithm
* @param model The terrain model

View File

@ -181,10 +181,6 @@ public class ServerTerrainManager {
return model.getElevationForChunk(x, y);
}
public double getHeightAtPosition(double x, double y, double z){
return y;
}
public float getDiscreteValue(int x, int y){
if(model != null){
return model.getElevation()[x][y];
@ -251,6 +247,21 @@ public class ServerTerrainManager {
return returnedChunk;
}
/**
* Performs logic once a server chunk is available
* @param worldX The world x position
* @param worldZ The world z position
* @param chunkX The chunk x position
* @param chunkZ THe chunk z position
* @return The ServerTerrainChunk
*/
public double getElevation(int worldX, int worldZ, int chunkX, int chunkZ){
Globals.profiler.beginCpuSample("ServerTerrainManager.getChunk");
double elevation = chunkGenerator.getElevation(worldX, worldZ, chunkX, chunkZ);
Globals.profiler.endCpuSample();
return elevation;
}
/**
* Performs logic once a server chunk is available
* @param worldX The world x position

View File

@ -13,6 +13,11 @@ import electrosphere.util.annotation.Exclude;
*/
public class TerrainModel {
/**
* Maximum size of the macro data
*/
public static final int MAX_MACRO_DATA_SIZE = 2000;
/**
* The scale of the macro data
@ -514,11 +519,10 @@ public class TerrainModel {
/**
* Gets the surface biome for a given world position
* @param worldX The world X
* @param worldY The world Y
* @param worldZ The world Z
* @return The biome
*/
public BiomeData getSurfaceBiome(int worldX, int worldY, int worldZ){
public BiomeData getSurfaceBiome(int worldX, int worldZ){
int macroX = worldX / macroDataScale;
int macroZ = worldZ / macroDataScale;
int surfaceBiomeIndex = this.biome[macroX][macroZ];