Add WrappedTeamParameters

This commit is contained in:
vytskalt 2024-02-28 20:30:45 +02:00
parent 4c878facbe
commit 0266b25548
5 changed files with 385 additions and 0 deletions

View File

@ -877,6 +877,16 @@ public abstract class AbstractStructure {
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 the MinecraftKey class.

View File

@ -1009,6 +1009,15 @@ 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 Gson class used by Minecraft.
*

View File

@ -10,6 +10,7 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
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;
@ -490,6 +491,133 @@ public abstract class EnumWrappers {
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;
@ -513,6 +641,7 @@ public abstract class EnumWrappers {
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<>();
@ -598,6 +727,7 @@ public abstract class EnumWrappers {
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());
@ -619,6 +749,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());
@ -780,6 +913,11 @@ public abstract class EnumWrappers {
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);
@ -869,6 +1007,10 @@ public abstract class EnumWrappers {
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,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 {
private static Class<?> getNmsClass() {
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(getNmsClass());
setHandle(handle);
this.modifier = new StructureModifier<>(getNmsClass()).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(getNmsClass());
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,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);
}
}