Fix the first #setInstance being called in the main update thread

This commit is contained in:
themode 2020-10-12 04:14:06 +02:00
parent 88ef4eb3c0
commit 9184b3054f
5 changed files with 32 additions and 16 deletions

View File

@ -70,7 +70,7 @@ public final class UpdateManager {
futures = threadProvider.update(time); 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(); entityManager.updateWaitingPlayers();
// Keep Alive Handling // Keep Alive Handling

View File

@ -20,6 +20,7 @@ import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.WorldBorder; import net.minestom.server.instance.WorldBorder;
import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.packet.server.play.*; 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.ArrayUtils;
import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position; import net.minestom.server.utils.Position;
@ -33,6 +34,7 @@ import net.minestom.server.utils.validate.Check;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -114,7 +116,8 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
protected boolean noGravity; protected boolean noGravity;
protected Pose pose = Pose.STANDING; protected Pose pose = Pose.STANDING;
protected final List<Consumer<Entity>> nextTick = Collections.synchronizedList(new ArrayList<>()); // list of scheduled tasks to be executed during the next entity tick
protected final ConcurrentLinkedQueue<Consumer<Entity>> nextTick = new ConcurrentLinkedQueue<>();
// Tick related // Tick related
private long ticks; private long ticks;
@ -134,8 +137,14 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
setVelocityUpdatePeriod(5); 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<Entity> callback) { public void scheduleNextTick(Consumer<Entity> callback) {
nextTick.add(callback); this.nextTick.add(callback);
} }
public Entity(EntityType entityType) { public Entity(EntityType entityType) {
@ -353,12 +362,14 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
return; return;
} }
synchronized (nextTick) { // scheduled tasks
for (final Consumer<Entity> e : nextTick) { if (!nextTick.isEmpty()) {
e.accept(this); Consumer<Entity> callback;
while ((callback = nextTick.poll()) != null) {
callback.accept(this);
} }
nextTick.clear();
} }
// Synchronization with updated fields in #getPosition() // Synchronization with updated fields in #getPosition()
{ {
// X/Y/Z axis // X/Y/Z axis

View File

@ -36,17 +36,20 @@ public final class EntityManager {
Check.notNull(spawningInstance, "You need to specify a spawning instance in the PlayerLoginEvent"); 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} * Call the player initialization callbacks and the event {@link PlayerPreLoginEvent}.
* If the player hasn't been kicked, add him to the waiting list * If the {@link Player} hasn't been kicked, add him to the waiting list.
* <p> * <p>
* 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) { public void addWaitingPlayer(Player player) {

View File

@ -176,8 +176,10 @@ public class Player extends LivingEntity implements CommandSender {
} }
/** /**
* Used when the player is created * Used when the player is created.
* Init the player and spawn him * Init the player and spawn him.
* <p>
* WARNING: executed in the main update thread
*/ */
protected void init() { protected void init() {
JoinGamePacket joinGamePacket = new JoinGamePacket(); JoinGamePacket joinGamePacket = new JoinGamePacket();

View File

@ -938,8 +938,8 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
* @param time the current time * @param time the current time
*/ */
public void tick(long time) { public void tick(long time) {
{ // scheduled tasks
// scheduled tasks if (!nextTick.isEmpty()) {
Consumer<Instance> callback; Consumer<Instance> callback;
while ((callback = nextTick.poll()) != null) { while ((callback = nextTick.poll()) != null) {
callback.accept(this); callback.accept(this);