package net.minecraft.network.protocol.game; import com.google.common.base.MoreObjects; import com.mojang.authlib.GameProfile; import java.util.Collection; import java.util.EnumSet; import java.util.List; import java.util.Objects; import java.util.UUID; import net.minecraft.Optionull; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.chat.RemoteChatSession; import net.minecraft.network.chat.RemoteChatSession.Data; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.PacketType; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Action.Reader; import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket.Action.Writer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.GameType; import org.jetbrains.annotations.Nullable; public class ClientboundPlayerInfoUpdatePacket implements Packet { public static final StreamCodec STREAM_CODEC = Packet.codec( ClientboundPlayerInfoUpdatePacket::write, ClientboundPlayerInfoUpdatePacket::new ); private final EnumSet actions; private final List entries; public ClientboundPlayerInfoUpdatePacket(EnumSet actions, Collection players) { this.actions = actions; this.entries = players.stream().map(ClientboundPlayerInfoUpdatePacket.Entry::new).toList(); } public ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action action, ServerPlayer player) { this.actions = EnumSet.of(action); this.entries = List.of(new ClientboundPlayerInfoUpdatePacket.Entry(player)); } public static ClientboundPlayerInfoUpdatePacket createPlayerInitializing(Collection players) { EnumSet enumSet = EnumSet.of( ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER ); return new ClientboundPlayerInfoUpdatePacket(enumSet, players); } private ClientboundPlayerInfoUpdatePacket(RegistryFriendlyByteBuf buffer) { this.actions = buffer.readEnumSet(ClientboundPlayerInfoUpdatePacket.Action.class); this.entries = buffer.readList(friendlyByteBuf -> { ClientboundPlayerInfoUpdatePacket.EntryBuilder entryBuilder = new ClientboundPlayerInfoUpdatePacket.EntryBuilder(friendlyByteBuf.readUUID()); for (ClientboundPlayerInfoUpdatePacket.Action action : this.actions) { action.reader.read(entryBuilder, (RegistryFriendlyByteBuf)friendlyByteBuf); } return entryBuilder.build(); }); } private void write(RegistryFriendlyByteBuf buffer) { buffer.writeEnumSet(this.actions, ClientboundPlayerInfoUpdatePacket.Action.class); buffer.writeCollection(this.entries, (friendlyByteBuf, entry) -> { friendlyByteBuf.writeUUID(entry.profileId()); for (ClientboundPlayerInfoUpdatePacket.Action action : this.actions) { action.writer.write((RegistryFriendlyByteBuf)friendlyByteBuf, entry); } }); } @Override public PacketType type() { return GamePacketTypes.CLIENTBOUND_PLAYER_INFO_UPDATE; } /** * Passes this Packet on to the PacketListener for processing. */ public void handle(ClientGamePacketListener handler) { handler.handlePlayerInfoUpdate(this); } public EnumSet actions() { return this.actions; } public List entries() { return this.entries; } public List newEntries() { return this.actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER) ? this.entries : List.of(); } public String toString() { return MoreObjects.toStringHelper(this).add("actions", this.actions).add("entries", this.entries).toString(); } public static enum Action { ADD_PLAYER((entryBuilder, registryFriendlyByteBuf) -> { GameProfile gameProfile = new GameProfile(entryBuilder.profileId, registryFriendlyByteBuf.readUtf(16)); gameProfile.getProperties().putAll(ByteBufCodecs.GAME_PROFILE_PROPERTIES.decode(registryFriendlyByteBuf)); entryBuilder.profile = gameProfile; }, (registryFriendlyByteBuf, entry) -> { GameProfile gameProfile = (GameProfile)Objects.requireNonNull(entry.profile()); registryFriendlyByteBuf.writeUtf(gameProfile.getName(), 16); ByteBufCodecs.GAME_PROFILE_PROPERTIES.encode(registryFriendlyByteBuf, gameProfile.getProperties()); }), INITIALIZE_CHAT( (entryBuilder, registryFriendlyByteBuf) -> entryBuilder.chatSession = registryFriendlyByteBuf.readNullable(Data::read), (registryFriendlyByteBuf, entry) -> registryFriendlyByteBuf.writeNullable(entry.chatSession, Data::write) ), UPDATE_GAME_MODE( (entryBuilder, registryFriendlyByteBuf) -> entryBuilder.gameMode = GameType.byId(registryFriendlyByteBuf.readVarInt()), (registryFriendlyByteBuf, entry) -> registryFriendlyByteBuf.writeVarInt(entry.gameMode().getId()) ), UPDATE_LISTED( (entryBuilder, registryFriendlyByteBuf) -> entryBuilder.listed = registryFriendlyByteBuf.readBoolean(), (registryFriendlyByteBuf, entry) -> registryFriendlyByteBuf.writeBoolean(entry.listed()) ), UPDATE_LATENCY( (entryBuilder, registryFriendlyByteBuf) -> entryBuilder.latency = registryFriendlyByteBuf.readVarInt(), (registryFriendlyByteBuf, entry) -> registryFriendlyByteBuf.writeVarInt(entry.latency()) ), UPDATE_DISPLAY_NAME( (entryBuilder, registryFriendlyByteBuf) -> entryBuilder.displayName = FriendlyByteBuf.readNullable( registryFriendlyByteBuf, ComponentSerialization.TRUSTED_STREAM_CODEC ), (registryFriendlyByteBuf, entry) -> FriendlyByteBuf.writeNullable(registryFriendlyByteBuf, entry.displayName(), ComponentSerialization.TRUSTED_STREAM_CODEC) ), UPDATE_LIST_ORDER( (entryBuilder, registryFriendlyByteBuf) -> entryBuilder.listOrder = registryFriendlyByteBuf.readVarInt(), (registryFriendlyByteBuf, entry) -> registryFriendlyByteBuf.writeVarInt(entry.listOrder) ); final Reader reader; final Writer writer; private Action(final Reader reader, final Writer writer) { this.reader = reader; this.writer = writer; } } public record Entry( UUID profileId, @Nullable GameProfile profile, boolean listed, int latency, GameType gameMode, @Nullable Component displayName, int listOrder, @Nullable Data chatSession ) { Entry(ServerPlayer player) { this( player.getUUID(), player.getGameProfile(), true, player.connection.latency(), player.gameMode.getGameModeForPlayer(), player.getTabListDisplayName(), player.getTabListOrder(), Optionull.map(player.getChatSession(), RemoteChatSession::asData) ); } } static class EntryBuilder { final UUID profileId; @Nullable GameProfile profile; boolean listed; int latency; GameType gameMode = GameType.DEFAULT_MODE; @Nullable Component displayName; int listOrder; @Nullable Data chatSession; EntryBuilder(UUID profileId) { this.profileId = profileId; } ClientboundPlayerInfoUpdatePacket.Entry build() { return new ClientboundPlayerInfoUpdatePacket.Entry( this.profileId, this.profile, this.listed, this.latency, this.gameMode, this.displayName, this.listOrder, this.chatSession ); } } }