多方块构造机制(Constructable)
基本信息
| 属性 | 值 |
|---|---|
| 交互入口 | com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility |
| 核心接口 | IConstructable、ISurvivalConstructable |
| 结构定义 | com.gtnewhorizon.structurelib.structure.IStructureDefinition / StructureDefinition |
本 mod 自身不包含任何多方块结构。它是一套供其他 mod 使用的构造框架:结构由各 mod 用 IStructureDefinition 描述,本 mod 提供遍历、检查、提示、放置的全部算法。因此本分类下没有 multiblock/ 目录。
参与者接口
| 接口 | 作用 |
|---|---|
IConstructable |
标记一个方块实体(或其委托)可被构造触发器驱动 |
IConstructableProvider |
由方块实体提供另一个 IConstructable 实例 |
ISurvivalConstructable |
扩展 IConstructable,额外允许生存模式自动放置 |
IMultiblockInfoContainer |
把"不认识 IConstructable 的方块实体类"转换出构造能力 |
目标解析顺序
ConstructableUtility.handle0 在目标位置取到方块实体后,按顺序尝试解析构造目标:
- 方块实体实现
IConstructableProvider→getConstructable() - 方块实体直接实现
IConstructable→ 直接使用 - 该方块实体类已通过
IMultiblockInfoContainer.registerTileClass登记 → 按点击面朝向转换出IConstructable
都不满足则不触发任何逻辑。
另外,目标位置没有方块实体、或操作者是 FakePlayer 时,函数直接返回 操作者 instanceof EntityPlayerMP 而不继续。
提示与放置的分流
解析成功后,是否潜行决定走哪条路径:
| 玩家状态 | 路径 | 说明 |
|---|---|---|
| 不潜行 | 客户端生成全息提示 | 服务端直接返回 true,不做任何检查 |
| 潜行 | 服务端生存自动放置 | 走限流校验后调用 survivalConstruct |
客户端侧还有一道校验:非服务端玩家必须满足 StructureLib.isCurrentPlayer(aPlayer),否则返回 false。
生存自动放置
潜行放置时服务端先做时间限流:
timePast = System.currentTimeMillis() - 上次使用时刻
若 timePast < autoPlaceInterval → 拒绝并提示剩余毫秒数
限流通过后调用 ISurvivalConstructable.survivalConstruct(stackSize, elementBudget, env),其中 elementBudget 取自 autoPlaceBudget 配置。
survivalConstruct 有多个重载:既可以传旧的 IItemSource + EntityPlayerMP 组合(已废弃),也可以传新的 ISurvivalBuildEnvironment。客户端玩家走后者时会触发一段"hacky migration code"(可由系统属性 structurelib.disable_isc_migration_code 关闭),把玩家临时塞进 GlobalStates 线程局部变量再委托给废弃重载。
返回值约定:
| 返回值 | 含义 |
|---|---|
-1 |
结构已建造完成 |
-2 |
不支持(例如环境 API 不兼容) |
>= 0 |
本轮实际放置的元素数量 |
结构定义
IStructureDefinition<T> 用字符串数组描述结构布局,配合 IStructureElement<T> 描述每个位置的语义。构建方式为 builder 模式,主要方法两个:
addShape(String name, String[][] structurePiece):以二维字符串数组描述布局,可用StructureUtility.transpose()转置以改善源码可读性addElement(Character name, IStructureElement<T>):把字符标识绑定到结构元素
IStructureDefinition 提供一组默认方法覆盖不同用途:check(检查)、hints(生成提示,仅客户端)、build(创造模式尽力放置)、buildOrHints(二者择一)、survivalBuild(生存建造)、iterate(低层遍历)。
坐标系:A / B / C
源码文档给出的定义是:站在控制器前方,控制器正面朝向你,未旋转也未翻转时的相对位置。
| 偏移 | 含义 |
|---|---|
A |
控制器左侧的方块数(不含控制器自身) |
B |
控制器上方的方块数(不含控制器自身) |
C |
你与控制器之间的方块数(不含控制器自身) |
放置结果 PlaceResult
IStructureElement.PlaceResult 定义了单个元素在一轮放置中的结果,遍历器据此决定后续行为:
| 枚举值 | 含义 | 遍历器行为 |
|---|---|---|
SKIP |
检查通过,位置已正确 | 继续迭代(不报错,不计入已放置) |
REJECT |
该元素无法处理此位置(被占用、缺资源等) | 停止迭代并显示错误粒子 |
REJECT_CONTINUE |
未实现 survivalPlaceBlock 或无法放置,但非错误 |
静默继续;链式结构中尝试下一个候选 |
STOP |
本 tick 无法继续 | 停止本轮,等待下一轮 |
ACCEPT |
放置成功 | 继续迭代 |
ACCEPT_STOP |
放置成功但需等待下一轮 | 停止本轮 |
缓存建议
IStructureDefinition 的类文档建议缓存实例:每个多方块只构造一次,可用 ClassValue<IStructureDefinition<?>>,或用 StructureUtility.defer(Function) 求值。源码注释指出 defer 写法略慢,因为每次结构检查都会分配新对象,而 ClassValue 只分配一次。
相关条目:Constructable Trigger、提示渲染系统、配置、通道系统。