Skip to content

Commit

Permalink
#85 Add sync flag for first sync to address long session history 'res…
Browse files Browse the repository at this point in the history
…toration' + limit history packet size
  • Loading branch information
dantaeusb committed Dec 22, 2022
1 parent 95f29bb commit 6f218bf
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 18 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ apply from: "https://moddingtutorials.org/applesilicon.gradle"
apply plugin: 'maven-publish'
//apply from: 'https://raw.githubusercontent.com/SizableShrimp/Forge-Class-Remapper/main/classremapper.gradle'

version = '0.19.0-1.19.2-rc'
version = '0.19.1-1.19.2-rc'
group = 'com.dantaeusb.zetter' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'zetter'

Expand Down Expand Up @@ -109,7 +109,7 @@ jar {
attributes([
"Specification-Title": "Zetter",
"Specification-Vendor": "dantaeusb",
"Specification-Version": "0.19.0-rc",
"Specification-Version": "0.19.1-rc",
"Implementation-Title": project.name,
"Implementation-Version": project.jar.archiveVersion,
"Implementation-Vendor" :"dantaeusb",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ public class EaselState {
private final EaselEntity easel;
private List<EaselStateListener> listeners;

/**
* Flag for cilent if it assumes that current state
* is actual state. Used for history packets:
* if the last history packed is marked as "actual"
* (no more unsent actions at the time of generating packet)
* then we have our easel state sync and can use snapshot
* restoration
*/
private boolean sync = false;
private int tick;

/**
Expand Down Expand Up @@ -95,6 +104,8 @@ public EaselState(EaselEntity entity) {
this.snapshots = new ArrayList<>(CLIENT_SNAPSHOT_HISTORY_SIZE + 1);
} else {
this.snapshots = new ArrayList<>(SNAPSHOT_HISTORY_SIZE + 1);
// Server is always synchronized with itself, technically
this.sync = true;
}
}

Expand Down Expand Up @@ -721,9 +732,11 @@ public void recollectPaintingData() {

if (firstCanceledAction != null) {
latestSnapshot = this.getSnapshotBefore(firstCanceledAction.getStartTime() - latency);
} else if (this.getLastAction() != null) {
} else if (this.sync && this.getLastAction() != null) {
// We CANNOT just use last snapshot, because it might have been captured "on a different timeline"
// Where some actions which are no longer in history are applied
// But to avoid visible "history fast-forward", we do not use that until history is
// fully synchronized
latestSnapshot = this.getSnapshotBefore(this.getLastAction().getStartTime() - latency);
} else {
latestSnapshot = this.getLastSnapshot();
Expand Down Expand Up @@ -1020,8 +1033,15 @@ public void performHistorySyncForServerPlayer(Player player) {
return;
}

/* When we are sending the last snapshot/actions and have no more actions/snapshots to sync, we consider state
* Sync; this does not mean that we are fully synchronized, but we can use previous snapshot as an authoritative
* state
* If we have unsynchronized actions/snapshots, we have last actions/snapshots, ignore errors */
boolean actionsSync = !hasUnsyncedActions || unsyncedActions.get(unsyncedActions.size() - 1).uuid.equals(this.getLastAction().uuid);
boolean snapshotsSync = !hasUnsyncedSnapshot || unsyncedSnapshot.uuid.equals(this.getLastSnapshot().uuid);

SEaselStateSyncPacket syncMessage = new SEaselStateSyncPacket(
this.easel.getId(), this.getCanvasCode(), unsyncedSnapshot, unsyncedActions
this.easel.getId(), this.getCanvasCode(), actionsSync && snapshotsSync, unsyncedSnapshot, unsyncedActions
);

ZetterNetwork.simpleChannel.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), syncMessage);
Expand All @@ -1043,6 +1063,8 @@ public void performHistorySyncForServerPlayer(Player player) {
* Get list of actions in history that was not synced with the
* player since the last sync, to keep history consistent between
* players
*
* Amount of action is limited by the packet SEaselStateSyncPacket
* @param player
* @return
*/
Expand All @@ -1060,16 +1082,17 @@ public void performHistorySyncForServerPlayer(Player player) {
while(actionBufferIterator.hasPrevious()) {
CanvasAction action = actionBufferIterator.previous();
// Because we're using reverse iterator, we add to the front
unsyncedActions.add(action);
if (action.uuid.equals(lastSyncedActionUuid)) {
while(actionBufferIterator.hasNext() && unsyncedActions.size() < SEaselStateSyncPacket.MAX_ACTIONS) {
action = actionBufferIterator.next();
unsyncedActions.add(action);
}

if (action.uuid == lastSyncedActionUuid) {
break;
}
}

Collections.reverse(unsyncedActions);

if (unsyncedActions.size() == 1 && unsyncedActions.get(0).uuid == lastSyncedActionUuid) {
if (unsyncedActions.size() == 1 && unsyncedActions.get(0).uuid.equals(lastSyncedActionUuid)) {
return null;
}

Expand Down Expand Up @@ -1247,7 +1270,7 @@ public void applyAction(CanvasAction action, boolean doDamageClient) {
* @param snapshot
* @param actions
*/
public void processHistorySyncClient(String canvasCode, @Nullable CanvasSnapshot snapshot, @Nullable ArrayList<CanvasAction> actions) {
public void processHistorySyncClient(String canvasCode, boolean sync, @Nullable CanvasSnapshot snapshot, @Nullable ArrayList<CanvasAction> actions) {
if (!canvasCode.equals(this.getCanvasCode())) {
Zetter.LOG.error("Different canvas code in history sync packet, ignoring");
return;
Expand All @@ -1271,6 +1294,8 @@ public void processHistorySyncClient(String canvasCode, @Nullable CanvasSnapshot


if (actions == null || actions.isEmpty()) {
this.sync = sync;

this.recollectPaintingData();

if (Zetter.DEBUG_MODE && Zetter.DEBUG_CLIENT) {
Expand Down Expand Up @@ -1342,6 +1367,8 @@ public void processHistorySyncClient(String canvasCode, @Nullable CanvasSnapshot
this.cleanupActionHistory();
}

this.sync = sync;

this.recollectPaintingData();
this.onStateChanged();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static void processEaselStateSync(final SEaselStateSyncPacket packetIn, L
EaselEntity easel = (EaselEntity) world.getEntity(packetIn.easelEntityId);

if (easel != null) {
easel.getStateHandler().processHistorySyncClient(packetIn.canvasCode, packetIn.snapshot, packetIn.unsyncedActions);
easel.getStateHandler().processHistorySyncClient(packetIn.canvasCode, packetIn.sync, packetIn.snapshot, packetIn.unsyncedActions);
} else {
Zetter.LOG.warn("Unable to find entity " + packetIn.easelEntityId + " disregarding canvas snapshot");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,31 @@
import java.util.function.Supplier;

/**
* Send snapshot of a canvas, used only for easel when drawing, a bit more specific
* object that is sent more frequently than default canvases when
* multiple players are drawing
* Send snapshot of a canvas and actions to keep every
* using player history of changes up to date
*/
public class SEaselStateSyncPacket {
public static final int MAX_ACTIONS = 50;

public final int easelEntityId;
public final String canvasCode;

/* If at the moment of creation of this packet,
* all sent data (actions or/and snapshots) are
* the latest available data for the easel state,
* so we can consider our easels sync after processing
* those; or if we expect more large updates to come
*/
public final boolean sync;

public final @Nullable CanvasSnapshot snapshot;
public final @Nullable ArrayList<CanvasAction> unsyncedActions;

public SEaselStateSyncPacket(int easelEntityId, String canvasCode, @Nullable CanvasSnapshot snapshot, @Nullable ArrayList<CanvasAction> unsyncedActions) {
public SEaselStateSyncPacket(int easelEntityId, String canvasCode, boolean sync, @Nullable CanvasSnapshot snapshot, @Nullable ArrayList<CanvasAction> unsyncedActions) {
this.easelEntityId = easelEntityId;

this.canvasCode = canvasCode;
this.sync = sync;

this.snapshot = snapshot;

this.unsyncedActions = unsyncedActions;
Expand All @@ -46,6 +56,7 @@ public static SEaselStateSyncPacket readPacketData(FriendlyByteBuf networkBuffer
try {
final int easelEntityId = networkBuffer.readInt();
final String canvasCode = networkBuffer.readUtf(128);
final boolean sync = networkBuffer.readBoolean();

CanvasSnapshot snapshot = null;
final boolean hasSnapshot = networkBuffer.readBoolean();
Expand All @@ -62,7 +73,7 @@ public static SEaselStateSyncPacket readPacketData(FriendlyByteBuf networkBuffer
int actionBuffersCount = networkBuffer.readInt();

if (actionBuffersCount == 0) {
return new SEaselStateSyncPacket(easelEntityId, canvasCode, snapshot, null);
return new SEaselStateSyncPacket(easelEntityId, canvasCode, sync, snapshot, null);
}

ArrayList<CanvasAction> unsyncedActions = new ArrayList<>();
Expand All @@ -77,7 +88,7 @@ public static SEaselStateSyncPacket readPacketData(FriendlyByteBuf networkBuffer
}
}

return new SEaselStateSyncPacket(easelEntityId, canvasCode, snapshot, unsyncedActions);
return new SEaselStateSyncPacket(easelEntityId, canvasCode, sync, snapshot, unsyncedActions);
} catch (IllegalArgumentException | IndexOutOfBoundsException e) {
Zetter.LOG.warn("Exception while reading SEaselStateSync: " + e);
return null;
Expand All @@ -90,6 +101,7 @@ public static SEaselStateSyncPacket readPacketData(FriendlyByteBuf networkBuffer
public void writePacketData(FriendlyByteBuf networkBuffer) {
networkBuffer.writeInt(this.easelEntityId);
networkBuffer.writeUtf(this.canvasCode, 128);
networkBuffer.writeBoolean(this.sync);

networkBuffer.writeBoolean(this.snapshot != null);

Expand Down

0 comments on commit 6f218bf

Please sign in to comment.