Skip to content

Commit 08ea2da

Browse files
authored
Improved Wrapping of PlayerInfoData and support chat session data (#2361)
* Improved Wrapping of PlayerInfoData and support chat session data * added constructor for unambiguous creation of playerinfodata without signature
1 parent 448e936 commit 08ea2da

File tree

9 files changed

+323
-82
lines changed

9 files changed

+323
-82
lines changed

src/main/java/com/comphenix/protocol/events/AbstractStructure.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ public StructureModifier<Instant> getInstants() {
961961
}
962962

963963
/**
964-
* Retrieve a read/write structure for profile public keys in 1.9
964+
* Retrieve a read/write structure for profile public keys in 1.19
965965
* @return The Structure Modifier
966966
*/
967967
public StructureModifier<WrappedProfilePublicKey> getProfilePublicKeys() {
@@ -971,13 +971,24 @@ public StructureModifier<WrappedProfilePublicKey> getProfilePublicKeys() {
971971
}
972972

973973
/**
974-
* Retrieve a read/write structure for profile public key data in 1.9
974+
* Retrieve a read/write structure for profile public key data in 1.19
975975
* @return The Structure Modifier
976976
*/
977977
public StructureModifier<WrappedProfileKeyData> getProfilePublicKeyData() {
978978
return structureModifier.withType(
979-
MinecraftReflection.getProfilePublicKeyDataClass(),
980-
BukkitConverters.getWrappedPublicKeyDataConverter());
979+
MinecraftReflection.getProfilePublicKeyDataClass(),
980+
BukkitConverters.getWrappedPublicKeyDataConverter());
981+
}
982+
983+
/**
984+
* Retrieves read/write structure for remote chat session data in 1.19.3
985+
* @return The Structure Modifier
986+
*/
987+
public StructureModifier<WrappedRemoteChatSessionData> getRemoteChatSessionData() {
988+
return structureModifier.withType(
989+
MinecraftReflection.getRemoteChatSessionDataClass(),
990+
BukkitConverters.getWrappedRemoteChatSessionDataConverter()
991+
);
981992
}
982993

983994
/**

src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,10 @@ public static Class<?> getRemoteChatSessionClass() {
16211621
return getMinecraftClass("network.chat.RemoteChatSession");
16221622
}
16231623

1624+
public static Class<?> getRemoteChatSessionDataClass() {
1625+
return getRemoteChatSessionClass().getClasses()[0];
1626+
}
1627+
16241628
public static Class<?> getFastUtilClass(String className) {
16251629
return getLibraryClass("it.unimi.dsi.fastutil." + className);
16261630
}

src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,10 @@ public static EquivalentConverter<WrappedProfileKeyData> getWrappedPublicKeyData
604604
return ignoreNull(handle(WrappedProfileKeyData::getHandle, WrappedProfileKeyData::new, WrappedProfileKeyData.class));
605605
}
606606

607+
public static EquivalentConverter<WrappedRemoteChatSessionData> getWrappedRemoteChatSessionDataConverter() {
608+
return ignoreNull(handle(WrappedRemoteChatSessionData::getHandle, WrappedRemoteChatSessionData::new, WrappedRemoteChatSessionData.class));
609+
}
610+
607611
/**
608612
* @return converter for cryptographic signature data that are used in login and chat packets
609613
*/

src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import com.comphenix.protocol.utility.MinecraftVersion;
3131
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
3232

33+
import javax.annotation.Nullable;
34+
3335
/**
3436
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
3537
* @author dmulloy2
@@ -43,6 +45,9 @@ public class PlayerInfoData {
4345
private final NativeGameMode gameMode;
4446
private final WrappedGameProfile profile;
4547
private final WrappedChatComponent displayName;
48+
@Nullable
49+
private final WrappedRemoteChatSessionData remoteChatSessionData;
50+
@Nullable
4651
private final WrappedProfileKeyData profileKeyData;
4752

4853
// This is the same order as the NMS class, minus the packet (which isn't a field)
@@ -54,21 +59,75 @@ public PlayerInfoData(WrappedGameProfile profile, int latency, NativeGameMode ga
5459
this(profile.getUUID(), latency, true, gameMode, profile, displayName, keyData);
5560
}
5661

57-
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, WrappedProfileKeyData profileKeyData) {
62+
/**
63+
* Constructs a new PlayerInfoData for Minecraft 1.19 or later without signature data
64+
* @see PlayerInfoData#PlayerInfoData(UUID, int, boolean, NativeGameMode, WrappedGameProfile, WrappedChatComponent, WrappedRemoteChatSessionData)
65+
*
66+
* @param profileId the id of the profile (has to be non-null)
67+
* @param latency the latency in milliseconds
68+
* @param listed whether the player is listed in the tab list
69+
* @param gameMode the game mode
70+
* @param profile the game profile
71+
* @param displayName display name in tab list (optional)
72+
*/
73+
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName) {
74+
this(profileId, latency, listed, gameMode, profile, displayName, (WrappedRemoteChatSessionData) null);
75+
}
76+
77+
/**
78+
* Constructs a new PlayerInfoData for Minecraft 1.19. This is incompatible on 1.19.3.
79+
* @see PlayerInfoData#PlayerInfoData(UUID, int, boolean, NativeGameMode, WrappedGameProfile, WrappedChatComponent, WrappedRemoteChatSessionData)
80+
*
81+
* @param profileId the id of the profile (has to be non-null)
82+
* @param latency the latency in milliseconds
83+
* @param listed whether the player is listed in the tab list
84+
* @param gameMode the game mode
85+
* @param profile the game profile
86+
* @param displayName display name in tab list (optional)
87+
* @param profileKeyData the public key for the profile or null
88+
*/
89+
@Deprecated
90+
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedProfileKeyData profileKeyData) {
5891
this.profileId = profileId;
5992
this.latency = latency;
6093
this.listed = listed;
6194
this.gameMode = gameMode;
6295
this.profile = profile;
6396
this.displayName = displayName;
6497
this.profileKeyData = profileKeyData;
98+
this.remoteChatSessionData = null;
99+
}
100+
101+
/**
102+
* Constructs a new PlayerInfoData for Minecraft 1.19.3 or later.
103+
*
104+
* @param profileId the id of the profile (has to be non-null)
105+
* @param latency the latency in milliseconds
106+
* @param listed whether the player is listed in the tab list
107+
* @param gameMode the game mode
108+
* @param profile the game profile
109+
* @param displayName display name in tab list (optional)
110+
* @param remoteChatSession the remote chat session for this profile or null
111+
*/
112+
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
113+
this.profileId = profileId;
114+
this.latency = latency;
115+
this.listed = listed;
116+
this.gameMode = gameMode;
117+
this.profile = profile;
118+
this.displayName = displayName;
119+
this.profileKeyData = null;
120+
this.remoteChatSessionData = remoteChatSession;
65121
}
66122

