diff --git a/server-deploy/overrides/sfcraft/announcements/2.3_update.txt b/server-deploy/overrides/sfcraft/announcements/2.3_update.txt index 8efcddf..3b8aaea 100644 --- a/server-deploy/overrides/sfcraft/announcements/2.3_update.txt +++ b/server-deploy/overrides/sfcraft/announcements/2.3_update.txt @@ -1,8 +1,8 @@ -§d§lSFCraft 已更新到 2.2.1。 +§d§lSFCraft 已更新到 2.3。 §a主要改动: - 修复了非被斩首怪物掉落头颅的问题 - 修复了 4K 图片无法下载到服务器中的问题。现在高分辨率图片会被降采样 - 修复了鞘翅撞进地狱门需要有空隙的问题 具体信息请查阅我们的更新日志: -§bhttps://sfclub.cc/~sfcraft/changelog/2026-08-08/ +§bhttps://sfclub.cc/~sfcraft/changelog/2026-08-24/ diff --git a/src/main/java/io/ib67/sfcraft/mixin/common/ConnectionMixin.java b/src/main/java/io/ib67/sfcraft/mixin/common/ConnectionMixin.java new file mode 100644 index 0000000..2a9c6fd --- /dev/null +++ b/src/main/java/io/ib67/sfcraft/mixin/common/ConnectionMixin.java @@ -0,0 +1,26 @@ +package io.ib67.sfcraft.mixin.common; + +import io.ib67.sfcraft.network.HandshakeAddress; +import net.minecraft.network.Connection; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +/** + * Connection 从握手一路存活到 play 阶段,拿来记玩家自报的进服地址。 + */ +@Mixin(Connection.class) +public class ConnectionMixin implements HandshakeAddress.Holder { + @Unique + private HandshakeAddress sfcraft$handshakeAddress; + + @Override + public @Nullable HandshakeAddress sfcraft$handshakeAddress() { + return sfcraft$handshakeAddress; + } + + @Override + public void sfcraft$setHandshakeAddress(@Nullable HandshakeAddress address) { + this.sfcraft$handshakeAddress = address; + } +} diff --git a/src/main/java/io/ib67/sfcraft/mixin/common/bridge/ServerCommonNetworkHandlerBridge.java b/src/main/java/io/ib67/sfcraft/mixin/common/bridge/ServerCommonNetworkHandlerBridge.java new file mode 100644 index 0000000..535e416 --- /dev/null +++ b/src/main/java/io/ib67/sfcraft/mixin/common/bridge/ServerCommonNetworkHandlerBridge.java @@ -0,0 +1,12 @@ +package io.ib67.sfcraft.mixin.common.bridge; + +import net.minecraft.network.Connection; +import net.minecraft.server.network.ServerCommonPacketListenerImpl; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ServerCommonPacketListenerImpl.class) +public interface ServerCommonNetworkHandlerBridge { + @Accessor("connection") + Connection sfcraft$getConnection(); +} diff --git a/src/main/java/io/ib67/sfcraft/mixin/server/ServerHandshakeNetworkHandlerMixin.java b/src/main/java/io/ib67/sfcraft/mixin/server/ServerHandshakeNetworkHandlerMixin.java index 6b96d35..9dc4de7 100644 --- a/src/main/java/io/ib67/sfcraft/mixin/server/ServerHandshakeNetworkHandlerMixin.java +++ b/src/main/java/io/ib67/sfcraft/mixin/server/ServerHandshakeNetworkHandlerMixin.java @@ -2,7 +2,9 @@ package io.ib67.sfcraft.mixin.server; import io.ib67.sfcraft.SFCraft; import io.ib67.sfcraft.callback.SFCallbacks; +import io.ib67.sfcraft.network.HandshakeAddress; import net.minecraft.network.Connection; +import net.minecraft.network.protocol.handshake.ClientIntentionPacket; import net.minecraft.network.protocol.status.ServerStatus; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerHandshakePacketListenerImpl; @@ -11,7 +13,9 @@ 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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ServerHandshakePacketListenerImpl.class) public abstract class ServerHandshakeNetworkHandlerMixin { @@ -19,6 +23,15 @@ public abstract class ServerHandshakeNetworkHandlerMixin { @Final private Connection connection; + /** + * 记下玩家实际用来进服的地址,房间 transfer 时把他送回同一个入口。 + */ + @Inject(method = "handleIntention", at = @At("HEAD")) + private void sf$captureAddress(ClientIntentionPacket packet, CallbackInfo ci) { + ((HandshakeAddress.Holder) connection) + .sfcraft$setHandshakeAddress(HandshakeAddress.sanitize(packet.hostName(), packet.port())); + } + @Redirect(method = "handleIntention", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;getStatus()Lnet/minecraft/network/protocol/status/ServerStatus;")) public ServerStatus getMetadata(MinecraftServer instance) { var result = SFCallbacks.MOTD.invoker().onMotd(instance,connection); diff --git a/src/main/java/io/ib67/sfcraft/module/RoomModule.java b/src/main/java/io/ib67/sfcraft/module/RoomModule.java index 7b497af..e5817ca 100644 --- a/src/main/java/io/ib67/sfcraft/module/RoomModule.java +++ b/src/main/java/io/ib67/sfcraft/module/RoomModule.java @@ -10,6 +10,7 @@ import io.ib67.kiwi.tuple.Tuple2; import io.ib67.sfcraft.ServerModule; import io.ib67.sfcraft.config.SFConfig; import io.ib67.sfcraft.inject.MinecraftServerSupplier; +import io.ib67.sfcraft.network.HandshakeAddress; import io.ib67.sfcraft.registry.RoomRegistry; import lombok.extern.log4j.Log4j2; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; @@ -20,7 +21,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.network.PacketSendListener; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.common.ClientboundStoreCookiePacket; -import net.minecraft.network.protocol.common.ClientboundTransferPacket; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.Identifier; import net.minecraft.server.level.TicketType; @@ -83,8 +83,9 @@ public class RoomModule extends ServerModule { return 0; } var networkHandler = player.connection; + var transfer = HandshakeAddress.transferPacketFor(player, config); networkHandler.send(new ClientboundStoreCookiePacket(ROOM_COOKIE, EMPTY), PacketSendListener.thenRun(() -> { - networkHandler.send(new ClientboundTransferPacket(config.domain, config.port)); + networkHandler.send(transfer); })); return 1; } diff --git a/src/main/java/io/ib67/sfcraft/network/HandshakeAddress.java b/src/main/java/io/ib67/sfcraft/network/HandshakeAddress.java new file mode 100644 index 0000000..6619da2 --- /dev/null +++ b/src/main/java/io/ib67/sfcraft/network/HandshakeAddress.java @@ -0,0 +1,61 @@ +package io.ib67.sfcraft.network; + +import io.ib67.sfcraft.config.SFConfig; +import io.ib67.sfcraft.mixin.common.bridge.ServerCommonNetworkHandlerBridge; +import net.minecraft.network.protocol.common.ClientboundTransferPacket; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.Nullable; + +/** + * 玩家在握手包里自报的服务器地址,也就是他实际用来进服的那个地址。 + * 由 {@code ServerHandshakeNetworkHandlerMixin} 记到 {@link net.minecraft.network.Connection} 上, + * transfer 时原样回显,让玩家重连回原本的入口而不是配置里写死的那个。 + */ +public record HandshakeAddress(String host, int port) { + /** + * 代理(BungeeCord/Velocity legacy forwarding、FML)会把转发数据拼在 hostName 后面,以 '\0' 分隔。 + */ + private static final char FORWARDING_SEPARATOR = '\0'; + + /** + * 挂在 {@link net.minecraft.network.Connection} 上的握手地址,由 ConnectionMixin 实现。 + */ + public interface Holder { + @Nullable + HandshakeAddress sfcraft$handshakeAddress(); + + void sfcraft$setHandshakeAddress(@Nullable HandshakeAddress address); + } + + /** + * 清洗握手包里的 hostName / port,不可用时返回 null。 + */ + public static @Nullable HandshakeAddress sanitize(String hostName, int port) { + if (hostName == null || port <= 0 || port > 65535) return null; + var host = hostName; + var separator = host.indexOf(FORWARDING_SEPARATOR); + if (separator >= 0) host = host.substring(0, separator); + // SRV 解析出来的 FQDN 可能带尾点 + while (host.endsWith(".")) host = host.substring(0, host.length() - 1); + host = host.trim(); + return host.isEmpty() ? null : new HandshakeAddress(host, port); + } + + /** + * 读玩家当前连接上记的握手地址,拿不到返回 null。 + */ + public static @Nullable HandshakeAddress of(ServerPlayer player) { + var connection = ((ServerCommonNetworkHandlerBridge) player.connection).sfcraft$getConnection(); + return connection instanceof Holder holder ? holder.sfcraft$handshakeAddress() : null; + } + + /** + * 优先把玩家送回他自己用的地址,拿不到才回退配置里的 domain / port。 + */ + public static ClientboundTransferPacket transferPacketFor(ServerPlayer player, SFConfig fallback) { + var address = of(player); + return address == null + ? new ClientboundTransferPacket(fallback.domain, fallback.port) + : new ClientboundTransferPacket(address.host(), address.port()); + } +} diff --git a/src/main/java/io/ib67/sfcraft/room/RoomTeleporter.java b/src/main/java/io/ib67/sfcraft/room/RoomTeleporter.java index 1bc746a..fe86ed5 100644 --- a/src/main/java/io/ib67/sfcraft/room/RoomTeleporter.java +++ b/src/main/java/io/ib67/sfcraft/room/RoomTeleporter.java @@ -6,13 +6,13 @@ import io.ib67.sfcraft.config.SFConfig; import io.ib67.sfcraft.inject.MinecraftServerSupplier; import io.ib67.sfcraft.module.RoomModule; import io.ib67.sfcraft.module.SignatureService; +import io.ib67.sfcraft.network.HandshakeAddress; import io.ib67.sfcraft.registry.RoomRegistry; import io.ib67.sfcraft.subserver.Room; import io.netty.buffer.Unpooled; import net.minecraft.network.PacketSendListener; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.common.ClientboundStoreCookiePacket; -import net.minecraft.network.protocol.common.ClientboundTransferPacket; import net.minecraft.resources.Identifier; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.CommonColors; @@ -44,6 +44,8 @@ public class RoomTeleporter { throw new IllegalStateException("world isn't exist"); } var networkHandler = player.connection; + // 趁连接还在 play 阶段先取好目标地址 + var transfer = HandshakeAddress.transferPacketFor(player, config); networkHandler.switchToConfig(); var request = new RequestedRoom(room.getServerIdentifier(), name, finalUuid); var requestBuf = Unpooled.buffer(); @@ -58,7 +60,7 @@ public class RoomTeleporter { ) ); networkHandler.send(new ClientboundStoreCookiePacket(ROOM_COOKIE, cookie), PacketSendListener.thenRun(() -> { - networkHandler.send(new ClientboundTransferPacket(config.domain, config.port)); + networkHandler.send(transfer); })); } } diff --git a/src/main/resources/sfcraft.mixins.json b/src/main/resources/sfcraft.mixins.json index 6a70365..54d8136 100644 --- a/src/main/resources/sfcraft.mixins.json +++ b/src/main/resources/sfcraft.mixins.json @@ -9,11 +9,13 @@ "client": [ ], "server": [ + "common.ConnectionMixin", "common.EntityMixin", "common.MobEntityMixin", "common.PlayerEntityMixin", "common.bridge.EntityBridge", "common.bridge.ServerChunkManagerBridge", + "common.bridge.ServerCommonNetworkHandlerBridge", "common.bridge.ServerPlayerBridge", "common.bridge.ServerWorldBridge", "common.fixes.EndermanEntityMixin",