From bd3f32c944c6ebbcaf32244c872bd7ba5b43577a Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Fri, 28 Jun 2024 19:04:11 -0700 Subject: [PATCH] Execute chunk tasks mid-tick. If the server tick length is high, then the amount of time available to process chunk tasks inbetween ticks is low. As a result, chunk loading and generation may appear to slow down. To ensure that chunk tasks are always processed, we add logic to execute chunk tasks during tile entity tick, entity tick, chunk random ticking, and scheduled block/fluid ticking. The mid-tick task execution is timed so that it is not prioritised over the server tick. --- .../mixin/block_entity_remove/LevelMixin.java | 7 ++ .../chunk_system/EntityTickListMixin.java | 2 +- .../mixin/chunk_system/LevelMixin.java | 22 ++++++ .../chunk_system/MinecraftServerMixin.java | 73 ++++++++++++++++++- .../chunk_system/ServerChunkCacheMixin.java | 24 ++++++ .../mixin/chunk_system/ServerLevelMixin.java | 61 ++++++++++++++++ .../chunk_system/level/ChunkSystemLevel.java | 2 + .../level/ChunkSystemServerLevel.java | 3 + .../server/ChunkSystemMinecraftServer.java | 2 + 9 files changed, 194 insertions(+), 2 deletions(-) diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/block_entity_remove/LevelMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/block_entity_remove/LevelMixin.java index 40eb75b..8f49669 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/block_entity_remove/LevelMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/block_entity_remove/LevelMixin.java @@ -1,5 +1,6 @@ package ca.spottedleaf.moonrise.mixin.block_entity_remove; +import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.core.BlockPos; import net.minecraft.world.TickRateManager; @@ -73,6 +74,8 @@ public abstract class LevelMixin implements LevelAccessor, AutoCloseable { int i = 0; // current ticking entity (exclusive) int len = tickList.size(); + int tickedEntities = 0; + Objects.checkFromToIndex(0, len, elements.length); try { for (; i < len; ++i) { @@ -84,6 +87,10 @@ public abstract class LevelMixin implements LevelAccessor, AutoCloseable { if (doTick && this.shouldTickBlocksAt(tileEntity.getPos())) { tileEntity.tick(); + // call mid tick tasks for chunk system + if ((++tickedEntities & 7) == 0) { + ((ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks(); + } } if (writeToBase) { diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/EntityTickListMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/EntityTickListMixin.java index 3094ae7..e2dcacf 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/EntityTickListMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/EntityTickListMixin.java @@ -111,6 +111,6 @@ public abstract class EntityTickListMixin { } } finally { iterator.finishedIterating(); - } + } } } diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/LevelMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/LevelMixin.java index b4a0167..64d2ca4 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/LevelMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/LevelMixin.java @@ -189,6 +189,11 @@ public abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityG return this.getChunkSource().getChunk(chunkX, chunkZ, leastStatus, false); } + @Override + public void moonrise$midTickTasks() { + // no-op on ClientLevel + } + /** * @reason Allow block updates in non-ticking chunks, as new chunk system sends non-ticking chunks to clients * @author Spottedleaf @@ -207,4 +212,21 @@ public abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityG } // TODO: Thread.currentThread() != this.thread to TickThread? + + + /** + * @reason Execute mid-tick chunk tasks during entity ticking + * @author Spottedleaf + */ + @Inject( + method = "guardEntityTick", + at = @At( + value = "INVOKE", + shift = At.Shift.AFTER, + target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V" + ) + ) + private void midTickEntity(final CallbackInfo ci) { + this.moonrise$midTickTasks(); + } } diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/MinecraftServerMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/MinecraftServerMixin.java index aae0537..cea9978 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/MinecraftServerMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/MinecraftServerMixin.java @@ -55,13 +55,84 @@ public abstract class MinecraftServerMixin extends ReentrantBlockableEventLoop= MAX_CHUNK_EXEC_TIME) { + if (!moreTasks) { + this.lastMidTickExecuteFailure = currTime; + } + + // note: negative values reduce the time + long overuse = diff - MAX_CHUNK_EXEC_TIME; + if (overuse >= (10L * 1000L * 1000L)) { // 10ms + // make sure something like a GC or dumb plugin doesn't screw us over... + overuse = 10L * 1000L * 1000L; // 10ms + } + + final double overuseCount = (double)overuse/(double)MAX_CHUNK_EXEC_TIME; + final long extraSleep = (long)Math.round(overuseCount*CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME); + + this.lastMidTickExecute = currTime + extraSleep; + return; + } + } + } + /** - * @reason Force execution of tasks for all worlds, so that the first world does not hog all of the task processing + * @reason Force execution of tasks for all worlds, so that the first world does not hog the task processing time. + * Additionally, perform mid-tick task execution when handling the normal server queue so that chunk tasks + * are guaranteed to be processed during tick sleep. * @author Spottedleaf */ @Overwrite private boolean pollTaskInternal() { if (super.pollTask()) { + this.moonrise$executeMidTickTasks(); return true; } diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerChunkCacheMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerChunkCacheMixin.java index f86c300..c47e1b9 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerChunkCacheMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerChunkCacheMixin.java @@ -8,6 +8,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; +import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer; import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache; import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkLevel; @@ -48,6 +49,9 @@ public abstract class ServerChunkCacheMixin extends ChunkSource implements Chunk @Unique private final ConcurrentLong2ReferenceChainedHashTable fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>(); + @Unique + private long chunksTicked; + @Override public final void moonrise$setFullChunk(final int chunkX, final int chunkZ, final LevelChunk chunk) { final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ); @@ -274,4 +278,24 @@ public abstract class ServerChunkCacheMixin extends ChunkSource implements Chunk private boolean skipSaveTicketUpdates(final ServerChunkCache instance) { return false; } + + /** + * @reason Perform mid-tick chunk task processing during chunk tick + * @author Spottedleaf + */ + @Inject( + method = "tickChunks", + at = @At( + value = "INVOKE", + shift = At.Shift.AFTER, + target = "Lnet/minecraft/server/level/ServerLevel;tickChunk(Lnet/minecraft/world/level/chunk/LevelChunk;I)V" + ) + ) + private void midTickChunks(final CallbackInfo ci) { + if ((++this.chunksTicked & 7L) != 0L) { + return; + } + + ((ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks(); + } } diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java index c84f057..8f2a1f7 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_system/ServerLevelMixin.java @@ -15,6 +15,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManage import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ThreadedTicketLevelPropagator; +import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; @@ -43,6 +44,7 @@ import net.minecraft.world.level.entity.PersistentEntitySectionManager; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraft.world.level.storage.WritableLevelData; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; @@ -67,6 +69,10 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe @Shadow private PersistentEntitySectionManager entityManager; + @Shadow + @Final + private MinecraftServer server; + protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey resourceKey, RegistryAccess registryAccess, Holder holder, Supplier supplier, boolean bl, boolean bl2, long l, int i) { super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i); } @@ -92,6 +98,12 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe @Unique private ChunkTaskScheduler chunkTaskScheduler; + @Unique + private long lastMidTickFailure; + + @Unique + private long tickedBlocksOrFluids; + /** * @reason Initialise fields / destroy entity manager state * @author Spottedleaf @@ -153,6 +165,11 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe return newChunkHolder.getChunkIfPresentUnchecked(leastStatus); } + @Override + public final void moonrise$midTickTasks() { + ((ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); + } + @Override public final ChunkAccess moonrise$syncLoadNonFull(final int chunkX, final int chunkZ, final ChunkStatus status) { return this.moonrise$getChunkTaskScheduler().syncLoadNonFull(chunkX, chunkZ, status); @@ -279,6 +296,16 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe return this.viewDistanceHolder; } + @Override + public final long moonrise$getLastMidTickFailure() { + return this.lastMidTickFailure; + } + + @Override + public final void moonrise$setLastMidTickFailure(final long time) { + this.lastMidTickFailure = time; + } + /** * @reason Entities are guaranteed to be ticking in the new chunk system * @author Spottedleaf @@ -600,4 +627,38 @@ public abstract class ServerLevelMixin extends Level implements ChunkSystemServe private int redirectCrashCount(final PersistentEntitySectionManager instance) { return this.moonrise$getEntityLookup().getEntityCount(); } + + /** + * @reason Execute mid-tick chunk tasks during fluid ticking + * @author Spottedleaf + */ + @Inject( + method = "tickFluid", + at = @At( + value = "RETURN" + ) + ) + private void midTickFluids(final CallbackInfo ci) { + if ((++this.tickedBlocksOrFluids & 7L) != 0L) { + return; + } + ((ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); + } + + /** + * @reason Execute mid-tick chunk tasks during block ticking + * @author Spottedleaf + */ + @Inject( + method = "tickBlock", + at = @At( + value = "RETURN" + ) + ) + private void midTickBlock(final CallbackInfo ci) { + if ((++this.tickedBlocksOrFluids & 7L) != 0L) { + return; + } + ((ChunkSystemMinecraftServer)this.server).moonrise$executeMidTickTasks(); + } } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java index eab0994..efcd905 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java @@ -17,4 +17,6 @@ public interface ChunkSystemLevel { public ChunkAccess moonrise$getSpecificChunkIfLoaded(final int chunkX, final int chunkZ, final ChunkStatus leastStatus); + public void moonrise$midTickTasks(); + } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java index a31c392..becb002 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java @@ -46,4 +46,7 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel { public RegionizedPlayerChunkLoader.ViewDistanceHolder moonrise$getViewDistanceHolder(); + public long moonrise$getLastMidTickFailure(); + + public void moonrise$setLastMidTickFailure(final long time); } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/server/ChunkSystemMinecraftServer.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/server/ChunkSystemMinecraftServer.java index 21c9562..cb6af37 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/server/ChunkSystemMinecraftServer.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/server/ChunkSystemMinecraftServer.java @@ -4,4 +4,6 @@ public interface ChunkSystemMinecraftServer { public void moonrise$setChunkSystemCrash(final Throwable throwable); + public void moonrise$executeMidTickTasks(); + }