rewrite client side chunks + transvoxel integration
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-11-04 13:51:04 -05:00
parent d6c476b4a3
commit d2ccf3c479
73 changed files with 4366 additions and 747 deletions

2
.gitignore vendored
View File

@ -11,7 +11,7 @@
/nb-configuration.xml /nb-configuration.xml
/Telephone-*.jar /Telephone-*.jar
/NetArranger-*.jar /NetArranger*.jar
/lwjglx-debug-*.jar /lwjglx-debug-*.jar
/hs_err_pid* /hs_err_pid*
/replay_pid* /replay_pid*

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Mon Oct 28 16:07:42 EDT 2024 #Mon Nov 04 12:41:10 EST 2024
buildNumber=364 buildNumber=375

View File

@ -0,0 +1,4 @@
@page chunkgenoptimizations Chunk Generation Optimizations
Strategies to try:
- Cache "is surface" "is sky" "is cave" at generator level, then looking x,z against cached values to find whether the newly requested chunk is surface or not

View File

@ -4,4 +4,5 @@
- @subpage biomeselection - @subpage biomeselection
- @subpage biomegenerationproblems - @subpage biomegenerationproblems
- @subpage terraingenerationprocess - @subpage terraingenerationprocess
- @subpage voxelgenideas - @subpage voxelgenideas
- @subpage chunkgenoptimizations

View File

@ -914,6 +914,22 @@ Update default resolution in config
Fix main menu ui test Fix main menu ui test
Refactor math utils to spatial math utils to make room for more fundamental utils Refactor math utils to spatial math utils to make room for more fundamental utils
(10/29/2024)
Begin drawcellmanager rewrite
(10/30/2024)
Integrate transvoxel algorithm
Document NetArranger
Break out datastructures library
(10/31/2024)
Fix some transvoxel bugs
Optimizations
Refactoring generator code
(11/01/2024)
Optimizations
Fix transvoxel xnzn edge generation
# TODO # TODO

43
pom.xml
View File

@ -305,7 +305,15 @@
<dependency> <dependency>
<groupId>io.github.studiorailgun</groupId> <groupId>io.github.studiorailgun</groupId>
<artifactId>MathUtils</artifactId> <artifactId>MathUtils</artifactId>
<version>1.0.1</version> <version>1.1.0</version>
</dependency>
<!--DataStructures-->
<!--License: MIT-->
<dependency>
<groupId>io.github.studiorailgun</groupId>
<artifactId>DataStructures</artifactId>
<version>1.1.0</version>
</dependency> </dependency>
@ -440,6 +448,24 @@
</arguments> </arguments>
</configuration> </configuration>
</execution> </execution>
<execution>
<id>Run Net Arranger</id>
<phase>generate-sources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>java.exe</executable>
<arguments>
<argument>-jar</argument>
<argument>NetArranger.jar</argument>
</arguments>
<successCodes>
<successCode>0</successCode>
<successCode>1</successCode>
</successCodes>
</configuration>
</execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
@ -452,6 +478,20 @@
<!-- To execute this profile, run a command like "mvn test -P integration" --> <!-- To execute this profile, run a command like "mvn test -P integration" -->
<profile> <profile>
<id>integration</id> <id>integration</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<!--If you want the logs to throw exceptions on opengl errors, remove the "n" after .jar=t-->
<argLine>-javaagent:./lwjglx-debug-1.0.0.jar=t;o=trace.log</argLine>
<forkCount>0</forkCount>
</configuration>
</plugin>
</plugins>
</build>
<properties> <properties>
<!--The tests to run--> <!--The tests to run-->
<groups>fast,unit,integration</groups> <groups>fast,unit,integration</groups>
@ -471,6 +511,7 @@
<configuration> <configuration>
<!--If you want the logs to throw exceptions on opengl errors, remove the "n" after .jar=t--> <!--If you want the logs to throw exceptions on opengl errors, remove the "n" after .jar=t-->
<argLine>-javaagent:./lwjglx-debug-1.0.0.jar=t;o=trace.log</argLine> <argLine>-javaagent:./lwjglx-debug-1.0.0.jar=t;o=trace.log</argLine>
<forkCount>0</forkCount>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

View File

@ -14,6 +14,7 @@ import electrosphere.engine.Globals;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.renderer.shader.ShaderProgram; import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
/** /**
* *
@ -78,7 +79,7 @@ public class FluidCellManager {
*/ */
public FluidCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){ public FluidCellManager(ClientTerrainManager clientTerrainManager, int discreteX, int discreteY, int discreteZ){
if(Globals.clientWorldData != null){ if(Globals.clientWorldData != null){
worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f); worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / ServerTerrainChunk.CHUNK_DIMENSION * 1.0f);
} }
cells = new HashSet<FluidCell>(); cells = new HashSet<FluidCell>();
hasNotRequested = new HashSet<String>(); hasNotRequested = new HashSet<String>();
@ -209,24 +210,55 @@ public class FluidCellManager {
} }
} }
/**
* Checks if the manager has not requested a cell yet
* @return true if a cell has not been requested yet, false otherwise
*/
public boolean containsUnrequestedCell(){ public boolean containsUnrequestedCell(){
return hasNotRequested.size() > 0; return hasNotRequested.size() > 0;
} }
/**
* Gets the number of unrequested cells
* @return The number of unrequested cells
*/
public int getUnrequestedSize(){
return hasNotRequested.size();
}
/**
* Checks if the manager has a cell that is not drawable
* @return true if there is an undrawable cell, false otherwise
*/
public boolean containsUndrawableCell(){ public boolean containsUndrawableCell(){
return undrawable.size() > 0; return undrawable.size() > 0;
} }
/**
* Gets the number of undrawable cells
* @return The number of undrawable cells
*/
public int getUndrawableSize(){
return undrawable.size();
}
/**
* Checks if the manager has a cell that is updateable
* @return true if there is an updateable cell, false otherwise
*/
public boolean containsUpdateableCell(){ public boolean containsUpdateableCell(){
return updateable.size() > 0; return updateable.size() > 0;
} }
/**
* Transforms real space into cell space
* @param input The real coordinate
* @return The cell coordinate
*/
public int transformRealSpaceToCellSpace(double input){ public int transformRealSpaceToCellSpace(double input){
return (int)(input / Globals.clientWorldData.getDynamicInterpolationRatio()); return (int)(input / ServerTerrainChunk.CHUNK_DIMENSION);
} }
/** /**
@ -321,9 +353,10 @@ public class FluidCellManager {
* Updates cells that need updating in this manager * Updates cells that need updating in this manager
*/ */
public void update(){ public void update(){
Globals.profiler.beginCpuSample("FluidCellManager.update");
calculateDeltas(); calculateDeltas();
if(update){ if(update){
if(containsUnrequestedCell() && !containsUndrawableCell()){ if(containsUnrequestedCell()){
updateUnrequestedCell(); updateUnrequestedCell();
} else if(containsUndrawableCell()){ } else if(containsUndrawableCell()){
makeCellDrawable(); makeCellDrawable();
@ -331,6 +364,7 @@ public class FluidCellManager {
updateCellModel(); updateCellModel();
} }
} }
Globals.profiler.endCpuSample();
} }
/** /**

View File

@ -83,6 +83,7 @@ public class ClientFoliageManager {
* @param worldPos The world position * @param worldPos The world position
*/ */
private void updatePosition(Vector3i worldPos){ private void updatePosition(Vector3i worldPos){
Globals.profiler.beginCpuSample("ClientFoliageManager.updatePosition");
FoliageChunk foundChunk = null; FoliageChunk foundChunk = null;
for(FoliageChunk chunk : chunkUpdateCache){ for(FoliageChunk chunk : chunkUpdateCache){
if(chunk.getWorldPos().equals(worldPos)){ if(chunk.getWorldPos().equals(worldPos)){
@ -98,7 +99,8 @@ public class ClientFoliageManager {
} else { } else {
chunkUpdateCache.remove(foundChunk); chunkUpdateCache.remove(foundChunk);
} }
foundChunk.updateCells(); foundChunk.updateCells(false);
Globals.profiler.endCpuSample();
} }
/** /**
@ -130,7 +132,7 @@ public class ClientFoliageManager {
public boolean dependenciesAreReady(){ public boolean dependenciesAreReady(){
return return
Globals.clientWorldData != null && Globals.clientWorldData != null &&
Globals.drawCellManager != null && Globals.clientDrawCellManager != null &&
Globals.playerEntity != null Globals.playerEntity != null
; ;
} }
@ -142,7 +144,7 @@ public class ClientFoliageManager {
public void evaluateChunk(Vector3i worldPos){ public void evaluateChunk(Vector3i worldPos){
for(FoliageChunk chunk : chunkUpdateCache){ for(FoliageChunk chunk : chunkUpdateCache){
if(chunk.getWorldPos().equals(worldPos)){ if(chunk.getWorldPos().equals(worldPos)){
chunk.updateCells(); chunk.updateCells(true);
break; break;
} }
} }

View File

@ -212,7 +212,7 @@ public class FoliageCell {
if(voxelPosition.y + 1 >= ServerTerrainChunk.CHUNK_DIMENSION){ if(voxelPosition.y + 1 >= ServerTerrainChunk.CHUNK_DIMENSION){
return; return;
} }
if(!Globals.drawCellManager.generatedPhysics(worldPosition)){ if(!Globals.clientDrawCellManager.isFullLOD(worldPosition)){
return; return;
} }
List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPosition)).getAmbientFoliage(); List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPosition)).getAmbientFoliage();

View File

@ -34,6 +34,11 @@ public class FoliageChunk {
*/ */
static final double HALF_RES_DIST = 30; static final double HALF_RES_DIST = 30;
/**
* Tracks whether this chunk contains a foliage voxel or not
*/
boolean containsFoliageVoxel = false;
/** /**
* The octree holding all the chunks to evaluate * The octree holding all the chunks to evaluate
*/ */
@ -75,27 +80,56 @@ public class FoliageChunk {
this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos); this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
// //evaluate top cells if chunk above this one exists // //evaluate top cells if chunk above this one exists
this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0)); this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0));
this.updateCells(); this.updateCells(true);
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} }
/** /**
* Updates all cells in the chunk * Updates all cells in the chunk
* @param force true if should ignore cache, false otherwise
*/ */
public void updateCells(){ public void updateCells(boolean force){
Globals.profiler.beginCpuSample("FoliageChunk.updateCells"); Globals.profiler.beginCpuSample("FoliageChunk.updateCells");
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity); //re-evaluate whether contains foliage voxel or not
//the sets to iterate through if(force){
boolean updated = true; this.containsFoliageVoxel = checkContainsFoliageVoxel();
int attempts = 0; }
while(updated && attempts < 3){ if(force || containsFoliageVoxel){
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot(); Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
updated = this.recursivelyUpdateCells(rootNode, playerPos); //the sets to iterate through
attempts++; boolean updated = true;
int attempts = 0;
while(updated && attempts < 3){
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot();
updated = this.recursivelyUpdateCells(rootNode, playerPos);
attempts++;
}
} }
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} }
/**
* Checks if the chunk contains a foliage voxel or not
* @return true if contains foliage voxel, false otherwise
*/
private boolean checkContainsFoliageVoxel(){
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(this.getWorldPos());
if(data == null){
return false;
}
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){
for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){
List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(new Vector3i(x,y,z))).getAmbientFoliage();
if(foliageTypesSupported != null && foliageTypesSupported.size() > 0){
return true;
}
}
}
}
return false;
}
/** /**
* Recursively update child nodes * Recursively update child nodes
* @param node The root node * @param node The root node
@ -104,6 +138,7 @@ public class FoliageChunk {
private boolean recursivelyUpdateCells(ChunkTreeNode<FoliageCell> node, Vector3d playerPos){ private boolean recursivelyUpdateCells(ChunkTreeNode<FoliageCell> node, Vector3d playerPos){
boolean updated = false; boolean updated = false;
if(this.shouldSplit(playerPos, node)){ if(this.shouldSplit(playerPos, node)){
Globals.profiler.beginCpuSample("FoliageChunk.split");
//perform op //perform op
ChunkTreeNode<FoliageCell> container = chunkTree.split(node); ChunkTreeNode<FoliageCell> container = chunkTree.split(node);
@ -119,8 +154,10 @@ public class FoliageChunk {
); );
child.convertToLeaf(new FoliageCell(worldPos, child.getMinBound(), realPos, 5 - child.getLevel())); child.convertToLeaf(new FoliageCell(worldPos, child.getMinBound(), realPos, 5 - child.getLevel()));
}); });
Globals.profiler.endCpuSample();
updated = true; updated = true;
} else if(this.shouldJoin(playerPos, node)) { } else if(this.shouldJoin(playerPos, node)) {
// Globals.profiler.beginCpuSample("FoliageChunk.join");
//perform op //perform op
ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(node); ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(node);
@ -129,9 +166,12 @@ public class FoliageChunk {
//do creations //do creations
newLeaf.convertToLeaf(new FoliageCell(worldPos, newLeaf.getMinBound(), realPos, 5 - newLeaf.getLevel())); newLeaf.convertToLeaf(new FoliageCell(worldPos, newLeaf.getMinBound(), realPos, 5 - newLeaf.getLevel()));
// Globals.profiler.endCpuSample();
updated = true; updated = true;
} else if(shouldGenerate(playerPos, node)){ } else if(shouldGenerate(playerPos, node)){
// Globals.profiler.beginCpuSample("FoliageChunk.generate");
node.getData().generate(); node.getData().generate();
// Globals.profiler.endCpuSample();
updated = true; updated = true;
} else if(!node.isLeaf()){ } else if(!node.isLeaf()){
List<ChunkTreeNode<FoliageCell>> children = new LinkedList<ChunkTreeNode<FoliageCell>>(node.getChildren()); List<ChunkTreeNode<FoliageCell>> children = new LinkedList<ChunkTreeNode<FoliageCell>>(node.getChildren());

View File

@ -4,6 +4,8 @@ import org.joml.Vector3d;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector3i; import org.joml.Vector3i;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
/** /**
* Client's data on the world * Client's data on the world
*/ */
@ -31,8 +33,6 @@ public class ClientWorldData {
Vector3f worldMinPoint; Vector3f worldMinPoint;
Vector3f worldMaxPoint; Vector3f worldMaxPoint;
int dynamicInterpolationRatio;
float randomDampener; float randomDampener;
@ -42,13 +42,11 @@ public class ClientWorldData {
public ClientWorldData( public ClientWorldData(
Vector3f worldMinPoint, Vector3f worldMinPoint,
Vector3f worldMaxPoint, Vector3f worldMaxPoint,
int dynamicInterpolationRatio,
float randomDampener, float randomDampener,
int worldDiscreteSize int worldDiscreteSize
) { ) {
this.worldMinPoint = worldMinPoint; this.worldMinPoint = worldMinPoint;
this.worldMaxPoint = worldMaxPoint; this.worldMaxPoint = worldMaxPoint;
this.dynamicInterpolationRatio = dynamicInterpolationRatio;
this.randomDampener = randomDampener; this.randomDampener = randomDampener;
this.worldDiscreteSize = worldDiscreteSize; this.worldDiscreteSize = worldDiscreteSize;
} }
@ -64,10 +62,6 @@ public class ClientWorldData {
return worldMaxPoint; return worldMaxPoint;
} }
public int getDynamicInterpolationRatio() {
return dynamicInterpolationRatio;
}
public float getRandomDampener() { public float getRandomDampener() {
return randomDampener; return randomDampener;
} }
@ -78,11 +72,11 @@ public class ClientWorldData {
public int convertRealToChunkSpace(double real){ public int convertRealToChunkSpace(double real){
return (int)Math.floor(real / dynamicInterpolationRatio); return (int)Math.floor(real / ServerTerrainChunk.CHUNK_DIMENSION);
} }
public float convertChunkToRealSpace(int chunk){ public float convertChunkToRealSpace(int chunk){
return chunk * dynamicInterpolationRatio; return chunk * ServerTerrainChunk.CHUNK_DIMENSION;
} }
public int convertRealToWorld(double real){ public int convertRealToWorld(double real){

View File

@ -166,7 +166,7 @@ public class ClientSimulation {
* Loads terrain that is in queue * Loads terrain that is in queue
*/ */
public void loadTerrain(){ public void loadTerrain(){
Globals.profiler.beginCpuSample("load terrain"); Globals.profiler.beginCpuSample("ClientSimulation.loadTerrain");
if(Globals.clientTerrainManager != null){ if(Globals.clientTerrainManager != null){
Globals.clientTerrainManager.handleMessages(); Globals.clientTerrainManager.handleMessages();
updateTerrainCellManager(); updateTerrainCellManager();
@ -185,9 +185,9 @@ public class ClientSimulation {
/// ///
/// C L I E N T C E L L M A N A G E R /// C L I E N T C E L L M A N A G E R
/// ///
if(Globals.drawCellManager != null && Globals.clientWorldData != null){ if(Globals.clientDrawCellManager != null && Globals.clientWorldData != null){
//Cell manager do your things //Cell manager do your things
Globals.drawCellManager.update(); Globals.clientDrawCellManager.update();
} }
} }

View File

@ -26,6 +26,31 @@ public class ChunkData {
//Used in DrawCell to keep track of which positions to invalidate //Used in DrawCell to keep track of which positions to invalidate
Set<String> modifiedSinceLastGeneration = new HashSet<String>(); Set<String> modifiedSinceLastGeneration = new HashSet<String>();
/**
* The word x coordinate
*/
int worldX;
/**
* The word y coordinate
*/
int worldY;
/**
* The word z coordinate
*/
int worldZ;
/**
* Creates a chunk data
* @param worldX The word x coordinate
* @param worldY The word y coordinate
* @param worldZ The word z coordinate
*/
public ChunkData(int worldX, int worldY, int worldZ){
this.worldX = worldX;
this.worldY = worldY;
this.worldZ = worldZ;
}
/** /**
* Gets the voxel type array in this container * Gets the voxel type array in this container
@ -178,5 +203,13 @@ public class ChunkData {
return position.x + "_" + position.y + "_" + position.z; return position.x + "_" + position.y + "_" + position.z;
} }
/**
* Gets the world position of the chunk data
* @return The world position
*/
public Vector3i getWorldPos(){
return new Vector3i(worldX,worldY,worldZ);
}
} }

View File

@ -8,6 +8,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.joml.Vector3i; import org.joml.Vector3i;
import io.github.studiorailgun.HashUtils;
/** /**
* Acts as a cache in front of terrain model to streamline receiving chunks * Acts as a cache in front of terrain model to streamline receiving chunks
@ -17,16 +19,21 @@ public class ClientTerrainCache {
//cache capacity //cache capacity
int cacheSize; int cacheSize;
//the map of chunk key -> chunk data //the map of chunk key -> chunk data
Map<String,ChunkData> cacheMap = new ConcurrentHashMap<String,ChunkData>(); Map<Long,ChunkData> cacheMap = new ConcurrentHashMap<Long,ChunkData>();
//the list of keys in the cache //the list of keys in the cache
List<String> cacheList = new CopyOnWriteArrayList<String>(); List<Long> cacheList = new CopyOnWriteArrayList<Long>();
//A map of chunk to its world position //A map of chunk to its world position
Map<ChunkData,Vector3i> chunkPositionMap = new ConcurrentHashMap<ChunkData,Vector3i>(); Map<ChunkData,Vector3i> chunkPositionMap = new ConcurrentHashMap<ChunkData,Vector3i>();
/**
* The map tracking chunks that have been requested
*/
Map<Long,Boolean> requestedChunks = new ConcurrentHashMap<Long,Boolean>();
/** /**
* Constructor * Constructor
* @param cacheSize The capacity of the cache * @param cacheSize The capacity of the cache
*/ */
public ClientTerrainCache(int cacheSize){ public ClientTerrainCache(int cacheSize){
this.cacheSize = cacheSize; this.cacheSize = cacheSize;
} }
@ -42,8 +49,10 @@ public class ClientTerrainCache {
cacheMap.put(getKey(worldX,worldY,worldZ),chunkData); cacheMap.put(getKey(worldX,worldY,worldZ),chunkData);
chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ)); chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ));
while(cacheList.size() > cacheSize){ while(cacheList.size() > cacheSize){
String currentChunk = cacheList.remove(0); Long currentChunk = cacheList.remove(0);
cacheMap.remove(currentChunk); ChunkData data = cacheMap.remove(currentChunk);
Vector3i worldPos = data.getWorldPos();
requestedChunks.remove(getKey(worldPos.x,worldPos.y,worldPos.z));
} }
} }
@ -64,8 +73,8 @@ public class ClientTerrainCache {
* @param worldZ The z world position * @param worldZ The z world position
* @return The cache key * @return The cache key
*/ */
public String getKey(int worldX, int worldY, int worldZ){ public long getKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ; return HashUtils.cantorHash(worldX, worldY, worldZ);
} }
/** /**
@ -110,5 +119,34 @@ public class ClientTerrainCache {
public Vector3i getChunkPosition(ChunkData chunk){ public Vector3i getChunkPosition(ChunkData chunk){
return chunkPositionMap.get(chunk); return chunkPositionMap.get(chunk);
} }
/**
* Gets the number of cells that have been requested
* @return The number of cells that have been requested
*/
public int getRequestedCellCount(){
return this.requestedChunks.size();
}
/**
* Checks if a chunk has been requested or not
* @param worldX The x coordinate in the world
* @param worldY The y coordinate in the world
* @param worldZ The z coordinate in the world
* @return true if it has been requested, false otherwise
*/
public boolean hasRequested(int worldX, int worldY, int worldZ){
return this.requestedChunks.containsKey(getKey(worldX, worldY, worldZ));
}
/**
* Marks a chunk as requested
* @param worldX The x coordinate in the world
* @param worldY The y coordinate in the world
* @param worldZ The z coordinate in the world
*/
public void markAsRequested(int worldX, int worldY, int worldZ){
this.requestedChunks.put(getKey(worldX, worldY, worldZ),true);
}
} }

View File

@ -0,0 +1,690 @@
package electrosphere.client.terrain.cells;
import java.util.LinkedList;
import java.util.List;
import org.joml.Vector3d;
import org.joml.Vector3i;
import electrosphere.client.terrain.cells.DrawCell.DrawCellFace;
import electrosphere.engine.Globals;
import electrosphere.entity.EntityUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.util.ds.octree.WorldOctTree;
import electrosphere.util.ds.octree.WorldOctTree.FloatingChunkTreeNode;
import electrosphere.util.math.GeomUtils;
/**
* Manages draw cells on the client
*/
public class ClientDrawCellManager {
/**
* Number of times to try updating per frame. Lower this to reduce lag but slow down terrain mesh generation.
*/
static final int UPDATE_ATTEMPTS_PER_FRAME = 3;
/**
* The distance to draw at full resolution
*/
public static final double FULL_RES_DIST = 5 * ServerTerrainChunk.CHUNK_DIMENSION;
/**
* The distance for half resolution
*/
public static final double HALF_RES_DIST = 15 * ServerTerrainChunk.CHUNK_DIMENSION;
/**
* The octree holding all the chunks to evaluate
*/
WorldOctTree<DrawCell> chunkTree;
/**
* Tracks whether the cell manager updated last frame or not
*/
boolean updatedLastFrame = true;
/**
* Controls whether the client draw cell manager should update or not
*/
boolean shouldUpdate = true;
/**
* The voxel texture atlas
*/
VoxelTextureAtlas textureAtlas;
/**
* The dimensions of the world
*/
int worldDim = 0;
/**
* Tracks the number of currently valid cells (ie didn't require an update this frame)
*/
int validCellCount = 0;
/**
* The number of maximum resolution chunks
*/
int maxResCount = 0;
/**
* The number of half resolution chunks
*/
int halfResCount = 0;
/**
* The number of generated chunks
*/
int generated = 0;
/**
* Constructor
* @param voxelTextureAtlas The voxel texture atlas
* @param worldDim The size of the world in chunks
*/
public ClientDrawCellManager(VoxelTextureAtlas voxelTextureAtlas, int worldDim){
this.chunkTree = new WorldOctTree<DrawCell>(new Vector3i(0,0,0), new Vector3i(worldDim, worldDim, worldDim));
this.worldDim = worldDim;
this.textureAtlas = voxelTextureAtlas;
}
/**
* Updates all cells in the chunk
*/
public void update(){
Globals.profiler.beginCpuSample("ClientDrawCellManager.updateCells");
if(shouldUpdate && Globals.playerEntity != null){
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
//the sets to iterate through
updatedLastFrame = true;
int attempts = 0;
validCellCount = 0;
while(updatedLastFrame && attempts < UPDATE_ATTEMPTS_PER_FRAME){
FloatingChunkTreeNode<DrawCell> rootNode = this.chunkTree.getRoot();
updatedLastFrame = this.recursivelyUpdateCells(rootNode, playerPos);
attempts++;
}
}
Globals.profiler.endCpuSample();
}
/**
* Recursively update child nodes
* @param node The root node
* @param playerPos The player's position
* @return true if there is work remaining to be done, false otherwise
*/
private boolean recursivelyUpdateCells(FloatingChunkTreeNode<DrawCell> node, Vector3d playerPos){
Vector3d playerRealPos = EntityUtils.getPosition(Globals.playerEntity);
boolean updated = false;
if(this.shouldSplit(playerPos, node)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.split");
//perform op
FloatingChunkTreeNode<DrawCell> container = chunkTree.split(node);
//do deletions
this.recursivelyDestroy(node);
//do creations
container.getChildren().forEach(child -> {
Vector3i cellWorldPos = new Vector3i(
child.getMinBound().x,
child.getMinBound().y,
child.getMinBound().z
);
DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos);
child.convertToLeaf(drawCell);
});
//update neighbors
this.conditionalUpdateAdjacentNodes(container, container.getChildren().get(0).getLevel());
Globals.profiler.endCpuSample();
updated = true;
} else if(this.shouldJoin(playerPos, node)) {
Globals.profiler.beginCpuSample("ClientDrawCellManager.join");
//perform op
FloatingChunkTreeNode<DrawCell> newLeaf = chunkTree.join(node);
//do deletions
this.recursivelyDestroy(node);
//do creations
DrawCell drawCell = DrawCell.generateTerrainCell(node.getMinBound());
newLeaf.convertToLeaf(drawCell);
//update neighbors
this.conditionalUpdateAdjacentNodes(newLeaf, newLeaf.getLevel());
Globals.profiler.endCpuSample();
updated = true;
} else if(shouldRequest(playerPos, node)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.request");
DrawCell cell = node.getData();
this.requestChunks(node);
cell.setHasRequested(true);
Globals.profiler.endCpuSample();
updated = true;
} else if(shouldGenerate(playerPos, node)){
Globals.profiler.beginCpuSample("ClientDrawCellManager.generate");
int lodLevel = this.getLODLevel(playerRealPos, node);
List<DrawCellFace> highResFaces = this.solveHighResFace(node);
if(containsDataToGenerate(node,highResFaces)){
node.getData().generateDrawableEntity(textureAtlas, lodLevel, highResFaces);
}
Globals.profiler.endCpuSample();
updated = true;
} else if(!node.isLeaf()){
this.validCellCount++;
List<FloatingChunkTreeNode<DrawCell>> children = new LinkedList<FloatingChunkTreeNode<DrawCell>>(node.getChildren());
for(FloatingChunkTreeNode<DrawCell> child : children){
boolean childUpdate = recursivelyUpdateCells(child, playerPos);
if(childUpdate == true){
updated = true;
}
}
}
return updated;
}
/**
* Gets the minimum distance from a node to a point
* @param pos the position to check against
* @param node the node
* @return the distance
*/
public double getMinDistance(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
Vector3i min = node.getMinBound();
Vector3i max = node.getMaxBound();
return GeomUtils.getMinDistanceAABB(pos, Globals.clientWorldData.convertWorldToRealSpace(min), Globals.clientWorldData.convertWorldToRealSpace(max));
}
/**
* Gets whether this should be split or not
* @param pos the player position
* @param node The node
* @return true if should split, false otherwise
*/
public boolean shouldSplit(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
//breaking out into dedicated function so can add case handling ie if we want
//to combine fullres nodes into larger nodes to conserve on draw calls
return
node.isLeaf() &&
node.canSplit() &&
(
(
node.getLevel() < this.chunkTree.getMaxLevel() - 1 &&
this.getMinDistance(pos, node) <= HALF_RES_DIST
)
||
(
node.getLevel() < this.chunkTree.getMaxLevel() &&
this.getMinDistance(pos, node) <= FULL_RES_DIST
)
)
;
}
/**
* Gets the LOD level of the draw cell
* @param pos The position of the cell
* @param node The node to consider
* @return -1 if outside of render range, -1 if the node is not a valid draw cell leaf, otherwise returns the LOD level
*/
private int getLODLevel(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
return this.chunkTree.getMaxLevel() - node.getLevel();
}
/**
* Solves which face (if any) is the high res face for a LOD chunk
* @param node The node for the chunk
* @return The face if there is a higher resolution face, null otherwise
*/
private List<DrawCellFace> solveHighResFace(FloatingChunkTreeNode<DrawCell> node){
//don't bother to check if it's a full res chunk
if(node.getLevel() == this.chunkTree.getMaxLevel()){
return null;
}
int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1;
int spacing = (int)Math.pow(2,lodMultiplitier);
List<DrawCellFace> faces = new LinkedList<DrawCellFace>();
if(node.getMinBound().x - 1 >= 0){
FloatingChunkTreeNode<DrawCell> xNegNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(-1,1,1), false);
if(xNegNode != null && xNegNode.getLevel() > node.getLevel()){
faces.add(DrawCellFace.X_NEGATIVE);
}
}
if(node.getMinBound().y - 1 >= 0){
FloatingChunkTreeNode<DrawCell> yNegNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(1,-1,1), false);
if(yNegNode != null && yNegNode.getLevel() > node.getLevel()){
faces.add(DrawCellFace.Y_NEGATIVE);
}
}
if(node.getMinBound().z - 1 >= 0){
FloatingChunkTreeNode<DrawCell> zNegNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(1,1,-1), false);
if(zNegNode != null && zNegNode.getLevel() > node.getLevel()){
faces.add(DrawCellFace.Z_NEGATIVE);
}
}
if(node.getMaxBound().x + spacing + 1 < this.worldDim){
FloatingChunkTreeNode<DrawCell> xPosNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(spacing + 1,1,1), false);
if(xPosNode != null && xPosNode.getLevel() > node.getLevel()){
faces.add(DrawCellFace.X_POSITIVE);
}
}
if(node.getMaxBound().y + spacing + 1 < this.worldDim){
FloatingChunkTreeNode<DrawCell> yPosNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(1,spacing + 1,1), false);
if(yPosNode != null && yPosNode.getLevel() > node.getLevel()){
faces.add(DrawCellFace.Y_POSITIVE);
}
}
if(node.getMaxBound().z + spacing + 1 < this.worldDim){
FloatingChunkTreeNode<DrawCell> zPosNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(1,1,spacing + 1), false);
if(zPosNode != null && zPosNode.getLevel() > node.getLevel()){
faces.add(DrawCellFace.Z_POSITIVE);
}
}
if(faces.size() > 0){
return faces;
}
return null;
}
/**
* Conditionally updates all adjacent nodes if their level would require transition cells in the voxel rasterization
* @param node The node to search from adjacencies from
* @param level The level to check against
*/
private void conditionalUpdateAdjacentNodes(FloatingChunkTreeNode<DrawCell> node, int level){
//don't bother to check if it's a lowest-res chunk
if(this.chunkTree.getMaxLevel() - level > DrawCell.LOWEST_LOD){
return;
}
if(node.getMinBound().x - 1 >= 0){
FloatingChunkTreeNode<DrawCell> xNegNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(-1,0,0), false);
if(xNegNode != null && xNegNode.getLevel() < level){
xNegNode.getData().setHasGenerated(false);
}
}
if(node.getMinBound().y - 1 >= 0){
FloatingChunkTreeNode<DrawCell> yNegNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(0,-1,0), false);
if(yNegNode != null && yNegNode.getLevel() < level){
yNegNode.getData().setHasGenerated(false);
}
}
if(node.getMinBound().z - 1 >= 0){
FloatingChunkTreeNode<DrawCell> zNegNode = this.chunkTree.search(new Vector3i(node.getMinBound()).add(0,0,-1), false);
if(zNegNode != null && zNegNode.getLevel() < level){
zNegNode.getData().setHasGenerated(false);
}
}
if(node.getMaxBound().x + 1 < this.worldDim){
FloatingChunkTreeNode<DrawCell> xPosNode = this.chunkTree.search(new Vector3i(node.getMaxBound()).add(1,-1,-1), false);
if(xPosNode != null && xPosNode.getLevel() < level){
xPosNode.getData().setHasGenerated(false);
}
}
if(node.getMaxBound().y + 1 < this.worldDim){
FloatingChunkTreeNode<DrawCell> yPosNode = this.chunkTree.search(new Vector3i(node.getMaxBound()).add(-1,1,-1), false);
if(yPosNode != null && yPosNode.getLevel() < level){
yPosNode.getData().setHasGenerated(false);
}
}
if(node.getMaxBound().z + 1 < this.worldDim){
FloatingChunkTreeNode<DrawCell> zPosNode = this.chunkTree.search(new Vector3i(node.getMaxBound()).add(-1,-1,1), false);
if(zPosNode != null && zPosNode.getLevel() < level){
zPosNode.getData().setHasGenerated(false);
}
}
}
/**
* Gets whether this should be joined or not
* @param pos the player position
* @param node The node
* @return true if should be joined, false otherwise
*/
public boolean shouldJoin(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
//breaking out into dedicated function so can add case handling ie if we want
//to combine fullres nodes into larger nodes to conserve on draw calls
return
node.getLevel() > 0 &&
!node.isLeaf() &&
(
(
node.getLevel() == this.chunkTree.getMaxLevel() &&
this.getMinDistance(pos, node) > FULL_RES_DIST
)
||
(
this.getMinDistance(pos, node) > HALF_RES_DIST
)
)
;
}
/**
* Checks if this cell should request chunk data
* @param pos the player's position
* @param node the node
* @return true if should request chunk data, false otherwise
*/
public boolean shouldRequest(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
return
node.isLeaf() &&
node.getData() != null &&
!node.getData().hasRequested() &&
(
(
node.getLevel() == this.chunkTree.getMaxLevel()
// &&
// this.getMinDistance(pos, node) <= FULL_RES_DIST
)
||
(
node.getLevel() == this.chunkTree.getMaxLevel() - 1
&&
this.getMinDistance(pos, node) <= HALF_RES_DIST
)
)
;
}
/**
* Checks if this cell should generate
* @param pos the player's position
* @param node the node
* @return true if should generate, false otherwise
*/
public boolean shouldGenerate(Vector3d pos, FloatingChunkTreeNode<DrawCell> node){
return
node.isLeaf() &&
node.getData() != null &&
!node.getData().hasGenerated() &&
(
(
node.getLevel() == this.chunkTree.getMaxLevel()
// &&
// this.getMinDistance(pos, node) <= FULL_RES_DIST
)
||
(
node.getLevel() == this.chunkTree.getMaxLevel() - 1
&&
this.getMinDistance(pos, node) <= HALF_RES_DIST
)
)
;
}
/**
* Checks if the node should have destroy called on it
* @param node The node
* @return true if should destroy, false otherwise
*/
public boolean shouldDestroy(FloatingChunkTreeNode<DrawCell> node){
return
node.getData() != null &&
node.getData().getEntity() != null
;
}
/**
* Destroys the foliage chunk
*/
protected void destroy(){
this.recursivelyDestroy(this.chunkTree.getRoot());
}
/**
* Recursively destroy a tree
* @param node The root of the tree
*/
private void recursivelyDestroy(FloatingChunkTreeNode<DrawCell> node){
if(node.getChildren().size() > 0){
node.getChildren().forEach(child -> recursivelyDestroy(child));
}
if(node.getData() != null){
node.getData().destroy();
}
}
/**
* Checks if the cell manager made an update last frame or not
* @return true if an update occurred, false otherwise
*/
public boolean updatedLastFrame(){
return this.updatedLastFrame;
}
/**
* Checks if the position is within the full LOD range
* @param worldPos The world position
* @return true if within full LOD range, false otherwise
*/
public boolean isFullLOD(Vector3i worldPos){
Vector3d playerRealPos = EntityUtils.getPosition(Globals.playerEntity);
Vector3d chunkMin = Globals.clientWorldData.convertWorldToRealSpace(worldPos);
Vector3d chunkMax = Globals.clientWorldData.convertWorldToRealSpace(new Vector3i(worldPos).add(1,1,1));
return GeomUtils.getMinDistanceAABB(playerRealPos, chunkMin, chunkMax) <= FULL_RES_DIST;
}
/**
* Evicts all cells
*/
public void evictAll(){
this.chunkTree.clear();
}
/**
* Marks a draw cell as updateable
* @param worldX The world x position
* @param worldY The world y position
* @param worldZ The world z position
*/
public void markUpdateable(float worldX, float worldY, float worldZ){
throw new Error("Unimplemented");
}
/**
* Checks if the terrain cache has a chunk at a given world point
* @param worldX the x coordinate
* @param worldY the y coordinate
* @param worldZ the z coordinate
* @return true if the chunk data exists, false otherwise
*/
boolean containsChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){
if(Globals.clientTerrainManager != null){
return Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldX,worldY,worldZ);
}
return true;
}
/**
* Requests all chunks for a given draw cell
* @param cell The cell
*/
private void requestChunks(WorldOctTree.FloatingChunkTreeNode<DrawCell> node){
DrawCell cell = node.getData();
int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1;
for(int i = 0; i < 2 * lodMultiplitier; i++){
for(int j = 0; j < 2 * lodMultiplitier; j++){
for(int k = 0; k < 2 * lodMultiplitier; k++){
Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i,j,k);
if(
posToCheck.x >= 0 &&
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.y >= 0 &&
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.z >= 0 &&
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z)
){
//client should request chunk data from server for each chunk necessary to create the model
LoggerInterface.loggerNetworking.DEBUG("(Client) Send Request for terrain at " + posToCheck);
Globals.clientTerrainManager.requestChunk(posToCheck.x, posToCheck.y, posToCheck.z);
}
}
}
}
}
/**
* Checks if all chunk data required to generate this draw cell is present
* @param node The node
* @param highResFace The higher resolution face of a not-full-resolution chunk. Null if the chunk is max resolution or there is no higher resolution face for the current chunk
* @return true if all data is available, false otherwise
*/
private boolean containsDataToGenerate(WorldOctTree.FloatingChunkTreeNode<DrawCell> node, List<DrawCellFace> highResFaces){
DrawCell cell = node.getData();
int lodMultiplitier = this.chunkTree.getMaxLevel() - node.getLevel() + 1;
for(int i = 0; i < 2 * lodMultiplitier; i++){
for(int j = 0; j < 2 * lodMultiplitier; j++){
for(int k = 0; k < 2 * lodMultiplitier; k++){
Vector3i posToCheck = new Vector3i(cell.getWorldPos()).add(i,j,k);
if(
posToCheck.x >= 0 &&
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.y >= 0 &&
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.z >= 0 &&
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z)
){
return false;
}
}
}
}
if(highResFaces != null){
for(DrawCellFace highResFace : highResFaces){
//x & y are in face-space
for(int x = 0; x < 2 * lodMultiplitier; x++){
for(int y = 0; y < 2 * lodMultiplitier; y++){
Vector3i posToCheck = null;
//implicitly performing transforms to adapt from face-space to world space
switch(highResFace){
case X_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(lodMultiplitier,x,y);
} break;
case X_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(-1,x,y);
} break;
case Y_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x,lodMultiplitier,y);
} break;
case Y_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x,-1,y);
} break;
case Z_POSITIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,lodMultiplitier);
} break;
case Z_NEGATIVE: {
posToCheck = new Vector3i(cell.getWorldPos()).add(x,y,-1);
} break;
}
if(
posToCheck.x >= 0 &&
posToCheck.x < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.y >= 0 &&
posToCheck.y < Globals.clientWorldData.getWorldDiscreteSize() &&
posToCheck.z >= 0 &&
posToCheck.z < Globals.clientWorldData.getWorldDiscreteSize() &&
!Globals.clientTerrainManager.containsChunkDataAtWorldPoint(posToCheck.x, posToCheck.y, posToCheck.z)
){
return false;
}
}
}
}
}
return true;
}
/**
* Sets whether the draw cell manager should update or not
* @param shouldUpdate true if should update, false otherwise
*/
public void setShouldUpdate(boolean shouldUpdate){
this.shouldUpdate = shouldUpdate;
}
/**
* Gets whether the client draw cell manager should update or not
* @return true if should update, false otherwise
*/
public boolean getShouldUpdate(){
return this.shouldUpdate;
}
/**
* Gets the number of currently valid cells
* @return The number of currently valid cells
*/
public int getValidCellCount(){
return validCellCount;
}
/**
* Calculates the status of the draw cell manager
*/
public void updateStatus(){
maxResCount = 0;
halfResCount = 0;
generated = 0;
this.recursivelyCalculateStatus(this.chunkTree.getRoot());
}
/**
* Recursively calculates the status of the manager
* @param node The root node
*/
private void recursivelyCalculateStatus(FloatingChunkTreeNode<DrawCell> node){
if(node.getLevel() == this.chunkTree.getMaxLevel() - 1){
halfResCount++;
}
if(node.getLevel() == this.chunkTree.getMaxLevel()){
maxResCount++;
}
if(node.getData() != null && node.getData().hasGenerated()){
generated++;
}
if(node.getChildren() != null && node.getChildren().size() > 0){
List<FloatingChunkTreeNode<DrawCell>> children = new LinkedList<FloatingChunkTreeNode<DrawCell>>(node.getChildren());
for(FloatingChunkTreeNode<DrawCell> child : children){
recursivelyCalculateStatus(child);
}
}
}
/**
* Gets The number of maximum resolution chunks
* @return The number of maximum resolution chunks
*/
public int getMaxResCount() {
return maxResCount;
}
/**
* Gets The number of half resolution chunks
* @return The number of half resolution chunks
*/
public int getHalfResCount() {
return halfResCount;
}
/**
* Gets The number of generated chunks
* @return
*/
public int getGenerated() {
return generated;
}
}

