Add several scoreboard type wrappers (#2819)

This commit is contained in:
vytskalt 2024-02-19 19:11:12 +02:00 committed by Dan Mulloy
parent 77ada09c11
commit 616431cc33
No known key found for this signature in database
GPG Key ID: 3C5AD5D866D1539A
13 changed files with 877 additions and 23 deletions

View File

@ -870,6 +870,55 @@ public abstract class AbstractStructure {
EnumWrappers.getChatTypeConverter());
}
/**
* Retrieve a read/write structure for the DisplaySlot enum in 1.20.2.
* @return A modifier for DisplaySlot enum fields.
*/
public StructureModifier<EnumWrappers.DisplaySlot> getDisplaySlots() {
return structureModifier.withType(
EnumWrappers.getDisplaySlotClass(),
EnumWrappers.getDisplaySlotConverter());
}
/**
* Retrieve a read/write structure for the RenderType enum.
* @return A modifier for RenderType enum fields.
*/
public StructureModifier<EnumWrappers.RenderType> getRenderTypes() {
return structureModifier.withType(
EnumWrappers.getRenderTypeClass(),
EnumWrappers.getRenderTypeConverter());
}
/**
* Retrieve a read/write structure for the ChatFormatting enum.
* @return A modifier for ChatFormatting enum fields.
*/
public StructureModifier<EnumWrappers.ChatFormatting> getChatFormattings() {
return structureModifier.withType(
EnumWrappers.getChatFormattingClass(),
EnumWrappers.getChatFormattingConverter());
}
/**
* Retrieve a read/write structure for optional team parameters in 1.17+.
* @return A modifier for optional team parameters fields.
*/
public StructureModifier<Optional<WrappedTeamParameters>> getOptionalTeamParameters() {
return getOptionals(BukkitConverters.getWrappedTeamParametersConverter());
}
/**
* Retrieve a read/write structure for the NumberFormat class in 1.20.4+.
* @return A modifier for NumberFormat fields.
*/
public StructureModifier<WrappedNumberFormat> getNumberFormats() {
return structureModifier.withType(
MinecraftReflection.getNumberFormatClass().orElse(null),
BukkitConverters.getWrappedNumberFormatConverter());
}
/**
* Retrieve a read/write structure for the MinecraftKey class.
* @return A modifier for MinecraftKey fields.

View File

@ -17,10 +17,25 @@
package com.comphenix.protocol.events;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.Util;
import net.bytebuddy.description.ByteCodeElement;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
@ -35,27 +50,19 @@ import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import org.bukkit.*;
import org.bukkit.BanEntry;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.Statistic;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.profile.PlayerProfile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Represents a player object that can be serialized by Java.
*

View File

@ -69,9 +69,12 @@ public class StructureCache {
public static Object newPacket(Class<?> packetClass) {
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, packetClassKey -> {
PacketCreator creator = PacketCreator.forPacket(packetClassKey);
if (creator.get() != null) {
return creator;
try {
PacketCreator creator = PacketCreator.forPacket(packetClassKey);
if (creator.get() != null) {
return creator;
}
} catch (Exception ignored) {
}
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(packetClassKey);

View File

@ -679,6 +679,16 @@ public final class MinecraftReflection {
return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer", "network.chat.Component$Serializer", "IChatBaseComponent$ChatSerializer");
}
/**
* Retrieve the component style serializer class.
*
* @return The serializer class.
*/
public static Class<?> getStyleSerializerClass() {
return getMinecraftClass("network.chat.ChatModifier$ChatModifierSerializer", "ChatModifier$ChatModifierSerializer");
}
/**
* Retrieve the ServerPing class.
*
@ -1020,6 +1030,60 @@ public final class MinecraftReflection {
return getMinecraftClass("world.level.block.entity.TileEntity", "world.level.block.entity.BlockEntity", "TileEntity");
}
/**
* Retrieve the NMS team parameters class.
*
* @return The team parameters class.
*/
public static Optional<Class<?>> getTeamParametersClass() {
return getOptionalNMS("network.protocol.game.PacketPlayOutScoreboardTeam$b");
}
/**
* Retrieve the NMS component style class.
*
* @return The component style class.
*/
public static Class<?> getComponentStyleClass() {
return getMinecraftClass("network.chat.ChatModifier", "ChatModifier");
}
/**
* Retrieve the NMS NumberFormat class.
*
* @return The NumberFormat class.
*/
public static Optional<Class<?>> getNumberFormatClass() {
return getOptionalNMS("network.chat.numbers.NumberFormat");
}
/**
* Retrieve the NMS BlankFormat class.
*
* @return The FixedFormat class.
*/
public static Optional<Class<?>> getBlankFormatClass() {
return getOptionalNMS("network.chat.numbers.BlankFormat");
}
/**
* Retrieve the NMS FixedFormat class.
*
* @return The FixedFormat class.
*/
public static Optional<Class<?>> getFixedFormatClass() {
return getOptionalNMS("network.chat.numbers.FixedFormat");
}
/**
* Retrieve the NMS StyledFormat class.
*
* @return The StyledFormat class.
*/
public static Optional<Class<?>> getStyledFormatClass() {
return getOptionalNMS("network.chat.numbers.StyledFormat");
}
/**
* Retrieve the Gson class used by Minecraft.
*

View File

@ -16,7 +16,10 @@
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.gson.JsonObject;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
/**
@ -25,7 +28,16 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
* Note: The Adventure API Component is not included in CraftBukkit, Bukkit or Spigot and but is present in PaperMC.
*/
public class AdventureComponentConverter {
private static final GsonComponentSerializer SERIALIZER;
static {
if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
SERIALIZER = GsonComponentSerializer.gson();
} else {
SERIALIZER = GsonComponentSerializer.colorDownsamplingGson();
}
}
private AdventureComponentConverter() {
}
@ -35,7 +47,7 @@ public class AdventureComponentConverter {
* @return Component
*/
public static Component fromWrapper(WrappedChatComponent wrapper) {
return GsonComponentSerializer.gson().deserialize(wrapper.getJson());
return SERIALIZER.deserialize(wrapper.getJson());
}
/**
@ -71,11 +83,29 @@ public class AdventureComponentConverter {
* @return ProtocolLib wrapper
*/
public static WrappedChatComponent fromComponent(Component component) {
return WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component));
return WrappedChatComponent.fromJson(SERIALIZER.serialize(component));
}
/**
* Converts a {@link WrappedComponentStyle} into a {@link Style}
* @param wrapper ProtocolLib wrapper
* @return Style
*/
public static Style fromWrapper(WrappedComponentStyle wrapper) {
return SERIALIZER.serializer().fromJson(wrapper.getJson(), Style.class);
}
/**
* Converts a {@link Style} into a ProtocolLib wrapper
* @param style Style
* @return ProtocolLib wrapper
*/
public static WrappedComponentStyle fromStyle(Style style) {
return WrappedComponentStyle.fromJson((JsonObject) SERIALIZER.serializer().toJsonTree(style));
}
public static Class<?> getComponentClass() {
return Component.class;
return Component.class;
}
public static Component clone(Object component) {

View File

@ -640,6 +640,14 @@ public class BukkitConverters {
return ignoreNull(handle(WrappedLevelChunkData.LightData::getHandle, WrappedLevelChunkData.LightData::new, WrappedLevelChunkData.LightData.class));
}
public static EquivalentConverter<WrappedTeamParameters> getWrappedTeamParametersConverter() {
return ignoreNull(handle(WrappedTeamParameters::getHandle, WrappedTeamParameters::new, WrappedTeamParameters.class));
}
public static EquivalentConverter<WrappedNumberFormat> getWrappedNumberFormatConverter() {
return ignoreNull(handle(WrappedNumberFormat::getHandle, WrappedNumberFormat::fromHandle, WrappedNumberFormat.class));
}
public static EquivalentConverter<PacketContainer> getPacketContainerConverter() {
return ignoreNull(handle(PacketContainer::getHandle, PacketContainer::fromPacket, PacketContainer.class));
}

View File

@ -12,6 +12,7 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import java.lang.reflect.Field;
@ -464,6 +465,163 @@ public abstract class EnumWrappers {
}
}
/**
* @since 1.20.2
*/
public enum DisplaySlot {
LIST,
SIDEBAR,
BELOW_NAME,
TEAM_BLACK,
TEAM_DARK_BLUE,
TEAM_DARK_GREEN,
TEAM_DARK_AQUA,
TEAM_DARK_RED,
TEAM_DARK_PURPLE,
TEAM_GOLD,
TEAM_GRAY,
TEAM_DARK_GRAY,
TEAM_BLUE,
TEAM_GREEN,
TEAM_AQUA,
TEAM_RED,
TEAM_LIGHT_PURPLE,
TEAM_YELLOW,
TEAM_WHITE;
}
public enum RenderType {
INTEGER,
HEARTS
}
public enum ChatFormatting {
BLACK,
DARK_BLUE,
DARK_GREEN,
DARK_AQUA,
DARK_RED,
DARK_PURPLE,
GOLD,
GRAY,
DARK_GRAY,
BLUE,
GREEN,
AQUA,
RED,
LIGHT_PURPLE,
YELLOW,
WHITE,
OBFUSCATED,
BOLD,
STRIKETHROUGH,
UNDERLINE,
ITALIC,
RESET;
public ChatColor toBukkit() {
switch (this){
case BLACK:
return ChatColor.BLACK;
case DARK_BLUE:
return ChatColor.DARK_BLUE;
case DARK_GREEN:
return ChatColor.DARK_GREEN;
case DARK_AQUA:
return ChatColor.DARK_AQUA;
case DARK_RED:
return ChatColor.DARK_RED;
case DARK_PURPLE:
return ChatColor.DARK_PURPLE;
case GOLD:
return ChatColor.GOLD;
case GRAY:
return ChatColor.GRAY;
case DARK_GRAY:
return ChatColor.DARK_GRAY;
case BLUE:
return ChatColor.BLUE;
case GREEN:
return ChatColor.GREEN;
case AQUA:
return ChatColor.AQUA;
case RED:
return ChatColor.RED;
case LIGHT_PURPLE:
return ChatColor.LIGHT_PURPLE;
case YELLOW:
return ChatColor.YELLOW;
case WHITE:
return ChatColor.WHITE;
case OBFUSCATED:
return ChatColor.MAGIC;
case BOLD:
return ChatColor.BOLD;
case STRIKETHROUGH:
return ChatColor.STRIKETHROUGH;
case UNDERLINE:
return ChatColor.UNDERLINE;
case ITALIC:
return ChatColor.ITALIC;
case RESET:
return ChatColor.RESET;
default:
throw new IllegalStateException("Unimplemented Bukkit equivalent for " + name());
}
}
public static ChatFormatting fromBukkit(ChatColor color) {
switch (color){
case BLACK:
return ChatFormatting.BLACK;
case DARK_BLUE:
return ChatFormatting.DARK_BLUE;
case DARK_GREEN:
return ChatFormatting.DARK_GREEN;
case DARK_AQUA:
return ChatFormatting.DARK_AQUA;
case DARK_RED:
return ChatFormatting.DARK_RED;
case DARK_PURPLE:
return ChatFormatting.DARK_PURPLE;
case GOLD:
return ChatFormatting.GOLD;
case GRAY:
return ChatFormatting.GRAY;
case DARK_GRAY:
return ChatFormatting.DARK_GRAY;
case BLUE:
return ChatFormatting.BLUE;
case GREEN:
return ChatFormatting.GREEN;
case AQUA:
return ChatFormatting.AQUA;
case RED:
return ChatFormatting.RED;
case LIGHT_PURPLE:
return ChatFormatting.LIGHT_PURPLE;
case YELLOW:
return ChatFormatting.YELLOW;
case WHITE:
return ChatFormatting.WHITE;
case MAGIC:
return ChatFormatting.OBFUSCATED;
case BOLD:
return ChatFormatting.BOLD;
case STRIKETHROUGH:
return ChatFormatting.STRIKETHROUGH;
case UNDERLINE:
return ChatFormatting.UNDERLINE;
case ITALIC:
return ChatFormatting.ITALIC;
case RESET:
return ChatFormatting.RESET;
default:
throw new IllegalStateException("Unknown ChatColor " + color);
}
}
}
private static Class<?> PROTOCOL_CLASS = null;
private static Class<?> CLIENT_COMMAND_CLASS = null;
private static Class<?> CHAT_VISIBILITY_CLASS = null;
@ -485,6 +643,9 @@ public abstract class EnumWrappers {
private static Class<?> DIRECTION_CLASS = null;
private static Class<?> CHAT_TYPE_CLASS = null;
private static Class<?> ENTITY_POSE_CLASS = null;
private static Class<?> DISPLAY_SLOT_CLASS = null;
private static Class<?> RENDER_TYPE_CLASS = null;
private static Class<?> CHAT_FORMATTING_CLASS = null;
private static boolean INITIALIZED = false;
private static Map<Class<?>, EquivalentConverter<?>> FROM_NATIVE = new HashMap<>();
@ -574,6 +735,9 @@ public abstract class EnumWrappers {
CHAT_TYPE_CLASS = getEnum(PacketType.Play.Server.CHAT.getPacketClass(), 0);
ENTITY_POSE_CLASS = MinecraftReflection.getNullableNMS("world.entity.EntityPose", "world.entity.Pose", "EntityPose");
DISPLAY_SLOT_CLASS = MinecraftReflection.getNullableNMS("world.scores.DisplaySlot");
RENDER_TYPE_CLASS = MinecraftReflection.getNullableNMS("world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay", "IScoreboardCriteria$EnumScoreboardHealthDisplay");
CHAT_FORMATTING_CLASS = MinecraftReflection.getNullableNMS("EnumChatFormat");
associate(PROTOCOL_CLASS, Protocol.class, getProtocolConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
@ -595,6 +759,9 @@ public abstract class EnumWrappers {
associate(CHAT_TYPE_CLASS, ChatType.class, getChatTypeConverter());
associate(HAND_CLASS, Hand.class, getHandConverter());
associate(ENTITY_USE_ACTION_CLASS, EntityUseAction.class, getEntityUseActionConverter());
associate(DISPLAY_SLOT_CLASS, DisplaySlot.class, getDisplaySlotConverter());
associate(RENDER_TYPE_CLASS, RenderType.class, getRenderTypeConverter());
associate(CHAT_FORMATTING_CLASS, ChatFormatting.class, getChatFormattingConverter());
if (ENTITY_POSE_CLASS != null) {
associate(ENTITY_POSE_CLASS, EntityPose.class, getEntityPoseConverter());
@ -746,6 +913,21 @@ public abstract class EnumWrappers {
return ENTITY_POSE_CLASS;
}
public static Class<?> getDisplaySlotClass() {
initialize();
return DISPLAY_SLOT_CLASS;
}
public static Class<?> getRenderTypeClass() {
initialize();
return RENDER_TYPE_CLASS;
}
public static Class<?> getChatFormattingClass() {
initialize();
return CHAT_FORMATTING_CLASS;
}
// Get the converters
public static EquivalentConverter<Protocol> getProtocolConverter() {
return new EnumConverter<>(getProtocolClass(), Protocol.class);
@ -826,7 +1008,19 @@ public abstract class EnumWrappers {
public static EquivalentConverter<ChatType> getChatTypeConverter() {
return new EnumConverter<>(getChatTypeClass(), ChatType.class);
}
public static EquivalentConverter<DisplaySlot> getDisplaySlotConverter() {
return new EnumConverter<>(getDisplaySlotClass(), DisplaySlot.class);
}
public static EquivalentConverter<RenderType> getRenderTypeConverter() {
return new EnumConverter<>(getRenderTypeClass(), RenderType.class);
}
public static EquivalentConverter<ChatFormatting> getChatFormattingConverter() {
return new EnumConverter<>(getChatFormattingClass(), ChatFormatting.class);
}
/**
* @since 1.13+
* @return {@link EnumConverter} or null (if bellow 1.13 / nms EnumPose class cannot be found)

View File

@ -0,0 +1,59 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.codecs.WrappedCodec;
import com.comphenix.protocol.wrappers.codecs.WrappedDynamicOps;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* A wrapper around the component style NMS class.
*
* @author vytskalt
*/
public class WrappedComponentStyle extends AbstractWrapper {
private static final WrappedCodec CODEC; // 1.20.4+
private static final Gson GSON; // Below 1.20.4
static {
if (MinecraftVersion.v1_20_4.atOrAbove()) {
FuzzyReflection fuzzySerializer = FuzzyReflection.fromClass(MinecraftReflection.getStyleSerializerClass(), true);
Object codec = Accessors.getFieldAccessor(fuzzySerializer.getFieldByType("CODEC", MinecraftReflection.getCodecClass())).get(null);
CODEC = WrappedCodec.fromHandle(codec);
GSON = null;
} else {
FuzzyReflection fuzzySerializer = FuzzyReflection.fromClass(MinecraftReflection.getChatSerializerClass(), true);
CODEC = null;
GSON = (Gson) Accessors.getFieldAccessor(fuzzySerializer.getFieldByType("gson", Gson.class)).get(null);
}
}
public WrappedComponentStyle(Object handle) {
super(MinecraftReflection.getComponentStyleClass());
setHandle(handle);
}
public JsonElement getJson() {
if (CODEC != null) {
return (JsonElement) CODEC.encode(handle, WrappedDynamicOps.json(false))
.getOrThrow(JsonParseException::new);
} else {
return GSON.toJsonTree(handle);
}
}
public static WrappedComponentStyle fromJson(JsonElement json) {
Object handle;
if (CODEC != null) {
handle = CODEC.parse(json, WrappedDynamicOps.json(false))
.getOrThrow(JsonParseException::new);
} else {
handle = GSON.fromJson(json, MinecraftReflection.getComponentStyleClass());
}
return new WrappedComponentStyle(handle);
}
}

View File

@ -0,0 +1,126 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.FuzzyReflection;
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 org.jetbrains.annotations.NotNull;
/**
* A wrapper around the NumberFormat NMS classes.
*
* @author vytskalt
* @since 1.20.4
*/
@SuppressWarnings("OptionalGetWithoutIsPresent")
public class WrappedNumberFormat extends AbstractWrapper {
private static final Object BLANK;
private static final ConstructorAccessor FIXED_CONSTRUCTOR, STYLED_CONSTRUCTOR;
static {
if (!isSupported()) {
BLANK = null;
FIXED_CONSTRUCTOR = null;
STYLED_CONSTRUCTOR = null;
} else {
Class<?> blankClass = MinecraftReflection.getBlankFormatClass().get();
FuzzyReflection fuzzyBlank = FuzzyReflection.fromClass(blankClass, true);
BLANK = Accessors.getFieldAccessor(fuzzyBlank.getFieldByType("INSTANCE", blankClass)).get(null);
FIXED_CONSTRUCTOR = Accessors.getConstructorAccessor(
MinecraftReflection.getFixedFormatClass().get(),
MinecraftReflection.getIChatBaseComponentClass()
);
STYLED_CONSTRUCTOR = Accessors.getConstructorAccessor(
MinecraftReflection.getStyledFormatClass().get(),
MinecraftReflection.getComponentStyleClass()
);
}
}
/**
* @return Whether the NumberFormat classes exist on the current server version
*/
public static boolean isSupported() {
return MinecraftReflection.getNumberFormatClass().isPresent();
}
public static WrappedNumberFormat fromHandle(Object handle) {
throwIfUnsupported();
if (MinecraftReflection.getBlankFormatClass().get().isInstance(handle)) {
return new Blank(handle);
} else if (MinecraftReflection.getFixedFormatClass().get().isInstance(handle)) {
return new Fixed(handle);
} else if (MinecraftReflection.getStyledFormatClass().get().isInstance(handle)) {
return new Styled(handle);
} else {
throw new IllegalArgumentException("handle is not a NumberFormat instance, but " + handle.getClass());
}
}
public static Blank blank() {
throwIfUnsupported();
return new Blank(WrappedNumberFormat.BLANK);
}
public static Fixed fixed(@NotNull WrappedChatComponent content) {
throwIfUnsupported();
Object handle = FIXED_CONSTRUCTOR.invoke(content.getHandle());
return new Fixed(handle);
}
public static Styled styled(@NotNull WrappedComponentStyle style) {
throwIfUnsupported();
Object handle = STYLED_CONSTRUCTOR.invoke(style.getHandle());
return new Styled(handle);
}
private static void throwIfUnsupported() {
if (!isSupported()) {
throw new IllegalStateException("NumberFormat classes don't exist on this server version");
}
}
private WrappedNumberFormat(Class<?> handleType) {
super(handleType);
}
public static class Blank extends WrappedNumberFormat {
private Blank(Object handle) {
super(MinecraftReflection.getBlankFormatClass().get());
setHandle(handle);
}
}
public static class Fixed extends WrappedNumberFormat {
private final StructureModifier<Object> modifier;
private Fixed(Object handle) {
super(MinecraftReflection.getFixedFormatClass().get());
setHandle(handle);
this.modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
}
public WrappedChatComponent getContent() {
Object handle = modifier.withType(MinecraftReflection.getIChatBaseComponentClass()).read(0);
return WrappedChatComponent.fromHandle(handle);
}
}
public static class Styled extends WrappedNumberFormat {
private final StructureModifier<Object> modifier;
private Styled(Object handle) {
super(MinecraftReflection.getStyledFormatClass().get());
setHandle(handle);
this.modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
}
public WrappedComponentStyle getStyle() {
Object handle = modifier.withType(MinecraftReflection.getComponentStyleClass()).read(0);
return new WrappedComponentStyle(handle);
}
}
}

View File

@ -0,0 +1,171 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A wrapper around the team parameters NMS class.
*
* @author vytskalt
* @since 1.17
*/
public class WrappedTeamParameters extends AbstractWrapper {
public static Class<?> getNmsClassOrThrow() {
return MinecraftReflection.getTeamParametersClass()
.orElseThrow(() -> new IllegalStateException("Team parameters class doesn't exist on this server version"));
}
/**
* @return Whether the team parameters class exists on the current server version
*/
public static boolean isSupported() {
return MinecraftReflection.getTeamParametersClass().isPresent();
}
public static Builder newBuilder() {
return newBuilder(null);
}
public static Builder newBuilder(@Nullable WrappedTeamParameters template) {
return new Builder(template);
}
private final StructureModifier<Object> modifier;
public WrappedTeamParameters(Object handle) {
super(getNmsClassOrThrow());
setHandle(handle);
this.modifier = new StructureModifier<>(getNmsClassOrThrow()).withTarget(handle);
}
@NotNull
public WrappedChatComponent getDisplayName() {
return readComponent(0);
}
@NotNull
public WrappedChatComponent getPrefix() {
return readComponent(1);
}
@NotNull
public WrappedChatComponent getSuffix() {
return readComponent(2);
}
@NotNull
public String getNametagVisibility() {
return modifier.<String>withType(String.class).read(0);
}
@NotNull
public String getCollisionRule() {
return modifier.<String>withType(String.class).read(1);
}
@NotNull
public EnumWrappers.ChatFormatting getColor() {
Object handle = modifier.withType(EnumWrappers.getChatFormattingClass()).read(0);
return EnumWrappers.getChatFormattingConverter().getSpecific(handle);
}
public int getOptions() {
return (int) modifier.withType(int.class).read(0);
}
private WrappedChatComponent readComponent(int index) {
Object handle = modifier.withType(MinecraftReflection.getIChatBaseComponentClass()).read(index);
return WrappedChatComponent.fromHandle(handle);
}
private void writeComponent(int index, WrappedChatComponent component) {
modifier.withType(MinecraftReflection.getIChatBaseComponentClass()).write(index, component.getHandle());
}
public static class Builder {
private WrappedChatComponent displayName, prefix, suffix;
private String nametagVisibility, collisionRule;
private EnumWrappers.ChatFormatting color;
private int options;
private Builder(@Nullable WrappedTeamParameters template) {
if (template != null) {
this.displayName = template.getDisplayName();
this.prefix = template.getDisplayName();
this.suffix = template.getDisplayName();
this.nametagVisibility = template.getNametagVisibility();
this.collisionRule = template.getCollisionRule();
this.color = template.getColor();
this.options = template.getOptions();
}
}
public Builder displayName(@NotNull WrappedChatComponent displayName) {
Preconditions.checkNotNull(displayName);
this.displayName = displayName;
return this;
}
public Builder prefix(@NotNull WrappedChatComponent prefix) {
Preconditions.checkNotNull(prefix);
this.prefix = prefix;
return this;
}
public Builder suffix(@NotNull WrappedChatComponent suffix) {
Preconditions.checkNotNull(suffix);
this.suffix = suffix;
return this;
}
public Builder nametagVisibility(@NotNull String nametagVisibility) {
Preconditions.checkNotNull(nametagVisibility);
this.nametagVisibility = nametagVisibility;
return this;
}
public Builder collisionRule(@NotNull String collisionRule) {
Preconditions.checkNotNull(collisionRule);
this.collisionRule = collisionRule;
return this;
}
public Builder color(@NotNull EnumWrappers.ChatFormatting color) {
Preconditions.checkNotNull(color);
this.color = color;
return this;
}
public Builder options(int options) {
Preconditions.checkNotNull(collisionRule);
this.options = options;
return this;
}
public WrappedTeamParameters build() {
Preconditions.checkNotNull(displayName, "Display name not set");
Preconditions.checkNotNull(prefix, "Prefix not set");
Preconditions.checkNotNull(suffix, "Suffix not set");
Preconditions.checkNotNull(nametagVisibility, "Nametag visibility not set");
Preconditions.checkNotNull(collisionRule, "Collision rule not set");
Preconditions.checkNotNull(color, "Color not set");
// Not technically a packet, but it has a PacketDataSerializer constructor, so it works fine
Object handle = StructureCache.newPacket(getNmsClassOrThrow());
WrappedTeamParameters wrapped = new WrappedTeamParameters(handle);
wrapped.writeComponent(0, displayName);
wrapped.writeComponent(1, prefix);
wrapped.writeComponent(2, suffix);
wrapped.modifier.withType(String.class).write(0, nametagVisibility);
wrapped.modifier.withType(String.class).write(1, collisionRule);
wrapped.modifier.withType(EnumWrappers.getChatFormattingClass()).write(0, EnumWrappers.getChatFormattingConverter().getGeneric(color));
wrapped.modifier.withType(int.class).write(0, options);
return wrapped;
}
}
}

View File

@ -0,0 +1,40 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import com.google.gson.JsonElement;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.ChatModifier;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class WrappedComponentStyleTest {
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
public void testComponentStyle() {
ChatModifier style = ChatModifier.a.b(EnumChatFormat.m).a(true);
WrappedComponentStyle wrapped = new WrappedComponentStyle(style);
JsonElement json = wrapped.getJson();
assertEquals("{\"color\":\"red\",\"bold\":true}", json.toString());
assertEquals(style, WrappedComponentStyle.fromJson(json).getHandle());
}
@Test
public void testStyleAdventureConversion() {
Style adventureStyle = Style.style(NamedTextColor.GREEN, TextDecoration.BOLD)
.clickEvent(ClickEvent.changePage(10));
WrappedComponentStyle wrapped = AdventureComponentConverter.fromStyle(adventureStyle);
assertEquals(adventureStyle, AdventureComponentConverter.fromWrapper(wrapped));
}
}

View File

@ -0,0 +1,50 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.ChatModifier;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.chat.numbers.BlankFormat;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.chat.numbers.StyledFormat;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
public class WrappedNumberFormatTest {
@BeforeAll
static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
void testBlankFormat() {
assertInstanceOf(WrappedNumberFormat.Blank.class, WrappedNumberFormat.fromHandle(BlankFormat.a));
assertEquals(BlankFormat.a, WrappedNumberFormat.blank().getHandle());
}
@Test
void testFixedFormat() {
IChatBaseComponent content = IChatBaseComponent.a("Fixed");
WrappedNumberFormat wrappedHandle = WrappedNumberFormat.fromHandle(new FixedFormat(content));
assertInstanceOf(WrappedNumberFormat.Fixed.class, wrappedHandle);
assertEquals(content, ((WrappedNumberFormat.Fixed) wrappedHandle).getContent().getHandle());
WrappedNumberFormat.Fixed wrapped = WrappedNumberFormat.fixed(WrappedChatComponent.fromHandle(content));
assertEquals(content, wrapped.getContent().getHandle());
}
@Test
void testStyledFormat() {
ChatModifier style = ChatModifier.a.b(EnumChatFormat.g);
WrappedNumberFormat wrappedHandle = WrappedNumberFormat.fromHandle(new StyledFormat(style));
assertInstanceOf(WrappedNumberFormat.Styled.class, wrappedHandle);
assertEquals(style, ((WrappedNumberFormat.Styled) wrappedHandle).getStyle().getHandle());
WrappedNumberFormat.Styled newWrapper = WrappedNumberFormat.styled(new WrappedComponentStyle(style));
assertEquals(style, newWrapper.getStyle().getHandle());
}
}

View File

@ -0,0 +1,53 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import net.minecraft.EnumChatFormat;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class WrappedTeamParametersTest {
@BeforeAll
static void initializeBukkit() {
BukkitInitialization.initializeAll();
}
@Test
void testTeamParameters() {
IChatBaseComponent displayName = IChatBaseComponent.b("display name");
IChatBaseComponent prefix = IChatBaseComponent.b("prefix");
IChatBaseComponent suffix = IChatBaseComponent.b("suffix");
String nametagVisibility = "always";
String collisionRule = "never";
WrappedTeamParameters wrapped = WrappedTeamParameters.newBuilder()
.displayName(WrappedChatComponent.fromHandle(displayName))
.prefix(WrappedChatComponent.fromHandle(prefix))
.suffix(WrappedChatComponent.fromHandle(suffix))
.nametagVisibility(nametagVisibility)
.collisionRule(collisionRule)
.color(EnumWrappers.ChatFormatting.RED)
.options(1)
.build();
assertEquals(displayName, wrapped.getDisplayName().getHandle());
assertEquals(prefix, wrapped.getPrefix().getHandle());
assertEquals(suffix, wrapped.getSuffix().getHandle());
assertEquals(nametagVisibility, wrapped.getNametagVisibility());
assertEquals(collisionRule, wrapped.getCollisionRule());
assertEquals(EnumWrappers.ChatFormatting.RED, wrapped.getColor());
assertEquals(1, wrapped.getOptions());
PacketPlayOutScoreboardTeam.b handle = (PacketPlayOutScoreboardTeam.b) wrapped.getHandle();
assertEquals(handle.a(), displayName);
assertEquals(handle.f(), prefix);
assertEquals(handle.g(), suffix);
assertEquals(handle.d(), nametagVisibility);
assertEquals(handle.e(), collisionRule);
assertEquals(handle.c(), EnumChatFormat.m);
assertEquals(handle.b(), 1);
}
}