智能拾取与背包分发系统 (MC 26.2)
本文档深入解析 Minecraft 26.2 下 Item Clumps 的单批次原生存放、容量模拟、纯原版物品堆创建及零 NBT 污染架构。
📊 特性信息框
| 属性 | 技术规格 |
|---|---|
| 系统名称 | 智能批量背包分发器 |
| 目标类 | net.minecraft.world.entity.item.ItemEntity |
| 背包上限 | 严格对齐原版规则(普通物品为 $64$,末影珍珠/鸡蛋为 $16$) |
| 数据纯净度 | $100%\text{ 纯原版物品堆}$(零自定义 NBT、标签或组件) |
| 拾取动画 | 原生 player.take(this, added) 实体吸附动画与拾取音效 |
| 统计追踪 | 触发原版 Stats.ITEM_PICKED_UP.get(item) |
| 部分拾取 | 完整支持(未拾取满的剩余物品留存在地面聚合体中) |
🎒 智能拾取完整生命周期
Item Clumps 的一项核心设计准则是零背包污染。尽管地面上的掉落物实体可容纳 $9,999$ 个物品,但玩家个人背包的堆叠上限绝不被篡改或扩充。
┌──────────────────────────────────────────────┐
│ Player Collides with Ground Clump (e.g. 500x)│
└──────────────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Pickup Validity & Target Check │
│ - pickupDelay == 0? │
│ - target == null || target == player? │
└──────────────────────┬───────────────────────┘
│ (Pass)
▼
┌──────────────────────────────────────────────┐
│ Single-Batch Native Inventory Dispatch │
│ player.getInventory().add(stack) │
└──────────────────────┬───────────────────────┘
│
┌─────────────────┴─────────────────┐
│ │
Inventory Has Space Inventory Becomes Full
│ │
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ Entire Clump Absorbed │ │ Partial Pickup │
│ - 500 items transferred │ │ - e.g. 192 items added │
│ - Clump entity removed │ │ - 308 items remain in │
│ - Sound & particles play│ │ ground clump entity │
└─────────────────────────┘ └─────────────────────────┘🛡️ 零 NBT 污染保证
地面上的物品会显示自定义铭牌(如 Stone x500)以向玩家传达其实际数量。然而当这些物品进入玩家背包时:
- 铭牌自动剥离:自定义铭牌仅属于
ItemEntity实体本身,绝不附加在底层的ItemStack物品堆上。 - 原版无缝堆叠:由于进入玩家背包的
ItemStack毫无自定义组件附带,因此可与箱子、潜影盒和原版工作台中的物品无缝自然合并。 - 零残留幽灵数据:世界存档保持 100% 纯净且绝无损坏风险,即便日后从服务器中彻底移除本模组也毫不受影响。
💻 真实源码实现参考
源自 Minecraft 26.2 的 ItemEntityMixin.java:
java
@Inject(method = "playerTouch", at = @At("HEAD"), cancellable = true)
private void item_clumps$smartPickup(Player player, CallbackInfo ci) {
if (!DynamicGameRuleManager.getBoolean(this.level(), ItemClumpsFabric.ENABLE_CLUMPING) ||
this.level().isClientSide()) return;
int count = this.getItem().getCount();
if (count <= 1) return; // Allow vanilla to handle normal 1-count items
if (this.pickupDelay == 0 && (this.target == null || this.target.equals(player.getUUID()))) {
ItemStack pickupStack = this.getItem().copy();
int originalCount = count;
// Native batch addition into player inventory
player.getInventory().add(pickupStack);
int added = originalCount - pickupStack.getCount();
if (added > 0) {
player.take(this, added);
player.awardStat(net.minecraft.stats.Stats.ITEM_PICKED_UP.get(pickupStack.getItem()), added);
ItemStack remainingStack = this.getItem().copyWithCount(pickupStack.getCount());
this.setItem(remainingStack);
if (pickupStack.getCount() <= 0) {
player.onItemPickup((ItemEntity) (Object) this);
}
}
ci.cancel(); // Prevent vanilla pickup interference
}
}