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
This commit is contained in:
Lukas Alt 2023-04-29 21:49:51 +02:00 committed by GitHub
parent 448e9369de
commit 08ea2da642
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 320 additions and 79 deletions

View File

@ -961,7 +961,7 @@ public abstract class AbstractStructure {
}
/**
* Retrieve a read/write structure for profile public keys in 1.9
* Retrieve a read/write structure for profile public keys in 1.19
* @return The Structure Modifier
*/
public StructureModifier<WrappedProfilePublicKey> getProfilePublicKeys() {
@ -971,13 +971,24 @@ public abstract class AbstractStructure {
}
/**
* Retrieve a read/write structure for profile public key data in 1.9
* Retrieve a read/write structure for profile public key data in 1.19
* @return The Structure Modifier
*/
public StructureModifier<WrappedProfileKeyData> getProfilePublicKeyData() {
return structureModifier.withType(
MinecraftReflection.getProfilePublicKeyDataClass(),
BukkitConverters.getWrappedPublicKeyDataConverter());
MinecraftReflection.getProfilePublicKeyDataClass(),
BukkitConverters.getWrappedPublicKeyDataConverter());
}
/**
* Retrieves read/write structure for remote chat session data in 1.19.3
* @return The Structure Modifier
*/
public StructureModifier<WrappedRemoteChatSessionData> getRemoteChatSessionData() {
return structureModifier.withType(
MinecraftReflection.getRemoteChatSessionDataClass(),
BukkitConverters.getWrappedRemoteChatSessionDataConverter()
);
}
/**

View File

@ -1621,6 +1621,10 @@ public final class MinecraftReflection {
return getMinecraftClass("network.chat.RemoteChatSession");
}
public static Class<?> getRemoteChatSessionDataClass() {
return getRemoteChatSessionClass().getClasses()[0];
}
public static Class<?> getFastUtilClass(String className) {
return getLibraryClass("it.unimi.dsi.fastutil." + className);
}

View File

@ -604,6 +604,10 @@ public class BukkitConverters {
return ignoreNull(handle(WrappedProfileKeyData::getHandle, WrappedProfileKeyData::new, WrappedProfileKeyData.class));
}
public static EquivalentConverter<WrappedRemoteChatSessionData> getWrappedRemoteChatSessionDataConverter() {
return ignoreNull(handle(WrappedRemoteChatSessionData::getHandle, WrappedRemoteChatSessionData::new, WrappedRemoteChatSessionData.class));
}
/**
* @return converter for cryptographic signature data that are used in login and chat packets
*/

View File

