From 9f6d60ea04e053e73b14b089e2ea8b3e2b87b56f Mon Sep 17 00:00:00 2001 From: MATRIX-feather Date: Fri, 15 Dec 2023 12:46:58 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E6=9E=84=E5=BB=BA=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=97=B6=E5=90=8C=E6=A0=B7=E4=B9=9F=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E8=BD=BD=E5=85=B7=E4=BF=A1=E6=81=AF=20misc:=20?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E4=BC=AA=E8=A3=85=E6=97=B6=E4=B9=9F=E7=94=A8?= =?UTF-8?q?PacketFactory=E4=B8=AD=E7=9A=84=E6=96=B9=E6=B3=95=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E7=9B=B8=E5=85=B3=E6=95=B0=E6=8D=AE=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/ServerDisguiseWrapper.java | 3 +- .../renderer/network/DisplayParameters.java | 27 +++++++ .../renderer/network/PacketFactory.java | 26 +++++-- .../datawatcher/values/PlayerValues.java | 4 +- .../watchers/types/PlayerWatcher.java | 40 +++++++++- .../network/listeners/SpawnPacketHandler.java | 77 +++++++------------ .../network/registries/EntryIndex.java | 4 +- .../network/registries/RenderRegistry.java | 2 +- 8 files changed, 115 insertions(+), 68 deletions(-) diff --git a/src/main/java/xiamomc/morph/backends/server/ServerDisguiseWrapper.java b/src/main/java/xiamomc/morph/backends/server/ServerDisguiseWrapper.java index d8de7a23..12e7db97 100644 --- a/src/main/java/xiamomc/morph/backends/server/ServerDisguiseWrapper.java +++ b/src/main/java/xiamomc/morph/backends/server/ServerDisguiseWrapper.java @@ -26,7 +26,6 @@ import xiamomc.morph.utilities.NbtUtils; import java.util.Objects; -import java.util.UUID; public class ServerDisguiseWrapper extends DisguiseWrapper { @@ -174,7 +173,7 @@ public void setDisguiseName(String name) this.instance.name = name; if (bindingWatcher != null) - bindingWatcher.write(EntryIndex.CUSTOM_NAME, name); + bindingWatcher.write(EntryIndex.DISGUISE_NAME, name); } @Override diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/DisplayParameters.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/DisplayParameters.java index e6a4848a..66d0c045 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/DisplayParameters.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/DisplayParameters.java @@ -2,7 +2,10 @@ import com.mojang.authlib.GameProfile; import org.bukkit.entity.EntityType; +import xiamomc.morph.backends.server.renderer.network.datawatcher.ValueIndex; import xiamomc.morph.backends.server.renderer.network.datawatcher.watchers.SingleWatcher; +import xiamomc.morph.backends.server.renderer.network.datawatcher.watchers.types.PlayerWatcher; +import xiamomc.morph.backends.server.renderer.network.registries.EntryIndex; public class DisplayParameters { @@ -24,6 +27,17 @@ public GameProfile getProfile() private final EntityType entityType; private final SingleWatcher singleWatcher; private final GameProfile gameProfile; + private boolean dontRandomProfileUUID; + + public void setDontRandomProfileUUID() + { + dontRandomProfileUUID = true; + } + + public boolean dontRandomProfileUUID() + { + return dontRandomProfileUUID; + } public DisplayParameters(EntityType bindingType, SingleWatcher watcher, GameProfile profile) { @@ -31,4 +45,17 @@ public DisplayParameters(EntityType bindingType, SingleWatcher watcher, GameProf this.singleWatcher = watcher; this.gameProfile = profile; } + + public static DisplayParameters fromWatcher(SingleWatcher watcher) + { + var profile = (watcher instanceof PlayerWatcher) + ? watcher.get(EntryIndex.PROFILE) + : null; + + return new DisplayParameters( + watcher.getEntityType(), + watcher, + profile + ); + } } diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/PacketFactory.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/PacketFactory.java index e843109d..5c5c1870 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/PacketFactory.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/PacketFactory.java @@ -1,16 +1,16 @@ package xiamomc.morph.backends.server.renderer.network; import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.wrappers.WrappedDataValue; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; -import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; -import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket; +import net.minecraft.network.protocol.game.*; import net.minecraft.world.level.GameType; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.EntityEquipment; import xiamomc.morph.MorphPluginObject; @@ -58,9 +58,12 @@ public List buildSpawnPackets(Player player, DisplayParameters Objects.requireNonNull(parametersProfile, "Null game profile!"); var gameProfile = new MorphGameProfile(parametersProfile); - //todo: Get random UUID from world - //玩家在客户端的UUID会根据其GameProfile中的UUID设定,我们需要避免伪装的UUID和某一玩家自己的UUID冲突 - gameProfile.setUUID(UUID.randomUUID()); + if (!parameters.dontRandomProfileUUID()) + { + //todo: Get random UUID from world to prevent duplicate UUID + //玩家在客户端的UUID会根据其GameProfile中的UUID设定,我们需要避免伪装的UUID和某一玩家自己的UUID冲突 + gameProfile.setUUID(UUID.randomUUID()); + } //Minecraft需要在生成玩家实体前先发送PlayerInfoUpdate消息 var uuid = gameProfile.getId(); @@ -98,11 +101,18 @@ public List buildSpawnPackets(Player player, DisplayParameters var watcher = parameters.getWatcher(); packets.add(buildFullMetaPacket(player, watcher)); - for (PacketContainer packet : packets) + if (player.getVehicle() != null) { - packet.setMeta(MORPH_PACKET_METAKEY, true); + var nmsEntity = ((CraftEntity)player.getVehicle()).getHandle(); + packets.add(PacketContainer.fromPacket(new ClientboundSetPassengersPacket(nmsEntity))); } + if (!player.getPassengers().isEmpty()) + packets.add(PacketContainer.fromPacket(new ClientboundSetPassengersPacket(NmsRecord.ofPlayer(player)))); + + for (PacketContainer packet : packets) + packet.setMeta(MORPH_PACKET_METAKEY, true); + return packets; } diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/datawatcher/values/PlayerValues.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/datawatcher/values/PlayerValues.java index 26089555..f3a1423f 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/datawatcher/values/PlayerValues.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/datawatcher/values/PlayerValues.java @@ -4,7 +4,7 @@ public class PlayerValues extends LivingEntityValues { public final SingleValue ABSORPTION_AMOUNT = getSingle(0f); public final SingleValue SCORE = getSingle(0); - public final SingleValue SKIN = getSingle((byte)0); //127 + public final SingleValue SKIN_FLAGS = getSingle((byte)0); //127 public final SingleValue MAINHAND = getSingle((byte)1); public final SingleValue LEFT_SHOULDER_PARROT_COMPOUND = getSingle(new Object()); public final SingleValue RIGHT_SHOULDER_PARROT_COMPOUND = getSingle(new Object()); @@ -13,6 +13,6 @@ public PlayerValues() { super(); - registerSingle(ABSORPTION_AMOUNT, SCORE, SKIN, MAINHAND); + registerSingle(ABSORPTION_AMOUNT, SCORE, SKIN_FLAGS, MAINHAND); } } diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/datawatcher/watchers/types/PlayerWatcher.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/datawatcher/watchers/types/PlayerWatcher.java index 879c32cc..bb4eb1ef 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/datawatcher/watchers/types/PlayerWatcher.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/datawatcher/watchers/types/PlayerWatcher.java @@ -1,13 +1,18 @@ package xiamomc.morph.backends.server.renderer.network.datawatcher.watchers.types; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.PacketContainer; import com.destroystokyo.paper.ClientOption; +import com.mojang.authlib.GameProfile; +import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; -import xiamomc.morph.backends.server.renderer.network.PacketFactory; +import xiamomc.morph.backends.server.renderer.network.DisplayParameters; import xiamomc.morph.backends.server.renderer.network.datawatcher.ValueIndex; import xiamomc.morph.backends.server.renderer.network.registries.EntryIndex; import xiamomc.morph.backends.server.renderer.network.registries.RegistryKey; -import xiamomc.pluginbase.Annotations.Resolved; + +import java.util.UUID; public class PlayerWatcher extends InventoryLivingWatcher { @@ -27,9 +32,38 @@ public PlayerWatcher(Player bindingPlayer) @Override protected void doSync() { - this.write(ValueIndex.PLAYER.SKIN, (byte)getBindingPlayer().getClientOption(ClientOption.SKIN_PARTS).getRaw()); + this.write(ValueIndex.PLAYER.SKIN_FLAGS, (byte)getBindingPlayer().getClientOption(ClientOption.SKIN_PARTS).getRaw()); this.write(ValueIndex.PLAYER.MAINHAND, (byte)getBindingPlayer().getMainHand().ordinal()); super.doSync(); } + + @Override + protected void onCustomWrite(RegistryKey key, Object oldVal, Object newVal) + { + super.onCustomWrite(key, oldVal, newVal); + var player = getBindingPlayer(); + + if (key.equals(EntryIndex.PROFILE)) + { + var profile = newVal == null + ? new GameProfile(UUID.randomUUID(), this.getOrDefault(EntryIndex.DISGUISE_NAME, "")) + : (GameProfile) newVal; + + var spawnPackets = getPacketFactory() + .buildSpawnPackets(player, + new DisplayParameters(this.getEntityType(), this, profile)); + + var packetRemove = PacketContainer.fromPacket(new ClientboundRemoveEntitiesPacket(player.getEntityId())); + var protocol = ProtocolLibrary.getProtocolManager(); + + var affected = getAffectedPlayers(player); + affected.forEach(p -> + { + protocol.sendServerPacket(p, packetRemove); + + spawnPackets.forEach(packet -> protocol.sendServerPacket(p, packet)); + }); + } + } } diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/listeners/SpawnPacketHandler.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/listeners/SpawnPacketHandler.java index 38067bea..afd0567e 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/listeners/SpawnPacketHandler.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/listeners/SpawnPacketHandler.java @@ -24,6 +24,7 @@ import xiamomc.morph.MorphPlugin; import xiamomc.morph.backends.server.renderer.network.DisplayParameters; import xiamomc.morph.backends.server.renderer.network.PacketFactory; +import xiamomc.morph.backends.server.renderer.network.datawatcher.watchers.SingleWatcher; import xiamomc.morph.backends.server.renderer.network.datawatcher.watchers.types.PlayerWatcher; import xiamomc.morph.backends.server.renderer.network.registries.EntryIndex; import xiamomc.morph.backends.server.renderer.network.registries.RenderRegistry; @@ -68,44 +69,19 @@ private void unDisguiseForPlayer(@Nullable Player player) var protocolManager = ProtocolLibrary.getProtocolManager(); var affectedPlayers = getAffectedPlayers(player); - affectedPlayers.remove(player); + var gameProfile = ((CraftPlayer) player).getProfile(); + var watcher = new PlayerWatcher(player); + + var parameters = new DisplayParameters(org.bukkit.entity.EntityType.PLAYER, watcher, gameProfile); + parameters.setDontRandomProfileUUID(); + var spawnPackets = getFactory().buildSpawnPackets(player, parameters); var removePacket = new ClientboundRemoveEntitiesPacket(player.getEntityId()); var rmPacketContainer = PacketContainer.fromPacket(removePacket); affectedPlayers.forEach(p -> protocolManager.sendServerPacket(p, rmPacketContainer)); - - var gameProfile = ((CraftPlayer) player).getProfile(); - var nmsPlayer = NmsRecord.ofPlayer(player); - - List packets = new ObjectArrayList<>(); - - var packetPlayerInfo = new ClientboundPlayerInfoUpdatePacket( - EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER), - new ClientboundPlayerInfoUpdatePacket.Entry( - player.getUniqueId(), gameProfile, false, 114514, GameType.DEFAULT_MODE, - Component.empty(), null - ) - ); - - var packetAdd = new ClientboundAddEntityPacket( - player.getEntityId(), player.getUniqueId(), - player.getX(), player.getY(), player.getZ(), - player.getPitch(), player.getYaw(), - EntityType.PLAYER, 0, - nmsPlayer.getDeltaMovement(), - nmsPlayer.getYHeadRot() - ); - - packets.add(PacketContainer.fromPacket(packetPlayerInfo)); - packets.add(PacketContainer.fromPacket(packetAdd)); - - var watcher = new PlayerWatcher(player); - packets.add(getFactory().getEquipmentPacket(player, watcher)); - packets.add(getFactory().buildFullMetaPacket(player, watcher)); - affectedPlayers.forEach(p -> { - for (PacketContainer packet : packets) + for (PacketContainer packet : spawnPackets) { protocolManager.sendServerPacket(p, packet); } @@ -117,7 +93,7 @@ private void unDisguiseForPlayer(@Nullable Player player) * @param player 目标玩家 * @param disguiseName 伪装的玩家名称 */ - private void scheduleRefreshPlayerDisplay(Player player, String disguiseName) + private void scheduleRefreshPlayerDisplay(Player player, String disguiseName, SingleWatcher currentWatcher) { PlayerSkinProvider.getInstance().fetchSkin(disguiseName) .thenApply(optional -> @@ -125,17 +101,13 @@ private void scheduleRefreshPlayerDisplay(Player player, String disguiseName) GameProfile outcomingProfile = new GameProfile(UUID.randomUUID(), disguiseName); if (optional.isPresent()) outcomingProfile = optional.get(); - var watcher = registry.getWatcher(player.getUniqueId()); - - if (watcher != null) - { - logger.info("Triggering refresh!"); - GameProfile finalOutcomingProfile = outcomingProfile; - watcher.write(EntryIndex.PROFILE, finalOutcomingProfile); + var newWatcher = registry.getWatcher(player.getUniqueId()); + if (newWatcher == null || currentWatcher != newWatcher) + return null; - this.addSchedule(() -> refreshStateForPlayer(player, - new DisplayParameters(watcher.getEntityType(), watcher, finalOutcomingProfile))); - } + logger.info("Triggering refresh!"); + GameProfile finalOutcomingProfile = outcomingProfile; + newWatcher.write(EntryIndex.PROFILE, finalOutcomingProfile); return null; }); @@ -165,14 +137,11 @@ private void refreshStateForPlayer(@Nullable Player player, @NotNull DisplayPara var protocolManager = ProtocolLibrary.getProtocolManager(); var affectedPlayers = getAffectedPlayers(player); - affectedPlayers.remove(player); //先发包移除当前实体 var packetRemove = new ClientboundRemoveEntitiesPacket(player.getEntityId()); var packetRemoveContainer = PacketContainer.fromPacket(packetRemove); - affectedPlayers.forEach(p -> protocolManager.sendServerPacket(p, packetRemoveContainer)); - var gameProfile = watcher.get(EntryIndex.PROFILE); //然后发包创建实体 @@ -180,7 +149,15 @@ private void refreshStateForPlayer(@Nullable Player player, @NotNull DisplayPara //如果没有profile,那么随机一个并计划刷新 if (displayType == org.bukkit.entity.EntityType.PLAYER && gameProfile == null) { - var disguiseName = watcher.get(EntryIndex.CUSTOM_NAME); + var disguiseName = watcher.get(EntryIndex.DISGUISE_NAME); + + if (disguiseName == null) + { + logger.error("Parameter 'disguiseName' cannot be null!"); + Thread.dumpStack(); + return; + } + var targetPlayer = Bukkit.getPlayerExact(disguiseName); var cachedProfile = targetPlayer == null @@ -189,7 +166,7 @@ private void refreshStateForPlayer(@Nullable Player player, @NotNull DisplayPara if (cachedProfile == null) { - scheduleRefreshPlayerDisplay(player, disguiseName); + scheduleRefreshPlayerDisplay(player, disguiseName, watcher); gameProfile = new GameProfile(Util.NIL_UUID, disguiseName); } else @@ -198,6 +175,9 @@ private void refreshStateForPlayer(@Nullable Player player, @NotNull DisplayPara var parametersFinal = new DisplayParameters(displayType, watcher, gameProfile); var spawnPackets = getFactory().buildSpawnPackets(player, parametersFinal); + + affectedPlayers.forEach(p -> protocolManager.sendServerPacket(p, packetRemoveContainer)); + spawnPackets.forEach(packet -> { for (var visiblePlayer : affectedPlayers) @@ -246,7 +226,6 @@ public ListeningWhitelist getSendingWhitelist() { return ListeningWhitelist .newBuilder() - .types(PacketType.Play.Server.ENTITY_METADATA) .types(PacketType.Play.Server.SPAWN_ENTITY) .gamePhase(GamePhase.PLAYING) .build(); diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/EntryIndex.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/EntryIndex.java index ab7b2e7e..fb839cd3 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/EntryIndex.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/EntryIndex.java @@ -10,9 +10,7 @@ public class EntryIndex { public static final RegistryKey PROFILE = RegistryKey.of("profile", new GameProfile(UUID.randomUUID(), "sample")); - public static final RegistryKey ENTITY_TYPE = RegistryKey.of("entity_type", EntityType.UNKNOWN).doRequireNonNull(); - public static final RegistryKey BINDING_WATCHER = new RegistryKey<>("binding_watcher", SingleWatcher.class).doRequireNonNull(); - public static final RegistryKey CUSTOM_NAME = RegistryKey.of("custom_name", "").doRequireNonNull(); + public static final RegistryKey DISGUISE_NAME = RegistryKey.of("disguise_name", "").doRequireNonNull(); public static final RegistryKey EQUIPMENT = RegistryKey.of("equip", new DisguiseEquipment()); public static final RegistryKey DISPLAY_FAKE_EQUIPMENT = RegistryKey.of("display_fake_equip", false); diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/RenderRegistry.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/RenderRegistry.java index 79f1ada7..46097d3a 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/RenderRegistry.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/RenderRegistry.java @@ -86,7 +86,7 @@ public void unregister(UUID uuid) public SingleWatcher register(@NotNull Player player, RegisterParameters registerParameters) { var watcher = WatcherIndex.getInstance().getWatcherForType(player, registerParameters.entityType()); - watcher.write(EntryIndex.CUSTOM_NAME, registerParameters.name()); + watcher.write(EntryIndex.DISGUISE_NAME, registerParameters.name()); register(player.getUniqueId(), watcher); return watcher;