From f921acf317c9da1f16de1c9c6fab85722d34c7e4 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Tue, 22 Dec 2020 19:33:27 +0300 Subject: [PATCH 01/10] Added generic implementations of of Chunk and World classes Also fixed a whole bunch of missing Override annotations --- .../graphics/backend/VertexBufferObject.java | 1 + .../graphics/backend/shaders/Shader.java | 1 + .../client/graphics/gui/GUILayer.java | 1 + .../graphics/model/ShapeRenderProgram.java | 3 + .../client/localization/MutableString.java | 1 + .../progressia/client/world/ChunkRender.java | 92 ++++++++- .../progressia/client/world/WorldRender.java | 20 +- .../client/world/block/BlockRender.java | 3 +- .../world/cro/ChunkRenderOptimizerCube.java | 2 +- .../client/world/entity/EntityRenderable.java | 13 +- .../client/world/tile/TileRender.java | 3 +- .../client/world/tile/TileRenderStack.java | 16 ++ .../common/state/OptimizedStateStorage.java | 2 + .../common/util/ByteBufferInputStream.java | 2 + .../common/util/ByteBufferOutputStream.java | 2 + .../common/util/SizeLimitedList.java | 1 + .../progressia/common/util/VectorUtil.java | 2 +- .../progressia/common/world/ChunkData.java | 34 ++-- .../progressia/common/world/WorldData.java | 50 ++--- .../common/world/block/BlockData.java | 3 +- .../common/world/entity/EntityData.java | 16 +- .../common/world/generic/GenericBlock.java | 7 + .../common/world/generic/GenericChunk.java | 85 +++++++++ .../common/world/generic/GenericEntity.java | 22 +++ .../common/world/generic/GenericTile.java | 7 + .../{tile => generic}/GenericTileStack.java | 12 +- .../common/world/generic/GenericWorld.java | 176 ++++++++++++++++++ .../common/world/tile/TileData.java | 3 +- .../common/world/tile/TileDataStack.java | 18 +- .../progressia/server/world/ChunkLogic.java | 115 ++++++------ .../server/world/TickContextMutable.java | 6 +- .../progressia/server/world/WorldLogic.java | 78 +++----- .../server/world/block/BlockLogic.java | 3 +- .../server/world/tile/TileLogic.java | 3 +- .../server/world/tile/TileLogicStack.java | 11 +- 35 files changed, 612 insertions(+), 202 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderStack.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/GenericBlock.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/GenericEntity.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/GenericTile.java rename src/main/java/ru/windcorp/progressia/common/world/{tile => generic}/GenericTileStack.java (84%) create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/VertexBufferObject.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/VertexBufferObject.java index 1039cde..ce301b7 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/VertexBufferObject.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/VertexBufferObject.java @@ -192,6 +192,7 @@ public class VertexBufferObject implements OpenGLDeletable { return usage; } + @Override public int getHandle() { return handle; } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java index 1f8f6d3..394c32f 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Shader.java @@ -91,6 +91,7 @@ public class Shader implements OpenGLDeletable { ); } + @Override public int getHandle() { return handle; } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/GUILayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/GUILayer.java index 5f53652..90aa557 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/GUILayer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/GUILayer.java @@ -24,6 +24,7 @@ import ru.windcorp.progressia.client.graphics.input.bus.Input; public abstract class GUILayer extends AssembledFlatLayer { private final Component root = new Component("Root") { + @Override protected void handleReassemblyRequest() { GUILayer.this.invalidate(); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeRenderProgram.java b/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeRenderProgram.java index aa24489..83213ea 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeRenderProgram.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/model/ShapeRenderProgram.java @@ -264,6 +264,7 @@ public class ShapeRenderProgram extends Program { private final List vertices = new ArrayList<>(); + @Override public VertexBuilder addVertex( float x, float y, float z, float r, float g, float b, @@ -278,6 +279,7 @@ public class ShapeRenderProgram extends Program { return this; } + @Override public VertexBuilder addVertex( Vec3 position, Vec3 colorMultiplier, @@ -292,6 +294,7 @@ public class ShapeRenderProgram extends Program { return this; } + @Override public ByteBuffer assemble() { ByteBuffer result = BufferUtils.createByteBuffer( DEFAULT_BYTES_PER_VERTEX * vertices.size() diff --git a/src/main/java/ru/windcorp/progressia/client/localization/MutableString.java b/src/main/java/ru/windcorp/progressia/client/localization/MutableString.java index 1aff3b3..f0a01e8 100644 --- a/src/main/java/ru/windcorp/progressia/client/localization/MutableString.java +++ b/src/main/java/ru/windcorp/progressia/client/localization/MutableString.java @@ -55,6 +55,7 @@ public abstract class MutableString { return data; } + @Override public String toString() { return get(); } diff --git a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java index dee73cb..9c512c5 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java @@ -18,8 +18,11 @@ package ru.windcorp.progressia.client.world; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.WeakHashMap; import java.util.stream.Collectors; import glm.mat._4.Mat4; @@ -38,11 +41,20 @@ import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSupplier; import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizers; import ru.windcorp.progressia.client.world.tile.TileRender; import ru.windcorp.progressia.client.world.tile.TileRenderRegistry; +import ru.windcorp.progressia.client.world.tile.TileRenderStack; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.generic.GenericChunk; import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.common.world.tile.TileDataStack; -public class ChunkRender { +public class ChunkRender +implements GenericChunk< + ChunkRender, + BlockRender, + TileRender, + TileRenderStack +> { private final WorldRender world; private final ChunkData data; @@ -50,11 +62,43 @@ public class ChunkRender { private boolean needsUpdate; private Model model = null; + private final Map tileRenderLists = + Collections.synchronizedMap(new WeakHashMap<>()); + public ChunkRender(WorldRender world, ChunkData data) { this.world = world; this.data = data; } + @Override + public Vec3i getPosition() { + return getData().getPosition(); + } + + @Override + public BlockRender getBlock(Vec3i posInChunk) { + return BlockRenderRegistry.getInstance().get( + getData().getBlock(posInChunk).getId() + ); + } + + @Override + public TileRenderStack getTiles(Vec3i blockInChunk, BlockFace face) { + return getTileStackWrapper(getData().getTiles(blockInChunk, face)); + } + + @Override + public boolean hasTiles(Vec3i blockInChunk, BlockFace face) { + return getData().hasTiles(blockInChunk, face); + } + + private TileRenderStack getTileStackWrapper(TileDataStack tileDataList) { + return tileRenderLists.computeIfAbsent( + tileDataList, + TileRenderStackImpl::new + ); + } + public WorldRender getWorld() { return world; } @@ -71,12 +115,6 @@ public class ChunkRender { return needsUpdate; } - public BlockRender getBlock(Vec3i posInChunk) { - return BlockRenderRegistry.getInstance().get( - getData().getBlock(posInChunk).getId() - ); - } - public void render(ShapeRenderHelper renderer) { if (model == null || needsUpdate()) { buildModel(); @@ -232,5 +270,45 @@ public class ChunkRender { new Mat4().identity().translate(pos) ); } + + private class TileRenderStackImpl extends TileRenderStack { + + private final TileDataStack parent; + + public TileRenderStackImpl(TileDataStack parent) { + this.parent = parent; + } + + @Override + public Vec3i getBlockInChunk(Vec3i output) { + return parent.getBlockInChunk(output); + } + + @Override + public ChunkRender getChunk() { + return ChunkRender.this; + } + + @Override + public BlockFace getFace() { + return parent.getFace(); + } + + @Override + public TileRender get(int index) { + return TileRenderRegistry.getInstance().get(parent.get(index).getId()); + } + + @Override + public int size() { + return parent.size(); + } + + @Override + public TileDataStack getData() { + return parent; + } + + } } diff --git a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java index 49b8272..71acda9 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java @@ -26,15 +26,26 @@ import java.util.WeakHashMap; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.graphics.backend.FaceCulling; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.client.world.block.BlockRender; import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; import ru.windcorp.progressia.client.world.entity.EntityRenderable; +import ru.windcorp.progressia.client.world.tile.TileRender; +import ru.windcorp.progressia.client.world.tile.TileRenderStack; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.ChunkDataListeners; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldDataListener; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.generic.GenericWorld; -public class WorldRender { +public class WorldRender +implements GenericWorld< + BlockRender, + TileRender, + TileRenderStack, + ChunkRender, + EntityRenderable +> { private final WorldData data; @@ -67,14 +78,21 @@ public class WorldRender { return chunks.get(chunkData); } + @Override public ChunkRender getChunk(Vec3i pos) { return chunks.get(getData().getChunk(pos)); } + @Override public Collection getChunks() { return chunks.values(); } + @Override + public Collection getEntities() { + return entityModels.values(); + } + public void render(ShapeRenderHelper renderer) { for (ChunkRender chunk : getChunks()) { chunk.render(renderer); diff --git a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRender.java b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRender.java index b7e8de5..66dd99b 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRender.java @@ -19,9 +19,10 @@ package ru.windcorp.progressia.client.world.block; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; import ru.windcorp.progressia.common.util.namespaces.Namespaced; +import ru.windcorp.progressia.common.world.generic.GenericBlock; import ru.windcorp.progressia.client.graphics.model.Renderable; -public abstract class BlockRender extends Namespaced { +public abstract class BlockRender extends Namespaced implements GenericBlock { public BlockRender(String id) { super(id); diff --git a/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerCube.java b/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerCube.java index 4ee0bae..161012c 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerCube.java +++ b/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerCube.java @@ -18,8 +18,8 @@ package ru.windcorp.progressia.client.world.cro; import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK; -import static ru.windcorp.progressia.common.world.tile.GenericTileStack.TILES_PER_FACE; import static ru.windcorp.progressia.common.world.block.BlockFace.BLOCK_FACE_COUNT; +import static ru.windcorp.progressia.common.world.generic.GenericTileStack.TILES_PER_FACE; import java.util.ArrayList; import java.util.Collection; diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java index e7e8466..067b640 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java @@ -3,8 +3,9 @@ package ru.windcorp.progressia.client.world.entity; import glm.vec._3.Vec3; import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.generic.GenericEntity; -public abstract class EntityRenderable implements Renderable { +public abstract class EntityRenderable implements Renderable, GenericEntity { private final EntityData data; @@ -16,6 +17,16 @@ public abstract class EntityRenderable implements Renderable { return data; } + @Override + public Vec3 getPosition() { + return getData().getPosition(); + } + + @Override + public String getId() { + return getData().getId(); + } + public void getViewPoint(Vec3 output) { output.set(0, 0, 0); } diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRender.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRender.java index 65cf3a2..3e78ee9 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRender.java @@ -5,8 +5,9 @@ import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer; import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.generic.GenericTile; -public class TileRender extends Namespaced { +public class TileRender extends Namespaced implements GenericTile { public TileRender(String id) { super(id); diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderStack.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderStack.java new file mode 100644 index 0000000..2d81c11 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderStack.java @@ -0,0 +1,16 @@ +package ru.windcorp.progressia.client.world.tile; + +import ru.windcorp.progressia.client.world.ChunkRender; +import ru.windcorp.progressia.common.world.generic.GenericTileStack; +import ru.windcorp.progressia.common.world.tile.TileDataStack; + +public abstract class TileRenderStack +extends GenericTileStack< + TileRenderStack, + TileRender, + ChunkRender +> { + + public abstract TileDataStack getData(); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/state/OptimizedStateStorage.java b/src/main/java/ru/windcorp/progressia/common/state/OptimizedStateStorage.java index ed6213b..234e399 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/OptimizedStateStorage.java +++ b/src/main/java/ru/windcorp/progressia/common/state/OptimizedStateStorage.java @@ -8,10 +8,12 @@ public class OptimizedStateStorage extends StateStorage { this.ints = new int[sizes.getInts()]; } + @Override public int getInt(int index) { return ints[index]; } + @Override public void setInt(int index, int value) { ints[index] = value; } diff --git a/src/main/java/ru/windcorp/progressia/common/util/ByteBufferInputStream.java b/src/main/java/ru/windcorp/progressia/common/util/ByteBufferInputStream.java index fee39a9..bdb1f14 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/ByteBufferInputStream.java +++ b/src/main/java/ru/windcorp/progressia/common/util/ByteBufferInputStream.java @@ -32,6 +32,7 @@ public class ByteBufferInputStream extends InputStream { this.buffer = buffer; } + @Override public int read() { if (!buffer.hasRemaining()) { return -1; @@ -39,6 +40,7 @@ public class ByteBufferInputStream extends InputStream { return buffer.get() & 0xFF; } + @Override public int read(byte[] bytes, int off, int len) { if (!buffer.hasRemaining()) { return -1; diff --git a/src/main/java/ru/windcorp/progressia/common/util/ByteBufferOutputStream.java b/src/main/java/ru/windcorp/progressia/common/util/ByteBufferOutputStream.java index e4f891c..1f1323e 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/ByteBufferOutputStream.java +++ b/src/main/java/ru/windcorp/progressia/common/util/ByteBufferOutputStream.java @@ -30,6 +30,7 @@ public class ByteBufferOutputStream extends OutputStream { this.buffer = buffer; } + @Override public void write(int b) throws IOException { try { buffer.put((byte) b); @@ -38,6 +39,7 @@ public class ByteBufferOutputStream extends OutputStream { } } + @Override public void write(byte[] bytes, int off, int len) throws IOException { try { buffer.put(bytes, off, len); diff --git a/src/main/java/ru/windcorp/progressia/common/util/SizeLimitedList.java b/src/main/java/ru/windcorp/progressia/common/util/SizeLimitedList.java index 09c3f26..83645a0 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/SizeLimitedList.java +++ b/src/main/java/ru/windcorp/progressia/common/util/SizeLimitedList.java @@ -44,6 +44,7 @@ public class SizeLimitedList extends ForwardingList { return standardAddAll(index, elements); } + @Override public boolean add(E e) { checkMaxSize(); return delegate().add(e); diff --git a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java index 2b42481..eaa02ed 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java +++ b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java @@ -22,7 +22,7 @@ public class VectorUtil { public static void forEachVectorInCuboid( int x0, int y0, int z0, int x1, int y1, int z1, - Consumer action + Consumer action ) { Vec3i cursor = Vectors.grab3i(); diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index 863d990..ed5e5d6 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -33,12 +33,19 @@ import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.generic.GenericChunk; import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileDataStack; import ru.windcorp.progressia.common.world.tile.TileReference; import ru.windcorp.progressia.common.world.tile.TileStackIsFullException; -public class ChunkData { +public class ChunkData +implements GenericChunk< + ChunkData, + BlockData, + TileData, + TileDataStack +> { public static final int BLOCKS_PER_CHUNK = Coordinates.CHUNK_SIZE; @@ -65,6 +72,12 @@ public class ChunkData { this.world = world; } + @Override + public Vec3i getPosition() { + return position; + } + + @Override public BlockData getBlock(Vec3i posInChunk) { return blocks[getBlockIndex(posInChunk)]; } @@ -81,6 +94,7 @@ public class ChunkData { } } + @Override public TileDataStack getTilesOrNull(Vec3i blockInChunk, BlockFace face) { return tiles[getTileIndex(blockInChunk, face)]; } @@ -98,10 +112,12 @@ public class ChunkData { this.tiles[getTileIndex(blockInChunk, face)] = tiles; } + @Override public boolean hasTiles(Vec3i blockInChunk, BlockFace face) { return getTilesOrNull(blockInChunk, face) != null; } + @Override public TileDataStack getTiles(Vec3i blockInChunk, BlockFace face) { int index = getTileIndex(blockInChunk, face); @@ -206,22 +222,6 @@ public class ChunkData { public void forEachEntity(Consumer action) { getEntities().forEach(action); } - - public int getX() { - return position.x; - } - - public int getY() { - return position.y; - } - - public int getZ() { - return position.z; - } - - public Vec3i getPosition() { - return position; - } public WorldData getWorld() { return world; diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index d719f20..1924487 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -30,9 +30,19 @@ import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.util.CoordinatePacker; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.generic.GenericWorld; +import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.common.world.tile.TileDataStack; import ru.windcorp.progressia.test.TestContent; -public class WorldData { +public class WorldData +implements GenericWorld< + BlockData, + TileData, + TileDataStack, + ChunkData, + EntityData +>{ private final TLongObjectMap chunksByPos = new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this); @@ -55,6 +65,21 @@ public class WorldData { } + @Override + public ChunkData getChunk(Vec3i pos) { + return chunksByPos.get(CoordinatePacker.pack3IntsIntoLong(pos)); + } + + @Override + public Collection getChunks() { + return chunks; + } + + @Override + public Collection getEntities() { + return entities; + } + public void tmp_generate() { final int size = 10; Vec3i cursor = new Vec3i(0, 0, 0); @@ -112,21 +137,6 @@ public class WorldData { return CoordinatePacker.pack3IntsIntoLong(chunk.getPosition()); } - public ChunkData getChunk(Vec3i pos) { - return chunksByPos.get(CoordinatePacker.pack3IntsIntoLong(pos)); - } - - public ChunkData getChunkByBlock(Vec3i blockInWorld) { - return getChunk(Coordinates.convertInWorldToChunk(blockInWorld, null)); - } - - public BlockData getBlock(Vec3i blockInWorld) { - ChunkData chunk = getChunkByBlock(blockInWorld); - if (chunk == null) return null; - - return chunk.getBlock(Coordinates.convertInWorldToInChunk(blockInWorld, null)); - } - public void setBlock(Vec3i blockInWorld, BlockData block, boolean notify) { ChunkData chunk = getChunkByBlock(blockInWorld); if (chunk == null) @@ -139,10 +149,6 @@ public class WorldData { chunk.setBlock(Coordinates.convertInWorldToInChunk(blockInWorld, null), block, notify); } - public Collection getChunks() { - return chunks; - } - public TLongSet getChunkKeys() { return chunksByPos.keySet(); } @@ -151,10 +157,6 @@ public class WorldData { return entitiesById.get(entityId); } - public Collection getEntities() { - return entities; - } - public float getTime() { return time; } diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java b/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java index 87feb5e..0e0cefb 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java @@ -20,8 +20,9 @@ package ru.windcorp.progressia.common.world.block; import ru.windcorp.progressia.common.collision.AABB; import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.util.namespaces.Namespaced; +import ru.windcorp.progressia.common.world.generic.GenericBlock; -public class BlockData extends Namespaced { +public class BlockData extends Namespaced implements GenericBlock { public BlockData(String id) { super(id); diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java index c7eda54..af3da1e 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java @@ -2,13 +2,12 @@ package ru.windcorp.progressia.common.world.entity; import glm.vec._2.Vec2; import glm.vec._3.Vec3; -import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.collision.Collideable; import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.state.StatefulObject; -import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.generic.GenericEntity; -public class EntityData extends StatefulObject implements Collideable { +public class EntityData extends StatefulObject implements Collideable, GenericEntity { private final Vec3 position = new Vec3(); private final Vec3 velocity = new Vec3(); @@ -25,20 +24,11 @@ public class EntityData extends StatefulObject implements Collideable { super(EntityDataRegistry.getInstance(), id); } + @Override public Vec3 getPosition() { return position; } - public Vec3i getBlockInWorld(Vec3i output) { - if (output == null) output = new Vec3i(); - return position.round(output); - } - - public Vec3i getChunkCoords(Vec3i output) { - output = getBlockInWorld(output); - return Coordinates.convertInWorldToChunk(output, output); - } - public void setPosition(Vec3 position) { move(position.sub_(getPosition())); } diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericBlock.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericBlock.java new file mode 100644 index 0000000..b0a6e2d --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericBlock.java @@ -0,0 +1,7 @@ +package ru.windcorp.progressia.common.world.generic; + +public interface GenericBlock { + + String getId(); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java new file mode 100644 index 0000000..8dc3cf8 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java @@ -0,0 +1,85 @@ +package ru.windcorp.progressia.common.world.generic; + +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.VectorUtil; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public interface GenericChunk< + Self extends GenericChunk, + B extends GenericBlock, + T extends GenericTile, + TS extends GenericTileStack +> { + + public static final int BLOCKS_PER_CHUNK = Coordinates.CHUNK_SIZE; + + Vec3i getPosition(); + + B getBlock(Vec3i blockInChunk); + TS getTiles(Vec3i blockInChunk, BlockFace face); + boolean hasTiles(Vec3i blockInChunk, BlockFace face); + + default int getX() { + return getPosition().x; + } + + default int getY() { + return getPosition().y; + } + + default int getZ() { + return getPosition().z; + } + + default boolean containsBiC(Vec3i blockInChunk) { + return + blockInChunk.x >= 0 && blockInChunk.x < BLOCKS_PER_CHUNK && + blockInChunk.y >= 0 && blockInChunk.y < BLOCKS_PER_CHUNK && + blockInChunk.z >= 0 && blockInChunk.z < BLOCKS_PER_CHUNK; + } + + default boolean containsBiW(Vec3i blockInWorld) { + Vec3i v = Vectors.grab3i(); + + v = Coordinates.getInWorld(getPosition(), Vectors.ZERO_3i, v); + v = blockInWorld.sub(v, v); + + boolean result = containsBiC(v); + + Vectors.release(v); + return result; + } + + default void forEachBiC(Consumer action) { + VectorUtil.forEachVectorInCuboid( + 0, 0, 0, + BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, + action + ); + } + + default void forEachBiW(Consumer action) { + VectorUtil.forEachVectorInCuboid( + Coordinates.getInWorld(getX(), 0), + Coordinates.getInWorld(getY(), 0), + Coordinates.getInWorld(getZ(), 0), + BLOCKS_PER_CHUNK, + BLOCKS_PER_CHUNK, + BLOCKS_PER_CHUNK, + action + ); + } + + default TS getTilesOrNull(Vec3i blockInChunk, BlockFace face) { + if (hasTiles(blockInChunk, face)) { + return getTiles(blockInChunk, face); + } + + return null; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericEntity.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericEntity.java new file mode 100644 index 0000000..ac8ca36 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericEntity.java @@ -0,0 +1,22 @@ +package ru.windcorp.progressia.common.world.generic; + +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.Coordinates; + +public interface GenericEntity { + + String getId(); + Vec3 getPosition(); + + default Vec3i getBlockInWorld(Vec3i output) { + if (output == null) output = new Vec3i(); + return getPosition().round(output); + } + + default Vec3i getChunkCoords(Vec3i output) { + output = getBlockInWorld(output); + return Coordinates.convertInWorldToChunk(output, output); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericTile.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericTile.java new file mode 100644 index 0000000..f674391 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericTile.java @@ -0,0 +1,7 @@ +package ru.windcorp.progressia.common.world.generic; + +public interface GenericTile { + + String getId(); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/GenericTileStack.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericTileStack.java similarity index 84% rename from src/main/java/ru/windcorp/progressia/common/world/tile/GenericTileStack.java rename to src/main/java/ru/windcorp/progressia/common/world/generic/GenericTileStack.java index e22cc8b..476ba31 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/GenericTileStack.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericTileStack.java @@ -1,4 +1,4 @@ -package ru.windcorp.progressia.common.world.tile; +package ru.windcorp.progressia.common.world.generic; import java.util.AbstractList; import java.util.Objects; @@ -6,11 +6,14 @@ import java.util.RandomAccess; import java.util.function.Consumer; import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.block.BlockFace; -public abstract class GenericTileStack +public abstract class GenericTileStack< + Self extends GenericTileStack, + T extends GenericTile, + C extends GenericChunk +> extends AbstractList implements RandomAccess { @@ -21,13 +24,12 @@ implements RandomAccess { public static final int TILES_PER_FACE = 8; public abstract Vec3i getBlockInChunk(Vec3i output); - protected abstract Vec3i getChunkPos(); public abstract C getChunk(); public abstract BlockFace getFace(); public Vec3i getBlockInWorld(Vec3i output) { // This is safe - return Coordinates.getInWorld(getChunkPos(), getBlockInChunk(output), output); + return Coordinates.getInWorld(getChunk().getPosition(), getBlockInChunk(output), output); } public boolean isFull() { diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java new file mode 100644 index 0000000..79746ed --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java @@ -0,0 +1,176 @@ +package ru.windcorp.progressia.common.world.generic; + +import java.util.Collection; +import java.util.function.Consumer; + +import glm.Glm; +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public interface GenericWorld< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericTileStack, + C extends GenericChunk, + E extends GenericEntity +> { + + Collection getChunks(); + C getChunk(Vec3i pos); + + Collection getEntities(); + + /* + * Chunks + */ + + default C getChunkByBlock(Vec3i blockInWorld) { + Vec3i chunkCoords = Vectors.grab3i(); + chunkCoords = Coordinates.convertInWorldToChunk(blockInWorld, chunkCoords); + C result = getChunk(chunkCoords); + Vectors.release(chunkCoords); + return result; + } + + default B getBlock(Vec3i blockInWorld) { + Vec3i v = Vectors.grab3i(); + B result; + + C chunk = getChunk(Coordinates.convertInWorldToChunk(blockInWorld, v)); + if (chunk == null) { + result = null; + } else { + result = chunk.getBlock(Coordinates.convertInWorldToInChunk(blockInWorld, v)); + } + + Vectors.release(v); + return result; + } + + default TS getTiles(Vec3i blockInWorld, BlockFace face) { + Vec3i v = Vectors.grab3i(); + TS result; + + C chunk = getChunk(Coordinates.convertInWorldToChunk(blockInWorld, v)); + if (chunk == null) { + result = null; + } else { + result = chunk.getTiles(Coordinates.convertInWorldToInChunk(blockInWorld, v), face); + } + + Vectors.release(v); + return result; + } + + default TS getTilesOrNull(Vec3i blockInWorld, BlockFace face) { + Vec3i v = Vectors.grab3i(); + TS result; + + C chunk = getChunk(Coordinates.convertInWorldToChunk(blockInWorld, v)); + if (chunk == null) { + result = null; + } else { + result = chunk.getTilesOrNull(Coordinates.convertInWorldToInChunk(blockInWorld, v), face); + } + + Vectors.release(v); + return result; + } + + default boolean hasTiles(Vec3i blockInWorld, BlockFace face) { + Vec3i v = Vectors.grab3i(); + boolean result; + + C chunk = getChunk(Coordinates.convertInWorldToChunk(blockInWorld, v)); + if (chunk == null) { + result = false; + } else { + result = chunk.hasTiles(Coordinates.convertInWorldToInChunk(blockInWorld, v), face); + } + + Vectors.release(v); + return result; + } + + default T getTile(Vec3i blockInWorld, BlockFace face, int layer) { + TS stack = getTilesOrNull(blockInWorld, face); + if (stack == null || stack.size() <= layer) return null; + return stack.get(layer); + } + + default boolean isChunkLoaded(Vec3i pos) { + return getChunk(pos) != null; + } + + default boolean isBlockLoaded(Vec3i blockInWorld) { + return getChunkByBlock(blockInWorld) != null; + } + + default void forEachChunk(Consumer action) { + getChunks().forEach(action); + } + + /* + * Entities + */ + + default void forEachEntity(Consumer action) { + getEntities().forEach(action); + } + + default void forEachEntityIn(Vec3i min, Vec3i max, Consumer action) { + forEachEntity(e -> { + Vec3 pos = e.getPosition(); + if (pos.x < min.x || pos.y < min.y || pos.z < min.z || pos.x > max.x || pos.y > max.y || pos.z > max.z) { + action.accept(e); + } + }); + } + + default void forEachEntityIn(Vec3 min, Vec3 max, Consumer action) { + forEachEntity(e -> { + Vec3 pos = e.getPosition(); + if (pos.x < min.x || pos.y < min.y || pos.z < min.z || pos.x > max.x || pos.y > max.y || pos.z > max.z) { + action.accept(e); + } + }); + } + + default void forEachEntityInChunk(Vec3i pos, Consumer action) { + Vec3i v = Vectors.grab3i(); + + forEachEntity(e -> { + e.getChunkCoords(v); + if (Glm.equals(v, pos)) { + action.accept(e); + } + }); + + Vectors.release(v); + } + + default void forEachEntityInChunk(C chunk, Consumer action) { + Vec3i v = Vectors.grab3i(); + + forEachEntity(e -> { + e.getChunkCoords(v); + if (Glm.equals(v, chunk.getPosition())) { + action.accept(e); + } + }); + + Vectors.release(v); + } + + default void forEachEntityWithId(String id, Consumer action) { + forEachEntity(e -> { + if (id.equals(e.getId())) { + action.accept(e); + } + }); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileData.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileData.java index d3354f6..a08edf7 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/TileData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileData.java @@ -18,8 +18,9 @@ package ru.windcorp.progressia.common.world.tile; import ru.windcorp.progressia.common.util.namespaces.Namespaced; +import ru.windcorp.progressia.common.world.generic.GenericTile; -public class TileData extends Namespaced { +public class TileData extends Namespaced implements GenericTile { public TileData(String id) { super(id); diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java index 489a5ca..f0f7609 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java @@ -1,10 +1,15 @@ package ru.windcorp.progressia.common.world.tile; -import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.generic.GenericTileStack; -public abstract class TileDataStack extends GenericTileStack { +public abstract class TileDataStack +extends GenericTileStack< + TileDataStack, + TileData, + ChunkData +> { /** * Inserts the specified tile at the specified position in this stack. @@ -54,15 +59,6 @@ public abstract class TileDataStack extends GenericTileStack { private final WorldLogic world; private final ChunkData data; @@ -43,31 +49,36 @@ public class ChunkLogic { this.world = world; this.data = data; - generateTickLists(); + tmp_generateTickLists(); } - private void generateTickLists() { - ChunkTickContext context = TickContextMutable.start().withChunk(this).build(); - - context.forEachBlock(bctxt -> { - BlockLogic block = bctxt.getBlock(); - - if (!(block instanceof TickableBlock)) return; - - if (((TickableBlock) block).getTickingPolicy(bctxt) == TickingPolicy.REGULAR) { - tickingBlocks.add(Coordinates.convertInWorldToInChunk(bctxt.getBlockInWorld(), null)); - } - - bctxt.forEachFace(fctxt -> fctxt.forEachTile(tctxt -> { - TileLogic tile = tctxt.getTile(); - - if (!(tile instanceof TickableTile)) return; - - if (((TickableTile) tile).getTickingPolicy(tctxt) == TickingPolicy.REGULAR) { - tickingTiles.add(tctxt.getReference()); - } - })); - }); + @Override + public Vec3i getPosition() { + return getData().getPosition(); + } + + @Override + public BlockLogic getBlock(Vec3i blockInChunk) { + return BlockLogicRegistry.getInstance().get( + getData().getBlock(blockInChunk).getId() + ); + } + + @Override + public TileLogicStack getTiles(Vec3i blockInChunk, BlockFace face) { + return getTileStackWrapper(getData().getTiles(blockInChunk, face)); + } + + @Override + public boolean hasTiles(Vec3i blockInChunk, BlockFace face) { + return getData().hasTiles(blockInChunk, face); + } + + private TileLogicStack getTileStackWrapper(TileDataStack tileDataList) { + return tileLogicLists.computeIfAbsent( + tileDataList, + TileLogicStackImpl::new + ); } public WorldLogic getWorld() { @@ -78,10 +89,6 @@ public class ChunkLogic { return data; } - public Vec3i getPosition() { - return getData().getPosition(); - } - public boolean hasTickingBlocks() { return !tickingBlocks.isEmpty(); } @@ -114,29 +121,6 @@ public class ChunkLogic { }); } - public BlockLogic getBlock(Vec3i blockInChunk) { - return BlockLogicRegistry.getInstance().get( - getData().getBlock(blockInChunk).getId() - ); - } - - public TileLogicStack getTiles(Vec3i blockInChunk, BlockFace face) { - return getTileStackWrapper(getData().getTiles(blockInChunk, face)); - } - - public TileLogicStack getTilesOrNull(Vec3i blockInChunk, BlockFace face) { - TileDataStack tiles = getData().getTilesOrNull(blockInChunk, face); - if (tiles == null) return null; - return getTileStackWrapper(tiles); - } - - private TileLogicStack getTileStackWrapper(TileDataStack tileDataList) { - return tileLogicLists.computeIfAbsent( - tileDataList, - TileLogicStackImpl::new - ); - } - public TickChunk getTickTask() { return tickTask; } @@ -154,11 +138,6 @@ public class ChunkLogic { return parent.getBlockInChunk(output); } - @Override - public Vec3i getChunkPos() { - return ChunkLogic.this.getPosition(); - } - @Override public ChunkLogic getChunk() { return ChunkLogic.this; @@ -185,5 +164,29 @@ public class ChunkLogic { } } + + private void tmp_generateTickLists() { + ChunkTickContext context = TickContextMutable.start().withChunk(this).build(); + + context.forEachBlock(bctxt -> { + BlockLogic block = bctxt.getBlock(); + + if (!(block instanceof TickableBlock)) return; + + if (((TickableBlock) block).getTickingPolicy(bctxt) == TickingPolicy.REGULAR) { + tickingBlocks.add(Coordinates.convertInWorldToInChunk(bctxt.getBlockInWorld(), null)); + } + + bctxt.forEachFace(fctxt -> fctxt.forEachTile(tctxt -> { + TileLogic tile = tctxt.getTile(); + + if (!(tile instanceof TickableTile)) return; + + if (((TickableTile) tile).getTickingPolicy(tctxt) == TickingPolicy.REGULAR) { + tickingTiles.add(tctxt.getReference()); + } + })); + }); + } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickContextMutable.java b/src/main/java/ru/windcorp/progressia/server/world/TickContextMutable.java index 5ed3ef1..f5ce2db 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/TickContextMutable.java +++ b/src/main/java/ru/windcorp/progressia/server/world/TickContextMutable.java @@ -8,7 +8,7 @@ import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.common.world.tile.GenericTileStack; +import ru.windcorp.progressia.common.world.generic.GenericTileStack; import ru.windcorp.progressia.common.world.tile.TileDataStack; import ru.windcorp.progressia.common.world.tile.TileReference; import ru.windcorp.progressia.server.Server; @@ -87,7 +87,7 @@ public abstract class TickContextMutable implements BlockTickContext, TSTickCont public static interface World extends Builder { Chunk withChunk(Vec3i chunk); Block withBlock(Vec3i blockInWorld); - TileStack withTS(GenericTileStack tileStack); + TileStack withTS(GenericTileStack tileStack); default Builder.Chunk withChunk(ChunkData chunk) { Objects.requireNonNull(chunk, "chunk"); @@ -237,7 +237,7 @@ public abstract class TickContextMutable implements BlockTickContext, TSTickCont } @Override - public TileStack withTS(GenericTileStack tileStack) { + public TileStack withTS(GenericTileStack tileStack) { Objects.requireNonNull(tileStack, "tileStack"); return withBlock(tileStack.getBlockInWorld(this.blockInWorld)).withFace(tileStack.getFace()); diff --git a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java index ca745a7..0142c08 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java @@ -2,22 +2,29 @@ package ru.windcorp.progressia.server.world; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.ChunkDataListeners; -import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldDataListener; -import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.generic.GenericWorld; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.tile.TileLogic; +import ru.windcorp.progressia.server.world.tile.TileLogicStack; import ru.windcorp.progressia.test.TestChunkSender; -public class WorldLogic { +public class WorldLogic +implements GenericWorld< + BlockLogic, + TileLogic, + TileLogicStack, + ChunkLogic, + EntityData // not using EntityLogic because it is stateless +> { private final WorldData data; private final Server server; @@ -44,6 +51,21 @@ public class WorldLogic { data.addListener(new TestChunkSender(server)); } + @Override + public ChunkLogic getChunk(Vec3i pos) { + return chunks.get(getData().getChunk(pos)); + } + + @Override + public Collection getChunks() { + return chunks.values(); + } + + @Override + public Collection getEntities() { + return getData().getEntities(); + } + public Server getServer() { return server; } @@ -55,53 +77,5 @@ public class WorldLogic { public ChunkLogic getChunk(ChunkData chunkData) { return chunks.get(chunkData); } - - public ChunkLogic getChunk(Vec3i pos) { - return chunks.get(getData().getChunk(pos)); - } - - public ChunkLogic getChunkByBlock(Vec3i blockInWorld) { - return getChunk(Coordinates.convertInWorldToChunk(blockInWorld, null)); - } - - public BlockLogic getBlock(Vec3i blockInWorld) { - ChunkLogic chunk = getChunkByBlock(blockInWorld); - if (chunk == null) return null; - - return chunk.getBlock(Coordinates.convertInWorldToInChunk(blockInWorld, null)); - } - - public List getTiles(Vec3i blockInWorld, BlockFace face) { - return getTilesImpl(blockInWorld, face, true); - } - - public List getTilesOrNull(Vec3i blockInWorld, BlockFace face) { - return getTilesImpl(blockInWorld, face, false); - } - - private List getTilesImpl(Vec3i blockInWorld, BlockFace face, boolean createIfMissing) { - ChunkLogic chunk = getChunkByBlock(blockInWorld); - if (chunk == null) return null; - - Vec3i blockInChunk = Coordinates.convertInWorldToInChunk(blockInWorld, null); - - List result = - createIfMissing - ? chunk.getTiles(blockInChunk, face) - : chunk.getTilesOrNull(blockInChunk, face); - - return result; - } - - public TileLogic getTile(Vec3i blockInWorld, BlockFace face, int layer) { - List tiles = getTilesOrNull(blockInWorld, face); - if (tiles == null || tiles.size() <= layer) return null; - - return tiles.get(layer); - } - - public Collection getChunks() { - return chunks.values(); - } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogic.java b/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogic.java index 045e707..cb8fa6d 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogic.java @@ -2,8 +2,9 @@ package ru.windcorp.progressia.server.world.block; import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.generic.GenericBlock; -public class BlockLogic extends Namespaced { +public class BlockLogic extends Namespaced implements GenericBlock { public BlockLogic(String id) { super(id); diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java index c44ec7e..3521143 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java @@ -2,8 +2,9 @@ package ru.windcorp.progressia.server.world.tile; import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.generic.GenericTile; -public class TileLogic extends Namespaced { +public class TileLogic extends Namespaced implements GenericTile { public TileLogic(String id) { super(id); diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicStack.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicStack.java index ea5789b..6c0b6ec 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicStack.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicStack.java @@ -1,12 +1,15 @@ package ru.windcorp.progressia.server.world.tile; -import ru.windcorp.progressia.common.world.tile.GenericTileStack; +import ru.windcorp.progressia.common.world.generic.GenericTileStack; import ru.windcorp.progressia.common.world.tile.TileDataStack; import ru.windcorp.progressia.server.world.ChunkLogic; -public abstract class TileLogicStack extends GenericTileStack { - - // TODO add @Deprecated or smth similar to all modification methods +public abstract class TileLogicStack +extends GenericTileStack< + TileLogicStack, + TileLogic, + ChunkLogic +> { public abstract TileDataStack getData(); From c6677ec8fdee20e286fab309332a961270993f8c Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Fri, 25 Dec 2020 18:12:14 +0300 Subject: [PATCH 02/10] Added ChunkMap and ChunkSet --- .../progressia/common/world/WorldData.java | 34 ++- .../common/world/generic/ChunkMap.java | 113 ++++++++++ .../common/world/generic/ChunkSet.java | 212 ++++++++++++++++++ .../world/generic/LongBasedChunkMap.java | 89 ++++++++ .../world/generic/LongBasedChunkSet.java | 152 +++++++++++++ .../progressia/server/ChunkLoadManager.java | 27 +-- 6 files changed, 590 insertions(+), 37 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/ChunkMap.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSet.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/LongBasedChunkMap.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/LongBasedChunkSet.java diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index 1924487..01b7c1b 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -25,12 +25,13 @@ import glm.vec._3.i.Vec3i; import gnu.trove.impl.sync.TSynchronizedLongObjectMap; import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; -import gnu.trove.set.TLongSet; import ru.windcorp.progressia.common.collision.CollisionModel; -import ru.windcorp.progressia.common.util.CoordinatePacker; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.generic.ChunkMap; +import ru.windcorp.progressia.common.world.generic.ChunkSet; import ru.windcorp.progressia.common.world.generic.GenericWorld; +import ru.windcorp.progressia.common.world.generic.LongBasedChunkMap; import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileDataStack; import ru.windcorp.progressia.test.TestContent; @@ -44,11 +45,12 @@ implements GenericWorld< EntityData >{ - private final TLongObjectMap chunksByPos = - new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this); + private final ChunkMap chunksByPos = new LongBasedChunkMap<>( + new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this) + ); private final Collection chunks = - Collections.unmodifiableCollection(chunksByPos.valueCollection()); + Collections.unmodifiableCollection(chunksByPos.values()); private final TLongObjectMap entitiesById = new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this); @@ -67,7 +69,7 @@ implements GenericWorld< @Override public ChunkData getChunk(Vec3i pos) { - return chunksByPos.get(CoordinatePacker.pack3IntsIntoLong(pos)); + return chunksByPos.get(pos); } @Override @@ -75,6 +77,10 @@ implements GenericWorld< return chunks; } + public ChunkSet getLoadedChunks() { + return chunksByPos.keys(); + } + @Override public Collection getEntities() { return entities; @@ -102,9 +108,7 @@ implements GenericWorld< public synchronized void addChunk(ChunkData chunk) { addChunkListeners(chunk); - long key = getChunkKey(chunk); - - ChunkData previous = chunksByPos.get(key); + ChunkData previous = chunksByPos.get(chunk); if (previous != null) { throw new IllegalArgumentException(String.format( "Chunk at (%d; %d; %d) already exists", @@ -112,7 +116,7 @@ implements GenericWorld< )); } - chunksByPos.put(key, chunk); + chunksByPos.put(chunk, chunk); chunk.forEachEntity(entity -> entitiesById.put(entity.getEntityId(), entity) @@ -130,11 +134,7 @@ implements GenericWorld< entitiesById.remove(entity.getEntityId()) ); - chunksByPos.remove(getChunkKey(chunk)); - } - - private static long getChunkKey(ChunkData chunk) { - return CoordinatePacker.pack3IntsIntoLong(chunk.getPosition()); + chunksByPos.remove(chunk); } public void setBlock(Vec3i blockInWorld, BlockData block, boolean notify) { @@ -149,10 +149,6 @@ implements GenericWorld< chunk.setBlock(Coordinates.convertInWorldToInChunk(blockInWorld, null), block, notify); } - public TLongSet getChunkKeys() { - return chunksByPos.keySet(); - } - public EntityData getEntity(long entityId) { return entitiesById.get(entityId); } diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkMap.java b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkMap.java new file mode 100644 index 0000000..ab83f9a --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkMap.java @@ -0,0 +1,113 @@ +package ru.windcorp.progressia.common.world.generic; + +import java.util.Collection; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import glm.vec._3.i.Vec3i; + +public interface ChunkMap { + + /* + * Size + */ + + int size(); + + default boolean isEmpty() { + return size() == 0; + } + + /* + * Basic operations + */ + + boolean containsKey(Vec3i pos); + + V get(Vec3i pos); + V put(Vec3i pos, V obj); + V remove(Vec3i pos); + + default boolean containsValue(V value) { + return values().contains(value); + } + + default V getOrDefault(Vec3i pos, V def) { + return containsKey(pos) ? def : get(pos); + } + + default V compute(Vec3i pos, BiFunction remappingFunction) { + V newValue = remappingFunction.apply(pos, get(pos)); + + if (newValue == null) { + remove(pos); + } else { + put(pos, newValue); + } + + return newValue; + } + + // TODO implement ALL default methods from Map + + /* + * Basic operation wrappers + */ + + // TODO implement (int, int, int) and GenericChunk versions of all of the above + + default boolean containsChunk(GenericChunk chunk) { + return containsKey(chunk.getPosition()); + } + + default V get(GenericChunk chunk) { + return get(chunk.getPosition()); + } + + default V put(GenericChunk chunk, V obj) { + return put(chunk.getPosition(), obj); + } + + default V remove(GenericChunk chunk) { + return remove(chunk.getPosition()); + } + + default V getOrDefault(GenericChunk chunk, V def) { + return containsChunk(chunk) ? def : get(chunk); + } + + default > V compute(C chunk, BiFunction remappingFunction) { + V newValue = remappingFunction.apply(chunk, get(chunk)); + + if (newValue == null) { + remove(chunk); + } else { + put(chunk, newValue); + } + + return newValue; + } + + /* + * Views + */ + + Collection values(); + ChunkSet keys(); + + /* + * Bulk operations + */ + + boolean removeIf(BiPredicate condition); + void forEach(BiConsumer action); + + default > void forEachIn(GenericWorld world, BiConsumer action) { + forEach((pos, value) -> { + C chunk = world.getChunk(pos); + if (chunk == null) return; + action.accept(chunk, value); + }); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSet.java b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSet.java new file mode 100644 index 0000000..84013ec --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSet.java @@ -0,0 +1,212 @@ +package ru.windcorp.progressia.common.world.generic; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import glm.vec._3.i.Vec3i; +import gnu.trove.set.hash.TLongHashSet; +import ru.windcorp.progressia.common.util.Vectors; + +public interface ChunkSet extends Iterable { + + /* + * Size + */ + + int size(); + + default boolean isEmpty() { + return size() == 0; + } + + /* + * Basic operations + */ + + boolean contains(Vec3i pos); + boolean add(Vec3i pos); + boolean remove(Vec3i pos); + + /* + * Basic operation wrappers + */ + + default boolean contains(int x, int y, int z) { + Vec3i v = Vectors.grab3i(); + boolean result = contains(v); + Vectors.release(v); + return result; + } + + default boolean add(int x, int y, int z) { + Vec3i v = Vectors.grab3i(); + boolean result = add(v); + Vectors.release(v); + return result; + } + + default boolean remove(int x, int y, int z) { + Vec3i v = Vectors.grab3i(); + boolean result = remove(v); + Vectors.release(v); + return result; + } + + default boolean contains(GenericChunk chunk) { + return contains(chunk.getPosition()); + } + + default boolean add(GenericChunk chunk) { + return add(chunk.getPosition()); + } + + default boolean remove(GenericChunk chunk) { + return remove(chunk.getPosition()); + } + + default > void forEachIn(GenericWorld world, Consumer action) { + forEach(position -> { + C chunk = world.getChunk(position); + if (chunk == null) return; + action.accept(chunk); + }); + } + + /* + * Bulk operations on ChunkSets + */ + + boolean containsAll(ChunkSet other); + boolean containsAny(ChunkSet other); + + void addAll(ChunkSet other); + void removeAll(ChunkSet other); + void retainAll(ChunkSet other); + + /* + * Other bulk operations + */ + + void clear(); + + default boolean containsAll(Iterable other) { + boolean[] hasMissing = new boolean[] { false }; + + other.forEach(v -> { + if (!contains(v)) { + hasMissing[0] = true; + } + }); + + return hasMissing[0]; + } + + default boolean containsAny(Iterable other) { + boolean[] hasPresent = new boolean[] { false }; + + other.forEach(v -> { + if (contains(v)) { + hasPresent[0] = true; + } + }); + + return hasPresent[0]; + } + + default void addAll(Iterable other) { + other.forEach(this::add); + } + + default void removeAll(Iterable other) { + other.forEach(this::remove); + } + + default void retainAll(Iterable other) { + if (other instanceof ChunkSet) { + // We shouldn't invoke retainAll(ChunkSet) because we could be the fallback for it + removeIf(v -> !((ChunkSet) other).contains(v)); + return; + } + + final int threshold = 16; // Maximum size of other at which point creating a Set becomes faster than iterating + + Collection collection = null; + int otherSize = -1; + + if (other instanceof Set) { + collection = (Set) other; + } else if (other instanceof Collection) { + Collection otherAsCollection = ((Collection) other); + otherSize = otherAsCollection.size(); + + if (otherSize < threshold) { + collection = otherAsCollection; + } + } + + if (collection != null) { + final Collection c = collection; + removeIf(v -> !c.contains(v)); + return; + } + + if (otherSize < 0) { + otherSize = gnu.trove.impl.Constants.DEFAULT_CAPACITY; + } + + retainAll(new LongBasedChunkSet(new TLongHashSet(otherSize), other)); + return; + } + + default void removeIf(Predicate condition) { + for (Iterator it = iterator(); it.hasNext();) { + if (condition.test(it.next())) { + it.remove(); + } + } + } + + default void retainIf(Predicate condition) { + for (Iterator it = iterator(); it.hasNext();) { + if (!condition.test(it.next())) { + it.remove(); + } + } + } + + default boolean containsAllChunks(Iterable> chunks) { + boolean[] hasMissing = new boolean[] { false }; + + chunks.forEach(c -> { + if (!contains(c.getPosition())) { + hasMissing[0] = true; + } + }); + + return hasMissing[0]; + } + + default boolean containsAnyChunks(Iterable> chunks) { + boolean[] hasPresent = new boolean[] { false }; + + chunks.forEach(c -> { + if (contains(c.getPosition())) { + hasPresent[0] = true; + } + }); + + return hasPresent[0]; + } + + default void addAllChunks(Iterable> chunks) { + chunks.forEach(this::add); + } + + default void removeAllChunks(Iterable> chunks) { + chunks.forEach(this::remove); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/LongBasedChunkMap.java b/src/main/java/ru/windcorp/progressia/common/world/generic/LongBasedChunkMap.java new file mode 100644 index 0000000..80c7e17 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/LongBasedChunkMap.java @@ -0,0 +1,89 @@ +package ru.windcorp.progressia.common.world.generic; + +import java.util.Collection; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; + +import glm.vec._3.i.Vec3i; +import gnu.trove.map.TLongObjectMap; +import ru.windcorp.progressia.common.util.CoordinatePacker; +import ru.windcorp.progressia.common.util.Vectors; + +public class LongBasedChunkMap implements ChunkMap { + + protected final TLongObjectMap impl; + private final ChunkSet keys; + + public LongBasedChunkMap(TLongObjectMap impl) { + this.impl = impl; + this.keys = new LongBasedChunkSet(impl.keySet()); + } + + private static long getKey(Vec3i v) { + return CoordinatePacker.pack3IntsIntoLong(v); + } + + private static Vec3i getVector(long key, Vec3i output) { + return CoordinatePacker.unpack3IntsFromLong(key, output); + } + + @Override + public int size() { + return impl.size(); + } + + @Override + public boolean containsKey(Vec3i pos) { + return impl.containsKey(getKey(pos)); + } + + @Override + public V get(Vec3i pos) { + return impl.get(getKey(pos)); + } + + @Override + public V put(Vec3i pos, V obj) { + return impl.put(getKey(pos), obj); + } + + @Override + public V remove(Vec3i pos) { + return impl.remove(getKey(pos)); + } + + @Override + public Collection values() { + return impl.valueCollection(); + } + + @Override + public ChunkSet keys() { + return keys; + } + + @Override + public boolean removeIf(BiPredicate condition) { + Vec3i v = Vectors.grab3i(); + + boolean result = impl.retainEntries((key, value) -> { + return !condition.test(getVector(key, v), value); + }); + + Vectors.release(v); + return result; + } + + @Override + public void forEach(BiConsumer action) { + Vec3i v = Vectors.grab3i(); + + impl.forEachEntry((key, value) -> { + action.accept(getVector(key, v), value); + return true; + }); + + Vectors.release(v); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/LongBasedChunkSet.java b/src/main/java/ru/windcorp/progressia/common/world/generic/LongBasedChunkSet.java new file mode 100644 index 0000000..ad4c0cb --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/LongBasedChunkSet.java @@ -0,0 +1,152 @@ +package ru.windcorp.progressia.common.world.generic; + +import java.util.Iterator; +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; +import gnu.trove.iterator.TLongIterator; +import gnu.trove.set.TLongSet; +import ru.windcorp.progressia.common.util.CoordinatePacker; +import ru.windcorp.progressia.common.util.Vectors; + +public class LongBasedChunkSet implements ChunkSet { + + protected final TLongSet impl; + + public LongBasedChunkSet(TLongSet impl) { + this.impl = impl; + } + + public LongBasedChunkSet(TLongSet impl, ChunkSet copyFrom) { + this(impl); + addAll(copyFrom); + } + + public LongBasedChunkSet(TLongSet impl, Iterable copyFrom) { + this(impl); + addAll(copyFrom); + } + + public LongBasedChunkSet(TLongSet impl, GenericWorld copyFrom) { + this(impl); + addAllChunks(copyFrom.getChunks()); + } + + private static long getKey(Vec3i v) { + return CoordinatePacker.pack3IntsIntoLong(v); + } + + private static Vec3i getVector(long key, Vec3i output) { + return CoordinatePacker.unpack3IntsFromLong(key, output); + } + + @Override + public Iterator iterator() { + return new IteratorImpl(); + } + + @Override + public int size() { + return impl.size(); + } + + @Override + public boolean contains(Vec3i pos) { + return impl.contains(getKey(pos)); + } + + @Override + public boolean add(Vec3i pos) { + return impl.add(getKey(pos)); + } + + @Override + public boolean remove(Vec3i pos) { + return impl.remove(getKey(pos)); + } + + @Override + public boolean containsAll(ChunkSet other) { + if (other instanceof LongBasedChunkSet) { + return impl.containsAll(((LongBasedChunkSet) other).impl); + } + + return ChunkSet.super.containsAll((Iterable) other); + } + + @Override + public boolean containsAny(ChunkSet other) { + return ChunkSet.super.containsAny((Iterable) other); + } + + @Override + public void addAll(ChunkSet other) { + if (other instanceof LongBasedChunkSet) { + impl.addAll(((LongBasedChunkSet) other).impl); + return; + } + + ChunkSet.super.addAll((Iterable) other); + } + + @Override + public void removeAll(ChunkSet other) { + if (other instanceof LongBasedChunkSet) { + impl.removeAll(((LongBasedChunkSet) other).impl); + return; + } + + ChunkSet.super.removeAll((Iterable) other); + } + + @Override + public void retainAll(ChunkSet other) { + if (other instanceof LongBasedChunkSet) { + impl.retainAll(((LongBasedChunkSet) other).impl); + return; + } + + ChunkSet.super.retainAll((Iterable) other); + } + + @Override + public void clear() { + impl.clear(); + } + + @Override + public void forEach(Consumer action) { + Vec3i v = Vectors.grab3i(); + + impl.forEach(key -> { + getVector(key, v); + action.accept(v); + return true; + }); + + Vectors.release(v); + } + + private class IteratorImpl implements Iterator { + + private final Vec3i vector = new Vec3i(); + private final TLongIterator parent = LongBasedChunkSet.this.impl.iterator(); + + @Override + public boolean hasNext() { + return parent.hasNext(); + } + + @Override + public Vec3i next() { + return getVector(parent.next(), vector); + } + + @Override + public void remove() { + parent.remove(); + } + + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java index 99493bc..1b2c539 100644 --- a/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java +++ b/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java @@ -5,10 +5,10 @@ import java.util.Collection; import java.util.Collections; import glm.vec._3.i.Vec3i; -import gnu.trove.set.TLongSet; import gnu.trove.set.hash.TLongHashSet; -import ru.windcorp.progressia.common.util.CoordinatePacker; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.generic.ChunkSet; +import ru.windcorp.progressia.common.world.generic.LongBasedChunkSet; import ru.windcorp.progressia.test.TestContent; public class ChunkLoadManager { @@ -18,9 +18,9 @@ public class ChunkLoadManager { private final Collection> allChunkLoaders = Collections.synchronizedCollection(new ArrayList<>()); - private final TLongSet requested = new TLongHashSet(); - private final TLongSet toLoad = new TLongHashSet(); - private final TLongSet toUnload = new TLongHashSet(); + private final ChunkSet requested = new LongBasedChunkSet(new TLongHashSet()); + private final ChunkSet toLoad = new LongBasedChunkSet(new TLongHashSet()); + private final ChunkSet toUnload = new LongBasedChunkSet(new TLongHashSet()); public ChunkLoadManager(Server server) { this.server = server; @@ -42,11 +42,11 @@ public class ChunkLoadManager { } private void gatherRequests(ChunkLoader loader) { - loader.requestChunksToLoad(v -> requested.add(CoordinatePacker.pack3IntsIntoLong(v))); + loader.requestChunksToLoad(requested::add); } private void updateQueues() { - TLongSet loaded = getServer().getWorld().getData().getChunkKeys(); + ChunkSet loaded = getServer().getWorld().getData().getLoadedChunks(); toLoad.clear(); toLoad.addAll(requested); @@ -58,17 +58,8 @@ public class ChunkLoadManager { } private void processQueues() { - Vec3i v = new Vec3i(); - - toLoad.forEach(key -> { - loadChunk(CoordinatePacker.unpack3IntsFromLong(key, v)); - return true; - }); - - toUnload.forEach(key -> { - unloadChunk(CoordinatePacker.unpack3IntsFromLong(key, v)); - return true; - }); + toUnload.forEach(this::unloadChunk); + toLoad.forEach(this::loadChunk); } public Server getServer() { From fd0269f913b5b758b95ed302c2f804b514ecf6e5 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sat, 26 Dec 2020 15:25:28 +0300 Subject: [PATCH 03/10] Added the concept of visible chunks and entities --- .../server/comms/ClientManager.java | 39 ++++++++++++++++++- .../progressia/server/comms/ClientPlayer.java | 11 ++++++ .../server/world/tasks/AddTile.java | 6 +++ .../server/world/tasks/CachedWorldChange.java | 18 ++++++++- .../server/world/tasks/ChangeEntity.java | 2 +- .../server/world/tasks/RemoveTile.java | 6 +++ .../server/world/tasks/SetBlock.java | 6 +++ .../progressia/test/TestChunkSender.java | 4 +- 8 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java index 6ab3852..1796977 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; +import glm.vec._3.i.Vec3i; import gnu.trove.TCollections; import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; @@ -68,6 +69,10 @@ public class ClientManager { getServer().getPlayerManager().getPlayers().add(player); for (ChunkData chunk : server.getWorld().getData().getChunks()) { + if (!client.canSeeChunk(chunk.getPosition())) { + continue; + } + PacketLoadChunk packet = new PacketLoadChunk("Core:LoadChunk"); packet.getPosition().set( chunk.getPosition().x, @@ -91,7 +96,11 @@ public class ClientManager { clientsById.remove(client.getId()); } - public void broadcastGamePacket(Packet packet) { + /** + * Sends the provided packet to all connected player clients. + * @param packet the packet to broadcast + */ + public void broadcastToAllPlayers(Packet packet) { getClients().forEach(c -> { if (c.getState() != State.CONNECTED) return; if (!(c instanceof ClientPlayer)) return; @@ -99,6 +108,34 @@ public class ClientManager { }); } + /** + * Sends the provided packet to all connected player clients that can see the chunk identified by {@code chunkPos}. + * @param packet the packet to broadcast + * @param chunkPos the chunk coordinates of the chunk that must be visible + */ + public void broadcastLocal(Packet packet, Vec3i chunkPos) { + getClients().forEach(c -> { + if (c.getState() != State.CONNECTED) return; + if (!(c instanceof ClientPlayer)) return; + if (!((ClientPlayer) c).canSeeChunk(chunkPos)) return; + c.sendPacket(packet); + }); + } + + /** + * Sends the provided packet to all connected player clients that can see the entity identified by {@code entityId}. + * @param packet the packet to broadcast + * @param entityId the ID of the entity that must be visible + */ + public void broadcastLocal(Packet packet, long entityId) { + getClients().forEach(c -> { + if (c.getState() != State.CONNECTED) return; + if (!(c instanceof ClientPlayer)) return; + if (!((ClientPlayer) c).canSeeEntity(entityId)) return; + c.sendPacket(packet); + }); + } + public Collection getClients() { return clients; } diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java index 3eb14e1..d49addd 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java @@ -1,5 +1,8 @@ package ru.windcorp.progressia.server.comms; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.test.TestContent; + public abstract class ClientPlayer extends Client { public ClientPlayer(int id) { @@ -7,5 +10,13 @@ public abstract class ClientPlayer extends Client { } public abstract String getLogin(); + + public boolean canSeeChunk(Vec3i chunkPos) { + return true; + } + + public boolean canSeeEntity(long entityId) { + return entityId == TestContent.PLAYER_ENTITY_ID; + } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java index 4402999..28d4558 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java @@ -45,6 +45,12 @@ class AddTile extends CachedWorldChange { Coordinates.convertInWorldToChunk(blockInWorld, output); } + @Override + protected Vec3i getAffectedChunk(Vec3i output) { + getRelevantChunk(output); + return output; + } + @Override public void dispose() { super.dispose(); diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedWorldChange.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedWorldChange.java index be587f8..ee965c7 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedWorldChange.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedWorldChange.java @@ -2,7 +2,9 @@ package ru.windcorp.progressia.server.world.tasks; import java.util.function.Consumer; +import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; +import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.server.Server; @@ -24,7 +26,21 @@ public abstract class CachedWorldChange extends CachedChange { @Override public void affect(Server server) { affectCommon(server.getWorld().getData()); - server.getClientManager().broadcastGamePacket(packet); + + Vec3i v = Vectors.grab3i(); + Vec3i chunkPos = getAffectedChunk(v); + + if (chunkPos == null) { + server.getClientManager().broadcastToAllPlayers(packet); + } else { + server.getClientManager().broadcastLocal(packet, chunkPos); + } + + Vectors.release(chunkPos); + } + + protected Vec3i getAffectedChunk(Vec3i output) { + return null; } /** diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/ChangeEntity.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/ChangeEntity.java index f671914..651bc83 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/ChangeEntity.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/ChangeEntity.java @@ -50,7 +50,7 @@ class ChangeEntity extends CachedChange { CrashReports.report(e, "Could not write entity %s", entity); } - server.getClientManager().broadcastGamePacket(packet); + server.getClientManager().broadcastLocal(packet, entity.getChunkCoords(null)); } @Override diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java index 7601a83..d33f80f 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java @@ -40,5 +40,11 @@ class RemoveTile extends CachedWorldChange { public void getRelevantChunk(Vec3i output) { Coordinates.convertInWorldToChunk(blockInWorld, output); } + + @Override + protected Vec3i getAffectedChunk(Vec3i output) { + getRelevantChunk(output); + return output; + } } \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/SetBlock.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/SetBlock.java index f34694c..44329ac 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/SetBlock.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/SetBlock.java @@ -36,6 +36,12 @@ class SetBlock extends CachedWorldChange { Coordinates.convertInWorldToChunk(blockInWorld, output); } + @Override + protected Vec3i getAffectedChunk(Vec3i output) { + getRelevantChunk(output); + return output; + } + @Override public void dispose() { super.dispose(); diff --git a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java index 672db8f..801c3d8 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java +++ b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java @@ -37,7 +37,7 @@ public class TestChunkSender implements WorldDataListener { CrashReports.report(e, "TestChunkSender fjcked up. javahorse stupid"); } - server.getClientManager().broadcastGamePacket(packet); + server.getClientManager().broadcastLocal(packet, chunk.getPosition()); tmp_sendPlayerIfPossible(world, chunk); } @@ -48,7 +48,7 @@ public class TestChunkSender implements WorldDataListener { if (Glm.equals(e.getChunkCoords(null), chunk.getPosition())) { System.out.printf("TestChunkSender: player found in (%d; %d; %d)\n", e.getChunkCoords(null).x, e.getChunkCoords(null).y, e.getChunkCoords(null).z); - server.getClientManager().broadcastGamePacket(new PacketSetLocalPlayer(e.getEntityId())); + server.getClientManager().broadcastToAllPlayers(new PacketSetLocalPlayer(e.getEntityId())); } } From eaea6fdad9d2572b397d51cba977e2b52824681e Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sat, 26 Dec 2020 18:02:17 +0300 Subject: [PATCH 04/10] Refactored Packets and most TickerTasks --- .../comms/DefaultClientCommsListener.java | 8 +-- .../common/comms/controls/PacketControl.java | 15 ++++ .../common/comms/packets/Packet.java | 12 +++- .../common/comms/packets/PacketLoadChunk.java | 38 ----------- .../comms/packets/PacketSetLocalPlayer.java | 24 ------- .../common/world/PacketChunkChange.java | 13 ++++ .../common/world/PacketLoadChunk.java | 68 +++++++++++++++++++ .../common/world/PacketSetLocalPlayer.java | 39 +++++++++++ .../packets => world}/PacketWorldChange.java | 4 +- .../common/world/block/PacketSetBlock.java | 56 +++++++++++++++ .../world/entity/PacketEntityChange.java | 25 ++++++- .../common/world/tile/PacketAddTile.java | 61 +++++++++++++++++ .../common/world/tile/PacketRemoveTile.java | 61 +++++++++++++++++ .../server/comms/ClientManager.java | 8 ++- .../progressia/server/comms/ClientPlayer.java | 3 +- .../server/world/tasks/AddTile.java | 53 +-------------- .../server/world/tasks/CachedChunkChange.java | 19 ++++++ .../server/world/tasks/CachedWorldChange.java | 38 +++++------ .../server/world/tasks/ChangeEntity.java | 18 +---- .../server/world/tasks/RemoveTile.java | 43 +----------- .../server/world/tasks/SetBlock.java | 44 +----------- .../server/world/tasks/WorldAccessor.java | 6 +- .../progressia/test/TestChunkSender.java | 9 ++- 23 files changed, 416 insertions(+), 249 deletions(-) delete mode 100644 src/main/java/ru/windcorp/progressia/common/comms/packets/PacketLoadChunk.java delete mode 100644 src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/PacketChunkChange.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/PacketLoadChunk.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/PacketSetLocalPlayer.java rename src/main/java/ru/windcorp/progressia/common/{comms/packets => world}/PacketWorldChange.java (58%) create mode 100644 src/main/java/ru/windcorp/progressia/common/world/block/PacketSetBlock.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/tile/PacketAddTile.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/tile/PacketRemoveTile.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/tasks/CachedChunkChange.java diff --git a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java index f2eb76a..b64798e 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java @@ -7,9 +7,9 @@ import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.graphics.world.EntityAnchor; import ru.windcorp.progressia.common.comms.CommsListener; import ru.windcorp.progressia.common.comms.packets.Packet; -import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer; -import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; import ru.windcorp.progressia.common.util.crash.CrashReports; +import ru.windcorp.progressia.common.world.PacketSetLocalPlayer; +import ru.windcorp.progressia.common.world.PacketWorldChange; import ru.windcorp.progressia.common.world.entity.EntityData; // TODO refactor with no mercy @@ -34,14 +34,14 @@ public class DefaultClientCommsListener implements CommsListener { private void setLocalPlayer(PacketSetLocalPlayer packet) { EntityData entity = getClient().getWorld().getData().getEntity( - packet.getLocalPlayerEntityId() + packet.getEntityId() ); if (entity == null) { CrashReports.report( null, "Player entity with ID %s not found", - new String(StringUtil.toFullHex(packet.getLocalPlayerEntityId())) + new String(StringUtil.toFullHex(packet.getEntityId())) ); } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java index e9b37d3..98448be 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java @@ -1,6 +1,11 @@ package ru.windcorp.progressia.common.comms.controls; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.common.world.DecodingException; public class PacketControl extends Packet { @@ -15,4 +20,14 @@ public class PacketControl extends Packet { return control; } + @Override + public void read(DataInput input) throws IOException, DecodingException { + // TODO implement controls + } + + @Override + public void write(DataOutput output) throws IOException { + // implement controls + } + } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java index 425a64b..941b157 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java @@ -1,11 +1,19 @@ package ru.windcorp.progressia.common.comms.packets; -import ru.windcorp.progressia.common.util.namespaces.Namespaced; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; -public class Packet extends Namespaced { +import ru.windcorp.progressia.common.util.namespaces.Namespaced; +import ru.windcorp.progressia.common.world.DecodingException; + +public abstract class Packet extends Namespaced { public Packet(String id) { super(id); } + + public abstract void read(DataInput input) throws IOException, DecodingException; + public abstract void write(DataOutput output) throws IOException; } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketLoadChunk.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketLoadChunk.java deleted file mode 100644 index e44f8da..0000000 --- a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketLoadChunk.java +++ /dev/null @@ -1,38 +0,0 @@ -package ru.windcorp.progressia.common.comms.packets; - -import java.io.IOException; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.io.ChunkIO; -import ru.windcorp.progressia.common.util.DataBuffer; -import ru.windcorp.progressia.common.util.crash.CrashReports; -import ru.windcorp.progressia.common.world.DecodingException; -import ru.windcorp.progressia.common.world.WorldData; - -public class PacketLoadChunk extends PacketWorldChange { - - private final DataBuffer data = new DataBuffer(); - private final Vec3i position = new Vec3i(); - - public PacketLoadChunk(String id) { - super(id); - } - - @Override - public void apply(WorldData world) { - try { - world.addChunk(ChunkIO.load(world, position, data.getInputStream())); - } catch (DecodingException | IOException e) { - CrashReports.report(e, "Could not load chunk"); - } - } - - public Vec3i getPosition() { - return position; - } - - public DataBuffer getData() { - return data; - } - -} diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java deleted file mode 100644 index c45ef9b..0000000 --- a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.windcorp.progressia.common.comms.packets; - -public class PacketSetLocalPlayer extends Packet { - - private long localPlayerEntityId; - - public PacketSetLocalPlayer(long entityId) { - this("Core:SetLocalPlayer", entityId); - } - - protected PacketSetLocalPlayer(String id, long entityId) { - super(id); - this.localPlayerEntityId = entityId; - } - - public long getLocalPlayerEntityId() { - return localPlayerEntityId; - } - - public void setLocalPlayerEntityId(long localPlayerEntityId) { - this.localPlayerEntityId = localPlayerEntityId; - } - -} diff --git a/src/main/java/ru/windcorp/progressia/common/world/PacketChunkChange.java b/src/main/java/ru/windcorp/progressia/common/world/PacketChunkChange.java new file mode 100644 index 0000000..2cd1a96 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketChunkChange.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.common.world; + +import glm.vec._3.i.Vec3i; + +public abstract class PacketChunkChange extends PacketWorldChange { + + public PacketChunkChange(String id) { + super(id); + } + + public abstract void getAffectedChunk(Vec3i output); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/PacketLoadChunk.java b/src/main/java/ru/windcorp/progressia/common/world/PacketLoadChunk.java new file mode 100644 index 0000000..74a9e3b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketLoadChunk.java @@ -0,0 +1,68 @@ +package ru.windcorp.progressia.common.world; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.io.ChunkIO; +import ru.windcorp.progressia.common.util.DataBuffer; +import ru.windcorp.progressia.common.util.crash.CrashReports; + +public class PacketLoadChunk extends PacketChunkChange { + + private final DataBuffer data = new DataBuffer(); + private final Vec3i position = new Vec3i(); + + public PacketLoadChunk(String id) { + super(id); + } + + public void set(ChunkData chunk) { + this.position.set(chunk.getX(), chunk.getY(), chunk.getZ()); + + try { + ChunkIO.save(chunk, this.data.getOutputStream()); + } catch (IOException e) { + // Impossible + } + } + + @Override + public void read(DataInput input) throws IOException { + this.position.set(input.readInt(), input.readInt(), input.readInt()); + this.data.fill(input, input.readInt()); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeInt(this.position.x); + output.writeInt(this.position.y); + output.writeInt(this.position.z); + output.writeInt(this.data.getSize()); + this.data.flush(output); + } + + @Override + public void apply(WorldData world) { + try { + world.addChunk(ChunkIO.load(world, position, data.getInputStream())); + } catch (DecodingException | IOException e) { + CrashReports.report(e, "Could not load chunk"); + } + } + + @Override + public void getAffectedChunk(Vec3i output) { + output.set(getPosition().x, getPosition().y, getPosition().z); + } + + public Vec3i getPosition() { + return position; + } + + public DataBuffer getData() { + return data; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/PacketSetLocalPlayer.java b/src/main/java/ru/windcorp/progressia/common/world/PacketSetLocalPlayer.java new file mode 100644 index 0000000..4eda94f --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketSetLocalPlayer.java @@ -0,0 +1,39 @@ +package ru.windcorp.progressia.common.world; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import ru.windcorp.progressia.common.comms.packets.Packet; + +public class PacketSetLocalPlayer extends Packet { + + private long entityId; + + public PacketSetLocalPlayer() { + this("Core:SetLocalPlayer"); + } + + protected PacketSetLocalPlayer(String id) { + super(id); + } + + public void set(long entityId) { + this.entityId = entityId; + } + + @Override + public void read(DataInput input) throws IOException, DecodingException { + this.entityId = input.readLong(); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeLong(this.entityId); + } + + public long getEntityId() { + return entityId; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java b/src/main/java/ru/windcorp/progressia/common/world/PacketWorldChange.java similarity index 58% rename from src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java rename to src/main/java/ru/windcorp/progressia/common/world/PacketWorldChange.java index 929902a..350c3f9 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketWorldChange.java @@ -1,6 +1,6 @@ -package ru.windcorp.progressia.common.comms.packets; +package ru.windcorp.progressia.common.world; -import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.comms.packets.Packet; public abstract class PacketWorldChange extends Packet { diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/PacketSetBlock.java b/src/main/java/ru/windcorp/progressia/common/world/block/PacketSetBlock.java new file mode 100644 index 0000000..0eefa71 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/block/PacketSetBlock.java @@ -0,0 +1,56 @@ +package ru.windcorp.progressia.common.world.block; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.DecodingException; +import ru.windcorp.progressia.common.world.PacketChunkChange; +import ru.windcorp.progressia.common.world.WorldData; + +public class PacketSetBlock extends PacketChunkChange { + + private String id; + private final Vec3i blockInWorld = new Vec3i(); + + public PacketSetBlock() { + this("Core:SetBlock"); + } + + protected PacketSetBlock(String id) { + super(id); + } + + public void set(BlockData block, Vec3i blockInWorld) { + this.id = block.getId(); + this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + } + + @Override + public void read(DataInput input) throws IOException, DecodingException { + this.id = input.readUTF(); + this.blockInWorld.set(input.readInt(), input.readInt(), input.readInt()); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeUTF(this.id); + output.writeInt(this.blockInWorld.x); + output.writeInt(this.blockInWorld.y); + output.writeInt(this.blockInWorld.z); + } + + @Override + public void apply(WorldData world) { + BlockData block = BlockDataRegistry.getInstance().get(id); + world.setBlock(blockInWorld, block, true); + } + + @Override + public void getAffectedChunk(Vec3i output) { + Coordinates.convertInWorldToChunk(this.blockInWorld, output); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java index 569db84..bb07297 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java @@ -4,10 +4,11 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; import ru.windcorp.progressia.common.state.IOContext; import ru.windcorp.progressia.common.util.DataBuffer; import ru.windcorp.progressia.common.util.crash.CrashReports; +import ru.windcorp.progressia.common.world.DecodingException; +import ru.windcorp.progressia.common.world.PacketWorldChange; import ru.windcorp.progressia.common.world.WorldData; public class PacketEntityChange extends PacketWorldChange { @@ -42,6 +43,28 @@ public class PacketEntityChange extends PacketWorldChange { public DataOutput getWriter() { return buffer.getWriter(); } + + public void set(EntityData entity) { + this.entityId = entity.getEntityId(); + try { + entity.write(this.buffer.getWriter(), IOContext.COMMS); + } catch (IOException e) { + CrashReports.report(e, "Entity could not be written"); + } + } + + @Override + public void read(DataInput input) throws IOException, DecodingException { + this.entityId = input.readLong(); + this.buffer.fill(input, input.readInt()); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeLong(this.entityId); + output.writeInt(this.buffer.getSize()); + this.buffer.flush(output); + } @Override public void apply(WorldData world) { diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/PacketAddTile.java b/src/main/java/ru/windcorp/progressia/common/world/tile/PacketAddTile.java new file mode 100644 index 0000000..5cc12ee --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/PacketAddTile.java @@ -0,0 +1,61 @@ +package ru.windcorp.progressia.common.world.tile; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.DecodingException; +import ru.windcorp.progressia.common.world.PacketChunkChange; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public class PacketAddTile extends PacketChunkChange { + + private String id; + private final Vec3i blockInWorld = new Vec3i(); + private BlockFace face; + + public PacketAddTile() { + this("Core:AddTile"); + } + + protected PacketAddTile(String id) { + super(id); + } + + public void set(TileData tile, Vec3i blockInWorld, BlockFace face) { + this.id = tile.getId(); + this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + this.face = face; + } + + @Override + public void read(DataInput input) throws IOException, DecodingException { + this.id = input.readUTF(); + this.blockInWorld.set(input.readInt(), input.readInt(), input.readInt()); + this.face = BlockFace.getFaces().get(input.readByte()); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeUTF(this.id); + output.writeInt(this.blockInWorld.x); + output.writeInt(this.blockInWorld.y); + output.writeInt(this.blockInWorld.z); + output.writeByte(this.face.getId()); + } + + @Override + public void apply(WorldData world) { + TileData tile = TileDataRegistry.getInstance().get(id); + world.getTiles(blockInWorld, face).add(tile); + } + + @Override + public void getAffectedChunk(Vec3i output) { + Coordinates.convertInWorldToChunk(this.blockInWorld, output); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/PacketRemoveTile.java b/src/main/java/ru/windcorp/progressia/common/world/tile/PacketRemoveTile.java new file mode 100644 index 0000000..b7d9e0b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/PacketRemoveTile.java @@ -0,0 +1,61 @@ +package ru.windcorp.progressia.common.world.tile; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.DecodingException; +import ru.windcorp.progressia.common.world.PacketChunkChange; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public class PacketRemoveTile extends PacketChunkChange { + + private final Vec3i blockInWorld = new Vec3i(); + private BlockFace face; + private int tag; + + public PacketRemoveTile() { + this("Core:RemoveTile"); + } + + protected PacketRemoveTile(String id) { + super(id); + } + + public void set(Vec3i blockInWorld, BlockFace face, int tag) { + this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + this.face = face; + this.tag = tag; + } + + @Override + public void read(DataInput input) throws IOException, DecodingException { + this.blockInWorld.set(input.readInt(), input.readInt(), input.readInt()); + this.face = BlockFace.getFaces().get(input.readByte()); + this.tag = input.readInt(); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeInt(this.blockInWorld.x); + output.writeInt(this.blockInWorld.y); + output.writeInt(this.blockInWorld.z); + output.writeByte(this.face.getId()); + output.writeInt(this.tag); + } + + @Override + public void apply(WorldData world) { + TileDataStack stack = world.getTiles(blockInWorld, face); + stack.remove(stack.getIndexByTag(tag)); + } + + @Override + public void getAffectedChunk(Vec3i output) { + Coordinates.convertInWorldToChunk(this.blockInWorld, output); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java index 1796977..54510ad 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -11,11 +11,11 @@ import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; import ru.windcorp.progressia.common.comms.CommsChannel.State; import ru.windcorp.progressia.common.comms.packets.Packet; -import ru.windcorp.progressia.common.comms.packets.PacketLoadChunk; -import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer; import ru.windcorp.progressia.common.io.ChunkIO; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.PacketLoadChunk; +import ru.windcorp.progressia.common.world.PacketSetLocalPlayer; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.server.Player; import ru.windcorp.progressia.server.Server; @@ -88,7 +88,9 @@ public class ClientManager { client.sendPacket(packet); } - client.sendPacket(new PacketSetLocalPlayer(entity.getEntityId())); + PacketSetLocalPlayer packet = new PacketSetLocalPlayer(); + packet.set(entity.getEntityId()); + client.sendPacket(packet); } public void disconnectClient(Client client) { diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java index d49addd..d8271a9 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java @@ -1,7 +1,6 @@ package ru.windcorp.progressia.server.comms; import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.test.TestContent; public abstract class ClientPlayer extends Client { @@ -16,7 +15,7 @@ public abstract class ClientPlayer extends Client { } public boolean canSeeEntity(long entityId) { - return entityId == TestContent.PLAYER_ENTITY_ID; + return true; } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java index 28d4558..d058ac9 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java @@ -2,59 +2,12 @@ package ru.windcorp.progressia.server.world.tasks; import java.util.function.Consumer; -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.world.Coordinates; -import ru.windcorp.progressia.common.world.WorldData; -import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.common.world.tile.TileData; -import ru.windcorp.progressia.common.world.tile.TileDataStack; +import ru.windcorp.progressia.common.world.tile.PacketAddTile; -class AddTile extends CachedWorldChange { - - private final Vec3i blockInWorld = new Vec3i(); - private BlockFace face; - private TileData tile; +class AddTile extends CachedChunkChange { public AddTile(Consumer disposer) { - super(disposer, "Core:AddTile"); - } - - public void initialize( - Vec3i position, BlockFace face, - TileData tile - ) { - if (this.tile != null) - throw new IllegalStateException("Payload is not null. Current: " + this.tile + "; requested: " + tile); - - this.blockInWorld.set(position.x, position.y, position.z); - this.face = face; - this.tile = tile; - } - - @Override - protected void affectCommon(WorldData world) { - TileDataStack tiles = world - .getChunkByBlock(blockInWorld) - .getTiles(Coordinates.convertInWorldToInChunk(blockInWorld, null), face); - - tiles.add(tile); - } - - @Override - public void getRelevantChunk(Vec3i output) { - Coordinates.convertInWorldToChunk(blockInWorld, output); - } - - @Override - protected Vec3i getAffectedChunk(Vec3i output) { - getRelevantChunk(output); - return output; - } - - @Override - public void dispose() { - super.dispose(); - this.tile = null; + super(disposer, new PacketAddTile()); } } \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedChunkChange.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedChunkChange.java new file mode 100644 index 0000000..e12e873 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedChunkChange.java @@ -0,0 +1,19 @@ +package ru.windcorp.progressia.server.world.tasks; + +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.PacketChunkChange; + +public abstract class CachedChunkChange

extends CachedWorldChange

{ + + public CachedChunkChange(Consumer disposer, P packet) { + super(disposer, packet); + } + + @Override + public void getRelevantChunk(Vec3i output) { + getPacket().getAffectedChunk(output); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedWorldChange.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedWorldChange.java index ee965c7..e3929a7 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedWorldChange.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/CachedWorldChange.java @@ -3,30 +3,30 @@ package ru.windcorp.progressia.server.world.tasks; import java.util.function.Consumer; import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; import ru.windcorp.progressia.common.util.Vectors; -import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.PacketWorldChange; import ru.windcorp.progressia.server.Server; -public abstract class CachedWorldChange extends CachedChange { +public abstract class CachedWorldChange

extends CachedChange { - private final PacketWorldChange packet; + private final P packet; - public CachedWorldChange(Consumer disposer, String packetId) { + public CachedWorldChange(Consumer disposer, P packet) { super(disposer); - - this.packet = new PacketWorldChange(packetId) { - @Override - public void apply(WorldData world) { - affectCommon(world); - } - }; + this.packet = packet; } @Override public void affect(Server server) { - affectCommon(server.getWorld().getData()); - + affectLocal(server); + sendPacket(server); + } + + protected void affectLocal(Server server) { + packet.apply(server.getWorld().getData()); + } + + protected void sendPacket(Server server) { Vec3i v = Vectors.grab3i(); Vec3i chunkPos = getAffectedChunk(v); @@ -38,15 +38,13 @@ public abstract class CachedWorldChange extends CachedChange { Vectors.release(chunkPos); } - + protected Vec3i getAffectedChunk(Vec3i output) { return null; } - /** - * Invoked by both Change and Packet. - * @param world the world to affect - */ - protected abstract void affectCommon(WorldData world); + public P getPacket() { + return packet; + } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/ChangeEntity.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/ChangeEntity.java index 651bc83..a813f15 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/ChangeEntity.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/ChangeEntity.java @@ -1,11 +1,8 @@ package ru.windcorp.progressia.server.world.tasks; -import java.io.IOException; import java.util.function.Consumer; import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.state.IOContext; -import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.PacketEntityChange; import ru.windcorp.progressia.server.Server; @@ -31,26 +28,15 @@ class ChangeEntity extends CachedChange { this.entity = entity; this.change = change; - packet.setEntityId(entity.getEntityId()); - try { - entity.write(packet.getWriter(), IOContext.COMMS); // TODO wtf is this... (see whole file) - } catch (IOException e) { - CrashReports.report(e, "Could not write entity %s", entity); - } } @SuppressWarnings("unchecked") @Override public void affect(Server server) { ((StateChange) change).change(entity); - - try { - entity.write(packet.getWriter(), IOContext.COMMS); // ...and this doing at the same time? - javapony at 1 AM - } catch (IOException e) { - CrashReports.report(e, "Could not write entity %s", entity); - } + packet.set(entity); - server.getClientManager().broadcastLocal(packet, entity.getChunkCoords(null)); + server.getClientManager().broadcastLocal(packet, entity.getEntityId()); } @Override diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java index d33f80f..02c01ba 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/RemoveTile.java @@ -2,49 +2,12 @@ package ru.windcorp.progressia.server.world.tasks; import java.util.function.Consumer; -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.world.Coordinates; -import ru.windcorp.progressia.common.world.WorldData; -import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.common.world.tile.TileDataStack; +import ru.windcorp.progressia.common.world.tile.PacketRemoveTile; -class RemoveTile extends CachedWorldChange { - - private final Vec3i blockInWorld = new Vec3i(); - private BlockFace face; - private int tag; +class RemoveTile extends CachedChunkChange { public RemoveTile(Consumer disposer) { - super(disposer, "Core:RemoveTile"); - } - - public void initialize( - Vec3i position, BlockFace face, - int tag - ) { - this.blockInWorld.set(position.x, position.y, position.z); - this.face = face; - this.tag = tag; - } - - @Override - protected void affectCommon(WorldData world) { - TileDataStack tiles = world - .getChunkByBlock(blockInWorld) - .getTiles(Coordinates.convertInWorldToInChunk(blockInWorld, null), face); - - tiles.remove(tiles.getIndexByTag(tag)); - } - - @Override - public void getRelevantChunk(Vec3i output) { - Coordinates.convertInWorldToChunk(blockInWorld, output); - } - - @Override - protected Vec3i getAffectedChunk(Vec3i output) { - getRelevantChunk(output); - return output; + super(disposer, new PacketRemoveTile()); } } \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/SetBlock.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/SetBlock.java index 44329ac..e595f2a 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/SetBlock.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/SetBlock.java @@ -2,50 +2,12 @@ package ru.windcorp.progressia.server.world.tasks; import java.util.function.Consumer; -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.world.Coordinates; -import ru.windcorp.progressia.common.world.WorldData; -import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.block.PacketSetBlock; -class SetBlock extends CachedWorldChange { - - private final Vec3i blockInWorld = new Vec3i(); - private BlockData block; +class SetBlock extends CachedChunkChange { public SetBlock(Consumer disposer) { - super(disposer, "Core:SetBlock"); - } - - public void initialize(Vec3i blockInWorld, BlockData block) { - if (this.block != null) - throw new IllegalStateException("Payload is not null. Current: " + this.block + "; requested: " + block); - - this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); - this.block = block; - } - - @Override - protected void affectCommon(WorldData world) { - world - .getChunkByBlock(blockInWorld) - .setBlock(Coordinates.convertInWorldToInChunk(blockInWorld, null), block, true); - } - - @Override - public void getRelevantChunk(Vec3i output) { - Coordinates.convertInWorldToChunk(blockInWorld, output); - } - - @Override - protected Vec3i getAffectedChunk(Vec3i output) { - getRelevantChunk(output); - return output; - } - - @Override - public void dispose() { - super.dispose(); - this.block = null; + super(disposer, new PacketSetBlock()); } } \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/WorldAccessor.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/WorldAccessor.java index ff073fc..85b6fa5 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/WorldAccessor.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/WorldAccessor.java @@ -38,7 +38,7 @@ public class WorldAccessor { public void setBlock(Vec3i blockInWorld, BlockData block) { SetBlock change = cache.grab(SetBlock.class); - change.initialize(blockInWorld, block); + change.getPacket().set(block, blockInWorld); server.requestChange(change); } @@ -48,7 +48,7 @@ public class WorldAccessor { public void addTile(Vec3i blockInWorld, BlockFace face, TileData tile) { AddTile change = cache.grab(AddTile.class); - change.initialize(blockInWorld, face, tile); + change.getPacket().set(tile, blockInWorld, face); server.requestChange(change); } @@ -58,7 +58,7 @@ public class WorldAccessor { public void removeTile(Vec3i blockInWorld, BlockFace face, int tag) { RemoveTile change = cache.grab(RemoveTile.class); - change.initialize(blockInWorld, face, tag); + change.getPacket().set(blockInWorld, face, tag); server.requestChange(change); } diff --git a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java index 801c3d8..b267206 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java +++ b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java @@ -3,11 +3,11 @@ package ru.windcorp.progressia.test; import java.io.IOException; import glm.Glm; -import ru.windcorp.progressia.common.comms.packets.PacketLoadChunk; -import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer; import ru.windcorp.progressia.common.io.ChunkIO; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.PacketLoadChunk; +import ru.windcorp.progressia.common.world.PacketSetLocalPlayer; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldDataListener; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -48,7 +48,10 @@ public class TestChunkSender implements WorldDataListener { if (Glm.equals(e.getChunkCoords(null), chunk.getPosition())) { System.out.printf("TestChunkSender: player found in (%d; %d; %d)\n", e.getChunkCoords(null).x, e.getChunkCoords(null).y, e.getChunkCoords(null).z); - server.getClientManager().broadcastToAllPlayers(new PacketSetLocalPlayer(e.getEntityId())); + + PacketSetLocalPlayer packet = new PacketSetLocalPlayer(); + packet.set(e.getEntityId()); + server.getClientManager().broadcastToAllPlayers(packet); } } From cde854346c810f80df3f10f87c006d06016904d7 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Mon, 28 Dec 2020 11:26:44 +0300 Subject: [PATCH 05/10] First implementation of dynamic chunk loading. Super buggy, WIP --- .../progressia/client/world/ChunkRender.java | 6 +- .../progressia/client/world/WorldRender.java | 20 +- .../common/world/PacketRevokeChunk.java | 56 ++++++ ...ketLoadChunk.java => PacketSendChunk.java} | 8 +- .../progressia/common/world/WorldData.java | 4 +- .../common/world/generic/ChunkSets.java | 22 +++ .../common/world/generic/GenericWorld.java | 4 +- .../progressia/server/ChunkLoadManager.java | 83 -------- .../progressia/server/ChunkManager.java | 180 ++++++++++++++++++ .../ru/windcorp/progressia/server/Player.java | 3 + .../progressia/server/PlayerManager.java | 10 +- .../ru/windcorp/progressia/server/Server.java | 16 +- .../server/comms/ClientManager.java | 37 +--- .../progressia/server/comms/ClientPlayer.java | 24 ++- .../progressia/server/world/WorldLogic.java | 2 - .../progressia/test/TestChunkSender.java | 56 ++---- .../windcorp/progressia/test/TestContent.java | 8 +- .../progressia/test/TestPlayerControls.java | 8 + 18 files changed, 365 insertions(+), 182 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/world/PacketRevokeChunk.java rename src/main/java/ru/windcorp/progressia/common/world/{PacketLoadChunk.java => PacketSendChunk.java} (90%) create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java delete mode 100644 src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java create mode 100644 src/main/java/ru/windcorp/progressia/server/ChunkManager.java diff --git a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java index 9c512c5..94af7f8 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java @@ -107,15 +107,15 @@ implements GenericChunk< return data; } - public void markForUpdate() { + public synchronized void markForUpdate() { this.needsUpdate = true; } - public boolean needsUpdate() { + public synchronized boolean needsUpdate() { return needsUpdate; } - public void render(ShapeRenderHelper renderer) { + public synchronized void render(ShapeRenderHelper renderer) { if (model == null || needsUpdate()) { buildModel(); } diff --git a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java index 71acda9..14f99d4 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java @@ -49,7 +49,8 @@ implements GenericWorld< private final WorldData data; - private final Map chunks = new HashMap<>(); + private final Map chunks = + Collections.synchronizedMap(new HashMap<>()); private final Map entityModels = Collections.synchronizedMap(new WeakHashMap<>()); @@ -94,23 +95,18 @@ implements GenericWorld< } public void render(ShapeRenderHelper renderer) { - for (ChunkRender chunk : getChunks()) { - chunk.render(renderer); - } - + getChunks().forEach(chunk -> chunk.render(renderer)); renderEntities(renderer); } private void renderEntities(ShapeRenderHelper renderer) { FaceCulling.push(false); - for (ChunkRender chunk : getChunks()) { - chunk.getData().forEachEntity(entity -> { - renderer.pushTransform().translate(entity.getPosition()); - getEntityRenderable(entity).render(renderer); - renderer.popTransform(); - }); - } + getData().forEachEntity(entity -> { + renderer.pushTransform().translate(entity.getPosition()); + getEntityRenderable(entity).render(renderer); + renderer.popTransform(); + }); FaceCulling.pop(); } diff --git a/src/main/java/ru/windcorp/progressia/common/world/PacketRevokeChunk.java b/src/main/java/ru/windcorp/progressia/common/world/PacketRevokeChunk.java new file mode 100644 index 0000000..e923259 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketRevokeChunk.java @@ -0,0 +1,56 @@ +package ru.windcorp.progressia.common.world; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import glm.vec._3.i.Vec3i; + +public class PacketRevokeChunk extends PacketChunkChange { + + private final Vec3i position = new Vec3i(); + + public PacketRevokeChunk() { + this("Core:RevokeChunk"); + } + + protected PacketRevokeChunk(String id) { + super(id); + } + + public void set(Vec3i chunkPos) { + this.position.set(chunkPos.x, chunkPos.y, chunkPos.z); + } + + @Override + public void read(DataInput input) throws IOException { + this.position.set(input.readInt(), input.readInt(), input.readInt()); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeInt(this.position.x); + output.writeInt(this.position.y); + output.writeInt(this.position.z); + } + + @Override + public void apply(WorldData world) { + synchronized (world) { + ChunkData chunk = world.getChunk(position); + if (chunk != null) { + world.removeChunk(chunk); + } + } + } + + @Override + public void getAffectedChunk(Vec3i output) { + output.set(getPosition().x, getPosition().y, getPosition().z); + } + + public Vec3i getPosition() { + return position; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/PacketLoadChunk.java b/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java similarity index 90% rename from src/main/java/ru/windcorp/progressia/common/world/PacketLoadChunk.java rename to src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java index 74a9e3b..a0fe418 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/PacketLoadChunk.java +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java @@ -9,12 +9,16 @@ import ru.windcorp.progressia.common.io.ChunkIO; import ru.windcorp.progressia.common.util.DataBuffer; import ru.windcorp.progressia.common.util.crash.CrashReports; -public class PacketLoadChunk extends PacketChunkChange { +public class PacketSendChunk extends PacketChunkChange { private final DataBuffer data = new DataBuffer(); private final Vec3i position = new Vec3i(); + + public PacketSendChunk() { + this("Core:SendChunk"); + } - public PacketLoadChunk(String id) { + protected PacketSendChunk(String id) { super(id); } diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index 01b7c1b..d96eaca 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -43,7 +43,7 @@ implements GenericWorld< TileDataStack, ChunkData, EntityData ->{ +> { private final ChunkMap chunksByPos = new LongBasedChunkMap<>( new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this) @@ -87,7 +87,7 @@ implements GenericWorld< } public void tmp_generate() { - final int size = 10; + final int size = 1; Vec3i cursor = new Vec3i(0, 0, 0); for (cursor.x = -(size / 2); cursor.x <= (size / 2); ++cursor.x) { diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java new file mode 100644 index 0000000..ba959cc --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java @@ -0,0 +1,22 @@ +package ru.windcorp.progressia.common.world.generic; + +import gnu.trove.impl.sync.TSynchronizedLongSet; +import gnu.trove.set.hash.TLongHashSet; + +public class ChunkSets { + + public static ChunkSet newHashSet() { + return new LongBasedChunkSet(new TLongHashSet()); + } + + public static ChunkSet newSyncHashSet(Object mutex) { + return new LongBasedChunkSet(new TSynchronizedLongSet(new TLongHashSet(), mutex)); + } + + public static ChunkSet newSyncHashSet() { + return new LongBasedChunkSet(new TSynchronizedLongSet(new TLongHashSet())); + } + + private ChunkSets() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java index 79746ed..54d13af 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java @@ -118,7 +118,9 @@ public interface GenericWorld< */ default void forEachEntity(Consumer action) { - getEntities().forEach(action); + synchronized (this) { // TODO HORRIBLY MUTILATE THE CORPSE OF TROVE4J so that gnu.trove.impl.sync.SynchronizedCollection.forEach is synchronized + getEntities().forEach(action); + } } default void forEachEntityIn(Vec3i min, Vec3i max, Consumer action) { diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java deleted file mode 100644 index 1b2c539..0000000 --- a/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java +++ /dev/null @@ -1,83 +0,0 @@ -package ru.windcorp.progressia.server; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -import glm.vec._3.i.Vec3i; -import gnu.trove.set.hash.TLongHashSet; -import ru.windcorp.progressia.common.world.ChunkData; -import ru.windcorp.progressia.common.world.generic.ChunkSet; -import ru.windcorp.progressia.common.world.generic.LongBasedChunkSet; -import ru.windcorp.progressia.test.TestContent; - -public class ChunkLoadManager { - - private final Server server; - - private final Collection> allChunkLoaders = - Collections.synchronizedCollection(new ArrayList<>()); - - private final ChunkSet requested = new LongBasedChunkSet(new TLongHashSet()); - private final ChunkSet toLoad = new LongBasedChunkSet(new TLongHashSet()); - private final ChunkSet toUnload = new LongBasedChunkSet(new TLongHashSet()); - - public ChunkLoadManager(Server server) { - this.server = server; - allChunkLoaders.add(server.getPlayerManager().getPlayers()); - } - - public void tick() { - gatherRequests(); - updateQueues(); - processQueues(); - } - - private void gatherRequests() { - requested.clear(); - - allChunkLoaders.forEach(collection -> { - collection.forEach(this::gatherRequests); - }); - } - - private void gatherRequests(ChunkLoader loader) { - loader.requestChunksToLoad(requested::add); - } - - private void updateQueues() { - ChunkSet loaded = getServer().getWorld().getData().getLoadedChunks(); - - toLoad.clear(); - toLoad.addAll(requested); - toLoad.removeAll(loaded); - - toUnload.clear(); - toUnload.addAll(loaded); - toUnload.removeAll(requested); - } - - private void processQueues() { - toUnload.forEach(this::unloadChunk); - toLoad.forEach(this::loadChunk); - } - - public Server getServer() { - return server; - } - - public void loadChunk(Vec3i pos) { - - ChunkData chunk = new ChunkData(pos, getServer().getWorld().getData()); - TestContent.generateChunk(chunk); - getServer().getWorld().getData().addChunk(chunk); - - } - - public void unloadChunk(Vec3i pos) { - - getServer().getWorld().getData().removeChunk(getServer().getWorld().getData().getChunk(pos)); - - } - -} diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java new file mode 100644 index 0000000..10a1c38 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java @@ -0,0 +1,180 @@ +package ru.windcorp.progressia.server; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.logging.log4j.LogManager; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.generic.ChunkSet; +import ru.windcorp.progressia.common.world.generic.ChunkSets; +import ru.windcorp.progressia.test.TestChunkSender; +import ru.windcorp.progressia.test.TestContent; + +public class ChunkManager { + + private class PlayerVision { + + private final ChunkSet visible = ChunkSets.newSyncHashSet(); + private final ChunkSet requested = ChunkSets.newHashSet(); + private final ChunkSet toSend = ChunkSets.newHashSet(); + private final ChunkSet toRevoke = ChunkSets.newHashSet(); + + public boolean isChunkVisible(Vec3i chunkPos) { + return visible.contains(chunkPos); + } + + public void gatherRequests(Player player) { + requested.clear(); + player.requestChunksToLoad(requested::add); + } + + public void updateQueues(Player player) { + toSend.clear(); + toSend.addAll(requested); + toSend.removeAll(visible); + toSend.retainAll(loaded); + + toRevoke.clear(); + toRevoke.addAll(visible); + toRevoke.removeIf(v -> loaded.contains(v) && requested.contains(v)); + } + + public void processQueues(Player player) { + toRevoke.forEach(chunkPos -> revokeChunk(player, chunkPos)); + toRevoke.clear(); + + toSend.forEach(chunkPos -> sendChunk(player, chunkPos)); + toSend.clear(); + } + + } + + private final Server server; + + private final ChunkSet loaded; + private final ChunkSet requested = ChunkSets.newHashSet(); + private final ChunkSet toLoad = ChunkSets.newHashSet(); + private final ChunkSet toUnload = ChunkSets.newHashSet(); + + // TODO replace with a normal Map managed by some sort of PlayerListener, weak maps are weak + private final Map visions = Collections.synchronizedMap(new WeakHashMap<>()); + + public ChunkManager(Server server) { + this.server = server; + this.loaded = server.getWorld().getData().getLoadedChunks(); + } + + public void tick() { + synchronized (getServer().getWorld().getData()) { + synchronized (visions) { + gatherRequests(); + updateQueues(); + processQueues(); + } + } + } + + private void gatherRequests() { + requested.clear(); + + server.getPlayerManager().getPlayers().forEach(p -> { + PlayerVision vision = getVision(p, true); + vision.gatherRequests(p); + requested.addAll(vision.requested); + }); + } + + private void updateQueues() { + toLoad.clear(); + toLoad.addAll(requested); + toLoad.removeAll(loaded); + + toUnload.clear(); + toUnload.addAll(loaded); + toUnload.removeAll(requested); + + visions.forEach((p, v) -> { + v.updateQueues(p); + }); + } + + private void processQueues() { + toUnload.forEach(this::unloadChunk); + toUnload.clear(); + toLoad.forEach(this::loadChunk); + toLoad.clear(); + + visions.forEach((p, v) -> { + v.processQueues(p); + }); + } + + private PlayerVision getVision(Player player, boolean createIfMissing) { + return createIfMissing ? visions.computeIfAbsent(player, k -> new PlayerVision()) : visions.get(player); + } + + public void loadChunk(Vec3i chunkPos) { + + LogManager.getLogger().info("Loading {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); + + WorldData world = getServer().getWorld().getData(); + + ChunkData chunk = new ChunkData(chunkPos, world); + TestContent.generateChunk(chunk); + world.addChunk(chunk); + + } + + public void unloadChunk(Vec3i chunkPos) { + + LogManager.getLogger().info("Unloading {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); + + WorldData world = getServer().getWorld().getData(); + + ChunkData chunk = world.getChunk(chunkPos); + if (chunk == null) { + throw new IllegalStateException(String.format( + "Chunk (%d; %d; %d) not loaded, cannot unload", + chunkPos.x, chunkPos.y, chunkPos.z + )); + } + world.removeChunk(chunk); + + } + + public void sendChunk(Player player, Vec3i chunkPos) { + LogManager.getLogger().info("Sending {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); + TestChunkSender.sendChunk(server, player.getClient(), chunkPos); + + getVision(player, true).visible.add(chunkPos); + } + + public void revokeChunk(Player player, Vec3i chunkPos) { + LogManager.getLogger().info("Revoking {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); + TestChunkSender.revokeChunk(player.getClient(), chunkPos); + + PlayerVision vision = getVision(player, false); + if (vision != null) { + vision.visible.remove(chunkPos); + } + } + + public boolean isChunkVisible(Vec3i chunkPos, Player player) { + PlayerVision vision = getVision(player, false); + + if (vision == null) { + return false; + } + + return vision.isChunkVisible(chunkPos); + } + + public Server getServer() { + return server; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/Player.java b/src/main/java/ru/windcorp/progressia/server/Player.java index 8a6001e..2e4da82 100644 --- a/src/main/java/ru/windcorp/progressia/server/Player.java +++ b/src/main/java/ru/windcorp/progressia/server/Player.java @@ -18,6 +18,8 @@ public class Player extends PlayerData implements ChunkLoader { super(entity); this.server = server; this.client = client; + + client.setPlayer(this); } public Server getServer() { @@ -43,6 +45,7 @@ public class Player extends PlayerData implements ChunkLoader { for (cursor.y = -iRadius; cursor.y <= +iRadius; ++cursor.y) { for (cursor.z = -iRadius; cursor.z <= +iRadius; ++cursor.z) { if (cursor.x * cursor.x + cursor.y * cursor.y + cursor.z * cursor.z <= radius) { + cursor.add(start); chunkConsumer.accept(cursor); cursor.sub(start); diff --git a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java index d32b7ea..b1a6425 100644 --- a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java +++ b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.test.TestContent; @@ -29,7 +31,13 @@ public class PlayerManager { public EntityData conjurePlayerEntity(String login) { // TODO Live up to the name if (TestContent.PLAYER_LOGIN.equals(login)) { - // TODO load appropriate chunks + + Vec3i chunkPos = Vectors.ZERO_3i; + + if (getServer().getWorld().getChunk(chunkPos) == null) { + getServer().getChunkManager().loadChunk(chunkPos); + } + return getServer().getWorld().getData().getEntity(TestContent.PLAYER_ENTITY_ID); } else { CrashReports.report(null, "Unknown login %s, javahorse stupid", login); diff --git a/src/main/java/ru/windcorp/progressia/server/Server.java b/src/main/java/ru/windcorp/progressia/server/Server.java index ac59f00..158639a 100644 --- a/src/main/java/ru/windcorp/progressia/server/Server.java +++ b/src/main/java/ru/windcorp/progressia/server/Server.java @@ -29,8 +29,9 @@ public class Server { private final ServerThread serverThread; - private final ClientManager clientManager = new ClientManager(this); - private final PlayerManager playerManager = new PlayerManager(this); + private final ClientManager clientManager; + private final PlayerManager playerManager; + private final ChunkManager chunkManager; private final TaskQueue taskQueue = new TaskQueue(this::isServerThread); @@ -39,8 +40,13 @@ public class Server { public Server(WorldData world) { this.world = new WorldLogic(world, this); this.serverThread = new ServerThread(this); + + this.clientManager = new ClientManager(this); + this.playerManager = new PlayerManager(this); + this.chunkManager = new ChunkManager(this); schedule(this::scheduleChunkTicks); + schedule(chunkManager::tick); } /** @@ -64,6 +70,10 @@ public class Server { return playerManager; } + public ChunkManager getChunkManager() { + return chunkManager; + } + /** * Checks if this thread is the main thread of this server. * @return {@code true} iff the invocation occurs in server main thread @@ -160,7 +170,7 @@ public class Server { } public float getLoadDistance(Player player) { - return Units.get(100.0f, "m"); + return Units.get(10.0f, "m"); } /** diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java index 54510ad..65b3221 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -1,6 +1,5 @@ package ru.windcorp.progressia.server.comms; -import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; @@ -11,10 +10,6 @@ import gnu.trove.map.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; import ru.windcorp.progressia.common.comms.CommsChannel.State; import ru.windcorp.progressia.common.comms.packets.Packet; -import ru.windcorp.progressia.common.io.ChunkIO; -import ru.windcorp.progressia.common.util.crash.CrashReports; -import ru.windcorp.progressia.common.world.ChunkData; -import ru.windcorp.progressia.common.world.PacketLoadChunk; import ru.windcorp.progressia.common.world.PacketSetLocalPlayer; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.server.Player; @@ -62,30 +57,16 @@ public class ClientManager { private void addClientPlayer(ClientPlayer client) { String login = client.getLogin(); - EntityData entity = getServer().getPlayerManager().conjurePlayerEntity(login); - Player player = new Player(entity, getServer(), client); - - getServer().getPlayerManager().getPlayers().add(player); - - for (ChunkData chunk : server.getWorld().getData().getChunks()) { - if (!client.canSeeChunk(chunk.getPosition())) { - continue; - } + EntityData entity; + synchronized (getServer().getWorld().getData()) { + entity = getServer().getPlayerManager().conjurePlayerEntity(login); - PacketLoadChunk packet = new PacketLoadChunk("Core:LoadChunk"); - packet.getPosition().set( - chunk.getPosition().x, - chunk.getPosition().y, - chunk.getPosition().z - ); + Player player = new Player(entity, getServer(), client); - try { - ChunkIO.save(chunk, packet.getData().getOutputStream()); - } catch (IOException e) { - CrashReports.report(e, "ClientManager fjcked up. javahorse stupid"); - } - client.sendPacket(packet); + getServer().getPlayerManager().getPlayers().add(player); + + getServer().getChunkManager().sendChunk(player, entity.getChunkCoords(null)); } PacketSetLocalPlayer packet = new PacketSetLocalPlayer(); @@ -119,7 +100,7 @@ public class ClientManager { getClients().forEach(c -> { if (c.getState() != State.CONNECTED) return; if (!(c instanceof ClientPlayer)) return; - if (!((ClientPlayer) c).canSeeChunk(chunkPos)) return; + if (!((ClientPlayer) c).isChunkVisible(chunkPos)) return; c.sendPacket(packet); }); } @@ -133,7 +114,7 @@ public class ClientManager { getClients().forEach(c -> { if (c.getState() != State.CONNECTED) return; if (!(c instanceof ClientPlayer)) return; - if (!((ClientPlayer) c).canSeeEntity(entityId)) return; + if (!((ClientPlayer) c).isChunkVisible(entityId)) return; c.sendPacket(packet); }); } diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java index d8271a9..bf7ac28 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java @@ -1,20 +1,32 @@ package ru.windcorp.progressia.server.comms; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.server.Player; -public abstract class ClientPlayer extends Client { +public abstract class ClientPlayer extends ClientChat { + + private Player player; public ClientPlayer(int id) { super(id); } - public abstract String getLogin(); - - public boolean canSeeChunk(Vec3i chunkPos) { - return true; + public Player getPlayer() { + return player; } - public boolean canSeeEntity(long entityId) { + public void setPlayer(Player player) { + this.player = player; + } + + public abstract String getLogin(); + + public boolean isChunkVisible(Vec3i chunkPos) { + if (player == null) return false; + return player.getServer().getChunkManager().isChunkVisible(chunkPos, player); + } + + public boolean isChunkVisible(long entityId) { return true; } diff --git a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java index 0142c08..00aae35 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java @@ -15,7 +15,6 @@ import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.tile.TileLogic; import ru.windcorp.progressia.server.world.tile.TileLogicStack; -import ru.windcorp.progressia.test.TestChunkSender; public class WorldLogic implements GenericWorld< @@ -48,7 +47,6 @@ implements GenericWorld< }); data.addListener(ChunkDataListeners.createAdder(new UpdateTriggerer(server))); - data.addListener(new TestChunkSender(server)); } @Override diff --git a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java index b267206..815fb6d 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java +++ b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java @@ -2,34 +2,29 @@ package ru.windcorp.progressia.test; import java.io.IOException; -import glm.Glm; +import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.io.ChunkIO; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.ChunkData; -import ru.windcorp.progressia.common.world.PacketLoadChunk; -import ru.windcorp.progressia.common.world.PacketSetLocalPlayer; -import ru.windcorp.progressia.common.world.WorldData; -import ru.windcorp.progressia.common.world.WorldDataListener; -import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.PacketRevokeChunk; +import ru.windcorp.progressia.common.world.PacketSendChunk; import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.comms.ClientPlayer; -public class TestChunkSender implements WorldDataListener { +public class TestChunkSender { - private final Server server; - - public TestChunkSender(Server server) { - this.server = server; - } - - @Override - public void onChunkLoaded(WorldData world, ChunkData chunk) { - PacketLoadChunk packet = new PacketLoadChunk("Core:LoadChunk"); + public static void sendChunk(Server server, ClientPlayer receiver, Vec3i chunkPos) { + ChunkData chunk = server.getWorld().getData().getChunk(chunkPos); - packet.getPosition().set( - chunk.getPosition().x, - chunk.getPosition().y, - chunk.getPosition().z - ); + if (chunk == null) { + throw new IllegalStateException(String.format( + "Chunk (%d; %d; %d) is not loaded, cannot send", + chunkPos.x, chunkPos.y, chunkPos.z + )); + } + + PacketSendChunk packet = new PacketSendChunk(); + packet.getPosition().set(chunkPos.x, chunkPos.y, chunkPos.z); try { ChunkIO.save(chunk, packet.getData().getOutputStream()); @@ -37,22 +32,13 @@ public class TestChunkSender implements WorldDataListener { CrashReports.report(e, "TestChunkSender fjcked up. javahorse stupid"); } - server.getClientManager().broadcastLocal(packet, chunk.getPosition()); - - tmp_sendPlayerIfPossible(world, chunk); + receiver.sendPacket(packet); } - private void tmp_sendPlayerIfPossible(WorldData world, ChunkData chunk) { - EntityData e = world.getEntity(TestContent.PLAYER_ENTITY_ID); - if (e == null) return; - - if (Glm.equals(e.getChunkCoords(null), chunk.getPosition())) { - System.out.printf("TestChunkSender: player found in (%d; %d; %d)\n", e.getChunkCoords(null).x, e.getChunkCoords(null).y, e.getChunkCoords(null).z); - - PacketSetLocalPlayer packet = new PacketSetLocalPlayer(); - packet.set(e.getEntityId()); - server.getClientManager().broadcastToAllPlayers(packet); - } + public static void revokeChunk(ClientPlayer receiver, Vec3i chunkPos) { + PacketRevokeChunk packet = new PacketRevokeChunk(); + packet.set(chunkPos); + receiver.sendPacket(packet); } } diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 6f64561..517df5f 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -346,10 +346,10 @@ public class TestContent { )); chunk.getEntities().add(player); - EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); - statie.setEntityId(STATIE_ENTITY_ID); - statie.setPosition(new Vec3(0, 15, 16)); - chunk.getEntities().add(statie); +// EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); +// statie.setEntityId(STATIE_ENTITY_ID); +// statie.setPosition(new Vec3(0, 15, 16)); +// chunk.getEntities().add(statie); } } diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index 74fc33c..1c67c7c 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -17,6 +17,7 @@ import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.util.FloatMathUtils; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.server.ServerState; public class TestPlayerControls { @@ -96,6 +97,13 @@ public class TestPlayerControls { } player.getVelocity().set(change); + + // THIS IS TERRIBLE TEST + EntityData serverEntity = ServerState.getInstance().getWorld().getData().getEntity(TestContent.PLAYER_ENTITY_ID); + if (serverEntity != null) { + serverEntity.setPosition(player.getPosition()); + } + } public void handleInput(Input input) { From b44540999bfd3c91db59bce213371328f8f1a896 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Tue, 29 Dec 2020 16:40:37 +0300 Subject: [PATCH 06/10] Entities are now transferred from server to client - Entities are no longer internally bound to chunks - Entity visibility for a player is now unique for each entity - Entities are send/revoked just like chunks by EntityManager - LocalPlayer no longer stores an entity; instead it stores entity ID and looks up the entity on demand --- .../ru/windcorp/progressia/client/Client.java | 24 ++- .../comms/DefaultClientCommsListener.java | 21 +- .../client/graphics/world/LayerWorld.java | 9 +- .../client/graphics/world/LocalPlayer.java | 51 ++++- .../client/world/entity/NPedModel.java | 2 + .../progressia/common/world/ChunkData.java | 13 -- .../progressia/common/world/WorldData.java | 60 ++++-- .../common/world/WorldDataListener.java | 15 ++ .../common/world/entity/EntityData.java | 23 +++ .../world/entity/PacketRevokeEntity.java | 41 ++++ .../common/world/generic/ChunkSets.java | 76 ++++++++ .../progressia/server/ChunkManager.java | 32 ++- .../progressia/server/EntityManager.java | 183 ++++++++++++++++++ .../progressia/server/PacketSendEntity.java | 70 +++++++ .../ru/windcorp/progressia/server/Player.java | 4 + .../progressia/server/PlayerManager.java | 20 +- .../ru/windcorp/progressia/server/Server.java | 8 +- .../progressia/server/ServerState.java | 2 +- .../server/comms/ClientManager.java | 24 +-- .../progressia/server/comms/ClientPlayer.java | 7 + .../progressia/server/world/ChunkLogic.java | 12 -- .../progressia/server/world/WorldLogic.java | 8 + .../server/world/tasks/TickChunk.java | 8 - .../server/world/tasks/TickEntitiesTask.java | 27 +++ .../progressia/test/TestChunkSender.java | 44 ----- .../windcorp/progressia/test/TestContent.java | 27 +-- .../progressia/test/TestPlayerControls.java | 4 +- 27 files changed, 647 insertions(+), 168 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/world/entity/PacketRevokeEntity.java create mode 100644 src/main/java/ru/windcorp/progressia/server/EntityManager.java create mode 100644 src/main/java/ru/windcorp/progressia/server/PacketSendEntity.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/tasks/TickEntitiesTask.java delete mode 100644 src/main/java/ru/windcorp/progressia/test/TestChunkSender.java diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index cd8c5a4..539e741 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -1,8 +1,11 @@ package ru.windcorp.progressia.client; +import org.apache.logging.log4j.LogManager; + import ru.windcorp.progressia.client.comms.DefaultClientCommsListener; import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.client.graphics.world.Camera; +import ru.windcorp.progressia.client.graphics.world.EntityAnchor; import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.common.world.WorldData; @@ -11,7 +14,7 @@ import ru.windcorp.progressia.common.world.entity.EntityData; public class Client { private final WorldRender world; - private LocalPlayer localPlayer; + private final LocalPlayer localPlayer = new LocalPlayer(this); private final Camera camera = new Camera((float) Math.toRadians(70)); @@ -27,13 +30,13 @@ public class Client { public WorldRender getWorld() { return world; } - + public LocalPlayer getLocalPlayer() { return localPlayer; } - public void setLocalPlayer(EntityData localPlayer) { - this.localPlayer = new LocalPlayer(localPlayer); + public boolean isReady() { + return localPlayer.hasEntity(); } public Camera getCamera() { @@ -44,4 +47,17 @@ public class Client { return comms; } + public void onLocalPlayerEntityChanged(EntityData entity, EntityData lastKnownEntity) { + LogManager.getLogger().info("LocalPlayer entity changed from {} to {}", lastKnownEntity, entity); + + if (entity == null) { + getCamera().setAnchor(null); + return; + } + + getCamera().setAnchor(new EntityAnchor( + getWorld().getEntityRenderable(entity) + )); + } + } diff --git a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java index b64798e..697a0ac 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java @@ -2,15 +2,12 @@ package ru.windcorp.progressia.client.comms; import java.io.IOException; -import ru.windcorp.jputil.chars.StringUtil; import ru.windcorp.progressia.client.Client; -import ru.windcorp.progressia.client.graphics.world.EntityAnchor; import ru.windcorp.progressia.common.comms.CommsListener; import ru.windcorp.progressia.common.comms.packets.Packet; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.PacketSetLocalPlayer; import ru.windcorp.progressia.common.world.PacketWorldChange; -import ru.windcorp.progressia.common.world.entity.EntityData; // TODO refactor with no mercy public class DefaultClientCommsListener implements CommsListener { @@ -33,26 +30,12 @@ public class DefaultClientCommsListener implements CommsListener { } private void setLocalPlayer(PacketSetLocalPlayer packet) { - EntityData entity = getClient().getWorld().getData().getEntity( - packet.getEntityId() - ); - - if (entity == null) { - CrashReports.report( - null, - "Player entity with ID %s not found", - new String(StringUtil.toFullHex(packet.getEntityId())) - ); - } - - getClient().setLocalPlayer(entity); - getClient().getCamera().setAnchor(new EntityAnchor( - getClient().getWorld().getEntityRenderable(entity) - )); + getClient().getLocalPlayer().setEntityId(packet.getEntityId()); } @Override public void onIOError(IOException reason) { + CrashReports.report(reason, "An IOException has occurred in communications"); // TODO implement } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index 0bbe753..94a5f16 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -75,7 +75,9 @@ public class LayerWorld extends Layer { renderWorld(); } - if (client.getLocalPlayer() != null) { + client.getLocalPlayer().getEntity(); + + if (client.isReady()) { client.getLocalPlayer().update(client.getWorld()); } } @@ -139,10 +141,9 @@ public class LayerWorld extends Layer { private static final Renderable SELECTION_BOX = tmp_createSelectionBox(); private void tmp_drawSelectionBox() { - LocalPlayer player = client.getLocalPlayer(); - if (player == null) return; + if (!client.isReady()) return; - Vec3i selection = player.getSelection().getBlock(); + Vec3i selection = client.getLocalPlayer().getSelection().getBlock(); if (selection == null) return; helper.pushTransform().translate(selection.x, selection.y, selection.z); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java index f0b4104..c877973 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java @@ -1,16 +1,59 @@ package ru.windcorp.progressia.client.graphics.world; +import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.client.world.entity.EntityRenderable; -import ru.windcorp.progressia.common.world.PlayerData; import ru.windcorp.progressia.common.world.entity.EntityData; -public class LocalPlayer extends PlayerData { +public class LocalPlayer { + private final Client client; + + private long entityId = EntityData.NULL_ENTITY_ID; + private EntityData lastKnownEntity = null; + private final Selection selection = new Selection(); - public LocalPlayer(EntityData entity) { - super(entity); + public LocalPlayer(Client client) { + this.client = client; + } + + public Client getClient() { + return client; + } + + public long getEntityId() { + return entityId; + } + + public void setEntityId(long entityId) { + this.entityId = entityId; + + this.lastKnownEntity = null; + getEntity(); + } + + public boolean hasEntityId() { + return entityId != EntityData.NULL_ENTITY_ID; + } + + public boolean hasEntity() { + return getEntity() != null; + } + + public EntityData getEntity() { + if (!hasEntityId()) { + return null; + } + + EntityData entity = getClient().getWorld().getData().getEntity(getEntityId()); + + if (entity != lastKnownEntity) { + getClient().onLocalPlayerEntityChanged(entity, lastKnownEntity); + this.lastKnownEntity = entity; + } + + return entity; } public Selection getSelection() { diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java index 43d9433..4c5397a 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java @@ -116,6 +116,8 @@ public abstract class NPedModel extends EntityRenderable { this.body = body; this.head = head; this.scale = scale; + + evaluateAngles(); } @Override diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index ed5e5d6..ff9fc71 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -32,7 +31,6 @@ import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.generic.GenericChunk; import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileDataStack; @@ -61,9 +59,6 @@ implements GenericChunk< BLOCK_FACE_COUNT ]; - private final List entities = - Collections.synchronizedList(new ArrayList<>()); - private final Collection listeners = Collections.synchronizedCollection(new ArrayList<>()); @@ -160,10 +155,6 @@ implements GenericChunk< face.getId(); } - public List getEntities() { - return entities; - } - private static void checkLocalCoordinates(Vec3i posInChunk) { if (!isInBounds(posInChunk)) { throw new IllegalCoordinatesException( @@ -219,10 +210,6 @@ implements GenericChunk< forEachTileStack(stack -> stack.forEach(tileData -> action.accept(stack, tileData))); } - public void forEachEntity(Consumer action) { - getEntities().forEach(action); - } - public WorldData getWorld() { return world; } diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index d96eaca..e031e0f 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -20,11 +20,12 @@ package ru.windcorp.progressia.common.world; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; - +import java.util.Objects; import glm.vec._3.i.Vec3i; -import gnu.trove.impl.sync.TSynchronizedLongObjectMap; +import gnu.trove.TCollections; import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; +import gnu.trove.set.TLongSet; import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -46,14 +47,14 @@ implements GenericWorld< > { private final ChunkMap chunksByPos = new LongBasedChunkMap<>( - new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this) + TCollections.synchronizedMap(new TLongObjectHashMap<>()) ); private final Collection chunks = Collections.unmodifiableCollection(chunksByPos.values()); private final TLongObjectMap entitiesById = - new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this); + TCollections.synchronizedMap(new TLongObjectHashMap<>()); private final Collection entities = Collections.unmodifiableCollection(entitiesById.valueCollection()); @@ -86,6 +87,10 @@ implements GenericWorld< return entities; } + public TLongSet getLoadedEntities() { + return entitiesById.keySet(); + } + public void tmp_generate() { final int size = 1; Vec3i cursor = new Vec3i(0, 0, 0); @@ -118,10 +123,6 @@ implements GenericWorld< chunksByPos.put(chunk, chunk); - chunk.forEachEntity(entity -> - entitiesById.put(entity.getEntityId(), entity) - ); - chunk.onLoaded(); getListeners().forEach(l -> l.onChunkLoaded(this, chunk)); } @@ -130,10 +131,6 @@ implements GenericWorld< getListeners().forEach(l -> l.beforeChunkUnloaded(this, chunk)); chunk.beforeUnloaded(); - chunk.forEachEntity(entity -> - entitiesById.remove(entity.getEntityId()) - ); - chunksByPos.remove(chunk); } @@ -153,6 +150,45 @@ implements GenericWorld< return entitiesById.get(entityId); } + public void addEntity(EntityData entity) { + Objects.requireNonNull(entity, "entity"); + + EntityData previous = entitiesById.putIfAbsent(entity.getEntityId(), entity); + + if (previous != null) { + String message = "Cannot add entity " + entity + ": "; + + if (previous == entity) { + message += "already present"; + } else { + message += "entity with the same EntityID already present (" + previous + ")"; + } + + throw new IllegalStateException(message); + } + + getListeners().forEach(l -> l.onEntityAdded(this, entity)); + } + + public void removeEntity(long entityId) { + synchronized (entitiesById) { + EntityData entity = entitiesById.get(entityId); + + if (entity == null) { + throw new IllegalArgumentException("Entity with EntityID " + EntityData.formatEntityId(entityId) + " not present"); + } else { + removeEntity(entity); + } + } + } + + public void removeEntity(EntityData entity) { + Objects.requireNonNull(entity, "entity"); + + getListeners().forEach(l -> l.beforeEntityRemoved(this, entity)); + entitiesById.remove(entity.getEntityId()); + } + public float getTime() { return time; } diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldDataListener.java b/src/main/java/ru/windcorp/progressia/common/world/WorldDataListener.java index 12b3b9f..19469fa 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldDataListener.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldDataListener.java @@ -3,6 +3,7 @@ package ru.windcorp.progressia.common.world; import java.util.function.Consumer; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.entity.EntityData; public interface WorldDataListener { @@ -30,5 +31,19 @@ public interface WorldDataListener { * @param chunk the chunk that is going to be unloaded */ default void beforeChunkUnloaded(WorldData world, ChunkData chunk) {} + + /** + * Invoked whenever an {@link EntityData} has been added. + * @param world the world instance + * @param entity the entity that has been added + */ + default void onEntityAdded(WorldData world, EntityData entity) {} + + /** + * Invoked whenever an {@link EntityData} is about to be removed. + * @param world the world instance + * @param entity the entity that is going to be removed + */ + default void beforeEntityRemoved(WorldData world, EntityData entity) {} } diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java index af3da1e..cea74b7 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java @@ -2,6 +2,7 @@ package ru.windcorp.progressia.common.world.entity; import glm.vec._2.Vec2; import glm.vec._3.Vec3; +import ru.windcorp.jputil.chars.StringUtil; import ru.windcorp.progressia.common.collision.Collideable; import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.state.StatefulObject; @@ -14,6 +15,12 @@ public class EntityData extends StatefulObject implements Collideable, GenericEn private final Vec2 direction = new Vec2(); + /** + * The unique {@code long} value guaranteed to never be assigned to an entity as its entity ID. + * This can safely be used as a placeholder or a sentinel value. + */ + public static final long NULL_ENTITY_ID = 0x0000_0000_0000_0000; + private long entityId; private CollisionModel collisionModel = null; @@ -69,6 +76,9 @@ public class EntityData extends StatefulObject implements Collideable, GenericEn } public void setEntityId(long entityId) { + if (entityId == NULL_ENTITY_ID) { + throw new IllegalArgumentException("Attempted to set entity ID to NULL_ENTITY_ID (" + entityId + ")"); + } this.entityId = entityId; } @@ -127,5 +137,18 @@ public class EntityData extends StatefulObject implements Collideable, GenericEn return output; } + + @Override + public String toString() { + return new StringBuilder(super.toString()) + .append(" (EntityID ") + .append(StringUtil.toFullHex(getEntityId())) + .append(")") + .toString(); + } + + public static String formatEntityId(long entityId) { + return new String(StringUtil.toFullHex(entityId)); + } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketRevokeEntity.java b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketRevokeEntity.java new file mode 100644 index 0000000..03ec081 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketRevokeEntity.java @@ -0,0 +1,41 @@ +package ru.windcorp.progressia.common.world.entity; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import ru.windcorp.progressia.common.world.PacketWorldChange; +import ru.windcorp.progressia.common.world.WorldData; + +public class PacketRevokeEntity extends PacketWorldChange { + + private long entityId; + + public PacketRevokeEntity() { + this("Core:RevokeEntity"); + } + + protected PacketRevokeEntity(String id) { + super(id); + } + + public void set(long entityId) { + this.entityId = entityId; + } + + @Override + public void read(DataInput input) throws IOException { + this.entityId = input.readLong(); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeLong(this.entityId); + } + + @Override + public void apply(WorldData world) { + world.removeEntity(this.entityId); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java index ba959cc..a8b23d9 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java @@ -1,5 +1,9 @@ package ru.windcorp.progressia.common.world.generic; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import glm.vec._3.i.Vec3i; import gnu.trove.impl.sync.TSynchronizedLongSet; import gnu.trove.set.hash.TLongHashSet; @@ -17,6 +21,78 @@ public class ChunkSets { return new LongBasedChunkSet(new TSynchronizedLongSet(new TLongHashSet())); } + private final static ChunkSet EMPTY_SET = new ChunkSet() { + + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + @Override + public Vec3i next() { + throw new NoSuchElementException(); + } + }; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean contains(Vec3i pos) { + return false; + } + + @Override + public boolean add(Vec3i pos) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Vec3i pos) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(ChunkSet other) { + return false; + } + + @Override + public boolean containsAny(ChunkSet other) { + return false; + } + + @Override + public void addAll(ChunkSet other) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeAll(ChunkSet other) { + throw new UnsupportedOperationException(); + } + + @Override + public void retainAll(ChunkSet other) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + }; + + public static ChunkSet empty() { + return EMPTY_SET; + } + private ChunkSets() {} } diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java index 10a1c38..9220827 100644 --- a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java +++ b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java @@ -8,10 +8,11 @@ import org.apache.logging.log4j.LogManager; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.PacketRevokeChunk; +import ru.windcorp.progressia.common.world.PacketSendChunk; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.generic.ChunkSet; import ru.windcorp.progressia.common.world.generic.ChunkSets; -import ru.windcorp.progressia.test.TestChunkSender; import ru.windcorp.progressia.test.TestContent; public class ChunkManager { @@ -148,14 +149,29 @@ public class ChunkManager { public void sendChunk(Player player, Vec3i chunkPos) { LogManager.getLogger().info("Sending {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); - TestChunkSender.sendChunk(server, player.getClient(), chunkPos); + + ChunkData chunk = server.getWorld().getData().getChunk(chunkPos); + + if (chunk == null) { + throw new IllegalStateException(String.format( + "Chunk (%d; %d; %d) is not loaded, cannot send", + chunkPos.x, chunkPos.y, chunkPos.z + )); + } + + PacketSendChunk packet = new PacketSendChunk(); + packet.set(chunk); + player.getClient().sendPacket(packet); getVision(player, true).visible.add(chunkPos); } public void revokeChunk(Player player, Vec3i chunkPos) { LogManager.getLogger().info("Revoking {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); - TestChunkSender.revokeChunk(player.getClient(), chunkPos); + + PacketRevokeChunk packet = new PacketRevokeChunk(); + packet.set(chunkPos); + player.getClient().sendPacket(packet); PlayerVision vision = getVision(player, false); if (vision != null) { @@ -172,6 +188,16 @@ public class ChunkManager { return vision.isChunkVisible(chunkPos); } + + public ChunkSet getVisibleChunks(Player player) { + PlayerVision vision = getVision(player, false); + + if (vision == null) { + return ChunkSets.empty(); + } + + return vision.visible; + } public Server getServer() { return server; diff --git a/src/main/java/ru/windcorp/progressia/server/EntityManager.java b/src/main/java/ru/windcorp/progressia/server/EntityManager.java new file mode 100644 index 0000000..3f810cf --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/EntityManager.java @@ -0,0 +1,183 @@ +package ru.windcorp.progressia.server; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.logging.log4j.LogManager; + +import glm.vec._3.i.Vec3i; +import gnu.trove.TCollections; +import gnu.trove.iterator.TLongIterator; +import gnu.trove.set.TLongSet; +import gnu.trove.set.hash.TLongHashSet; +import ru.windcorp.jputil.chars.StringUtil; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.entity.PacketRevokeEntity; +import ru.windcorp.progressia.common.world.generic.ChunkSet; + +public class EntityManager { + + private class PlayerVision { + + private final TLongSet visible = TCollections.synchronizedSet(new TLongHashSet()); + private final TLongSet requested = new TLongHashSet(); + private final TLongSet toSend = new TLongHashSet(); + private final TLongSet toRevoke = new TLongHashSet(); + + public boolean isEntityVisible(long entityId) { + return visible.contains(entityId); + } + + public void gatherRequests(Player player) { + requested.clear(); + + ChunkSet visibleChunks = player.getClient().getVisibleChunks(); + Vec3i v = Vectors.grab3i(); + + getServer().getWorld().forEachEntity(entity -> { + if (visibleChunks.contains(entity.getChunkCoords(v))) { + requested.add(entity.getEntityId()); + } + }); + + Vectors.release(v); + } + + public void updateQueues(Player player) { + toSend.clear(); + toSend.addAll(requested); + toSend.removeAll(visible); + toSend.retainAll(loaded); + + toRevoke.clear(); + + for (TLongIterator it = visible.iterator(); it.hasNext();) { + long entityId = it.next(); + if (!loaded.contains(entityId) || !requested.contains(entityId)) { + toRevoke.add(entityId); + } + } + } + + public void processQueues(Player player) { + toRevoke.forEach(entityId -> { + revokeEntity(player, entityId); + return true; + }); + toRevoke.clear(); + + toSend.forEach(entityId -> { + sendEntity(player, entityId); + return true; + }); + toSend.clear(); + } + + } + + private final Server server; + + private final TLongSet loaded; + + // TODO replace with a normal Map managed by some sort of PlayerListener, weak maps are weak + private final Map visions = Collections.synchronizedMap(new WeakHashMap<>()); + + public EntityManager(Server server) { + this.server = server; + this.loaded = server.getWorld().getData().getLoadedEntities(); + } + + public void tick() { + synchronized (getServer().getWorld().getData()) { + synchronized (visions) { + gatherRequests(); + updateQueues(); + processQueues(); + } + } + } + + private void gatherRequests() { + server.getPlayerManager().getPlayers().forEach(p -> { + PlayerVision vision = getVision(p, true); + vision.gatherRequests(p); + }); + } + + private void updateQueues() { + visions.forEach((p, v) -> { + v.updateQueues(p); + }); + } + + private void processQueues() { + visions.forEach((p, v) -> { + v.processQueues(p); + }); + } + + private PlayerVision getVision(Player player, boolean createIfMissing) { + return createIfMissing ? visions.computeIfAbsent(player, k -> new PlayerVision()) : visions.get(player); + } + + public void sendEntity(Player player, long entityId) { + + EntityData entity = server.getWorld().getData().getEntity(entityId); + + if (entity == null) { + throw new IllegalStateException( + "Entity with entity ID " + new String(StringUtil.toFullHex(entityId)) + " is not loaded, cannot send" + ); + } + + LogManager.getLogger().info("Sending {}", entity); + + PacketSendEntity packet = new PacketSendEntity(); + packet.set(entity); + player.getClient().sendPacket(packet); + + getVision(player, true).visible.add(entityId); + } + + public void revokeEntity(Player player, long entityId) { + LogManager.getLogger().info("Revoking {}", new String(StringUtil.toFullHex(entityId))); + + PacketRevokeEntity packet = new PacketRevokeEntity(); + packet.set(entityId); + player.getClient().sendPacket(packet); + + PlayerVision vision = getVision(player, false); + if (vision != null) { + vision.visible.remove(entityId); + } + } + + public boolean isEntityVisible(long entityId, Player player) { + PlayerVision vision = getVision(player, false); + + if (vision == null) { + return false; + } + + return vision.isEntityVisible(entityId); + } + + private static final TLongSet EMPTY_LONG_SET = TCollections.unmodifiableSet(new TLongHashSet()); + + public TLongSet getVisibleEntities(Player player) { + PlayerVision vision = getVision(player, false); + + if (vision == null) { + return EMPTY_LONG_SET; + } + + return vision.visible; + } + + public Server getServer() { + return server; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/PacketSendEntity.java b/src/main/java/ru/windcorp/progressia/server/PacketSendEntity.java new file mode 100644 index 0000000..d1da490 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/PacketSendEntity.java @@ -0,0 +1,70 @@ +package ru.windcorp.progressia.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import ru.windcorp.progressia.common.state.IOContext; +import ru.windcorp.progressia.common.util.DataBuffer; +import ru.windcorp.progressia.common.util.crash.CrashReports; +import ru.windcorp.progressia.common.world.DecodingException; +import ru.windcorp.progressia.common.world.PacketWorldChange; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.entity.EntityDataRegistry; + +public class PacketSendEntity extends PacketWorldChange { + + private String id; + private long entityId; + private final DataBuffer buffer = new DataBuffer(); + + public PacketSendEntity() { + this("Core:SendEntity"); + } + + protected PacketSendEntity(String id) { + super(id); + } + + public void set(EntityData entity) { + this.id = entity.getId(); + this.entityId = entity.getEntityId(); + + try { + entity.write(this.buffer.getWriter(), IOContext.COMMS); + } catch (IOException e) { + CrashReports.report(e, "Could not write an entity into an internal buffer"); + } + } + + @Override + public void read(DataInput input) throws IOException, DecodingException { + this.id = input.readUTF(); + this.entityId = input.readLong(); + this.buffer.fill(input, input.readInt()); + } + + @Override + public void write(DataOutput output) throws IOException { + output.writeUTF(this.id); + output.writeLong(this.entityId); + output.writeInt(this.buffer.getSize()); + this.buffer.flush(output); + } + + @Override + public void apply(WorldData world) { + EntityData entity = EntityDataRegistry.getInstance().create(this.id); + + entity.setEntityId(this.entityId); + try { + entity.read(this.buffer.getReader(), IOContext.COMMS); + } catch (IOException e) { + CrashReports.report(e, "Could not read an entity from an internal buffer"); + } + + world.addEntity(entity); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/Player.java b/src/main/java/ru/windcorp/progressia/server/Player.java index 2e4da82..a20a49e 100644 --- a/src/main/java/ru/windcorp/progressia/server/Player.java +++ b/src/main/java/ru/windcorp/progressia/server/Player.java @@ -54,5 +54,9 @@ public class Player extends PlayerData implements ChunkLoader { } } } + + public String getLogin() { + return getClient().getLogin(); + } } diff --git a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java index b1a6425..4a4bb65 100644 --- a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java +++ b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java @@ -4,10 +4,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.entity.EntityDataRegistry; import ru.windcorp.progressia.test.TestContent; public class PlayerManager { @@ -38,13 +41,28 @@ public class PlayerManager { getServer().getChunkManager().loadChunk(chunkPos); } - return getServer().getWorld().getData().getEntity(TestContent.PLAYER_ENTITY_ID); + EntityData entity = spawnPlayerEntity(login); + return entity; } else { CrashReports.report(null, "Unknown login %s, javahorse stupid", login); return null; } } + private EntityData spawnPlayerEntity(String login) { + EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); + + player.setEntityId(TestContent.PLAYER_ENTITY_ID); + player.setPosition(new Vec3(8, 8, 8)); + player.setDirection(new Vec2( + Math.toRadians(40), Math.toRadians(10) + )); + + getServer().getWorld().getData().addEntity(player); + + return player; + } + public Server getServer() { return server; } diff --git a/src/main/java/ru/windcorp/progressia/server/Server.java b/src/main/java/ru/windcorp/progressia/server/Server.java index 158639a..d2459c7 100644 --- a/src/main/java/ru/windcorp/progressia/server/Server.java +++ b/src/main/java/ru/windcorp/progressia/server/Server.java @@ -32,6 +32,7 @@ public class Server { private final ClientManager clientManager; private final PlayerManager playerManager; private final ChunkManager chunkManager; + private final EntityManager entityManager; private final TaskQueue taskQueue = new TaskQueue(this::isServerThread); @@ -44,9 +45,11 @@ public class Server { this.clientManager = new ClientManager(this); this.playerManager = new PlayerManager(this); this.chunkManager = new ChunkManager(this); + this.entityManager = new EntityManager(this); - schedule(this::scheduleChunkTicks); + schedule(this::scheduleWorldTicks); schedule(chunkManager::tick); + schedule(entityManager::tick); } /** @@ -197,8 +200,9 @@ public class Server { serverThread.stop(); } - private void scheduleChunkTicks(Server server) { + private void scheduleWorldTicks(Server server) { server.getWorld().getChunks().forEach(chunk -> requestEvaluation(chunk.getTickTask())); + requestEvaluation(server.getWorld().getTickEntitiesTask()); } /** diff --git a/src/main/java/ru/windcorp/progressia/server/ServerState.java b/src/main/java/ru/windcorp/progressia/server/ServerState.java index aa19807..1c18a37 100644 --- a/src/main/java/ru/windcorp/progressia/server/ServerState.java +++ b/src/main/java/ru/windcorp/progressia/server/ServerState.java @@ -16,7 +16,7 @@ public class ServerState { public static void startServer() { Server server = new Server(new WorldData()); - server.getWorld().getData().tmp_generate(); +// server.getWorld().getData().tmp_generate(); setInstance(server); server.start(); } diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java index 65b3221..c82a582 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -4,6 +4,8 @@ import java.util.Collection; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.logging.log4j.LogManager; + import glm.vec._3.i.Vec3i; import gnu.trove.TCollections; import gnu.trove.map.TIntObjectMap; @@ -41,10 +43,10 @@ public class ClientManager { if (client instanceof ClientChat) { addClientChat((ClientChat) client); - } - - if (client instanceof ClientPlayer) { - addClientPlayer((ClientPlayer) client); + + if (client instanceof ClientPlayer) { + addClientPlayer((ClientPlayer) client); + } } client.addListener(new DefaultServerCommsListener(this, client)); @@ -58,18 +60,12 @@ public class ClientManager { private void addClientPlayer(ClientPlayer client) { String login = client.getLogin(); - EntityData entity; - synchronized (getServer().getWorld().getData()) { - entity = getServer().getPlayerManager().conjurePlayerEntity(login); - - Player player = new Player(entity, getServer(), client); - - getServer().getPlayerManager().getPlayers().add(player); - - getServer().getChunkManager().sendChunk(player, entity.getChunkCoords(null)); - } + EntityData entity = getServer().getPlayerManager().conjurePlayerEntity(login); + Player player = new Player(entity, getServer(), client); + getServer().getPlayerManager().getPlayers().add(player); PacketSetLocalPlayer packet = new PacketSetLocalPlayer(); + LogManager.getLogger().info("Sending local player ID {}", EntityData.formatEntityId(entity.getEntityId())); packet.set(entity.getEntityId()); client.sendPacket(packet); } diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java index bf7ac28..00db77c 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientPlayer.java @@ -1,6 +1,8 @@ package ru.windcorp.progressia.server.comms; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.generic.ChunkSet; +import ru.windcorp.progressia.common.world.generic.ChunkSets; import ru.windcorp.progressia.server.Player; public abstract class ClientPlayer extends ClientChat { @@ -26,6 +28,11 @@ public abstract class ClientPlayer extends ClientChat { return player.getServer().getChunkManager().isChunkVisible(chunkPos, player); } + public ChunkSet getVisibleChunks() { + if (player == null) return ChunkSets.empty(); + return player.getServer().getChunkManager().getVisibleChunks(player); + } + public boolean isChunkVisible(long entityId) { return true; } diff --git a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java index b8b1779..622ca00 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java @@ -11,15 +11,12 @@ import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.block.BlockFace; -import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.generic.GenericChunk; import ru.windcorp.progressia.common.world.tile.TileDataStack; import ru.windcorp.progressia.common.world.tile.TileReference; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.BlockLogicRegistry; import ru.windcorp.progressia.server.world.block.TickableBlock; -import ru.windcorp.progressia.server.world.entity.EntityLogic; -import ru.windcorp.progressia.server.world.entity.EntityLogicRegistry; import ru.windcorp.progressia.server.world.tasks.TickChunk; import ru.windcorp.progressia.server.world.ticking.TickingPolicy; import ru.windcorp.progressia.server.world.tile.TickableTile; @@ -112,15 +109,6 @@ public class ChunkLogic implements GenericChunk< }); } - public void forEachEntity(BiConsumer action) { - getData().forEachEntity(data -> { - action.accept( - EntityLogicRegistry.getInstance().get(data.getId()), - data - ); - }); - } - public TickChunk getTickTask() { return tickTask; } diff --git a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java index 00aae35..e5ec697 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java @@ -13,6 +13,8 @@ import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.generic.GenericWorld; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.world.block.BlockLogic; +import ru.windcorp.progressia.server.world.tasks.TickEntitiesTask; +import ru.windcorp.progressia.server.world.ticking.Evaluation; import ru.windcorp.progressia.server.world.tile.TileLogic; import ru.windcorp.progressia.server.world.tile.TileLogicStack; @@ -30,6 +32,8 @@ implements GenericWorld< private final Map chunks = new HashMap<>(); + private final Evaluation tickEntitiesTask = new TickEntitiesTask(); + public WorldLogic(WorldData data, Server server) { this.data = data; this.server = server; @@ -64,6 +68,10 @@ implements GenericWorld< return getData().getEntities(); } + public Evaluation getTickEntitiesTask() { + return tickEntitiesTask; + } + public Server getServer() { return server; } diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java index 76570b5..631e5a5 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java @@ -14,7 +14,6 @@ import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.tile.TileDataStack; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.world.ChunkLogic; -import ru.windcorp.progressia.server.world.TickAndUpdateUtil; import ru.windcorp.progressia.server.world.TickContextMutable; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.TickableBlock; @@ -55,7 +54,6 @@ public class TickChunk extends Evaluation { public void evaluate(Server server) { tickRegulars(server); tickRandom(server); - tickEntities(server); } private void tickRegulars(Server server) { @@ -165,12 +163,6 @@ public class TickChunk extends Evaluation { ); } - private void tickEntities(Server server) { - chunk.getData().forEachEntity(entity -> { - TickAndUpdateUtil.tickEntity(entity, server); - }); - } - @Override public void getRelevantChunk(Vec3i output) { Vec3i p = chunk.getData().getPosition(); diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/TickEntitiesTask.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/TickEntitiesTask.java new file mode 100644 index 0000000..da964bd --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/TickEntitiesTask.java @@ -0,0 +1,27 @@ +package ru.windcorp.progressia.server.world.tasks; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.world.TickAndUpdateUtil; +import ru.windcorp.progressia.server.world.ticking.Evaluation; + +public class TickEntitiesTask extends Evaluation { + + @Override + public void evaluate(Server server) { + server.getWorld().forEachEntity(entity -> { + TickAndUpdateUtil.tickEntity(entity, server); + }); + } + + @Override + public void getRelevantChunk(Vec3i output) { + // Do nothing + } + + @Override + public boolean isThreadSensitive() { + return false; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java deleted file mode 100644 index 815fb6d..0000000 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java +++ /dev/null @@ -1,44 +0,0 @@ -package ru.windcorp.progressia.test; - -import java.io.IOException; - -import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.common.io.ChunkIO; -import ru.windcorp.progressia.common.util.crash.CrashReports; -import ru.windcorp.progressia.common.world.ChunkData; -import ru.windcorp.progressia.common.world.PacketRevokeChunk; -import ru.windcorp.progressia.common.world.PacketSendChunk; -import ru.windcorp.progressia.server.Server; -import ru.windcorp.progressia.server.comms.ClientPlayer; - -public class TestChunkSender { - - public static void sendChunk(Server server, ClientPlayer receiver, Vec3i chunkPos) { - ChunkData chunk = server.getWorld().getData().getChunk(chunkPos); - - if (chunk == null) { - throw new IllegalStateException(String.format( - "Chunk (%d; %d; %d) is not loaded, cannot send", - chunkPos.x, chunkPos.y, chunkPos.z - )); - } - - PacketSendChunk packet = new PacketSendChunk(); - packet.getPosition().set(chunkPos.x, chunkPos.y, chunkPos.z); - - try { - ChunkIO.save(chunk, packet.getData().getOutputStream()); - } catch (IOException e) { - CrashReports.report(e, "TestChunkSender fjcked up. javahorse stupid"); - } - - receiver.sendPacket(packet); - } - - public static void revokeChunk(ClientPlayer receiver, Vec3i chunkPos) { - PacketRevokeChunk packet = new PacketRevokeChunk(); - packet.set(chunkPos); - receiver.sendPacket(packet); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 517df5f..ee6bedb 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -7,16 +7,12 @@ import java.util.function.Consumer; import org.lwjgl.glfw.GLFW; -import glm.Glm; -import glm.vec._2.Vec2; -import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.audio.SoundEffect; import ru.windcorp.progressia.client.comms.controls.*; import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.KeyMatcher; -import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.graphics.world.Selection; import ru.windcorp.progressia.client.world.block.*; import ru.windcorp.progressia.client.world.entity.*; @@ -26,7 +22,6 @@ import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.comms.controls.*; import ru.windcorp.progressia.common.io.ChunkIO; import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory; -import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.block.*; @@ -214,12 +209,9 @@ public class TestContent { private static Selection getSelection() { ru.windcorp.progressia.client.Client client = ClientState.getInstance(); - if (client == null) return null; + if (client == null || !client.isReady()) return null; - LocalPlayer player = client.getLocalPlayer(); - if (player == null) return null; - - return player.getSelection(); + return client.getLocalPlayer().getSelection(); } private static void onBlockBreakTrigger(ControlData control) { @@ -336,21 +328,6 @@ public class TestContent { } } } - - if (Glm.equals(chunk.getPosition(), Vectors.ZERO_3i)) { - EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); - player.setEntityId(PLAYER_ENTITY_ID); - player.setPosition(new Vec3(8, 8, 8)); - player.setDirection(new Vec2( - (float) Math.toRadians(40), (float) Math.toRadians(45) - )); - chunk.getEntities().add(player); - -// EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); -// statie.setEntityId(STATIE_ENTITY_ID); -// statie.setPosition(new Vec3(0, 15, 16)); -// chunk.getEntities().add(statie); - } } private static void registerMisc() { diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index 1c67c7c..d00ca09 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -61,7 +61,7 @@ public class TestPlayerControls { private Runnable updateCallback = null; public void applyPlayerControls() { - if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) { + if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) { return; } @@ -107,7 +107,7 @@ public class TestPlayerControls { } public void handleInput(Input input) { - if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) { + if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) { return; } From 5d570a810b5a0093234c971ad070c94979f1da5f Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Tue, 29 Dec 2020 16:56:15 +0300 Subject: [PATCH 07/10] Fixed chunk load radius and removed debug leftovers --- .../ru/windcorp/progressia/client/Client.java | 4 --- .../progressia/common/world/WorldData.java | 26 +++++++------------ .../common/world/generic/GenericWorld.java | 4 +-- .../progressia/server/ChunkManager.java | 10 ------- .../progressia/server/EntityManager.java | 6 ----- .../ru/windcorp/progressia/server/Player.java | 9 ++++--- .../ru/windcorp/progressia/server/Server.java | 2 +- .../progressia/server/ServerState.java | 1 - 8 files changed, 17 insertions(+), 45 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index 539e741..9902de5 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -1,7 +1,5 @@ package ru.windcorp.progressia.client; -import org.apache.logging.log4j.LogManager; - import ru.windcorp.progressia.client.comms.DefaultClientCommsListener; import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.client.graphics.world.Camera; @@ -48,8 +46,6 @@ public class Client { } public void onLocalPlayerEntityChanged(EntityData entity, EntityData lastKnownEntity) { - LogManager.getLogger().info("LocalPlayer entity changed from {} to {}", lastKnownEntity, entity); - if (entity == null) { getCamera().setAnchor(null); return; diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index e031e0f..c4d69ef 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Objects; +import java.util.function.Consumer; + import glm.vec._3.i.Vec3i; import gnu.trove.TCollections; import gnu.trove.map.TLongObjectMap; @@ -35,7 +37,6 @@ import ru.windcorp.progressia.common.world.generic.GenericWorld; import ru.windcorp.progressia.common.world.generic.LongBasedChunkMap; import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileDataStack; -import ru.windcorp.progressia.test.TestContent; public class WorldData implements GenericWorld< @@ -87,23 +88,16 @@ implements GenericWorld< return entities; } - public TLongSet getLoadedEntities() { - return entitiesById.keySet(); + @Override + public void forEachEntity(Consumer action) { + synchronized (entitiesById) { // TODO HORRIBLY MUTILATE THE CORPSE OF TROVE4J so that gnu.trove.impl.sync.SynchronizedCollection.forEach is synchronized + getEntities().forEach(action); + } } - public void tmp_generate() { - final int size = 1; - Vec3i cursor = new Vec3i(0, 0, 0); - - for (cursor.x = -(size / 2); cursor.x <= (size / 2); ++cursor.x) { - for (cursor.y = -(size / 2); cursor.y <= (size / 2); ++cursor.y) { - for (cursor.z = -(size / 2); cursor.z <= (size / 2); ++cursor.z) { - ChunkData chunk = new ChunkData(cursor, this); - TestContent.generateChunk(chunk); - addChunk(chunk); - } - } - } + + public TLongSet getLoadedEntities() { + return entitiesById.keySet(); } private void addChunkListeners(ChunkData chunk) { diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java index 54d13af..79746ed 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericWorld.java @@ -118,9 +118,7 @@ public interface GenericWorld< */ default void forEachEntity(Consumer action) { - synchronized (this) { // TODO HORRIBLY MUTILATE THE CORPSE OF TROVE4J so that gnu.trove.impl.sync.SynchronizedCollection.forEach is synchronized - getEntities().forEach(action); - } + getEntities().forEach(action); } default void forEachEntityIn(Vec3i min, Vec3i max, Consumer action) { diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java index 9220827..4810e3a 100644 --- a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java +++ b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java @@ -4,8 +4,6 @@ import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; -import org.apache.logging.log4j.LogManager; - import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.PacketRevokeChunk; @@ -119,8 +117,6 @@ public class ChunkManager { } public void loadChunk(Vec3i chunkPos) { - - LogManager.getLogger().info("Loading {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); WorldData world = getServer().getWorld().getData(); @@ -132,8 +128,6 @@ public class ChunkManager { public void unloadChunk(Vec3i chunkPos) { - LogManager.getLogger().info("Unloading {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); - WorldData world = getServer().getWorld().getData(); ChunkData chunk = world.getChunk(chunkPos); @@ -148,8 +142,6 @@ public class ChunkManager { } public void sendChunk(Player player, Vec3i chunkPos) { - LogManager.getLogger().info("Sending {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); - ChunkData chunk = server.getWorld().getData().getChunk(chunkPos); if (chunk == null) { @@ -167,8 +159,6 @@ public class ChunkManager { } public void revokeChunk(Player player, Vec3i chunkPos) { - LogManager.getLogger().info("Revoking {} {} {}", chunkPos.x, chunkPos.y, chunkPos.z); - PacketRevokeChunk packet = new PacketRevokeChunk(); packet.set(chunkPos); player.getClient().sendPacket(packet); diff --git a/src/main/java/ru/windcorp/progressia/server/EntityManager.java b/src/main/java/ru/windcorp/progressia/server/EntityManager.java index 3f810cf..aab1004 100644 --- a/src/main/java/ru/windcorp/progressia/server/EntityManager.java +++ b/src/main/java/ru/windcorp/progressia/server/EntityManager.java @@ -4,8 +4,6 @@ import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; -import org.apache.logging.log4j.LogManager; - import glm.vec._3.i.Vec3i; import gnu.trove.TCollections; import gnu.trove.iterator.TLongIterator; @@ -132,8 +130,6 @@ public class EntityManager { ); } - LogManager.getLogger().info("Sending {}", entity); - PacketSendEntity packet = new PacketSendEntity(); packet.set(entity); player.getClient().sendPacket(packet); @@ -142,8 +138,6 @@ public class EntityManager { } public void revokeEntity(Player player, long entityId) { - LogManager.getLogger().info("Revoking {}", new String(StringUtil.toFullHex(entityId))); - PacketRevokeEntity packet = new PacketRevokeEntity(); packet.set(entityId); player.getClient().sendPacket(packet); diff --git a/src/main/java/ru/windcorp/progressia/server/Player.java b/src/main/java/ru/windcorp/progressia/server/Player.java index a20a49e..c77457a 100644 --- a/src/main/java/ru/windcorp/progressia/server/Player.java +++ b/src/main/java/ru/windcorp/progressia/server/Player.java @@ -36,19 +36,20 @@ public class Player extends PlayerData implements ChunkLoader { Coordinates.convertInWorldToChunk(start, start); Vec3i cursor = new Vec3i(); - float radius = getServer().getLoadDistance(this); - float radiusSq = radius / Units.get(16.0f, "m"); - radiusSq *= radiusSq; + float radius = getServer().getLoadDistance(this) / Units.get(16.0f, "m"); + + float radiusSq = radius * radius; int iRadius = (int) Math.ceil(radius); for (cursor.x = -iRadius; cursor.x <= +iRadius; ++cursor.x) { for (cursor.y = -iRadius; cursor.y <= +iRadius; ++cursor.y) { for (cursor.z = -iRadius; cursor.z <= +iRadius; ++cursor.z) { - if (cursor.x * cursor.x + cursor.y * cursor.y + cursor.z * cursor.z <= radius) { + if (cursor.x * cursor.x + cursor.y * cursor.y + cursor.z * cursor.z <= radiusSq) { cursor.add(start); chunkConsumer.accept(cursor); cursor.sub(start); + } } } diff --git a/src/main/java/ru/windcorp/progressia/server/Server.java b/src/main/java/ru/windcorp/progressia/server/Server.java index d2459c7..40df32b 100644 --- a/src/main/java/ru/windcorp/progressia/server/Server.java +++ b/src/main/java/ru/windcorp/progressia/server/Server.java @@ -173,7 +173,7 @@ public class Server { } public float getLoadDistance(Player player) { - return Units.get(10.0f, "m"); + return Units.get(100.0f, "m"); } /** diff --git a/src/main/java/ru/windcorp/progressia/server/ServerState.java b/src/main/java/ru/windcorp/progressia/server/ServerState.java index 1c18a37..d8071f6 100644 --- a/src/main/java/ru/windcorp/progressia/server/ServerState.java +++ b/src/main/java/ru/windcorp/progressia/server/ServerState.java @@ -16,7 +16,6 @@ public class ServerState { public static void startServer() { Server server = new Server(new WorldData()); -// server.getWorld().getData().tmp_generate(); setInstance(server); server.start(); } From c9087e72150a784239eddc81719cb65fed1a3b5e Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Wed, 30 Dec 2020 00:29:25 +0300 Subject: [PATCH 08/10] Spread chunk updates over several frames - At most 1 chunk is updated (previously known as 'buildModel') every frame - 3x3x3 chunks closest to the player are updated in real-time without the aforementioned limit --- .../ru/windcorp/progressia/client/Client.java | 2 +- .../progressia/client/world/ChunkRender.java | 14 +- .../progressia/client/world/WorldRender.java | 93 ++++++++- .../progressia/common/util/VectorUtil.java | 53 ++++- .../progressia/common/world/ChunkData.java | 2 +- .../common/world/generic/ChunkSets.java | 186 +++++++++++++++++- .../common/world/generic/GenericChunk.java | 4 +- .../server/world/TickAndUpdateUtil.java | 43 ---- .../progressia/test/LayerTestGUI.java | 6 + .../progressia/test/TestPlayerControls.java | 4 +- 10 files changed, 336 insertions(+), 71 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index 9902de5..0d84fe3 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -19,7 +19,7 @@ public class Client { private final ServerCommsChannel comms; public Client(WorldData world, ServerCommsChannel comms) { - this.world = new WorldRender(world); + this.world = new WorldRender(world, this); this.comms = comms; comms.addListener(new DefaultClientCommsListener(this)); diff --git a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java index 94af7f8..5cf3555 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java @@ -59,7 +59,6 @@ implements GenericChunk< private final WorldRender world; private final ChunkData data; - private boolean needsUpdate; private Model model = null; private final Map tileRenderLists = @@ -108,16 +107,12 @@ implements GenericChunk< } public synchronized void markForUpdate() { - this.needsUpdate = true; - } - - public synchronized boolean needsUpdate() { - return needsUpdate; + getWorld().markChunkForUpdate(getPosition()); } public synchronized void render(ShapeRenderHelper renderer) { - if (model == null || needsUpdate()) { - buildModel(); + if (model == null) { + return; } renderer.pushTransform().translate( @@ -131,7 +126,7 @@ implements GenericChunk< renderer.popTransform(); } - private void buildModel() { + public synchronized void update() { Collection optimizers = ChunkRenderOptimizers.getAllSuppliers().stream() .map(ChunkRenderOptimizerSupplier::createOptimizer) @@ -159,7 +154,6 @@ implements GenericChunk< .forEach(builder::addPart); model = new StaticModel(builder); - needsUpdate = false; } private void buildBlock( diff --git a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java index 14f99d4..4b01aaf 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java @@ -20,10 +20,12 @@ package ru.windcorp.progressia.client.world; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.WeakHashMap; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.graphics.backend.FaceCulling; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; import ru.windcorp.progressia.client.world.block.BlockRender; @@ -31,11 +33,14 @@ import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; import ru.windcorp.progressia.client.world.entity.EntityRenderable; import ru.windcorp.progressia.client.world.tile.TileRender; import ru.windcorp.progressia.client.world.tile.TileRenderStack; +import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.ChunkDataListeners; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldDataListener; import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.generic.ChunkSet; +import ru.windcorp.progressia.common.world.generic.ChunkSets; import ru.windcorp.progressia.common.world.generic.GenericWorld; public class WorldRender @@ -46,35 +51,52 @@ implements GenericWorld< ChunkRender, EntityRenderable > { - + private final WorldData data; + private final Client client; private final Map chunks = Collections.synchronizedMap(new HashMap<>()); private final Map entityModels = Collections.synchronizedMap(new WeakHashMap<>()); - public WorldRender(WorldData data) { + private final ChunkSet chunksToUpdate = ChunkSets.newSyncHashSet(); + + public WorldRender(WorldData data, Client client) { this.data = data; + this.client = client; data.addListener(ChunkDataListeners.createAdder(new ChunkUpdateListener(this))); data.addListener(new WorldDataListener() { @Override public void onChunkLoaded(WorldData world, ChunkData chunk) { - chunks.put(chunk, new ChunkRender(WorldRender.this, chunk)); + addChunk(chunk); } @Override public void beforeChunkUnloaded(WorldData world, ChunkData chunk) { - chunks.remove(chunk); + removeChunk(chunk); } }); } + + protected void addChunk(ChunkData chunk) { + chunks.put(chunk, new ChunkRender(WorldRender.this, chunk)); + markChunkForUpdate(chunk.getPosition()); + } + protected void removeChunk(ChunkData chunk) { + chunks.remove(chunk); + } + public WorldData getData() { return data; } + public Client getClient() { + return client; + } + public ChunkRender getChunk(ChunkData chunkData) { return chunks.get(chunkData); } @@ -95,10 +117,67 @@ implements GenericWorld< } public void render(ShapeRenderHelper renderer) { + updateChunks(); + getChunks().forEach(chunk -> chunk.render(renderer)); renderEntities(renderer); } + private void updateChunks() { + synchronized (chunksToUpdate) { + if (chunksToUpdate.isEmpty()) return; + + int updates = updateChunksNearLocalPlayer(); + int maximumUpdates = getMaximumChunkUpdatesPerFrame(); + + updateRandomChunks(maximumUpdates - updates); + } + } + + private int updateChunksNearLocalPlayer() { + EntityData entity = getClient().getLocalPlayer().getEntity(); + if (entity == null) return 0; + + int[] updates = new int[] { 0 }; + + VectorUtil.iterateCuboidAround(entity.getChunkCoords(null), 3, chunkPos -> { + if (chunksToUpdate.contains(chunkPos)) { + getChunk(chunkPos).update(); + chunksToUpdate.remove(chunkPos); + + updates[0]++; + } + }); + + return updates[0]; + } + + private void updateRandomChunks(int allowedUpdates) { + if (allowedUpdates <= 0) return; + + for (Iterator it = chunksToUpdate.iterator(); it.hasNext();) { + Vec3i chunkPos = it.next(); + ChunkRender chunk = getChunk(chunkPos); + + if (chunk != null) { + chunk.update(); + allowedUpdates--; + } + + it.remove(); + + if (allowedUpdates <= 0) return; + } + } + + private int getMaximumChunkUpdatesPerFrame() { + return 1; + } + + public int getPendingChunkUpdates() { + return chunksToUpdate.size(); + } + private void renderEntities(ShapeRenderHelper renderer) { FaceCulling.push(false); @@ -122,5 +201,11 @@ implements GenericWorld< return EntityRenderRegistry.getInstance().get(entity.getId()) .createRenderable(entity); } + + public void markChunkForUpdate(Vec3i chunkPos) { + if (getData().getChunk(chunkPos) != null) { + chunksToUpdate.add(chunkPos); + } + } } diff --git a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java index eaa02ed..4e7ac70 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java +++ b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java @@ -19,7 +19,7 @@ public class VectorUtil { X, Y, Z, W; } - public static void forEachVectorInCuboid( + public static void iterateCuboid( int x0, int y0, int z0, int x1, int y1, int z1, Consumer action @@ -38,6 +38,57 @@ public class VectorUtil { Vectors.release(cursor); } + public static void iterateCuboid( + Vec3i vMin, Vec3i vMax, + Consumer action + ) { + iterateCuboid(vMin.x, vMin.y, vMin.z, vMax.x, vMax.y, vMax.z, action); + } + + public static void iterateCuboidAround( + int cx, int cy, int cz, + int dx, int dy, int dz, + Consumer action + ) { + if (dx < 0) throw new IllegalArgumentException("dx " + dx + " is negative"); + if (dy < 0) throw new IllegalArgumentException("dy " + dx + " is negative"); + if (dz < 0) throw new IllegalArgumentException("dz " + dx + " is negative"); + + if (dx % 2 == 0) throw new IllegalArgumentException("dx " + dx + " is even, only odd accepted"); + if (dy % 2 == 0) throw new IllegalArgumentException("dy " + dy + " is even, only odd accepted"); + if (dz % 2 == 0) throw new IllegalArgumentException("dz " + dz + " is even, only odd accepted"); + + dx /= 2; + dy /= 2; + dz /= 2; + + iterateCuboid(cx - dx, cy - dy, cz - dz, cx + dx + 1, cy + dy + 1, cz + dz + 1, action); + } + + public static void iterateCuboidAround( + Vec3i center, + Vec3i diameters, + Consumer action + ) { + iterateCuboidAround(center.x, center.y, center.z, diameters.x, diameters.y, diameters.z, action); + } + + public static void iterateCuboidAround( + int cx, int cy, int cz, + int diameter, + Consumer action + ) { + iterateCuboidAround(cx, cy, cz, diameter, diameter, diameter, action); + } + + public static void iterateCuboidAround( + Vec3i center, + int diameter, + Consumer action + ) { + iterateCuboidAround(center.x, center.y, center.z, diameter, action); + } + public static void applyMat4(Vec3 in, Mat4 mat, Vec3 out) { Vec4 vec4 = Vectors.grab4(); vec4.set(in, 1f); diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index ff9fc71..2e454e0 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -183,7 +183,7 @@ implements GenericChunk< } public void forEachBlock(Consumer action) { - VectorUtil.forEachVectorInCuboid( + VectorUtil.iterateCuboid( 0, 0, 0, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, action diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java index a8b23d9..3e50dd3 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java @@ -2,9 +2,11 @@ package ru.windcorp.progressia.common.world.generic; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Predicate; import glm.vec._3.i.Vec3i; -import gnu.trove.impl.sync.TSynchronizedLongSet; import gnu.trove.set.hash.TLongHashSet; public class ChunkSets { @@ -14,13 +16,19 @@ public class ChunkSets { } public static ChunkSet newSyncHashSet(Object mutex) { - return new LongBasedChunkSet(new TSynchronizedLongSet(new TLongHashSet(), mutex)); + return new SynchronizedChunkSet(new LongBasedChunkSet(new TLongHashSet()), mutex); } public static ChunkSet newSyncHashSet() { - return new LongBasedChunkSet(new TSynchronizedLongSet(new TLongHashSet())); + return newSyncHashSet(null); } + public static ChunkSet empty() { + return EMPTY_SET; + } + + private ChunkSets() {} + private final static ChunkSet EMPTY_SET = new ChunkSet() { @Override @@ -89,10 +97,174 @@ public class ChunkSets { }; - public static ChunkSet empty() { - return EMPTY_SET; + private static class SynchronizedChunkSet implements ChunkSet { + + private final ChunkSet parent; + private final Object mutex; + + public SynchronizedChunkSet(ChunkSet parent, Object mutex) { + Objects.requireNonNull(parent, "parent"); + this.parent = parent; + + this.mutex = mutex == null ? this : mutex; + } + + @Override + public Iterator iterator() { + return parent.iterator(); // Must be synchronized manually by user! + } + + @Override + public void forEach(Consumer action) { + synchronized (mutex) { parent.forEach(action); } + } + + @Override + public int size() { + synchronized (mutex) { return parent.size(); } + } + + @Override + public boolean isEmpty() { + synchronized (mutex) { return parent.isEmpty(); } + } + + @Override + public boolean contains(Vec3i pos) { + synchronized (mutex) { return parent.contains(pos); } + } + + @Override + public boolean add(Vec3i pos) { + synchronized (mutex) { return parent.add(pos); } + } + + @Override + public boolean remove(Vec3i pos) { + synchronized (mutex) { return parent.remove(pos); } + } + + @Override + public boolean contains(int x, int y, int z) { + synchronized (mutex) { return parent.contains(x, y, z); } + } + + @Override + public boolean add(int x, int y, int z) { + synchronized (mutex) { return parent.add(x, y, z); } + } + + @Override + public boolean remove(int x, int y, int z) { + synchronized (mutex) { return parent.remove(x, y, z); } + } + + @Override + public boolean contains(GenericChunk chunk) { + synchronized (mutex) { return parent.contains(chunk); } + } + + @Override + public boolean add(GenericChunk chunk) { + synchronized (mutex) { return parent.add(chunk); } + } + + @Override + public boolean remove(GenericChunk chunk) { + synchronized (mutex) { return parent.remove(chunk); } + } + + @Override + public > void forEachIn(GenericWorld world, + Consumer action) { + synchronized (mutex) { parent.forEachIn(world, action); } + } + + @Override + public boolean containsAll(ChunkSet other) { + synchronized (mutex) { return parent.containsAll(other); } + } + + @Override + public boolean containsAny(ChunkSet other) { + synchronized (mutex) { return parent.containsAny(other); } + } + + @Override + public void addAll(ChunkSet other) { + synchronized (mutex) { parent.addAll(other); } + } + + @Override + public void removeAll(ChunkSet other) { + synchronized (mutex) { parent.removeAll(other); } + } + + @Override + public void retainAll(ChunkSet other) { + synchronized (mutex) { parent.retainAll(other); } + } + + @Override + public void clear() { + synchronized (mutex) { parent.clear(); } + } + + @Override + public boolean containsAll(Iterable other) { + synchronized (mutex) { return parent.containsAll(other); } + } + + @Override + public boolean containsAny(Iterable other) { + synchronized (mutex) { return parent.containsAny(other); } + } + + @Override + public void addAll(Iterable other) { + synchronized (mutex) { parent.addAll(other); } + } + + @Override + public void removeAll(Iterable other) { + synchronized (mutex) { parent.removeAll(other); } + } + + @Override + public void retainAll(Iterable other) { + synchronized (mutex) { parent.retainAll(other); } + } + + @Override + public void removeIf(Predicate condition) { + synchronized (mutex) { parent.removeIf(condition); } + } + + @Override + public void retainIf(Predicate condition) { + synchronized (mutex) { parent.retainIf(condition); } + } + + @Override + public boolean containsAllChunks(Iterable> chunks) { + synchronized (mutex) { return parent.containsAllChunks(chunks); } + } + + @Override + public boolean containsAnyChunks(Iterable> chunks) { + synchronized (mutex) { return parent.containsAnyChunks(chunks); } + } + + @Override + public void addAllChunks(Iterable> chunks) { + synchronized (mutex) { parent.addAllChunks(chunks); } + } + + @Override + public void removeAllChunks(Iterable> chunks) { + synchronized (mutex) { parent.removeAllChunks(chunks); } + } + } - - private ChunkSets() {} } diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java index 8dc3cf8..45a6ad0 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/GenericChunk.java @@ -55,7 +55,7 @@ public interface GenericChunk< } default void forEachBiC(Consumer action) { - VectorUtil.forEachVectorInCuboid( + VectorUtil.iterateCuboid( 0, 0, 0, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, action @@ -63,7 +63,7 @@ public interface GenericChunk< } default void forEachBiW(Consumer action) { - VectorUtil.forEachVectorInCuboid( + VectorUtil.iterateCuboid( Coordinates.getInWorld(getX(), 0), Coordinates.getInWorld(getY(), 0), Coordinates.getInWorld(getZ(), 0), diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java b/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java index f260675..a38ccd7 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java +++ b/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java @@ -112,49 +112,6 @@ public class TickAndUpdateUtil { tickEntity(EntityLogicRegistry.getInstance().get(data.getId()), data, TickContextMutable.start().withServer(server).build()); } -// public static BlockTickContext getBlockTickContext( -// Server server, -// Vec3i blockInWorld -// ) { -// MutableBlockTickContext result = new MutableBlockTickContext(); -// result.init(server, blockInWorld); -// return result; -// } -// -// public static TileTickContext getTileTickContext( -// Server server, -// Vec3i blockInWorld, -// BlockFace face, -// int layer -// ) { -// MutableTileTickContext result = new MutableTileTickContext(); -// result.init(server, blockInWorld, face, layer); -// return result; -// } -// -// public static TileTickContext getTileTickContext( -// Server server, -// TileDataStack stack, -// int index -// ) { -// MutableTileTickContext result = new MutableTileTickContext(); -// result.init(server, stack, index); -// return result; -// } -// -// public static TileTickContext getTileTickContext( -// Server server, -// TileReference ref -// ) { -// MutableTileTickContext result = new MutableTileTickContext(); -// result.init(server, ref); -// return result; -// } -// -// public static TickContext getTickContext(Server server) { -// return getBlockTickContext(server, null); -// } - private TickAndUpdateUtil() {} } diff --git a/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java b/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java index 94d80a1..0395f35 100755 --- a/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java +++ b/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java @@ -73,6 +73,12 @@ public class LayerTestGUI extends GUILayer { 128 )); + panel.addChild(new DynamicLabel( + "ChunkUpdatesDisplay", new Font().withColor(0x37A3E6).deriveShadow(), + () -> "Pending updates: " + Integer.toString(ClientState.getInstance().getWorld().getPendingChunkUpdates()), + 128 + )); + panel.getChildren().forEach(c -> { if (c instanceof Label) { labels.add((Label) c); diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index d00ca09..8aea90f 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -29,8 +29,8 @@ public class TestPlayerControls { private TestPlayerControls() {} - private static final double MODE_SWITCH_MAX_DELAY = 100 * Units.MILLISECONDS; - private static final double MIN_JUMP_DELAY = 200 * Units.MILLISECONDS; + private static final double MODE_SWITCH_MAX_DELAY = 300 * Units.MILLISECONDS; + private static final double MIN_JUMP_DELAY = 400 * Units.MILLISECONDS; // Horizontal and vertical max control speed when flying private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND; From ef928b382f92ad89f9bfd232dd356857da7e2697 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Wed, 30 Dec 2020 14:22:34 +0300 Subject: [PATCH 09/10] Implemented TestChunkCodec and removed chunk generation from client --- .../progressia/common/util/DataBuffer.java | 2 +- .../progressia/common/world/ChunkData.java | 18 ++ .../common/world/tile/TileDataStack.java | 10 + .../progressia/test/TestChunkCodec.java | 210 +++++++++++++++++- 4 files changed, 230 insertions(+), 10 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java b/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java index e2527ea..ab5dbc8 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/DataBuffer.java @@ -25,7 +25,7 @@ public class DataBuffer { @Override public int read() throws IOException { if (DataBuffer.this.position >= buffer.size()) return -1; - int result = buffer.getQuick(DataBuffer.this.position); + int result = buffer.getQuick(DataBuffer.this.position) & 0xFF; ++DataBuffer.this.position; return result; } diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index 2e454e0..af6c9c7 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -393,6 +393,24 @@ implements GenericChunk< report(null, tile); } + + @Override + public void load(TileData tile, int tag) { + addFarthest(tile); + + int assignedTag = getIndexByTag(tag); + + if (assignedTag == tag) return; + if (assignedTag == -1) { + throw new IllegalArgumentException("Tag " + tag + " already used by tile at index " + getIndexByTag(tag)); + } + + indicesByTag[tagsByIndex[size() - 1]] = -1; + tagsByIndex[size() - 1] = tag; + indicesByTag[tag] = size() - 1; + + assert checkConsistency(); + } @Override public TileData remove(int index) { diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java index f0f7609..9f72116 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataStack.java @@ -25,6 +25,16 @@ extends GenericTileStack< */ @Override public abstract void add(int index, TileData tile); + + /** + * Adds the specified tile at the end of this stack assigning it the provided tag. + * This method is useful for copying stacks when preserving tags is necessary. + * @param tile the tile to add + * @param tag the tag to assign the new tile + * @throws IllegalArgumentException if this stack already contains a tile with the + * provided tag + */ + public abstract void load(TileData tile, int tag); /** * Replaces the tile at the specified position in this stack with the specified tile. diff --git a/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java b/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java index 5b00683..8e0a190 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java +++ b/src/main/java/ru/windcorp/progressia/test/TestChunkCodec.java @@ -1,36 +1,228 @@ package ru.windcorp.progressia.test; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; import glm.vec._3.i.Vec3i; +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.hash.TObjectIntHashMap; +import ru.windcorp.jputil.functions.ThrowingConsumer; import ru.windcorp.progressia.common.io.ChunkCodec; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.DecodingException; import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.block.BlockDataRegistry; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.common.world.tile.TileDataRegistry; public class TestChunkCodec extends ChunkCodec { + private static class Palette { + private final List nidToElement = new ArrayList<>(); + private final TObjectIntMap elementToNid = new TObjectIntHashMap<>(); + + public void add(E element) { + if (elementToNid.containsKey(element)) return; + + nidToElement.add(element); + elementToNid.put(element, elementToNid.size()); + } + + public E getByNid(int nid) { + return nidToElement.get(nid); + } + + public int getNid(E element) { + return elementToNid.get(element); + } + + public int size() { + return nidToElement.size(); + } + } + public TestChunkCodec() { super("Test:TestCodec", 0x00); } - @Override - public ChunkData decode(WorldData world, Vec3i position, InputStream data) throws DecodingException, IOException { - ChunkData chunk = new ChunkData(position, world); - TestContent.generateChunk(chunk); - return chunk; - } - @Override public boolean shouldEncode(ChunkData chunk) { return true; } + + /* + * Decoding + */ @Override - public void encode(ChunkData chunk, OutputStream output) throws IOException { - // Do nothing. Heh. + public ChunkData decode(WorldData world, Vec3i position, InputStream inputStream) throws DecodingException, IOException { + DataInput input = new DataInputStream(inputStream); + + BlockData[] blockPalette = readBlockPalette(input); + TileData[] tilePalette = readTilePalette(input); + + ChunkData chunk = new ChunkData(position, world); + readBlocks(input, blockPalette, chunk); + readTiles(input, tilePalette, chunk); + + return chunk; + } + + private BlockData[] readBlockPalette(DataInput input) throws IOException { + BlockData[] palette = new BlockData[input.readInt()]; + + for (int nid = 0; nid < palette.length; ++nid) { + String id = input.readUTF(); + palette[nid] = BlockDataRegistry.getInstance().get(id); + } + + return palette; + } + + private TileData[] readTilePalette(DataInput input) throws IOException { + TileData[] palette = new TileData[input.readInt()]; + + for (int nid = 0; nid < palette.length; ++nid) { + String id = input.readUTF(); + palette[nid] = TileDataRegistry.getInstance().get(id); + } + + return palette; + } + + private void readBlocks(DataInput input, BlockData[] blockPalette, ChunkData chunk) throws IOException { + try { + chunk.forEachBiC(guard(v -> { + chunk.setBlock(v, blockPalette[input.readInt()], false); + })); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } + + private void readTiles(DataInput input, TileData[] tilePalette, ChunkData chunk) throws IOException { + Vec3i bic = new Vec3i(); + + while (true) { + int xOrEndMarker = input.readByte() & 0xFF; + if (xOrEndMarker == 0xFF) break; + + bic.set(xOrEndMarker, input.readByte() & 0xFF, input.readByte() & 0xFF); + BlockFace face = BlockFace.getFaces().get(input.readByte() & 0xFF); + + int tiles = input.readByte() & 0xFF; + + for (int i = 0; i < tiles; ++i) { + TileData tile = tilePalette[input.readInt()]; + int tag = input.readInt(); + chunk.getTiles(bic, face).load(tile, tag); + } + } + } + + /* + * Encoding + */ + + @Override + public void encode(ChunkData chunk, OutputStream outputStream) throws IOException { + + DataOutput output = new DataOutputStream(outputStream); + + Palette blockPalette = createBlockPalette(chunk); + Palette tilePalette = createTilePalette(chunk); + + writeBlockPalette(blockPalette, output); + writeTilePalette(tilePalette, output); + + writeBlocks(chunk, blockPalette, output); + writeTiles(chunk, tilePalette, output); + } + + private Palette createBlockPalette(ChunkData chunk) { + Palette blockPalette = new Palette<>(); + chunk.forEachBiC(v -> blockPalette.add(chunk.getBlock(v))); + return blockPalette; + } + + private Palette createTilePalette(ChunkData chunk) { + Palette tilePalette = new Palette<>(); + chunk.forEachTile((ts, t) -> tilePalette.add(t)); + return tilePalette; + } + + private void writeBlockPalette(Palette blockPalette, DataOutput output) throws IOException { + output.writeInt(blockPalette.size()); + for (int nid = 0; nid < blockPalette.size(); ++nid) { + BlockData block = blockPalette.getByNid(nid); + output.writeUTF(block.getId()); + } + } + + private void writeTilePalette(Palette tilePalette, DataOutput output) throws IOException { + output.writeInt(tilePalette.size()); + for (int nid = 0; nid < tilePalette.size(); ++nid) { + TileData tile = tilePalette.getByNid(nid); + output.writeUTF(tile.getId()); + } + } + + private void writeBlocks(ChunkData chunk, Palette blockPalette, DataOutput output) throws IOException { + try { + chunk.forEachBiC(guard(v -> { + output.writeInt(blockPalette.getNid(chunk.getBlock(v))); + })); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } + + private void writeTiles(ChunkData chunk, Palette tilePalette, DataOutput output) throws IOException { + Vec3i bic = new Vec3i(); + + try { + chunk.forEachTileStack(guard(ts -> { + if (ts.isEmpty()) return; + + ts.getBlockInChunk(bic); + output.writeByte(bic.x); + output.writeByte(bic.y); + output.writeByte(bic.z); + + output.writeByte(ts.getFace().getId()); + output.writeByte(ts.size()); + + for (int index = 0; index < ts.size(); ++index) { + output.writeInt(tilePalette.getNid(ts.get(index))); + output.writeInt(ts.getTagByIndex(index)); + } + })); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + + output.writeByte(0xFF); + } + + private static Consumer guard(ThrowingConsumer action) { + return v -> { + try { + action.accept(v); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; } } From 2947e9acff9495b8f54c125cf946a6a0d63761d1 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Wed, 30 Dec 2020 14:47:43 +0300 Subject: [PATCH 10/10] Add world persistence because I can This is terribly inefficient both storage- and TPS-wise. Enable/disable with ru.windcorp.progressia.test.TestWorldDiskIO.ENABLE --- .../progressia/server/ChunkManager.java | 12 ++- .../progressia/test/TestWorldDiskIO.java | 96 +++++++++++++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/test/TestWorldDiskIO.java diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java index 4810e3a..2705c8e 100644 --- a/src/main/java/ru/windcorp/progressia/server/ChunkManager.java +++ b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java @@ -12,6 +12,7 @@ import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.generic.ChunkSet; import ru.windcorp.progressia.common.world.generic.ChunkSets; import ru.windcorp.progressia.test.TestContent; +import ru.windcorp.progressia.test.TestWorldDiskIO; public class ChunkManager { @@ -120,8 +121,12 @@ public class ChunkManager { WorldData world = getServer().getWorld().getData(); - ChunkData chunk = new ChunkData(chunkPos, world); - TestContent.generateChunk(chunk); + ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world); + if (chunk == null) { + chunk = new ChunkData(chunkPos, world); + TestContent.generateChunk(chunk); + } + world.addChunk(chunk); } @@ -137,8 +142,11 @@ public class ChunkManager { chunkPos.x, chunkPos.y, chunkPos.z )); } + world.removeChunk(chunk); + TestWorldDiskIO.saveChunk(chunk); + } public void sendChunk(Player player, Vec3i chunkPos) { diff --git a/src/main/java/ru/windcorp/progressia/test/TestWorldDiskIO.java b/src/main/java/ru/windcorp/progressia/test/TestWorldDiskIO.java new file mode 100644 index 0000000..7971260 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/TestWorldDiskIO.java @@ -0,0 +1,96 @@ +package ru.windcorp.progressia.test; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.io.ChunkIO; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.DecodingException; +import ru.windcorp.progressia.common.world.WorldData; + +public class TestWorldDiskIO { + + private static final Path SAVE_DIR = Paths.get("tmp_world"); + private static final Logger LOG = LogManager.getLogger("TestWorldDiskIO"); + + private static final boolean ENABLE = true; + + public static void saveChunk(ChunkData chunk) { + if (!ENABLE) return; + + try { + LOG.debug( + "Saving {} {} {}", + chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z + ); + + Files.createDirectories(SAVE_DIR); + + Path path = SAVE_DIR.resolve(String.format( + "chunk_%+d_%+d_%+d.progressia_chunk", + chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z + )); + + try (OutputStream output = new DeflaterOutputStream(new BufferedOutputStream(Files.newOutputStream(path)))) { + ChunkIO.save(chunk, output); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static ChunkData tryToLoad(Vec3i chunkPos, WorldData world) { + if (!ENABLE) return null; + + Path path = SAVE_DIR.resolve(String.format( + "chunk_%+d_%+d_%+d.progressia_chunk", + chunkPos.x, chunkPos.y, chunkPos.z + )); + + if (!Files.exists(path)) { + LOG.debug( + "Not found {} {} {}", + chunkPos.x, chunkPos.y, chunkPos.z + ); + + return null; + } + + try { + ChunkData result = load(path, chunkPos, world); + + LOG.debug( + "Loaded {} {} {}", + chunkPos.x, chunkPos.y, chunkPos.z + ); + + return result; + } catch (Exception e) { + e.printStackTrace(); + LOG.debug( + "Could not load {} {} {}", + chunkPos.x, chunkPos.y, chunkPos.z + ); + return null; + } + } + + private static ChunkData load(Path path, Vec3i chunkPos, WorldData world) throws IOException, DecodingException { + try (InputStream input = new InflaterInputStream(new BufferedInputStream(Files.newInputStream(path)))) { + return ChunkIO.load(world, chunkPos, input); + } + } + +}