2016-05-21 23:37:13 +02:00
|
|
|
/**
|
|
|
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
|
|
|
* Copyright (C) 2015 dmulloy2
|
|
|
|
*
|
|
|
|
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
* 02111-1307 USA
|
|
|
|
*/
|
|
|
|
package com.comphenix.protocol.wrappers;
|
|
|
|
|
2022-06-08 03:24:31 +02:00
|
|
|
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
2016-05-21 23:37:13 +02:00
|
|
|
import java.lang.reflect.Constructor;
|
2021-06-13 17:36:44 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2021-11-14 06:41:20 +01:00
|
|
|
import java.util.Objects;
|
2022-12-14 02:38:37 +01:00
|
|
|
import java.util.UUID;
|
2016-05-21 23:37:13 +02:00
|
|
|
|
2021-06-13 17:36:44 +02:00
|
|
|
import com.comphenix.protocol.PacketType;
|
2016-05-21 23:37:13 +02:00
|
|
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
|
|
|
import com.comphenix.protocol.reflect.StructureModifier;
|
|
|
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
2021-06-13 17:36:44 +02:00
|
|
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
2016-05-21 23:37:13 +02:00
|
|
|
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
|
|
|
|
|
2023-04-29 21:49:51 +02:00
|
|
|
import javax.annotation.Nullable;
|
|
|
|
|
2016-05-21 23:37:13 +02:00
|
|
|
/**
|
|
|
|
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
|
|
|
|
* @author dmulloy2
|
|
|
|
*/
|
|
|
|
public class PlayerInfoData {
|
|
|
|
private static Constructor<?> constructor;
|
|
|
|
|
2022-12-14 02:38:37 +01:00
|
|
|
private final UUID profileId;
|
2016-05-21 23:37:13 +02:00
|
|
|
private final int latency;
|
2022-12-14 02:38:37 +01:00
|
|
|
private final boolean listed;
|
2016-05-21 23:37:13 +02:00
|
|
|
private final NativeGameMode gameMode;
|
|
|
|
private final WrappedGameProfile profile;
|
|
|
|
private final WrappedChatComponent displayName;
|
2023-04-29 21:49:51 +02:00
|
|
|
@Nullable
|
|
|
|
private final WrappedRemoteChatSessionData remoteChatSessionData;
|
|
|
|
@Nullable
|
2022-06-08 03:24:31 +02:00
|
|
|
private final WrappedProfileKeyData profileKeyData;
|
2016-05-21 23:37:13 +02:00
|
|
|
|
|
|
|
// This is the same order as the NMS class, minus the packet (which isn't a field)
|
|
|
|
public PlayerInfoData(WrappedGameProfile profile, int latency, NativeGameMode gameMode, WrappedChatComponent displayName) {
|
2022-06-08 03:24:31 +02:00
|
|
|
this(profile, latency, gameMode, displayName, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public PlayerInfoData(WrappedGameProfile profile, int latency, NativeGameMode gameMode, WrappedChatComponent displayName, WrappedProfileKeyData keyData) {
|
2022-12-14 02:38:37 +01:00
|
|
|
this(profile.getUUID(), latency, true, gameMode, profile, displayName, keyData);
|
|
|
|
}
|
|
|
|
|
2023-04-29 21:49:51 +02:00
|
|
|
/**
|
|
|
|
* Constructs a new PlayerInfoData for Minecraft 1.19 or later without signature data
|
|
|
|
* @see PlayerInfoData#PlayerInfoData(UUID, int, boolean, NativeGameMode, WrappedGameProfile, WrappedChatComponent, WrappedRemoteChatSessionData)
|
|
|
|
*
|
|
|
|
* @param profileId the id of the profile (has to be non-null)
|
|
|
|
* @param latency the latency in milliseconds
|
|
|
|
* @param listed whether the player is listed in the tab list
|
|
|
|
* @param gameMode the game mode
|
|
|
|
* @param profile the game profile
|
|
|
|
* @param displayName display name in tab list (optional)
|
|
|
|
*/
|
|
|
|
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName) {
|
|
|
|
this(profileId, latency, listed, gameMode, profile, displayName, (WrappedRemoteChatSessionData) null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructs a new PlayerInfoData for Minecraft 1.19. This is incompatible on 1.19.3.
|
|
|
|
* @see PlayerInfoData#PlayerInfoData(UUID, int, boolean, NativeGameMode, WrappedGameProfile, WrappedChatComponent, WrappedRemoteChatSessionData)
|
|
|
|
*
|
|
|
|
* @param profileId the id of the profile (has to be non-null)
|
|
|
|
* @param latency the latency in milliseconds
|
|
|
|
* @param listed whether the player is listed in the tab list
|
|
|
|
* @param gameMode the game mode
|
|
|
|
* @param profile the game profile
|
|
|
|
* @param displayName display name in tab list (optional)
|
|
|
|
* @param profileKeyData the public key for the profile or null
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedProfileKeyData profileKeyData) {
|
2022-12-14 02:38:37 +01:00
|
|
|
this.profileId = profileId;
|
2016-05-21 23:37:13 +02:00
|
|
|
this.latency = latency;
|
2022-12-14 02:38:37 +01:00
|
|
|
this.listed = listed;
|
2016-05-21 23:37:13 +02:00
|
|
|
this.gameMode = gameMode;
|
2022-12-14 02:38:37 +01:00
|
|
|
this.profile = profile;
|
2016-05-21 23:37:13 +02:00
|
|
|
this.displayName = displayName;
|
2022-12-14 02:38:37 +01:00
|
|
|
this.profileKeyData = profileKeyData;
|
2023-04-29 21:49:51 +02:00
|
|
|
this.remoteChatSessionData = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructs a new PlayerInfoData for Minecraft 1.19.3 or later.
|
|
|
|
*
|
|
|
|
* @param profileId the id of the profile (has to be non-null)
|
|
|
|
* @param latency the latency in milliseconds
|
|
|
|
* @param listed whether the player is listed in the tab list
|
|
|
|
* @param gameMode the game mode
|
|
|
|
* @param profile the game profile
|
|
|
|
* @param displayName display name in tab list (optional)
|
|
|
|
* @param remoteChatSession the remote chat session for this profile or null
|
|
|
|
*/
|
|
|
|
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
|
|
|
|
this.profileId = profileId;
|
|
|
|
this.latency = latency;
|
|
|
|
this.listed = listed;
|
|
|
|
this.gameMode = gameMode;
|
|
|
|
this.profile = profile;
|
|
|
|
this.displayName = displayName;
|
|
|
|
this.profileKeyData = null;
|
|
|
|
this.remoteChatSessionData = remoteChatSession;
|
2022-12-14 02:38:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the id of the affected profile (since 1.19.3)
|
|
|
|
* @return the id of the profile
|
|
|
|
*/
|
|
|
|
public UUID getProfileId() {
|
2023-04-29 21:49:51 +02:00
|
|
|
if(profileId == null && profile != null) {
|
|
|
|
return profile.getUUID(); // Ensure forward compatability
|
|
|
|
}
|
2022-12-14 02:38:37 +01:00
|
|
|
return profileId;
|
2016-05-21 23:37:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the GameProfile of the player represented by this data.
|
|
|
|
* @return The GameProfile
|
|
|
|
*/
|
|
|
|
public WrappedGameProfile getProfile() {
|
|
|
|
return profile;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @deprecated Replaced by {@link #getLatency()}
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
public int getPing() {
|
|
|
|
return latency;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the latency between the client and the server.
|
|
|
|
* @return The latency
|
|
|
|
*/
|
|
|
|
public int getLatency() {
|
|
|
|
return latency;
|
|
|
|
}
|
|
|
|
|
2022-12-14 02:38:37 +01:00
|
|
|
/**
|
2023-04-29 21:49:51 +02:00
|
|
|
* Gets if the player is listed on the client (since 1.19.3)
|
2022-12-14 02:38:37 +01:00
|
|
|
* @return if the player is listed
|
|
|
|
*/
|
|
|
|
public boolean isListed() {
|
|
|
|
return listed;
|
|
|
|
}
|
|
|
|
|
2016-05-21 23:37:13 +02:00
|
|
|
/**
|
|
|
|
* Gets the GameMode of the player represented by this data.
|
|
|
|
* @return The GameMode
|
|
|
|
*/
|
|
|
|
public NativeGameMode getGameMode() {
|
|
|
|
return gameMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the display name of the player represented by this data.
|
|
|
|
* @return The display name
|
|
|
|
*/
|
|
|
|
public WrappedChatComponent getDisplayName() {
|
|
|
|
return displayName;
|
|
|
|
}
|
|
|
|
|
2022-06-08 03:24:31 +02:00
|
|
|
/**
|
2023-04-29 21:49:51 +02:00
|
|
|
* Returns the public key of the profile (since 1.19). Returns the public key of the remote chat session since 1.19.3
|
|
|
|
* @return The public key of the profile.
|
2022-06-08 03:24:31 +02:00
|
|
|
*/
|
2023-04-29 21:49:51 +02:00
|
|
|
@Nullable
|
2022-06-08 03:24:31 +02:00
|
|
|
public WrappedProfileKeyData getProfileKeyData() {
|
2023-04-29 21:49:51 +02:00
|
|
|
return this.profileKeyData != null ? this.profileKeyData : (this.remoteChatSessionData != null ? this.remoteChatSessionData.getProfilePublicKey() : null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the remoteChatSessionData (since 1.19.3)
|
|
|
|
* @return The remote chat sesion data or null
|
|
|
|
*/
|
|
|
|
@Nullable
|
|
|
|
public WrappedRemoteChatSessionData getRemoteChatSessionData() {
|
|
|
|
return this.remoteChatSessionData;
|
2022-06-08 03:24:31 +02:00
|
|
|
}
|
|
|
|
|
2016-05-21 23:37:13 +02:00
|
|
|
/**
|
|
|
|
* Used to convert between NMS PlayerInfoData and the wrapper instance.
|
|
|
|
* @return A new converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<PlayerInfoData> getConverter() {
|
|
|
|
return new EquivalentConverter<PlayerInfoData>() {
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(PlayerInfoData specific) {
|
2016-05-21 23:37:13 +02:00
|
|
|
if (constructor == null) {
|
|
|
|
try {
|
2021-06-13 17:36:44 +02:00
|
|
|
List<Class<?>> args = new ArrayList<>();
|
|
|
|
if (!MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
|
|
|
args.add(PacketType.Play.Server.PLAYER_INFO.getPacketClass());
|
|
|
|
}
|
|
|
|
|
2022-12-14 02:38:37 +01:00
|
|
|
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
|
|
|
|
args.add(UUID.class);
|
|
|
|
}
|
|
|
|
|
2021-06-13 17:36:44 +02:00
|
|
|
args.add(MinecraftReflection.getGameProfileClass());
|
2022-12-14 02:38:37 +01:00
|
|
|
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
|
|
|
|
args.add(boolean.class);
|
|
|
|
}
|
|
|
|
|
2021-06-13 17:36:44 +02:00
|
|
|
args.add(int.class);
|
|
|
|
args.add(EnumWrappers.getGameModeClass());
|
|
|
|
args.add(MinecraftReflection.getIChatBaseComponentClass());
|
|
|
|
|
2022-12-14 02:38:37 +01:00
|
|
|
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
|
2023-04-29 21:49:51 +02:00
|
|
|
args.add(MinecraftReflection.getRemoteChatSessionDataClass());
|
2022-12-14 02:38:37 +01:00
|
|
|
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
|
2022-06-08 03:24:31 +02:00
|
|
|
args.add(MinecraftReflection.getProfilePublicKeyDataClass());
|
|
|
|
}
|
|
|
|
|
2021-06-13 17:36:44 +02:00
|
|
|
constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(args.toArray(new Class<?>[0]));
|
2016-05-21 23:37:13 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException("Cannot find PlayerInfoData constructor.", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to construct the underlying PlayerInfoData
|
|
|
|
|
|
|
|
try {
|
2021-06-13 17:36:44 +02:00
|
|
|
Object gameMode = EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode);
|
|
|
|
Object displayName = specific.displayName != null ? specific.displayName.handle : null;
|
|
|
|
|
2023-04-29 21:49:51 +02:00
|
|
|
Object profile = specific.profile != null ? specific.profile.handle : null;
|
2022-12-14 02:38:37 +01:00
|
|
|
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
|
|
|
|
return constructor.newInstance(
|
|
|
|
specific.profileId,
|
2023-04-29 21:49:51 +02:00
|
|
|
profile,
|
2022-12-14 02:38:37 +01:00
|
|
|
specific.listed,
|
|
|
|
specific.latency,
|
|
|
|
gameMode,
|
|
|
|
displayName,
|
2023-04-29 21:49:51 +02:00
|
|
|
specific.remoteChatSessionData != null ? BukkitConverters.getWrappedRemoteChatSessionDataConverter().getGeneric(specific.remoteChatSessionData) : null
|
|
|
|
);
|
2022-12-14 02:38:37 +01:00
|
|
|
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
|
2022-06-08 03:24:31 +02:00
|
|
|
return constructor.newInstance(
|
2023-04-29 21:49:51 +02:00
|
|
|
profile,
|
2022-06-08 03:24:31 +02:00
|
|
|
specific.latency,
|
|
|
|
gameMode,
|
|
|
|
displayName,
|
|
|
|
specific.profileKeyData == null ? null : specific.profileKeyData.handle);
|
|
|
|
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
2023-04-29 21:49:51 +02:00
|
|
|
return constructor.newInstance(profile, specific.latency, gameMode, displayName);
|
2021-06-13 17:36:44 +02:00
|
|
|
} else {
|
2023-04-29 21:49:51 +02:00
|
|
|
return constructor.newInstance(null, profile, specific.latency, gameMode, displayName);
|
2021-06-13 17:36:44 +02:00
|
|
|
}
|
2016-05-21 23:37:13 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public PlayerInfoData getSpecific(Object generic) {
|
|
|
|
if (MinecraftReflection.isPlayerInfoData(generic)) {
|
2017-07-24 20:15:56 +02:00
|
|
|
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass(), null, false)
|
2016-05-21 23:37:13 +02:00
|
|
|
.withTarget(generic);
|
|
|
|
|
|
|
|
StructureModifier<WrappedGameProfile> gameProfiles = modifier.withType(
|
|
|
|
MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter());
|
|
|
|
WrappedGameProfile gameProfile = gameProfiles.read(0);
|
|
|
|
|
|
|
|
StructureModifier<Integer> ints = modifier.withType(int.class);
|
|
|
|
int latency = ints.read(0);
|
|
|
|
|
|
|
|
StructureModifier<NativeGameMode> gameModes = modifier.withType(
|
|
|
|
EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
|
|
|
|
NativeGameMode gameMode = gameModes.read(0);
|
|
|
|
|
|
|
|
StructureModifier<WrappedChatComponent> displayNames = modifier.withType(
|
|
|
|
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
|
|
|
|
WrappedChatComponent displayName = displayNames.read(0);
|
2022-06-08 03:24:31 +02:00
|
|
|
|
2023-04-29 21:49:51 +02:00
|
|
|
if(MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
|
|
|
|
return new PlayerInfoData(modifier.<UUID>withType(UUID.class).read(0),
|
|
|
|
latency,
|
|
|
|
modifier.<Boolean>withType(boolean.class).read(0),
|
|
|
|
gameMode,
|
|
|
|
gameProfile,
|
|
|
|
displayName,
|
|
|
|
modifier.withType(MinecraftReflection.getRemoteChatSessionDataClass(), BukkitConverters.getWrappedRemoteChatSessionDataConverter()).read(0)
|
|
|
|
);
|
|
|
|
}
|
2022-06-14 18:54:45 +02:00
|
|
|
WrappedProfileKeyData key = null;
|
2023-04-29 21:49:51 +02:00
|
|
|
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
|
2022-06-14 18:54:45 +02:00
|
|
|
StructureModifier<WrappedProfileKeyData> keyData = modifier.withType(
|
|
|
|
MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter());
|
|
|
|
key = keyData.read(0);
|
|
|
|
}
|
2022-06-08 03:24:31 +02:00
|
|
|
|
|
|
|
return new PlayerInfoData(gameProfile, latency, gameMode, displayName, key);
|
2016-05-21 23:37:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, return null
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Thanks Java Generics!
|
|
|
|
@Override
|
|
|
|
public Class<PlayerInfoData> getSpecificType() {
|
|
|
|
return PlayerInfoData.class;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
// Fast checks
|
|
|
|
if (this == obj) return true;
|
|
|
|
if (obj == null) return false;
|
2022-06-08 03:24:31 +02:00
|
|
|
|
2016-05-21 23:37:13 +02:00
|
|
|
// Only compare objects of similar type
|
|
|
|
if (obj instanceof PlayerInfoData) {
|
|
|
|
PlayerInfoData other = (PlayerInfoData) obj;
|
2023-04-29 21:49:51 +02:00
|
|
|
return Objects.equals(profile, other.profile)
|
|
|
|
&& Objects.equals(profileId, other.profileId)
|
|
|
|
&& latency == other.latency
|
|
|
|
&& gameMode == other.gameMode
|
|
|
|
&& Objects.equals(displayName, other.displayName)
|
|
|
|
&& listed == other.listed
|
|
|
|
&& Objects.equals(remoteChatSessionData, other.remoteChatSessionData)
|
|
|
|
&& Objects.equals(profileKeyData, other.profileKeyData);
|
2016-05-21 23:37:13 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2022-06-08 03:24:31 +02:00
|
|
|
|
2016-05-21 23:37:13 +02:00
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
2023-04-29 21:49:51 +02:00
|
|
|
return Objects.hash(latency, gameMode, profile, displayName, profileKeyData, remoteChatSessionData, listed);
|
2016-05-21 23:37:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
2023-04-29 21:49:51 +02:00
|
|
|
if(MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
|
|
|
|
return String.format("PlayerInfoData[latency=%s, listed=%b, gameMode=%s, profile=%s, displayName=%s, remoteChatSession=%s]",
|
|
|
|
latency, listed, gameMode, profile, displayName, remoteChatSessionData);
|
|
|
|
}
|
|
|
|
if(MinecraftVersion.WILD_UPDATE.atOrAbove()) {
|
|
|
|
return String.format("PlayerInfoData[latency=%s, listed=%b, gameMode=%s, profile=%s, displayName=%s, profilePublicKey=%s]",
|
|
|
|
latency, listed, gameMode, profile, displayName, profileKeyData);
|
|
|
|
}
|
2021-11-14 06:41:20 +01:00
|
|
|
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s]",
|
2023-04-29 21:49:51 +02:00
|
|
|
latency, gameMode, profile, displayName);
|
2016-05-21 23:37:13 +02:00
|
|
|
}
|
|
|
|
}
|