rewrite client side chunks + transvoxel integration
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
d6c476b4a3
commit
d2ccf3c479
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,7 +11,7 @@
|
||||
/nb-configuration.xml
|
||||
|
||||
/Telephone-*.jar
|
||||
/NetArranger-*.jar
|
||||
/NetArranger*.jar
|
||||
/lwjglx-debug-*.jar
|
||||
/hs_err_pid*
|
||||
/replay_pid*
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#maven.buildNumber.plugin properties file
|
||||
#Mon Oct 28 16:07:42 EDT 2024
|
||||
buildNumber=364
|
||||
#Mon Nov 04 12:41:10 EST 2024
|
||||
buildNumber=375
|
||||
|
||||
@ -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
|
||||
@ -4,4 +4,5 @@
|
||||
- @subpage biomeselection
|
||||
- @subpage biomegenerationproblems
|
||||
- @subpage terraingenerationprocess
|
||||
- @subpage voxelgenideas
|
||||
- @subpage voxelgenideas
|
||||
- @subpage chunkgenoptimizations
|
||||
@ -914,6 +914,22 @@ Update default resolution in config
|
||||
Fix main menu ui test
|
||||
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
|
||||
|
||||
43
pom.xml
43
pom.xml
@ -305,7 +305,15 @@
|
||||
<dependency>
|
||||
<groupId>io.github.studiorailgun</groupId>
|
||||
<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>
|
||||
|
||||
|
||||
@ -440,6 +448,24 @@
|
||||
</arguments>
|
||||
</configuration>
|
||||
</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>
|
||||
</plugin>
|
||||
</plugins>
|
||||
@ -452,6 +478,20 @@
|
||||
<!-- To execute this profile, run a command like "mvn test -P integration" -->
|
||||
<profile>
|
||||
<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>
|
||||
<!--The tests to run-->
|
||||
<groups>fast,unit,integration</groups>
|
||||
@ -471,6 +511,7 @@
|
||||
<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>
|
||||
|
||||
@ -14,6 +14,7 @@ import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
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){
|
||||
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>();
|
||||
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(){
|
||||
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(){
|
||||
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(){
|
||||
return updateable.size() > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Transforms real space into cell space
|
||||
* @param input The real coordinate
|
||||
* @return The cell coordinate
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public void update(){
|
||||
Globals.profiler.beginCpuSample("FluidCellManager.update");
|
||||
calculateDeltas();
|
||||
if(update){
|
||||
if(containsUnrequestedCell() && !containsUndrawableCell()){
|
||||
if(containsUnrequestedCell()){
|
||||
updateUnrequestedCell();
|
||||
} else if(containsUndrawableCell()){
|
||||
makeCellDrawable();
|
||||
@ -331,6 +364,7 @@ public class FluidCellManager {
|
||||
updateCellModel();
|
||||
}
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -83,6 +83,7 @@ public class ClientFoliageManager {
|
||||
* @param worldPos The world position
|
||||
*/
|
||||
private void updatePosition(Vector3i worldPos){
|
||||
Globals.profiler.beginCpuSample("ClientFoliageManager.updatePosition");
|
||||
FoliageChunk foundChunk = null;
|
||||
for(FoliageChunk chunk : chunkUpdateCache){
|
||||
if(chunk.getWorldPos().equals(worldPos)){
|
||||
@ -98,7 +99,8 @@ public class ClientFoliageManager {
|
||||
} else {
|
||||
chunkUpdateCache.remove(foundChunk);
|
||||
}
|
||||
foundChunk.updateCells();
|
||||
foundChunk.updateCells(false);
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,7 +132,7 @@ public class ClientFoliageManager {
|
||||
public boolean dependenciesAreReady(){
|
||||
return
|
||||
Globals.clientWorldData != null &&
|
||||
Globals.drawCellManager != null &&
|
||||
Globals.clientDrawCellManager != null &&
|
||||
Globals.playerEntity != null
|
||||
;
|
||||
}
|
||||
@ -142,7 +144,7 @@ public class ClientFoliageManager {
|
||||
public void evaluateChunk(Vector3i worldPos){
|
||||
for(FoliageChunk chunk : chunkUpdateCache){
|
||||
if(chunk.getWorldPos().equals(worldPos)){
|
||||
chunk.updateCells();
|
||||
chunk.updateCells(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,7 +212,7 @@ public class FoliageCell {
|
||||
if(voxelPosition.y + 1 >= ServerTerrainChunk.CHUNK_DIMENSION){
|
||||
return;
|
||||
}
|
||||
if(!Globals.drawCellManager.generatedPhysics(worldPosition)){
|
||||
if(!Globals.clientDrawCellManager.isFullLOD(worldPosition)){
|
||||
return;
|
||||
}
|
||||
List<String> foliageTypesSupported = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(data.getType(voxelPosition)).getAmbientFoliage();
|
||||
|
||||
@ -34,6 +34,11 @@ public class FoliageChunk {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -75,27 +80,56 @@ public class FoliageChunk {
|
||||
this.currentChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
|
||||
// //evaluate top cells if chunk above this one exists
|
||||
this.aboveChunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0));
|
||||
this.updateCells();
|
||||
this.updateCells(true);
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
//the sets to iterate through
|
||||
boolean updated = true;
|
||||
int attempts = 0;
|
||||
while(updated && attempts < 3){
|
||||
ChunkTreeNode<FoliageCell> rootNode = this.chunkTree.getRoot();
|
||||
updated = this.recursivelyUpdateCells(rootNode, playerPos);
|
||||
attempts++;
|
||||
//re-evaluate whether contains foliage voxel or not
|
||||
if(force){
|
||||
this.containsFoliageVoxel = checkContainsFoliageVoxel();
|
||||
}
|
||||
if(force || containsFoliageVoxel){
|
||||
Vector3d playerPos = EntityUtils.getPosition(Globals.playerEntity);
|
||||
//the sets to iterate through
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param node The root node
|
||||
@ -104,6 +138,7 @@ public class FoliageChunk {
|
||||
private boolean recursivelyUpdateCells(ChunkTreeNode<FoliageCell> node, Vector3d playerPos){
|
||||
boolean updated = false;
|
||||
if(this.shouldSplit(playerPos, node)){
|
||||
Globals.profiler.beginCpuSample("FoliageChunk.split");
|
||||
//perform op
|
||||
ChunkTreeNode<FoliageCell> container = chunkTree.split(node);
|
||||
|
||||
@ -119,8 +154,10 @@ public class FoliageChunk {
|
||||
);
|
||||
child.convertToLeaf(new FoliageCell(worldPos, child.getMinBound(), realPos, 5 - child.getLevel()));
|
||||
});
|
||||
Globals.profiler.endCpuSample();
|
||||
updated = true;
|
||||
} else if(this.shouldJoin(playerPos, node)) {
|
||||
// Globals.profiler.beginCpuSample("FoliageChunk.join");
|
||||
//perform op
|
||||
ChunkTreeNode<FoliageCell> newLeaf = chunkTree.join(node);
|
||||
|
||||
@ -129,9 +166,12 @@ public class FoliageChunk {
|
||||
|
||||
//do creations
|
||||
newLeaf.convertToLeaf(new FoliageCell(worldPos, newLeaf.getMinBound(), realPos, 5 - newLeaf.getLevel()));
|
||||
// Globals.profiler.endCpuSample();
|
||||
updated = true;
|
||||
} else if(shouldGenerate(playerPos, node)){
|
||||
// Globals.profiler.beginCpuSample("FoliageChunk.generate");
|
||||
node.getData().generate();
|
||||
// Globals.profiler.endCpuSample();
|
||||
updated = true;
|
||||
} else if(!node.isLeaf()){
|
||||
List<ChunkTreeNode<FoliageCell>> children = new LinkedList<ChunkTreeNode<FoliageCell>>(node.getChildren());
|
||||
|
||||
@ -4,6 +4,8 @@ import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
* Client's data on the world
|
||||
*/
|
||||
@ -31,8 +33,6 @@ public class ClientWorldData {
|
||||
Vector3f worldMinPoint;
|
||||
Vector3f worldMaxPoint;
|
||||
|
||||
int dynamicInterpolationRatio;
|
||||
|
||||
float randomDampener;
|
||||
|
||||
|
||||
@ -42,13 +42,11 @@ public class ClientWorldData {
|
||||
public ClientWorldData(
|
||||
Vector3f worldMinPoint,
|
||||
Vector3f worldMaxPoint,
|
||||
int dynamicInterpolationRatio,
|
||||
float randomDampener,
|
||||
int worldDiscreteSize
|
||||
) {
|
||||
this.worldMinPoint = worldMinPoint;
|
||||
this.worldMaxPoint = worldMaxPoint;
|
||||
this.dynamicInterpolationRatio = dynamicInterpolationRatio;
|
||||
this.randomDampener = randomDampener;
|
||||
this.worldDiscreteSize = worldDiscreteSize;
|
||||
}
|
||||
@ -64,10 +62,6 @@ public class ClientWorldData {
|
||||
return worldMaxPoint;
|
||||
}
|
||||
|
||||
public int getDynamicInterpolationRatio() {
|
||||
return dynamicInterpolationRatio;
|
||||
}
|
||||
|
||||
public float getRandomDampener() {
|
||||
return randomDampener;
|
||||
}
|
||||
@ -78,11 +72,11 @@ public class ClientWorldData {
|
||||
|
||||
|
||||
public int convertRealToChunkSpace(double real){
|
||||
return (int)Math.floor(real / dynamicInterpolationRatio);
|
||||
return (int)Math.floor(real / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
}
|
||||
|
||||
public float convertChunkToRealSpace(int chunk){
|
||||
return chunk * dynamicInterpolationRatio;
|
||||
return chunk * ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
}
|
||||
|
||||
public int convertRealToWorld(double real){
|
||||
|
||||
@ -166,7 +166,7 @@ public class ClientSimulation {
|
||||
* Loads terrain that is in queue
|
||||
*/
|
||||
public void loadTerrain(){
|
||||
Globals.profiler.beginCpuSample("load terrain");
|
||||
Globals.profiler.beginCpuSample("ClientSimulation.loadTerrain");
|
||||
if(Globals.clientTerrainManager != null){
|
||||
Globals.clientTerrainManager.handleMessages();
|
||||
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
|
||||
///
|
||||
if(Globals.drawCellManager != null && Globals.clientWorldData != null){
|
||||
if(Globals.clientDrawCellManager != null && Globals.clientWorldData != null){
|
||||
//Cell manager do your things
|
||||
Globals.drawCellManager.update();
|
||||
Globals.clientDrawCellManager.update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,31 @@ public class ChunkData {
|
||||
//Used in DrawCell to keep track of which positions to invalidate
|
||||
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
|
||||
@ -178,5 +203,13 @@ public class ChunkData {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import io.github.studiorailgun.HashUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Acts as a cache in front of terrain model to streamline receiving chunks
|
||||
@ -17,16 +19,21 @@ public class ClientTerrainCache {
|
||||
//cache capacity
|
||||
int cacheSize;
|
||||
//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
|
||||
List<String> cacheList = new CopyOnWriteArrayList<String>();
|
||||
List<Long> cacheList = new CopyOnWriteArrayList<Long>();
|
||||
//A map of chunk to its world position
|
||||
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
|
||||
* @param cacheSize The capacity of the cache
|
||||
*/
|
||||
*/
|
||||
public ClientTerrainCache(int cacheSize){
|
||||
this.cacheSize = cacheSize;
|
||||
}
|
||||
@ -42,8 +49,10 @@ public class ClientTerrainCache {
|
||||
cacheMap.put(getKey(worldX,worldY,worldZ),chunkData);
|
||||
chunkPositionMap.put(chunkData,new Vector3i(worldX,worldY,worldZ));
|
||||
while(cacheList.size() > cacheSize){
|
||||
String currentChunk = cacheList.remove(0);
|
||||
cacheMap.remove(currentChunk);
|
||||
Long currentChunk = cacheList.remove(0);
|
||||
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
|
||||
* @return The cache key
|
||||
*/
|
||||
public String getKey(int worldX, int worldY, int worldZ){
|
||||
return worldX + "_" + worldY + "_" + worldZ;
|
||||
public long getKey(int worldX, int worldY, int worldZ){
|
||||
return HashUtils.cantorHash(worldX, worldY, worldZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,5 +119,34 @@ public class ClientTerrainCache {
|
||||
public Vector3i getChunkPosition(ChunkData 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
package electrosphere.client.terrain.cells;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.joml.Quaterniond;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
@ -10,6 +12,7 @@ import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.ClientEntityUtils;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.types.terrain.TerrainChunk;
|
||||
import electrosphere.renderer.meshgen.TransvoxelModelGeneration;
|
||||
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];
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
DrawCell(){
|
||||
private DrawCell(){
|
||||
|
||||
}
|
||||
|
||||
@ -66,17 +111,35 @@ public class DrawCell {
|
||||
/**
|
||||
* 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){
|
||||
Globals.clientScene.deregisterEntity(modelEntity);
|
||||
}
|
||||
this.fillInData();
|
||||
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());
|
||||
this.setHasGenerated(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,147 +153,322 @@ public class DrawCell {
|
||||
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
|
||||
*/
|
||||
public void destroy(){
|
||||
CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
|
||||
collisionEngine.destroyPhysics(modelEntity);
|
||||
ClientEntityUtils.destroyEntity(modelEntity);
|
||||
if(modelEntity != null){
|
||||
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
|
||||
* @return true if the data was successfully filled, false otherwise
|
||||
*/
|
||||
private void fillInData(){
|
||||
//
|
||||
//fill in data
|
||||
//
|
||||
//main chunk
|
||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos);
|
||||
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++){
|
||||
weights[x][y][z] = currentChunk.getWeight(x,y,z);
|
||||
types[x][y][z] = currentChunk.getType(x,y,z);
|
||||
private boolean fillInData(int lod){
|
||||
int spacingFactor = (int)Math.pow(2,lod);
|
||||
for(int x = 0; x < ChunkData.CHUNK_DATA_GENERATOR_SIZE; x++){
|
||||
for(int y = 0; y < ChunkData.CHUNK_DATA_GENERATOR_SIZE; y++){
|
||||
for(int z = 0; z < ChunkData.CHUNK_DATA_GENERATOR_SIZE; z++){
|
||||
ChunkData currentChunk = Globals.clientTerrainManager.getChunkDataAtWorldPoint(
|
||||
worldPos.x + (x * spacingFactor) / ChunkData.CHUNK_SIZE,
|
||||
worldPos.y + (y * spacingFactor) / ChunkData.CHUNK_SIZE,
|
||||
worldPos.z + (z * spacingFactor) / ChunkData.CHUNK_SIZE
|
||||
);
|
||||
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
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package electrosphere.client.terrain.cells;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@ -18,6 +19,7 @@ import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.renderer.shader.ShaderProgram;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
@Deprecated
|
||||
/**
|
||||
* Manages the graphical entities for the terrain chunks
|
||||
*
|
||||
@ -235,7 +237,7 @@ public class DrawCellManager {
|
||||
);
|
||||
cells.add(cell);
|
||||
keyCellMap.put(targetKey,cell);
|
||||
DrawCellFace higherLODFace = null;
|
||||
List<DrawCellFace> higherLODFace = null;
|
||||
keyCellMap.get(targetKey).generateDrawableEntity(atlas,0,higherLODFace);
|
||||
|
||||
//evaluate for foliage
|
||||
@ -261,7 +263,7 @@ public class DrawCellManager {
|
||||
worldPos.z < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
keyCellMap.get(targetKey).destroy();
|
||||
DrawCellFace higherLODFace = null;
|
||||
List<DrawCellFace> higherLODFace = null;
|
||||
keyCellMap.get(targetKey).generateDrawableEntity(atlas,0,higherLODFace);
|
||||
}
|
||||
drawable.add(targetKey);
|
||||
@ -304,7 +306,7 @@ public class DrawCellManager {
|
||||
* @return the cell coordinate
|
||||
*/
|
||||
public int transformRealSpaceToCellSpace(double input){
|
||||
return (int)(input / Globals.clientWorldData.getDynamicInterpolationRatio());
|
||||
return (int)(input / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,12 +8,14 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
import electrosphere.client.terrain.cache.ChunkData;
|
||||
import electrosphere.client.terrain.cache.ClientTerrainCache;
|
||||
import electrosphere.client.terrain.cells.ClientDrawCellManager;
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.types.terrain.TerrainChunkData;
|
||||
@ -21,6 +23,7 @@ import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.renderer.meshgen.TerrainChunkModelGeneration;
|
||||
import electrosphere.renderer.model.Model;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
|
||||
/**
|
||||
@ -30,12 +33,22 @@ public class ClientTerrainManager {
|
||||
|
||||
//queues messages from server
|
||||
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
|
||||
public static final int INTERPOLATION_RATIO = ServerTerrainManager.SERVER_TERRAIN_MANAGER_INTERPOLATION_RATIO;
|
||||
|
||||
//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
|
||||
ClientTerrainCache terrainCache;
|
||||
@ -58,6 +71,8 @@ public class ClientTerrainManager {
|
||||
* Handles messages that have been received from the server
|
||||
*/
|
||||
public void handleMessages(){
|
||||
Globals.profiler.beginCpuSample("ClientTerrainManager.handleMessages");
|
||||
lock.acquireUninterruptibly();
|
||||
List<TerrainMessage> bouncedMessages = new LinkedList<TerrainMessage>();
|
||||
for(TerrainMessage message : messageQueue){
|
||||
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.setVoxelWeight(weights);
|
||||
terrainCache.addChunkDataToCache(
|
||||
@ -99,6 +114,8 @@ public class ClientTerrainManager {
|
||||
for(TerrainMessage message : bouncedMessages){
|
||||
messageQueue.add(message);
|
||||
}
|
||||
lock.release();
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,7 +130,9 @@ public class ClientTerrainManager {
|
||||
* @param message The message
|
||||
*/
|
||||
public void attachTerrainMessage(TerrainMessage message){
|
||||
lock.acquireUninterruptibly();
|
||||
messageQueue.add(message);
|
||||
lock.release();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,6 +154,22 @@ public class ClientTerrainManager {
|
||||
public boolean containsChunkDataAtWorldPoint(Vector3i worldPos){
|
||||
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
|
||||
@ -184,7 +219,9 @@ public class ClientTerrainManager {
|
||||
UUID newUUID = UUID.randomUUID();
|
||||
promisedHash = newUUID.toString();
|
||||
TerrainChunkGenQueueItem queueItem = new TerrainChunkGenQueueItem(data, promisedHash, atlas);
|
||||
lock.acquireUninterruptibly();
|
||||
terrainChunkGenerationQueue.add(queueItem);
|
||||
lock.release();
|
||||
return promisedHash;
|
||||
}
|
||||
|
||||
@ -192,12 +229,14 @@ public class ClientTerrainManager {
|
||||
* Pushes all terrain data in queue to the gpu and registers the resulting models
|
||||
*/
|
||||
public static void generateTerrainChunkGeometry(){
|
||||
Globals.profiler.beginCpuSample("generateTerrainChunkGeometry");
|
||||
Globals.profiler.beginCpuSample("ClientTerrainManager.generateTerrainChunkGeometry");
|
||||
lock.acquireUninterruptibly();
|
||||
for(TerrainChunkGenQueueItem queueItem : terrainChunkGenerationQueue){
|
||||
Model terrainModel = TerrainChunkModelGeneration.generateTerrainModel(queueItem.getData(), queueItem.getAtlas());
|
||||
Globals.assetManager.registerModelToSpecificString(terrainModel, queueItem.getPromisedHash());
|
||||
}
|
||||
terrainChunkGenerationQueue.clear();
|
||||
lock.release();
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
|
||||
@ -217,5 +256,13 @@ public class ClientTerrainManager {
|
||||
public Vector3i getPositionOfChunk(ChunkData 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -184,6 +184,9 @@ public class WindowUtils {
|
||||
initTooltipWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inits the loading window
|
||||
*/
|
||||
static void initLoadingWindow(){
|
||||
Window loadingWindow = Window.create(Globals.renderingEngine.getOpenGLState(), 0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, false);
|
||||
Label loadingLabel = Label.createLabel("LOADING");
|
||||
@ -208,6 +211,18 @@ public class WindowUtils {
|
||||
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
|
||||
*/
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -47,7 +47,7 @@ public class ImGuiTestGen {
|
||||
if(ImGui.button("Regenerate")){
|
||||
GriddedDataCellManager gridManager = (GriddedDataCellManager)Globals.realmManager.first().getDataCellManager();
|
||||
gridManager.evictAll();
|
||||
Globals.drawCellManager.evictAll();
|
||||
Globals.clientDrawCellManager.evictAll();
|
||||
Globals.clientTerrainManager.evictAll();
|
||||
}
|
||||
|
||||
@ -80,6 +80,11 @@ public class ImGuiTestGen {
|
||||
if(ImGui.inputInt("biome[1][1]", biome11)){
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@ -51,6 +51,7 @@ public class ImGuiWindowMacros {
|
||||
ImGuiLogger.createLoggersWindows();
|
||||
ImGuiRenderer.createRendererWindows();
|
||||
ImGuiTestGen.createTestGenWindows();
|
||||
ImGuiChunkMonitor.createChunkMonitorWindows();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,6 +184,10 @@ public class ImGuiWindowMacros {
|
||||
){
|
||||
ImGuiTestGen.testGenWindow.setOpen(true);
|
||||
}
|
||||
//chunk monitor
|
||||
if(ImGui.button("Chunk Monitor")){
|
||||
ImGuiChunkMonitor.chunkMonitorWindow.setOpen(true);
|
||||
}
|
||||
//close button
|
||||
if(ImGui.button("Close")){
|
||||
mainDebugWindow.setOpen(false);
|
||||
|
||||
@ -724,7 +724,9 @@ public class CollisionEngine {
|
||||
DBody rigidBody = PhysicsEntityUtils.getDBody(e);
|
||||
deregisterCollisionObject(rigidBody,PhysicsEntityUtils.getCollidable(e));
|
||||
e.removeData(EntityDataStrings.PHYSICS_COLLISION_BODY);
|
||||
deregisterPhysicsObject(rigidBody);
|
||||
if(rigidBody != null){
|
||||
deregisterPhysicsObject(rigidBody);
|
||||
}
|
||||
}
|
||||
if(ServerPhysicsSyncTree.hasTree(e)){
|
||||
ServerPhysicsSyncTree.detachTree(e, ServerPhysicsSyncTree.getTree(e));
|
||||
|
||||
@ -62,7 +62,7 @@ public class CollisionWorldData {
|
||||
|
||||
public int getDynamicInterpolationRatio(){
|
||||
if(clientWorldData != null){
|
||||
return clientWorldData.getDynamicInterpolationRatio();
|
||||
return clientWorldData.getWorldDiscreteSize();
|
||||
} else {
|
||||
return serverWorldData.getDynamicInterpolationRatio();
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ import electrosphere.client.player.ClientPlayerData;
|
||||
import electrosphere.client.scene.ClientSceneWrapper;
|
||||
import electrosphere.client.scene.ClientWorldData;
|
||||
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.manager.ClientTerrainManager;
|
||||
import electrosphere.client.ui.menu.WindowUtils;
|
||||
@ -235,7 +235,7 @@ public class Globals {
|
||||
//
|
||||
//Generic OpenGL Statements
|
||||
//
|
||||
public static long window;
|
||||
public static long window = -1;
|
||||
|
||||
|
||||
|
||||
@ -351,7 +351,7 @@ public class Globals {
|
||||
|
||||
//chunk stuff
|
||||
//draw cell manager
|
||||
public static DrawCellManager drawCellManager;
|
||||
public static ClientDrawCellManager clientDrawCellManager;
|
||||
public static VoxelTextureAtlas voxelTextureAtlas = new VoxelTextureAtlas();
|
||||
|
||||
//fluid cell manager
|
||||
@ -696,6 +696,7 @@ public class Globals {
|
||||
Globals.realmManager = null;
|
||||
Globals.clientSceneWrapper = null;
|
||||
Globals.clientScene = null;
|
||||
Globals.clientWorldData = null;
|
||||
Globals.audioEngine = null;
|
||||
Globals.renderingEngine = null;
|
||||
Globals.threadManager = null;
|
||||
|
||||
@ -420,14 +420,7 @@ public class Main {
|
||||
Globals.profiler.endCpuSample();
|
||||
|
||||
} catch (NullPointerException ex){
|
||||
LoggerInterface.loggerEngine.ERROR("Main frame uncaught NPE", 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!");
|
||||
}
|
||||
LoggerInterface.loggerEngine.ERROR(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import electrosphere.client.entity.crosshair.Crosshair;
|
||||
import electrosphere.client.fluid.cells.FluidCellManager;
|
||||
import electrosphere.client.foliagemanager.ClientFoliageManager;
|
||||
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.WindowStrings;
|
||||
import electrosphere.client.ui.menu.WindowUtils;
|
||||
@ -29,16 +29,27 @@ import electrosphere.net.NetUtils;
|
||||
import electrosphere.net.client.ClientNetworking;
|
||||
import electrosphere.renderer.actor.Actor;
|
||||
import electrosphere.renderer.actor.ActorTextureMask;
|
||||
import electrosphere.renderer.ui.elements.Window;
|
||||
|
||||
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){
|
||||
Window loadingWindow = (Window)Globals.elementService.getWindow(WindowStrings.WINDOW_LOADING);
|
||||
WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_MENU_MAIN), false);
|
||||
WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu());
|
||||
loadingWindow.setVisible(true);
|
||||
WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_LOADING), true);
|
||||
WindowUtils.updateLoadingWindow("Waiting on server");
|
||||
//disable menu input
|
||||
Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT);
|
||||
//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
|
||||
WindowUtils.replaceMainMenuContents(MenuGeneratorsMultiplayer.createMultiplayerCharacterCreationWindow());
|
||||
//make loading dialog disappear
|
||||
loadingWindow.setVisible(false);
|
||||
WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_LOADING), false);
|
||||
//make character creation window visible
|
||||
WindowUtils.recursiveSetVisible(Globals.elementService.getWindow(WindowStrings.WINDOW_MENU_MAIN), true);
|
||||
//recapture window
|
||||
@ -70,6 +81,7 @@ public class ClientLoading {
|
||||
Globals.signalSystem.post(SignalType.UI_MODIFICATION, () -> {
|
||||
WindowUtils.closeWindow(WindowStrings.WINDOW_MENU_MAIN);
|
||||
WindowUtils.recursiveSetVisible(WindowStrings.WINDOW_LOADING, true);
|
||||
WindowUtils.updateLoadingWindow("LOADING");
|
||||
});
|
||||
//disable menu input
|
||||
Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT);
|
||||
@ -263,7 +275,8 @@ public class ClientLoading {
|
||||
*/
|
||||
static void initDrawCellManager(boolean blockForInit){
|
||||
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 {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
iterations++;
|
||||
@ -278,28 +291,25 @@ public class ClientLoading {
|
||||
}
|
||||
}
|
||||
//initialize draw cell manager
|
||||
Globals.drawCellManager = new DrawCellManager(Globals.clientTerrainManager, 0, 0, 0);
|
||||
//construct texture atlas
|
||||
Globals.drawCellManager.attachTextureAtlas(Globals.voxelTextureAtlas);
|
||||
//set our draw cell manager to actually generate drawable chunks
|
||||
Globals.drawCellManager.setGenerateDrawables(true);
|
||||
// Globals.drawCellManager = new DrawCellManager(Globals.clientTerrainManager, 0, 0, 0);
|
||||
Globals.clientDrawCellManager = new ClientDrawCellManager(Globals.voxelTextureAtlas, Globals.clientWorldData.getWorldDiscreteSize());
|
||||
//Alerts the client simulation that it should start loading terrain
|
||||
Globals.clientSimulation.setLoadingTerrain(true);
|
||||
//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 {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
while(blockForInit && Globals.drawCellManager.containsUndrawableCell()){
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
if(i < DRAW_CELL_EXPECTED_MINIMUM_FRAMES_TO_INIT){
|
||||
LoggerInterface.loggerEngine.WARNING("Probably didn't block for draw cell manager initialization!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,7 +320,8 @@ public class ClientLoading {
|
||||
static void initFluidCellManager(boolean blockForInit){
|
||||
|
||||
//wait for world data
|
||||
while(blockForInit && Globals.clientWorldData == null){
|
||||
WindowUtils.updateLoadingWindow("WAITING ON WORLD DATA");
|
||||
while(blockForInit && Globals.clientWorldData == null && Globals.threadManager.shouldKeepRunning()){
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
@ -323,7 +334,8 @@ public class ClientLoading {
|
||||
Globals.clientSimulation.setLoadingTerrain(true);
|
||||
|
||||
//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 {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
@ -332,7 +344,8 @@ public class ClientLoading {
|
||||
}
|
||||
|
||||
//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 {
|
||||
TimeUnit.MILLISECONDS.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
|
||||
@ -17,7 +17,6 @@ import electrosphere.entity.types.creature.CreatureTemplate;
|
||||
import electrosphere.entity.types.creature.CreatureToolbarData.ToolbarItem;
|
||||
import electrosphere.game.data.creature.type.CreatureData;
|
||||
import electrosphere.game.data.creature.type.visualattribute.VisualAttribute;
|
||||
import electrosphere.game.server.world.MacroData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.NetUtils;
|
||||
import electrosphere.net.client.ClientNetworking;
|
||||
@ -26,41 +25,17 @@ import electrosphere.net.server.Server;
|
||||
import electrosphere.net.server.ServerConnectionHandler;
|
||||
import electrosphere.net.server.player.Player;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.simulation.MacroSimulation;
|
||||
import electrosphere.server.simulation.MicroSimulation;
|
||||
|
||||
/**
|
||||
* Utilities for all loading thread types
|
||||
*/
|
||||
public class LoadingUtils {
|
||||
|
||||
|
||||
// static void initCommonWorldData(boolean FLAG_INIT_SERVER){
|
||||
// if(Globals.commonWorldData == null){
|
||||
// if(FLAG_INIT_SERVER){
|
||||
// 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);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* The size of the buffer
|
||||
*/
|
||||
static final int STREAM_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||
|
||||
|
||||
|
||||
@ -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){
|
||||
ServerConnectionHandler rVal = null;
|
||||
try {
|
||||
@ -106,7 +86,7 @@ public class LoadingUtils {
|
||||
PipedInputStream clientInput = new PipedInputStream(STREAM_BUFFER_SIZE);
|
||||
PipedOutputStream serverOutput = new PipedOutputStream(clientInput);
|
||||
//server -> client pipe
|
||||
PipedInputStream serverInput = new PipedInputStream();
|
||||
PipedInputStream serverInput = new PipedInputStream(STREAM_BUFFER_SIZE);
|
||||
PipedOutputStream clientOutput;
|
||||
clientOutput = new PipedOutputStream(serverInput);
|
||||
//start server communication thread
|
||||
|
||||
@ -29,6 +29,14 @@ public class Signal {
|
||||
YOGA_DESTROY,
|
||||
UI_MODIFICATION,
|
||||
|
||||
//
|
||||
//Terrain
|
||||
//
|
||||
REQUEST_CHUNK,
|
||||
CHUNK_CREATED,
|
||||
REQUEST_CHUNK_EDIT,
|
||||
CHUNK_EDITED,
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -9,6 +9,8 @@ import java.util.concurrent.TimeUnit;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.loadingthreads.LoadingThread;
|
||||
import electrosphere.engine.threads.LabeledThread.ThreadLabel;
|
||||
import electrosphere.entity.types.terrain.TerrainChunk;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.util.CodeUtils;
|
||||
|
||||
/**
|
||||
@ -110,6 +112,19 @@ public class ThreadManager {
|
||||
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
|
||||
for(int i = 0; i < 3; i++){
|
||||
|
||||
@ -129,9 +129,6 @@ public class EntityCreationUtils {
|
||||
*/
|
||||
public static void makeEntityDrawablePreexistingModel(Entity entity, String 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.DRAW_SOLID_PASS, true);
|
||||
Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAWABLE);
|
||||
|
||||
@ -127,6 +127,15 @@ public class Scene {
|
||||
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
|
||||
* @param tree The behavior tree to register
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
package electrosphere.entity.types.terrain;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
|
||||
import electrosphere.client.terrain.cells.DrawCell;
|
||||
import electrosphere.client.terrain.cells.VoxelTextureAtlas;
|
||||
import electrosphere.client.terrain.manager.ClientTerrainManager;
|
||||
import electrosphere.collision.PhysicsEntityUtils;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityCreationUtils;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
@ -20,32 +24,44 @@ import electrosphere.server.datacell.Realm;
|
||||
*/
|
||||
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
|
||||
* @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 hasPolygons true if the chunk has polygons to generate a model with, false otherwise
|
||||
* @return The terrain chunk entity
|
||||
*/
|
||||
public static Entity clientCreateTerrainChunkEntity(TransvoxelChunkData chunkData, int levelOfDetail, VoxelTextureAtlas atlas){
|
||||
|
||||
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);
|
||||
public static Entity clientCreateTerrainChunkEntity(TransvoxelChunkData chunkData, int levelOfDetail, VoxelTextureAtlas atlas, boolean hasPolygons){
|
||||
Globals.profiler.beginCpuSample("TerrainChunk.clientCreateTerrainChunkEntity");
|
||||
|
||||
Entity rVal = EntityCreationUtils.createClientSpatialEntity();
|
||||
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
|
||||
if(data.vertices.size() > 0 && levelOfDetail == DrawCell.FULL_DETAIL_LOD){
|
||||
PhysicsEntityUtils.clientAttachTerrainChunkRigidBody(rVal, data);
|
||||
|
||||
if(hasPolygons){
|
||||
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.DRAW_CAST_SHADOW, true);
|
||||
|
||||
Globals.profiler.endCpuSample();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -59,7 +75,7 @@ public class TerrainChunk {
|
||||
*/
|
||||
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);
|
||||
if(data.vertices.size() > 0){
|
||||
@ -84,4 +100,11 @@ public class TerrainChunk {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Halts all running generation threads
|
||||
*/
|
||||
public static void haltThreads(){
|
||||
generationService.shutdownNow();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -18,6 +18,11 @@ import java.util.concurrent.TimeUnit;
|
||||
* Client networking thread
|
||||
*/
|
||||
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
|
||||
@ -62,7 +67,7 @@ public class ClientNetworking implements Runnable {
|
||||
|
||||
//thresholds for when to send pings and to determine when we've disconnected
|
||||
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
|
||||
long lastPingTime = 0;
|
||||
long lastPongTime = 0;
|
||||
@ -174,18 +179,43 @@ public class ClientNetworking implements Runnable {
|
||||
//start parsing messages
|
||||
initialized = true;
|
||||
while(Globals.threadManager.shouldKeepRunning() && !this.shouldDisconnect){
|
||||
|
||||
//
|
||||
//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
|
||||
try {
|
||||
parser.pushMessagesOut();
|
||||
} catch(IOException 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();
|
||||
//basically if we haven't sent a ping in a while, send one
|
||||
if(currentTime - lastPingTime > SEND_PING_THRESHOLD){
|
||||
@ -228,7 +258,8 @@ public class ClientNetworking implements Runnable {
|
||||
/**
|
||||
* Parses messages asynchronously
|
||||
*/
|
||||
public void parseMessagesAsynchronously(){
|
||||
public boolean parseMessagesAsynchronously(){
|
||||
boolean foundMessages = false;
|
||||
if(initialized){
|
||||
while(parser.hasIncomingMessaage()){
|
||||
NetworkMessage message = parser.popIncomingMessage();
|
||||
@ -241,9 +272,11 @@ public class ClientNetworking implements Runnable {
|
||||
//do something
|
||||
Globals.profiler.beginCpuSample("ClientProtocol.handleMessage");
|
||||
this.messageProtocol.handleAsyncMessage(message);
|
||||
foundMessages = true;
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
}
|
||||
return foundMessages;
|
||||
}
|
||||
|
||||
|
||||
@ -314,6 +347,17 @@ public class ClientNetworking implements Runnable {
|
||||
public boolean isInitialized(){
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -97,9 +97,11 @@ public class MessageProtocol {
|
||||
}
|
||||
//queue bounced messages for synchronous resolution
|
||||
if(result != null){
|
||||
Globals.profiler.beginAggregateCpuSample("MessageProtocol(client) Await lock to synchronize message");
|
||||
this.synchronousMessageLock.acquireUninterruptibly();
|
||||
this.synchronousMessageQueue.add(result);
|
||||
this.synchronousMessageLock.release();
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
|
||||
@ -34,7 +34,6 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
|
||||
//Vector3f worldMinPoint, Vector3f worldMaxPoint, int dynamicInterpolationRatio, float randomDampener, int worldDiscreteSize
|
||||
new Vector3f(message.getworldMinX(),0,message.getworldMinY()),
|
||||
new Vector3f(message.getworldMaxX(),32,message.getworldMaxY()),
|
||||
ChunkData.CHUNK_SIZE,
|
||||
message.getrandomDampener(),
|
||||
message.getworldSizeDiscrete()
|
||||
);
|
||||
@ -44,9 +43,10 @@ public class TerrainProtocol implements ClientProtocolTemplate<TerrainMessage> {
|
||||
case SPAWNPOSITION:
|
||||
LoggerInterface.loggerNetworking.WARNING("Received spawnPosition packet on client. This is deprecated!");
|
||||
break;
|
||||
case SENDCHUNKDATA:
|
||||
case SENDCHUNKDATA: {
|
||||
LoggerInterface.loggerNetworking.DEBUG("(Client) Received terrain at " + message.getworldX() + " " + message.getworldY() + " " + message.getworldZ());
|
||||
Globals.clientTerrainManager.attachTerrainMessage(message);
|
||||
break;
|
||||
} break;
|
||||
case UPDATEVOXEL: {
|
||||
//
|
||||
//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)){
|
||||
ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPosToUpdate.x, worldPosToUpdate.y, worldPosToUpdate.z);
|
||||
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;
|
||||
case SENDFLUIDDATA: {
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class AuthMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum AuthMessageType {
|
||||
AUTHREQUEST,
|
||||
AUTHDETAILS,
|
||||
@ -14,10 +17,17 @@ public class AuthMessage extends NetworkMessage {
|
||||
AUTHFAILURE,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
AuthMessageType messageType;
|
||||
String user;
|
||||
String pass;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
AuthMessage(AuthMessageType messageType){
|
||||
this.type = MessageType.AUTH_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -27,26 +37,48 @@ public class AuthMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets user
|
||||
*/
|
||||
public String getuser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets user
|
||||
*/
|
||||
public void setuser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pass
|
||||
*/
|
||||
public String getpass() {
|
||||
return pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets pass
|
||||
*/
|
||||
public void setpass(String pass) {
|
||||
this.pass = pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.AUTH_MESSAGE_TYPE_AUTHREQUEST:
|
||||
@ -73,18 +105,27 @@ public class AuthMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type AuthRequest
|
||||
*/
|
||||
public static AuthMessage parseAuthRequestMessage(CircularByteBuffer byteBuffer){
|
||||
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHREQUEST);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type AuthRequest
|
||||
*/
|
||||
public static AuthMessage constructAuthRequestMessage(){
|
||||
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHREQUEST);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type AuthDetails can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseAuthDetailsMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -117,6 +158,9 @@ public class AuthMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type AuthDetails
|
||||
*/
|
||||
public static AuthMessage parseAuthDetailsMessage(CircularByteBuffer byteBuffer){
|
||||
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHDETAILS);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -125,6 +169,9 @@ public class AuthMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type AuthDetails
|
||||
*/
|
||||
public static AuthMessage constructAuthDetailsMessage(String user,String pass){
|
||||
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHDETAILS);
|
||||
rVal.setuser(user);
|
||||
@ -133,24 +180,36 @@ public class AuthMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type AuthSuccess
|
||||
*/
|
||||
public static AuthMessage parseAuthSuccessMessage(CircularByteBuffer byteBuffer){
|
||||
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHSUCCESS);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type AuthSuccess
|
||||
*/
|
||||
public static AuthMessage constructAuthSuccessMessage(){
|
||||
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHSUCCESS);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type AuthFailure
|
||||
*/
|
||||
public static AuthMessage parseAuthFailureMessage(CircularByteBuffer byteBuffer){
|
||||
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHFAILURE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type AuthFailure
|
||||
*/
|
||||
public static AuthMessage constructAuthFailureMessage(){
|
||||
AuthMessage rVal = new AuthMessage(AuthMessageType.AUTHFAILURE);
|
||||
rVal.serialize();
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class CharacterMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum CharacterMessageType {
|
||||
REQUESTCHARACTERLIST,
|
||||
RESPONSECHARACTERLIST,
|
||||
@ -17,9 +20,16 @@ public class CharacterMessage extends NetworkMessage {
|
||||
RESPONSESPAWNCHARACTER,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
CharacterMessageType messageType;
|
||||
String data;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
CharacterMessage(CharacterMessageType messageType){
|
||||
this.type = MessageType.CHARACTER_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -29,18 +39,34 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data
|
||||
*/
|
||||
public String getdata() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data
|
||||
*/
|
||||
public void setdata(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST:
|
||||
@ -77,18 +103,27 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestCharacterList
|
||||
*/
|
||||
public static CharacterMessage parseRequestCharacterListMessage(CircularByteBuffer byteBuffer){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCHARACTERLIST);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestCharacterList
|
||||
*/
|
||||
public static CharacterMessage constructRequestCharacterListMessage(){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCHARACTERLIST);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type ResponseCharacterList can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseResponseCharacterListMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -108,6 +143,9 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type ResponseCharacterList
|
||||
*/
|
||||
public static CharacterMessage parseResponseCharacterListMessage(CircularByteBuffer byteBuffer){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECHARACTERLIST);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -115,6 +153,9 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type ResponseCharacterList
|
||||
*/
|
||||
public static CharacterMessage constructResponseCharacterListMessage(String data){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECHARACTERLIST);
|
||||
rVal.setdata(data);
|
||||
@ -122,6 +163,9 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type RequestCreateCharacter can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseRequestCreateCharacterMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -141,6 +185,9 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestCreateCharacter
|
||||
*/
|
||||
public static CharacterMessage parseRequestCreateCharacterMessage(CircularByteBuffer byteBuffer){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCREATECHARACTER);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -148,6 +195,9 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestCreateCharacter
|
||||
*/
|
||||
public static CharacterMessage constructRequestCreateCharacterMessage(String data){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTCREATECHARACTER);
|
||||
rVal.setdata(data);
|
||||
@ -155,42 +205,63 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type ResponseCreateCharacterSuccess
|
||||
*/
|
||||
public static CharacterMessage parseResponseCreateCharacterSuccessMessage(CircularByteBuffer byteBuffer){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERSUCCESS);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type ResponseCreateCharacterSuccess
|
||||
*/
|
||||
public static CharacterMessage constructResponseCreateCharacterSuccessMessage(){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERSUCCESS);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type ResponseCreateCharacterFailure
|
||||
*/
|
||||
public static CharacterMessage parseResponseCreateCharacterFailureMessage(CircularByteBuffer byteBuffer){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERFAILURE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type ResponseCreateCharacterFailure
|
||||
*/
|
||||
public static CharacterMessage constructResponseCreateCharacterFailureMessage(){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSECREATECHARACTERFAILURE);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestSpawnCharacter
|
||||
*/
|
||||
public static CharacterMessage parseRequestSpawnCharacterMessage(CircularByteBuffer byteBuffer){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestSpawnCharacter
|
||||
*/
|
||||
public static CharacterMessage constructRequestSpawnCharacterMessage(){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type ResponseSpawnCharacter can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseResponseSpawnCharacterMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -210,6 +281,9 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type ResponseSpawnCharacter
|
||||
*/
|
||||
public static CharacterMessage parseResponseSpawnCharacterMessage(CircularByteBuffer byteBuffer){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSESPAWNCHARACTER);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -217,6 +291,9 @@ public class CharacterMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type ResponseSpawnCharacter
|
||||
*/
|
||||
public static CharacterMessage constructResponseSpawnCharacterMessage(String data){
|
||||
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.RESPONSESPAWNCHARACTER);
|
||||
rVal.setdata(data);
|
||||
|
||||
@ -1,16 +1,22 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class CombatMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum CombatMessageType {
|
||||
SERVERREPORTHITBOXCOLLISION,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
CombatMessageType messageType;
|
||||
int entityID;
|
||||
int receiverEntityID;
|
||||
@ -25,6 +31,10 @@ public class CombatMessage extends NetworkMessage {
|
||||
String hitboxType;
|
||||
String hurtboxType;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
CombatMessage(CombatMessageType messageType){
|
||||
this.type = MessageType.COMBAT_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -34,106 +44,188 @@ public class CombatMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entityID
|
||||
*/
|
||||
public int getentityID() {
|
||||
return entityID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entityID
|
||||
*/
|
||||
public void setentityID(int entityID) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets receiverEntityID
|
||||
*/
|
||||
public int getreceiverEntityID() {
|
||||
return receiverEntityID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets receiverEntityID
|
||||
*/
|
||||
public void setreceiverEntityID(int receiverEntityID) {
|
||||
this.receiverEntityID = receiverEntityID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets positionX
|
||||
*/
|
||||
public double getpositionX() {
|
||||
return positionX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets positionX
|
||||
*/
|
||||
public void setpositionX(double positionX) {
|
||||
this.positionX = positionX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets positionY
|
||||
*/
|
||||
public double getpositionY() {
|
||||
return positionY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets positionY
|
||||
*/
|
||||
public void setpositionY(double positionY) {
|
||||
this.positionY = positionY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets positionZ
|
||||
*/
|
||||
public double getpositionZ() {
|
||||
return positionZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets positionZ
|
||||
*/
|
||||
public void setpositionZ(double positionZ) {
|
||||
this.positionZ = positionZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rotationX
|
||||
*/
|
||||
public double getrotationX() {
|
||||
return rotationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotationX
|
||||
*/
|
||||
public void setrotationX(double rotationX) {
|
||||
this.rotationX = rotationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rotationY
|
||||
*/
|
||||
public double getrotationY() {
|
||||
return rotationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotationY
|
||||
*/
|
||||
public void setrotationY(double rotationY) {
|
||||
this.rotationY = rotationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rotationZ
|
||||
*/
|
||||
public double getrotationZ() {
|
||||
return rotationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotationZ
|
||||
*/
|
||||
public void setrotationZ(double rotationZ) {
|
||||
this.rotationZ = rotationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rotationW
|
||||
*/
|
||||
public double getrotationW() {
|
||||
return rotationW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotationW
|
||||
*/
|
||||
public void setrotationW(double rotationW) {
|
||||
this.rotationW = rotationW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets time
|
||||
*/
|
||||
public long gettime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets time
|
||||
*/
|
||||
public void settime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets hitboxType
|
||||
*/
|
||||
public String gethitboxType() {
|
||||
return hitboxType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets hitboxType
|
||||
*/
|
||||
public void sethitboxType(String hitboxType) {
|
||||
this.hitboxType = hitboxType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets hurtboxType
|
||||
*/
|
||||
public String gethurtboxType() {
|
||||
return hurtboxType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets hurtboxType
|
||||
*/
|
||||
public void sethurtboxType(String hurtboxType) {
|
||||
this.hurtboxType = hurtboxType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.COMBAT_MESSAGE_TYPE_SERVERREPORTHITBOXCOLLISION:
|
||||
@ -142,6 +234,9 @@ public class CombatMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type serverReportHitboxCollision can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseserverReportHitboxCollisionMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -192,6 +287,9 @@ public class CombatMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type serverReportHitboxCollision
|
||||
*/
|
||||
public static CombatMessage parseserverReportHitboxCollisionMessage(CircularByteBuffer byteBuffer){
|
||||
CombatMessage rVal = new CombatMessage(CombatMessageType.SERVERREPORTHITBOXCOLLISION);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -206,6 +304,9 @@ public class CombatMessage extends NetworkMessage {
|
||||
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){
|
||||
CombatMessage rVal = new CombatMessage(CombatMessageType.SERVERREPORTHITBOXCOLLISION);
|
||||
rVal.setentityID(entityID);
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class EntityMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum EntityMessageType {
|
||||
CREATE,
|
||||
MOVEUPDATE,
|
||||
@ -20,6 +23,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
SYNCPHYSICS,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
EntityMessageType messageType;
|
||||
int entityCategory;
|
||||
String entitySubtype;
|
||||
@ -56,6 +62,10 @@ public class EntityMessage extends NetworkMessage {
|
||||
int bTreeID;
|
||||
int propertyValueInt;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
EntityMessage(EntityMessageType messageType){
|
||||
this.type = MessageType.ENTITY_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -65,282 +75,496 @@ public class EntityMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entityCategory
|
||||
*/
|
||||
public int getentityCategory() {
|
||||
return entityCategory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entityCategory
|
||||
*/
|
||||
public void setentityCategory(int entityCategory) {
|
||||
this.entityCategory = entityCategory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entitySubtype
|
||||
*/
|
||||
public String getentitySubtype() {
|
||||
return entitySubtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entitySubtype
|
||||
*/
|
||||
public void setentitySubtype(String entitySubtype) {
|
||||
this.entitySubtype = entitySubtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entityID
|
||||
*/
|
||||
public int getentityID() {
|
||||
return entityID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entityID
|
||||
*/
|
||||
public void setentityID(int entityID) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets creatureTemplate
|
||||
*/
|
||||
public String getcreatureTemplate() {
|
||||
return creatureTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets creatureTemplate
|
||||
*/
|
||||
public void setcreatureTemplate(String creatureTemplate) {
|
||||
this.creatureTemplate = creatureTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets positionX
|
||||
*/
|
||||
public double getpositionX() {
|
||||
return positionX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets positionX
|
||||
*/
|
||||
public void setpositionX(double positionX) {
|
||||
this.positionX = positionX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets positionY
|
||||
*/
|
||||
public double getpositionY() {
|
||||
return positionY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets positionY
|
||||
*/
|
||||
public void setpositionY(double positionY) {
|
||||
this.positionY = positionY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets positionZ
|
||||
*/
|
||||
public double getpositionZ() {
|
||||
return positionZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets positionZ
|
||||
*/
|
||||
public void setpositionZ(double positionZ) {
|
||||
this.positionZ = positionZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rotationX
|
||||
*/
|
||||
public double getrotationX() {
|
||||
return rotationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotationX
|
||||
*/
|
||||
public void setrotationX(double rotationX) {
|
||||
this.rotationX = rotationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rotationY
|
||||
*/
|
||||
public double getrotationY() {
|
||||
return rotationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotationY
|
||||
*/
|
||||
public void setrotationY(double rotationY) {
|
||||
this.rotationY = rotationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rotationZ
|
||||
*/
|
||||
public double getrotationZ() {
|
||||
return rotationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotationZ
|
||||
*/
|
||||
public void setrotationZ(double rotationZ) {
|
||||
this.rotationZ = rotationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rotationW
|
||||
*/
|
||||
public double getrotationW() {
|
||||
return rotationW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotationW
|
||||
*/
|
||||
public void setrotationW(double rotationW) {
|
||||
this.rotationW = rotationW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets linVelX
|
||||
*/
|
||||
public double getlinVelX() {
|
||||
return linVelX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets linVelX
|
||||
*/
|
||||
public void setlinVelX(double linVelX) {
|
||||
this.linVelX = linVelX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets linVelY
|
||||
*/
|
||||
public double getlinVelY() {
|
||||
return linVelY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets linVelY
|
||||
*/
|
||||
public void setlinVelY(double linVelY) {
|
||||
this.linVelY = linVelY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets linVelZ
|
||||
*/
|
||||
public double getlinVelZ() {
|
||||
return linVelZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets linVelZ
|
||||
*/
|
||||
public void setlinVelZ(double linVelZ) {
|
||||
this.linVelZ = linVelZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets angVelX
|
||||
*/
|
||||
public double getangVelX() {
|
||||
return angVelX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets angVelX
|
||||
*/
|
||||
public void setangVelX(double angVelX) {
|
||||
this.angVelX = angVelX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets angVelY
|
||||
*/
|
||||
public double getangVelY() {
|
||||
return angVelY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets angVelY
|
||||
*/
|
||||
public void setangVelY(double angVelY) {
|
||||
this.angVelY = angVelY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets angVelZ
|
||||
*/
|
||||
public double getangVelZ() {
|
||||
return angVelZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets angVelZ
|
||||
*/
|
||||
public void setangVelZ(double angVelZ) {
|
||||
this.angVelZ = angVelZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets linForceX
|
||||
*/
|
||||
public double getlinForceX() {
|
||||
return linForceX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets linForceX
|
||||
*/
|
||||
public void setlinForceX(double linForceX) {
|
||||
this.linForceX = linForceX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets linForceY
|
||||
*/
|
||||
public double getlinForceY() {
|
||||
return linForceY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets linForceY
|
||||
*/
|
||||
public void setlinForceY(double linForceY) {
|
||||
this.linForceY = linForceY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets linForceZ
|
||||
*/
|
||||
public double getlinForceZ() {
|
||||
return linForceZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets linForceZ
|
||||
*/
|
||||
public void setlinForceZ(double linForceZ) {
|
||||
this.linForceZ = linForceZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets angForceX
|
||||
*/
|
||||
public double getangForceX() {
|
||||
return angForceX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets angForceX
|
||||
*/
|
||||
public void setangForceX(double angForceX) {
|
||||
this.angForceX = angForceX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets angForceY
|
||||
*/
|
||||
public double getangForceY() {
|
||||
return angForceY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets angForceY
|
||||
*/
|
||||
public void setangForceY(double angForceY) {
|
||||
this.angForceY = angForceY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets angForceZ
|
||||
*/
|
||||
public double getangForceZ() {
|
||||
return angForceZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets angForceZ
|
||||
*/
|
||||
public void setangForceZ(double angForceZ) {
|
||||
this.angForceZ = angForceZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets yaw
|
||||
*/
|
||||
public double getyaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets yaw
|
||||
*/
|
||||
public void setyaw(double yaw) {
|
||||
this.yaw = yaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pitch
|
||||
*/
|
||||
public double getpitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets pitch
|
||||
*/
|
||||
public void setpitch(double pitch) {
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets velocity
|
||||
*/
|
||||
public double getvelocity() {
|
||||
return velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets velocity
|
||||
*/
|
||||
public void setvelocity(double velocity) {
|
||||
this.velocity = velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets treeState
|
||||
*/
|
||||
public int gettreeState() {
|
||||
return treeState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets treeState
|
||||
*/
|
||||
public void settreeState(int treeState) {
|
||||
this.treeState = treeState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets propertyType
|
||||
*/
|
||||
public int getpropertyType() {
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets propertyType
|
||||
*/
|
||||
public void setpropertyType(int propertyType) {
|
||||
this.propertyType = propertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets propertyValue
|
||||
*/
|
||||
public int getpropertyValue() {
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets propertyValue
|
||||
*/
|
||||
public void setpropertyValue(int propertyValue) {
|
||||
this.propertyValue = propertyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets time
|
||||
*/
|
||||
public long gettime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets time
|
||||
*/
|
||||
public void settime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets bone
|
||||
*/
|
||||
public String getbone() {
|
||||
return bone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bone
|
||||
*/
|
||||
public void setbone(String bone) {
|
||||
this.bone = bone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets targetID
|
||||
*/
|
||||
public int gettargetID() {
|
||||
return targetID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets targetID
|
||||
*/
|
||||
public void settargetID(int targetID) {
|
||||
this.targetID = targetID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets bTreeID
|
||||
*/
|
||||
public int getbTreeID() {
|
||||
return bTreeID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bTreeID
|
||||
*/
|
||||
public void setbTreeID(int bTreeID) {
|
||||
this.bTreeID = bTreeID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets propertyValueInt
|
||||
*/
|
||||
public int getpropertyValueInt() {
|
||||
return propertyValueInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets propertyValueInt
|
||||
*/
|
||||
public void setpropertyValueInt(int propertyValueInt) {
|
||||
this.propertyValueInt = propertyValueInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.ENTITY_MESSAGE_TYPE_CREATE:
|
||||
@ -399,6 +623,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type Create can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseCreateMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -458,6 +685,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type Create
|
||||
*/
|
||||
public static EntityMessage parseCreateMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.CREATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -475,6 +705,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
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){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.CREATE);
|
||||
rVal.setentityID(entityID);
|
||||
@ -492,6 +725,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type moveUpdate
|
||||
*/
|
||||
public static EntityMessage parsemoveUpdateMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.MOVEUPDATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -510,6 +746,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
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){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.MOVEUPDATE);
|
||||
rVal.setentityID(entityID);
|
||||
@ -528,6 +767,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type attackUpdate
|
||||
*/
|
||||
public static EntityMessage parseattackUpdateMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACKUPDATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -544,6 +786,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
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){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACKUPDATE);
|
||||
rVal.setentityID(entityID);
|
||||
@ -560,18 +805,27 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type startAttack
|
||||
*/
|
||||
public static EntityMessage parsestartAttackMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.STARTATTACK);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type startAttack
|
||||
*/
|
||||
public static EntityMessage constructstartAttackMessage(){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.STARTATTACK);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type Kill
|
||||
*/
|
||||
public static EntityMessage parseKillMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.KILL);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -580,6 +834,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type Kill
|
||||
*/
|
||||
public static EntityMessage constructKillMessage(long time,int entityID){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.KILL);
|
||||
rVal.settime(time);
|
||||
@ -588,6 +845,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type Destroy
|
||||
*/
|
||||
public static EntityMessage parseDestroyMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.DESTROY);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -595,6 +855,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type Destroy
|
||||
*/
|
||||
public static EntityMessage constructDestroyMessage(int entityID){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.DESTROY);
|
||||
rVal.setentityID(entityID);
|
||||
@ -602,6 +865,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type setProperty
|
||||
*/
|
||||
public static EntityMessage parsesetPropertyMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.SETPROPERTY);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -612,6 +878,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type setProperty
|
||||
*/
|
||||
public static EntityMessage constructsetPropertyMessage(int entityID,long time,int propertyType,int propertyValue){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.SETPROPERTY);
|
||||
rVal.setentityID(entityID);
|
||||
@ -622,6 +891,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type attachEntityToEntity can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseattachEntityToEntityMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -647,6 +919,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type attachEntityToEntity
|
||||
*/
|
||||
public static EntityMessage parseattachEntityToEntityMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACHENTITYTOENTITY);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -656,6 +931,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type attachEntityToEntity
|
||||
*/
|
||||
public static EntityMessage constructattachEntityToEntityMessage(int entityID,String bone,int targetID){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.ATTACHENTITYTOENTITY);
|
||||
rVal.setentityID(entityID);
|
||||
@ -665,6 +943,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type updateEntityViewDir
|
||||
*/
|
||||
public static EntityMessage parseupdateEntityViewDirMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.UPDATEENTITYVIEWDIR);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -676,6 +957,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type updateEntityViewDir
|
||||
*/
|
||||
public static EntityMessage constructupdateEntityViewDirMessage(int entityID,long time,int propertyType,double yaw,double pitch){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.UPDATEENTITYVIEWDIR);
|
||||
rVal.setentityID(entityID);
|
||||
@ -687,6 +971,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type syncPhysics
|
||||
*/
|
||||
public static EntityMessage parsesyncPhysicsMessage(CircularByteBuffer byteBuffer){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.SYNCPHYSICS);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -714,6 +1001,9 @@ public class EntityMessage extends NetworkMessage {
|
||||
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){
|
||||
EntityMessage rVal = new EntityMessage(EntityMessageType.SYNCPHYSICS);
|
||||
rVal.setentityID(entityID);
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class InventoryMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum InventoryMessageType {
|
||||
ADDITEMTOINVENTORY,
|
||||
REMOVEITEMFROMINVENTORY,
|
||||
@ -21,6 +24,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
CLIENTREQUESTPERFORMITEMACTION,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
InventoryMessageType messageType;
|
||||
String itemTemplate;
|
||||
String equipPointId;
|
||||
@ -31,6 +37,10 @@ public class InventoryMessage extends NetworkMessage {
|
||||
int itemActionCode;
|
||||
int itemActionCodeState;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
InventoryMessage(InventoryMessageType messageType){
|
||||
this.type = MessageType.INVENTORY_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -40,74 +50,132 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets itemTemplate
|
||||
*/
|
||||
public String getitemTemplate() {
|
||||
return itemTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets itemTemplate
|
||||
*/
|
||||
public void setitemTemplate(String itemTemplate) {
|
||||
this.itemTemplate = itemTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets equipPointId
|
||||
*/
|
||||
public String getequipPointId() {
|
||||
return equipPointId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets equipPointId
|
||||
*/
|
||||
public void setequipPointId(String equipPointId) {
|
||||
this.equipPointId = equipPointId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entityId
|
||||
*/
|
||||
public int getentityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entityId
|
||||
*/
|
||||
public void setentityId(int entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets equipperId
|
||||
*/
|
||||
public int getequipperId() {
|
||||
return equipperId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets equipperId
|
||||
*/
|
||||
public void setequipperId(int equipperId) {
|
||||
this.equipperId = equipperId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets containerType
|
||||
*/
|
||||
public int getcontainerType() {
|
||||
return containerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets containerType
|
||||
*/
|
||||
public void setcontainerType(int containerType) {
|
||||
this.containerType = containerType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets toolbarId
|
||||
*/
|
||||
public int gettoolbarId() {
|
||||
return toolbarId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets toolbarId
|
||||
*/
|
||||
public void settoolbarId(int toolbarId) {
|
||||
this.toolbarId = toolbarId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets itemActionCode
|
||||
*/
|
||||
public int getitemActionCode() {
|
||||
return itemActionCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets itemActionCode
|
||||
*/
|
||||
public void setitemActionCode(int itemActionCode) {
|
||||
this.itemActionCode = itemActionCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets itemActionCodeState
|
||||
*/
|
||||
public int getitemActionCodeState() {
|
||||
return itemActionCodeState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets itemActionCodeState
|
||||
*/
|
||||
public void setitemActionCodeState(int itemActionCodeState) {
|
||||
this.itemActionCodeState = itemActionCodeState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.INVENTORY_MESSAGE_TYPE_ADDITEMTOINVENTORY:
|
||||
@ -152,6 +220,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type addItemToInventory can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseaddItemToInventoryMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -174,6 +245,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type addItemToInventory
|
||||
*/
|
||||
public static InventoryMessage parseaddItemToInventoryMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.ADDITEMTOINVENTORY);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -182,6 +256,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type addItemToInventory
|
||||
*/
|
||||
public static InventoryMessage constructaddItemToInventoryMessage(int entityId,String itemTemplate){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.ADDITEMTOINVENTORY);
|
||||
rVal.setentityId(entityId);
|
||||
@ -190,6 +267,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type removeItemFromInventory
|
||||
*/
|
||||
public static InventoryMessage parseremoveItemFromInventoryMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.REMOVEITEMFROMINVENTORY);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -197,6 +277,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type removeItemFromInventory
|
||||
*/
|
||||
public static InventoryMessage constructremoveItemFromInventoryMessage(int entityId){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.REMOVEITEMFROMINVENTORY);
|
||||
rVal.setentityId(entityId);
|
||||
@ -204,6 +287,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type clientRequestEquipItem can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseclientRequestEquipItemMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -226,6 +312,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type clientRequestEquipItem
|
||||
*/
|
||||
public static InventoryMessage parseclientRequestEquipItemMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTEQUIPITEM);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -234,6 +323,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type clientRequestEquipItem
|
||||
*/
|
||||
public static InventoryMessage constructclientRequestEquipItemMessage(String equipPointId,int entityId){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTEQUIPITEM);
|
||||
rVal.setequipPointId(equipPointId);
|
||||
@ -242,6 +334,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type serverCommandMoveItemContainer can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseserverCommandMoveItemContainerMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -267,6 +362,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type serverCommandMoveItemContainer
|
||||
*/
|
||||
public static InventoryMessage parseserverCommandMoveItemContainerMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDMOVEITEMCONTAINER);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -276,6 +374,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type serverCommandMoveItemContainer
|
||||
*/
|
||||
public static InventoryMessage constructserverCommandMoveItemContainerMessage(int entityId,int containerType,String equipPointId){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDMOVEITEMCONTAINER);
|
||||
rVal.setentityId(entityId);
|
||||
@ -285,6 +386,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type serverCommandEquipItem can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseserverCommandEquipItemMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -326,6 +430,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type serverCommandEquipItem
|
||||
*/
|
||||
public static InventoryMessage parseserverCommandEquipItemMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDEQUIPITEM);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -337,6 +444,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type serverCommandEquipItem
|
||||
*/
|
||||
public static InventoryMessage constructserverCommandEquipItemMessage(int equipperId,int containerType,String equipPointId,int entityId,String itemTemplate){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDEQUIPITEM);
|
||||
rVal.setequipperId(equipperId);
|
||||
@ -348,6 +458,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type serverCommandUnequipItem can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseserverCommandUnequipItemMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -373,6 +486,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type serverCommandUnequipItem
|
||||
*/
|
||||
public static InventoryMessage parseserverCommandUnequipItemMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDUNEQUIPITEM);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -382,6 +498,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type serverCommandUnequipItem
|
||||
*/
|
||||
public static InventoryMessage constructserverCommandUnequipItemMessage(int equipperId,int containerType,String equipPointId){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.SERVERCOMMANDUNEQUIPITEM);
|
||||
rVal.setequipperId(equipperId);
|
||||
@ -391,6 +510,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type clientRequestUnequipItem can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseclientRequestUnequipItemMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -410,6 +532,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type clientRequestUnequipItem
|
||||
*/
|
||||
public static InventoryMessage parseclientRequestUnequipItemMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTUNEQUIPITEM);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -417,6 +542,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type clientRequestUnequipItem
|
||||
*/
|
||||
public static InventoryMessage constructclientRequestUnequipItemMessage(String equipPointId){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTUNEQUIPITEM);
|
||||
rVal.setequipPointId(equipPointId);
|
||||
@ -424,6 +552,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type clientRequestAddToolbar
|
||||
*/
|
||||
public static InventoryMessage parseclientRequestAddToolbarMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDTOOLBAR);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -432,6 +563,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type clientRequestAddToolbar
|
||||
*/
|
||||
public static InventoryMessage constructclientRequestAddToolbarMessage(int entityId,int toolbarId){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDTOOLBAR);
|
||||
rVal.setentityId(entityId);
|
||||
@ -440,6 +574,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type clientRequestAddNatural
|
||||
*/
|
||||
public static InventoryMessage parseclientRequestAddNaturalMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDNATURAL);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -447,6 +584,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type clientRequestAddNatural
|
||||
*/
|
||||
public static InventoryMessage constructclientRequestAddNaturalMessage(int entityId){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTADDNATURAL);
|
||||
rVal.setentityId(entityId);
|
||||
@ -454,6 +594,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type clientUpdateToolbar
|
||||
*/
|
||||
public static InventoryMessage parseclientUpdateToolbarMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTUPDATETOOLBAR);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -461,6 +604,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type clientUpdateToolbar
|
||||
*/
|
||||
public static InventoryMessage constructclientUpdateToolbarMessage(int toolbarId){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTUPDATETOOLBAR);
|
||||
rVal.settoolbarId(toolbarId);
|
||||
@ -468,6 +614,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type clientRequestPerformItemAction can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -493,6 +642,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type clientRequestPerformItemAction
|
||||
*/
|
||||
public static InventoryMessage parseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTPERFORMITEMACTION);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -502,6 +654,9 @@ public class InventoryMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type clientRequestPerformItemAction
|
||||
*/
|
||||
public static InventoryMessage constructclientRequestPerformItemActionMessage(String equipPointId,int itemActionCode,int itemActionCodeState){
|
||||
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTPERFORMITEMACTION);
|
||||
rVal.setequipPointId(equipPointId);
|
||||
|
||||
@ -1,20 +1,30 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class LoreMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum LoreMessageType {
|
||||
REQUESTRACES,
|
||||
RESPONSERACES,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
LoreMessageType messageType;
|
||||
String data;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
LoreMessage(LoreMessageType messageType){
|
||||
this.type = MessageType.LORE_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -24,18 +34,34 @@ public class LoreMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data
|
||||
*/
|
||||
public String getdata() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data
|
||||
*/
|
||||
public void setdata(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.LORE_MESSAGE_TYPE_REQUESTRACES:
|
||||
@ -50,18 +76,27 @@ public class LoreMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestRaces
|
||||
*/
|
||||
public static LoreMessage parseRequestRacesMessage(CircularByteBuffer byteBuffer){
|
||||
LoreMessage rVal = new LoreMessage(LoreMessageType.REQUESTRACES);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestRaces
|
||||
*/
|
||||
public static LoreMessage constructRequestRacesMessage(){
|
||||
LoreMessage rVal = new LoreMessage(LoreMessageType.REQUESTRACES);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type ResponseRaces can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseResponseRacesMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -81,6 +116,9 @@ public class LoreMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type ResponseRaces
|
||||
*/
|
||||
public static LoreMessage parseResponseRacesMessage(CircularByteBuffer byteBuffer){
|
||||
LoreMessage rVal = new LoreMessage(LoreMessageType.RESPONSERACES);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -88,6 +126,9 @@ public class LoreMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type ResponseRaces
|
||||
*/
|
||||
public static LoreMessage constructResponseRacesMessage(String data){
|
||||
LoreMessage rVal = new LoreMessage(LoreMessageType.RESPONSERACES);
|
||||
rVal.setdata(data);
|
||||
|
||||
@ -1,37 +1,64 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
|
||||
import java.util.List;
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
|
||||
/**
|
||||
* A network message
|
||||
*/
|
||||
public abstract class NetworkMessage {
|
||||
|
||||
/**
|
||||
* The different categories of network messages
|
||||
*/
|
||||
public enum MessageType {
|
||||
ENTITY_MESSAGE,
|
||||
LORE_MESSAGE,
|
||||
PLAYER_MESSAGE,
|
||||
TERRAIN_MESSAGE,
|
||||
SERVER_MESSAGE,
|
||||
AUTH_MESSAGE,
|
||||
CHARACTER_MESSAGE,
|
||||
INVENTORY_MESSAGE,
|
||||
SYNCHRONIZATION_MESSAGE,
|
||||
COMBAT_MESSAGE,
|
||||
ENTITY_MESSAGE,
|
||||
LORE_MESSAGE,
|
||||
PLAYER_MESSAGE,
|
||||
TERRAIN_MESSAGE,
|
||||
SERVER_MESSAGE,
|
||||
AUTH_MESSAGE,
|
||||
CHARACTER_MESSAGE,
|
||||
INVENTORY_MESSAGE,
|
||||
SYNCHRONIZATION_MESSAGE,
|
||||
COMBAT_MESSAGE,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Gets the type of the message
|
||||
* @return The type of the message
|
||||
*/
|
||||
public MessageType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw bytes of the message
|
||||
* @return The raw bytes
|
||||
*/
|
||||
public byte[] getRawBytes() {
|
||||
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){
|
||||
NetworkMessage rVal = null;
|
||||
byte firstByte;
|
||||
@ -409,10 +436,17 @@ COMBAT_MESSAGE,
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this message is serialized or not
|
||||
* @return true if it is serialized, false otherwise
|
||||
*/
|
||||
public boolean isSerialized(){
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the message
|
||||
*/
|
||||
abstract void serialize();
|
||||
|
||||
}
|
||||
|
||||
@ -1,23 +1,30 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum PlayerMessageType {
|
||||
SET_ID,
|
||||
SETINITIALDISCRETEPOSITION,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
PlayerMessageType messageType;
|
||||
int playerID;
|
||||
int initialDiscretePositionX;
|
||||
int initialDiscretePositionY;
|
||||
int initialDiscretePositionZ;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
PlayerMessage(PlayerMessageType messageType){
|
||||
this.type = MessageType.PLAYER_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -27,42 +34,76 @@ public class PlayerMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets playerID
|
||||
*/
|
||||
public int getplayerID() {
|
||||
return playerID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets playerID
|
||||
*/
|
||||
public void setplayerID(int playerID) {
|
||||
this.playerID = playerID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets initialDiscretePositionX
|
||||
*/
|
||||
public int getinitialDiscretePositionX() {
|
||||
return initialDiscretePositionX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets initialDiscretePositionX
|
||||
*/
|
||||
public void setinitialDiscretePositionX(int initialDiscretePositionX) {
|
||||
this.initialDiscretePositionX = initialDiscretePositionX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets initialDiscretePositionY
|
||||
*/
|
||||
public int getinitialDiscretePositionY() {
|
||||
return initialDiscretePositionY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets initialDiscretePositionY
|
||||
*/
|
||||
public void setinitialDiscretePositionY(int initialDiscretePositionY) {
|
||||
this.initialDiscretePositionY = initialDiscretePositionY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets initialDiscretePositionZ
|
||||
*/
|
||||
public int getinitialDiscretePositionZ() {
|
||||
return initialDiscretePositionZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets initialDiscretePositionZ
|
||||
*/
|
||||
public void setinitialDiscretePositionZ(int initialDiscretePositionZ) {
|
||||
this.initialDiscretePositionZ = initialDiscretePositionZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.PLAYER_MESSAGE_TYPE_SET_ID:
|
||||
@ -81,6 +122,9 @@ public class PlayerMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type Set_ID
|
||||
*/
|
||||
public static PlayerMessage parseSet_IDMessage(CircularByteBuffer byteBuffer){
|
||||
PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SET_ID);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -88,6 +132,9 @@ public class PlayerMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type Set_ID
|
||||
*/
|
||||
public static PlayerMessage constructSet_IDMessage(int playerID){
|
||||
PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SET_ID);
|
||||
rVal.setplayerID(playerID);
|
||||
@ -95,6 +142,9 @@ public class PlayerMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type SetInitialDiscretePosition
|
||||
*/
|
||||
public static PlayerMessage parseSetInitialDiscretePositionMessage(CircularByteBuffer byteBuffer){
|
||||
PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SETINITIALDISCRETEPOSITION);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -104,6 +154,9 @@ public class PlayerMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type SetInitialDiscretePosition
|
||||
*/
|
||||
public static PlayerMessage constructSetInitialDiscretePositionMessage(int initialDiscretePositionX,int initialDiscretePositionY,int initialDiscretePositionZ){
|
||||
PlayerMessage rVal = new PlayerMessage(PlayerMessageType.SETINITIALDISCRETEPOSITION);
|
||||
rVal.setinitialDiscretePositionX(initialDiscretePositionX);
|
||||
@ -116,7 +169,6 @@ public class PlayerMessage extends NetworkMessage {
|
||||
@Override
|
||||
void serialize(){
|
||||
byte[] intValues = new byte[8];
|
||||
byte[] stringBytes;
|
||||
switch(this.messageType){
|
||||
case SET_ID:
|
||||
rawBytes = new byte[2+4];
|
||||
|
||||
@ -1,19 +1,25 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
public class ServerMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum ServerMessageType {
|
||||
PING,
|
||||
PONG,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
ServerMessageType messageType;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
ServerMessage(ServerMessageType messageType){
|
||||
this.type = MessageType.SERVER_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -23,10 +29,20 @@ public class ServerMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.SERVER_MESSAGE_TYPE_PING:
|
||||
@ -45,24 +61,36 @@ public class ServerMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type Ping
|
||||
*/
|
||||
public static ServerMessage parsePingMessage(CircularByteBuffer byteBuffer){
|
||||
ServerMessage rVal = new ServerMessage(ServerMessageType.PING);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type Ping
|
||||
*/
|
||||
public static ServerMessage constructPingMessage(){
|
||||
ServerMessage rVal = new ServerMessage(ServerMessageType.PING);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type Pong
|
||||
*/
|
||||
public static ServerMessage parsePongMessage(CircularByteBuffer byteBuffer){
|
||||
ServerMessage rVal = new ServerMessage(ServerMessageType.PONG);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type Pong
|
||||
*/
|
||||
public static ServerMessage constructPongMessage(){
|
||||
ServerMessage rVal = new ServerMessage(ServerMessageType.PONG);
|
||||
rVal.serialize();
|
||||
@ -71,8 +99,6 @@ public class ServerMessage extends NetworkMessage {
|
||||
|
||||
@Override
|
||||
void serialize(){
|
||||
byte[] intValues = new byte[8];
|
||||
byte[] stringBytes;
|
||||
switch(this.messageType){
|
||||
case PING:
|
||||
rawBytes = new byte[2];
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class SynchronizationMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum SynchronizationMessageType {
|
||||
UPDATECLIENTSTATE,
|
||||
UPDATECLIENTSTRINGSTATE,
|
||||
@ -21,6 +24,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
LOADSCENE,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
SynchronizationMessageType messageType;
|
||||
int entityId;
|
||||
int bTreeId;
|
||||
@ -32,6 +38,10 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
float floatValue;
|
||||
double doubleValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
SynchronizationMessage(SynchronizationMessageType messageType){
|
||||
this.type = MessageType.SYNCHRONIZATION_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -41,82 +51,146 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entityId
|
||||
*/
|
||||
public int getentityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entityId
|
||||
*/
|
||||
public void setentityId(int entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets bTreeId
|
||||
*/
|
||||
public int getbTreeId() {
|
||||
return bTreeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bTreeId
|
||||
*/
|
||||
public void setbTreeId(int bTreeId) {
|
||||
this.bTreeId = bTreeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets fieldId
|
||||
*/
|
||||
public int getfieldId() {
|
||||
return fieldId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets fieldId
|
||||
*/
|
||||
public void setfieldId(int fieldId) {
|
||||
this.fieldId = fieldId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets bTreeValue
|
||||
*/
|
||||
public int getbTreeValue() {
|
||||
return bTreeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bTreeValue
|
||||
*/
|
||||
public void setbTreeValue(int bTreeValue) {
|
||||
this.bTreeValue = bTreeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets stringValue
|
||||
*/
|
||||
public String getstringValue() {
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets stringValue
|
||||
*/
|
||||
public void setstringValue(String stringValue) {
|
||||
this.stringValue = stringValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets intValue
|
||||
*/
|
||||
public int getintValue() {
|
||||
return intValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets intValue
|
||||
*/
|
||||
public void setintValue(int intValue) {
|
||||
this.intValue = intValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets longValue
|
||||
*/
|
||||
public long getlongValue() {
|
||||
return longValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets longValue
|
||||
*/
|
||||
public void setlongValue(long longValue) {
|
||||
this.longValue = longValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets floatValue
|
||||
*/
|
||||
public float getfloatValue() {
|
||||
return floatValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets floatValue
|
||||
*/
|
||||
public void setfloatValue(float floatValue) {
|
||||
this.floatValue = floatValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets doubleValue
|
||||
*/
|
||||
public double getdoubleValue() {
|
||||
return doubleValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doubleValue
|
||||
*/
|
||||
public void setdoubleValue(double doubleValue) {
|
||||
this.doubleValue = doubleValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.SYNCHRONIZATION_MESSAGE_TYPE_UPDATECLIENTSTATE:
|
||||
@ -181,6 +255,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type UpdateClientState
|
||||
*/
|
||||
public static SynchronizationMessage parseUpdateClientStateMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -191,6 +268,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type UpdateClientState
|
||||
*/
|
||||
public static SynchronizationMessage constructUpdateClientStateMessage(int entityId,int bTreeId,int fieldId,int bTreeValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTATE);
|
||||
rVal.setentityId(entityId);
|
||||
@ -201,6 +281,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type UpdateClientStringState can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseUpdateClientStringStateMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -229,6 +312,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type UpdateClientStringState
|
||||
*/
|
||||
public static SynchronizationMessage parseUpdateClientStringStateMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTRINGSTATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -239,6 +325,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type UpdateClientStringState
|
||||
*/
|
||||
public static SynchronizationMessage constructUpdateClientStringStateMessage(int entityId,int bTreeId,int fieldId,String stringValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTSTRINGSTATE);
|
||||
rVal.setentityId(entityId);
|
||||
@ -249,6 +338,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type UpdateClientIntState
|
||||
*/
|
||||
public static SynchronizationMessage parseUpdateClientIntStateMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTINTSTATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -259,6 +351,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type UpdateClientIntState
|
||||
*/
|
||||
public static SynchronizationMessage constructUpdateClientIntStateMessage(int entityId,int bTreeId,int fieldId,int intValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTINTSTATE);
|
||||
rVal.setentityId(entityId);
|
||||
@ -269,6 +364,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type UpdateClientLongState
|
||||
*/
|
||||
public static SynchronizationMessage parseUpdateClientLongStateMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTLONGSTATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -279,6 +377,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type UpdateClientLongState
|
||||
*/
|
||||
public static SynchronizationMessage constructUpdateClientLongStateMessage(int entityId,int bTreeId,int fieldId,long longValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTLONGSTATE);
|
||||
rVal.setentityId(entityId);
|
||||
@ -289,6 +390,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type UpdateClientFloatState
|
||||
*/
|
||||
public static SynchronizationMessage parseUpdateClientFloatStateMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTFLOATSTATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -299,6 +403,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type UpdateClientFloatState
|
||||
*/
|
||||
public static SynchronizationMessage constructUpdateClientFloatStateMessage(int entityId,int bTreeId,int fieldId,float floatValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTFLOATSTATE);
|
||||
rVal.setentityId(entityId);
|
||||
@ -309,6 +416,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type UpdateClientDoubleState
|
||||
*/
|
||||
public static SynchronizationMessage parseUpdateClientDoubleStateMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTDOUBLESTATE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -319,6 +429,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type UpdateClientDoubleState
|
||||
*/
|
||||
public static SynchronizationMessage constructUpdateClientDoubleStateMessage(int entityId,int bTreeId,int fieldId,double doubleValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.UPDATECLIENTDOUBLESTATE);
|
||||
rVal.setentityId(entityId);
|
||||
@ -329,6 +442,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type ClientRequestBTreeAction
|
||||
*/
|
||||
public static SynchronizationMessage parseClientRequestBTreeActionMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.CLIENTREQUESTBTREEACTION);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -338,6 +454,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type ClientRequestBTreeAction
|
||||
*/
|
||||
public static SynchronizationMessage constructClientRequestBTreeActionMessage(int entityId,int bTreeId,int bTreeValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.CLIENTREQUESTBTREEACTION);
|
||||
rVal.setentityId(entityId);
|
||||
@ -347,6 +466,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type ServerNotifyBTreeTransition
|
||||
*/
|
||||
public static SynchronizationMessage parseServerNotifyBTreeTransitionMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.SERVERNOTIFYBTREETRANSITION);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -357,6 +479,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type ServerNotifyBTreeTransition
|
||||
*/
|
||||
public static SynchronizationMessage constructServerNotifyBTreeTransitionMessage(int entityId,int bTreeId,int fieldId,int bTreeValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.SERVERNOTIFYBTREETRANSITION);
|
||||
rVal.setentityId(entityId);
|
||||
@ -367,6 +492,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type AttachTree
|
||||
*/
|
||||
public static SynchronizationMessage parseAttachTreeMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.ATTACHTREE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -375,6 +503,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type AttachTree
|
||||
*/
|
||||
public static SynchronizationMessage constructAttachTreeMessage(int entityId,int bTreeId){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.ATTACHTREE);
|
||||
rVal.setentityId(entityId);
|
||||
@ -383,6 +514,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type DetatchTree
|
||||
*/
|
||||
public static SynchronizationMessage parseDetatchTreeMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.DETATCHTREE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -391,6 +525,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type DetatchTree
|
||||
*/
|
||||
public static SynchronizationMessage constructDetatchTreeMessage(int entityId,int bTreeId){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.DETATCHTREE);
|
||||
rVal.setentityId(entityId);
|
||||
@ -399,6 +536,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type LoadScene can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseLoadSceneMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -418,6 +558,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type LoadScene
|
||||
*/
|
||||
public static SynchronizationMessage parseLoadSceneMessage(CircularByteBuffer byteBuffer){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.LOADSCENE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -425,6 +568,9 @@ public class SynchronizationMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type LoadScene
|
||||
*/
|
||||
public static SynchronizationMessage constructLoadSceneMessage(String stringValue){
|
||||
SynchronizationMessage rVal = new SynchronizationMessage(SynchronizationMessageType.LOADSCENE);
|
||||
rVal.setstringValue(stringValue);
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import electrosphere.net.parser.util.ByteStreamUtils;
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class TerrainMessage extends NetworkMessage {
|
||||
|
||||
/**
|
||||
* The types of messages available in this category.
|
||||
*/
|
||||
public enum TerrainMessageType {
|
||||
REQUESTMETADATA,
|
||||
RESPONSEMETADATA,
|
||||
@ -23,6 +26,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
UPDATEFLUIDDATA,
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this message in particular.
|
||||
*/
|
||||
TerrainMessageType messageType;
|
||||
int worldSizeDiscrete;
|
||||
int dynamicInterpolationRatio;
|
||||
@ -46,6 +52,10 @@ public class TerrainMessage extends NetworkMessage {
|
||||
float terrainWeight;
|
||||
int terrainValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param messageType The type of this message
|
||||
*/
|
||||
TerrainMessage(TerrainMessageType messageType){
|
||||
this.type = MessageType.TERRAIN_MESSAGE;
|
||||
this.messageType = messageType;
|
||||
@ -55,178 +65,314 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return this.messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets worldSizeDiscrete
|
||||
*/
|
||||
public int getworldSizeDiscrete() {
|
||||
return worldSizeDiscrete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets worldSizeDiscrete
|
||||
*/
|
||||
public void setworldSizeDiscrete(int worldSizeDiscrete) {
|
||||
this.worldSizeDiscrete = worldSizeDiscrete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets dynamicInterpolationRatio
|
||||
*/
|
||||
public int getdynamicInterpolationRatio() {
|
||||
return dynamicInterpolationRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets dynamicInterpolationRatio
|
||||
*/
|
||||
public void setdynamicInterpolationRatio(int dynamicInterpolationRatio) {
|
||||
this.dynamicInterpolationRatio = dynamicInterpolationRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets randomDampener
|
||||
*/
|
||||
public float getrandomDampener() {
|
||||
return randomDampener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets randomDampener
|
||||
*/
|
||||
public void setrandomDampener(float randomDampener) {
|
||||
this.randomDampener = randomDampener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets worldMinX
|
||||
*/
|
||||
public int getworldMinX() {
|
||||
return worldMinX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets worldMinX
|
||||
*/
|
||||
public void setworldMinX(int worldMinX) {
|
||||
this.worldMinX = worldMinX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets worldMinY
|
||||
*/
|
||||
public int getworldMinY() {
|
||||
return worldMinY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets worldMinY
|
||||
*/
|
||||
public void setworldMinY(int worldMinY) {
|
||||
this.worldMinY = worldMinY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets worldMaxX
|
||||
*/
|
||||
public int getworldMaxX() {
|
||||
return worldMaxX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets worldMaxX
|
||||
*/
|
||||
public void setworldMaxX(int worldMaxX) {
|
||||
this.worldMaxX = worldMaxX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets worldMaxY
|
||||
*/
|
||||
public int getworldMaxY() {
|
||||
return worldMaxY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets worldMaxY
|
||||
*/
|
||||
public void setworldMaxY(int worldMaxY) {
|
||||
this.worldMaxY = worldMaxY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value
|
||||
*/
|
||||
public float getvalue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value
|
||||
*/
|
||||
public void setvalue(float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets worldX
|
||||
*/
|
||||
public int getworldX() {
|
||||
return worldX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets worldX
|
||||
*/
|
||||
public void setworldX(int worldX) {
|
||||
this.worldX = worldX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets worldY
|
||||
*/
|
||||
public int getworldY() {
|
||||
return worldY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets worldY
|
||||
*/
|
||||
public void setworldY(int worldY) {
|
||||
this.worldY = worldY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets worldZ
|
||||
*/
|
||||
public int getworldZ() {
|
||||
return worldZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets worldZ
|
||||
*/
|
||||
public void setworldZ(int worldZ) {
|
||||
this.worldZ = worldZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets voxelX
|
||||
*/
|
||||
public int getvoxelX() {
|
||||
return voxelX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets voxelX
|
||||
*/
|
||||
public void setvoxelX(int voxelX) {
|
||||
this.voxelX = voxelX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets voxelY
|
||||
*/
|
||||
public int getvoxelY() {
|
||||
return voxelY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets voxelY
|
||||
*/
|
||||
public void setvoxelY(int voxelY) {
|
||||
this.voxelY = voxelY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets voxelZ
|
||||
*/
|
||||
public int getvoxelZ() {
|
||||
return voxelZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets voxelZ
|
||||
*/
|
||||
public void setvoxelZ(int voxelZ) {
|
||||
this.voxelZ = voxelZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets realLocationX
|
||||
*/
|
||||
public double getrealLocationX() {
|
||||
return realLocationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets realLocationX
|
||||
*/
|
||||
public void setrealLocationX(double realLocationX) {
|
||||
this.realLocationX = realLocationX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets realLocationY
|
||||
*/
|
||||
public double getrealLocationY() {
|
||||
return realLocationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets realLocationY
|
||||
*/
|
||||
public void setrealLocationY(double realLocationY) {
|
||||
this.realLocationY = realLocationY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets realLocationZ
|
||||
*/
|
||||
public double getrealLocationZ() {
|
||||
return realLocationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets realLocationZ
|
||||
*/
|
||||
public void setrealLocationZ(double realLocationZ) {
|
||||
this.realLocationZ = realLocationZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets chunkData
|
||||
*/
|
||||
public byte[] getchunkData() {
|
||||
return chunkData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets chunkData
|
||||
*/
|
||||
public void setchunkData(byte[] chunkData) {
|
||||
this.chunkData = chunkData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets chunkResolution
|
||||
*/
|
||||
public int getchunkResolution() {
|
||||
return chunkResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets chunkResolution
|
||||
*/
|
||||
public void setchunkResolution(int chunkResolution) {
|
||||
this.chunkResolution = chunkResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets terrainWeight
|
||||
*/
|
||||
public float getterrainWeight() {
|
||||
return terrainWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets terrainWeight
|
||||
*/
|
||||
public void setterrainWeight(float terrainWeight) {
|
||||
this.terrainWeight = terrainWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets terrainValue
|
||||
*/
|
||||
public int getterrainValue() {
|
||||
return terrainValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets terrainValue
|
||||
*/
|
||||
public void setterrainValue(int terrainValue) {
|
||||
this.terrainValue = terrainValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the packet header from the buffer
|
||||
* @param byteBuffer The buffer
|
||||
*/
|
||||
static void stripPacketHeader(CircularByteBuffer byteBuffer){
|
||||
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){
|
||||
switch(secondByte){
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA:
|
||||
@ -295,18 +441,27 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestMetadata
|
||||
*/
|
||||
public static TerrainMessage parseRequestMetadataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestMetadata
|
||||
*/
|
||||
public static TerrainMessage constructRequestMetadataMessage(){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type ResponseMetadata
|
||||
*/
|
||||
public static TerrainMessage parseResponseMetadataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -320,6 +475,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
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){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA);
|
||||
rVal.setworldSizeDiscrete(worldSizeDiscrete);
|
||||
@ -333,6 +491,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestEditVoxel
|
||||
*/
|
||||
public static TerrainMessage parseRequestEditVoxelMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -347,6 +508,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
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){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL);
|
||||
rVal.setworldX(worldX);
|
||||
@ -361,6 +525,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type UpdateVoxel
|
||||
*/
|
||||
public static TerrainMessage parseUpdateVoxelMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -375,6 +542,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
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){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL);
|
||||
rVal.setworldX(worldX);
|
||||
@ -389,6 +559,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestUseTerrainPalette
|
||||
*/
|
||||
public static TerrainMessage parseRequestUseTerrainPaletteMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -401,6 +574,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestUseTerrainPalette
|
||||
*/
|
||||
public static TerrainMessage constructRequestUseTerrainPaletteMessage(double realLocationX,double realLocationY,double realLocationZ,float value,float terrainWeight,int terrainValue){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE);
|
||||
rVal.setrealLocationX(realLocationX);
|
||||
@ -413,6 +589,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type SpawnPosition
|
||||
*/
|
||||
public static TerrainMessage parseSpawnPositionMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -422,6 +601,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type SpawnPosition
|
||||
*/
|
||||
public static TerrainMessage constructSpawnPositionMessage(double realLocationX,double realLocationY,double realLocationZ){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION);
|
||||
rVal.setrealLocationX(realLocationX);
|
||||
@ -431,6 +613,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestChunkData
|
||||
*/
|
||||
public static TerrainMessage parseRequestChunkDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -440,6 +625,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestChunkData
|
||||
*/
|
||||
public static TerrainMessage constructRequestChunkDataMessage(int worldX,int worldY,int worldZ){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA);
|
||||
rVal.setworldX(worldX);
|
||||
@ -449,6 +637,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type sendChunkData can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParsesendChunkDataMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -477,6 +668,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type sendChunkData
|
||||
*/
|
||||
public static TerrainMessage parsesendChunkDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -487,6 +681,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type sendChunkData
|
||||
*/
|
||||
public static TerrainMessage constructsendChunkDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA);
|
||||
rVal.setworldX(worldX);
|
||||
@ -497,6 +694,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestReducedChunkData
|
||||
*/
|
||||
public static TerrainMessage parseRequestReducedChunkDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -507,6 +707,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestReducedChunkData
|
||||
*/
|
||||
public static TerrainMessage constructRequestReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA);
|
||||
rVal.setworldX(worldX);
|
||||
@ -517,6 +720,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type SendReducedChunkData can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -548,6 +754,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type SendReducedChunkData
|
||||
*/
|
||||
public static TerrainMessage parseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -559,6 +768,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type SendReducedChunkData
|
||||
*/
|
||||
public static TerrainMessage constructSendReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,byte[] chunkData){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA);
|
||||
rVal.setworldX(worldX);
|
||||
@ -570,6 +782,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type RequestFluidData
|
||||
*/
|
||||
public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -579,6 +794,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type RequestFluidData
|
||||
*/
|
||||
public static TerrainMessage constructRequestFluidDataMessage(int worldX,int worldY,int worldZ){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
|
||||
rVal.setworldX(worldX);
|
||||
@ -588,6 +806,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type sendFluidData can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParsesendFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -616,6 +837,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type sendFluidData
|
||||
*/
|
||||
public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -626,6 +850,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type sendFluidData
|
||||
*/
|
||||
public static TerrainMessage constructsendFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
|
||||
rVal.setworldX(worldX);
|
||||
@ -636,6 +863,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a message of type updateFluidData can be parsed from the byte stream
|
||||
*/
|
||||
public static boolean canParseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
|
||||
@ -664,6 +894,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a message of type updateFluidData
|
||||
*/
|
||||
public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
@ -674,6 +907,9 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a message of type updateFluidData
|
||||
*/
|
||||
public static TerrainMessage constructupdateFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
|
||||
rVal.setworldX(worldX);
|
||||
@ -687,7 +923,6 @@ public class TerrainMessage extends NetworkMessage {
|
||||
@Override
|
||||
void serialize(){
|
||||
byte[] intValues = new byte[8];
|
||||
byte[] stringBytes;
|
||||
switch(this.messageType){
|
||||
case REQUESTMETADATA:
|
||||
rawBytes = new byte[2];
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
package electrosphere.net.parser.net.message;
|
||||
|
||||
|
||||
/**
|
||||
* The constants used in serializing/deserializing messages
|
||||
*/
|
||||
public class TypeBytes {
|
||||
/*
|
||||
Message categories
|
||||
*/
|
||||
/**
|
||||
* Message categories
|
||||
*/
|
||||
public static final byte MESSAGE_TYPE_ENTITY = 0;
|
||||
public static final byte MESSAGE_TYPE_LORE = 1;
|
||||
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_COMBAT = 9;
|
||||
/*
|
||||
Entity subcategories
|
||||
Entity subcategories
|
||||
*/
|
||||
public static final byte ENTITY_MESSAGE_TYPE_CREATE = 0;
|
||||
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_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_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_UPDATEENTITYVIEWDIR_SIZE = 34;
|
||||
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_RESPONSERACES = 1;
|
||||
/*
|
||||
Lore packet sizes
|
||||
Lore packet sizes
|
||||
*/
|
||||
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_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_SETINITIALDISCRETEPOSITION_SIZE = 14;
|
||||
|
||||
/*
|
||||
Terrain subcategories
|
||||
Terrain subcategories
|
||||
*/
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTMETADATA = 0;
|
||||
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_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_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_REQUESTREDUCEDCHUNKDATA_SIZE = 18;
|
||||
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_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_PONG_SIZE = 2;
|
||||
|
||||
/*
|
||||
Auth subcategories
|
||||
Auth subcategories
|
||||
*/
|
||||
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_AUTHSUCCESS = 2;
|
||||
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_AUTHSUCCESS_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_RESPONSECHARACTERLIST = 1;
|
||||
@ -120,14 +128,15 @@ Message categories
|
||||
public static final byte CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER = 5;
|
||||
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_RESPONSECREATECHARACTERSUCCESS_SIZE = 2;
|
||||
public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE_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_REMOVEITEMFROMINVENTORY = 1;
|
||||
@ -141,14 +150,15 @@ Message categories
|
||||
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTUPDATETOOLBAR = 9;
|
||||
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_CLIENTREQUESTADDTOOLBAR_SIZE = 10;
|
||||
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTADDNATURAL_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_UPDATECLIENTSTRINGSTATE = 1;
|
||||
@ -162,7 +172,7 @@ Message categories
|
||||
public static final byte SYNCHRONIZATION_MESSAGE_TYPE_DETATCHTREE = 9;
|
||||
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_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_ATTACHTREE_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;
|
||||
/*
|
||||
Combat packet sizes
|
||||
Combat packet sizes
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,76 +1,132 @@
|
||||
package electrosphere.net.parser.net.raw;
|
||||
|
||||
import electrosphere.net.parser.net.message.NetworkMessage;
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* The main message parser. This is used to serialize/deserialize messages to/from the provided streams.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* The output stream for the parser
|
||||
*/
|
||||
OutputStream outgoingStream;
|
||||
|
||||
/**
|
||||
* The queue of incoming messages that have been parsed
|
||||
*/
|
||||
CopyOnWriteArrayList<NetworkMessage> incomingMessageQueue = new CopyOnWriteArrayList<NetworkMessage>();
|
||||
|
||||
/**
|
||||
* The queue of outgoing messages that have yet to be sent
|
||||
*/
|
||||
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>();
|
||||
|
||||
/**
|
||||
* 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){
|
||||
this.incomingStream = incomingStream;
|
||||
this.outgoingStream = outgoingStream;
|
||||
}
|
||||
|
||||
public void start(){
|
||||
|
||||
}
|
||||
|
||||
static final int READ_BUFFER_SIZE = 64 * 1024 * 1024;
|
||||
byte[] readBuffer = new byte[READ_BUFFER_SIZE];
|
||||
public void readMessagesIn(){
|
||||
try {
|
||||
//read in bytes
|
||||
int bytesRead = 0;
|
||||
byte currentByte = -1;
|
||||
while(incomingStream.available() > 0){
|
||||
// nextValue = incomingStream.read();
|
||||
bytesRead = incomingStream.read(readBuffer, 0, READ_BUFFER_SIZE);
|
||||
if(bytesRead > 0){
|
||||
incomingByteBuffer.add(readBuffer, bytesRead);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads messages from the input stream
|
||||
*/
|
||||
public void readMessagesIn() throws IOException {
|
||||
//read in bytes
|
||||
int bytesRead = 0;
|
||||
while(incomingStream.available() > 0){
|
||||
// nextValue = incomingStream.read();
|
||||
bytesRead = incomingStream.read(readBuffer, 0, READ_BLOCK_SIZE);
|
||||
if(bytesRead > 0){
|
||||
incomingByteBuffer.add(readBuffer, bytesRead);
|
||||
}
|
||||
//parse byte queue for messages
|
||||
//for each message, append to clientIncomingMessageQueue
|
||||
NetworkMessage newMessage;
|
||||
while((newMessage = NetworkMessage.parseBytestreamForMessage(incomingByteBuffer))!=null){
|
||||
incomingMessageQueue.add(newMessage);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(0);
|
||||
totalBytesRead = totalBytesRead + bytesRead;
|
||||
}
|
||||
//parse byte queue for messages
|
||||
//for each message, append to clientIncomingMessageQueue
|
||||
NetworkMessage newMessage;
|
||||
while((newMessage = NetworkMessage.parseBytestreamForMessage(incomingByteBuffer))!=null){
|
||||
incomingMessageQueue.add(newMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
for(NetworkMessage message : outgoingMessageQueue){
|
||||
outgoingMessageQueue.remove(message);
|
||||
// System.out.println("Write message of type " + message.getType());
|
||||
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(){
|
||||
return incomingMessageQueue.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops a fully parsed incoming message from the queue
|
||||
* @return The message
|
||||
*/
|
||||
public NetworkMessage popIncomingMessage(){
|
||||
return incomingMessageQueue.remove(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message to the outgoing queue
|
||||
* @param message The message
|
||||
*/
|
||||
public void addOutgoingMessage(NetworkMessage message){
|
||||
outgoingMessageQueue.add(message);
|
||||
}
|
||||
@ -90,5 +146,13 @@ public class NetworkParser {
|
||||
public void copyOutgoingMessages(List<NetworkMessage> messages){
|
||||
messages.addAll(outgoingMessageQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of bytes read by this connection
|
||||
* @return The total number of bytes
|
||||
*/
|
||||
public long getNumberOfBytesRead(){
|
||||
return totalBytesRead;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package electrosphere.net.parser.util;
|
||||
|
||||
import electrosphere.net.parser.net.raw.CircularByteBuffer;
|
||||
import io.github.studiorailgun.CircularByteBuffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
@ -67,7 +67,7 @@ public class MessageProtocol {
|
||||
* @param message The message
|
||||
*/
|
||||
public void handleAsyncMessage(NetworkMessage message){
|
||||
Globals.profiler.beginAggregateCpuSample("MessageProtocol(client).handleAsyncMessage");
|
||||
Globals.profiler.beginAggregateCpuSample("MessageProtocol(server).handleAsyncMessage");
|
||||
printMessage(message);
|
||||
NetworkMessage result = null;
|
||||
switch(message.getType()){
|
||||
@ -113,7 +113,7 @@ public class MessageProtocol {
|
||||
}
|
||||
|
||||
public void handleSyncMessages(){
|
||||
Globals.profiler.beginAggregateCpuSample("MessageProtocol(client).handleSyncMessages");
|
||||
Globals.profiler.beginAggregateCpuSample("MessageProtocol(server).handleSyncMessages");
|
||||
this.synchronousMessageLock.acquireUninterruptibly();
|
||||
LoggerInterface.loggerNetworking.DEBUG_LOOP("[SERVER] HANDLE SYNC MESSAGE [Sync queue size: " + this.synchronousMessageQueue.size() + "]");
|
||||
for(NetworkMessage message : synchronousMessageQueue){
|
||||
|
||||
@ -170,8 +170,11 @@ public class Server implements Runnable {
|
||||
* @return The first connection
|
||||
*/
|
||||
public ServerConnectionHandler getFirstConnection(){
|
||||
ServerConnectionHandler firstCon = null;
|
||||
connectListLock.acquireUninterruptibly();
|
||||
ServerConnectionHandler firstCon = this.activeConnections.get(0);
|
||||
if(this.activeConnections.size() > 0){
|
||||
firstCon = this.activeConnections.get(0);
|
||||
}
|
||||
connectListLock.release();
|
||||
return firstCon;
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import electrosphere.net.parser.net.message.NetworkMessage;
|
||||
import electrosphere.net.parser.net.message.ServerMessage;
|
||||
import electrosphere.net.parser.net.raw.NetworkParser;
|
||||
import electrosphere.net.server.player.Player;
|
||||
import electrosphere.util.CodeUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
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
|
||||
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
|
||||
long lastPingTime = 0;
|
||||
long lastPongTime = 0;
|
||||
@ -191,18 +192,19 @@ public class ServerConnectionHandler implements Runnable {
|
||||
initialized = true;
|
||||
while(Globals.threadManager.shouldKeepRunning() && this.isConnected == true && Globals.server != null && Globals.server.isOpen()){
|
||||
|
||||
boolean receivedMessageThisLoop = false;
|
||||
//
|
||||
// Main Loop
|
||||
//
|
||||
//parse messages both incoming and outgoing
|
||||
try {
|
||||
parseMessages();
|
||||
receivedMessageThisLoop = parseMessages();
|
||||
} catch (SocketException e) {
|
||||
//if we get a SocketException broken pipe (basically the client dc'd without telling us)
|
||||
//set flag to disconnect client
|
||||
//TODO: fix, this doesn't actually catch the socket exception which is exceedingly obnoxious
|
||||
socketException = true;
|
||||
LoggerInterface.loggerNetworking.DEBUG(e.getLocalizedMessage());
|
||||
LoggerInterface.loggerNetworking.ERROR("Client disconnected", e);
|
||||
this.disconnect();
|
||||
break;
|
||||
} catch (IOException e){
|
||||
@ -210,14 +212,18 @@ public class ServerConnectionHandler implements Runnable {
|
||||
//set flag to disconnect client
|
||||
//TODO: fix, this doesn't actually catch the socket exception which is exceedingly obnoxious
|
||||
socketException = true;
|
||||
LoggerInterface.loggerNetworking.DEBUG(e.getLocalizedMessage());
|
||||
LoggerInterface.loggerNetworking.ERROR("Client disconnected", e);
|
||||
this.disconnect();
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Pings
|
||||
// Timeout logic
|
||||
//
|
||||
//mark as alive if a message was received from client
|
||||
if(receivedMessageThisLoop){
|
||||
this.markReceivedPongMessage();
|
||||
}
|
||||
//ping logic
|
||||
long currentTime = System.currentTimeMillis();
|
||||
//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
|
||||
//has it been too long since the last ping?
|
||||
//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
|
||||
LoggerInterface.loggerNetworking.WARNING("Client disconnected");
|
||||
//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
|
||||
* without my linter freaking out
|
||||
* @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
|
||||
//
|
||||
//attempt poll incoming messages
|
||||
networkParser.readMessagesIn();
|
||||
rVal = networkParser.hasIncomingMessaage();
|
||||
|
||||
|
||||
//
|
||||
@ -305,8 +321,9 @@ public class ServerConnectionHandler implements Runnable {
|
||||
TimeUnit.MILLISECONDS.sleep(1);
|
||||
} catch (InterruptedException ex) {
|
||||
//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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,10 +3,12 @@ package electrosphere.net.server.protocol;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.net.server.ServerConnectionHandler;
|
||||
import electrosphere.net.server.player.Player;
|
||||
@ -23,20 +25,32 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSyncMessage(ServerConnectionHandler connectionHandler, TerrainMessage message) {
|
||||
switch(message.getMessageSubtype()){
|
||||
case REQUESTMETADATA:
|
||||
case REQUESTMETADATA: {
|
||||
sendWorldMetadata(connectionHandler);
|
||||
break;
|
||||
case REQUESTCHUNKDATA:
|
||||
} break;
|
||||
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,
|
||||
message.getworldX(), message.getworldY(), message.getworldZ()
|
||||
);
|
||||
break;
|
||||
} break;
|
||||
case REQUESTEDITVOXEL: {
|
||||
attemptTerrainEdit(connectionHandler, message);
|
||||
} break;
|
||||
@ -44,6 +58,8 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
|
||||
attemptUseTerrainEditPalette(connectionHandler, message);
|
||||
} break;
|
||||
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,
|
||||
message.getworldX(), message.getworldY(), message.getworldZ()
|
||||
);
|
||||
@ -70,28 +86,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
|
||||
* @param worldZ the world z
|
||||
*/
|
||||
static void sendWorldSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){
|
||||
/*
|
||||
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
|
||||
*/
|
||||
Globals.profiler.beginAggregateCpuSample("TerrainProtocol(server).sendWorldSubChunk");
|
||||
|
||||
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());
|
||||
Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer());
|
||||
@ -99,12 +94,8 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
|
||||
return;
|
||||
}
|
||||
|
||||
//get the chunk
|
||||
//request chunk
|
||||
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.
|
||||
//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()));
|
||||
|
||||
// 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(
|
||||
// TerrainMessage.constructMacroValueMessage(
|
||||
// worldX,
|
||||
// worldY,
|
||||
|
||||
|
||||
// macroValues[0][0],
|
||||
// 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]
|
||||
// )
|
||||
// );
|
||||
Consumer<ServerTerrainChunk> onLoad = (ServerTerrainChunk chunk) -> {
|
||||
//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
|
||||
//chunk data that connects seamlessly to the next chunk.
|
||||
int xWidth = chunk.getWeights().length;
|
||||
int yWidth = chunk.getWeights()[0].length;
|
||||
int zWidth = chunk.getWeights()[0][0].length;
|
||||
|
||||
// for(TerrainModification modification : chunk.getModifications()){
|
||||
// connectionHandler.addMessagetoOutgoingQueue(
|
||||
// TerrainMessage.constructheightMapModificationMessage(
|
||||
// modification.getValue(),
|
||||
// modification.getWorldX(),
|
||||
// 0,
|
||||
// modification.getWorldY(),
|
||||
// modification.getLocationX(),
|
||||
// 0,
|
||||
// modification.getLocationY()
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
ByteBuffer buffer = ByteBuffer.allocate(xWidth*yWidth*zWidth*(4+4));
|
||||
FloatBuffer floatView = buffer.asFloatBuffer();
|
||||
|
||||
for(int x = 0; x < xWidth; x++){
|
||||
for(int y = 0; y < yWidth; y++){
|
||||
for(int z = 0; z < zWidth; z++){
|
||||
floatView.put(chunk.getWeights()[x][y][z]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -25,6 +25,11 @@ import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
public class TerrainChunkModelGeneration {
|
||||
|
||||
/**
|
||||
* The minimum iso value
|
||||
*/
|
||||
public static final float MIN_ISO_VALUE = 0.01f;
|
||||
|
||||
//http://paulbourke.net/geometry/polygonise/
|
||||
|
||||
public static int edgeTable[]={
|
||||
@ -620,7 +625,7 @@ public class TerrainChunkModelGeneration {
|
||||
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-------+ ^ 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]
|
||||
);
|
||||
//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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,11 @@ public class TransvoxelModelGeneration {
|
||||
//the lower the value, the more of the low resolution chunk we will see
|
||||
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){
|
||||
samplerIndex[i] = getTransvoxelTextureValue(transitionCell.simpleFaceAtlasValues,transitionCell.complexFaceAtlasValues,firstCornerSampleIndex);
|
||||
} 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
|
||||
* @param terrainGrid The chunk data
|
||||
* @param textureGrid The chunk texture data
|
||||
* @param chunkData The chunk data
|
||||
* @return The mesh data
|
||||
*/
|
||||
public static TerrainChunkData generateTerrainChunkData(TransvoxelChunkData chunkData){
|
||||
@ -1019,9 +1023,9 @@ public class TransvoxelModelGeneration {
|
||||
List<Vector3f> samplerTriangles = new LinkedList<Vector3f>();
|
||||
//List of UVs
|
||||
List<Float> UVs = new LinkedList<Float>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
//Generate the interior of the mesh
|
||||
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]
|
||||
);
|
||||
//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
|
||||
if(chunkData.xPositiveEdgeIso != null){
|
||||
int x = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2;
|
||||
for(int y = yStartIndex; y < yEndIndex - 1; y++){
|
||||
for(int z = zStartIndex; z < zEndIndex - 1; z++){
|
||||
for(int y = yStartIndex; y < yEndIndex; y++){
|
||||
for(int z = zStartIndex; z < zEndIndex; z++){
|
||||
//
|
||||
//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+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
|
||||
@ -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]
|
||||
);
|
||||
//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 {
|
||||
@ -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]
|
||||
);
|
||||
//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
|
||||
if(chunkData.xNegativeEdgeIso != null){
|
||||
int x = 0;
|
||||
for(int y = yStartIndex; y < yEndIndex - 1; y++){
|
||||
for(int z = zStartIndex; z < zEndIndex - 1; z++){
|
||||
for(int y = yStartIndex; y < yEndIndex; y++){
|
||||
for(int z = zStartIndex; z < zEndIndex; z++){
|
||||
//
|
||||
//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+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
|
||||
@ -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]
|
||||
);
|
||||
//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 {
|
||||
@ -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]
|
||||
);
|
||||
//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
|
||||
if(chunkData.yPositiveEdgeIso != null){
|
||||
int y = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2;
|
||||
for(int x = xStartIndex; x < xEndIndex - 1; x++){
|
||||
for(int z = zStartIndex; z < zEndIndex - 1; z++){
|
||||
for(int x = xStartIndex; x < xEndIndex; x++){
|
||||
for(int z = zStartIndex; z < zEndIndex; z++){
|
||||
//
|
||||
//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+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
|
||||
@ -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]
|
||||
);
|
||||
//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 {
|
||||
@ -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]
|
||||
);
|
||||
//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
|
||||
if(chunkData.yNegativeEdgeIso != null){
|
||||
int y = 0;
|
||||
for(int x = xStartIndex; x < xEndIndex - 1; x++){
|
||||
for(int z = zStartIndex; z < zEndIndex - 1; z++){
|
||||
for(int x = xStartIndex; x < xEndIndex; x++){
|
||||
for(int z = zStartIndex; z < zEndIndex; z++){
|
||||
//
|
||||
//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+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
|
||||
@ -1315,13 +1319,13 @@ public class TransvoxelModelGeneration {
|
||||
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+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.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]
|
||||
);
|
||||
//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 {
|
||||
@ -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]
|
||||
);
|
||||
//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
|
||||
if(chunkData.zPositiveEdgeIso != null){
|
||||
int z = ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE - 2;
|
||||
for(int x = xStartIndex; x < xEndIndex - 1; x++){
|
||||
for(int y = yStartIndex; y < yEndIndex - 1; y++){
|
||||
for(int x = xStartIndex; x < xEndIndex; x++){
|
||||
for(int y = yStartIndex; y < yEndIndex; y++){
|
||||
//
|
||||
//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+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
|
||||
@ -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]
|
||||
);
|
||||
//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 {
|
||||
@ -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]
|
||||
);
|
||||
//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
|
||||
if(chunkData.zNegativeEdgeIso != null){
|
||||
int z = 0;
|
||||
for(int x = xStartIndex; x < xEndIndex - 1; x++){
|
||||
for(int y = yStartIndex; y < yEndIndex - 1; y++){
|
||||
for(int x = xStartIndex; x < xEndIndex; x++){
|
||||
for(int y = yStartIndex; y < yEndIndex; y++){
|
||||
//
|
||||
//Generate the 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+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),
|
||||
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+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),
|
||||
@ -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+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
|
||||
@ -1463,13 +1467,13 @@ public class TransvoxelModelGeneration {
|
||||
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+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+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.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+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.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+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+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+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(currentCell, 0.01f, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
polygonize(currentCell, TerrainChunkModelGeneration.MIN_ISO_VALUE, triangles, samplerTriangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
}
|
||||
}
|
||||
} 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]
|
||||
);
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -18,6 +18,11 @@ public class ImGuiLinePlot implements ImGuiElement {
|
||||
//the data sets to draw
|
||||
List<ImGuiLinePlotDataset> dataSets = new LinkedList<ImGuiLinePlotDataset>();
|
||||
|
||||
/**
|
||||
* Size of the plot
|
||||
*/
|
||||
ImVec2 size = new ImVec2(-1,-1);
|
||||
|
||||
/**
|
||||
* Creates an im gui line plot
|
||||
*/
|
||||
@ -25,9 +30,18 @@ public class ImGuiLinePlot implements ImGuiElement {
|
||||
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
|
||||
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){
|
||||
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()]);
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -25,6 +25,8 @@ public class MainServerFunctions {
|
||||
if(Globals.server != null){
|
||||
Globals.server.synchronousPacketHandling();
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
Globals.profiler.beginCpuSample("Server process synchronization messages");
|
||||
if(Globals.serverSynchronizationManager != null){
|
||||
Globals.serverSynchronizationManager.processMessages();
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -3,6 +3,7 @@ package electrosphere.server.datacell.physics;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.renderer.shader.ShaderProgram;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -65,7 +66,7 @@ public class DataCellPhysicsManager {
|
||||
* @param discreteY The initial discrete position Y coordinate
|
||||
*/
|
||||
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>();
|
||||
invalid = new HashSet<String>();
|
||||
updateable = new HashSet<String>();
|
||||
|
||||
@ -7,6 +7,7 @@ import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterOutputStream;
|
||||
|
||||
@ -14,6 +15,7 @@ import electrosphere.engine.Globals;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.util.FileUtils;
|
||||
import electrosphere.util.annotation.Exclude;
|
||||
|
||||
/**
|
||||
* 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
|
||||
Map<String,String> worldPosFileMap = new HashMap<String,String>();
|
||||
|
||||
/**
|
||||
* Locks the chunk disk map for thread safety
|
||||
*/
|
||||
@Exclude
|
||||
Semaphore lock = new Semaphore(1);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@ -81,7 +89,10 @@ public class ChunkDiskMap {
|
||||
* @return True if the map contains the chunk, false otherwise
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public ServerTerrainChunk getTerrainChunk(int worldX, int worldY, int worldZ){
|
||||
lock.acquireUninterruptibly();
|
||||
LoggerInterface.loggerEngine.INFO("Load chunk " + worldX + " " + worldY + " " + worldZ);
|
||||
ServerTerrainChunk rVal = null;
|
||||
if(containsTerrainAtPosition(worldX, worldY, worldZ)){
|
||||
@ -148,6 +163,7 @@ public class ChunkDiskMap {
|
||||
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
|
||||
}
|
||||
}
|
||||
lock.release();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -156,6 +172,7 @@ public class ChunkDiskMap {
|
||||
* @param terrainChunk The terrain chunk
|
||||
*/
|
||||
public void saveToDisk(ServerTerrainChunk terrainChunk){
|
||||
lock.acquireUninterruptibly();
|
||||
LoggerInterface.loggerEngine.DEBUG("Save to disk: " + terrainChunk.getWorldX() + " " + terrainChunk.getWorldY() + " " + terrainChunk.getWorldZ());
|
||||
//get the file name for this chunk
|
||||
String fileName = null;
|
||||
@ -202,6 +219,7 @@ public class ChunkDiskMap {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
lock.release();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.game.data.biome.BiomeData;
|
||||
import electrosphere.game.data.biome.BiomeSurfaceGenerationParams;
|
||||
import electrosphere.game.server.world.ServerWorldData;
|
||||
@ -65,6 +66,7 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
|
||||
@Override
|
||||
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) {
|
||||
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.generateChunk");
|
||||
ServerTerrainChunk rVal = null;
|
||||
float[][][] weights;
|
||||
int[][][] values;
|
||||
@ -95,19 +97,37 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
values = new int[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
|
||||
|
||||
//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++){
|
||||
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice");
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
weights[x][y][z] = this.getChunkWeight(worldX, worldY, worldZ, x, y, z, this.terrainModel, biome);
|
||||
values[x][y][z] = this.getChunkValue(worldX, worldY, worldZ, x, y, z, this.terrainModel, biome);
|
||||
GeneratedVoxel voxel = this.getVoxel(worldX, worldY, worldZ, x, y, z, heightfield, this.terrainModel, surfaceBiome);
|
||||
weights[x][y][z] = voxel.weight;
|
||||
values[x][y][z] = voxel.type;
|
||||
}
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
}
|
||||
|
||||
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
|
||||
Globals.profiler.endCpuSample();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -116,7 +136,6 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
this.terrainModel = model;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value for a chunk
|
||||
* @param worldX The world x pos
|
||||
@ -125,71 +144,133 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
* @param chunkX The chunk x pos
|
||||
* @param chunkY The chunk y pos
|
||||
* @param chunkZ The chunk z pos
|
||||
* @param heightfield The precomputed heightfield
|
||||
* @param terrainModel The terrain model
|
||||
* @param surfaceBiome The surface biome of the chunk
|
||||
* @return The value of the chunk
|
||||
*/
|
||||
private int getChunkValue(
|
||||
private GeneratedVoxel getVoxel(
|
||||
int worldX, int worldY, int worldZ,
|
||||
int chunkX, int chunkY, int chunkZ,
|
||||
float[][] heightfield,
|
||||
TerrainModel terrainModel,
|
||||
BiomeData surfaceBiome
|
||||
){
|
||||
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.getChunkValue");
|
||||
BiomeSurfaceGenerationParams surfaceParams = surfaceBiome.getSurfaceGenerationParams();
|
||||
HeightmapGenerator heightmapGen = this.tagGeneratorMap.get(surfaceParams.getSurfaceGenTag());
|
||||
if(heightmapGen == null){
|
||||
throw new Error("Undefined heightmap generator in biome! " + surfaceBiome.getId() + " " + surfaceBiome.getDisplayName() + " " + surfaceParams.getSurfaceGenTag());
|
||||
}
|
||||
|
||||
double realX = this.serverWorldData.convertVoxelToRealSpace(chunkX,worldX);
|
||||
double realX = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldX);
|
||||
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);
|
||||
if(realY <= surfaceHeight){
|
||||
return 1;
|
||||
float surfaceHeight = heightfield[chunkX][chunkZ];
|
||||
double flooredSurfaceHeight = Math.floor(surfaceHeight);
|
||||
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 {
|
||||
return 0;
|
||||
return getSurfaceVoxel(
|
||||
worldX,worldY, worldZ,
|
||||
chunkX, chunkY, chunkZ,
|
||||
realX, realY, realZ,
|
||||
surfaceHeight, flooredSurfaceHeight,
|
||||
terrainModel,
|
||||
surfaceBiome
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the weight for a chunk
|
||||
* @param worldX The world x pos
|
||||
* @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
|
||||
* Gets the voxel on the surface
|
||||
* @return The voxel
|
||||
*/
|
||||
private float getChunkWeight(
|
||||
private GeneratedVoxel getSurfaceVoxel(
|
||||
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
|
||||
){
|
||||
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());
|
||||
}
|
||||
GeneratedVoxel voxel = new GeneratedVoxel();
|
||||
voxel.weight = (float)(surfaceHeight - flooredSurfaceHeight) * 2 - 1;
|
||||
voxel.type = 1;
|
||||
return voxel;
|
||||
}
|
||||
|
||||
double realX = this.serverWorldData.convertVoxelToRealSpace(chunkX,worldX);
|
||||
double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY);
|
||||
double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkZ,worldZ);
|
||||
|
||||
float surfaceHeight = heightmapGen.getHeight(terrainModel.getSeed(), realX, realZ);
|
||||
double flooredSurfaceHeight = Math.floor(surfaceHeight);
|
||||
if(realY < flooredSurfaceHeight){
|
||||
return 1;
|
||||
} else if(realY > flooredSurfaceHeight) {
|
||||
return -1;
|
||||
/**
|
||||
* Gets the voxel below the surface
|
||||
* @return The voxel
|
||||
*/
|
||||
private GeneratedVoxel getSubsurfaceVoxel(
|
||||
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();
|
||||
if(realY < surfaceHeight - 5){
|
||||
voxel.weight = 1;
|
||||
voxel.type = 6;
|
||||
} 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package electrosphere.server.terrain.generation.heightmap;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.util.noise.OpenSimplex2S;
|
||||
|
||||
/**
|
||||
@ -48,9 +49,12 @@ public class HillsGen implements HeightmapGenerator {
|
||||
* @return The height
|
||||
*/
|
||||
public float getHeight(long SEED, double x, double y){
|
||||
Globals.profiler.beginAggregateCpuSample("HillsGen.getHeight");
|
||||
double scaledX = x * 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package electrosphere.server.terrain.manager;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.game.server.world.ServerWorldData;
|
||||
import electrosphere.server.terrain.diskmap.ChunkDiskMap;
|
||||
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.TerrainModification;
|
||||
import electrosphere.util.FileUtils;
|
||||
import electrosphere.util.annotation.Exclude;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.joml.Vector3i;
|
||||
|
||||
@ -22,6 +24,11 @@ import org.joml.Vector3i;
|
||||
*/
|
||||
public class ServerTerrainManager {
|
||||
|
||||
/**
|
||||
* The number of threads for chunk generation
|
||||
*/
|
||||
public static final int GENERATION_THREAD_POOL_SIZE = 1;
|
||||
|
||||
/**
|
||||
* Full world discrete size
|
||||
*/
|
||||
@ -51,21 +58,24 @@ public class ServerTerrainManager {
|
||||
//The model of the terrain this manager is managing
|
||||
TerrainModel model;
|
||||
|
||||
|
||||
//In memory cache of chunk data
|
||||
//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
|
||||
//offset regenerating the array every time we want a new one
|
||||
int cacheSize = 500;
|
||||
Map<String, ServerTerrainChunk> chunkCache;
|
||||
List<String> chunkCacheContents;
|
||||
/**
|
||||
* The cache of chunks
|
||||
*/
|
||||
@Exclude
|
||||
ServerChunkCache chunkCache = new ServerChunkCache();
|
||||
|
||||
//The map of chunk position <-> file on disk containing chunk data
|
||||
ChunkDiskMap chunkDiskMap = null;
|
||||
|
||||
//The generation algorithm for this terrain manager
|
||||
@Exclude
|
||||
ChunkGenerator chunkGenerator;
|
||||
|
||||
|
||||
/**
|
||||
* The threadpool for chunk generation
|
||||
*/
|
||||
@Exclude
|
||||
ExecutorService chunkExecutorService = Executors.newFixedThreadPool(GENERATION_THREAD_POOL_SIZE);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -76,8 +86,6 @@ public class ServerTerrainManager {
|
||||
ChunkGenerator chunkGenerator
|
||||
){
|
||||
this.parent = parent;
|
||||
this.chunkCache = new ConcurrentHashMap<String, ServerTerrainChunk>();
|
||||
this.chunkCacheContents = new CopyOnWriteArrayList<String>();
|
||||
this.seed = seed;
|
||||
this.chunkGenerator = chunkGenerator;
|
||||
}
|
||||
@ -118,8 +126,7 @@ public class ServerTerrainManager {
|
||||
FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array());
|
||||
}
|
||||
//for each chunk, save via disk map
|
||||
for(String chunkKey : chunkCacheContents){
|
||||
ServerTerrainChunk chunk = chunkCache.get(chunkKey);
|
||||
for(ServerTerrainChunk chunk : this.chunkCache.getContents()){
|
||||
chunkDiskMap.saveToDisk(chunk);
|
||||
}
|
||||
//save disk map itself
|
||||
@ -167,7 +174,6 @@ public class ServerTerrainManager {
|
||||
*/
|
||||
public void evictAll(){
|
||||
this.chunkCache.clear();
|
||||
this.chunkCacheContents.clear();
|
||||
}
|
||||
|
||||
public float[][] getTerrainAtChunk(int x, int y){
|
||||
@ -215,37 +221,19 @@ public class ServerTerrainManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a server terrain chunk
|
||||
* 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
|
||||
* @return The ServerTerrainChunk
|
||||
*/
|
||||
public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){
|
||||
Globals.profiler.beginCpuSample("ServerTerrainManager.getChunk");
|
||||
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
|
||||
String key = getKey(worldX,worldY,worldZ);
|
||||
ServerTerrainChunk returnedChunk = null;
|
||||
if(chunkCache.containsKey(key)){
|
||||
chunkCacheContents.remove(key);
|
||||
chunkCacheContents.add(0, key);
|
||||
returnedChunk = chunkCache.get(key);
|
||||
return returnedChunk;
|
||||
if(chunkCache.containsChunk(worldX,worldY,worldZ)){
|
||||
returnedChunk = chunkCache.get(worldX,worldY,worldZ);
|
||||
} else {
|
||||
if(chunkCacheContents.size() >= cacheSize){
|
||||
String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1);
|
||||
chunkCache.remove(oldChunk);
|
||||
}
|
||||
//pull from disk if it exists
|
||||
if(chunkDiskMap != null){
|
||||
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){
|
||||
@ -256,10 +244,23 @@ public class ServerTerrainManager {
|
||||
if(returnedChunk == null){
|
||||
returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ);
|
||||
}
|
||||
chunkCache.put(key, returnedChunk);
|
||||
chunkCacheContents.add(key);
|
||||
return returnedChunk;
|
||||
this.chunkCache.add(worldX, worldY, worldZ, 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){
|
||||
model.addModification(modification);
|
||||
}
|
||||
String key = getKey(worldPos.x,worldPos.y,worldPos.z);
|
||||
if(chunkCache.containsKey(key)){
|
||||
ServerTerrainChunk chunk = chunkCache.get(key);
|
||||
if(chunkCache.containsChunk(worldPos.x,worldPos.y,worldPos.z)){
|
||||
ServerTerrainChunk chunk = chunkCache.get(worldPos.x,worldPos.y,worldPos.z);
|
||||
chunk.addModification(modification);
|
||||
}
|
||||
}
|
||||
@ -309,4 +309,11 @@ public class ServerTerrainManager {
|
||||
return chunkGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the generation threadpool
|
||||
*/
|
||||
public void closeThreads(){
|
||||
this.chunkExecutorService.shutdownNow();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
435
src/main/java/electrosphere/util/ds/octree/WorldOctTree.java
Normal file
435
src/main/java/electrosphere/util/ds/octree/WorldOctTree.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -30,6 +30,7 @@ public class StateCleanupCheckerExtension implements AfterEachCallback {
|
||||
Globals.playerManager,
|
||||
LoggerInterface.loggerEngine,
|
||||
RenderingEngine.screenFramebuffer,
|
||||
Globals.clientWorldData,
|
||||
};
|
||||
for(Object object : objectsToCheck){
|
||||
if(object != null){
|
||||
|
||||
@ -2,9 +2,20 @@ package electrosphere.util.ds.octree;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import electrosphere.test.annotations.UnitTest;
|
||||
|
||||
/**
|
||||
* Unit testing for the chunk octree implementation
|
||||
*/
|
||||
public class ChunkTreeTests {
|
||||
|
||||
/**
|
||||
* Creates a chunk tree
|
||||
*/
|
||||
@UnitTest
|
||||
public void testCreateChunkTree(){
|
||||
ChunkTree<String> tree = new ChunkTree<String>();
|
||||
assertNotNull(tree);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -10,7 +10,6 @@
|
||||
"./net/lore.json",
|
||||
"./net/player.json",
|
||||
"./net/terrain.json",
|
||||
"./net/world.json",
|
||||
"./net/server.json",
|
||||
"./net/character.json",
|
||||
"./net/inventory.json",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user