3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-16 21:10:30 +01:00

Improve chat registry de/serialisation

now actually passes through the registry 1:1, magically making chat work
properly on the server, woo!
Dieser Commit ist enthalten in:
Shane Freeder 2022-06-15 18:02:15 +01:00
Ursprung 5448f54380
Commit fe54721ff3
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: A3F61EA5A085289C
4 geänderte Dateien mit 257 neuen und 193 gelöschten Zeilen

Datei anzeigen

@ -17,64 +17,37 @@
package com.velocitypowered.proxy.connection.registry; 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 com.velocitypowered.api.network.ProtocolVersion;
import java.util.List; import com.velocitypowered.proxy.connection.registry.chat.ChatTypeElement;
import java.util.Locale; import net.kyori.adventure.nbt.BinaryTag;
import java.util.Map;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag; 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.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
// TODO Implement // TODO Implement
public class ChatData { public class ChatData {
private static final ListBinaryTag EMPTY_LIST_TAG = ListBinaryTag.empty();
private final String identifier; private final String identifier;
private final int id; private final int id;
@Nullable private final ChatTypeElement chatElement;
private final Decoration chatDecoration; private final ChatTypeElement overlayElement;
@Nullable private final ChatTypeElement narrationElement;
private final Priority narrationPriority;
// TODO: move to own thing?
@Nullable
private final Decoration narrationDecoration;
/** /**
* Represents a ChatRegistry entry. * Represents a ChatRegistry entry.
* *
* @param id chat type id * @param id chat type id
* @param identifier chat type identifier * @param identifier chat type identifier
* @param chatDecoration chat decoration * @param chatElement chat element
* @param narrationDecoration narration decoration * @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.id = id;
this.identifier = identifier; this.identifier = identifier;
this.chatDecoration = chatDecoration; this.chatElement = chatElement;
this.narrationPriority = narrationPriority; this.overlayElement = overlayElement;
this.narrationDecoration = narrationDecoration; this.narrationElement = narrationElement;
} }
/** /**
@ -94,32 +67,36 @@ public class ChatData {
} }
private ChatData annotateWith(Integer id, String registryIdentifier) { 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) { private static ChatData decodeElementCompound(CompoundBinaryTag element, ProtocolVersion version) {
Decoration chatDecoration = null; ChatTypeElement chatElement = null;
Decoration narrationDecoration = null; ChatTypeElement overlayElement = null;
Priority narrationPriority = null; ChatTypeElement narrationElement = null;
final CompoundBinaryTag chatCompound = element.getCompound("chat"); final BinaryTag chatCompound = element.get("chat");
final CompoundBinaryTag chatDecorationCompound = (CompoundBinaryTag) chatCompound.get("decoration"); if (chatCompound != null) {
if (chatDecorationCompound != null) { chatElement =
chatDecoration = Decoration.decodeRegistryEntry(chatDecorationCompound); ChatTypeElement.decodeFromRegistry(ChatTypeElement.ElementType.CHAT, (CompoundBinaryTag) chatCompound,
version);
} }
final CompoundBinaryTag narrationCompound = element.getCompound("narration"); final BinaryTag overlayCompound = element.get("overlay");
final String priorityString = narrationCompound.getString("priority"); if (overlayCompound != null) {
if (!priorityString.isEmpty()) { overlayElement =
narrationPriority = Priority.valueOf(priorityString.toUpperCase(Locale.ROOT)); ChatTypeElement.decodeFromRegistry(ChatTypeElement.ElementType.OVERLAY, (CompoundBinaryTag) overlayCompound,
version);
} }
final CompoundBinaryTag narrationDecorationCompound = (CompoundBinaryTag) narrationCompound.get("decoration"); final BinaryTag narrationCompound = element.get("narration");
if (narrationDecorationCompound != null) { if (narrationCompound != null) {
narrationDecoration = Decoration.decodeRegistryEntry(narrationDecorationCompound); 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() { public String getIdentifier() {
@ -143,22 +120,16 @@ public class ChatData {
final CompoundBinaryTag.Builder elementCompound = CompoundBinaryTag.builder(); final CompoundBinaryTag.Builder elementCompound = CompoundBinaryTag.builder();
CompoundBinaryTag.@NotNull Builder chatCompound = CompoundBinaryTag.builder(); if (chatElement != null) {
if (chatDecoration != null) { elementCompound.put("chat", chatElement.encodeForRegistry(version));
chatCompound.put("decoration", chatDecoration.encodeRegistryEntry(version));
elementCompound.put("chat", chatCompound.build());
} }
final CompoundBinaryTag.Builder narrationCompoundBuilder = CompoundBinaryTag.builder(); if (overlayElement != null) {
if (narrationPriority != null) { elementCompound.put("overlay", overlayElement.encodeForRegistry(version));
narrationCompoundBuilder.putString("priority", narrationPriority.name().toLowerCase(Locale.ROOT));
} }
if (narrationDecoration != null) {
narrationCompoundBuilder.put("decoration", narrationDecoration.encodeRegistryEntry(version)); if (narrationElement != null) {
} elementCompound.put("narration", narrationElement.encodeForRegistry(version));
final CompoundBinaryTag narrationCompound = narrationCompoundBuilder.build();
if (!narrationCompound.equals(CompoundBinaryTag.empty())) {
elementCompound.put("narration", narrationCompound);
} }
compound.put("element", elementCompound.build()); compound.put("element", elementCompound.build());
@ -167,107 +138,6 @@ public class ChatData {
} }
public static class Decoration {
private final List<String> parameters;
private final List<TextFormat> style;
@Nullable
private final String translationKey;
public List<String> getParameters() {
return parameters;
}
public List<TextFormat> 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<String> parameters, List<TextFormat> 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<String> 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<TextFormat> 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<StringBinaryTag> 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 { public static enum Priority {
SYSTEM, SYSTEM,
CHAT CHAT

Datei anzeigen

@ -46,31 +46,10 @@ public final class ChatRegistry {
*/ */
public static ImmutableList<ChatData> fromGameData(ListBinaryTag compound, ProtocolVersion version) { public static ImmutableList<ChatData> fromGameData(ListBinaryTag compound, ProtocolVersion version) {
final ImmutableList.Builder<ChatData> builder = ImmutableList.builder(); final ImmutableList.Builder<ChatData> 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) { for (BinaryTag binaryTag : compound) {
if (binaryTag instanceof CompoundBinaryTag) { if (binaryTag instanceof CompoundBinaryTag) {
final ChatData chatData = ChatData.decodeRegistryEntry((CompoundBinaryTag) binaryTag, version); final ChatData chatData = ChatData.decodeRegistryEntry((CompoundBinaryTag) binaryTag, version);
builder.add(chatData); 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(); return builder.build();

Datei anzeigen

@ -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 <https://www.gnu.org/licenses/>.
*/
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<String> parameters;
private final List<TextFormat> style;
@Nullable
private final String translationKey;
public List<String> getParameters() {
return parameters;
}
public List<TextFormat> 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<String> parameters, List<TextFormat> 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<String> parameters = ImmutableList.builder();
ListBinaryTag paramList = toDecode.getList("parameters", ListBinaryTag.empty());
if (paramList != ListBinaryTag.empty()) {
paramList.forEach(binaryTag -> parameters.add(((StringBinaryTag) binaryTag).value()));
}
ImmutableList.Builder<TextFormat> 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<StringBinaryTag> parametersBuilder = ListBinaryTag.builder(BinaryTagTypes.STRING);
parameters.forEach(param -> parametersBuilder.add(StringBinaryTag.of(param)));
compoundBinaryTag.put("parameters", parametersBuilder.build());
}
return compoundBinaryTag.build();
}
}

Datei anzeigen

@ -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 <https://www.gnu.org/licenses/>.
*/
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
}
}