Replaced chat component translation with mcstructs

This commit is contained in:
RaphiMC 2024-04-05 19:01:04 +02:00
parent 4998dbec16
commit 4d2161d5f1
No known key found for this signature in database
GPG Key ID: 0F6BB0657A03AC94
8 changed files with 215 additions and 285 deletions

View File

@ -54,7 +54,10 @@ import net.raphimc.vialegacy.api.remapper.LegacyItemRewriter;
import net.raphimc.vialegacy.api.splitter.PreNettySplitter; import net.raphimc.vialegacy.api.splitter.PreNettySplitter;
import net.raphimc.vialegacy.api.util.PacketUtil; import net.raphimc.vialegacy.api.util.PacketUtil;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.providers.EncryptionProvider; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.providers.EncryptionProvider;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.*; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.ChatComponentRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.ItemRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.SoundRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter.StatisticRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.*; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.*;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.MetaType1_6_4; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.MetaType1_6_4;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.types.Types1_6_4;
@ -125,7 +128,7 @@ public class Protocol1_7_2_5to1_6_4 extends StatelessTransitionProtocol<Clientbo
this.registerClientbound(ClientboundPackets1_6_4.CHAT_MESSAGE, new PacketHandlers() { this.registerClientbound(ClientboundPackets1_6_4.CHAT_MESSAGE, new PacketHandlers() {
@Override @Override
public void register() { public void register() {
map(Types1_6_4.STRING, Type.STRING, msg -> TranslationRewriter.toClient(ChatComponentRewriter.toClient(msg))); // message map(Types1_6_4.STRING, Type.STRING, ChatComponentRewriter::toClient); // message
} }
}); });
this.registerClientbound(ClientboundPackets1_6_4.ENTITY_EQUIPMENT, new PacketHandlers() { this.registerClientbound(ClientboundPackets1_6_4.ENTITY_EQUIPMENT, new PacketHandlers() {

View File

@ -18,6 +18,8 @@
package net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter; package net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.mcstructs.text.ATextComponent; import com.viaversion.viaversion.libs.mcstructs.text.ATextComponent;
import com.viaversion.viaversion.libs.mcstructs.text.components.StringComponent; import com.viaversion.viaversion.libs.mcstructs.text.components.StringComponent;
import com.viaversion.viaversion.libs.mcstructs.text.components.TranslationComponent; import com.viaversion.viaversion.libs.mcstructs.text.components.TranslationComponent;
@ -27,27 +29,81 @@ import com.viaversion.viaversion.libs.mcstructs.text.utils.TextUtils;
public class ChatComponentRewriter { public class ChatComponentRewriter {
private static final Object2ObjectMap<String, String> TRANSLATIONS = new Object2ObjectOpenHashMap<>(37, 0.99F);
static {
TRANSLATIONS.put("menu.playdemo", "Play Demo World");
TRANSLATIONS.put("options.ao.off", "Off");
TRANSLATIONS.put("options.framerateLimit", "Performance");
TRANSLATIONS.put("options.resourcepack", "Resource Packs");
TRANSLATIONS.put("performance.max", "Max FPS");
TRANSLATIONS.put("performance.balanced", "Balanced");
TRANSLATIONS.put("performance.powersaver", "Power saver");
TRANSLATIONS.put("key.forward", "Forward");
TRANSLATIONS.put("key.left", "Left");
TRANSLATIONS.put("key.back", "Back");
TRANSLATIONS.put("key.right", "Right");
TRANSLATIONS.put("key.drop", "Drop");
TRANSLATIONS.put("key.chat", "Chat");
TRANSLATIONS.put("key.fog", "Toggle Fog");
TRANSLATIONS.put("key.attack", "Attack");
TRANSLATIONS.put("key.use", "Use Item");
TRANSLATIONS.put("key.command", "Command");
TRANSLATIONS.put("resourcePack.title", "Select Resource Pack");
TRANSLATIONS.put("tile.dirt.name", "Dirt");
TRANSLATIONS.put("tile.sand.name", "Sand");
TRANSLATIONS.put("tile.flower.name", "Flower");
TRANSLATIONS.put("tile.rose.name", "Rose");
TRANSLATIONS.put("item.fishRaw.name", "Raw Fish");
TRANSLATIONS.put("item.fishCooked.name", "Cooked Fish");
TRANSLATIONS.put("commands.give.usage", "/give <player> <item> [amount] [data]");
TRANSLATIONS.put("commands.give.success", "Given %s (ID %s) * %s to %s");
TRANSLATIONS.put("commands.scoreboard.objectives.add.wrongType", "Invalid objective criteria type. Valid types are: %s");
TRANSLATIONS.put("commands.scoreboard.objectives.list.count", "Showing %s objective(s) on scoreboard");
TRANSLATIONS.put("commands.scoreboard.players.list.count", "Showing %s tracked players on the scoreboard");
TRANSLATIONS.put("commands.scoreboard.players.list.player.count", "Showing %s tracked objective(s) for %s");
TRANSLATIONS.put("commands.scoreboard.teams.list.count", "Showing %s teams on the scoreboard");
TRANSLATIONS.put("commands.scoreboard.teams.list.player.count", "Showing %s player(s) in team %s");
TRANSLATIONS.put("commands.scoreboard.teams.empty.usage", "/scoreboard teams clear <name>");
TRANSLATIONS.put("commands.scoreboard.teams.option.usage", "/scoreboard teams option <team> <friendlyfire|color> <value>");
TRANSLATIONS.put("commands.weather.usage", "/weather <clear/rain/thunder> [duration in seconds]");
TRANSLATIONS.put("mco.configure.world.subscription.extend", "Extend");
TRANSLATIONS.put("mco.configure.world.restore.question.line1", "Your realm will be restored to a previous version");
}
public static String toClient(final String text) { public static String toClient(final String text) {
final ATextComponent component = TextComponentSerializer.V1_6.deserialize(text); ATextComponent component = TextComponentSerializer.V1_6.deserialize(text);
// Replace translation keys with their actual translations
TextUtils.iterateAll(component, c -> {
if (c instanceof TranslationComponent) {
final TranslationComponent translationComponent = (TranslationComponent) c;
if (TRANSLATIONS.containsKey(translationComponent.getKey())) {
translationComponent.setKey(TRANSLATIONS.get(translationComponent.getKey()));
}
}
});
// Convert all section sign formatted strings to json formatted ones with styles so the formatting isn't reset on chat line split // Convert all section sign formatted strings to json formatted ones with styles so the formatting isn't reset on chat line split
ATextComponent newComponent = TextUtils.replace(component, ".*", c -> LegacyStringDeserializer.parse(c.asSingleString(), true).setParentStyle(c.getStyle())); component = TextUtils.replace(component, c -> {
if (c instanceof StringComponent) {
return LegacyStringDeserializer.parse(c.asSingleString(), true).setParentStyle(c.getStyle());
} else {
return c;
}
});
// Clickable URLs are handled clientside -> Add click events to the json components // Clickable URLs are handled clientside -> Add click events to the json components
newComponent.forEach(c -> { TextUtils.iterateAll(component, c -> {
if (c instanceof TranslationComponent) { if (c instanceof TranslationComponent) {
final TranslationComponent translationComponent = (TranslationComponent) c; final TranslationComponent translationComponent = (TranslationComponent) c;
final Object[] args = translationComponent.getArgs(); final Object[] args = translationComponent.getArgs();
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ATextComponent) { if (args[i] != null && !(args[i] instanceof ATextComponent)) {
args[i] = TextUtils.makeURLsClickable((ATextComponent) args[i]); args[i] = new StringComponent(args[i].toString());
} else {
args[i] = TextUtils.makeURLsClickable(new StringComponent(args[i].toString()));
} }
} }
} }
}); });
newComponent = TextUtils.makeURLsClickable(newComponent); component = TextUtils.replace(component, TextUtils::makeURLsClickable);
// Also convert "using" -> "with" in translatable components return TextComponentSerializer.V1_7.serialize(component);
return TextComponentSerializer.V1_7.serialize(newComponent);
} }
public static String toClientDisconnect(final String reason) { public static String toClientDisconnect(final String reason) {

View File

@ -1,84 +0,0 @@
/*
* This file is part of ViaLegacy - https://github.com/RaphiMC/ViaLegacy
* Copyright (C) 2020-2024 RK_01/RaphiMC 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 <http://www.gnu.org/licenses/>.
*/
package net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.rewriter;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.ClientboundPackets1_6_4;
public class TranslationRewriter {
private static final Object2ObjectMap<String, String> TRANSLATIONS = new Object2ObjectOpenHashMap<>(37, 0.99F);
static {
TRANSLATIONS.put("menu.playdemo", "Play Demo World");
TRANSLATIONS.put("options.ao.off", "Off");
TRANSLATIONS.put("options.framerateLimit", "Performance");
TRANSLATIONS.put("options.resourcepack", "Resource Packs");
TRANSLATIONS.put("performance.max", "Max FPS");
TRANSLATIONS.put("performance.balanced", "Balanced");
TRANSLATIONS.put("performance.powersaver", "Power saver");
TRANSLATIONS.put("key.forward", "Forward");
TRANSLATIONS.put("key.left", "Left");
TRANSLATIONS.put("key.back", "Back");
TRANSLATIONS.put("key.right", "Right");
TRANSLATIONS.put("key.drop", "Drop");
TRANSLATIONS.put("key.chat", "Chat");
TRANSLATIONS.put("key.fog", "Toggle Fog");
TRANSLATIONS.put("key.attack", "Attack");
TRANSLATIONS.put("key.use", "Use Item");
TRANSLATIONS.put("key.command", "Command");
TRANSLATIONS.put("resourcePack.title", "Select Resource Pack");
TRANSLATIONS.put("tile.dirt.name", "Dirt");
TRANSLATIONS.put("tile.sand.name", "Sand");
TRANSLATIONS.put("tile.flower.name", "Flower");
TRANSLATIONS.put("tile.rose.name", "Rose");
TRANSLATIONS.put("item.fishRaw.name", "Raw Fish");
TRANSLATIONS.put("item.fishCooked.name", "Cooked Fish");
TRANSLATIONS.put("commands.give.usage", "/give <player> <item> [amount] [data]");
TRANSLATIONS.put("commands.give.success", "Given %s (ID %s) * %s to %s");
TRANSLATIONS.put("commands.scoreboard.objectives.add.wrongType", "Invalid objective criteria type. Valid types are: %s");
TRANSLATIONS.put("commands.scoreboard.objectives.list.count", "Showing %s objective(s) on scoreboard");
TRANSLATIONS.put("commands.scoreboard.players.list.count", "Showing %s tracked players on the scoreboard");
TRANSLATIONS.put("commands.scoreboard.players.list.player.count", "Showing %s tracked objective(s) for %s");
TRANSLATIONS.put("commands.scoreboard.teams.list.count", "Showing %s teams on the scoreboard");
TRANSLATIONS.put("commands.scoreboard.teams.list.player.count", "Showing %s player(s) in team %s");
TRANSLATIONS.put("commands.scoreboard.teams.empty.usage", "/scoreboard teams clear <name>");
TRANSLATIONS.put("commands.scoreboard.teams.option.usage", "/scoreboard teams option <team> <friendlyfire|color> <value>");
TRANSLATIONS.put("commands.weather.usage", "/weather <clear/rain/thunder> [duration in seconds]");
TRANSLATIONS.put("mco.configure.world.subscription.extend", "Extend");
TRANSLATIONS.put("mco.configure.world.restore.question.line1", "Your realm will be restored to a previous version");
}
private static final ComponentRewriter<ClientboundPackets1_6_4> REWRITER = new ComponentRewriter<ClientboundPackets1_6_4>(null, ComponentRewriter.ReadType.JSON) {
@Override
protected void handleTranslate(JsonObject object, String translate) {
final String text = TRANSLATIONS.get(translate);
if (text != null) {
object.addProperty("translate", text);
}
}
};
public static String toClient(final String text) {
return REWRITER.processText(text).toString();
}
}

View File

@ -32,7 +32,7 @@ import com.viaversion.viaversion.protocols.base.BaseProtocol1_7;
import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets; import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
import net.raphimc.vialegacy.ViaLegacy; import net.raphimc.vialegacy.ViaLegacy;
import net.raphimc.vialegacy.api.util.UuidUtil; import net.raphimc.vialegacy.api.util.UuidUtil;
import net.raphimc.vialegacy.protocols.release.protocol1_7_6_10to1_7_2_5.rewriter.TranslationRewriter; import net.raphimc.vialegacy.protocols.release.protocol1_7_6_10to1_7_2_5.rewriter.ChatComponentRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.types.Types1_7_6; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.types.Types1_7_6;
@ -79,7 +79,7 @@ public class Protocol1_7_6_10to1_7_2_5 extends AbstractProtocol<ClientboundPacke
this.registerClientbound(ClientboundPackets1_7_2.CHAT_MESSAGE, new PacketHandlers() { this.registerClientbound(ClientboundPackets1_7_2.CHAT_MESSAGE, new PacketHandlers() {
@Override @Override
public void register() { public void register() {
map(Type.STRING, Type.STRING, TranslationRewriter::toClient); // message map(Type.STRING, Type.STRING, ChatComponentRewriter::toClient); // message
} }
}); });
this.registerClientbound(ClientboundPackets1_7_2.BLOCK_ENTITY_DATA, new PacketHandlers() { this.registerClientbound(ClientboundPackets1_7_2.BLOCK_ENTITY_DATA, new PacketHandlers() {

View File

@ -19,11 +19,12 @@ package net.raphimc.vialegacy.protocols.release.protocol1_7_6_10to1_7_2_5.rewrit
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap; import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectOpenHashMap; import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.gson.JsonObject; import com.viaversion.viaversion.libs.mcstructs.text.ATextComponent;
import com.viaversion.viaversion.rewriter.ComponentRewriter; import com.viaversion.viaversion.libs.mcstructs.text.components.TranslationComponent;
import net.raphimc.vialegacy.protocols.release.protocol1_7_6_10to1_7_2_5.ClientboundPackets1_7_2; import com.viaversion.viaversion.libs.mcstructs.text.serializer.TextComponentSerializer;
import com.viaversion.viaversion.libs.mcstructs.text.utils.TextUtils;
public class TranslationRewriter { public class ChatComponentRewriter {
private static final Object2ObjectMap<String, String> TRANSLATIONS = new Object2ObjectOpenHashMap<>(86, 0.99F); private static final Object2ObjectMap<String, String> TRANSLATIONS = new Object2ObjectOpenHashMap<>(86, 0.99F);
@ -116,18 +117,18 @@ public class TranslationRewriter {
TRANSLATIONS.put("mco.invites.nopending", "No pending invitations!"); TRANSLATIONS.put("mco.invites.nopending", "No pending invitations!");
} }
private static final ComponentRewriter<ClientboundPackets1_7_2> REWRITER = new ComponentRewriter<ClientboundPackets1_7_2>(null, ComponentRewriter.ReadType.JSON) {
@Override
protected void handleTranslate(JsonObject object, String translate) {
final String text = TRANSLATIONS.get(translate);
if (text != null) {
object.addProperty("translate", text);
}
}
};
public static String toClient(final String text) { public static String toClient(final String text) {
return REWRITER.processText(text).toString(); final ATextComponent component = TextComponentSerializer.V1_7.deserialize(text);
// Replace translation keys with their actual translations
TextUtils.iterateAll(component, c -> {
if (c instanceof TranslationComponent) {
final TranslationComponent translationComponent = (TranslationComponent) c;
if (TRANSLATIONS.containsKey(translationComponent.getKey())) {
translationComponent.setKey(TRANSLATIONS.get(translationComponent.getKey()));
}
}
});
return TextComponentSerializer.V1_7.serialize(component);
} }
} }

View File

@ -60,9 +60,8 @@ import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.MapDa
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.MapIcon; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.MapIcon;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.TabListEntry; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.TabListEntry;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.rewriter.ChatItemRewriter; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.rewriter.ChatComponentRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.rewriter.ItemRewriter; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.rewriter.ItemRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.rewriter.TranslationRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.storage.*; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.storage.*;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.types.Types1_7_6; import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.types.Types1_7_6;
@ -72,7 +71,7 @@ import java.util.*;
public class Protocol1_8to1_7_6_10 extends AbstractProtocol<ClientboundPackets1_7_2, ClientboundPackets1_8, ServerboundPackets1_7_2, ServerboundPackets1_8> { public class Protocol1_8to1_7_6_10 extends AbstractProtocol<ClientboundPackets1_7_2, ClientboundPackets1_8, ServerboundPackets1_7_2, ServerboundPackets1_8> {
private final LegacyItemRewriter<Protocol1_8to1_7_6_10> itemRewriter = new ItemRewriter(this); private final LegacyItemRewriter<Protocol1_8to1_7_6_10> itemRewriter = new ItemRewriter(this);
private final ChatItemRewriter chatItemRewriter = new ChatItemRewriter(this); private final ChatComponentRewriter chatComponentRewriter = new ChatComponentRewriter(this);
private final MetadataRewriter metadataRewriter = new MetadataRewriter(this); private final MetadataRewriter metadataRewriter = new MetadataRewriter(this);
public Protocol1_8to1_7_6_10() { public Protocol1_8to1_7_6_10() {
@ -154,7 +153,7 @@ public class Protocol1_8to1_7_6_10 extends AbstractProtocol<ClientboundPackets1_
this.registerClientbound(ClientboundPackets1_7_2.CHAT_MESSAGE, new PacketHandlers() { this.registerClientbound(ClientboundPackets1_7_2.CHAT_MESSAGE, new PacketHandlers() {
@Override @Override
public void register() { public void register() {
map(Type.STRING, Type.STRING, msg -> TranslationRewriter.toClient(chatItemRewriter.remapShowItem(msg))); // message map(Type.STRING, Type.STRING, chatComponentRewriter::toClient); // message
create(Type.BYTE, (byte) 0); // position create(Type.BYTE, (byte) 0); // position
} }
}); });

View File

@ -17,30 +17,94 @@
*/ */
package net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.rewriter; package net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.rewriter;
import com.viaversion.viaversion.api.minecraft.item.DataItem; import com.viaversion.viaversion.api.minecraft.item.DataItem;
import com.viaversion.viaversion.api.minecraft.item.Item; import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap; import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.gson.JsonArray; import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import com.viaversion.viaversion.libs.gson.JsonElement; import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.libs.mcstructs.snbt.SNbtSerializer; import com.viaversion.viaversion.libs.mcstructs.snbt.SNbtSerializer;
import com.viaversion.viaversion.libs.mcstructs.text.ATextComponent; import com.viaversion.viaversion.libs.mcstructs.text.ATextComponent;
import com.viaversion.viaversion.libs.mcstructs.text.components.StringComponent;
import com.viaversion.viaversion.libs.mcstructs.text.components.TranslationComponent;
import com.viaversion.viaversion.libs.mcstructs.text.events.hover.HoverEventAction;
import com.viaversion.viaversion.libs.mcstructs.text.events.hover.impl.TextHoverEvent;
import com.viaversion.viaversion.libs.mcstructs.text.serializer.TextComponentSerializer; import com.viaversion.viaversion.libs.mcstructs.text.serializer.TextComponentSerializer;
import com.viaversion.viaversion.libs.mcstructs.text.utils.TextUtils;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag; import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.ShortTag; import com.viaversion.viaversion.libs.opennbt.tag.builtin.ShortTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.StringTag; import com.viaversion.viaversion.libs.opennbt.tag.builtin.StringTag;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import net.raphimc.vialegacy.ViaLegacy; import net.raphimc.vialegacy.ViaLegacy;
import net.raphimc.vialegacy.protocols.release.protocol1_7_6_10to1_7_2_5.ClientboundPackets1_7_2;
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.Protocol1_8to1_7_6_10;
import java.util.logging.Level; import java.util.logging.Level;
public class ChatItemRewriter { public class ChatComponentRewriter {
private static final Object2ObjectMap<String, String> TRANSLATIONS = new Object2ObjectOpenHashMap<>(59, 0.99F);
private static final Int2ObjectOpenHashMap<String> ID_TO_NAME = new Int2ObjectOpenHashMap<>(315, 0.99F); private static final Int2ObjectOpenHashMap<String> ID_TO_NAME = new Int2ObjectOpenHashMap<>(315, 0.99F);
static { static {
TRANSLATIONS.put("gui.toMenu", "Back to title screen");
TRANSLATIONS.put("generator.amplified", "Amplified");
TRANSLATIONS.put("disconnect.loginFailedInfo.serversUnavailable", "The authentication are currently down for maintenance.");
TRANSLATIONS.put("options.aoDesc0", "Enable faux ambient occlusion on blocks.");
TRANSLATIONS.put("options.framerateLimitDesc0", "Selects the maximum frame rate:");
TRANSLATIONS.put("options.framerateLimitDesc1", "35fps, 120fps, or 200+fps.");
TRANSLATIONS.put("options.viewBobbingDesc0", "Enables view-bob when moving.");
TRANSLATIONS.put("options.renderCloudsDesc0", "Enables the rendering of clouds.");
TRANSLATIONS.put("options.graphicsDesc0", "'Fancy': Enables extra transparency.");
TRANSLATIONS.put("options.graphicsDesc1", "'Fast': Suggested for lower-end hardware.");
TRANSLATIONS.put("options.renderDistanceDesc0", "Maximum render distance. Smaller values");
TRANSLATIONS.put("options.renderDistanceDesc1", "run better on lower-end hardware.");
TRANSLATIONS.put("options.particlesDesc0", "Selects the overall amount of particles.");
TRANSLATIONS.put("options.particlesDesc1", "On lower-end hardware, less is better.");
TRANSLATIONS.put("options.advancedOpenglDesc0", "Enables occlusion queries. On AMD and Intel");
TRANSLATIONS.put("options.advancedOpenglDesc1", "hardware, this may decrease performance.");
TRANSLATIONS.put("options.fboEnableDesc0", "Enables the use of Framebuffer Objects.");
TRANSLATIONS.put("options.fboEnableDesc1", "Necessary for certain Minecraft features.");
TRANSLATIONS.put("options.postProcessEnableDesc0", "Enables post-processing. Disabling will");
TRANSLATIONS.put("options.postProcessEnableDesc1", "result in reduction in Awesome Levels.");
TRANSLATIONS.put("options.showCape", "Show Cape");
TRANSLATIONS.put("options.anisotropicFiltering", "Anisotropic Filtering");
TRANSLATIONS.put("tile.stone.name", "Stone");
TRANSLATIONS.put("tile.sapling.roofed_oak.name", "Dark Oak Sapling");
TRANSLATIONS.put("tile.sponge.name", "Sponge");
TRANSLATIONS.put("tile.stairsStone.name", "Stone Stairs");
TRANSLATIONS.put("tile.pressurePlate.name", "Pressure Plate");
TRANSLATIONS.put("tile.fence.name", "Fence");
TRANSLATIONS.put("tile.fenceGate.name", "Fence Gate");
TRANSLATIONS.put("tile.trapdoor.name", "Trapdoor");
TRANSLATIONS.put("item.doorWood.name", "Wooden Door");
TRANSLATIONS.put("entity.Arrow.name", "arrow");
TRANSLATIONS.put("achievement.overkill.desc", "Deal eight hearts of damage in a single hit");
TRANSLATIONS.put("commands.generic.deprecatedId", "Warning: Using numeric IDs will not be supported in the future. Please use names, such as '%s'");
TRANSLATIONS.put("commands.give.notFound", "There is no such item with ID %s");
TRANSLATIONS.put("commands.effect.usage", "/effect <player> <effect> [seconds] [amplifier]");
TRANSLATIONS.put("commands.clear.usage", "/clear <player> [item] [data]");
TRANSLATIONS.put("commands.time.usage", "/time <set|add> <value>");
TRANSLATIONS.put("commands.kill.usage", "/kill");
TRANSLATIONS.put("commands.kill.success", "Ouch! That looked like it hurt");
TRANSLATIONS.put("commands.tp.success.coordinates", "Teleported %s to %s,%s,%s");
TRANSLATIONS.put("commands.tp.usage", "/tp [target player] <destination player> OR /tp [target player] <x> <y> <z>");
TRANSLATIONS.put("commands.scoreboard.usage", "/scoreboard <objectives|players|teams>");
TRANSLATIONS.put("commands.scoreboard.objectives.usage", "/scoreboard objectives <list|add|remove|setdisplay>");
TRANSLATIONS.put("commands.scoreboard.players.usage", "/scoreboard players <set|add|remove|reset|list>");
TRANSLATIONS.put("commands.scoreboard.players.set.usage", "/scoreboard players set <player> <objective> <score>");
TRANSLATIONS.put("commands.scoreboard.players.add.usage", "/scoreboard players add <player> <objective> <count>");
TRANSLATIONS.put("commands.scoreboard.players.remove.usage", "/scoreboard players remove <player> <objective> <count>");
TRANSLATIONS.put("commands.scoreboard.players.reset.usage", "/scoreboard players reset <player>");
TRANSLATIONS.put("commands.scoreboard.players.reset.success", "Reset all scores of player %s");
TRANSLATIONS.put("commands.scoreboard.teams.usage", "/scoreboard teams <list|add|remove|empty|join|leave|option>");
TRANSLATIONS.put("commands.scoreboard.teams.empty.usage", "/scoreboard teams empty");
TRANSLATIONS.put("commands.scoreboard.teams.option.usage", "/scoreboard teams option <team> <friendlyfire|color|seeFriendlyInvisibles> <value>");
TRANSLATIONS.put("commands.spawnpoint.usage", "/spawnpoint OR /spawnpoint <player> OR /spawnpoint <player> <x> <y> <z>");
TRANSLATIONS.put("commands.setworldspawn.usage", "/setworldspawn OR /setworldspawn <x> <y> <z>");
TRANSLATIONS.put("commands.gamerule.usage", "/gamerule <rule name> <value> OR /gamerule <rule name>");
TRANSLATIONS.put("commands.testfor.usage", "/testfor <player>");
TRANSLATIONS.put("commands.testfor.failed", "/testfor is only usable by commandblocks with analog output");
TRANSLATIONS.put("commands.achievement.usage", "/achievement give <stat_name> [player]");
ID_TO_NAME.put(1, "stone"); ID_TO_NAME.put(1, "stone");
ID_TO_NAME.put(2, "grass"); ID_TO_NAME.put(2, "grass");
ID_TO_NAME.put(3, "dirt"); ID_TO_NAME.put(3, "dirt");
@ -358,64 +422,61 @@ public class ChatItemRewriter {
ID_TO_NAME.put(2267, "record_wait"); ID_TO_NAME.put(2267, "record_wait");
} }
private final ComponentRewriter<ClientboundPackets1_7_2> SHOW_ITEM; private final Protocol<?, ?, ?, ?> protocol;
public ChatItemRewriter(final Protocol1_8to1_7_6_10 protocol) { public ChatComponentRewriter(final Protocol<?, ?, ?, ?> protocol) {
this.SHOW_ITEM = new ComponentRewriter<ClientboundPackets1_7_2>(protocol, ComponentRewriter.ReadType.JSON) { this.protocol = protocol;
@Override
protected void handleHoverEvent(JsonObject hoverEvent) {
super.handleHoverEvent(hoverEvent);
final String action = hoverEvent.getAsJsonPrimitive("action").getAsString();
if (!action.equals("show_item")) return;
final JsonElement value = hoverEvent.get("value");
if (value == null) return;
final ATextComponent nbt = TextComponentSerializer.V1_7.deserialize(value);
try {
final CompoundTag tag = (CompoundTag) SNbtSerializer.V1_7.deserialize(nbt.asUnformattedString());
final CompoundTag itemTag = tag.get("tag");
final ShortTag idTag = tag.get("id");
final ShortTag damageTag = tag.get("Damage");
// Call item converter
final short damage = damageTag != null ? damageTag.asShort() : 0;
final short id = idTag != null ? idTag.asShort() : 1;
final Item item = new DataItem();
item.setIdentifier(id);
item.setData(damage);
item.setTag(itemTag);
this.handleItem(item);
// Serialize again
if (damage != item.data()) {
tag.put("Damage", new ShortTag(item.data()));
}
tag.put("id", new StringTag("minecraft:" + ID_TO_NAME.getOrDefault(item.identifier(), "stone")));
if (item.tag() != null) {
tag.put("tag", new CompoundTag(item.tag().getValue()));
}
final JsonArray array = new JsonArray();
final JsonObject object = new JsonObject();
array.add(object);
final String serializedNBT = SNbtSerializer.V1_8.serialize(tag);
object.addProperty("text", serializedNBT);
hoverEvent.add("value", array);
} catch (Throwable e) {
ViaLegacy.getPlatform().getLogger().log(Level.WARNING, "Error remapping NBT in show_item:" + nbt.asUnformattedString(), e);
}
}
private void handleItem(Item item) {
this.protocol.getItemRewriter().handleItemToClient(item);
}
};
} }
public String remapShowItem(final String text) { public String toClient(final String text) {
return SHOW_ITEM.processText(text).toString(); final ATextComponent component = TextComponentSerializer.V1_7.deserialize(text);
// Replace translation keys with their actual translations
TextUtils.iterateAll(component, c -> {
if (c instanceof TranslationComponent) {
final TranslationComponent translationComponent = (TranslationComponent) c;
if (TRANSLATIONS.containsKey(translationComponent.getKey())) {
translationComponent.setKey(TRANSLATIONS.get(translationComponent.getKey()));
}
}
});
// Translate item hover events
TextUtils.iterateAll(component, c -> {
if (c.getStyle().getHoverEvent() instanceof TextHoverEvent) {
final TextHoverEvent textHoverEvent = (TextHoverEvent) c.getStyle().getHoverEvent();
if (textHoverEvent.getAction().equals(HoverEventAction.SHOW_ITEM)) {
try {
final CompoundTag tag = (CompoundTag) SNbtSerializer.V1_7.deserialize(textHoverEvent.getText().asUnformattedString());
final ShortTag idTag = tag.get("id");
final ShortTag damageTag = tag.get("Damage");
final CompoundTag itemTag = tag.get("tag");
final short damage = damageTag != null ? damageTag.asShort() : 0;
Item item = new DataItem();
item.setIdentifier(idTag.asShort());
item.setData(damage);
item.setTag(itemTag);
item = this.protocol.getItemRewriter().handleItemToClient(item);
if (!ID_TO_NAME.containsKey(item.identifier())) {
throw new IllegalArgumentException("Invalid item ID: " + item.identifier());
}
tag.put("id", new StringTag("minecraft:" + ID_TO_NAME.get(item.identifier())));
if (damage != item.data()) {
tag.put("Damage", new ShortTag(item.data()));
}
if (item.tag() != itemTag) {
tag.put("tag", item.tag());
}
c.getStyle().setHoverEvent(new TextHoverEvent(textHoverEvent.getAction(), new StringComponent(SNbtSerializer.V1_8.serialize(tag))));
} catch (Throwable e) {
ViaLegacy.getPlatform().getLogger().log(Level.WARNING, "Error remapping NBT in show_item:" + textHoverEvent.getText().asUnformattedString(), e);
c.getStyle().setHoverEvent(new TextHoverEvent(textHoverEvent.getAction(), new StringComponent())); // Invalid item
}
}
}
});
return TextComponentSerializer.V1_8.serialize(component);
} }
} }

View File

@ -1,106 +0,0 @@
/*
* This file is part of ViaLegacy - https://github.com/RaphiMC/ViaLegacy
* Copyright (C) 2020-2024 RK_01/RaphiMC 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 <http://www.gnu.org/licenses/>.
*/
package net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.rewriter;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.rewriter.ComponentRewriter;
import net.raphimc.vialegacy.protocols.release.protocol1_7_6_10to1_7_2_5.ClientboundPackets1_7_2;
public class TranslationRewriter {
private static final Object2ObjectMap<String, String> TRANSLATIONS = new Object2ObjectOpenHashMap<>(59, 0.99F);
static {
TRANSLATIONS.put("gui.toMenu", "Back to title screen");
TRANSLATIONS.put("generator.amplified", "Amplified");
TRANSLATIONS.put("disconnect.loginFailedInfo.serversUnavailable", "The authentication are currently down for maintenance.");
TRANSLATIONS.put("options.aoDesc0", "Enable faux ambient occlusion on blocks.");
TRANSLATIONS.put("options.framerateLimitDesc0", "Selects the maximum frame rate:");
TRANSLATIONS.put("options.framerateLimitDesc1", "35fps, 120fps, or 200+fps.");
TRANSLATIONS.put("options.viewBobbingDesc0", "Enables view-bob when moving.");
TRANSLATIONS.put("options.renderCloudsDesc0", "Enables the rendering of clouds.");
TRANSLATIONS.put("options.graphicsDesc0", "'Fancy': Enables extra transparency.");
TRANSLATIONS.put("options.graphicsDesc1", "'Fast': Suggested for lower-end hardware.");
TRANSLATIONS.put("options.renderDistanceDesc0", "Maximum render distance. Smaller values");
TRANSLATIONS.put("options.renderDistanceDesc1", "run better on lower-end hardware.");
TRANSLATIONS.put("options.particlesDesc0", "Selects the overall amount of particles.");
TRANSLATIONS.put("options.particlesDesc1", "On lower-end hardware, less is better.");
TRANSLATIONS.put("options.advancedOpenglDesc0", "Enables occlusion queries. On AMD and Intel");
TRANSLATIONS.put("options.advancedOpenglDesc1", "hardware, this may decrease performance.");
TRANSLATIONS.put("options.fboEnableDesc0", "Enables the use of Framebuffer Objects.");
TRANSLATIONS.put("options.fboEnableDesc1", "Necessary for certain Minecraft features.");
TRANSLATIONS.put("options.postProcessEnableDesc0", "Enables post-processing. Disabling will");
TRANSLATIONS.put("options.postProcessEnableDesc1", "result in reduction in Awesome Levels.");
TRANSLATIONS.put("options.showCape", "Show Cape");
TRANSLATIONS.put("options.anisotropicFiltering", "Anisotropic Filtering");
TRANSLATIONS.put("tile.stone.name", "Stone");
TRANSLATIONS.put("tile.sapling.roofed_oak.name", "Dark Oak Sapling");
TRANSLATIONS.put("tile.sponge.name", "Sponge");
TRANSLATIONS.put("tile.stairsStone.name", "Stone Stairs");
TRANSLATIONS.put("tile.pressurePlate.name", "Pressure Plate");
TRANSLATIONS.put("tile.fence.name", "Fence");
TRANSLATIONS.put("tile.fenceGate.name", "Fence Gate");
TRANSLATIONS.put("tile.trapdoor.name", "Trapdoor");
TRANSLATIONS.put("item.doorWood.name", "Wooden Door");
TRANSLATIONS.put("entity.Arrow.name", "arrow");
TRANSLATIONS.put("achievement.overkill.desc", "Deal eight hearts of damage in a single hit");
TRANSLATIONS.put("commands.generic.deprecatedId", "Warning: Using numeric IDs will not be supported in the future. Please use names, such as '%s'");
TRANSLATIONS.put("commands.give.notFound", "There is no such item with ID %s");
TRANSLATIONS.put("commands.effect.usage", "/effect <player> <effect> [seconds] [amplifier]");
TRANSLATIONS.put("commands.clear.usage", "/clear <player> [item] [data]");
TRANSLATIONS.put("commands.time.usage", "/time <set|add> <value>");
TRANSLATIONS.put("commands.kill.usage", "/kill");
TRANSLATIONS.put("commands.kill.success", "Ouch! That looked like it hurt");
TRANSLATIONS.put("commands.tp.success.coordinates", "Teleported %s to %s,%s,%s");
TRANSLATIONS.put("commands.tp.usage", "/tp [target player] <destination player> OR /tp [target player] <x> <y> <z>");
TRANSLATIONS.put("commands.scoreboard.usage", "/scoreboard <objectives|players|teams>");
TRANSLATIONS.put("commands.scoreboard.objectives.usage", "/scoreboard objectives <list|add|remove|setdisplay>");
TRANSLATIONS.put("commands.scoreboard.players.usage", "/scoreboard players <set|add|remove|reset|list>");
TRANSLATIONS.put("commands.scoreboard.players.set.usage", "/scoreboard players set <player> <objective> <score>");
TRANSLATIONS.put("commands.scoreboard.players.add.usage", "/scoreboard players add <player> <objective> <count>");
TRANSLATIONS.put("commands.scoreboard.players.remove.usage", "/scoreboard players remove <player> <objective> <count>");
TRANSLATIONS.put("commands.scoreboard.players.reset.usage", "/scoreboard players reset <player>");
TRANSLATIONS.put("commands.scoreboard.players.reset.success", "Reset all scores of player %s");
TRANSLATIONS.put("commands.scoreboard.teams.usage", "/scoreboard teams <list|add|remove|empty|join|leave|option>");
TRANSLATIONS.put("commands.scoreboard.teams.empty.usage", "/scoreboard teams empty");
TRANSLATIONS.put("commands.scoreboard.teams.option.usage", "/scoreboard teams option <team> <friendlyfire|color|seeFriendlyInvisibles> <value>");
TRANSLATIONS.put("commands.spawnpoint.usage", "/spawnpoint OR /spawnpoint <player> OR /spawnpoint <player> <x> <y> <z>");
TRANSLATIONS.put("commands.setworldspawn.usage", "/setworldspawn OR /setworldspawn <x> <y> <z>");
TRANSLATIONS.put("commands.gamerule.usage", "/gamerule <rule name> <value> OR /gamerule <rule name>");
TRANSLATIONS.put("commands.testfor.usage", "/testfor <player>");
TRANSLATIONS.put("commands.testfor.failed", "/testfor is only usable by commandblocks with analog output");
TRANSLATIONS.put("commands.achievement.usage", "/achievement give <stat_name> [player]");
}
private static final ComponentRewriter<ClientboundPackets1_7_2> REWRITER = new ComponentRewriter<ClientboundPackets1_7_2>(null, ComponentRewriter.ReadType.JSON) {
@Override
protected void handleTranslate(JsonObject object, String translate) {
final String text = TRANSLATIONS.get(translate);
if (text != null) {
object.addProperty("translate", text);
}
}
};
public static String toClient(final String text) {
return REWRITER.processText(text).toString();
}
}