mirror of
https://github.com/BX-Team/DivineMC.git
synced 2025-12-19 14:59:25 +00:00
update rct patch
This commit is contained in:
@@ -5,7 +5,7 @@ Subject: [PATCH] Async Chunk Sending
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||
index 76b8d42ae530b59cdaba0583365a557da6b90ede..235772cc9a7c878235b97c8d84cacda3016f91ca 100644
|
||||
index 886825a10bd06b4b656d19a05624c74f2686feb3..1f3f15e3c3eb2c8e71caba069b457cec53f635f2 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||
@@ -55,6 +55,8 @@ public final class RegionizedPlayerChunkLoader {
|
||||
@@ -1,114 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
|
||||
Date: Mon, 24 Feb 2025 19:58:39 +0300
|
||||
Subject: [PATCH] Regionized Chunk Ticking
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index ae0e36d198ad8243920c8e8a55c0be4945542763..7f982949304535376dabf42aab1848cabc8987cf 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -54,6 +54,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final DistanceManager distanceManager;
|
||||
private final ServerLevel level;
|
||||
+ // DivineMC - Regionized Chunk Ticking
|
||||
+ public static final Executor REGION_EXECUTOR = java.util.concurrent.Executors.newFixedThreadPool(org.bxteam.divinemc.DivineConfig.regionizedChunkTickingExecutorThreadCount, new org.bxteam.divinemc.util.NamedAgnosticThreadFactory<>("region_ticking", ca.spottedleaf.moonrise.common.util.TickThread::new, org.bxteam.divinemc.DivineConfig.regionizedChunkTickingExecutorThreadPriority));
|
||||
+ public volatile int tickingRegionsCount = 0;
|
||||
+ // DivineMC end - Regionized Chunk Ticking
|
||||
public final Thread mainThread;
|
||||
final ThreadedLevelLightEngine lightEngine;
|
||||
public final ServerChunkCache.MainThreadExecutor mainThreadProcessor;
|
||||
@@ -461,6 +465,46 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(save, true); // Paper - rewrite chunk system
|
||||
}
|
||||
|
||||
+ // DivineMC start - Regionized Chunk Ticking
|
||||
+ private static final int[] DX = {1, -1, 0, 0, 1, -1, -1, 1};
|
||||
+ private static final int[] DZ = {0, 0, 1, -1, 1, 1, -1, -1};
|
||||
+
|
||||
+ private List<LevelChunk>[] splitChunksIntoRegions(List<LevelChunk> chunks) {
|
||||
+ int size = chunks.size();
|
||||
+ java.util.IdentityHashMap<LevelChunk, Boolean> chunkSet = new java.util.IdentityHashMap<>(size);
|
||||
+
|
||||
+ for (LevelChunk chunk : chunks) {
|
||||
+ chunkSet.put(chunk, Boolean.TRUE);
|
||||
+ }
|
||||
+
|
||||
+ List<List<LevelChunk>> groups = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(size >> 3);
|
||||
+ LevelChunk[] stack = new LevelChunk[size];
|
||||
+ int stackPointer = 0;
|
||||
+
|
||||
+ for (LevelChunk chunk : chunks) {
|
||||
+ if (chunkSet.remove(chunk) == null) continue;
|
||||
+
|
||||
+ List<LevelChunk> group = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(64);
|
||||
+ stack[stackPointer++] = chunk;
|
||||
+
|
||||
+ while (stackPointer > 0) {
|
||||
+ LevelChunk current = stack[--stackPointer];
|
||||
+ group.add(current);
|
||||
+
|
||||
+ for (int i = 0; i < 8; i++) {
|
||||
+ LevelChunk neighbor = getChunk(current.locX + DX[i], current.locZ + DZ[i], false);
|
||||
+ if (neighbor == null || chunkSet.remove(neighbor) == null) continue;
|
||||
+ stack[stackPointer++] = neighbor;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ groups.add(group);
|
||||
+ }
|
||||
+
|
||||
+ return groups.toArray(new List[0]);
|
||||
+ }
|
||||
+ // DivineMC end - Regionized Chunk Ticking
|
||||
+
|
||||
@Override
|
||||
public void tick(BooleanSupplier hasTimeLeft, boolean tickChunks) {
|
||||
if (this.level.tickRateManager().runsNormally() || !tickChunks || this.level.spigotConfig.unloadFrozenChunks) { // Spigot
|
||||
@@ -492,7 +536,44 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
this.shuffleRandom.setSeed(this.level.random.nextLong());
|
||||
if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
|
||||
// Paper end - chunk tick iteration optimisation
|
||||
- this.tickChunks(l, list);
|
||||
+ // DivineMC start - Regionized Chunk Ticking
|
||||
+ if (org.bxteam.divinemc.DivineConfig.enableRegionizedChunkTicking) {
|
||||
+ List<LevelChunk>[] regions = splitChunksIntoRegions(list);
|
||||
+ int regionCount = regions.length;
|
||||
+ this.tickingRegionsCount = regionCount;
|
||||
+ java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(regionCount);
|
||||
+
|
||||
+ try {
|
||||
+ java.util.concurrent.ForkJoinPool.managedBlock(new java.util.concurrent.ForkJoinPool.ManagedBlocker() {
|
||||
+ @Override
|
||||
+ public boolean block() throws InterruptedException {
|
||||
+ for (List<LevelChunk> region : regions) {
|
||||
+ if (region == null) continue;
|
||||
+ REGION_EXECUTOR.execute(() -> {
|
||||
+ try {
|
||||
+ tickChunks(l, region);
|
||||
+ } finally {
|
||||
+ latch.countDown();
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ latch.await();
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isReleasable() {
|
||||
+ return latch.getCount() == 0;
|
||||
+ }
|
||||
+ });
|
||||
+ } catch (InterruptedException ex) {
|
||||
+ throw new RuntimeException("Interrupted managed block during region ticking", ex);
|
||||
+ }
|
||||
+ } else {
|
||||
+ this.tickChunks(l, list);
|
||||
+ }
|
||||
+ // DivineMC end - Regionized Chunk Ticking
|
||||
} finally {
|
||||
list.clear();
|
||||
}
|
||||
@@ -23,7 +23,7 @@ index b402f6a6ecb8047bbb791b212fba375f4c9e6af5..f381f3ecd27315e06ca6883006a8a4c3
|
||||
player.awardStat(Stats.ITEM_USED.get(item));
|
||||
// level.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); // CraftBukkit
|
||||
diff --git a/net/minecraft/core/dispenser/DispenseItemBehavior.java b/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
index c8c8351f5645cf4041d26b0e02c072546ad329c6..75a695aaf0dd2f58e1fa5e1c532ae298c2a2abdb 100644
|
||||
index 075987ec1eabb7385918049c54d7210ff2b38847..d7488164313cbf038ff7669da275bfdb41e8343c 100644
|
||||
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
|
||||
@@ -727,13 +727,25 @@ public interface DispenseItemBehavior {
|
||||
@@ -5,7 +5,7 @@ Subject: [PATCH] Leaf: Improve BlockEntity ticking isRemoved check
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index 0337f4b9ca3c9c9a1e2a7cf19fcbad5e78b949dc..1820069a7c5833b0a13e034c232f06af234788e3 100644
|
||||
index 534384727e852dc8ea822f49f182af49eb3a40c1..d779ff76432e498123ed1d70946a9833c939b754 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -989,13 +989,26 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
@@ -22,10 +22,10 @@ Example config in paper-world-defaults.yml:
|
||||
```
|
||||
|
||||
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
||||
index 650dfce05bfc68d4c664471b430bd5c0f9629283..3e9ab446632ffe56de45f7622db44070e1cbaf1f 100644
|
||||
index d3f5242fc66529bf3137da4d505a6cf55e749e43..1056a17c53e7d16d5fba7f9a354dfbc235d4d974 100644
|
||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -175,29 +175,52 @@ public final class NaturalSpawner {
|
||||
@@ -164,29 +164,52 @@ public final class NaturalSpawner {
|
||||
// Copied from getFilteredSpawningCategories
|
||||
int limit = mobCategory.getMaxInstancesPerChunk();
|
||||
SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(mobCategory);
|
||||
@@ -91,7 +91,7 @@ index 650dfce05bfc68d4c664471b430bd5c0f9629283..3e9ab446632ffe56de45f7622db44070
|
||||
// Paper end - Optional per player mob spawns
|
||||
}
|
||||
}
|
||||
@@ -221,12 +244,21 @@ public final class NaturalSpawner {
|
||||
@@ -210,12 +233,21 @@ public final class NaturalSpawner {
|
||||
}
|
||||
public static void spawnCategoryForChunk(
|
||||
MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final Consumer<Entity> trackEntity
|
||||
@@ -114,7 +114,7 @@ index 650dfce05bfc68d4c664471b430bd5c0f9629283..3e9ab446632ffe56de45f7622db44070
|
||||
}
|
||||
|
||||
@VisibleForDebug
|
||||
@@ -246,16 +278,22 @@ public final class NaturalSpawner {
|
||||
@@ -235,16 +267,22 @@ public final class NaturalSpawner {
|
||||
}
|
||||
public static void spawnCategoryForPosition(
|
||||
MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer<Entity> trackEntity
|
||||
@@ -139,7 +139,7 @@ index 650dfce05bfc68d4c664471b430bd5c0f9629283..3e9ab446632ffe56de45f7622db44070
|
||||
for (int i1 = 0; i1 < 3; i1++) {
|
||||
int x = pos.getX();
|
||||
int z = pos.getZ();
|
||||
@@ -295,13 +333,13 @@ public final class NaturalSpawner {
|
||||
@@ -284,13 +322,13 @@ public final class NaturalSpawner {
|
||||
}
|
||||
// Paper end - per player mob count backoff
|
||||
if (doSpawning == PreSpawnStatus.ABORT) {
|
||||
@@ -155,7 +155,7 @@ index 650dfce05bfc68d4c664471b430bd5c0f9629283..3e9ab446632ffe56de45f7622db44070
|
||||
}
|
||||
|
||||
mobForSpawn.moveTo(d, y, d1, level.random.nextFloat() * 360.0F, 0.0F);
|
||||
@@ -324,7 +362,7 @@ public final class NaturalSpawner {
|
||||
@@ -313,7 +351,7 @@ public final class NaturalSpawner {
|
||||
}
|
||||
// CraftBukkit end
|
||||
if (i >= mobForSpawn.getMaxSpawnClusterSize() || i >= maxSpawns) { // Paper - Optional per player mob spawns
|
||||
@@ -164,7 +164,7 @@ index 650dfce05bfc68d4c664471b430bd5c0f9629283..3e9ab446632ffe56de45f7622db44070
|
||||
}
|
||||
|
||||
if (mobForSpawn.isMaxGroupSizeReached(i3)) {
|
||||
@@ -337,6 +375,8 @@ public final class NaturalSpawner {
|
||||
@@ -326,6 +364,8 @@ public final class NaturalSpawner {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ Subject: [PATCH] Optimize Raids
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 92e35158b68dcb8d1f34fb1b748c12d1d39468c7..c87d1f81bd1b4b9756bd2f4c1dbc58a2dc85c63b 100644
|
||||
index 18719b17cfe19b7f864adcec0842da9ab6ee6abb..9ed108791b6e6eb81dfd90b36f054566ce62022f 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -219,6 +219,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -0,0 +1,357 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
|
||||
Date: Mon, 24 Feb 2025 19:58:39 +0300
|
||||
Subject: [PATCH] Regionized Chunk Ticking
|
||||
|
||||
This patch adds regionized chunk ticking feature, by grouping adjacent chunks into regions and processing each region on its own thread.
|
||||
|
||||
Original idea by Dueris, modified by NONPLAYT and dan28000
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index ae0e36d198ad8243920c8e8a55c0be4945542763..aa515bc07b899351f2b0ac8d61df8e5586616084 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -54,6 +54,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final DistanceManager distanceManager;
|
||||
private final ServerLevel level;
|
||||
+ public static final Executor REGION_EXECUTOR = java.util.concurrent.Executors.newFixedThreadPool(org.bxteam.divinemc.DivineConfig.regionizedChunkTickingExecutorThreadCount, new org.bxteam.divinemc.util.NamedAgnosticThreadFactory<>("region_ticking", ca.spottedleaf.moonrise.common.util.TickThread::new, org.bxteam.divinemc.DivineConfig.regionizedChunkTickingExecutorThreadPriority)); // DivineMC - Regionized Chunk Ticking
|
||||
public final Thread mainThread;
|
||||
final ThreadedLevelLightEngine lightEngine;
|
||||
public final ServerChunkCache.MainThreadExecutor mainThreadProcessor;
|
||||
@@ -66,8 +67,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
private final long[] lastChunkPos = new long[4];
|
||||
private final ChunkStatus[] lastChunkStatus = new ChunkStatus[4];
|
||||
private final ChunkAccess[] lastChunk = new ChunkAccess[4];
|
||||
- private final List<LevelChunk> tickingChunks = new ArrayList<>();
|
||||
- private final Set<ChunkHolder> chunkHoldersToBroadcast = new ReferenceOpenHashSet<>();
|
||||
+ // DivineMC start - Regionized Chunk Ticking
|
||||
+ private final it.unimi.dsi.fastutil.objects.ObjectArrayList<LevelChunk> tickingChunks = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
|
||||
+ private final Set<ChunkHolder> chunkHoldersToBroadcast = java.util.Collections.synchronizedSet(new ReferenceOpenHashSet<>());
|
||||
+ // DivineMC end - Regionized Chunk Ticking
|
||||
@Nullable
|
||||
@VisibleForDebug
|
||||
private NaturalSpawner.SpawnState lastSpawnState;
|
||||
@@ -80,6 +83,92 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
// Paper end
|
||||
// Paper start - rewrite chunk system
|
||||
|
||||
+ // DivineMC start - Regionized Chunk Ticking
|
||||
+ private record RegionData(List<LevelChunk> chunks, List<Entity> entities) {
|
||||
+ public boolean isEmpty() {
|
||||
+ return chunks.isEmpty();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static final int[] DX = {1, -1, 0, 0, 1, -1, -1, 1};
|
||||
+ private static final int[] DZ = {0, 0, 1, -1, 1, 1, -1, -1};
|
||||
+
|
||||
+ private RegionData[] splitChunksIntoRegions(List<LevelChunk> chunks) {
|
||||
+ if (chunks.isEmpty()) return new RegionData[0];
|
||||
+
|
||||
+ int size = chunks.size();
|
||||
+ it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<LevelChunk> chunkSet = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(chunks);
|
||||
+
|
||||
+ it.unimi.dsi.fastutil.objects.ObjectArrayList<RegionData> groups = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(Math.max(1, level.players.size()));
|
||||
+ LevelChunk[] stack = new LevelChunk[size];
|
||||
+ it.unimi.dsi.fastutil.longs.Long2IntMap chunkToRegionMap = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(size);
|
||||
+
|
||||
+ for (LevelChunk chunk : chunks) {
|
||||
+ if (!chunkSet.contains(chunk)) continue;
|
||||
+
|
||||
+ it.unimi.dsi.fastutil.objects.ObjectArrayList<LevelChunk> group = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(64);
|
||||
+ stack[0] = chunk;
|
||||
+ int stackPointer = 1;
|
||||
+ chunkSet.remove(chunk);
|
||||
+
|
||||
+ while (stackPointer > 0) {
|
||||
+ LevelChunk current = stack[--stackPointer];
|
||||
+ group.add(current);
|
||||
+
|
||||
+ for (int i = 0; i < 8; i++) {
|
||||
+ LevelChunk neighbor = getChunk(current.locX + DX[i], current.locZ + DZ[i], false);
|
||||
+ if (neighbor != null && chunkSet.remove(neighbor)) {
|
||||
+ stack[stackPointer++] = neighbor;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ RegionData regionData = new RegionData(group, new it.unimi.dsi.fastutil.objects.ObjectArrayList<>());
|
||||
+ groups.add(regionData);
|
||||
+ int index = groups.indexOf(regionData);
|
||||
+
|
||||
+ for (LevelChunk regionChunk : group) {
|
||||
+ chunkToRegionMap.put(regionChunk.coordinateKey, index);
|
||||
+ }
|
||||
+
|
||||
+ if (chunkSet.isEmpty()) break;
|
||||
+ }
|
||||
+
|
||||
+ level.entityTickList.entities.forEach(entity -> {
|
||||
+ long chunkKey = entity.chunkPosition().longKey;
|
||||
+
|
||||
+ int index = chunkToRegionMap.get(chunkKey);
|
||||
+ RegionData regionData = groups.get(index);
|
||||
+ if (regionData != null) {
|
||||
+ regionData.entities().add(entity);
|
||||
+ } else {
|
||||
+ tickEntity(entity);
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ return groups.toArray(new RegionData[0]);
|
||||
+ }
|
||||
+
|
||||
+ private void tickEntity(Entity entity) {
|
||||
+ if (!entity.isRemoved()) {
|
||||
+ if (!level.tickRateManager().isEntityFrozen(entity)) {
|
||||
+ entity.checkDespawn();
|
||||
+ // Paper - rewrite chunk system
|
||||
+ Entity vehicle = entity.getVehicle();
|
||||
+ if (vehicle != null) {
|
||||
+ if (!vehicle.isRemoved() && vehicle.hasPassenger(entity)) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ entity.stopRiding();
|
||||
+ }
|
||||
+
|
||||
+ level.guardEntityTick(level::tickNonPassenger, entity);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // DivineMC end - Regionized Chunk Ticking
|
||||
+
|
||||
@Override
|
||||
public final void moonrise$setFullChunk(final int chunkX, final int chunkZ, final LevelChunk chunk) {
|
||||
final long key = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
||||
@@ -478,39 +567,106 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
+ // DivineMC start - Regionized Chunk Ticking
|
||||
private void tickChunks() {
|
||||
- long gameTime = this.level.getGameTime();
|
||||
- long l = gameTime - this.lastInhabitedUpdate;
|
||||
+ final long gameTime = this.level.getGameTime();
|
||||
+ final long l = gameTime - this.lastInhabitedUpdate;
|
||||
this.lastInhabitedUpdate = gameTime;
|
||||
- if (!this.level.isDebug()) {
|
||||
- if (this.level.tickRateManager().runsNormally()) {
|
||||
- List<LevelChunk> list = this.tickingChunks;
|
||||
-
|
||||
- try {
|
||||
- this.collectTickingChunks(list);
|
||||
- // Paper start - chunk tick iteration optimisation
|
||||
- this.shuffleRandom.setSeed(this.level.random.nextLong());
|
||||
- if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
|
||||
- // Paper end - chunk tick iteration optimisation
|
||||
- this.tickChunks(l, list);
|
||||
- } finally {
|
||||
- list.clear();
|
||||
- }
|
||||
- }
|
||||
|
||||
+ if (this.level.isDebug()) return;
|
||||
+
|
||||
+ if (!this.level.tickRateManager().runsNormally()) {
|
||||
this.broadcastChangedChunks();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ final it.unimi.dsi.fastutil.objects.ObjectArrayList<LevelChunk> list = this.tickingChunks;
|
||||
+
|
||||
+ try {
|
||||
+ this.collectTickingChunks(list);
|
||||
+
|
||||
+ if (list.isEmpty()) return;
|
||||
+
|
||||
+ // Paper start - chunk tick iteration optimisation
|
||||
+ this.shuffleRandom.setSeed(this.level.random.nextLong());
|
||||
+
|
||||
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
||||
+ Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
|
||||
+ }
|
||||
+ // Paper end - chunk tick iteration optimisation
|
||||
+
|
||||
+ if (org.bxteam.divinemc.DivineConfig.enableRegionizedChunkTicking) {
|
||||
+ this.processChunksRegionized(l, list);
|
||||
+ } else {
|
||||
+ this.tickChunks(l, list);
|
||||
+ this.broadcastChangedChunks();
|
||||
+ }
|
||||
+ } finally {
|
||||
+ list.clear();
|
||||
}
|
||||
}
|
||||
|
||||
+ private void processChunksRegionized(final long timeDelta, final List<LevelChunk> chunks) {
|
||||
+ final RegionData[] regions = splitChunksIntoRegions(chunks);
|
||||
+ final int regionCount = regions.length;
|
||||
+
|
||||
+ if (regionCount == 0) return;
|
||||
+
|
||||
+ java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(regionCount);
|
||||
+
|
||||
+ try {
|
||||
+ io.papermc.paper.entity.activation.ActivationRange.activateEntities(level);
|
||||
+ java.util.concurrent.ForkJoinPool.managedBlock(new java.util.concurrent.ForkJoinPool.ManagedBlocker() {
|
||||
+ @Override
|
||||
+ public boolean block() throws InterruptedException {
|
||||
+ for (final RegionData region : regions) {
|
||||
+ if (region == null || region.isEmpty()) {
|
||||
+ latch.countDown();
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ REGION_EXECUTOR.execute(() -> {
|
||||
+ try {
|
||||
+ tickChunks(timeDelta, region.chunks());
|
||||
+
|
||||
+ for (Entity entity : region.entities()) {
|
||||
+ tickEntity(entity);
|
||||
+ }
|
||||
+ } finally {
|
||||
+ latch.countDown();
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ broadcastChangedChunks();
|
||||
+
|
||||
+ latch.await();
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isReleasable() {
|
||||
+ return latch.getCount() == 0;
|
||||
+ }
|
||||
+ });
|
||||
+ } catch (InterruptedException ex) {
|
||||
+ throw new RuntimeException("Interrupted managed block during region ticking", ex);
|
||||
+ }
|
||||
+ }
|
||||
+ // DivineMC end - Regionized Chunk Ticking
|
||||
+
|
||||
private void broadcastChangedChunks() {
|
||||
- for (ChunkHolder chunkHolder : this.chunkHoldersToBroadcast) {
|
||||
- LevelChunk tickingChunk = chunkHolder.getChunkToSend(); // Paper - rewrite chunk system
|
||||
- if (tickingChunk != null) {
|
||||
- chunkHolder.broadcastChanges(tickingChunk);
|
||||
+ // DivineMC start - Regionized Chunk Ticking
|
||||
+ synchronized (chunkHoldersToBroadcast) {
|
||||
+ for (ChunkHolder chunkHolder : this.chunkHoldersToBroadcast) {
|
||||
+ LevelChunk tickingChunk = chunkHolder.getChunkToSend(); // Paper - rewrite chunk system
|
||||
+ if (tickingChunk != null) {
|
||||
+ chunkHolder.broadcastChanges(tickingChunk);
|
||||
+ }
|
||||
}
|
||||
- }
|
||||
|
||||
- this.chunkHoldersToBroadcast.clear();
|
||||
+ this.chunkHoldersToBroadcast.clear();
|
||||
+ }
|
||||
+ // DivineMC end - Regionized Chunk Ticking
|
||||
}
|
||||
|
||||
private void collectTickingChunks(List<LevelChunk> output) {
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 9ed108791b6e6eb81dfd90b36f054566ce62022f..a21924072632d8195d803b372058c2557d6428b2 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -193,7 +193,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
private final LevelTicks<Block> blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded);
|
||||
private final LevelTicks<Fluid> fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded);
|
||||
private final PathTypeCache pathTypesByPosCache = new PathTypeCache();
|
||||
- final Set<Mob> navigatingMobs = new ObjectOpenHashSet<>();
|
||||
+ final Set<Mob> navigatingMobs = java.util.Collections.synchronizedSet(new ObjectOpenHashSet<>()); // DivineMC - Regionized Chunk Ticking
|
||||
volatile boolean isUpdatingNavigations;
|
||||
protected final Raids raids;
|
||||
private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents = new ObjectLinkedOpenHashSet<>();
|
||||
@@ -816,6 +816,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
this.dragonFight.tick();
|
||||
}
|
||||
|
||||
+ if (org.bxteam.divinemc.DivineConfig.enableRegionizedChunkTicking) {
|
||||
+ this.tickBlockEntities();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
|
||||
this.entityTickList
|
||||
.forEach(
|
||||
@@ -1793,22 +1798,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
if (Shapes.joinIsNotEmpty(collisionShape, collisionShape1, BooleanOp.NOT_SAME)) {
|
||||
List<PathNavigation> list = new ObjectArrayList<>();
|
||||
|
||||
- try { // Paper - catch CME see below why
|
||||
- for (Mob mob : this.navigatingMobs) {
|
||||
- PathNavigation navigation = mob.getNavigation();
|
||||
- if (navigation.shouldRecomputePath(pos)) {
|
||||
- list.add(navigation);
|
||||
+ // DivineMC start - Regionized Chunk Ticking
|
||||
+ synchronized (this.navigatingMobs) {
|
||||
+ for (Mob mob : this.navigatingMobs) {
|
||||
+ PathNavigation navigation = mob.getNavigation();
|
||||
+ if (navigation.shouldRecomputePath(pos)) {
|
||||
+ list.add(navigation);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
- // Paper start - catch CME see below why
|
||||
- } catch (final java.util.ConcurrentModificationException concurrentModificationException) {
|
||||
- // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register
|
||||
- // In this case we just run the update again across all the iterators as the chunk will then be loaded
|
||||
- // As this is a relative edge case it is much faster than copying navigators (on either read or write)
|
||||
- this.sendBlockUpdated(pos, oldState, newState, flags);
|
||||
- return;
|
||||
- }
|
||||
- // Paper end - catch CME see below why
|
||||
+ // DivineMC end - Regionized Chunk Ticking
|
||||
|
||||
try {
|
||||
this.isUpdatingNavigations = true;
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 502473920a8dec1fcd3321d863f07c977b3643f2..f420cf116ca010f00a41df2f28fc8d9c658fc1f3 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -115,7 +115,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
public static final int MIN_ENTITY_SPAWN_Y = -20000000;
|
||||
public final org.bxteam.divinemc.util.BlockEntityTickersList blockEntityTickers = new org.bxteam.divinemc.util.BlockEntityTickersList(); // Paper - public // DivineMC - optimize block entity removals - Fix MC-117075
|
||||
protected final NeighborUpdater neighborUpdater;
|
||||
- private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.newArrayList();
|
||||
+ private final List<TickingBlockEntity> pendingBlockEntityTickers = java.util.Collections.synchronizedList(Lists.newArrayList()); // DivineMC - Regionized Chunk Ticking
|
||||
private boolean tickingBlockEntities;
|
||||
public final Thread thread;
|
||||
private final boolean isDebug;
|
||||
@@ -148,7 +148,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
public boolean captureBlockStates = false;
|
||||
public boolean captureTreeGeneration = false;
|
||||
public boolean isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
|
||||
- public Map<BlockPos, org.bukkit.craftbukkit.block.CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
|
||||
+ public Map<BlockPos, org.bukkit.craftbukkit.block.CraftBlockState> capturedBlockStates = java.util.Collections.synchronizedMap(new java.util.LinkedHashMap<>()); // Paper // DivineMC - Regionized Chunk Ticking
|
||||
public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
|
||||
public List<net.minecraft.world.entity.item.ItemEntity> captureDrops;
|
||||
public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<SpawnCategory> ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>();
|
||||
diff --git a/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
|
||||
index 028eae2f9a459b60e92f3344091083aa93b54485..51e5a54aff069cac14deef6c04899d3a469842ce 100644
|
||||
--- a/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
|
||||
+++ b/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
|
||||
@@ -46,7 +46,7 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
|
||||
this.addAndRun(pos, new CollectingNeighborUpdater.MultiNeighborUpdate(pos.immutable(), block, orientation, facing));
|
||||
}
|
||||
|
||||
- private void addAndRun(BlockPos pos, CollectingNeighborUpdater.NeighborUpdates updates) {
|
||||
+ private synchronized void addAndRun(BlockPos pos, CollectingNeighborUpdater.NeighborUpdates updates) { // DivineMC - Regionized Chunk Ticking - synchronized
|
||||
boolean flag = this.count > 0;
|
||||
boolean flag1 = this.maxChainedNeighborUpdates >= 0 && this.count >= this.maxChainedNeighborUpdates;
|
||||
this.count++;
|
||||
@@ -65,7 +65,7 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
- private void runUpdates() {
|
||||
+ private synchronized void runUpdates() { // DivineMC - Regionized Chunk Ticking - synchronized
|
||||
try {
|
||||
while (!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) {
|
||||
for (int i = this.addedThisLayer.size() - 1; i >= 0; i--) {
|
||||
@@ -17,10 +17,10 @@ index 51b79f614417f231951e9ba05b29ff0044e081e7..dee93ae262a2a06e68dfe8ae1b719317
|
||||
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
|
||||
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 7f982949304535376dabf42aab1848cabc8987cf..a2bb32b964d08079456d93d49f12b23f7c17a7db 100644
|
||||
index aa515bc07b899351f2b0ac8d61df8e5586616084..83157183ab2363eebd8a19d1e018b6bd5c736507 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -183,6 +183,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
@@ -268,6 +268,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
}
|
||||
// Paper end - chunk tick iteration optimisations
|
||||
|
||||
@@ -31,9 +31,9 @@ index 7f982949304535376dabf42aab1848cabc8987cf..a2bb32b964d08079456d93d49f12b23f
|
||||
|
||||
public ServerChunkCache(
|
||||
ServerLevel level,
|
||||
@@ -581,6 +585,35 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
|
||||
this.broadcastChangedChunks();
|
||||
@@ -651,6 +655,35 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException("Interrupted managed block during region ticking", ex);
|
||||
}
|
||||
+
|
||||
+ // DivineMC start - Async mob spawning
|
||||
@@ -65,9 +65,9 @@ index 7f982949304535376dabf42aab1848cabc8987cf..a2bb32b964d08079456d93d49f12b23f
|
||||
+ }
|
||||
+ // DivineMC end - Async mob spawning
|
||||
}
|
||||
// DivineMC end - Regionized Chunk Ticking
|
||||
|
||||
private void broadcastChangedChunks() {
|
||||
@@ -621,27 +654,31 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
@@ -696,27 +729,31 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
int naturalSpawnChunkCount = this.distanceManager.getNaturalSpawnChunkCount();
|
||||
// Paper start - Optional per player mob spawns
|
||||
NaturalSpawner.SpawnState spawnState;
|
||||
@@ -113,7 +113,7 @@ index 7f982949304535376dabf42aab1848cabc8987cf..a2bb32b964d08079456d93d49f12b23f
|
||||
boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
|
||||
int _int = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
|
||||
List<MobCategory> filteredSpawningCategories;
|
||||
@@ -655,7 +692,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
@@ -730,7 +767,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
}
|
||||
// Paper end - PlayerNaturallySpawnCreaturesEvent
|
||||
boolean flag = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
|
||||
@@ -122,7 +122,7 @@ index 7f982949304535376dabf42aab1848cabc8987cf..a2bb32b964d08079456d93d49f12b23f
|
||||
} else {
|
||||
filteredSpawningCategories = List.of();
|
||||
}
|
||||
@@ -663,8 +700,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
@@ -738,8 +775,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
for (LevelChunk levelChunk : chunks) {
|
||||
ChunkPos pos = levelChunk.getPos();
|
||||
levelChunk.incrementInhabitedTime(timeInhabited);
|
||||
@@ -136,7 +136,7 @@ index 7f982949304535376dabf42aab1848cabc8987cf..a2bb32b964d08079456d93d49f12b23f
|
||||
|
||||
if (true) { // Paper - rewrite chunk system
|
||||
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
||||
index d3f5242fc66529bf3137da4d505a6cf55e749e43..650dfce05bfc68d4c664471b430bd5c0f9629283 100644
|
||||
index 1056a17c53e7d16d5fba7f9a354dfbc235d4d974..3e9ab446632ffe56de45f7622db44070e1cbaf1f 100644
|
||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||
@@ -155,7 +155,18 @@ public final class NaturalSpawner {
|
||||
@@ -158,7 +158,7 @@ index d3f5242fc66529bf3137da4d505a6cf55e749e43..650dfce05bfc68d4c664471b430bd5c0
|
||||
for (MobCategory mobCategory : categories) {
|
||||
// Paper start - Optional per player mob spawns
|
||||
final boolean canSpawn;
|
||||
@@ -642,6 +653,13 @@ public final class NaturalSpawner {
|
||||
@@ -682,6 +693,13 @@ public final class NaturalSpawner {
|
||||
}
|
||||
|
||||
boolean canSpawnForCategoryLocal(MobCategory category, ChunkPos chunkPos) {
|
||||
@@ -30,11 +30,23 @@ index ae0a3c3d9d6300293a6d0dff5cae49ebe7c11dab..3b08dad7a9fac7ac9acec0bfb85d4826
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||
index 83157183ab2363eebd8a19d1e018b6bd5c736507..4377b6712c8990f9bd444d662414b68ab9d92963 100644
|
||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -150,6 +150,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||
}
|
||||
|
||||
private void tickEntity(Entity entity) {
|
||||
+ entity.activatedPriorityReset = false; // DivineMC - Dynamic Activation of Brain
|
||||
if (!entity.isRemoved()) {
|
||||
if (!level.tickRateManager().isEntityFrozen(entity)) {
|
||||
entity.checkDespawn();
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 561066a2cf769e13ef3cea0881f7a2010ecbf2ec..5fe908ce51f95e1eab024dcd41ed108373f17fea 100644
|
||||
index a21924072632d8195d803b372058c2557d6428b2..b50f8ff69157c07b509e88c65fb217d3dde73f1e 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -818,6 +818,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -825,6 +825,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
this.entityTickList
|
||||
.forEach(
|
||||
entity -> {
|
||||
@@ -43,7 +55,7 @@ index 561066a2cf769e13ef3cea0881f7a2010ecbf2ec..5fe908ce51f95e1eab024dcd41ed1083
|
||||
if (!tickRateManager.isEntityFrozen(entity)) {
|
||||
entity.checkDespawn();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 04ae7636d14a40a427b5d9b746632b0c489efa21..f1cd66d7d96771bc4967e214f70c756fec30efe5 100644
|
||||
index 07e8bda8eb200d5a7554e0319e1a00dc85454e1a..ed8c715f4a24188e43769fa04af8e71e1573c041 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -336,6 +336,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@@ -414,7 +426,7 @@ index 59a12809e5c7f5ee85ca1f587f6b77383a1ff062..9777f5e99909790b49b05ea64fe12ded
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
|
||||
index fec90e482c8935dfca609bbf90e67f86a1586221..2ac8a11e9afc6f776eba3dbe852d7b680ed21705 100644
|
||||
index 4ee1791f293ba3abdbfe69824b85566fd9a36586..2a4b5821d352f828e3c955a6ffa0bfdf6ff6911a 100644
|
||||
--- a/net/minecraft/world/entity/npc/Villager.java
|
||||
+++ b/net/minecraft/world/entity/npc/Villager.java
|
||||
@@ -179,6 +179,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
@@ -18,10 +18,10 @@ index 34682217252cb98a70511a8cb25f077ec9f872b8..eccd330a332a927354f47acd16295c23
|
||||
this.scheduleReadIO();
|
||||
return;
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index bea62fca118efdd6257188cd53c813dc5fd0c19c..adca106b1100b5c5236f40eb3795ac89c1fa0288 100644
|
||||
index b50f8ff69157c07b509e88c65fb217d3dde73f1e..9e0d482065bd6434128cd653c3580b633ed7202d 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1403,7 +1403,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -1408,7 +1408,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
|
||||
if (doFull) {
|
||||
|
||||
@@ -95,4 +95,11 @@ public final class BlockEntityTickersList extends ObjectArrayList<TickingBlockEn
|
||||
Arrays.fill(a, j, size, null);
|
||||
size = j;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, final Collection<? extends TickingBlockEntity> c) {
|
||||
synchronized (c) {
|
||||
return super.addAll(index, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user