スマート回収とインベントリ分配 (MC 26.2)
本書では、Minecraft 26.2 におけるインベントリへの一括取得、容量シミュレーション、バニラスタック生成、NBT汚染防止を解説します。
📊 機能情報ボックス
| 項目 | 仕様 |
|---|---|
| システム名 | スマート一括インベントリ分配エンジン |
| 対象クラス | net.minecraft.world.entity.item.ItemEntity |
| インベントリ上限 | バニラ制限を厳守 (通常アイテム: $64$、パール/卵: $16$) |
| データ純度 | $100%\text{ 完全なバニラItemStack}$ (独自NBTやタグの付与ゼロ) |
| 回収アニメーション | バニラ標準の player.take(this, added) アニメーション&SE |
| 統計追跡 | バニラの 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汚染ゼロの保証
地上のアイテムは数量を示すカスタムネームタグ (例: 丸石 x500) を表示しますが、プレイヤーのインベントリに入ると:
- ネームタグの消去: カスタム名は
ItemEntityにのみ付与されており、内部のItemStackには付与されません。 - バニラスタック: インベントリに入るアイテムには独自データが含まれないため、チェストやシュルカーボックス、作業台で既存のアイテムと完全にスタックできます。
- 不要データの非残留: 後からサーバーからMODを削除した場合でも、ワールドデータが破損したり異常データが残ることはありません。
💻 実際のJavaソースコード実装
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
}
}