diff --git a/patches/server/0064-Hearse-Move-player-ticking-to-main-thread-and-add-lo.patch b/patches/server/0064-Hearse-Move-player-ticking-to-main-thread-and-add-lo.patch index 48f00062..b04bce12 100644 --- a/patches/server/0064-Hearse-Move-player-ticking-to-main-thread-and-add-lo.patch +++ b/patches/server/0064-Hearse-Move-player-ticking-to-main-thread-and-add-lo.patch @@ -7,27 +7,6 @@ Subject: [PATCH] Hearse: Move player ticking to main thread and add lock in Original license: MIT Original project: https://github.com/NaturalCodeClub/HearseRewrite -diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java -index 9d26ff7d07f1e972f1720f5b2d0e66d4c9c3f1e5..86f8afd54c0cbb449403c3f43e6880ade13cfecc 100644 ---- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java -+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java -@@ -8,6 +8,7 @@ import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory; - import net.minecraft.server.MinecraftServer; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.world.entity.Entity; -+import net.minecraft.world.entity.player.Player; - import org.apache.logging.log4j.LogManager; - import org.apache.logging.log4j.Logger; - import java.util.concurrent.LinkedBlockingQueue; -@@ -87,7 +88,7 @@ public class ServerEntityTickHook { - } - } - }; -- if (!asyncEntityEnabled){ -+ if (!asyncEntityEnabled || entity instanceof Player){ - task.run(); - return; - } diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java index ae22ca9ea5fd3d78d8c5bf9f1ab96f1129fddc11..1012b8d1d192a946b0982c88c12a0fc0e6051972 100644 --- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java diff --git a/patches/server/0070-Hearse-Add-new-command.patch b/patches/server/0069-Hearse-Add-new-command.patch similarity index 100% rename from patches/server/0070-Hearse-Add-new-command.patch rename to patches/server/0069-Hearse-Add-new-command.patch diff --git a/patches/server/0069-Hearse-Change-something.patch b/patches/server/0069-Hearse-Change-something.patch deleted file mode 100644 index 33fe3d34..00000000 --- a/patches/server/0069-Hearse-Change-something.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: wangxyper -Date: Fri, 13 Jan 2023 15:25:47 +0800 -Subject: [PATCH] Hearse: Change something - -Original license: MIT -Original project: https://github.com/NaturalCodeClub/HearseRewrite - -diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java -index 86f8afd54c0cbb449403c3f43e6880ade13cfecc..c0e7a9cf79ddf00827daba0aa9c7a32fa76b0c7c 100644 ---- a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java -+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java -@@ -88,7 +88,7 @@ public class ServerEntityTickHook { - } - } - }; -- if (!asyncEntityEnabled || entity instanceof Player){ -+ if (!asyncEntityEnabled){ - task.run(); - return; - } diff --git a/patches/server/0070-Hearse-Fix-a-NoSuchElementError-in-Delayed8-and-Dela.patch b/patches/server/0070-Hearse-Fix-a-NoSuchElementError-in-Delayed8-and-Dela.patch new file mode 100644 index 00000000..c0cbfeea --- /dev/null +++ b/patches/server/0070-Hearse-Fix-a-NoSuchElementError-in-Delayed8-and-Dela.patch @@ -0,0 +1,225 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Sat, 14 Jan 2023 09:27:14 +0800 +Subject: [PATCH] Hearse: Fix a NoSuchElementError in Delayed8 and Delayed26 + distance propagators + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java +index 762f09c8f374fbccc9f5be985401ad334e1655a0..e831738a2988746fe4e065f6ded811a8bdf5dabe 100644 +--- a/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java ++++ b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java +@@ -94,24 +94,42 @@ public final class Delayed26WayDistancePropagator3D { + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[level]; +- queue.queuedCoordinates.add(coordinate); +- queue.queuedLevels.add(level); ++ ++ final long id = queue.lock.writeLock(); ++ try { ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + this.levelIncreaseWorkQueueBitset |= (1L << level); + } + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[index]; +- queue.queuedCoordinates.add(coordinate); +- queue.queuedLevels.add(level); ++ ++ final long id = queue.lock.writeLock(); ++ try { ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + this.levelIncreaseWorkQueueBitset |= (1L << index); + } + + protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[level]; +- queue.queuedCoordinates.add(coordinate); +- queue.queuedLevels.add(level); ++ ++ final long id = queue.lock.writeLock(); ++ try { ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + this.levelRemoveWorkQueueBitset |= (1L << level); + } +@@ -163,9 +181,20 @@ public final class Delayed26WayDistancePropagator3D { + this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) { + + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; +- while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirst(); +- byte level = queue.queuedLevels.removeFirst(); ++ while (true) { ++ ++ long coordinate; ++ byte level; ++ final long id = queue.lock.writeLock(); ++ try { ++ if (queue.queuedLevels.isEmpty()){ ++ break; ++ } ++ coordinate = queue.queuedCoordinates.removeFirst(); ++ level = queue.queuedLevels.removeFirst(); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + final boolean neighbourCheck = level < 0; + +@@ -232,9 +261,19 @@ public final class Delayed26WayDistancePropagator3D { + this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) { + + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; +- while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirst(); +- final byte level = queue.queuedLevels.removeFirst(); ++ while (true) { ++ long coordinate; ++ byte level; ++ final long id = queue.lock.writeLock(); ++ try { ++ if (queue.queuedLevels.isEmpty()){ ++ break; ++ } ++ coordinate = queue.queuedCoordinates.removeFirst(); ++ level = queue.queuedLevels.removeFirst(); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); + if (currentLevel == 0) { +diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java +index 8c5a51b5992eccf3627f326e164288b5f6bbcff6..0fa95d81bafc7fe5c1bede7a0608b54795a78fa0 100644 +--- a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java ++++ b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java +@@ -8,6 +8,7 @@ import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; + + import java.util.Deque; + import java.util.concurrent.ConcurrentLinkedDeque; ++import java.util.concurrent.locks.StampedLock; + + public final class Delayed8WayDistancePropagator2D { + +@@ -357,24 +358,42 @@ public final class Delayed8WayDistancePropagator2D { + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { + final WorkQueue queue = this.levelIncreaseWorkQueues[level]; +- queue.queuedCoordinates.add(coordinate); +- queue.queuedLevels.add(level); ++ ++ final long id = queue.lock.writeLock(); ++ try { ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + this.levelIncreaseWorkQueueBitset |= (1L << level); + } + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) { + final WorkQueue queue = this.levelIncreaseWorkQueues[index]; +- queue.queuedCoordinates.add(coordinate); +- queue.queuedLevels.add(level); ++ ++ final long id = queue.lock.writeLock(); ++ try { ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + this.levelIncreaseWorkQueueBitset |= (1L << index); + } + + protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { + final WorkQueue queue = this.levelRemoveWorkQueues[level]; +- queue.queuedCoordinates.add(coordinate); +- queue.queuedLevels.add(level); ++ ++ final long id = queue.lock.writeLock(); ++ try { ++ queue.queuedCoordinates.add(coordinate); ++ queue.queuedLevels.add(level); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + this.levelRemoveWorkQueueBitset |= (1L << level); + } +@@ -426,9 +445,19 @@ public final class Delayed8WayDistancePropagator2D { + this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) { + + final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; +- while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirst(); +- byte level = queue.queuedLevels.removeFirst(); ++ while (true) { ++ byte level; ++ long coordinate; ++ final long id = queue.lock.writeLock(); ++ try { ++ if (queue.queuedLevels.isEmpty()){ ++ break; ++ } ++ coordinate = queue.queuedCoordinates.removeFirst(); ++ level = queue.queuedLevels.removeFirst(); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + final boolean neighbourCheck = level < 0; + +@@ -492,9 +521,20 @@ public final class Delayed8WayDistancePropagator2D { + this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) { + + final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; +- while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirst(); +- final byte level = queue.queuedLevels.removeFirst(); ++ while (true) { ++ long coordinate; ++ byte level; ++ ++ final long id = queue.lock.writeLock(); ++ try { ++ if (queue.queuedLevels.isEmpty()){ ++ break; ++ } ++ coordinate = queue.queuedCoordinates.removeFirst(); ++ level = queue.queuedLevels.removeFirst(); ++ }finally { ++ queue.lock.unlockWrite(id); ++ } + + final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); + if (currentLevel == 0) { +@@ -681,6 +721,7 @@ public final class Delayed8WayDistancePropagator2D { + protected static final class WorkQueue { + public final Deque queuedCoordinates = new ConcurrentLinkedDeque<>(); + public final Deque queuedLevels = new ConcurrentLinkedDeque<>(); ++ public final StampedLock lock = new StampedLock(); + } + + } diff --git a/patches/server/0071-Hearse-Optimized-some-locks-in-ChunkEntitySlices.patch b/patches/server/0071-Hearse-Optimized-some-locks-in-ChunkEntitySlices.patch new file mode 100644 index 00000000..f7942a71 --- /dev/null +++ b/patches/server/0071-Hearse-Optimized-some-locks-in-ChunkEntitySlices.patch @@ -0,0 +1,317 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Sat, 14 Jan 2023 10:14:54 +0800 +Subject: [PATCH] Hearse: Optimized some locks in ChunkEntitySlices + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +index 122699937606f5e00e356f5c1ea12db0563508a3..b12c02962e9dad92ae79d762887c65db10765488 100644 +--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java ++++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +@@ -66,50 +66,67 @@ public final class ChunkEntitySlices { + this.status = status; + } + ++ private org.bukkit.entity.Entity[] getChunkEntitiesUnsafe(){ ++ List ret = new java.util.ArrayList<>(); ++ final Entity[] entities = this.entities.getRawData(); ++ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { ++ final Entity entity = entities[i]; ++ if (entity == null) { ++ continue; ++ } ++ final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity(); ++ if (bukkit != null && bukkit.isValid()) { ++ ret.add(bukkit); ++ } ++ } ++ return ret.toArray(new org.bukkit.entity.Entity[0]); ++ } ++ + // Paper start - optimise CraftChunk#getEntities + public org.bukkit.entity.Entity[] getChunkEntities() { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)) { ++ return this.getChunkEntitiesUnsafe(); ++ } ++ ++ id = this.accessLock.readLock(); + try { +- List ret = new java.util.ArrayList<>(); +- final Entity[] entities = this.entities.getRawData(); +- for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { +- final Entity entity = entities[i]; +- if (entity == null) { +- continue; +- } +- final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity(); +- if (bukkit != null && bukkit.isValid()) { +- ret.add(bukkit); +- } +- } +- return ret.toArray(new org.bukkit.entity.Entity[0]); ++ return this.getChunkEntitiesUnsafe(); + } finally { + this.accessLock.unlockRead(id); + } + } + +- public CompoundTag save() { +- final long id = this.accessLock.readLock(); +- try { +- final int len = this.entities.size(); +- if (len == 0) { +- return null; +- } ++ private CompoundTag saveUnsafe(){ ++ final int len = this.entities.size(); ++ if (len == 0) { ++ return null; ++ } + +- final Entity[] rawData = this.entities.getRawData(); +- final List collectedEntities = new ArrayList<>(len); +- for (int i = 0; i < len; ++i) { +- final Entity entity = rawData[i]; +- if (entity.shouldBeSaved()) { +- collectedEntities.add(entity); +- } ++ final Entity[] rawData = this.entities.getRawData(); ++ final List collectedEntities = new ArrayList<>(len); ++ for (int i = 0; i < len; ++i) { ++ final Entity entity = rawData[i]; ++ if (entity.shouldBeSaved()) { ++ collectedEntities.add(entity); + } ++ } + +- if (collectedEntities.isEmpty()) { +- return null; +- } ++ if (collectedEntities.isEmpty()) { ++ return null; ++ } ++ ++ return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world); ++ } + +- return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world); ++ public CompoundTag save() { ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ return this.saveUnsafe(); ++ } ++ id = this.accessLock.readLock(); ++ try { ++ return this.saveUnsafe(); + } finally { + this.accessLock.unlockRead(id); + } +@@ -119,12 +136,18 @@ public final class ChunkEntitySlices { + public boolean unload() { + Entity[] collectedEntities; + int len; +- long id = this.accessLock.readLock(); +- try { ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ + len = this.entities.size(); + collectedEntities = Arrays.copyOf(this.entities.getRawData(), len); +- } finally { +- this.accessLock.unlockRead(id); ++ }else { ++ id = this.accessLock.readLock(); ++ try { ++ len = this.entities.size(); ++ collectedEntities = Arrays.copyOf(this.entities.getRawData(), len); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + for (int i = 0; i < len; ++i) { + final Entity entity = collectedEntities[i]; +@@ -160,7 +183,12 @@ public final class ChunkEntitySlices { + } + + public void callEntitiesLoadEvent() { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities()); ++ return; ++ } ++ id = this.accessLock.readLock(); + try { + CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities()); + } finally { +@@ -169,7 +197,12 @@ public final class ChunkEntitySlices { + } + + public void callEntitiesUnloadEvent() { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities()); ++ return; ++ } ++ id = this.accessLock.readLock(); + try { + CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities()); + } finally { +@@ -179,7 +212,11 @@ public final class ChunkEntitySlices { + // Paper end - optimise CraftChunk#getEntities + + public boolean isEmpty() { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ return this.entities.size() == 0; ++ } ++ id = this.accessLock.readLock(); + try { + return this.entities.size() == 0; + } finally { +@@ -188,16 +225,25 @@ public final class ChunkEntitySlices { + } + + public void mergeInto(final ChunkEntitySlices slices) { +- final long id = this.accessLock.readLock(); + final List cop = new ArrayList<>(); +- try { ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ + final Entity[] entities = this.entities.getRawData(); + for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { + final Entity entity = entities[i]; + cop.add(entity); + } +- } finally { +- this.accessLock.unlockRead(id); ++ }else { ++ id = this.accessLock.readLock(); ++ try { ++ final Entity[] entities = this.entities.getRawData(); ++ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) { ++ final Entity entity = entities[i]; ++ cop.add(entity); ++ } ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + for (Entity entity : cop){ + slices.addEntity(entity, entity.sectionY); +@@ -209,13 +255,19 @@ public final class ChunkEntitySlices { + + Entity[] entities; + +- final long id = this.accessLock.readLock(); +- try { ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ + entities = Arrays.copyOf(this.entities.getRawData(), this.entities.getRawData().length); +- } finally { +- this.accessLock.unlockRead(id); ++ }else { ++ id = this.accessLock.readLock(); ++ try { ++ entities = Arrays.copyOf(this.entities.getRawData(), this.entities.getRawData().length); ++ } finally { ++ this.accessLock.unlockRead(id); ++ } + } + ++ + for (final Entity entity : entities) { + final Visibility oldVisibility = EntityLookup.getEntityStatus(entity); + entity.chunkStatus = status; +@@ -278,7 +330,12 @@ public final class ChunkEntitySlices { + } + + public void getHardCollidingEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ this.hardCollidingEntities.getEntities(except, box, into, predicate); ++ return; ++ } ++ id = this.accessLock.readLock(); + try { + this.hardCollidingEntities.getEntities(except, box, into, predicate); + } finally { +@@ -287,34 +344,46 @@ public final class ChunkEntitySlices { + } + + public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate); ++ return; ++ } ++ id = this.accessLock.readLock(); + try { + this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate); + } finally { + this.accessLock.unlockRead(id); + } +- + } + + public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List into, final Predicate predicate) { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ this.allEntities.getEntities(except, box, into, predicate); ++ return; ++ } ++ id = this.accessLock.readLock(); + try { + this.allEntities.getEntities(except, box, into, predicate); + } finally { + this.accessLock.unlockRead(id); + } +- + } + + public void getEntities(final EntityType type, final AABB box, final List into, + final Predicate predicate) { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ this.allEntities.getEntities(type, box, (List) into, (Predicate) predicate); ++ return; ++ } ++ id = this.accessLock.readLock(); + try { + this.allEntities.getEntities(type, box, (List) into, (Predicate) predicate); + } finally { + this.accessLock.unlockRead(id); + } +- + } + + protected EntityCollectionBySection initClass(final Class clazz) { +@@ -342,7 +411,18 @@ public final class ChunkEntitySlices { + + public void getEntities(final Class clazz, final Entity except, final AABB box, final List into, + final Predicate predicate) { +- final long id = this.accessLock.readLock(); ++ long id = this.accessLock.tryOptimisticRead(); ++ if (this.accessLock.validate(id)){ ++ EntityCollectionBySection collection = this.entitiesByClass.get(clazz); ++ if (collection != null) { ++ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate); ++ } else { ++ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz)); ++ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List) into, (Predicate) predicate); ++ } ++ return; ++ } ++ id = this.accessLock.readLock(); + try { + EntityCollectionBySection collection = this.entitiesByClass.get(clazz); + if (collection != null) { diff --git a/patches/server/0072-Hearse-Remove-a-lock-in-ServerChunkCache.patch b/patches/server/0072-Hearse-Remove-a-lock-in-ServerChunkCache.patch new file mode 100644 index 00000000..d9475159 --- /dev/null +++ b/patches/server/0072-Hearse-Remove-a-lock-in-ServerChunkCache.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: wangxyper +Date: Sat, 14 Jan 2023 14:23:39 +0800 +Subject: [PATCH] Hearse: Remove a lock in ServerChunkCache + +Original license: MIT +Original project: https://github.com/NaturalCodeClub/HearseRewrite + +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 85c03dc7c1e714fab281374a177cd4c54e97d939..e311724d2e723115bc9549a61e6206a8aed835d8 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -4,6 +4,9 @@ import com.google.common.annotations.VisibleForTesting; + import com.google.common.collect.Lists; + import com.mojang.datafixers.DataFixer; + import com.mojang.datafixers.util.Either; ++import it.unimi.dsi.fastutil.longs.Long2ObjectMap; ++import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + import it.unimi.dsi.fastutil.objects.ObjectArraySet; + import it.unimi.dsi.fastutil.objects.ObjectSet; + import net.minecraft.Util; +@@ -57,8 +60,7 @@ public class ServerChunkCache extends ChunkSource { + @VisibleForDebug + private NaturalSpawner.SpawnState lastSpawnState; + // Paper start +- final com.destroystokyo.paper.util.concurrent.WeakSeqLock loadedChunkMapSeqLock = new com.destroystokyo.paper.util.concurrent.WeakSeqLock(); +- final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap loadedChunkMap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(8192, 0.5f); ++ private final Long2ObjectMap loadedChunkMap = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(8192, 0.5f)); + + private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4]; + +@@ -70,12 +72,7 @@ public class ServerChunkCache extends ChunkSource { + } + + public void addLoadedChunk(LevelChunk chunk) { +- this.loadedChunkMapSeqLock.acquireWrite(); +- try { +- this.loadedChunkMap.put(chunk.coordinateKey, chunk); +- } finally { +- this.loadedChunkMapSeqLock.releaseWrite(); +- } ++ this.loadedChunkMap.put(chunk.coordinateKey, chunk); + + // rewrite cache if we have to + // we do this since we also cache null chunks +@@ -85,13 +82,7 @@ public class ServerChunkCache extends ChunkSource { + } + + public void removeLoadedChunk(LevelChunk chunk) { +- this.loadedChunkMapSeqLock.acquireWrite(); +- try { +- this.loadedChunkMap.remove(chunk.coordinateKey); +- } finally { +- this.loadedChunkMapSeqLock.releaseWrite(); +- } +- ++ this.loadedChunkMap.remove(chunk.coordinateKey); + // rewrite cache if we have to + // we do this since we also cache null chunks + int cacheKey = getChunkCacheKey(chunk.locX, chunk.locZ); +@@ -360,22 +351,7 @@ public class ServerChunkCache extends ChunkSource { + return this.getChunkAtIfLoadedMainThread(x, z); + } + +- LevelChunk ret = null; +- long readlock; +- do { +- readlock = this.loadedChunkMapSeqLock.acquireRead(); +- try { +- ret = this.loadedChunkMap.get(k); +- } catch (Throwable thr) { +- if (thr instanceof ThreadDeath) { +- throw (ThreadDeath)thr; +- } +- // re-try, this means a CME occurred... +- continue; +- } +- } while (!this.loadedChunkMapSeqLock.tryReleaseRead(readlock)); +- +- return ret; ++ return this.loadedChunkMap.get(k); + } + // Paper end + // Paper start - async chunk io +@@ -436,7 +412,7 @@ public class ServerChunkCache extends ChunkSource { + // Paper end + com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info + //this.level.timings.syncChunkLoad.startTiming(); // Paper // Purpur +- chunkproviderserver_b.managedBlock(completablefuture::isDone); ++ chunkproviderserver_b.managedBlock(completablefuture::isDone); + io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system + //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur + } // Paper +@@ -495,6 +471,7 @@ public class ServerChunkCache extends ChunkSource { + // Paper start - add isUrgent - old sig left in place for dirty nms plugins + return getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create, false); + } ++ + private CompletableFuture> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create, boolean isUrgent) { + // Paper start - rewrite chunk system + io.papermc.paper.util.TickThread.ensureTickThread(this.level, chunkX, chunkZ, "Scheduling chunk load off-main");