feat: configurable entity synchronization interval (#2047)

* feat: entity synchronization overhaul

* chore: add getter for sync interval

---------

Co-authored-by: mworzala <mattheworzala@gmail.com>
This commit is contained in:
DeidaraMC 2024-03-25 19:44:27 -04:00 committed by GitHub
parent 4d2e78e7cf
commit 4f1017d398
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 28 deletions

View File

@ -15,6 +15,7 @@ public final class ServerFlag {
public static final int SERVER_TICKS_PER_SECOND = Integer.getInteger("minestom.tps", 20);
public static final int CHUNK_VIEW_DISTANCE = Integer.getInteger("minestom.chunk-view-distance", 8);
public static final int ENTITY_VIEW_DISTANCE = Integer.getInteger("minestom.entity-view-distance", 5);
public static final int ENTITY_SYNCHRONIZATION_TICKS = Integer.getInteger("minestom.entity-synchronization-ticks", 20);
public static final int WORKER_COUNT = Integer.getInteger("minestom.workers", Runtime.getRuntime().availableProcessors());
public static final int MAX_PACKET_SIZE = Integer.getInteger("minestom.max-packet-size", 2_097_151); // 3 bytes var-int
public static final int SOCKET_SEND_BUFFER_SIZE = Integer.getInteger("minestom.send-buffer-size", 262_143);

View File

@ -5,10 +5,7 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEvent.ShowEntity;
import net.kyori.adventure.text.event.HoverEventSource;
import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerProcess;
import net.minestom.server.Tickable;
import net.minestom.server.Viewable;
import net.minestom.server.*;
import net.minestom.server.collision.*;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
@ -31,8 +28,6 @@ import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.network.packet.server.CachedPacket;
import net.minestom.server.network.packet.server.LazyPacket;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.permission.Permission;
import net.minestom.server.permission.PermissionHandler;
@ -57,7 +52,6 @@ import net.minestom.server.utils.chunk.ChunkCache;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.entity.EntityUtils;
import net.minestom.server.utils.player.PlayerUtils;
import net.minestom.server.utils.time.Cooldown;
import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.ApiStatus;
@ -169,10 +163,9 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
protected EntityType entityType; // UNSAFE to change, modify at your own risk
// Network synchronization, send the absolute position of the entity each X milliseconds
private static final Duration SYNCHRONIZATION_COOLDOWN = Duration.of(1, TimeUnit.MINUTE);
private Duration customSynchronizationCooldown;
private long lastAbsoluteSynchronizationTime;
// Network synchronization, send the absolute position of the entity every n ticks
private long synchronizationTicks = ServerFlag.ENTITY_SYNCHRONIZATION_TICKS;
private long nextSynchronizationTick = synchronizationTicks;
protected Metadata metadata = new Metadata(this);
protected EntityMeta entityMeta;
@ -582,7 +575,7 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
effectTick(time);
}
// Scheduled synchronization
if (!Cooldown.hasCooldown(time, lastAbsoluteSynchronizationTime, getSynchronizationCooldown())) {
if (ticks >= nextSynchronizationTick) {
synchronizePosition(false);
}
}
@ -1371,6 +1364,11 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
this.position = position;
this.previousPosition = previousPosition;
if (!position.samePoint(previousPosition)) refreshCoordinate(position);
if (nextSynchronizationTick <= ticks + 1) {
// The entity will be synchronized at the end of its tick
// not returning here will duplicate position packets
return;
}
// Update viewers
final boolean viewChange = !position.sameView(lastSyncedPosition);
final double distanceX = Math.abs(position.x() - lastSyncedPosition.x());
@ -1381,7 +1379,7 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
final Chunk chunk = getChunk();
if (distanceX > 8 || distanceY > 8 || distanceZ > 8) {
PacketUtils.prepareViewablePacket(chunk, new EntityTeleportPacket(getEntityId(), position, isOnGround()), this);
this.lastAbsoluteSynchronizationTime = System.currentTimeMillis();
nextSynchronizationTick = synchronizationTicks + 1;
} else if (positionChange && viewChange) {
PacketUtils.prepareViewablePacket(chunk, EntityPositionAndRotationPacket.getPacket(getEntityId(), position,
lastSyncedPosition, isOnGround()), this);
@ -1681,9 +1679,11 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
@ApiStatus.Internal
protected void synchronizePosition(boolean includeSelf) {
final Pos posCache = this.position;
final ServerPacket packet = new EntityTeleportPacket(getEntityId(), posCache, isOnGround());
PacketUtils.prepareViewablePacket(currentChunk, packet, this);
this.lastAbsoluteSynchronizationTime = System.currentTimeMillis();
PacketUtils.prepareViewablePacket(currentChunk, new EntityTeleportPacket(getEntityId(), posCache, isOnGround()), this);
if (posCache.yaw() != lastSyncedPosition.yaw()) {
PacketUtils.prepareViewablePacket(currentChunk, new EntityHeadLookPacket(getEntityId(), position.yaw()), this);
}
nextSynchronizationTick = ticks + synchronizationTicks;
this.lastSyncedPosition = posCache;
}
@ -1693,28 +1693,34 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
}
/**
* Asks for a synchronization (position) to happen during next entity tick.
* Asks for a position synchronization to happen during next entity tick.
*/
public void askSynchronization() {
this.lastAbsoluteSynchronizationTime = 0;
public void synchronizeNextTick() {
this.nextSynchronizationTick = 0;
}
/**
* Set custom cooldown for position synchronization.
* Returns the current synchronization interval. The default value is {@link ServerFlag#ENTITY_SYNCHRONIZATION_TICKS}
* but can be overridden per entity with {@link #setSynchronizationTicks(long)}.
*
* @param cooldown custom cooldown for position synchronization.
* @return The current synchronization ticks
*/
public void setCustomSynchronizationCooldown(@Nullable Duration cooldown) {
this.customSynchronizationCooldown = cooldown;
public long getSynchronizationTicks() {
return this.synchronizationTicks;
}
/**
* Set the tick period until this entity's position is synchronized.
*
* @param ticks the new synchronization tick period
*/
public void setSynchronizationTicks(long ticks) {
this.synchronizationTicks = ticks;
}
@Override
public @NotNull HoverEvent<ShowEntity> asHoverEvent(@NotNull UnaryOperator<ShowEntity> op) {
return HoverEvent.showEntity(ShowEntity.of(this.entityType, this.uuid));
}
private Duration getSynchronizationCooldown() {
return Objects.requireNonNullElse(this.customSynchronizationCooldown, SYNCHRONIZATION_COOLDOWN);
return HoverEvent.showEntity(ShowEntity.showEntity(this.entityType, this.uuid));
}
@ApiStatus.Experimental