View File

@ -1,5 +1,7 @@
package electrosphere.client.terrain.cells; package electrosphere.client.terrain.cells;
import java.util.List;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3i; import org.joml.Vector3i;
@ -10,6 +12,7 @@ import electrosphere.engine.Globals;
import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.types.terrain.TerrainChunk; import electrosphere.entity.types.terrain.TerrainChunk;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration;
import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData;
/** /**
@ -39,15 +42,57 @@ public class DrawCell {
float[][][] weights = new float[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE]; float[][][] weights = new float[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE];
int[][][] types = new int[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE]; int[][][] types = new int[ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE][ChunkData.CHUNK_DATA_GENERATOR_SIZE];
/**
* An invalid LOD level
*/
public static final int INVALID_LOD_LEVEL = -1;
//the maximum detail LOD level /**
* The maximum detail LOD level
*/
public static final int FULL_DETAIL_LOD = 0; public static final int FULL_DETAIL_LOD = 0;
/**
* The half detail lod level
*/
public static final int HALF_DETAIL_LOD = 1;
/**
* The lowest available LOD
*/
public static final int LOWEST_LOD = HALF_DETAIL_LOD;
/**
* The lod level of this draw cell
*/
int lodLevel = FULL_DETAIL_LOD;
/**
* Tracks whether the draw cell has requested its chunk data or not
*/
boolean hasRequested = false;
/**
* Tracks whether the draw cell has generated its entity or not
*/
boolean hasGenerated = false;
/**
* The initial voxel type encountered
*/
int initialVoxelType = -1;
/**
* True if there are multiple types of voxels in this chunk
*/
boolean multiVoxelType = false;
/** /**
* Private constructor * Private constructor
*/ */
DrawCell(){ private DrawCell(){
} }
@ -66,17 +111,35 @@ public class DrawCell {
/** /**
* Generates a drawable entity based on this chunk * Generates a drawable entity based on this chunk
*/ */
public void generateDrawableEntity(VoxelTextureAtlas atlas, int lod, DrawCellFace higherLODFace){ public void generateDrawableEntity(VoxelTextureAtlas atlas, int lod, List<DrawCellFace> higherLODFaces){
Globals.profiler.beginCpuSample("DrawCell.fillInData");
boolean success = this.fillInData(lod);
Globals.profiler.endCpuSample();
if(!success){
this.setHasRequested(false);
return;
}
if(modelEntity != null){ if(modelEntity != null){
Globals.clientScene.deregisterEntity(modelEntity); Globals.clientScene.deregisterEntity(modelEntity);
} }
this.fillInData();
TransvoxelChunkData chunkData = new TransvoxelChunkData(weights, types, lod); TransvoxelChunkData chunkData = new TransvoxelChunkData(weights, types, lod);
if(lod > FULL_DETAIL_LOD){ if(higherLODFaces != null){
for(DrawCellFace face : higherLODFaces){
Globals.profiler.beginCpuSample("DrawCell.fillInFaceData");
success = this.fillInFaceData(chunkData,face,lod);
Globals.profiler.endCpuSample();
if(!success){
this.setHasRequested(false);
return;
}
}
} }
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(chunkData, lod, atlas); if(lod == INVALID_LOD_LEVEL){
throw new Error("Trying to generate invalid LOD");
}
modelEntity = TerrainChunk.clientCreateTerrainChunkEntity(chunkData, lod, atlas, this.hasPolygons());
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos(), new Quaterniond()); ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos(), new Quaterniond());
this.setHasGenerated(true);
} }
/** /**
@ -90,147 +153,322 @@ public class DrawCell {
worldPos.z * ChunkData.CHUNK_SIZE worldPos.z * ChunkData.CHUNK_SIZE
); );
} }
/**
* Gets the world-space position of the draw cell
* @return the world-space position
*/
protected Vector3i getWorldPos(){
return new Vector3i(worldPos);
}
/** /**
* Destroys a drawcell including its physics * Destroys a drawcell including its physics
*/ */
public void destroy(){ public void destroy(){
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
collisionEngine.destroyPhysics(modelEntity); if(modelEntity != null){
ClientEntityUtils.destroyEntity(modelEntity); collisionEngine.destroyPhysics(modelEntity);
ClientEntityUtils.destroyEntity(modelEntity);
}
}
/**
* Gets the entity for the cell
* @return The entity if it exists, null otherwise
*/
public Entity getEntity(){
return modelEntity;
} }
/** /**
* Fills in the internal arrays of data for generate terrain models * Fills in the internal arrays of data for generate terrain models
* @return true if the data was successfully filled, false otherwise
*/ */
private void fillInData(){ private boolean fillInData(int lod){
// int spacingFactor = (int)Math.pow(2,lod);
//fill in data for(int x = 0; x < ChunkData.CHUNK_DATA_GENERATOR_SIZE; x++){
// for(int y = 0; y < ChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){
//main chunk for(int z = 0; z < ChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos); ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){ worldPos.x + (x * spacingFactor) / ChunkData.CHUNK_SIZE,
for(int y = 0; y < ChunkData.CHUNK_SIZE; y++){ worldPos.y + (y * spacingFactor) / ChunkData.CHUNK_SIZE,
for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){ worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE
weights[x][y][z] = currentChunk.getWeight(x,y,z); );
types[x][y][z] = currentChunk.getType(x,y,z); if(currentChunk == null){
return false;
}
weights[x][y][z] = currentChunk.getWeight(
(x * spacingFactor) % ChunkData.CHUNK_SIZE,
(y * spacingFactor) % ChunkData.CHUNK_SIZE,
(z * spacingFactor) % ChunkData.CHUNK_SIZE
);
types[x][y][z] = currentChunk.getType(
(x * spacingFactor) % ChunkData.CHUNK_SIZE,
(y * spacingFactor) % ChunkData.CHUNK_SIZE,
(z * spacingFactor) % ChunkData.CHUNK_SIZE
);
//checks to see if there is only one type of voxel in this chunk
if(!this.multiVoxelType){
if(this.initialVoxelType == -1){
this.initialVoxelType = types[x][y][z];
} else if(this.initialVoxelType != types[x][y][z]){
this.multiVoxelType = true;
}
}
} }
} }
} }
//face X return true;
if(worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z);
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
weights[ChunkData.CHUNK_SIZE][i][j] = currentChunk.getWeight(0, i, j);
types[ChunkData.CHUNK_SIZE][i][j] = currentChunk.getType(0, i, j);
}
}
} else {
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
weights[ChunkData.CHUNK_SIZE][i][j] = -1;
types[ChunkData.CHUNK_SIZE][i][j] = 0;
}
}
}
//face Y
if(worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z);
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
weights[i][ChunkData.CHUNK_SIZE][j] = currentChunk.getWeight(i, 0, j);
types[i][ChunkData.CHUNK_SIZE][j] = currentChunk.getType(i, 0, j);
}
}
} else {
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
weights[i][ChunkData.CHUNK_SIZE][j] = -1;
types[i][ChunkData.CHUNK_SIZE][j] = 0;
}
}
}
//face Z
if(worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()){
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z + 1);
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
weights[i][j][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(i, j, 0);
types[i][j][ChunkData.CHUNK_SIZE] = currentChunk.getType(i, j, 0);
}
}
} else {
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
for(int j = 0; j < ChunkData.CHUNK_SIZE; j++){
weights[i][j][ChunkData.CHUNK_SIZE] = -1;
types[i][j][ChunkData.CHUNK_SIZE] = 0;
}
}
}
//edge X-Y
if(
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize()
){
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z);
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = currentChunk.getWeight(0, 0, i);
types [ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = currentChunk.getType(0, 0, i);
}
} else {
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = -1;
types [ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][i] = 0;
}
}
//edge X-Z
if(
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
){
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y, worldPos.z + 1);
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
weights[ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(0, i, 0);
types [ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = currentChunk.getType(0, i, 0);
}
} else {
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
weights[ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = -1;
types [ChunkData.CHUNK_SIZE][i][ChunkData.CHUNK_SIZE] = 0;
}
}
//edge Y-Z
if(
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
){
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x, worldPos.y + 1, worldPos.z + 1);
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
weights[i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(i, 0, 0);
types [i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getType(i, 0, 0);
}
} else {
for(int i = 0; i < ChunkData.CHUNK_SIZE; i++){
weights[i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = -1;
types [i][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
}
}
if(
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
){
currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z + 1);
if(currentChunk != null){
weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getWeight(0, 0, 0);
types[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = currentChunk.getType(0, 0, 0);
}
} else {
weights[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = -1;
types[ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE][ChunkData.CHUNK_SIZE] = 0;
}
} }
/**
* Fills in the data for the higher resolution face
* @param chunkData The data for the chunk to generate
* @param higherLODFace The face that is higher LOD
* @param lod The Level of Detail for this chunk
* @return true if successfully filled in data, false otherwise
*/
private boolean fillInFaceData(TransvoxelChunkData chunkData, DrawCellFace higherLODFace, int lod){
int mainSpacing = (int)Math.pow(2,lod);
int higherResSpacing = (int)Math.pow(2,lod - 1);
float[][] faceWeights = new float[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS];
int[][] faceTypes = new int[TransvoxelModelGeneration.FACE_DATA_DIMENSIONS][TransvoxelModelGeneration.FACE_DATA_DIMENSIONS];
//allocate face array
for(int x = 0; x < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; x++){
for(int y = 0; y < TransvoxelModelGeneration.FACE_DATA_DIMENSIONS; y++){
int worldCoordOffset1 = (x * higherResSpacing) / ChunkData.CHUNK_SIZE;
int worldCoordOffset2 = (y * higherResSpacing) / ChunkData.CHUNK_SIZE;
//solve coordinates relative to the face
int localCoord1 = (x * higherResSpacing) % ChunkData.CHUNK_SIZE;
int localCoord2 = (y * higherResSpacing) % ChunkData.CHUNK_SIZE;
//implicitly performing transforms to adapt from face-space to world & local space
switch(higherLODFace){
case X_POSITIVE: {
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
worldPos.x + (17 * mainSpacing) / ChunkData.CHUNK_SIZE,
worldPos.y + worldCoordOffset1,
worldPos.z + worldCoordOffset2
));
if(currentChunk == null){
return false;
}
faceWeights[x][y] = currentChunk.getWeight(
0,
localCoord1,
localCoord2
);
faceTypes[x][y] = currentChunk.getType(
0,
localCoord1,
localCoord2
);
} break;
case X_NEGATIVE: {
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
worldPos.x,
worldPos.y + worldCoordOffset1,
worldPos.z + worldCoordOffset2
));
if(currentChunk == null){
return false;
}
faceWeights[x][y] = currentChunk.getWeight(
0,
localCoord1,
localCoord2
);
faceTypes[x][y] = currentChunk.getType(
0,
localCoord1,
localCoord2
);
} break;
case Y_POSITIVE: {
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
worldPos.x + worldCoordOffset1,
worldPos.y + (17 * mainSpacing) / ChunkData.CHUNK_SIZE,
worldPos.z + worldCoordOffset2
));
if(currentChunk == null){
return false;
}
faceWeights[x][y] = currentChunk.getWeight(
localCoord1,
0,
localCoord2
);
faceTypes[x][y] = currentChunk.getType(
localCoord1,
0,
localCoord2
);
} break;
case Y_NEGATIVE: {
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
worldPos.x + worldCoordOffset1,
worldPos.y,
worldPos.z + worldCoordOffset2
));
if(currentChunk == null){
return false;
}
faceWeights[x][y] = currentChunk.getWeight(
localCoord1,
0,
localCoord2
);
faceTypes[x][y] = currentChunk.getType(
localCoord1,
0,
localCoord2
);
} break;
case Z_POSITIVE: {
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
worldPos.x + worldCoordOffset1,
worldPos.y + worldCoordOffset2,
worldPos.z + (17 * mainSpacing) / ChunkData.CHUNK_SIZE
));
if(currentChunk == null){
return false;
}
faceWeights[x][y] = currentChunk.getWeight(
localCoord1,
localCoord2,
0
);
faceTypes[x][y] = currentChunk.getType(
localCoord1,
localCoord2,
0
);
} break;
case Z_NEGATIVE: {
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(
worldPos.x + worldCoordOffset1,
worldPos.y + worldCoordOffset2,
worldPos.z
));
if(currentChunk == null){
return false;
}
faceWeights[x][y] = currentChunk.getWeight(
localCoord1,
localCoord2,
0
);
faceTypes[x][y] = currentChunk.getType(
localCoord1,
localCoord2,
0
);
} break;
}
// Vector3i sampleChunkWorldPos = new Vector3i(
// worldPos.x + (x * higherResSpacing) / ChunkData.CHUNK_SIZE,
// worldPos.y + (y * higherResSpacing) / ChunkData.CHUNK_SIZE,
// worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE
// );
// ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(sampleChunkWorldPos);
// if(currentChunk == null){
// throw new Error("Chunk is null! " + worldPos);
// }
// weights[x][y][z] = currentChunk.getWeight(
// (x * higherResSpacing) % ChunkData.CHUNK_SIZE,
// (y * higherResSpacing) % ChunkData.CHUNK_SIZE,
// (z * spacingFactor) % ChunkData.CHUNK_SIZE
// );
// types[x][y][z] = currentChunk.getType(
// (x * higherResSpacing) % ChunkData.CHUNK_SIZE,
// (y * higherResSpacing) % ChunkData.CHUNK_SIZE,
// (z * spacingFactor) % ChunkData.CHUNK_SIZE
// );
}
}
switch(higherLODFace){
case X_POSITIVE: {
chunkData.addXPositiveEdge(faceWeights, faceTypes);
} break;
case X_NEGATIVE: {
chunkData.addXNegativeEdge(faceWeights, faceTypes);
} break;
case Y_POSITIVE: {
chunkData.addYPositiveEdge(faceWeights, faceTypes);
} break;
case Y_NEGATIVE: {
chunkData.addYNegativeEdge(faceWeights, faceTypes);
} break;
case Z_POSITIVE: {
chunkData.addZPositiveEdge(faceWeights, faceTypes);
} break;
case Z_NEGATIVE: {
chunkData.addZNegativeEdge(faceWeights, faceTypes);
} break;
}
return true;
}
/**
* Gets the LOD level
* @return The LOD level
*/
public int getLodLevel() {
return lodLevel;
}
/**
* Sets the LOD level
* @param lodLevel The LOD level
*/
public void setLodLevel(int lodLevel) {
this.lodLevel = lodLevel;
}
/**
* Gets whether this draw cell has requested its chunk data or not
* @return true if has requested, false otherwise
*/
public boolean hasRequested() {
return hasRequested;
}
/**
* Sets whether this draw cell has requested its chunk data or not
* @param hasRequested true if has requested, false otherwise
*/
public void setHasRequested(boolean hasRequested) {
this.hasRequested = hasRequested;
}
/**
* Gets whether this draw cell has generated its entity or not
* @return true if has generated, false otherwise
*/
public boolean hasGenerated() {
return hasGenerated;
}
/**
* Sets whether this draw cell has generated its entity or not
* @param hasGenerated true if has generated, false otherwise
*/
public void setHasGenerated(boolean hasGenerated) {
this.hasGenerated = hasGenerated;
}
/**
* Gets whether this draw cell will generate polygons or not
* @return true if it has polygons, false otherwise
*/
private boolean hasPolygons(){
return this.multiVoxelType;
}
} }

View File

@ -2,6 +2,7 @@ package electrosphere.client.terrain.cells;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -18,6 +19,7 @@ import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.renderer.shader.ShaderProgram; import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainChunk;
@Deprecated
/** /**
* Manages the graphical entities for the terrain chunks * Manages the graphical entities for the terrain chunks
* *
@ -235,7 +237,7 @@ public class DrawCellManager {
); );
cells.add(cell); cells.add(cell);
keyCellMap.put(targetKey,cell); keyCellMap.put(targetKey,cell);
DrawCellFace higherLODFace = null; List<DrawCellFace> higherLODFace = null;
keyCellMap.get(targetKey).generateDrawableEntity(atlas,0,higherLODFace); keyCellMap.get(targetKey).generateDrawableEntity(atlas,0,higherLODFace);
//evaluate for foliage //evaluate for foliage
@ -261,7 +263,7 @@ public class DrawCellManager {
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize() worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
){ ){
keyCellMap.get(targetKey).destroy(); keyCellMap.get(targetKey).destroy();
DrawCellFace higherLODFace = null; List<DrawCellFace> higherLODFace = null;
keyCellMap.get(targetKey).generateDrawableEntity(atlas,0,higherLODFace); keyCellMap.get(targetKey).generateDrawableEntity(atlas,0,higherLODFace);
} }
drawable.add(targetKey); drawable.add(targetKey);
@ -304,7 +306,7 @@ public class DrawCellManager {
* @return the cell coordinate * @return the cell coordinate
*/ */
public int transformRealSpaceToCellSpace(double input){ public int transformRealSpaceToCellSpace(double input){
return (int)(input / Globals.clientWorldData.getDynamicInterpolationRatio()); return (int)(input / ServerTerrainChunk.CHUNK_DIMENSION);
} }
/** /**

View File

@ -8,12 +8,14 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Semaphore;
import org.joml.Vector3i; import org.joml.Vector3i;
import electrosphere.client.scene.ClientWorldData; import electrosphere.client.scene.ClientWorldData;
import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.client.terrain.cache.ClientTerrainCache; import electrosphere.client.terrain.cache.ClientTerrainCache;
import electrosphere.client.terrain.cells.ClientDrawCellManager;
import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.client.terrain.cells.VoxelTextureAtlas;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.types.terrain.TerrainChunkData; import electrosphere.entity.types.terrain.TerrainChunkData;
@ -21,6 +23,7 @@ import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.renderer.meshgen.TerrainChunkModelGeneration; import electrosphere.renderer.meshgen.TerrainChunkModelGeneration;
import electrosphere.renderer.model.Model; import electrosphere.renderer.model.Model;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.manager.ServerTerrainManager;
/** /**
@ -30,12 +33,22 @@ public class ClientTerrainManager {
//queues messages from server //queues messages from server
List<TerrainMessage> messageQueue = new CopyOnWriteArrayList<TerrainMessage>(); List<TerrainMessage> messageQueue = new CopyOnWriteArrayList<TerrainMessage>();
/**
* Locks the terrain manager (eg if adding message from network)
*/
static Semaphore lock = new Semaphore(1);
//The interpolation ratio of terrain //The interpolation ratio of terrain
public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO; public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO;
//caches chunks from server //caches chunks from server
static final int CACHE_SIZE = 500; static final int CACHE_SIZE = 1500 + (int)(ClientDrawCellManager.FULL_RES_DIST * 10) + (int)(ClientDrawCellManager.HALF_RES_DIST * 10);
/**
* Size of the cache in bytes
*/
static final int CACHE_SIZE_IN_MB = (CACHE_SIZE * ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION * 2 * 4) / 1024 / 1024;
//used for caching the macro values //used for caching the macro values
ClientTerrainCache terrainCache; ClientTerrainCache terrainCache;
@ -58,6 +71,8 @@ public class ClientTerrainManager {
* Handles messages that have been received from the server * Handles messages that have been received from the server
*/ */
public void handleMessages(){ public void handleMessages(){
Globals.profiler.beginCpuSample("ClientTerrainManager.handleMessages");
lock.acquireUninterruptibly();
List<TerrainMessage> bouncedMessages = new LinkedList<TerrainMessage>(); List<TerrainMessage> bouncedMessages = new LinkedList<TerrainMessage>();
for(TerrainMessage message : messageQueue){ for(TerrainMessage message : messageQueue){
messageQueue.remove(message); messageQueue.remove(message);
@ -83,7 +98,7 @@ public class ClientTerrainManager {
} }
} }
} }
ChunkData data = new ChunkData(); ChunkData data = new ChunkData(message.getworldX(), message.getworldY(), message.getworldZ());
data.setVoxelType(values); data.setVoxelType(values);
data.setVoxelWeight(weights); data.setVoxelWeight(weights);
terrainCache.addChunkDataToCache( terrainCache.addChunkDataToCache(
@ -99,6 +114,8 @@ public class ClientTerrainManager {
for(TerrainMessage message : bouncedMessages){ for(TerrainMessage message : bouncedMessages){
messageQueue.add(message); messageQueue.add(message);
} }
lock.release();
Globals.profiler.endCpuSample();
} }
/** /**
@ -113,7 +130,9 @@ public class ClientTerrainManager {
* @param message The message * @param message The message
*/ */
public void attachTerrainMessage(TerrainMessage message){ public void attachTerrainMessage(TerrainMessage message){
lock.acquireUninterruptibly();
messageQueue.add(message); messageQueue.add(message);
lock.release();
} }
/** /**
@ -135,6 +154,22 @@ public class ClientTerrainManager {
public boolean containsChunkDataAtWorldPoint(Vector3i worldPos){ public boolean containsChunkDataAtWorldPoint(Vector3i worldPos){
return terrainCache.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z); return terrainCache.containsChunkDataAtWorldPoint(worldPos.x, worldPos.y, worldPos.z);
} }
/**
* Requests a chunk from the server
* @param worldX the world x coordinate of the chunk
* @param worldY the world y coordinate of the chunk
* @param worldZ the world z coordinate of the chunk
*/
public void requestChunk(int worldX, int worldY, int worldZ){
if(!this.terrainCache.hasRequested(worldX, worldY, worldZ)){
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestChunkDataMessage(
worldX,
worldY,
worldZ
));
}
}
/** /**
* Checks that the cache contains chunk data at a real-space coordinate * Checks that the cache contains chunk data at a real-space coordinate
@ -184,7 +219,9 @@ public class ClientTerrainManager {
UUID newUUID = UUID.randomUUID(); UUID newUUID = UUID.randomUUID();
promisedHash = newUUID.toString(); promisedHash = newUUID.toString();
TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash, atlas); TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash, atlas);
lock.acquireUninterruptibly();
terrainChunkGenerationQueue.add(queueItem); terrainChunkGenerationQueue.add(queueItem);
lock.release();
return promisedHash; return promisedHash;
} }
@ -192,12 +229,14 @@ public class ClientTerrainManager {
* Pushes all terrain data in queue to the gpu and registers the resulting models * Pushes all terrain data in queue to the gpu and registers the resulting models
*/ */
public static void generateTerrainChunkGeometry(){ public static void generateTerrainChunkGeometry(){
Globals.profiler.beginCpuSample("generateTerrainChunkGeometry"); Globals.profiler.beginCpuSample("ClientTerrainManager.generateTerrainChunkGeometry");
lock.acquireUninterruptibly();
for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){ for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){
Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas()); Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas());
Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash()); Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash());
} }
terrainChunkGenerationQueue.clear(); terrainChunkGenerationQueue.clear();
lock.release();
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} }
@ -217,5 +256,13 @@ public class ClientTerrainManager {
public Vector3i getPositionOfChunk(ChunkData chunk){ public Vector3i getPositionOfChunk(ChunkData chunk){
return terrainCache.getChunkPosition(chunk); return terrainCache.getChunkPosition(chunk);
} }
/**
* Gets the number of chunks that have been requested
* @return The number of chunks
*/
public int getRequestedCellCount(){
return this.terrainCache.getRequestedCellCount();
}
} }

View File

@ -184,6 +184,9 @@ public class WindowUtils {
initTooltipWindow(); initTooltipWindow();
} }
/**
* Inits the loading window
*/
static void initLoadingWindow(){ static void initLoadingWindow(){
Window loadingWindow = Window.create(Globals.renderingEngine.getOpenGLState(), 0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, false); Window loadingWindow = Window.create(Globals.renderingEngine.getOpenGLState(), 0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, false);
Label loadingLabel = Label.createLabel("LOADING"); Label loadingLabel = Label.createLabel("LOADING");
@ -208,6 +211,18 @@ public class WindowUtils {
Globals.elementService.registerWindow(WindowStrings.WINDOW_ITEM_DRAG_CONTAINER, itemDragContainerWindow); Globals.elementService.registerWindow(WindowStrings.WINDOW_ITEM_DRAG_CONTAINER, itemDragContainerWindow);
} }
/**
* Updates the loading window
*/
public static void updateLoadingWindow(String message){
Globals.signalSystem.post(SignalType.UI_MODIFICATION,() -> {
Window loadingWindow = (Window)Globals.elementService.getWindow(WindowStrings.WINDOW_LOADING);
loadingWindow.clear();
loadingWindow.addChild(Label.createLabel(message));
Globals.signalSystem.post(SignalType.YOGA_APPLY,loadingWindow);
});
}
/** /**
* Creates the tooltip window * Creates the tooltip window
*/ */

View File

@ -0,0 +1,87 @@
package electrosphere.client.ui.menu.debug;
import electrosphere.engine.Globals;
import electrosphere.renderer.ui.imgui.ImGuiLinePlot;
import electrosphere.renderer.ui.imgui.ImGuiLinePlot.ImGuiLinePlotDataset;
import electrosphere.renderer.ui.imgui.ImGuiWindow;
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
import imgui.ImGui;
public class ImGuiChunkMonitor {
/**
* Num datapoints
*/
public static final int PRESSURE_GRAPH_POINT_COUNT = 100;
/**
* Window for viewing chunk status on server and client
*/
protected static ImGuiWindow chunkMonitorWindow;
/**
* Creates the windows in this file
*/
protected static void createChunkMonitorWindows(){
createChunkMonitorWindow();
}
/**
* Client scene entity view
*/
protected static void createChunkMonitorWindow(){
chunkMonitorWindow = new ImGuiWindow("Chunk Monitor");
//client network pressure graph
ImGuiLinePlot clientNetworkBandwith = new ImGuiLinePlot("Client Network Pressure",400,400);
ImGuiLinePlotDataset clientPressureDataset = new ImGuiLinePlotDataset("Client bytes per frame", PRESSURE_GRAPH_POINT_COUNT);
clientPressureDataset.zeroOut();
clientNetworkBandwith.addDataset(clientPressureDataset);
//server network pressure graph
ImGuiLinePlot serverNetworkPressureGraph = new ImGuiLinePlot("Server Network Pressure",400,400);
ImGuiLinePlotDataset serverPressureDataset = new ImGuiLinePlotDataset("Server bytes per frame", PRESSURE_GRAPH_POINT_COUNT);
serverPressureDataset.zeroOut();
serverNetworkPressureGraph.addDataset(serverPressureDataset);
chunkMonitorWindow.setCallback(new ImGuiWindowCallback() {
long clientPressureLastValue = 0;
long serverPressureLastValue = 0;
@Override
public void exec() {
//ui framework text
ImGui.text("Chunk Monitor");
Globals.clientDrawCellManager.updateStatus();
ImGui.text("Renderable chunks: " + Globals.clientDrawCellManager.getGenerated());
ImGui.text("Full res chunks: " + Globals.clientDrawCellManager.getMaxResCount());
ImGui.text("Half res chunks: " + Globals.clientDrawCellManager.getHalfResCount());
//client network pressure
if(Globals.clientConnection != null){
long clientPressureNewTotal = Globals.clientConnection.getNumBytesRead();
long clientPressureDelta = clientPressureNewTotal - clientPressureLastValue;
clientPressureDataset.addPoint(clientPressureDelta);
clientPressureLastValue = clientPressureNewTotal;
}
clientNetworkBandwith.draw();
//server network pressure
if(Globals.server != null && Globals.server.getFirstConnection() != null){
long serverPressureNewTotal = Globals.server.getFirstConnection().getNumBytesRead();
long serverPressureDelta = serverPressureNewTotal - serverPressureLastValue;
serverPressureDataset.addPoint(serverPressureDelta);
serverPressureLastValue = serverPressureNewTotal;
}
serverNetworkPressureGraph.draw();
}
});
chunkMonitorWindow.setOpen(false);
Globals.renderingEngine.getImGuiPipeline().addImGuiWindow(chunkMonitorWindow);
}
}

View File

@ -47,7 +47,7 @@ public class ImGuiTestGen {
if(ImGui.button("Regenerate")){ if(ImGui.button("Regenerate")){
GriddedDataCellManager gridManager = (GriddedDataCellManager)Globals.realmManager.first().getDataCellManager(); GriddedDataCellManager gridManager = (GriddedDataCellManager)Globals.realmManager.first().getDataCellManager();
gridManager.evictAll(); gridManager.evictAll();
Globals.drawCellManager.evictAll(); Globals.clientDrawCellManager.evictAll();
Globals.clientTerrainManager.evictAll(); Globals.clientTerrainManager.evictAll();
} }
@ -80,6 +80,11 @@ public class ImGuiTestGen {
if(ImGui.inputInt("biome[1][1]", biome11)){ if(ImGui.inputInt("biome[1][1]", biome11)){
terrainModel.getBiome()[1][1] = biome11.shortValue(); terrainModel.getBiome()[1][1] = biome11.shortValue();
} }
//Toggles whether the client draws cell manager should update or not
if(ImGui.button("Toggle ClientDrawCellManager updates")){
Globals.clientDrawCellManager.setShouldUpdate(!Globals.clientDrawCellManager.getShouldUpdate());
}
} }
}); });

View File

