diff --git a/src/main/java/net/minestom/server/UpdateManager.java b/src/main/java/net/minestom/server/UpdateManager.java index af9bc6fc3..ba648209c 100644 --- a/src/main/java/net/minestom/server/UpdateManager.java +++ b/src/main/java/net/minestom/server/UpdateManager.java @@ -70,7 +70,7 @@ public final class UpdateManager { futures = threadProvider.update(time); } - // Waiting players update (newly connected waiting to get into the server) + // Waiting players update (newly connected clients waiting to get into the server) entityManager.updateWaitingPlayers(); // Keep Alive Handling diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index def813c69..ef571f8b4 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -20,6 +20,7 @@ import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.WorldBorder; import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.network.packet.server.play.*; +import net.minestom.server.thread.ThreadProvider; import net.minestom.server.utils.ArrayUtils; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.Position; @@ -33,6 +34,7 @@ import net.minestom.server.utils.validate.Check; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -114,7 +116,8 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { protected boolean noGravity; protected Pose pose = Pose.STANDING; - protected final List> nextTick = Collections.synchronizedList(new ArrayList<>()); + // list of scheduled tasks to be executed during the next entity tick + protected final ConcurrentLinkedQueue> nextTick = new ConcurrentLinkedQueue<>(); // Tick related private long ticks; @@ -134,8 +137,14 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { setVelocityUpdatePeriod(5); } + /** + * Schedule a task to be run during the next entity tick + * It ensures that the task will be executed in the same thread as the entity (depending of the {@link ThreadProvider}) + * + * @param callback the task to execute during the next entity tick + */ public void scheduleNextTick(Consumer callback) { - nextTick.add(callback); + this.nextTick.add(callback); } public Entity(EntityType entityType) { @@ -353,12 +362,14 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { return; } - synchronized (nextTick) { - for (final Consumer e : nextTick) { - e.accept(this); + // scheduled tasks + if (!nextTick.isEmpty()) { + Consumer callback; + while ((callback = nextTick.poll()) != null) { + callback.accept(this); } - nextTick.clear(); } + // Synchronization with updated fields in #getPosition() { // X/Y/Z axis diff --git a/src/main/java/net/minestom/server/entity/EntityManager.java b/src/main/java/net/minestom/server/entity/EntityManager.java index 0fd0d5db6..87cf67a58 100644 --- a/src/main/java/net/minestom/server/entity/EntityManager.java +++ b/src/main/java/net/minestom/server/entity/EntityManager.java @@ -36,17 +36,20 @@ public final class EntityManager { Check.notNull(spawningInstance, "You need to specify a spawning instance in the PlayerLoginEvent"); - waitingPlayer.setInstance(spawningInstance); + { + final Player finalWaitingPlayer = waitingPlayer; + spawningInstance.scheduleNextTick(instance -> finalWaitingPlayer.setInstance(instance)); + } } } /** - * Call the player initialization callbacks and the event {@link PlayerPreLoginEvent} - * If the player hasn't been kicked, add him to the waiting list + * Call the player initialization callbacks and the event {@link PlayerPreLoginEvent}. + * If the {@link Player} hasn't been kicked, add him to the waiting list. *

- * Can be considered as a pre-init thing + * Can be considered as a pre-init thing. * - * @param player the player to add + * @param player the {@link Player} to add */ public void addWaitingPlayer(Player player) { diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 70acd58dc..04efb4d68 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -176,8 +176,10 @@ public class Player extends LivingEntity implements CommandSender { } /** - * Used when the player is created - * Init the player and spawn him + * Used when the player is created. + * Init the player and spawn him. + *

+ * WARNING: executed in the main update thread */ protected void init() { JoinGamePacket joinGamePacket = new JoinGamePacket(); diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index d170a1919..cbefcbf33 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -938,8 +938,8 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta * @param time the current time */ public void tick(long time) { - { - // scheduled tasks + // scheduled tasks + if (!nextTick.isEmpty()) { Consumer callback; while ((callback = nextTick.poll()) != null) { callback.accept(this);