diff --git a/src/main/java/net/minestom/server/UpdateManager.java b/src/main/java/net/minestom/server/UpdateManager.java index b68cb4e13..16f3bc958 100644 --- a/src/main/java/net/minestom/server/UpdateManager.java +++ b/src/main/java/net/minestom/server/UpdateManager.java @@ -3,7 +3,7 @@ package net.minestom.server; import net.minestom.server.entity.EntityManager; import net.minestom.server.instance.Instance; import net.minestom.server.instance.InstanceManager; -import net.minestom.server.thread.PerGroupChunkProvider; +import net.minestom.server.thread.PerInstanceThreadProvider; import net.minestom.server.thread.ThreadProvider; import net.minestom.server.utils.thread.MinestomThread; import net.minestom.server.utils.validate.Check; @@ -33,7 +33,8 @@ public final class UpdateManager { { // DEFAULT THREAD PROVIDER - threadProvider = new PerGroupChunkProvider(); + //threadProvider = new PerGroupChunkProvider(); + threadProvider = new PerInstanceThreadProvider(); } /** diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 1e130ad9b..4a548535c 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -31,7 +31,9 @@ import net.minestom.server.utils.callback.OptionalCallback; 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.CooldownUtils; import net.minestom.server.utils.time.TimeUnit; +import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -103,9 +105,9 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P private long lastUpdate; private final EntityType entityType; - // Network synchronization - private static final long SYNCHRONIZATION_DELAY = 1500; // In ms - private long lastSynchronizationTime; + // Network synchronization, send the absolute position of the entity each X milliseconds + private static final UpdateOption SYNCHRONIZATION_COOLDOWN = new UpdateOption(1500, TimeUnit.MILLISECOND); + private long lastAbsoluteSynchronizationTime; // Events private final Map, Collection> eventCallbacks = new ConcurrentHashMap<>(); @@ -543,8 +545,8 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P } // Scheduled synchronization - if (time - lastSynchronizationTime >= SYNCHRONIZATION_DELAY) { - lastSynchronizationTime = time; + if (!CooldownUtils.hasCooldown(time, lastAbsoluteSynchronizationTime, SYNCHRONIZATION_COOLDOWN)) { + this.lastAbsoluteSynchronizationTime = time; sendSynchronization(); } @@ -672,7 +674,7 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P */ @Nullable public Chunk getChunk() { - return instance.getChunkAt(lastX, lastZ); + return instance.getChunkAt(position.getX(), position.getZ()); } /** @@ -1354,7 +1356,7 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P * Asks for a synchronization (position) to happen during next entity tick. */ public void askSynchronization() { - this.lastSynchronizationTime = 0; + this.lastAbsoluteSynchronizationTime = 0; } private boolean shouldUpdate(long time) { diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 31764aa27..db91d09b0 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1,5 +1,6 @@ package net.minestom.server.entity; +import io.netty.channel.Channel; import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.AdvancementTab; import net.minestom.server.attribute.Attribute; @@ -131,6 +132,11 @@ public class Player extends LivingEntity implements CommandSender { private byte targetStage; // The current stage of the target block, only if multi player breaking is disabled private final Set targetBreakers = new HashSet<>(1); // Only used if multi player breaking is disabled, contains only this player + // Position synchronization with viewers + protected UpdateOption playerSynchronizationCooldown = new UpdateOption(2, TimeUnit.TICK); + private long lastPlayerSynchronizationTime; + private float lastPlayerSyncX, lastPlayerSyncY, lastPlayerSyncZ, lastPlayerSyncYaw, lastPlayerSyncPitch; + // Experience orb pickup protected UpdateOption experiencePickupCooldown = new UpdateOption(10, TimeUnit.TICK); private long lastExperiencePickupCheckTime; @@ -301,7 +307,8 @@ public class Player extends LivingEntity implements CommandSender { // Flush all pending packets if (PlayerUtils.isNettyClient(this)) { - ((NettyPlayerConnection) playerConnection).getChannel().flush(); + Channel channel = ((NettyPlayerConnection) playerConnection).getChannel(); + channel.eventLoop().execute(() -> channel.flush()); } // Network tick verification @@ -412,9 +419,11 @@ public class Player extends LivingEntity implements CommandSender { callEvent(PlayerTickEvent.class, playerTickEvent); // Multiplayer sync - final boolean positionChanged = position.getX() != lastX || position.getY() != lastY || position.getZ() != lastZ; - final boolean viewChanged = position.getYaw() != lastYaw || position.getPitch() != lastPitch; - if (!viewers.isEmpty()) { + final boolean positionChanged = position.getX() != lastPlayerSyncX || position.getY() != lastPlayerSyncY || position.getZ() != lastPlayerSyncZ; + final boolean viewChanged = position.getYaw() != lastPlayerSyncYaw || position.getPitch() != lastPlayerSyncPitch; + if (!viewers.isEmpty() && !CooldownUtils.hasCooldown(time, lastPlayerSynchronizationTime, playerSynchronizationCooldown)) { + this.lastPlayerSynchronizationTime = time; + if (positionChanged || viewChanged) { // Player moved since last time @@ -423,9 +432,9 @@ public class Player extends LivingEntity implements CommandSender { if (positionChanged && viewChanged) { EntityPositionAndRotationPacket entityPositionAndRotationPacket = new EntityPositionAndRotationPacket(); entityPositionAndRotationPacket.entityId = getEntityId(); - entityPositionAndRotationPacket.deltaX = (short) ((position.getX() * 32 - lastX * 32) * 128); - entityPositionAndRotationPacket.deltaY = (short) ((position.getY() * 32 - lastY * 32) * 128); - entityPositionAndRotationPacket.deltaZ = (short) ((position.getZ() * 32 - lastZ * 32) * 128); + entityPositionAndRotationPacket.deltaX = (short) ((position.getX() * 32 - lastPlayerSyncX * 32) * 128); + entityPositionAndRotationPacket.deltaY = (short) ((position.getY() * 32 - lastPlayerSyncY * 32) * 128); + entityPositionAndRotationPacket.deltaZ = (short) ((position.getZ() * 32 - lastPlayerSyncZ * 32) * 128); entityPositionAndRotationPacket.yaw = position.getYaw(); entityPositionAndRotationPacket.pitch = position.getPitch(); entityPositionAndRotationPacket.onGround = onGround; @@ -434,9 +443,9 @@ public class Player extends LivingEntity implements CommandSender { } else if (positionChanged) { EntityPositionPacket entityPositionPacket = new EntityPositionPacket(); entityPositionPacket.entityId = getEntityId(); - entityPositionPacket.deltaX = (short) ((position.getX() * 32 - lastX * 32) * 128); - entityPositionPacket.deltaY = (short) ((position.getY() * 32 - lastY * 32) * 128); - entityPositionPacket.deltaZ = (short) ((position.getZ() * 32 - lastZ * 32) * 128); + entityPositionPacket.deltaX = (short) ((position.getX() * 32 - lastPlayerSyncX * 32) * 128); + entityPositionPacket.deltaY = (short) ((position.getY() * 32 - lastPlayerSyncY * 32) * 128); + entityPositionPacket.deltaZ = (short) ((position.getZ() * 32 - lastPlayerSyncZ * 32) * 128); entityPositionPacket.onGround = onGround; updatePacket = entityPositionPacket; @@ -472,16 +481,17 @@ public class Player extends LivingEntity implements CommandSender { entityMovementPacket.entityId = getEntityId(); sendPacketToViewers(entityMovementPacket); } - } - if (positionChanged) { - lastX = position.getX(); - lastY = position.getY(); - lastZ = position.getZ(); - } - if (viewChanged) { - lastYaw = position.getYaw(); - lastPitch = position.getPitch(); + // Update sync data + if (positionChanged) { + lastPlayerSyncX = position.getX(); + lastPlayerSyncY = position.getY(); + lastPlayerSyncZ = position.getZ(); + } + if (viewChanged) { + lastPlayerSyncYaw = position.getYaw(); + lastPlayerSyncPitch = position.getPitch(); + } } } diff --git a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java b/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java index eae4f8313..4e9c8ed8c 100644 --- a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java +++ b/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java @@ -33,7 +33,7 @@ public class PacketCompressor extends ByteToMessageCodec { private final byte[] buffer = new byte[8192]; - private final Deflater deflater = new Deflater(3); + private final Deflater deflater = new Deflater(); private final Inflater inflater = new Inflater(); public PacketCompressor(int threshold) { diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index e97a28689..2be5a2127 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -57,7 +57,7 @@ public class NettyPlayerConnection extends PlayerConnection { } /** - * Sets the encryption key and add the channels to the pipeline. + * Sets the encryption key and add the codecs to the pipeline. * * @param secretKey the secret key to use in the encryption * @throws IllegalStateException if encryption is already enabled for this connection @@ -70,7 +70,7 @@ public class NettyPlayerConnection extends PlayerConnection { } /** - * Enables compression and add a new channel to the pipeline. + * Enables compression and add a new codec to the pipeline. * * @param threshold the threshold for a packet to be compressible * @throws IllegalStateException if encryption is already enabled for this connection