|
26 | 26 | package org.geysermc.geyser.translator.text; |
27 | 27 |
|
28 | 28 | import net.kyori.adventure.text.Component; |
| 29 | +import net.kyori.adventure.text.JoinConfiguration; |
29 | 30 | import net.kyori.adventure.text.ScoreComponent; |
30 | 31 | import net.kyori.adventure.text.TranslatableComponent; |
31 | 32 | import net.kyori.adventure.text.flattener.ComponentFlattener; |
| 33 | +import net.kyori.adventure.text.format.NamedTextColor; |
| 34 | +import net.kyori.adventure.text.format.Style; |
32 | 35 | import net.kyori.adventure.text.format.TextColor; |
| 36 | +import net.kyori.adventure.text.format.TextDecoration; |
33 | 37 | import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; |
34 | 38 | import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; |
35 | 39 | import net.kyori.adventure.text.serializer.legacy.CharacterAndFormat; |
36 | 40 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; |
37 | 41 | import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; |
38 | 42 | import org.cloudburstmc.nbt.NbtMap; |
| 43 | +import org.cloudburstmc.nbt.NbtType; |
39 | 44 | import org.cloudburstmc.protocol.bedrock.packet.TextPacket; |
40 | 45 | import org.geysermc.geyser.GeyserImpl; |
41 | 46 | import org.geysermc.geyser.session.GeyserSession; |
@@ -341,16 +346,16 @@ public static void handleChatPacket(GeyserSession session, Component message, Ho |
341 | 346 | // Though, Bedrock cannot care about the signed stuff. |
342 | 347 | TranslatableComponent.Builder withDecoration = Component.translatable() |
343 | 348 | .key(chat.translationKey()) |
344 | | - .style(TextDecoration.getStyle(chat)); |
| 349 | + .style(ChatDecoration.getStyle(chat)); |
345 | 350 | List<ChatTypeDecoration.Parameter> parameters = chat.parameters(); |
346 | 351 | List<Component> args = new ArrayList<>(3); |
347 | | - if (parameters.contains(TextDecoration.Parameter.TARGET)) { |
| 352 | + if (parameters.contains(ChatDecoration.Parameter.TARGET)) { |
348 | 353 | args.add(targetName); |
349 | 354 | } |
350 | | - if (parameters.contains(TextDecoration.Parameter.SENDER)) { |
| 355 | + if (parameters.contains(ChatDecoration.Parameter.SENDER)) { |
351 | 356 | args.add(sender); |
352 | 357 | } |
353 | | - if (parameters.contains(TextDecoration.Parameter.CONTENT)) { |
| 358 | + if (parameters.contains(ChatDecoration.Parameter.CONTENT)) { |
354 | 359 | args.add(message); |
355 | 360 | } |
356 | 361 | withDecoration.arguments(args); |
@@ -426,17 +431,91 @@ public static String normalizeSpace(String string) { |
426 | 431 | } |
427 | 432 |
|
428 | 433 | /** |
429 | | - * Deserialize an NbtMap provided from a registry into a string. |
| 434 | + * Deserialize an NbtMap with a description text component (usually provided from a registry) into a Bedrock-formatted string. |
430 | 435 | */ |
431 | | - // This may be a Component in the future. |
432 | | - public static String deserializeDescription(NbtMap tag) { |
| 436 | + public static String deserializeDescription(GeyserSession session, NbtMap tag) { |
433 | 437 | NbtMap description = tag.getCompound("description"); |
434 | | - String translate = description.getString("translate", null); |
435 | | - if (translate == null) { |
436 | | - GeyserImpl.getInstance().getLogger().debug("Don't know how to read description! " + tag); |
437 | | - return ""; |
| 438 | + Component parsed = componentFromNbtTag(description); |
| 439 | + return convertMessage(session, parsed); |
| 440 | + } |
| 441 | + |
| 442 | + public static Component componentFromNbtTag(Object nbtTag) { |
| 443 | + return componentFromNbtTag(nbtTag, Style.empty()); |
| 444 | + } |
| 445 | + |
| 446 | + private static Component componentFromNbtTag(Object nbtTag, Style style) { |
| 447 | + if (nbtTag instanceof String literal) { |
| 448 | + return Component.text(literal).style(style); |
| 449 | + } else if (nbtTag instanceof List<?> list) { |
| 450 | + return Component.join(JoinConfiguration.noSeparators(), componentsFromNbtList(list, style)); |
| 451 | + } else if (nbtTag instanceof NbtMap map) { |
| 452 | + Component component = null; |
| 453 | + String text = map.getString("text", null); |
| 454 | + if (text != null) { |
| 455 | + component = Component.text(text); |
| 456 | + } else { |
| 457 | + String translateKey = map.getString("translate", null); |
| 458 | + if (translateKey != null) { |
| 459 | + String fallback = map.getString("fallback", ""); |
| 460 | + List<Component> args = new ArrayList<>(); |
| 461 | + |
| 462 | + Object with = map.get("with"); |
| 463 | + if (with instanceof List<?> list) { |
| 464 | + args = componentsFromNbtList(list, style); |
| 465 | + } else if (with != null) { |
| 466 | + args.add(componentFromNbtTag(with, style)); |
| 467 | + } |
| 468 | + component = Component.translatable(translateKey, fallback, args); |
| 469 | + } |
| 470 | + } |
| 471 | + |
| 472 | + if (component != null) { |
| 473 | + Style newStyle = getStyleFromNbtMap(map, style); |
| 474 | + component = component.style(newStyle); |
| 475 | + |
| 476 | + Object extra = map.get("extra"); |
| 477 | + if (extra != null) { |
| 478 | + component = component.append(componentFromNbtTag(extra, newStyle)); |
| 479 | + } |
| 480 | + |
| 481 | + return component; |
| 482 | + } |
438 | 483 | } |
439 | | - return translate; |
| 484 | + |
| 485 | + throw new IllegalArgumentException("Expected tag to be a literal string, a list of components, or a component object with a text/translate key"); |
| 486 | + } |
| 487 | + |
| 488 | + private static List<Component> componentsFromNbtList(List<?> list, Style style) { |
| 489 | + List<Component> components = new ArrayList<>(); |
| 490 | + for (Object entry : list) { |
| 491 | + components.add(componentFromNbtTag(entry, style)); |
| 492 | + } |
| 493 | + return components; |
| 494 | + } |
| 495 | + |
| 496 | + public static Style getStyleFromNbtMap(NbtMap map) { |
| 497 | + Style.Builder style = Style.style(); |
| 498 | + |
| 499 | + String colorString = map.getString("color", null); |
| 500 | + if (colorString != null) { |
| 501 | + if (colorString.startsWith(TextColor.HEX_PREFIX)) { |
| 502 | + style.color(TextColor.fromHexString(colorString)); |
| 503 | + } else { |
| 504 | + style.color(NamedTextColor.NAMES.value(colorString)); |
| 505 | + } |
| 506 | + } |
| 507 | + |
| 508 | + map.listenForBoolean("bold", value -> style.decoration(TextDecoration.BOLD, value)); |
| 509 | + map.listenForBoolean("italic", value -> style.decoration(TextDecoration.ITALIC, value)); |
| 510 | + map.listenForBoolean("underlined", value -> style.decoration(TextDecoration.UNDERLINED, value)); |
| 511 | + map.listenForBoolean("strikethrough", value -> style.decoration(TextDecoration.STRIKETHROUGH, value)); |
| 512 | + map.listenForBoolean("obfuscated", value -> style.decoration(TextDecoration.OBFUSCATED, value)); |
| 513 | + |
| 514 | + return style.build(); |
| 515 | + } |
| 516 | + |
| 517 | + public static Style getStyleFromNbtMap(NbtMap map, Style base) { |
| 518 | + return base.merge(getStyleFromNbtMap(map)); |
440 | 519 | } |
441 | 520 |
|
442 | 521 | public static void init() { |
|
0 commit comments