diff --git a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/ClientPacketListenerMixin.java b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/ClientPacketListenerMixin.java new file mode 100644 index 00000000..2e2d6b53 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/ClientPacketListenerMixin.java @@ -0,0 +1,36 @@ +package org.embeddedt.modernfix.common.mixin.perf.suspend_integrated_server_during_load; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.duck.suspend_integrated_server_during_load.IDeferrableIntegratedServer; +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.callback.CallbackInfo; + +@Mixin(ClientPacketListener.class) +@ClientOnlyMixin +public class ClientPacketListenerMixin { + @Shadow + @Final + private Minecraft minecraft; + + @Inject(method = "handleCustomPayload", at = @At("HEAD"), cancellable = true) + private void detectClientLoadSentinel(ClientboundCustomPayloadPacket packet, CallbackInfo ci) { + if (packet.getIdentifier().equals(IDeferrableIntegratedServer.CLIENT_LOAD_SENTINEL)) { + // Important: flag must be changed on the client thread, as later packets can start decoding + // while earlier ones are still being applied. + this.minecraft.executeIfPossible(() -> { + packet.getData().release(); + if (this.minecraft.hasSingleplayerServer()) { + ((IDeferrableIntegratedServer)this.minecraft.getSingleplayerServer()).mfix$markClientLoadFinished(); + } + }); + ci.cancel(); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/IntegratedServerMixin.java b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/IntegratedServerMixin.java new file mode 100644 index 00000000..00791452 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/IntegratedServerMixin.java @@ -0,0 +1,33 @@ +package org.embeddedt.modernfix.common.mixin.perf.suspend_integrated_server_during_load; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.client.Minecraft; +import net.minecraft.client.server.IntegratedServer; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.duck.suspend_integrated_server_during_load.IDeferrableIntegratedServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.concurrent.atomic.AtomicBoolean; + +@Mixin(IntegratedServer.class) +@ClientOnlyMixin +public abstract class IntegratedServerMixin implements IDeferrableIntegratedServer { + private final AtomicBoolean mfix$hasPrimaryClientJoined = new AtomicBoolean(false); + + /** + * @author embeddedt + * @reason Wait to be finished processing all expensive packets (recipes, tags, etc.) + * before continuing to tick the integrated server. + */ + @WrapOperation(method = "tickServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;isPaused()Z", ordinal = 0)) + private boolean preventTicks(Minecraft instance, Operation original) { + return !mfix$hasPrimaryClientJoined.get() || original.call(instance); + } + + @Override + public void mfix$markClientLoadFinished() { + mfix$hasPrimaryClientJoined.set(true); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/PlayerListMixin.java b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/PlayerListMixin.java new file mode 100644 index 00000000..ad802156 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/suspend_integrated_server_during_load/PlayerListMixin.java @@ -0,0 +1,26 @@ +package org.embeddedt.modernfix.common.mixin.perf.suspend_integrated_server_during_load; + +import io.netty.buffer.Unpooled; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.duck.suspend_integrated_server_during_load.IDeferrableIntegratedServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerList.class) +@ClientOnlyMixin +public class PlayerListMixin { + @Inject(method = "placeNewPlayer", at = @At("RETURN")) + private void sendConfigFinishedSentinelPacket(Connection connection, ServerPlayer player, CallbackInfo ci) { + if (connection.isMemoryConnection()) { + FriendlyByteBuf friendlybytebuf = new FriendlyByteBuf(Unpooled.buffer()); + player.connection.send(new ClientboundCustomPayloadPacket(IDeferrableIntegratedServer.CLIENT_LOAD_SENTINEL, friendlybytebuf)); + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/duck/suspend_integrated_server_during_load/IDeferrableIntegratedServer.java b/src/main/java/org/embeddedt/modernfix/duck/suspend_integrated_server_during_load/IDeferrableIntegratedServer.java new file mode 100644 index 00000000..452fe0fa --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/duck/suspend_integrated_server_during_load/IDeferrableIntegratedServer.java @@ -0,0 +1,10 @@ +package org.embeddedt.modernfix.duck.suspend_integrated_server_during_load; + +import net.minecraft.resources.ResourceLocation; +import org.embeddedt.modernfix.ModernFix; + +public interface IDeferrableIntegratedServer { + ResourceLocation CLIENT_LOAD_SENTINEL = new ResourceLocation(ModernFix.MODID, "mark_client_load_finished"); + + void mfix$markClientLoadFinished(); +}