From db5e66823d18a24fbb6e17a9b790be351cf5fc30 Mon Sep 17 00:00:00 2001
From: modmuss <modmuss50@gmail.com>
Date: Sun, 16 Mar 2025 13:35:17 +0000
Subject: [PATCH] Add ServerPlayNetworking.reconfigure (#4493)

* Add ServerPlayNetworking.reconfigure

* Add ServerConfigurationNetworking.isReconfiguring, allowing tasks to be skipped if not required again.
---
 .../v1/ServerConfigurationNetworking.java     | 12 ++++++++++
 .../networking/v1/ServerPlayNetworking.java   | 22 +++++++++++++++++++
 .../ServerConfigurationNetworkAddon.java      |  9 ++++++++
 .../server/ServerPlayNetworkAddon.java        | 17 +++++++++++++-
 .../ServerPlayNetworkHandlerMixin.java        | 18 +++++++++++++++
 .../NetworkingConfigurationTest.java          |  4 ++++
 .../play/NetworkingPlayPacketTest.java        |  7 +++++-
 7 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/ServerConfigurationNetworking.java b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/ServerConfigurationNetworking.java
index 7f6c36cef..b0c6eb897 100644
--- a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/ServerConfigurationNetworking.java
+++ b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/ServerConfigurationNetworking.java
@@ -235,6 +235,18 @@ public final class ServerConfigurationNetworking {
 		return ((ServerCommonNetworkHandlerAccessor) handler).getServer();
 	}
 
+	/**
+	 * Returns true if the client has previously completed configuration, and has re-entered the configuration phase.
+	 *
+	 * @param handler the server configuration network handler
+	 * @return {@code true} if the client is reconfiguring
+	 */
+	public static boolean isReconfiguring(ServerConfigurationNetworkHandler handler) {
+		Objects.requireNonNull(handler, "Server configuration network handler cannot be null");
+
+		return ServerNetworkingImpl.getAddon(handler).isReconfiguring();
+	}
+
 	private ServerConfigurationNetworking() {
 	}
 
diff --git a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/ServerPlayNetworking.java b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/ServerPlayNetworking.java
index 23d415919..fdffa32cf 100644
--- a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/ServerPlayNetworking.java
+++ b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/api/networking/v1/ServerPlayNetworking.java
@@ -293,6 +293,28 @@ public final class ServerPlayNetworking {
 		player.networkHandler.sendPacket(createS2CPacket(payload));
 	}
 
+	/**
+	 * Put the player back into configuration phase and re-run all of the configuration tasks.
+	 *
+	 * @param player the player
+	 */
+	public static void reconfigure(ServerPlayerEntity player) {
+		Objects.requireNonNull(player, "Server player entity cannot be null");
+
+		reconfigure(player.networkHandler);
+	}
+
+	/**
+	 * Put the player back into configuration phase and re-run all of the configuration tasks.
+	 *
+	 * @param handler the network handler
+	 */
+	public static void reconfigure(ServerPlayNetworkHandler handler) {
+		Objects.requireNonNull(handler, "Server play network handler cannot be null");
+
+		ServerNetworkingImpl.getAddon(handler).reconfigure();
+	}
+
 	private ServerPlayNetworking() {
 	}
 
diff --git a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/server/ServerConfigurationNetworkAddon.java b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/server/ServerConfigurationNetworkAddon.java
index e3179cba0..34b5eb884 100644
--- a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/server/ServerConfigurationNetworkAddon.java
+++ b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/server/ServerConfigurationNetworkAddon.java
@@ -49,6 +49,7 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
 	private RegisterState registerState = RegisterState.NOT_SENT;
 	@Nullable
 	private String clientBrand = null;
+	private boolean isReconfiguring = false;
 
 	public ServerConfigurationNetworkAddon(ServerConfigurationNetworkHandler handler, MinecraftServer server) {
 		super(ServerNetworkingImpl.CONFIGURATION, ((ServerCommonNetworkHandlerAccessor) handler).getConnection(), "ServerConfigurationNetworkAddon for " + handler.getDebugProfile().getName());
@@ -188,6 +189,14 @@ public final class ServerConfigurationNetworkAddon extends AbstractChanneledNetw
 		return clientBrand;
 	}
 
+	public boolean isReconfiguring() {
+		return isReconfiguring;
+	}
+
+	public void setReconfiguring() {
+		isReconfiguring = true;
+	}
+
 	private enum RegisterState {
 		NOT_SENT,
 		SENT,
diff --git a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/server/ServerPlayNetworkAddon.java b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/server/ServerPlayNetworkAddon.java
index 2406133a0..b8fe3e8a1 100644
--- a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/server/ServerPlayNetworkAddon.java
+++ b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/server/ServerPlayNetworkAddon.java
@@ -41,9 +41,11 @@ import net.fabricmc.fabric.impl.networking.RegistrationPayload;
 public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<ServerPlayNetworking.PlayPayloadHandler<?>> {
 	private final ServerPlayNetworkHandler handler;
 	private final MinecraftServer server;
-	private boolean sentInitialRegisterPacket;
 	private final ServerPlayNetworking.Context context;
 
+	private boolean sentInitialRegisterPacket;
+	private boolean requestedReconfigure = false;
+
 	public ServerPlayNetworkAddon(ServerPlayNetworkHandler handler, ClientConnection connection, MinecraftServer server) {
 		super(ServerNetworkingImpl.PLAY, connection, "ServerPlayNetworkAddon for " + handler.player.getDisplayName());
 		this.handler = handler;
@@ -129,6 +131,19 @@ public final class ServerPlayNetworkAddon extends AbstractChanneledNetworkAddon<
 		return NetworkingImpl.isReservedCommonChannel(channelName);
 	}
 
+	public void reconfigure() {
+		if (requestedReconfigure) {
+			throw new IllegalStateException("Already requested reconfigure");
+		}
+
+		requestedReconfigure = true;
+		handler.reconfigure();
+	}
+
+	public boolean requestedReconfigure() {
+		return requestedReconfigure;
+	}
+
 	private record ContextImpl(MinecraftServer server, ServerPlayNetworkHandler handler, PacketSender responseSender) implements ServerPlayNetworking.Context {
 		private ContextImpl {
 			Objects.requireNonNull(server, "server");
diff --git a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerPlayNetworkHandlerMixin.java b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerPlayNetworkHandlerMixin.java
index 7fae773f1..192f527d8 100644
--- a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerPlayNetworkHandlerMixin.java
+++ b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerPlayNetworkHandlerMixin.java
@@ -16,6 +16,8 @@
 
 package net.fabricmc.fabric.mixin.networking;
 
+import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
+import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
 import org.spongepowered.asm.mixin.Mixin;
 import org.spongepowered.asm.mixin.Unique;
 import org.spongepowered.asm.mixin.injection.At;
@@ -23,14 +25,18 @@ import org.spongepowered.asm.mixin.injection.Inject;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 
 import net.minecraft.network.ClientConnection;
+import net.minecraft.network.NetworkState;
+import net.minecraft.network.listener.PacketListener;
 import net.minecraft.network.packet.c2s.common.CustomPayloadC2SPacket;
 import net.minecraft.server.MinecraftServer;
 import net.minecraft.server.network.ConnectedClientData;
 import net.minecraft.server.network.ServerCommonNetworkHandler;
+import net.minecraft.server.network.ServerConfigurationNetworkHandler;
 import net.minecraft.server.network.ServerPlayNetworkHandler;
 
 import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions;
 import net.fabricmc.fabric.impl.networking.UntrackedNetworkHandler;
+import net.fabricmc.fabric.impl.networking.server.ServerNetworkingImpl;
 import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkAddon;
 
 // We want to apply a bit earlier than other mods which may not use us in order to prevent refCount issues
@@ -60,6 +66,18 @@ abstract class ServerPlayNetworkHandlerMixin extends ServerCommonNetworkHandler
 		}
 	}
 
+	@WrapOperation(method = "onAcknowledgeReconfiguration", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ClientConnection;transitionInbound(Lnet/minecraft/network/NetworkState;Lnet/minecraft/network/listener/PacketListener;)V"))
+	private <T extends PacketListener> void onAcknowledgeReconfiguration(ClientConnection instance, NetworkState<T> state, T packetListener, Operation<Void> original) {
+		original.call(instance, state, packetListener);
+
+		ServerConfigurationNetworkHandler networkHandler = (ServerConfigurationNetworkHandler) packetListener;
+		ServerNetworkingImpl.getAddon(networkHandler).setReconfiguring();
+
+		if (addon.requestedReconfigure()) {
+			networkHandler.sendConfigurations();
+		}
+	}
+
 	@Override
 	public ServerPlayNetworkAddon getAddon() {
 		return this.addon;
diff --git a/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/configuration/NetworkingConfigurationTest.java b/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/configuration/NetworkingConfigurationTest.java
index f2f331428..f9ed8a09d 100644
--- a/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/configuration/NetworkingConfigurationTest.java
+++ b/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/configuration/NetworkingConfigurationTest.java
@@ -50,6 +50,10 @@ public class NetworkingConfigurationTest implements ModInitializer {
 		PayloadTypeRegistry.configurationC2S().register(ConfigurationStartPacket.ID, ConfigurationStartPacket.CODEC);
 
 		ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
+			if (ServerConfigurationNetworking.isReconfiguring(handler)) {
+				LOGGER.info("Reconfiguring client");
+			}
+
 			// You must check to see if the client can handle your config task
 			if (ServerConfigurationNetworking.canSend(handler, ConfigurationPacket.ID)) {
 				handler.addTask(new TestConfigurationTask("Example data"));
diff --git a/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/play/NetworkingPlayPacketTest.java b/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/play/NetworkingPlayPacketTest.java
index 3cc4305a0..767a6140e 100644
--- a/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/play/NetworkingPlayPacketTest.java
+++ b/fabric-networking-api-v1/src/testmod/java/net/fabricmc/fabric/test/networking/play/NetworkingPlayPacketTest.java
@@ -90,7 +90,12 @@ public final class NetworkingPlayPacketTest implements ModInitializer {
 					));
 					ServerPlayNetworking.getSender(ctx.getSource().getPlayer()).sendPacket(packet);
 					return Command.SINGLE_SUCCESS;
-				})));
+				}))
+				.then(literal("reconfigure").executes(ctx -> {
+					ServerPlayNetworking.reconfigure(ctx.getSource().getPlayer());
+					return Command.SINGLE_SUCCESS;
+				}))
+		);
 	}
 
 	@Override