mirror of
https://github.com/BX-Team/DivineMC.git
synced 2025-12-21 15:59:23 +00:00
rewrite chunk system
This commit is contained in:
@@ -6,6 +6,17 @@ private-f net.minecraft.world.level.levelgen.NoiseChunk$FlatCache noiseFiller
|
||||
private-f net.minecraft.world.level.levelgen.NoiseChunk$NoiseInterpolator noiseFiller
|
||||
private-f net.minecraft.world.level.levelgen.RandomState router
|
||||
private-f net.minecraft.world.level.levelgen.RandomState sampler
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.executor.RadiusAwarePrioritisedExecutor$Task
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.executor.RadiusAwarePrioritisedExecutor$Task chunkX
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.executor.RadiusAwarePrioritisedExecutor$Task chunkZ
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkProgressionTask chunkX
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkProgressionTask chunkZ
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkProgressionTask world
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask chunkX
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask chunkZ
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask world
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask$ProcessOffMainTask
|
||||
public ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask$ProcessOnMainTask
|
||||
public net.minecraft.util.Mth SIN
|
||||
public net.minecraft.world.entity.ai.Brain sensors
|
||||
public net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities lineOfSightTest
|
||||
|
||||
@@ -538,8 +538,66 @@ index 0c99bffa769d53562a10d23c4a9b37dc59c7f478..f4fcc3b2676b17ebc276dcb177285f18
|
||||
final ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket<?>>> coordinateTickets = iterator.next();
|
||||
final long coordinate = coordinateTickets.getKey();
|
||||
final SortedArraySet<Ticket<?>> tickets = coordinateTickets.getValue();
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java
|
||||
index 67532b85073b7978254a0b04caadfe822679e61f..4b97b676d4245e7ece956eb4c78bed96ff452b2d 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java
|
||||
@@ -65,14 +65,6 @@ public final class ChunkTaskScheduler {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
|
||||
- public static void init(final boolean useParallelGen) {
|
||||
- for (final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor executor : MoonriseCommon.RADIUS_AWARE_GROUP.getAllExecutors()) {
|
||||
- executor.setMaxParallelism(useParallelGen ? -1 : 1);
|
||||
- }
|
||||
-
|
||||
- LOGGER.info("Chunk system is using population gen parallelism: " + useParallelGen);
|
||||
- }
|
||||
-
|
||||
public static final TicketType<Long> CHUNK_LOAD = TicketType.create("chunk_system:chunk_load", Long::compareTo);
|
||||
private static final AtomicLong CHUNK_LOAD_IDS = new AtomicLong();
|
||||
|
||||
@@ -115,12 +107,12 @@ public final class ChunkTaskScheduler {
|
||||
|
||||
public final ServerLevel world;
|
||||
public final RadiusAwarePrioritisedExecutor radiusAwareScheduler;
|
||||
- public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor parallelGenExecutor;
|
||||
- private final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor radiusAwareGenExecutor;
|
||||
- public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor loadExecutor;
|
||||
+ public final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor parallelGenExecutor;
|
||||
+ private final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor radiusAwareGenExecutor;
|
||||
+ public final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor loadExecutor;
|
||||
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor ioExecutor;
|
||||
- public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor compressionExecutor;
|
||||
- public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor saveExecutor;
|
||||
+ public final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor compressionExecutor;
|
||||
+ public final org.bxteam.divinemc.server.chunk.TheChunkSystem.ExecutorGroup.ThreadPoolExecutor saveExecutor;
|
||||
|
||||
private final PrioritisedTaskQueue mainThreadExecutor = new PrioritisedTaskQueue();
|
||||
|
||||
@@ -291,14 +283,14 @@ public final class ChunkTaskScheduler {
|
||||
this.lockShift = Math.max(((ChunkSystemServerLevel)world).moonrise$getRegionChunkShift(), ThreadedTicketLevelPropagator.SECTION_SHIFT);
|
||||
this.schedulingLockArea = new ReentrantAreaLock(this.getChunkSystemLockShift());
|
||||
|
||||
- this.parallelGenExecutor = MoonriseCommon.PARALLEL_GEN_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
||||
- this.radiusAwareGenExecutor = MoonriseCommon.RADIUS_AWARE_GROUP.createExecutor(1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
||||
- this.loadExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
||||
- this.radiusAwareScheduler = new RadiusAwarePrioritisedExecutor(this.radiusAwareGenExecutor, 16);
|
||||
+ this.parallelGenExecutor = MoonriseCommon.PARALLEL_GEN_GROUP.createExecutor();
|
||||
+ this.radiusAwareGenExecutor = MoonriseCommon.RADIUS_AWARE_GROUP.createExecutor();
|
||||
+ this.loadExecutor = MoonriseCommon.LOAD_GROUP.createExecutor();
|
||||
+ this.radiusAwareScheduler = new RadiusAwarePrioritisedExecutor(this.radiusAwareGenExecutor, 10_000);
|
||||
this.ioExecutor = MoonriseCommon.SERVER_REGION_IO_GROUP.createExecutor(-1, MoonriseCommon.IO_QUEUE_HOLD_TIME, 0);
|
||||
// we need a separate executor here so that on shutdown we can continue to process I/O tasks
|
||||
- this.compressionExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
||||
- this.saveExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
||||
+ this.compressionExecutor = MoonriseCommon.LOAD_GROUP.createExecutor();
|
||||
+ this.saveExecutor = MoonriseCommon.LOAD_GROUP.createExecutor();
|
||||
this.chunkHolderManager = new ChunkHolderManager(world, this);
|
||||
}
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java
|
||||
index e4a5fa25ed368fc4662c30934da2963ef446d782..6da0ea5cd83a00578223e0a19f952c917bcbcdae 100644
|
||||
index e4a5fa25ed368fc4662c30934da2963ef446d782..33bb74186bfd0e1781e85410e98abe8dfff8150c 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java
|
||||
@@ -644,11 +644,19 @@ public final class NewChunkHolder {
|
||||
@@ -562,7 +620,7 @@ index e4a5fa25ed368fc4662c30934da2963ef446d782..6da0ea5cd83a00578223e0a19f952c91
|
||||
this.scheduler = scheduler;
|
||||
this.vanillaChunkHolder = new ChunkHolder(
|
||||
new ChunkPos(chunkX, chunkZ), ChunkHolderManager.MAX_TICKET_LEVEL, world,
|
||||
@@ -790,9 +798,11 @@ public final class NewChunkHolder {
|
||||
@@ -790,12 +798,14 @@ 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
|
||||
@@ -576,7 +634,20 @@ index e4a5fa25ed368fc4662c30934da2963ef446d782..6da0ea5cd83a00578223e0a19f952c91
|
||||
+ // DivineMC end - Chunk System optimization
|
||||
|
||||
public static final record UnloadTask(CallbackCompletable<CompoundTag> completable, PrioritisedExecutor.PrioritisedTask task,
|
||||
LazyRunnable toRun) {}
|
||||
- LazyRunnable toRun) {}
|
||||
+ org.bxteam.divinemc.server.chunk.ChunkRunnable toRun) {} // DivineMC - Chunk System optimization
|
||||
|
||||
public UnloadTask getUnloadTask(final MoonriseRegionFileIO.RegionFileType type) {
|
||||
switch (type) {
|
||||
@@ -858,7 +868,7 @@ public final class NewChunkHolder {
|
||||
this.priorityLocked = false;
|
||||
|
||||
if (chunk != null) {
|
||||
- final LazyRunnable toRun = new LazyRunnable();
|
||||
+ final org.bxteam.divinemc.server.chunk.ChunkRunnable toRun = new org.bxteam.divinemc.server.chunk.ChunkRunnable(this.chunkX, this.chunkZ, this.world, null); // DivineMC - Chunk System optimization'
|
||||
this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.saveExecutor.createTask(toRun), toRun);
|
||||
}
|
||||
if (poiChunk != null) {
|
||||
@@ -1190,6 +1200,7 @@ public final class NewChunkHolder {
|
||||
for (int dz = -NEIGHBOUR_RADIUS; dz <= NEIGHBOUR_RADIUS; ++dz) {
|
||||
for (int dx = -NEIGHBOUR_RADIUS; dx <= NEIGHBOUR_RADIUS; ++dx) {
|
||||
@@ -633,6 +704,198 @@ index e4a5fa25ed368fc4662c30934da2963ef446d782..6da0ea5cd83a00578223e0a19f952c91
|
||||
}).add(consumer);
|
||||
}
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java
|
||||
index 28ffa653e87a4e8ef7cf614916ef3fe61681fe16..b35b92b204fbefd139c4544f15e32d46bfa30777 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java
|
||||
@@ -110,60 +110,27 @@ public class RadiusAwarePrioritisedExecutor {
|
||||
return priorityId == this.selectedQueue ? queue.tryPushTasks() : null;
|
||||
}
|
||||
|
||||
+ // DivineMC start - Chunk System Optimizations
|
||||
public PrioritisedExecutor.PrioritisedTask createTask(final int chunkX, final int chunkZ, final int radius,
|
||||
- final Runnable run, final Priority priority) {
|
||||
+ final Runnable run, final Priority priority, final net.minecraft.server.level.ServerLevel world) {
|
||||
if (radius < 0) {
|
||||
throw new IllegalArgumentException("Radius must be > 0: " + radius);
|
||||
}
|
||||
- return new Task(this, chunkX, chunkZ, radius, run, priority);
|
||||
+ return new Task(this, chunkX, chunkZ, radius, run, priority, world);
|
||||
}
|
||||
|
||||
- public PrioritisedExecutor.PrioritisedTask createTask(final int chunkX, final int chunkZ, final int radius,
|
||||
- final Runnable run) {
|
||||
- return this.createTask(chunkX, chunkZ, radius, run, Priority.NORMAL);
|
||||
- }
|
||||
-
|
||||
- public PrioritisedExecutor.PrioritisedTask queueTask(final int chunkX, final int chunkZ, final int radius,
|
||||
- final Runnable run, final Priority priority) {
|
||||
- final PrioritisedExecutor.PrioritisedTask ret = this.createTask(chunkX, chunkZ, radius, run, priority);
|
||||
-
|
||||
- ret.queue();
|
||||
-
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- public PrioritisedExecutor.PrioritisedTask queueTask(final int chunkX, final int chunkZ, final int radius,
|
||||
- final Runnable run) {
|
||||
- final PrioritisedExecutor.PrioritisedTask ret = this.createTask(chunkX, chunkZ, radius, run);
|
||||
-
|
||||
- ret.queue();
|
||||
-
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run, final Priority priority) {
|
||||
- return new Task(this, 0, 0, -1, run, priority);
|
||||
- }
|
||||
-
|
||||
- public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run) {
|
||||
- return this.createInfiniteRadiusTask(run, Priority.NORMAL);
|
||||
- }
|
||||
-
|
||||
- public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run, final Priority priority) {
|
||||
- final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, priority);
|
||||
-
|
||||
- ret.queue();
|
||||
-
|
||||
- return ret;
|
||||
+ public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run, final Priority priority, final net.minecraft.server.level.ServerLevel world) {
|
||||
+ return new Task(this, 0, 0, -1, run, priority, world);
|
||||
}
|
||||
|
||||
- public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run) {
|
||||
- final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, Priority.NORMAL);
|
||||
+ public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run, final net.minecraft.server.level.ServerLevel world) {
|
||||
+ final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, Priority.NORMAL, world);
|
||||
|
||||
ret.queue();
|
||||
|
||||
return ret;
|
||||
}
|
||||
+ // DivineMC end - Chunk System Optimizations
|
||||
|
||||
private static void scheduleTasks(final List<PrioritisedExecutor.PrioritisedTask> toSchedule) {
|
||||
if (toSchedule != null) {
|
||||
@@ -440,18 +407,20 @@ public class RadiusAwarePrioritisedExecutor {
|
||||
private final int radius;
|
||||
private Runnable run;
|
||||
private Priority priority;
|
||||
+ public final net.minecraft.server.level.ServerLevel world; // DivineMC - Chunk System Optimization
|
||||
|
||||
private DependencyNode dependencyNode;
|
||||
private PrioritisedExecutor.PrioritisedTask queuedTask;
|
||||
|
||||
private Task(final RadiusAwarePrioritisedExecutor scheduler, final int chunkX, final int chunkZ, final int radius,
|
||||
- final Runnable run, final Priority priority) {
|
||||
+ final Runnable run, final Priority priority, final net.minecraft.server.level.ServerLevel world) { // DivineMC - Chunk System Optimization
|
||||
this.scheduler = scheduler;
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
this.radius = radius;
|
||||
this.run = run;
|
||||
this.priority = priority;
|
||||
+ this.world = world; // DivineMC - Chunk System Optimization
|
||||
}
|
||||
|
||||
private boolean isFiniteRadius() {
|
||||
@@ -473,6 +442,7 @@ public class RadiusAwarePrioritisedExecutor {
|
||||
synchronized (this.scheduler) {
|
||||
final DependencyNode node = this.dependencyNode;
|
||||
this.dependencyNode = null;
|
||||
+ if (node == null) return; // DivineMC - Chunk System Optimization
|
||||
toSchedule = node.tree.returnNode(node);
|
||||
}
|
||||
|
||||
@@ -489,6 +459,7 @@ public class RadiusAwarePrioritisedExecutor {
|
||||
final Runnable run = this.run;
|
||||
this.run = null;
|
||||
try {
|
||||
+ if (run == null) return; // DivineMC - Chunk System Optimization
|
||||
run.run();
|
||||
} finally {
|
||||
this.returnNode();
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java
|
||||
index 6ab353b0d2465c3680bb3c8d0852ba0f65c00fd2..70067c12d4d82460d55d8f90d01b0bc1e5368408 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java
|
||||
@@ -35,7 +35,7 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
|
||||
super(scheduler, world, chunkX, chunkZ);
|
||||
this.chunkHolder = chunkHolder;
|
||||
this.fromChunk = fromChunk;
|
||||
- this.convertToFullTask = scheduler.createChunkTask(chunkX, chunkZ, this, priority);
|
||||
+ this.convertToFullTask = scheduler.radiusAwareScheduler.createInfiniteRadiusTask(this, priority, world); // DivineMC - Chunk System Optimizations
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java
|
||||
index e9623c334858ebc698c92d05b36b70f6d846d0b1..43ef4d7b7db64a766bb815e1c2202b1f30ae4f99 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java
|
||||
@@ -69,7 +69,7 @@ public abstract class ChunkProgressionTask {
|
||||
}
|
||||
}
|
||||
|
||||
- protected final void complete(final ChunkAccess chunk, final Throwable throwable) {
|
||||
+ protected void complete(final ChunkAccess chunk, final Throwable throwable) { // DivineMC - not final
|
||||
try {
|
||||
this.complete0(chunk, throwable);
|
||||
} catch (final Throwable thr2) {
|
||||
@@ -81,7 +81,7 @@ public abstract class ChunkProgressionTask {
|
||||
|
||||
private void complete0(final ChunkAccess chunk, final Throwable throwable) {
|
||||
if ((boolean)COMPLETED_HANDLE.getAndSet((ChunkProgressionTask)this, (boolean)true)) {
|
||||
- throw new IllegalStateException("Already completed");
|
||||
+ return; // DivineMC - do not crash the server
|
||||
}
|
||||
this.completedChunk = chunk;
|
||||
this.completedThrowable = throwable;
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java
|
||||
index 25d8da4773dcee5096053e7e3788bfc224d705a7..2112ccbe382993dcfb56a50d991c3613765d7d56 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java
|
||||
@@ -51,9 +51,9 @@ public final class ChunkUpgradeGenericStatusTask extends ChunkProgressionTask im
|
||||
} else {
|
||||
final int writeRadius = ((ChunkSystemChunkStatus)this.toStatus).moonrise$getWriteRadius();
|
||||
if (writeRadius < 0) {
|
||||
- this.generateTask = this.scheduler.radiusAwareScheduler.createInfiniteRadiusTask(this, priority);
|
||||
+ this.generateTask = this.scheduler.radiusAwareScheduler.createInfiniteRadiusTask(this, priority, world); // DivineMC - Chunk System Optimizations
|
||||
} else {
|
||||
- this.generateTask = this.scheduler.radiusAwareScheduler.createTask(chunkX, chunkZ, writeRadius, this, priority);
|
||||
+ this.generateTask = this.scheduler.radiusAwareScheduler.createTask(chunkX, chunkZ, writeRadius, this, priority, world); // DivineMC - Chunk System Optimizations
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java
|
||||
index 95ed5a0ff3f0588f625ba48a5fee3aafbab9d13f..f2fd6d5eb024f646875868c441eb2da284206026 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java
|
||||
@@ -333,6 +333,12 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
||||
GenericDataLoadTask.this.onComplete((TaskResult<FinalCompletion, Throwable>)newData);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // DivineMC start - Chunk System Optimizations
|
||||
+ public GenericDataLoadTask<OnMain,FinalCompletion> loadTask() {
|
||||
+ return GenericDataLoadTask.this;
|
||||
+ }
|
||||
+ // DivineMC end - Chunk System Optimizations
|
||||
}
|
||||
|
||||
public final class ProcessOnMainTask implements Runnable {
|
||||
@@ -351,6 +357,12 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
||||
|
||||
GenericDataLoadTask.this.onComplete(result);
|
||||
}
|
||||
+
|
||||
+ // DivineMC start - Chunk System Optimizations
|
||||
+ public GenericDataLoadTask<OnMain,FinalCompletion> loadTask() {
|
||||
+ return GenericDataLoadTask.this;
|
||||
+ }
|
||||
+ // DivineMC end - Chunk System Optimizations
|
||||
}
|
||||
|
||||
protected static final class LoadDataFromDiskTask {
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java b/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java
|
||||
index e97e7d276faf055c89207385d3820debffb06463..4aeb75a2cdcfb4206bab3eee5ad674dd9890e720 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/chunk_tick_iteration/ChunkTickConstants.java
|
||||
@@ -658,6 +921,35 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..f94f443f862611f039454d1dc8ff2a4b
|
||||
if (!this.isDirty()) {
|
||||
return false;
|
||||
}
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
||||
index 571db5f9bf94745a8afe2cd313e593fb15db5e37..c8e59f6abed384f45314fc995d3822724c9b4001 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
||||
@@ -888,7 +888,7 @@ public final class StarLightInterface {
|
||||
super(chunkCoordinate, lightEngine, queue);
|
||||
this.task = ((ChunkSystemServerLevel)(ServerLevel)lightEngine.getWorld()).moonrise$getChunkTaskScheduler().radiusAwareScheduler.createTask(
|
||||
CoordinateUtils.getChunkX(chunkCoordinate), CoordinateUtils.getChunkZ(chunkCoordinate),
|
||||
- ((ChunkSystemChunkStatus)ChunkStatus.LIGHT).moonrise$getWriteRadius(), this, priority
|
||||
+ ((ChunkSystemChunkStatus)ChunkStatus.LIGHT).moonrise$getWriteRadius(), this, priority, lightEngine.world.getMinecraftWorld() // DivineMC - Chunk System Optimizations
|
||||
);
|
||||
}
|
||||
|
||||
diff --git a/io/papermc/paper/FeatureHooks.java b/io/papermc/paper/FeatureHooks.java
|
||||
index 460bb584db04b582f3297ae419183f430aff1ec0..72c4e1876115745fbeec12fe8a1ad6f4803e9d7f 100644
|
||||
--- a/io/papermc/paper/FeatureHooks.java
|
||||
+++ b/io/papermc/paper/FeatureHooks.java
|
||||
@@ -32,11 +32,6 @@ import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
|
||||
public final class FeatureHooks {
|
||||
-
|
||||
- public static void initChunkTaskScheduler(final boolean useParallelGen) {
|
||||
- ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.init(useParallelGen); // Paper - Chunk system
|
||||
- }
|
||||
-
|
||||
public static void registerPaperCommands(final Map<Set<String>, PaperSubcommand> commands) {
|
||||
commands.put(Set.of("fixlight"), new FixLightCommand()); // Paper - rewrite chunk system
|
||||
commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand()); // Paper - rewrite chunk system
|
||||
diff --git a/net/minecraft/server/level/DistanceManager.java b/net/minecraft/server/level/DistanceManager.java
|
||||
index 5eab6179ce3913cb4e4d424f910ba423faf21c85..4b1efd53e423bdfe90d5efd472823869fc87e73b 100644
|
||||
--- a/net/minecraft/server/level/DistanceManager.java
|
||||
@@ -695,7 +987,7 @@ index ab30af9cd58ff7310e05be87b08f42bacf69e11e..ae0e36d198ad8243920c8e8a55c0be49
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 973198ae0a73d6747e73548bdcbc1de46b6fb107..59aa7f9c06b62de4e8997f325d66daba28fa5871 100644
|
||||
index 973198ae0a73d6747e73548bdcbc1de46b6fb107..e17f5ba74295eb6585520ab9e5cc1cb2e3107098 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -181,6 +181,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -706,7 +998,15 @@ index 973198ae0a73d6747e73548bdcbc1de46b6fb107..59aa7f9c06b62de4e8997f325d66daba
|
||||
private int lastSpawnChunkRadius;
|
||||
final EntityTickList entityTickList = new EntityTickList(this); // DivineMC - parallel world ticking
|
||||
// Paper - rewrite chunk system
|
||||
@@ -689,6 +690,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -291,6 +292,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
// Paper end - optimise getPlayerByUUID
|
||||
// Paper start - rewrite chunk system
|
||||
+ public final org.bxteam.divinemc.server.chunk.PriorityHandler chunkSystemPriorities;
|
||||
private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder viewDistanceHolder = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder();
|
||||
private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader chunkLoader = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader((ServerLevel)(Object)this);
|
||||
private final ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityDataController entityDataController;
|
||||
@@ -689,6 +691,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);
|
||||
@@ -714,7 +1014,15 @@ index 973198ae0a73d6747e73548bdcbc1de46b6fb107..59aa7f9c06b62de4e8997f325d66daba
|
||||
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"),
|
||||
@@ -832,8 +834,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -703,6 +706,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
|
||||
this.tickExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new org.bxteam.divinemc.server.ServerLevelTickExecutorThreadFactory(getWorld().getName())); // DivineMC - parallel world ticking
|
||||
this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle
|
||||
+ this.chunkSystemPriorities = new org.bxteam.divinemc.server.chunk.PriorityHandler(this); // DivineMC - Chunk System optimizations
|
||||
}
|
||||
|
||||
// Paper start
|
||||
@@ -832,8 +836,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@Override
|
||||
public boolean shouldTickBlocksAt(long chunkPos) {
|
||||
// Paper start - rewrite chunk system
|
||||
@@ -724,7 +1032,7 @@ index 973198ae0a73d6747e73548bdcbc1de46b6fb107..59aa7f9c06b62de4e8997f325d66daba
|
||||
// Paper end - rewrite chunk system
|
||||
}
|
||||
|
||||
@@ -889,7 +890,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -889,7 +892,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
|
||||
private void optimiseRandomTick(final LevelChunk chunk, final int tickSpeed) {
|
||||
final LevelChunkSection[] sections = chunk.getSections();
|
||||
@@ -733,7 +1041,7 @@ index 973198ae0a73d6747e73548bdcbc1de46b6fb107..59aa7f9c06b62de4e8997f325d66daba
|
||||
final ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom simpleRandom = this.simpleRandom;
|
||||
final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294();
|
||||
|
||||
@@ -897,42 +898,41 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -897,42 +900,41 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
final int offsetX = cpos.x << 4;
|
||||
final int offsetZ = cpos.z << 4;
|
||||
|
||||
@@ -786,7 +1094,7 @@ index 973198ae0a73d6747e73548bdcbc1de46b6fb107..59aa7f9c06b62de4e8997f325d66daba
|
||||
}
|
||||
// Paper end - optimise random ticking
|
||||
|
||||
@@ -2525,30 +2525,25 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -2525,30 +2527,25 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
|
||||
private boolean isPositionTickingWithEntitiesLoaded(long chunkPos) {
|
||||
// Paper start - rewrite chunk system
|
||||
@@ -821,6 +1129,19 @@ index 973198ae0a73d6747e73548bdcbc1de46b6fb107..59aa7f9c06b62de4e8997f325d66daba
|
||||
// Paper end - rewrite chunk system
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/server/level/ThreadedLevelLightEngine.java b/net/minecraft/server/level/ThreadedLevelLightEngine.java
|
||||
index 5c9ac44a3b4bc8e047feaf61a94eb163761498a2..66dc6d77263d6f5de7d0a96b8b6575e7a363f5bf 100644
|
||||
--- a/net/minecraft/server/level/ThreadedLevelLightEngine.java
|
||||
+++ b/net/minecraft/server/level/ThreadedLevelLightEngine.java
|
||||
@@ -138,7 +138,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
|
||||
}
|
||||
}
|
||||
);
|
||||
- });
|
||||
+ }, world); // DivineMC - Chunk System Optimizations
|
||||
|
||||
return chunks.size();
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/LevelReader.java b/net/minecraft/world/level/LevelReader.java
|
||||
index 26c8c1e5598daf3550aef05b12218c47bda6618b..94c824ab1457939c425e1f99929d3222ee2c18a0 100644
|
||||
--- a/net/minecraft/world/level/LevelReader.java
|
||||
@@ -2894,16 +3215,3 @@ index ab1dcbe416e2c3c94cfddf04b7ed053425a71806..a37eb2e29b4577ebc711e8ef7b47fbbc
|
||||
private Vec3i size = Vec3i.ZERO;
|
||||
private String author = "?";
|
||||
// CraftBukkit start - data containers
|
||||
diff --git a/net/minecraft/world/level/levelgen/synth/PerlinNoise.java b/net/minecraft/world/level/levelgen/synth/PerlinNoise.java
|
||||
index ffac5b7b1eb1364ab8442d7145a7b4ebde68ee10..ef28df96ed569113a9d61f6ac4b4d84d578c02da 100644
|
||||
--- a/net/minecraft/world/level/levelgen/synth/PerlinNoise.java
|
||||
+++ b/net/minecraft/world/level/levelgen/synth/PerlinNoise.java
|
||||
@@ -187,7 +187,7 @@ public class PerlinNoise {
|
||||
}
|
||||
|
||||
public static double wrap(double value) {
|
||||
- return value - Mth.lfloor(value / 3.3554432E7 + 0.5) * 3.3554432E7;
|
||||
+ return value - Math.floor(value / 3.3554432E7 + 0.5) * 3.3554432E7; // DivineMC - avoid casting
|
||||
}
|
||||
|
||||
protected int firstOctave() {
|
||||
|
||||
@@ -17,7 +17,7 @@ index 9f7698f8ce56d5d89cf86f6ea2d5b4d51b18c9a2..351b035d1f3025af28b5147b95b912e0
|
||||
if (org.bxteam.divinemc.DivineConfig.enableParallelWorldTicking) {
|
||||
serverLevelTickingSemaphore.acquire();
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 59aa7f9c06b62de4e8997f325d66daba28fa5871..5cb64b5da32183b7cd1e052c638e0789cc796bc4 100644
|
||||
index e17f5ba74295eb6585520ab9e5cc1cb2e3107098..15e6804a5faccb0a4f514d6b94f5856c7c15ab76 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -194,7 +194,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -29,7 +29,7 @@ index 59aa7f9c06b62de4e8997f325d66daba28fa5871..5cb64b5da32183b7cd1e052c638e0789
|
||||
protected final Raids raids;
|
||||
private final ObjectLinkedOpenHashSet<BlockEventData> blockEvents = new ObjectLinkedOpenHashSet<>();
|
||||
private final List<BlockEventData> blockEventsToReschedule = new ArrayList<>(64);
|
||||
@@ -1728,7 +1728,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -1730,7 +1730,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
|
||||
@Override
|
||||
public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) {
|
||||
@@ -38,7 +38,7 @@ index 59aa7f9c06b62de4e8997f325d66daba28fa5871..5cb64b5da32183b7cd1e052c638e0789
|
||||
String string = "recursive call to sendBlockUpdated";
|
||||
Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated"));
|
||||
}
|
||||
@@ -1759,13 +1759,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -1761,13 +1761,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
// Paper end - catch CME see below why
|
||||
|
||||
try {
|
||||
@@ -54,7 +54,7 @@ index 59aa7f9c06b62de4e8997f325d66daba28fa5871..5cb64b5da32183b7cd1e052c638e0789
|
||||
}
|
||||
}
|
||||
} // Paper - option to disable pathfinding updates
|
||||
@@ -2652,7 +2652,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -2654,7 +2654,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
|
||||
if (entity instanceof Mob mob) {
|
||||
@@ -63,7 +63,7 @@ index 59aa7f9c06b62de4e8997f325d66daba28fa5871..5cb64b5da32183b7cd1e052c638e0789
|
||||
String string = "onTrackingStart called during navigation iteration";
|
||||
Util.logAndPauseIfInIde(
|
||||
"onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration")
|
||||
@@ -2722,7 +2722,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -2724,7 +2724,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
|
||||
if (entity instanceof Mob mob) {
|
||||
|
||||
@@ -25,7 +25,7 @@ index 351b035d1f3025af28b5147b95b912e0e2ab9212..1bcccba4df407ec4d53f49c3c2c7493d
|
||||
this.tickCount++;
|
||||
this.tickRateManager.tick();
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 5cb64b5da32183b7cd1e052c638e0789cc796bc4..e3e00afd4621c4b487f5d2f0a74f773e631ed776 100644
|
||||
index 15e6804a5faccb0a4f514d6b94f5856c7c15ab76..a7743ea4e5d672e1aa0d0945e19f3469ef81df62 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -218,6 +218,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -36,7 +36,7 @@ index 5cb64b5da32183b7cd1e052c638e0789cc796bc4..e3e00afd4621c4b487f5d2f0a74f773e
|
||||
|
||||
public LevelChunk getChunkIfLoaded(int x, int z) {
|
||||
return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
|
||||
@@ -772,6 +773,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -774,6 +775,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ index 5cb64b5da32183b7cd1e052c638e0789cc796bc4..e3e00afd4621c4b487f5d2f0a74f773e
|
||||
this.updateSkyBrightness();
|
||||
if (runsNormally) {
|
||||
this.tickTime();
|
||||
@@ -851,11 +854,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -853,11 +856,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
this.setDayTime(this.preciseTime);
|
||||
} else
|
||||
// Purpur end - Configurable daylight cycle
|
||||
|
||||
@@ -25,10 +25,10 @@ index 1bcccba4df407ec4d53f49c3c2c7493db87b2240..54c8605a4e36605208344e39726a4c3b
|
||||
CrashReport crashReport = CrashReport.forThrowable(levelTickingException, "Exception ticking world");
|
||||
serverLevel.fillReportDetails(crashReport);
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index e3e00afd4621c4b487f5d2f0a74f773e631ed776..a8a5da1f8519fe97f45ddd140afb9dc2a64c015e 100644
|
||||
index a7743ea4e5d672e1aa0d0945e19f3469ef81df62..67a1d115ff5facd2293ac7ba9d5eba890df343dd 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -574,6 +574,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -575,6 +575,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
// Paper end - chunk tick iteration
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ index ae0a3c3d9d6300293a6d0dff5cae49ebe7c11dab..3b08dad7a9fac7ac9acec0bfb85d4826
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index a8a5da1f8519fe97f45ddd140afb9dc2a64c015e..60fe99648a3d579f24172eb10067d859115d0ac9 100644
|
||||
index 67a1d115ff5facd2293ac7ba9d5eba890df343dd..c7bbadc61f51d04bc675926600e83ee0efb98e4a 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -816,6 +816,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
@@ -818,6 +818,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
this.entityTickList
|
||||
.forEach(
|
||||
entity -> {
|
||||
|
||||
@@ -0,0 +1,476 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
|
||||
Date: Sun, 6 Apr 2025 18:03:38 +0300
|
||||
Subject: [PATCH] Chunk System Optimizations
|
||||
|
||||
|
||||
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..353f1412b6edf481162ded50fa9a23d3442b9ed5 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<Entity> {
|
||||
|
||||
- private final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
|
||||
+ private final Int2IntMap entityToIndex = Int2IntMaps.synchronize(new Int2IntOpenHashMap(2, 0.8f)); // DivineMC - Chunk System Optimizations
|
||||
{
|
||||
this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
||||
}
|
||||
@@ -27,11 +29,11 @@ public final class EntityList implements Iterable<Entity> {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
- public boolean contains(final Entity entity) {
|
||||
+ public synchronized boolean contains(final Entity entity) { // DivineMC - Chunk System Optimizations
|
||||
return this.entityToIndex.containsKey(entity.getId());
|
||||
}
|
||||
|
||||
- public boolean remove(final Entity entity) {
|
||||
+ public synchronized boolean remove(final Entity entity) { // DivineMC - Chunk System Optimizations
|
||||
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<Entity> {
|
||||
return true;
|
||||
}
|
||||
|
||||
- public boolean add(final Entity entity) {
|
||||
+ public synchronized boolean add(final Entity entity) { // DivineMC - Chunk System Optimizations
|
||||
final int count = this.count;
|
||||
final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count);
|
||||
|
||||
@@ -82,18 +84,18 @@ public final class EntityList implements Iterable<Entity> {
|
||||
return this.entities[index];
|
||||
}
|
||||
|
||||
- public Entity[] getRawData() {
|
||||
+ public synchronized Entity[] getRawData() { // DivineMC - Chunk System Optimizations
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
- public void clear() {
|
||||
+ public synchronized void clear() { // DivineMC - Chunk System Optimizations
|
||||
this.entityToIndex.clear();
|
||||
Arrays.fill(this.entities, 0, this.count, null);
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
- public Iterator<Entity> iterator() {
|
||||
+ public synchronized Iterator<Entity> iterator() { // DivineMC - Chunk System Optimizations
|
||||
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..aab585e226e0928d778dc83a33bdcaf5f5a6f213 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 - Chunk System Optimizations - rewrite
|
||||
+import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||
+import it.unimi.dsi.fastutil.objects.ReferenceLists;
|
||||
|
||||
-public final class ReferenceList<E> implements Iterable<E> {
|
||||
-
|
||||
- private static final Object[] EMPTY_LIST = new Object[0];
|
||||
-
|
||||
- private final Reference2IntOpenHashMap<E> 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<E> referenceToIndex) {
|
||||
- this.references = references;
|
||||
- this.count = count;
|
||||
- this.referenceToIndex = referenceToIndex;
|
||||
- }
|
||||
-
|
||||
- public ReferenceList<E> 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<E> extends ReferenceLists.SynchronizedList<E> {
|
||||
+ 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<E>) 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<A> extends ReferenceArrayList<A> {
|
||||
+ 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<E> 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 - Chunk System Optimizations - rewrite
|
||||
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..11bf4ddb298bb39f7f39a9c33c90b48a3171266b 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 - Chunk System Optimizations
|
||||
{
|
||||
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 - Chunk System Optimizations
|
||||
|
||||
public int size() {
|
||||
- return (int)this.count;
|
||||
+ return this.count; // DivineMC - Chunk System Optimizations
|
||||
}
|
||||
|
||||
- public short getRaw(final int index) {
|
||||
+ public synchronized short getRaw(final int index) { // DivineMC - Chunk System Optimizations
|
||||
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 - Chunk System Optimizations
|
||||
+ final int count = this.count; // DivineMC - Chunk System Optimizations
|
||||
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 - Chunk System Optimizations
|
||||
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 - Chunk System Optimizations
|
||||
this.count = (short)0;
|
||||
this.map.clear();
|
||||
}
|
||||
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
index 632920e04686d8a0fd0a60e87348be1fe7862a3c..f10c6c156b8dd9acecc8b1ee81bd28260fb6e4d8 100644
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
@@ -3,6 +3,12 @@ package ca.spottedleaf.moonrise.common.util;
|
||||
import ca.spottedleaf.concurrentutil.executor.thread.PrioritisedThreadPool;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import com.mojang.logging.LogUtils;
|
||||
+import org.bxteam.divinemc.DivineConfig;
|
||||
+import org.bxteam.divinemc.server.chunk.ChunkSystemAlgorithms;
|
||||
+import org.bxteam.divinemc.server.chunk.TheChunkSystem;
|
||||
+import org.bxteam.divinemc.spark.ThreadDumperRegistry;
|
||||
+import org.bxteam.divinemc.util.ThreadBuilder;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -12,83 +18,63 @@ public final class MoonriseCommon {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
|
||||
- public static final PrioritisedThreadPool WORKER_POOL = new PrioritisedThreadPool(
|
||||
- new Consumer<>() {
|
||||
- private final AtomicInteger idGenerator = new AtomicInteger();
|
||||
+ public static TheChunkSystem WORKER_POOL;
|
||||
+ public static TheChunkSystem.ExecutorGroup PARALLEL_GEN_GROUP;
|
||||
+ public static TheChunkSystem.ExecutorGroup RADIUS_AWARE_GROUP;
|
||||
+ public static TheChunkSystem.ExecutorGroup LOAD_GROUP;
|
||||
|
||||
- @Override
|
||||
- public void accept(Thread thread) {
|
||||
- thread.setDaemon(true);
|
||||
- thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement());
|
||||
- thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
- @Override
|
||||
- public void uncaughtException(final Thread thread, final Throwable throwable) {
|
||||
- LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
|
||||
- }
|
||||
- });
|
||||
- }
|
||||
- }
|
||||
- );
|
||||
- public static final long WORKER_QUEUE_HOLD_TIME = (long)(20.0e6); // 20ms
|
||||
- public static final int CLIENT_DIVISION = 0;
|
||||
- public static final PrioritisedThreadPool.ExecutorGroup RENDER_EXECUTOR_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(CLIENT_DIVISION, 0);
|
||||
- public static final int SERVER_DIVISION = 1;
|
||||
- public static final PrioritisedThreadPool.ExecutorGroup PARALLEL_GEN_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
|
||||
- public static final PrioritisedThreadPool.ExecutorGroup RADIUS_AWARE_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
|
||||
- public static final PrioritisedThreadPool.ExecutorGroup LOAD_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
|
||||
-
|
||||
- public static void adjustWorkerThreads(final int configWorkerThreads, final int configIoThreads) {
|
||||
- int defaultWorkerThreads = Runtime.getRuntime().availableProcessors() / 2;
|
||||
- if (defaultWorkerThreads <= 4) {
|
||||
- defaultWorkerThreads = defaultWorkerThreads <= 3 ? 1 : 2;
|
||||
- } else {
|
||||
- defaultWorkerThreads = defaultWorkerThreads / 2;
|
||||
- }
|
||||
- defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
||||
-
|
||||
- int workerThreads = configWorkerThreads;
|
||||
+ public static void init(final int configWorkerThreads, final int configIoThreads) {
|
||||
+ ChunkSystemAlgorithms algorithm = DivineConfig.chunkWorkerAlgorithm;
|
||||
+ int workerThreads = algorithm.evalWorkers(configWorkerThreads, configIoThreads);
|
||||
+ int ioThreads = algorithm.evalIO(configWorkerThreads, configIoThreads);
|
||||
|
||||
- if (workerThreads <= 0) {
|
||||
- workerThreads = defaultWorkerThreads;
|
||||
- }
|
||||
+ WORKER_POOL = buildChunkSystem(workerThreads);
|
||||
|
||||
- final int ioThreads = Math.max(1, configIoThreads);
|
||||
+ PARALLEL_GEN_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup();
|
||||
+ RADIUS_AWARE_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup();
|
||||
+ LOAD_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup();
|
||||
|
||||
- WORKER_POOL.adjustThreadCount(workerThreads);
|
||||
IO_POOL.adjustThreadCount(ioThreads);
|
||||
+ LOGGER.info("Running ChunkSystem with {} worker threads and {} I/O threads", workerThreads, ioThreads);
|
||||
+ }
|
||||
|
||||
- LOGGER.info(PlatformHooks.get().getBrand() + " is using " + workerThreads + " worker threads, " + ioThreads + " I/O threads");
|
||||
+ private static @NotNull TheChunkSystem buildChunkSystem(int workerThreads) {
|
||||
+ return new TheChunkSystem(workerThreads, new ThreadBuilder() {
|
||||
+ @Override
|
||||
+ public void accept(final Thread thread) {
|
||||
+ thread.setPriority(DivineConfig.threadPoolPriority);
|
||||
+ thread.setDaemon(true);
|
||||
+ thread.setUncaughtExceptionHandler((thread1, throwable) -> LOGGER.error("Uncaught exception in thread {}", thread1.getName(), throwable));
|
||||
+ thread.setName("World Gen Worker #" + getAndIncrementId());
|
||||
+ ThreadDumperRegistry.REGISTRY.add(thread.getName());
|
||||
+ }
|
||||
+ });
|
||||
}
|
||||
|
||||
public static final PrioritisedThreadPool IO_POOL = new PrioritisedThreadPool(
|
||||
- new Consumer<>() {
|
||||
- private final AtomicInteger idGenerator = new AtomicInteger();
|
||||
-
|
||||
- @Override
|
||||
- public void accept(final Thread thread) {
|
||||
- thread.setDaemon(true);
|
||||
- thread.setName(PlatformHooks.get().getBrand() + " I/O Worker #" + this.idGenerator.getAndIncrement());
|
||||
- thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
- @Override
|
||||
- public void uncaughtException(final Thread thread, final Throwable throwable) {
|
||||
- LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
|
||||
- }
|
||||
- });
|
||||
- }
|
||||
+ new Consumer<>() {
|
||||
+ private final AtomicInteger idGenerator = new AtomicInteger();
|
||||
+
|
||||
+ @Override
|
||||
+ public void accept(final Thread thread) {
|
||||
+ thread.setDaemon(true);
|
||||
+ thread.setName(PlatformHooks.get().getBrand() + " I/O Worker #" + this.idGenerator.getAndIncrement());
|
||||
+ ThreadDumperRegistry.REGISTRY.add(thread.getName());
|
||||
+ thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
+ @Override
|
||||
+ public void uncaughtException(final Thread thread, final Throwable throwable) {
|
||||
+ LOGGER.error("Uncaught exception in thread {}", thread.getName(), throwable);
|
||||
+ }
|
||||
+ });
|
||||
}
|
||||
+ }
|
||||
);
|
||||
public static final long IO_QUEUE_HOLD_TIME = (long)(100.0e6); // 100ms
|
||||
- public static final PrioritisedThreadPool.ExecutorGroup CLIENT_PROFILER_IO_GROUP = IO_POOL.createExecutorGroup(CLIENT_DIVISION, 0);
|
||||
- public static final PrioritisedThreadPool.ExecutorGroup SERVER_REGION_IO_GROUP = IO_POOL.createExecutorGroup(SERVER_DIVISION, 0);
|
||||
+ public static final PrioritisedThreadPool.ExecutorGroup SERVER_REGION_IO_GROUP = IO_POOL.createExecutorGroup(1, 0);
|
||||
|
||||
public static void haltExecutors() {
|
||||
- MoonriseCommon.WORKER_POOL.shutdown(false);
|
||||
- LOGGER.info("Awaiting termination of worker pool for up to 60s...");
|
||||
- if (!MoonriseCommon.WORKER_POOL.join(TimeUnit.SECONDS.toMillis(60L))) {
|
||||
- LOGGER.error("Worker pool did not shut down in time!");
|
||||
- MoonriseCommon.WORKER_POOL.halt(false);
|
||||
- }
|
||||
-
|
||||
+ LOGGER.info("Shutting down ChunkSystem...");
|
||||
+ MoonriseCommon.WORKER_POOL.shutdown();
|
||||
MoonriseCommon.IO_POOL.shutdown(false);
|
||||
LOGGER.info("Awaiting termination of I/O pool for up to 60s...");
|
||||
if (!MoonriseCommon.IO_POOL.join(TimeUnit.SECONDS.toMillis(60L))) {
|
||||
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
|
||||
index 559c959aff3c9deef867b9e425fba3e2e669cac6..a5b0585b56d71d21c9da3b129d213def142bb1f6 100644
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
|
||||
@@ -4,7 +4,7 @@ import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
|
||||
public final class MoonriseConstants {
|
||||
|
||||
- public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
|
||||
+ public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", org.bxteam.divinemc.DivineConfig.maxViewDistance); // DivineMC - Configurable view distance
|
||||
|
||||
private MoonriseConstants() {}
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
|
||||
index 8b70a8e9b0aacbe7964b0441b5bbbaab228962d8..f0c420f4a1b282fb976825c33cb7a118e45de36d 100644
|
||||
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
|
||||
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
|
||||
@@ -229,7 +229,7 @@ public class GlobalConfiguration extends ConfigurationPart {
|
||||
|
||||
@PostProcess
|
||||
private void postProcess() {
|
||||
- ca.spottedleaf.moonrise.common.util.MoonriseCommon.adjustWorkerThreads(this.workerThreads, this.ioThreads);
|
||||
+ ca.spottedleaf.moonrise.common.util.MoonriseCommon.init(this.workerThreads, this.ioThreads);
|
||||
String newChunkSystemGenParallelism = this.genParallelism;
|
||||
if (newChunkSystemGenParallelism.equalsIgnoreCase("default")) {
|
||||
newChunkSystemGenParallelism = "true";
|
||||
@@ -245,7 +245,6 @@ public class GlobalConfiguration extends ConfigurationPart {
|
||||
} else {
|
||||
throw new IllegalStateException("Invalid option for gen-parallelism: must be one of [on, off, enabled, disabled, true, false, default]");
|
||||
}
|
||||
- FeatureHooks.initChunkTaskScheduler(useParallelGen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com>
|
||||
Date: Sat, 22 Feb 2025 02:33:28 +0300
|
||||
Subject: [PATCH] Add chunk worker algorithm
|
||||
|
||||
|
||||
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
index 632920e04686d8a0fd0a60e87348be1fe7862a3c..27447481c6e6b526cda032aff54a5c87256c217d 100644
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
@@ -3,6 +3,8 @@ package ca.spottedleaf.moonrise.common.util;
|
||||
import ca.spottedleaf.concurrentutil.executor.thread.PrioritisedThreadPool;
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import com.mojang.logging.LogUtils;
|
||||
+import org.bxteam.divinemc.DivineConfig;
|
||||
+import org.bxteam.divinemc.server.chunk.ChunkSystemAlgorithms;
|
||||
import org.slf4j.Logger;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -29,7 +31,7 @@ public final class MoonriseCommon {
|
||||
}
|
||||
}
|
||||
);
|
||||
- public static final long WORKER_QUEUE_HOLD_TIME = (long)(20.0e6); // 20ms
|
||||
+ public static final long WORKER_QUEUE_HOLD_TIME = (long)(2.0e6); // 2ms // DivineMC - Reduce from 20ms to 2ms
|
||||
public static final int CLIENT_DIVISION = 0;
|
||||
public static final PrioritisedThreadPool.ExecutorGroup RENDER_EXECUTOR_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(CLIENT_DIVISION, 0);
|
||||
public static final int SERVER_DIVISION = 1;
|
||||
@@ -38,26 +40,16 @@ public final class MoonriseCommon {
|
||||
public static final PrioritisedThreadPool.ExecutorGroup LOAD_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
|
||||
|
||||
public static void adjustWorkerThreads(final int configWorkerThreads, final int configIoThreads) {
|
||||
- int defaultWorkerThreads = Runtime.getRuntime().availableProcessors() / 2;
|
||||
- if (defaultWorkerThreads <= 4) {
|
||||
- defaultWorkerThreads = defaultWorkerThreads <= 3 ? 1 : 2;
|
||||
- } else {
|
||||
- defaultWorkerThreads = defaultWorkerThreads / 2;
|
||||
- }
|
||||
- defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
||||
-
|
||||
- int workerThreads = configWorkerThreads;
|
||||
-
|
||||
- if (workerThreads <= 0) {
|
||||
- workerThreads = defaultWorkerThreads;
|
||||
- }
|
||||
-
|
||||
- final int ioThreads = Math.max(1, configIoThreads);
|
||||
+ // DivineMC start - Add chunk worker algorithm
|
||||
+ ChunkSystemAlgorithms algorithm = DivineConfig.chunkWorkerAlgorithm;
|
||||
+ int workerThreads = algorithm.evalWorkers(configWorkerThreads, configIoThreads);
|
||||
+ int ioThreads = algorithm.evalIO(configWorkerThreads, configIoThreads);
|
||||
|
||||
WORKER_POOL.adjustThreadCount(workerThreads);
|
||||
IO_POOL.adjustThreadCount(ioThreads);
|
||||
|
||||
- LOGGER.info(PlatformHooks.get().getBrand() + " is using " + workerThreads + " worker threads, " + ioThreads + " I/O threads");
|
||||
+ LOGGER.info("ChunkSystem using '{}' algorithm, {} worker threads, {} I/O threads", algorithm.asDebugString(), workerThreads, ioThreads);
|
||||
+ // DivineMC end - Add chunk worker algorithm
|
||||
}
|
||||
|
||||
public static final PrioritisedThreadPool IO_POOL = new PrioritisedThreadPool(
|
||||
@@ -5,11 +5,11 @@ Subject: [PATCH] Configurable thread pool priority
|
||||
|
||||
|
||||
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
index 27447481c6e6b526cda032aff54a5c87256c217d..87d22532c680b7c6d3244a13e91fccbcc1a7e004 100644
|
||||
index f10c6c156b8dd9acecc8b1ee81bd28260fb6e4d8..c720304d8f2427cd4433d76e28ede13552181648 100644
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
|
||||
@@ -28,6 +28,7 @@ public final class MoonriseCommon {
|
||||
LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
|
||||
@@ -66,6 +66,7 @@ public final class MoonriseCommon {
|
||||
LOGGER.error("Uncaught exception in thread {}", thread.getName(), throwable);
|
||||
}
|
||||
});
|
||||
+ thread.setPriority(DivineConfig.threadPoolPriority); // DivineMC - Configurable thread pool priority
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java
|
||||
@@ -1,5 +_,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 +_,7 @@
|
||||
*/
|
||||
public final class EntityList implements Iterable<Entity> {
|
||||
|
||||
- private final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
|
||||
+ private final Int2IntMap entityToIndex = Int2IntMaps.synchronize(new Int2IntOpenHashMap(2, 0.8f));
|
||||
{
|
||||
this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
||||
}
|
||||
@@ -27,11 +_,11 @@
|
||||
return this.count;
|
||||
}
|
||||
|
||||
- public boolean contains(final Entity entity) {
|
||||
+ public synchronized boolean contains(final Entity entity) {
|
||||
return this.entityToIndex.containsKey(entity.getId());
|
||||
}
|
||||
|
||||
- public boolean remove(final Entity entity) {
|
||||
+ public synchronized boolean remove(final Entity entity) {
|
||||
final int index = this.entityToIndex.remove(entity.getId());
|
||||
if (index == Integer.MIN_VALUE) {
|
||||
return false;
|
||||
@@ -50,7 +_,7 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
- public boolean add(final Entity entity) {
|
||||
+ public synchronized boolean add(final Entity entity) {
|
||||
final int count = this.count;
|
||||
final int currIndex = this.entityToIndex.putIfAbsent(entity.getId(), count);
|
||||
|
||||
@@ -82,18 +_,18 @@
|
||||
return this.entities[index];
|
||||
}
|
||||
|
||||
- public Entity[] getRawData() {
|
||||
+ public synchronized Entity[] getRawData() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
- public void clear() {
|
||||
+ public synchronized void clear() {
|
||||
this.entityToIndex.clear();
|
||||
Arrays.fill(this.entities, 0, this.count, null);
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
- public Iterator<Entity> iterator() {
|
||||
+ public synchronized Iterator<Entity> iterator() {
|
||||
return new Iterator<>() {
|
||||
private Entity lastRet;
|
||||
private int current;
|
||||
@@ -1,165 +0,0 @@
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ReferenceList.java
|
||||
@@ -1,142 +_,24 @@
|
||||
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;
|
||||
-
|
||||
-public final class ReferenceList<E> implements Iterable<E> {
|
||||
-
|
||||
- private static final Object[] EMPTY_LIST = new Object[0];
|
||||
-
|
||||
- private final Reference2IntOpenHashMap<E> 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<E> referenceToIndex) {
|
||||
- this.references = references;
|
||||
- this.count = count;
|
||||
- this.referenceToIndex = referenceToIndex;
|
||||
- }
|
||||
-
|
||||
- public ReferenceList<E> 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 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 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
|
||||
- }
|
||||
-
|
||||
- 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
|
||||
- }
|
||||
-
|
||||
- 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<E> 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;
|
||||
- }
|
||||
- };
|
||||
+import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||
+import it.unimi.dsi.fastutil.objects.ReferenceLists;
|
||||
+
|
||||
+public class ReferenceList<E> extends ReferenceLists.SynchronizedList<E> {
|
||||
+ public ReferenceList(E[] elements) {
|
||||
+ super(new RefListInner<>(elements));
|
||||
+ }
|
||||
+
|
||||
+ public synchronized E[] getRawDataUnchecked() {
|
||||
+ return ((RefListInner<E>) this.list).getRawDataUnchecked();
|
||||
+ }
|
||||
+
|
||||
+ public static class RefListInner<A> extends ReferenceArrayList<A> {
|
||||
+ public RefListInner(A[] elements) {
|
||||
+ super(elements, true);
|
||||
+ }
|
||||
+
|
||||
+ public A[] getRawDataUnchecked() {
|
||||
+ return this.a;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java
|
||||
@@ -1,11 +_,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());
|
||||
{
|
||||
this.map.defaultReturnValue(Short.MIN_VALUE);
|
||||
}
|
||||
@@ -13,13 +_,13 @@
|
||||
private static final short[] EMPTY_LIST = new short[0];
|
||||
|
||||
private short[] byIndex = EMPTY_LIST;
|
||||
- private short count;
|
||||
+ private volatile short count;
|
||||
|
||||
public int size() {
|
||||
- return (int)this.count;
|
||||
+ return this.count;
|
||||
}
|
||||
|
||||
- public short getRaw(final int index) {
|
||||
+ public synchronized short getRaw(final int index) {
|
||||
return this.byIndex[index];
|
||||
}
|
||||
|
||||
@@ -30,8 +_,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
- public boolean add(final short value) {
|
||||
- final int count = (int)this.count;
|
||||
+ public synchronized boolean add(final short value) {
|
||||
+ final int count = this.count;
|
||||
final short currIndex = this.map.putIfAbsent(value, (short)count);
|
||||
|
||||
if (currIndex != Short.MIN_VALUE) {
|
||||
@@ -51,7 +_,7 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
- public boolean remove(final short value) {
|
||||
+ public synchronized boolean remove(final short value) {
|
||||
final short index = this.map.remove(value);
|
||||
if (index == Short.MIN_VALUE) {
|
||||
return false;
|
||||
@@ -70,7 +_,7 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
- public void clear() {
|
||||
+ public synchronized void clear() {
|
||||
this.count = (short)0;
|
||||
this.map.clear();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
|
||||
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
|
||||
@@ -4,7 +_,7 @@
|
||||
|
||||
public final class MoonriseConstants {
|
||||
|
||||
- public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
|
||||
+ public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", org.bxteam.divinemc.DivineConfig.maxViewDistance); // DivineMC - Configurable view distance
|
||||
|
||||
private MoonriseConstants() {}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.ishland.flowsched.executor;
|
||||
|
||||
import com.ishland.flowsched.structs.DynamicPriorityQueue;
|
||||
import com.ishland.flowsched.util.Assertions;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ExecutorManager {
|
||||
public final DynamicPriorityQueue<Task> globalWorkQueue;
|
||||
protected final ConcurrentMap<LockToken, FreeableTaskList> lockListeners = new ConcurrentHashMap<>();
|
||||
protected final WorkerThread[] workerThreads;
|
||||
final Object workerMonitor = new Object();
|
||||
|
||||
/**
|
||||
* Creates a new executor manager.
|
||||
*
|
||||
* @param workerThreadCount the number of worker threads.
|
||||
*/
|
||||
public ExecutorManager(int workerThreadCount) {
|
||||
this(workerThreadCount, thread -> { });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new executor manager.
|
||||
*
|
||||
* @param workerThreadCount the number of worker threads.
|
||||
* @param threadInitializer the thread initializer.
|
||||
*/
|
||||
public ExecutorManager(int workerThreadCount, Consumer<Thread> threadInitializer) {
|
||||
globalWorkQueue = new DynamicPriorityQueue<>();
|
||||
workerThreads = new WorkerThread[workerThreadCount];
|
||||
for (int i = 0; i < workerThreadCount; i++) {
|
||||
final WorkerThread thread = new WorkerThread(this);
|
||||
threadInitializer.accept(thread);
|
||||
thread.start();
|
||||
workerThreads[i] = thread;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to lock the given tokens.
|
||||
* The caller should discard the task if this method returns false, as it reschedules the task.
|
||||
*
|
||||
* @return {@code true} if the lock is acquired, {@code false} otherwise.
|
||||
*/
|
||||
boolean tryLock(Task task) {
|
||||
retry:
|
||||
while (true) {
|
||||
final FreeableTaskList listenerSet = new FreeableTaskList();
|
||||
LockToken[] lockTokens = task.lockTokens();
|
||||
for (int i = 0; i < lockTokens.length; i++) {
|
||||
LockToken token = lockTokens[i];
|
||||
final FreeableTaskList present = this.lockListeners.putIfAbsent(token, listenerSet);
|
||||
if (present != null) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
this.lockListeners.remove(lockTokens[j], listenerSet);
|
||||
}
|
||||
callListeners(listenerSet);
|
||||
synchronized (present) {
|
||||
if (present.freed) {
|
||||
continue retry;
|
||||
} else {
|
||||
present.add(task);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the locks held by the given task.
|
||||
*
|
||||
* @param task the task.
|
||||
*/
|
||||
void releaseLocks(Task task) {
|
||||
FreeableTaskList expectedListeners = null;
|
||||
for (LockToken token : task.lockTokens()) {
|
||||
final FreeableTaskList listeners = this.lockListeners.remove(token);
|
||||
if (listeners != null) {
|
||||
if (expectedListeners == null) {
|
||||
expectedListeners = listeners;
|
||||
} else {
|
||||
Assertions.assertTrue(expectedListeners == listeners, "Inconsistent lock listeners");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Lock token " + token + " is not locked");
|
||||
}
|
||||
}
|
||||
if (expectedListeners != null) {
|
||||
callListeners(expectedListeners); // synchronizes
|
||||
}
|
||||
}
|
||||
|
||||
private void callListeners(FreeableTaskList listeners) {
|
||||
synchronized (listeners) {
|
||||
listeners.freed = true;
|
||||
if (listeners.isEmpty()) return;
|
||||
for (Task listener : listeners) {
|
||||
this.schedule0(listener);
|
||||
}
|
||||
}
|
||||
this.wakeup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls an executable task from the global work queue.
|
||||
*
|
||||
* @return the task, or {@code null} if no task is executable.
|
||||
*/
|
||||
Task pollExecutableTask() {
|
||||
Task task;
|
||||
while ((task = this.globalWorkQueue.dequeue()) != null) {
|
||||
if (this.tryLock(task)) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the executor manager.
|
||||
*/
|
||||
public void shutdown() {
|
||||
for (WorkerThread workerThread : workerThreads) {
|
||||
workerThread.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task.
|
||||
*
|
||||
* @param task the task.
|
||||
*/
|
||||
public void schedule(Task task) {
|
||||
schedule0(task);
|
||||
wakeup();
|
||||
}
|
||||
|
||||
private void schedule0(Task task) {
|
||||
this.globalWorkQueue.enqueue(task, task.priority());
|
||||
}
|
||||
|
||||
public void wakeup() { // Canvas - private -> public
|
||||
synchronized (this.workerMonitor) {
|
||||
this.workerMonitor.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPendingTasks() {
|
||||
return this.globalWorkQueue.size() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a runnable for execution with the given priority.
|
||||
*
|
||||
* @param runnable the runnable.
|
||||
* @param priority the priority.
|
||||
*/
|
||||
public void schedule(Runnable runnable, int priority) {
|
||||
this.schedule(new SimpleTask(runnable, priority));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an executor that schedules runnables with the given priority.
|
||||
*
|
||||
* @param priority the priority.
|
||||
* @return the executor.
|
||||
*/
|
||||
public Executor executor(int priority) {
|
||||
return runnable -> this.schedule(runnable, priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the executor manager that the priority of the given task has changed.
|
||||
*
|
||||
* @param task the task.
|
||||
*/
|
||||
public void notifyPriorityChange(Task task) {
|
||||
this.globalWorkQueue.changePriority(task, task.priority());
|
||||
}
|
||||
|
||||
protected static class FreeableTaskList extends ReferenceArrayList<Task> { // Canvas - private -> protected
|
||||
private boolean freed = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.ishland.flowsched.executor;
|
||||
|
||||
public interface LockToken { }
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.ishland.flowsched.executor;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class SimpleTask implements Task {
|
||||
private final Runnable wrapped;
|
||||
private final int priority;
|
||||
|
||||
public SimpleTask(Runnable wrapped, int priority) {
|
||||
this.wrapped = Objects.requireNonNull(wrapped);
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(Runnable releaseLocks) {
|
||||
try {
|
||||
wrapped.run();
|
||||
} finally {
|
||||
releaseLocks.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propagateException(Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockToken[] lockTokens() {
|
||||
return new LockToken[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int priority() {
|
||||
return this.priority;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.ishland.flowsched.executor;
|
||||
|
||||
public interface Task {
|
||||
void run(Runnable releaseLocks);
|
||||
|
||||
void propagateException(Throwable t);
|
||||
|
||||
LockToken[] lockTokens();
|
||||
|
||||
int priority();
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.ishland.flowsched.executor;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class WorkerThread extends TickThread {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("FlowSched Executor Worker Thread");
|
||||
|
||||
private final ExecutorManager executorManager;
|
||||
private final AtomicBoolean shutdown = new AtomicBoolean(false);
|
||||
public volatile boolean active = false;
|
||||
|
||||
public WorkerThread(ExecutorManager executorManager) {
|
||||
super("null_worker");
|
||||
this.executorManager = executorManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
main_loop:
|
||||
while (true) {
|
||||
if (this.shutdown.get()) {
|
||||
return;
|
||||
}
|
||||
active = true;
|
||||
if (pollTasks()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
synchronized (this.executorManager.workerMonitor) {
|
||||
if (this.executorManager.hasPendingTasks()) continue main_loop;
|
||||
try {
|
||||
active = false;
|
||||
this.executorManager.workerMonitor.wait();
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean pollTasks() {
|
||||
final Task task = executorManager.pollExecutableTask();
|
||||
try {
|
||||
if (task != null) {
|
||||
AtomicBoolean released = new AtomicBoolean(false);
|
||||
try {
|
||||
task.run(() -> {
|
||||
if (released.compareAndSet(false, true)) {
|
||||
executorManager.releaseLocks(task);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
try {
|
||||
if (released.compareAndSet(false, true)) {
|
||||
executorManager.releaseLocks(task);
|
||||
}
|
||||
} catch (Throwable t1) {
|
||||
t.addSuppressed(t1);
|
||||
LOGGER.error("Exception thrown while releasing locks", t);
|
||||
}
|
||||
try {
|
||||
task.propagateException(t);
|
||||
} catch (Throwable t1) {
|
||||
t.addSuppressed(t1);
|
||||
LOGGER.error("Exception thrown while propagating exception", t);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Exception thrown while executing task", t);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
shutdown.set(true);
|
||||
LockSupport.unpark(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.ishland.flowsched.structs;
|
||||
|
||||
import org.bxteam.divinemc.server.chunk.PriorityHandler;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class DynamicPriorityQueue<E> {
|
||||
private final AtomicIntegerArray taskCount;
|
||||
public final ConcurrentLinkedQueue<E>[] priorities;
|
||||
private final ConcurrentHashMap<E, Integer> priorityMap = new ConcurrentHashMap<>();
|
||||
|
||||
public DynamicPriorityQueue() {
|
||||
this.taskCount = new AtomicIntegerArray(PriorityHandler.MAX_PRIORITY + 1);
|
||||
this.priorities = new ConcurrentLinkedQueue[PriorityHandler.MAX_PRIORITY + 1];
|
||||
for (int i = 0; i < (PriorityHandler.MAX_PRIORITY + 1); i++) {
|
||||
this.priorities[i] = new ConcurrentLinkedQueue<>();
|
||||
}
|
||||
}
|
||||
|
||||
public void enqueue(E element, int priority) {
|
||||
if (this.priorityMap.putIfAbsent(element, priority) != null)
|
||||
throw new IllegalArgumentException("Element already in queue");
|
||||
|
||||
this.priorities[priority].add(element);
|
||||
this.taskCount.incrementAndGet(priority);
|
||||
}
|
||||
|
||||
public boolean changePriority(E element, int newPriority) {
|
||||
Integer currentPriority = this.priorityMap.get(element);
|
||||
if (currentPriority == null || currentPriority == newPriority) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int currentIndex = currentPriority;
|
||||
boolean removedFromQueue = this.priorities[currentIndex].remove(element);
|
||||
if (!removedFromQueue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.taskCount.decrementAndGet(currentIndex);
|
||||
final boolean changeSuccess = this.priorityMap.replace(element, currentPriority, newPriority);
|
||||
if (!changeSuccess) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.priorities[newPriority].add(element);
|
||||
this.taskCount.incrementAndGet(newPriority);
|
||||
return true;
|
||||
}
|
||||
|
||||
public E dequeue() {
|
||||
for (int i = 0; i < this.priorities.length; i++) {
|
||||
if (this.taskCount.get(i) == 0) continue;
|
||||
E element = priorities[i].poll();
|
||||
if (element != null) {
|
||||
this.taskCount.decrementAndGet(i);
|
||||
this.priorityMap.remove(element);
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean contains(E element) {
|
||||
return priorityMap.containsKey(element);
|
||||
}
|
||||
|
||||
public void remove(E element) {
|
||||
Integer priority = this.priorityMap.remove(element);
|
||||
if (priority == null) return;
|
||||
|
||||
boolean removed = this.priorities[priority].remove(element);
|
||||
if (removed) this.taskCount.decrementAndGet(priority);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return priorityMap.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.ishland.flowsched.util;
|
||||
|
||||
public final class Assertions {
|
||||
public static void assertTrue(boolean value, String message) {
|
||||
if (!value) {
|
||||
final AssertionError error = new AssertionError(message);
|
||||
error.printStackTrace();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertTrue(boolean state, String format, Object... args) {
|
||||
if (!state) {
|
||||
final AssertionError error = new AssertionError(String.format(format, args));
|
||||
error.printStackTrace();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertTrue(boolean value) {
|
||||
if (!value) {
|
||||
final AssertionError error = new AssertionError();
|
||||
error.printStackTrace();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.bxteam.divinemc.server.chunk;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import java.lang.invoke.VarHandle;
|
||||
|
||||
public class ChunkRunnable implements Runnable {
|
||||
public final int chunkX;
|
||||
public final int chunkZ;
|
||||
public final ServerLevel world;
|
||||
private volatile Runnable toRun;
|
||||
private static final VarHandle TO_RUN_HANDLE = ConcurrentUtil.getVarHandle(ChunkRunnable.class, "toRun", Runnable.class);
|
||||
|
||||
public ChunkRunnable(int chunkX, int chunkZ, ServerLevel world, Runnable run) {
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
this.world = world;
|
||||
this.toRun = run;
|
||||
}
|
||||
|
||||
public void setRunnable(final Runnable run) {
|
||||
final Runnable prev = (Runnable)TO_RUN_HANDLE.compareAndExchange(this, (Runnable)null, run);
|
||||
if (prev != null) {
|
||||
throw new IllegalStateException("Runnable already set");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
((Runnable)TO_RUN_HANDLE.getVolatile(this)).run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
package org.bxteam.divinemc.server.chunk;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
||||
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.executor.RadiusAwarePrioritisedExecutor;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkUpgradeGenericStatusTask;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public final class ChunkSystemTaskQueue implements PrioritisedExecutor {
|
||||
private final AtomicLong taskIdGenerator = new AtomicLong();
|
||||
private final AtomicLong scheduledTasks = new AtomicLong();
|
||||
private final AtomicLong executedTasks = new AtomicLong();
|
||||
private final AtomicLong subOrderGenerator = new AtomicLong();
|
||||
private final AtomicBoolean shutdown = new AtomicBoolean();
|
||||
private final ConcurrentSkipListMap<ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder, Boolean> tasks = new ConcurrentSkipListMap<>(ChunkSystemTaskQueue.PrioritisedQueuedTask.COMPARATOR);
|
||||
private final TheChunkSystem chunkSystem;
|
||||
|
||||
public ChunkSystemTaskQueue(TheChunkSystem chunkSystem) {
|
||||
this.chunkSystem = chunkSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTasksScheduled() {
|
||||
return this.scheduledTasks.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTasksExecuted() {
|
||||
return this.executedTasks.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long generateNextSubOrder() {
|
||||
return this.subOrderGenerator.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdown() {
|
||||
return !this.shutdown.getAndSet(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return this.shutdown.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeTask() {
|
||||
for (; ; ) {
|
||||
final Map.Entry<ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder, Boolean> firstEntry = this.tasks.pollFirstEntry();
|
||||
if (firstEntry != null) {
|
||||
final ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder task = firstEntry.getKey();
|
||||
task.markRemoved();
|
||||
if (!task.task.execute()) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask createTask(final Runnable task) {
|
||||
return this.createTask(task, Priority.NORMAL, this.generateNextSubOrder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask createTask(final Runnable task, final Priority priority) {
|
||||
return this.createTask(task, priority, this.generateNextSubOrder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask createTask(final Runnable task, final Priority priority, final long subOrder) {
|
||||
return new ChunkSystemTaskQueue.PrioritisedQueuedTask(task, priority, subOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask queueTask(final Runnable task) {
|
||||
return this.queueTask(task, Priority.NORMAL, this.generateNextSubOrder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask queueTask(final Runnable task, final Priority priority) {
|
||||
return this.queueTask(task, priority, this.generateNextSubOrder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask queueTask(final Runnable task, final Priority priority, final long subOrder) {
|
||||
final ChunkSystemTaskQueue.PrioritisedQueuedTask ret = new ChunkSystemTaskQueue.PrioritisedQueuedTask(task, priority, subOrder);
|
||||
|
||||
ret.queue();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private final class PrioritisedQueuedTask implements PrioritisedExecutor.PrioritisedTask {
|
||||
public static final Comparator<ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder> COMPARATOR = (final ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder t1, final ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder t2) -> {
|
||||
final int priorityCompare = t1.priority - t2.priority;
|
||||
if (priorityCompare != 0) {
|
||||
return priorityCompare;
|
||||
}
|
||||
|
||||
final int subOrderCompare = Long.compare(t1.subOrder, t2.subOrder);
|
||||
if (subOrderCompare != 0) {
|
||||
return subOrderCompare;
|
||||
}
|
||||
|
||||
return Long.compare(t1.id, t2.id);
|
||||
};
|
||||
private final long id;
|
||||
private final Runnable execute;
|
||||
private Priority priority;
|
||||
private long subOrder;
|
||||
private ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder holder;
|
||||
|
||||
public PrioritisedQueuedTask(final Runnable execute, final Priority priority, final long subOrder) {
|
||||
if (!Priority.isValidPriority(priority)) {
|
||||
throw new IllegalArgumentException("Invalid priority " + priority);
|
||||
}
|
||||
|
||||
this.execute = execute;
|
||||
this.priority = priority;
|
||||
this.subOrder = subOrder;
|
||||
this.id = ChunkSystemTaskQueue.this.taskIdGenerator.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedExecutor getExecutor() {
|
||||
return ChunkSystemTaskQueue.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queue() {
|
||||
synchronized (this) {
|
||||
if (this.holder != null || this.priority == Priority.COMPLETING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ChunkSystemTaskQueue.this.isShutdown()) {
|
||||
throw new IllegalStateException("Queue is shutdown");
|
||||
}
|
||||
|
||||
this.holder = new Holder(this, this.priority.priority, this.subOrder, this.id);
|
||||
|
||||
ChunkSystemTaskQueue.this.scheduledTasks.getAndIncrement();
|
||||
int priority = this.holder.task.priority.priority;
|
||||
if (this.holder.task.priority.isHigherOrEqualPriority(Priority.BLOCKING)) {
|
||||
priority = PriorityHandler.BLOCKING;
|
||||
} else if (this.holder.task.execute instanceof ChunkUpgradeGenericStatusTask upgradeTask) {
|
||||
int x = upgradeTask.chunkX;
|
||||
int z = upgradeTask.chunkZ;
|
||||
priority = upgradeTask.world.chunkSystemPriorities.priority(x, z);
|
||||
} else if (this.holder.task.execute instanceof RadiusAwarePrioritisedExecutor.Task task) {
|
||||
int x = task.chunkX;
|
||||
int z = task.chunkZ;
|
||||
if (!(x == 0 && z == 0)) {
|
||||
priority = task.world.chunkSystemPriorities.priority(x, z);
|
||||
} // else | infinite radius task, ignore.
|
||||
} else if (this.holder.task.execute instanceof GenericDataLoadTask<?, ?>.ProcessOffMainTask offMainTask) {
|
||||
int x = offMainTask.loadTask().chunkX;
|
||||
int z = offMainTask.loadTask().chunkZ;
|
||||
priority = offMainTask.loadTask().world.chunkSystemPriorities.priority(x, z);
|
||||
} else if (this.holder.task.execute instanceof ChunkRunnable chunkRunnable) {
|
||||
int x = chunkRunnable.chunkX;
|
||||
int z = chunkRunnable.chunkZ;
|
||||
priority = chunkRunnable.world.chunkSystemPriorities.priority(x, z);
|
||||
}
|
||||
ChunkSystemTaskQueue.this.chunkSystem.schedule(this.holder.task.execute, priority);
|
||||
}
|
||||
|
||||
if (ChunkSystemTaskQueue.this.isShutdown()) {
|
||||
this.cancel();
|
||||
throw new IllegalStateException("Queue is shutdown");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isQueued() {
|
||||
synchronized (this) {
|
||||
return this.holder != null && this.priority != Priority.COMPLETING;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.priority = Priority.COMPLETING;
|
||||
|
||||
if (this.holder != null) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
ChunkSystemTaskQueue.this.executedTasks.getAndIncrement();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute() {
|
||||
final boolean increaseExecuted;
|
||||
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.priority = Priority.COMPLETING;
|
||||
|
||||
if (increaseExecuted = (this.holder != null)) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.execute.run();
|
||||
return true;
|
||||
} finally {
|
||||
if (increaseExecuted) {
|
||||
ChunkSystemTaskQueue.this.executedTasks.getAndIncrement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Priority getPriority() {
|
||||
synchronized (this) {
|
||||
return this.priority;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setPriority(final Priority priority) {
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING || this.priority == priority) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.priority = priority;
|
||||
|
||||
if (this.holder != null) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
this.holder = new ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder(this, priority.priority, this.subOrder, this.id);
|
||||
ChunkSystemTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean raisePriority(final Priority priority) {
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING || this.priority.isHigherOrEqualPriority(priority)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.priority = priority;
|
||||
|
||||
if (this.holder != null) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
this.holder = new ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder(this, priority.priority, this.subOrder, this.id);
|
||||
ChunkSystemTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lowerPriority(Priority priority) {
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING || this.priority.isLowerOrEqualPriority(priority)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.priority = priority;
|
||||
|
||||
if (this.holder != null) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
this.holder = new ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder(this, priority.priority, this.subOrder, this.id);
|
||||
ChunkSystemTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSubOrder() {
|
||||
synchronized (this) {
|
||||
return this.subOrder;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSubOrder(final long subOrder) {
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING || this.subOrder == subOrder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.subOrder = subOrder;
|
||||
|
||||
if (this.holder != null) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
this.holder = new ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder(this, priority.priority, this.subOrder, this.id);
|
||||
ChunkSystemTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean raiseSubOrder(long subOrder) {
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING || this.subOrder >= subOrder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.subOrder = subOrder;
|
||||
|
||||
if (this.holder != null) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
this.holder = new ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder(this, priority.priority, this.subOrder, this.id);
|
||||
ChunkSystemTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lowerSubOrder(final long subOrder) {
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING || this.subOrder <= subOrder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.subOrder = subOrder;
|
||||
|
||||
if (this.holder != null) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
this.holder = new ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder(this, priority.priority, this.subOrder, this.id);
|
||||
ChunkSystemTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setPriorityAndSubOrder(final Priority priority, final long subOrder) {
|
||||
synchronized (this) {
|
||||
if (this.priority == Priority.COMPLETING || (this.priority == priority && this.subOrder == subOrder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.priority = priority;
|
||||
this.subOrder = subOrder;
|
||||
|
||||
if (this.holder != null) {
|
||||
if (this.holder.markRemoved()) {
|
||||
ChunkSystemTaskQueue.this.tasks.remove(this.holder);
|
||||
}
|
||||
this.holder = new ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder(this, priority.priority, this.subOrder, this.id);
|
||||
ChunkSystemTaskQueue.this.tasks.put(this.holder, Boolean.TRUE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Holder {
|
||||
private static final VarHandle REMOVED_HANDLE = ConcurrentUtil.getVarHandle(ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder.class, "removed", boolean.class);
|
||||
private final ChunkSystemTaskQueue.PrioritisedQueuedTask task;
|
||||
private final int priority;
|
||||
private final long subOrder;
|
||||
private final long id;
|
||||
private volatile boolean removed;
|
||||
|
||||
private Holder(
|
||||
final ChunkSystemTaskQueue.PrioritisedQueuedTask task, final int priority, final long subOrder,
|
||||
final long id
|
||||
) {
|
||||
this.task = task;
|
||||
this.priority = priority;
|
||||
this.subOrder = subOrder;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean markRemoved() {
|
||||
return !(boolean) REMOVED_HANDLE.getAndSet((ChunkSystemTaskQueue.PrioritisedQueuedTask.Holder) this, (boolean) true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.bxteam.divinemc.server.chunk;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
import static ca.spottedleaf.moonrise.common.util.MoonriseConstants.MAX_VIEW_DISTANCE;
|
||||
|
||||
public class PriorityHandler {
|
||||
public static final int MAX_PRIORITY = MAX_VIEW_DISTANCE + 2;
|
||||
public static final int BLOCKING = 0;
|
||||
public final ServerLevel level;
|
||||
|
||||
public PriorityHandler(ServerLevel world) {
|
||||
this.level = world;
|
||||
}
|
||||
|
||||
public int priority(int chunkX, int chunkZ) {
|
||||
int priority = MAX_PRIORITY;
|
||||
for (final ServerPlayer player : this.level.players()) {
|
||||
ChunkPos playerChunk = player.chunkPosition();
|
||||
int playerChunkX = playerChunk.x;
|
||||
int playerChunkZ = playerChunk.z;
|
||||
|
||||
int dist = Math.max(Mth.abs(playerChunkX - chunkX), Mth.abs(playerChunkZ - chunkZ));
|
||||
int distPriority = Math.max(0, MAX_VIEW_DISTANCE - dist);
|
||||
priority = Math.min(priority, distPriority);
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
package org.bxteam.divinemc.server.chunk;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||
import com.ishland.flowsched.executor.ExecutorManager;
|
||||
import org.bxteam.divinemc.util.ThreadBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class TheChunkSystem extends ExecutorManager {
|
||||
protected final Logger LOGGER = LoggerFactory.getLogger("TheChunkSystem");
|
||||
|
||||
private final TheChunkSystem.COWArrayList<TheChunkSystem.ExecutorGroup> executors = new TheChunkSystem.COWArrayList<>(TheChunkSystem.ExecutorGroup.class);
|
||||
private boolean shutdown;
|
||||
|
||||
public TheChunkSystem(final int workerThreadCount, final ThreadBuilder threadInitializer) {
|
||||
super(workerThreadCount, threadInitializer);
|
||||
LOGGER.info("Initialized new ChunkSystem with {} allocated threads", workerThreadCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
synchronized (this) {
|
||||
this.shutdown = true;
|
||||
}
|
||||
|
||||
super.shutdown();
|
||||
this.wakeup();
|
||||
|
||||
for (final TheChunkSystem.ExecutorGroup group : this.executors.getArray()) {
|
||||
for (final TheChunkSystem.ExecutorGroup.ThreadPoolExecutor executor : group.executors.getArray()) {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("ChunkSystem shutdown complete");
|
||||
}
|
||||
|
||||
private void notifyAllThreads() {
|
||||
this.wakeup();
|
||||
}
|
||||
|
||||
public TheChunkSystem.ExecutorGroup createExecutorGroup() {
|
||||
synchronized (this) {
|
||||
if (this.shutdown) {
|
||||
throw new IllegalStateException("Queue is shutdown: " + this);
|
||||
}
|
||||
|
||||
final TheChunkSystem.ExecutorGroup ret = new TheChunkSystem.ExecutorGroup();
|
||||
|
||||
this.executors.add(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class COWArrayList<E> {
|
||||
private volatile E[] array;
|
||||
|
||||
public COWArrayList(final Class<E> clazz) {
|
||||
this.array = (E[]) Array.newInstance(clazz, 0);
|
||||
}
|
||||
|
||||
public E[] getArray() {
|
||||
return this.array;
|
||||
}
|
||||
|
||||
public void add(final E element) {
|
||||
synchronized (this) {
|
||||
final E[] array = this.array;
|
||||
|
||||
final E[] copy = Arrays.copyOf(array, array.length + 1);
|
||||
copy[array.length] = element;
|
||||
|
||||
this.array = copy;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean remove(final E element) {
|
||||
synchronized (this) {
|
||||
final E[] array = this.array;
|
||||
int index = -1;
|
||||
for (int i = 0, len = array.length; i < len; ++i) {
|
||||
if (array[i] == element) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final E[] copy = (E[]) Array.newInstance(array.getClass().getComponentType(), array.length - 1);
|
||||
|
||||
System.arraycopy(array, 0, copy, 0, index);
|
||||
System.arraycopy(array, index + 1, copy, index, (array.length - 1) - index);
|
||||
|
||||
this.array = copy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public final class ExecutorGroup {
|
||||
private final AtomicLong subOrderGenerator = new AtomicLong();
|
||||
private final TheChunkSystem.COWArrayList<TheChunkSystem.ExecutorGroup.ThreadPoolExecutor> executors = new TheChunkSystem.COWArrayList<>(TheChunkSystem.ExecutorGroup.ThreadPoolExecutor.class);
|
||||
|
||||
private ExecutorGroup() { }
|
||||
|
||||
public TheChunkSystem.ExecutorGroup.ThreadPoolExecutor[] getAllExecutors() {
|
||||
return this.executors.getArray().clone();
|
||||
}
|
||||
|
||||
private TheChunkSystem getThreadPool() {
|
||||
return TheChunkSystem.this;
|
||||
}
|
||||
|
||||
public TheChunkSystem.ExecutorGroup.@NotNull ThreadPoolExecutor createExecutor() {
|
||||
synchronized (TheChunkSystem.this) {
|
||||
if (TheChunkSystem.this.shutdown) {
|
||||
throw new IllegalStateException("Queue is shutdown: " + TheChunkSystem.this);
|
||||
}
|
||||
|
||||
final TheChunkSystem.ExecutorGroup.ThreadPoolExecutor ret = new TheChunkSystem.ExecutorGroup.ThreadPoolExecutor();
|
||||
|
||||
this.executors.add(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public final class ThreadPoolExecutor implements PrioritisedExecutor {
|
||||
private final ChunkSystemTaskQueue taskBuilder = new ChunkSystemTaskQueue(TheChunkSystem.this);
|
||||
private volatile boolean halt;
|
||||
|
||||
private ThreadPoolExecutor() { }
|
||||
|
||||
private TheChunkSystem.ExecutorGroup getGroup() {
|
||||
return TheChunkSystem.ExecutorGroup.this;
|
||||
}
|
||||
|
||||
private void notifyPriorityShift() {
|
||||
TheChunkSystem.this.notifyAllThreads();
|
||||
}
|
||||
|
||||
private void notifyScheduled() {
|
||||
TheChunkSystem.this.notifyAllThreads();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this queue from the thread pool without shutting the queue down or waiting for queued tasks to be executed
|
||||
*/
|
||||
public void halt() {
|
||||
this.halt = true;
|
||||
|
||||
TheChunkSystem.ExecutorGroup.this.executors.remove(this);
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
if (this.halt) {
|
||||
return false;
|
||||
} else {
|
||||
if (!this.isShutdown()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !TheChunkSystem.this.globalWorkQueue.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdown() {
|
||||
if (TheChunkSystem.this.globalWorkQueue.isEmpty()) {
|
||||
TheChunkSystem.ExecutorGroup.this.executors.remove(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return TheChunkSystem.this.shutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTasksScheduled() {
|
||||
return 0; // TODO: implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTasksExecuted() {
|
||||
return 0; // TODO: implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public long generateNextSubOrder() {
|
||||
return TheChunkSystem.ExecutorGroup.this.subOrderGenerator.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeTask() {
|
||||
throw new UnsupportedOperationException("Unable to execute task from ThreadPoolExecutor as interface into FlowSched");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask queueTask(final Runnable task) {
|
||||
final PrioritisedTask ret = this.createTask(task);
|
||||
|
||||
ret.queue();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask queueTask(final Runnable task, final Priority priority) {
|
||||
final PrioritisedTask ret = this.createTask(task, priority);
|
||||
|
||||
ret.queue();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask queueTask(final Runnable task, final Priority priority, final long subOrder) {
|
||||
final PrioritisedTask ret = this.createTask(task, priority, subOrder);
|
||||
|
||||
ret.queue();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask createTask(final Runnable task) {
|
||||
return this.createTask(task, Priority.NORMAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask createTask(final Runnable task, final Priority priority) {
|
||||
return this.createTask(task, priority, this.generateNextSubOrder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedTask createTask(final Runnable task, final Priority priority, final long subOrder) {
|
||||
return new TheChunkSystem.ExecutorGroup.ThreadPoolExecutor.WrappedTask(this.taskBuilder.createTask(task, priority, subOrder));
|
||||
}
|
||||
|
||||
private final class WrappedTask implements PrioritisedTask {
|
||||
private final PrioritisedTask wrapped;
|
||||
|
||||
private WrappedTask(final PrioritisedTask wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrioritisedExecutor getExecutor() {
|
||||
return TheChunkSystem.ExecutorGroup.ThreadPoolExecutor.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queue() {
|
||||
if (this.wrapped.queue()) {
|
||||
final Priority priority = this.getPriority();
|
||||
if (priority != Priority.COMPLETING) {
|
||||
TheChunkSystem.ExecutorGroup.ThreadPoolExecutor.this.notifyScheduled();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isQueued() {
|
||||
return this.wrapped.isQueued();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
return this.wrapped.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute() {
|
||||
return this.wrapped.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Priority getPriority() {
|
||||
return this.wrapped.getPriority();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setPriority(final Priority priority) {
|
||||
if (this.wrapped.setPriority(priority)) {
|
||||
TheChunkSystem.ExecutorGroup.ThreadPoolExecutor.this.notifyPriorityShift();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean raisePriority(final Priority priority) {
|
||||
if (this.wrapped.raisePriority(priority)) {
|
||||
TheChunkSystem.ExecutorGroup.ThreadPoolExecutor.this.notifyPriorityShift();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lowerPriority(final Priority priority) {
|
||||
return this.wrapped.lowerPriority(priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSubOrder() {
|
||||
return this.wrapped.getSubOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSubOrder(final long subOrder) {
|
||||
return this.wrapped.setSubOrder(subOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean raiseSubOrder(final long subOrder) {
|
||||
return this.wrapped.raiseSubOrder(subOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lowerSubOrder(final long subOrder) {
|
||||
return this.wrapped.lowerSubOrder(subOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setPriorityAndSubOrder(final Priority priority, final long subOrder) {
|
||||
if (this.wrapped.setPriorityAndSubOrder(priority, subOrder)) {
|
||||
TheChunkSystem.ExecutorGroup.ThreadPoolExecutor.this.notifyPriorityShift();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface ThreadBuilder extends Consumer<Thread> {
|
||||
AtomicInteger id = new AtomicInteger();
|
||||
|
||||
default int getAndIncrementId() {
|
||||
return id.getAndIncrement();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user