From 08ea2da642cb679de8f279c4d26f9dd8c9def2cc Mon Sep 17 00:00:00 2001 From: Lukas Alt Date: Sat, 29 Apr 2023 21:49:51 +0200 Subject: [PATCH] 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 --- .../protocol/events/AbstractStructure.java | 19 ++- .../protocol/utility/MinecraftReflection.java | 4 + .../protocol/wrappers/BukkitConverters.java | 4 + .../protocol/wrappers/PlayerInfoData.java | 128 +++++++++++++++--- .../WrappedRemoteChatSessionData.java | 96 +++++++++++++ .../protocol/events/PacketContainerTest.java | 22 +-- .../comphenix/protocol/utility/TestUtils.java | 21 +++ .../wrappers/BukkitConvertersTest.java | 93 ++++++++----- .../protocol/wrappers/PlayerInfoDataTest.java | 12 +- 9 files changed, 320 insertions(+), 79 deletions(-) create mode 100644 src/main/java/com/comphenix/protocol/wrappers/WrappedRemoteChatSessionData.java diff --git a/src/main/java/com/comphenix/protocol/events/AbstractStructure.java b/src/main/java/com/comphenix/protocol/events/AbstractStructure.java index 5aa84f10..3c75254b 100644 --- a/src/main/java/com/comphenix/protocol/events/AbstractStructure.java +++ b/src/main/java/com/comphenix/protocol/events/AbstractStructure.java @@ -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 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 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 getRemoteChatSessionData() { + return structureModifier.withType( + MinecraftReflection.getRemoteChatSessionDataClass(), + BukkitConverters.getWrappedRemoteChatSessionDataConverter() + ); } /** diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index 8e675d1a..3263782e 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -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); } diff --git a/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java index 5c807fb6..8a554950 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -604,6 +604,10 @@ public class BukkitConverters { return ignoreNull(handle(WrappedProfileKeyData::getHandle, WrappedProfileKeyData::new, WrappedProfileKeyData.class)); } + public static EquivalentConverter getWrappedRemoteChatSessionDataConverter() { + return ignoreNull(handle(WrappedRemoteChatSessionData::getHandle, WrappedRemoteChatSessionData::new, WrappedRemoteChatSessionData.class)); + } + /** * @return converter for cryptographic signature data that are used in login and chat packets */ diff --git a/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java b/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java index d9299980..d1f94759 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java +++ b/src/main/java/com/comphenix/protocol/wrappers/PlayerInfoData.java @@ -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.withType(UUID.class).read(0), + latency, + modifier.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 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); } } diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedRemoteChatSessionData.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedRemoteChatSessionData.java new file mode 100644 index 00000000..21494cb9 --- /dev/null +++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedRemoteChatSessionData.java @@ -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 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.withType(UUID.class).read(0); + } + + /** + * Set the id for this session + * @param sessionId new session id + */ + public void setSessionID(UUID sessionId) { + this.modifier.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(); + } +} diff --git a/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index 9cf86978..f2c01948 100644 --- a/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -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 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); } } } diff --git a/src/test/java/com/comphenix/protocol/utility/TestUtils.java b/src/test/java/com/comphenix/protocol/utility/TestUtils.java index 6211fb4d..36f2c1fb 100644 --- a/src/test/java/com/comphenix/protocol/utility/TestUtils.java +++ b/src/test/java/com/comphenix/protocol/utility/TestUtils.java @@ -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)); + } } diff --git a/src/test/java/com/comphenix/protocol/wrappers/BukkitConvertersTest.java b/src/test/java/com/comphenix/protocol/wrappers/BukkitConvertersTest.java index 7dae374d..21ba518e 100644 --- a/src/test/java/com/comphenix/protocol/wrappers/BukkitConvertersTest.java +++ b/src/test/java/com/comphenix/protocol/wrappers/BukkitConvertersTest.java @@ -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 converter = BukkitConverters.getItemStackConverter(); - Object nmsStack = converter.getGeneric(item); - ItemStack back = converter.getSpecific(nmsStack); + EquivalentConverter 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 test = new Left<>("bla"); EquivalentConverter> converter = BukkitConverters.getEitherConverter( - Converters.passthrough(String.class), Converters.passthrough(String.class) + Converters.passthrough(String.class), Converters.passthrough(String.class) ); com.mojang.datafixers.util.Either nmsEither = (com.mojang.datafixers.util.Either) 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); + + } } diff --git a/src/test/java/com/comphenix/protocol/wrappers/PlayerInfoDataTest.java b/src/test/java/com/comphenix/protocol/wrappers/PlayerInfoDataTest.java index ba7c8b5a..bfd0c9cd 100644 --- a/src/test/java/com/comphenix/protocol/wrappers/PlayerInfoDataTest.java +++ b/src/test/java/com/comphenix/protocol/wrappers/PlayerInfoDataTest.java @@ -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); } }