Compare commits
No commits in common. "db8ad7d10aaa93978d2607208cb955daf418da9c" and "7ab0debc459749b04ce1564aaa8609e2c246bf27" have entirely different histories.
db8ad7d10a
...
7ab0debc45
@ -1,5 +0,0 @@
|
||||
|
||||
//courtesy https://stackoverflow.com/a/4275343
|
||||
float rand(vec2 co){
|
||||
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
#maven.buildNumber.plugin properties file
|
||||
#Tue Mar 19 19:54:15 EDT 2024
|
||||
buildNumber=77
|
||||
#Sun Mar 17 09:54:23 EDT 2024
|
||||
buildNumber=75
|
||||
|
||||
@ -41,8 +41,6 @@
|
||||
|
||||
## Ruins
|
||||
|
||||
## Light Valley
|
||||
Valley with lights shooting around the sky from projectors placed sporadically around the valley, tie into lore and provide some kind of function (portal controller or something?)
|
||||
|
||||
|
||||
|
||||
@ -289,17 +287,3 @@ Plains with lots of fire
|
||||
## Thunder Planes
|
||||
Constant Thunderstorms and lots of lightning strikes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Sky Specific
|
||||
|
||||
## Sky Reefs
|
||||
Full of kelp dangling from island, lots of sea-themed flying creatures
|
||||
|
||||
|
||||
@ -5,8 +5,3 @@
|
||||
|
||||
### "Black Hole" Zone
|
||||
Zone with a big magical fire in the center or something like that. IE it's a huge pit and the zone you play in is around this pit.
|
||||
|
||||
### Solitairy Giant
|
||||
Enclosed zone centered around a silent giant performing some toil (walking in circles around the periphery? Moving boulders? idk)
|
||||
Ambient audio effect of him doing work the whole time
|
||||
|
||||
|
||||
@ -181,17 +181,6 @@ Fix Character creation preview not working
|
||||
|
||||
Clean up main method/class
|
||||
|
||||
Shader library system
|
||||
- Abiltiy to include the shader library in individual files (ie implement #include)
|
||||
|
||||
Level loading/saving + Basic Editor
|
||||
- Spin up voxel level (think arena mode)
|
||||
- Save voxel level
|
||||
- Basic editor functionality
|
||||
- Menu of types of entities to spawn
|
||||
- Button to spawn them at cursor
|
||||
|
||||
|
||||
Transvoxel Algorithm
|
||||
Client Terrain Entity Management (specifically creation/teardown for client)
|
||||
- Also queries for far out chunks to load far away terrain
|
||||
|
||||
@ -201,15 +201,6 @@
|
||||
"worldZ",
|
||||
"chunkData"
|
||||
]
|
||||
},
|
||||
{
|
||||
"messageName" : "updateFluidData",
|
||||
"data" : [
|
||||
"worldX",
|
||||
"worldY",
|
||||
"worldZ",
|
||||
"chunkData"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -25,6 +25,10 @@ public class FluidChunkData {
|
||||
float[][][] velocityY;
|
||||
float[][][] velocityZ;
|
||||
|
||||
//the list of positions modified since the last call to resetModifiedPositions
|
||||
//Used in DrawCell to keep track of which positions to invalidate
|
||||
Set<String> modifiedSinceLastGeneration = new HashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the voxel weight array in this container
|
||||
@ -39,10 +43,43 @@ public class FluidChunkData {
|
||||
* @param voxelWeight The voxel weight array
|
||||
*/
|
||||
public void setVoxelWeight(float[][][] voxelWeight){
|
||||
//mark changed cells
|
||||
if(this.voxelWeight != null){
|
||||
for(int x = 0; x < CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < CHUNK_SIZE; z++){
|
||||
if(voxelWeight[x][y][z] != this.voxelWeight[x][y][z]){
|
||||
String key = getVoxelPositionKey(new Vector3i(x,y,z));
|
||||
if(!modifiedSinceLastGeneration.contains(key)){
|
||||
modifiedSinceLastGeneration.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//update data
|
||||
this.voxelWeight = voxelWeight;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Updates the value of a single voxel in the chunk
|
||||
// * @param localX The local position X
|
||||
// * @param localY The local position Y
|
||||
// * @param localZ The local position Z
|
||||
// * @param weight The weight to set it to
|
||||
// * @param type The type to set the voxel to
|
||||
// */
|
||||
// public void updatePosition(int localX, int localY, int localZ, float weight, int type){
|
||||
// voxelWeight[localX][localY][localZ] = weight;
|
||||
// voxelType[localX][localY][localZ] = type;
|
||||
// //store as modified in cache
|
||||
// String key = getVoxelPositionKey(new Vector3i(localX,localY,localZ));
|
||||
// if(!modifiedSinceLastGeneration.contains(key)){
|
||||
// modifiedSinceLastGeneration.add(key);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Gets the weight of a voxel at a poisiton
|
||||
* @param localPosition The local position
|
||||
@ -57,7 +94,27 @@ public class FluidChunkData {
|
||||
* @return The weight of the specified voxel
|
||||
*/
|
||||
public float getWeight(int x, int y, int z){
|
||||
return voxelWeight[x][y][z];
|
||||
return voxelWeight[z][y][z];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the cache of modified positions
|
||||
*/
|
||||
public void resetModifiedPositions(){
|
||||
this.modifiedSinceLastGeneration.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the set of all modified positions since the last call to resetModifiedPositions
|
||||
* @return The set of all modified positions
|
||||
*/
|
||||
public Set<Vector3i> getModifiedPositions(){
|
||||
Set<Vector3i> rVal = new HashSet<Vector3i>();
|
||||
for(String key : modifiedSinceLastGeneration){
|
||||
String[] split = key.split("_");
|
||||
rVal.add(new Vector3i(Integer.parseInt(split[0]),Integer.parseInt(split[1]),Integer.parseInt(split[2])));
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -65,7 +65,7 @@ public class FluidCell {
|
||||
rVal.data = data;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a drawable entity based on this chunk
|
||||
*/
|
||||
@ -76,7 +76,7 @@ public class FluidCell {
|
||||
|
||||
fillInData();
|
||||
|
||||
modelEntity = FluidChunk.clientCreateFluidChunkEntity(weights);
|
||||
modelEntity = FluidChunk.clientCreateFluidChunkEntity(data.getVoxelWeight());
|
||||
|
||||
ClientEntityUtils.initiallyPositionEntity(modelEntity, getRealPos());
|
||||
}
|
||||
@ -110,9 +110,6 @@ public class FluidCell {
|
||||
* Fills in the internal arrays of data for generate terrain models
|
||||
*/
|
||||
private void fillInData(){
|
||||
if(worldPos.x == 1 && worldPos.y == 0 && worldPos.z == 0){
|
||||
System.out.println("aaaa");
|
||||
}
|
||||
//
|
||||
//fill in data
|
||||
//
|
||||
@ -134,12 +131,6 @@ public class FluidCell {
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = currentChunk.getWeight(0, i, j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][j] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
@ -157,12 +148,6 @@ public class FluidCell {
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = currentChunk.getWeight(i, 0, j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][j] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
@ -180,12 +165,6 @@ public class FluidCell {
|
||||
weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(i, j, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
for(int j = 0; j < ServerTerrainChunk.CHUNK_DIMENSION; j++){
|
||||
weights[i][j][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
@ -204,10 +183,6 @@ public class FluidCell {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = currentChunk.getWeight(0, 0, i);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][i] = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
@ -224,10 +199,6 @@ public class FluidCell {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, i, 0);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][i][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
@ -244,10 +215,6 @@ public class FluidCell {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(i, 0, 0);
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
weights[i][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < ServerTerrainChunk.CHUNK_DIMENSION; i++){
|
||||
@ -255,15 +222,15 @@ public class FluidCell {
|
||||
}
|
||||
}
|
||||
if(
|
||||
worldPos.x + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.y + 1 < Globals.clientWorldData.getWorldDiscreteSize() &&
|
||||
worldPos.z + 1 < Globals.clientWorldData.getWorldDiscreteSize()
|
||||
){
|
||||
currentChunk = Globals.clientFluidManager.getChunkDataAtWorldPoint(worldPos.x + 1, worldPos.y + 1, worldPos.z + 1);
|
||||
if(currentChunk != null){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, 0, 0);
|
||||
} else {
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
|
||||
if(currentChunk != null){
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = currentChunk.getWeight(0, 0, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
weights[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION] = -1;
|
||||
|
||||
@ -58,23 +58,46 @@ public class ClientFluidManager {
|
||||
messageQueue.remove(message);
|
||||
switch(message.getMessageSubtype()){
|
||||
case SENDFLUIDDATA: {
|
||||
float[][][] weights = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityX = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityY = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityZ = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData());
|
||||
FluidChunkData data = parseFluidDataBuffer(buffer);
|
||||
fluidCache.addChunkDataToCache(
|
||||
message.getworldX(), message.getworldY(), message.getworldZ(),
|
||||
data
|
||||
);
|
||||
} break;
|
||||
case UPDATEFLUIDDATA: {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(message.getchunkData());
|
||||
FluidChunkData data = parseFluidDataBuffer(buffer);
|
||||
fluidCache.addChunkDataToCache(
|
||||
message.getworldX(), message.getworldY(), message.getworldZ(),
|
||||
data
|
||||
);
|
||||
if(Globals.fluidCellManager != null){
|
||||
Globals.fluidCellManager.markUpdateable(message.getworldX(), message.getworldY(), message.getworldZ());
|
||||
FloatBuffer floatBuffer = buffer.asFloatBuffer();
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
weights[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityX[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityY[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityZ[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
FluidChunkData data = new FluidChunkData();
|
||||
data.setVoxelWeight(weights);
|
||||
fluidCache.addChunkDataToCache(
|
||||
message.getworldX(), message.getworldY(), message.getworldZ(),
|
||||
data
|
||||
);
|
||||
} break;
|
||||
default:
|
||||
LoggerInterface.loggerEngine.WARNING("ClientFluidManager: unhandled network message of type" + message.getMessageSubtype());
|
||||
@ -149,52 +172,5 @@ public class ClientFluidManager {
|
||||
}
|
||||
fluidChunkGenerationQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a byte buffer into a fluid data object
|
||||
* @param buffer the buffer
|
||||
* @return the object
|
||||
*/
|
||||
private FluidChunkData parseFluidDataBuffer(ByteBuffer buffer){
|
||||
float[][][] weights = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityX = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityY = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
float[][][] velocityZ = new float[FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE][FluidChunkData.CHUNK_SIZE];
|
||||
FloatBuffer floatBuffer = buffer.asFloatBuffer();
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
weights[x][y][z] = floatBuffer.get();
|
||||
if(weights[x][y][z] <= 0){
|
||||
weights[x][y][z] = -1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityX[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityY[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int x = 0; x < FluidChunkData.CHUNK_SIZE; x++){
|
||||
for(int y = 0; y < FluidChunkData.CHUNK_SIZE; y++){
|
||||
for(int z = 0; z < FluidChunkData.CHUNK_SIZE; z++){
|
||||
velocityZ[x][y][z] = floatBuffer.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
FluidChunkData data = new FluidChunkData();
|
||||
data.setVoxelWeight(weights);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ public class ArenaLoading {
|
||||
|
||||
private static void initServerArenaTerrainManager(){
|
||||
Globals.serverTerrainManager = ServerTerrainManager.constructArenaTerrainManager();
|
||||
Globals.serverFluidManager = ServerFluidManager.constructArenaFluidManager(Globals.serverTerrainManager);
|
||||
Globals.serverFluidManager = ServerFluidManager.constructArenaFluidManager();
|
||||
}
|
||||
|
||||
private static void initServerArenaWorldData(){
|
||||
|
||||
@ -30,7 +30,6 @@ public class FluidChunk {
|
||||
|
||||
rVal.putData(EntityDataStrings.FLUID_IS_FLUID, true);
|
||||
rVal.putData(EntityDataStrings.DRAW_TRANSPARENT_PASS, true);
|
||||
rVal.removeData(EntityDataStrings.DRAW_SOLID_PASS);
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -53,9 +53,6 @@ public class TerrainProtocol {
|
||||
case SENDFLUIDDATA: {
|
||||
Globals.clientFluidManager.attachFluidMessage(message);
|
||||
} break;
|
||||
case UPDATEFLUIDDATA: {
|
||||
Globals.clientFluidManager.attachFluidMessage(message);
|
||||
} break;
|
||||
default:
|
||||
LoggerInterface.loggerNetworking.WARNING("Client networking: Unhandled message of type: " + message.getMessageSubtype());
|
||||
break;
|
||||
|
||||
@ -246,11 +246,6 @@ SYNCHRONIZATION_MESSAGE,
|
||||
rVal = TerrainMessage.parsesendFluidDataMessage(byteBuffer);
|
||||
}
|
||||
break;
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA:
|
||||
if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){
|
||||
rVal = TerrainMessage.parseupdateFluidDataMessage(byteBuffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TypeBytes.MESSAGE_TYPE_SERVER:
|
||||
|
||||
@ -19,7 +19,6 @@ public class TerrainMessage extends NetworkMessage {
|
||||
SENDCHUNKDATA,
|
||||
REQUESTFLUIDDATA,
|
||||
SENDFLUIDDATA,
|
||||
UPDATEFLUIDDATA,
|
||||
}
|
||||
|
||||
TerrainMessageType messageType;
|
||||
@ -277,8 +276,6 @@ public class TerrainMessage extends NetworkMessage {
|
||||
}
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
|
||||
return TerrainMessage.canParsesendFluidDataMessage(byteBuffer);
|
||||
case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA:
|
||||
return TerrainMessage.canParseupdateFluidDataMessage(byteBuffer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -567,54 +564,6 @@ public class TerrainMessage extends NetworkMessage {
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public static boolean canParseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
int currentStreamLength = byteBuffer.getRemaining();
|
||||
List<Byte> temporaryByteQueue = new LinkedList();
|
||||
if(currentStreamLength < 6){
|
||||
return false;
|
||||
}
|
||||
if(currentStreamLength < 10){
|
||||
return false;
|
||||
}
|
||||
if(currentStreamLength < 14){
|
||||
return false;
|
||||
}
|
||||
int chunkDataSize = 0;
|
||||
if(currentStreamLength < 18){
|
||||
return false;
|
||||
} else {
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 0));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 1));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 2));
|
||||
temporaryByteQueue.add(byteBuffer.peek(14 + 3));
|
||||
chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
|
||||
}
|
||||
if(currentStreamLength < 18 + chunkDataSize){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
|
||||
stripPacketHeader(byteBuffer);
|
||||
rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
|
||||
rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public static TerrainMessage constructupdateFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
|
||||
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
|
||||
rVal.setworldX(worldX);
|
||||
rVal.setworldY(worldY);
|
||||
rVal.setworldZ(worldZ);
|
||||
rVal.setchunkData(chunkData);
|
||||
rVal.serialize();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
void serialize(){
|
||||
byte[] intValues = new byte[8];
|
||||
@ -890,32 +839,6 @@ public class TerrainMessage extends NetworkMessage {
|
||||
rawBytes[18+i] = chunkData[i];
|
||||
}
|
||||
break;
|
||||
case UPDATEFLUIDDATA:
|
||||
rawBytes = new byte[2+4+4+4+4+chunkData.length];
|
||||
//message header
|
||||
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
|
||||
//entity messaage header
|
||||
rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA;
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldX);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[2+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldY);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[6+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[10+i] = intValues[i];
|
||||
}
|
||||
intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
|
||||
for(int i = 0; i < 4; i++){
|
||||
rawBytes[14+i] = intValues[i];
|
||||
}
|
||||
for(int i = 0; i < chunkData.length; i++){
|
||||
rawBytes[18+i] = chunkData[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
serialized = true;
|
||||
}
|
||||
|
||||
@ -90,7 +90,6 @@ Message categories
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA = 8;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA = 9;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 10;
|
||||
public static final byte TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA = 11;
|
||||
/*
|
||||
Terrain packet sizes
|
||||
*/
|
||||
|
||||
@ -45,7 +45,6 @@ public class TerrainProtocol {
|
||||
);
|
||||
} break;
|
||||
//all ignored message types
|
||||
case UPDATEFLUIDDATA:
|
||||
case RESPONSEMETADATA:
|
||||
case SPAWNPOSITION:
|
||||
case UPDATEVOXEL:
|
||||
@ -208,55 +207,11 @@ public class TerrainProtocol {
|
||||
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());
|
||||
|
||||
ServerFluidChunk chunk = Globals.serverFluidManager.getChunk(worldX, worldY, worldZ);
|
||||
|
||||
// float[][] macroValues = chunk.getMacroValues();//Globals.serverTerrainManager.getRad5MacroValues(message.getworldX(), message.getworldY());
|
||||
|
||||
|
||||
ByteBuffer buffer = constructFluidByteBuffer(chunk);
|
||||
// long[][] randomizer = chunk.getRandomizer();//Globals.serverTerrainManager.getRandomizer(message.getworldX(), message.getworldY());
|
||||
|
||||
connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendFluidDataMessage(worldX, worldY, worldZ, buffer.array()));
|
||||
|
||||
}
|
||||
|
||||
static void sendWorldMetadata(ServerConnectionHandler connectionHandler){
|
||||
//world metadata
|
||||
connectionHandler.addMessagetoOutgoingQueue(
|
||||
TerrainMessage.constructResponseMetadataMessage(
|
||||
Globals.serverWorldData.getWorldSizeDiscrete(),
|
||||
Globals.serverWorldData.getDynamicInterpolationRatio(),
|
||||
Globals.serverWorldData.getRandomDampener(),
|
||||
(int)Globals.serverWorldData.getWorldBoundMin().x,
|
||||
(int)Globals.serverWorldData.getWorldBoundMin().z,
|
||||
(int)Globals.serverWorldData.getWorldBoundMax().x,
|
||||
(int)Globals.serverWorldData.getWorldBoundMax().z
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to perform an edit requested by a client
|
||||
* @param message The message containing the edit request
|
||||
*/
|
||||
static void attemptTerrainEdit(ServerConnectionHandler connectionHandler, TerrainMessage message){
|
||||
Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to use an edit palette on the terrain
|
||||
* @param connectionHandler The connection handler
|
||||
* @param message The message that contains the request to use an edit palette
|
||||
*/
|
||||
static void attemptUseTerrainEditPalette(ServerConnectionHandler connectionHandler, TerrainMessage message){
|
||||
Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId());
|
||||
Realm realm = Globals.realmManager.getPlayerRealm(player);
|
||||
Vector3d location = new Vector3d(message.getrealLocationX(), message.getrealLocationY(), message.getrealLocationZ());
|
||||
TerrainEditing.editTerrain(realm, location, message.getvalue(), message.getterrainValue(), message.getterrainWeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a buffer to send a fluid chunk to the client
|
||||
* @param chunk The chunk to send
|
||||
* @return the buffer
|
||||
*/
|
||||
public static ByteBuffer constructFluidByteBuffer(ServerFluidChunk 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.
|
||||
@ -299,7 +254,44 @@ public class TerrainProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
|
||||
connectionHandler.addMessagetoOutgoingQueue(TerrainMessage.constructsendFluidDataMessage(worldX, worldY, worldZ, buffer.array()));
|
||||
|
||||
}
|
||||
|
||||
static void sendWorldMetadata(ServerConnectionHandler connectionHandler){
|
||||
//world metadata
|
||||
connectionHandler.addMessagetoOutgoingQueue(
|
||||
TerrainMessage.constructResponseMetadataMessage(
|
||||
Globals.serverWorldData.getWorldSizeDiscrete(),
|
||||
Globals.serverWorldData.getDynamicInterpolationRatio(),
|
||||
Globals.serverWorldData.getRandomDampener(),
|
||||
(int)Globals.serverWorldData.getWorldBoundMin().x,
|
||||
(int)Globals.serverWorldData.getWorldBoundMin().z,
|
||||
(int)Globals.serverWorldData.getWorldBoundMax().x,
|
||||
(int)Globals.serverWorldData.getWorldBoundMax().z
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to perform an edit requested by a client
|
||||
* @param message The message containing the edit request
|
||||
*/
|
||||
static void attemptTerrainEdit(ServerConnectionHandler connectionHandler, TerrainMessage message){
|
||||
Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to use an edit palette on the terrain
|
||||
* @param connectionHandler The connection handler
|
||||
* @param message The message that contains the request to use an edit palette
|
||||
*/
|
||||
static void attemptUseTerrainEditPalette(ServerConnectionHandler connectionHandler, TerrainMessage message){
|
||||
Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId());
|
||||
Realm realm = Globals.realmManager.getPlayerRealm(player);
|
||||
Vector3d location = new Vector3d(message.getrealLocationX(), message.getrealLocationY(), message.getrealLocationZ());
|
||||
TerrainEditing.editTerrain(realm, location, message.getvalue(), message.getterrainValue(), message.getterrainWeight());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -588,7 +588,7 @@ public class FluidChunkModelGeneration {
|
||||
List<Integer> faceElements = new LinkedList<Integer>();
|
||||
//List of UVs
|
||||
List<Float> UVs = new LinkedList<Float>();
|
||||
|
||||
|
||||
|
||||
|
||||
for(int x = 0; x < weightGrid.length - 1; x++){
|
||||
@ -602,7 +602,7 @@ public class FluidChunkModelGeneration {
|
||||
weightGrid[x+0][y+1][z+0], weightGrid[x+0][y+1][z+1], weightGrid[x+1][y+1][z+1], weightGrid[x+1][y+1][z+0]
|
||||
);
|
||||
//polygonize the current gridcell
|
||||
polygonize(currentCell, 0.0f, triangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
polygonize(currentCell, 0, triangles, vertMap, verts, normals, trianglesSharingVert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ import electrosphere.game.server.world.ServerWorldData;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.net.server.player.Player;
|
||||
import electrosphere.net.server.protocol.TerrainProtocol;
|
||||
import electrosphere.server.content.ServerContentManager;
|
||||
import electrosphere.server.datacell.interfaces.DataCellManager;
|
||||
import electrosphere.server.datacell.interfaces.VoxelCellManager;
|
||||
@ -354,14 +353,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
loadedCellsLock.acquireUninterruptibly();
|
||||
for(ServerDataCell cell : loadedCells){
|
||||
Globals.microSimulation.simulate(cell, parent.getHitboxManager());
|
||||
|
||||
//simulate fluid
|
||||
Vector3i cellPos = this.getCellWorldPosition(cell);
|
||||
boolean update = this.serverFluidManager.simulate(cellPos.x,cellPos.y,cellPos.z);
|
||||
if(update){
|
||||
rebroadcastFluidChunk(cellPos);
|
||||
}
|
||||
}
|
||||
//simulate fluid
|
||||
this.serverFluidManager.simulate();
|
||||
loadedCellsLock.release();
|
||||
updatePlayerPositions();
|
||||
}
|
||||
@ -488,23 +482,8 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Gets the fluid chunk at a given position
|
||||
*/
|
||||
public ServerFluidChunk getFluidChunkAtPosition(Vector3i worldPosition) {
|
||||
return serverFluidManager.getChunk(worldPosition.x, worldPosition.y, worldPosition.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebroadcasts the fluid cell at a given position
|
||||
* @param worldPosition the world position
|
||||
*/
|
||||
private void rebroadcastFluidChunk(Vector3i worldPosition){
|
||||
ServerDataCell cell = getCellAtWorldPosition(worldPosition);
|
||||
ServerFluidChunk chunk = getFluidChunkAtPosition(worldPosition);
|
||||
cell.broadcastNetworkMessage(
|
||||
TerrainMessage.constructupdateFluidDataMessage(worldPosition.x, worldPosition.y, worldPosition.z, TerrainProtocol.constructFluidByteBuffer(chunk).array())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -17,14 +17,12 @@ public class ArenaFluidGenerator implements FluidGenerator {
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
weights[x][y][z] = 0f;
|
||||
weights[x][y][z] = -1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(worldX == 0 && worldY == 0 && worldZ == 0){
|
||||
weights[7][5][7] = 1f;
|
||||
}
|
||||
weights[3][3][3] = 0.8f;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
|
||||
@ -6,10 +6,6 @@ import electrosphere.server.fluid.diskmap.FluidDiskMap;
|
||||
import electrosphere.server.fluid.generation.ArenaFluidGenerator;
|
||||
import electrosphere.server.fluid.generation.FluidGenerator;
|
||||
import electrosphere.server.fluid.models.FluidModel;
|
||||
import electrosphere.server.fluid.simulator.ServerFluidSimulator;
|
||||
import electrosphere.server.fluid.simulator.cellularautomata.FluidCellularAutomataSimulator;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.util.FileUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
@ -52,26 +48,18 @@ public class ServerFluidManager {
|
||||
|
||||
//The generation algorithm for this fluid manager
|
||||
FluidGenerator chunkGenerator;
|
||||
|
||||
//the fluid simulator
|
||||
ServerFluidSimulator serverFluidSimulator;
|
||||
|
||||
//the terrain manager associated
|
||||
ServerTerrainManager serverTerrainManager;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ServerFluidManager(
|
||||
ServerTerrainManager serverTerrainManager,
|
||||
int worldSizeDiscrete,
|
||||
int verticalInterpolationRatio,
|
||||
float interpolationRandomDampener,
|
||||
long seed,
|
||||
FluidGenerator chunkGenerator
|
||||
){
|
||||
this.serverTerrainManager = serverTerrainManager;
|
||||
this.worldSizeDiscrete = worldSizeDiscrete;
|
||||
this.verticalInterpolationRatio = verticalInterpolationRatio;
|
||||
this.chunkCache = new ConcurrentHashMap<String, ServerFluidChunk>();
|
||||
@ -89,16 +77,14 @@ public class ServerFluidManager {
|
||||
* Constructs an arena fluid manager
|
||||
* @return The arena fluid manager
|
||||
*/
|
||||
public static ServerFluidManager constructArenaFluidManager(ServerTerrainManager serverTerrainManager){
|
||||
public static ServerFluidManager constructArenaFluidManager(){
|
||||
ServerFluidManager rVal = new ServerFluidManager();
|
||||
rVal.serverTerrainManager = serverTerrainManager;
|
||||
rVal.worldSizeDiscrete = 2;
|
||||
rVal.verticalInterpolationRatio = 0;
|
||||
rVal.chunkCache = new ConcurrentHashMap<String, ServerFluidChunk>();
|
||||
rVal.chunkCacheContents = new CopyOnWriteArrayList<String>();
|
||||
rVal.interpolationRandomDampener = 0.0f;
|
||||
rVal.chunkGenerator = new ArenaFluidGenerator();
|
||||
rVal.serverFluidSimulator = new FluidCellularAutomataSimulator();
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -289,15 +275,10 @@ public class ServerFluidManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a chunk
|
||||
* Simulates all active fluid chunks
|
||||
*/
|
||||
public boolean simulate(int worldX, int worldY, int worldZ){
|
||||
ServerFluidChunk fluidChunk = this.getChunk(worldX, worldY, worldZ);
|
||||
ServerTerrainChunk terrainChunk = this.serverTerrainManager.getChunk(worldX, worldY, worldZ);
|
||||
if(fluidChunk != null && terrainChunk != null && this.serverFluidSimulator != null){
|
||||
return this.serverFluidSimulator.simulate(fluidChunk,terrainChunk,worldX,worldY,worldZ);
|
||||
}
|
||||
return false;
|
||||
public void simulate(){
|
||||
//TODO: implement
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
package electrosphere.server.fluid.simulator;
|
||||
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
* A system capable of simulating a server fluid chunk for a single frame
|
||||
*/
|
||||
public interface ServerFluidSimulator {
|
||||
|
||||
/**
|
||||
* Simulates the chunk for single step
|
||||
* @param fluidChunk The fluid chunk
|
||||
* @param terrainChunk The terrain chunk
|
||||
* @param worldX the world x coordinate
|
||||
* @param worldY the world y coordinate
|
||||
* @param worldZ the world z coordinate
|
||||
* @return True if the server data cell should update all players connected to it with new values, false otherwise
|
||||
*/
|
||||
public boolean simulate(ServerFluidChunk fluidChunk, ServerTerrainChunk terrainChunk, int worldX, int worldY, int worldZ);
|
||||
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
package electrosphere.server.fluid.simulator.cellularautomata;
|
||||
|
||||
import electrosphere.server.fluid.manager.ServerFluidChunk;
|
||||
import electrosphere.server.fluid.simulator.ServerFluidSimulator;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
|
||||
/**
|
||||
* Simulates server fluid chunks via cellular automata
|
||||
*/
|
||||
public class FluidCellularAutomataSimulator implements ServerFluidSimulator {
|
||||
|
||||
static final float MAX_WEIGHT = 1.0f;
|
||||
|
||||
static final float GRAVITY_DIFF = 0.04f;
|
||||
|
||||
@Override
|
||||
public boolean simulate(ServerFluidChunk fluidChunk, ServerTerrainChunk terrainChunk, int worldX, int worldY, int worldZ) {
|
||||
|
||||
float[][][] weights = fluidChunk.getWeights();
|
||||
float[][][] terrainWeights = terrainChunk.getWeights();
|
||||
|
||||
//if true, alerts the server data cell to broadcast a new update message to all clients within it
|
||||
boolean update = false;
|
||||
|
||||
if(worldX == 0 && worldY == 0 && worldZ == 0){
|
||||
if(weights[8][3][8] < MAX_WEIGHT){
|
||||
weights[8][3][8] = MAX_WEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
if(weights[x][y][z] <= 0){
|
||||
continue;
|
||||
} else {
|
||||
if(y > 0 && weights[x][y-1][z] < MAX_WEIGHT){
|
||||
update = true;
|
||||
weights[x][y][z] -= GRAVITY_DIFF;
|
||||
weights[x][y-1][z] += GRAVITY_DIFF;
|
||||
} else {
|
||||
//propagate sideways
|
||||
int[] offsetX = new int[]{-1,1,0,0};
|
||||
int[] offsetZ = new int[]{0,0,-1,1};
|
||||
for(int i = 0; i < 4; i++){
|
||||
int realX = x + offsetX[i];
|
||||
int realZ = z + offsetZ[i];
|
||||
if(realX > 0 && realX < ServerTerrainChunk.CHUNK_DIMENSION - 1 &&
|
||||
realZ > 0 && realZ < ServerTerrainChunk.CHUNK_DIMENSION - 1
|
||||
){
|
||||
if(
|
||||
weights[realX][y][realZ] < weights[x][y][z] &&
|
||||
terrainWeights[realX][y][realZ] < MAX_WEIGHT
|
||||
){
|
||||
update = true;
|
||||
weights[x][y][z] -= GRAVITY_DIFF;
|
||||
weights[realX][y][realZ] += GRAVITY_DIFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -27,19 +27,7 @@ public class ArenaChunkGenerator implements ChunkGenerator {
|
||||
for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DIMENSION; weightX++){
|
||||
for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DIMENSION; weightZ++){
|
||||
weights[weightX][0][weightZ] = 0.1f;
|
||||
values[weightX][0][weightZ] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(worldX == 0 && worldY == 0 && worldZ == 0){
|
||||
for(int x = 5; x < 11; x++){
|
||||
for(int z = 5; z < 11; z++){
|
||||
if((x == 10 || x == 5 || z == 10 || z == 5)){
|
||||
weights[x][0][z] = 1.0f;
|
||||
weights[x][1][z] = 1.0f;
|
||||
values[x][0][z] = 1;
|
||||
values[x][1][z] = 1;
|
||||
}
|
||||
values[weightX][0][weightZ] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user