Merge branch 'multichunk'
This commit is contained in:
commit
6e6701d2e5
@ -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)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -192,6 +192,7 @@ public class VertexBufferObject implements OpenGLDeletable {
|
||||
return usage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ public class Shader implements OpenGLDeletable {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -264,6 +264,7 @@ public class ShapeRenderProgram extends Program {
|
||||
|
||||
private final List<Vertex> 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()
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -55,6 +55,7 @@ public abstract class MutableString {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return get();
|
||||
}
|
||||
|
@ -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<TileDataStack, TileRenderStackImpl> 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<ChunkRenderOptimizer> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<ChunkData, ChunkRender> chunks = new HashMap<>();
|
||||
private final Map<ChunkData, ChunkRender> chunks =
|
||||
Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<EntityData, EntityRenderable> 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<ChunkRender> getChunks() {
|
||||
return chunks.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<EntityRenderable> 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<Vec3i> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -116,6 +116,8 @@ public abstract class NPedModel extends EntityRenderable {
|
||||
this.body = body;
|
||||
this.head = head;
|
||||
this.scale = scale;
|
||||
|
||||
evaluateAngles();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ public class SizeLimitedList<E> extends ForwardingList<E> {
|
||||
return standardAddAll(index, elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
checkMaxSize();
|
||||
return delegate().add(e);
|
||||
|
@ -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<Vec3i> action
|
||||
Consumer<? super Vec3i> action
|
||||
) {
|
||||
Vec3i cursor = Vectors.grab3i();
|
||||
|
||||
@ -38,6 +38,57 @@ public class VectorUtil {
|
||||
Vectors.release(cursor);
|
||||
}
|
||||
|
||||
public static void iterateCuboid(
|
||||
Vec3i vMin, Vec3i vMax,
|
||||
Consumer<? super Vec3i> 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<? super Vec3i> 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<? super Vec3i> 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<? super Vec3i> action
|
||||
) {
|
||||
iterateCuboidAround(cx, cy, cz, diameter, diameter, diameter, action);
|
||||
}
|
||||
|
||||
public static void iterateCuboidAround(
|
||||
Vec3i center,
|
||||
int diameter,
|
||||
Consumer<? super Vec3i> 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);
|
||||
|
@ -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<EntityData> entities =
|
||||
Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private final Collection<ChunkDataListener> 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<EntityData> 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<Vec3i> 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<EntityData> 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) {
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
@ -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<ChunkData> chunksByPos =
|
||||
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this);
|
||||
private final ChunkMap<ChunkData> chunksByPos = new LongBasedChunkMap<>(
|
||||
TCollections.synchronizedMap(new TLongObjectHashMap<>())
|
||||
);
|
||||
|
||||
private final Collection<ChunkData> chunks =
|
||||
Collections.unmodifiableCollection(chunksByPos.valueCollection());
|
||||
Collections.unmodifiableCollection(chunksByPos.values());
|
||||
|
||||
private final TLongObjectMap<EntityData> entitiesById =
|
||||
new TSynchronizedLongObjectMap<>(new TLongObjectHashMap<>(), this);
|
||||
TCollections.synchronizedMap(new TLongObjectHashMap<>());
|
||||
|
||||
private final Collection<EntityData> 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<ChunkData> getChunks() {
|
||||
return chunks;
|
||||
}
|
||||
|
||||
public ChunkSet getLoadedChunks() {
|
||||
return chunksByPos.keys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<EntityData> getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachEntity(Consumer<? super EntityData> 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<ChunkData> getChunks() {
|
||||
return chunks;
|
||||
}
|
||||
|
||||
public TLongSet getChunkKeys() {
|
||||
return chunksByPos.keySet();
|
||||
}
|
||||
|
||||
public EntityData getEntity(long entityId) {
|
||||
return entitiesById.get(entityId);
|
||||
}
|
||||
|
||||
public Collection<EntityData> 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() {
|
||||
|
@ -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) {}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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<V> {
|
||||
|
||||
/*
|
||||
* 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<? super Vec3i, ? super V, ? extends V> 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 <C extends GenericChunk<C, ?, ?, ?>> V compute(C chunk, BiFunction<? super C, ? super V, ? extends V> remappingFunction) {
|
||||
V newValue = remappingFunction.apply(chunk, get(chunk));
|
||||
|
||||
if (newValue == null) {
|
||||
remove(chunk);
|
||||
} else {
|
||||
put(chunk, newValue);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Views
|
||||
*/
|
||||
|
||||
Collection<V> values();
|
||||
ChunkSet keys();
|
||||
|
||||
/*
|
||||
* Bulk operations
|
||||
*/
|
||||
|
||||
boolean removeIf(BiPredicate<? super Vec3i, ? super V> condition);
|
||||
void forEach(BiConsumer<? super Vec3i, ? super V> action);
|
||||
|
||||
default <C extends GenericChunk<C, ?, ?, ?>> void forEachIn(GenericWorld<?, ?, ?, C, ?> world, BiConsumer<? super C, ? super V> action) {
|
||||
forEach((pos, value) -> {
|
||||
C chunk = world.getChunk(pos);
|
||||
if (chunk == null) return;
|
||||
action.accept(chunk, value);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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<Vec3i> {
|
||||
|
||||
/*
|
||||
* 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 <C extends GenericChunk<C, ?, ?, ?>> void forEachIn(GenericWorld<?, ?, ?, C, ?> world, Consumer<? super C> 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<? extends Vec3i> other) {
|
||||
boolean[] hasMissing = new boolean[] { false };
|
||||
|
||||
other.forEach(v -> {
|
||||
if (!contains(v)) {
|
||||
hasMissing[0] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return hasMissing[0];
|
||||
}
|
||||
|
||||
default boolean containsAny(Iterable<? extends Vec3i> other) {
|
||||
boolean[] hasPresent = new boolean[] { false };
|
||||
|
||||
other.forEach(v -> {
|
||||
if (contains(v)) {
|
||||
hasPresent[0] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return hasPresent[0];
|
||||
}
|
||||
|
||||
default void addAll(Iterable<? extends Vec3i> other) {
|
||||
other.forEach(this::add);
|
||||
}
|
||||
|
||||
default void removeAll(Iterable<? extends Vec3i> other) {
|
||||
other.forEach(this::remove);
|
||||
}
|
||||
|
||||
default void retainAll(Iterable<? extends Vec3i> 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<? extends Vec3i> collection = null;
|
||||
int otherSize = -1;
|
||||
|
||||
if (other instanceof Set<?>) {
|
||||
collection = (Set<? extends Vec3i>) other;
|
||||
} else if (other instanceof Collection<?>) {
|
||||
Collection<? extends Vec3i> otherAsCollection = ((Collection<? extends Vec3i>) other);
|
||||
otherSize = otherAsCollection.size();
|
||||
|
||||
if (otherSize < threshold) {
|
||||
collection = otherAsCollection;
|
||||
}
|
||||
}
|
||||
|
||||
if (collection != null) {
|
||||
final Collection<? extends Vec3i> 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<? super Vec3i> condition) {
|
||||
for (Iterator<? extends Vec3i> it = iterator(); it.hasNext();) {
|
||||
if (condition.test(it.next())) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void retainIf(Predicate<? super Vec3i> condition) {
|
||||
for (Iterator<? extends Vec3i> it = iterator(); it.hasNext();) {
|
||||
if (!condition.test(it.next())) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default boolean containsAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
|
||||
boolean[] hasMissing = new boolean[] { false };
|
||||
|
||||
chunks.forEach(c -> {
|
||||
if (!contains(c.getPosition())) {
|
||||
hasMissing[0] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return hasMissing[0];
|
||||
}
|
||||
|
||||
default boolean containsAnyChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
|
||||
boolean[] hasPresent = new boolean[] { false };
|
||||
|
||||
chunks.forEach(c -> {
|
||||
if (contains(c.getPosition())) {
|
||||
hasPresent[0] = true;
|
||||
}
|
||||
});
|
||||
|
||||
return hasPresent[0];
|
||||
}
|
||||
|
||||
default void addAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
|
||||
chunks.forEach(this::add);
|
||||
}
|
||||
|
||||
default void removeAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
|
||||
chunks.forEach(this::remove);
|
||||
}
|
||||
|
||||
}
|
@ -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<Vec3i> iterator() {
|
||||
return new Iterator<Vec3i>() {
|
||||
@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<Vec3i> iterator() {
|
||||
return parent.iterator(); // Must be synchronized manually by user!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super Vec3i> 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 <C extends GenericChunk<C, ?, ?, ?>> void forEachIn(GenericWorld<?, ?, ?, C, ?> world,
|
||||
Consumer<? super C> 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<? extends Vec3i> other) {
|
||||
synchronized (mutex) { return parent.containsAll(other); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAny(Iterable<? extends Vec3i> other) {
|
||||
synchronized (mutex) { return parent.containsAny(other); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(Iterable<? extends Vec3i> other) {
|
||||
synchronized (mutex) { parent.addAll(other); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll(Iterable<? extends Vec3i> other) {
|
||||
synchronized (mutex) { parent.removeAll(other); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retainAll(Iterable<? extends Vec3i> other) {
|
||||
synchronized (mutex) { parent.retainAll(other); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIf(Predicate<? super Vec3i> condition) {
|
||||
synchronized (mutex) { parent.removeIf(condition); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retainIf(Predicate<? super Vec3i> condition) {
|
||||
synchronized (mutex) { parent.retainIf(condition); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
|
||||
synchronized (mutex) { return parent.containsAllChunks(chunks); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAnyChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
|
||||
synchronized (mutex) { return parent.containsAnyChunks(chunks); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
|
||||
synchronized (mutex) { parent.addAllChunks(chunks); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllChunks(Iterable<? extends GenericChunk<?, ?, ?, ?>> chunks) {
|
||||
synchronized (mutex) { parent.removeAllChunks(chunks); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.windcorp.progressia.common.world.generic;
|
||||
|
||||
public interface GenericBlock {
|
||||
|
||||
String getId();
|
||||
|
||||
}
|
@ -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<Self, B, T, TS>,
|
||||
B extends GenericBlock,
|
||||
T extends GenericTile,
|
||||
TS extends GenericTileStack<TS, T, Self>
|
||||
> {
|
||||
|
||||
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<? super Vec3i> action) {
|
||||
VectorUtil.iterateCuboid(
|
||||
0, 0, 0,
|
||||
BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK,
|
||||
action
|
||||
);
|
||||
}
|
||||
|
||||
default void forEachBiW(Consumer<? super Vec3i> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.windcorp.progressia.common.world.generic;
|
||||
|
||||
public interface GenericTile {
|
||||
|
||||
String getId();
|
||||
|
||||
}
|
@ -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<T extends Namespaced, C>
|
||||
public abstract class GenericTileStack<
|
||||
Self extends GenericTileStack<Self, T, C>,
|
||||
T extends GenericTile,
|
||||
C extends GenericChunk<C, ?, T, Self>
|
||||
>
|
||||
extends AbstractList<T>
|
||||
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() {
|
@ -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<TS, T, C>,
|
||||
C extends GenericChunk<C, B, T, TS>,
|
||||
E extends GenericEntity
|
||||
> {
|
||||
|
||||
Collection<C> getChunks();
|
||||
C getChunk(Vec3i pos);
|
||||
|
||||
Collection<E> 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<? super C> action) {
|
||||
getChunks().forEach(action);
|
||||
}
|
||||
|
||||
/*
|
||||
* Entities
|
||||
*/
|
||||
|
||||
default void forEachEntity(Consumer<? super E> action) {
|
||||
getEntities().forEach(action);
|
||||
}
|
||||
|
||||
default void forEachEntityIn(Vec3i min, Vec3i max, Consumer<? super E> 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<? super E> 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<? super E> 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<? super E> 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<? super E> action) {
|
||||
forEachEntity(e -> {
|
||||
if (id.equals(e.getId())) {
|
||||
action.accept(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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<V> implements ChunkMap<V> {
|
||||
|
||||
protected final TLongObjectMap<V> impl;
|
||||
private final ChunkSet keys;
|
||||
|
||||
public LongBasedChunkMap(TLongObjectMap<V> 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<V> values() {
|
||||
return impl.valueCollection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkSet keys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIf(BiPredicate<? super Vec3i, ? super V> 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<? super Vec3i, ? super V> action) {
|
||||
Vec3i v = Vectors.grab3i();
|
||||
|
||||
impl.forEachEntry((key, value) -> {
|
||||
action.accept(getVector(key, v), value);
|
||||
return true;
|
||||
});
|
||||
|
||||
Vectors.release(v);
|
||||
}
|
||||
|
||||
}
|
@ -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<? extends Vec3i> 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<Vec3i> 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<? extends Vec3i>) other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAny(ChunkSet other) {
|
||||
return ChunkSet.super.containsAny((Iterable<? extends Vec3i>) other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(ChunkSet other) {
|
||||
if (other instanceof LongBasedChunkSet) {
|
||||
impl.addAll(((LongBasedChunkSet) other).impl);
|
||||
return;
|
||||
}
|
||||
|
||||
ChunkSet.super.addAll((Iterable<? extends Vec3i>) other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll(ChunkSet other) {
|
||||
if (other instanceof LongBasedChunkSet) {
|
||||
impl.removeAll(((LongBasedChunkSet) other).impl);
|
||||
return;
|
||||
}
|
||||
|
||||
ChunkSet.super.removeAll((Iterable<? extends Vec3i>) other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retainAll(ChunkSet other) {
|
||||
if (other instanceof LongBasedChunkSet) {
|
||||
impl.retainAll(((LongBasedChunkSet) other).impl);
|
||||
return;
|
||||
}
|
||||
|
||||
ChunkSet.super.retainAll((Iterable<? extends Vec3i>) other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
impl.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super Vec3i> action) {
|
||||
Vec3i v = Vectors.grab3i();
|
||||
|
||||
impl.forEach(key -> {
|
||||
getVector(key, v);
|
||||
action.accept(v);
|
||||
return true;
|
||||
});
|
||||
|
||||
Vectors.release(v);
|
||||
}
|
||||
|
||||
private class IteratorImpl implements Iterator<Vec3i> {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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<TileData, ChunkData> {
|
||||
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<TileData, ChunkData
|
||||
*/
|
||||
@Override
|
||||
public abstract void add(int index, TileData tile);
|
||||
|
||||
/**
|
||||
* Adds the specified tile at the end of this stack assigning it the provided tag.
|
||||
* This method is useful for copying stacks when preserving tags is necessary.
|
||||
* @param tile the tile to add
|
||||
* @param tag the tag to assign the new tile
|
||||
* @throws IllegalArgumentException if this stack already contains a tile with the
|
||||
* provided tag
|
||||
*/
|
||||
public abstract void load(TileData tile, int tag);
|
||||
|
||||
/**
|
||||
* Replaces the tile at the specified position in this stack with the specified tile.
|
||||
@ -54,15 +69,6 @@ public abstract class TileDataStack extends GenericTileStack<TileData, ChunkData
|
||||
|
||||
public abstract int getTagByIndex(int index);
|
||||
|
||||
/*
|
||||
* Implementation
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Vec3i getChunkPos() {
|
||||
return getChunk().getPosition();
|
||||
}
|
||||
|
||||
/*
|
||||
* Aliases and overloads
|
||||
*/
|
||||
|
@ -1,92 +0,0 @@
|
||||
package ru.windcorp.progressia.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import gnu.trove.set.TLongSet;
|
||||
import gnu.trove.set.hash.TLongHashSet;
|
||||
import ru.windcorp.progressia.common.util.CoordinatePacker;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.test.TestContent;
|
||||
|
||||
public class ChunkLoadManager {
|
||||
|
||||
private final Server server;
|
||||
|
||||
private final Collection<Collection<? extends ChunkLoader>> 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));
|
||||
|
||||
}
|
||||
|
||||
}
|
204
src/main/java/ru/windcorp/progressia/server/ChunkManager.java
Normal file
204
src/main/java/ru/windcorp/progressia/server/ChunkManager.java
Normal file
@ -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<Player, PlayerVision> 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;
|
||||
}
|
||||
|
||||
}
|
177
src/main/java/ru/windcorp/progressia/server/EntityManager.java
Normal file
177
src/main/java/ru/windcorp/progressia/server/EntityManager.java
Normal file
@ -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<Player, PlayerVision> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<Client> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<EntityLogic, EntityData> 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());
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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<ChunkData, ChunkLogic> 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<ChunkLogic> getChunks() {
|
||||
return chunks.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<EntityData> 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<TileLogic> getTiles(Vec3i blockInWorld, BlockFace face) {
|
||||
return getTilesImpl(blockInWorld, face, true);
|
||||
}
|
||||
|
||||
public List<TileLogic> getTilesOrNull(Vec3i blockInWorld, BlockFace face) {
|
||||
return getTilesImpl(blockInWorld, face, false);
|
||||
}
|
||||
|
||||
private List<TileLogic> getTilesImpl(Vec3i blockInWorld, BlockFace face, boolean createIfMissing) {
|
||||
ChunkLogic chunk = getChunkByBlock(blockInWorld);
|
||||
if (chunk == null) return null;
|
||||
|
||||
Vec3i blockInChunk = Coordinates.convertInWorldToInChunk(blockInWorld, null);
|
||||
|
||||
List<TileLogic> result =
|
||||
createIfMissing
|
||||
? chunk.getTiles(blockInChunk, face)
|
||||
: chunk.getTilesOrNull(blockInChunk, face);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public TileLogic getTile(Vec3i blockInWorld, BlockFace face, int layer) {
|
||||
List<TileLogic> tiles = getTilesOrNull(blockInWorld, face);
|
||||
if (tiles == null || tiles.size() <= layer) return null;
|
||||
|
||||
return tiles.get(layer);
|
||||
}
|
||||
|
||||
public Collection<ChunkLogic> getChunks() {
|
||||
return chunks.values();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<PacketAddTile> {
|
||||
|
||||
public AddTile(Consumer<? super CachedChange> 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());
|
||||
}
|
||||
|
||||
}
|
@ -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<P extends PacketChunkChange> extends CachedWorldChange<P> {
|
||||
|
||||
public CachedChunkChange(Consumer<? super CachedChange> disposer, P packet) {
|
||||
super(disposer, packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getRelevantChunk(Vec3i output) {
|
||||
getPacket().getAffectedChunk(output);
|
||||
}
|
||||
|
||||
}
|
@ -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<P extends PacketWorldChange> extends CachedChange {
|
||||
|
||||
private final PacketWorldChange packet;
|
||||
private final P packet;
|
||||
|
||||
public CachedWorldChange(Consumer<? super CachedChange> disposer, String packetId) {
|
||||
public CachedWorldChange(Consumer<? super CachedChange> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<EntityData>) 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
|
||||
|
@ -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<PacketRemoveTile> {
|
||||
|
||||
public RemoveTile(Consumer<? super CachedChange> 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());
|
||||
}
|
||||
|
||||
}
|
@ -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<PacketSetBlock> {
|
||||
|
||||
public SetBlock(Consumer<? super CachedChange> 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());
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<TileLogic, ChunkLogic> {
|
||||
|
||||
// TODO add @Deprecated or smth similar to all modification methods
|
||||
public abstract class TileLogicStack
|
||||
extends GenericTileStack<
|
||||
TileLogicStack,
|
||||
TileLogic,
|
||||
ChunkLogic
|
||||
> {
|
||||
|
||||
public abstract TileDataStack getData();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<E> {
|
||||
private final List<E> nidToElement = new ArrayList<>();
|
||||
private final TObjectIntMap<E> 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<BlockData> blockPalette = createBlockPalette(chunk);
|
||||
Palette<TileData> tilePalette = createTilePalette(chunk);
|
||||
|
||||
writeBlockPalette(blockPalette, output);
|
||||
writeTilePalette(tilePalette, output);
|
||||
|
||||
writeBlocks(chunk, blockPalette, output);
|
||||
writeTiles(chunk, tilePalette, output);
|
||||
}
|
||||
|
||||
private Palette<BlockData> createBlockPalette(ChunkData chunk) {
|
||||
Palette<BlockData> blockPalette = new Palette<>();
|
||||
chunk.forEachBiC(v -> blockPalette.add(chunk.getBlock(v)));
|
||||
return blockPalette;
|
||||
}
|
||||
|
||||
private Palette<TileData> createTilePalette(ChunkData chunk) {
|
||||
Palette<TileData> tilePalette = new Palette<>();
|
||||
chunk.forEachTile((ts, t) -> tilePalette.add(t));
|
||||
return tilePalette;
|
||||
}
|
||||
|
||||
private void writeBlockPalette(Palette<BlockData> 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<TileData> 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<BlockData> 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<TileData> 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 <V> Consumer<V> guard(ThrowingConsumer<? super V, IOException> action) {
|
||||
return v -> {
|
||||
try {
|
||||
action.accept(v);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user