压缩与拆分(Compress / Decompress)
基本信息
| 属性 | 值 |
|---|---|
| 热键 | 压缩物品、拆分物品 |
| 服务端入口 | net.blay09.mods.craftingtweaks.net.HandlerCompress(封包 id 6) |
| 客户端入口 | ClientProvider.compress / ClientProvider.decompress |
| 配方查询 | CraftingManager.getInstance().findMatchingRecipe(...) |
| 辅助容器 | InventoryCraftingCompress、InventoryCraftingDecompress |
| 门控配置 | compressAnywhere(默认 false) |
注意:本 mod 不注册任何配方。它只是读取 CraftingManager 中已存在的
配方,把物品按配方形状摆进一个临时的合成矩阵再触发合成。
两个辅助 InventoryCrafting
| 类 | 构造签名 | 尺寸 | 填充方式 |
|---|---|---|---|
InventoryCraftingCompress |
(Container container, int size, ItemStack itemStack) |
size × size |
每一个槽都填 itemStack.copy() |
InventoryCraftingDecompress |
(Container container, ItemStack itemStack) |
3 × 3 |
只有槽 0 填 itemStack.copy() |
两者都调用 super(container, w, h) 把真实的 Container 传进去,
使 InventoryCrafting 的配方依赖检查能拿到正确的 world。
InventoryCraftingCompress 因此天然表达「把同一种物品铺满 N×N」的配方形状,
InventoryCraftingDecompress 表达「一个产物放在左上角」的还原形状。
压缩(compress)
服务端路径(HandlerCompress)
先做 4 层校验:
container != nullslotNumber在container.inventorySlots范围内mouseSlot.inventory instanceof InventoryPlayer(只允许操作玩家背包槽)mouseStack != nullCraftingTweaksConfig.compressAnywhere || provider != null
然后按方向分支。
压缩方向,先按 provider 网格大小分派(provider == null 时按 9 处理):
| 条件 | 尝试的配方形状 | 每次消耗 |
|---|---|---|
size == 9 && stackSize >= 9 |
先试 3×3 全铺 | decrStackSize(9) |
| ↑ 失败后 | 再试 2×2(4 格) | decrStackSize(4) |
size >= 4 && stackSize >= 4 |
只试 2×2 | decrStackSize(4) |
即 9 格网格优先 3×3 配方,匹配不上才退 2×2;4 格网格只能 2×2。
每次循环:产物 addItemStackToInventory(result.copy()) 成功才
decrStackSize(n),背包满则 break;循环条件
compressAll && mouseSlot.getHasStack() && stackSize >= n。
客户端路径(ClientProvider.compress)
额外要求 canClear(...),即 !provider.requiresServerSide()。
固定流程:
clearGrid(provider, player, container, 0, forced=false)—— 先清场- 逐槽校验网格已空,有残留就整体中止
- 取出
mouseSlot的物品到鼠标上 - 用**拖拽分配(drag splitting)**把物品铺进网格:
windowClick(-999, 0, 0, 5)开始 → 对每个目标槽windowClick(slot, 1, 0, 5)加一个 →windowClick(-999, 2, 0, 5)结束 - 找到第一个有物品的
SlotCrafting,windowClick(slot, 0, compressAll ? 1 : 0)(clickType 1 = Shift 点击 = 全部合成) dropOffMouseStack(..., mouseSlot.slotNumber)归位- 残留物品逐槽放回
mouseSlot,最后再dropOffMouseStack
拖拽按钮号由 getDragSplittingButton(id, limit) = id & 3 | (limit & 3) << 2
算出,压缩路径固定用 id = 0/1/2、limit = 0,即按钮 0。
⚠️ 客户端路径的 3×3 分支依次点 start .. start+8 全部 9 槽;
2×2 分支只点 start、start+1、start+3、start+4 四个。
拆分(decompress)
服务端(HandlerCompress 的 isDecompress 分支):
result = findMatchingRecipe(new InventoryCraftingDecompress(container, mouseStack), world)
if (result != null && mouseStack.stackSize >= 1):
do:
addItemStackToInventory(result.copy()) → mouseSlot.decrStackSize(1)
while (compressAll && mouseSlot.getHasStack() && mouseSlot.getStack().stackSize >= 1)
每次只消耗 1 个产物物品,循环条件只看 stackSize >= 1,
所以「拆分」始终是 1→N 的还原方向,不存在 3×3/2×2 分派。
客户端(ClientProvider.decompress)流程:
clearGrid(..., forced=false)+ 空网格校验windowClick(mouseSlot)拿起 →windowClick(start)放进网格 槽 0- 找到第一个有物品的
SlotCrafting后windowClick(slot, 0, decompressAll ? 1 : 0) dropOffMouseStack(..., mouseSlot.slotNumber)windowClick(start)+windowClick(mouseSlot)把剩余物品取回
前后端一致性
| 对比项 | 服务端 HandlerCompress |
客户端 ClientProvider |
|---|---|---|
| 网格来源 | provider,或 null 时按 9 | 必须 provider 非 null |
| 清场 | 不清场 | 强制 clearGrid 且校验为空 |
| 铺货方式 | 直接构造 InventoryCrafting |
模拟拖拽点击 |
| 触发合成 | 直接 addItemStackToInventory |
windowClick 到 SlotCrafting |
compressAnywhere |
✅ 生效 | ❌ 不读该配置 |
⚠️ 因此 compressAnywhere 只在服务端有意义,与配置注释
「only works if installed on server」完全一致。