From fe54721ff3fe641027d236272a505388d92690ea Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Wed, 15 Jun 2022 18:02:15 +0100 Subject: [PATCH] Improve chat registry de/serialisation now actually passes through the registry 1:1, magically making chat work properly on the server, woo! --- .../proxy/connection/registry/ChatData.java | 214 ++++-------------- .../connection/registry/ChatRegistry.java | 21 -- .../registry/chat/ChatDecoration.java | 140 ++++++++++++ .../registry/chat/ChatTypeElement.java | 75 ++++++ 4 files changed, 257 insertions(+), 193 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/connection/registry/chat/ChatDecoration.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/connection/registry/chat/ChatTypeElement.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/ChatData.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/ChatData.java index 17a48db61..593350d8b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/ChatData.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/ChatData.java @@ -17,64 +17,37 @@ package com.velocitypowered.proxy.connection.registry; -/* -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import net.kyori.adventure.nbt.*; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.format.TextFormat; -import net.kyori.adventure.translation.Translatable; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -*/ - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; import com.velocitypowered.api.network.ProtocolVersion; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import net.kyori.adventure.nbt.BinaryTagTypes; +import com.velocitypowered.proxy.connection.registry.chat.ChatTypeElement; +import net.kyori.adventure.nbt.BinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag; -import net.kyori.adventure.nbt.ListBinaryTag; -import net.kyori.adventure.nbt.StringBinaryTag; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextColor; -import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.format.TextFormat; import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.NotNull; // TODO Implement public class ChatData { - private static final ListBinaryTag EMPTY_LIST_TAG = ListBinaryTag.empty(); private final String identifier; private final int id; - @Nullable - private final Decoration chatDecoration; - @Nullable - private final Priority narrationPriority; - // TODO: move to own thing? - @Nullable - private final Decoration narrationDecoration; + private final ChatTypeElement chatElement; + private final ChatTypeElement overlayElement; + private final ChatTypeElement narrationElement; /** * Represents a ChatRegistry entry. * - * @param id chat type id - * @param identifier chat type identifier - * @param chatDecoration chat decoration - * @param narrationDecoration narration decoration + * @param id chat type id + * @param identifier chat type identifier + * @param chatElement chat element + * @param overlayElement overlay element + * @param narrationElement narration element */ - public ChatData(int id, String identifier, @Nullable Decoration chatDecoration, @Nullable Priority narrationPriority, @Nullable Decoration narrationDecoration) { + public ChatData(int id, String identifier, @Nullable ChatTypeElement chatElement, ChatTypeElement overlayElement, + @Nullable ChatTypeElement narrationElement) { this.id = id; this.identifier = identifier; - this.chatDecoration = chatDecoration; - this.narrationPriority = narrationPriority; - this.narrationDecoration = narrationDecoration; + this.chatElement = chatElement; + this.overlayElement = overlayElement; + this.narrationElement = narrationElement; } /** @@ -94,32 +67,36 @@ public class ChatData { } private ChatData annotateWith(Integer id, String registryIdentifier) { - return new ChatData(id, registryIdentifier, this.chatDecoration, this.narrationPriority, this.narrationDecoration); + return new ChatData(id, registryIdentifier, this.chatElement, this.overlayElement, + this.narrationElement); } private static ChatData decodeElementCompound(CompoundBinaryTag element, ProtocolVersion version) { - Decoration chatDecoration = null; - Decoration narrationDecoration = null; - Priority narrationPriority = null; + ChatTypeElement chatElement = null; + ChatTypeElement overlayElement = null; + ChatTypeElement narrationElement = null; - final CompoundBinaryTag chatCompound = element.getCompound("chat"); - final CompoundBinaryTag chatDecorationCompound = (CompoundBinaryTag) chatCompound.get("decoration"); - if (chatDecorationCompound != null) { - chatDecoration = Decoration.decodeRegistryEntry(chatDecorationCompound); + final BinaryTag chatCompound = element.get("chat"); + if (chatCompound != null) { + chatElement = + ChatTypeElement.decodeFromRegistry(ChatTypeElement.ElementType.CHAT, (CompoundBinaryTag) chatCompound, + version); } - final CompoundBinaryTag narrationCompound = element.getCompound("narration"); - final String priorityString = narrationCompound.getString("priority"); - if (!priorityString.isEmpty()) { - narrationPriority = Priority.valueOf(priorityString.toUpperCase(Locale.ROOT)); + final BinaryTag overlayCompound = element.get("overlay"); + if (overlayCompound != null) { + overlayElement = + ChatTypeElement.decodeFromRegistry(ChatTypeElement.ElementType.OVERLAY, (CompoundBinaryTag) overlayCompound, + version); } - final CompoundBinaryTag narrationDecorationCompound = (CompoundBinaryTag) narrationCompound.get("decoration"); - if (narrationDecorationCompound != null) { - narrationDecoration = Decoration.decodeRegistryEntry(narrationDecorationCompound); + final BinaryTag narrationCompound = element.get("narration"); + if (narrationCompound != null) { + narrationElement = ChatTypeElement.decodeFromRegistry(ChatTypeElement.ElementType.NARRATION, + (CompoundBinaryTag) narrationCompound, version); } - return new ChatData(-1, "invalid", chatDecoration, narrationPriority, narrationDecoration); + return new ChatData(-1, "invalid", chatElement, overlayElement, narrationElement); } public String getIdentifier() { @@ -143,22 +120,16 @@ public class ChatData { final CompoundBinaryTag.Builder elementCompound = CompoundBinaryTag.builder(); - CompoundBinaryTag.@NotNull Builder chatCompound = CompoundBinaryTag.builder(); - if (chatDecoration != null) { - chatCompound.put("decoration", chatDecoration.encodeRegistryEntry(version)); - elementCompound.put("chat", chatCompound.build()); + if (chatElement != null) { + elementCompound.put("chat", chatElement.encodeForRegistry(version)); } - final CompoundBinaryTag.Builder narrationCompoundBuilder = CompoundBinaryTag.builder(); - if (narrationPriority != null) { - narrationCompoundBuilder.putString("priority", narrationPriority.name().toLowerCase(Locale.ROOT)); + if (overlayElement != null) { + elementCompound.put("overlay", overlayElement.encodeForRegistry(version)); } - if (narrationDecoration != null) { - narrationCompoundBuilder.put("decoration", narrationDecoration.encodeRegistryEntry(version)); - } - final CompoundBinaryTag narrationCompound = narrationCompoundBuilder.build(); - if (!narrationCompound.equals(CompoundBinaryTag.empty())) { - elementCompound.put("narration", narrationCompound); + + if (narrationElement != null) { + elementCompound.put("narration", narrationElement.encodeForRegistry(version)); } compound.put("element", elementCompound.build()); @@ -167,107 +138,6 @@ public class ChatData { } - public static class Decoration { - - private final List parameters; - private final List style; - @Nullable - private final String translationKey; - - public List getParameters() { - return parameters; - } - - public List getStyle() { - return style; - } - - public @Nullable String translationKey() { - return translationKey; - } - - /** - * Creates a Decoration with the associated data. - * - * @param parameters chat params - * @param style chat style - * @param translationKey translation key - */ - public Decoration(List parameters, List style, @Nullable String translationKey) { - this.parameters = Preconditions.checkNotNull(parameters); - this.style = Preconditions.checkNotNull(style); - this.translationKey = translationKey; - } - - /** - * Decodes a decoration entry. - * - * @param toDecode Compound Tag to decode - * @return the parsed Decoration entry. - */ - public static Decoration decodeRegistryEntry(CompoundBinaryTag toDecode) { - ImmutableList.Builder parameters = ImmutableList.builder(); - ListBinaryTag paramList = toDecode.getList("parameters", EMPTY_LIST_TAG); - if (paramList != EMPTY_LIST_TAG) { - paramList.forEach(binaryTag -> parameters.add(((StringBinaryTag) binaryTag).value())); - } - - ImmutableList.Builder style = ImmutableList.builder(); - CompoundBinaryTag styleList = toDecode.getCompound("style"); - for (String key : styleList.keySet()) { - if ("color".equals(key)) { - NamedTextColor color = Preconditions.checkNotNull( - NamedTextColor.NAMES.value(styleList.getString(key))); - style.add(color); - } else { - // Key is a Style option instead - TextDecoration deco = TextDecoration.NAMES.value(key); - // This wouldn't be here if it wasn't applied, but here it goes anyway: - byte val = styleList.getByte(key); - if (val != 0) { - style.add(deco); - } - } - } - - String translationKey = toDecode.getString("translation_key"); - - return new Decoration(parameters.build(), style.build(), translationKey.isEmpty() ? null : translationKey); - } - - public CompoundBinaryTag encodeRegistryEntry(ProtocolVersion version) { - - CompoundBinaryTag.Builder compoundBinaryTag = CompoundBinaryTag.builder(); - - if (translationKey != null) { - compoundBinaryTag.put("translation_key", StringBinaryTag.of(translationKey)); - } - - final CompoundBinaryTag.Builder styleBuilder = CompoundBinaryTag.builder(); - style.forEach(styleEntry -> { - if (styleEntry instanceof TextColor color) { - styleBuilder.putString("color", color.toString()); - } else if (styleEntry instanceof TextDecoration decoration) { - styleBuilder.putByte(decoration.name().toLowerCase(Locale.ROOT), (byte) 1); // This won't be here if not applied - } - }); - compoundBinaryTag.put("style", styleBuilder.build()); - - if (parameters.size() == 0) { - compoundBinaryTag.put("parameters", EMPTY_LIST_TAG); - } else { - final ListBinaryTag.Builder parametersBuilder = ListBinaryTag.builder(BinaryTagTypes.STRING); - parameters.forEach(param -> parametersBuilder.add(StringBinaryTag.of(param))); - compoundBinaryTag.put("parameters", parametersBuilder.build()); - } - - - - return compoundBinaryTag.build(); - } - - } - public static enum Priority { SYSTEM, CHAT diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/ChatRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/ChatRegistry.java index 0fa18d0f2..77f81b2a0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/ChatRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/ChatRegistry.java @@ -46,31 +46,10 @@ public final class ChatRegistry { */ public static ImmutableList fromGameData(ListBinaryTag compound, ProtocolVersion version) { final ImmutableList.Builder builder = ImmutableList.builder(); - try { - System.out.println(TagStringIO.builder().indent(2).build().asString(CompoundBinaryTag.empty().put("tag", compound))); - } catch (IOException e) { - throw new RuntimeException(e); - } for (BinaryTag binaryTag : compound) { if (binaryTag instanceof CompoundBinaryTag) { final ChatData chatData = ChatData.decodeRegistryEntry((CompoundBinaryTag) binaryTag, version); builder.add(chatData); - System.out.println(chatData.encodeAsCompoundTag(version).equals(binaryTag)); - System.out.println("========"); - System.out.println("========"); - try { - System.out.println(TagStringIO.builder().indent(2).build().asString((CompoundBinaryTag) binaryTag)); - } catch (IOException e) { - throw new RuntimeException(e); - } - System.out.println("========"); - try { - System.out.println(TagStringIO.builder().indent(2).build().asString(chatData.encodeAsCompoundTag(version))); - } catch (IOException e) { - throw new RuntimeException(e); - } - System.out.println("========"); - System.out.println("========"); } } return builder.build(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/chat/ChatDecoration.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/chat/ChatDecoration.java new file mode 100644 index 000000000..ef0d51bc9 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/chat/ChatDecoration.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2018 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.connection.registry.chat; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.velocitypowered.api.network.ProtocolVersion; +import java.util.List; +import java.util.Locale; +import net.kyori.adventure.nbt.BinaryTagTypes; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.ListBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.format.TextFormat; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class ChatDecoration { + + private final List parameters; + private final List style; + @Nullable + private final String translationKey; + + public List getParameters() { + return parameters; + } + + public List getStyle() { + return style; + } + + public @Nullable String translationKey() { + return translationKey; + } + + /** + * Creates a Decoration with the associated data. + * + * @param parameters chat params + * @param style chat style + * @param translationKey translation key + */ + public ChatDecoration(List parameters, List style, @Nullable String translationKey) { + this.parameters = Preconditions.checkNotNull(parameters); + this.style = Preconditions.checkNotNull(style); + this.translationKey = translationKey; + } + + /** + * Decodes a decoration entry. + * + * @param toDecode Compound Tag to decode + * @param version + * @return the parsed Decoration entry. + */ + public static ChatDecoration decodeRegistryEntry(CompoundBinaryTag toDecode, ProtocolVersion version) { + ImmutableList.Builder parameters = ImmutableList.builder(); + ListBinaryTag paramList = toDecode.getList("parameters", ListBinaryTag.empty()); + if (paramList != ListBinaryTag.empty()) { + paramList.forEach(binaryTag -> parameters.add(((StringBinaryTag) binaryTag).value())); + } + + ImmutableList.Builder style = ImmutableList.builder(); + CompoundBinaryTag styleList = toDecode.getCompound("style"); + for (String key : styleList.keySet()) { + if ("color".equals(key)) { + final NamedTextColor value = NamedTextColor.NAMES.value(styleList.getString(key)); + if (value != null) { + style.add(value); + } else { + throw new IllegalArgumentException("Unable to map color value: " + styleList.getString(key)); + } + } else { + // Key is a Style option instead + TextDecoration deco = TextDecoration.NAMES.value(key); + if (deco == null) { + throw new IllegalArgumentException("Unable to map text style of: " + key); + } + // This wouldn't be here if it wasn't applied, but here it goes anyway: + byte val = styleList.getByte(key); + if (val != 0) { + style.add(deco); + } + } + } + + String translationKey = toDecode.getString("translation_key"); + + return new ChatDecoration(parameters.build(), style.build(), translationKey.isEmpty() ? null : translationKey); + } + + public CompoundBinaryTag encodeRegistryEntry(ProtocolVersion version) { + + CompoundBinaryTag.Builder compoundBinaryTag = CompoundBinaryTag.builder(); + + if (translationKey != null) { + compoundBinaryTag.put("translation_key", StringBinaryTag.of(translationKey)); + } + + final CompoundBinaryTag.Builder styleBuilder = CompoundBinaryTag.builder(); + style.forEach(styleEntry -> { + if (styleEntry instanceof TextColor color) { + styleBuilder.putString("color", color.toString()); + } else if (styleEntry instanceof TextDecoration decoration) { + styleBuilder.putByte(decoration.name().toLowerCase(Locale.ROOT), (byte) 1); // This won't be here if not applied + } + }); + compoundBinaryTag.put("style", styleBuilder.build()); + + if (parameters.size() == 0) { + compoundBinaryTag.put("parameters", ListBinaryTag.empty()); + } else { + final ListBinaryTag.Builder parametersBuilder = ListBinaryTag.builder(BinaryTagTypes.STRING); + parameters.forEach(param -> parametersBuilder.add(StringBinaryTag.of(param))); + compoundBinaryTag.put("parameters", parametersBuilder.build()); + } + + + return compoundBinaryTag.build(); + } + +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/chat/ChatTypeElement.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/chat/ChatTypeElement.java new file mode 100644 index 000000000..48c75db77 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/registry/chat/ChatTypeElement.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2018 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.connection.registry.chat; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.registry.ChatData; +import java.util.Locale; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.nbt.StringBinaryTag; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class ChatTypeElement { + + + @Nullable + private final ElementType type; + @Nullable + private final ChatDecoration decoration; + private final ChatData.@Nullable Priority priority; + + public ChatTypeElement(ElementType type, ChatDecoration decoration, ChatData.Priority priority) { + this.type = type; + this.decoration = decoration; + this.priority = priority; + } + + public static ChatTypeElement decodeFromRegistry(ElementType type, CompoundBinaryTag elementCompound, ProtocolVersion version) { + ChatDecoration decoration = null; + ChatData.Priority priority = null; + final CompoundBinaryTag decorationCompound = (CompoundBinaryTag) elementCompound.get("decoration"); + if (decorationCompound != null) { + decoration = ChatDecoration.decodeRegistryEntry(decorationCompound, version); + } + + if (elementCompound.get("priority") != null) { + priority = ChatData.Priority.valueOf(elementCompound.getString("priority").toUpperCase(Locale.ROOT)); + } + return new ChatTypeElement(type, decoration, priority); + } + + public CompoundBinaryTag encodeForRegistry(ProtocolVersion version) { + final CompoundBinaryTag.Builder compoundBuilder = CompoundBinaryTag.builder(); + if (priority != null) { + compoundBuilder.put("priority", StringBinaryTag.of(priority.name().toLowerCase(Locale.ROOT))); + } + + if (decoration != null) { + compoundBuilder.put("decoration", decoration.encodeRegistryEntry(version)); + } + + return compoundBuilder.build(); + } + + public enum ElementType { + CHAT, + OVERLAY, + NARRATION + } +}