Skip to content

Механика объединения в мега-стаки (MC 26.1.2)

Этот документ содержит технический и математический разбор агрегации предметов на земле, вычисления радиуса поиска и слияния сущностей в Item Clumps для Minecraft 26.1.2.


📊 Информационный блок функции

СвойствоСпецификация
Имя системыАгрегатор мега-стаков предметов на земле
Целевой классnet.minecraft.world.entity.item.ItemEntity
Лимит по умолчанию9 999 предметов (настраивается до $2,147,483,647$)
Радиус поиска ($r$)По горизонтали $1\text{–}10\text{ блоков}$ (по умолч.: $1\text{ блок}$)
Точка внедрения радиуса@ModifyArgs на AABB.inflate(DDD)
Управляющие GameRulesitem_clumps:enable_clumping, item_clumps:max_clump_size, item_clumps:merge_radius

⚙️ Как работает объединение в мега-стаки

В ванильном Minecraft выброшенные предметы каждый тик вызывают mergeWithNeighbours(). Однако слияние запрещено, если количество превысит itemStack.getMaxStackSize() (обычно 64).

Item Clumps снимает это ограничение через ItemEntityMixin.java, строго сохраняя целостность компонентов данных.

                  ┌──────────────────────────────┐
                  │    ItemEntity A Ticks In     │
                  └──────────────┬───────────────┘


                  ┌──────────────────────────────┐
                  │     Feasibility Check        │
                  │  - Matching player target?   │
                  │  - Identical DataComponents? │
                  └──────────────┬───────────────┘
                                 │ (Pass)

                  ┌──────────────────────────────┐
                  │   Combine Stacks Math Check  │
                  └──────────────┬───────────────┘

                 ┌───────────────┴───────────────┐
                 │                               │
    thisCount + otherCount <= maxClump    thisCount + otherCount > maxClump
                 │                               │
                 ▼                               ▼
       ┌───────────────────┐           ┌───────────────────┐
       │   Complete Merge  │           │   Partial Merge   │
       │ Larger absorbs    │           │ Fill entity A to  │
       │ smaller entity;   │           │ maxClumpSize;     │
       │ Younger age kept. │           │ B keeps remainder.│
       └───────────────────┘           └───────────────────┘

📐 Математические формулы и точные вычисления

1. Расширение горизонтального хитбокса (@ModifyArgs)

$$\text{args.set}(0, r), \quad \text{args.set}(2, r)$$

Где:

  • $r = \text{DynamicGameRuleManager}.\text{getInt}(\text{level}, \text{MERGE_RADIUS}) \in [1, 10]$ блоков.
  • Вертикальный индекс ($1$) остается неизменным, исключая смешивание предметов с разных этажей.

2. Математика передачи количества предметов

  • Полное поглощение ($S \le \text{maxClumpSize}$): $$\text{count}_{\text{merged}} = \text{thisCount} + \text{otherCount}$$
  • Частичное поглощение ($S > \text{maxClumpSize}$): $$\Delta = \text{maxClumpSize} - \text{thisCount}$$ $$\text{thisCount}' = \text{maxClumpSize}, \quad \text{otherCount}' = \text{otherCount} - \Delta$$

💻 Исходный код реализации на Java

Из ItemEntityMixin.java в Minecraft 26.1.2:

java
@ModifyArgs(method = "mergeWithNeighbours", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/AABB;inflate(DDD)Lnet/minecraft/world/phys/AABB;"))
private void item_clumps$modifySearchRadius(Args args) {
    if (DynamicGameRuleManager.getBoolean(this.level(), ItemClumpsFabric.ENABLE_CLUMPING)) {
        double radius = DynamicGameRuleManager.getInt(this.level(), ItemClumpsFabric.MERGE_RADIUS);
        args.set(0, radius);
        args.set(2, radius);
    }
}

@Inject(method = "tryToMerge", at = @At("HEAD"), cancellable = true)
private void item_clumps$customMerge(ItemEntity other, CallbackInfo ci) {
    if (!DynamicGameRuleManager.getBoolean(this.level(), ItemClumpsFabric.ENABLE_CLUMPING)) return;

    ItemStack thisStack = this.getItem();
    ItemStack otherStack = other.getItem();

    if (!Objects.equals(this.target, ((ItemEntityMixin)(Object)other).target) || 
        !ItemStack.isSameItemSameComponents(thisStack, otherStack)) {
        return;
    }

    int thisCount = thisStack.getCount();
    int otherCount = otherStack.getCount();
    int maxClump = DynamicGameRuleManager.getInt(this.level(), ItemClumpsFabric.MAX_CLUMP_SIZE);

    if (thisCount + otherCount > maxClump) {
        int spaceLeft = maxClump - thisCount;
        if (spaceLeft > 0) {
            thisStack.setCount(maxClump);
            this.setItem(thisStack);
            otherStack.setCount(otherCount - spaceLeft);
            other.setItem(otherStack);
        }
        ci.cancel();
        return;
    }

    // Full Merge: larger stack absorbs the smaller stack
    if (otherCount < thisCount) {
        thisStack.setCount(thisCount + otherCount);
        this.setItem(thisStack);
        this.pickupDelay = Math.max(this.pickupDelay, ((ItemEntityMixin)(Object)other).pickupDelay);
        this.age = Math.min(this.age, ((ItemEntityMixin)(Object)other).age);
        other.discard();
    } else {
        otherStack.setCount(thisCount + otherCount);
        other.setItem(otherStack);
        ((ItemEntityMixin)(Object)other).pickupDelay = Math.max(((ItemEntityMixin)(Object)other).pickupDelay, this.pickupDelay);
        ((ItemEntityMixin)(Object)other).age = Math.min(((ItemEntityMixin)(Object)other).age, this.age);
        this.discard();
    }
    ci.cancel();
}

🔗 Связанная документация (MC 26.1.2)

Official documentation & web portal for Dasik Igaijinn mods.