mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-23 00:49:31 +00:00
* init Multithreaded Tracker * Rebase & Clean up * Some clean up * Some work * Checked some petal issues * Fix tracker * Unify thread name again * Nitori: Async playerdata Save * Rebase * Fix Citizens player type NPC tracking issue (WIP) Temporary move sendChanges to off-main only. This can fix Citizens's player type NPC visible issue. But still working on making updatePlayer async too, since it also takes big part of performance, and also need to be compat with Citizens. * Drop useless patch * Adjust comments * Optimize tracker, batch processing sendChanges tasks * Clean up and fix * Rebase * Partial update player asynchronously & Fix citizens player type NPC visual issue This made async tracker compat with CItizens, but still need to further optimize * Optimize and update config * Fix realPlayer detect condition & Made more async & Update patch comment * Add compat mode for tracker By isolating Citizens compat logic into compat mode, it can gain more performance if Citizens is not installed. * Update comment
138 lines
9.2 KiB
Diff
138 lines
9.2 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: MrPowerGamerBR <git@mrpowergamerbr.com>
|
|
Date: Sun, 19 Nov 2023 12:35:16 -0300
|
|
Subject: [PATCH] SparklyPaper: Skip EntityScheduler's executeTick checks if
|
|
there isn't any tasks to be run
|
|
|
|
Original project: https://github.com/SparklyPower/SparklyPaper
|
|
|
|
On each tick, Paper runs EntityScheduler's executeTick of each entity. This is a bit expensive, due to ArrayDeque's size() call because it ain't a simple "get the current queue size" function, due to the thread checks, and because it needs to iterate all entities in all worlds.
|
|
|
|
To avoid the hefty ArrayDeque's size() call, we check if we *really* need to execute the executeTick, by adding all entities with scheduled tasks to a global set.
|
|
|
|
Most entities won't have any scheduled tasks, so this is a nice performance bonus. These optimizations, however, wouldn't work in a Folia environment, but because in SparklyPaper executeTick is always executed on the main thread, it ain't an issue for us (yay).
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
|
|
index c03608fec96b51e1867f43d8f42e5aefb1520e46..15b21fa3907db1b77ed5b5d1050a37f42d27d5ab 100644
|
|
--- a/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
|
|
+++ b/src/main/java/io/papermc/paper/threadedregions/EntityScheduler.java
|
|
@@ -36,6 +36,7 @@ public final class EntityScheduler {
|
|
* The Entity. Note that it is the CraftEntity, since only that class properly tracks world transfers.
|
|
*/
|
|
public final CraftEntity entity;
|
|
+ public final net.minecraft.server.MinecraftServer server; // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
|
|
private static final record ScheduledTask(Consumer<? extends Entity> run, Consumer<? extends Entity> retired) {}
|
|
|
|
@@ -46,7 +47,8 @@ public final class EntityScheduler {
|
|
|
|
private final ArrayDeque<ScheduledTask> currentlyExecuting = new ArrayDeque<>();
|
|
|
|
- public EntityScheduler(final CraftEntity entity) {
|
|
+ public EntityScheduler(final net.minecraft.server.MinecraftServer server, final CraftEntity entity) { // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
+ this.server = Validate.notNull(server);
|
|
this.entity = Validate.notNull(entity);
|
|
}
|
|
|
|
@@ -61,14 +63,16 @@ public final class EntityScheduler {
|
|
* @throws IllegalStateException If the scheduler is already retired.
|
|
*/
|
|
public void retire() {
|
|
+ final Entity thisEntity = this.entity.getHandleRaw(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
synchronized (this.stateLock) {
|
|
if (this.tickCount == RETIRED_TICK_COUNT) {
|
|
throw new IllegalStateException("Already retired");
|
|
}
|
|
this.tickCount = RETIRED_TICK_COUNT;
|
|
+ this.server.entitiesWithScheduledTasks.remove(thisEntity); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
}
|
|
|
|
- final Entity thisEntity = this.entity.getHandleRaw();
|
|
+ // final Entity thisEntity = this.entity.getHandleRaw(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (moved up)
|
|
|
|
// correctly handle and order retiring while running executeTick
|
|
for (int i = 0, len = this.currentlyExecuting.size(); i < len; ++i) {
|
|
@@ -124,6 +128,7 @@ public final class EntityScheduler {
|
|
if (this.tickCount == RETIRED_TICK_COUNT) {
|
|
return false;
|
|
}
|
|
+ this.server.entitiesWithScheduledTasks.add(this.entity.getHandleRaw()); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
this.oneTimeDelayed.computeIfAbsent(this.tickCount + Math.max(1L, delay), (final long keyInMap) -> {
|
|
return new ArrayList<>();
|
|
}).add(task);
|
|
@@ -143,6 +148,13 @@ public final class EntityScheduler {
|
|
TickThread.ensureTickThread(thisEntity, "May not tick entity scheduler asynchronously");
|
|
final List<ScheduledTask> toRun;
|
|
synchronized (this.stateLock) {
|
|
+ // SparklyPaper start - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
+ // Do we *really* have scheduled tasks tho?
|
|
+ if (this.currentlyExecuting.isEmpty() && this.oneTimeDelayed.isEmpty()) { // Check if we have any pending tasks and, if not, skip!
|
|
+ this.server.entitiesWithScheduledTasks.remove(thisEntity); // We don't! Bye bye!!
|
|
+ return;
|
|
+ }
|
|
+ // SparklyPaper end
|
|
if (this.tickCount == RETIRED_TICK_COUNT) {
|
|
throw new IllegalStateException("Ticking retired scheduler");
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 55466ff58d724a6254fd6e53c72adb592f2d433a..0255d5a23e2f5a1a67af3ff1588b608521e4c133 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -312,6 +312,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
|
|
|
|
public gg.pufferfish.pufferfish.util.AsyncExecutor mobSpawnExecutor = new gg.pufferfish.pufferfish.util.AsyncExecutor("Leaf Async Mob Spawn Thread"); // Pufferfish - optimize mob spawning // Leaf - Unify thread name
|
|
+ public final Set<Entity> entitiesWithScheduledTasks = java.util.concurrent.ConcurrentHashMap.newKeySet(); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run (concurrent because plugins may schedule tasks async)
|
|
|
|
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
|
|
AtomicReference<S> atomicreference = new AtomicReference();
|
|
@@ -1732,6 +1733,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
this.server.getScheduler().mainThreadHeartbeat(this.tickCount); // CraftBukkit
|
|
// Paper start - Folia scheduler API
|
|
((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) Bukkit.getGlobalRegionScheduler()).tick();
|
|
+ // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
+ for (final Entity entity : entitiesWithScheduledTasks) {
|
|
+ if (entity.isRemoved()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw();
|
|
+ if (bukkit != null) {
|
|
+ bukkit.taskScheduler.executeTick();
|
|
+ }
|
|
+ }
|
|
+ /*
|
|
getAllLevels().forEach(level -> {
|
|
for (final Entity entity : level.moonrise$getEntityLookup().getAllCopy()) { // Paper - rewrite chunk system
|
|
if (entity.isRemoved()) {
|
|
@@ -1743,6 +1756,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
}
|
|
});
|
|
+ */
|
|
+ // SparklyPaper end
|
|
// Paper end - Folia scheduler API
|
|
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
|
|
this.getFunctions().tick();
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
index bec40e26e17ba6fdefb68c79e2fa7a9593b0f4fe..233df12bfa5c49137ad50f421bf723100844758f 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
@@ -71,7 +71,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftEntity.DATA_TYPE_REGISTRY);
|
|
protected net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
|
|
// Paper start - Folia shedulers
|
|
- public final io.papermc.paper.threadedregions.EntityScheduler taskScheduler = new io.papermc.paper.threadedregions.EntityScheduler(this);
|
|
+ public final io.papermc.paper.threadedregions.EntityScheduler taskScheduler; // = new io.papermc.paper.threadedregions.EntityScheduler(this); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
private final io.papermc.paper.threadedregions.scheduler.FoliaEntityScheduler apiScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaEntityScheduler(this);
|
|
|
|
@Override
|
|
@@ -84,6 +84,7 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
this.server = server;
|
|
this.entity = entity;
|
|
this.entityType = CraftEntityType.minecraftToBukkit(entity.getType());
|
|
+ this.taskScheduler = new io.papermc.paper.threadedregions.EntityScheduler(this.entity.getServer(), this); // SparklyPaper - skip EntityScheduler's executeTick checks if there isn't any tasks to be run
|
|
}
|
|
|
|
// Purpur start - API for any mob to burn daylight
|