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.util.crash.CrashReports;
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
public class DefaultClientCommsListener implements CommsListener {
@ -20,8 +20,8 @@ public class DefaultClientCommsListener implements CommsListener {
@Override
public void onPacketReceived(Packet packet) {
if (packet instanceof PacketWorldChange) {
((PacketWorldChange) packet).apply(
if (packet instanceof PacketAffectWorld) {
((PacketAffectWorld) packet).apply(
getClient().getWorld().getData()
);
} else if (packet instanceof PacketSetLocalPlayer) {

View File

@ -2,9 +2,9 @@ package ru.windcorp.progressia.common.world;
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);
}

View File

@ -2,9 +2,9 @@ package ru.windcorp.progressia.common.world;
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);
}

View File

@ -6,7 +6,7 @@ import java.io.IOException;
import glm.vec._3.i.Vec3i;
public class PacketRevokeChunk extends PacketChunkChange {
public class PacketRevokeChunk extends PacketAffectChunk {
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.crash.CrashReports;
public class PacketSendChunk extends PacketChunkChange {
public class PacketSendChunk extends PacketAffectChunk {
private final DataBuffer data = new DataBuffer();
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 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 {
public class PacketSetBlock extends PacketAffectBlock {
private String id;
private final Vec3i blockInWorld = new Vec3i();
private String blockId;
public PacketSetBlock() {
this("Core:SetBlock");
@ -23,34 +20,31 @@ public class PacketSetBlock extends PacketChunkChange {
super(id);
}
public String getBlockId() {
return blockId;
}
public void set(BlockData block, Vec3i blockInWorld) {
this.id = block.getId();
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
super.set(blockInWorld);
this.blockId = block.getId();
}
@Override
public void read(DataInput input) throws IOException, DecodingException {
this.id = input.readUTF();
this.blockInWorld.set(input.readInt(), input.readInt(), input.readInt());
super.read(input);
this.blockId = input.readUTF();
}
@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);
super.write(output);
output.writeUTF(this.blockId);
}
@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);
BlockData block = BlockDataRegistry.getInstance().get(getBlockId());
world.setBlock(getBlockInWorld(), block, true);
}
}

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.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 {
public class PacketChangeEntity extends PacketAffectEntity {
private long entityId;
private final DataBuffer buffer = new DataBuffer();
public PacketEntityChange() {
public PacketChangeEntity() {
super("Core:EntityChange");
}
protected PacketEntityChange(String id) {
protected PacketChangeEntity(String id) {
super(id);
}
public long getEntityId() {
return entityId;
}
public void setEntityId(long entityId) {
this.entityId = entityId;
}
public DataBuffer getBuffer() {
return buffer;
}
public DataInput getReader() {
return buffer.getReader();
}
public DataOutput getWriter() {
return buffer.getWriter();
}
public void set(EntityData entity) {
this.entityId = entity.getEntityId();
super.set(entity.getEntityId());
try {
entity.write(this.buffer.getWriter(), IOContext.COMMS);
} catch (IOException e) {
@ -55,13 +38,13 @@ public class PacketEntityChange extends PacketWorldChange {
@Override
public void read(DataInput input) throws IOException, DecodingException {
this.entityId = input.readLong();
super.read(input);
this.buffer.fill(input, input.readInt());
}
@Override
public void write(DataOutput output) throws IOException {
output.writeLong(this.entityId);
super.write(output);
output.writeInt(this.buffer.getSize());
this.buffer.flush(output);
}
@ -75,7 +58,7 @@ public class PacketEntityChange extends PacketWorldChange {
}
try {
entity.read(getReader(), IOContext.COMMS);
entity.read(getBuffer().getReader(), IOContext.COMMS);
} catch (IOException e) {
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.IOException;
import ru.windcorp.progressia.common.world.PacketWorldChange;
import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.WorldData;
public class PacketRevokeEntity extends PacketWorldChange {
private long entityId;
public class PacketRevokeEntity extends PacketAffectEntity {
public PacketRevokeEntity() {
this("Core:RevokeEntity");
@ -19,23 +17,24 @@ public class PacketRevokeEntity extends PacketWorldChange {
super(id);
}
@Override
public void set(long entityId) {
this.entityId = entityId;
super.set(entityId);
}
@Override
public void read(DataInput input) throws IOException {
this.entityId = input.readLong();
public void read(DataInput input) throws IOException, DecodingException {
super.read(input);
}
@Override
public void write(DataOutput output) throws IOException {
output.writeLong(this.entityId);
super.write(output);
}
@Override
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.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 PacketSendEntity extends PacketWorldChange {
public class PacketSendEntity extends PacketAffectEntity {
private String id;
private long entityId;
private String entityTypeId;
private final DataBuffer buffer = new DataBuffer();
public PacketSendEntity() {
@ -25,9 +23,23 @@ public class PacketSendEntity extends PacketWorldChange {
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) {
this.id = entity.getId();
this.entityId = entity.getEntityId();
super.set(entity.getEntityId());
this.entityTypeId = entity.getId();
try {
entity.write(this.buffer.getWriter(), IOContext.COMMS);
@ -38,26 +50,28 @@ public class PacketSendEntity extends PacketWorldChange {
@Override
public void read(DataInput input) throws IOException, DecodingException {
this.id = input.readUTF();
this.entityId = input.readLong();
super.read(input);
this.entityTypeId = input.readUTF();
this.buffer.fill(input, input.readInt());
}
@Override
public void write(DataOutput output) throws IOException {
output.writeUTF(this.id);
output.writeLong(this.entityId);
super.write(output);
output.writeUTF(this.entityTypeId);
output.writeInt(this.buffer.getSize());
this.buffer.flush(output);
}
@Override
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 {
entity.read(this.buffer.getReader(), IOContext.COMMS);
entity.read(getBuffer().getReader(), IOContext.COMMS);
} catch (IOException e) {
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 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 {
public class PacketAddTile extends PacketAffectTile {
private String id;
private final Vec3i blockInWorld = new Vec3i();
private BlockFace face;
private String tileId;
public PacketAddTile() {
this("Core:AddTile");
@ -25,37 +21,31 @@ public class PacketAddTile extends PacketChunkChange {
super(id);
}
public String getTileId() {
return tileId;
}
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;
super.set(blockInWorld, face, -1);
this.tileId = tile.getId();
}
@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());
super.read(input);
this.tileId = input.readUTF();
}
@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());
super.write(output);
output.writeUTF(this.tileId);
}
@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);
TileData tile = TileDataRegistry.getInstance().get(getTileId());
world.getTiles(getBlockInWorld(), getFace()).add(tile);
}
}

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 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.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 class PacketRemoveTile extends PacketAffectTile {
public PacketRemoveTile() {
this("Core:RemoveTile");
@ -25,37 +20,37 @@ public class PacketRemoveTile extends PacketChunkChange {
super(id);
}
@Override
public void set(Vec3i blockInWorld, BlockFace face, int tag) {
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
this.face = face;
this.tag = tag;
super.set(blockInWorld, face, 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();
super.read(input);
}
@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);
super.write(output);
}
@Override
public void apply(WorldData world) {
TileDataStack stack = world.getTiles(blockInWorld, face);
stack.remove(stack.getIndexByTag(tag));
TileDataStack stack = world.getTiles(getBlockInWorld(), getFace());
int index = stack.getIndexByTag(getTag());
if (index < 0) {
throw CrashReports.report(null,
"Could not find tile with tag %d at (%d; %d; %d; %s)",
getTag(),
getBlockInWorld().x, getBlockInWorld().y, getBlockInWorld().z,
getFace()
);
}
@Override
public void getAffectedChunk(Vec3i output) {
Coordinates.convertInWorldToChunk(this.blockInWorld, output);
stack.remove(index);
}
}

View File

@ -22,7 +22,7 @@ public class TickAndUpdateUtil {
try {
block.tick(context);
} 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 {
tile.tick(context);
} 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;
class AddTile extends CachedChunkChange<PacketAddTile> {
class AddTile extends CachedTileChange<PacketAddTile> {
public AddTile(Consumer<? super CachedChange> disposer) {
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 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) {
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 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;
public abstract class CachedWorldChange<P extends PacketWorldChange> extends CachedChange {
public abstract class CachedWorldChange<P extends PacketAffectWorld> extends CachedChange {
private final P packet;

View File

@ -4,7 +4,7 @@ import java.util.function.Consumer;
import glm.vec._3.i.Vec3i;
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;
class ChangeEntity extends CachedChange {
@ -12,7 +12,7 @@ class ChangeEntity extends CachedChange {
private EntityData entity;
private StateChange<?> change;
private final PacketEntityChange packet = new PacketEntityChange();
private final PacketChangeEntity packet = new PacketChangeEntity();
public ChangeEntity(Consumer<? super CachedChange> disposer) {
super(disposer);
@ -56,4 +56,15 @@ class ChangeEntity extends CachedChange {
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;
class RemoveTile extends CachedChunkChange<PacketRemoveTile> {
class RemoveTile extends CachedTileChange<PacketRemoveTile> {
public RemoveTile(Consumer<? super CachedChange> disposer) {
super(disposer, new PacketRemoveTile());

View File

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

View File

@ -24,4 +24,16 @@ public abstract class Change extends TickerTask {
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.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
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.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;
/**
@ -28,7 +33,7 @@ public class TickerCoordinator {
private final Server server;
// Synchronized manually
private final Collection<Change> pendingChanges = new ArrayList<>(INITIAL_QUEUE_SIZE);
private final Collection<Change> pendingChanges = new HashSet<>(INITIAL_QUEUE_SIZE);
// Synchronized manually
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 AtomicBoolean canChange = new AtomicBoolean(true);
private boolean isTickStartSet = false;
private long tickStart = -1;
private double tickLength = 1.0 / 20; // Do something about it
@ -66,6 +73,15 @@ public class TickerCoordinator {
this.tickers = ImmutableList.copyOf(tickerCollection);
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 {
canChange.set(false);
runPassStage(pendingEvaluations, "EVALUATION");
canChange.set(true);
runPassStage(pendingChanges, "CHANGE");
}