diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index 08bbeb4b..8ce7c38b 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -70,6 +70,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.dynamic_structure_manager", true); this.addMixinRule("bugfix.mc218112", true); this.addMixinRule("bugfix.chunk_deadlock", true); + this.addMixinRule("bugfix.chunk_not_unloading", true); this.addMixinRule("bugfix.chunk_deadlock.valhesia", modPresent("valhelsia_structures")); this.addMixinRule("bugfix.tf_cme_on_load", modPresent("twilightforest")); this.addMixinRule("bugfix.refinedstorage", modPresent("refinedstorage")); diff --git a/src/main/java/org/embeddedt/modernfix/duck/IPaperChunkHolder.java b/src/main/java/org/embeddedt/modernfix/duck/IPaperChunkHolder.java new file mode 100644 index 00000000..0d83c721 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/duck/IPaperChunkHolder.java @@ -0,0 +1,5 @@ +package org.embeddedt.modernfix.duck; + +public interface IPaperChunkHolder { + boolean mfix$canAdvanceStatus(); +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/bugfix/paper_chunk_patches/ChunkHolderMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/paper_chunk_patches/ChunkHolderMixin.java new file mode 100644 index 00000000..796097d6 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/paper_chunk_patches/ChunkHolderMixin.java @@ -0,0 +1,61 @@ +package org.embeddedt.modernfix.mixin.bugfix.paper_chunk_patches; + +import com.mojang.datafixers.util.Either; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import org.embeddedt.modernfix.duck.IPaperChunkHolder; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@Mixin(ChunkHolder.class) +public abstract class ChunkHolderMixin implements IPaperChunkHolder { + + @Shadow public abstract CompletableFuture> getFutureIfPresentUnchecked(ChunkStatus arg); + + @Shadow @Final private static List CHUNK_STATUSES; + + public ChunkStatus mfix$getChunkHolderStatus() { + for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) { + CompletableFuture> future = this.getFutureIfPresentUnchecked(curr); + Either either = future.getNow(null); + if (either == null || !either.left().isPresent()) { + continue; + } + return curr; + } + + return null; + } + + public ChunkAccess mfix$getAvailableChunkNow() { + // TODO can we just getStatusFuture(EMPTY)? + for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) { + CompletableFuture> future = this.getFutureIfPresentUnchecked(curr); + Either either = future.getNow(null); + if (either == null || !either.left().isPresent()) { + continue; + } + return either.left().get(); + } + return null; + } + + public static ChunkStatus mfix$getNextStatus(ChunkStatus status) { + if (status == ChunkStatus.FULL) { + return status; + } + return CHUNK_STATUSES.get(status.getIndex() + 1); + } + + @Override + public boolean mfix$canAdvanceStatus() { + ChunkStatus status = mfix$getChunkHolderStatus(); + ChunkAccess chunk = mfix$getAvailableChunkNow(); + return chunk != null && (status == null || chunk.getStatus().isOrAfter(mfix$getNextStatus(status))); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/bugfix/paper_chunk_patches/ChunkMapMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/paper_chunk_patches/ChunkMapMixin.java new file mode 100644 index 00000000..aed16d3f --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/paper_chunk_patches/ChunkMapMixin.java @@ -0,0 +1,59 @@ +package org.embeddedt.modernfix.mixin.bugfix.paper_chunk_patches; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.util.thread.BlockableEventLoop; +import net.minecraftforge.fml.server.ServerLifecycleHooks; +import org.embeddedt.modernfix.duck.IPaperChunkHolder; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.concurrent.Executor; + + +@Mixin(ChunkMap.class) +public class ChunkMapMixin { + @Shadow @Final private BlockableEventLoop mainThreadExecutor; + + private Executor mainInvokingExecutor; + + @Inject(method = "", at = @At("RETURN"), cancellable = true) + private void setup(CallbackInfo ci) { + this.mainInvokingExecutor = (runnable) -> { + if(ServerLifecycleHooks.getCurrentServer().isSameThread()) + runnable.run(); + else + this.mainThreadExecutor.execute(runnable); + }; + } + + + /* https://github.com/PaperMC/Paper/blob/ver/1.17.1/patches/server/0752-Fix-chunks-refusing-to-unload-at-low-TPS.patch */ + @ModifyArg(method = "unpackTicks", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1) + private Executor useMainThreadExecutor(Executor executor) { + return this.mainThreadExecutor; + } + + /* https://github.com/PaperMC/Paper/blob/master/patches/removed/1.19.2-legacy-chunksystem/0482-Improve-Chunk-Status-Transition-Speed.patch */ + @ModifyArg(method = "getEntityTickingRangeFuture", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1) + private Executor useMainInvokingExecutor(Executor executor) { + return this.mainInvokingExecutor; + } + + @ModifyArg(method = "scheduleChunkGeneration", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenComposeAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1) + private Executor skipWorkerIfPossible(Executor executor, ChunkHolder chunkHolder) { + return (runnable) -> { + if(((IPaperChunkHolder)chunkHolder).mfix$canAdvanceStatus()) { + this.mainInvokingExecutor.execute(runnable); + return; + } + executor.execute(runnable); + }; + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index ddd419c7..f57416aa 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -9,6 +9,8 @@ "core.BootstrapMixin", "bugfix.edge_chunk_not_saved.ChunkManagerMixin", "bugfix.starlight_emptiness.StarLightEngineMixin", + "bugfix.paper_chunk_patches.ChunkMapMixin", + "bugfix.paper_chunk_patches.ChunkHolderMixin", "perf.dynamic_structure_manager.StructureManagerMixin", "bugfix.tf_cme_on_load.TwilightForestModMixin", "bugfix.refinedstorage.te_bug.ItemExternalStorageProviderMixin",