9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2026-01-04 15:41:40 +00:00

Petal: Async Pathfinding & Multithreaded Tracker

This commit is contained in:
Dreeam
2023-07-28 13:43:44 +08:00
parent 821228fc20
commit d315f4948c
4 changed files with 1704 additions and 0 deletions

View File

@@ -66,3 +66,4 @@ Thanks to these projects below. Leaf just mix some of their patches together. If
- [Slice](https://github.com/Cryptite/Slice)
- [Parchment](https://github.com/ProjectEdenGG/Parchment)
- [Leaves](https://github.com/LeavesMC/Leaves)
- [Kaiiju](https://github.com/KaiijuMC/Kaiiju)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,458 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: peaches94 <peachescu94@gmail.com>
Date: Sat, 2 Jul 2022 00:35:56 -0500
Subject: [PATCH] Petal: Multithreaded Tracker
Original code by Bloom-host, licensed under GPL v3
You can find the original code on https://github.com/Bloom-host/Petal
This patch was ported downstream from the Petal fork, and is derived from
the Airplane fork by Paul Sauve
Based off the Airplane multithreaded tracker, this patch properly handles
concurrent accesses everywhere, as well as being much simpler to maintain
Some things are too unsafe to run off the main thread so we don't attempt to do
that. This multithreaded tracker remains accurate, non-breaking and fast.
diff --git a/src/main/java/dev/etil/mirai/tracker/MultithreadedTracker.java b/src/main/java/dev/etil/mirai/tracker/MultithreadedTracker.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c0571af25494efe3369c8cea865812e5443f966
--- /dev/null
+++ b/src/main/java/dev/etil/mirai/tracker/MultithreadedTracker.java
@@ -0,0 +1,154 @@
+package dev.etil.mirai.tracker;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet;
+import io.papermc.paper.world.ChunkEntitySlices;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ChunkMap;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.chunk.LevelChunk;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class MultithreadedTracker {
+
+ private enum TrackerStage {
+ UPDATE_PLAYERS,
+ SEND_CHANGES
+ }
+
+ private static final int parallelism = Math.max(4, Runtime.getRuntime().availableProcessors());
+ private static final Executor trackerExecutor = Executors.newFixedThreadPool(parallelism, new ThreadFactoryBuilder()
+ .setNameFormat("mirai-tracker-%d")
+ .setPriority(Thread.NORM_PRIORITY - 2)
+ .build());
+
+ private final IteratorSafeOrderedReferenceSet<LevelChunk> entityTickingChunks;
+ private final AtomicInteger taskIndex = new AtomicInteger();
+
+ private final ConcurrentLinkedQueue<Runnable> mainThreadTasks;
+ private final AtomicInteger finishedTasks = new AtomicInteger();
+
+ public MultithreadedTracker(IteratorSafeOrderedReferenceSet<LevelChunk> entityTickingChunks, ConcurrentLinkedQueue<Runnable> mainThreadTasks) {
+ this.entityTickingChunks = entityTickingChunks;
+ this.mainThreadTasks = mainThreadTasks;
+ }
+
+ public void tick() {
+ int iterator = this.entityTickingChunks.createRawIterator();
+
+ if (iterator == -1) {
+ return;
+ }
+
+ // start with updating players
+ try {
+ this.taskIndex.set(iterator);
+ this.finishedTasks.set(0);
+
+ for (int i = 0; i < parallelism; i++) {
+ trackerExecutor.execute(this::runUpdatePlayers);
+ }
+
+ while (this.taskIndex.get() < this.entityTickingChunks.getListSize()) {
+ this.runMainThreadTasks();
+ this.handleChunkUpdates(5); // assist
+ }
+
+ while (this.finishedTasks.get() != parallelism) {
+ this.runMainThreadTasks();
+ }
+
+ this.runMainThreadTasks(); // finish any remaining tasks
+ } finally {
+ this.entityTickingChunks.finishRawIterator();
+ }
+
+ // then send changes
+ iterator = this.entityTickingChunks.createRawIterator();
+
+ if (iterator == -1) {
+ return;
+ }
+
+ try {
+ do {
+ LevelChunk chunk = this.entityTickingChunks.rawGet(iterator);
+
+ if (chunk != null) {
+ this.updateChunkEntities(chunk, TrackerStage.SEND_CHANGES);
+ }
+ } while (++iterator < this.entityTickingChunks.getListSize());
+ } finally {
+ this.entityTickingChunks.finishRawIterator();
+ }
+ }
+
+ private void runMainThreadTasks() {
+ try {
+ Runnable task;
+ while ((task = this.mainThreadTasks.poll()) != null) {
+ task.run();
+ }
+ } catch (Throwable throwable) {
+ MinecraftServer.LOGGER.warn("Tasks failed while ticking track queue", throwable);
+ }
+ }
+
+ private void runUpdatePlayers() {
+ try {
+ while (handleChunkUpdates(10));
+ } finally {
+ this.finishedTasks.incrementAndGet();
+ }
+ }
+
+ private boolean handleChunkUpdates(int tasks) {
+ int index;
+ while ((index = this.taskIndex.getAndAdd(tasks)) < this.entityTickingChunks.getListSize()) {
+ for (int i = index; i < index + tasks && i < this.entityTickingChunks.getListSize(); i++) {
+ LevelChunk chunk = this.entityTickingChunks.rawGet(i);
+ if (chunk != null) {
+ try {
+ this.updateChunkEntities(chunk, TrackerStage.UPDATE_PLAYERS);
+ } catch (Throwable throwable) {
+ MinecraftServer.LOGGER.warn("Ticking tracker failed", throwable);
+ }
+
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private void updateChunkEntities(LevelChunk chunk, TrackerStage trackerStage) {
+ final ChunkEntitySlices entitySlices = chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ);
+ if (entitySlices == null) {
+ return;
+ }
+
+ final Entity[] rawEntities = entitySlices.entities.getRawData();
+ final ChunkMap chunkMap = chunk.level.chunkSource.chunkMap;
+
+ for (int i = 0; i < rawEntities.length; i++) {
+ Entity entity = rawEntities[i];
+ if (entity != null) {
+ ChunkMap.TrackedEntity entityTracker = chunkMap.entityMap.get(entity.getId());
+ if (entityTracker != null) {
+ if (trackerStage == TrackerStage.SEND_CHANGES) {
+ entityTracker.serverEntity.sendChanges();
+ } else if (trackerStage == TrackerStage.UPDATE_PLAYERS) {
+ entityTracker.updatePlayers(entityTracker.entity.getPlayersInTrackRange());
+ }
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
index 760535e1d6335cf07b6222525610a11318d2596f..2632dade6bfaa185a94e95210a31dc3824b7746f 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
@@ -4,6 +4,7 @@ import co.aikar.timings.TimedEventExecutor;
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
import com.destroystokyo.paper.exception.ServerEventException;
import com.google.common.collect.Sets;
+import net.minecraft.server.MinecraftServer;
import org.bukkit.Server;
import org.bukkit.Warning;
import org.bukkit.event.Event;
@@ -43,7 +44,14 @@ class PaperEventManager {
if (isAsync && onPrimaryThread) {
throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously.");
} else if (!isAsync && !onPrimaryThread && !this.server.isStopping()) {
- throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
+ // Mirai start
+ if (org.dreeam.leaf.LeafConfig.enableAsyncEntityTracker) {
+ MinecraftServer.getServer().scheduleOnMain(event::callEvent);
+ return;
+ } else {
+ throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
+ }
+ // Mirai end
}
// KTP stop - Optimise spigot event bus
}
diff --git a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
index 0fd814f1d65c111266a2b20f86561839a4cef755..dc06747df171678c8531e1153c5fa9b80b70baed 100644
--- a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
+++ b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java
@@ -15,7 +15,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
/* list impl */
protected E[] listElements;
- protected int listSize;
+ protected int listSize; public int getListSize() { return this.listSize; } // Mirai - expose listSize
protected final double maxFragFactor;
diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
index c21398bd39bd1e96f8005539909aa7497fb19c9e..c1ff73d0b959e7e8b663b84d1fa0725e1967283b 100644
--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
@@ -36,7 +36,7 @@ public final class ChunkEntitySlices {
protected final EntityCollectionBySection allEntities;
protected final EntityCollectionBySection hardCollidingEntities;
protected final Reference2ObjectOpenHashMap<Class<? extends Entity>, EntityCollectionBySection> entitiesByClass;
- protected final EntityList entities = new EntityList();
+ public final EntityList entities = new EntityList();
public FullChunkStatus status;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 5412f7921674d00be5c83f25f677b4896223930e..25672a4d61876241044000170cd651bfe0031313 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1242,8 +1242,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = null; // Paper - We're no longer tracked
}
+ // Mirai start - multithreaded tracker
+ private @Nullable dev.etil.mirai.tracker.MultithreadedTracker multithreadedTracker;
+ private final java.util.concurrent.ConcurrentLinkedQueue<Runnable> trackerMainThreadTasks = new java.util.concurrent.ConcurrentLinkedQueue<>();
+ private boolean tracking = false;
+
+ public void runOnTrackerMainThread(final Runnable runnable) {
+ if (this.tracking) {
+ this.trackerMainThreadTasks.add(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
// Paper start - optimised tracker
private final void processTrackQueue() {
+ if (org.dreeam.leaf.LeafConfig.enableAsyncEntityTracker) {
+ if (this.multithreadedTracker == null) {
+ this.multithreadedTracker = new dev.etil.mirai.tracker.MultithreadedTracker(this.level.chunkSource.entityTickingChunks, this.trackerMainThreadTasks);
+ }
+
+ this.tracking = true;
+ try {
+ this.multithreadedTracker.tick();
+ } finally {
+ this.tracking = false;
+ }
+ return;
+ }
+ // Mirai end
this.level.timings.tracker1.startTiming();
try {
for (TrackedEntity tracker : this.entityMap.values()) {
@@ -1421,11 +1448,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public class TrackedEntity {
- public final ServerEntity serverEntity;
- final Entity entity;
+ public final ServerEntity serverEntity; // Mirai -> public
+ public final Entity entity; // Mirai -> public
private final int range;
SectionPos lastSectionPos;
- public final Set<ServerPlayerConnection> seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl
+ public final Set<ServerPlayerConnection> seenBy = it.unimi.dsi.fastutil.objects.ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); // Paper - optimise map impl // Mirai - sync
public TrackedEntity(Entity entity, int i, int j, boolean flag) {
this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit
@@ -1437,7 +1464,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper start - use distance map to optimise tracker
com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> lastTrackerCandidates;
- final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newTrackerCandidates) {
+ public final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newTrackerCandidates) { // Mirai -> public
com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> oldTrackerCandidates = this.lastTrackerCandidates;
this.lastTrackerCandidates = newTrackerCandidates;
@@ -1479,14 +1506,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcast(Packet<?> packet) {
- Iterator iterator = this.seenBy.iterator();
-
- while (iterator.hasNext()) {
- ServerPlayerConnection serverplayerconnection = (ServerPlayerConnection) iterator.next();
-
+ // Mirai start - avoid NPE
+ for (ServerPlayerConnection serverplayerconnection : this.seenBy.toArray(new ServerPlayerConnection[0])) {
serverplayerconnection.send(packet);
}
-
+ // Mirai end
}
public void broadcastAndSend(Packet<?> packet) {
@@ -1498,18 +1522,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void broadcastRemoved() {
- Iterator iterator = this.seenBy.iterator();
-
- while (iterator.hasNext()) {
- ServerPlayerConnection serverplayerconnection = (ServerPlayerConnection) iterator.next();
-
+ // Miria start - avoid NPE
+ for (ServerPlayerConnection serverplayerconnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { // Mirai - avoid NPE
this.serverEntity.removePairing(serverplayerconnection.getPlayer());
}
-
+ // Mirai end
}
public void removePlayer(ServerPlayer player) {
- org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
+ //org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot // Mirai - we can remove async too
if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
}
@@ -1517,7 +1538,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void updatePlayer(ServerPlayer player) {
- org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
+ //org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot // Mirai - we can update async
if (player != this.entity) {
// Paper start - remove allocation of Vec3D here
// Vec3 vec3d = player.position().subtract(this.entity.position());
diff --git a/src/main/java/net/minecraft/server/level/ServerBossEvent.java b/src/main/java/net/minecraft/server/level/ServerBossEvent.java
index ca42c2642a729b90d22b968af7258f3aee72e14b..c4d6d5be09c788bbc062c50d8547538eb84530b4 100644
--- a/src/main/java/net/minecraft/server/level/ServerBossEvent.java
+++ b/src/main/java/net/minecraft/server/level/ServerBossEvent.java
@@ -13,7 +13,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.BossEvent;
public class ServerBossEvent extends BossEvent {
- private final Set<ServerPlayer> players = Sets.newHashSet();
+ private final Set<ServerPlayer> players = Sets.newConcurrentHashSet(); // Mirai - players can be removed in async tracking
private final Set<ServerPlayer> unmodifiablePlayers = Collections.unmodifiableSet(this.players);
public boolean visible = true;
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
index 04e749a0b3842fd9e1d8ca801e0c826aa07cbdec..97fc97ea45bc9779d94d24e12efc7b5608695f3f 100644
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
@@ -277,7 +277,11 @@ public class ServerEntity {
public void removePairing(ServerPlayer player) {
this.entity.stopSeenByPlayer(player);
- player.connection.send(new ClientboundRemoveEntitiesPacket(new int[]{this.entity.getId()}));
+ // Mirai start - ensure main thread
+ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() ->
+ player.connection.send(new ClientboundRemoveEntitiesPacket(new int[]{this.entity.getId()}))
+ );
+ // Mirai end
}
public void addPairing(ServerPlayer player) {
@@ -285,7 +289,7 @@ public class ServerEntity {
Objects.requireNonNull(list);
this.sendPairingData(player, list::add);
- player.connection.send(new ClientboundBundlePacket(list));
+ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> player.connection.send(new ClientboundBundlePacket(list))); // Mirai - main thread
this.entity.startSeenByPlayer(player);
}
@@ -377,19 +381,29 @@ public class ServerEntity {
if (list != null) {
this.trackedDataValues = datawatcher.getNonDefaultValues();
- this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list));
+ // Mirai start - sync
+ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() ->
+ this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list))
+ );
+ // Mirai end
}
if (this.entity instanceof LivingEntity) {
Set<AttributeInstance> set = ((LivingEntity) this.entity).getAttributes().getDirtyAttributes();
if (!set.isEmpty()) {
+ // Mirai start - sync
+ final var copy = Lists.newArrayList(set);
+ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> {
// CraftBukkit start - Send scaled max health
if (this.entity instanceof ServerPlayer) {
- ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(set, false);
+ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(copy, false);
}
// CraftBukkit end
- this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), set));
+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), copy));
+
+ });
+ // Mirai end
}
set.clear();
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index b77a84a5ab85839e37aee24da0f4356be3f478e2..75b53f1f237472f55d883a22cc8289c433b801c0 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -2614,7 +2614,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
@Override
public LevelEntityGetter<Entity> getEntities() {
- org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
+ // org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot // Mirai
return this.entityLookup; // Paper - rewrite chunk system
}
diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java
index b8a56e79e2526bdb137c20c083cb0843d80f39ef..2aea6580eaa3702a2eead379e7d2e21879543c3b 100644
--- a/src/main/java/org/dreeam/leaf/LeafConfig.java
+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java
@@ -199,6 +199,8 @@ public class LeafConfig {
public static boolean useSpigotItemMergingMechanism = true;
public static boolean enableAsyncPathfinding;
public static boolean enableAsyncPathfindingInitialized;
+ public static boolean enableAsyncEntityTracker;
+ public static boolean enableAsyncEntityTrackerInitialized;
private static void performance() {
String sentryEnvironment = System.getenv("SENTRY_DSN");
String sentryConfig = getString("performance.sentry-dsn", sentryDsn, "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/");
@@ -262,6 +264,13 @@ public class LeafConfig {
enableAsyncPathfindingInitialized = true;
enableAsyncPathfinding = asyncPathfinding;
}
+ boolean asyncEntityTracker = getBoolean("performance.enable-async-entity-tracker", true,
+ "Whether or not async entity tracking should be enabled.",
+ "You may encounter issues with NPCs.");
+ if (!enableAsyncEntityTrackerInitialized) {
+ enableAsyncEntityTrackerInitialized = true;
+ enableAsyncEntityTracker = asyncEntityTracker;
+ }
}
public static boolean jadeProtocol = false;

View File

@@ -0,0 +1,48 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: rafaelflromao <12960698+rafaelflromao@users.noreply.github.com>
Date: Mon, 12 Jun 2023 06:01:40 +0100
Subject: [PATCH] Petal: Sync event calls on async threads
Original code by Bloom-host, licensed under GPL v3
You can find the original code on https://github.com/Bloom-host/Petal
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
index 2632dade6bfaa185a94e95210a31dc3824b7746f..9687d4a87112a6ed991935321ef15f32dd116b48 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
@@ -45,7 +45,7 @@ class PaperEventManager {
throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously.");
} else if (!isAsync && !onPrimaryThread && !this.server.isStopping()) {
// Mirai start
- if (org.dreeam.leaf.LeafConfig.enableAsyncEntityTracker) {
+ if (org.dreeam.leaf.LeafConfig.enableAsyncEntityTracker || org.dreeam.leaf.LeafConfig.enableSyncEventCallsOnAsyncThreads) {
MinecraftServer.getServer().scheduleOnMain(event::callEvent);
return;
} else {
diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java
index 2aea6580eaa3702a2eead379e7d2e21879543c3b..2eda021e11216013af7875371797b387b6c018f9 100644
--- a/src/main/java/org/dreeam/leaf/LeafConfig.java
+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java
@@ -201,6 +201,8 @@ public class LeafConfig {
public static boolean enableAsyncPathfindingInitialized;
public static boolean enableAsyncEntityTracker;
public static boolean enableAsyncEntityTrackerInitialized;
+ public static boolean enableSyncEventCallsOnAsyncThreads;
+ public static boolean enableSyncEventCallsOnAsyncThreadsInitialized;
private static void performance() {
String sentryEnvironment = System.getenv("SENTRY_DSN");
String sentryConfig = getString("performance.sentry-dsn", sentryDsn, "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/");
@@ -271,6 +273,13 @@ public class LeafConfig {
enableAsyncEntityTrackerInitialized = true;
enableAsyncEntityTracker = asyncEntityTracker;
}
+ boolean syncEventCalls = getBoolean("performance.enable-sync-event-calls-on-async-threads", true,
+ "Whether or not sync event calls on async threads should be enabled. (If async entity tracker is enabled, this is enabled.)",
+ "You may encounter issues with plugins.");
+ if (!enableSyncEventCallsOnAsyncThreadsInitialized) {
+ enableSyncEventCallsOnAsyncThreadsInitialized = true;
+ enableSyncEventCallsOnAsyncThreads = syncEventCalls;
+ }
}
public static boolean jadeProtocol = false;