mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-12 11:21:15 +01:00
Fix potential deadlock on instance join
This commit is contained in:
parent
2f5661b313
commit
1e58318a3a
@ -67,7 +67,7 @@ import net.minestom.server.snapshot.EntitySnapshot;
|
|||||||
import net.minestom.server.snapshot.PlayerSnapshot;
|
import net.minestom.server.snapshot.PlayerSnapshot;
|
||||||
import net.minestom.server.snapshot.SnapshotUpdater;
|
import net.minestom.server.snapshot.SnapshotUpdater;
|
||||||
import net.minestom.server.statistic.PlayerStatistic;
|
import net.minestom.server.statistic.PlayerStatistic;
|
||||||
import net.minestom.server.timer.SchedulerManager;
|
import net.minestom.server.timer.Scheduler;
|
||||||
import net.minestom.server.utils.MathUtils;
|
import net.minestom.server.utils.MathUtils;
|
||||||
import net.minestom.server.utils.PacketUtils;
|
import net.minestom.server.utils.PacketUtils;
|
||||||
import net.minestom.server.utils.async.AsyncUtils;
|
import net.minestom.server.utils.async.AsyncUtils;
|
||||||
@ -92,7 +92,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
@ -529,41 +529,49 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||||||
}
|
}
|
||||||
// Must update the player chunks
|
// Must update the player chunks
|
||||||
final boolean dimensionChange = !Objects.equals(dimensionType, instance.getDimensionType());
|
final boolean dimensionChange = !Objects.equals(dimensionType, instance.getDimensionType());
|
||||||
final Thread runThread = Thread.currentThread();
|
|
||||||
final Consumer<Instance> runnable = (i) -> spawnPlayer(i, spawnPosition,
|
final Consumer<Instance> runnable = (i) -> spawnPlayer(i, spawnPosition,
|
||||||
currentInstance == null, dimensionChange, true);
|
currentInstance == null, dimensionChange, true);
|
||||||
// Wait for all surrounding chunks to load
|
|
||||||
List<CompletableFuture<Chunk>> futures = new ArrayList<>();
|
|
||||||
ChunkUtils.forChunksInRange(spawnPosition, MinecraftServer.getChunkViewDistance(),
|
|
||||||
(chunkX, chunkZ) -> futures.add(instance.loadOptionalChunk(chunkX, chunkZ)));
|
|
||||||
|
|
||||||
SchedulerManager scheduler = MinecraftServer.getSchedulerManager();
|
// Ensure that surrounding chunks are loaded
|
||||||
AtomicBoolean join = new AtomicBoolean();
|
List<CompletableFuture<Chunk>> futures = new ArrayList<>();
|
||||||
|
ChunkUtils.forChunksInRange(spawnPosition, MinecraftServer.getChunkViewDistance(), (chunkX, chunkZ) -> {
|
||||||
|
final CompletableFuture<Chunk> future = instance.loadOptionalChunk(chunkX, chunkZ);
|
||||||
|
if (!future.isDone()) futures.add(future);
|
||||||
|
});
|
||||||
|
if (futures.isEmpty()) {
|
||||||
|
// All chunks are already loaded
|
||||||
|
runnable.accept(instance);
|
||||||
|
return AsyncUtils.VOID_FUTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One or more chunks need to be loaded
|
||||||
|
final Thread runThread = Thread.currentThread();
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
Scheduler scheduler = scheduler();
|
||||||
CompletableFuture<Void> future = new CompletableFuture<>() {
|
CompletableFuture<Void> future = new CompletableFuture<>() {
|
||||||
@Override
|
@Override
|
||||||
public Void join() {
|
public Void join() {
|
||||||
// Prevent deadlock
|
// Prevent deadlock
|
||||||
scheduler.process();
|
if (runThread == Thread.currentThread()) {
|
||||||
join.set(true);
|
try {
|
||||||
final Void result = super.join();
|
latch.await();
|
||||||
join.set(false);
|
} catch (InterruptedException e) {
|
||||||
return result;
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
scheduler.process();
|
||||||
|
assert isDone();
|
||||||
|
}
|
||||||
|
return super.join();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))
|
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))
|
||||||
.thenRun(() -> {
|
.thenRun(() -> {
|
||||||
if (runThread == Thread.currentThread()) {
|
scheduler.scheduleNextProcess(() -> {
|
||||||
runnable.accept(instance);
|
runnable.accept(instance);
|
||||||
future.complete(null);
|
future.complete(null);
|
||||||
} else {
|
});
|
||||||
scheduler.scheduleNextProcess(() -> {
|
latch.countDown();
|
||||||
runnable.accept(instance);
|
|
||||||
future.complete(null);
|
|
||||||
});
|
|
||||||
if (join.compareAndSet(true, false))
|
|
||||||
scheduler.process();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user