diff --git a/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java b/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java index d5a47d1a2..2ecf408a1 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java +++ b/api/src/main/java/com/viaversion/viaversion/api/configuration/ViaVersionConfig.java @@ -28,6 +28,7 @@ import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.minecraft.WorldIdentifiers; import com.viaversion.viaversion.api.protocol.version.BlockedProtocolVersions; import it.unimi.dsi.fastutil.ints.IntSet; +import org.checkerframework.checker.nullness.qual.Nullable; public interface ViaVersionConfig { @@ -455,4 +456,6 @@ public interface ViaVersionConfig { WorldIdentifiers get1_16WorldNamesMap(); boolean cache1_17Light(); + + @Nullable String chatTypeFormat(String translationKey); } diff --git a/api/src/main/java/com/viaversion/viaversion/api/protocol/Protocol.java b/api/src/main/java/com/viaversion/viaversion/api/protocol/Protocol.java index ff7358b5c..df1c518a3 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/protocol/Protocol.java +++ b/api/src/main/java/com/viaversion/viaversion/api/protocol/Protocol.java @@ -302,7 +302,7 @@ public interface Protocol chatTypeFormats; protected AbstractViaConfig(File configFile) { super(configFile); @@ -158,6 +159,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf worlds.getOrDefault("nether", WorldIdentifiers.NETHER_DEFAULT), worlds.getOrDefault("end", WorldIdentifiers.END_DEFAULT)); cache1_17Light = getBoolean("cache-1_17-light", true); + chatTypeFormats = get("chat-types-1_19", Map.class, new HashMap()); } private BlockedProtocolVersions loadBlockedProtocolVersions() { @@ -526,4 +528,9 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf public boolean cache1_17Light() { return cache1_17Light; } + + @Override + public @Nullable String chatTypeFormat(final String translationKey) { + return chatTypeFormats.get(translationKey); + } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/Protocol1_19_1To1_19.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/Protocol1_19_1To1_19.java index 9a5126ebe..189057763 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/Protocol1_19_1To1_19.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/Protocol1_19_1To1_19.java @@ -17,16 +17,31 @@ */ package com.viaversion.viaversion.protocols.protocol1_19_1to1_19; +import com.github.steveice10.opennbt.tag.builtin.ByteTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.github.steveice10.opennbt.tag.builtin.NumberTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.google.common.base.Preconditions; import com.google.gson.JsonElement; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.minecraft.ProfileKey; import com.viaversion.viaversion.api.minecraft.nbt.BinaryTagIO; import com.viaversion.viaversion.api.protocol.AbstractProtocol; +import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.protocol.packet.State; import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper; import com.viaversion.viaversion.api.type.Type; +import com.viaversion.viaversion.libs.kyori.adventure.text.Component; +import com.viaversion.viaversion.libs.kyori.adventure.text.TextReplacementConfig; +import com.viaversion.viaversion.libs.kyori.adventure.text.format.NamedTextColor; +import com.viaversion.viaversion.libs.kyori.adventure.text.format.TextDecoration; +import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets; import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets; +import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.storage.ChatTypeStorage; import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.storage.NonceStorage; import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.ClientboundPackets1_19; import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.ServerboundPackets1_19; @@ -94,18 +109,18 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol { - // Back to system chat - bye bye chat formats for 1.19.0 servers - // ... not that big of a deal since the majority of modded servers only has Vanilla /say command and the alike sent as proper player chat + // Back to system chat final JsonElement signedContent = wrapper.read(Type.COMPONENT); final JsonElement unsignedContent = wrapper.read(Type.OPTIONAL_COMPONENT); - wrapper.write(Type.COMPONENT, unsignedContent != null ? unsignedContent : signedContent); + final int chatType = wrapper.read(Type.VAR_INT); - final int type = wrapper.read(Type.VAR_INT); - wrapper.write(Type.BOOLEAN, type == 2); // Overlay, going by the default 1.19 chat type registry + wrapper.read(Type.UUID); // Sender UUID + final JsonElement senderName = wrapper.read(Type.COMPONENT); + final JsonElement teamName = wrapper.read(Type.OPTIONAL_COMPONENT); + if (!decorateChatMessage(wrapper, chatType, senderName, teamName, unsignedContent != null ? unsignedContent : signedContent)) { + wrapper.cancel(); + } }); - read(Type.UUID); // Sender uuid - read(Type.COMPONENT); // Sender display name - read(Type.OPTIONAL_COMPONENT); // Target display name read(Type.LONG); // Timestamp read(Type.LONG); // Salt read(Type.BYTE_ARRAY_PRIMITIVE); // Signature @@ -151,11 +166,20 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol { - // Replace chat types - not worth the effort of handling them properly - final CompoundTag tag = wrapper.get(Type.NBT, 0); - tag.put("minecraft:chat_type", CHAT_REGISTRY.clone()); + final ChatTypeStorage chatTypeStorage = wrapper.user().get(ChatTypeStorage.class); + chatTypeStorage.clear(); + + final CompoundTag registry = wrapper.passthrough(Type.NBT); + final ListTag chatTypes = ((CompoundTag) registry.get("minecraft:chat_type")).get("value"); + for (final Tag chatType : chatTypes) { + final CompoundTag chatTypeCompound = (CompoundTag) chatType; + final NumberTag idTag = chatTypeCompound.get("id"); + chatTypeStorage.addChatType(idTag.asInt(), chatTypeCompound); + } + + // Replace chat types - they won't actually be used + registry.put("minecraft:chat_type", CHAT_REGISTRY.clone()); }); } }); @@ -222,4 +246,83 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocolget("element").get("chat"); + boolean overlay = false; + if (chatData == null) { + chatData = chatType.get("element").get("overlay"); + if (chatData == null) { + // Either narration or something we don't know + return false; + } + + overlay = true; + } + + final CompoundTag decoaration = chatData.get("decoration"); + if (decoaration == null) { + wrapper.write(Type.COMPONENT, message); + wrapper.write(Type.BOOLEAN, overlay); + return true; + } + + final String translationKey = (String) decoaration.get("translation_key").getValue(); + final String rawTranslation = Via.getConfig().chatTypeFormat(translationKey); + if (rawTranslation == null) { + Via.getPlatform().getLogger().warning("Missing chat type translation for key " + translationKey); + return false; + } + + Component component = Component.text(rawTranslation); + final CompoundTag style = decoaration.get("style"); + if (style != null) { + final StringTag color = style.get("color"); + if (color != null && NamedTextColor.NAMES.value(color.getValue()) != null) { + component = component.color(NamedTextColor.NAMES.value(color.getValue())); + } + for (final String key : TextDecoration.NAMES.keys()) { + if (style.contains(key) && style.get(key).asByte() == 1) { + component = component.decorate(TextDecoration.NAMES.value(key)); + } + } + } + + final ListTag parameters = decoaration.get("parameters"); + if (parameters != null) for (final Tag element : parameters) { + switch ((String) element.getValue()) { + case "sender": + component = component.replaceText(replace(senderName)); + break; + case "content": + component = component.replaceText(replace(message)); + break; + case "team_name": + Preconditions.checkNotNull(teamName, "Team name is null"); + component = component.replaceText(replace(teamName)); + break; + default: + Via.getPlatform().getLogger().warning("Unknown parameter for chat decoration: " + element.getValue()); + } + } + + wrapper.write(Type.COMPONENT, GsonComponentSerializer.gson().serializeToTree(component)); + wrapper.write(Type.BOOLEAN, overlay); + return true; + } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/storage/ChatTypeStorage.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/storage/ChatTypeStorage.java new file mode 100644 index 000000000..0712b26fd --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/storage/ChatTypeStorage.java @@ -0,0 +1,47 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2022 ViaVersion and 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.viaversion.viaversion.protocols.protocol1_19_1to1_19.storage; + +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.Protocol1_19To1_18_2; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.checkerframework.checker.nullness.qual.Nullable; + +public final class ChatTypeStorage implements StorableObject { + + private final Int2ObjectMap chatTypes = new Int2ObjectOpenHashMap<>(); + + public @Nullable CompoundTag chatType(final int id) { + return chatTypes.isEmpty() ? Protocol1_19To1_18_2.MAPPINGS.chatType(id) : chatTypes.get(id); + } + + public void addChatType(final int id, final CompoundTag chatType) { + chatTypes.put(id, chatType); + } + + public void clear() { + chatTypes.clear(); + } + + @Override + public boolean clearOnServerSwitch() { + return false; + } +} diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19to1_18_2/Protocol1_19To1_18_2.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19to1_18_2/Protocol1_19To1_18_2.java index dd78f114f..03ed4b904 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19to1_18_2/Protocol1_19To1_18_2.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19to1_18_2/Protocol1_19To1_18_2.java @@ -20,8 +20,6 @@ package com.viaversion.viaversion.protocols.protocol1_19to1_18_2; import com.google.gson.JsonElement; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.connection.UserConnection; -import com.viaversion.viaversion.api.data.MappingData; -import com.viaversion.viaversion.api.data.MappingDataBase; import com.viaversion.viaversion.api.minecraft.entities.Entity1_19Types; import com.viaversion.viaversion.api.platform.providers.ViaProviders; import com.viaversion.viaversion.api.protocol.AbstractProtocol; @@ -40,6 +38,7 @@ import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets; import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets; import com.viaversion.viaversion.protocols.protocol1_17to1_16_4.ServerboundPackets1_17; import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.ClientboundPackets1_18; +import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.data.MappingData; import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.packets.EntityPackets; import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.packets.InventoryPackets; import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.packets.WorldPackets; @@ -57,7 +56,7 @@ import java.util.concurrent.ThreadLocalRandom; public final class Protocol1_19To1_18_2 extends AbstractProtocol { - public static final MappingData MAPPINGS = new MappingDataBase("1.18", "1.19", true); + public static final MappingData MAPPINGS = new MappingData(); private final EntityPackets entityRewriter = new EntityPackets(this); private final InventoryPackets itemRewriter = new InventoryPackets(this); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19to1_18_2/data/MappingData.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19to1_18_2/data/MappingData.java new file mode 100644 index 000000000..7aaae7693 --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19to1_18_2/data/MappingData.java @@ -0,0 +1,59 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2022 ViaVersion and 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.viaversion.viaversion.protocols.protocol1_19to1_18_2.data; + +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.github.steveice10.opennbt.tag.builtin.NumberTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.google.gson.JsonObject; +import com.viaversion.viaversion.api.data.MappingDataBase; +import com.viaversion.viaversion.api.data.MappingDataLoader; +import com.viaversion.viaversion.api.minecraft.nbt.BinaryTagIO; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.io.IOException; + +public final class MappingData extends MappingDataBase { + + private final Int2ObjectMap defaultChatTypes = new Int2ObjectOpenHashMap<>(); + + public MappingData() { + super("1.18", "1.19", true); + } + + @Override + protected void loadExtras(final JsonObject oldMappings, final JsonObject newMappings, @Nullable final JsonObject diffMappings) { + try { + final ListTag chatTypes = BinaryTagIO.readCompressedInputStream(MappingDataLoader.getResource("chat-types-1.19.nbt")).get("values"); + for (final Tag chatType : chatTypes) { + final CompoundTag chatTypeCompound = (CompoundTag) chatType; + final NumberTag idTag = chatTypeCompound.get("id"); + defaultChatTypes.put(idTag.asInt(), chatTypeCompound); + } + } catch (final IOException e) { + e.printStackTrace(); + } + } + + public @Nullable CompoundTag chatType(final int id) { + return defaultChatTypes.get(id); + } +} diff --git a/common/src/main/resources/assets/viaversion/config.yml b/common/src/main/resources/assets/viaversion/config.yml index c204acfb6..bc992dd42 100644 --- a/common/src/main/resources/assets/viaversion/config.yml +++ b/common/src/main/resources/assets/viaversion/config.yml @@ -164,6 +164,14 @@ resource-pack-1_17-prompt: '' # Only disable this if you know what you are doing. cache-1_17-light: true # +# 1.19 chat type formats used for 1.19.1+ clients. +chat-types-1_19: + "chat.type.text": "<%s> %s" + "chat.type.announcement": "[%s] %s" + "commands.message.display.incoming": "%s whispers to you: %s" + "chat.type.team.text": "%s <%s> %s" + "chat.type.emote": "* %s %s" +# #----------------------------------------------------------# # 1.9+ CLIENTS ON 1.8 SERVERS OPTIONS # #----------------------------------------------------------# diff --git a/common/src/main/resources/assets/viaversion/data/chat-types-1.19.nbt b/common/src/main/resources/assets/viaversion/data/chat-types-1.19.nbt new file mode 100644 index 000000000..5c5fe2798 Binary files /dev/null and b/common/src/main/resources/assets/viaversion/data/chat-types-1.19.nbt differ