From b1a98b6f8f1c205fa73ffcd5fd63f2b1be9e1681 Mon Sep 17 00:00:00 2001 From: Artem Ostrasev <76615486+NONPLAYT@users.noreply.github.com> Date: Sun, 17 Aug 2025 14:56:05 +0300 Subject: [PATCH] Moonrise and Level Ticking opts (#33) * moonrise and ticking opts * additional optimizations * cleanup * update entities patch --- divinemc-server/build.gradle.kts.patch | 15 +- ...ain.patch => 0017-Optimize-entities.patch} | 293 ++++++++-- ...-SparklyPaper-Parallel-world-ticking.patch | 6 +- .../0058-Raytrace-Entity-Tracker.patch | 4 +- .../0063-Cleanup-dead-code-from-Paper.patch | 6 +- ...gurable-player-spawn-tracking-range.patch} | 6 +- ....patch => 0069-Optimize-collections.patch} | 56 +- .../0070-Optimize-level-ticking.patch | 221 ++++++++ .../features/0071-Optimize-Moonrise.patch | 480 +++++++++++++++++ .../world/ticks/LevelTicks.java.patch | 10 - ....patch => 0018-Leaves-Protocol-Core.patch} | 0 .../features/0018-Rewrite-ReferenceList.patch | 167 ------ ...verListPingEvent-for-secondary-motd.patch} | 0 .../features/0020-Optimize-Moonrise.patch | 508 ++++++++++++++++++ 14 files changed, 1500 insertions(+), 272 deletions(-) rename divinemc-server/minecraft-patches/features/{0017-Optimize-entity-brain.patch => 0017-Optimize-entities.patch} (60%) rename divinemc-server/minecraft-patches/features/{0069-Configurable-player-spawn-tracking-range.patch => 0068-Configurable-player-spawn-tracking-range.patch} (92%) rename divinemc-server/minecraft-patches/features/{0068-Optimize-collections.patch => 0069-Optimize-collections.patch} (64%) create mode 100644 divinemc-server/minecraft-patches/features/0070-Optimize-level-ticking.patch create mode 100644 divinemc-server/minecraft-patches/features/0071-Optimize-Moonrise.patch delete mode 100644 divinemc-server/minecraft-patches/sources/net/minecraft/world/ticks/LevelTicks.java.patch rename divinemc-server/paper-patches/features/{0019-Leaves-Protocol-Core.patch => 0018-Leaves-Protocol-Core.patch} (100%) delete mode 100644 divinemc-server/paper-patches/features/0018-Rewrite-ReferenceList.patch rename divinemc-server/paper-patches/features/{0020-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch => 0019-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch} (100%) create mode 100644 divinemc-server/paper-patches/features/0020-Optimize-Moonrise.patch diff --git a/divinemc-server/build.gradle.kts.patch b/divinemc-server/build.gradle.kts.patch index d5623c9..3b26334 100644 --- a/divinemc-server/build.gradle.kts.patch +++ b/divinemc-server/build.gradle.kts.patch @@ -72,7 +72,7 @@ } } val log4jPlugins = sourceSets.create("log4jPlugins") { -@@ -156,10 +_,21 @@ +@@ -156,10 +_,22 @@ } dependencies { @@ -87,6 +87,7 @@ + implementation("com.github.luben:zstd-jni:1.5.7-3") + implementation("org.lz4:lz4-java:1.8.0") + implementation("net.openhft:zero-allocation-hashing:0.16") ++ implementation("org.agrona:agrona:2.2.4") + // DivineMC end - Dependencies + implementation("ca.spottedleaf:concurrentutil:0.0.3") @@ -175,9 +176,8 @@ val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() + val experimental = rootProject.providers.gradleProperty("experimental").get() // DivineMC - Experimental flag attributes( -- "Main-Class" to "org.bukkit.craftbukkit.Main", + "Main-Class" to "org.bukkit.craftbukkit.Main", - "Implementation-Title" to "Purpur", // Purpur -+ "Main-Class" to "net.minecraft.server.Main", // DivineMC - Rebrand + "Implementation-Title" to "DivineMC", // DivineMC - Rebrand "Implementation-Version" to implementationVersion, "Implementation-Vendor" to date, @@ -198,12 +198,3 @@ ) for (tld in setOf("net", "com", "org")) { attributes("$tld/bukkit", "Sealed" to true) -@@ -328,7 +_,7 @@ - block: JavaExec.() -> Unit - ): TaskProvider = register(name) { - group = "runs" -- mainClass.set("org.bukkit.craftbukkit.Main") -+ mainClass.set("net.minecraft.server.Main") // DivineMC - Rebrand - standardInput = System.`in` - workingDir = rootProject.layout.projectDirectory - .dir(providers.gradleProperty("paper.runWorkDir").getOrElse("run")) diff --git a/divinemc-server/minecraft-patches/features/0017-Optimize-entity-brain.patch b/divinemc-server/minecraft-patches/features/0017-Optimize-entities.patch similarity index 60% rename from divinemc-server/minecraft-patches/features/0017-Optimize-entity-brain.patch rename to divinemc-server/minecraft-patches/features/0017-Optimize-entities.patch index a46a021..f97ccf6 100644 --- a/divinemc-server/minecraft-patches/features/0017-Optimize-entity-brain.patch +++ b/divinemc-server/minecraft-patches/features/0017-Optimize-entities.patch @@ -1,18 +1,18 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Sat, 1 Feb 2025 16:05:57 +0300 -Subject: [PATCH] Optimize entity brain +Subject: [PATCH] Optimize entities diff --git a/net/minecraft/world/entity/AgeableMob.java b/net/minecraft/world/entity/AgeableMob.java -index 04875840085541ebfc7014868beec49bb7ab9976..bbfb1de1a03c4208406feb803a2f378db9910556 100644 +index 04875840085541ebfc7014868beec49bb7ab9976..caea723a4614d3c406dbce71a392716b7442987c 100644 --- a/net/minecraft/world/entity/AgeableMob.java +++ b/net/minecraft/world/entity/AgeableMob.java @@ -125,6 +125,16 @@ public abstract class AgeableMob extends PathfinderMob { public void onSyncedDataUpdated(EntityDataAccessor key) { if (DATA_BABY_ID.equals(key)) { this.refreshDimensions(); -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + if (isBaby()) { + org.bxteam.divinemc.util.entity.SensorHelper.enableSensor(this, net.minecraft.world.entity.ai.sensing.SensorType.NEAREST_ADULT, true); + } else { @@ -21,12 +21,204 @@ index 04875840085541ebfc7014868beec49bb7ab9976..bbfb1de1a03c4208406feb803a2f378d + this.getBrain().setMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.NEAREST_VISIBLE_ADULT, java.util.Optional.empty()); + } + } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities } super.onSyncedDataUpdated(key); +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 989043d73af2c719e6c36ac5d4c35e7d31d3410c..6c7f0c04e94eb3fa2f22d2637a1c2abceb1563f4 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -1687,6 +1687,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + + private void checkInsideBlocks(Vec3 vec3, Vec3 vec31, InsideBlockEffectApplier.StepBasedCollector stepBasedCollector, LongSet set) { + AABB aabb = this.makeBoundingBox(vec31).deflate(1.0E-5F); ++ // DivineMC start - Optimize entities ++ final net.minecraft.world.level.chunk.ChunkAccess[] cachedChunk = new net.minecraft.world.level.chunk.ChunkAccess[] { null }; ++ final long[] lastChunkPos = { Long.MIN_VALUE }; ++ // DivineMC end - Optimize entities + BlockGetter.forEachBlockIntersectedBetween( + vec3, + vec31, +@@ -1695,7 +1699,21 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + if (!this.isAlive()) { + return false; + } else { +- BlockState blockState = this.level().getBlockState(pos); ++ // DivineMC start - Optimize entities ++ final int chunkX = pos.getX() >> 4; ++ final int chunkZ = pos.getZ() >> 4; ++ final long chunkLongPos = ((long) chunkZ << 32) | (chunkX & 0xFFFFFFFFL); ++ if (lastChunkPos[0] != chunkLongPos) { ++ // update cache, this is a different chunk than previous ++ lastChunkPos[0] = chunkLongPos; ++ cachedChunk[0] = this.level.getChunkIfLoaded(chunkX, chunkZ); ++ } ++ net.minecraft.world.level.chunk.ChunkAccess chunk = cachedChunk[0]; ++ if (chunk == null) { ++ return true; ++ } ++ BlockState blockState = chunk.getBlockState(pos); ++ // DivineMC end - Optimize entities + if (blockState.isAir()) { + this.debugBlockIntersection(pos, false, false); + return true; +@@ -1738,6 +1756,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public boolean collidedWithFluid(FluidState fluid, BlockPos pos, Vec3 from, Vec3 to) { ++ if (fluid.isEmpty()) return false; // DivineMC - Optimize entities + AABB aabb = fluid.getAABB(this.level(), pos); + return aabb != null && this.collidedWithShapeMovingFrom(from, to, List.of(aabb)); + } +diff --git a/net/minecraft/world/entity/InsideBlockEffectApplier.java b/net/minecraft/world/entity/InsideBlockEffectApplier.java +index a7bc5ead2062504ceac95f603bc1ca8d4290bbfd..533c790ce305043f53c76f03a12676c567118a44 100644 +--- a/net/minecraft/world/entity/InsideBlockEffectApplier.java ++++ b/net/minecraft/world/entity/InsideBlockEffectApplier.java +@@ -31,68 +31,115 @@ public interface InsideBlockEffectApplier { + + public static class StepBasedCollector implements InsideBlockEffectApplier { + private static final InsideBlockEffectType[] APPLY_ORDER = InsideBlockEffectType.values(); +- private static final int NO_STEP = -1; +- private final Map> effectsInStep = new java.util.EnumMap<>(InsideBlockEffectType.class); // Paper - track position inside effect was triggered on +- private final Map>> beforeEffectsInStep = Util.makeEnumMap( +- InsideBlockEffectType.class, insideBlockEffectType -> new ArrayList<>() +- ); +- private final Map>> afterEffectsInStep = Util.makeEnumMap( +- InsideBlockEffectType.class, insideBlockEffectType -> new ArrayList<>() +- ); +- private final List> finalEffects = new ArrayList<>(); ++ // DivineMC start - Optimize entities ++ private final Consumer[] effectsInStep = new Consumer[APPLY_ORDER.length]; ++ private final it.unimi.dsi.fastutil.objects.ObjectArrayList>[] beforeEffectsInStep = new it.unimi.dsi.fastutil.objects.ObjectArrayList[APPLY_ORDER.length]; ++ private final it.unimi.dsi.fastutil.objects.ObjectArrayList>[] afterEffectsInStep = new it.unimi.dsi.fastutil.objects.ObjectArrayList[APPLY_ORDER.length]; ++ private final it.unimi.dsi.fastutil.objects.ObjectArrayList> finalEffects = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); ++ // DivineMC end - Optimize entities + private int lastStep = -1; + +- public void advanceStep(int step, net.minecraft.core.BlockPos pos) { // Paper - track position inside effect was triggered on +- this.currentBlockPos = pos; // Paper - track position inside effect was triggered on ++ // DivineMC start - Optimize entities ++ public StepBasedCollector() { ++ for (int i = 0; i < APPLY_ORDER.length; i++) { ++ beforeEffectsInStep[i] = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(2); ++ afterEffectsInStep[i] = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(2); ++ } ++ } ++ ++ public void advanceStep(int step, net.minecraft.core.BlockPos pos) { ++ this.currentBlockPos = pos; + if (this.lastStep != step) { + this.lastStep = step; + this.flushStep(); + } + } ++ // DivineMC end - Optimize entities + + public void applyAndClear(Entity entity) { + this.flushStep(); + +- for (Consumer consumer : this.finalEffects) { ++ // DivineMC start - Optimize entities ++ List> effects = this.finalEffects; ++ int size = effects.size(); ++ ++ if (size == 0) { ++ this.lastStep = -1; ++ return; ++ } ++ ++ if (!entity.isAlive()) { ++ effects.clear(); ++ this.lastStep = -1; ++ return; ++ } ++ ++ int i = 0; ++ while (i < size - 3) { ++ effects.get(i++).accept(entity); ++ effects.get(i++).accept(entity); ++ effects.get(i++).accept(entity); ++ effects.get(i++).accept(entity); + if (!entity.isAlive()) { + break; + } ++ } + +- consumer.accept(entity); ++ if (entity.isAlive()) { ++ for (; i < size; i++) { ++ effects.get(i).accept(entity); ++ if (!entity.isAlive()) { ++ break; ++ } ++ } + } + +- this.finalEffects.clear(); ++ effects.clear(); ++ // DivineMC end - Optimize entities + this.lastStep = -1; + } + + private void flushStep() { +- for (InsideBlockEffectType insideBlockEffectType : APPLY_ORDER) { +- List> list = this.beforeEffectsInStep.get(insideBlockEffectType); +- this.finalEffects.addAll(list); +- list.clear(); +- if (this.effectsInStep.remove(insideBlockEffectType) instanceof final Consumer recordedEffect) { // Paper - track position inside effect was triggered on - better than null check to avoid diff. +- this.finalEffects.add(recordedEffect); // Paper - track position inside effect was triggered on ++ // DivineMC start - Optimize entities ++ final int len = APPLY_ORDER.length; ++ final Consumer[] effectArr = this.effectsInStep; ++ final List> finalList = this.finalEffects; ++ ++ for (int i = 0; i < len; i++) { ++ List> beforeList = this.beforeEffectsInStep[i]; ++ if (!beforeList.isEmpty()) { ++ finalList.addAll(beforeList); ++ beforeList.clear(); + } + +- List> list1 = this.afterEffectsInStep.get(insideBlockEffectType); +- this.finalEffects.addAll(list1); +- list1.clear(); ++ Consumer effect = effectArr[i]; ++ if (effect != null) { ++ finalList.add(effect); ++ effectArr[i] = null; ++ } ++ ++ List> afterList = this.afterEffectsInStep[i]; ++ if (!afterList.isEmpty()) { ++ finalList.addAll(afterList); ++ afterList.clear(); ++ } + } ++ // DivineMC end - Optimize entities + } + + @Override + public void apply(InsideBlockEffectType type) { +- this.effectsInStep.put(type, recorded(type)); // Paper - track position inside effect was triggered on ++ effectsInStep[type.ordinal()] = recorded(type); // DivineMC - Optimize entities + } + + @Override + public void runBefore(InsideBlockEffectType type, Consumer effect) { +- this.beforeEffectsInStep.get(type).add(effect); ++ beforeEffectsInStep[type.ordinal()].add(effect); // DivineMC - Optimize entities + } + + @Override + public void runAfter(InsideBlockEffectType type, Consumer effect) { +- this.afterEffectsInStep.get(type).add(effect); ++ afterEffectsInStep[type.ordinal()].add(effect); // DivineMC - Optimize entities + } + + // Paper start - track position inside effect was triggered on diff --git a/net/minecraft/world/entity/ai/Brain.java b/net/minecraft/world/entity/ai/Brain.java -index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da25b9c9327 100644 +index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..f839b1429c8ef2a630ae3d6e6a02678161aa5858 100644 --- a/net/minecraft/world/entity/ai/Brain.java +++ b/net/minecraft/world/entity/ai/Brain.java @@ -45,16 +45,75 @@ public class Brain { @@ -35,20 +227,20 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 private static final int SCHEDULE_UPDATE_DELAY = 20; - private final Map, Optional>> memories = Maps.newHashMap(); - public final Map>, Sensor> sensors = Maps.newLinkedHashMap(); -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + private Map, Optional>> memories = Maps.newConcurrentMap(); + public Map>, Sensor> sensors = Maps.newLinkedHashMap(); private final Map>>> availableBehaviorsByPriority = Maps.newTreeMap(); private Schedule schedule = Schedule.EMPTY; - private final Map, MemoryStatus>>> activityRequirements = Maps.newHashMap(); + private Map, MemoryStatus>>> activityRequirements = Maps.newHashMap(); -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities private final Map>> activityMemoriesToEraseWhenStopped = Maps.newHashMap(); private Set coreActivities = Sets.newHashSet(); private final Set activeActivities = Sets.newHashSet(); private Activity defaultActivity = Activity.IDLE; private long lastScheduleUpdate = -9999L; -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + private java.util.ArrayList> possibleTasks; + private org.bxteam.divinemc.util.collections.MaskedList> runningTasks; + @@ -104,7 +296,7 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 + } + this.runningTasks = list; + } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities public static Brain.Provider provider( Collection> memoryTypes, Collection>> sensorTypes @@ -112,12 +304,12 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 for (Brain.MemoryValue memoryValue : memoryValues) { memoryValue.setMemoryInternal(this); } -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + this.onTasksChanged(); + this.memories = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(this.memories); + this.sensors = new it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap<>(this.sensors); + this.activityRequirements = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(this.activityRequirements); -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities } public DataResult serializeStart(DynamicOps ops) { @@ -125,7 +317,7 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 } public void eraseMemory(MemoryModuleType type) { -+ if (!this.memories.containsKey(type)) return; // DivineMC - skip if memory does not contain key ++ if (!this.memories.containsKey(type)) return; // DivineMC - Optimize entities this.setMemory(type, Optional.empty()); } @@ -133,7 +325,7 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 this.setMemoryInternal(memoryType, memory.map(ExpirableValue::of)); } -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities void setMemoryInternal(MemoryModuleType memoryType, Optional> memory) { + if (memory.isPresent() && this.isEmptyCollection(memory.get().getValue())) { + this.eraseMemory(memoryType); @@ -163,7 +355,7 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 + } + return oldValue; + } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities + public Optional getMemory(MemoryModuleType type) { Optional> optional = this.memories.get(type); @@ -185,7 +377,7 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 - } - - return list; -+ return this.getCurrentlyRunningTasks(); // DivineMC - Optimize entity brain ++ return this.getCurrentlyRunningTasks(); // DivineMC - Optimize entities } public void useDefaultActivity() { @@ -193,7 +385,7 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 this.activeActivities.clear(); this.activeActivities.addAll(this.coreActivities); this.activeActivities.add(activity); -+ this.onPossibleActivitiesChanged(); // DivineMC - Optimize entity brain ++ this.onPossibleActivitiesChanged(); // DivineMC - Optimize entities } } @@ -201,13 +393,13 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 .computeIfAbsent(activity, activity1 -> Sets.newLinkedHashSet()) .add((BehaviorControl)pair.getSecond()); } -+ this.onTasksChanged(); // DivineMC - Optimize entity brain ++ this.onTasksChanged(); // DivineMC - Optimize entities } @VisibleForTesting public void removeAllBehaviors() { this.availableBehaviorsByPriority.clear(); -+ this.onTasksChanged(); // DivineMC - Optimize entity brain ++ this.onTasksChanged(); // DivineMC - Optimize entities } public boolean isActive(Activity activity) { @@ -215,7 +407,7 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 } } -+ brain.memoryModCount = this.memoryModCount + 1; // DivineMC - Optimize entity brain ++ brain.memoryModCount = this.memoryModCount + 1; // DivineMC - Optimize entities return brain; } @@ -223,15 +415,15 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 for (BehaviorControl behaviorControl : this.getRunningBehaviors()) { behaviorControl.doStop(level, owner, gameTime); -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + if (this.runningTasks != null) { + this.runningTasks.setVisible(behaviorControl, false); + } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities } } -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities private void startEachNonRunningBehavior(ServerLevel level, E entity) { - long gameTime = level.getGameTime(); - @@ -254,33 +446,33 @@ index 99ced3cb7d3d4c7a80d890689d7585fa9acd17f6..9277b5da6065a5500e40727815083da2 } } } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities private void tickEachRunningBehavior(ServerLevel level, E entity) { long gameTime = level.getGameTime(); for (BehaviorControl behaviorControl : this.getRunningBehaviors()) { behaviorControl.tickOrStop(level, entity, gameTime); -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + if (this.runningTasks != null && behaviorControl.getStatus() != Behavior.Status.RUNNING) { + this.runningTasks.setVisible(behaviorControl, false); + } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities } } diff --git a/net/minecraft/world/entity/ai/behavior/Behavior.java b/net/minecraft/world/entity/ai/behavior/Behavior.java -index 5b0cadd2544fb2a627822e645ff32fec2e9cfda9..f367012ebcc4d06ac868bbc1733bee39ad2d67b1 100644 +index 5b0cadd2544fb2a627822e645ff32fec2e9cfda9..02508662c722a515cfd78f872c8ba8bbffd8b6fb 100644 --- a/net/minecraft/world/entity/ai/behavior/Behavior.java +++ b/net/minecraft/world/entity/ai/behavior/Behavior.java @@ -14,6 +14,10 @@ public abstract class Behavior implements BehaviorContro private long endTimestamp; private final int minDuration; private final int maxDuration; -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + private long cachedMemoryModCount = -1; + private boolean cachedHasRequiredMemoryState; -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities private final String configKey; // Paper - configurable behavior tick rate and timings public Behavior(Map, MemoryStatus> entryCondition) { @@ -289,7 +481,7 @@ index 5b0cadd2544fb2a627822e645ff32fec2e9cfda9..f367012ebcc4d06ac868bbc1733bee39 this.minDuration = minDuration; this.maxDuration = maxDuration; - this.entryCondition = entryCondition; -+ this.entryCondition = new it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap<>(entryCondition); // DivineMC - Optimize entity brain - Use fastutil ++ this.entryCondition = new it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap<>(entryCondition); // DivineMC - Optimize entities - Use fastutil // Paper start - configurable behavior tick rate and timings String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName(); int lastSeparator = key.lastIndexOf('.'); @@ -303,7 +495,7 @@ index 5b0cadd2544fb2a627822e645ff32fec2e9cfda9..f367012ebcc4d06ac868bbc1733bee39 - MemoryStatus memoryStatus = entry.getValue(); - if (!owner.getBrain().checkMemory(memoryModuleType, memoryStatus)) { - return false; -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + public boolean hasRequiredMemories(E entity) { + net.minecraft.world.entity.ai.Brain brain = entity.getBrain(); + long modCount = brain.getMemoryModCount(); @@ -323,24 +515,24 @@ index 5b0cadd2544fb2a627822e645ff32fec2e9cfda9..f367012ebcc4d06ac868bbc1733bee39 - return true; + return this.cachedHasRequiredMemoryState = true; } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities public static enum Status { STOPPED, diff --git a/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java b/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java -index 977afa268838304abdb34be253ca36ac1c22e99f..ff04ea295b70583137deb8babe5e75ebc755f697 100644 +index 977afa268838304abdb34be253ca36ac1c22e99f..9fdb2ce7b72d0a10c2148027990a0048ca93f976 100644 --- a/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java +++ b/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java @@ -119,6 +119,12 @@ public class LongJumpToRandomPos extends Behavior { int x = blockPos.getX(); int y = blockPos.getY(); int z = blockPos.getZ(); -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + if (this.maxLongJumpWidth < 128 && this.maxLongJumpHeight < 128) { + this.jumpCandidates = org.bxteam.divinemc.util.collections.LongJumpChoiceList.forCenter(blockPos, (byte) this.maxLongJumpWidth, (byte) this.maxLongJumpHeight); + return; + } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities this.jumpCandidates = BlockPos.betweenClosedStream( x - this.maxLongJumpWidth, y - this.maxLongJumpHeight, @@ -348,7 +540,7 @@ index 977afa268838304abdb34be253ca36ac1c22e99f..ff04ea295b70583137deb8babe5e75eb } } -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities protected Optional getJumpCandidate(ServerLevel level) { - Optional randomItem = WeightedRandom.getRandomItem( - level.random, this.jumpCandidates, LongJumpToRandomPos.PossibleJump::weight @@ -375,6 +567,23 @@ index 977afa268838304abdb34be253ca36ac1c22e99f..ff04ea295b70583137deb8babe5e75eb } private boolean isAcceptableLandingPosition(ServerLevel level, E entity, BlockPos pos) { +diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java +index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..dfc62772d5617f0dce72b45a1bebf1b2f051efd5 100644 +--- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java ++++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java +@@ -17,10 +17,10 @@ public class NearestLivingEntitySensor extends Sensor + protected void doTick(ServerLevel level, T entity) { + double attributeValue = entity.getAttributeValue(Attributes.FOLLOW_RANGE); + AABB aabb = entity.getBoundingBox().inflate(attributeValue, attributeValue, attributeValue); +- List entitiesOfClass = level.getEntitiesOfClass( ++ it.unimi.dsi.fastutil.objects.ObjectArrayList entitiesOfClass = (it.unimi.dsi.fastutil.objects.ObjectArrayList) level.getEntitiesOfClass( // DivineMC - Optimize collections + LivingEntity.class, aabb, matchableEntity -> matchableEntity != entity && matchableEntity.isAlive() + ); +- entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); ++ entitiesOfClass.unstableSort(Comparator.comparingDouble(entity::distanceToSqr)); // DivineMC - Optimize collections + Brain brain = entity.getBrain(); + brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, entitiesOfClass); + brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(level, entity, entitiesOfClass)); diff --git a/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java b/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java index 24d1928445b5571e040a2b12d5c82e77a880d9bd..dac0a23aebf2dea1972c07d5c82079da7c9837ac 100644 --- a/net/minecraft/world/entity/ai/sensing/VillagerBabiesSensor.java @@ -404,38 +613,38 @@ index 24d1928445b5571e040a2b12d5c82e77a880d9bd..dac0a23aebf2dea1972c07d5c82079da private boolean isVillagerBaby(LivingEntity livingEntity) { return livingEntity.getType() == EntityType.VILLAGER && livingEntity.isBaby(); diff --git a/net/minecraft/world/entity/animal/goat/Goat.java b/net/minecraft/world/entity/animal/goat/Goat.java -index 70b32e0d06f9b8b7999df5fdfd773c09394e23fb..997cff138d5c99b1be9224cb3c96bdfeeb79915a 100644 +index 70b32e0d06f9b8b7999df5fdfd773c09394e23fb..188c233721d7a98c8a437dd5bd155b1f3de9134e 100644 --- a/net/minecraft/world/entity/animal/goat/Goat.java +++ b/net/minecraft/world/entity/animal/goat/Goat.java @@ -100,6 +100,13 @@ public class Goat extends Animal { this.getNavigation().setCanFloat(true); this.setPathfindingMalus(PathType.POWDER_SNOW, -1.0F); this.setPathfindingMalus(PathType.DANGER_POWDER_SNOW, -1.0F); -+ // DivineMC start - Optimize entity brain ++ // DivineMC start - Optimize entities + if (!this.getBrain().hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM)) { + org.bxteam.divinemc.util.entity.SensorHelper.disableSensor(this, SensorType.NEAREST_ITEMS); + } else if (net.minecraft.SharedConstants.IS_RUNNING_IN_IDE) { + throw new IllegalStateException("Goat Entity has a nearest visible wanted item memory module! This patch(Optimize-Brain, Goat.java changes) should probably be removed permanently!"); + } -+ // DivineMC end - Optimize entity brain ++ // DivineMC end - Optimize entities } public ItemStack createHorn() { diff --git a/net/minecraft/world/entity/schedule/Activity.java b/net/minecraft/world/entity/schedule/Activity.java -index 5a143bb6fabba3dc4e2272afb0be636d5722ea22..f17cc8135121f1a97f13bf83a0205e7fcb08b431 100644 +index 5a143bb6fabba3dc4e2272afb0be636d5722ea22..133a51ed45500aba7b0bc4a7acb19731a524d8c1 100644 --- a/net/minecraft/world/entity/schedule/Activity.java +++ b/net/minecraft/world/entity/schedule/Activity.java @@ -32,10 +32,12 @@ public class Activity { public static final Activity DIG = register("dig"); private final String name; private final int hashCode; -+ public final int id; // DivineMC - Cache registry ID ++ public final int id; // DivineMC - Optimize entities - cache registry ID - private Activity(String name) { + private Activity(String name, int id) { this.name = name; this.hashCode = name.hashCode(); -+ this.id = id; // DivineMC - Cache registry ID ++ this.id = id; // DivineMC - Optimize entities - cache registry ID } public String getName() { @@ -444,7 +653,7 @@ index 5a143bb6fabba3dc4e2272afb0be636d5722ea22..f17cc8135121f1a97f13bf83a0205e7f private static Activity register(String key) { - return Registry.register(BuiltInRegistries.ACTIVITY, key, new Activity(key)); -+ return Registry.register(BuiltInRegistries.ACTIVITY, key, new Activity(key, BuiltInRegistries.ACTIVITY.size())); // DivineMC - Cache registry ID ++ return Registry.register(BuiltInRegistries.ACTIVITY, key, new Activity(key, BuiltInRegistries.ACTIVITY.size())); // DivineMC - Optimize entities - cache registry ID } @Override diff --git a/divinemc-server/minecraft-patches/features/0050-SparklyPaper-Parallel-world-ticking.patch b/divinemc-server/minecraft-patches/features/0050-SparklyPaper-Parallel-world-ticking.patch index 809173f..fc1d903 100644 --- a/divinemc-server/minecraft-patches/features/0050-SparklyPaper-Parallel-world-ticking.patch +++ b/divinemc-server/minecraft-patches/features/0050-SparklyPaper-Parallel-world-ticking.patch @@ -640,10 +640,10 @@ index f9e7532f86122a379692561a639a209a126e8bba..839f6b7696ef85314da185bedba7cfc5 if (isLocatorBarEnabledFor(player)) { if (!connection.isBroken()) { diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 461abfac4749ca06b613cfee9e1ad9e6961299df..137b786bf15bbbe52f22bd9e25c1ef3c0d04e642 100644 +index 6963be899ac218bd091e73b72c9ccef82e33de36..85da5aec677d84aea005449d97c1ee3ca616e2e4 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -3461,14 +3461,34 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -3480,14 +3480,34 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.portalProcess != null) { if (this.portalProcess.processPortalTeleportation(serverLevel, this, this.canUsePortal(false))) { this.setPortalCooldown(); @@ -684,7 +684,7 @@ index 461abfac4749ca06b613cfee9e1ad9e6961299df..137b786bf15bbbe52f22bd9e25c1ef3c } else if (this.portalProcess.hasExpired()) { this.portalProcess = null; } -@@ -4045,6 +4065,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4064,6 +4084,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } private Entity teleportCrossDimension(ServerLevel oldLevel, ServerLevel newLevel, TeleportTransition teleportTransition) { diff --git a/divinemc-server/minecraft-patches/features/0058-Raytrace-Entity-Tracker.patch b/divinemc-server/minecraft-patches/features/0058-Raytrace-Entity-Tracker.patch index ad5d962..26ff8de 100644 --- a/divinemc-server/minecraft-patches/features/0058-Raytrace-Entity-Tracker.patch +++ b/divinemc-server/minecraft-patches/features/0058-Raytrace-Entity-Tracker.patch @@ -23,7 +23,7 @@ index 7ca147cf9da67c399806056e5092841f7ca32321..a6bf257ca93e4b3819b65b4ef4ba71d9 double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1); if (rangeY != -1) { diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 0915a08bc8d2dd38ade01e31c34abd69e1abd365..b720230c4bef699fa1b60d949c47aa4c193a24db 100644 +index 85da5aec677d84aea005449d97c1ee3ca616e2e4..4c9e0eaedad3f4a372b48580cbe9b03fe2f8c037 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -145,7 +145,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter; @@ -35,7 +35,7 @@ index 0915a08bc8d2dd38ade01e31c34abd69e1abd365..b720230c4bef699fa1b60d949c47aa4c public static javax.script.ScriptEngine scriptEngine = new javax.script.ScriptEngineManager().getEngineByName("rhino"); // Purpur - Configurable entity base attributes // CraftBukkit start private static final int CURRENT_LEVEL = 2; -@@ -5476,4 +5476,47 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -5495,4 +5495,47 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return false; } // Purpur end - Ridables diff --git a/divinemc-server/minecraft-patches/features/0063-Cleanup-dead-code-from-Paper.patch b/divinemc-server/minecraft-patches/features/0063-Cleanup-dead-code-from-Paper.patch index 9e0f207..0662018 100644 --- a/divinemc-server/minecraft-patches/features/0063-Cleanup-dead-code-from-Paper.patch +++ b/divinemc-server/minecraft-patches/features/0063-Cleanup-dead-code-from-Paper.patch @@ -105,7 +105,7 @@ index 0ad18866c323308ad9b87322932e03a283f740b1..386fdc23b35675a7db66d16bf2a8a6dd private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2 diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index b720230c4bef699fa1b60d949c47aa4c193a24db..c671370a0addb31bc8ca7d7fa642d3b53b5c00ee 100644 +index 4c9e0eaedad3f4a372b48580cbe9b03fe2f8c037..d947bff42cbf7aaac0b7f62bc78c239a5f6af381 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -1111,29 +1111,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -152,7 +152,7 @@ index b720230c4bef699fa1b60d949c47aa4c193a24db..c671370a0addb31bc8ca7d7fa642d3b5 } private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) { -@@ -4950,9 +4924,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4969,9 +4943,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void setDeltaMovement(Vec3 deltaMovement) { @@ -162,7 +162,7 @@ index b720230c4bef699fa1b60d949c47aa4c193a24db..c671370a0addb31bc8ca7d7fa642d3b5 } public void addDeltaMovement(Vec3 addend) { -@@ -5050,9 +5022,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -5069,9 +5041,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } // Paper end - Block invalid positions and bounding box if (this.position.x != x || this.position.y != y || this.position.z != z) { diff --git a/divinemc-server/minecraft-patches/features/0069-Configurable-player-spawn-tracking-range.patch b/divinemc-server/minecraft-patches/features/0068-Configurable-player-spawn-tracking-range.patch similarity index 92% rename from divinemc-server/minecraft-patches/features/0069-Configurable-player-spawn-tracking-range.patch rename to divinemc-server/minecraft-patches/features/0068-Configurable-player-spawn-tracking-range.patch index 078d21b..bf8e91f 100644 --- a/divinemc-server/minecraft-patches/features/0069-Configurable-player-spawn-tracking-range.patch +++ b/divinemc-server/minecraft-patches/features/0068-Configurable-player-spawn-tracking-range.patch @@ -18,10 +18,10 @@ index 6d1fe8028739145b11fce98ad62b2f8044299548..9f086ded18d1fc8850877c6be113d880 // note: vanilla does not subtract 0.5 but the result is (luckily!) the same public static final int NARROW_SPAWN_TRACK_RANGE = (int)Math.floor(((double)PLAYER_SPAWN_TRACK_RANGE / Math.sqrt(2.0)) - 0.5); diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 038d75aa39a158b65887c81f2687cb3c8140a6e2..3ca2f56cb31d41e88eb2a3e59c3749fbdef7c85e 100644 +index a6bf257ca93e4b3819b65b4ef4ba71d9e2b40933..de7800b46f7e8c68f24de8476032f2179edc4797 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java -@@ -824,10 +824,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -822,10 +822,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider final ServerPlayer[] raw = players.getRawDataUnchecked(); final int len = players.size(); @@ -35,7 +35,7 @@ index 038d75aa39a158b65887c81f2687cb3c8140a6e2..3ca2f56cb31d41e88eb2a3e59c3749fb if (ret == null) { ret = new ArrayList<>(len - i); ret.add(player); -@@ -1222,6 +1222,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider +@@ -1220,6 +1220,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider for (int i = 0; i < playersLength; ++i) { // DivineMC - Multithreaded tracker final ServerPlayer player = playersRaw[i]; diff --git a/divinemc-server/minecraft-patches/features/0068-Optimize-collections.patch b/divinemc-server/minecraft-patches/features/0069-Optimize-collections.patch similarity index 64% rename from divinemc-server/minecraft-patches/features/0068-Optimize-collections.patch rename to divinemc-server/minecraft-patches/features/0069-Optimize-collections.patch index ee67048..ccaae73 100644 --- a/divinemc-server/minecraft-patches/features/0068-Optimize-collections.patch +++ b/divinemc-server/minecraft-patches/features/0069-Optimize-collections.patch @@ -37,7 +37,7 @@ index 7e31c5c8659d24948fd45a2d6ee7bdeca6027d27..95221434fd8eef388f0308b72af3f934 protected NonNullList(List list, @Nullable E defaultValue) { diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index a6bf257ca93e4b3819b65b4ef4ba71d9e2b40933..038d75aa39a158b65887c81f2687cb3c8140a6e2 100644 +index de7800b46f7e8c68f24de8476032f2179edc4797..3ca2f56cb31d41e88eb2a3e59c3749fbdef7c85e 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -132,8 +132,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -53,23 +53,19 @@ index a6bf257ca93e4b3819b65b4ef4ba71d9e2b40933..038d75aa39a158b65887c81f2687cb3c // Paper - rewrite chunk system public int serverViewDistance; public final WorldGenContext worldGenContext; // Paper - public -diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..dfc62772d5617f0dce72b45a1bebf1b2f051efd5 100644 ---- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -+++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -@@ -17,10 +17,10 @@ public class NearestLivingEntitySensor extends Sensor - protected void doTick(ServerLevel level, T entity) { - double attributeValue = entity.getAttributeValue(Attributes.FOLLOW_RANGE); - AABB aabb = entity.getBoundingBox().inflate(attributeValue, attributeValue, attributeValue); -- List entitiesOfClass = level.getEntitiesOfClass( -+ it.unimi.dsi.fastutil.objects.ObjectArrayList entitiesOfClass = (it.unimi.dsi.fastutil.objects.ObjectArrayList) level.getEntitiesOfClass( // DivineMC - Optimize collections - LivingEntity.class, aabb, matchableEntity -> matchableEntity != entity && matchableEntity.isAlive() - ); -- entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); -+ entitiesOfClass.unstableSort(Comparator.comparingDouble(entity::distanceToSqr)); // DivineMC - Optimize collections - Brain brain = entity.getBrain(); - brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, entitiesOfClass); - brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(level, entity, entitiesOfClass)); +diff --git a/net/minecraft/stats/StatType.java b/net/minecraft/stats/StatType.java +index e42b894f0bdecd9417c3d53bfc172cd0726340bc..0865554f79536dc324766346223413c670348600 100644 +--- a/net/minecraft/stats/StatType.java ++++ b/net/minecraft/stats/StatType.java +@@ -11,7 +11,7 @@ import net.minecraft.network.codec.StreamCodec; + + public class StatType implements Iterable> { + private final Registry registry; +- private final Map> map = new IdentityHashMap<>(); ++ private final Map> map = new it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap<>(); // DivineMC - Optimize collections + private final Component displayName; + private final StreamCodec> streamCodec; + diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java index 66ba223dacefb3531c46b144c4499b2b2285eafe..0b7f9af0c4e43115878769043ebd06a09ccdf059 100644 --- a/net/minecraft/world/level/Level.java @@ -92,16 +88,16 @@ index 66ba223dacefb3531c46b144c4499b2b2285eafe..0b7f9af0c4e43115878769043ebd06a0 ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate); -diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 24f13e2b0b694ff4dd01aeea876ef874f1828304..1135aa373a631cb90b6c0c75fd3590bcbefd4a4c 100644 ---- a/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/net/minecraft/world/level/chunk/LevelChunk.java -@@ -75,7 +75,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p - return ""; - } - }; -- private final Map tickersInLevel = Maps.newHashMap(); -+ private final Map tickersInLevel = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // DivineMC - Optimize collections - public boolean loaded; - public final ServerLevel level; // CraftBukkit - type +diff --git a/net/minecraft/world/level/chunk/ProtoChunk.java b/net/minecraft/world/level/chunk/ProtoChunk.java +index aa085d8a78a3fb40a214e4b152ab04d9a409f76f..5ed1a8b6ac025d03fdeb408a6eaa00320087f690 100644 +--- a/net/minecraft/world/level/chunk/ProtoChunk.java ++++ b/net/minecraft/world/level/chunk/ProtoChunk.java +@@ -46,7 +46,7 @@ public class ProtoChunk extends ChunkAccess { + @Nullable + private volatile LevelLightEngine lightEngine; + private volatile ChunkStatus status = ChunkStatus.EMPTY; +- private final List entities = Lists.newArrayList(); ++ private final List entities = Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Optimize collections + @Nullable + private CarvingMask carvingMask; @Nullable diff --git a/divinemc-server/minecraft-patches/features/0070-Optimize-level-ticking.patch b/divinemc-server/minecraft-patches/features/0070-Optimize-level-ticking.patch new file mode 100644 index 0000000..9e8158b --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0070-Optimize-level-ticking.patch @@ -0,0 +1,221 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Thu, 24 Jul 2025 14:07:47 +0300 +Subject: [PATCH] Optimize level ticking + + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index 386fdc23b35675a7db66d16bf2a8a6dd5b44059a..4934ce03ac533d9c60674632cdac6621e62f6b44 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -908,9 +908,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + // Paper start - optimise random ticking + private final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom simpleRandom = new ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom(net.minecraft.world.level.levelgen.RandomSupport.generateUniqueSeed()); + ++ // DivineMC start - Optimize level ticking + private void optimiseRandomTick(final LevelChunk chunk, final int tickSpeed) { + final LevelChunkSection[] sections = chunk.getSections(); +- final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection((ServerLevel)(Object)this); ++ final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this); // DivineMC - Optimize level ticking + final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom simpleRandom = this.simpleRandom; + final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); + +@@ -919,42 +920,38 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + final int offsetZ = cpos.z << 4; + + for (int sectionIndex = 0, sectionsLen = sections.length; sectionIndex < sectionsLen; sectionIndex++) { +- final int offsetY = (sectionIndex + minSection) << 4; + final LevelChunkSection section = sections[sectionIndex]; ++ if (!section.isRandomlyTickingBlocks()) continue; ++ final int offsetY = (sectionIndex + minSection) << 4; + final net.minecraft.world.level.chunk.PalettedContainer states = section.states; +- if (!section.isRandomlyTickingBlocks()) { +- continue; +- } + +- final ca.spottedleaf.moonrise.common.list.ShortList tickList = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection)section).moonrise$getTickingBlockList(); ++ final ca.spottedleaf.moonrise.common.list.ShortList tickList = section.moonrise$getTickingBlockList(); + + for (int i = 0; i < tickSpeed; ++i) { +- final int tickingBlocks = tickList.size(); + final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1); + +- if (index >= tickingBlocks) { ++ if (index >= tickList.size()) { + // most of the time we fall here + continue; + } + +- final int location = (int)tickList.getRaw(index) & 0xFFFF; ++ final int location = tickList.getRaw(index); + final BlockState state = states.get(location); + + // do not use a mutable pos, as some random tick implementations store the input without calling immutable()! +- final BlockPos pos = new BlockPos((location & 15) | offsetX, ((location >>> (4 + 4)) & 15) | offsetY, ((location >>> 4) & 15) | offsetZ); ++ final BlockPos pos = new BlockPos((location & 15) | offsetX, (location >>> (4 + 4)) | offsetY, ((location >>> 4) & 15) | offsetZ); + +- state.randomTick((ServerLevel)(Object)this, pos, simpleRandom); ++ state.randomTick(this, pos, simpleRandom); + if (doubleTickFluids) { + final FluidState fluidState = state.getFluidState(); + if (fluidState.isRandomlyTicking()) { +- fluidState.randomTick((ServerLevel)(Object)this, pos, simpleRandom); ++ fluidState.randomTick(this, pos, simpleRandom); + } + } + } + } +- +- return; + } ++ // DivineMC end - Optimize level ticking + // Paper end - optimise random ticking + + public void tickChunk(LevelChunk chunk, int randomTickSpeed) { +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index 24f13e2b0b694ff4dd01aeea876ef874f1828304..404ed7c6ebe2364b9404df6e29d07a0aed83ed08 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -75,7 +75,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + return ""; + } + }; +- private final Map tickersInLevel = Maps.newHashMap(); ++ private final Map tickersInLevel = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // DivineMC - Optimize level ticking + public boolean loaded; + public final ServerLevel level; // CraftBukkit - type + @Nullable +diff --git a/net/minecraft/world/ticks/LevelChunkTicks.java b/net/minecraft/world/ticks/LevelChunkTicks.java +index 66d0a6390febe929ef774b0a7813329015bc8cc2..c17549c4f8a877852c4b86453b1db7b17aab4665 100644 +--- a/net/minecraft/world/ticks/LevelChunkTicks.java ++++ b/net/minecraft/world/ticks/LevelChunkTicks.java +@@ -14,10 +14,10 @@ import javax.annotation.Nullable; + import net.minecraft.core.BlockPos; + + public class LevelChunkTicks implements SerializableTickContainer, TickContainerAccess, ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks { // Paper - rewrite chunk system +- private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); ++ private final Queue> tickQueue = new java.util.concurrent.PriorityBlockingQueue<>(11, ScheduledTick.DRAIN_ORDER); // DivineMC - Optimize level ticking + @Nullable + private List> pendingTicks; +- private final Set> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); ++ private final Set> ticksPerPosition = it.unimi.dsi.fastutil.objects.ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); // DivineMC - Optimize level ticking + @Nullable + private BiConsumer, ScheduledTick> onTickAdded; + +@@ -67,10 +67,18 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + + @Nullable + public ScheduledTick poll() { +- ScheduledTick scheduledTick = this.tickQueue.poll(); +- if (scheduledTick != null) { +- this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system ++ // DivineMC start - Optimize collections ++ ScheduledTick scheduledTick = null; ++ try { ++ scheduledTick = this.tickQueue.poll(); ++ if (scheduledTick != null) { ++ this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system ++ } ++ } catch (Exception e) { ++ net.minecraft.server.MinecraftServer.LOGGER.error("Encountered caught exception when polling chunk ticks, blocking and returning null.", e); ++ return null; + } ++ // DivineMC end - Optimize collections + + return scheduledTick; + } +@@ -83,6 +91,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + } + + private void scheduleUnchecked(ScheduledTick tick) { ++ if (tick == null) return; // DivineMC - Optimize level ticking + this.tickQueue.add(tick); + if (this.onTickAdded != null) { + this.onTickAdded.accept(this, tick); +@@ -124,6 +133,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + } + + for (ScheduledTick scheduledTick : this.tickQueue) { ++ if (scheduledTick == null) continue; // DivineMC - Optimize level ticking + list.add(scheduledTick.toSavedTick(gametime)); + } + +diff --git a/net/minecraft/world/ticks/LevelTicks.java b/net/minecraft/world/ticks/LevelTicks.java +index c7f9485191dc797de78e6524c5c2c737581ed838..14f2d0088cd9e6d4a2eb084439bab18bd365c41f 100644 +--- a/net/minecraft/world/ticks/LevelTicks.java ++++ b/net/minecraft/world/ticks/LevelTicks.java +@@ -30,17 +30,20 @@ public class LevelTicks implements LevelTickAccess { + private static final Comparator> CONTAINER_DRAIN_ORDER = (levelChunkTicks, levelChunkTicks1) -> ScheduledTick.INTRA_TICK_DRAIN_ORDER + .compare(levelChunkTicks.peek(), levelChunkTicks1.peek()); + private final LongPredicate tickCheck; +- private final Long2ObjectMap> allContainers = new Long2ObjectOpenHashMap<>(); +- private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), map -> map.defaultReturnValue(Long.MAX_VALUE)); +- private final Queue> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER); +- private final Queue> toRunThisTick = new ArrayDeque<>(); ++ // DivineMC start - Optimize collections ++ private final Long2ObjectMap> allContainers = it.unimi.dsi.fastutil.longs.Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); ++ private final java.util.Map nextTickForContainer = new java.util.concurrent.ConcurrentHashMap<>(); ++ private final Queue> containersToTick = new java.util.concurrent.PriorityBlockingQueue<>(11, CONTAINER_DRAIN_ORDER); ++ private final Queue> toRunThisTick = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ // DivineMC end - Optimize collections + private final List> alreadyRunThisTick = new ArrayList<>(); +- private final Set> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); ++ private final Set> toRunThisTickSet = com.google.common.collect.Sets.newConcurrentHashSet(); // DivineMC - Optimize level ticking + private final BiConsumer, ScheduledTick> chunkScheduleUpdater = (levelChunkTicks, scheduledTick) -> { + if (scheduledTick.equals(levelChunkTicks.peek())) { + this.updateContainerScheduling(scheduledTick); + } + }; ++ private final java.util.concurrent.atomic.AtomicInteger toRunThisTickCount = new java.util.concurrent.atomic.AtomicInteger(0); // DivineMC - Optimize level ticking + + public LevelTicks(LongPredicate tickCheck) { + this.tickCheck = tickCheck; +@@ -90,12 +93,14 @@ public class LevelTicks implements LevelTickAccess { + } + + private void sortContainersToTick(long gameTime) { +- ObjectIterator objectIterator = Long2LongMaps.fastIterator(this.nextTickForContainer); ++ java.util.Iterator> objectIterator = this.nextTickForContainer.entrySet().iterator(); // DivineMC - Optimize level ticking + + while (objectIterator.hasNext()) { +- Entry entry = objectIterator.next(); +- long longKey = entry.getLongKey(); +- long longValue = entry.getLongValue(); ++ // DivineMC start - Optimize collections ++ java.util.Map.Entry entry = objectIterator.next(); ++ long longKey = entry.getKey(); ++ long longValue = entry.getValue(); ++ // DivineMC end - Optimize collections + if (longValue <= gameTime) { + LevelChunkTicks levelChunkTicks = this.allContainers.get(longKey); + if (levelChunkTicks == null) { +@@ -162,16 +167,19 @@ public class LevelTicks implements LevelTickAccess { + } + + private void scheduleForThisTick(ScheduledTick tick) { ++ if (tick == null) return; // DivineMC - Optimize level ticking + this.toRunThisTick.add(tick); ++ this.toRunThisTickCount.incrementAndGet(); // DivineMC - Optimize level ticking + } + + private boolean canScheduleMoreTicks(int maxAllowedTicks) { +- return this.toRunThisTick.size() < maxAllowedTicks; ++ return this.toRunThisTickCount.get() < maxAllowedTicks; // DivineMC - Optimize level ticking + } + + private void runCollectedTicks(BiConsumer ticker) { + while (!this.toRunThisTick.isEmpty()) { + ScheduledTick scheduledTick = this.toRunThisTick.poll(); ++ this.toRunThisTickCount.decrementAndGet(); // DivineMC - Optimize level ticking + if (!this.toRunThisTickSet.isEmpty()) { + this.toRunThisTickSet.remove(scheduledTick); + } +@@ -182,7 +190,7 @@ public class LevelTicks implements LevelTickAccess { + } + + private void cleanupAfterTick() { +- this.toRunThisTick.clear(); ++ this.toRunThisTickCount.set(0); // DivineMC - Optimize level ticking + this.containersToTick.clear(); + this.alreadyRunThisTick.clear(); + this.toRunThisTickSet.clear(); diff --git a/divinemc-server/minecraft-patches/features/0071-Optimize-Moonrise.patch b/divinemc-server/minecraft-patches/features/0071-Optimize-Moonrise.patch new file mode 100644 index 0000000..fd42a37 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0071-Optimize-Moonrise.patch @@ -0,0 +1,480 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Mon, 11 Aug 2025 02:42:19 +0300 +Subject: [PATCH] Optimize Moonrise + + +diff --git a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java +index 93272808d94e81d31af728ebe85df9a2bc7aedab..b47be4b838f4c7f6c3fb62e4e18105c6b4972016 100644 +--- a/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java ++++ b/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java +@@ -59,7 +59,7 @@ public final class NearbyPlayers { + public static final int GENERAL_REALLY_SMALL_AREA_VIEW_DISTANCE_BLOCKS = (GENERAL_REALLY_SMALL_VIEW_DISTANCE << 4); + + private final ServerLevel world; +- private final Reference2ReferenceOpenHashMap players = new Reference2ReferenceOpenHashMap<>(); ++ private final it.unimi.dsi.fastutil.objects.Reference2ReferenceMap players = it.unimi.dsi.fastutil.objects.Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>()); // DivineMC - Optimize collections + // DivineMC start - Multithreaded Tracker + private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap byChunk; + { +@@ -70,10 +70,10 @@ public final class NearbyPlayers { + } + } + // DivineMC end - Multithreaded Tracker +- private final Long2ReferenceOpenHashMap>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES]; ++ private final it.unimi.dsi.fastutil.longs.Long2ReferenceMap>[] directByChunk = new it.unimi.dsi.fastutil.longs.Long2ReferenceMap[TOTAL_MAP_TYPES]; // DivineMC - Optimize collections + { + for (int i = 0; i < this.directByChunk.length; ++i) { +- this.directByChunk[i] = new Long2ReferenceOpenHashMap<>(); ++ this.directByChunk[i] = it.unimi.dsi.fastutil.longs.Long2ReferenceMaps.synchronize(new Long2ReferenceOpenHashMap<>()); // DivineMC - Optimize collections + } + } + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +index 57fec1f9a210d2ecb74ff7b05cec790ae77f9178..4d0e904d7d7659b24a883893cef167f3e80dfa36 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +@@ -73,37 +73,51 @@ public final class ChunkHolderManager { + private static final long NO_TIMEOUT_MARKER = Long.MIN_VALUE; + public final ReentrantAreaLock ticketLockArea; + +- private final ConcurrentLong2ReferenceChainedHashTable> tickets = new ConcurrentLong2ReferenceChainedHashTable<>(); +- private final ConcurrentLong2ReferenceChainedHashTable sectionToChunkToExpireCount = new ConcurrentLong2ReferenceChainedHashTable<>(); ++ // DivineMC start - Optimize Moonrise ++ private final ConcurrentLong2ReferenceChainedHashTable> tickets = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(20, 0.9F); ++ private final ConcurrentLong2ReferenceChainedHashTable sectionToChunkToExpireCount = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(20, 0.9F); ++ // DivineMC end - Optimize Moonrise + final ChunkUnloadQueue unloadQueue; + +- private final ConcurrentLong2ReferenceChainedHashTable chunkHolders = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(16384, 0.25f); ++ private final ConcurrentLong2ReferenceChainedHashTable chunkHolders = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(20, 0.9F); // DivineMC - Optimize Moonrise + private final ServerLevel world; + private final ChunkTaskScheduler taskScheduler; + private long currentTick; + +- private final ArrayDeque pendingFullLoadUpdate = new ArrayDeque<>(); +- private final MultiThreadedQueue offThreadPendingFullLoadUpdate = new MultiThreadedQueue<>(); +- private final ObjectRBTreeSet autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> { +- if (c1 == c2) { +- return 0; +- } ++ // DivineMC start - Optimize Moonrise ++ public static class LevelHolderData { ++ private final java.util.concurrent.ConcurrentLinkedDeque pendingFullLoadUpdate = new java.util.concurrent.ConcurrentLinkedDeque<>(); ++ private final MultiThreadedQueue offThreadPendingFullLoadUpdate = new MultiThreadedQueue<>(); ++ private final ObjectRBTreeSet autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> { ++ if (c1 == c2) { ++ return 0; ++ } + +- final int saveTickCompare = Long.compare(c1.lastAutoSave, c2.lastAutoSave); ++ final int saveTickCompare = Long.compare(c1.lastAutoSave, c2.lastAutoSave); + +- if (saveTickCompare != 0) { +- return saveTickCompare; +- } ++ if (saveTickCompare != 0) { ++ return saveTickCompare; ++ } ++ ++ final long coord1 = CoordinateUtils.getChunkKey(c1.chunkX, c1.chunkZ); ++ final long coord2 = CoordinateUtils.getChunkKey(c2.chunkX, c2.chunkZ); + +- final long coord1 = CoordinateUtils.getChunkKey(c1.chunkX, c1.chunkZ); +- final long coord2 = CoordinateUtils.getChunkKey(c2.chunkX, c2.chunkZ); ++ if (coord1 == coord2) { ++ throw new IllegalStateException("Duplicate chunkholder in auto save queue"); ++ } ++ ++ return Long.compare(coord1, coord2); ++ }); ++ } + +- if (coord1 == coord2) { +- throw new IllegalStateException("Duplicate chunkholder in auto save queue"); ++ public LevelHolderData getData() { ++ if (this.world == null) { ++ throw new RuntimeException("World was null!"); + } + +- return Long.compare(coord1, coord2); +- }); ++ return world.chunkHolderData; ++ } ++ // DivineMC end - Optimize Moonrise + + private final ConcurrentLong2ReferenceChainedHashTable ticketCounters = new ConcurrentLong2ReferenceChainedHashTable<>(); + +@@ -226,26 +240,29 @@ public final class ChunkHolderManager { + this.taskScheduler.setShutdown(true); + } + +- void ensureInAutosave(final NewChunkHolder holder) { +- if (!this.autoSaveQueue.contains(holder)) { ++ // DivineMC start - Optimize Moonrise ++ synchronized void ensureInAutosave(final NewChunkHolder holder) { ++ final LevelHolderData data = getData(); ++ if (!data.autoSaveQueue.contains(holder)) { + holder.lastAutoSave = this.currentTick; +- this.autoSaveQueue.add(holder); ++ data.autoSaveQueue.add(holder); + } + } + +- public void autoSave() { ++ public synchronized void autoSave() { ++ final LevelHolderData data = getData(); + final List reschedule = new ArrayList<>(); + final long currentTick = this.currentTick; + final long maxSaveTime = currentTick - Math.max(1L, PlatformHooks.get().configAutoSaveInterval(this.world)); + final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick(this.world); +- for (int autoSaved = 0; autoSaved < maxToSave && !this.autoSaveQueue.isEmpty();) { +- final NewChunkHolder holder = this.autoSaveQueue.first(); ++ for (int autoSaved = 0; autoSaved < maxToSave && !data.autoSaveQueue.isEmpty();) { ++ final NewChunkHolder holder = data.autoSaveQueue.first(); + + if (holder.lastAutoSave > maxSaveTime) { + break; + } + +- this.autoSaveQueue.remove(holder); ++ data.autoSaveQueue.remove(holder); + + holder.lastAutoSave = currentTick; + if (holder.save(false) != null) { +@@ -259,10 +276,11 @@ public final class ChunkHolderManager { + + for (final NewChunkHolder holder : reschedule) { + if (holder.getChunkStatus().isOrAfter(FullChunkStatus.FULL)) { +- this.autoSaveQueue.add(holder); ++ data.autoSaveQueue.add(holder); + } + } + } ++ // DivineMC end - Optimize Moonrise + + public void saveAllChunks(final boolean flush, final boolean shutdown, final boolean logProgress) { + final List holders = this.getChunkHolders(); +@@ -461,8 +479,8 @@ public final class ChunkHolderManager { + final Long2ObjectOpenHashMap> ret = new Long2ObjectOpenHashMap<>(); + final Long2ObjectOpenHashMap sections = new Long2ObjectOpenHashMap<>(); + final int sectionShift = this.taskScheduler.getChunkSystemLockShift(); +- for (final PrimitiveIterator.OfLong iterator = this.tickets.keyIterator(); iterator.hasNext();) { +- final long coord = iterator.nextLong(); ++ for (final Iterator iterator = this.tickets.keyIterator(); iterator.hasNext();) { // DivineMC - Optimize Moonrise ++ final long coord = iterator.next(); // DivineMC - Optimize Moonrise + sections.computeIfAbsent( + CoordinateUtils.getChunkKey( + CoordinateUtils.getChunkX(coord) >> sectionShift, +@@ -559,7 +577,7 @@ public final class ChunkHolderManager { + chunkZ >> sectionShift + ); + +- this.sectionToChunkToExpireCount.computeIfAbsent(sectionKey, (final long keyInMap) -> { ++ this.sectionToChunkToExpireCount.computeIfAbsent(sectionKey, (keyInMap) -> { // DivineMC - Optimize Moonrise + return new Long2IntOpenHashMap(); + }).addTo(chunkKey, 1); + } +@@ -603,8 +621,8 @@ public final class ChunkHolderManager { + + final ReentrantAreaLock.Node ticketLock = lock ? this.ticketLockArea.lock(chunkX, chunkZ) : null; + try { +- final SortedArraySet ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (final long keyInMap) -> { +- return (SortedArraySet)SortedArraySet.create(4); ++ final SortedArraySet ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (keyInMap) -> { // DivineMC - Optimize Moonrise ++ return SortedArraySet.create(4); // DivineMC - Optimize Moonrise + }); + + final int levelBefore = getTicketLevelAt(ticketsAtChunk); +@@ -784,8 +802,8 @@ public final class ChunkHolderManager { + + final Long2ObjectOpenHashMap sections = new Long2ObjectOpenHashMap<>(); + final int sectionShift = this.taskScheduler.getChunkSystemLockShift(); +- for (final PrimitiveIterator.OfLong iterator = this.tickets.keyIterator(); iterator.hasNext();) { +- final long coord = iterator.nextLong(); ++ for (final Iterator iterator = this.tickets.keyIterator(); iterator.hasNext();) { // DivineMC - Optimize Moonrise ++ final long coord = iterator.next(); // DivineMC - Optimize Moonrise + sections.computeIfAbsent( + CoordinateUtils.getChunkKey( + CoordinateUtils.getChunkX(coord) >> sectionShift, +@@ -836,8 +854,8 @@ public final class ChunkHolderManager { + final List scheduledTasks = new ArrayList<>(); + final List changedFullStatus = new ArrayList<>(); + +- for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) { +- final long sectionKey = iterator.nextLong(); ++ for (final Iterator iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) { // DivineMC - Optimize Moonrise ++ final long sectionKey = iterator.next(); // DivineMC - Optimize Moonrise + + if (!this.sectionToChunkToExpireCount.containsKey(sectionKey)) { + // removed concurrently +@@ -1145,18 +1163,18 @@ public final class ChunkHolderManager { + if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking && !TickThread.isTickThreadFor(world) || !TickThread.isTickThread()) { // DivineMC - Parallel world ticking + // These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update + // which will invoke processTicketUpdates +- this.offThreadPendingFullLoadUpdate.addAll(changedFullStatus); ++ this.getData().offThreadPendingFullLoadUpdate.addAll(changedFullStatus); // DivineMC - Optimize Moonrise + } else { +- final ArrayDeque pendingFullLoadUpdate = this.pendingFullLoadUpdate; ++ final java.util.Deque pendingFullLoadUpdate = this.getData().pendingFullLoadUpdate; // DivineMC - Optimize Moonrise + for (int i = 0, len = changedFullStatus.size(); i < len; ++i) { + pendingFullLoadUpdate.add(changedFullStatus.get(i)); + } + } + } + +- private void removeChunkHolder(final NewChunkHolder holder) { ++ private synchronized void removeChunkHolder(final NewChunkHolder holder) { // DivineMC - Optimize Moonrise + holder.onUnload(); +- this.autoSaveQueue.remove(holder); ++ this.getData().autoSaveQueue.remove(holder); // DivineMC - Optimize Moonrise + PlatformHooks.get().onChunkHolderDelete(this.world, holder.vanillaChunkHolder); + this.chunkHolders.remove(CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ)); + } +@@ -1320,6 +1338,27 @@ public final class ChunkHolderManager { + } + } + ++ // DivineMC start - Optimize Moonrise ++ public final java.util.Set blockTickingChunkHolders = java.util.Collections.synchronizedSet(new org.agrona.collections.ObjectHashSet<>(10, 0.88f, true)); ++ public final java.util.Set entityTickingChunkHolders = java.util.Collections.synchronizedSet(new org.agrona.collections.ObjectHashSet<>(10, 0.88f, true)); ++ ++ public void markBlockTicking(@org.jetbrains.annotations.NotNull NewChunkHolder newChunkHolder) { ++ this.blockTickingChunkHolders.add(newChunkHolder.getCachedLongPos()); ++ } ++ ++ public void markNonBlockTickingIfPossible(@org.jetbrains.annotations.NotNull NewChunkHolder newChunkHolder) { ++ this.blockTickingChunkHolders.remove(newChunkHolder.getCachedLongPos()); ++ } ++ ++ public void markEntityTicking(@org.jetbrains.annotations.NotNull NewChunkHolder newChunkHolder) { ++ this.entityTickingChunkHolders.add(newChunkHolder.getCachedLongPos()); ++ } ++ ++ public void markNonEntityTickingIfPossible(@org.jetbrains.annotations.NotNull NewChunkHolder newChunkHolder) { ++ this.entityTickingChunkHolders.remove(newChunkHolder.getCachedLongPos()); ++ } ++ // DivineMC end - Optimize Moonrise ++ + public enum TicketOperationType { + ADD, REMOVE, ADD_IF_REMOVED, ADD_AND_REMOVE + } +@@ -1479,8 +1518,10 @@ public final class ChunkHolderManager { + + // only call on tick thread + private void processOffThreadFullUpdates() { +- final ArrayDeque pendingFullLoadUpdate = this.pendingFullLoadUpdate; +- final MultiThreadedQueue offThreadPendingFullLoadUpdate = this.offThreadPendingFullLoadUpdate; ++ // DivineMC start - Optimize Moonrise ++ final java.util.concurrent.ConcurrentLinkedDeque pendingFullLoadUpdate = this.getData().pendingFullLoadUpdate; ++ final MultiThreadedQueue offThreadPendingFullLoadUpdate = this.getData().offThreadPendingFullLoadUpdate; ++ // DivineMC end - Optimize Moonrise + + NewChunkHolder toUpdate; + while ((toUpdate = offThreadPendingFullLoadUpdate.poll()) != null) { +@@ -1492,7 +1533,7 @@ public final class ChunkHolderManager { + private boolean processPendingFullUpdate() { + this.processOffThreadFullUpdates(); + +- final ArrayDeque pendingFullLoadUpdate = this.pendingFullLoadUpdate; ++ final java.util.Deque pendingFullLoadUpdate = this.getData().pendingFullLoadUpdate; // DivineMC - Optimize Moonrise + + boolean ret = false; + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java +index 2cc0e7c72d2b2e562452138f2b41fd1dcaf0570a..affa0dac8633ce3a43c9609888ed96d0aabdab5e 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java +@@ -646,12 +646,20 @@ public final class NewChunkHolder { + } + + public final ChunkHolder vanillaChunkHolder; ++ // DivineMC start - Optimize Moonrise ++ private final long cachedLongPos; ++ ++ public long getCachedLongPos() { ++ return cachedLongPos; ++ } ++ // DivineMC end - Optimize Moonrise + + public NewChunkHolder(final ServerLevel world, final int chunkX, final int chunkZ, final ChunkTaskScheduler scheduler) { + this.world = world; + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.scheduler = scheduler; ++ this.cachedLongPos = ((long)chunkZ << 32) | (chunkX & 0xFFFFFFFFL); // DivineMC - Optimize Moonrise + this.vanillaChunkHolder = new ChunkHolder( + new ChunkPos(chunkX, chunkZ), ChunkHolderManager.MAX_TICKET_LEVEL, world, + world.getLightEngine(), null, world.getChunkSource().chunkMap +@@ -792,9 +800,11 @@ public final class NewChunkHolder { + + // note: these are completed with null to indicate that no write occurred + // they are also completed with null to indicate a null write occurred +- private UnloadTask chunkDataUnload; +- private UnloadTask entityDataUnload; +- private UnloadTask poiDataUnload; ++ // DivineMC start - Optimize Moonrise ++ private volatile UnloadTask chunkDataUnload; ++ private volatile UnloadTask entityDataUnload; ++ private volatile UnloadTask poiDataUnload; ++ // DivineMC end - Optimize Moonrise + + public static final record UnloadTask(CallbackCompletable completable, PrioritisedExecutor.PrioritisedTask task, + LazyRunnable toRun) {} +@@ -879,7 +889,11 @@ public final class NewChunkHolder { + MoonriseRegionFileIO.scheduleSave(this.world, this.chunkX, this.chunkZ, data, type); + } + +- this.getUnloadTask(type).completable().complete(data); ++ // DivineMC start - Optimize Moonrise ++ UnloadTask task = this.getUnloadTask(type); ++ if (task == null) return; ++ task.completable().complete(data); ++ // DivineMC end - Optimize Moonrise + final ReentrantAreaLock.Node schedulingLock = this.scheduler.schedulingLockArea.lock(this.chunkX, this.chunkZ); + try { + // can only write to these fields while holding the schedule lock +@@ -1192,6 +1206,7 @@ public final class NewChunkHolder { + for (int dz = -NEIGHBOUR_RADIUS; dz <= NEIGHBOUR_RADIUS; ++dz) { + for (int dx = -NEIGHBOUR_RADIUS; dx <= NEIGHBOUR_RADIUS; ++dx) { + final NewChunkHolder holder = (dx | dz) == 0 ? this : this.scheduler.chunkHolderManager.getChunkHolder(dx + this.chunkX, dz + this.chunkZ); ++ if (holder == null) continue; // DivineMC - Optimize Moonrise + if (loaded) { + if (holder.setNeighbourFullLoaded(-dx, -dz)) { + changedFullStatus.add(holder); +@@ -1216,6 +1231,19 @@ public final class NewChunkHolder { + + private void updateCurrentState(final FullChunkStatus to) { + this.currentFullChunkStatus = to; ++ // DivineMC start - Optimize Moonrise ++ if (to.isOrAfter(FullChunkStatus.BLOCK_TICKING)) { ++ this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.markBlockTicking(this); ++ } else { ++ this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.markNonBlockTickingIfPossible(this); ++ } ++ ++ if (to.isOrAfter(FullChunkStatus.ENTITY_TICKING)) { ++ this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.markEntityTicking(this); ++ } else { ++ this.world.moonrise$getChunkTaskScheduler().chunkHolderManager.markNonEntityTickingIfPossible(this); ++ } ++ // DivineMC end - Optimize Moonrise + } + + // only to be called on the main thread, no locks need to be held +@@ -1350,7 +1378,7 @@ public final class NewChunkHolder { + return this.requestedGenStatus; + } + +- private final Reference2ObjectOpenHashMap>> statusWaiters = new Reference2ObjectOpenHashMap<>(); ++ private final Reference2ObjectMap>> statusWaiters = it.unimi.dsi.fastutil.objects.Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); // DivineMC - Optimize Moonrise + + void addStatusConsumer(final ChunkStatus status, final Consumer consumer) { + this.statusWaiters.computeIfAbsent(status, (final ChunkStatus keyInMap) -> { +@@ -1396,7 +1424,7 @@ public final class NewChunkHolder { + }, Priority.HIGHEST); + } + +- private final Reference2ObjectOpenHashMap>> fullStatusWaiters = new Reference2ObjectOpenHashMap<>(); ++ private final Reference2ObjectMap>> fullStatusWaiters = it.unimi.dsi.fastutil.objects.Reference2ObjectMaps.synchronize(new Reference2ObjectOpenHashMap<>()); // DivineMC - Optimize Moonrise + + void addFullStatusConsumer(final FullChunkStatus status, final Consumer consumer) { + this.fullStatusWaiters.computeIfAbsent(status, (final FullChunkStatus keyInMap) -> { +diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java +index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..1ac37db68341672481cd4bbdf7bab90572c35453 100644 +--- a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java ++++ b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java +@@ -325,7 +325,7 @@ public final class SWMRNibbleArray { + } + + // operation type: updating +- public boolean updateVisible() { ++ public synchronized boolean updateVisible() { // DivineMC - Optimize Moonrise + if (!this.isDirty()) { + return false; + } +diff --git a/net/minecraft/server/level/DistanceManager.java b/net/minecraft/server/level/DistanceManager.java +index 50bc5b940812432bc472e5b272582efb8bbfc7a7..0bece4ed69b332174cbe37f82df1f7da9276d591 100644 +--- a/net/minecraft/server/level/DistanceManager.java ++++ b/net/minecraft/server/level/DistanceManager.java +@@ -127,15 +127,13 @@ public abstract class DistanceManager implements ca.spottedleaf.moonrise.patches + + public boolean inEntityTickingRange(long chunkPos) { + // Paper start - rewrite chunk system +- final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos); +- return chunkHolder != null && chunkHolder.isEntityTickingReady(); ++ return this.moonrise$getChunkHolderManager().entityTickingChunkHolders.contains(chunkPos); // DivineMC - Optimize Moonrise + // Paper end - rewrite chunk system + } + + public boolean inBlockTickingRange(long chunkPos) { + // Paper start - rewrite chunk system +- final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos); +- return chunkHolder != null && chunkHolder.isTickingReady(); ++ return this.moonrise$getChunkHolderManager().blockTickingChunkHolders.contains(chunkPos); // DivineMC - Optimize Moonrise + // Paper end - rewrite chunk system + } + +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 440bd8f16664fdc29db69b92f2c856dd7cc4ffd1..0abfd610caf529952fee857d881920a5ddad8ed3 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -667,8 +667,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + + public boolean isPositionTicking(long chunkPos) { + // Paper start - rewrite chunk system +- final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder newChunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); +- return newChunkHolder != null && newChunkHolder.isTickingReady(); ++ return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.blockTickingChunkHolders.contains(chunkPos); // DivineMC - Optimize Moonrise + // Paper end - rewrite chunk system + } + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index 4934ce03ac533d9c60674632cdac6621e62f6b44..b50afea7c2e4c61a3df196e74afd8f82b30aca8a 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -179,6 +179,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + public final ServerChunkCache chunkSource; + private final MinecraftServer server; + public final net.minecraft.world.level.storage.PrimaryLevelData serverLevelData; // CraftBukkit - type ++ public final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager.LevelHolderData chunkHolderData; // DivineMC - Optimize Moonrise + private int lastSpawnChunkRadius; + final EntityTickList entityTickList = new EntityTickList(this); // DivineMC - Parallel world ticking + private final ServerWaypointManager waypointManager; +@@ -691,6 +692,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + // Paper start - rewrite chunk system + this.moonrise$setEntityLookup(new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup((ServerLevel)(Object)this, ((ServerLevel)(Object)this).new EntityCallbacks())); + this.chunkTaskScheduler = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler((ServerLevel)(Object)this); ++ this.chunkHolderData = new ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager.LevelHolderData(); // DivineMC - Optimize Moonrise + this.entityDataController = new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController( + new ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController.EntityRegionFileStorage( + new RegionStorageInfo(levelStorageAccess.getLevelId(), dimension, "entities"), +@@ -846,8 +848,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + @Override + public boolean shouldTickBlocksAt(long chunkPos) { + // Paper start - rewrite chunk system +- final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); +- return holder != null && holder.isTickingReady(); ++ return this.moonrise$getChunkTaskScheduler().chunkHolderManager.blockTickingChunkHolders.contains(chunkPos); // DivineMC - Optimize Moonrise + // Paper end - rewrite chunk system + } + +@@ -2584,16 +2585,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + + public boolean isPositionTickingWithEntitiesLoaded(long chunkPos) { + // Paper start - rewrite chunk system +- final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkPos); +- // isTicking implies the chunk is loaded, and the chunk is loaded now implies the entities are loaded +- return chunkHolder != null && chunkHolder.isTickingReady(); ++ return this.moonrise$getChunkTaskScheduler().chunkHolderManager.blockTickingChunkHolders.contains(chunkPos); // DivineMC - Optimize Moonrise + // Paper end - rewrite chunk system + } + + public boolean isPositionEntityTicking(BlockPos pos) { + // Paper start - rewrite chunk system +- final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); +- return chunkHolder != null && chunkHolder.isEntityTickingReady(); ++ return this.moonrise$getChunkTaskScheduler().chunkHolderManager.entityTickingChunkHolders.contains(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(pos)); // DivineMC - Optimize Moonrise + // Paper end - rewrite chunk system + } + diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/ticks/LevelTicks.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/ticks/LevelTicks.java.patch deleted file mode 100644 index 32f1ee6..0000000 --- a/divinemc-server/minecraft-patches/sources/net/minecraft/world/ticks/LevelTicks.java.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- a/net/minecraft/world/ticks/LevelTicks.java -+++ b/net/minecraft/world/ticks/LevelTicks.java -@@ -191,7 +_,6 @@ - } - - private void cleanupAfterTick() { -- this.toRunThisTick.clear(); - this.containersToTick.clear(); - this.alreadyRunThisTick.clear(); - this.toRunThisTickSet.clear(); diff --git a/divinemc-server/paper-patches/features/0019-Leaves-Protocol-Core.patch b/divinemc-server/paper-patches/features/0018-Leaves-Protocol-Core.patch similarity index 100% rename from divinemc-server/paper-patches/features/0019-Leaves-Protocol-Core.patch rename to divinemc-server/paper-patches/features/0018-Leaves-Protocol-Core.patch diff --git a/divinemc-server/paper-patches/features/0018-Rewrite-ReferenceList.patch b/divinemc-server/paper-patches/features/0018-Rewrite-ReferenceList.patch deleted file mode 100644 index 1135f2e..0000000 --- a/divinemc-server/paper-patches/features/0018-Rewrite-ReferenceList.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Wed, 9 Jul 2025 03:37:51 +0300 -Subject: [PATCH] Rewrite ReferenceList - - -diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java -index 2e876b918672e8ef3b5197b7e6b1597247fdeaa1..94232d9ab2b1b064c032d9710734f6fbcdb52e10 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java -+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java -@@ -1,142 +1,26 @@ - package ca.spottedleaf.moonrise.common.list; - --import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; --import java.util.Arrays; --import java.util.Iterator; --import java.util.NoSuchElementException; -+// DivineMC start - Rewrite ReferenceList -+import it.unimi.dsi.fastutil.objects.ReferenceArrayList; -+import it.unimi.dsi.fastutil.objects.ReferenceLists; - --public final class ReferenceList implements Iterable { -- -- private static final Object[] EMPTY_LIST = new Object[0]; -- -- private final Reference2IntOpenHashMap referenceToIndex; -- private E[] references; -- private int count; -- -- public ReferenceList() { -- this((E[])EMPTY_LIST); -- } -- -- public ReferenceList(final E[] referenceArray) { -- this.references = referenceArray; -- this.referenceToIndex = new Reference2IntOpenHashMap<>(2, 0.8f); -- this.referenceToIndex.defaultReturnValue(Integer.MIN_VALUE); -- } -- -- private ReferenceList(final E[] references, final int count, final Reference2IntOpenHashMap referenceToIndex) { -- this.references = references; -- this.count = count; -- this.referenceToIndex = referenceToIndex; -- } -- -- public ReferenceList copy() { -- return new ReferenceList<>(this.references.clone(), this.count, this.referenceToIndex.clone()); -- } -- -- public int size() { -- return this.count; -- } -- -- public boolean contains(final E obj) { -- return this.referenceToIndex.containsKey(obj); -+public class ReferenceList extends ReferenceLists.SynchronizedList { -+ public ReferenceList(E[] elements) { -+ super(new RefListInner<>(elements)); - } - -- public boolean remove(final E obj) { -- final int index = this.referenceToIndex.removeInt(obj); -- if (index == Integer.MIN_VALUE) { -- return false; -- } -- -- // move the object at the end to this index -- final int endIndex = --this.count; -- final E end = (E)this.references[endIndex]; -- if (index != endIndex) { -- // not empty after this call -- this.referenceToIndex.put(end, index); // update index -- } -- this.references[index] = end; -- this.references[endIndex] = null; -- -- return true; -+ public synchronized E[] getRawDataUnchecked() { -+ return ((RefListInner) this.list).getRawDataUnchecked(); - } - -- public boolean add(final E obj) { -- final int count = this.count; -- final int currIndex = this.referenceToIndex.putIfAbsent(obj, count); -- -- if (currIndex != Integer.MIN_VALUE) { -- return false; // already in this list -+ public static class RefListInner extends ReferenceArrayList { -+ public RefListInner(A[] elements) { -+ super(elements, true); - } - -- E[] list = this.references; -- -- if (list.length == count) { -- // resize required -- list = this.references = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative -+ public A[] getRawDataUnchecked() { -+ return this.a; - } -- -- list[count] = obj; -- this.count = count + 1; -- -- return true; -- } -- -- public E getChecked(final int index) { -- if (index < 0 || index >= this.count) { -- throw new IndexOutOfBoundsException("Index: " + index + " is out of bounds, size: " + this.count); -- } -- return this.references[index]; -- } -- -- public E getUnchecked(final int index) { -- return this.references[index]; -- } -- -- public Object[] getRawData() { -- return this.references; -- } -- -- public E[] getRawDataUnchecked() { -- return this.references; -- } -- -- public void clear() { -- this.referenceToIndex.clear(); -- Arrays.fill(this.references, 0, this.count, null); -- this.count = 0; -- } -- -- @Override -- public Iterator iterator() { -- return new Iterator<>() { -- private E lastRet; -- private int current; -- -- @Override -- public boolean hasNext() { -- return this.current < ReferenceList.this.count; -- } -- -- @Override -- public E next() { -- if (this.current >= ReferenceList.this.count) { -- throw new NoSuchElementException(); -- } -- return this.lastRet = ReferenceList.this.references[this.current++]; -- } -- -- @Override -- public void remove() { -- final E lastRet = this.lastRet; -- -- if (lastRet == null) { -- throw new IllegalStateException(); -- } -- this.lastRet = null; -- -- ReferenceList.this.remove(lastRet); -- --this.current; -- } -- }; - } - } -+// DivineMC end - Rewrite ReferenceList diff --git a/divinemc-server/paper-patches/features/0020-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch b/divinemc-server/paper-patches/features/0019-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch similarity index 100% rename from divinemc-server/paper-patches/features/0020-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch rename to divinemc-server/paper-patches/features/0019-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch diff --git a/divinemc-server/paper-patches/features/0020-Optimize-Moonrise.patch b/divinemc-server/paper-patches/features/0020-Optimize-Moonrise.patch new file mode 100644 index 0000000..645a38c --- /dev/null +++ b/divinemc-server/paper-patches/features/0020-Optimize-Moonrise.patch @@ -0,0 +1,508 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Wed, 9 Jul 2025 03:37:51 +0300 +Subject: [PATCH] Optimize Moonrise + + +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java +index 7fed43a1e7bcf35c4d7fd3224837a47fedd59860..6c002ba690a8cc7335276e039f49c4251076cc92 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java +@@ -1,5 +1,7 @@ + package ca.spottedleaf.moonrise.common.list; + ++import it.unimi.dsi.fastutil.ints.Int2IntMap; ++import it.unimi.dsi.fastutil.ints.Int2IntMaps; + import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; + import net.minecraft.world.entity.Entity; + import java.util.Arrays; +@@ -13,7 +15,7 @@ import java.util.NoSuchElementException; + */ + public final class EntityList implements Iterable { + +- private final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f); ++ private final Int2IntMap entityToIndex = Int2IntMaps.synchronize(new Int2IntOpenHashMap(2, 0.8f)); // DivineMC - Optimize Moonrise + { + this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE); + } +@@ -27,11 +29,11 @@ public final class EntityList implements Iterable { + return this.count; + } + +- public boolean contains(final Entity entity) { ++ public synchronized boolean contains(final Entity entity) { // DivineMC - Optimize Moonrise + return this.entityToIndex.containsKey(entity.getId()); + } + +- public boolean remove(final Entity entity) { ++ public synchronized boolean remove(final Entity entity) { // DivineMC - Optimize Moonrise + final int index = this.entityToIndex.remove(entity.getId()); + if (index == Integer.MIN_VALUE) { + return false; +@@ -50,7 +52,7 @@ public final class EntityList implements Iterable { + return true; + } + +- public boolean add(final Entity entity) { ++ public synchronized boolean add(final Entity entity) { // DivineMC - Optimize Moonrise + final int count = this.count; + final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count); + +@@ -82,18 +84,18 @@ public final class EntityList implements Iterable { + return this.entities[index]; + } + +- public Entity[] getRawData() { ++ public synchronized Entity[] getRawData() { // DivineMC - Optimize Moonrise + return this.entities; + } + +- public void clear() { ++ public synchronized void clear() { // DivineMC - Optimize Moonrise + this.entityToIndex.clear(); + Arrays.fill(this.entities, 0, this.count, null); + this.count = 0; + } + + @Override +- public Iterator iterator() { ++ public synchronized Iterator iterator() { // DivineMC - Optimize Moonrise + return new Iterator<>() { + private Entity lastRet; + private int current; +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java +index 2e876b918672e8ef3b5197b7e6b1597247fdeaa1..c93a9d2de61c9aa789c0758de0bdafaa4af2c93f 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java +@@ -1,142 +1,26 @@ + package ca.spottedleaf.moonrise.common.list; + +-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +-import java.util.Arrays; +-import java.util.Iterator; +-import java.util.NoSuchElementException; ++// DivineMC start - Optimize Moonrise ++import it.unimi.dsi.fastutil.objects.ReferenceArrayList; ++import it.unimi.dsi.fastutil.objects.ReferenceLists; + +-public final class ReferenceList implements Iterable { +- +- private static final Object[] EMPTY_LIST = new Object[0]; +- +- private final Reference2IntOpenHashMap referenceToIndex; +- private E[] references; +- private int count; +- +- public ReferenceList() { +- this((E[])EMPTY_LIST); +- } +- +- public ReferenceList(final E[] referenceArray) { +- this.references = referenceArray; +- this.referenceToIndex = new Reference2IntOpenHashMap<>(2, 0.8f); +- this.referenceToIndex.defaultReturnValue(Integer.MIN_VALUE); +- } +- +- private ReferenceList(final E[] references, final int count, final Reference2IntOpenHashMap referenceToIndex) { +- this.references = references; +- this.count = count; +- this.referenceToIndex = referenceToIndex; +- } +- +- public ReferenceList copy() { +- return new ReferenceList<>(this.references.clone(), this.count, this.referenceToIndex.clone()); +- } +- +- public int size() { +- return this.count; +- } +- +- public boolean contains(final E obj) { +- return this.referenceToIndex.containsKey(obj); ++public class ReferenceList extends ReferenceLists.SynchronizedList { ++ public ReferenceList(E[] elements) { ++ super(new RefListInner<>(elements)); + } + +- public boolean remove(final E obj) { +- final int index = this.referenceToIndex.removeInt(obj); +- if (index == Integer.MIN_VALUE) { +- return false; +- } +- +- // move the object at the end to this index +- final int endIndex = --this.count; +- final E end = (E)this.references[endIndex]; +- if (index != endIndex) { +- // not empty after this call +- this.referenceToIndex.put(end, index); // update index +- } +- this.references[index] = end; +- this.references[endIndex] = null; +- +- return true; ++ public synchronized E[] getRawDataUnchecked() { ++ return ((RefListInner) this.list).getRawDataUnchecked(); + } + +- public boolean add(final E obj) { +- final int count = this.count; +- final int currIndex = this.referenceToIndex.putIfAbsent(obj, count); +- +- if (currIndex != Integer.MIN_VALUE) { +- return false; // already in this list ++ public static class RefListInner extends ReferenceArrayList { ++ public RefListInner(A[] elements) { ++ super(elements, true); + } + +- E[] list = this.references; +- +- if (list.length == count) { +- // resize required +- list = this.references = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative ++ public A[] getRawDataUnchecked() { ++ return this.a; + } +- +- list[count] = obj; +- this.count = count + 1; +- +- return true; +- } +- +- public E getChecked(final int index) { +- if (index < 0 || index >= this.count) { +- throw new IndexOutOfBoundsException("Index: " + index + " is out of bounds, size: " + this.count); +- } +- return this.references[index]; +- } +- +- public E getUnchecked(final int index) { +- return this.references[index]; +- } +- +- public Object[] getRawData() { +- return this.references; +- } +- +- public E[] getRawDataUnchecked() { +- return this.references; +- } +- +- public void clear() { +- this.referenceToIndex.clear(); +- Arrays.fill(this.references, 0, this.count, null); +- this.count = 0; +- } +- +- @Override +- public Iterator iterator() { +- return new Iterator<>() { +- private E lastRet; +- private int current; +- +- @Override +- public boolean hasNext() { +- return this.current < ReferenceList.this.count; +- } +- +- @Override +- public E next() { +- if (this.current >= ReferenceList.this.count) { +- throw new NoSuchElementException(); +- } +- return this.lastRet = ReferenceList.this.references[this.current++]; +- } +- +- @Override +- public void remove() { +- final E lastRet = this.lastRet; +- +- if (lastRet == null) { +- throw new IllegalStateException(); +- } +- this.lastRet = null; +- +- ReferenceList.this.remove(lastRet); +- --this.current; +- } +- }; + } + } ++// DivineMC end - Optimize Moonrise +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java +index 2bae9949ef325d0001aa638150fbbdf968367e75..f946e625dcfec5c6ee9bf90bb79ac1c2f84ec048 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java +@@ -1,11 +1,13 @@ + package ca.spottedleaf.moonrise.common.list; + ++import it.unimi.dsi.fastutil.shorts.Short2ShortMap; ++import it.unimi.dsi.fastutil.shorts.Short2ShortMaps; + import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap; + import java.util.Arrays; + + public final class ShortList { + +- private final Short2ShortOpenHashMap map = new Short2ShortOpenHashMap(); ++ private final Short2ShortMap map = Short2ShortMaps.synchronize(new Short2ShortOpenHashMap()); // DivineMC - Optimize Moonrise + { + this.map.defaultReturnValue(Short.MIN_VALUE); + } +@@ -13,13 +15,13 @@ public final class ShortList { + private static final short[] EMPTY_LIST = new short[0]; + + private short[] byIndex = EMPTY_LIST; +- private short count; ++ private volatile short count; // DivineMC - Optimize Moonrise + + public int size() { +- return (int)this.count; ++ return this.count; // DivineMC - Optimize Moonrise + } + +- public short getRaw(final int index) { ++ public synchronized short getRaw(final int index) { // DivineMC - Optimize Moonrise + return this.byIndex[index]; + } + +@@ -30,8 +32,8 @@ public final class ShortList { + } + } + +- public boolean add(final short value) { +- final int count = (int)this.count; ++ public synchronized boolean add(final short value) { // DivineMC - Optimize Moonrise ++ final int count = this.count; // DivineMC - Optimize Moonrise + final short currIndex = this.map.putIfAbsent(value, (short)count); + + if (currIndex != Short.MIN_VALUE) { +@@ -51,7 +53,7 @@ public final class ShortList { + return true; + } + +- public boolean remove(final short value) { ++ public synchronized boolean remove(final short value) { // DivineMC - Optimize Moonrise + final short index = this.map.remove(value); + if (index == Short.MIN_VALUE) { + return false; +@@ -70,7 +72,7 @@ public final class ShortList { + return true; + } + +- public void clear() { ++ public synchronized void clear() { // DivineMC - Optimize Moonrise + this.count = (short)0; + this.map.clear(); + } +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed26WayDistancePropagator3D.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed26WayDistancePropagator3D.java +index 460e27ab0506c83a28934800ee74ee886d4b025e..59e80748de2b0b35ecf25739e1876ebcbce7eca5 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed26WayDistancePropagator3D.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed26WayDistancePropagator3D.java +@@ -15,7 +15,7 @@ public final class Delayed26WayDistancePropagator3D { + + // Generally updates to positions are made close to other updates, so we link to decrease cache misses when + // propagating updates +- protected final LongLinkedOpenHashSet updatedSources = new LongLinkedOpenHashSet(); ++ protected final it.unimi.dsi.fastutil.longs.LongSet updatedSources = it.unimi.dsi.fastutil.longs.LongSets.synchronize(new LongLinkedOpenHashSet()); // DivineMC - Optimize Moonrise + + @FunctionalInterface + public static interface LevelChangeCallback { +@@ -94,29 +94,29 @@ public final class Delayed26WayDistancePropagator3D { + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Optimize Moonrise ++ queue.queuedLevels.add(level); // DivineMC - Optimize Moonrise + + 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.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Optimize Moonrise ++ queue.queuedLevels.add(level); // DivineMC - Optimize Moonrise + + this.levelIncreaseWorkQueueBitset |= (1L << index); + } + + protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Optimize Moonrise ++ queue.queuedLevels.add(level); // DivineMC - Optimize Moonrise + + this.levelRemoveWorkQueueBitset |= (1L << level); + } + +- public boolean propagateUpdates() { ++ public synchronized boolean propagateUpdates() { // DivineMC - Optimize Moonrise + if (this.updatedSources.isEmpty()) { + return false; + } +@@ -157,15 +157,15 @@ public final class Delayed26WayDistancePropagator3D { + return ret; + } + +- protected void propagateIncreases() { ++ protected synchronized void propagateIncreases() { // DivineMC - Optimize Moonrise + for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset); + this.levelIncreaseWorkQueueBitset != 0L; + 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.removeFirstLong(); +- byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); // DivineMC - Optimize Moonrise ++ byte level = queue.queuedLevels.removeFirst(); // DivineMC - Optimize Moonrise + + final boolean neighbourCheck = level < 0; + +@@ -226,15 +226,15 @@ public final class Delayed26WayDistancePropagator3D { + } + } + +- protected void propagateDecreases() { ++ protected synchronized void propagateDecreases() { // DivineMC - Optimize Moonrise + for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset); + this.levelRemoveWorkQueueBitset != 0L; + 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.removeFirstLong(); +- final byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); // DivineMC - Optimize Moonrise ++ final byte level = queue.queuedLevels.removeFirst(); // DivineMC - Optimize Moonrise + + final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); + if (currentLevel == 0) { +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed8WayDistancePropagator2D.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed8WayDistancePropagator2D.java +index ab2fa1563d5e32a5313dfcc1da411cab45fb5ca0..f568fcf85667cd3fe53f59e08451096de5fa3492 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed8WayDistancePropagator2D.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed8WayDistancePropagator2D.java +@@ -356,24 +356,24 @@ public final class Delayed8WayDistancePropagator2D { + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { + final WorkQueue queue = this.levelIncreaseWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Optimize Moonrise ++ queue.queuedLevels.add(level); // DivineMC - Optimize Moonrise + + 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.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Optimize Moonrise ++ queue.queuedLevels.add(level); // DivineMC - Optimize Moonrise + + this.levelIncreaseWorkQueueBitset |= (1L << index); + } + + protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { + final WorkQueue queue = this.levelRemoveWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Optimize Moonrise ++ queue.queuedLevels.add(level); // DivineMC - Optimize Moonrise + + this.levelRemoveWorkQueueBitset |= (1L << level); + } +@@ -426,8 +426,8 @@ public final class Delayed8WayDistancePropagator2D { + + final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); // DivineMC - Optimize Moonrise ++ byte level = queue.queuedLevels.removeFirst(); // DivineMC - Optimize Moonrise + + final boolean neighbourCheck = level < 0; + +@@ -492,8 +492,8 @@ public final class Delayed8WayDistancePropagator2D { + + final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- final byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); // DivineMC - Optimize Moonrise ++ final byte level = queue.queuedLevels.removeFirst(); // DivineMC - Optimize Moonrise + + final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); + if (currentLevel == 0) { +@@ -561,7 +561,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- private int find(final long k) { ++ private synchronized int find(final long k) { // DivineMC - Optimize Moonrise + if (k == 0L) { + return this.containsNullKey ? this.n : -(this.n + 1); + } else { +@@ -585,7 +585,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- private void insert(final int pos, final long k, final byte v) { ++ private synchronized void insert(final int pos, final long k, final byte v) { // DivineMC - Optimize Moonrise + if (pos == this.n) { + this.containsNullKey = true; + } +@@ -598,7 +598,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- public byte putIfGreater(final long key, final byte value) { ++ public synchronized byte putIfGreater(final long key, final byte value) { // DivineMC - Optimize Moonrise + final int pos = this.find(key); + if (pos < 0) { + if (this.defRetValue < value) { +@@ -616,7 +616,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- private void removeEntry(final int pos) { ++ private synchronized void removeEntry(final int pos) { // DivineMC - Optimize Moonrise + --this.size; + this.shiftKeys(pos); + if (this.n > this.minN && this.size < this.maxFill / 4 && this.n > 16) { +@@ -625,7 +625,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- private void removeNullEntry() { ++ private synchronized void removeNullEntry() { // DivineMC - Optimize Moonrise + this.containsNullKey = false; + --this.size; + if (this.n > this.minN && this.size < this.maxFill / 4 && this.n > 16) { +@@ -634,7 +634,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- public byte removeIfGreaterOrEqual(final long key, final byte value) { ++ public synchronized byte removeIfGreaterOrEqual(final long key, final byte value) { // DivineMC - Optimize Moonrise + if (key == 0L) { + if (!this.containsNullKey) { + return this.defRetValue; +@@ -679,8 +679,8 @@ public final class Delayed8WayDistancePropagator2D { + + protected static final class WorkQueue { + +- public final NoResizeLongArrayFIFODeque queuedCoordinates = new NoResizeLongArrayFIFODeque(); +- public final NoResizeByteArrayFIFODeque queuedLevels = new NoResizeByteArrayFIFODeque(); ++ public final java.util.concurrent.ConcurrentLinkedDeque queuedCoordinates = new java.util.concurrent.ConcurrentLinkedDeque<>(); ++ public final java.util.concurrent.ConcurrentLinkedDeque queuedLevels = new java.util.concurrent.ConcurrentLinkedDeque<>(); + + } +