From bc5b85efcca5460bcfa3a9020b0314ed364c3ec5 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Tue, 21 Feb 2023 22:24:50 -0500 Subject: [PATCH] Move 441 chunk loading to after join game packets are sent on integrated server --- .../embeddedt/modernfix/ModernFixClient.java | 93 +++++++++++++++++++ .../core/config/ModernFixEarlyConfig.java | 1 + .../MinecraftServerMixin.java | 40 ++++++++ .../screen/DeferredLevelLoadingScreen.java | 27 ++++++ .../resources/META-INF/accesstransformer.cfg | 5 +- src/main/resources/modernfix.mixins.json | 3 +- 6 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java create mode 100644 src/main/java/org/embeddedt/modernfix/screen/DeferredLevelLoadingScreen.java diff --git a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java index 33974e37..9f8db69f 100644 --- a/src/main/java/org/embeddedt/modernfix/ModernFixClient.java +++ b/src/main/java/org/embeddedt/modernfix/ModernFixClient.java @@ -1,14 +1,33 @@ package org.embeddedt.modernfix; +import it.unimi.dsi.fastutil.longs.LongIterator; +import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.ConnectScreen; +import net.minecraft.client.gui.screens.LevelLoadingScreen; +import net.minecraft.client.gui.screens.ProgressScreen; import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.server.IntegratedServer; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.*; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.util.Unit; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.ForcedChunksSavedData; import net.minecraftforge.client.event.GuiOpenEvent; +import net.minecraftforge.common.world.ForgeChunkManager; import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; +import net.minecraftforge.fml.server.ServerLifecycleHooks; +import org.embeddedt.modernfix.screen.DeferredLevelLoadingScreen; import java.lang.management.ManagementFactory; +import java.util.concurrent.locks.LockSupport; +import java.util.function.BooleanSupplier; public class ModernFixClient { @@ -42,4 +61,78 @@ public class ModernFixClient { } } + private boolean hasFirstPlayerJoined = false; + + @SubscribeEvent + public void serverWillStart(FMLServerAboutToStartEvent event) { + hasFirstPlayerJoined = false; + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) { + if(!hasFirstPlayerJoined && integratedWorldLoadListener != null) { + hasFirstPlayerJoined = true; + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + if(server instanceof IntegratedServer) { + handleInitialChunkLoad(); + } + } + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onWorldShow(GuiOpenEvent event) { + if(ServerLifecycleHooks.getCurrentServer() instanceof IntegratedServer && integratedWorldLoadListener != null) { + if(event.getGui() == null && Minecraft.getInstance().level != null) { + /* this means the world is being displayed, check if 441 initialized */ + ServerChunkCache provider = ServerLifecycleHooks.getCurrentServer().overworld().getChunkSource(); + BooleanSupplier worldLoadDone = () -> provider.getTickingGenerated() >= 441; + if(!worldLoadDone.getAsBoolean()) { + DeferredLevelLoadingScreen newScreen = new DeferredLevelLoadingScreen(Minecraft.getInstance().progressListener.get(), worldLoadDone); + event.setGui(newScreen); + } + } else if(event.getGui() instanceof LevelLoadingScreen && Minecraft.getInstance().level == null) { + ProgressScreen loadscreen = new ProgressScreen(); + loadscreen.progressStartNoAbort(new TranslatableComponent("connect.joining")); + event.setGui(loadscreen); + } + } + } + + public static ChunkProgressListener integratedWorldLoadListener; + + private void handleInitialChunkLoad() { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + ServerLevel overworld = server.overworld(); + ServerChunkCache provider = overworld.getChunkSource(); + provider.getLightEngine().setTaskPerBatch(500); + provider.addRegionTicket(TicketType.START, new ChunkPos(overworld.getSharedSpawnPos()), 11, Unit.INSTANCE); + while(provider.getTickingGenerated() < 441) { + server.runAllTasks(); + Thread.yield(); + LockSupport.parkNanos("waiting for world load", 100000L); + server.nextTickTime = Util.getMillis() + 10; + } + for(ServerLevel serverworld1 : server.getAllLevels()) { + ForcedChunksSavedData forcedchunkssavedata = serverworld1.getDataStorage().get(ForcedChunksSavedData::new, "chunks"); + if (forcedchunkssavedata != null) { + LongIterator longiterator = forcedchunkssavedata.getChunks().iterator(); + + while(longiterator.hasNext()) { + long i = longiterator.nextLong(); + ChunkPos chunkpos = new ChunkPos(i); + serverworld1.getChunkSource().updateChunkForced(chunkpos, true); + } + + ForgeChunkManager.reinstatePersistentChunks(serverworld1, forcedchunkssavedata); + } + } + server.runAllTasks(); + server.nextTickTime = Util.getMillis() + 10; + provider.getLightEngine().setTaskPerBatch(5); + if(integratedWorldLoadListener != null) { + integratedWorldLoadListener.stop(); + integratedWorldLoadListener = null; + } + } + } 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 d1a1ceb3..c94b8b24 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -46,6 +46,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.async_locator", true); this.addMixinRule("perf.faster_texture_stitching", true); this.addMixinRule("perf.kubejs", true); + this.addMixinRule("perf.faster_singleplayer_load", true); /* Keep this off if JEI isn't installed to prevent breaking vanilla gameplay */ this.addMixinRule("perf.blast_search_trees", FMLLoader.getLoadingModList().getModFileById("jei") != null); this.addMixinRule("safety", true); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java new file mode 100644 index 00000000..2596c9ce --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/faster_singleplayer_load/MinecraftServerMixin.java @@ -0,0 +1,40 @@ +package org.embeddedt.modernfix.mixin.perf.faster_singleplayer_load; + +import net.minecraft.Util; +import net.minecraft.client.server.IntegratedServer; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import org.embeddedt.modernfix.ModernFixClient; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(MinecraftServer.class) +public abstract class MinecraftServerMixin { + @Shadow protected long nextTickTime; + + @Shadow public abstract ServerLevel overworld(); + + @Shadow protected abstract void updateMobSpawningFlags(); + + /** + * @author embeddedt + * @reason defer the 441 chunk load until *after* join game packets are sent to the client, in order to allow + * mods that process advancements, etc. to work on that at the same time + */ + @Inject(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getChunkSource()Lnet/minecraft/server/level/ServerChunkCache;", ordinal = 0), cancellable = true) + private void skipInitialChunkLoad(ChunkProgressListener arg, CallbackInfo ci) { + if(((Object)this) instanceof IntegratedServer) { + ci.cancel(); + ModernFixClient.integratedWorldLoadListener = arg; + this.nextTickTime = Util.getMillis(); + this.overworld().getChunkSource().getLightEngine().setTaskPerBatch(5); + this.updateMobSpawningFlags(); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/screen/DeferredLevelLoadingScreen.java b/src/main/java/org/embeddedt/modernfix/screen/DeferredLevelLoadingScreen.java new file mode 100644 index 00000000..cd49687a --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/screen/DeferredLevelLoadingScreen.java @@ -0,0 +1,27 @@ +package org.embeddedt.modernfix.screen; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.screens.LevelLoadingScreen; +import net.minecraft.server.level.progress.StoringChunkProgressListener; + +import java.util.function.BooleanSupplier; + +public class DeferredLevelLoadingScreen extends LevelLoadingScreen { + private final BooleanSupplier worldLoadFinished; + public DeferredLevelLoadingScreen(StoringChunkProgressListener arg, BooleanSupplier worldLoadFinished) { + super(arg); + this.worldLoadFinished = worldLoadFinished; + } + + @Override + public void tick() { + super.tick(); + if(this.worldLoadFinished.getAsBoolean()) + this.onClose(); + } + + @Override + public void renderBackground(PoseStack matrixStack, int vOffset) { + renderDirtBackground(vOffset); + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 0d376b05..47aa894e 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -5,4 +5,7 @@ public net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache public net.minecraft.world.phys.shapes.VoxelShape (Lnet/minecraft/util/math/shapes/VoxelShapePart;)V # public net.minecraft.client.resources.model.ModelBakery$BlockStateDefinitionException public net.minecraft.client.renderer.model.ModelBakery field_217849_F # unbakedCache -public net.minecraft.client.renderer.texture.Stitcher$Holder \ No newline at end of file +public net.minecraft.client.renderer.texture.Stitcher$Holder +public net.minecraft.util.concurrent.ThreadTaskExecutor func_213160_bf()V # runAllTasks +public net.minecraft.server.MinecraftServer field_211151_aa # nextTickTime +public net.minecraft.client.Minecraft field_213277_ad # progressListener \ No newline at end of file diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 80a036d6..f7a43a5a 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -83,7 +83,8 @@ "perf.cache_model_materials.MultipartMixin", "perf.faster_texture_stitching.StitcherMixin", "bugfix.packet_leak.ClientPlayNetHandlerMixin", - "bugfix.packet_leak.SCustomPayloadPlayPacketMixin" + "bugfix.packet_leak.SCustomPayloadPlayPacketMixin", + "perf.faster_singleplayer_load.MinecraftServerMixin" ], "injectors": { "defaultRequire": 1