⏳ El Continuo: Motor de persistencia y crecimiento offline
El Continuo (The Continuum) es el motor de simulación insignia. En vanilla, el crecimiento se detiene al descargar el chunk. The Continuum guarda marcas temporales y simula el crecimiento offline en tiempo real sin congelar el servidor.
📊 Infobox de especificaciones técnicas
| Component | Specification |
|---|---|
| Engine Class | net.instantgratification.agrarianreform.continuum.ContinuumManager |
| Data Handler | net.instantgratification.agrarianreform.continuum.ContinuumData |
| Scanner Helper | net.instantgratification.agrarianreform.continuum.CropScanner |
| Saved Data ID | agrarian_reform_continuum (Dimension-scoped SavedDataType) |
| Límite de actualización | CROPS_PER_TICK = 5 (Cola global regulada sin bloqueos) |
| Palette Pre-Filter | section.hasOnlyAir() & section.maybeHas(AgrarianCropRules::isCropBlock) |
| Caducidad de marcas | Entradas de más de 30 días reales ($51,840,000\text{ ticks}$) se eliminan al guardar |
| Plantas compatibles | Cultivos universales, caña de azúcar, cactus, verruga del Nether, cacao, lianas, brotes, bayas |
⚙️ Cómo funciona El Continuo
┌─────────────────────────────────────────────────────────────┐
│ CHUNK UNLOAD EVENT │
│ ServerChunkEvents.CHUNK_UNLOAD records server game time pos │
│ Saved to world storage via ContinuumData (SavedDataType) │
└──────────────────────────────┬──────────────────────────────┘
│
▼ (Offline / World Unloaded Time Delta)
┌──────────────────────────────┴──────────────────────────────┐
│ CHUNK LOAD EVENT │
│ ServerChunkEvents.CHUNK_LOAD retrieves unload timestamp │
│ Calculates timeDelta = currentTick - unloadTick │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌──────────────────────────────┴──────────────────────────────┐
│ SUB-CHUNK PALETTE-GATED SCANNER │
│ CropScanner skips empty air & non-crop sections (O(1)) │
│ Queues matching plants into ConcurrentLinkedQueue │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌──────────────────────────────┴──────────────────────────────┐
│ THROTTLED PER-CROP TICK PROCESSOR │
│ ServerTickEvents.END_SERVER_TICK processes 5 crops/tick │
│ Scales delta per-crop via AgrarianCropRules & updates state │
└─────────────────────────────────────────────────────────────┘📐 Fórmulas matemáticas de compensación
$$\Delta t_{\text{effective}} = \begin{cases} 0 & \text{if } M_{\text{crop}} \le 0 \ \left\lfloor \frac{\Delta t \cdot M_{\text{crop}}}{100} \right\rfloor & \text{if } M_{\text{crop}} > 0 \end{cases}$$
1. Cultivos estándar (CropBlock y mods)
$$S = \text{CropScanner.getSpeed}(\text{crop}, \text{level}, \text{pos})$$ $$T_{\text{stage}} = \left( \frac{25.0}{S} + 1.0 \right) \cdot \frac{4096.0}{3.0}$$ $$\Delta \text{age} = \left\lfloor \frac{\Delta t_{\text{effective}}}{T_{\text{stage}}} \right\rfloor$$
2. Altura de caña de azúcar y cactus
$$\Delta \text{age} = \left\lfloor \frac{\Delta t_{\text{effective}}}{1365} \right\rfloor$$
3. Intervalos de fases de otras plantas
| Tipo de planta | Ticks por fase | Probabilidad por tick | Fase máx / Límite |
|---|---|---|---|
| Verruga del Nether | 13,650 ticks (~11.37 min) | 10% por tick aleatorio | Fase 3 |
| Vainas de cacao | 6,825 ticks (~5.68 min) | 20% por tick aleatorio | Fase 2 |
| Arbusto de bayas | 6,825 ticks (~5.68 min) | 20% por tick aleatorio | Fase 3 |
| Lianas | 13,650 ticks (~11.37 min) | 10% por tick aleatorio | Propagación hacia abajo |
| Brotes de árbol | 95,550 ticks (~79.6 min) | 1.4% por tick aleatorio | Fase 2 (Crecimiento de árbol) |
⚡ Optimizaciones de rendimiento
1. Sub-Chunk Palette-Level Pre-Filtering
En lugar de examinar $98,304$ coordenadas, CropScanner consulta la paleta de cada LevelChunkSection de $16 \times 16 \times 16$:
section.hasOnlyAir(): Descarta sub-chunks vacíos en $0.0001\mu\text{s}$.section.maybeHas(AgrarianCropRules::isCropBlock): Consulta la paleta. Si no hay cultivos, se descartan los 4,096 bloques. Esto rechaza entre un 85% y un 95% de sub-chunks sin plantas agrícolas sin coste de iteración.
2. Purga de marcas temporales de más de 30 días
$$\text{Max Timestamp Age} = 30\text{ days} \times 86,400\text{ s/day} \times 20\text{ ticks/s} = 51,840,000\text{ ticks}$$ Durante los guardados automáticos (BEFORE_SAVE), las entradas con más de 30 días reales se eliminan de memoria y disco.
💾 Persistencia de chunks y cero escrituras en disco
- Marcas desacopladas: Se almacenan en
ContinuumData, manteniendo el flagLevelChunk.unsavedenfalse. - Escaneo de lectura pura: Inspecciona estados de bloques sin ensuciar el chunk.
- Modificación condicional: Solo se llama a
level.setBlock()si hay crecimiento real ($\Delta \text{age} > 0$), ahorrando escrituras en disco ($0\text{ I/O de disco}$).
See also: Rendimiento y control de colas, Registro de cultivos y soporte universal, and Arquitectura y mixins.
