Skip to content

Commit

Permalink
add: 构建实体生成时同样也构建载具信息
Browse files Browse the repository at this point in the history
misc: 取消伪装时也用PacketFactory中的方法构建相关数据包
  • Loading branch information
MATRIX-feather committed Dec 15, 2023
1 parent 1668229 commit 9f6d60e
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import xiamomc.morph.utilities.NbtUtils;

import java.util.Objects;
import java.util.UUID;

public class ServerDisguiseWrapper extends DisguiseWrapper<ServerDisguise>
{
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -24,11 +27,35 @@ 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)
{
this.entityType = bindingType;
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
);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -58,9 +58,12 @@ public List<PacketContainer> 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();
Expand Down Expand Up @@ -98,11 +101,18 @@ public List<PacketContainer> 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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class PlayerValues extends LivingEntityValues
{
public final SingleValue<Float> ABSORPTION_AMOUNT = getSingle(0f);
public final SingleValue<Integer> SCORE = getSingle(0);
public final SingleValue<Byte> SKIN = getSingle((byte)0); //127
public final SingleValue<Byte> SKIN_FLAGS = getSingle((byte)0); //127
public final SingleValue<Byte> MAINHAND = getSingle((byte)1);
public final SingleValue<Object> LEFT_SHOULDER_PARROT_COMPOUND = getSingle(new Object());
public final SingleValue<Object> RIGHT_SHOULDER_PARROT_COMPOUND = getSingle(new Object());
Expand All @@ -13,6 +13,6 @@ public PlayerValues()
{
super();

registerSingle(ABSORPTION_AMOUNT, SCORE, SKIN, MAINHAND);
registerSingle(ABSORPTION_AMOUNT, SCORE, SKIN_FLAGS, MAINHAND);
}
}
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -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));
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<PacketContainer> 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);
}
Expand All @@ -117,25 +93,21 @@ 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 ->
{
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;
});
Expand Down Expand Up @@ -165,22 +137,27 @@ 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);

//然后发包创建实体
//确保gameProfile非空
//如果没有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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
public class EntryIndex
{
public static final RegistryKey<GameProfile> PROFILE = RegistryKey.of("profile", new GameProfile(UUID.randomUUID(), "sample"));
public static final RegistryKey<EntityType> ENTITY_TYPE = RegistryKey.of("entity_type", EntityType.UNKNOWN).doRequireNonNull();
public static final RegistryKey<SingleWatcher> BINDING_WATCHER = new RegistryKey<>("binding_watcher", SingleWatcher.class).doRequireNonNull();
public static final RegistryKey<String> CUSTOM_NAME = RegistryKey.of("custom_name", "").doRequireNonNull();
public static final RegistryKey<String> DISGUISE_NAME = RegistryKey.of("disguise_name", "").doRequireNonNull();

public static final RegistryKey<DisguiseEquipment> EQUIPMENT = RegistryKey.of("equip", new DisguiseEquipment());
public static final RegistryKey<Boolean> DISPLAY_FAKE_EQUIPMENT = RegistryKey.of("display_fake_equip", false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 9f6d60e

Please sign in to comment.