diff --git a/README.md b/README.md index 6fc4939643..48bb6f0ed7 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,14 @@ A list of small tweaks I made: - The proxy setting is in the `auth-proxy` section in `velocity.toml`, of course you know how to fill it - Supported proxy types: `socks4`, `socks5`, `http` - If enabled, velocity will firstly try authenticating with the given proxy, if failed it will try again without the proxy -- Implement UUID rewrite for TabList packets, like what bungeecord does - - So `online-mode=true` on velocity + `online-mode=false` on backend mc servers + `player-info-forwarding-mode=none` can correctly work +- Implement player UUID rewrite, like what bungeecord does. + Make setup with online velocity + offline Minecraft server work correctly + (`online-mode=true` on velocity + `online-mode=false` on backend mc servers + `player-info-forwarding-mode=none`) + - TabList packets rewrite + - Affects `LegacyPlayerListItemPacket`, `UpsertPlayerInfoPacket`, `RemovePlayerInfoPacket` packets + - Rewrites player UUIDs inside those packets to their UUIDs in the velocity server + - Entity packets rewrite + - Rewrites player UUIDs inside player creation packets and spectator teleport packets, to their UUIDs in the velocity server # Velocity diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index 04e65c849b..c021121435 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -81,6 +81,14 @@ public interface Player extends */ UUID getUniqueId(); + // [fallen's fork] player uuid rewrite: store offline uuid for reuse + /** + * Returns the player's offline UUID. + * + * @return the UUID + */ + UUID getOfflineUuid(); + /** * Returns the server that the player is currently connected to. * diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java index b36d9f0ab4..1049d74370 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java @@ -78,6 +78,9 @@ import com.velocitypowered.proxy.protocol.packet.title.TitleSubtitlePacket; import com.velocitypowered.proxy.protocol.packet.title.TitleTextPacket; import com.velocitypowered.proxy.protocol.packet.title.TitleTimesPacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpawnEntityS2CPacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpawnPlayerS2CPacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpectatorTeleportC2SPacket; import io.netty.buffer.ByteBuf; /** @@ -309,6 +312,20 @@ default boolean handle(ActiveFeaturesPacket packet) { return false; } + // [fallen's fork] player uuid rewrite - entity packet + default boolean handle(UrSpawnPlayerS2CPacket packet) { + return false; + } + + default boolean handle(UrSpawnEntityS2CPacket packet) { + return false; + } + + default boolean handle(UrSpectatorTeleportC2SPacket packet) { + return false; + } + // [fallen's fork] ends + default boolean handle(FinishedUpdatePacket packet) { return false; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index b6833212aa..4ddd31de61 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -66,6 +66,9 @@ import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.EntityPacketUuidRewriter; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpawnEntityS2CPacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpawnPlayerS2CPacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.tablist.TabListUuidRewriter; import io.netty.buffer.ByteBuf; @@ -322,8 +325,8 @@ public boolean handle(TabCompleteResponsePacket packet) { @Override public boolean handle(LegacyPlayerListItemPacket packet) { - // [fallen's fork] tab list entry uuid rewrite: impl - TabListUuidRewriter.rewrite(serverConn.getPlayer(), packet); + // [fallen's fork] player uuid rewrite - tab list entry: impl + TabListUuidRewriter.rewrite(server, packet); serverConn.getPlayer().getTabList().processLegacy(packet); return false; @@ -331,8 +334,8 @@ public boolean handle(LegacyPlayerListItemPacket packet) { @Override public boolean handle(UpsertPlayerInfoPacket packet) { - // [fallen's fork] tab list entry uuid rewrite: impl - TabListUuidRewriter.rewrite(serverConn.getPlayer(), packet); + // [fallen's fork] player uuid rewrite - tab list entry: impl + TabListUuidRewriter.rewrite(server, packet); serverConn.getPlayer().getTabList().processUpdate(packet); return false; @@ -340,13 +343,27 @@ public boolean handle(UpsertPlayerInfoPacket packet) { @Override public boolean handle(RemovePlayerInfoPacket packet) { - // [fallen's fork] tab list entry uuid rewrite: impl - TabListUuidRewriter.rewrite(serverConn.getPlayer(), packet); + // [fallen's fork] player uuid rewrite - tab list entry: impl + TabListUuidRewriter.rewrite(server, packet); serverConn.getPlayer().getTabList().processRemove(packet); return false; } + // [fallen's fork] player uuid rewrite - entity packet + @Override + public boolean handle(UrSpawnPlayerS2CPacket packet) { + EntityPacketUuidRewriter.rewriteS2C(server, serverConn.getPlayer(), packet); + return false; + } + + @Override + public boolean handle(UrSpawnEntityS2CPacket packet) { + EntityPacketUuidRewriter.rewriteS2C(server, serverConn.getPlayer(), packet); + return false; + } + // [fallen's fork] ends + @Override public boolean handle(AvailableCommandsPacket commands) { RootCommandNode rootNode = commands.getRootNode(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index a76f054e72..f1a03bd1f7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -71,6 +71,8 @@ import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerCommandPacket; import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket; import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.EntityPacketUuidRewriter; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpectatorTeleportC2SPacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.util.CharacterUtil; import io.netty.buffer.ByteBuf; @@ -402,6 +404,14 @@ public boolean handle(ResourcePackResponsePacket packet) { packet.getStatus())); } + // [fallen's fork] player uuid rewrite - entity packet + @Override + public boolean handle(UrSpectatorTeleportC2SPacket packet) { + EntityPacketUuidRewriter.rewriteC2S(server, player, packet); + return false; + } + // [fallen's fork] ends + @Override public boolean handle(FinishedUpdatePacket packet) { // Complete client switch diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index e7d1d7f888..78d47c7b23 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -188,7 +188,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, private final ChatQueue chatQueue; private final ChatBuilderFactory chatBuilderFactory; - // [fallen's fork] tab list entry uuid rewrite: store offline uuid for reuse + // [fallen's fork] player uuid rewrite: store offline uuid for reuse private final UUID offlineUuid; ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, @@ -214,7 +214,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, this.chatBuilderFactory = new ChatBuilderFactory(this.getProtocolVersion()); this.resourcePackHandler = ResourcePackHandler.create(this, server); - // [fallen's fork] tab list entry uuid rewrite: store offline uuid for reuse + // [fallen's fork] player uuid rewrite: store offline uuid for reuse this.offlineUuid = UuidUtils.generateOfflinePlayerUuid(this.getUsername()); } @@ -267,7 +267,8 @@ public UUID getUniqueId() { return profile.getId(); } - // [fallen's fork] tab list entry uuid rewrite: store offline uuid for reuse + // [fallen's fork] player uuid rewrite: store offline uuid for reuse + @Override public UUID getOfflineUuid() { return offlineUuid; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 41d444a36b..53794831b2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -108,6 +108,9 @@ import com.velocitypowered.proxy.protocol.packet.title.TitleSubtitlePacket; import com.velocitypowered.proxy.protocol.packet.title.TitleTextPacket; import com.velocitypowered.proxy.protocol.packet.title.TitleTimesPacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpawnEntityS2CPacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpawnPlayerS2CPacket; +import com.velocitypowered.proxy.protocol.packet.uuidrewrite.UrSpectatorTeleportC2SPacket; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; @@ -670,6 +673,30 @@ public enum StateRegistry { map(0x7A, MINECRAFT_1_21, false)); clientbound.register(ClientboundServerLinksPacket.class, ClientboundServerLinksPacket::new, map(0x7B, MINECRAFT_1_21, false)); + + // [fallen's fork] player uuid rewrite - entity packet + clientbound.register(UrSpawnPlayerS2CPacket.class, UrSpawnPlayerS2CPacket::new, + map(0x05, MINECRAFT_1_8, false), + map(0x04, MINECRAFT_1_16, false), + map(0x02, MINECRAFT_1_19, false), + map(0x03, MINECRAFT_1_19_4, false), + map(-1, MINECRAFT_1_20_2, false)); + clientbound.register(UrSpawnEntityS2CPacket.class, UrSpawnEntityS2CPacket::new, + map(0x01, MINECRAFT_1_20_2, false)); + serverbound.register(UrSpectatorTeleportC2SPacket.class, UrSpectatorTeleportC2SPacket::new, + map(0x18, MINECRAFT_1_8, false), + map(0x1B, MINECRAFT_1_9, false), + map(0x1E, MINECRAFT_1_12, false), + map(0x28, MINECRAFT_1_13, false), + map(0x2B, MINECRAFT_1_14, false), + map(0x2C, MINECRAFT_1_16, false), + map(0x2D, MINECRAFT_1_16_2, false), + map(0x2F, MINECRAFT_1_19, false), + map(0x30, MINECRAFT_1_19_1, false), + map(0x33, MINECRAFT_1_20_2, false), + map(0x34, MINECRAFT_1_20_3, false), + map(0x37, MINECRAFT_1_20_5, false)); + // [fallen's fork] ends } }, LOGIN { @@ -804,6 +831,11 @@

