藿香

藿香

Minecraft NeoForge 指南书模组

基础库

Ageratum

Ageratum Logo

License Asset License

一个面向 Minecraft NeoForge 的指南书模组,旨在为其他模组提供游戏内指南。Ageratum 提供丰富的 Markdown 渲染、i18n 本地化,以及可扩展的自定义语法/组件系统。

文档

特性

核心 Markdown 支持

✅ 块级元素

  • ATX 标题(# ~ ######)和 Setext 标题(下划线风格)
  • 段落和换行
  • 有序、无序和任务列表(多级嵌套)
  • 引用块(多级嵌套)
  • 围栏代码块(反引号和波浪号)以及缩进代码块
  • 分隔线
  • 支持对齐设置的表格
  • 图片(命名空间本地引用)

✅ 行内元素

  • 粗体、斜体、删除线
  • 行内代码片段(支持多个反引号)
  • 链接 和自动链接
  • 转义字符支持
  • 自定义颜色标签
  • 悬停和点击事件(<hover> 和 <click> 标签)

✅ 高级特性

  • 引用链接定义和引用链接语法
  • 自动链接扩展
  • 代码块行号
  • 表格列对齐(左/中/右)

国际化(i18n)

  • 文档按 ageratum/<语言代码>/ 组织(例如 en_us、zh_cn)
  • 如果缺少本地化版本,默认回退到 en_us
  • 完全支持多字节字符(中文、日文等)

扩展语法

用于自定义组件的两种块级扩展语法:

1. 冒号语法

::: info
这是一个信息框。
:::

::: tip
这是一个提示。
:::

::: warning
这是一个警告。
:::

::: danger
这是一个危险警告。
:::

2. 标签语法

<namespace:component key="value" param=123>
块内容支持 Markdown 语法。
</namespace:component>

<namespace:component/>
无内容的自闭合形式。

命名空间可以省略(默认为 ageratum:)。

内置扩展组件

  • ageratum:info - 蓝色信息框
  • ageratum:tip - 绿色提示框
  • ageratum:warning - 橙色警告框
  • ageratum:danger - 红色危险框

悬停和点击事件

支持交互式文本,允许玩家悬停查看工具提示或点击执行操作。

悬停事件(<hover>)

悬停在文本上时显示提示文本:

<hover type="SHOW_TEXT" data="这是工具提示">悬停在我上面</hover>

支持的类型:

  • SHOW_TEXT - 显示纯文本工具提示(data 为工具提示内容)

点击事件(<click>)

点击文本时执行操作:

<click type="OPEN_URL" data="https://example.com">点击打开链接</click>
<click type="COPY_TO_CLIPBOARD" data="要复制的文本">点击复制</click>
<click type="SUGGEST_COMMAND" data="/say hello">点击建议命令</click>

支持的类型:

  • OPEN_URL - 打开 URL(data 为完整 URL)
  • COPY_TO_CLIPBOARD - 复制文本到剪贴板(data 为要复制的文本)
  • SUGGEST_COMMAND - 在聊天中建议命令(data 为命令文本)

组合样式

你可以在同一段文本中组合多种样式:

<hover type="SHOW_TEXT" data="这是工具提示"><click type="OPEN_URL" data="https://example.com">点击并悬停在我上面!</click></hover>

配方组件(<recipe/>)

使用 recipe 扩展可直接在 Markdown 文档中渲染配方:

<recipe id="minecraft:acacia_boat"/>
  • id:必填,目标配方 ResourceLocation
  • 内置支持:RecipeType.CRAFTING(工作台配方)
  • 渲染行为:每个输入槽位显示其 Ingredient 的第一个候选物品
  • 回退行为:如果客户端关卡不可用、配方缺失或没有匹配的工厂,组件将渲染为无可见高度

你可以通过 AgeratumRegistries.RECIPE_COMPONENT_FACTORIES 注册额外的配方组件工厂:

public static final DeferredHolder<MDRecipeComponent.RecipeComponentFactory<?>, MDRecipeComponent.RecipeComponentFactory<?>> SMELTING =
    AgeratumRegistries.RECIPE_COMPONENT_FACTORIES.register(
        "smelting",
        () -> MDRecipeComponent.RecipeComponentFactory.create(RecipeType.SMELTING, MDSmeltingRecipeComponent::new)
    );

结构 NBT 组件(<structure/>)

使用 structure 扩展可直接在文档中为 .nbt 结构文件渲染摘要、俯视方块预览和有界 NBT 树:

<structure id="minecraft:village/plains/houses/plains_small_house_1"/>

<structure id="./test.nbt"/>
  • id / path:必填,目标结构文件 ResourceLocation
  • maxDepth:可选,最大展开深度,默认 2
  • maxEntries:可选,每层显示的键/列表条目最大数量,默认 12
  • 支持相对路径,会相对于当前文档目录解析,包括放置在资源包中 Markdown 文档旁的 .nbt 文件;在预览模式下会从 run/ageratum_review/ 加载匹配的文件
  • 该组件首先显示结构元数据,如尺寸、调色板、方块数量和实体数量,然后渲染俯视方块预览,随后显示深度受限的 NBT 树
  • 悬停预览中的方块会显示其方块 ID、结构坐标、调色板索引,以及是否携带方块实体 NBT

预加载与缓存

  • 在资源加载时自动扫描并预解析 Markdown 文档为 MDComponent 列表
  • 立即打开缓存的组件,无解析延迟
  • 资源重载时自动刷新缓存

跨端打开指南

// Client: open directly
Ageratum.openGuide(ResourceLocation location);

// Server: notify client via network packet
Ageratum.

openGuide(ResourceLocation location);

项目结构

目录布局

src/main/java/dev/anvilcraft/resource/ageratum/
├── Ageratum.java                           // Main mod class + command registration
├── GuideDocumentLoader.java                // Document loading utils
├── GuideDocumentCache.java                 // Preload cache & reload listener
│
├── client/
│   ├── AgeratumClient.java                 // Client hooks (reserved)
│   ├── gui/
│   │   └── GuideScreen.java                // Guide reading GUI
│   └── feat/markdown/
│       ├── MarkdownParser.java             // Markdown block-level parser
│       ├── BuiltinExtensionComponents.java // Built-in extension registration
│       ├── BlockExtensionState.java        // Block extension state machine
│       ├── SelfClosingBlockExtensionState.java
│       ├── ExtensionParamParser.java       // Parameter parsing utility
│       ├── MDExtensionContext.java         // Extension execution context
│       ├── MDExtensionComponentFactory.java // Extension factory interface
│       └── component/
│           ├── MDComponent.java            // Base class + inline parsing
│           ├── MDTextComponent.java        // Plain text paragraphs
│           ├── MDHeaderComponent.java      // Headings
│           ├── MDCodeBlockComponent.java   // Code blocks
│           ├── MDListComponent.java        // Lists (inc. task lists)
│           ├── MDQuoteComponent.java       // Blockquotes
│           ├── MDTableComponent.java       // Tables
│           ├── MDImageComponent.java       // Images
│           ├── MDHorizontalRuleComponent.java
│           └── MDNoticeBoxComponent.java   // Notice box container
│
└── network/
    ├── AgeratumNetwork.java                // Network registration & dispatch
    └── OpenGuidePayload.java               // Guide open network packet

设计原则

  • 关注点分离:每个类只负责单一职责
  • 无超大类:最长文件约 400 行,所有内部类均已提取
  • 全面文档:所有公共 API 均有中文 Javadoc,复杂逻辑有内联注释
  • 可扩展性:通过 registerExtensionComponent() 注册自定义块类型

使用指南

玩家

使用客户端命令打开指南:

/ageratum <namespace> [file]

Examples:
/ageratum ageratum                  # Opens ageratum:en_us/index.md
/ageratum mymod guide              # Opens mymod:en_us/guide.md
/ageratum mymod zh_cn/tutorial     # Opens mymod:zh_cn/tutorial.md

支持命名空间和文件名的 Tab 补全。

开发者

注册自定义扩展

使用 NeoForge 文档中描述的注册方法:

  1. DeferredRegister(推荐)
  2. RegisterEvent(高级用法)
public static final DeferredRegister<MDExtensionComponentFactory> EXT_COMPONENT_FACTORIES =
    AgeratumRegistries.createExtensionComponentFactoryRegister("your_modid");

public static final DeferredHolder<MDExtensionComponentFactory, MDExtensionComponentFactory> CUSTOM =
    EXT_COMPONENT_FACTORIES.register(
        "custom", () ->
            context -> new MyComponent(context.renderedContent(), context.params())
    );

// In your mod constructor
EXT_COMPONENT_FACTORIES.

register(modEventBus);

注册自定义行内样式解析器

行内样式解析器通过 INLINE_STYLE_PARSER_REGISTRY_KEY 注册。MDComponent 查询该注册表,并按位置 + 解析器优先级解析匹配项。

package com.example.mymod.client.markdown;

import dev.anvilcraft.resource.ageratum.client.feat.markdown.component.MDInlineStyleParser;
import dev.anvilcraft.resource.ageratum.client.registries.AgeratumRegistries;
import net.minecraft.network.chat.Style;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;

import java.util.regex.Pattern;

public final class MyInlineStyleParsers {
    // Use your own modid here, not ageratum
    public static final DeferredRegister<MDInlineStyleParser> INLINE_STYLE_PARSERS = DeferredRegister.create(
        AgeratumRegistries.INLINE_STYLE_PARSER_REGISTRY_KEY,
        "mymod"
    );

    // Example tag: <rainbow>text</rainbow>
    public static final DeferredHolder<MDInlineStyleParser, MDInlineStyleParser> RAINBOW =
        INLINE_STYLE_PARSERS.register(
            "rainbow",
            () -> MDInlineStyleParser.create(
                100, // smaller value = higher precedence at same position
                Pattern.compile("<rainbow>"),
                "</rainbow>",
                (Style parentStyle, java.util.regex.Matcher matcher) -> parentStyle.withColor(0xFF55FF)
            )
        );

    private MyInlineStyleParsers() {
    }
}

在你的客户端初始化中注册:

public class MyModClient {
    public MyModClient(IEventBus modEventBus) {
        MyInlineStyleParsers.INLINE_STYLE_PARSERS.register(modEventBus);
    }
}

Markdown 用法:

normal text <rainbow>colored text</rainbow> normal text

查看完整指南:docs/inline-style-parser-example.en.md。

添加文档

在资源包中创建:

assets/<namespace>/ageratum/<language>/index.md
assets/<namespace>/ageratum/en_us/index.md
assets/<namespace>/ageratum/zh_cn/index.md

许可证

  • 代码除非另有说明,默认遵循我们的 LICENSE 文件(LGPL-3.0)
  • 非代码资产(位于此处)遵循我们的 ASSET_LICENSE 文件(ARR)