67123
/**
68124
* Get the id of the affected profile (since 1.19.3)
69125
* @return the id of the profile
70126
*/
71127
public UUID getProfileId() {
128+
if(profileId == null && profile != null) {
129+
return profile.getUUID(); // Ensure forward compatability
130+
}
72131
return profileId;
73132
}
74133

@@ -97,7 +156,7 @@ public int getLatency() {
97156
}
98157

99158
/**
100-
* Gets if the player is listed on the client.
159+
* Gets if the player is listed on the client (since 1.19.3)
101160
* @return if the player is listed
102161
*/
103162
public boolean isListed() {
@@ -121,11 +180,21 @@ public WrappedChatComponent getDisplayName() {
121180
}
122181

123182
/**
124-
* Gets the profile key data of the player represented by this data, null if not present.
125-
* @return The profile key data
183+
* Returns the public key of the profile (since 1.19). Returns the public key of the remote chat session since 1.19.3
184+
* @return The public key of the profile.
126185
*/
186+
@Nullable
127187
public WrappedProfileKeyData getProfileKeyData() {
128-
return this.profileKeyData;
188+
return this.profileKeyData != null ? this.profileKeyData : (this.remoteChatSessionData != null ? this.remoteChatSessionData.getProfilePublicKey() : null);
189+
}
190+
191+
/**
192+
* Returns the remoteChatSessionData (since 1.19.3)
193+
* @return The remote chat sesion data or null
194+
*/
195+
@Nullable
196+
public WrappedRemoteChatSessionData getRemoteChatSessionData() {
197+
return this.remoteChatSessionData;
129198
}
130199

131200
/**
@@ -157,8 +226,7 @@ public Object getGeneric(PlayerInfoData specific) {
157226
args.add(MinecraftReflection.getIChatBaseComponentClass());
158227

159228
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
160-
// RemoteChatSession$a...
161-
args.add(MinecraftReflection.getRemoteChatSessionClass().getClasses()[0]);
229+
args.add(MinecraftReflection.getRemoteChatSessionDataClass());
162230
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
163231
args.add(MinecraftReflection.getProfilePublicKeyDataClass());
164232
}
@@ -175,26 +243,28 @@ public Object getGeneric(PlayerInfoData specific) {
175243
Object gameMode = EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode);
176244
Object displayName = specific.displayName != null ? specific.displayName.handle : null;
177245

246+
Object profile = specific.profile != null ? specific.profile.handle : null;
178247
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
179248
return constructor.newInstance(
180249
specific.profileId,
181-
specific.profile.handle,
250+
profile,
182251
specific.listed,
183252
specific.latency,
184253
gameMode,
185254
displayName,
186-
null); // TODO: do we want to support this?
255+
specific.remoteChatSessionData != null ? BukkitConverters.getWrappedRemoteChatSessionDataConverter().getGeneric(specific.remoteChatSessionData) : null
256+
);
187257
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
188258
return constructor.newInstance(
189-
specific.profile.handle,
259+
profile,
190260
specific.latency,
191261
gameMode,
192262
displayName,
193263
specific.profileKeyData == null ? null : specific.profileKeyData.handle);
194264
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
195-
return constructor.newInstance(specific.profile.handle, specific.latency, gameMode, displayName);
265+
return constructor.newInstance(profile, specific.latency, gameMode, displayName);
196266
} else {
197-
return constructor.newInstance(null, specific.profile.handle, specific.latency, gameMode, displayName);
267+
return constructor.newInstance(null, profile, specific.latency, gameMode, displayName);
198268
}
199269
} catch (Exception e) {
200270
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
@@ -222,8 +292,18 @@ public PlayerInfoData getSpecific(Object generic) {
222292
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
223293
WrappedChatComponent displayName = displayNames.read(0);
224294

295+
if(MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
296+
return new PlayerInfoData(modifier.<UUID>withType(UUID.class).read(0),
297+
latency,
298+
modifier.<Boolean>withType(boolean.class).read(0),
299+
gameMode,
300+
gameProfile,
301+
displayName,
302+
modifier.withType(MinecraftReflection.getRemoteChatSessionDataClass(), BukkitConverters.getWrappedRemoteChatSessionDataConverter()).read(0)
303+
);
304+
}
225305
WrappedProfileKeyData key = null;
226-
if (MinecraftVersion.WILD_UPDATE.atOrAbove() && !MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
306+
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
227307
StructureModifier<WrappedProfileKeyData> keyData = modifier.withType(
228308
MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter());
229309
key = keyData.read(0);
@@ -253,20 +333,34 @@ public boolean equals(Object obj) {
253333
// Only compare objects of similar type
254334
if (obj instanceof PlayerInfoData) {
255335
PlayerInfoData other = (PlayerInfoData) obj;
256-
return profile.equals(other.profile) && latency == other.latency && gameMode == other.gameMode
257-
&& Objects.equals(displayName, other.displayName);
336+
return Objects.equals(profile, other.profile)
337+
&& Objects.equals(profileId, other.profileId)
338+
&& latency == other.latency
339+
&& gameMode == other.gameMode
340+
&& Objects.equals(displayName, other.displayName)
341+
&& listed == other.listed
342+
&& Objects.equals(remoteChatSessionData, other.remoteChatSessionData)
343+
&& Objects.equals(profileKeyData, other.profileKeyData);
258344
}
259345
return false;
260346
}
261347

262348
@Override
263349
public int hashCode() {
264-
return Objects.hash(latency, gameMode, profile, displayName);
350+
return Objects.hash(latency, gameMode, profile, displayName, profileKeyData, remoteChatSessionData, listed);
265351
}
266352

267353
@Override
268354
public String toString() {
355+
if(MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
356+
return String.format("PlayerInfoData[latency=%s, listed=%b, gameMode=%s, profile=%s, displayName=%s, remoteChatSession=%s]",
357+
latency, listed, gameMode, profile, displayName, remoteChatSessionData);
358+
}
359+
if(MinecraftVersion.WILD_UPDATE.atOrAbove()) {
360+
return String.format("PlayerInfoData[latency=%s, listed=%b, gameMode=%s, profile=%s, displayName=%s, profilePublicKey=%s]",
361+
latency, listed, gameMode, profile, displayName, profileKeyData);
362+
}
269363
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s]",
270-
latency, gameMode, profile, displayName);
364+
latency, gameMode, profile, displayName);
271365
}
272366
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.comphenix.protocol.wrappers;
2+
3+
import com.comphenix.protocol.reflect.StructureModifier;
4+
import com.comphenix.protocol.reflect.accessors.Accessors;
5+
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
6+
import com.comphenix.protocol.utility.MinecraftReflection;
7+
8+
import java.util.UUID;
9+
10+
/**
11+
* A wrapper around the remote chat session.
12+
*
13+
* @since 1.19.3
14+
*/
15+
public class WrappedRemoteChatSessionData extends AbstractWrapper {
16+
private final static Class<?> HANDLE_TYPE = MinecraftReflection.getRemoteChatSessionDataClass();
17+
private static ConstructorAccessor CONSTRUCTOR;
18+
private StructureModifier<Object> modifier;
19+
20+
/**
21+
* Constructs a new profile public key wrapper directly from a nms RemoteChatSession.Data/RemoteChatSession.a object.
22+
*
23+
* @param handle the handle to create the wrapper from.
24+
*/
25+
public WrappedRemoteChatSessionData(Object handle) {
26+
super(HANDLE_TYPE);
27+
this.setHandle(handle);
28+
}
29+
30+
public WrappedRemoteChatSessionData(UUID sessionId, WrappedProfilePublicKey.WrappedProfileKeyData profilePublicKey) {
31+
super(HANDLE_TYPE);
32+
if (CONSTRUCTOR == null) {
33+
CONSTRUCTOR = Accessors.getConstructorAccessor(
34+
this.getHandleType(),
35+
UUID.class, MinecraftReflection.getProfilePublicKeyDataClass());
36+
}
37+
38+
this.setHandle(CONSTRUCTOR.invoke(sessionId, profilePublicKey.getHandle()));
39+
}
40+
41+
/**
42+
* Retrieves the id of the current session
43+
* @return session id
44+
*/
45+
public UUID getSessionID() {
46+
return this.modifier.<UUID>withType(UUID.class).read(0);
47+
}
48+
49+
/**
50+
* Set the id for this session
51+
* @param sessionId new session id
52+
*/
53+
public void setSessionID(UUID sessionId) {
54+
this.modifier.<UUID>withType(UUID.class).write(0, sessionId);
55+
}
56+
57+
/**
58+
* Retrieves the ProfileKeyData
59+
* @return the public key data for this session
60+
*/
61+
public WrappedProfilePublicKey.WrappedProfileKeyData getProfilePublicKey() {
62+
return this.modifier.withType(MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter()).read(0);
63+
}
64+
65+
/**
66+
* Set the profile key data for this session
67+
* @param data ProfileKeyData
68+
*/
69+
public void setProfilePublicKey(WrappedProfilePublicKey.WrappedProfileKeyData data) {
70+
this.modifier.withType(MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter()).write(0, data);
71+
}
72+
73+
@Override
74+
protected void setHandle(Object handle) {
75+
super.setHandle(handle);
76+
this.modifier = new StructureModifier<>(HANDLE_TYPE).withTarget(handle);
77+
}
78+
79+
@Override
80+
public boolean equals(Object obj) {
81+
if(obj instanceof WrappedRemoteChatSessionData) {
82+
return handle.equals(((WrappedRemoteChatSessionData) obj).getHandle());
83+
}
84+
return false;
85+
}
86+
87+
@Override
88+
public String toString() {
89+
return handle.toString();
90+
}
91+
92+
@Override
93+
public int hashCode() {
94+
return handle.hashCode();
95+
}
96+
}

0 commit comments

Comments
 (0)