From 83011eb903f6ab86819899c3ed60a33997f53c20 Mon Sep 17 00:00:00 2001 From: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> Date: Tue, 23 Aug 2022 09:35:37 +1000 Subject: [PATCH] fix written book meta to allow modern components (#1359) --- .../server/item/metadata/WrittenBookMeta.java | 4 +- .../server/item/ItemMetaWrittenBookTest.java | 210 ++++++++++++++++++ 2 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/test/java/net/minestom/server/item/ItemMetaWrittenBookTest.java diff --git a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java index a295ba70d..20be04fc3 100644 --- a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java @@ -1,6 +1,7 @@ package net.minestom.server.item.metadata; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.minestom.server.item.ItemMetaView; import net.minestom.server.tag.Tag; @@ -19,8 +20,7 @@ public record WrittenBookMeta(TagReadable readable) implements ItemMetaView AUTHOR = Tag.String("author"); private static final Tag TITLE = Tag.String("title"); private static final Tag> PAGES = Tag.String("pages") - .map(s -> LegacyComponentSerializer.legacySection().deserialize(s), - textComponent -> LegacyComponentSerializer.legacySection().serialize(textComponent)) + .map(GsonComponentSerializer.gson()::deserialize, GsonComponentSerializer.gson()::serialize) .list().defaultValue(List.of()); public boolean isResolved() { diff --git a/src/test/java/net/minestom/server/item/ItemMetaWrittenBookTest.java b/src/test/java/net/minestom/server/item/ItemMetaWrittenBookTest.java new file mode 100644 index 000000000..f8eb46b2f --- /dev/null +++ b/src/test/java/net/minestom/server/item/ItemMetaWrittenBookTest.java @@ -0,0 +1,210 @@ +package net.minestom.server.item; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.minestom.server.item.metadata.WrittenBookMeta; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import org.jglrxavpok.hephaistos.parser.SNBTParser; +import org.junit.jupiter.api.Test; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class ItemMetaWrittenBookTest { + @Test + public void testPages() { + ItemStack book = ItemStack.of(Material.WRITTEN_BOOK); + + Component pageA = Component.text("Page A"); + Component pageB = Component.text("Page B").append(Component.newline()).append(Component.text("Page B")); + Component pageC = Component.text("Page C"); + List originalPages = List.of(pageA, pageB, pageC); + + book = book.withMeta(WrittenBookMeta.class, meta -> meta.pages(List.of(pageA, pageB, pageC))); + + WrittenBookMeta meta = book.meta(WrittenBookMeta.class); + + assertEquals(originalPages, meta.getPages(), "written book output meta must equal original input meta"); + } + + @Test + public void testStyleComponents() { + List colors = List.of(NamedTextColor.BLUE, NamedTextColor.WHITE, NamedTextColor.DARK_BLUE); + List decorations = List.of(TextDecoration.UNDERLINED, TextDecoration.STRIKETHROUGH, TextDecoration.ITALIC); + List clicks = List.of( + ClickEvent.runCommand("/test"), + ClickEvent.openUrl("https://minestom.net"), + ClickEvent.changePage(2), + ClickEvent.openFile("/test"), + ClickEvent.copyToClipboard("clipboard text"), + ClickEvent.suggestCommand("/test")); + List> hovers = List.of( + HoverEvent.showText(Component.text("Hover text")), + ItemStack.of(Material.STONE).asHoverEvent(), + HoverEvent.showText(Component.text("Hover text")), + HoverEvent.showText(Component.text("Hover text"))); + + for (TextColor color : colors) { + for (TextDecoration decoration : decorations) { + for (ClickEvent click : clicks) { + for (HoverEvent hover : hovers) { + + Component pageA = Component.text("Page A").style(style -> { + style.color(color); + style.decoration(decoration, true); + style.clickEvent(click); + style.hoverEvent(hover); + }); + Component pageB = Component.text("test"); + + List originalPages = List.of(pageA, pageB); + + ItemStack book = ItemStack.of(Material.WRITTEN_BOOK) + .withMeta(WrittenBookMeta.class, meta -> meta.pages(pageA, pageB)); + + WrittenBookMeta meta = book.meta(WrittenBookMeta.class); + assertEquals(originalPages, meta.getPages(), "written book output meta must equal original input meta"); + } + } + } + } + } + + @Test + public void buildFromVanillSNBT() { + String vanillaSNBT = """ +{ + pages:['[ + "", + {"text":"\\\\n\\\\ntest\\\\n\\\\n"}, + {"text":"TESTSETSET","clickEvent":{"action":"run_command","value":"hi"}}, + {"text":"\\\\n\\\\n"}, + {"text":"COLORS","color":"dark_red"}, + {"text":"\\\\n\\\\n","color":"reset"}, + {"text":"EVERYTHING","bold":true,"italic":true,"strikethrough":true,"underlined":true,"obfuscated":true, + "color":"green","clickEvent":{"action":"run_command","value":"EVERYTHING"}, + "hoverEvent":{"action":"show_text","contents":"Test"}} + ]'], + title:"Minestom Book", + author:"https://minestom.net/", + display:{Lore:["Minestom."]} +} + """; + SNBTParser parser = new SNBTParser(new StringReader(vanillaSNBT)); + + ItemStack book = ItemStack.of(Material.WRITTEN_BOOK).withMeta(WrittenBookMeta.class, meta -> { + List pageA = new ArrayList<>(); + + pageA.add(Component.newline()); + pageA.add(Component.newline()); + pageA.add(Component.text("test")); + pageA.add(Component.newline()); + pageA.add(Component.newline()); + pageA.add(Component.text("TESTSETSET").clickEvent(ClickEvent.runCommand("hi"))); + pageA.add(Component.newline()); + pageA.add(Component.newline()); + pageA.add(Component.text("COLORS").color(NamedTextColor.DARK_RED)); + pageA.add(Component.newline()); + pageA.add(Component.newline()); + pageA.add(Component.text("EVERYTHING").style(style -> { + style.decoration(TextDecoration.BOLD, true); + style.decoration(TextDecoration.ITALIC, true); + style.decoration(TextDecoration.UNDERLINED, true); + style.decoration(TextDecoration.STRIKETHROUGH, true); + style.decoration(TextDecoration.OBFUSCATED, true); + style.color(NamedTextColor.GREEN); + style.clickEvent(ClickEvent.runCommand("EVERYTHING")); + style.hoverEvent(HoverEvent.showText(Component.text("Test"))); + })); + + Component firstPage = pageA.stream().reduce(Component::append).orElseGet(Component::empty); + + meta.pages(firstPage.compact()); + meta.title(Component.text("Minestom Book")); + meta.author(Component.text("https://minestom.net/")); + meta.lore(Component.text("Minestom.")); + }); + + try { + NBTCompound nbt = (NBTCompound) parser.parse(); + ItemStack vanillaBook = ItemStack.fromNBT(Material.WRITTEN_BOOK, nbt); + + var pagesA = vanillaBook.meta(WrittenBookMeta.class).getPages(); + var pagesB = book.meta(WrittenBookMeta.class).getPages(); + + // Compact the components to ensure they are the same format. + pagesA = pagesA.stream().map(Component::compact).toList(); + pagesB = pagesB.stream().map(Component::compact).toList(); + + assertEquals(pagesA, pagesB, "written book output meta must equal original input meta"); + } catch (Throwable e) { + fail(e); + } + } + + // TODO: Compare to vanilla snbt. This depends on a modern serializer that defaults to legacy in some cases. +// @Test +// public void compareToVanilla() { +// String vanillaSNBT = """ +//{ +// pages:['[ +// "", +// {"text":"\\\\n\\\\ntest\\\\n\\\\n"}, +// {"text":"TESTSETSET","clickEvent":{"action":"run_command","value":"hi"}}, +// {"text":"\\\\n\\\\n"}, +// {"text":"COLORS","color":"dark_red"}, +// {"text":"\\\\n\\\\n","color":"reset"}, +// {"text":"EVERYTHING","bold":true,"italic":true,"strikethrough":true,"underlined":true,"obfuscated":true, +// "color":"green","clickEvent":{"action":"run_command","value":"EVERYTHING"}, +// "hoverEvent":{"action":"show_text","contents":"Test"}} +// ]'], +// title:"Minestom Book", +// author:"https://minestom.net/", +// display:{Lore:["Minestom."]} +//} +// """; +// +// ItemStack book = ItemStack.of(Material.WRITTEN_BOOK).withMeta(WrittenBookMeta.class, meta -> { +// List pageA = new ArrayList<>(); +// +// pageA.add(Component.newline()); +// pageA.add(Component.newline()); +// pageA.add(Component.text("test")); +// pageA.add(Component.newline()); +// pageA.add(Component.newline()); +// pageA.add(Component.text("TESTSETSET").clickEvent(ClickEvent.runCommand("hi"))); +// pageA.add(Component.newline()); +// pageA.add(Component.newline()); +// pageA.add(Component.text("COLORS").color(NamedTextColor.DARK_RED)); +// pageA.add(Component.newline()); +// pageA.add(Component.newline()); +// pageA.add(Component.text("EVERYTHING").style(style -> { +// style.decoration(TextDecoration.BOLD, true); +// style.decoration(TextDecoration.ITALIC, true); +// style.decoration(TextDecoration.UNDERLINED, true); +// style.decoration(TextDecoration.STRIKETHROUGH, true); +// style.decoration(TextDecoration.OBFUSCATED, true); +// style.color(NamedTextColor.GREEN); +// style.clickEvent(ClickEvent.runCommand("EVERYTHING")); +// style.hoverEvent(HoverEvent.showText(Component.text("Test"))); +// })); +// +// Component firstPage = pageA.stream().reduce(Component::append).orElseGet(() -> Component.empty()); +// +// meta.pages(firstPage.compact()); +// meta.title(Component.text("Minestom Book")); +// meta.author(Component.text("https://minestom.net/")); +// meta.lore(Component.text("Minestom.")); +// }); +// +// TestUtils.assertEqualsSNBT(vanillaSNBT, (NBTCompound) book.toItemNBT().get("tag")); +// } +}