9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-19 14:59:25 +00:00

RCT update (#37)

* Move regionized chunk ticking into it's own package

* minor fix

* rename config variable and move it
This commit is contained in:
dan28000
2025-10-28 00:29:04 +01:00
committed by GitHub
parent 93763445fd
commit 9218371c67
9 changed files with 412 additions and 275 deletions

View File

@@ -20,19 +20,33 @@ index 411e1284a208ca1a097cf6eaa92e1e0d2203d83d..3f60d1b0ac91cfd3418e791222cd7267
} catch (Exception var2) {
if (var2 instanceof ClosedChannelException) {
LOGGER.info("Connection closed during protocol change");
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 7ca147cf9da67c399806056e5092841f7ca32321..b77f702398d7c04258974cc7cd25ed044603b417 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -730,7 +730,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
void collectSpawningChunks(List<LevelChunk> output) {
final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.world.level.chunk.LevelChunk> tickingChunks = ((ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
- final LevelChunk[] raw = tickingChunks.getRawDataUnchecked();
+ final LevelChunk[] raw = tickingChunks.toArray(new LevelChunk[0]); //DivineMC Regionized Chunk Ticking sync fix
final int size = tickingChunks.size();
Objects.checkFromToIndex(0, size, raw.length);
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 2039e636b1a52aff5403621e7281d618e4b87864..45bf13dc23b886ea2d660c38c74becf0e3754963 100644
index 2039e636b1a52aff5403621e7281d618e4b87864..6c0398a8118463ef7c606229cc0078324ebba3f5 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -57,6 +57,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -56,7 +56,7 @@ import org.slf4j.Logger;
public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache { // Paper - rewrite chunk system
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.config.DivineConfig.AsyncCategory.regionizedChunkTickingExecutorThreadCount, new org.bxteam.divinemc.util.NamedAgnosticThreadFactory<>("Region Ticking", ca.spottedleaf.moonrise.common.util.TickThread::new, org.bxteam.divinemc.config.DivineConfig.AsyncCategory.regionizedChunkTickingExecutorThreadPriority)); // DivineMC - Regionized Chunk Ticking
- private final ServerLevel level;
+ protected final ServerLevel level; //DivineMC Regionized Chunk Ticking private -> protected
public final Thread mainThread;
final ThreadedLevelLightEngine lightEngine;
public final ServerChunkCache.MainThreadExecutor mainThreadProcessor;
@@ -70,8 +71,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -70,8 +70,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];
@@ -45,141 +59,15 @@ index 2039e636b1a52aff5403621e7281d618e4b87864..45bf13dc23b886ea2d660c38c74becf0
@Nullable
@VisibleForDebug
private NaturalSpawner.SpawnState lastSpawnState;
@@ -153,36 +156,253 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -153,36 +155,49 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
return load ? this.syncLoad(chunkX, chunkZ, toStatus) : null;
}
// Paper end - rewrite chunk system
+
+ // DivineMC start - Regionized Chunk Ticking
+ private record RegionData(it.unimi.dsi.fastutil.longs.LongOpenHashSet chunks, Set<Entity> entities) {
+ public boolean isEmpty() {
+ return chunks.isEmpty();
+ }
+ }
+
+ private record Output(RegionData[] regions, Set<Entity> entities) {}
+
+ private Output computePlayerRegionsParallel() {
+ int tickViewDistance = level.moonrise$getViewDistanceHolder().getViewDistances().tickViewDistance();
+ List<ServerPlayer> players = new java.util.ArrayList<>(level.players);
+ int max = maxChunksForViewDistance();
+
+ List<it.unimi.dsi.fastutil.longs.LongOpenHashSet> playerChunkSets = players.parallelStream()
+ .map(player -> {
+ ChunkPos playerChunk = player.chunkPosition();
+ int px = playerChunk.x;
+ int pz = playerChunk.z;
+ it.unimi.dsi.fastutil.longs.LongOpenHashSet chunkKeys = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(max);
+ for (int dx = -tickViewDistance; dx <= tickViewDistance; dx++) {
+ for (int dz = -tickViewDistance; dz <= tickViewDistance; dz++) {
+ long key = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(px + dx, pz + dz);
+ chunkKeys.add(key);
+ }
+ }
+ return chunkKeys;
+ }).toList();
+
+ List<it.unimi.dsi.fastutil.longs.LongOpenHashSet> mergedRegions = new java.util.ArrayList<>();
+ boolean[] merged = new boolean[playerChunkSets.size()];
+
+ for (int i = 0; i < playerChunkSets.size(); i++) {
+ if (merged[i]) continue;
+
+ it.unimi.dsi.fastutil.longs.LongOpenHashSet region = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(playerChunkSets.get(i));
+ merged[i] = true;
+
+ boolean madeChanges;
+ do {
+ madeChanges = false;
+ for (int j = i + 1; j < playerChunkSets.size(); j++) {
+ if (merged[j]) continue;
+
+ it.unimi.dsi.fastutil.longs.LongOpenHashSet set = playerChunkSets.get(j);
+
+ boolean hasIntersection = false;
+ it.unimi.dsi.fastutil.longs.LongIterator iter = set.iterator();
+ while (iter.hasNext()) {
+ if (region.contains(iter.nextLong())) {
+ hasIntersection = true;
+ break;
+ }
+ }
+
+ if (hasIntersection) {
+ region.addAll(set);
+ merged[j] = true;
+ madeChanges = true;
+ }
+ }
+ } while (madeChanges);
+
+ mergedRegions.add(region);
+ }
+
+ ObjectArrayList<RegionData> regions = new ObjectArrayList<>();
+ it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap chunkToRegion = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap(max * mergedRegions.size());
+ chunkToRegion.defaultReturnValue(-1);
+ for (int i = 0; i < mergedRegions.size(); i++) {
+ regions.add(new RegionData(mergedRegions.get(i), java.util.Collections.newSetFromMap(new java.util.concurrent.ConcurrentHashMap<>())));
+ for (long key : mergedRegions.get(i)) {
+ chunkToRegion.put(key, i);
+ }
+ }
+
+ final Set<Entity> firstTick = java.util.Collections.newSetFromMap(new java.util.concurrent.ConcurrentHashMap<>());
+
+ synchronized (level.entityTickList.entities) {
+ final ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = level.entityTickList.entities.iterator();
+ try {
+ while (iterator.hasNext()) {
+ Entity entity = iterator.next();
+ long chunkKey = entity.chunkPosition().longKey;
+ int regionIndex = chunkToRegion.get(chunkKey);
+ if (regionIndex != -1) {
+ regions.get(regionIndex).entities().add(entity);
+ } else {
+ firstTick.add(entity);
+ }
+ }
+ } finally {
+ iterator.finishedIterating();
+ }
+ }
+
+ return new Output(regions.toArray(new RegionData[0]), firstTick);
+ }
+
+ // Should be max safe estimate of ticking chunks in a region
+ private int maxChunksForViewDistance() {
+ ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistances distances = level.moonrise$getViewDistanceHolder().getViewDistances();
+ int diameter = 2 * distances.tickViewDistance() + 1;
+ return diameter * diameter;
+ }
+
+ 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
+
// Paper start - chunk tick iteration optimisations
private final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom shuffleRandom = new ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom(0L);
- private void iterateTickingChunksFaster() {
+ private void iterateTickingChunksFaster(final java.util.concurrent.CompletableFuture<Void> spawns) { // DivineMC - Regionized Chunk Ticking
+ private void iterateTickingChunksFaster(final CompletableFuture<Void> spawns) { // DivineMC - Regionized Chunk Ticking
final ServerLevel world = this.level;
final int randomTickSpeed = world.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
@@ -198,98 +86,17 @@ index 2039e636b1a52aff5403621e7281d618e4b87864..45bf13dc23b886ea2d660c38c74becf0
- java.util.Objects.checkFromToIndex(0, size, raw.length);
- for (int i = 0; i < size; ++i) {
- world.tickChunk(raw[i], randomTickSpeed);
+ // DivineMC start - Regionized Chunk Ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableRegionizedChunkTicking) {
+ final Output output = computePlayerRegionsParallel();
+ final RegionData[] regions = output.regions();
+ int regionCount = regions.length;
+
+ java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(regionCount);
+ io.papermc.paper.entity.activation.ActivationRange.activateEntities(level); // Paper - EAR
+
+ try {
+ java.util.concurrent.ForkJoinPool.managedBlock(new java.util.concurrent.ForkJoinPool.ManagedBlocker() {
+ @Override
+ public boolean block() throws InterruptedException {
+ ObjectArrayList<java.util.concurrent.CompletableFuture<it.unimi.dsi.fastutil.longs.LongOpenHashSet>> ticked = new ObjectArrayList<>(regionCount);
+ for (final RegionData region : regions) {
+ if (region == null || region.isEmpty()) {
+ latch.countDown();
+ continue;
+ }
+
+ ticked.add(java.util.concurrent.CompletableFuture.supplyAsync(() -> {
+ ObjectArrayList<LevelChunk> regionChunks = new ObjectArrayList<>(region.chunks().size());
+ it.unimi.dsi.fastutil.longs.LongOpenHashSet regionChunksIDs = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(region.chunks().size());
+ for (long key : region.chunks()) {
+ LevelChunk chunk = fullChunks.get(key);
+ if (chunk != null) {
+ regionChunks.add(chunk);
+ regionChunksIDs.add(key);
+ }
+ }
+
+ try {
+ for (LevelChunk chunk : regionChunks) {
+ world.tickChunk(chunk, randomTickSpeed);
+ }
+ for (net.minecraft.world.entity.Entity entity : region.entities()) {
+ tickEntity(entity);
+ }
+ } finally {
+ latch.countDown();
+ }
+ return regionChunksIDs;
+ }, REGION_EXECUTOR));
+ }
-
- // call mid-tick tasks for chunk system
- if ((i & 7) == 0) {
- // DivineMC start - Parallel world ticking
- if (!org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableParallelWorldTicking) {
- ((ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer) this.level.getServer()).moonrise$executeMidTickTasks();
- continue;
+ CompletableFuture.runAsync(() -> {
+ try {
+ CompletableFuture.allOf(ticked.toArray(new CompletableFuture[0])).join();
+ } catch (java.util.concurrent.CompletionException ex) {
+ LOGGER.error("Error during region chunk ticking", ex.getCause());
+ }
+
+ it.unimi.dsi.fastutil.longs.LongOpenHashSet tickedChunkKeys = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(raw.length);
+
+ for (CompletableFuture<it.unimi.dsi.fastutil.longs.LongOpenHashSet> future : ticked) {
+ if (!future.isCompletedExceptionally()) {
+ try {
+ it.unimi.dsi.fastutil.longs.LongOpenHashSet regionChunks = future.join();
+ tickedChunkKeys.addAll(regionChunks);
+ } catch (Exception e) {
+ LOGGER.error("Exception in region ticking future", e);
+ }
+ }
+ }
+
+ for (LevelChunk chunk : raw) {
+ if (!tickedChunkKeys.contains(chunk.coordinateKey)) {
+ world.tickChunk(chunk, randomTickSpeed);
+ }
+ }
+
+ output.entities.forEach(ServerChunkCache.this::tickEntity);
+ spawns.join();
+ }, REGION_EXECUTOR).join();
+
+ 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 start - Regionized Chunk Ticking
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableRegionizedChunkTicking) {
+ if (this instanceof org.bxteam.divinemc.async.rct.RegionizedChunkTicking rct) {
+ rct.execute(spawns, raw);
+ }
+ } else {
+ java.util.Objects.checkFromToIndex(0, size, raw.length);
@@ -306,13 +113,17 @@ index 2039e636b1a52aff5403621e7281d618e4b87864..45bf13dc23b886ea2d660c38c74becf0
+ // DivineMC end - Parallel world ticking
}
- // DivineMC end - Parallel world ticking
+ }
+
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncNaturalSpawn) {
+ spawns.join();
}
}
+ // DivineMC end - Regionized Chunk Ticking
}
// Paper end - chunk tick iteration optimisations
@@ -507,14 +727,21 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -507,14 +522,21 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
long gameTime = this.level.getGameTime();
long l = gameTime - this.lastInhabitedUpdate;
this.lastInhabitedUpdate = gameTime;
@@ -338,7 +149,7 @@ index 2039e636b1a52aff5403621e7281d618e4b87864..45bf13dc23b886ea2d660c38c74becf0
// DivineMC start - Pufferfish: Optimize mob spawning
if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableAsyncSpawning) {
for (ServerPlayer player : this.level.players) {
@@ -558,14 +785,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -558,14 +580,18 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
}
private void broadcastChangedChunks() {
@@ -363,51 +174,59 @@ index 2039e636b1a52aff5403621e7281d618e4b87864..45bf13dc23b886ea2d660c38c74becf0
}
private void tickChunks(long timeInhabited) {
@@ -615,23 +846,28 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -615,13 +641,32 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
filteredSpawningCategories = List.of();
}
- List<LevelChunk> list = this.spawningChunks;
+ // DivineMC start - Regionized Chunk Ticking
+ final java.util.concurrent.CompletableFuture<Void> spawns = java.util.concurrent.CompletableFuture.runAsync(() -> {
+ List<LevelChunk> list = this.spawningChunks;
+ final CompletableFuture<Void> spawns;
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.asyncNaturalSpawn) {
+ spawns = CompletableFuture.runAsync(() -> naturalSpawn(filteredSpawningCategories, timeInhabited), org.bxteam.divinemc.async.rct.RegionizedChunkTicking.REGION_EXECUTOR);
+ } else {
+ naturalSpawn(filteredSpawningCategories, timeInhabited);
+ spawns = new CompletableFuture<>();
+ }
+ // DivineMC end - Regionized Chunk Ticking
+
+ this.iterateTickingChunksFaster(spawns); // Paper - chunk tick iteration optimisations // DivineMC - Regionized Chunk Ticking
+ if (_boolean) {
+ this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
+ }
+ }
+
+ // DivineMC start - Regionized Chunk Ticking
+ private void naturalSpawn(List<MobCategory> filteredSpawningCategories, long timeInhabited) {
List<LevelChunk> list = this.spawningChunks;
- try {
- this.chunkMap.collectSpawningChunks(list);
- // Paper start - chunk tick iteration optimisation
- this.shuffleRandom.setSeed(this.level.random.nextLong());
try {
this.chunkMap.collectSpawningChunks(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
-
- for (LevelChunk levelChunk : list) {
- this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, lastSpawnState); // DivineMC - Pufferfish: Optimize mob spawning
+ try {
+ this.chunkMap.collectSpawningChunks(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
+
+ for (LevelChunk levelChunk : list) {
+ this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, lastSpawnState); // DivineMC - Pufferfish: Optimize mob spawning
+ }
+ } finally {
+ list.clear();
// Paper end - chunk tick iteration optimisation
for (LevelChunk levelChunk : list) {
@@ -630,12 +675,12 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
} finally {
list.clear();
}
- } finally {
- list.clear();
- }
+ }, REGION_EXECUTOR);
+ // DivineMC end - Regionized Chunk Ticking
+ }
- this.iterateTickingChunksFaster(); // Paper - chunk tick iteration optimisations
+ this.iterateTickingChunksFaster(spawns); // Paper - chunk tick iteration optimisations // DivineMC - Regionized Chunk Ticking
if (_boolean) {
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
- if (_boolean) {
- this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
- }
+ protected net.minecraft.world.level.entity.EntityTickList getEntityTickList() {
+ return level.entityTickList;
}
+ // DivineMC end - Regionized Chunk Ticking
private void tickSpawningChunk(LevelChunk chunk, long timeInhabited, List<MobCategory> spawnCategories, NaturalSpawner.SpawnState spawnState) {
ChunkPos pos = chunk.getPos();
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index f9091b2daf735fd0788f8d6d60e3c812fd6dd4f2..0ad18866c323308ad9b87322932e03a283f740b1 100644
index f9091b2daf735fd0788f8d6d60e3c812fd6dd4f2..6c59d11107958f835ebe09317ed0d129f64d4583 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -191,7 +191,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -419,7 +238,59 @@ index f9091b2daf735fd0788f8d6d60e3c812fd6dd4f2..0ad18866c323308ad9b87322932e03a2
volatile boolean isUpdatingNavigations;
protected final Raids raids;
private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents = new ObjectLinkedOpenHashSet<>();
@@ -806,6 +806,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -640,20 +640,37 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
boolean flag = server.forceSynchronousWrites();
DataFixer fixerUpper = server.getFixerUpper();
// Paper - rewrite chunk system
- this.chunkSource = new ServerChunkCache(
- this,
- levelStorageAccess,
- fixerUpper,
- server.getStructureManager(),
- dispatcher,
- chunkGenerator,
- this.spigotConfig.viewDistance, // Spigot
- this.spigotConfig.simulationDistance, // Spigot
- flag,
- progressListener,
- null, // Paper - rewrite chunk system
- () -> server.overworld().getDataStorage()
- );
+ if (org.bxteam.divinemc.config.DivineConfig.AsyncCategory.enableRegionizedChunkTicking) {
+ this.chunkSource = new org.bxteam.divinemc.async.rct.RegionizedChunkTicking(this,
+ levelStorageAccess,
+ fixerUpper,
+ server.getStructureManager(),
+ dispatcher,
+ chunkGenerator,
+ this.spigotConfig.viewDistance, // Spigot
+ this.spigotConfig.simulationDistance, // Spigot
+ flag,
+ progressListener,
+ null, // Paper - rewrite chunk system
+ () -> server.overworld().getDataStorage()
+ );
+ } else {
+ this.chunkSource = new ServerChunkCache(
+ this,
+ levelStorageAccess,
+ fixerUpper,
+ server.getStructureManager(),
+ dispatcher,
+ chunkGenerator,
+ this.spigotConfig.viewDistance, // Spigot
+ this.spigotConfig.simulationDistance, // Spigot
+ flag,
+ progressListener,
+ null, // Paper - rewrite chunk system
+ () -> server.overworld().getDataStorage()
+ );
+ }
+
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
this.portalForcer = new PortalForcer(this);
this.updateSkyBrightness();
@@ -806,6 +823,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.dragonFight.tick();
}
@@ -433,7 +304,7 @@ index f9091b2daf735fd0788f8d6d60e3c812fd6dd4f2..0ad18866c323308ad9b87322932e03a2
io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
this.entityTickList
.forEach(
@@ -1828,22 +1835,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1828,22 +1852,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
if (Shapes.joinIsNotEmpty(collisionShape, collisionShape1, BooleanOp.NOT_SAME)) {
List<PathNavigation> list = new ObjectArrayList<>();

View File

@@ -73,10 +73,10 @@ index aa4dd7517e8be167aef1eaf7aa907e3ce7cc0e62..e3d3b062e273fee4a9d3ba3cadc21278
- // Paper end - detailed watchdog information
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 0ad18866c323308ad9b87322932e03a283f740b1..386fdc23b35675a7db66d16bf2a8a6dd5b44059a 100644
index 6c59d11107958f835ebe09317ed0d129f64d4583..ab14baf5a8a0fd7090702390197366b5a118cc6f 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -1349,13 +1349,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1366,13 +1366,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Paper end - log detailed entity tick information
public void tickNonPassenger(Entity entity) {
@@ -90,7 +90,7 @@ index 0ad18866c323308ad9b87322932e03a283f740b1..386fdc23b35675a7db66d16bf2a8a6dd
entity.setOldPosAndRot();
entity.tickCount++;
entity.totalEntityAge++; // Paper - age-like counter for all entities
@@ -1368,13 +1362,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -1385,13 +1379,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
for (Entity entity1 : entity.getPassengers()) {
this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2
}
@@ -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 fa48496222ea922204163d48988246c44e09851f..2c7c5dab268625e1328f57ac3ec2a735a82fea42 100644
index a5f6b4827019d2041d2b4bad5eab34e1bb1cbe23..d82ed6dcd49b4f0bd040d2bd3a6f0f54cd87758f 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

View File

@@ -5,10 +5,10 @@ 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
index ab14baf5a8a0fd7090702390197366b5a118cc6f..6e6f44d60b36d935cf004b856b6c1d6d1633f406 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
@@ -925,9 +925,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());
@@ -20,7 +20,7 @@ index 386fdc23b35675a7db66d16bf2a8a6dd5b44059a..4934ce03ac533d9c60674632cdac6621
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
@@ -936,42 +937,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++) {

View File

@@ -465,10 +465,10 @@ index 50bc5b940812432bc472e5b272582efb8bbfc7a7..0bece4ed69b332174cbe37f82df1f7da
}
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
index 45bf13dc23b886ea2d660c38c74becf0e3754963..b05fb5946564264771f998cff418513916eb6adb 100644
index df84bd2a2e306776ba92da74168b7c6c06c94beb..2da02f04606be99bfb691207886b623c1a35af7d 100644
--- a/net/minecraft/server/level/ServerChunkCache.java
+++ b/net/minecraft/server/level/ServerChunkCache.java
@@ -672,8 +672,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -467,8 +467,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
public boolean isPositionTicking(long chunkPos) {
// Paper start - rewrite chunk system
@@ -479,7 +479,7 @@ index 45bf13dc23b886ea2d660c38c74becf0e3754963..b05fb5946564264771f998cff4185139
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 4934ce03ac533d9c60674632cdac6621e62f6b44..b50afea7c2e4c61a3df196e74afd8f82b30aca8a 100644
index 6e6f44d60b36d935cf004b856b6c1d6d1633f406..9387bf252af4dfe0eddd3ab9ce6fe7eb83b7e4ad 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
@@ -490,7 +490,7 @@ index 4934ce03ac533d9c60674632cdac6621e62f6b44..b50afea7c2e4c61a3df196e74afd8f82
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
@@ -708,6 +709,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);
@@ -498,7 +498,7 @@ index 4934ce03ac533d9c60674632cdac6621e62f6b44..b50afea7c2e4c61a3df196e74afd8f82
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
@@ -863,8 +865,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@Override
public boolean shouldTickBlocksAt(long chunkPos) {
// Paper start - rewrite chunk system
@@ -508,7 +508,7 @@ index 4934ce03ac533d9c60674632cdac6621e62f6b44..b50afea7c2e4c61a3df196e74afd8f82
// Paper end - rewrite chunk system
}
@@ -2584,16 +2585,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2601,16 +2602,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
public boolean isPositionTickingWithEntitiesLoaded(long chunkPos) {
// Paper start - rewrite chunk system

View File

@@ -71,10 +71,10 @@ index 3092454bf7071deca75fecfc203072593fe5c7e7..098dd4647ae1500195729d6531e90c2b
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index b50afea7c2e4c61a3df196e74afd8f82b30aca8a..dc5889c97b4aa1fe9be83b1c10c7c855e5a96d45 100644
index 9387bf252af4dfe0eddd3ab9ce6fe7eb83b7e4ad..d5a0c2d6344de8bd57d56cff0e264df804772e84 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2438,6 +2438,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2455,6 +2455,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
for (TickingBlockEntity tickingBlockEntity : this.blockEntityTickers) {
BlockPos pos = tickingBlockEntity.getPos();

View File

@@ -0,0 +1,11 @@
package org.bxteam.divinemc.async.rct;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.world.entity.Entity;
import java.util.Set;
record RegionData(LongOpenHashSet chunks, Set<Entity> entities) {
public boolean isEmpty() {
return chunks.isEmpty();
}
}

View File

@@ -0,0 +1,245 @@
package org.bxteam.divinemc.async.rct;
import ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistances;
import com.mojang.datafixers.DataFixer;
import com.mojang.logging.LogUtils;
import io.papermc.paper.entity.activation.ActivationRange;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.entity.ChunkStatusUpdateListener;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.bxteam.divinemc.config.DivineConfig;
import org.bxteam.divinemc.util.NamedAgnosticThreadFactory;
import org.slf4j.Logger;
public final class RegionizedChunkTicking extends ServerChunkCache {
private static final Logger LOGGER = LogUtils.getLogger();
public static final Executor REGION_EXECUTOR = Executors.newFixedThreadPool(DivineConfig.AsyncCategory.regionizedChunkTickingExecutorThreadCount,
new NamedAgnosticThreadFactory<>("Region Ticking", TickThread::new, DivineConfig.AsyncCategory.regionizedChunkTickingExecutorThreadPriority)
);
public RegionizedChunkTicking(final ServerLevel level, final LevelStorageSource.LevelStorageAccess levelStorageAccess, final DataFixer fixerUpper, final StructureTemplateManager structureManager, final Executor dispatcher, final ChunkGenerator generator, final int viewDistance, final int simulationDistance, final boolean sync, final ChunkProgressListener progressListener, final ChunkStatusUpdateListener chunkStatusListener, final Supplier<DimensionDataStorage> overworldDataStorage) {
super(level, levelStorageAccess, fixerUpper, structureManager, dispatcher, generator, viewDistance, simulationDistance, sync, progressListener, chunkStatusListener, overworldDataStorage);
}
public void execute(CompletableFuture<Void> spawns, final LevelChunk[] raw) {
final TickPair tickPair = computePlayerRegionsParallel();
final RegionData[] regions = tickPair.regions();
final int regionCount = regions.length;
ActivationRange.activateEntities(level); // Paper - EAR
final int randomTickSpeed = level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
ObjectArrayList<CompletableFuture<LongOpenHashSet>> ticked = new ObjectArrayList<>(regionCount);
for (final RegionData region : regions) {
if (region == null || region.isEmpty()) {
continue;
}
ticked.add(tick(region, randomTickSpeed));
}
CompletableFuture.runAsync(() -> {
finishTicking(ticked, randomTickSpeed, raw, tickPair);
spawns.join();
}, REGION_EXECUTOR).join();
}
private CompletableFuture<LongOpenHashSet> tick(RegionData region, int randomTickSpeed) {
return CompletableFuture.supplyAsync(() -> {
ObjectArrayList<LevelChunk> regionChunks = new ObjectArrayList<>(region.chunks().size());
LongOpenHashSet regionChunksIDs = new LongOpenHashSet(region.chunks().size());
for (long key : region.chunks()) {
LevelChunk chunk = fullChunks.get(key);
if (chunk != null) {
regionChunks.add(chunk);
regionChunksIDs.add(key);
}
}
for (LevelChunk chunk : regionChunks) {
level.tickChunk(chunk, randomTickSpeed);
}
for (Entity entity : region.entities()) {
tickEntity(entity);
}
return regionChunksIDs;
}, REGION_EXECUTOR);
}
private void finishTicking(final ObjectArrayList<CompletableFuture<LongOpenHashSet>> ticked, final int randomTickSpeed, final LevelChunk[] raw, final TickPair tickPair) {
try {
CompletableFuture.allOf(ticked.toArray(new CompletableFuture[0])).join();
} catch (CompletionException ex) {
LOGGER.error("Error during region chunk ticking", ex.getCause());
}
LongOpenHashSet tickedChunkKeys = new LongOpenHashSet(raw.length);
for (CompletableFuture<LongOpenHashSet> future : ticked) {
if (!future.isCompletedExceptionally()) {
try {
LongOpenHashSet regionChunks = future.join();
tickedChunkKeys.addAll(regionChunks);
} catch (Exception e) {
LOGGER.error("Exception in region ticking future", e);
}
}
}
for (LevelChunk chunk : raw) {
if (!tickedChunkKeys.contains(chunk.coordinateKey)) {
level.tickChunk(chunk, randomTickSpeed);
}
}
for (Entity entity : tickPair.entities()) {
tickEntity(entity);
}
}
private TickPair computePlayerRegionsParallel() {
int tickViewDistance = level.moonrise$getViewDistanceHolder().getViewDistances().tickViewDistance();
List<ServerPlayer> players = new ArrayList<>(level.players());
int max = maxChunksForViewDistance();
List<LongOpenHashSet> playerChunkSets = players.parallelStream()
.map(player -> {
ChunkPos playerChunk = player.chunkPosition();
int px = playerChunk.x;
int pz = playerChunk.z;
LongOpenHashSet chunkKeys = new LongOpenHashSet(max);
for (int dx = -tickViewDistance; dx <= tickViewDistance; dx++) {
for (int dz = -tickViewDistance; dz <= tickViewDistance; dz++) {
long key = CoordinateUtils.getChunkKey(px + dx, pz + dz);
chunkKeys.add(key);
}
}
return chunkKeys;
}).toList();
List<LongOpenHashSet> mergedRegions = new ArrayList<>();
boolean[] merged = new boolean[playerChunkSets.size()];
for (int i = 0; i < playerChunkSets.size(); i++) {
if (merged[i]) continue;
LongOpenHashSet region = new LongOpenHashSet(playerChunkSets.get(i));
merged[i] = true;
boolean madeChanges;
do {
madeChanges = false;
for (int j = i + 1; j < playerChunkSets.size(); j++) {
if (merged[j]) continue;
LongOpenHashSet set = playerChunkSets.get(j);
boolean hasIntersection = false;
LongIterator iter = set.iterator();
while (iter.hasNext()) {
if (region.contains(iter.nextLong())) {
hasIntersection = true;
break;
}
}
if (hasIntersection) {
region.addAll(set);
merged[j] = true;
madeChanges = true;
}
}
} while (madeChanges);
mergedRegions.add(region);
}
ObjectArrayList<RegionData> regions = new ObjectArrayList<>();
Long2IntOpenHashMap chunkToRegion = new Long2IntOpenHashMap(max * mergedRegions.size());
chunkToRegion.defaultReturnValue(-1);
for (int i = 0; i < mergedRegions.size(); i++) {
regions.add(new RegionData(mergedRegions.get(i), new ObjectOpenHashSet<>()));
for (long key : mergedRegions.get(i)) {
chunkToRegion.put(key, i);
}
}
final Set<Entity> firstTick = new ObjectOpenHashSet<>();
synchronized (getEntityTickList().entities) {
final IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = getEntityTickList().entities.iterator();
try {
while (iterator.hasNext()) {
Entity entity = iterator.next();
long chunkKey = entity.chunkPosition().longKey;
int regionIndex = chunkToRegion.get(chunkKey);
if (regionIndex != -1) {
regions.get(regionIndex).entities().add(entity);
} else {
firstTick.add(entity);
}
}
} finally {
iterator.finishedIterating();
}
}
return new TickPair(regions.toArray(new RegionData[0]), firstTick);
}
// Should be max safe estimate of ticking chunks in a region
private int maxChunksForViewDistance() {
ViewDistances distances = level.moonrise$getViewDistanceHolder().getViewDistances();
int diameter = 2 * distances.tickViewDistance() + 1;
return diameter * diameter;
}
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);
}
}
}
}

View File

@@ -0,0 +1,7 @@
package org.bxteam.divinemc.async.rct;
import net.minecraft.world.entity.Entity;
import java.util.Set;
record TickPair(RegionData[] regions, Set<Entity> entities) {
}

View File

@@ -227,6 +227,7 @@ public class DivineConfig {
// Async mob spawning settings
public static boolean enableAsyncSpawning = true;
public static boolean asyncNaturalSpawn = true;
public static void load() {
parallelWorldTicking();
@@ -354,6 +355,8 @@ public class DivineConfig {
private static void asyncMobSpawning() {
enableAsyncSpawning = getBoolean(ConfigCategory.ASYNC.key("mob-spawning.enable"), enableAsyncSpawning,
"Enables optimization that will offload much of the computational effort involved with spawning new mobs to a different thread.");
asyncNaturalSpawn = getBoolean(ConfigCategory.ASYNC.key("async-ticking-of-natural-spawns"), asyncNaturalSpawn,
"Enables offloading of natural spawning to a different thread");
}
}