跳到主要内容

多方块结构

6.3.0 新增

多方块结构模块在 TabooLib 6.3.0 中新增。

多方块结构模块提供在 Minecraft 世界中定义、验证和匹配多方块结构的能力。参考 Patchouli 的 Multiblock 设计,适配 TabooLib/Bukkit 生态。

模块提供三项核心能力:

  • 结构定义 — 用图案或坐标描述结构由哪些方块组成
  • 结构验证 — 检测世界中某个位置是否存在该结构(支持自动旋转检测)
  • 结构模拟 — 计算结构中每个方块的世界坐标(用于预览、进度提示等)

两种定义方式

类型适用场景定义方式
DenseMultiblock紧凑结构、方块密度高字符图案(String[][]),类似合成配方
SparseMultiblock大型稀疏结构、不规则形状坐标映射(Map<BlockPos, IStateMatcher>

两种方式在功能上完全等价——都支持验证、模拟和旋转检测。选择哪种取决于结构的形状特征。

密集型(DenseMultiblock)

使用二维字符串数组定义结构,类似 Minecraft 合成配方的格式。每个字符代表一个方块,通过 mapping 映射到具体的方块匹配器。

基础用法

val altar = DenseMultiblock(
pattern = arrayOf(
// 第一个数组 = 最顶层(最高 Y)
arrayOf(
" ",
" S ",
" "
),
// 最后一个数组 = 最底层(最低 Y)
arrayOf(
"SSS",
"S0S", // '0' = 中心锚点
"SSS"
)
),
mapping = mapOf(
'S' to StringStateMatcher.parse("minecraft:stone_bricks")
)
)

图案坐标系

pattern = arrayOf(           ← 外层数组:Y 层
arrayOf( ← 中层数组:Z 行(北→南)
"ABCDE", ← 字符串字符:X 列(西→东)
"FGHIJ",
"KLMNO"
),
...
)
维度索引方向说明
Y 层pattern[0] = 最顶层,pattern[N] = 最底层从上到下排列
Z 行layer[0] = Z=0(北),layer[N] = 最大 Z(南)从北到南排列
X 列row[0] = X=0(西),row[N] = 最大 X(东)从西到东排列

俯视图(单层):

         X(西→东)
0 1 2
Z 0 [ A B C ] ← 北
( 1 [ D E F ]
北 2 [ G H I ] ← 南

南)

特殊字符

字符含义默认匹配器可否覆盖
0中心锚点(必须恰好出现一次)StateMatcher.AIR可在 mapping 中覆盖
_任意方块(不参与验证)StateMatcher.ANY可覆盖
(空格)空气StateMatcher.AIR可覆盖
重要规则
  • 图案中必须恰好包含一个 0 字符作为锚点
  • 图案中出现的所有其他字符(除 0_、空格外)必须在 mapping 中定义
  • 特殊字符的默认行为可以在 mapping 中覆盖

大型多方块示例

// 一个 7x5x7 的穹顶结构
val dome = DenseMultiblock(
pattern = arrayOf(
arrayOf(
"GGGGGGG",
"GGG GGG",
"GG GG",
"G G",
"GG GG",
"GGG GGG",
"GGGGGGG"
),
arrayOf(
"GGG GGG",
"GG GG",
"G G",
" ",
"G G",
"GG GG",
"GGG GGG"
),
arrayOf(
"GG GG",
"G G",
" ",
" ",
" ",
"G G",
"GG GG"
),
arrayOf(
"GGG GGG",
"GG GG",
"G G",
" C ",
"G G",
"GG GG",
"GGG GGG"
),
arrayOf(
"RRRSRRR",
"RRSSSRR",
"RSSSSSR",
"SSS0SSS",
"RSSSSSR",
"RRSSSRR",
"RRRSRRR"
)
),
mapping = mapOf(
' ' to StateMatcher.ANY,
'0' to StringStateMatcher.parse("minecraft:lapis_block"),
'G' to StringStateMatcher.parse("minecraft:purple_stained_glass"),
'R' to StringStateMatcher.parse("minecraft:stone"),
'S' to StringStateMatcher.parse("minecraft:sponge"),
'C' to StringStateMatcher.parse("minecraft:chest"),
)
).apply { symmetrical = true }

带方块属性的朝向结构

方块属性(如楼梯的 facing)在旋转检测中非常重要:

val stairPlatform = DenseMultiblock(
pattern = arrayOf(
arrayOf(
" WWW ",
"N S",
"N 0 S",
"N S",
" EEE "
)
),
mapping = mapOf(
'N' to StringStateMatcher.parse("minecraft:oak_stairs[facing=south]"),
'S' to StringStateMatcher.parse("minecraft:oak_stairs[facing=north]"),
'W' to StringStateMatcher.parse("minecraft:oak_stairs[facing=east]"),
'E' to StringStateMatcher.parse("minecraft:oak_stairs[facing=west]"),
)
)

包含流体的结构

流体方块和含水方块同样可以作为匹配条件:

val fluidStructure = DenseMultiblock(
pattern = arrayOf(
arrayOf(
"GG GG",
" LLLLLG",
"GGGGGGG",
" WW0WWG",
"GGGGGGG",
" SSSSSG",
"GG GG"
)
),
mapping = mapOf(
' ' to StateMatcher.ANY,
'0' to StringStateMatcher.parse("minecraft:lapis_block"),
'G' to StringStateMatcher.parse("minecraft:bricks"),
'W' to StringStateMatcher.parse("minecraft:water"),
'L' to StringStateMatcher.parse("minecraft:lava"),
'S' to StringStateMatcher.parse("minecraft:brick_slab[type=bottom,waterlogged=true]"),
)
)

稀疏型(SparseMultiblock)

使用坐标到匹配器的映射定义结构,只需列出有方块的位置。适合大型但方块稀疏的结构(如四角立柱、大型框架等)——不需要定义大量的空气/任意方块。

基础用法

val cross = SparseMultiblock(
blocks = mapOf(
BlockPos(0, 0, 0) to StringStateMatcher.parse("minecraft:diamond_block"),
BlockPos(1, 0, 0) to StringStateMatcher.parse("minecraft:iron_block"),
BlockPos(-1, 0, 0) to StringStateMatcher.parse("minecraft:iron_block"),
BlockPos(0, 0, 1) to StringStateMatcher.parse("minecraft:iron_block"),
BlockPos(0, 0, -1) to StringStateMatcher.parse("minecraft:iron_block"),
)
)

坐标说明

  • 所有位置相对于锚点 (0, 0, 0)
  • 锚点就是验证时传入的 anchor 位置
  • 未定义的位置不参与验证(等效于 ANY
  • 如果需要某个位置必须为空气,显式添加 BlockPos(...) to StateMatcher.AIR

大型稀疏结构示例

// 四角立柱(10x4x10 的空间中只有 4 根柱子 + 中心标记)
val fourPillars = SparseMultiblock(
blocks = buildMap {
val pillarBlock = StringStateMatcher.parse("minecraft:stone_bricks")
for (x in listOf(-5, 5)) {
for (z in listOf(-5, 5)) {
for (y in 0..2) {
put(BlockPos(x, y, z), pillarBlock)
}
}
}
val capBlock = StringStateMatcher.parse("minecraft:chiseled_stone_bricks")
for (x in listOf(-5, 5)) {
for (z in listOf(-5, 5)) {
put(BlockPos(x, 3, z), capBlock)
}
}
put(BlockPos(0, 0, 0), StringStateMatcher.parse("minecraft:diamond_block"))
}
)

如果用 DenseMultiblock 定义同样的结构,需要填写 11x4x11 = 484 个字符,其中绝大多数是空格。稀疏型只需定义 21 个有效位置。

密集型 vs 稀疏型对比

特性DenseMultiblockSparseMultiblock
定义方式字符图案 String[][]坐标映射 Map<BlockPos, IStateMatcher>
锚点'0' 字符位置默认 (0,0,0)
空气检测空格字符自动检测空气需显式指定 StateMatcher.AIR
未定义位置不存在"未定义"(图案覆盖整个包围盒)未定义 = 不检测(ANY)
适合场景紧凑、规则、方块密度高大型、稀疏、不规则
可读性图案直观可视坐标列表,适合程序生成

状态匹配器

状态匹配器(IStateMatcher)定义了"某个位置应该是什么方块"的规则。

内置匹配器

匹配器说明
StateMatcher.ANY匹配任意方块(包括空气),等效于不检测
StateMatcher.AIR仅匹配空气方块

工厂方法

// 匹配指定材质(忽略方块状态属性)
StateMatcher.fromMaterial(Material.STONE)

// 精确匹配方块数据(包括状态属性)
val data = Bukkit.createBlockData("minecraft:oak_stairs[facing=north,half=bottom]")
StateMatcher.fromBlockData(data)

// 自定义谓词
StateMatcher.fromPredicate("iron_or_gold") { block ->
block.type == Material.IRON_BLOCK || block.type == Material.GOLD_BLOCK
}

// 仅用于显示(始终返回 true)
StateMatcher.displayOnly("decorative")

字符串解析匹配器

StringStateMatcher.parse() 支持三种格式:

格式示例说明
材质名minecraft:stone匹配材质,忽略状态属性
方块数据minecraft:oak_stairs[facing=north,half=bottom]精确匹配,包含状态属性
方块标签#minecraft:wool匹配标签中的所有方块
StringStateMatcher.parse("minecraft:stone")
StringStateMatcher.parse("minecraft:oak_stairs[facing=north]")
StringStateMatcher.parse("#minecraft:planks") // 所有木板
StringStateMatcher.parse("#minecraft:logs") // 所有原木

自定义匹配器

实现 IStateMatcher 接口:

class LightLevelMatcher(private val minLight: Int) : IStateMatcher {
override val displayName = "light>=$minLight"
override fun test(block: Block): Boolean {
return block.lightLevel >= minLight
}
}

验证结构

自动旋转验证

validate(world, anchor) 对非对称结构自动尝试 4 个旋转方向,返回第一个匹配的旋转:

val anchor = BlockPos(block.x, block.y, block.z)

val rotation = multiblock.validate(world, anchor)
if (rotation != null) {
// 结构匹配!rotation 为匹配时的旋转方向
}

指定旋转验证

val matched = multiblock.validate(world, anchor, MultiblockRotation.NONE)

单方块测试

val blockOk = multiblock.test(world, anchor, 1, 0, 0, MultiblockRotation.NONE)

旋转方向

枚举值角度坐标变换 (x, y, z)
NONE(x, y, z)
CLOCKWISE_90顺时针 90°(-z, y, x)
CLOCKWISE_180180°(-x, y, -z)
COUNTERCLOCKWISE_90逆时针 90°(z, y, -x)

旋转围绕 Y 轴进行(水平旋转),Y 坐标不变。

对称优化

如果结构在水平面上关于中心轴对称,设置 symmetrical = true 可跳过多余的旋转检查:

multiblock.symmetrical = true
// 验证时只检查 1 个旋转,性能提升约 4 倍

模拟

simulate() 返回结构中所有方块的世界坐标和匹配器,不访问世界——纯坐标计算。

val results = multiblock.simulate(anchor, MultiblockRotation.NONE)
for (result in results) {
val pos = result.worldPosition // 世界坐标
val matcher = result.stateMatcher // 匹配器
val char = result.character // 图案字符(仅 Dense 有值)
}

用途:

  • 预览高亮 — 在世界中高亮显示结构方块位置
  • 进度检测 — 逐个检查哪些方块已放置、哪些缺失
  • 放置引导 — 提示玩家每个位置需要什么方块

StateMatcher.ANY 匹配器在 simulate 中会被跳过(不产生 SimulateResult),因为"任意方块"不需要检测。

注册与管理

使用 MultiblockRegistry 进行全局注册,建议使用 命名空间:名称 格式的 ID:

// 注册
MultiblockRegistry.register("my_plugin:altar", altar)

// 获取
val mb = MultiblockRegistry.get("my_plugin:altar")

// 移除
MultiblockRegistry.unregister("my_plugin:altar")

// 获取所有
val all = MultiblockRegistry.getAll()

// 清空
MultiblockRegistry.clear()

完整示例

示例 1:多方块熔炉

import taboolib.module.multiblocks.*

val blastFurnace = DenseMultiblock(
pattern = arrayOf(
arrayOf(
"BBB",
"B B",
"BBB"
),
arrayOf(
"BBB",
"B0B",
"BBB"
),
arrayOf(
"BBB",
"BBB",
"BBB"
)
),
mapping = mapOf(
'B' to StringStateMatcher.parse("minecraft:bricks")
)
)

MultiblockRegistry.register("my_plugin:blast_furnace", blastFurnace)

示例 2:监听玩家交互检测结构

@SubscribeEvent
fun onInteract(e: PlayerInteractEvent) {
if (e.action != Action.RIGHT_CLICK_BLOCK) return
val block = e.clickedBlock ?: return
val anchor = BlockPos(block.x, block.y, block.z)

for ((id, multiblock) in MultiblockRegistry.getAll()) {
val rotation = multiblock.validate(block.world, anchor)
if (rotation != null) {
e.player.sendMessage("检测到结构: $id (旋转: $rotation)")
val results = multiblock.simulate(anchor, rotation)
// ... 进一步处理
return
}
}
}

示例 3:进度检测(显示缺失方块)

fun checkProgress(
world: World,
multiblock: IMultiblock,
anchor: BlockPos,
rotation: MultiblockRotation
) {
val results = multiblock.simulate(anchor, rotation)
var completed = 0
val missing = mutableListOf<SimulateResult>()

for (result in results) {
val pos = result.worldPosition
val block = world.getBlockAt(pos.x, pos.y, pos.z)
if (result.stateMatcher.test(block)) {
completed++
} else {
missing.add(result)
}
}

println("进度: $completed/${results.size}")
for (m in missing) {
println(" 缺失: ${m.worldPosition} 需要 ${m.stateMatcher.displayName}")
}
}

API 参考

核心类一览

说明
IMultiblock多方块结构核心接口
IStateMatcher方块状态匹配器接口
AbstractMultiblock抽象基类,实现旋转验证逻辑
DenseMultiblock密集型实现(字符图案)
SparseMultiblock稀疏型实现(坐标映射)
StateMatcher内置匹配器工厂(ANY / AIR / fromMaterial / fromBlockData / fromPredicate)
StringStateMatcher字符串解析匹配器(材质名 / 方块属性 / 标签)
MultiblockRegistry全局注册表
BlockPos整数坐标,支持旋转和加减运算
MultiblockRotation旋转方向枚举(4 方向)

IMultiblock 接口方法

方法返回值说明
validate(world, anchor)MultiblockRotation?自动旋转验证,返回匹配的旋转方向或 null
validate(world, anchor, rotation)Boolean指定旋转方向验证
simulate(anchor, rotation)List<SimulateResult>模拟结构,返回所有方块位置和匹配器
test(world, anchor, x, y, z, rotation)Boolean测试单个方块是否匹配
offset(x, y, z)IMultiblock设置锚点偏移(链式调用)

SimulateResult 字段

字段类型说明
worldPositionBlockPos方块在世界中的实际坐标
stateMatcherIStateMatcher该位置的匹配规则
characterChar?图案中对应的字符(仅 DenseMultiblock 有值)