结构模板与导出
基本信息
| 属性 | 值 |
|---|---|
| 包路径 | com.gtnewhorizons.horizonqa.structure(10 个类) |
| 加载器 | structure/HybridStructureLoader.java |
| 模板模型 | structure/HybridStructureTemplate.java |
| 放置器 | structure/StructurePlacer.java |
| 导出器 | structure/StructureExporter.java |
| 标签解析 | structure/StructureAnnotations.java |
| 标签名校验 | structure/RegistryStringResolver.java |
| 相关指令 | /horizonqa load / /horizonqa export |
| 模板路径规则 | [A-Za-z0-9_.-]+(/[A-Za-z0-9_.-]+)*+(StructureExporter.java:35) |
功能
Horizon-QA 的结构系统用混合格式(hybrid)模板:方块布局走 JSON,TileEntity 与实体走 NBT。
玩家用 Horizon Wand 框选 → 编辑 → 导出,得到可被 @GameTest(template = "...") 引用的模板。
加载
HybridStructureLoader 提供两个入口(HybridStructureLoader.java:43-56):
| 方法 | 用途 | 数字 ItemStack ID |
|---|---|---|
load(name) |
测试执行路径 | 一律拒绝 |
loadForEditing(name) |
交互式编辑路径 | 仅在 interactive 模式且 -Dhorizonqa.allowLegacyNumericItemIds=true 时放行 |
拒绝的理由写在 javadoc 里:数字 ItemStack ID 的含义依赖当前注册表分配结果,跨环境不可靠
(HybridStructureLoader.java:32-35)。测试 runner 必须走 load,保证执行始终严格。
放置
/horizonqa load <namespace:path> [rotation 0-3](HorizonQACommand.java:667-764):
- 校验参数与旋转值(0–3 顺时针四分之一圈)
- 要求玩家在服务端且持有(或背包内有)Wand
- 从 Wand 当前瞄准位置取放置原点(
ItemHorizonWand.getTargetedPosition) HybridStructureLoader.loadForEditing加载模板StructurePlacer.placeStrict(..., GTNHGameTestHelper::rotateStructureTileNbt)放置, TileEntity 的 NBT 旋转交给 GT 适配器处理- 自动把 Wand 选区设为该模板的包围盒(
armWandForTemplate,HorizonQACommand.java:890-922) - 同步模板自带的坐标标签(按旋转换算世界坐标)
加载失败时分别捕获 IOException 与 RuntimeException 并回报到聊天与日志
(HorizonQACommand.java:757-763)。
导出
/horizonqa export [name](HorizonQACommand.java:766-864):
- 必须由玩家执行,且必须持有 Wand(
HorizonQACommand.java:767-780) - 必须 Pos1 与 Pos2 都已设置,否则拒绝并提示先左键 Pos1、右键 Pos2
(
HorizonQACommand.java:783-790) - 输出目录为服务器目录下的
horizonqastructures(HorizonQACommand.java:821-822) - 未给出名字时使用 Wand NBT 中记住的
exportName(HorizonQACommand.java:792、940-951) - 名字必须通过
StructureExporter.isValidTemplatePath校验
导出产物(HorizonQACommand.java:828-859 的成功提示):
| 产物 | 说明 |
|---|---|
<name>.json |
方块布局 |
<name>.json 内的标签段 |
坐标标签(数量为 result.labelCount()) |
<name><extension> |
TileEntity 与实体数据,含 tileEntityCount 与 entityCount |
ExportResult 记录 4 个字段:tileEntityCount、entityCount、structureDataExtension、
labelCount(StructureExporter.java:41-52)。
实体导出捕获
导出时需要知道哪些 ItemStack 真正被序列化。MixinItemStack 在 ItemStack.writeToNBT 的
RETURN 处注入,调用 ItemStackExportCapture.markSerializedStack(...)
(mixin/MixinItemStack.java:18-21)。
标签
StructureAnnotations 负责模板内标签的定义与解析;
ItemHorizonWand.isValidLabelName 直接委托给它(ItemHorizonWand.java:226-228),
规则为 [A-Za-z_][A-Za-z0-9_]*(en_US.lang:31)。
测试侧用 helper.pos("名字") 解析,失败抛 LabelResolutionException。
数值
| 数值名 | 值 | 来源 |
|---|---|---|
structure 包类数 |
10 | ls src/main/java/com/gtnewhorizons/horizonqa/structure/ |
| 模板路径正则 | [A-Za-z0-9_.-]+(/[A-Za-z0-9_.-]+)*+ |
StructureExporter.java:35 |
| 旋转档位数 | 4(0 / 1 / 2 / 3) | HorizonQACommand.java:881 |
| 命名空间正则 | [A-Za-z0-9_.-]+ |
HorizonQACommand.java:934 |
| 导出目录名 | horizonqastructures |
HorizonQACommand.java:822 |
| 格式版本号 | 2 | HybridStructureTemplate.java:9(CURRENT_FORMAT_VERSION = 2) |
| 实体 ID 序列字符集 | A-Za-z0-9(62 字符) |
StructureExporter.java:34 |
| 旋转档位含义 | 0 无 / 1 90° / 2 180° / 3 270°(均顺时针,Y 轴) | api/annotation/GameTest.java:23-27 |
交互
| 指令 | 前置条件 | 结果 |
|---|---|---|
/horizonqa load <ns:path> [rot] |
interactive 模式、玩家、服务端、持有 Wand、无批次执行中 | 放置模板并把 Wand 选区对齐到模板包围盒 |
/horizonqa export [name] |
玩家、持有 Wand、Pos1 与 Pos2 均已设置 | 向 horizonqastructures/ 写出 JSON + NBT |
/horizonqa clear |
玩家、持有 Wand | 清空 Wand 全部 NBT(HorizonQACommand.java:983-999) |
/horizonqa label <name> |
玩家、持有 Wand | 给当前瞄准坐标打标签(HorizonQACommand.java:1001-1018) |
/horizonqa labels list/remove/clear |
玩家、持有 Wand | 列出 / 删除 / 清空标签(HorizonQACommand.java:1020-1096) |
load 成功后会提示下一步:编辑后执行 /qa export 写入导出的名字(HorizonQACommand.java:744-756)。
相关条目
- Horizon Wand - 选区与标签的数据载体
- /horizonqa 指令 - 全部 13 个子指令
- Horizon Editor - 放置后的可视化编辑面板
- 测试注解与发现 -
template()与rotation()引用此处导出的模板