@ -30,6 +30,8 @@ import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import javax.annotation.Nullable;
/**
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
* @author dmulloy2
@ -43,6 +45,9 @@ public class PlayerInfoData {
private final NativeGameMode gameMode;
private final WrappedGameProfile profile;
private final WrappedChatComponent displayName;
@Nullable
private final WrappedRemoteChatSessionData remoteChatSessionData;
@Nullable
private final WrappedProfileKeyData profileKeyData;
// This is the same order as the NMS class, minus the packet (which isn't a field)
@ -54,7 +59,35 @@ public class PlayerInfoData {
this(profile.getUUID(), latency, true, gameMode, profile, displayName, keyData);
}
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, WrappedProfileKeyData profileKeyData) {
/**
* 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) {
this.profileId = profileId;
this.latency = latency;
this.listed = listed;
@ -62,6 +95,29 @@ public class PlayerInfoData {
this.profile = profile;
this.displayName = displayName;
this.profileKeyData = profileKeyData;
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;
}
/**
@ -69,6 +125,9 @@ public class PlayerInfoData {
* @return the id of the profile
*/
public UUID getProfileId() {
if(profileId == null && profile != null) {
return profile.getUUID(); // Ensure forward compatability
}
return profileId;
}
@ -97,7 +156,7 @@ public class PlayerInfoData {
}
/**
* Gets if the player is listed on the client.
* Gets if the player is listed on the client (since 1.19.3)
* @return if the player is listed
*/
public boolean isListed() {
@ -121,11 +180,21 @@ public class PlayerInfoData {
}
/**
* Gets the profile key data of the player represented by this data, null if not present.
* @return The profile key data
* 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.
*/
@Nullable
public WrappedProfileKeyData getProfileKeyData() {
return this.profileKeyData;
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;
}
/**
@ -157,8 +226,7 @@ public class PlayerInfoData {
args.add(MinecraftReflection.getIChatBaseComponentClass());
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
// RemoteChatSession$a...
args.add(MinecraftReflection.getRemoteChatSessionClass().getClasses()[0]);
args.add(MinecraftReflection.getRemoteChatSessionDataClass());
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
args.add(MinecraftReflection.getProfilePublicKeyDataClass());
}
@ -175,26 +243,28 @@ public class PlayerInfoData {
Object gameMode = EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode);
Object displayName = specific.displayName != null ? specific.displayName.handle : null;
Object profile = specific.profile != null ? specific.profile.handle : null;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
return constructor.newInstance(
specific.profileId,
specific.profile.handle,
profile,
specific.listed,
specific.latency,
gameMode,
displayName,
null); // TODO: do we want to support this?
specific.remoteChatSessionData != null ? BukkitConverters.getWrappedRemoteChatSessionDataConverter().getGeneric(specific.remoteChatSessionData) : null
);
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
return constructor.newInstance(
specific.profile.handle,
profile,
specific.latency,
gameMode,
displayName,
specific.profileKeyData == null ? null : specific.profileKeyData.handle);
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
return constructor.newInstance(specific.profile.handle, specific.latency, gameMode, displayName);
return constructor.newInstance(profile, specific.latency, gameMode, displayName);
} else {
return constructor.newInstance(null, specific.profile.handle, specific.latency, gameMode, displayName);
return constructor.newInstance(null, profile, specific.latency, gameMode, displayName);
}
} catch (Exception e) {
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
@ -222,8 +292,18 @@ public class PlayerInfoData {
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
WrappedChatComponent displayName = displayNames.read(0);
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)
);
}
WrappedProfileKeyData key = null;
if (MinecraftVersion.WILD_UPDATE.atOrAbove() && !MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
StructureModifier<WrappedProfileKeyData> keyData = modifier.withType(
MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter());
key = keyData.read(0);
@ -253,20 +333,34 @@ public class PlayerInfoData {
// Only compare objects of similar type
if (obj instanceof PlayerInfoData) {
PlayerInfoData other = (PlayerInfoData) obj;
return profile.equals(other.profile) && latency == other.latency && gameMode == other.gameMode
&& Objects.equals(displayName, other.displayName);
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);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(latency, gameMode, profile, displayName);
return Objects.hash(latency, gameMode, profile, displayName, profileKeyData, remoteChatSessionData, listed);
}
@Override
public String toString() {
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);
}
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s]",
latency, gameMode, profile, displayName);
latency, gameMode, profile, displayName);
}
}

View File

@ -0,0 +1,96 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.util.UUID;
/**
* A wrapper around the remote chat session.
*
* @since 1.19.3
*/
public class WrappedRemoteChatSessionData extends AbstractWrapper {
private final static Class<?> HANDLE_TYPE = MinecraftReflection.getRemoteChatSessionDataClass();
private static ConstructorAccessor CONSTRUCTOR;
private StructureModifier<Object> modifier;
/**
* Constructs a new profile public key wrapper directly from a nms RemoteChatSession.Data/RemoteChatSession.a object.
*
* @param handle the handle to create the wrapper from.
*/
public WrappedRemoteChatSessionData(Object handle) {
super(HANDLE_TYPE);
this.setHandle(handle);
}
public WrappedRemoteChatSessionData(UUID sessionId, WrappedProfilePublicKey.WrappedProfileKeyData profilePublicKey) {
super(HANDLE_TYPE);
if (CONSTRUCTOR == null) {
CONSTRUCTOR = Accessors.getConstructorAccessor(
this.getHandleType(),
UUID.class, MinecraftReflection.getProfilePublicKeyDataClass());
}
this.setHandle(CONSTRUCTOR.invoke(sessionId, profilePublicKey.getHandle()));
}
/**
* Retrieves the id of the current session
* @return session id
*/
public UUID getSessionID() {
return this.modifier.<UUID>withType(UUID.class).read(0);
}
/**
* Set the id for this session
* @param sessionId new session id
*/
public void setSessionID(UUID sessionId) {
this.modifier.<UUID>withType(UUID.class).write(0, sessionId);
}
/**
* Retrieves the ProfileKeyData
* @return the public key data for this session
*/
public WrappedProfilePublicKey.WrappedProfileKeyData getProfilePublicKey() {
return this.modifier.withType(MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter()).read(0);
}
/**
* Set the profile key data for this session
* @param data ProfileKeyData
*/
public void setProfilePublicKey(WrappedProfilePublicKey.WrappedProfileKeyData data) {
this.modifier.withType(MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter()).write(0, data);
}
@Override
protected void setHandle(Object handle) {
super.setHandle(handle);
this.modifier = new StructureModifier<>(HANDLE_TYPE).withTarget(handle);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof WrappedRemoteChatSessionData) {
return handle.equals(((WrappedRemoteChatSessionData) obj).getHandle());
}
return false;
}
@Override
public String toString() {
return handle.toString();
}
@Override
public int hashCode() {
return handle.hashCode();
}
}

