diff --git a/docs/superpowers/plans/2026-06-14-minimessage-config.md b/docs/superpowers/plans/2026-06-14-minimessage-config.md new file mode 100644 index 0000000..6eabea9 --- /dev/null +++ b/docs/superpowers/plans/2026-06-14-minimessage-config.md @@ -0,0 +1,217 @@ +# MiniMessage Config Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Let player-visible configuration text support MiniMessage while keeping existing legacy `&` and `#RRGGBB` formatting compatible. + +**Architecture:** Keep `ColorUtil` as the single text-formatting boundary. Add Adventure text serializers there, return legacy `String` values to preserve current command, quest, reward, and API signatures. + +**Tech Stack:** Java 21, Maven, Paper API Adventure classes, JUnit 5 for utility tests. + +--- + +## File Structure + +- Modify `pom.xml`: add test dependencies for JUnit 5 and Surefire so Maven can run unit tests. +- Create `src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java`: focused tests for legacy compatibility and MiniMessage parsing. +- Modify `src/main/java/com/io/yaohun/questengine/util/ColorUtil.java`: implement MiniMessage parsing while retaining legacy output. + +### Task 1: Add ColorUtil Regression Tests + +**Files:** +- Modify: `pom.xml` +- Create: `src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java` + +- [ ] **Step 1: Add JUnit test dependencies** + +Add these dependencies under `` in `pom.xml`: + +```xml + + org.junit.jupiter + junit-jupiter + 5.10.3 + test + +``` + +Add this plugin under `` in `pom.xml`: + +```xml + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + +``` + +- [ ] **Step 2: Write failing tests** + +Create `src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java`: + +```java +package com.io.yaohun.questengine.util; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ColorUtilTest { + + @Test + void colorKeepsLegacyAmpersandFormatting() { + assertEquals("§a任务 §f完成", ColorUtil.color("&a任务 &f完成")); + } + + @Test + void colorKeepsLegacyHexFormatting() { + assertEquals("§x§1§2§3§4§5§6任务", ColorUtil.color("#123456任务")); + } + + @Test + void colorSupportsMiniMessageNamedColors() { + assertEquals("§a任务", ColorUtil.color("任务")); + } + + @Test + void colorSupportsMiniMessageDecorations() { + assertEquals("§l加粗", ColorUtil.color("加粗")); + } + + @Test + void colorParsesEveryListEntry() { + assertEquals( + List.of("§a领取", "§c完成"), + ColorUtil.color(List.of("领取", "&c完成")) + ); + } +} +``` + +- [ ] **Step 3: Run tests to verify RED** + +Run: + +```bash +mvn -q -Dtest=ColorUtilTest test +``` + +Expected: tests compile, legacy tests pass, MiniMessage tests fail because tags are not parsed yet. + +### Task 2: Implement MiniMessage Support + +**Files:** +- Modify: `src/main/java/com/io/yaohun/questengine/util/ColorUtil.java` +- Test: `src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java` + +- [ ] **Step 1: Replace ColorUtil implementation** + +Update `ColorUtil.java` to: + +```java +package com.io.yaohun.questengine.util; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.ChatColor; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class ColorUtil { + + private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}"); + private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); + private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection(); + + public static String color(String text) { + if (text == null) { + return ""; + } + String legacyText = translateLegacyColors(text); + Component component = MINI_MESSAGE.deserialize(legacyText); + return LEGACY_SERIALIZER.serialize(component); + } + + public static List color(List list) { + return list.stream().map(ColorUtil::color).collect(Collectors.toList()); + } + + private static String translateLegacyColors(String text) { + Matcher matcher = HEX_PATTERN.matcher(text); + while (matcher.find()) { + String hexCode = text.substring(matcher.start(), matcher.end()); + String replaceSharp = hexCode.replace('#', 'x'); + char[] ch = replaceSharp.toCharArray(); + StringBuilder builder = new StringBuilder(); + for (char c : ch) { + builder.append("&").append(c); + } + text = text.replace(hexCode, builder.toString()); + matcher = HEX_PATTERN.matcher(text); + } + + return ChatColor.translateAlternateColorCodes('&', text); + } +} +``` + +- [ ] **Step 2: Run focused tests to verify GREEN** + +Run: + +```bash +mvn -q -Dtest=ColorUtilTest test +``` + +Expected: all `ColorUtilTest` tests pass. + +### Task 3: Verify Plugin Build + +**Files:** +- Verify: full project + +- [ ] **Step 1: Run full test suite** + +Run: + +```bash +mvn test +``` + +Expected: build succeeds and `ColorUtilTest` passes. + +- [ ] **Step 2: Run compile** + +Run: + +```bash +mvn compile +``` + +Expected: production code compiles. + +- [ ] **Step 3: Review changed files** + +Run: + +```bash +git diff --check +git status --short +``` + +Expected: no whitespace errors; only planned files are modified or added. + +- [ ] **Step 4: Commit implementation** + +Run: + +```bash +git add pom.xml src/main/java/com/io/yaohun/questengine/util/ColorUtil.java src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java docs/superpowers/plans/2026-06-14-minimessage-config.md +git commit -m "feat: 支持配置 MiniMessage 文本" +``` diff --git a/pom.xml b/pom.xml index dfa04c0..cf0afc9 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,12 @@ 3.6.52 provided + + org.junit.jupiter + junit-jupiter + 5.10.3 + test + @@ -124,6 +130,11 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + diff --git a/src/main/java/com/io/yaohun/questengine/util/ColorUtil.java b/src/main/java/com/io/yaohun/questengine/util/ColorUtil.java index 7ad7216..6ab503d 100644 --- a/src/main/java/com/io/yaohun/questengine/util/ColorUtil.java +++ b/src/main/java/com/io/yaohun/questengine/util/ColorUtil.java @@ -1,5 +1,8 @@ package com.io.yaohun.questengine.util; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.ChatColor; import java.util.List; @@ -10,11 +13,28 @@ import java.util.stream.Collectors; public class ColorUtil { private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}"); + private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); + private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection(); public static String color(String text) { if (text == null) { return ""; } + if (text.indexOf('§') >= 0) { + Component component = LEGACY_SERIALIZER.deserialize(text); + return LEGACY_SERIALIZER.serialize(component); + } + String hexText = translateHexColors(text); + Component component = MINI_MESSAGE.deserialize(hexText); + String serialized = LEGACY_SERIALIZER.serialize(component); + return ChatColor.translateAlternateColorCodes('&', serialized); + } + + public static List color(List list) { + return list.stream().map(ColorUtil::color).collect(Collectors.toList()); + } + + private static String translateHexColors(String text) { Matcher matcher = HEX_PATTERN.matcher(text); while (matcher.find()) { String hexCode = text.substring(matcher.start(), matcher.end()); @@ -28,10 +48,6 @@ public class ColorUtil { matcher = HEX_PATTERN.matcher(text); } - return ChatColor.translateAlternateColorCodes('&', text); + return text; } - - public static List color(List list) { - return list.stream().map(ColorUtil::color).collect(Collectors.toList()); - } -} \ No newline at end of file +} diff --git a/src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java b/src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java new file mode 100644 index 0000000..8ceb83c --- /dev/null +++ b/src/test/java/com/io/yaohun/questengine/util/ColorUtilTest.java @@ -0,0 +1,53 @@ +package com.io.yaohun.questengine.util; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ColorUtilTest { + + @Test + void colorKeepsLegacyAmpersandFormatting() { + assertEquals("§a任务 §f完成", ColorUtil.color("&a任务 &f完成")); + } + + @Test + void colorKeepsLegacyHexFormatting() { + assertEquals("§x§1§2§3§4§5§6任务", ColorUtil.color("#123456任务")); + } + + @Test + void colorSupportsMiniMessageNamedColors() { + assertEquals("§a任务", ColorUtil.color("任务")); + } + + @Test + void colorSupportsMiniMessageDecorations() { + assertEquals("§l加粗", ColorUtil.color("加粗")); + } + + @Test + void colorSupportsMixedMiniMessageAndLegacyFormatting() { + assertEquals("§a领取§r §c奖励", ColorUtil.color("领取 &c奖励")); + } + + @Test + void colorCanBeCalledOnAlreadyColoredText() { + assertEquals("§a任务", ColorUtil.color(ColorUtil.color("任务"))); + } + + @Test + void colorLeavesUnknownMiniMessageTagsVisible() { + assertEquals("任务", ColorUtil.color("任务")); + } + + @Test + void colorParsesEveryListEntry() { + assertEquals( + List.of("§a领取", "§c完成"), + ColorUtil.color(List.of("领取", "&c完成")) + ); + } +}