Current work on migrating chat over to a registry

This commit is contained in:
Shane Freeder
2022-06-08 16:12:51 +01:00
parent 1a1391a519
commit d9c5886786
5 changed files with 243 additions and 78 deletions

View File

@@ -318,7 +318,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
Component translated = translateMessage(message); Component translated = translateMessage(message);
connection.write(ChatBuilder.builder(this.getProtocolVersion()) connection.write(ChatBuilder.builder(this.getProtocolVersion())
.component(translated).forIdentity(identity).toClient()); .component(translated).forIdentity(identity).toClient(this));
} }
@Override @Override
@@ -332,7 +332,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
connection.write(ChatBuilder.builder(this.getProtocolVersion()) connection.write(ChatBuilder.builder(this.getProtocolVersion())
.component(translated).forIdentity(identity) .component(translated).forIdentity(identity)
.setType(type == MessageType.CHAT ? ChatBuilder.ChatType.CHAT : ChatBuilder.ChatType.SYSTEM) .setType(type == MessageType.CHAT ? ChatBuilder.ChatType.CHAT : ChatBuilder.ChatType.SYSTEM)
.toClient()); .toClient(this));
} }
@Override @Override

View File

@@ -30,80 +30,148 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; 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 net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
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;
// TODO Implement // TODO Implement
public class ChatData { public class ChatData {
/*
private static final ListBinaryTag EMPTY_LIST_TAG = ListBinaryTag.empty();
private static final ListBinaryTag EMPTY_LIST_TAG = ListBinaryTag.empty();
private final String identifier;
private final int id;
private final String identifier; public ChatData(int id, String identifier) {
private final int id; this.id = id;
private final Map<> this.identifier = identifier;
}
/**
* Decodes an entry in the registry.
*
* @param binaryTag the binary tag to decode.
* @param version the version to decode for.
* @return The decoded ChatData
*/
public static ChatData decodeRegistryEntry(CompoundBinaryTag binaryTag, ProtocolVersion version) {
final String registryIdentifier = binaryTag.getString("name");
final Integer id = binaryTag.getInt("id");
public static class Decoration implements Translatable { CompoundBinaryTag element = binaryTag.getCompound("element");
ChatData decodedChatData = decodeElementCompound(element);
return decodedChatData.annotateWith(id, registryIdentifier);
}
private final List<String> parameters; private ChatData annotateWith(Integer id, String registryIdentifier) {
private final List<TextFormat> style; return new ChatData(id, registryIdentifier);
private final String translationKey; }
public List<String> getParameters() { private static ChatData decodeElementCompound(CompoundBinaryTag element) {
return parameters; System.out.println(element);
} final CompoundBinaryTag chatCompund = element.getCompound("chat");
public List<TextFormat> getStyle() { Decoration chatDecoration = null;
return style;
}
@Override
public @NotNull String translationKey() {
return translationKey;
}
public Decoration(List<String> parameters, List<TextFormat> style, String translationKey) {
this.parameters = Preconditions.checkNotNull(parameters);
this.style = Preconditions.checkNotNull(style);
this.translationKey = Preconditions.checkNotNull(translationKey);
Preconditions.checkArgument(translationKey.length() > 0);
}
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(binaryTag.toString()));
}
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);
}
public void encodeRegistryEntry(CompoundBinaryTag )
final CompoundBinaryTag chatDecorationCompound = chatCompund.getCompound("decoration");
if (chatDecorationCompound != CompoundBinaryTag.empty()) {
chatDecoration = Decoration.decodeRegistryEntry(chatDecorationCompound);
} }
public static enum Priority { return new ChatData(-1, "invalid");
SYSTEM, }
CHAT
public String getIdentifier() {
return identifier;
}
public int getId() {
return id;
}
public static class Decoration implements Translatable {
private final List<String> parameters;
private final List<TextFormat> style;
private final String translationKey;
public List<String> getParameters() {
return parameters;
} }
*/
public List<TextFormat> getStyle() {
return style;
}
@Override
public @NotNull 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, String translationKey) {
this.parameters = Preconditions.checkNotNull(parameters);
this.style = Preconditions.checkNotNull(style);
this.translationKey = Preconditions.checkNotNull(translationKey);
Preconditions.checkArgument(translationKey.length() > 0);
}
/**
* 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(binaryTag.toString()));
}
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);
}
public void encodeRegistryEntry(CompoundBinaryTag compoundBinaryTag) {}
}
public static enum Priority {
SYSTEM,
CHAT
}
} }

View File

@@ -0,0 +1,58 @@
/*
* 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;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.velocitypowered.api.network.ProtocolVersion;
import java.util.Map;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
public final class ChatRegistry {
private final Map<String, ChatData> registeredChatTypes;
public ChatRegistry(ImmutableList<ChatData> chatDataImmutableList) {
registeredChatTypes = Maps.uniqueIndex(chatDataImmutableList, ChatData::getIdentifier);
}
/**
* Decodes a CompoundTag storing a Chat Type Registry.
*
* @param compound The Compound to decode
* @param version Protocol version
* @return an ImmutableList of read ChatData
*/
public static ImmutableList<ChatData> fromGameData(ListBinaryTag compound, ProtocolVersion version) {
final ImmutableList.Builder<ChatData> builder = ImmutableList.builder();
for (BinaryTag binaryTag : compound) {
if (binaryTag instanceof CompoundBinaryTag) {
builder.add(ChatData.decodeRegistryEntry((CompoundBinaryTag) binaryTag, version));
}
}
return builder.build();
}
public BinaryTag build() {
return null;
}
}