void register(Class

clazz, Supplier

packetSupp "Next mapping version (%s) should be lower then current (%s)", to, from)); } + // [fallen's fork] support skipping id=-1 + if (current.id == -1) { + break; + } + for (ProtocolVersion protocol : EnumSet.range(from, to)) { if (protocol == to && next != current) { break; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/EntityPacketUuidRewriter.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/EntityPacketUuidRewriter.java new file mode 100644 index 0000000000..bb1d2b5cff --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/EntityPacketUuidRewriter.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet.uuidrewrite; + +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.config.PlayerInfoForwarding; + +import java.util.UUID; +import java.util.function.Function; + +public class EntityPacketUuidRewriter { + + public static void rewriteS2C(VelocityServer server, Player connectionPlayer, PacketToRewriteEntityUuid packet) { + rewrite(server, connectionPlayer, packet, Player::getOfflineUuid, Player::getUniqueId); + } + + public static void rewriteC2S(VelocityServer server, Player connectionPlayer, PacketToRewriteEntityUuid packet) { + rewrite(server, connectionPlayer, packet, Player::getUniqueId, Player::getOfflineUuid); + } + + private static void rewrite(VelocityServer server, Player connectionPlayer, PacketToRewriteEntityUuid packet, + Function uuidFrom, Function uuidTo) { + var config = server.getConfiguration(); + if (!(config.isOnlineMode() && config.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE)) { + return; + } + if (!packet.isPlayer()) { + return; + } + UUID uuid = packet.getEntityUuid(); + if (uuid == null) { + return; + } + + // FIXME: inefficient implementation using for loop. Replace it with map lookup? + for (Player player : server.getAllPlayers()) { + if (uuidFrom.apply(player).equals(uuid)) { + UUID newUuid = uuidTo.apply(player); + if (!newUuid.equals(uuid)) { + packet.setEntityUuid(newUuid); + } + break; + } + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/PacketToRewriteEntityUuid.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/PacketToRewriteEntityUuid.java new file mode 100644 index 0000000000..303d6d36d4 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/PacketToRewriteEntityUuid.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet.uuidrewrite; + +import java.util.UUID; + +public interface PacketToRewriteEntityUuid { + + boolean isPlayer(); + + UUID getEntityUuid(); + + void setEntityUuid(UUID entityUuid); +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpawnEntityS2CPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpawnEntityS2CPacket.java new file mode 100644 index 0000000000..ff176e593c --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpawnEntityS2CPacket.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet.uuidrewrite; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; + +import java.util.UUID; + +// [fallen's fork] player uuid rewrite - entity packet +public class UrSpawnEntityS2CPacket implements MinecraftPacket, PacketToRewriteEntityUuid { + + private int entityId; + private UUID entityUuid; + private int entityType; + private byte[] remainingBuf; + + @Override + public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + this.entityId = ProtocolUtils.readVarInt(buf); + this.entityUuid = ProtocolUtils.readUuid(buf); + this.entityType = ProtocolUtils.readVarInt(buf); + this.remainingBuf = new byte[buf.readableBytes()]; + buf.readBytes(this.remainingBuf); + } + + @Override + public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, this.entityId); + ProtocolUtils.writeUuid(buf, this.entityUuid); + ProtocolUtils.writeVarInt(buf, this.entityType); + buf.writeBytes(this.remainingBuf); + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + + @Override + public boolean isPlayer() { + // https://wiki.vg/Entity_metadata#Mobs + int playerEntityType = 122; // mc1.20.2 + return this.entityType == playerEntityType; + } + + @Override + public UUID getEntityUuid() { + return this.entityUuid; + } + + @Override + public void setEntityUuid(UUID entityUuid) { + this.entityUuid = entityUuid; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpawnPlayerS2CPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpawnPlayerS2CPacket.java new file mode 100644 index 0000000000..c8f1beff65 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpawnPlayerS2CPacket.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet.uuidrewrite; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; + +import java.util.UUID; + +// [fallen's fork] player uuid rewrite - entity packet +public class UrSpawnPlayerS2CPacket implements MinecraftPacket, PacketToRewriteEntityUuid { + + private int entityId; + private UUID entityUuid; + private byte[] remainingBuf; + + @Override + public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + this.entityId = ProtocolUtils.readVarInt(buf); + this.entityUuid = ProtocolUtils.readUuid(buf); + this.remainingBuf = new byte[buf.readableBytes()]; + buf.readBytes(this.remainingBuf); + } + + @Override + public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, this.entityId); + ProtocolUtils.writeUuid(buf, this.entityUuid); + buf.writeBytes(this.remainingBuf); + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + + @Override + public boolean isPlayer() { + return true; + } + + @Override + public UUID getEntityUuid() { + return this.entityUuid; + } + + @Override + public void setEntityUuid(UUID entityUuid) { + this.entityUuid = entityUuid; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpectatorTeleportC2SPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpectatorTeleportC2SPacket.java new file mode 100644 index 0000000000..80ef95c6ad --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/uuidrewrite/UrSpectatorTeleportC2SPacket.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet.uuidrewrite; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; + +import java.util.UUID; + +public class UrSpectatorTeleportC2SPacket implements MinecraftPacket, PacketToRewriteEntityUuid { + + private UUID targetUuid; + + @Override + public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + this.targetUuid = ProtocolUtils.readUuid(buf); + } + + @Override + public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeUuid(buf, this.targetUuid); + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + + @Override + public boolean isPlayer() { + return true; // assuming yes + } + + @Override + public UUID getEntityUuid() { + return this.targetUuid; + } + + @Override + public void setEntityUuid(UUID entityUuid) { + this.targetUuid = entityUuid; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/TabListUuidRewriter.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/TabListUuidRewriter.java index 6cd8a8f835..aa4e77edc1 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/TabListUuidRewriter.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/TabListUuidRewriter.java @@ -17,23 +17,39 @@ package com.velocitypowered.proxy.tablist; -import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.protocol.packet.LegacyPlayerListItemPacket; import com.velocitypowered.proxy.protocol.packet.RemovePlayerInfoPacket; import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket; +import java.util.Optional; +import java.util.UUID; import java.util.stream.Collectors; /** - * [fallen's fork] tab list entry uuid rewrite: rewrite logic. + * [fallen's fork] player uuid rewrite - tab list entry: rewrite logic. */ public class TabListUuidRewriter { + /** + * Common use case: input uuid is an offline uuid, and we want to get its online uuid. + */ + private static Optional getRealUuid(VelocityServer server, UUID playerUuid) { + for (Player player : server.getAllPlayers()) { + if (player.getOfflineUuid().equals(playerUuid)) { + return Optional.of(player.getUniqueId()); + } + } + return Optional.empty(); + } + /** * Rewrite uuid for a LegacyPlayerListItem packet. */ - public static void rewrite(ConnectedPlayer player, LegacyPlayerListItemPacket packet) { + public static void rewrite(VelocityServer server, LegacyPlayerListItemPacket packet) { packet.getItems().replaceAll(item -> { - if (player.getOfflineUuid().equals(item.getUuid())) { - var newItem = new LegacyPlayerListItemPacket.Item(player.getUniqueId()); + var realUuid = getRealUuid(server, item.getUuid()); + if (realUuid.isPresent() && !realUuid.get().equals(item.getUuid())) { + var newItem = new LegacyPlayerListItemPacket.Item(realUuid.get()); newItem.setName(item.getName()); newItem.setProperties(item.getProperties()); @@ -52,12 +68,13 @@ public static void rewrite(ConnectedPlayer player, LegacyPlayerListItemPacket pa /** * Rewrite uuid for a UpsertPlayerInfo packet. */ - public static void rewrite(ConnectedPlayer player, UpsertPlayerInfoPacket packet) { + public static void rewrite(VelocityServer server, UpsertPlayerInfoPacket packet) { packet.getEntries().replaceAll(entry -> { - if (player.getOfflineUuid().equals(entry.getProfileId())) { - var newEntry = new UpsertPlayerInfoPacket.Entry(player.getUniqueId()); + var realUuid = getRealUuid(server, entry.getProfileId()); + if (realUuid.isPresent() && !realUuid.get().equals(entry.getProfileId())) { + var newEntry = new UpsertPlayerInfoPacket.Entry(realUuid.get()); - newEntry.setProfile(player.getGameProfile()); + newEntry.setProfile(entry.getProfile()); newEntry.setListed(entry.isListed()); newEntry.setLatency(entry.getLatency()); newEntry.setGameMode(entry.getGameMode()); @@ -74,14 +91,9 @@ public static void rewrite(ConnectedPlayer player, UpsertPlayerInfoPacket packet /** * Rewrite uuid for a RemovePlayerInfo packet. */ - public static void rewrite(ConnectedPlayer player, RemovePlayerInfoPacket packet) { + public static void rewrite(VelocityServer server, RemovePlayerInfoPacket packet) { var newProfiles = packet.getProfilesToRemove().stream() - .map(uuid -> { - if (player.getOfflineUuid().equals(uuid)) { - uuid = player.getUniqueId(); - } - return uuid; - }) + .map(uuid -> getRealUuid(server, uuid).orElse(uuid)) .collect(Collectors.toList()); packet.setProfilesToRemove(newProfiles); }