结构模板与导出

基本信息

属性 值
包路径 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):

  1. 校验参数与旋转值(0–3 顺时针四分之一圈)
  2. 要求玩家在服务端且持有(或背包内有)Wand
  3. 从 Wand 当前瞄准位置取放置原点(ItemHorizonWand.getTargetedPosition)
  4. HybridStructureLoader.loadForEditing 加载模板
  5. StructurePlacer.placeStrict(..., GTNHGameTestHelper::rotateStructureTileNbt) 放置, TileEntity 的 NBT 旋转交给 GT 适配器处理
  6. 自动把 Wand 选区设为该模板的包围盒(armWandForTemplate,HorizonQACommand.java:890-922)
  7. 同步模板自带的坐标标签(按旋转换算世界坐标)

加载失败时分别捕获 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)。

相关条目