Skip to content

Mecánicas de agrupación en mega-stacks (MC 26.2)

Este documento proporciona un desglose técnico y matemático exhaustivo de la agregación de objetos en el suelo, el cálculo del radio de búsqueda, la prevención de desbordamiento de 64 bits y las optimizaciones de asignación de memoria en Item Clumps para Minecraft 26.2.


📊 Ficha técnica de características

PropiedadEspecificación
Nombre del sistemaAgregador de mega-stacks de objetos en suelo
Clase objetivonet.minecraft.world.entity.item.ItemEntity
Límite de fusión predeterminado9,999 objetos (Configurable hasta $2,147,483,647$)
Radio de búsqueda ($r$)Horizontal $1\text{ a }10\text{ bloques}$ (Predeterminado: $1\text{ bloque}$)
Sobrecarga por tick$0.00\text{ ms}$ en objetos estáticos (despacho exclusivo en setItem())
Asignación de memoriaCero asignaciones de objetos en búsqueda y absorción (copyWithCount(int))
GameRules de controlitem_clumps:enable_clumping, item_clumps:max_clump_size, item_clumps:merge_radius

⚙️ Cómo funciona la agregación en mega-stacks

En Minecraft Vanilla, los objetos caídos llaman a mergeWithNeighbours() durante su ciclo de ticks, buscando objetos cercanos del mismo tipo. Sin embargo, Vanilla prohíbe estrictamente la fusión si el recuento de objetos de la entidad objetivo excediera itemStack.getMaxStackSize() (normalmente 64, o 16 para perlas de ender/huevos).

Item Clumps intercepta esta comprobación a través de ItemEntityMixin.java para eliminar esta restricción artificial manteniendo una estricta seguridad en los componentes de datos.

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


                  ┌──────────────────────────────┐
                  │     Fast-Path Validation     │
                  │  - Matching player target?   │
                  │  - Identical DataComponents? │
                  └──────────────┬───────────────┘
                                 │ (Pass)

                  ┌──────────────────────────────┐
                  │   In-Flight Magnet Check     │
                  │ (Is either item magnetized?) │
                  └──────────────┬───────────────┘
                                 │ (No)

                  ┌──────────────────────────────┐
                  │    64-Bit Arithmetic Sum     │
                  │   S = (long) A + (long) B    │
                  └──────────────┬───────────────┘

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

📐 Fórmulas matemáticas y cálculo de precisión

1. Expansión horizontal del cuadro delimitador

Vanilla busca en un cuadro delimitador diminuto: $\text{AABB}_{\text{vanilla}} = \text{AABB}.\text{inflate}(0.5, 0.0, 0.5)$.

Item Clumps expande dinámicamente el cuadro delimitador horizontal mediante una inyección @Redirect sin asignación de memoria:

$$\text{AABB}_{\text{clump}} = \text{boundingBox}.\text{inflate}(r, y, r)$$

Donde:

  • $r = \text{DynamicGameRuleManager}.\text{getInt}(\text{level}, \text{MERGE_RADIUS}) \in [1, 10]$ bloques.
  • $y = 0.0$ (la altura de búsqueda vertical se preserva estrictamente para evitar que objetos en pisos o tolvas verticales distintos se fusionen entre sí).

2. Suma de enteros de 64 bits y seguridad contra desbordamiento aritmético

Cuando dos grupos masivos se fusionan en configuraciones de servidor grandes (por ejemplo, límite máximo establecido en $2\text{ mil millones}$), la suma estándar de int de 32 bits puede desbordarse en enteros negativos ($\text{Integer.MAX_VALUE} + 1 = -2,147,483,648$), lo que corrompe el estado de las entidades.

Item Clumps calcula la suma con precisión de 64 bits:

$$S = (\text{long}),\text{thisCount} + (\text{long}),\text{otherCount}$$

  • Absorción completa ($S \le \text{maxClumpSize}$): $$\text{count}_{\text{merged}} = (\text{int}),S$$
  • Absorción parcial ($S > \text{maxClumpSize}$): $$\Delta = \text{maxClumpSize} - \text{thisCount}$$ $$\text{thisCount}' = \text{maxClumpSize}, \quad \text{otherCount}' = \text{otherCount} - \Delta$$

3. Rigurosidad de componentes y estado

Los objetos nunca se fusionan a menos que todos los componentes de datos (encantamientos, durabilidad dañada, nombres personalizados, adornos, efectos de pociones) sean 100% idénticos a nivel de bits:

$$\text{ItemStack}.\text{isSameItemSameComponents}(\text{stack}_A, \text{stack}_B) == \text{true}$$


💻 Implementación del código fuente Java real

De ItemEntityMixin.java en Minecraft 26.2:

java
@Inject(method = "tryToMerge", at = @At("HEAD"), cancellable = true)
private void item_clumps$customMerge(ItemEntity other, CallbackInfo ci) {
    ItemStack thisStack = this.getItem();
    ItemStack otherStack = other.getItem();

    // Fast-path exit before GameRule lookups
    if (!Objects.equals(this.target, ((ItemEntityMixin)(Object)other).target) || 
        !ItemStack.isSameItemSameComponents(thisStack, otherStack)) {
        return;
    }

    if (!DynamicGameRuleManager.getBoolean(this.level(), ItemClumpsFabric.ENABLE_CLUMPING)) return;

    // Magnet mod in-flight protection
    if (net.fabricmc.loader.api.FabricLoader.getInstance().isModLoaded("magnet")) {
        try {
            java.lang.reflect.Method isMagnetizedMethod;
            try {
                isMagnetizedMethod = this.getClass().getMethod("ig_magnet$isMagnetized");
            } catch (NoSuchMethodException e) {
                isMagnetizedMethod = this.getClass().getMethod("ig$isMagnetized");
            }
            if ((boolean) isMagnetizedMethod.invoke(this) || (boolean) isMagnetizedMethod.invoke(other)) {
                ci.cancel();
                return;
            }
        } catch (Throwable ignored) {}
    }

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

    long sum = (long) thisCount + (long) otherCount;
    if (sum > (long) maxClump) {
        int spaceLeft = maxClump - thisCount;
        if (spaceLeft > 0) {
            ItemStack thisCopy = thisStack.copyWithCount(maxClump);
            this.setItem(thisCopy);
            
            ItemStack otherCopy = otherStack.copyWithCount(otherCount - spaceLeft);
            other.setItem(otherCopy);

            this.pickupDelay = Math.max(this.pickupDelay, ((ItemEntityMixin)(Object)other).pickupDelay);
            this.age = Math.min(this.age, ((ItemEntityMixin)(Object)other).age);
        }
        ci.cancel();
        return;
    }

    // Full Merge: larger stack absorbs the smaller stack
    if (otherCount < thisCount) {
        ItemStack thisCopy = thisStack.copyWithCount((int) sum);
        this.setItem(thisCopy);
        this.pickupDelay = Math.max(this.pickupDelay, ((ItemEntityMixin)(Object)other).pickupDelay);
        this.age = Math.min(this.age, ((ItemEntityMixin)(Object)other).age);
        other.discard();
    } else {
        ItemStack otherCopy = otherStack.copyWithCount((int) sum);
        other.setItem(otherCopy);
        ((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();
}

🔗 Documentación relacionada (MC 26.2)

Official documentation & web portal for Dasik Igaijinn mods.