Hopefully fixed #8 by enforcing only one change per tile per tick

- TickerCoordinator.pendingChanges is now a HashSet
- TickerCoordinator now makes some best-effort checks to make sure the
world does not change during evaluation phase
- Extracted Cached{Block,Tile}Change
  - these classes implement hashCode() and equals() based on the
location of the block/tile they affect, thus ensuring that only one per
block/tile remain in the aforementioned set
- Similarly extracted PacketAffect{Block,Tile,Entity}
- Renamed a bunch of packets
This commit is contained in:
OLEGSHA 2021-01-03 13:21:47 +03:00
parent cc031529a1
commit daba418361
25 changed files with 402 additions and 138 deletions

View File

@ -7,7 +7,7 @@ import ru.windcorp.progressia.common.comms.CommsListener;
import ru.windcorp.progressia.common.comms.packets.Packet; import ru.windcorp.progressia.common.comms.packets.Packet;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.PacketSetLocalPlayer; import ru.windcorp.progressia.common.world.PacketSetLocalPlayer;
import ru.windcorp.progressia.common.world.PacketWorldChange; import ru.windcorp.progressia.common.world.PacketAffectWorld;
// TODO refactor with no mercy // TODO refactor with no mercy
public class DefaultClientCommsListener implements CommsListener { public class DefaultClientCommsListener implements CommsListener {
@ -20,8 +20,8 @@ public class DefaultClientCommsListener implements CommsListener {
@Override @Override
public void onPacketReceived(Packet packet) { public void onPacketReceived(Packet packet) {
if (packet instanceof PacketWorldChange) { if (packet instanceof PacketAffectWorld) {
((PacketWorldChange) packet).apply( ((PacketAffectWorld) packet).apply(
getClient().getWorld().getData() getClient().getWorld().getData()
); );
} else if (packet instanceof PacketSetLocalPlayer) { } else if (packet instanceof PacketSetLocalPlayer) {

View File

@ -2,9 +2,9 @@ package ru.windcorp.progressia.common.world;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
public abstract class PacketChunkChange extends PacketWorldChange { public abstract class PacketAffectChunk extends PacketAffectWorld {
public PacketChunkChange(String id) { public PacketAffectChunk(String id) {
super(id); super(id);
} }

View File

@ -2,9 +2,9 @@ package ru.windcorp.progressia.common.world;
import ru.windcorp.progressia.common.comms.packets.Packet; import ru.windcorp.progressia.common.comms.packets.Packet;
public abstract class PacketWorldChange extends Packet { public abstract class PacketAffectWorld extends Packet {
public PacketWorldChange(String id) { public PacketAffectWorld(String id) {
super(id); super(id);
} }

View File

@ -6,7 +6,7 @@ import java.io.IOException;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
public class PacketRevokeChunk extends PacketChunkChange { public class PacketRevokeChunk extends PacketAffectChunk {
private final Vec3i position = new Vec3i(); private final Vec3i position = new Vec3i();

View File

@ -10,7 +10,7 @@ import ru.windcorp.progressia.common.state.IOContext;
import ru.windcorp.progressia.common.util.DataBuffer; import ru.windcorp.progressia.common.util.DataBuffer;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
public class PacketSendChunk extends PacketChunkChange { public class PacketSendChunk extends PacketAffectChunk {
private final DataBuffer data = new DataBuffer(); private final DataBuffer data = new DataBuffer();
private final Vec3i position = new Vec3i(); private final Vec3i position = new Vec3i();

View File

@ -0,0 +1,45 @@
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.PacketAffectChunk;
public abstract class PacketAffectBlock extends PacketAffectChunk {
private final Vec3i blockInWorld = new Vec3i();
public PacketAffectBlock(String id) {
super(id);
}
public Vec3i getBlockInWorld() {
return blockInWorld;
}
public void set(Vec3i blockInWorld) {
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
}
@Override
public void read(DataInput input) throws IOException, DecodingException {
this.blockInWorld.set(input.readInt(), input.readInt(), 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);
}
@Override
public void getAffectedChunk(Vec3i output) {
Coordinates.convertInWorldToChunk(this.blockInWorld, output);
}
}

View File

@ -5,15 +5,12 @@ import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import glm.vec._3.i.Vec3i; 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.DecodingException;
import ru.windcorp.progressia.common.world.PacketChunkChange;
import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldData;
public class PacketSetBlock extends PacketChunkChange { public class PacketSetBlock extends PacketAffectBlock {
private String id; private String blockId;
private final Vec3i blockInWorld = new Vec3i();
public PacketSetBlock() { public PacketSetBlock() {
this("Core:SetBlock"); this("Core:SetBlock");
@ -23,34 +20,31 @@ public class PacketSetBlock extends PacketChunkChange {
super(id); super(id);
} }
public String getBlockId() {
return blockId;
}
public void set(BlockData block, Vec3i blockInWorld) { public void set(BlockData block, Vec3i blockInWorld) {
this.id = block.getId(); super.set(blockInWorld);
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); this.blockId = block.getId();
} }
@Override @Override
public void read(DataInput input) throws IOException, DecodingException { public void read(DataInput input) throws IOException, DecodingException {
this.id = input.readUTF(); super.read(input);
this.blockInWorld.set(input.readInt(), input.readInt(), input.readInt()); this.blockId = input.readUTF();
} }
@Override @Override
public void write(DataOutput output) throws IOException { public void write(DataOutput output) throws IOException {
output.writeUTF(this.id); super.write(output);
output.writeInt(this.blockInWorld.x); output.writeUTF(this.blockId);
output.writeInt(this.blockInWorld.y);
output.writeInt(this.blockInWorld.z);
} }
@Override @Override
public void apply(WorldData world) { public void apply(WorldData world) {
BlockData block = BlockDataRegistry.getInstance().get(id); BlockData block = BlockDataRegistry.getInstance().get(getBlockId());
world.setBlock(blockInWorld, block, true); world.setBlock(getBlockInWorld(), block, true);
}
@Override
public void getAffectedChunk(Vec3i output) {
Coordinates.convertInWorldToChunk(this.blockInWorld, output);
} }
} }

View File

@ -0,0 +1,42 @@
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.DecodingException;
import ru.windcorp.progressia.common.world.PacketAffectWorld;
import ru.windcorp.progressia.common.world.WorldData;
public class PacketAffectEntity extends PacketAffectWorld {
private long entityId;
public PacketAffectEntity(String id) {
super(id);
}
public long getEntityId() {
return entityId;
}
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);
}
@Override
public void apply(WorldData world) {
world.removeEntity(this.entityId);
}
}

View File

@ -8,44 +8,27 @@ import ru.windcorp.progressia.common.state.IOContext;
import ru.windcorp.progressia.common.util.DataBuffer; import ru.windcorp.progressia.common.util.DataBuffer;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.DecodingException; 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.WorldData;
public class PacketEntityChange extends PacketWorldChange { public class PacketChangeEntity extends PacketAffectEntity {
private long entityId;
private final DataBuffer buffer = new DataBuffer(); private final DataBuffer buffer = new DataBuffer();
public PacketEntityChange() { public PacketChangeEntity() {
super("Core:EntityChange"); super("Core:EntityChange");
} }
protected PacketEntityChange(String id) { protected PacketChangeEntity(String id) {
super(id); super(id);
} }
public long getEntityId() {
return entityId;
}
public void setEntityId(long entityId) {
this.entityId = entityId;
}
public DataBuffer getBuffer() { public DataBuffer getBuffer() {
return buffer; return buffer;
} }
public DataInput getReader() {
return buffer.getReader();
}
public DataOutput getWriter() {
return buffer.getWriter();
}
public void set(EntityData entity) { public void set(EntityData entity) {
this.entityId = entity.getEntityId(); super.set(entity.getEntityId());
try { try {
entity.write(this.buffer.getWriter(), IOContext.COMMS); entity.write(this.buffer.getWriter(), IOContext.COMMS);
} catch (IOException e) { } catch (IOException e) {
@ -55,13 +38,13 @@ public class PacketEntityChange extends PacketWorldChange {
@Override @Override
public void read(DataInput input) throws IOException, DecodingException { public void read(DataInput input) throws IOException, DecodingException {
this.entityId = input.readLong(); super.read(input);
this.buffer.fill(input, input.readInt()); this.buffer.fill(input, input.readInt());
} }
@Override @Override
public void write(DataOutput output) throws IOException { public void write(DataOutput output) throws IOException {
output.writeLong(this.entityId); super.write(output);
output.writeInt(this.buffer.getSize()); output.writeInt(this.buffer.getSize());
this.buffer.flush(output); this.buffer.flush(output);
} }
@ -75,7 +58,7 @@ public class PacketEntityChange extends PacketWorldChange {
} }
try { try {
entity.read(getReader(), IOContext.COMMS); entity.read(getBuffer().getReader(), IOContext.COMMS);
} catch (IOException e) { } catch (IOException e) {
throw CrashReports.report(e, "Entity could not be read"); throw CrashReports.report(e, "Entity could not be read");
} }

View File

@ -4,12 +4,10 @@ import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import ru.windcorp.progressia.common.world.PacketWorldChange; import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldData;
public class PacketRevokeEntity extends PacketWorldChange { public class PacketRevokeEntity extends PacketAffectEntity {
private long entityId;
public PacketRevokeEntity() { public PacketRevokeEntity() {
this("Core:RevokeEntity"); this("Core:RevokeEntity");
@ -19,23 +17,24 @@ public class PacketRevokeEntity extends PacketWorldChange {
super(id); super(id);
} }
@Override
public void set(long entityId) { public void set(long entityId) {
this.entityId = entityId; super.set(entityId);
} }
@Override @Override
public void read(DataInput input) throws IOException { public void read(DataInput input) throws IOException, DecodingException {
this.entityId = input.readLong(); super.read(input);
} }
@Override @Override
public void write(DataOutput output) throws IOException { public void write(DataOutput output) throws IOException {
output.writeLong(this.entityId); super.write(output);
} }
@Override @Override
public void apply(WorldData world) { public void apply(WorldData world) {
world.removeEntity(this.entityId); world.removeEntity(getEntityId());
} }
} }

View File

@ -8,13 +8,11 @@ import ru.windcorp.progressia.common.state.IOContext;
import ru.windcorp.progressia.common.util.DataBuffer; import ru.windcorp.progressia.common.util.DataBuffer;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.DecodingException; 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.WorldData;
public class PacketSendEntity extends PacketWorldChange { public class PacketSendEntity extends PacketAffectEntity {
private String id; private String entityTypeId;
private long entityId;
private final DataBuffer buffer = new DataBuffer(); private final DataBuffer buffer = new DataBuffer();
public PacketSendEntity() { public PacketSendEntity() {
@ -25,9 +23,23 @@ public class PacketSendEntity extends PacketWorldChange {
super(id); super(id);
} }
/**
* Returns the text ID of the entity added by this packet.
* @return text ID
* @see #getEntityId()
*/
public String getEntityTypeId() {
return entityTypeId;
}
public DataBuffer getBuffer() {
return buffer;
}
public void set(EntityData entity) { public void set(EntityData entity) {
this.id = entity.getId(); super.set(entity.getEntityId());
this.entityId = entity.getEntityId();
this.entityTypeId = entity.getId();
try { try {
entity.write(this.buffer.getWriter(), IOContext.COMMS); entity.write(this.buffer.getWriter(), IOContext.COMMS);
@ -38,26 +50,28 @@ public class PacketSendEntity extends PacketWorldChange {
@Override @Override
public void read(DataInput input) throws IOException, DecodingException { public void read(DataInput input) throws IOException, DecodingException {
this.id = input.readUTF(); super.read(input);
this.entityId = input.readLong();
this.entityTypeId = input.readUTF();
this.buffer.fill(input, input.readInt()); this.buffer.fill(input, input.readInt());
} }
@Override @Override
public void write(DataOutput output) throws IOException { public void write(DataOutput output) throws IOException {
output.writeUTF(this.id); super.write(output);
output.writeLong(this.entityId);
output.writeUTF(this.entityTypeId);
output.writeInt(this.buffer.getSize()); output.writeInt(this.buffer.getSize());
this.buffer.flush(output); this.buffer.flush(output);
} }
@Override @Override
public void apply(WorldData world) { public void apply(WorldData world) {
EntityData entity = EntityDataRegistry.getInstance().create(this.id); EntityData entity = EntityDataRegistry.getInstance().create(getEntityTypeId());
entity.setEntityId(this.entityId); entity.setEntityId(getEntityId());
try { try {
entity.read(this.buffer.getReader(), IOContext.COMMS); entity.read(getBuffer().getReader(), IOContext.COMMS);
} catch (IOException e) { } catch (IOException e) {
throw CrashReports.report(e, "Could not read an entity from an internal buffer"); throw CrashReports.report(e, "Could not read an entity from an internal buffer");
} }

View File

@ -5,17 +5,13 @@ import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import glm.vec._3.i.Vec3i; 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.DecodingException;
import ru.windcorp.progressia.common.world.PacketChunkChange;
import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldData;
import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class PacketAddTile extends PacketChunkChange { public class PacketAddTile extends PacketAffectTile {
private String id; private String tileId;
private final Vec3i blockInWorld = new Vec3i();
private BlockFace face;
public PacketAddTile() { public PacketAddTile() {
this("Core:AddTile"); this("Core:AddTile");
@ -25,37 +21,31 @@ public class PacketAddTile extends PacketChunkChange {
super(id); super(id);
} }
public String getTileId() {
return tileId;
}
public void set(TileData tile, Vec3i blockInWorld, BlockFace face) { public void set(TileData tile, Vec3i blockInWorld, BlockFace face) {
this.id = tile.getId(); super.set(blockInWorld, face, -1);
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); this.tileId = tile.getId();
this.face = face;
} }
@Override @Override
public void read(DataInput input) throws IOException, DecodingException { public void read(DataInput input) throws IOException, DecodingException {
this.id = input.readUTF(); super.read(input);
this.blockInWorld.set(input.readInt(), input.readInt(), input.readInt()); this.tileId = input.readUTF();
this.face = BlockFace.getFaces().get(input.readByte());
} }
@Override @Override
public void write(DataOutput output) throws IOException { public void write(DataOutput output) throws IOException {
output.writeUTF(this.id); super.write(output);
output.writeInt(this.blockInWorld.x); output.writeUTF(this.tileId);
output.writeInt(this.blockInWorld.y);
output.writeInt(this.blockInWorld.z);
output.writeByte(this.face.getId());
} }
@Override @Override
public void apply(WorldData world) { public void apply(WorldData world) {
TileData tile = TileDataRegistry.getInstance().get(id); TileData tile = TileDataRegistry.getInstance().get(getTileId());
world.getTiles(blockInWorld, face).add(tile); world.getTiles(getBlockInWorld(), getFace()).add(tile);
}
@Override
public void getAffectedChunk(Vec3i output) {
Coordinates.convertInWorldToChunk(this.blockInWorld, output);
} }
} }

View File

@ -0,0 +1,62 @@
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.PacketAffectChunk;
import ru.windcorp.progressia.common.world.block.BlockFace;
public abstract class PacketAffectTile extends PacketAffectChunk {
private final Vec3i blockInWorld = new Vec3i();
private BlockFace face;
private int tag;
public PacketAffectTile(String id) {
super(id);
}
public Vec3i getBlockInWorld() {
return blockInWorld;
}
public BlockFace getFace() {
return face;
}
public int getTag() {
return tag;
}
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 getAffectedChunk(Vec3i output) {
Coordinates.convertInWorldToChunk(this.blockInWorld, output);
}
}

View File

@ -5,17 +5,12 @@ import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.DecodingException; 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.WorldData;
import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class PacketRemoveTile extends PacketChunkChange { public class PacketRemoveTile extends PacketAffectTile {
private final Vec3i blockInWorld = new Vec3i();
private BlockFace face;
private int tag;
public PacketRemoveTile() { public PacketRemoveTile() {
this("Core:RemoveTile"); this("Core:RemoveTile");
@ -25,37 +20,37 @@ public class PacketRemoveTile extends PacketChunkChange {
super(id); super(id);
} }
@Override
public void set(Vec3i blockInWorld, BlockFace face, int tag) { public void set(Vec3i blockInWorld, BlockFace face, int tag) {
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); super.set(blockInWorld, face, tag);
this.face = face;
this.tag = tag;
} }
@Override @Override
public void read(DataInput input) throws IOException, DecodingException { public void read(DataInput input) throws IOException, DecodingException {
this.blockInWorld.set(input.readInt(), input.readInt(), input.readInt()); super.read(input);
this.face = BlockFace.getFaces().get(input.readByte());
this.tag = input.readInt();
} }
@Override @Override
public void write(DataOutput output) throws IOException { public void write(DataOutput output) throws IOException {
output.writeInt(this.blockInWorld.x); super.write(output);
output.writeInt(this.blockInWorld.y);
output.writeInt(this.blockInWorld.z);
output.writeByte(this.face.getId());
output.writeInt(this.tag);
} }
@Override @Override
public void apply(WorldData world) { public void apply(WorldData world) {
TileDataStack stack = world.getTiles(blockInWorld, face); TileDataStack stack = world.getTiles(getBlockInWorld(), getFace());
stack.remove(stack.getIndexByTag(tag));
} int index = stack.getIndexByTag(getTag());
@Override if (index < 0) {
public void getAffectedChunk(Vec3i output) { throw CrashReports.report(null,
Coordinates.convertInWorldToChunk(this.blockInWorld, output); "Could not find tile with tag %d at (%d; %d; %d; %s)",
getTag(),
getBlockInWorld().x, getBlockInWorld().y, getBlockInWorld().z,
getFace()
);
}
stack.remove(index);
} }
} }

View File

@ -22,7 +22,7 @@ public class TickAndUpdateUtil {
try { try {
block.tick(context); block.tick(context);
} catch (Exception e) { } catch (Exception e) {
throw CrashReports.report(e, "Could not tick block {}", block); throw CrashReports.report(e, "Could not tick block %s", block);
} }
} }
@ -38,7 +38,7 @@ public class TickAndUpdateUtil {
try { try {
tile.tick(context); tile.tick(context);
} catch (Exception e) { } catch (Exception e) {
throw CrashReports.report(e, "Could not tick tile {}", tile); throw CrashReports.report(e, "Could not tick tile %s", tile);
} }
} }

View File

@ -4,7 +4,7 @@ import java.util.function.Consumer;
import ru.windcorp.progressia.common.world.tile.PacketAddTile; import ru.windcorp.progressia.common.world.tile.PacketAddTile;
class AddTile extends CachedChunkChange<PacketAddTile> { class AddTile extends CachedTileChange<PacketAddTile> {
public AddTile(Consumer<? super CachedChange> disposer) { public AddTile(Consumer<? super CachedChange> disposer) {
super(disposer, new PacketAddTile()); super(disposer, new PacketAddTile());

View File

@ -0,0 +1,45 @@
package ru.windcorp.progressia.server.world.tasks;
import java.util.function.Consumer;
import glm.Glm;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.block.PacketAffectBlock;
public class CachedBlockChange<P extends PacketAffectBlock> extends CachedChunkChange<P> {
public CachedBlockChange(Consumer<? super CachedChange> disposer, P packet) {
super(disposer, packet);
}
@Override
public int hashCode() {
PacketAffectBlock packet = getPacket();
Vec3i biw = packet.getBlockInWorld();
final int prime = 31;
int result = 1;
result = prime * result + biw.x;
result = prime * result + biw.y;
result = prime * result + biw.z;
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) return false;
PacketAffectBlock my = getPacket();
PacketAffectBlock other = ((CachedBlockChange<?>) obj).getPacket();
return Glm.equals(my.getBlockInWorld(), other.getBlockInWorld());
}
@Override
public String toString() {
Vec3i biw = getPacket().getBlockInWorld();
return getClass().getSimpleName() + " (" + biw.x + "; " + biw.y + "; " + biw.z + ")";
}
}

View File

@ -3,9 +3,9 @@ package ru.windcorp.progressia.server.world.tasks;
import java.util.function.Consumer; import java.util.function.Consumer;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.PacketChunkChange; import ru.windcorp.progressia.common.world.PacketAffectChunk;
public abstract class CachedChunkChange<P extends PacketChunkChange> extends CachedWorldChange<P> { public abstract class CachedChunkChange<P extends PacketAffectChunk> extends CachedWorldChange<P> {
public CachedChunkChange(Consumer<? super CachedChange> disposer, P packet) { public CachedChunkChange(Consumer<? super CachedChange> disposer, P packet) {
super(disposer, packet); super(disposer, packet);

View File

@ -0,0 +1,54 @@
package ru.windcorp.progressia.server.world.tasks;
import java.util.Objects;
import java.util.function.Consumer;
import glm.Glm;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.tile.PacketAffectTile;
public class CachedTileChange<P extends PacketAffectTile> extends CachedChunkChange<P> {
public CachedTileChange(Consumer<? super CachedChange> disposer, P packet) {
super(disposer, packet);
}
@Override
public int hashCode() {
PacketAffectTile packet = getPacket();
Vec3i biw = packet.getBlockInWorld();
final int prime = 31;
int result = 1;
result = prime * result + biw.x;
result = prime * result + biw.y;
result = prime * result + biw.z;
result = prime * result + Objects.hashCode(packet.getFace());
result = prime * result + packet.getTag();
return result;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof CachedTileChange<?>)) return false;
PacketAffectTile my = getPacket();
PacketAffectTile other = ((CachedTileChange<?>) obj).getPacket();
// Tag of -1 signals that we should ignore it
if (my.getTag() == -1 || other.getTag() == -1) return false;
return Glm.equals(my.getBlockInWorld(), other.getBlockInWorld())
&& (my.getFace() == other.getFace())
&& (my.getTag() == other.getTag());
}
@Override
public String toString() {
PacketAffectTile packet = getPacket();
Vec3i biw = packet.getBlockInWorld();
return getClass().getSimpleName() + " (" + biw.x + "; " + biw.y + "; " + biw.z + "; " + packet.getFace() + "; tag: " + packet.getTag() + ")";
}
}

View File

@ -4,10 +4,10 @@ import java.util.function.Consumer;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.PacketWorldChange; import ru.windcorp.progressia.common.world.PacketAffectWorld;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
public abstract class CachedWorldChange<P extends PacketWorldChange> extends CachedChange { public abstract class CachedWorldChange<P extends PacketAffectWorld> extends CachedChange {
private final P packet; private final P packet;

View File

@ -4,7 +4,7 @@ import java.util.function.Consumer;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.entity.PacketEntityChange; import ru.windcorp.progressia.common.world.entity.PacketChangeEntity;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
class ChangeEntity extends CachedChange { class ChangeEntity extends CachedChange {
@ -12,7 +12,7 @@ class ChangeEntity extends CachedChange {
private EntityData entity; private EntityData entity;
private StateChange<?> change; private StateChange<?> change;
private final PacketEntityChange packet = new PacketEntityChange(); private final PacketChangeEntity packet = new PacketChangeEntity();
public ChangeEntity(Consumer<? super CachedChange> disposer) { public ChangeEntity(Consumer<? super CachedChange> disposer) {
super(disposer); super(disposer);
@ -55,5 +55,16 @@ class ChangeEntity extends CachedChange {
this.entity = null; this.entity = null;
this.change = null; this.change = null;
} }
@Override
public int hashCode() {
return System.identityHashCode(entity);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ChangeEntity)) return false;
return ((ChangeEntity) obj).entity == entity;
}
} }

View File

@ -4,7 +4,7 @@ import java.util.function.Consumer;
import ru.windcorp.progressia.common.world.tile.PacketRemoveTile; import ru.windcorp.progressia.common.world.tile.PacketRemoveTile;
class RemoveTile extends CachedChunkChange<PacketRemoveTile> { class RemoveTile extends CachedTileChange<PacketRemoveTile> {
public RemoveTile(Consumer<? super CachedChange> disposer) { public RemoveTile(Consumer<? super CachedChange> disposer) {
super(disposer, new PacketRemoveTile()); super(disposer, new PacketRemoveTile());

View File

@ -4,7 +4,7 @@ import java.util.function.Consumer;
import ru.windcorp.progressia.common.world.block.PacketSetBlock; import ru.windcorp.progressia.common.world.block.PacketSetBlock;
class SetBlock extends CachedChunkChange<PacketSetBlock> { class SetBlock extends CachedBlockChange<PacketSetBlock> {
public SetBlock(Consumer<? super CachedChange> disposer) { public SetBlock(Consumer<? super CachedChange> disposer) {
super(disposer, new PacketSetBlock()); super(disposer, new PacketSetBlock());

View File

@ -23,5 +23,17 @@ public abstract class Change extends TickerTask {
void run(Server server) { void run(Server server) {
affect(server); affect(server);
} }
@Override
public int hashCode() {
// Use instance hash code by default
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
// Use instance-based equals() by default
return super.equals(obj);
}
} }

View File

@ -3,7 +3,9 @@ package ru.windcorp.progressia.server.world.ticking;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -14,6 +16,9 @@ import com.google.common.collect.ImmutableList;
import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.ChunkDataListener;
import ru.windcorp.progressia.common.world.ChunkDataListeners;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
/** /**
@ -28,7 +33,7 @@ public class TickerCoordinator {
private final Server server; private final Server server;
// Synchronized manually // Synchronized manually
private final Collection<Change> pendingChanges = new ArrayList<>(INITIAL_QUEUE_SIZE); private final Collection<Change> pendingChanges = new HashSet<>(INITIAL_QUEUE_SIZE);
// Synchronized manually // Synchronized manually
private final Collection<Evaluation> pendingEvaluations = new ArrayList<>(INITIAL_QUEUE_SIZE); private final Collection<Evaluation> pendingEvaluations = new ArrayList<>(INITIAL_QUEUE_SIZE);
@ -49,6 +54,8 @@ public class TickerCoordinator {
private final AtomicInteger workingTickers = new AtomicInteger(); private final AtomicInteger workingTickers = new AtomicInteger();
private final AtomicBoolean canChange = new AtomicBoolean(true);
private boolean isTickStartSet = false; private boolean isTickStartSet = false;
private long tickStart = -1; private long tickStart = -1;
private double tickLength = 1.0 / 20; // Do something about it private double tickLength = 1.0 / 20; // Do something about it
@ -66,6 +73,15 @@ public class TickerCoordinator {
this.tickers = ImmutableList.copyOf(tickerCollection); this.tickers = ImmutableList.copyOf(tickerCollection);
this.threads = Collections2.transform(this.tickers, Ticker::getThread); // Immutable because it is a view this.threads = Collections2.transform(this.tickers, Ticker::getThread); // Immutable because it is a view
server.getWorld().getData().addListener(ChunkDataListeners.createAdder(new ChunkDataListener() {
@Override
public void onChunkChanged(ChunkData chunk) {
if (!canChange.get()) {
throw CrashReports.report(null, "A change has been detected during evaluation phase");
}
}
}));
} }
/* /*
@ -157,7 +173,9 @@ public class TickerCoordinator {
} }
private synchronized void runOnePass() throws InterruptedException { private synchronized void runOnePass() throws InterruptedException {
canChange.set(false);
runPassStage(pendingEvaluations, "EVALUATION"); runPassStage(pendingEvaluations, "EVALUATION");
canChange.set(true);
runPassStage(pendingChanges, "CHANGE"); runPassStage(pendingChanges, "CHANGE");
} }