View File

@ -40,26 +40,13 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ComponentConverter;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
import com.comphenix.protocol.wrappers.Pair;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedBlockData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataValue;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedRegistry;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
@ -768,7 +755,7 @@ public class PacketContainerTest {
NativeGameMode.CREATIVE,
new WrappedGameProfile(new UUID(0, 0), "system"),
null,
null);
(WrappedRemoteChatSessionData) null);
updatePlayerInfoActions.getPlayerInfoDataLists().write(1, Collections.singletonList(data));
Set<EnumWrappers.PlayerInfoAction> readActions = updatePlayerInfoActions.getPlayerInfoActions().read(0);
@ -871,7 +858,6 @@ public class PacketContainerTest {
}
// gives some indication which cloning process fails as the checks itself are happening outside this method
System.out.println("Cloning " + type);
// Clone the packet all three ways
PacketContainer shallowCloned = constructed.shallowClone();
@ -886,8 +872,8 @@ public class PacketContainerTest {
serializedCloned.getLongs().write(0, 0L);
}
this.assertPacketsEqualAndSerializable(constructed, serializedCloned);
} catch (Exception ex) {
Assertions.fail("Unable to clone " + type, ex);
} catch (Throwable t) {
Assertions.fail("Unable to clone " + type, t);
}
}
}

View File

@ -7,7 +7,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.reflect.accessors.Accessors;
import java.lang.reflect.Field;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.time.Instant;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
@ -49,4 +57,17 @@ public class TestUtils {
public static void setFinalField(Object obj, Field field, Object newValue) {
Accessors.getFieldAccessor(field).set(obj, newValue);
}
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
return keyPairGenerator.generateKeyPair();
}
public static WrappedRemoteChatSessionData creteDummyRemoteChatSessionData() throws Exception {
byte[] signature = new byte[256];
new Random().nextBytes(signature);
return new WrappedRemoteChatSessionData(UUID.randomUUID(), new WrappedProfilePublicKey.WrappedProfileKeyData(Instant.now(), TestUtils.generateKeyPair().getPublic(), signature));
}
}

View File