@ -51,6 +51,7 @@ public class ImGuiWindowMacros {
ImGuiLogger.createLoggersWindows(); ImGuiLogger.createLoggersWindows();
ImGuiRenderer.createRendererWindows(); ImGuiRenderer.createRendererWindows();
ImGuiTestGen.createTestGenWindows(); ImGuiTestGen.createTestGenWindows();
ImGuiChunkMonitor.createChunkMonitorWindows();
} }
/** /**
@ -183,6 +184,10 @@ public class ImGuiWindowMacros {
){ ){
ImGuiTestGen.testGenWindow.setOpen(true); ImGuiTestGen.testGenWindow.setOpen(true);
} }
//chunk monitor
if(ImGui.button("Chunk Monitor")){
ImGuiChunkMonitor.chunkMonitorWindow.setOpen(true);
}
//close button //close button
if(ImGui.button("Close")){ if(ImGui.button("Close")){
mainDebugWindow.setOpen(false); mainDebugWindow.setOpen(false);

View File

@ -724,7 +724,9 @@ public class CollisionEngine {
DBody rigidBody = PhysicsEntityUtils.getDBody(e); DBody rigidBody = PhysicsEntityUtils.getDBody(e);
deregisterCollisionObject(rigidBody,PhysicsEntityUtils.getCollidable(e)); deregisterCollisionObject(rigidBody,PhysicsEntityUtils.getCollidable(e));
e.removeData(EntityDataStrings.PHYSICS_COLLISION_BODY); e.removeData(EntityDataStrings.PHYSICS_COLLISION_BODY);
deregisterPhysicsObject(rigidBody); if(rigidBody != null){
deregisterPhysicsObject(rigidBody);
}
} }
if(ServerPhysicsSyncTree.hasTree(e)){ if(ServerPhysicsSyncTree.hasTree(e)){
ServerPhysicsSyncTree.detachTree(e, ServerPhysicsSyncTree.getTree(e)); ServerPhysicsSyncTree.detachTree(e, ServerPhysicsSyncTree.getTree(e));

View File

@ -62,7 +62,7 @@ public class CollisionWorldData {
public int getDynamicInterpolationRatio(){ public int getDynamicInterpolationRatio(){
if(clientWorldData != null){ if(clientWorldData != null){
return clientWorldData.getDynamicInterpolationRatio(); return clientWorldData.getWorldDiscreteSize();
} else { } else {
return serverWorldData.getDynamicInterpolationRatio(); return serverWorldData.getDynamicInterpolationRatio();
} }

View File

@ -21,7 +21,7 @@ import electrosphere.client.player.ClientPlayerData;
import electrosphere.client.scene.ClientSceneWrapper; import electrosphere.client.scene.ClientSceneWrapper;
import electrosphere.client.scene.ClientWorldData; import electrosphere.client.scene.ClientWorldData;
import electrosphere.client.sim.ClientSimulation; import electrosphere.client.sim.ClientSimulation;
import electrosphere.client.terrain.cells.DrawCellManager; import electrosphere.client.terrain.cells.ClientDrawCellManager;
import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.client.terrain.cells.VoxelTextureAtlas;
import electrosphere.client.terrain.manager.ClientTerrainManager; import electrosphere.client.terrain.manager.ClientTerrainManager;
import electrosphere.client.ui.menu.WindowUtils; import electrosphere.client.ui.menu.WindowUtils;
@ -235,7 +235,7 @@ public class Globals {
// //
//Generic OpenGL Statements //Generic OpenGL Statements
// //
public static long window; public static long window = -1;
@ -351,7 +351,7 @@ public class Globals {
//chunk stuff //chunk stuff
//draw cell manager //draw cell manager
public static DrawCellManager drawCellManager; public static ClientDrawCellManager clientDrawCellManager;
public static VoxelTextureAtlas voxelTextureAtlas = new VoxelTextureAtlas(); public static VoxelTextureAtlas voxelTextureAtlas = new VoxelTextureAtlas();
//fluid cell manager //fluid cell manager
@ -696,6 +696,7 @@ public class Globals {
Globals.realmManager = null; Globals.realmManager = null;
Globals.clientSceneWrapper = null; Globals.clientSceneWrapper = null;
Globals.clientScene = null; Globals.clientScene = null;
Globals.clientWorldData = null;
Globals.audioEngine = null; Globals.audioEngine = null;
Globals.renderingEngine = null; Globals.renderingEngine = null;
Globals.threadManager = null; Globals.threadManager = null;

View File

@ -420,14 +420,7 @@ public class Main {
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} catch (NullPointerException ex){ } catch (NullPointerException ex){
LoggerInterface.loggerEngine.ERROR("Main frame uncaught NPE", ex); LoggerInterface.loggerEngine.ERROR(ex);
//after a while, jvm will stop reporting stack traces with errors
//need to explicitly kill the vm if you want to see the stack trace
if(Globals.ENGINE_DEBUG){
System.exit(1);
} else {
throw new Error("NPE!");
}
} }
} }

View File

@ -10,7 +10,7 @@ import electrosphere.client.entity.crosshair.Crosshair;
import electrosphere.client.fluid.cells.FluidCellManager; import electrosphere.client.fluid.cells.FluidCellManager;
import electrosphere.client.foliagemanager.ClientFoliageManager; import electrosphere.client.foliagemanager.ClientFoliageManager;
import electrosphere.client.sim.ClientSimulation; import electrosphere.client.sim.ClientSimulation;
import electrosphere.client.terrain.cells.DrawCellManager; import electrosphere.client.terrain.cells.ClientDrawCellManager;
import electrosphere.client.ui.menu.MenuGenerators; import electrosphere.client.ui.menu.MenuGenerators;
import electrosphere.client.ui.menu.WindowStrings; import electrosphere.client.ui.menu.WindowStrings;
import electrosphere.client.ui.menu.WindowUtils; import electrosphere.client.ui.menu.WindowUtils;
@ -29,16 +29,27 @@ import electrosphere.net.NetUtils;
import electrosphere.net.client.ClientNetworking; import electrosphere.net.client.ClientNetworking;
import electrosphere.renderer.actor.Actor; import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.actor.ActorTextureMask; import electrosphere.renderer.actor.ActorTextureMask;
import electrosphere.renderer.ui.elements.Window;
public class ClientLoading { public class ClientLoading {
/**
* Number of frames to wait before updating status of draw cell manager loading
*/
static final int DRAW_CELL_UPDATE_RATE = 60;
/**
* The number of frames the draw cell is expected to take (minimum) to init
*/
static final int DRAW_CELL_EXPECTED_MINIMUM_FRAMES_TO_INIT = 100;
protected static void loadCharacterServer(Object[] params){ protected static void loadCharacterServer(Object[] params){
Window loadingWindow = (Window)Globals.elementService.getWindow(WindowStrings.WINDOW_LOADING);
WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_MENU_MAIN), false); WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_MENU_MAIN), false);
WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu()); WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu());
loadingWindow.setVisible(true); WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_LOADING), true);
WindowUtils.updateLoadingWindow("Waiting on server");
//disable menu input //disable menu input
Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT); Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT);
//initialize the client thread (client) //initialize the client thread (client)
@ -54,7 +65,7 @@ public class ClientLoading {
//eventually should replace with at ui to select an already created character or create a new one //eventually should replace with at ui to select an already created character or create a new one
WindowUtils.replaceMainMenuContents(MenuGeneratorsMultiplayer.createMultiplayerCharacterCreationWindow()); WindowUtils.replaceMainMenuContents(MenuGeneratorsMultiplayer.createMultiplayerCharacterCreationWindow());
//make loading dialog disappear //make loading dialog disappear
loadingWindow.setVisible(false); WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_LOADING), false);
//make character creation window visible //make character creation window visible
WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_MENU_MAIN), true); WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_MENU_MAIN), true);
//recapture window //recapture window
@ -70,6 +81,7 @@ public class ClientLoading {
Globals.signalSystem.post(SignalType.UI_MODIFICATION, () -> { Globals.signalSystem.post(SignalType.UI_MODIFICATION, () -> {
WindowUtils.closeWindow(WindowStrings.WINDOW_MENU_MAIN); WindowUtils.closeWindow(WindowStrings.WINDOW_MENU_MAIN);
WindowUtils.recursiveSetVisible(WindowStrings.WINDOW_LOADING, true); WindowUtils.recursiveSetVisible(WindowStrings.WINDOW_LOADING, true);
WindowUtils.updateLoadingWindow("LOADING");
}); });
//disable menu input //disable menu input
Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT); Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT);
@ -263,7 +275,8 @@ public class ClientLoading {
*/ */
static void initDrawCellManager(boolean blockForInit){ static void initDrawCellManager(boolean blockForInit){
int iterations = 0; int iterations = 0;
while(blockForInit && (Globals.clientWorldData == null || InitialAssetLoading.atlasQueuedTexture == null || !InitialAssetLoading.atlasQueuedTexture.hasLoaded())){ WindowUtils.updateLoadingWindow("WAITING ON WORLD DATA");
while(blockForInit && (Globals.clientWorldData == null || InitialAssetLoading.atlasQueuedTexture == null || !InitialAssetLoading.atlasQueuedTexture.hasLoaded()) && Globals.threadManager.shouldKeepRunning()){
try { try {
TimeUnit.MILLISECONDS.sleep(10); TimeUnit.MILLISECONDS.sleep(10);
iterations++; iterations++;
@ -278,28 +291,25 @@ public class ClientLoading {
} }
} }
//initialize draw cell manager //initialize draw cell manager
Globals.drawCellManager = new DrawCellManager(Globals.clientTerrainManager, 0, 0, 0); // Globals.drawCellManager = new DrawCellManager(Globals.clientTerrainManager, 0, 0, 0);
//construct texture atlas Globals.clientDrawCellManager = new ClientDrawCellManager(Globals.voxelTextureAtlas, Globals.clientWorldData.getWorldDiscreteSize());
Globals.drawCellManager.attachTextureAtlas(Globals.voxelTextureAtlas);
//set our draw cell manager to actually generate drawable chunks
Globals.drawCellManager.setGenerateDrawables(true);
//Alerts the client simulation that it should start loading terrain //Alerts the client simulation that it should start loading terrain
Globals.clientSimulation.setLoadingTerrain(true); Globals.clientSimulation.setLoadingTerrain(true);
//wait for all the terrain data to arrive //wait for all the terrain data to arrive
while(blockForInit && Globals.drawCellManager.containsUnrequestedCell()){ int i = 0;
while(blockForInit && Globals.clientDrawCellManager.updatedLastFrame() && Globals.threadManager.shouldKeepRunning()){
i++;
if(i % DRAW_CELL_UPDATE_RATE == 0){
WindowUtils.updateLoadingWindow("WAITING ON SERVER TO SEND TERRAIN (" + Globals.clientTerrainManager.getAllChunks().size() + "/" + Globals.clientTerrainManager.getRequestedCellCount() + ")");
}
try { try {
TimeUnit.MILLISECONDS.sleep(10); TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
if(i < DRAW_CELL_EXPECTED_MINIMUM_FRAMES_TO_INIT){
while(blockForInit && Globals.drawCellManager.containsUndrawableCell()){ LoggerInterface.loggerEngine.WARNING("Probably didn't block for draw cell manager initialization!");
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
} }
} }
@ -310,7 +320,8 @@ public class ClientLoading {
static void initFluidCellManager(boolean blockForInit){ static void initFluidCellManager(boolean blockForInit){
//wait for world data //wait for world data
while(blockForInit && Globals.clientWorldData == null){ WindowUtils.updateLoadingWindow("WAITING ON WORLD DATA");
while(blockForInit && Globals.clientWorldData == null && Globals.threadManager.shouldKeepRunning()){
try { try {
TimeUnit.MILLISECONDS.sleep(10); TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
@ -323,7 +334,8 @@ public class ClientLoading {
Globals.clientSimulation.setLoadingTerrain(true); Globals.clientSimulation.setLoadingTerrain(true);
//wait for all the terrain data to arrive //wait for all the terrain data to arrive
while(blockForInit && Globals.fluidCellManager.containsUnrequestedCell()){ WindowUtils.updateLoadingWindow("REQUESTING FLUID CHUNKS FROM SERVER (" + Globals.fluidCellManager.getUnrequestedSize() + ")");
while(blockForInit && Globals.fluidCellManager.containsUnrequestedCell() && Globals.threadManager.shouldKeepRunning()){
try { try {
TimeUnit.MILLISECONDS.sleep(10); TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
@ -332,7 +344,8 @@ public class ClientLoading {
} }
//wait for undrawable cells //wait for undrawable cells
while(blockForInit && Globals.fluidCellManager.containsUndrawableCell()){ WindowUtils.updateLoadingWindow("WAITING ON SERVER TO SEND FLUID CHUNKS (" + Globals.fluidCellManager.getUndrawableSize() + ")");
while(blockForInit && Globals.fluidCellManager.containsUndrawableCell() && Globals.threadManager.shouldKeepRunning()){
try { try {
TimeUnit.MILLISECONDS.sleep(10); TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {

View File

@ -17,7 +17,6 @@ import electrosphere.entity.types.creature.CreatureTemplate;
import electrosphere.entity.types.creature.CreatureToolbarData.ToolbarItem; import electrosphere.entity.types.creature.CreatureToolbarData.ToolbarItem;
import electrosphere.game.data.creature.type.CreatureData; import electrosphere.game.data.creature.type.CreatureData;
import electrosphere.game.data.creature.type.visualattribute.VisualAttribute; import electrosphere.game.data.creature.type.visualattribute.VisualAttribute;
import electrosphere.game.server.world.MacroData;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.NetUtils; import electrosphere.net.NetUtils;
import electrosphere.net.client.ClientNetworking; import electrosphere.net.client.ClientNetworking;
@ -26,41 +25,17 @@ import electrosphere.net.server.Server;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
import electrosphere.server.simulation.MacroSimulation;
import electrosphere.server.simulation.MicroSimulation; import electrosphere.server.simulation.MicroSimulation;
/** /**
* Utilities for all loading thread types * Utilities for all loading thread types
*/ */
public class LoadingUtils { public class LoadingUtils {
/**
// static void initCommonWorldData(boolean FLAG_INIT_SERVER){ * The size of the buffer
// if(Globals.commonWorldData == null){ */
// if(FLAG_INIT_SERVER){ static final int STREAM_BUFFER_SIZE = 16 * 1024 * 1024;
// Globals.commonWorldData = new CommonWorldData(Globals.serverWorldData, Globals.serverTerrainManager);
// if(Globals.macroSimulation != null){
// Town startTown = Globals.macroData.getTowns().get(0);
// Vector2i firstPos = startTown.getPositions().get(0);
// // double startX = firstPos.x * Globals.serverTerrainManager.getChunkWidth();
// // double startZ = firstPos.y * Globals.serverTerrainManager.getChunkWidth();
// double startX = Globals.commonWorldData.convertWorldToReal(firstPos.x);
// double startZ = Globals.commonWorldData.convertWorldToReal(firstPos.y);
// Globals.spawnPoint.set((float)startX,(float)Globals.commonWorldData.getElevationAtPoint(new Vector3d(startX,0,startZ)),(float)startZ);
// }
// } else {
// //basically wait for the client to receive the world metadata
// while(!Globals.clientConnection.getClientProtocol().hasReceivedWorld()){
// try {
// TimeUnit.MILLISECONDS.sleep(5);
// } catch (InterruptedException ex) {
// }
// }
// //then create common world data
// Globals.commonWorldData = new CommonWorldData(Globals.clientWorldData, Globals.clientTerrainManager);
// }
// }
// }
@ -93,7 +68,12 @@ public class LoadingUtils {
} }
} }
static final int STREAM_BUFFER_SIZE = 32 * 1024 * 1024;
/**
* Initializes a local connection
* @param runServerThread true if should run the server in a separate thread, false otherwise
* @return The server connection
*/
static ServerConnectionHandler initLocalConnection(boolean runServerThread){ static ServerConnectionHandler initLocalConnection(boolean runServerThread){
ServerConnectionHandler rVal = null; ServerConnectionHandler rVal = null;
try { try {
@ -106,7 +86,7 @@ public class LoadingUtils {
PipedInputStream clientInput = new PipedInputStream(STREAM_BUFFER_SIZE); PipedInputStream clientInput = new PipedInputStream(STREAM_BUFFER_SIZE);
PipedOutputStream serverOutput = new PipedOutputStream(clientInput); PipedOutputStream serverOutput = new PipedOutputStream(clientInput);
//server -> client pipe //server -> client pipe
PipedInputStream serverInput = new PipedInputStream(); PipedInputStream serverInput = new PipedInputStream(STREAM_BUFFER_SIZE);
PipedOutputStream clientOutput; PipedOutputStream clientOutput;
clientOutput = new PipedOutputStream(serverInput); clientOutput = new PipedOutputStream(serverInput);
//start server communication thread //start server communication thread

View File

@ -29,6 +29,14 @@ public class Signal {
YOGA_DESTROY, YOGA_DESTROY,
UI_MODIFICATION, UI_MODIFICATION,
//
//Terrain
//
REQUEST_CHUNK,
CHUNK_CREATED,
REQUEST_CHUNK_EDIT,
CHUNK_EDITED,
} }
/** /**

View File

@ -9,6 +9,8 @@ import java.util.concurrent.TimeUnit;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.loadingthreads.LoadingThread; import electrosphere.engine.loadingthreads.LoadingThread;
import electrosphere.engine.threads.LabeledThread.ThreadLabel; import electrosphere.engine.threads.LabeledThread.ThreadLabel;
import electrosphere.entity.types.terrain.TerrainChunk;
import electrosphere.server.datacell.Realm;
import electrosphere.util.CodeUtils; import electrosphere.util.CodeUtils;
/** /**
@ -110,6 +112,19 @@ public class ThreadManager {
Globals.server.close(); Globals.server.close();
} }
if(Globals.realmManager.getRealms() != null){
for(Realm realm : Globals.realmManager.getRealms()){
if(realm.getServerWorldData() != null && realm.getServerWorldData().getServerTerrainManager() != null){
realm.getServerWorldData().getServerTerrainManager().closeThreads();
}
}
}
/**
* Halds all terrain chunk threads
*/
TerrainChunk.haltThreads();
// //
//interrupt all threads //interrupt all threads
for(int i = 0; i < 3; i++){ for(int i = 0; i < 3; i++){

View File

@ -129,9 +129,6 @@ public class EntityCreationUtils {
*/ */
public static void makeEntityDrawablePreexistingModel(Entity entity, String modelPath){ public static void makeEntityDrawablePreexistingModel(Entity entity, String modelPath){
entity.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorOfLoadingModel(modelPath)); entity.putData(EntityDataStrings.DATA_STRING_ACTOR, ActorUtils.createActorOfLoadingModel(modelPath));
entity.putData(EntityDataStrings.DATA_STRING_POSITION, new Vector3d(0,0,0));
entity.putData(EntityDataStrings.DATA_STRING_ROTATION, new Quaterniond().identity());
entity.putData(EntityDataStrings.DATA_STRING_SCALE, new Vector3f(1,1,1));
entity.putData(EntityDataStrings.DATA_STRING_DRAW, true); entity.putData(EntityDataStrings.DATA_STRING_DRAW, true);
entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true); entity.putData(EntityDataStrings.DRAW_SOLID_PASS, true);
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAWABLE); Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAWABLE);

View File

@ -127,6 +127,15 @@ public class Scene {
return (Entity)entityIdMap.get(id); return (Entity)entityIdMap.get(id);
} }
/**
* Checks if a scene contains a given entity
* @param e The entity
* @return true if the scene contains the entity, false otherwise
*/
public boolean containsEntity(Entity e){
return entityIdMap.containsKey(e.getId());
}
/** /**
* Registers a behavior tree to simulate each scene simulation frame * Registers a behavior tree to simulate each scene simulation frame
* @param tree The behavior tree to register * @param tree The behavior tree to register

View File

@ -1,11 +1,15 @@
package electrosphere.entity.types.terrain; package electrosphere.entity.types.terrain;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.client.terrain.cells.DrawCell; import electrosphere.client.terrain.cells.DrawCell;
import electrosphere.client.terrain.cells.VoxelTextureAtlas; import electrosphere.client.terrain.cells.VoxelTextureAtlas;
import electrosphere.client.terrain.manager.ClientTerrainManager; import electrosphere.client.terrain.manager.ClientTerrainManager;
import electrosphere.collision.PhysicsEntityUtils; import electrosphere.collision.PhysicsEntityUtils;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
@ -20,32 +24,44 @@ import electrosphere.server.datacell.Realm;
*/ */
public class TerrainChunk { public class TerrainChunk {
/**
* Used for generating terrain chunks
*/
static ExecutorService generationService = Executors.newFixedThreadPool(2);
/** /**
* Creates a client terrain chunk based on weights and values provided * Creates a client terrain chunk based on weights and values provided
* @param chunkData the chunk data to generate with * @param chunkData the chunk data to generate with
* @param levelOfDetail Increasing value that increments level of detail. 0 would be full resolution, 1 would be half resolution and so on. Only generates physics if levelOfDetail is 0 * @param levelOfDetail Increasing value that increments level of detail. 0 would be full resolution, 1 would be half resolution and so on. Only generates physics if levelOfDetail is 0
* @param hasPolygons true if the chunk has polygons to generate a model with, false otherwise
* @return The terrain chunk entity * @return The terrain chunk entity
*/ */
public static Entity clientCreateTerrainChunkEntity(TransvoxelChunkData chunkData, int levelOfDetail, VoxelTextureAtlas atlas){ public static Entity clientCreateTerrainChunkEntity(TransvoxelChunkData chunkData, int levelOfDetail, VoxelTextureAtlas atlas, boolean hasPolygons){
Globals.profiler.beginCpuSample("TerrainChunk.clientCreateTerrainChunkEntity");
TerrainChunkData data;
if(levelOfDetail == DrawCell.FULL_DETAIL_LOD){
data = TerrainChunkModelGeneration.generateTerrainChunkData(chunkData.terrainGrid, chunkData.textureGrid, levelOfDetail);
} else {
data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData);
}
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas);
Entity rVal = EntityCreationUtils.createClientSpatialEntity(); Entity rVal = EntityCreationUtils.createClientSpatialEntity();
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
if(data.vertices.size() > 0 && levelOfDetail == DrawCell.FULL_DETAIL_LOD){ if(hasPolygons){
PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data); generationService.submit(() -> {
TerrainChunkData data;
if(levelOfDetail == DrawCell.FULL_DETAIL_LOD){
data = TerrainChunkModelGeneration.generateTerrainChunkData(chunkData.terrainGrid, chunkData.textureGrid);
} else {
data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData);
}
if(Globals.clientScene.containsEntity(rVal)){
String modelPath = ClientTerrainManager.queueTerrainGridGeneration(data, atlas);
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
if(levelOfDetail == DrawCell.FULL_DETAIL_LOD){
PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data);
}
}
});
} }
rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true); rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
Globals.profiler.endCpuSample();
return rVal; return rVal;
} }
@ -59,7 +75,7 @@ public class TerrainChunk {
*/ */
public static Entity serverCreateTerrainChunkEntity(Realm realm, Vector3d position, float[][][] weights, int[][][] values){ public static Entity serverCreateTerrainChunkEntity(Realm realm, Vector3d position, float[][][] weights, int[][][] values){
TerrainChunkData data = TerrainChunkModelGeneration.generateTerrainChunkData(weights, values, DrawCell.FULL_DETAIL_LOD); TerrainChunkData data = TerrainChunkModelGeneration.generateTerrainChunkData(weights, values);
Entity rVal = EntityCreationUtils.createServerEntity(realm, position); Entity rVal = EntityCreationUtils.createServerEntity(realm, position);
if(data.vertices.size() > 0){ if(data.vertices.size() > 0){
@ -84,4 +100,11 @@ public class TerrainChunk {
return rVal; return rVal;
} }
/**
* Halts all running generation threads
*/
public static void haltThreads(){
generationService.shutdownNow();
}
} }

View File

@ -18,6 +18,11 @@ import java.util.concurrent.TimeUnit;
* Client networking thread * Client networking thread
*/ */
public class ClientNetworking implements Runnable { public class ClientNetworking implements Runnable {
/**
* Milliseconds after which reading is considered slow enough to be warning-worthy
*/
static final int SOCKET_READ_WARNING_THRESHOLD = 1000;
/** /**
* The server's address * The server's address
@ -62,7 +67,7 @@ public class ClientNetworking implements Runnable {
//thresholds for when to send pings and to determine when we've disconnected //thresholds for when to send pings and to determine when we've disconnected
static final long SEND_PING_THRESHOLD = 3000; static final long SEND_PING_THRESHOLD = 3000;
static final long PING_DISCONNECT_THRESHOLD = 20000; static final long PING_DISCONNECT_THRESHOLD = 60 * 1000;
//times for calculating ping-pong //times for calculating ping-pong
long lastPingTime = 0; long lastPingTime = 0;
long lastPongTime = 0; long lastPongTime = 0;
@ -174,18 +179,43 @@ public class ClientNetworking implements Runnable {
//start parsing messages //start parsing messages
initialized = true; initialized = true;
while(Globals.threadManager.shouldKeepRunning() && !this.shouldDisconnect){ while(Globals.threadManager.shouldKeepRunning() && !this.shouldDisconnect){
//
//attempt poll incoming messages //attempt poll incoming messages
parser.readMessagesIn(); long readStart = System.currentTimeMillis();
try {
parser.readMessagesIn();
} catch (IOException e) {
LoggerInterface.loggerNetworking.ERROR(e);
}
if(System.currentTimeMillis() - readStart > SOCKET_READ_WARNING_THRESHOLD){
LoggerInterface.loggerNetworking.WARNING("Client is slow to read from network! Delay: " + (System.currentTimeMillis() - readStart) + " Number of total bytes read(mb): " + (parser.getNumberOfBytesRead() / 1024 / 1024));
}
//
//outgoing messages //outgoing messages
try { try {
parser.pushMessagesOut(); parser.pushMessagesOut();
} catch(IOException e){ } catch(IOException e){
LoggerInterface.loggerNetworking.ERROR(e); LoggerInterface.loggerNetworking.ERROR(e);
} }
//parses messages asynchronously
this.parseMessagesAsynchronously();
//ping logic
//
//parses messages asynchronously
boolean foundMessages = this.parseMessagesAsynchronously();
//timeout logic
//if received message from server, can't have timed out
if(foundMessages){
this.markReceivedPongMessage();
}
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
//basically if we haven't sent a ping in a while, send one //basically if we haven't sent a ping in a while, send one
if(currentTime - lastPingTime > SEND_PING_THRESHOLD){ if(currentTime - lastPingTime > SEND_PING_THRESHOLD){
@ -228,7 +258,8 @@ public class ClientNetworking implements Runnable {
/** /**
* Parses messages asynchronously * Parses messages asynchronously
*/ */
public void parseMessagesAsynchronously(){ public boolean parseMessagesAsynchronously(){
boolean foundMessages = false;
if(initialized){ if(initialized){
while(parser.hasIncomingMessaage()){ while(parser.hasIncomingMessaage()){
NetworkMessage message = parser.popIncomingMessage(); NetworkMessage message = parser.popIncomingMessage();
@ -241,9 +272,11 @@ public class ClientNetworking implements Runnable {
//do something //do something
Globals.profiler.beginCpuSample("ClientProtocol.handleMessage"); Globals.profiler.beginCpuSample("ClientProtocol.handleMessage");
this.messageProtocol.handleAsyncMessage(message); this.messageProtocol.handleAsyncMessage(message);
foundMessages = true;
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} }
} }
return foundMessages;
} }
@ -314,6 +347,17 @@ public class ClientNetworking implements Runnable {
public boolean isInitialized(){ public boolean isInitialized(){
return initialized; return initialized;
} }
/**
* Gets the total number of bytes read by this connection
* @return The total number of bytes
*/
public long getNumBytesRead(){
if(this.parser == null){
return 0;
}
return this.parser.getNumberOfBytesRead();
}
} }

View File

@ -97,9 +97,11 @@ public class MessageProtocol {
} }
//queue bounced messages for synchronous resolution //queue bounced messages for synchronous resolution
if(result != null){ if(result != null){
Globals.profiler.beginAggregateCpuSample("MessageProtocol(client) Await lock to synchronize message");
this.synchronousMessageLock.acquireUninterruptibly(); this.synchronousMessageLock.acquireUninterruptibly();
this.synchronousMessageQueue.add(result); this.synchronousMessageQueue.add(result);
this.synchronousMessageLock.release(); this.synchronousMessageLock.release();
Globals.profiler.endCpuSample();
} }
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} }

View File

@ -34,7 +34,6 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
//Vector3f worldMinPoint, Vector3f worldMaxPoint, int dynamicInterpolationRatio, float randomDampener, int worldDiscreteSize //Vector3f worldMinPoint, Vector3f worldMaxPoint, int dynamicInterpolationRatio, float randomDampener, int worldDiscreteSize
new Vector3f(message.getworldMinX(),0,message.getworldMinY()), new Vector3f(message.getworldMinX(),0,message.getworldMinY()),
new Vector3f(message.getworldMaxX(),32,message.getworldMaxY()), new Vector3f(message.getworldMaxX(),32,message.getworldMaxY()),
ChunkData.CHUNK_SIZE,
message.getrandomDampener(), message.getrandomDampener(),
message.getworldSizeDiscrete() message.getworldSizeDiscrete()
); );
@ -44,9 +43,10 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
case SPAWNPOSITION: case SPAWNPOSITION:
LoggerInterface.loggerNetworking.WARNING("Received spawnPosition packet on client. This is deprecated!"); LoggerInterface.loggerNetworking.WARNING("Received spawnPosition packet on client. This is deprecated!");
break; break;
case SENDCHUNKDATA: case SENDCHUNKDATA: {
LoggerInterface.loggerNetworking.DEBUG("(Client) Received terrain at " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
Globals.clientTerrainManager.attachTerrainMessage(message); Globals.clientTerrainManager.attachTerrainMessage(message);
break; } break;
case UPDATEVOXEL: { case UPDATEVOXEL: {
// //
//find what all drawcells might be updated by this voxel update //find what all drawcells might be updated by this voxel update
@ -97,9 +97,10 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z)){ if(Globals.clientTerrainManager.containsChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z)){
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z); ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z);
if(data != null){ if(data != null){
Globals.drawCellManager.markUpdateable(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z); Globals.clientDrawCellManager.markUpdateable(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z);
} }
} }
Globals.clientFoliageManager.evaluateChunk(worldPos);
} }
} break; } break;
case SENDFLUIDDATA: { case SENDFLUIDDATA: {

View File

@ -1,12 +1,15 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class AuthMessage extends NetworkMessage { public class AuthMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum AuthMessageType { public enum AuthMessageType {
AUTHREQUEST, AUTHREQUEST,
AUTHDETAILS, AUTHDETAILS,
@ -14,10 +17,17 @@ public class AuthMessage extends NetworkMessage {
AUTHFAILURE, AUTHFAILURE,
} }
/**
* The type of this message in particular.
*/
AuthMessageType messageType; AuthMessageType messageType;
String user; String user;
String pass; String pass;
/**
* Constructor
* @param messageType The type of this message
*/
AuthMessage(AuthMessageType messageType){ AuthMessage(AuthMessageType messageType){
this.type = MessageType.AUTH_MESSAGE; this.type = MessageType.AUTH_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -27,26 +37,48 @@ public class AuthMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets user
*/
public String getuser() { public String getuser() {
return user; return user;
} }
/**
* Sets user
*/
public void setuser(String user) { public void setuser(String user) {
this.user = user; this.user = user;
} }
/**
* Gets pass
*/
public String getpass() { public String getpass() {
return pass; return pass;
} }
/**
* Sets pass
*/
public void setpass(String pass) { public void setpass(String pass) {
this.pass = pass; this.pass = pass;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.AUTH_MESSAGE_TYPE_AUTHREQUEST: case TypeBytes.AUTH_MESSAGE_TYPE_AUTHREQUEST:
@ -73,18 +105,27 @@ public class AuthMessage extends NetworkMessage {
return false; return false;
} }
/**
* Parses a message of type AuthRequest
*/
public static AuthMessage parseAuthRequestMessage(CircularByteBuffer byteBuffer){ public static AuthMessage parseAuthRequestMessage(CircularByteBuffer byteBuffer){
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHREQUEST); AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHREQUEST);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type AuthRequest
*/
public static AuthMessage constructAuthRequestMessage(){ public static AuthMessage constructAuthRequestMessage(){
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHREQUEST); AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHREQUEST);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Checks if a message of type AuthDetails can be parsed from the byte stream
*/
public static boolean canParseAuthDetailsMessage(CircularByteBuffer byteBuffer){ public static boolean canParseAuthDetailsMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -117,6 +158,9 @@ public class AuthMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type AuthDetails
*/
public static AuthMessage parseAuthDetailsMessage(CircularByteBuffer byteBuffer){ public static AuthMessage parseAuthDetailsMessage(CircularByteBuffer byteBuffer){
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHDETAILS); AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHDETAILS);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -125,6 +169,9 @@ public class AuthMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type AuthDetails
*/
public static AuthMessage constructAuthDetailsMessage(String user,String pass){ public static AuthMessage constructAuthDetailsMessage(String user,String pass){
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHDETAILS); AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHDETAILS);
rVal.setuser(user); rVal.setuser(user);
@ -133,24 +180,36 @@ public class AuthMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type AuthSuccess
*/
public static AuthMessage parseAuthSuccessMessage(CircularByteBuffer byteBuffer){ public static AuthMessage parseAuthSuccessMessage(CircularByteBuffer byteBuffer){
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHSUCCESS); AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHSUCCESS);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type AuthSuccess
*/
public static AuthMessage constructAuthSuccessMessage(){ public static AuthMessage constructAuthSuccessMessage(){
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHSUCCESS); AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHSUCCESS);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Parses a message of type AuthFailure
*/
public static AuthMessage parseAuthFailureMessage(CircularByteBuffer byteBuffer){ public static AuthMessage parseAuthFailureMessage(CircularByteBuffer byteBuffer){
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHFAILURE); AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHFAILURE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type AuthFailure
*/
public static AuthMessage constructAuthFailureMessage(){ public static AuthMessage constructAuthFailureMessage(){
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHFAILURE); AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHFAILURE);
rVal.serialize(); rVal.serialize();

View File

@ -1,12 +1,15 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class CharacterMessage extends NetworkMessage { public class CharacterMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum CharacterMessageType { public enum CharacterMessageType {
REQUESTCHARACTERLIST, REQUESTCHARACTERLIST,
RESPONSECHARACTERLIST, RESPONSECHARACTERLIST,
@ -17,9 +20,16 @@ public class CharacterMessage extends NetworkMessage {
RESPONSESPAWNCHARACTER, RESPONSESPAWNCHARACTER,
} }
/**
* The type of this message in particular.
*/
CharacterMessageType messageType; CharacterMessageType messageType;
String data; String data;
/**
* Constructor
* @param messageType The type of this message
*/
CharacterMessage(CharacterMessageType messageType){ CharacterMessage(CharacterMessageType messageType){
this.type = MessageType.CHARACTER_MESSAGE; this.type = MessageType.CHARACTER_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -29,18 +39,34 @@ public class CharacterMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets data
*/
public String getdata() { public String getdata() {
return data; return data;
} }
/**
* Sets data
*/
public void setdata(String data) { public void setdata(String data) {
this.data = data; this.data = data;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST: case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST:
@ -77,18 +103,27 @@ public class CharacterMessage extends NetworkMessage {
return false; return false;
} }
/**
* Parses a message of type RequestCharacterList
*/
public static CharacterMessage parseRequestCharacterListMessage(CircularByteBuffer byteBuffer){ public static CharacterMessage parseRequestCharacterListMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCHARACTERLIST); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCHARACTERLIST);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestCharacterList
*/
public static CharacterMessage constructRequestCharacterListMessage(){ public static CharacterMessage constructRequestCharacterListMessage(){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCHARACTERLIST); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCHARACTERLIST);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Checks if a message of type ResponseCharacterList can be parsed from the byte stream
*/
public static boolean canParseResponseCharacterListMessage(CircularByteBuffer byteBuffer){ public static boolean canParseResponseCharacterListMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -108,6 +143,9 @@ public class CharacterMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type ResponseCharacterList
*/
public static CharacterMessage parseResponseCharacterListMessage(CircularByteBuffer byteBuffer){ public static CharacterMessage parseResponseCharacterListMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECHARACTERLIST); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECHARACTERLIST);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -115,6 +153,9 @@ public class CharacterMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type ResponseCharacterList
*/
public static CharacterMessage constructResponseCharacterListMessage(String data){ public static CharacterMessage constructResponseCharacterListMessage(String data){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECHARACTERLIST); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECHARACTERLIST);
rVal.setdata(data); rVal.setdata(data);
@ -122,6 +163,9 @@ public class CharacterMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type RequestCreateCharacter can be parsed from the byte stream
*/
public static boolean canParseRequestCreateCharacterMessage(CircularByteBuffer byteBuffer){ public static boolean canParseRequestCreateCharacterMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -141,6 +185,9 @@ public class CharacterMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type RequestCreateCharacter
*/
public static CharacterMessage parseRequestCreateCharacterMessage(CircularByteBuffer byteBuffer){ public static CharacterMessage parseRequestCreateCharacterMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCREATECHARACTER); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCREATECHARACTER);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -148,6 +195,9 @@ public class CharacterMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestCreateCharacter
*/
public static CharacterMessage constructRequestCreateCharacterMessage(String data){ public static CharacterMessage constructRequestCreateCharacterMessage(String data){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCREATECHARACTER); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCREATECHARACTER);
rVal.setdata(data); rVal.setdata(data);
@ -155,42 +205,63 @@ public class CharacterMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type ResponseCreateCharacterSuccess
*/
public static CharacterMessage parseResponseCreateCharacterSuccessMessage(CircularByteBuffer byteBuffer){ public static CharacterMessage parseResponseCreateCharacterSuccessMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERSUCCESS); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERSUCCESS);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type ResponseCreateCharacterSuccess
*/
public static CharacterMessage constructResponseCreateCharacterSuccessMessage(){ public static CharacterMessage constructResponseCreateCharacterSuccessMessage(){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERSUCCESS); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERSUCCESS);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Parses a message of type ResponseCreateCharacterFailure
*/
public static CharacterMessage parseResponseCreateCharacterFailureMessage(CircularByteBuffer byteBuffer){ public static CharacterMessage parseResponseCreateCharacterFailureMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERFAILURE); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERFAILURE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type ResponseCreateCharacterFailure
*/
public static CharacterMessage constructResponseCreateCharacterFailureMessage(){ public static CharacterMessage constructResponseCreateCharacterFailureMessage(){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERFAILURE); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERFAILURE);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Parses a message of type RequestSpawnCharacter
*/
public static CharacterMessage parseRequestSpawnCharacterMessage(CircularByteBuffer byteBuffer){ public static CharacterMessage parseRequestSpawnCharacterMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestSpawnCharacter
*/
public static CharacterMessage constructRequestSpawnCharacterMessage(){ public static CharacterMessage constructRequestSpawnCharacterMessage(){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Checks if a message of type ResponseSpawnCharacter can be parsed from the byte stream
*/
public static boolean canParseResponseSpawnCharacterMessage(CircularByteBuffer byteBuffer){ public static boolean canParseResponseSpawnCharacterMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -210,6 +281,9 @@ public class CharacterMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type ResponseSpawnCharacter
*/
public static CharacterMessage parseResponseSpawnCharacterMessage(CircularByteBuffer byteBuffer){ public static CharacterMessage parseResponseSpawnCharacterMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSESPAWNCHARACTER); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSESPAWNCHARACTER);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -217,6 +291,9 @@ public class CharacterMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type ResponseSpawnCharacter
*/
public static CharacterMessage constructResponseSpawnCharacterMessage(String data){ public static CharacterMessage constructResponseSpawnCharacterMessage(String data){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSESPAWNCHARACTER); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSESPAWNCHARACTER);
rVal.setdata(data); rVal.setdata(data);

View File

@ -1,16 +1,22 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class CombatMessage extends NetworkMessage { public class CombatMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum CombatMessageType { public enum CombatMessageType {
SERVERREPORTHITBOXCOLLISION, SERVERREPORTHITBOXCOLLISION,
} }
/**
* The type of this message in particular.
*/
CombatMessageType messageType; CombatMessageType messageType;
int entityID; int entityID;
int receiverEntityID; int receiverEntityID;
@ -25,6 +31,10 @@ public class CombatMessage extends NetworkMessage {
String hitboxType; String hitboxType;
String hurtboxType; String hurtboxType;
/**
* Constructor
* @param messageType The type of this message
*/
CombatMessage(CombatMessageType messageType){ CombatMessage(CombatMessageType messageType){
this.type = MessageType.COMBAT_MESSAGE; this.type = MessageType.COMBAT_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -34,106 +44,188 @@ public class CombatMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets entityID
*/
public int getentityID() { public int getentityID() {
return entityID; return entityID;
} }
/**
* Sets entityID
*/
public void setentityID(int entityID) { public void setentityID(int entityID) {
this.entityID = entityID; this.entityID = entityID;
} }
/**
* Gets receiverEntityID
*/
public int getreceiverEntityID() { public int getreceiverEntityID() {
return receiverEntityID; return receiverEntityID;
} }
/**
* Sets receiverEntityID
*/
public void setreceiverEntityID(int receiverEntityID) { public void setreceiverEntityID(int receiverEntityID) {
this.receiverEntityID = receiverEntityID; this.receiverEntityID = receiverEntityID;
} }
/**
* Gets positionX
*/
public double getpositionX() { public double getpositionX() {
return positionX; return positionX;
} }
/**
* Sets positionX
*/
public void setpositionX(double positionX) { public void setpositionX(double positionX) {
this.positionX = positionX; this.positionX = positionX;
} }
/**
* Gets positionY
*/
public double getpositionY() { public double getpositionY() {
return positionY; return positionY;
} }
/**
* Sets positionY
*/
public void setpositionY(double positionY) { public void setpositionY(double positionY) {
this.positionY = positionY; this.positionY = positionY;
} }
/**
* Gets positionZ
*/
public double getpositionZ() { public double getpositionZ() {
return positionZ; return positionZ;
} }
/**
* Sets positionZ
*/
public void setpositionZ(double positionZ) { public void setpositionZ(double positionZ) {
this.positionZ = positionZ; this.positionZ = positionZ;
} }
/**
* Gets rotationX
*/
public double getrotationX() { public double getrotationX() {
return rotationX; return rotationX;
} }
/**
* Sets rotationX
*/
public void setrotationX(double rotationX) { public void setrotationX(double rotationX) {
this.rotationX = rotationX; this.rotationX = rotationX;
} }
/**
* Gets rotationY
*/
public double getrotationY() { public double getrotationY() {
return rotationY; return rotationY;
} }
/**
* Sets rotationY
*/
public void setrotationY(double rotationY) { public void setrotationY(double rotationY) {
this.rotationY = rotationY; this.rotationY = rotationY;
} }
/**
* Gets rotationZ
*/
public double getrotationZ() { public double getrotationZ() {
return rotationZ; return rotationZ;
} }
/**
* Sets rotationZ
*/
public void setrotationZ(double rotationZ) { public void setrotationZ(double rotationZ) {
this.rotationZ = rotationZ; this.rotationZ = rotationZ;
} }
/**
* Gets rotationW
*/
public double getrotationW() { public double getrotationW() {
return rotationW; return rotationW;
} }
/**
* Sets rotationW
*/
public void setrotationW(double rotationW) { public void setrotationW(double rotationW) {
this.rotationW = rotationW; this.rotationW = rotationW;
} }
/**
* Gets time
*/
public long gettime() { public long gettime() {
return time; return time;
} }
/**
* Sets time
*/
public void settime(long time) { public void settime(long time) {
this.time = time; this.time = time;
} }
/**
* Gets hitboxType
*/
public String gethitboxType() { public String gethitboxType() {
return hitboxType; return hitboxType;
} }
/**
* Sets hitboxType
*/
public void sethitboxType(String hitboxType) { public void sethitboxType(String hitboxType) {
this.hitboxType = hitboxType; this.hitboxType = hitboxType;
} }
/**
* Gets hurtboxType
*/
public String gethurtboxType() { public String gethurtboxType() {
return hurtboxType; return hurtboxType;
} }
/**
* Sets hurtboxType
*/
public void sethurtboxType(String hurtboxType) { public void sethurtboxType(String hurtboxType) {
this.hurtboxType = hurtboxType; this.hurtboxType = hurtboxType;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.COMBAT_MESSAGE_TYPE_SERVERREPORTHITBOXCOLLISION: case TypeBytes.COMBAT_MESSAGE_TYPE_SERVERREPORTHITBOXCOLLISION:
@ -142,6 +234,9 @@ public class CombatMessage extends NetworkMessage {
return false; return false;
} }
/**
* Checks if a message of type serverReportHitboxCollision can be parsed from the byte stream
*/
public static boolean canParseserverReportHitboxCollisionMessage(CircularByteBuffer byteBuffer){ public static boolean canParseserverReportHitboxCollisionMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -192,6 +287,9 @@ public class CombatMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type serverReportHitboxCollision
*/
public static CombatMessage parseserverReportHitboxCollisionMessage(CircularByteBuffer byteBuffer){ public static CombatMessage parseserverReportHitboxCollisionMessage(CircularByteBuffer byteBuffer){
CombatMessage rVal = new CombatMessage(CombatMessageType.SERVERREPORTHITBOXCOLLISION); CombatMessage rVal = new CombatMessage(CombatMessageType.SERVERREPORTHITBOXCOLLISION);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -206,6 +304,9 @@ public class CombatMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type serverReportHitboxCollision
*/
public static CombatMessage constructserverReportHitboxCollisionMessage(int entityID,int receiverEntityID,long time,String hitboxType,String hurtboxType,double positionX,double positionY,double positionZ){ public static CombatMessage constructserverReportHitboxCollisionMessage(int entityID,int receiverEntityID,long time,String hitboxType,String hurtboxType,double positionX,double positionY,double positionZ){
CombatMessage rVal = new CombatMessage(CombatMessageType.SERVERREPORTHITBOXCOLLISION); CombatMessage rVal = new CombatMessage(CombatMessageType.SERVERREPORTHITBOXCOLLISION);
rVal.setentityID(entityID); rVal.setentityID(entityID);

View File

@ -1,12 +1,15 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class EntityMessage extends NetworkMessage { public class EntityMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum EntityMessageType { public enum EntityMessageType {
CREATE, CREATE,
MOVEUPDATE, MOVEUPDATE,
@ -20,6 +23,9 @@ public class EntityMessage extends NetworkMessage {
SYNCPHYSICS, SYNCPHYSICS,
} }
/**
* The type of this message in particular.
*/
EntityMessageType messageType; EntityMessageType messageType;
int entityCategory; int entityCategory;
String entitySubtype; String entitySubtype;
@ -56,6 +62,10 @@ public class EntityMessage extends NetworkMessage {
int bTreeID; int bTreeID;
int propertyValueInt; int propertyValueInt;
/**
* Constructor
* @param messageType The type of this message
*/
EntityMessage(EntityMessageType messageType){ EntityMessage(EntityMessageType messageType){
this.type = MessageType.ENTITY_MESSAGE; this.type = MessageType.ENTITY_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -65,282 +75,496 @@ public class EntityMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets entityCategory
*/
public int getentityCategory() { public int getentityCategory() {
return entityCategory; return entityCategory;
} }
/**
* Sets entityCategory
*/
public void setentityCategory(int entityCategory) { public void setentityCategory(int entityCategory) {
this.entityCategory = entityCategory; this.entityCategory = entityCategory;
} }
/**
* Gets entitySubtype
*/
public String getentitySubtype() { public String getentitySubtype() {
return entitySubtype; return entitySubtype;
} }
/**
* Sets entitySubtype
*/
public void setentitySubtype(String entitySubtype) { public void setentitySubtype(String entitySubtype) {
this.entitySubtype = entitySubtype; this.entitySubtype = entitySubtype;
} }
/**
* Gets entityID
*/
public int getentityID() { public int getentityID() {
return entityID; return entityID;
} }
/**
* Sets entityID
*/
public void setentityID(int entityID) { public void setentityID(int entityID) {
this.entityID = entityID; this.entityID = entityID;
} }
/**
* Gets creatureTemplate
*/
public String getcreatureTemplate() { public String getcreatureTemplate() {
return creatureTemplate; return creatureTemplate;
} }
/**
* Sets creatureTemplate
*/
public void setcreatureTemplate(String creatureTemplate) { public void setcreatureTemplate(String creatureTemplate) {
this.creatureTemplate = creatureTemplate; this.creatureTemplate = creatureTemplate;
} }
/**
* Gets positionX
*/
public double getpositionX() { public double getpositionX() {
return positionX; return positionX;
} }
/**
* Sets positionX
*/
public void setpositionX(double positionX) { public void setpositionX(double positionX) {
this.positionX = positionX; this.positionX = positionX;
} }
/**
* Gets positionY
*/
public double getpositionY() { public double getpositionY() {
return positionY; return positionY;
} }
/**
* Sets positionY
*/
public void setpositionY(double positionY) { public void setpositionY(double positionY) {
this.positionY = positionY; this.positionY = positionY;
} }
/**
* Gets positionZ
*/
public double getpositionZ() { public double getpositionZ() {
return positionZ; return positionZ;
} }
/**
* Sets positionZ
*/
public void setpositionZ(double positionZ) { public void setpositionZ(double positionZ) {
this.positionZ = positionZ; this.positionZ = positionZ;
} }
/**
* Gets rotationX
*/
public double getrotationX() { public double getrotationX() {
return rotationX; return rotationX;
} }
/**
* Sets rotationX
*/
public void setrotationX(double rotationX) { public void setrotationX(double rotationX) {
this.rotationX = rotationX; this.rotationX = rotationX;
} }
/**
* Gets rotationY
*/
public double getrotationY() { public double getrotationY() {
return rotationY; return rotationY;
} }
/**
* Sets rotationY
*/
public void setrotationY(double rotationY) { public void setrotationY(double rotationY) {
this.rotationY = rotationY; this.rotationY = rotationY;
} }
/**
* Gets rotationZ
*/
public double getrotationZ() { public double getrotationZ() {
return rotationZ; return rotationZ;
} }
/**
* Sets rotationZ
*/
public void setrotationZ(double rotationZ) { public void setrotationZ(double rotationZ) {
this.rotationZ = rotationZ; this.rotationZ = rotationZ;
} }
/**
* Gets rotationW
*/
public double getrotationW() { public double getrotationW() {
return rotationW; return rotationW;
} }
/**
* Sets rotationW
*/
public void setrotationW(double rotationW) { public void setrotationW(double rotationW) {
this.rotationW = rotationW; this.rotationW = rotationW;
} }
/**
* Gets linVelX
*/
public double getlinVelX() { public double getlinVelX() {
return linVelX; return linVelX;
} }
/**
* Sets linVelX
*/
public void setlinVelX(double linVelX) { public void setlinVelX(double linVelX) {
this.linVelX = linVelX; this.linVelX = linVelX;
} }
/**
* Gets linVelY
*/
public double getlinVelY() { public double getlinVelY() {
return linVelY; return linVelY;
} }
/**
* Sets linVelY
*/
public void setlinVelY(double linVelY) { public void setlinVelY(double linVelY) {
this.linVelY = linVelY; this.linVelY = linVelY;
} }
/**
* Gets linVelZ
*/
public double getlinVelZ() { public double getlinVelZ() {
return linVelZ; return linVelZ;
} }
/**
* Sets linVelZ
*/
public void setlinVelZ(double linVelZ) { public void setlinVelZ(double linVelZ) {
this.linVelZ = linVelZ; this.linVelZ = linVelZ;
} }
/**
* Gets angVelX
*/
public double getangVelX() { public double getangVelX() {
return angVelX; return angVelX;
} }
/**
* Sets angVelX
*/
public void setangVelX(double angVelX) { public void setangVelX(double angVelX) {
this.angVelX = angVelX; this.angVelX = angVelX;
} }
/**
* Gets angVelY
*/
public double getangVelY() { public double getangVelY() {
return angVelY; return angVelY;
} }
/**
* Sets angVelY
*/
public void setangVelY(double angVelY) { public void setangVelY(double angVelY) {
this.angVelY = angVelY; this.angVelY = angVelY;
} }
/**
* Gets angVelZ
*/
public double getangVelZ() { public double getangVelZ() {
return angVelZ; return angVelZ;
} }
/**
* Sets angVelZ
*/
public void setangVelZ(double angVelZ) { public void setangVelZ(double angVelZ) {
this.angVelZ = angVelZ; this.angVelZ = angVelZ;
} }
/**
* Gets linForceX
*/
public double getlinForceX() { public double getlinForceX() {
return linForceX; return linForceX;
} }
/**
* Sets linForceX
*/
public void setlinForceX(double linForceX) { public void setlinForceX(double linForceX) {
this.linForceX = linForceX; this.linForceX = linForceX;
} }
/**
* Gets linForceY
*/
public double getlinForceY() { public double getlinForceY() {
return linForceY; return linForceY;
} }
/**
* Sets linForceY
*/
public void setlinForceY(double linForceY) { public void setlinForceY(double linForceY) {
this.linForceY = linForceY; this.linForceY = linForceY;
} }
/**
* Gets linForceZ
*/
public double getlinForceZ() { public double getlinForceZ() {
return linForceZ; return linForceZ;
} }
/**
* Sets linForceZ
*/
public void setlinForceZ(double linForceZ) { public void setlinForceZ(double linForceZ) {
this.linForceZ = linForceZ; this.linForceZ = linForceZ;
} }
/**
* Gets angForceX
*/
public double getangForceX() { public double getangForceX() {
return angForceX; return angForceX;
} }
/**
* Sets angForceX
*/
public void setangForceX(double angForceX) { public void setangForceX(double angForceX) {
this.angForceX = angForceX; this.angForceX = angForceX;
} }
/**
* Gets angForceY
*/
public double getangForceY() { public double getangForceY() {
return angForceY; return angForceY;
} }
/**
* Sets angForceY
*/
public void setangForceY(double angForceY) { public void setangForceY(double angForceY) {
this.angForceY = angForceY; this.angForceY = angForceY;
} }
/**
* Gets angForceZ
*/
public double getangForceZ() { public double getangForceZ() {
return angForceZ; return angForceZ;
} }
/**
* Sets angForceZ
*/
public void setangForceZ(double angForceZ) { public void setangForceZ(double angForceZ) {
this.angForceZ = angForceZ; this.angForceZ = angForceZ;
} }
/**
* Gets yaw
*/
public double getyaw() { public double getyaw() {
return yaw; return yaw;
} }
/**
* Sets yaw
*/
public void setyaw(double yaw) { public void setyaw(double yaw) {
this.yaw = yaw; this.yaw = yaw;
} }
/**
* Gets pitch
*/
public double getpitch() { public double getpitch() {
return pitch; return pitch;
} }
/**
* Sets pitch
*/
public void setpitch(double pitch) { public void setpitch(double pitch) {
this.pitch = pitch; this.pitch = pitch;
} }
/**
* Gets velocity
*/
public double getvelocity() { public double getvelocity() {
return velocity; return velocity;
} }
/**
* Sets velocity
*/
public void setvelocity(double velocity) { public void setvelocity(double velocity) {
this.velocity = velocity; this.velocity = velocity;
} }
/**
* Gets treeState
*/
public int gettreeState() { public int gettreeState() {
return treeState; return treeState;
} }
/**
* Sets treeState
*/
public void settreeState(int treeState) { public void settreeState(int treeState) {
this.treeState = treeState; this.treeState = treeState;
} }
/**
* Gets propertyType
*/
public int getpropertyType() { public int getpropertyType() {
return propertyType; return propertyType;
} }
/**
* Sets propertyType
*/
public void setpropertyType(int propertyType) { public void setpropertyType(int propertyType) {
this.propertyType = propertyType; this.propertyType = propertyType;
} }
/**
* Gets propertyValue
*/
public int getpropertyValue() { public int getpropertyValue() {
return propertyValue; return propertyValue;
} }
/**
* Sets propertyValue
*/
public void setpropertyValue(int propertyValue) { public void setpropertyValue(int propertyValue) {
this.propertyValue = propertyValue; this.propertyValue = propertyValue;
} }
/**
* Gets time
*/
public long gettime() { public long gettime() {
return time; return time;
} }
/**
* Sets time
*/
public void settime(long time) { public void settime(long time) {
this.time = time; this.time = time;
} }
/**
* Gets bone
*/
public String getbone() { public String getbone() {
return bone; return bone;
} }
/**
* Sets bone
*/
public void setbone(String bone) { public void setbone(String bone) {
this.bone = bone; this.bone = bone;
} }
/**
* Gets targetID
*/
public int gettargetID() { public int gettargetID() {
return targetID; return targetID;
} }
/**
* Sets targetID
*/
public void settargetID(int targetID) { public void settargetID(int targetID) {
this.targetID = targetID; this.targetID = targetID;
} }
/**
* Gets bTreeID
*/
public int getbTreeID() { public int getbTreeID() {
return bTreeID; return bTreeID;
} }
/**
* Sets bTreeID
*/
public void setbTreeID(int bTreeID) { public void setbTreeID(int bTreeID) {
this.bTreeID = bTreeID; this.bTreeID = bTreeID;
} }
/**
* Gets propertyValueInt
*/
public int getpropertyValueInt() { public int getpropertyValueInt() {
return propertyValueInt; return propertyValueInt;
} }
/**
* Sets propertyValueInt
*/
public void setpropertyValueInt(int propertyValueInt) { public void setpropertyValueInt(int propertyValueInt) {
this.propertyValueInt = propertyValueInt; this.propertyValueInt = propertyValueInt;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.ENTITY_MESSAGE_TYPE_CREATE: case TypeBytes.ENTITY_MESSAGE_TYPE_CREATE:
@ -399,6 +623,9 @@ public class EntityMessage extends NetworkMessage {
return false; return false;
} }
/**
* Checks if a message of type Create can be parsed from the byte stream
*/
public static boolean canParseCreateMessage(CircularByteBuffer byteBuffer){ public static boolean canParseCreateMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -458,6 +685,9 @@ public class EntityMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type Create
*/
public static EntityMessage parseCreateMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parseCreateMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.CREATE); EntityMessage rVal = new EntityMessage(EntityMessageType.CREATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -475,6 +705,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type Create
*/
public static EntityMessage constructCreateMessage(int entityID,int entityCategory,String entitySubtype,String creatureTemplate,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double rotationW){ public static EntityMessage constructCreateMessage(int entityID,int entityCategory,String entitySubtype,String creatureTemplate,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double rotationW){
EntityMessage rVal = new EntityMessage(EntityMessageType.CREATE); EntityMessage rVal = new EntityMessage(EntityMessageType.CREATE);
rVal.setentityID(entityID); rVal.setentityID(entityID);
@ -492,6 +725,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type moveUpdate
*/
public static EntityMessage parsemoveUpdateMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parsemoveUpdateMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.MOVEUPDATE); EntityMessage rVal = new EntityMessage(EntityMessageType.MOVEUPDATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -510,6 +746,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type moveUpdate
*/
public static EntityMessage constructmoveUpdateMessage(int entityID,long time,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double rotationW,double velocity,int propertyValueInt,int treeState){ public static EntityMessage constructmoveUpdateMessage(int entityID,long time,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double rotationW,double velocity,int propertyValueInt,int treeState){
EntityMessage rVal = new EntityMessage(EntityMessageType.MOVEUPDATE); EntityMessage rVal = new EntityMessage(EntityMessageType.MOVEUPDATE);
rVal.setentityID(entityID); rVal.setentityID(entityID);
@ -528,6 +767,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type attackUpdate
*/
public static EntityMessage parseattackUpdateMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parseattackUpdateMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACKUPDATE); EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACKUPDATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -544,6 +786,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type attackUpdate
*/
public static EntityMessage constructattackUpdateMessage(int entityID,long time,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double velocity,int treeState){ public static EntityMessage constructattackUpdateMessage(int entityID,long time,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double velocity,int treeState){
EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACKUPDATE); EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACKUPDATE);
rVal.setentityID(entityID); rVal.setentityID(entityID);
@ -560,18 +805,27 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type startAttack
*/
public static EntityMessage parsestartAttackMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parsestartAttackMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.STARTATTACK); EntityMessage rVal = new EntityMessage(EntityMessageType.STARTATTACK);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type startAttack
*/
public static EntityMessage constructstartAttackMessage(){ public static EntityMessage constructstartAttackMessage(){
EntityMessage rVal = new EntityMessage(EntityMessageType.STARTATTACK); EntityMessage rVal = new EntityMessage(EntityMessageType.STARTATTACK);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Parses a message of type Kill
*/
public static EntityMessage parseKillMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parseKillMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.KILL); EntityMessage rVal = new EntityMessage(EntityMessageType.KILL);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -580,6 +834,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type Kill
*/
public static EntityMessage constructKillMessage(long time,int entityID){ public static EntityMessage constructKillMessage(long time,int entityID){
EntityMessage rVal = new EntityMessage(EntityMessageType.KILL); EntityMessage rVal = new EntityMessage(EntityMessageType.KILL);
rVal.settime(time); rVal.settime(time);
@ -588,6 +845,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type Destroy
*/
public static EntityMessage parseDestroyMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parseDestroyMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.DESTROY); EntityMessage rVal = new EntityMessage(EntityMessageType.DESTROY);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -595,6 +855,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type Destroy
*/
public static EntityMessage constructDestroyMessage(int entityID){ public static EntityMessage constructDestroyMessage(int entityID){
EntityMessage rVal = new EntityMessage(EntityMessageType.DESTROY); EntityMessage rVal = new EntityMessage(EntityMessageType.DESTROY);
rVal.setentityID(entityID); rVal.setentityID(entityID);
@ -602,6 +865,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type setProperty
*/
public static EntityMessage parsesetPropertyMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parsesetPropertyMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.SETPROPERTY); EntityMessage rVal = new EntityMessage(EntityMessageType.SETPROPERTY);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -612,6 +878,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type setProperty
*/
public static EntityMessage constructsetPropertyMessage(int entityID,long time,int propertyType,int propertyValue){ public static EntityMessage constructsetPropertyMessage(int entityID,long time,int propertyType,int propertyValue){
EntityMessage rVal = new EntityMessage(EntityMessageType.SETPROPERTY); EntityMessage rVal = new EntityMessage(EntityMessageType.SETPROPERTY);
rVal.setentityID(entityID); rVal.setentityID(entityID);
@ -622,6 +891,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type attachEntityToEntity can be parsed from the byte stream
*/
public static boolean canParseattachEntityToEntityMessage(CircularByteBuffer byteBuffer){ public static boolean canParseattachEntityToEntityMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -647,6 +919,9 @@ public class EntityMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type attachEntityToEntity
*/
public static EntityMessage parseattachEntityToEntityMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parseattachEntityToEntityMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACHENTITYTOENTITY); EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACHENTITYTOENTITY);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -656,6 +931,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type attachEntityToEntity
*/
public static EntityMessage constructattachEntityToEntityMessage(int entityID,String bone,int targetID){ public static EntityMessage constructattachEntityToEntityMessage(int entityID,String bone,int targetID){
EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACHENTITYTOENTITY); EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACHENTITYTOENTITY);
rVal.setentityID(entityID); rVal.setentityID(entityID);
@ -665,6 +943,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type updateEntityViewDir
*/
public static EntityMessage parseupdateEntityViewDirMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parseupdateEntityViewDirMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.UPDATEENTITYVIEWDIR); EntityMessage rVal = new EntityMessage(EntityMessageType.UPDATEENTITYVIEWDIR);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -676,6 +957,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type updateEntityViewDir
*/
public static EntityMessage constructupdateEntityViewDirMessage(int entityID,long time,int propertyType,double yaw,double pitch){ public static EntityMessage constructupdateEntityViewDirMessage(int entityID,long time,int propertyType,double yaw,double pitch){
EntityMessage rVal = new EntityMessage(EntityMessageType.UPDATEENTITYVIEWDIR); EntityMessage rVal = new EntityMessage(EntityMessageType.UPDATEENTITYVIEWDIR);
rVal.setentityID(entityID); rVal.setentityID(entityID);
@ -687,6 +971,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type syncPhysics
*/
public static EntityMessage parsesyncPhysicsMessage(CircularByteBuffer byteBuffer){ public static EntityMessage parsesyncPhysicsMessage(CircularByteBuffer byteBuffer){
EntityMessage rVal = new EntityMessage(EntityMessageType.SYNCPHYSICS); EntityMessage rVal = new EntityMessage(EntityMessageType.SYNCPHYSICS);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -714,6 +1001,9 @@ public class EntityMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type syncPhysics
*/
public static EntityMessage constructsyncPhysicsMessage(int entityID,long time,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double rotationW,double linVelX,double linVelY,double linVelZ,double angVelX,double angVelY,double angVelZ,double linForceX,double linForceY,double linForceZ,double angForceX,double angForceY,double angForceZ){ public static EntityMessage constructsyncPhysicsMessage(int entityID,long time,double positionX,double positionY,double positionZ,double rotationX,double rotationY,double rotationZ,double rotationW,double linVelX,double linVelY,double linVelZ,double angVelX,double angVelY,double angVelZ,double linForceX,double linForceY,double linForceZ,double angForceX,double angForceY,double angForceZ){
EntityMessage rVal = new EntityMessage(EntityMessageType.SYNCPHYSICS); EntityMessage rVal = new EntityMessage(EntityMessageType.SYNCPHYSICS);
rVal.setentityID(entityID); rVal.setentityID(entityID);

View File

@ -1,12 +1,15 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class InventoryMessage extends NetworkMessage { public class InventoryMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum InventoryMessageType { public enum InventoryMessageType {
ADDITEMTOINVENTORY, ADDITEMTOINVENTORY,
REMOVEITEMFROMINVENTORY, REMOVEITEMFROMINVENTORY,
@ -21,6 +24,9 @@ public class InventoryMessage extends NetworkMessage {
CLIENTREQUESTPERFORMITEMACTION, CLIENTREQUESTPERFORMITEMACTION,
} }
/**
* The type of this message in particular.
*/
InventoryMessageType messageType; InventoryMessageType messageType;
String itemTemplate; String itemTemplate;
String equipPointId; String equipPointId;
@ -31,6 +37,10 @@ public class InventoryMessage extends NetworkMessage {
int itemActionCode; int itemActionCode;
int itemActionCodeState; int itemActionCodeState;
/**
* Constructor
* @param messageType The type of this message
*/
InventoryMessage(InventoryMessageType messageType){ InventoryMessage(InventoryMessageType messageType){
this.type = MessageType.INVENTORY_MESSAGE; this.type = MessageType.INVENTORY_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -40,74 +50,132 @@ public class InventoryMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets itemTemplate
*/
public String getitemTemplate() { public String getitemTemplate() {
return itemTemplate; return itemTemplate;
} }
/**
* Sets itemTemplate
*/
public void setitemTemplate(String itemTemplate) { public void setitemTemplate(String itemTemplate) {
this.itemTemplate = itemTemplate; this.itemTemplate = itemTemplate;
} }
/**
* Gets equipPointId
*/
public String getequipPointId() { public String getequipPointId() {
return equipPointId; return equipPointId;
} }
/**
* Sets equipPointId
*/
public void setequipPointId(String equipPointId) { public void setequipPointId(String equipPointId) {
this.equipPointId = equipPointId; this.equipPointId = equipPointId;
} }
/**
* Gets entityId
*/
public int getentityId() { public int getentityId() {
return entityId; return entityId;
} }
/**
* Sets entityId
*/
public void setentityId(int entityId) { public void setentityId(int entityId) {
this.entityId = entityId; this.entityId = entityId;
} }
/**
* Gets equipperId
*/
public int getequipperId() { public int getequipperId() {
return equipperId; return equipperId;
} }
/**
* Sets equipperId
*/
public void setequipperId(int equipperId) { public void setequipperId(int equipperId) {
this.equipperId = equipperId; this.equipperId = equipperId;
} }
/**
* Gets containerType
*/
public int getcontainerType() { public int getcontainerType() {
return containerType; return containerType;
} }
/**
* Sets containerType
*/
public void setcontainerType(int containerType) { public void setcontainerType(int containerType) {
this.containerType = containerType; this.containerType = containerType;
} }
/**
* Gets toolbarId
*/
public int gettoolbarId() { public int gettoolbarId() {
return toolbarId; return toolbarId;
} }
/**
* Sets toolbarId
*/
public void settoolbarId(int toolbarId) { public void settoolbarId(int toolbarId) {
this.toolbarId = toolbarId; this.toolbarId = toolbarId;
} }
/**
* Gets itemActionCode
*/
public int getitemActionCode() { public int getitemActionCode() {
return itemActionCode; return itemActionCode;
} }
/**
* Sets itemActionCode
*/
public void setitemActionCode(int itemActionCode) { public void setitemActionCode(int itemActionCode) {
this.itemActionCode = itemActionCode; this.itemActionCode = itemActionCode;
} }
/**
* Gets itemActionCodeState
*/
public int getitemActionCodeState() { public int getitemActionCodeState() {
return itemActionCodeState; return itemActionCodeState;
} }
/**
* Sets itemActionCodeState
*/
public void setitemActionCodeState(int itemActionCodeState) { public void setitemActionCodeState(int itemActionCodeState) {
this.itemActionCodeState = itemActionCodeState; this.itemActionCodeState = itemActionCodeState;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.INVENTORY_MESSAGE_TYPE_ADDITEMTOINVENTORY: case TypeBytes.INVENTORY_MESSAGE_TYPE_ADDITEMTOINVENTORY:
@ -152,6 +220,9 @@ public class InventoryMessage extends NetworkMessage {
return false; return false;
} }
/**
* Checks if a message of type addItemToInventory can be parsed from the byte stream
*/
public static boolean canParseaddItemToInventoryMessage(CircularByteBuffer byteBuffer){ public static boolean canParseaddItemToInventoryMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -174,6 +245,9 @@ public class InventoryMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type addItemToInventory
*/
public static InventoryMessage parseaddItemToInventoryMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseaddItemToInventoryMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.ADDITEMTOINVENTORY); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.ADDITEMTOINVENTORY);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -182,6 +256,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type addItemToInventory
*/
public static InventoryMessage constructaddItemToInventoryMessage(int entityId,String itemTemplate){ public static InventoryMessage constructaddItemToInventoryMessage(int entityId,String itemTemplate){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.ADDITEMTOINVENTORY); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.ADDITEMTOINVENTORY);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -190,6 +267,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type removeItemFromInventory
*/
public static InventoryMessage parseremoveItemFromInventoryMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseremoveItemFromInventoryMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.REMOVEITEMFROMINVENTORY); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.REMOVEITEMFROMINVENTORY);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -197,6 +277,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type removeItemFromInventory
*/
public static InventoryMessage constructremoveItemFromInventoryMessage(int entityId){ public static InventoryMessage constructremoveItemFromInventoryMessage(int entityId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.REMOVEITEMFROMINVENTORY); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.REMOVEITEMFROMINVENTORY);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -204,6 +287,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type clientRequestEquipItem can be parsed from the byte stream
*/
public static boolean canParseclientRequestEquipItemMessage(CircularByteBuffer byteBuffer){ public static boolean canParseclientRequestEquipItemMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -226,6 +312,9 @@ public class InventoryMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type clientRequestEquipItem
*/
public static InventoryMessage parseclientRequestEquipItemMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseclientRequestEquipItemMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTEQUIPITEM); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTEQUIPITEM);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -234,6 +323,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type clientRequestEquipItem
*/
public static InventoryMessage constructclientRequestEquipItemMessage(String equipPointId,int entityId){ public static InventoryMessage constructclientRequestEquipItemMessage(String equipPointId,int entityId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTEQUIPITEM); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTEQUIPITEM);
rVal.setequipPointId(equipPointId); rVal.setequipPointId(equipPointId);
@ -242,6 +334,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type serverCommandMoveItemContainer can be parsed from the byte stream
*/
public static boolean canParseserverCommandMoveItemContainerMessage(CircularByteBuffer byteBuffer){ public static boolean canParseserverCommandMoveItemContainerMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -267,6 +362,9 @@ public class InventoryMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type serverCommandMoveItemContainer
*/
public static InventoryMessage parseserverCommandMoveItemContainerMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseserverCommandMoveItemContainerMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDMOVEITEMCONTAINER); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDMOVEITEMCONTAINER);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -276,6 +374,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type serverCommandMoveItemContainer
*/
public static InventoryMessage constructserverCommandMoveItemContainerMessage(int entityId,int containerType,String equipPointId){ public static InventoryMessage constructserverCommandMoveItemContainerMessage(int entityId,int containerType,String equipPointId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDMOVEITEMCONTAINER); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDMOVEITEMCONTAINER);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -285,6 +386,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type serverCommandEquipItem can be parsed from the byte stream
*/
public static boolean canParseserverCommandEquipItemMessage(CircularByteBuffer byteBuffer){ public static boolean canParseserverCommandEquipItemMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -326,6 +430,9 @@ public class InventoryMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type serverCommandEquipItem
*/
public static InventoryMessage parseserverCommandEquipItemMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseserverCommandEquipItemMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDEQUIPITEM); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDEQUIPITEM);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -337,6 +444,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type serverCommandEquipItem
*/
public static InventoryMessage constructserverCommandEquipItemMessage(int equipperId,int containerType,String equipPointId,int entityId,String itemTemplate){ public static InventoryMessage constructserverCommandEquipItemMessage(int equipperId,int containerType,String equipPointId,int entityId,String itemTemplate){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDEQUIPITEM); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDEQUIPITEM);
rVal.setequipperId(equipperId); rVal.setequipperId(equipperId);
@ -348,6 +458,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type serverCommandUnequipItem can be parsed from the byte stream
*/
public static boolean canParseserverCommandUnequipItemMessage(CircularByteBuffer byteBuffer){ public static boolean canParseserverCommandUnequipItemMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -373,6 +486,9 @@ public class InventoryMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type serverCommandUnequipItem
*/
public static InventoryMessage parseserverCommandUnequipItemMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseserverCommandUnequipItemMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDUNEQUIPITEM); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDUNEQUIPITEM);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -382,6 +498,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type serverCommandUnequipItem
*/
public static InventoryMessage constructserverCommandUnequipItemMessage(int equipperId,int containerType,String equipPointId){ public static InventoryMessage constructserverCommandUnequipItemMessage(int equipperId,int containerType,String equipPointId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDUNEQUIPITEM); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDUNEQUIPITEM);
rVal.setequipperId(equipperId); rVal.setequipperId(equipperId);
@ -391,6 +510,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type clientRequestUnequipItem can be parsed from the byte stream
*/
public static boolean canParseclientRequestUnequipItemMessage(CircularByteBuffer byteBuffer){ public static boolean canParseclientRequestUnequipItemMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -410,6 +532,9 @@ public class InventoryMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type clientRequestUnequipItem
*/
public static InventoryMessage parseclientRequestUnequipItemMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseclientRequestUnequipItemMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTUNEQUIPITEM); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTUNEQUIPITEM);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -417,6 +542,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type clientRequestUnequipItem
*/
public static InventoryMessage constructclientRequestUnequipItemMessage(String equipPointId){ public static InventoryMessage constructclientRequestUnequipItemMessage(String equipPointId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTUNEQUIPITEM); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTUNEQUIPITEM);
rVal.setequipPointId(equipPointId); rVal.setequipPointId(equipPointId);
@ -424,6 +552,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type clientRequestAddToolbar
*/
public static InventoryMessage parseclientRequestAddToolbarMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseclientRequestAddToolbarMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDTOOLBAR); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDTOOLBAR);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -432,6 +563,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type clientRequestAddToolbar
*/
public static InventoryMessage constructclientRequestAddToolbarMessage(int entityId,int toolbarId){ public static InventoryMessage constructclientRequestAddToolbarMessage(int entityId,int toolbarId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDTOOLBAR); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDTOOLBAR);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -440,6 +574,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type clientRequestAddNatural
*/
public static InventoryMessage parseclientRequestAddNaturalMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseclientRequestAddNaturalMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDNATURAL); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDNATURAL);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -447,6 +584,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type clientRequestAddNatural
*/
public static InventoryMessage constructclientRequestAddNaturalMessage(int entityId){ public static InventoryMessage constructclientRequestAddNaturalMessage(int entityId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDNATURAL); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDNATURAL);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -454,6 +594,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type clientUpdateToolbar
*/
public static InventoryMessage parseclientUpdateToolbarMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseclientUpdateToolbarMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTUPDATETOOLBAR); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTUPDATETOOLBAR);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -461,6 +604,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type clientUpdateToolbar
*/
public static InventoryMessage constructclientUpdateToolbarMessage(int toolbarId){ public static InventoryMessage constructclientUpdateToolbarMessage(int toolbarId){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTUPDATETOOLBAR); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTUPDATETOOLBAR);
rVal.settoolbarId(toolbarId); rVal.settoolbarId(toolbarId);
@ -468,6 +614,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type clientRequestPerformItemAction can be parsed from the byte stream
*/
public static boolean canParseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){ public static boolean canParseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -493,6 +642,9 @@ public class InventoryMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type clientRequestPerformItemAction
*/
public static InventoryMessage parseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){ public static InventoryMessage parseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTPERFORMITEMACTION); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTPERFORMITEMACTION);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -502,6 +654,9 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type clientRequestPerformItemAction
*/
public static InventoryMessage constructclientRequestPerformItemActionMessage(String equipPointId,int itemActionCode,int itemActionCodeState){ public static InventoryMessage constructclientRequestPerformItemActionMessage(String equipPointId,int itemActionCode,int itemActionCodeState){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTPERFORMITEMACTION); InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTPERFORMITEMACTION);
rVal.setequipPointId(equipPointId); rVal.setequipPointId(equipPointId);

View File

@ -1,20 +1,30 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class LoreMessage extends NetworkMessage { public class LoreMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum LoreMessageType { public enum LoreMessageType {
REQUESTRACES, REQUESTRACES,
RESPONSERACES, RESPONSERACES,
} }
/**
* The type of this message in particular.
*/
LoreMessageType messageType; LoreMessageType messageType;
String data; String data;
/**
* Constructor
* @param messageType The type of this message
*/
LoreMessage(LoreMessageType messageType){ LoreMessage(LoreMessageType messageType){
this.type = MessageType.LORE_MESSAGE; this.type = MessageType.LORE_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -24,18 +34,34 @@ public class LoreMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets data
*/
public String getdata() { public String getdata() {
return data; return data;
} }
/**
* Sets data
*/
public void setdata(String data) { public void setdata(String data) {
this.data = data; this.data = data;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.LORE_MESSAGE_TYPE_REQUESTRACES: case TypeBytes.LORE_MESSAGE_TYPE_REQUESTRACES:
@ -50,18 +76,27 @@ public class LoreMessage extends NetworkMessage {
return false; return false;
} }
/**
* Parses a message of type RequestRaces
*/
public static LoreMessage parseRequestRacesMessage(CircularByteBuffer byteBuffer){ public static LoreMessage parseRequestRacesMessage(CircularByteBuffer byteBuffer){
LoreMessage rVal = new LoreMessage(LoreMessageType.REQUESTRACES); LoreMessage rVal = new LoreMessage(LoreMessageType.REQUESTRACES);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestRaces
*/
public static LoreMessage constructRequestRacesMessage(){ public static LoreMessage constructRequestRacesMessage(){
LoreMessage rVal = new LoreMessage(LoreMessageType.REQUESTRACES); LoreMessage rVal = new LoreMessage(LoreMessageType.REQUESTRACES);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Checks if a message of type ResponseRaces can be parsed from the byte stream
*/
public static boolean canParseResponseRacesMessage(CircularByteBuffer byteBuffer){ public static boolean canParseResponseRacesMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -81,6 +116,9 @@ public class LoreMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type ResponseRaces
*/
public static LoreMessage parseResponseRacesMessage(CircularByteBuffer byteBuffer){ public static LoreMessage parseResponseRacesMessage(CircularByteBuffer byteBuffer){
LoreMessage rVal = new LoreMessage(LoreMessageType.RESPONSERACES); LoreMessage rVal = new LoreMessage(LoreMessageType.RESPONSERACES);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -88,6 +126,9 @@ public class LoreMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type ResponseRaces
*/
public static LoreMessage constructResponseRacesMessage(String data){ public static LoreMessage constructResponseRacesMessage(String data){
LoreMessage rVal = new LoreMessage(LoreMessageType.RESPONSERACES); LoreMessage rVal = new LoreMessage(LoreMessageType.RESPONSERACES);
rVal.setdata(data); rVal.setdata(data);

View File

@ -1,37 +1,64 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import electrosphere.net.parser.util.ByteStreamUtils; import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.List;
/**
* A network message
*/
public abstract class NetworkMessage { public abstract class NetworkMessage {
/**
* The different categories of network messages
*/
public enum MessageType { public enum MessageType {
ENTITY_MESSAGE, ENTITY_MESSAGE,
LORE_MESSAGE, LORE_MESSAGE,
PLAYER_MESSAGE, PLAYER_MESSAGE,
TERRAIN_MESSAGE, TERRAIN_MESSAGE,
SERVER_MESSAGE, SERVER_MESSAGE,
AUTH_MESSAGE, AUTH_MESSAGE,
CHARACTER_MESSAGE, CHARACTER_MESSAGE,
INVENTORY_MESSAGE, INVENTORY_MESSAGE,
SYNCHRONIZATION_MESSAGE, SYNCHRONIZATION_MESSAGE,
COMBAT_MESSAGE, COMBAT_MESSAGE,
} }
/**
* The type of this message
*/
MessageType type; MessageType type;
boolean serialized; // has this message been converted to bytes?
/**
* Tracks whether the message has been serialized to bytes or not
*/
boolean serialized;
/**
* The raw bytes contained in the message
*/
byte[] rawBytes; byte[] rawBytes;
/**
* Gets the type of the message
* @return The type of the message
*/
public MessageType getType() { public MessageType getType() {
return type; return type;
} }
/**
* Gets the raw bytes of the message
* @return The raw bytes
*/
public byte[] getRawBytes() { public byte[] getRawBytes() {
return rawBytes; return rawBytes;
} }
/**
* Parses the byte stream for the next message
* @param byteBuffer The byte buffer
* @return The message if one is at the front of the byte stream, null otherwise
*/
public static NetworkMessage parseBytestreamForMessage(CircularByteBuffer byteBuffer){ public static NetworkMessage parseBytestreamForMessage(CircularByteBuffer byteBuffer){
NetworkMessage rVal = null; NetworkMessage rVal = null;
byte firstByte; byte firstByte;
@ -409,10 +436,17 @@ COMBAT_MESSAGE,
return rVal; return rVal;
} }
/**
* Checks if this message is serialized or not
* @return true if it is serialized, false otherwise
*/
public boolean isSerialized(){ public boolean isSerialized(){
return serialized; return serialized;
} }
/**
* Serializes the message
*/
abstract void serialize(); abstract void serialize();
} }

View File

@ -1,23 +1,30 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList;
import java.util.List;
public class PlayerMessage extends NetworkMessage { public class PlayerMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum PlayerMessageType { public enum PlayerMessageType {
SET_ID, SET_ID,
SETINITIALDISCRETEPOSITION, SETINITIALDISCRETEPOSITION,
} }
/**
* The type of this message in particular.
*/
PlayerMessageType messageType; PlayerMessageType messageType;
int playerID; int playerID;
int initialDiscretePositionX; int initialDiscretePositionX;
int initialDiscretePositionY; int initialDiscretePositionY;
int initialDiscretePositionZ; int initialDiscretePositionZ;
/**
* Constructor
* @param messageType The type of this message
*/
PlayerMessage(PlayerMessageType messageType){ PlayerMessage(PlayerMessageType messageType){
this.type = MessageType.PLAYER_MESSAGE; this.type = MessageType.PLAYER_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -27,42 +34,76 @@ public class PlayerMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets playerID
*/
public int getplayerID() { public int getplayerID() {
return playerID; return playerID;
} }
/**
* Sets playerID
*/
public void setplayerID(int playerID) { public void setplayerID(int playerID) {
this.playerID = playerID; this.playerID = playerID;
} }
/**
* Gets initialDiscretePositionX
*/
public int getinitialDiscretePositionX() { public int getinitialDiscretePositionX() {
return initialDiscretePositionX; return initialDiscretePositionX;
} }
/**
* Sets initialDiscretePositionX
*/
public void setinitialDiscretePositionX(int initialDiscretePositionX) { public void setinitialDiscretePositionX(int initialDiscretePositionX) {
this.initialDiscretePositionX = initialDiscretePositionX; this.initialDiscretePositionX = initialDiscretePositionX;
} }
/**
* Gets initialDiscretePositionY
*/
public int getinitialDiscretePositionY() { public int getinitialDiscretePositionY() {
return initialDiscretePositionY; return initialDiscretePositionY;
} }
/**
* Sets initialDiscretePositionY
*/
public void setinitialDiscretePositionY(int initialDiscretePositionY) { public void setinitialDiscretePositionY(int initialDiscretePositionY) {
this.initialDiscretePositionY = initialDiscretePositionY; this.initialDiscretePositionY = initialDiscretePositionY;
} }
/**
* Gets initialDiscretePositionZ
*/
public int getinitialDiscretePositionZ() { public int getinitialDiscretePositionZ() {
return initialDiscretePositionZ; return initialDiscretePositionZ;
} }
/**
* Sets initialDiscretePositionZ
*/
public void setinitialDiscretePositionZ(int initialDiscretePositionZ) { public void setinitialDiscretePositionZ(int initialDiscretePositionZ) {
this.initialDiscretePositionZ = initialDiscretePositionZ; this.initialDiscretePositionZ = initialDiscretePositionZ;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.PLAYER_MESSAGE_TYPE_SET_ID: case TypeBytes.PLAYER_MESSAGE_TYPE_SET_ID:
@ -81,6 +122,9 @@ public class PlayerMessage extends NetworkMessage {
return false; return false;
} }
/**
* Parses a message of type Set_ID
*/
public static PlayerMessage parseSet_IDMessage(CircularByteBuffer byteBuffer){ public static PlayerMessage parseSet_IDMessage(CircularByteBuffer byteBuffer){
PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SET_ID); PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SET_ID);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -88,6 +132,9 @@ public class PlayerMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type Set_ID
*/
public static PlayerMessage constructSet_IDMessage(int playerID){ public static PlayerMessage constructSet_IDMessage(int playerID){
PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SET_ID); PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SET_ID);
rVal.setplayerID(playerID); rVal.setplayerID(playerID);
@ -95,6 +142,9 @@ public class PlayerMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type SetInitialDiscretePosition
*/
public static PlayerMessage parseSetInitialDiscretePositionMessage(CircularByteBuffer byteBuffer){ public static PlayerMessage parseSetInitialDiscretePositionMessage(CircularByteBuffer byteBuffer){
PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SETINITIALDISCRETEPOSITION); PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SETINITIALDISCRETEPOSITION);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -104,6 +154,9 @@ public class PlayerMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type SetInitialDiscretePosition
*/
public static PlayerMessage constructSetInitialDiscretePositionMessage(int initialDiscretePositionX,int initialDiscretePositionY,int initialDiscretePositionZ){ public static PlayerMessage constructSetInitialDiscretePositionMessage(int initialDiscretePositionX,int initialDiscretePositionY,int initialDiscretePositionZ){
PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SETINITIALDISCRETEPOSITION); PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SETINITIALDISCRETEPOSITION);
rVal.setinitialDiscretePositionX(initialDiscretePositionX); rVal.setinitialDiscretePositionX(initialDiscretePositionX);
@ -116,7 +169,6 @@ public class PlayerMessage extends NetworkMessage {
@Override @Override
void serialize(){ void serialize(){
byte[] intValues = new byte[8]; byte[] intValues = new byte[8];
byte[] stringBytes;
switch(this.messageType){ switch(this.messageType){
case SET_ID: case SET_ID:
rawBytes = new byte[2+4]; rawBytes = new byte[2+4];

View File

@ -1,19 +1,25 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import electrosphere.net.parser.util.ByteStreamUtils; import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList;
import java.util.List;
public class ServerMessage extends NetworkMessage { public class ServerMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum ServerMessageType { public enum ServerMessageType {
PING, PING,
PONG, PONG,
} }
/**
* The type of this message in particular.
*/
ServerMessageType messageType; ServerMessageType messageType;
/**
* Constructor
* @param messageType The type of this message
*/
ServerMessage(ServerMessageType messageType){ ServerMessage(ServerMessageType messageType){
this.type = MessageType.SERVER_MESSAGE; this.type = MessageType.SERVER_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -23,10 +29,20 @@ public class ServerMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.SERVER_MESSAGE_TYPE_PING: case TypeBytes.SERVER_MESSAGE_TYPE_PING:
@ -45,24 +61,36 @@ public class ServerMessage extends NetworkMessage {
return false; return false;
} }
/**
* Parses a message of type Ping
*/
public static ServerMessage parsePingMessage(CircularByteBuffer byteBuffer){ public static ServerMessage parsePingMessage(CircularByteBuffer byteBuffer){
ServerMessage rVal = new ServerMessage(ServerMessageType.PING); ServerMessage rVal = new ServerMessage(ServerMessageType.PING);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type Ping
*/
public static ServerMessage constructPingMessage(){ public static ServerMessage constructPingMessage(){
ServerMessage rVal = new ServerMessage(ServerMessageType.PING); ServerMessage rVal = new ServerMessage(ServerMessageType.PING);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Parses a message of type Pong
*/
public static ServerMessage parsePongMessage(CircularByteBuffer byteBuffer){ public static ServerMessage parsePongMessage(CircularByteBuffer byteBuffer){
ServerMessage rVal = new ServerMessage(ServerMessageType.PONG); ServerMessage rVal = new ServerMessage(ServerMessageType.PONG);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type Pong
*/
public static ServerMessage constructPongMessage(){ public static ServerMessage constructPongMessage(){
ServerMessage rVal = new ServerMessage(ServerMessageType.PONG); ServerMessage rVal = new ServerMessage(ServerMessageType.PONG);
rVal.serialize(); rVal.serialize();
@ -71,8 +99,6 @@ public class ServerMessage extends NetworkMessage {
@Override @Override
void serialize(){ void serialize(){
byte[] intValues = new byte[8];
byte[] stringBytes;
switch(this.messageType){ switch(this.messageType){
case PING: case PING:
rawBytes = new byte[2]; rawBytes = new byte[2];

View File

@ -1,12 +1,15 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class SynchronizationMessage extends NetworkMessage { public class SynchronizationMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum SynchronizationMessageType { public enum SynchronizationMessageType {
UPDATECLIENTSTATE, UPDATECLIENTSTATE,
UPDATECLIENTSTRINGSTATE, UPDATECLIENTSTRINGSTATE,
@ -21,6 +24,9 @@ public class SynchronizationMessage extends NetworkMessage {
LOADSCENE, LOADSCENE,
} }
/**
* The type of this message in particular.
*/
SynchronizationMessageType messageType; SynchronizationMessageType messageType;
int entityId; int entityId;
int bTreeId; int bTreeId;
@ -32,6 +38,10 @@ public class SynchronizationMessage extends NetworkMessage {
float floatValue; float floatValue;
double doubleValue; double doubleValue;
/**
* Constructor
* @param messageType The type of this message
*/
SynchronizationMessage(SynchronizationMessageType messageType){ SynchronizationMessage(SynchronizationMessageType messageType){
this.type = MessageType.SYNCHRONIZATION_MESSAGE; this.type = MessageType.SYNCHRONIZATION_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -41,82 +51,146 @@ public class SynchronizationMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets entityId
*/
public int getentityId() { public int getentityId() {
return entityId; return entityId;
} }
/**
* Sets entityId
*/
public void setentityId(int entityId) { public void setentityId(int entityId) {
this.entityId = entityId; this.entityId = entityId;
} }
/**
* Gets bTreeId
*/
public int getbTreeId() { public int getbTreeId() {
return bTreeId; return bTreeId;
} }
/**
* Sets bTreeId
*/
public void setbTreeId(int bTreeId) { public void setbTreeId(int bTreeId) {
this.bTreeId = bTreeId; this.bTreeId = bTreeId;
} }
/**
* Gets fieldId
*/
public int getfieldId() { public int getfieldId() {
return fieldId; return fieldId;
} }
/**
* Sets fieldId
*/
public void setfieldId(int fieldId) { public void setfieldId(int fieldId) {
this.fieldId = fieldId; this.fieldId = fieldId;
} }
/**
* Gets bTreeValue
*/
public int getbTreeValue() { public int getbTreeValue() {
return bTreeValue; return bTreeValue;
} }
/**
* Sets bTreeValue
*/
public void setbTreeValue(int bTreeValue) { public void setbTreeValue(int bTreeValue) {
this.bTreeValue = bTreeValue; this.bTreeValue = bTreeValue;
} }
/**
* Gets stringValue
*/
public String getstringValue() { public String getstringValue() {
return stringValue; return stringValue;
} }
/**
* Sets stringValue
*/
public void setstringValue(String stringValue) { public void setstringValue(String stringValue) {
this.stringValue = stringValue; this.stringValue = stringValue;
} }
/**
* Gets intValue
*/
public int getintValue() { public int getintValue() {
return intValue; return intValue;
} }
/**
* Sets intValue
*/
public void setintValue(int intValue) { public void setintValue(int intValue) {
this.intValue = intValue; this.intValue = intValue;
} }
/**
* Gets longValue
*/
public long getlongValue() { public long getlongValue() {
return longValue; return longValue;
} }
/**
* Sets longValue
*/
public void setlongValue(long longValue) { public void setlongValue(long longValue) {
this.longValue = longValue; this.longValue = longValue;
} }
/**
* Gets floatValue
*/
public float getfloatValue() { public float getfloatValue() {
return floatValue; return floatValue;
} }
/**
* Sets floatValue
*/
public void setfloatValue(float floatValue) { public void setfloatValue(float floatValue) {
this.floatValue = floatValue; this.floatValue = floatValue;
} }
/**
* Gets doubleValue
*/
public double getdoubleValue() { public double getdoubleValue() {
return doubleValue; return doubleValue;
} }
/**
* Sets doubleValue
*/
public void setdoubleValue(double doubleValue) { public void setdoubleValue(double doubleValue) {
this.doubleValue = doubleValue; this.doubleValue = doubleValue;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTATE: case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTATE:
@ -181,6 +255,9 @@ public class SynchronizationMessage extends NetworkMessage {
return false; return false;
} }
/**
* Parses a message of type UpdateClientState
*/
public static SynchronizationMessage parseUpdateClientStateMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseUpdateClientStateMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -191,6 +268,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type UpdateClientState
*/
public static SynchronizationMessage constructUpdateClientStateMessage(int entityId,int bTreeId,int fieldId,int bTreeValue){ public static SynchronizationMessage constructUpdateClientStateMessage(int entityId,int bTreeId,int fieldId,int bTreeValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTATE);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -201,6 +281,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type UpdateClientStringState can be parsed from the byte stream
*/
public static boolean canParseUpdateClientStringStateMessage(CircularByteBuffer byteBuffer){ public static boolean canParseUpdateClientStringStateMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -229,6 +312,9 @@ public class SynchronizationMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type UpdateClientStringState
*/
public static SynchronizationMessage parseUpdateClientStringStateMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseUpdateClientStringStateMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTRINGSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTRINGSTATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -239,6 +325,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type UpdateClientStringState
*/
public static SynchronizationMessage constructUpdateClientStringStateMessage(int entityId,int bTreeId,int fieldId,String stringValue){ public static SynchronizationMessage constructUpdateClientStringStateMessage(int entityId,int bTreeId,int fieldId,String stringValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTRINGSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTRINGSTATE);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -249,6 +338,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type UpdateClientIntState
*/
public static SynchronizationMessage parseUpdateClientIntStateMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseUpdateClientIntStateMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTINTSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTINTSTATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -259,6 +351,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type UpdateClientIntState
*/
public static SynchronizationMessage constructUpdateClientIntStateMessage(int entityId,int bTreeId,int fieldId,int intValue){ public static SynchronizationMessage constructUpdateClientIntStateMessage(int entityId,int bTreeId,int fieldId,int intValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTINTSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTINTSTATE);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -269,6 +364,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type UpdateClientLongState
*/
public static SynchronizationMessage parseUpdateClientLongStateMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseUpdateClientLongStateMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTLONGSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTLONGSTATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -279,6 +377,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type UpdateClientLongState
*/
public static SynchronizationMessage constructUpdateClientLongStateMessage(int entityId,int bTreeId,int fieldId,long longValue){ public static SynchronizationMessage constructUpdateClientLongStateMessage(int entityId,int bTreeId,int fieldId,long longValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTLONGSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTLONGSTATE);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -289,6 +390,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type UpdateClientFloatState
*/
public static SynchronizationMessage parseUpdateClientFloatStateMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseUpdateClientFloatStateMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTFLOATSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTFLOATSTATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -299,6 +403,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type UpdateClientFloatState
*/
public static SynchronizationMessage constructUpdateClientFloatStateMessage(int entityId,int bTreeId,int fieldId,float floatValue){ public static SynchronizationMessage constructUpdateClientFloatStateMessage(int entityId,int bTreeId,int fieldId,float floatValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTFLOATSTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTFLOATSTATE);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -309,6 +416,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type UpdateClientDoubleState
*/
public static SynchronizationMessage parseUpdateClientDoubleStateMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseUpdateClientDoubleStateMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTDOUBLESTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTDOUBLESTATE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -319,6 +429,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type UpdateClientDoubleState
*/
public static SynchronizationMessage constructUpdateClientDoubleStateMessage(int entityId,int bTreeId,int fieldId,double doubleValue){ public static SynchronizationMessage constructUpdateClientDoubleStateMessage(int entityId,int bTreeId,int fieldId,double doubleValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTDOUBLESTATE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTDOUBLESTATE);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -329,6 +442,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type ClientRequestBTreeAction
*/
public static SynchronizationMessage parseClientRequestBTreeActionMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseClientRequestBTreeActionMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.CLIENTREQUESTBTREEACTION); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.CLIENTREQUESTBTREEACTION);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -338,6 +454,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type ClientRequestBTreeAction
*/
public static SynchronizationMessage constructClientRequestBTreeActionMessage(int entityId,int bTreeId,int bTreeValue){ public static SynchronizationMessage constructClientRequestBTreeActionMessage(int entityId,int bTreeId,int bTreeValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.CLIENTREQUESTBTREEACTION); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.CLIENTREQUESTBTREEACTION);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -347,6 +466,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type ServerNotifyBTreeTransition
*/
public static SynchronizationMessage parseServerNotifyBTreeTransitionMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseServerNotifyBTreeTransitionMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.SERVERNOTIFYBTREETRANSITION); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.SERVERNOTIFYBTREETRANSITION);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -357,6 +479,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type ServerNotifyBTreeTransition
*/
public static SynchronizationMessage constructServerNotifyBTreeTransitionMessage(int entityId,int bTreeId,int fieldId,int bTreeValue){ public static SynchronizationMessage constructServerNotifyBTreeTransitionMessage(int entityId,int bTreeId,int fieldId,int bTreeValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.SERVERNOTIFYBTREETRANSITION); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.SERVERNOTIFYBTREETRANSITION);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -367,6 +492,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type AttachTree
*/
public static SynchronizationMessage parseAttachTreeMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseAttachTreeMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.ATTACHTREE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.ATTACHTREE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -375,6 +503,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type AttachTree
*/
public static SynchronizationMessage constructAttachTreeMessage(int entityId,int bTreeId){ public static SynchronizationMessage constructAttachTreeMessage(int entityId,int bTreeId){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.ATTACHTREE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.ATTACHTREE);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -383,6 +514,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type DetatchTree
*/
public static SynchronizationMessage parseDetatchTreeMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseDetatchTreeMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.DETATCHTREE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.DETATCHTREE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -391,6 +525,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type DetatchTree
*/
public static SynchronizationMessage constructDetatchTreeMessage(int entityId,int bTreeId){ public static SynchronizationMessage constructDetatchTreeMessage(int entityId,int bTreeId){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.DETATCHTREE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.DETATCHTREE);
rVal.setentityId(entityId); rVal.setentityId(entityId);
@ -399,6 +536,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type LoadScene can be parsed from the byte stream
*/
public static boolean canParseLoadSceneMessage(CircularByteBuffer byteBuffer){ public static boolean canParseLoadSceneMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -418,6 +558,9 @@ public class SynchronizationMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type LoadScene
*/
public static SynchronizationMessage parseLoadSceneMessage(CircularByteBuffer byteBuffer){ public static SynchronizationMessage parseLoadSceneMessage(CircularByteBuffer byteBuffer){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.LOADSCENE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.LOADSCENE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -425,6 +568,9 @@ public class SynchronizationMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type LoadScene
*/
public static SynchronizationMessage constructLoadSceneMessage(String stringValue){ public static SynchronizationMessage constructLoadSceneMessage(String stringValue){
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.LOADSCENE); SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.LOADSCENE);
rVal.setstringValue(stringValue); rVal.setstringValue(stringValue);

View File

@ -1,12 +1,15 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
import io.github.studiorailgun.CircularByteBuffer;
import electrosphere.net.parser.util.ByteStreamUtils; import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class TerrainMessage extends NetworkMessage { public class TerrainMessage extends NetworkMessage {
/**
* The types of messages available in this category.
*/
public enum TerrainMessageType { public enum TerrainMessageType {
REQUESTMETADATA, REQUESTMETADATA,
RESPONSEMETADATA, RESPONSEMETADATA,
@ -23,6 +26,9 @@ public class TerrainMessage extends NetworkMessage {
UPDATEFLUIDDATA, UPDATEFLUIDDATA,
} }
/**
* The type of this message in particular.
*/
TerrainMessageType messageType; TerrainMessageType messageType;
int worldSizeDiscrete; int worldSizeDiscrete;
int dynamicInterpolationRatio; int dynamicInterpolationRatio;
@ -46,6 +52,10 @@ public class TerrainMessage extends NetworkMessage {
float terrainWeight; float terrainWeight;
int terrainValue; int terrainValue;
/**
* Constructor
* @param messageType The type of this message
*/
TerrainMessage(TerrainMessageType messageType){ TerrainMessage(TerrainMessageType messageType){
this.type = MessageType.TERRAIN_MESSAGE; this.type = MessageType.TERRAIN_MESSAGE;
this.messageType = messageType; this.messageType = messageType;
@ -55,178 +65,314 @@ public class TerrainMessage extends NetworkMessage {
return this.messageType; return this.messageType;
} }
/**
* Gets worldSizeDiscrete
*/
public int getworldSizeDiscrete() { public int getworldSizeDiscrete() {
return worldSizeDiscrete; return worldSizeDiscrete;
} }
/**
* Sets worldSizeDiscrete
*/
public void setworldSizeDiscrete(int worldSizeDiscrete) { public void setworldSizeDiscrete(int worldSizeDiscrete) {
this.worldSizeDiscrete = worldSizeDiscrete; this.worldSizeDiscrete = worldSizeDiscrete;
} }
/**
* Gets dynamicInterpolationRatio
*/
public int getdynamicInterpolationRatio() { public int getdynamicInterpolationRatio() {
return dynamicInterpolationRatio; return dynamicInterpolationRatio;
} }
/**
* Sets dynamicInterpolationRatio
*/
public void setdynamicInterpolationRatio(int dynamicInterpolationRatio) { public void setdynamicInterpolationRatio(int dynamicInterpolationRatio) {
this.dynamicInterpolationRatio = dynamicInterpolationRatio; this.dynamicInterpolationRatio = dynamicInterpolationRatio;
} }
/**
* Gets randomDampener
*/
public float getrandomDampener() { public float getrandomDampener() {
return randomDampener; return randomDampener;
} }
/**
* Sets randomDampener
*/
public void setrandomDampener(float randomDampener) { public void setrandomDampener(float randomDampener) {
this.randomDampener = randomDampener; this.randomDampener = randomDampener;
} }
/**
* Gets worldMinX
*/
public int getworldMinX() { public int getworldMinX() {
return worldMinX; return worldMinX;
} }
/**
* Sets worldMinX
*/
public void setworldMinX(int worldMinX) { public void setworldMinX(int worldMinX) {
this.worldMinX = worldMinX; this.worldMinX = worldMinX;
} }
/**
* Gets worldMinY
*/
public int getworldMinY() { public int getworldMinY() {
return worldMinY; return worldMinY;
} }
/**
* Sets worldMinY
*/
public void setworldMinY(int worldMinY) { public void setworldMinY(int worldMinY) {
this.worldMinY = worldMinY; this.worldMinY = worldMinY;
} }
/**
* Gets worldMaxX
*/
public int getworldMaxX() { public int getworldMaxX() {
return worldMaxX; return worldMaxX;
} }
/**
* Sets worldMaxX
*/
public void setworldMaxX(int worldMaxX) { public void setworldMaxX(int worldMaxX) {
this.worldMaxX = worldMaxX; this.worldMaxX = worldMaxX;
} }
/**
* Gets worldMaxY
*/
public int getworldMaxY() { public int getworldMaxY() {
return worldMaxY; return worldMaxY;
} }
/**
* Sets worldMaxY
*/
public void setworldMaxY(int worldMaxY) { public void setworldMaxY(int worldMaxY) {
this.worldMaxY = worldMaxY; this.worldMaxY = worldMaxY;
} }
/**
* Gets value
*/
public float getvalue() { public float getvalue() {
return value; return value;
} }
/**
* Sets value
*/
public void setvalue(float value) { public void setvalue(float value) {
this.value = value; this.value = value;
} }
/**
* Gets worldX
*/
public int getworldX() { public int getworldX() {
return worldX; return worldX;
} }
/**
* Sets worldX
*/
public void setworldX(int worldX) { public void setworldX(int worldX) {
this.worldX = worldX; this.worldX = worldX;
} }
/**
* Gets worldY
*/
public int getworldY() { public int getworldY() {
return worldY; return worldY;
} }
/**
* Sets worldY
*/
public void setworldY(int worldY) { public void setworldY(int worldY) {
this.worldY = worldY; this.worldY = worldY;
} }
/**
* Gets worldZ
*/
public int getworldZ() { public int getworldZ() {
return worldZ; return worldZ;
} }
/**
* Sets worldZ
*/
public void setworldZ(int worldZ) { public void setworldZ(int worldZ) {
this.worldZ = worldZ; this.worldZ = worldZ;
} }
/**
* Gets voxelX
*/
public int getvoxelX() { public int getvoxelX() {
return voxelX; return voxelX;
} }
/**
* Sets voxelX
*/
public void setvoxelX(int voxelX) { public void setvoxelX(int voxelX) {
this.voxelX = voxelX; this.voxelX = voxelX;
} }
/**
* Gets voxelY
*/
public int getvoxelY() { public int getvoxelY() {
return voxelY; return voxelY;
} }
/**
* Sets voxelY
*/
public void setvoxelY(int voxelY) { public void setvoxelY(int voxelY) {
this.voxelY = voxelY; this.voxelY = voxelY;
} }
/**
* Gets voxelZ
*/
public int getvoxelZ() { public int getvoxelZ() {
return voxelZ; return voxelZ;
} }
/**
* Sets voxelZ
*/
public void setvoxelZ(int voxelZ) { public void setvoxelZ(int voxelZ) {
this.voxelZ = voxelZ; this.voxelZ = voxelZ;
} }
/**
* Gets realLocationX
*/
public double getrealLocationX() { public double getrealLocationX() {
return realLocationX; return realLocationX;
} }
/**
* Sets realLocationX
*/
public void setrealLocationX(double realLocationX) { public void setrealLocationX(double realLocationX) {
this.realLocationX = realLocationX; this.realLocationX = realLocationX;
} }
/**
* Gets realLocationY
*/
public double getrealLocationY() { public double getrealLocationY() {
return realLocationY; return realLocationY;
} }
/**
* Sets realLocationY
*/
public void setrealLocationY(double realLocationY) { public void setrealLocationY(double realLocationY) {
this.realLocationY = realLocationY; this.realLocationY = realLocationY;
} }
/**
* Gets realLocationZ
*/
public double getrealLocationZ() { public double getrealLocationZ() {
return realLocationZ; return realLocationZ;
} }
/**
* Sets realLocationZ
*/
public void setrealLocationZ(double realLocationZ) { public void setrealLocationZ(double realLocationZ) {
this.realLocationZ = realLocationZ; this.realLocationZ = realLocationZ;
} }
/**
* Gets chunkData
*/
public byte[] getchunkData() { public byte[] getchunkData() {
return chunkData; return chunkData;
} }
/**
* Sets chunkData
*/
public void setchunkData(byte[] chunkData) { public void setchunkData(byte[] chunkData) {
this.chunkData = chunkData; this.chunkData = chunkData;
} }
/**
* Gets chunkResolution
*/
public int getchunkResolution() { public int getchunkResolution() {
return chunkResolution; return chunkResolution;
} }
/**
* Sets chunkResolution
*/
public void setchunkResolution(int chunkResolution) { public void setchunkResolution(int chunkResolution) {
this.chunkResolution = chunkResolution; this.chunkResolution = chunkResolution;
} }
/**
* Gets terrainWeight
*/
public float getterrainWeight() { public float getterrainWeight() {
return terrainWeight; return terrainWeight;
} }
/**
* Sets terrainWeight
*/
public void setterrainWeight(float terrainWeight) { public void setterrainWeight(float terrainWeight) {
this.terrainWeight = terrainWeight; this.terrainWeight = terrainWeight;
} }
/**
* Gets terrainValue
*/
public int getterrainValue() { public int getterrainValue() {
return terrainValue; return terrainValue;
} }
/**
* Sets terrainValue
*/
public void setterrainValue(int terrainValue) { public void setterrainValue(int terrainValue) {
this.terrainValue = terrainValue; this.terrainValue = terrainValue;
} }
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
*/
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
/**
* Checks if this message can be parsed (ie are all bytes present)
* @param byteBuffer The buffer
* @param secondByte The second byte, signifying the subtype of the message
* @return true if the message can be parsed, false otherwise
*/
public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){ public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
switch(secondByte){ switch(secondByte){
case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA: case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA:
@ -295,18 +441,27 @@ public class TerrainMessage extends NetworkMessage {
return false; return false;
} }
/**
* Parses a message of type RequestMetadata
*/
public static TerrainMessage parseRequestMetadataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseRequestMetadataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestMetadata
*/
public static TerrainMessage constructRequestMetadataMessage(){ public static TerrainMessage constructRequestMetadataMessage(){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
/**
* Parses a message of type ResponseMetadata
*/
public static TerrainMessage parseResponseMetadataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseResponseMetadataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -320,6 +475,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type ResponseMetadata
*/
public static TerrainMessage constructResponseMetadataMessage(int worldSizeDiscrete,int dynamicInterpolationRatio,float randomDampener,int worldMinX,int worldMinY,int worldMaxX,int worldMaxY){ public static TerrainMessage constructResponseMetadataMessage(int worldSizeDiscrete,int dynamicInterpolationRatio,float randomDampener,int worldMinX,int worldMinY,int worldMaxX,int worldMaxY){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA);
rVal.setworldSizeDiscrete(worldSizeDiscrete); rVal.setworldSizeDiscrete(worldSizeDiscrete);
@ -333,6 +491,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type RequestEditVoxel
*/
public static TerrainMessage parseRequestEditVoxelMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseRequestEditVoxelMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -347,6 +508,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestEditVoxel
*/
public static TerrainMessage constructRequestEditVoxelMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,float terrainWeight,int terrainValue){ public static TerrainMessage constructRequestEditVoxelMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,float terrainWeight,int terrainValue){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -361,6 +525,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type UpdateVoxel
*/
public static TerrainMessage parseUpdateVoxelMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseUpdateVoxelMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -375,6 +542,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type UpdateVoxel
*/
public static TerrainMessage constructUpdateVoxelMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,float terrainWeight,int terrainValue){ public static TerrainMessage constructUpdateVoxelMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,float terrainWeight,int terrainValue){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -389,6 +559,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type RequestUseTerrainPalette
*/
public static TerrainMessage parseRequestUseTerrainPaletteMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseRequestUseTerrainPaletteMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -401,6 +574,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestUseTerrainPalette
*/
public static TerrainMessage constructRequestUseTerrainPaletteMessage(double realLocationX,double realLocationY,double realLocationZ,float value,float terrainWeight,int terrainValue){ public static TerrainMessage constructRequestUseTerrainPaletteMessage(double realLocationX,double realLocationY,double realLocationZ,float value,float terrainWeight,int terrainValue){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE);
rVal.setrealLocationX(realLocationX); rVal.setrealLocationX(realLocationX);
@ -413,6 +589,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type SpawnPosition
*/
public static TerrainMessage parseSpawnPositionMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseSpawnPositionMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -422,6 +601,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type SpawnPosition
*/
public static TerrainMessage constructSpawnPositionMessage(double realLocationX,double realLocationY,double realLocationZ){ public static TerrainMessage constructSpawnPositionMessage(double realLocationX,double realLocationY,double realLocationZ){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION);
rVal.setrealLocationX(realLocationX); rVal.setrealLocationX(realLocationX);
@ -431,6 +613,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type RequestChunkData
*/
public static TerrainMessage parseRequestChunkDataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseRequestChunkDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -440,6 +625,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestChunkData
*/
public static TerrainMessage constructRequestChunkDataMessage(int worldX,int worldY,int worldZ){ public static TerrainMessage constructRequestChunkDataMessage(int worldX,int worldY,int worldZ){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -449,6 +637,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type sendChunkData can be parsed from the byte stream
*/
public static boolean canParsesendChunkDataMessage(CircularByteBuffer byteBuffer){ public static boolean canParsesendChunkDataMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -477,6 +668,9 @@ public class TerrainMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type sendChunkData
*/
public static TerrainMessage parsesendChunkDataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parsesendChunkDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -487,6 +681,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type sendChunkData
*/
public static TerrainMessage constructsendChunkDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){ public static TerrainMessage constructsendChunkDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -497,6 +694,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type RequestReducedChunkData
*/
public static TerrainMessage parseRequestReducedChunkDataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseRequestReducedChunkDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -507,6 +707,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestReducedChunkData
*/
public static TerrainMessage constructRequestReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution){ public static TerrainMessage constructRequestReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -517,6 +720,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type SendReducedChunkData can be parsed from the byte stream
*/
public static boolean canParseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){ public static boolean canParseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -548,6 +754,9 @@ public class TerrainMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type SendReducedChunkData
*/
public static TerrainMessage parseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -559,6 +768,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type SendReducedChunkData
*/
public static TerrainMessage constructSendReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,byte[] chunkData){ public static TerrainMessage constructSendReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,byte[] chunkData){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -570,6 +782,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Parses a message of type RequestFluidData
*/
public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -579,6 +794,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type RequestFluidData
*/
public static TerrainMessage constructRequestFluidDataMessage(int worldX,int worldY,int worldZ){ public static TerrainMessage constructRequestFluidDataMessage(int worldX,int worldY,int worldZ){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -588,6 +806,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type sendFluidData can be parsed from the byte stream
*/
public static boolean canParsesendFluidDataMessage(CircularByteBuffer byteBuffer){ public static boolean canParsesendFluidDataMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -616,6 +837,9 @@ public class TerrainMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type sendFluidData
*/
public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -626,6 +850,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type sendFluidData
*/
public static TerrainMessage constructsendFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){ public static TerrainMessage constructsendFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -636,6 +863,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type updateFluidData can be parsed from the byte stream
*/
public static boolean canParseupdateFluidDataMessage(CircularByteBuffer byteBuffer){ public static boolean canParseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining(); int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>(); List<Byte> temporaryByteQueue = new LinkedList<Byte>();
@ -664,6 +894,9 @@ public class TerrainMessage extends NetworkMessage {
return true; return true;
} }
/**
* Parses a message of type updateFluidData
*/
public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer){ public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
@ -674,6 +907,9 @@ public class TerrainMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Constructs a message of type updateFluidData
*/
public static TerrainMessage constructupdateFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){ public static TerrainMessage constructupdateFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA); TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
rVal.setworldX(worldX); rVal.setworldX(worldX);
@ -687,7 +923,6 @@ public class TerrainMessage extends NetworkMessage {
@Override @Override
void serialize(){ void serialize(){
byte[] intValues = new byte[8]; byte[] intValues = new byte[8];
byte[] stringBytes;
switch(this.messageType){ switch(this.messageType){
case REQUESTMETADATA: case REQUESTMETADATA:
rawBytes = new byte[2]; rawBytes = new byte[2];

View File

@ -1,10 +1,12 @@
package electrosphere.net.parser.net.message; package electrosphere.net.parser.net.message;
/**
* The constants used in serializing/deserializing messages
*/
public class TypeBytes { public class TypeBytes {
/* /**
Message categories * Message categories
*/ */
public static final byte MESSAGE_TYPE_ENTITY = 0; public static final byte MESSAGE_TYPE_ENTITY = 0;
public static final byte MESSAGE_TYPE_LORE = 1; public static final byte MESSAGE_TYPE_LORE = 1;
public static final byte MESSAGE_TYPE_PLAYER = 2; public static final byte MESSAGE_TYPE_PLAYER = 2;
@ -16,7 +18,7 @@ Message categories
public static final byte MESSAGE_TYPE_SYNCHRONIZATION = 8; public static final byte MESSAGE_TYPE_SYNCHRONIZATION = 8;
public static final byte MESSAGE_TYPE_COMBAT = 9; public static final byte MESSAGE_TYPE_COMBAT = 9;
/* /*
Entity subcategories Entity subcategories
*/ */
public static final byte ENTITY_MESSAGE_TYPE_CREATE = 0; public static final byte ENTITY_MESSAGE_TYPE_CREATE = 0;
public static final byte ENTITY_MESSAGE_TYPE_MOVEUPDATE = 1; public static final byte ENTITY_MESSAGE_TYPE_MOVEUPDATE = 1;
@ -29,7 +31,7 @@ Message categories
public static final byte ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR = 8; public static final byte ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR = 8;
public static final byte ENTITY_MESSAGE_TYPE_SYNCPHYSICS = 9; public static final byte ENTITY_MESSAGE_TYPE_SYNCPHYSICS = 9;
/* /*
Entity packet sizes Entity packet sizes
*/ */
public static final byte ENTITY_MESSAGE_TYPE_MOVEUPDATE_SIZE = 86; public static final byte ENTITY_MESSAGE_TYPE_MOVEUPDATE_SIZE = 86;
public static final byte ENTITY_MESSAGE_TYPE_ATTACKUPDATE_SIZE = 74; public static final byte ENTITY_MESSAGE_TYPE_ATTACKUPDATE_SIZE = 74;
@ -39,27 +41,30 @@ Message categories
public static final byte ENTITY_MESSAGE_TYPE_SETPROPERTY_SIZE = 22; public static final byte ENTITY_MESSAGE_TYPE_SETPROPERTY_SIZE = 22;
public static final byte ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR_SIZE = 34; public static final byte ENTITY_MESSAGE_TYPE_UPDATEENTITYVIEWDIR_SIZE = 34;
public static final short ENTITY_MESSAGE_TYPE_SYNCPHYSICS_SIZE = 166; public static final short ENTITY_MESSAGE_TYPE_SYNCPHYSICS_SIZE = 166;
/* /*
Lore subcategories Lore subcategories
*/ */
public static final byte LORE_MESSAGE_TYPE_REQUESTRACES = 0; public static final byte LORE_MESSAGE_TYPE_REQUESTRACES = 0;
public static final byte LORE_MESSAGE_TYPE_RESPONSERACES = 1; public static final byte LORE_MESSAGE_TYPE_RESPONSERACES = 1;
/* /*
Lore packet sizes Lore packet sizes
*/ */
public static final byte LORE_MESSAGE_TYPE_REQUESTRACES_SIZE = 2; public static final byte LORE_MESSAGE_TYPE_REQUESTRACES_SIZE = 2;
/* /*
Player subcategories Player subcategories
*/ */
public static final byte PLAYER_MESSAGE_TYPE_SET_ID = 0; public static final byte PLAYER_MESSAGE_TYPE_SET_ID = 0;
public static final byte PLAYER_MESSAGE_TYPE_SETINITIALDISCRETEPOSITION = 1; public static final byte PLAYER_MESSAGE_TYPE_SETINITIALDISCRETEPOSITION = 1;
/* /*
Player packet sizes Player packet sizes
*/ */
public static final byte PLAYER_MESSAGE_TYPE_SET_ID_SIZE = 6; public static final byte PLAYER_MESSAGE_TYPE_SET_ID_SIZE = 6;
public static final byte PLAYER_MESSAGE_TYPE_SETINITIALDISCRETEPOSITION_SIZE = 14; public static final byte PLAYER_MESSAGE_TYPE_SETINITIALDISCRETEPOSITION_SIZE = 14;
/* /*
Terrain subcategories Terrain subcategories
*/ */
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTMETADATA = 0; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTMETADATA = 0;
public static final byte TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA = 1; public static final byte TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA = 1;
@ -75,7 +80,7 @@ Message categories
public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 11; public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 11;
public static final byte TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA = 12; public static final byte TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA = 12;
/* /*
Terrain packet sizes Terrain packet sizes
*/ */
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTMETADATA_SIZE = 2; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTMETADATA_SIZE = 2;
public static final byte TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA_SIZE = 30; public static final byte TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA_SIZE = 30;
@ -86,31 +91,34 @@ Message categories
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA_SIZE = 14; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA_SIZE = 14;
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA_SIZE = 18; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA_SIZE = 18;
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE = 14; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE = 14;
/* /*
Server subcategories Server subcategories
*/ */
public static final byte SERVER_MESSAGE_TYPE_PING = 0; public static final byte SERVER_MESSAGE_TYPE_PING = 0;
public static final byte SERVER_MESSAGE_TYPE_PONG = 1; public static final byte SERVER_MESSAGE_TYPE_PONG = 1;
/* /*
Server packet sizes Server packet sizes
*/ */
public static final byte SERVER_MESSAGE_TYPE_PING_SIZE = 2; public static final byte SERVER_MESSAGE_TYPE_PING_SIZE = 2;
public static final byte SERVER_MESSAGE_TYPE_PONG_SIZE = 2; public static final byte SERVER_MESSAGE_TYPE_PONG_SIZE = 2;
/* /*
Auth subcategories Auth subcategories
*/ */
public static final byte AUTH_MESSAGE_TYPE_AUTHREQUEST = 0; public static final byte AUTH_MESSAGE_TYPE_AUTHREQUEST = 0;
public static final byte AUTH_MESSAGE_TYPE_AUTHDETAILS = 1; public static final byte AUTH_MESSAGE_TYPE_AUTHDETAILS = 1;
public static final byte AUTH_MESSAGE_TYPE_AUTHSUCCESS = 2; public static final byte AUTH_MESSAGE_TYPE_AUTHSUCCESS = 2;
public static final byte AUTH_MESSAGE_TYPE_AUTHFAILURE = 3; public static final byte AUTH_MESSAGE_TYPE_AUTHFAILURE = 3;
/* /*
Auth packet sizes Auth packet sizes
*/ */
public static final byte AUTH_MESSAGE_TYPE_AUTHREQUEST_SIZE = 2; public static final byte AUTH_MESSAGE_TYPE_AUTHREQUEST_SIZE = 2;
public static final byte AUTH_MESSAGE_TYPE_AUTHSUCCESS_SIZE = 2; public static final byte AUTH_MESSAGE_TYPE_AUTHSUCCESS_SIZE = 2;
public static final byte AUTH_MESSAGE_TYPE_AUTHFAILURE_SIZE = 2; public static final byte AUTH_MESSAGE_TYPE_AUTHFAILURE_SIZE = 2;
/* /*
Character subcategories Character subcategories
*/ */
public static final byte CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST = 0; public static final byte CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST = 0;
public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECHARACTERLIST = 1; public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECHARACTERLIST = 1;
@ -120,14 +128,15 @@ Message categories
public static final byte CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER = 5; public static final byte CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER = 5;
public static final byte CHARACTER_MESSAGE_TYPE_RESPONSESPAWNCHARACTER = 6; public static final byte CHARACTER_MESSAGE_TYPE_RESPONSESPAWNCHARACTER = 6;
/* /*
Character packet sizes Character packet sizes
*/ */
public static final byte CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST_SIZE = 2; public static final byte CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST_SIZE = 2;
public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERSUCCESS_SIZE = 2; public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERSUCCESS_SIZE = 2;
public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE_SIZE = 2; public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE_SIZE = 2;
public static final byte CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER_SIZE = 2; public static final byte CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER_SIZE = 2;
/* /*
Inventory subcategories Inventory subcategories
*/ */
public static final byte INVENTORY_MESSAGE_TYPE_ADDITEMTOINVENTORY = 0; public static final byte INVENTORY_MESSAGE_TYPE_ADDITEMTOINVENTORY = 0;
public static final byte INVENTORY_MESSAGE_TYPE_REMOVEITEMFROMINVENTORY = 1; public static final byte INVENTORY_MESSAGE_TYPE_REMOVEITEMFROMINVENTORY = 1;
@ -141,14 +150,15 @@ Message categories
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR = 9; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR = 9;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION = 10; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION = 10;
/* /*
Inventory packet sizes Inventory packet sizes
*/ */
public static final byte INVENTORY_MESSAGE_TYPE_REMOVEITEMFROMINVENTORY_SIZE = 6; public static final byte INVENTORY_MESSAGE_TYPE_REMOVEITEMFROMINVENTORY_SIZE = 6;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR_SIZE = 10; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDTOOLBAR_SIZE = 10;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL_SIZE = 6; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL_SIZE = 6;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR_SIZE = 6; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR_SIZE = 6;
/* /*
Synchronization subcategories Synchronization subcategories
*/ */
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTATE = 0; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTATE = 0;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTRINGSTATE = 1; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTRINGSTATE = 1;
@ -162,7 +172,7 @@ Message categories
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE = 9; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE = 9;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_LOADSCENE = 10; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_LOADSCENE = 10;
/* /*
Synchronization packet sizes Synchronization packet sizes
*/ */
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTATE_SIZE = 18; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTATE_SIZE = 18;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTINTSTATE_SIZE = 18; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTINTSTATE_SIZE = 18;
@ -173,12 +183,14 @@ Message categories
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_SERVERNOTIFYBTREETRANSITION_SIZE = 18; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_SERVERNOTIFYBTREETRANSITION_SIZE = 18;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE_SIZE = 10; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_ATTACHTREE_SIZE = 10;
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE_SIZE = 10; public static final byte SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE_SIZE = 10;
/* /*
Combat subcategories Combat subcategories
*/ */
public static final byte COMBAT_MESSAGE_TYPE_SERVERREPORTHITBOXCOLLISION = 0; public static final byte COMBAT_MESSAGE_TYPE_SERVERREPORTHITBOXCOLLISION = 0;
/* /*
Combat packet sizes Combat packet sizes
*/ */
} }

View File

@ -1,120 +0,0 @@
package electrosphere.net.parser.net.raw;
import java.util.concurrent.Semaphore;
/**
* A circular byte buffer optimized for high throughput (relative to a list) and peaking at early elements of the current position.
*/
public class CircularByteBuffer {
//The array backing this circular byte buffer
byte[] backingArray;
//the current read position of the buffer in the backing array
int position;
//the remaining bytes to read before the read position equals the write position
int remaining;
//the capacity of the backing array
int capacity;
//Lock to make the structure threadsafe
Semaphore lock = new Semaphore(1);
/**
* Constructs a CircularByteBuffer
* @param capacity The capacity of the backing array in bytes
*/
public CircularByteBuffer(int capacity){
backingArray = new byte[capacity];
position = 0;
remaining = 0;
this.capacity = capacity;
}
/**
* Adds an array of bytes to the circular buffer
* @param bytes The bytes
* @param len The number of bytes to pull from the array bytes
*/
public void add(byte[] bytes, int len){
lock.acquireUninterruptibly();
// System.out.println("Add start");
int writePosition = (position + remaining) % capacity;
//amount possible to write before wrapping
int writeBeforeWrap = capacity - writePosition;
//only run wrapping logic if necessary
if(len > writeBeforeWrap){
System.arraycopy(bytes, 0, backingArray, writePosition, writeBeforeWrap);
System.arraycopy(bytes, writeBeforeWrap, backingArray, 0, len - writeBeforeWrap);
} else {
System.arraycopy(bytes, 0, backingArray, writePosition, len);
}
remaining = remaining + len;
lock.release();
}
/**
* Peeks at the next element in the buffer
* @return The value of the byte next in the buffer
*/
public byte peek(){
byte rVal = peek(0);
return rVal;
}
/**
* Peeks at an element @param offset elements further along the buffer from the current position
* @param offset The offset, in bytes, to look forward in the buffer
* @return The value of the byte at the current position + @param offset
*/
public byte peek(int offset){
lock.acquireUninterruptibly();
byte rVal = backingArray[(position + offset) % capacity];
lock.release();
return rVal;
}
/**
* Gets the remaining number of bytes in the buffer
* @return The remaining number of bytes
*/
public int getRemaining(){
lock.acquireUninterruptibly();
int rVal = remaining;
lock.release();
return rVal;
}
/**
* Gets the capacity of the buffer
* @return The capacity
*/
public int getCapacity(){
lock.acquireUninterruptibly();
int rVal = capacity;
lock.release();
return rVal;
}
/**
* Reads a given number of bytes from the buffer
* @param len The number of bytes to read
* @return The bytes in an array
*/
public byte[] read(int len){
lock.acquireUninterruptibly();
byte[] rVal = new byte[len];
//amount possible to read before loop
int toReadBeforeLoop = capacity - position;
if(len > capacity - position){
System.arraycopy(backingArray, position, rVal, 0, toReadBeforeLoop);
System.arraycopy(backingArray, 0, rVal, toReadBeforeLoop, len - toReadBeforeLoop);
} else {
System.arraycopy(backingArray, position, rVal, 0, len);
}
position = (position + len) % capacity;
remaining = remaining - len;
lock.release();
return rVal;
}
}

View File

@ -1,76 +1,132 @@
package electrosphere.net.parser.net.raw; package electrosphere.net.parser.net.raw;
import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.NetworkMessage;
import io.github.studiorailgun.CircularByteBuffer;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
/**
* The main message parser. This is used to serialize/deserialize messages to/from the provided streams.
*/
public class NetworkParser { public class NetworkParser {
/**
* The size of the read buffer
*/
static final int READ_BLOCK_SIZE = 16 * 1024 * 1024;
/**
* The size of the circular buffer
*/
static final int CIRCULAR_BUFFER_SIZE = 64 * 1024 * 1024;
/**
* The input stream for the parser
*/
InputStream incomingStream; InputStream incomingStream;
/**
* The output stream for the parser
*/
OutputStream outgoingStream; OutputStream outgoingStream;
/**
* The queue of incoming messages that have been parsed
*/
CopyOnWriteArrayList<NetworkMessage> incomingMessageQueue = new CopyOnWriteArrayList<NetworkMessage>(); CopyOnWriteArrayList<NetworkMessage> incomingMessageQueue = new CopyOnWriteArrayList<NetworkMessage>();
/**
* The queue of outgoing messages that have yet to be sent
*/
CopyOnWriteArrayList<NetworkMessage> outgoingMessageQueue = new CopyOnWriteArrayList<NetworkMessage>(); CopyOnWriteArrayList<NetworkMessage> outgoingMessageQueue = new CopyOnWriteArrayList<NetworkMessage>();
CircularByteBuffer incomingByteBuffer = new CircularByteBuffer(64 * 1024 * 124); /**
* The byte buffer for storing incoming bytes
*/
CircularByteBuffer incomingByteBuffer = new CircularByteBuffer(CIRCULAR_BUFFER_SIZE);
/**
* The block array used to read blocks of bytes in
*/
byte[] readBuffer = new byte[READ_BLOCK_SIZE];
/**
* The outgoing byte buffer
*/
CopyOnWriteArrayList<Byte> outgoingByteQueue = new CopyOnWriteArrayList<Byte>(); CopyOnWriteArrayList<Byte> outgoingByteQueue = new CopyOnWriteArrayList<Byte>();
/**
* The number of bytes read
*/
long totalBytesRead = 0;
/**
* Constructor
* @param incomingStream The stream of incoming bytes
* @param outgoingStream The stream of outgoing bytes
*/
public NetworkParser(InputStream incomingStream, OutputStream outgoingStream){ public NetworkParser(InputStream incomingStream, OutputStream outgoingStream){
this.incomingStream = incomingStream; this.incomingStream = incomingStream;
this.outgoingStream = outgoingStream; this.outgoingStream = outgoingStream;
} }
public void start(){ /**
* Reads messages from the input stream
} */
public void readMessagesIn() throws IOException {
static final int READ_BUFFER_SIZE = 64 * 1024 * 1024; //read in bytes
byte[] readBuffer = new byte[READ_BUFFER_SIZE]; int bytesRead = 0;
public void readMessagesIn(){ while(incomingStream.available() > 0){
try { // nextValue = incomingStream.read();
//read in bytes bytesRead = incomingStream.read(readBuffer, 0, READ_BLOCK_SIZE);
int bytesRead = 0; if(bytesRead > 0){
byte currentByte = -1; incomingByteBuffer.add(readBuffer, bytesRead);
while(incomingStream.available() > 0){
// nextValue = incomingStream.read();
bytesRead = incomingStream.read(readBuffer, 0, READ_BUFFER_SIZE);
if(bytesRead > 0){
incomingByteBuffer.add(readBuffer, bytesRead);
}
} }
//parse byte queue for messages totalBytesRead = totalBytesRead + bytesRead;
//for each message, append to clientIncomingMessageQueue }
NetworkMessage newMessage; //parse byte queue for messages
while((newMessage = NetworkMessage.parseBytestreamForMessage(incomingByteBuffer))!=null){ //for each message, append to clientIncomingMessageQueue
incomingMessageQueue.add(newMessage); NetworkMessage newMessage;
} while((newMessage = NetworkMessage.parseBytestreamForMessage(incomingByteBuffer))!=null){
} catch (IOException ex) { incomingMessageQueue.add(newMessage);
ex.printStackTrace();
System.exit(0);
} }
} }
/**
* Pushes messages out across the output stream
* @throws IOException Thrown if a message fails to serialize or the output stream fails to write
*/
public void pushMessagesOut() throws IOException { public void pushMessagesOut() throws IOException {
for(NetworkMessage message : outgoingMessageQueue){ for(NetworkMessage message : outgoingMessageQueue){
outgoingMessageQueue.remove(message); outgoingMessageQueue.remove(message);
// System.out.println("Write message of type " + message.getType());
outgoingStream.write(message.getRawBytes()); outgoingStream.write(message.getRawBytes());
} }
} }
/**
* Checks if there is a fully parsed incoming message in the queue
* @return true if there is message in the queue, false otherwise
*/
public boolean hasIncomingMessaage(){ public boolean hasIncomingMessaage(){
return incomingMessageQueue.size() > 0; return incomingMessageQueue.size() > 0;
} }
/**
* Pops a fully parsed incoming message from the queue
* @return The message
*/
public NetworkMessage popIncomingMessage(){ public NetworkMessage popIncomingMessage(){
return incomingMessageQueue.remove(0); return incomingMessageQueue.remove(0);
} }
/**
* Adds a message to the outgoing queue
* @param message The message
*/
public void addOutgoingMessage(NetworkMessage message){ public void addOutgoingMessage(NetworkMessage message){
outgoingMessageQueue.add(message); outgoingMessageQueue.add(message);
} }
@ -90,5 +146,13 @@ public class NetworkParser {
public void copyOutgoingMessages(List<NetworkMessage> messages){ public void copyOutgoingMessages(List<NetworkMessage> messages){
messages.addAll(outgoingMessageQueue); messages.addAll(outgoingMessageQueue);
} }
/**
* Gets the total number of bytes read by this connection
* @return The total number of bytes
*/
public long getNumberOfBytesRead(){
return totalBytesRead;
}
} }

View File

@ -1,6 +1,6 @@
package electrosphere.net.parser.util; package electrosphere.net.parser.util;
import electrosphere.net.parser.net.raw.CircularByteBuffer; import io.github.studiorailgun.CircularByteBuffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List; import java.util.List;

View File

@ -67,7 +67,7 @@ public class MessageProtocol {
* @param message The message * @param message The message
*/ */
public void handleAsyncMessage(NetworkMessage message){ public void handleAsyncMessage(NetworkMessage message){
Globals.profiler.beginAggregateCpuSample("MessageProtocol(client).handleAsyncMessage"); Globals.profiler.beginAggregateCpuSample("MessageProtocol(server).handleAsyncMessage");
printMessage(message); printMessage(message);
NetworkMessage result = null; NetworkMessage result = null;
switch(message.getType()){ switch(message.getType()){
@ -113,7 +113,7 @@ public class MessageProtocol {
} }
public void handleSyncMessages(){ public void handleSyncMessages(){
Globals.profiler.beginAggregateCpuSample("MessageProtocol(client).handleSyncMessages"); Globals.profiler.beginAggregateCpuSample("MessageProtocol(server).handleSyncMessages");
this.synchronousMessageLock.acquireUninterruptibly(); this.synchronousMessageLock.acquireUninterruptibly();
LoggerInterface.loggerNetworking.DEBUG_LOOP("[SERVER] HANDLE SYNC MESSAGE [Sync queue size: " + this.synchronousMessageQueue.size() + "]"); LoggerInterface.loggerNetworking.DEBUG_LOOP("[SERVER] HANDLE SYNC MESSAGE [Sync queue size: " + this.synchronousMessageQueue.size() + "]");
for(NetworkMessage message : synchronousMessageQueue){ for(NetworkMessage message : synchronousMessageQueue){

View File

@ -170,8 +170,11 @@ public class Server implements Runnable {
* @return The first connection * @return The first connection
*/ */
public ServerConnectionHandler getFirstConnection(){ public ServerConnectionHandler getFirstConnection(){
ServerConnectionHandler firstCon = null;
connectListLock.acquireUninterruptibly(); connectListLock.acquireUninterruptibly();
ServerConnectionHandler firstCon = this.activeConnections.get(0); if(this.activeConnections.size() > 0){
firstCon = this.activeConnections.get(0);
}
connectListLock.release(); connectListLock.release();
return firstCon; return firstCon;
} }

View File

@ -8,6 +8,7 @@ import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.parser.net.message.ServerMessage; import electrosphere.net.parser.net.message.ServerMessage;
import electrosphere.net.parser.net.raw.NetworkParser; import electrosphere.net.parser.net.raw.NetworkParser;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
import electrosphere.util.CodeUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -69,7 +70,7 @@ public class ServerConnectionHandler implements Runnable {
//thresholds for determining when to send pings and when a client has disconnected //thresholds for determining when to send pings and when a client has disconnected
static final long SEND_PING_THRESHOLD = 3000; static final long SEND_PING_THRESHOLD = 3000;
static final long PING_DISCONNECT_THRESHOLD = 20000; static final long PING_DISCONNECT_THRESHOLD = 60 * 1000;
//used to keep track of ping/pong messages with client //used to keep track of ping/pong messages with client
long lastPingTime = 0; long lastPingTime = 0;
long lastPongTime = 0; long lastPongTime = 0;
@ -191,18 +192,19 @@ public class ServerConnectionHandler implements Runnable {
initialized = true; initialized = true;
while(Globals.threadManager.shouldKeepRunning() && this.isConnected == true && Globals.server != null && Globals.server.isOpen()){ while(Globals.threadManager.shouldKeepRunning() && this.isConnected == true && Globals.server != null && Globals.server.isOpen()){
boolean receivedMessageThisLoop = false;
// //
// Main Loop // Main Loop
// //
//parse messages both incoming and outgoing //parse messages both incoming and outgoing
try { try {
parseMessages(); receivedMessageThisLoop = parseMessages();
} catch (SocketException e) { } catch (SocketException e) {
//if we get a SocketException broken pipe (basically the client dc'd without telling us) //if we get a SocketException broken pipe (basically the client dc'd without telling us)
//set flag to disconnect client //set flag to disconnect client
//TODO: fix, this doesn't actually catch the socket exception which is exceedingly obnoxious //TODO: fix, this doesn't actually catch the socket exception which is exceedingly obnoxious
socketException = true; socketException = true;
LoggerInterface.loggerNetworking.DEBUG(e.getLocalizedMessage()); LoggerInterface.loggerNetworking.ERROR("Client disconnected", e);
this.disconnect(); this.disconnect();
break; break;
} catch (IOException e){ } catch (IOException e){
@ -210,14 +212,18 @@ public class ServerConnectionHandler implements Runnable {
//set flag to disconnect client //set flag to disconnect client
//TODO: fix, this doesn't actually catch the socket exception which is exceedingly obnoxious //TODO: fix, this doesn't actually catch the socket exception which is exceedingly obnoxious
socketException = true; socketException = true;
LoggerInterface.loggerNetworking.DEBUG(e.getLocalizedMessage()); LoggerInterface.loggerNetworking.ERROR("Client disconnected", e);
this.disconnect(); this.disconnect();
break; break;
} }
// //
// Pings // Timeout logic
// //
//mark as alive if a message was received from client
if(receivedMessageThisLoop){
this.markReceivedPongMessage();
}
//ping logic //ping logic
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
//basically if we haven't sent a ping in a while, send one //basically if we haven't sent a ping in a while, send one
@ -235,7 +241,14 @@ public class ServerConnectionHandler implements Runnable {
//check if we meet disconnection criteria //check if we meet disconnection criteria
//has it been too long since the last ping? //has it been too long since the last ping?
//have we had a socket exception? //have we had a socket exception?
if(lastPingTime - lastPongTime > PING_DISCONNECT_THRESHOLD || this.socketException == true){ if(lastPingTime - lastPongTime > PING_DISCONNECT_THRESHOLD){
//disconnected from the server
LoggerInterface.loggerNetworking.WARNING("Client timeout");
//run disconnect routine
disconnect();
break;
}
if(this.socketException == true){
//disconnected from the server //disconnected from the server
LoggerInterface.loggerNetworking.WARNING("Client disconnected"); LoggerInterface.loggerNetworking.WARNING("Client disconnected");
//run disconnect routine //run disconnect routine
@ -251,13 +264,16 @@ public class ServerConnectionHandler implements Runnable {
* Had to wrap the message parsing block in a function to throw a SocketException * Had to wrap the message parsing block in a function to throw a SocketException
* without my linter freaking out * without my linter freaking out
* @throws SocketException * @throws SocketException
* @return true if connection is alive, false otherwise
*/ */
void parseMessages() throws SocketException, IOException { boolean parseMessages() throws SocketException, IOException {
boolean rVal = false;
// //
//Read in messages //Read in messages
// //
//attempt poll incoming messages //attempt poll incoming messages
networkParser.readMessagesIn(); networkParser.readMessagesIn();
rVal = networkParser.hasIncomingMessaage();
// //
@ -305,8 +321,9 @@ public class ServerConnectionHandler implements Runnable {
TimeUnit.MILLISECONDS.sleep(1); TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
//silently ignore //silently ignore
// CodeUtils.todo(ex, "Handle sleep interrupt on server connection"); CodeUtils.todo(ex, "Handle sleep interrupt on server connection");
} }
return rVal;
} }
/** /**
@ -404,4 +421,15 @@ public class ServerConnectionHandler implements Runnable {
} }
} }
/**
* Gets the total number of bytes read by this connection
* @return The total number of bytes
*/
public long getNumBytesRead(){
if(this.networkParser == null){
return 0;
}
return this.networkParser.getNumberOfBytesRead();
}
} }

View File

@ -3,10 +3,12 @@ package electrosphere.net.server.protocol;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.function.Consumer;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
@ -23,20 +25,32 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
@Override @Override
public TerrainMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, TerrainMessage message) { public TerrainMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, TerrainMessage message) {
switch(message.getMessageSubtype()){
case REQUESTCHUNKDATA: {
sendWorldSubChunkAsync(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ()
);
return null;
}
default: {
} break;
}
return message; return message;
} }
@Override @Override
public void handleSyncMessage(ServerConnectionHandler connectionHandler, TerrainMessage message) { public void handleSyncMessage(ServerConnectionHandler connectionHandler, TerrainMessage message) {
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case REQUESTMETADATA: case REQUESTMETADATA: {
sendWorldMetadata(connectionHandler); sendWorldMetadata(connectionHandler);
break; } break;
case REQUESTCHUNKDATA: case REQUESTCHUNKDATA: {
LoggerInterface.loggerNetworking.DEBUG("(Server) Received request for terrain " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
// System.out.println("Received request for terrain " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
sendWorldSubChunk(connectionHandler, sendWorldSubChunk(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ() message.getworldX(), message.getworldY(), message.getworldZ()
); );
break; } break;
case REQUESTEDITVOXEL: { case REQUESTEDITVOXEL: {
attemptTerrainEdit(connectionHandler, message); attemptTerrainEdit(connectionHandler, message);
} break; } break;
@ -44,6 +58,8 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
attemptUseTerrainEditPalette(connectionHandler, message); attemptUseTerrainEditPalette(connectionHandler, message);
} break; } break;
case REQUESTFLUIDDATA: { case REQUESTFLUIDDATA: {
LoggerInterface.loggerNetworking.DEBUG("(Server) Received request for fluid " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
// System.out.println("Received request for fluid " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
sendWorldFluidSubChunk(connectionHandler, sendWorldFluidSubChunk(connectionHandler,
message.getworldX(), message.getworldY(), message.getworldZ() message.getworldX(), message.getworldY(), message.getworldZ()
); );
@ -70,28 +86,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
* @param worldZ the world z * @param worldZ the world z
*/ */
static void sendWorldSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){ static void sendWorldSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){
/* Globals.profiler.beginAggregateCpuSample("TerrainProtocol(server).sendWorldSubChunk");
int locationX,
int locationY,
float macroValue00,
float macroValue01,
float macroValue02,
float macroValue10,
float macroValue11,
float macroValue12,
float macroValue20,
float macroValue21,
float macroValue22,
long randomizerValue00,
long randomizerValue01,
long randomizerValue02,
long randomizerValue10,
long randomizerValue11,
long randomizerValue12,
long randomizerValue20,
long randomizerValue21,
long randomizerValue22
*/
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY()); // System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());
Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer()); Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer());
@ -99,12 +94,8 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
return; return;
} }
//get the chunk //request chunk
ServerTerrainChunk chunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldX, worldY, worldZ); ServerTerrainChunk chunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldX, worldY, worldZ);
// float[][] macroValues = chunk.getMacroValues();//Globals.serverTerrainManager.getRad5MacroValues(message.getworldX(), message.getworldY());
// long[][] randomizer = chunk.getRandomizer();//Globals.serverTerrainManager.getRandomizer(message.getworldX(), message.getworldY());
//The length along each access of the chunk data. Typically, should be at least 17. //The length along each access of the chunk data. Typically, should be at least 17.
//Because CHUNK_SIZE is 16, 17 adds the necessary extra value. Each chunk needs the value of the immediately following position to generate //Because CHUNK_SIZE is 16, 17 adds the necessary extra value. Each chunk needs the value of the immediately following position to generate
@ -135,89 +126,68 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
} }
} }
System.out.println("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ);
LoggerInterface.loggerNetworking.DEBUG("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ);
connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendChunkDataMessage(worldX, worldY, worldZ, buffer.array())); connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendChunkDataMessage(worldX, worldY, worldZ, buffer.array()));
// int numMessages = 2 + chunk.getModifications().size(); Globals.profiler.endCpuSample();
}
// connectionHandler.addMessagetoOutgoingQueue( /**
// TerrainMessage.constructchunkLoadStartMessage(worldX, worldY, numMessages) * Sends a subchunk to the client
// ); * @param connectionHandler The connection handler
* @param worldX the world x
* @param worldY the world y
* @param worldZ the world z
*/
static void sendWorldSubChunkAsync(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){
Globals.profiler.beginAggregateCpuSample("TerrainProtocol(server).sendWorldSubChunk");
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());
Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer());
if(realm.getServerWorldData().getServerTerrainManager() == null){
return;
}
// connectionHandler.addMessagetoOutgoingQueue( Consumer<ServerTerrainChunk> onLoad = (ServerTerrainChunk chunk) -> {
// TerrainMessage.constructMacroValueMessage( //The length along each access of the chunk data. Typically, should be at least 17.
// worldX, //Because CHUNK_SIZE is 16, 17 adds the necessary extra value. Each chunk needs the value of the immediately following position to generate
// worldY, //chunk data that connects seamlessly to the next chunk.
int xWidth = chunk.getWeights().length;
int yWidth = chunk.getWeights()[0].length;
// macroValues[0][0], int zWidth = chunk.getWeights()[0][0].length;
// macroValues[0][1],
// macroValues[0][2],
// macroValues[0][3],
// macroValues[0][4],
// macroValues[1][0],
// macroValues[1][1],
// macroValues[1][2],
// macroValues[1][3],
// macroValues[1][4],
// macroValues[2][0],
// macroValues[2][1],
// macroValues[2][2],
// macroValues[2][3],
// macroValues[2][4],
// macroValues[3][0],
// macroValues[3][1],
// macroValues[3][2],
// macroValues[3][3],
// macroValues[3][4],
// macroValues[4][0],
// macroValues[4][1],
// macroValues[4][2],
// macroValues[4][3],
// macroValues[4][4],
// randomizer[0][0],
// randomizer[0][1],
// randomizer[0][2],
// randomizer[0][3],
// randomizer[0][4],
// randomizer[1][0],
// randomizer[1][1],
// randomizer[1][2],
// randomizer[1][3],
// randomizer[1][4],
// randomizer[2][0],
// randomizer[2][1],
// randomizer[2][2],
// randomizer[2][3],
// randomizer[2][4],
// randomizer[3][0],
// randomizer[3][1],
// randomizer[3][2],
// randomizer[3][3],
// randomizer[3][4],
// randomizer[4][0],
// randomizer[4][1],
// randomizer[4][2],
// randomizer[4][3],
// randomizer[4][4]
// )
// );
// for(TerrainModification modification : chunk.getModifications()){ ByteBuffer buffer = ByteBuffer.allocate(xWidth*yWidth*zWidth*(4+4));
// connectionHandler.addMessagetoOutgoingQueue( FloatBuffer floatView = buffer.asFloatBuffer();
// TerrainMessage.constructheightMapModificationMessage(
// modification.getValue(), for(int x = 0; x < xWidth; x++){
// modification.getWorldX(), for(int y = 0; y < yWidth; y++){
// 0, for(int z = 0; z < zWidth; z++){
// modification.getWorldY(), floatView.put(chunk.getWeights()[x][y][z]);
// modification.getLocationX(), }
// 0, }
// modification.getLocationY() }
// )
// ); IntBuffer intView = buffer.asIntBuffer();
// } intView.position(floatView.position());
for(int x = 0; x < xWidth; x++){
for(int y = 0; y < yWidth; y++){
for(int z = 0; z < zWidth; z++){
intView.put(chunk.getValues()[x][y][z]);
}
}
}
// System.out.println("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ);
LoggerInterface.loggerNetworking.DEBUG("(Server) Send terrain at " + worldX + " " + worldY + " " + worldZ);
connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendChunkDataMessage(worldX, worldY, worldZ, buffer.array()));
};
//request chunk
realm.getServerWorldData().getServerTerrainManager().getChunkAsync(worldX, worldY, worldZ, onLoad);
Globals.profiler.endCpuSample();
} }

View File

@ -25,6 +25,11 @@ import electrosphere.server.terrain.manager.ServerTerrainChunk;
public class TerrainChunkModelGeneration { public class TerrainChunkModelGeneration {
/**
* The minimum iso value
*/
public static final float MIN_ISO_VALUE = 0.01f;
//http://paulbourke.net/geometry/polygonise/ //http://paulbourke.net/geometry/polygonise/
public static int edgeTable[]={ public static int edgeTable[]={
@ -620,7 +625,7 @@ public class TerrainChunkModelGeneration {
return new Vector3f(x,y,z); return new Vector3f(x,y,z);
} }
public static TerrainChunkData generateTerrainChunkData(float[][][] terrainGrid, int[][][] textureGrid, int lod){ public static TerrainChunkData generateTerrainChunkData(float[][][] terrainGrid, int[][][] textureGrid){
// 5 6 // 5 6
// +-------------+ +-----5-------+ ^ Y // +-------------+ +-----5-------+ ^ Y
@ -664,7 +669,7 @@ public class TerrainChunkModelGeneration {
textureGrid[x+0][y+1][z+0], textureGrid[x+0][y+1][z+1], textureGrid[x+1][y+1][z+1], textureGrid[x+1][y+1][z+0] textureGrid[x+0][y+1][z+0], textureGrid[x+0][y+1][z+1], textureGrid[x+1][y+1][z+1], textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} }

View File

@ -25,6 +25,11 @@ public class TransvoxelModelGeneration {
//the lower the value, the more of the low resolution chunk we will see //the lower the value, the more of the low resolution chunk we will see
static final float TRANSITION_CELL_WIDTH = 0.5f; static final float TRANSITION_CELL_WIDTH = 0.5f;
/**
* The dimension of the array for the face generator. It must be 2 * <size of chunk> + 1. The extra 1 is for the neighbor value
*/
public static final int FACE_DATA_DIMENSIONS = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE + ServerTerrainChunk.CHUNK_DIMENSION;
@ -647,7 +652,7 @@ public class TransvoxelModelGeneration {
if(firstSample > isolevel){ if(firstSample > isolevel){
samplerIndex[i] = getTransvoxelTextureValue(transitionCell.simpleFaceAtlasValues,transitionCell.complexFaceAtlasValues,firstCornerSampleIndex); samplerIndex[i] = getTransvoxelTextureValue(transitionCell.simpleFaceAtlasValues,transitionCell.complexFaceAtlasValues,firstCornerSampleIndex);
} else { } else {
samplerIndex[i] = sampleIndexTable[1][i]; samplerIndex[i] = getTransvoxelTextureValue(transitionCell.simpleFaceAtlasValues,transitionCell.complexFaceAtlasValues,secondCornerSampleIndex);
} }
} }
} }
@ -982,8 +987,7 @@ public class TransvoxelModelGeneration {
/** /**
* Generates mesh data given chunk data * Generates mesh data given chunk data
* @param terrainGrid The chunk data * @param chunkData The chunk data
* @param textureGrid The chunk texture data
* @return The mesh data * @return The mesh data
*/ */
public static TerrainChunkData generateTerrainChunkData(TransvoxelChunkData chunkData){ public static TerrainChunkData generateTerrainChunkData(TransvoxelChunkData chunkData){
@ -1019,9 +1023,9 @@ public class TransvoxelModelGeneration {
List<Vector3f> samplerTriangles = new LinkedList<Vector3f>(); List<Vector3f> samplerTriangles = new LinkedList<Vector3f>();
//List of UVs //List of UVs
List<Float> UVs = new LinkedList<Float>(); List<Float> UVs = new LinkedList<Float>();
// //
//Generate the interior of the mesh //Generate the interior of the mesh
for(int x = 1; x < chunkData.terrainGrid.length - 2; x++){ for(int x = 1; x < chunkData.terrainGrid.length - 2; x++){
@ -1037,7 +1041,7 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} }
@ -1059,8 +1063,8 @@ public class TransvoxelModelGeneration {
//generate the x-positive face //generate the x-positive face
if(chunkData.xPositiveEdgeIso != null){ if(chunkData.xPositiveEdgeIso != null){
int x = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; int x = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2;
for(int y = yStartIndex; y < yEndIndex - 1; y++){ for(int y = yStartIndex; y < yEndIndex; y++){
for(int z = zStartIndex; z < zEndIndex - 1; z++){ for(int z = zStartIndex; z < zEndIndex; z++){
// //
//Generate the transition cell //Generate the transition cell
// //
@ -1087,7 +1091,7 @@ public class TransvoxelModelGeneration {
chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+0)*2+0], chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+0)*2+0],
chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+1)*2+0] chunkData.xPositiveEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+1)*2+0]
); );
polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
// //
//Generate the normal cell with half width //Generate the normal cell with half width
@ -1101,7 +1105,7 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+0)*2+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+1)*2+0], chunkData.xPositiveEdgeAtlas[(y+1)*2+0][(z+0)*2+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} else { } else {
@ -1118,7 +1122,7 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} }
@ -1133,8 +1137,8 @@ public class TransvoxelModelGeneration {
//generate the x-negative face //generate the x-negative face
if(chunkData.xNegativeEdgeIso != null){ if(chunkData.xNegativeEdgeIso != null){
int x = 0; int x = 0;
for(int y = yStartIndex; y < yEndIndex - 1; y++){ for(int y = yStartIndex; y < yEndIndex; y++){
for(int z = zStartIndex; z < zEndIndex - 1; z++){ for(int z = zStartIndex; z < zEndIndex; z++){
// //
//Generate the transition cell //Generate the transition cell
// //
@ -1161,7 +1165,7 @@ public class TransvoxelModelGeneration {
chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0],
chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0] chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0]
); );
polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
// //
//Generate the normal cell with half width //Generate the normal cell with half width
@ -1175,7 +1179,7 @@ public class TransvoxelModelGeneration {
chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} else { } else {
@ -1192,7 +1196,7 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} }
@ -1206,8 +1210,8 @@ public class TransvoxelModelGeneration {
//generate the y-positive face //generate the y-positive face
if(chunkData.yPositiveEdgeIso != null){ if(chunkData.yPositiveEdgeIso != null){
int y = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; int y = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2;
for(int x = xStartIndex; x < xEndIndex - 1; x++){ for(int x = xStartIndex; x < xEndIndex; x++){
for(int z = zStartIndex; z < zEndIndex - 1; z++){ for(int z = zStartIndex; z < zEndIndex; z++){
// //
//Generate the transition cell //Generate the transition cell
// //
@ -1234,7 +1238,7 @@ public class TransvoxelModelGeneration {
chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+0)*2+0],
chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+1)*2+0] chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+1)*2+0]
); );
polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
// //
//Generate the normal cell with half width //Generate the normal cell with half width
@ -1248,7 +1252,7 @@ public class TransvoxelModelGeneration {
chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+0)*2+0] chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yPositiveEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+1)*2+0], chunkData.yPositiveEdgeAtlas[(x+1)*2+0][(z+0)*2+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} else { } else {
@ -1265,7 +1269,7 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} }
@ -1279,8 +1283,8 @@ public class TransvoxelModelGeneration {
//generate the y-negative face //generate the y-negative face
if(chunkData.yNegativeEdgeIso != null){ if(chunkData.yNegativeEdgeIso != null){
int y = 0; int y = 0;
for(int x = xStartIndex; x < xEndIndex - 1; x++){ for(int x = xStartIndex; x < xEndIndex; x++){
for(int z = zStartIndex; z < zEndIndex - 1; z++){ for(int z = zStartIndex; z < zEndIndex; z++){
// //
//Generate the transition cell //Generate the transition cell
// //
@ -1307,7 +1311,7 @@ public class TransvoxelModelGeneration {
chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0],
chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0] chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0]
); );
polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
// //
//Generate the normal cell with half width //Generate the normal cell with half width
@ -1315,13 +1319,13 @@ public class TransvoxelModelGeneration {
currentCell.setValues( currentCell.setValues(
new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+0), new Vector3f(x+0,y+1,z+0), new Vector3f(x+0,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+0),
new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+0), new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+0), new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+0), new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z+0),
chunkData.terrainGrid[x+0][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0], chunkData.terrainGrid[x+0][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0],
chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeIso[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeIso[(x+1)*2+0][(z+0)*2+0],
chunkData.textureGrid[x+0][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0], chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0],
chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0] chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+0)*2+0], chunkData.yNegativeEdgeAtlas[(x+0)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+1)*2+0], chunkData.yNegativeEdgeAtlas[(x+1)*2+0][(z+0)*2+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} else { } else {
@ -1338,7 +1342,7 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} }
@ -1353,8 +1357,8 @@ public class TransvoxelModelGeneration {
//generate the z-positive face //generate the z-positive face
if(chunkData.zPositiveEdgeIso != null){ if(chunkData.zPositiveEdgeIso != null){
int z = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2; int z = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2;
for(int x = xStartIndex; x < xEndIndex - 1; x++){ for(int x = xStartIndex; x < xEndIndex; x++){
for(int y = yStartIndex; y < yEndIndex - 1; y++){ for(int y = yStartIndex; y < yEndIndex; y++){
// //
//Generate the transition cell //Generate the transition cell
// //
@ -1381,7 +1385,7 @@ public class TransvoxelModelGeneration {
chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+1)*2+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+1)*2+0],
chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+1)*2+0] chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+1)*2+0]
); );
polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
// //
//Generate the normal cell with half width //Generate the normal cell with half width
@ -1395,7 +1399,7 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+1)*2+0], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+1)*2+0], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.zPositiveEdgeAtlas[(x+0)*2+0][(y+1)*2+0], chunkData.zPositiveEdgeAtlas[(x+1)*2+0][(y+1)*2+0], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} else { } else {
@ -1412,7 +1416,7 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} }
@ -1427,16 +1431,16 @@ public class TransvoxelModelGeneration {
//generate the z-negative face //generate the z-negative face
if(chunkData.zNegativeEdgeIso != null){ if(chunkData.zNegativeEdgeIso != null){
int z = 0; int z = 0;
for(int x = xStartIndex; x < xEndIndex - 1; x++){ for(int x = xStartIndex; x < xEndIndex; x++){
for(int y = yStartIndex; y < yEndIndex - 1; y++){ for(int y = yStartIndex; y < yEndIndex; y++){
// //
//Generate the transition cell //Generate the transition cell
// //
currentTransitionCell.setValues( currentTransitionCell.setValues(
//complex face vertex coordinates //complex face vertex coordinates
new Vector3f(x+0,y,z), new Vector3f(x+0,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+0,y+1,z), new Vector3f(x+0, y,z), new Vector3f(x+0, y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+0, y+1,z),
new Vector3f(x+TRANSITION_CELL_WIDTH,y,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z),
new Vector3f(x+1,y,z), new Vector3f(x+1,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+1,y+1,z), new Vector3f(x+1, y,z), new Vector3f(x+1, y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+1, y+1,z),
//simple face vertex coordinates //simple face vertex coordinates
new Vector3f(x+0,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+0,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+0,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+0,y+1,z+TRANSITION_CELL_WIDTH),
new Vector3f(x+1,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH),
@ -1455,7 +1459,7 @@ public class TransvoxelModelGeneration {
chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0],
chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0] chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0]
); );
polygonizeTransition(currentTransitionCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
// //
//Generate the normal cell with half width //Generate the normal cell with half width
@ -1463,13 +1467,13 @@ public class TransvoxelModelGeneration {
currentCell.setValues( currentCell.setValues(
new Vector3f(x+0,y+0,z+1), new Vector3f(x+0,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+1), new Vector3f(x+0,y+0,z+1), new Vector3f(x+0,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+0,z+1),
new Vector3f(x+0,y+1,z+1), new Vector3f(x+0,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+1), new Vector3f(x+0,y+1,z+1), new Vector3f(x+0,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+1),
chunkData.terrainGrid[x+0][y+0][z+0], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.terrainGrid[x+1][y+0][z+0], chunkData.terrainGrid[x+0][y+0][z+1], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.terrainGrid[x+1][y+0][z+1],
chunkData.terrainGrid[x+0][y+1][z+0], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+1)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+1)*2+0], chunkData.terrainGrid[x+1][y+1][z+0], chunkData.terrainGrid[x+0][y+1][z+1], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+1)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+1)*2+0], chunkData.terrainGrid[x+1][y+1][z+1],
chunkData.textureGrid[x+0][y+0][z+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.textureGrid[x+1][y+0][z+0], chunkData.textureGrid[x+0][y+0][z+1], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.textureGrid[x+1][y+0][z+1],
chunkData.textureGrid[x+0][y+1][z+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+1], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0], chunkData.textureGrid[x+1][y+1][z+1]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} else { } else {
@ -1486,11 +1490,92 @@ public class TransvoxelModelGeneration {
chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0] chunkData.textureGrid[x+0][y+1][z+0], chunkData.textureGrid[x+0][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
); );
//polygonize the current gridcell //polygonize the current gridcell
polygonize(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert); polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
} }
} }
} }
//xn-zn edge
if(chunkData.xNegativeEdgeIso != null && chunkData.zNegativeEdgeIso != null){
int x = 0;
int z = 0;
int edgeLength = chunkWidth - (chunkData.yNegativeEdgeIso != null ? 1 : 0) - (chunkData.yPositiveEdgeIso != null ? 1 : 0);
int startIndex = 0 + (chunkData.yNegativeEdgeIso != null ? 1 : 0);
for(int i = startIndex; i < edgeLength - 1; i++){
int y = i;
//
//Generate the x-side transition cell
//
currentTransitionCell.setValues(
//complex face vertex coordinates
new Vector3f(x,y,z), new Vector3f(x,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x,y+1,z),
new Vector3f(x,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x,y+TRANSITION_CELL_WIDTH,z+TRANSITION_CELL_WIDTH), new Vector3f(x,y+1,z+TRANSITION_CELL_WIDTH),
new Vector3f(x,y,z+1), new Vector3f(x,y+TRANSITION_CELL_WIDTH,z+1), new Vector3f(x,y+1,z+1),
//simple face vertex coordinates
new Vector3f(x+TRANSITION_CELL_WIDTH,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+TRANSITION_CELL_WIDTH),
new Vector3f(x+TRANSITION_CELL_WIDTH,y,z+1), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+1),
//complex face iso values
chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+0)*2+1][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+0)*2+0],
chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+0)*2+1], chunkData.xNegativeEdgeIso[(y+0)*2+1][(z+0)*2+1], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+0)*2+1],
chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeIso[(y+0)*2+1][(z+1)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+1)*2+0],
//simple face iso values
chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+0)*2+0],
chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+1)*2+0],
//complex face texture atlas values
chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+0)*2+1][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0],
chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+1], chunkData.xNegativeEdgeAtlas[(y+0)*2+1][(z+0)*2+1], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+1],
chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeAtlas[(y+0)*2+1][(z+1)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0],
//simple face texture atlas values
chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0],
chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0]
);
polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
//
//Generate the z-side transition cell
//
currentTransitionCell.setValues(
//complex face vertex coordinates
new Vector3f(x+0, y,z), new Vector3f(x+0, y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+0, y+1,z),
new Vector3f(x+TRANSITION_CELL_WIDTH,y,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z),
new Vector3f(x+1, y,z), new Vector3f(x+1, y+TRANSITION_CELL_WIDTH,z), new Vector3f(x+1, y+1,z),
//simple face vertex coordinates
new Vector3f(x+TRANSITION_CELL_WIDTH,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+TRANSITION_CELL_WIDTH),
new Vector3f(x+1,y,z+TRANSITION_CELL_WIDTH), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH),
//complex face iso values
chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+1], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+1)*2+0],
chunkData.zNegativeEdgeIso[(x+0)*2+1][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+0)*2+1][(y+0)*2+1], chunkData.zNegativeEdgeIso[(x+0)*2+1][(y+1)*2+0],
chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+1], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+1)*2+0],
//simple face iso values
chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+0)*2+0][(y+1)*2+0],
chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeIso[(x+1)*2+0][(y+1)*2+0],
//complex face texture atlas values
chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+1], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0],
chunkData.zNegativeEdgeAtlas[(x+0)*2+1][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+1][(y+0)*2+1], chunkData.zNegativeEdgeAtlas[(x+0)*2+1][(y+1)*2+0],
chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+1], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0],
//simple face texture atlas values
chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+0)*2+0][(y+1)*2+0],
chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+0)*2+0], chunkData.zNegativeEdgeAtlas[(x+1)*2+0][(y+1)*2+0]
);
polygonizeTransition(currentTransitionCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
//
//Generate the normal cell with half width
//
currentCell.setValues(
new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z+TRANSITION_CELL_WIDTH), new Vector3f(x+TRANSITION_CELL_WIDTH,y+0,z+1), new Vector3f(x+1,y+0,z+1), new Vector3f(x+1,y+0,z+TRANSITION_CELL_WIDTH),
new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+TRANSITION_CELL_WIDTH), new Vector3f(x+TRANSITION_CELL_WIDTH,y+1,z+1), new Vector3f(x+1,y+1,z+1), new Vector3f(x+1,y+1,z+TRANSITION_CELL_WIDTH),
chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+0)*2+0][(z+1)*2+0], chunkData.terrainGrid[x+1][y+0][z+1], chunkData.terrainGrid[x+1][y+0][z+0],
chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeIso[(y+1)*2+0][(z+1)*2+0], chunkData.terrainGrid[x+1][y+1][z+1], chunkData.terrainGrid[x+1][y+1][z+0],
chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+0)*2+0][(z+1)*2+0], chunkData.textureGrid[x+1][y+0][z+1], chunkData.textureGrid[x+1][y+0][z+0],
chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+0)*2+0], chunkData.xNegativeEdgeAtlas[(y+1)*2+0][(z+1)*2+0], chunkData.textureGrid[x+1][y+1][z+1], chunkData.textureGrid[x+1][y+1][z+0]
);
//polygonize the current gridcell
polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
}
}

View File

@ -18,6 +18,11 @@ public class ImGuiLinePlot implements ImGuiElement {
//the data sets to draw //the data sets to draw
List<ImGuiLinePlotDataset> dataSets = new LinkedList<ImGuiLinePlotDataset>(); List<ImGuiLinePlotDataset> dataSets = new LinkedList<ImGuiLinePlotDataset>();
/**
* Size of the plot
*/
ImVec2 size = new ImVec2(-1,-1);
/** /**
* Creates an im gui line plot * Creates an im gui line plot
*/ */
@ -25,9 +30,18 @@ public class ImGuiLinePlot implements ImGuiElement {
this.plotTitle = plotTitle; this.plotTitle = plotTitle;
} }
/**
* Creates an im gui line plot
*/
public ImGuiLinePlot(String plotTitle, int sizeX, int sizeY){
this.plotTitle = plotTitle;
this.size.x = sizeX;
this.size.y = sizeY;
}
@Override @Override
public void draw() { public void draw() {
if(ImPlot.beginPlot(plotTitle,"","",new ImVec2(-1,-1),0,ImPlotAxisFlags.AutoFit,ImPlotAxisFlags.AutoFit)){ if(ImPlot.beginPlot(plotTitle,"","",size,0,ImPlotAxisFlags.AutoFit,ImPlotAxisFlags.AutoFit)){
for(ImGuiLinePlotDataset dataSet : dataSets){ for(ImGuiLinePlotDataset dataSet : dataSets){
double[] xs = dataSet.xData.stream().mapToDouble(Double::doubleValue).toArray();//(Double[])dataSet.xData.toArray(new Double[dataSet.xData.size()]); double[] xs = dataSet.xData.stream().mapToDouble(Double::doubleValue).toArray();//(Double[])dataSet.xData.toArray(new Double[dataSet.xData.size()]);
double[] ys = dataSet.yData.stream().mapToDouble(Double::doubleValue).toArray();//(Double[])dataSet.yData.toArray(new Double[dataSet.yData.size()]); double[] ys = dataSet.yData.stream().mapToDouble(Double::doubleValue).toArray();//(Double[])dataSet.yData.toArray(new Double[dataSet.yData.size()]);
@ -102,6 +116,15 @@ public class ImGuiLinePlot implements ImGuiElement {
} }
} }
/**
* Zeroes out the dataset
*/
public void zeroOut(){
for(int i = 0; i < limit; i++){
this.addPoint(i, 0);
}
}
} }

View File

@ -25,6 +25,8 @@ public class MainServerFunctions {
if(Globals.server != null){ if(Globals.server != null){
Globals.server.synchronousPacketHandling(); Globals.server.synchronousPacketHandling();
} }
Globals.profiler.endCpuSample();
Globals.profiler.beginCpuSample("Server process synchronization messages");
if(Globals.serverSynchronizationManager != null){ if(Globals.serverSynchronizationManager != null){
Globals.serverSynchronizationManager.processMessages(); Globals.serverSynchronizationManager.processMessages();
} }

View File

@ -44,7 +44,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
/** /**
* The max grid size allowed * The max grid size allowed
*/ */
public static final int MAX_GRID_SIZE = 10; public static final int MAX_GRID_SIZE = 2000 * 1024;
/** /**
* Tracks whether this manager has been flagged to unload cells or not * Tracks whether this manager has been flagged to unload cells or not

View File

@ -3,6 +3,7 @@ package electrosphere.server.datacell.physics;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.renderer.shader.ShaderProgram; import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -65,7 +66,7 @@ public class DataCellPhysicsManager {
* @param discreteY The initial discrete position Y coordinate * @param discreteY The initial discrete position Y coordinate
*/ */
public DataCellPhysicsManager(Realm realm, int discreteX, int discreteY, int discreteZ){ public DataCellPhysicsManager(Realm realm, int discreteX, int discreteY, int discreteZ){
worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / Globals.clientWorldData.getDynamicInterpolationRatio() * 1.0f); worldBoundDiscreteMax = (int)(Globals.clientWorldData.getWorldBoundMin().x / ServerTerrainChunk.CHUNK_DIMENSION * 1.0f);
cells = new HashSet<PhysicsDataCell>(); cells = new HashSet<PhysicsDataCell>();
invalid = new HashSet<String>(); invalid = new HashSet<String>();
updateable = new HashSet<String>(); updateable = new HashSet<String>();

View File

@ -7,6 +7,7 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterOutputStream; import java.util.zip.InflaterOutputStream;
@ -14,6 +15,7 @@ import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.util.FileUtils; import electrosphere.util.FileUtils;
import electrosphere.util.annotation.Exclude;
/** /**
* An interface for accessing the disk map of chunk information * An interface for accessing the disk map of chunk information
@ -23,6 +25,12 @@ public class ChunkDiskMap {
//The map of world position+chunk type to the file that actually houses that information //The map of world position+chunk type to the file that actually houses that information
Map<String,String> worldPosFileMap = new HashMap<String,String>(); Map<String,String> worldPosFileMap = new HashMap<String,String>();
/**
* Locks the chunk disk map for thread safety
*/
@Exclude
Semaphore lock = new Semaphore(1);
/** /**
* Constructor * Constructor
*/ */
@ -81,7 +89,10 @@ public class ChunkDiskMap {
* @return True if the map contains the chunk, false otherwise * @return True if the map contains the chunk, false otherwise
*/ */
public boolean containsTerrainAtPosition(int worldX, int worldY, int worldZ){ public boolean containsTerrainAtPosition(int worldX, int worldY, int worldZ){
return worldPosFileMap.containsKey(getTerrainChunkKey(worldX, worldY, worldZ)); lock.acquireUninterruptibly();
boolean rVal = worldPosFileMap.containsKey(getTerrainChunkKey(worldX, worldY, worldZ));
lock.release();
return rVal;
} }
/** /**
@ -92,7 +103,10 @@ public class ChunkDiskMap {
* @return True if the map contains the chunk, false otherwise * @return True if the map contains the chunk, false otherwise
*/ */
public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){ public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){
return worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ)); lock.acquireUninterruptibly();
boolean rVal = worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ));
lock.release();
return rVal;
} }
/** /**
@ -103,6 +117,7 @@ public class ChunkDiskMap {
* @return The server terrain chunk if it exists, null otherwise * @return The server terrain chunk if it exists, null otherwise
*/ */
public ServerTerrainChunk getTerrainChunk(int worldX, int worldY, int worldZ){ public ServerTerrainChunk getTerrainChunk(int worldX, int worldY, int worldZ){
lock.acquireUninterruptibly();
LoggerInterface.loggerEngine.INFO("Load chunk " + worldX + " " + worldY + " " + worldZ); LoggerInterface.loggerEngine.INFO("Load chunk " + worldX + " " + worldY + " " + worldZ);
ServerTerrainChunk rVal = null; ServerTerrainChunk rVal = null;
if(containsTerrainAtPosition(worldX, worldY, worldZ)){ if(containsTerrainAtPosition(worldX, worldY, worldZ)){
@ -148,6 +163,7 @@ public class ChunkDiskMap {
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
} }
} }
lock.release();
return rVal; return rVal;
} }
@ -156,6 +172,7 @@ public class ChunkDiskMap {
* @param terrainChunk The terrain chunk * @param terrainChunk The terrain chunk
*/ */
public void saveToDisk(ServerTerrainChunk terrainChunk){ public void saveToDisk(ServerTerrainChunk terrainChunk){
lock.acquireUninterruptibly();
LoggerInterface.loggerEngine.DEBUG("Save to disk: " + terrainChunk.getWorldX() + " " + terrainChunk.getWorldY() + " " + terrainChunk.getWorldZ()); LoggerInterface.loggerEngine.DEBUG("Save to disk: " + terrainChunk.getWorldX() + " " + terrainChunk.getWorldY() + " " + terrainChunk.getWorldZ());
//get the file name for this chunk //get the file name for this chunk
String fileName = null; String fileName = null;
@ -202,6 +219,7 @@ public class ChunkDiskMap {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
lock.release();
} }
} }

View File

@ -4,6 +4,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import electrosphere.engine.Globals;
import electrosphere.game.data.biome.BiomeData; import electrosphere.game.data.biome.BiomeData;
import electrosphere.game.data.biome.BiomeSurfaceGenerationParams; import electrosphere.game.data.biome.BiomeSurfaceGenerationParams;
import electrosphere.game.server.world.ServerWorldData; import electrosphere.game.server.world.ServerWorldData;
@ -65,6 +66,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
@Override @Override
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) { public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) {
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.generateChunk");
ServerTerrainChunk rVal = null; ServerTerrainChunk rVal = null;
float[][][] weights; float[][][] weights;
int[][][] values; int[][][] values;
@ -95,19 +97,37 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION]; values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
//biome of the current chunk //biome of the current chunk
BiomeData biome = this.terrainModel.getSurfaceBiome(worldX, worldY, worldZ); BiomeData surfaceBiome = this.terrainModel.getSurfaceBiome(worldX, worldY, 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());
}
//presolve heightfield
float[][] heightfield = new float[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
heightfield[x][z] = heightmapGen.getHeight(this.terrainModel.getSeed(), this.serverWorldData.convertVoxelToRealSpace(x, worldX), this.serverWorldData.convertVoxelToRealSpace(z, worldZ));
}
}
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){ for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice");
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){ for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){ for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
weights[x][y][z] = this.getChunkWeight(worldX, worldY, worldZ, x, y, z, this.terrainModel, biome); GeneratedVoxel voxel = this.getVoxel(worldX, worldY, worldZ, x, y, z, heightfield, this.terrainModel, surfaceBiome);
values[x][y][z] = this.getChunkValue(worldX, worldY, worldZ, x, y, z, this.terrainModel, biome); weights[x][y][z] = voxel.weight;
values[x][y][z] = voxel.type;
} }
} }
Globals.profiler.endCpuSample();
} }
} }
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values); rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
Globals.profiler.endCpuSample();
return rVal; return rVal;
} }
@ -116,7 +136,6 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
this.terrainModel = model; this.terrainModel = model;
} }
/** /**
* Gets the value for a chunk * Gets the value for a chunk
* @param worldX The world x pos * @param worldX The world x pos
@ -125,71 +144,133 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
* @param chunkX The chunk x pos * @param chunkX The chunk x pos
* @param chunkY The chunk y pos * @param chunkY The chunk y pos
* @param chunkZ The chunk z pos * @param chunkZ The chunk z pos
* @param heightfield The precomputed heightfield
* @param terrainModel The terrain model * @param terrainModel The terrain model
* @param surfaceBiome The surface biome of the chunk * @param surfaceBiome The surface biome of the chunk
* @return The value of the chunk * @return The value of the chunk
*/ */
private int getChunkValue( private GeneratedVoxel getVoxel(
int worldX, int worldY, int worldZ, int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ, int chunkX, int chunkY, int chunkZ,
float[][] heightfield,
TerrainModel terrainModel, TerrainModel terrainModel,
BiomeData surfaceBiome BiomeData surfaceBiome
){ ){
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.getChunkValue");
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag());
if(heightmapGen == null){ if(heightmapGen == null){
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
} }
double realX = this.serverWorldData.convertVoxelToRealSpace(chunkX,worldX); double realX = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldX);
double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY); double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY);
double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkZ,worldZ); double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldZ);
float surfaceHeight = heightmapGen.getHeight(terrainModel.getSeed(), realX, realZ); float surfaceHeight = heightfield[chunkX][chunkZ];
if(realY <= surfaceHeight){ double flooredSurfaceHeight = Math.floor(surfaceHeight);
return 1; Globals.profiler.endCpuSample();
if(realY < surfaceHeight - 1){
return getSubsurfaceVoxel(
worldX,worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfaceHeight, flooredSurfaceHeight,
terrainModel,
surfaceBiome
);
} else if(realY > flooredSurfaceHeight) {
return getOverSurfaceVoxel(
worldX,worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfaceHeight, flooredSurfaceHeight,
terrainModel,
surfaceBiome
);
} else { } else {
return 0; return getSurfaceVoxel(
worldX,worldY, worldZ,
chunkX, chunkY, chunkZ,
realX, realY, realZ,
surfaceHeight, flooredSurfaceHeight,
terrainModel,
surfaceBiome
);
} }
} }
/** /**
* Gets the weight for a chunk * Gets the voxel on the surface
* @param worldX The world x pos * @return The voxel
* @param worldY The world y pos
* @param worldZ The world z pos
* @param chunkX The chunk x pos
* @param chunkY The chunk y pos
* @param chunkZ The chunk z pos
* @param terrainModel The terrain model
* @param surfaceBiome The surface biome of the chunk
* @return The weight of the chunk
*/ */
private float getChunkWeight( private GeneratedVoxel getSurfaceVoxel(
int worldX, int worldY, int worldZ, int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ, int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
float surfaceHeight, double flooredSurfaceHeight,
TerrainModel terrainModel, TerrainModel terrainModel,
BiomeData surfaceBiome BiomeData surfaceBiome
){ ){
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams(); GeneratedVoxel voxel = new GeneratedVoxel();
HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag()); voxel.weight = (float)(surfaceHeight - flooredSurfaceHeight) * 2 - 1;
if(heightmapGen == null){ voxel.type = 1;
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag()); return voxel;
} }
double realX = this.serverWorldData.convertVoxelToRealSpace(chunkX,worldX); /**
double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY); * Gets the voxel below the surface
double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkZ,worldZ); * @return The voxel
*/
float surfaceHeight = heightmapGen.getHeight(terrainModel.getSeed(), realX, realZ); private GeneratedVoxel getSubsurfaceVoxel(
double flooredSurfaceHeight = Math.floor(surfaceHeight); int worldX, int worldY, int worldZ,
if(realY < flooredSurfaceHeight){ int chunkX, int chunkY, int chunkZ,
return 1; double realX, double realY, double realZ,
} else if(realY > flooredSurfaceHeight) { float surfaceHeight, double flooredSurfaceHeight,
return -1; TerrainModel terrainModel,
BiomeData surfaceBiome
){
GeneratedVoxel voxel = new GeneratedVoxel();
if(realY < surfaceHeight - 5){
voxel.weight = 1;
voxel.type = 6;
} else { } else {
return (float)(surfaceHeight - flooredSurfaceHeight) * 2 - 1; voxel.weight = 1;
voxel.type = 1;
} }
return voxel;
}
/**
* Gets the voxel above the service
* @return The voxel
*/
private GeneratedVoxel getOverSurfaceVoxel(
int worldX, int worldY, int worldZ,
int chunkX, int chunkY, int chunkZ,
double realX, double realY, double realZ,
float surfaceHeight, double flooredSurfaceHeight,
TerrainModel terrainModel,
BiomeData surfaceBiome
){
GeneratedVoxel voxel = new GeneratedVoxel();
voxel.weight = -1;
voxel.type = 0;
return voxel;
}
/**
* A voxel that was generated
*/
static class GeneratedVoxel {
/**
* The type of the voxel
*/
int type;
/**
* The weight of the voxel
*/
float weight;
} }
} }

View File

@ -1,5 +1,6 @@
package electrosphere.server.terrain.generation.heightmap; package electrosphere.server.terrain.generation.heightmap;
import electrosphere.engine.Globals;
import electrosphere.util.noise.OpenSimplex2S; import electrosphere.util.noise.OpenSimplex2S;
/** /**
@ -48,9 +49,12 @@ public class HillsGen implements HeightmapGenerator {
* @return The height * @return The height
*/ */
public float getHeight(long SEED, double x, double y){ public float getHeight(long SEED, double x, double y){
Globals.profiler.beginAggregateCpuSample("HillsGen.getHeight");
double scaledX = x * POSITION_SCALE; double scaledX = x * POSITION_SCALE;
double scaledY = y * POSITION_SCALE; double scaledY = y * POSITION_SCALE;
return gradientHeight(SEED, scaledX, scaledY) * VERTICAL_SCALE + HEIGHT_OFFSET; float rVal = gradientHeight(SEED, scaledX, scaledY) * VERTICAL_SCALE + HEIGHT_OFFSET;
Globals.profiler.endCpuSample();
return rVal;
} }

View File

@ -0,0 +1,123 @@
package electrosphere.server.terrain.manager;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import electrosphere.engine.Globals;
import electrosphere.server.terrain.diskmap.ChunkDiskMap;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
/**
* A job that fetches a chunk, either by generating it or by reading it from disk
*/
public class ChunkGenerationThread implements Runnable {
/**
* The number of milliseconds to wait per iteration
*/
static final int WAIT_TIME_MS = 2;
/**
* The maximum number of iterations to wait before failing
*/
static final int MAX_TIME_TO_WAIT = 10;
/**
* The chunk disk map
*/
ChunkDiskMap chunkDiskMap;
/**
* The chunk cache on the server
*/
ServerChunkCache chunkCache;
/**
* The chunk generator
*/
ChunkGenerator chunkGenerator;
/**
* The world x coordinate
*/
int worldX;
/**
* The world y coordinate
*/
int worldY;
/**
* The world z coordinate
*/
int worldZ;
/**
* The work to do once the chunk is available
*/
Consumer<ServerTerrainChunk> onLoad;
/**
* Creates the chunk generation job
* @param chunkDiskMap The chunk disk map
* @param chunkCache The chunk cache on the server
* @param chunkGenerator The chunk generator
* @param worldX The world x coordinate
* @param worldY The world y coordinate
* @param worldZ The world z coordinate
* @param onLoad The work to do once the chunk is available
*/
public ChunkGenerationThread(
ChunkDiskMap chunkDiskMap,
ServerChunkCache chunkCache,
ChunkGenerator chunkGenerator,
int worldX, int worldY, int worldZ,
Consumer<ServerTerrainChunk> onLoad
){
this.chunkDiskMap = chunkDiskMap;
this.chunkCache = chunkCache;
this.chunkGenerator = chunkGenerator;
this.worldX = worldX;
this.worldY = worldY;
this.worldZ = worldZ;
this.onLoad = onLoad;
}
@Override
public void run() {
ServerTerrainChunk chunk = null;
int i = 0;
while(chunk == null && i < MAX_TIME_TO_WAIT && Globals.threadManager.shouldKeepRunning()){
if(chunkCache.containsChunk(worldX,worldY,worldZ)){
chunk = chunkCache.get(worldX,worldY,worldZ);
} else {
//pull from disk if it exists
if(chunkDiskMap != null){
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){
chunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ);
}
}
//generate if it does not exist
if(chunk == null){
chunk = chunkGenerator.generateChunk(worldX, worldY, worldZ);
}
if(chunk != null){
chunkCache.add(worldX, worldY, worldZ, chunk);
}
}
if(chunk == null){
try {
TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
i++;
}
if(i >= MAX_TIME_TO_WAIT){
throw new Error("Failed to resolve chunk!");
}
this.onLoad.accept(chunk);
}
}

View File

@ -0,0 +1,166 @@
package electrosphere.server.terrain.manager;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
/**
* Caches chunk data on the server
*/
public class ServerChunkCache {
/**
* Number of chunks to cache
*/
static final int CACHE_SIZE = 5000;
/**
* The size of the cache
*/
int cacheSize = CACHE_SIZE;
/**
* The cached data
*/
Map<String, ServerTerrainChunk> chunkCache = new HashMap<String,ServerTerrainChunk>();
/**
* Tracks how recently a chunk has been queries for (used for evicting old chunks from cache)
*/
List<String> queryRecencyQueue = new LinkedList<String>();
/**
* Tracks what chunks are already queued to be asynchronously loaded. Used so we don't have two threads generating/fetching the same chunk
*/
Map<String, Boolean> queuedChunkMap = new HashMap<String,Boolean>();
/**
* The lock for thread safety
*/
Semaphore lock = new Semaphore(1);
/**
* Gets the collection of server terrain chunks that are cached
* @return The collection of chunks
*/
public Collection<ServerTerrainChunk> getContents(){
lock.acquireUninterruptibly();
Collection<ServerTerrainChunk> rVal = Collections.unmodifiableCollection(chunkCache.values());
lock.release();
return rVal;
}
/**
* Evicts all chunks in the cache
*/
public void clear(){
lock.acquireUninterruptibly();
chunkCache.clear();
lock.release();
}
/**
* Gets the chunk at a given world position
* @param worldX The world x coordinate
* @param worldY The world y coordinate
* @param worldZ The world z coordinate
* @return The chunk
*/
public ServerTerrainChunk get(int worldX, int worldY, int worldZ){
ServerTerrainChunk rVal = null;
String key = this.getKey(worldX, worldY, worldZ);
lock.acquireUninterruptibly();
queryRecencyQueue.remove(key);
queryRecencyQueue.add(0, key);
rVal = this.chunkCache.get(key);
lock.release();
return rVal;
}
/**
* Adds a chunk to the cache
* @param worldX The world x coordinate of the chunk
* @param worldY The world y coordinate of the chunk
* @param worldZ The world z coordinate of the chunk
* @param chunk The chunk itself
*/
public void add(int worldX, int worldY, int worldZ, ServerTerrainChunk chunk){
String key = this.getKey(worldX, worldY, worldZ);
lock.acquireUninterruptibly();
queryRecencyQueue.add(0, key);
this.chunkCache.put(key, chunk);
lock.release();
}
/**
* Checks if the cache contains the chunk at a given world position
* @param worldX The world x coordinate
* @param worldY The world y coordinate
* @param worldZ The world z coordinate
* @return true if the cache contains this chunk, false otherwise
*/
public boolean containsChunk(int worldX, int worldY, int worldZ){
String key = this.getKey(worldX,worldY,worldZ);
lock.acquireUninterruptibly();
boolean rVal = this.chunkCache.containsKey(key);
lock.release();
return rVal;
}
/**
* Gets the key for a given world position
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
public String getKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ;
}
/**
* Checks if the chunk is already queued or not
* @param worldX The world x position of the chunk
* @param worldY The world y position of the chunk
* @param worldZ The world z position of the chunk
* @return true if the chunk is already queued, false otherwise
*/
public boolean chunkIsQueued(int worldX, int worldY, int worldZ){
String key = this.getKey(worldX,worldY,worldZ);
lock.acquireUninterruptibly();
boolean rVal = this.queuedChunkMap.containsKey(key);
lock.release();
return rVal;
}
/**
* Flags a chunk as queued
* @param worldX The world x position of the chunk
* @param worldY The world y position of the chunk
* @param worldZ The world z position of the chunk
*/
public void queueChunk(int worldX, int worldY, int worldZ){
String key = this.getKey(worldX,worldY,worldZ);
lock.acquireUninterruptibly();
this.queuedChunkMap.put(key,true);
lock.release();
}
/**
* Unflags a chunk as queued
* @param worldX The world x position of the chunk
* @param worldY The world y position of the chunk
* @param worldZ The world z position of the chunk
*/
public void unqueueChunk(int worldX, int worldY, int worldZ){
String key = this.getKey(worldX,worldY,worldZ);
lock.acquireUninterruptibly();
this.queuedChunkMap.remove(key);
lock.release();
}
}

View File

@ -1,5 +1,6 @@
package electrosphere.server.terrain.manager; package electrosphere.server.terrain.manager;
import electrosphere.engine.Globals;
import electrosphere.game.server.world.ServerWorldData; import electrosphere.game.server.world.ServerWorldData;
import electrosphere.server.terrain.diskmap.ChunkDiskMap; import electrosphere.server.terrain.diskmap.ChunkDiskMap;
import electrosphere.server.terrain.generation.TestGenerationChunkGenerator; import electrosphere.server.terrain.generation.TestGenerationChunkGenerator;
@ -8,12 +9,13 @@ import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.models.TerrainModel; import electrosphere.server.terrain.models.TerrainModel;
import electrosphere.server.terrain.models.TerrainModification; import electrosphere.server.terrain.models.TerrainModification;
import electrosphere.util.FileUtils; import electrosphere.util.FileUtils;
import electrosphere.util.annotation.Exclude;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.util.List; import java.util.concurrent.ExecutorService;
import java.util.Map; import java.util.concurrent.Executors;
import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer;
import java.util.concurrent.CopyOnWriteArrayList;
import org.joml.Vector3i; import org.joml.Vector3i;
@ -22,6 +24,11 @@ import org.joml.Vector3i;
*/ */
public class ServerTerrainManager { public class ServerTerrainManager {
/**
* The number of threads for chunk generation
*/
public static final int GENERATION_THREAD_POOL_SIZE = 1;
/** /**
* Full world discrete size * Full world discrete size
*/ */
@ -51,21 +58,24 @@ public class ServerTerrainManager {
//The model of the terrain this manager is managing //The model of the terrain this manager is managing
TerrainModel model; TerrainModel model;
/**
//In memory cache of chunk data * The cache of chunks
//Basic idea is we associate string that contains chunk x&y&z with elevation */
//While we incur a penalty with converting ints -> string, think this will @Exclude
//offset regenerating the array every time we want a new one ServerChunkCache chunkCache = new ServerChunkCache();
int cacheSize = 500;
Map<String, ServerTerrainChunk> chunkCache;
List<String> chunkCacheContents;
//The map of chunk position <-> file on disk containing chunk data //The map of chunk position <-> file on disk containing chunk data
ChunkDiskMap chunkDiskMap = null; ChunkDiskMap chunkDiskMap = null;
//The generation algorithm for this terrain manager //The generation algorithm for this terrain manager
@Exclude
ChunkGenerator chunkGenerator; ChunkGenerator chunkGenerator;
/**
* The threadpool for chunk generation
*/
@Exclude
ExecutorService chunkExecutorService = Executors.newFixedThreadPool(GENERATION_THREAD_POOL_SIZE);
/** /**
* Constructor * Constructor
@ -76,8 +86,6 @@ public class ServerTerrainManager {
ChunkGenerator chunkGenerator ChunkGenerator chunkGenerator
){ ){
this.parent = parent; this.parent = parent;
this.chunkCache = new ConcurrentHashMap<String, ServerTerrainChunk>();
this.chunkCacheContents = new CopyOnWriteArrayList<String>();
this.seed = seed; this.seed = seed;
this.chunkGenerator = chunkGenerator; this.chunkGenerator = chunkGenerator;
} }
@ -118,8 +126,7 @@ public class ServerTerrainManager {
FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array()); FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array());
} }
//for each chunk, save via disk map //for each chunk, save via disk map
for(String chunkKey : chunkCacheContents){ for(ServerTerrainChunk chunk : this.chunkCache.getContents()){
ServerTerrainChunk chunk = chunkCache.get(chunkKey);
chunkDiskMap.saveToDisk(chunk); chunkDiskMap.saveToDisk(chunk);
} }
//save disk map itself //save disk map itself
@ -167,7 +174,6 @@ public class ServerTerrainManager {
*/ */
public void evictAll(){ public void evictAll(){
this.chunkCache.clear(); this.chunkCache.clear();
this.chunkCacheContents.clear();
} }
public float[][] getTerrainAtChunk(int x, int y){ public float[][] getTerrainAtChunk(int x, int y){
@ -215,37 +221,19 @@ public class ServerTerrainManager {
} }
/** /**
* Gets the key for a given world position * Performs logic once a server chunk is available
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
public String getKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ;
}
/**
* Gets a server terrain chunk
* @param worldX The world x position * @param worldX The world x position
* @param worldY The world y position * @param worldY The world y position
* @param worldZ The world z position * @param worldZ The world z position
* @return The ServerTerrainChunk * @return The ServerTerrainChunk
*/ */
public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){ public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){
Globals.profiler.beginCpuSample("ServerTerrainManager.getChunk");
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING //THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
String key = getKey(worldX,worldY,worldZ);
ServerTerrainChunk returnedChunk = null; ServerTerrainChunk returnedChunk = null;
if(chunkCache.containsKey(key)){ if(chunkCache.containsChunk(worldX,worldY,worldZ)){
chunkCacheContents.remove(key); returnedChunk = chunkCache.get(worldX,worldY,worldZ);
chunkCacheContents.add(0, key);
returnedChunk = chunkCache.get(key);
return returnedChunk;
} else { } else {
if(chunkCacheContents.size() >= cacheSize){
String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1);
chunkCache.remove(oldChunk);
}
//pull from disk if it exists //pull from disk if it exists
if(chunkDiskMap != null){ if(chunkDiskMap != null){
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){ if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){
@ -256,10 +244,23 @@ public class ServerTerrainManager {
if(returnedChunk == null){ if(returnedChunk == null){
returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ); returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ);
} }
chunkCache.put(key, returnedChunk); this.chunkCache.add(worldX, worldY, worldZ, returnedChunk);
chunkCacheContents.add(key);
return returnedChunk;
} }
Globals.profiler.endCpuSample();
return returnedChunk;
}
/**
* Performs logic once a server chunk is available
* @param worldX The world x position
* @param worldY The world y position
* @param worldZ The world z position
* @param onLoad The logic to run once the chunk is available
*/
public void getChunkAsync(int worldX, int worldY, int worldZ, Consumer<ServerTerrainChunk> onLoad){
Globals.profiler.beginCpuSample("ServerTerrainManager.getChunkAsync");
this.chunkExecutorService.submit(new ChunkGenerationThread(chunkDiskMap, chunkCache, chunkGenerator, worldX, worldY, worldZ, onLoad));
Globals.profiler.endCpuSample();
} }
/** /**
@ -286,9 +287,8 @@ public class ServerTerrainManager {
if(model != null){ if(model != null){
model.addModification(modification); model.addModification(modification);
} }
String key = getKey(worldPos.x,worldPos.y,worldPos.z); if(chunkCache.containsChunk(worldPos.x,worldPos.y,worldPos.z)){
if(chunkCache.containsKey(key)){ ServerTerrainChunk chunk = chunkCache.get(worldPos.x,worldPos.y,worldPos.z);
ServerTerrainChunk chunk = chunkCache.get(key);
chunk.addModification(modification); chunk.addModification(modification);
} }
} }
@ -309,4 +309,11 @@ public class ServerTerrainManager {
return chunkGenerator; return chunkGenerator;
} }
/**
* Closes the generation threadpool
*/
public void closeThreads(){
this.chunkExecutorService.shutdownNow();
}
} }

View File

@ -0,0 +1,435 @@
package electrosphere.util.ds.octree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.joml.Vector3i;
import io.github.studiorailgun.MathUtils;
/**
* A power of two oct tree that supports arbitrary world size (not fixed)
*/
public class WorldOctTree <T> {
/**
* The maximum level of the chunk tree
*/
int maxLevel;
/**
* The root node
*/
private FloatingChunkTreeNode<T> root = null;
/**
* The list of all nodes in the tree
*/
List<FloatingChunkTreeNode<T>> nodes = null;
/**
* The minimum position
*/
Vector3i min;
/**
* The maximum position
*/
Vector3i max;
/**
* Constructor
* @param min The minimum position of the world
* @param max The maximum position of the world
*/
public WorldOctTree(Vector3i min, Vector3i max){
//check that dimensions are a multiple of 2
if(
((max.x - min.x) & (max.x - min.x - 1)) != 0 ||
((max.y - min.y) & (max.y - min.y - 1)) != 0 ||
((max.z - min.z) & (max.z - min.z - 1)) != 0
){
throw new Error("Invalid dimensions! Must be a power of two! " + min + " " + max);
}
if(max.x - min.x != max.y - min.y || max.x - min.x != max.z - min.z){
throw new Error("Invalid dimensions! Must be the same size along all three axis! " + min + " " + max);
}
this.min = new Vector3i(min);
this.max = new Vector3i(max);
//calculate max level
int dimRaw = max.x - min.x;
this.maxLevel = (int)MathUtils.log2(dimRaw);
this.nodes = new ArrayList<FloatingChunkTreeNode<T>>();
this.root = new FloatingChunkTreeNode<T>(this, 0, new Vector3i(min), new Vector3i(max));
this.root.isLeaf = true;
this.nodes.add(this.root);
}
/**
* Splits a parent into child nodes
* @param parent The parent
* @return The new non-leaf node
*/
public FloatingChunkTreeNode<T> split(FloatingChunkTreeNode<T> existing){
if(!existing.isLeaf()){
throw new IllegalArgumentException("Tried to split non-leaf!");
}
Vector3i min = existing.getMinBound();
Vector3i max = existing.getMaxBound();
int midX = (max.x - min.x) / 2 + min.x;
int midY = (max.y - min.y) / 2 + min.y;
int midZ = (max.z - min.z) / 2 + min.z;
int currentLevel = existing.getLevel();
FloatingChunkTreeNode<T> newContainer = new FloatingChunkTreeNode<>(this, currentLevel, min, max);
//add children
newContainer.addChild(new FloatingChunkTreeNode<T>(this, currentLevel + 1, new Vector3i(min.x,min.y,min.z), new Vector3i(midX,midY,midZ)));
newContainer.addChild(new FloatingChunkTreeNode<T>(this, currentLevel + 1, new Vector3i(midX,min.y,min.z), new Vector3i(max.x,midY,midZ)));
newContainer.addChild(new FloatingChunkTreeNode<T>(this, currentLevel + 1, new Vector3i(min.x,midY,min.z), new Vector3i(midX,max.y,midZ)));
newContainer.addChild(new FloatingChunkTreeNode<T>(this, currentLevel + 1, new Vector3i(midX,midY,min.z), new Vector3i(max.x,max.y,midZ)));
//
newContainer.addChild(new FloatingChunkTreeNode<T>(this, currentLevel + 1, new Vector3i(min.x,min.y,midZ), new Vector3i(midX,midY,max.z)));
newContainer.addChild(new FloatingChunkTreeNode<T>(this, currentLevel + 1, new Vector3i(midX,min.y,midZ), new Vector3i(max.x,midY,max.z)));
newContainer.addChild(new FloatingChunkTreeNode<T>(this, currentLevel + 1, new Vector3i(min.x,midY,midZ), new Vector3i(midX,max.y,max.z)));
newContainer.addChild(new FloatingChunkTreeNode<T>(this, currentLevel + 1, new Vector3i(midX,midY,midZ), new Vector3i(max.x,max.y,max.z)));
boolean foundMin = false;
for(FloatingChunkTreeNode<T> child : newContainer.getChildren()){
if(child.getMinBound().distance(newContainer.getMinBound()) == 0){
foundMin = true;
}
}
if(!foundMin){
String message = "Failed to sanity check!\n";
message = message + min + " " + max + "\n";
message = message + midX + " " + midY + " " + midZ + "\n";
message = message + "container mid: " + newContainer.getMinBound();
for(FloatingChunkTreeNode<T> child : newContainer.getChildren()){
message = message + "child min: " + child.getMinBound() + "\n";
}
throw new Error(message);
}
//replace existing node
replaceNode(existing,newContainer);
//update tracking
this.nodes.remove(existing);
this.nodes.add(newContainer);
this.nodes.addAll(newContainer.getChildren());
return newContainer;
}
/**
* Joins a non-leaf node's children into a single node
* @param parent The non-leaf
* @return The new leaf node
*/
public FloatingChunkTreeNode<T> join(FloatingChunkTreeNode<T> existing){
if(existing.isLeaf()){
throw new IllegalArgumentException("Tried to split non-leaf!");
}
Vector3i min = existing.getMinBound();
Vector3i max = existing.getMaxBound();
int currentLevel = existing.getLevel();
FloatingChunkTreeNode<T> newContainer = new FloatingChunkTreeNode<>(this, currentLevel, min, max);
//replace existing node
replaceNode(existing,newContainer);
//update tracking
this.nodes.remove(existing);
this.nodes.removeAll(existing.getChildren());
this.nodes.add(newContainer);
return newContainer;
}
/**
* Replaces an existing node with a new node
* @param existing the existing node
* @param newNode the new node
*/
private void replaceNode(FloatingChunkTreeNode<T> existing, FloatingChunkTreeNode<T> newNode){
if(existing == this.root){
this.root = newNode;
} else {
FloatingChunkTreeNode<T> parent = existing.getParent();
parent.removeChild(existing);
parent.addChild(newNode);
}
}
/**
* Gets the root node of the tree
*/
public FloatingChunkTreeNode<T> getRoot() {
return this.root;
}
/**
* Sets the max level of the tree
* @param maxLevel The max level
*/
public void setMaxLevel(int maxLevel){
this.maxLevel = maxLevel;
}
/**
* Gets the max level allowed for the tree
* @return The max level
*/
public int getMaxLevel(){
return maxLevel;
}
/**
* Clears the tree
*/
public void clear(){
this.nodes.clear();
this.root = new FloatingChunkTreeNode<T>(this, 0, new Vector3i(min), new Vector3i(max));
this.root.isLeaf = true;
this.nodes.add(this.root);
}
/**
* Searches for the node at a given position
* @param position The position
* @param returnNonLeaf If true, the function can return non-leaf nodes, otherwise will only return leaf nodes
* @return The leaf if it exists, null otherwise
*/
public FloatingChunkTreeNode<T> search(Vector3i position, boolean returnNonLeaf){
return this.search(position,returnNonLeaf,this.maxLevel);
}
/**
* Searches for the node at a given position
* @param position The position
* @param returnNonLeaf If true, the function can return non-leaf nodes, otherwise will only return leaf nodes
* @param maxLevel The maximum level to search for
* @return The leaf if it exists, null otherwise
*/
public FloatingChunkTreeNode<T> search(Vector3i position, boolean returnNonLeaf, int maxLevel){
//out of bounds check
if(
position.x < min.x || position.x > max.x ||
position.y < min.y || position.y > max.y ||
position.z < min.z || position.z > max.z
){
throw new Error("Trying to search for node outside tree range!");
}
FloatingChunkTreeNode<T> searchResult = recursiveSearchUnsafe(root,position,maxLevel);
if(!returnNonLeaf && !searchResult.isLeaf()){
return null;
}
return searchResult;
}
/**
* Recursively searches for the node at the position. Unsafe because it does not bounds check.
* @param currentNode The current node searching from
* @param position The position to search at
* @param maxLevel The maximum level to search for
* @return The found node
*/
private FloatingChunkTreeNode<T> recursiveSearchUnsafe(FloatingChunkTreeNode<T> currentNode, Vector3i position, int maxLevel){
if(maxLevel < 0){
throw new Error("Provided invalid max level! Must be created than 0! " + maxLevel);
}
if(currentNode.level > maxLevel){
throw new Error("Failed to stop before max level!");
}
if(currentNode.level == maxLevel){
return currentNode;
}
if(currentNode.getChildren().size() > 0){
for(FloatingChunkTreeNode<T> child : currentNode.getChildren()){
if(
position.x < child.getMaxBound().x && position.x >= child.getMinBound().x &&
position.y < child.getMaxBound().y && position.y >= child.getMinBound().y &&
position.z < child.getMaxBound().z && position.z >= child.getMinBound().z
){
return recursiveSearchUnsafe(child, position, maxLevel);
}
}
String message = "Current node is within range, but no children are! This does not make any sense.\n";
message = message + " current pos: " + currentNode.getMinBound() + " " + currentNode.getMaxBound() + "\n";
for(FloatingChunkTreeNode<T> child : currentNode.getChildren()){
message = message + " child " + child + " pos: " + child.getMinBound() + " " + child.getMaxBound() + "\n";
}
message = message + "position to search: " + position + "\n";
throw new Error(message);
} else {
return currentNode;
}
}
/**
* A node in a chunk tree
*/
public static class FloatingChunkTreeNode<T> {
//True if this is a leaf node, false otherwise
private boolean isLeaf;
//the parent node
private FloatingChunkTreeNode<T> parent;
/**
* The tree containing this node
*/
private WorldOctTree<T> containingTree;
//the children of this node
private List<FloatingChunkTreeNode<T>> children = new LinkedList<FloatingChunkTreeNode<T>>();
//The data at the node
private T data;
/**
* The min bound
*/
private Vector3i min;
/**
* The max bound.
* !!NOTE!! max is exclusive, not inclusive
*/
private Vector3i max;
/**
* The level of the chunk tree node
*/
int level;
/**
* Constructor for non-leaf node
* @param tree The parent tree
* @param level The level of the node
* @param min The minimum position of the node
* @param max The maximum position of then ode
*/
private FloatingChunkTreeNode(WorldOctTree<T> tree, int level, Vector3i min, Vector3i max){
if(tree == null){
throw new Error("Invalid tree provided " + tree);
}
int maxPos = (int)Math.pow(2,tree.getMaxLevel());
if(min.x == maxPos || min.y == maxPos || min.z == maxPos){
throw new IllegalArgumentException("Invalid minimum! " + min);
}
if(level < 0 || level > tree.getMaxLevel()){
throw new IllegalArgumentException("Invalid level! " + level);
}
this.containingTree = tree;
this.isLeaf = false;
this.level = level;
this.min = min;
this.max = max;
}
/**
* Constructor for use in tests
* @param tree The Tree
* @param level The level
* @param min The min point
* @param max The max point
* @return The node
*/
public static <T> FloatingChunkTreeNode<T> constructorForTests(WorldOctTree<T> tree, int level, Vector3i min, Vector3i max){
return new FloatingChunkTreeNode<T>(tree, level, min, max);
}
/**
* Converts this node to a leaf
* @param data The data to put in the leaf
*/
public void convertToLeaf(T data){
this.isLeaf = true;
this.data = data;
}
/**
* Gets the data associated with this node
*/
public T getData() {
return data;
}
/**
* Gets the parent of this node
*/
public FloatingChunkTreeNode<T> getParent() {
return parent;
}
/**
* Gets the children of this node
*/
public List<FloatingChunkTreeNode<T>> getChildren() {
return Collections.unmodifiableList(this.children);
}
/**
* Checks if this node is a leaf
* @return true if it is a leaf, false otherwise
*/
public boolean isLeaf() {
return isLeaf;
}
/**
* Checks if the node can split
* @return true if can split, false otherwise
*/
public boolean canSplit(){
return isLeaf && level < containingTree.getMaxLevel();
}
/**
* Gets the level of the node
* @return The level of the node
*/
public int getLevel(){
return level;
}
/**
* Gets the min bound of this node
* @return The min bound
*/
public Vector3i getMinBound(){
return new Vector3i(min);
}
/**
* Gets the max bound of this node
* @return The max bound
*/
public Vector3i getMaxBound(){
return new Vector3i(max);
}
/**
* Adds a child to this node
* @param child The child
*/
private void addChild(FloatingChunkTreeNode<T> child){
this.children.add(child);
child.parent = this;
}
/**
* Removes a child node
* @param child the child
*/
private void removeChild(FloatingChunkTreeNode<T> child){
this.children.remove(child);
child.parent = null;
}
}
}

View File

@ -0,0 +1,51 @@
package electrosphere.client.terrain.cells;
import static org.junit.jupiter.api.Assertions.*;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector3i;
import org.junit.jupiter.api.extension.ExtendWith;
import electrosphere.client.scene.ClientWorldData;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.test.annotations.UnitTest;
import electrosphere.test.template.extensions.StateCleanupCheckerExtension;
import electrosphere.util.ds.octree.WorldOctTree.FloatingChunkTreeNode;
/**
* Tests for the client draw cell manager
*/
@ExtendWith(StateCleanupCheckerExtension.class)
public class ClientDrawCellManagerTests {
/**
* Test creating a manager
*/
@UnitTest
public void testCreation(){
assertDoesNotThrow(() -> {
new ClientDrawCellManager(null, 64);
});
}
@UnitTest
public void testJoinCase(){
int worldDiscreteSize = 64;
Globals.clientWorldData = new ClientWorldData(new Vector3f(0), new Vector3f(worldDiscreteSize * ServerTerrainChunk.CHUNK_DIMENSION), 0, worldDiscreteSize);
ClientDrawCellManager manager = new ClientDrawCellManager(null, 64);
Vector3d playerPos = new Vector3d(0,0,0);
FloatingChunkTreeNode<DrawCell> node = FloatingChunkTreeNode.constructorForTests(manager.chunkTree, 1, new Vector3i(16,0,0), new Vector3i(32,16,16));
node.convertToLeaf(DrawCell.generateTerrainCell(new Vector3i(0,0,0)));
assertFalse(manager.shouldSplit(playerPos, node));
//cleanup
Main.shutdown();
}
}

View File

@ -30,6 +30,7 @@ public class StateCleanupCheckerExtension implements AfterEachCallback {
Globals.playerManager, Globals.playerManager,
LoggerInterface.loggerEngine, LoggerInterface.loggerEngine,
RenderingEngine.screenFramebuffer, RenderingEngine.screenFramebuffer,
Globals.clientWorldData,
}; };
for(Object object : objectsToCheck){ for(Object object : objectsToCheck){
if(object != null){ if(object != null){

View File

@ -2,9 +2,20 @@ package electrosphere.util.ds.octree;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import electrosphere.test.annotations.UnitTest;
/** /**
* Unit testing for the chunk octree implementation * Unit testing for the chunk octree implementation
*/ */
public class ChunkTreeTests { public class ChunkTreeTests {
/**
* Creates a chunk tree
*/
@UnitTest
public void testCreateChunkTree(){
ChunkTree<String> tree = new ChunkTree<String>();
assertNotNull(tree);
}
} }

View File

@ -0,0 +1,54 @@
package electrosphere.util.ds.octree;
import static org.junit.jupiter.api.Assertions.*;
import org.joml.Vector3i;
import electrosphere.test.annotations.UnitTest;
/**
* Tests the floating position chunk tree implementation
*/
public class WorldOctTreeTests {
/**
* Creates a chunk tree
*/
@UnitTest
public void testCreateFloatingChunkTree(){
WorldOctTree<String> tree = new WorldOctTree<String>(new Vector3i(0,0,0), new Vector3i(64,64,64));
assertNotNull(tree);
}
/**
* Test changing the centered point of the floating tree
*/
@UnitTest
public void testMaxLevelSetting(){
WorldOctTree<String> tree = new WorldOctTree<String>(new Vector3i(0,0,0), new Vector3i(64,64,64));
assertEquals(6,tree.getMaxLevel());
}
/**
* Assert non-power-of-two dims fail
*/
@UnitTest
public void testFailOnNonPowTwoDim(){
assertThrows(Error.class, () -> {
new WorldOctTree<String>(new Vector3i(0,0,0), new Vector3i(63,63,63));
});
}
/**
* Assert unequal dims fail
*/
@UnitTest
public void testFailOnUnequalDim(){
assertThrows(Error.class, () -> {
new WorldOctTree<String>(new Vector3i(0,0,0), new Vector3i(64,1,64));
});
}
}

View File

@ -10,7 +10,6 @@
"./net/lore.json", "./net/lore.json",
"./net/player.json", "./net/player.json",
"./net/terrain.json", "./net/terrain.json",
"./net/world.json",
"./net/server.json", "./net/server.json",
"./net/character.json", "./net/character.json",
"./net/inventory.json", "./net/inventory.json",