View File

@@ -17,9 +17,12 @@
package com.velocitypowered.proxy.protocol.packet; package com.velocitypowered.proxy.protocol.packet;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.registry.ChatData;
import com.velocitypowered.proxy.connection.registry.ChatRegistry;
import com.velocitypowered.proxy.connection.registry.DimensionData; import com.velocitypowered.proxy.connection.registry.DimensionData;
import com.velocitypowered.proxy.connection.registry.DimensionInfo; import com.velocitypowered.proxy.connection.registry.DimensionInfo;
import com.velocitypowered.proxy.connection.registry.DimensionRegistry; import com.velocitypowered.proxy.connection.registry.DimensionRegistry;
@@ -32,6 +35,8 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag; import net.kyori.adventure.nbt.ListBinaryTag;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
public class JoinGame implements MinecraftPacket { public class JoinGame implements MinecraftPacket {
private static final BinaryTagIO.Reader JOINGAME_READER = BinaryTagIO.reader(4 * 1024 * 1024); private static final BinaryTagIO.Reader JOINGAME_READER = BinaryTagIO.reader(4 * 1024 * 1024);
@@ -53,7 +58,7 @@ public class JoinGame implements MinecraftPacket {
private CompoundBinaryTag biomeRegistry; // 1.16.2+ private CompoundBinaryTag biomeRegistry; // 1.16.2+
private int simulationDistance; // 1.18+ private int simulationDistance; // 1.18+
private @Nullable Pair<String, Long> lastDeathPosition; private @Nullable Pair<String, Long> lastDeathPosition;
private CompoundBinaryTag chatTypeRegistry; // placeholder, 1.19+ private ChatRegistry chatTypeRegistry; // placeholder, 1.19+
public int getEntityId() { public int getEntityId() {
return entityId; return entityId;
@@ -183,11 +188,11 @@ public class JoinGame implements MinecraftPacket {
this.lastDeathPosition = lastDeathPosition; this.lastDeathPosition = lastDeathPosition;
} }
public CompoundBinaryTag getChatTypeRegistry() { public ChatRegistry getChatTypeRegistry() {
return chatTypeRegistry; return chatTypeRegistry;
} }
public void setChatTypeRegistry(CompoundBinaryTag chatTypeRegistry) { public void setChatTypeRegistry(ChatRegistry chatTypeRegistry) {
this.chatTypeRegistry = chatTypeRegistry; this.chatTypeRegistry = chatTypeRegistry;
} }
@@ -274,9 +279,10 @@ public class JoinGame implements MinecraftPacket {
.getList("value", BinaryTagTypes.COMPOUND); .getList("value", BinaryTagTypes.COMPOUND);
this.biomeRegistry = registryContainer.getCompound("minecraft:worldgen/biome"); this.biomeRegistry = registryContainer.getCompound("minecraft:worldgen/biome");
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
this.chatTypeRegistry = registryContainer.getCompound("minecraft:chat_type"); final ImmutableList<ChatData> chatDataList = ChatRegistry.fromGameData(registryContainer.getCompound("minecraft:chat_type").getList("value"), version);
this.chatTypeRegistry = new ChatRegistry(chatDataList);
} else { } else {
this.chatTypeRegistry = CompoundBinaryTag.empty(); this.chatTypeRegistry = null; // TODO: Faux registry?
} }
} else { } else {
dimensionRegistryContainer = registryContainer.getList("dimension", dimensionRegistryContainer = registryContainer.getList("dimension",
@@ -387,7 +393,7 @@ public class JoinGame implements MinecraftPacket {
registryContainer.put("minecraft:dimension_type", dimensionRegistryEntry.build()); registryContainer.put("minecraft:dimension_type", dimensionRegistryEntry.build());
registryContainer.put("minecraft:worldgen/biome", biomeRegistry); registryContainer.put("minecraft:worldgen/biome", biomeRegistry);
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
registryContainer.put("minecraft:chat_type", chatTypeRegistry); registryContainer.put("minecraft:chat_type", chatTypeRegistry.build());
} }
} else { } else {
registryContainer.put("dimension", encodedDimensionRegistry); registryContainer.put("dimension", encodedDimensionRegistry);

View File

@@ -21,15 +21,22 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.crypto.SignedChatCommand; import com.velocitypowered.proxy.crypto.SignedChatCommand;
import com.velocitypowered.proxy.crypto.SignedChatMessage; import com.velocitypowered.proxy.crypto.SignedChatMessage;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import java.time.Instant; import java.time.Instant;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import net.kyori.adventure.identity.Identity; import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class ChatBuilder { public class ChatBuilder {
@@ -125,10 +132,10 @@ public class ChatBuilder {
* *
* @return The {@link MinecraftPacket} to send to the client. * @return The {@link MinecraftPacket} to send to the client.
*/ */
public MinecraftPacket toClient() { public MinecraftPacket toClient(ConnectedPlayer player) {
// This is temporary // This is temporary
UUID identity = sender == null ? (senderIdentity == null ? Identity.nil().uuid() UUID identity = sender == null ? (senderIdentity == null ? Identity.nil().uuid()
: senderIdentity.uuid()) : sender.getUniqueId(); : senderIdentity.uuid()) : sender.getUniqueId();
Component msg = component == null ? Component.text(message) : component; Component msg = component == null ? Component.text(message) : component;
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
@@ -164,19 +171,45 @@ public class ChatBuilder {
return chat; return chat;
} }
public static enum ChatType { public static class ChatType {
CHAT((byte) 0), public static final ChatType CHAT = new ChatType((byte) 0, Key.key("minecraft", "chat"));
SYSTEM((byte) 1), public static final ChatType SYSTEM = new ChatType((byte) 1, Key.key("minecraft", "system"));
GAME_INFO((byte) 2); public static final ChatType GAME_INFO = new ChatType((byte) 2, Key.key("minecraft", "game_info"));
private final byte raw; private final byte raw;
@NonNull
private final Key key;
ChatType(byte raw) { ChatType(byte raw, @NonNull Key key) {
Preconditions.checkNotNull(key, "Key cannot be null!");
this.raw = raw; this.raw = raw;
this.key = key;
} }
public byte getId() { public byte getId() {
return raw; return raw;
} }
@NonNull
public Key getKey() {
return key;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ChatType chatType = (ChatType) o;
return raw == chatType.raw && key.equals(chatType.key);
}
@Override
public int hashCode() {
return Objects.hash(raw, key);
}
} }
} }