diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index cd8c5a4..0d84fe3 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -3,6 +3,7 @@ package ru.windcorp.progressia.client; 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,14 +12,14 @@ 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)); 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)); @@ -27,13 +28,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 +45,15 @@ public class Client { return comms; } + public void onLocalPlayerEntityChanged(EntityData entity, EntityData lastKnownEntity) { + 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 f2eb76a..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.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.entity.EntityData; +import ru.windcorp.progressia.common.world.PacketSetLocalPlayer; +import ru.windcorp.progressia.common.world.PacketWorldChange; // 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.getLocalPlayerEntityId() - ); - - if (entity == null) { - CrashReports.report( - null, - "Player entity with ID %s not found", - new String(StringUtil.toFullHex(packet.getLocalPlayerEntityId())) - ); - } - - 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/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/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/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..5cf3555 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,23 +41,63 @@ 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; - 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; } @@ -63,23 +106,13 @@ public class ChunkRender { return data; } - public void markForUpdate() { - this.needsUpdate = true; + public synchronized void markForUpdate() { + getWorld().markChunkForUpdate(getPosition()); } - public boolean needsUpdate() { - 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(); + public synchronized void render(ShapeRenderHelper renderer) { + if (model == null) { + return; } renderer.pushTransform().translate( @@ -93,7 +126,7 @@ public class ChunkRender { renderer.popTransform(); } - private void buildModel() { + public synchronized void update() { Collection optimizers = ChunkRenderOptimizers.getAllSuppliers().stream() .map(ChunkRenderOptimizerSupplier::createOptimizer) @@ -121,7 +154,6 @@ public class ChunkRender { .forEach(builder::addPart); model = new StaticModel(builder); - needsUpdate = false; } private void buildBlock( @@ -232,5 +264,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..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,79 +20,172 @@ 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; 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 +implements GenericWorld< + BlockRender, + TileRender, + TileRenderStack, + ChunkRender, + EntityRenderable +> { -public class WorldRender { - private final WorldData data; + private final Client client; - private final Map chunks = new HashMap<>(); + 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); } + @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); - } + 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); - 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(); } @@ -108,5 +201,11 @@ public class WorldRender { 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/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/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/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/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/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/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/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..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,10 +19,10 @@ 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 + Consumer action ) { Vec3i cursor = Vectors.grab3i(); @@ -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 863d990..af6c9c7 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,13 +31,19 @@ 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; 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; @@ -54,9 +59,6 @@ public class ChunkData { BLOCK_FACE_COUNT ]; - private final List entities = - Collections.synchronizedList(new ArrayList<>()); - private final Collection listeners = Collections.synchronizedCollection(new ArrayList<>()); @@ -65,6 +67,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 +89,7 @@ public class ChunkData { } } + @Override public TileDataStack getTilesOrNull(Vec3i blockInChunk, BlockFace face) { return tiles[getTileIndex(blockInChunk, face)]; } @@ -98,10 +107,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); @@ -144,10 +155,6 @@ public class ChunkData { face.getId(); } - public List getEntities() { - return entities; - } - private static void checkLocalCoordinates(Vec3i posInChunk) { if (!isInBounds(posInChunk)) { throw new IllegalCoordinatesException( @@ -176,7 +183,7 @@ public class ChunkData { } public void forEachBlock(Consumer action) { - VectorUtil.forEachVectorInCuboid( + VectorUtil.iterateCuboid( 0, 0, 0, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, action @@ -203,26 +210,6 @@ public class ChunkData { forEachTileStack(stack -> stack.forEach(tileData -> action.accept(stack, tileData))); } - 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; } @@ -406,6 +393,24 @@ public class ChunkData { 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/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/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/PacketSendChunk.java b/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java new file mode 100644 index 0000000..a0fe418 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/PacketSendChunk.java @@ -0,0 +1,72 @@ +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 PacketSendChunk extends PacketChunkChange { + + private final DataBuffer data = new DataBuffer(); + private final Vec3i position = new Vec3i(); + + public PacketSendChunk() { + this("Core:SendChunk"); + } + + protected PacketSendChunk(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/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index d719f20..c4d69ef 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -20,28 +20,42 @@ package ru.windcorp.progressia.common.world; 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.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.util.CoordinatePacker; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; -import ru.windcorp.progressia.test.TestContent; +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; -public class WorldData { +public class WorldData +implements GenericWorld< + BlockData, + TileData, + TileDataStack, + ChunkData, + EntityData +> { - private final TLongObjectMap chunksByPos = - new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this); + private final ChunkMap chunksByPos = new LongBasedChunkMap<>( + TCollections.synchronizedMap(new TLongObjectHashMap<>()) + ); private final Collection chunks = - Collections.unmodifiableCollection(chunksByPos.valueCollection()); + 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()); @@ -55,21 +69,37 @@ public class WorldData { } - public void tmp_generate() { - final int size = 10; - 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); - } - } + @Override + public ChunkData getChunk(Vec3i pos) { + return chunksByPos.get(pos); + } + + @Override + public Collection getChunks() { + return chunks; + } + + public ChunkSet getLoadedChunks() { + return chunksByPos.keys(); + } + + @Override + public Collection getEntities() { + return entities; + } + + @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 TLongSet getLoadedEntities() { + return entitiesById.keySet(); + } + private void addChunkListeners(ChunkData chunk) { getListeners().forEach(l -> l.getChunkListeners(this, chunk.getPosition(), chunk::addListener)); } @@ -77,9 +107,7 @@ public class WorldData { 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", @@ -87,11 +115,7 @@ public class WorldData { )); } - chunksByPos.put(key, chunk); - - chunk.forEachEntity(entity -> - entitiesById.put(entity.getEntityId(), entity) - ); + chunksByPos.put(chunk, chunk); chunk.onLoaded(); getListeners().forEach(l -> l.onChunkLoaded(this, chunk)); @@ -101,30 +125,7 @@ public class WorldData { getListeners().forEach(l -> l.beforeChunkUnloaded(this, chunk)); chunk.beforeUnloaded(); - chunk.forEachEntity(entity -> - entitiesById.remove(entity.getEntityId()) - ); - - chunksByPos.remove(getChunkKey(chunk)); - } - - private static long getChunkKey(ChunkData chunk) { - 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)); + chunksByPos.remove(chunk); } public void setBlock(Vec3i blockInWorld, BlockData block, boolean notify) { @@ -139,20 +140,47 @@ public class WorldData { chunk.setBlock(Coordinates.convertInWorldToInChunk(blockInWorld, null), block, notify); } - public Collection getChunks() { - return chunks; - } - - public TLongSet getChunkKeys() { - return chunksByPos.keySet(); - } - public EntityData getEntity(long entityId) { return entitiesById.get(entityId); } - public Collection getEntities() { - return entities; + 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() { 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/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/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/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java index c7eda54..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,19 +2,25 @@ 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.jputil.chars.StringUtil; 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(); 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; @@ -25,20 +31,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())); } @@ -79,6 +76,9 @@ public class EntityData extends StatefulObject implements Collideable { } 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; } @@ -137,5 +137,18 @@ public class EntityData extends StatefulObject implements Collideable { 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/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/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/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/ChunkSets.java b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java new file mode 100644 index 0000000..3e50dd3 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/ChunkSets.java @@ -0,0 +1,270 @@ +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.set.hash.TLongHashSet; + +public class ChunkSets { + + public static ChunkSet newHashSet() { + return new LongBasedChunkSet(new TLongHashSet()); + } + + public static ChunkSet newSyncHashSet(Object mutex) { + return new SynchronizedChunkSet(new LongBasedChunkSet(new TLongHashSet()), mutex); + } + + public static ChunkSet newSyncHashSet() { + return newSyncHashSet(null); + } + + public static ChunkSet empty() { + return EMPTY_SET; + } + + private ChunkSets() {} + + 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(); + } + + }; + + 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); } + } + + } + +} 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..45a6ad0 --- /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.iterateCuboid( + 0, 0, 0, + BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, + action + ); + } + + default void forEachBiW(Consumer action) { + VectorUtil.iterateCuboid( + 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/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/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/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..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 @@ -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. @@ -20,6 +25,16 @@ public abstract class TileDataStack extends GenericTileStack> allChunkLoaders = - Collections.synchronizedCollection(new ArrayList<>()); - - private final TLongSet requested = new TLongHashSet(); - private final TLongSet toLoad = new TLongHashSet(); - private final TLongSet toUnload = 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(v -> requested.add(CoordinatePacker.pack3IntsIntoLong(v))); - } - - private void updateQueues() { - TLongSet loaded = getServer().getWorld().getData().getChunkKeys(); - - toLoad.clear(); - toLoad.addAll(requested); - toLoad.removeAll(loaded); - - toUnload.clear(); - toUnload.addAll(loaded); - toUnload.removeAll(requested); - } - - 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; - }); - } - - 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..2705c8e --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/ChunkManager.java @@ -0,0 +1,204 @@ +package ru.windcorp.progressia.server; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +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.TestContent; +import ru.windcorp.progressia.test.TestWorldDiskIO; + +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) { + + WorldData world = getServer().getWorld().getData(); + + ChunkData chunk = TestWorldDiskIO.tryToLoad(chunkPos, world); + if (chunk == null) { + chunk = new ChunkData(chunkPos, world); + TestContent.generateChunk(chunk); + } + + world.addChunk(chunk); + + } + + public void unloadChunk(Vec3i chunkPos) { + + 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); + + TestWorldDiskIO.saveChunk(chunk); + + } + + public void sendChunk(Player player, 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.set(chunk); + player.getClient().sendPacket(packet); + + getVision(player, true).visible.add(chunkPos); + } + + public void revokeChunk(Player player, Vec3i chunkPos) { + PacketRevokeChunk packet = new PacketRevokeChunk(); + packet.set(chunkPos); + player.getClient().sendPacket(packet); + + 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 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..aab1004 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/EntityManager.java @@ -0,0 +1,177 @@ +package ru.windcorp.progressia.server; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +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" + ); + } + + PacketSendEntity packet = new PacketSendEntity(); + packet.set(entity); + player.getClient().sendPacket(packet); + + getVision(player, true).visible.add(entityId); + } + + public void revokeEntity(Player player, long 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 8a6001e..c77457a 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() { @@ -34,22 +36,28 @@ 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); + } } } } } + + 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 d32b7ea..4a4bb65 100644 --- a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java +++ b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java @@ -4,8 +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 { @@ -29,14 +34,35 @@ public class PlayerManager { public EntityData conjurePlayerEntity(String login) { // TODO Live up to the name if (TestContent.PLAYER_LOGIN.equals(login)) { - // TODO load appropriate chunks - return getServer().getWorld().getData().getEntity(TestContent.PLAYER_ENTITY_ID); + + Vec3i chunkPos = Vectors.ZERO_3i; + + if (getServer().getWorld().getChunk(chunkPos) == null) { + getServer().getChunkManager().loadChunk(chunkPos); + } + + 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 ac59f00..40df32b 100644 --- a/src/main/java/ru/windcorp/progressia/server/Server.java +++ b/src/main/java/ru/windcorp/progressia/server/Server.java @@ -29,8 +29,10 @@ 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 EntityManager entityManager; private final TaskQueue taskQueue = new TaskQueue(this::isServerThread); @@ -39,8 +41,15 @@ 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); + this.entityManager = new EntityManager(this); - schedule(this::scheduleChunkTicks); + schedule(this::scheduleWorldTicks); + schedule(chunkManager::tick); + schedule(entityManager::tick); } /** @@ -64,6 +73,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 @@ -187,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..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(); } 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..c82a582 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -1,20 +1,18 @@ package ru.windcorp.progressia.server.comms; -import java.io.IOException; 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; 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.PacketSetLocalPlayer; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.server.Player; import ru.windcorp.progressia.server.Server; @@ -45,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)); @@ -61,29 +59,15 @@ 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()) { - PacketLoadChunk packet = new PacketLoadChunk("Core:LoadChunk"); - packet.getPosition().set( - chunk.getPosition().x, - chunk.getPosition().y, - chunk.getPosition().z - ); - - try { - ChunkIO.save(chunk, packet.getData().getOutputStream()); - } catch (IOException e) { - CrashReports.report(e, "ClientManager fjcked up. javahorse stupid"); - } - client.sendPacket(packet); - } - client.sendPacket(new PacketSetLocalPlayer(entity.getEntityId())); + PacketSetLocalPlayer packet = new PacketSetLocalPlayer(); + LogManager.getLogger().info("Sending local player ID {}", EntityData.formatEntityId(entity.getEntityId())); + packet.set(entity.getEntityId()); + client.sendPacket(packet); } public void disconnectClient(Client client) { @@ -91,7 +75,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 +87,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).isChunkVisible(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).isChunkVisible(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..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,11 +1,40 @@ package ru.windcorp.progressia.server.comms; -public abstract class ClientPlayer extends Client { +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 { + + private Player player; public ClientPlayer(int id) { super(id); } + public Player getPlayer() { + return player; + } + + 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 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 51de5d4..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,14 +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; @@ -26,7 +24,12 @@ import ru.windcorp.progressia.server.world.tile.TileLogic; import ru.windcorp.progressia.server.world.tile.TileLogicRegistry; import ru.windcorp.progressia.server.world.tile.TileLogicStack; -public class ChunkLogic { +public class ChunkLogic implements GenericChunk< + ChunkLogic, + BlockLogic, + TileLogic, + TileLogicStack +> { private final WorldLogic world; private final ChunkData data; @@ -43,31 +46,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 +86,6 @@ public class ChunkLogic { return data; } - public Vec3i getPosition() { - return getData().getPosition(); - } - public boolean hasTickingBlocks() { return !tickingBlocks.isEmpty(); } @@ -105,38 +109,6 @@ public class ChunkLogic { }); } - public void forEachEntity(BiConsumer action) { - getData().forEachEntity(data -> { - action.accept( - EntityLogicRegistry.getInstance().get(data.getId()), - data - ); - }); - } - - 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 +126,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 +152,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/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/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..e5ec697 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java @@ -2,28 +2,38 @@ 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.tasks.TickEntitiesTask; +import ru.windcorp.progressia.server.world.ticking.Evaluation; import ru.windcorp.progressia.server.world.tile.TileLogic; -import ru.windcorp.progressia.test.TestChunkSender; +import ru.windcorp.progressia.server.world.tile.TileLogicStack; -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; private final Map chunks = new HashMap<>(); + private final Evaluation tickEntitiesTask = new TickEntitiesTask(); + public WorldLogic(WorldData data, Server server) { this.data = data; this.server = server; @@ -41,7 +51,25 @@ public class WorldLogic { }); data.addListener(ChunkDataListeners.createAdder(new UpdateTriggerer(server))); - 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 Evaluation getTickEntitiesTask() { + return tickEntitiesTask; } public Server getServer() { @@ -55,53 +83,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/tasks/AddTile.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/AddTile.java index 4402999..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,53 +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 - 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 be587f8..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 @@ -2,35 +2,49 @@ package ru.windcorp.progressia.server.world.tasks; import java.util.function.Consumer; -import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; -import ru.windcorp.progressia.common.world.WorldData; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.Vectors; +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()); - server.getClientManager().broadcastGamePacket(packet); + affectLocal(server); + sendPacket(server); + } + + protected void affectLocal(Server server) { + packet.apply(server.getWorld().getData()); } - /** - * Invoked by both Change and Packet. - * @param world the world to affect - */ - protected abstract void affectCommon(WorldData world); + protected void sendPacket(Server server) { + 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; + } + + 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 f671914..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().broadcastGamePacket(packet); + 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 7601a83..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,43 +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); + 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 f34694c..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,44 +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 - 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/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/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/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(); 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/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); + } + }; } } 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 672db8f..0000000 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java +++ /dev/null @@ -1,55 +0,0 @@ -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.WorldData; -import ru.windcorp.progressia.common.world.WorldDataListener; -import ru.windcorp.progressia.common.world.entity.EntityData; -import ru.windcorp.progressia.server.Server; - -public class TestChunkSender implements WorldDataListener { - - 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"); - - packet.getPosition().set( - chunk.getPosition().x, - chunk.getPosition().y, - chunk.getPosition().z - ); - - try { - ChunkIO.save(chunk, packet.getData().getOutputStream()); - } catch (IOException e) { - CrashReports.report(e, "TestChunkSender fjcked up. javahorse stupid"); - } - - server.getClientManager().broadcastGamePacket(packet); - - tmp_sendPlayerIfPossible(world, chunk); - } - - 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); - server.getClientManager().broadcastGamePacket(new PacketSetLocalPlayer(e.getEntityId())); - } - } - -} diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 6f64561..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 74fc33c..8aea90f 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 { @@ -28,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; @@ -60,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; } @@ -96,10 +97,17 @@ 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) { - if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) { + if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) { return; } 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); + } + } + +}