@ -1,15 +1,11 @@
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.TestUtils;
import com.comphenix.protocol.wrappers.Either.Left;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@ -20,37 +16,50 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.Instant;
import java.util.Random;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class BukkitConvertersTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
item.setItemMeta(meta);
@Test
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
item.setItemMeta(meta);
EquivalentConverter<ItemStack> converter = BukkitConverters.getItemStackConverter();
Object nmsStack = converter.getGeneric(item);
ItemStack back = converter.getSpecific(nmsStack);
EquivalentConverter<ItemStack> converter = BukkitConverters.getItemStackConverter();
Object nmsStack = converter.getGeneric(item);
ItemStack back = converter.getSpecific(nmsStack);
assertEquals(item.getType(), back.getType());
assertEquals(item.getDurability(), back.getDurability());
assertEquals(item.hasItemMeta(), back.hasItemMeta());
assertTrue(Bukkit.getItemFactory().equals(item.getItemMeta(), back.getItemMeta()));
}
assertEquals(item.getType(), back.getType());
assertEquals(item.getDurability(), back.getDurability());
assertEquals(item.hasItemMeta(), back.hasItemMeta());
assertTrue(Bukkit.getItemFactory().equals(item.getItemMeta(), back.getItemMeta()));
}
@Test
public void testEither() {
Either<String, String> test = new Left<>("bla");
EquivalentConverter<Either<String, String>> converter = BukkitConverters.getEitherConverter(
Converters.passthrough(String.class), Converters.passthrough(String.class)
Converters.passthrough(String.class), Converters.passthrough(String.class)
);
com.mojang.datafixers.util.Either<String, String> nmsEither = (com.mojang.datafixers.util.Either<String, String>) converter.getGeneric(test);
@ -60,16 +69,26 @@ public class BukkitConvertersTest {
assertEquals(wrapped.right(), nmsEither.right());
}
@Test
public void testPacketContainerConverter() {
for (PacketType type : PacketType.values()) {
if(!type.isSupported()) {
continue;
}
PacketContainer container = new PacketContainer(type);
Object generic = BukkitConverters.getPacketContainerConverter().getGeneric(container);
Object specific = BukkitConverters.getPacketContainerConverter().getSpecific(generic);
assertTrue(EqualsBuilder.reflectionEquals(container, specific)); // PacketContainer does not properly implement equals(.)
}
}
@Test
public void testPacketContainerConverter() {
for (PacketType type : PacketType.values()) {
if (!type.isSupported()) {
continue;
}
PacketContainer container = new PacketContainer(type);
Object generic = BukkitConverters.getPacketContainerConverter().getGeneric(container);
Object specific = BukkitConverters.getPacketContainerConverter().getSpecific(generic);
assertTrue(EqualsBuilder.reflectionEquals(container, specific)); // PacketContainer does not properly implement equals(.)
}
}
@Test
public void testRemoteChatSessionDataConverter() throws Exception {
WrappedRemoteChatSessionData wrappedRemoteChatSessionData = TestUtils.creteDummyRemoteChatSessionData();
Object generic = BukkitConverters.getWrappedRemoteChatSessionDataConverter().getGeneric(wrappedRemoteChatSessionData);
WrappedRemoteChatSessionData specific = BukkitConverters.getWrappedRemoteChatSessionDataConverter().getSpecific(generic);
assertEquals(wrappedRemoteChatSessionData, specific);
}
}

View File

@ -17,6 +17,7 @@ package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.TestUtils;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
@ -33,14 +34,19 @@ public class PlayerInfoDataTest {
}
@Test
public void test() {
public void test() throws Exception {
WrappedGameProfile profile = new WrappedGameProfile(UUID.randomUUID(), "Name");
WrappedChatComponent displayName = WrappedChatComponent.fromText("Name's Name");
PlayerInfoData data = new PlayerInfoData(profile, 42, NativeGameMode.CREATIVE, displayName);
testWriteBack(new PlayerInfoData(profile, 42, NativeGameMode.CREATIVE, displayName));
testWriteBack(new PlayerInfoData(profile.getUUID(), 42, false, NativeGameMode.CREATIVE, profile, displayName, TestUtils.creteDummyRemoteChatSessionData()));
testWriteBack(new PlayerInfoData(profile.getUUID(), 42, false, NativeGameMode.CREATIVE, null, null, TestUtils.creteDummyRemoteChatSessionData()));
testWriteBack(new PlayerInfoData(profile.getUUID(), 42, true, NativeGameMode.CREATIVE, null, displayName));
}
private static void testWriteBack(PlayerInfoData data) {
Object generic = PlayerInfoData.getConverter().getGeneric(data);
PlayerInfoData back = PlayerInfoData.getConverter().getSpecific(generic);
assertEquals(data, back);
}
}