⏳ Le Continuum : Moteur de persistance et croissance hors-ligne
Le Continuum (The Continuum) est le moteur de simulation phare. En vanilla, la pousse cesse au déchargement d'un chunk. The Continuum comble ce manque en conservant l'horodatage et en simulant la pousse hors-ligne sans à-coups.
📊 Infobox des spécifications techniques
| 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) |
| Cadence de mise à jour | CROPS_PER_TICK = 5 (File d'attente globale régulée) |
| Palette Pre-Filter | section.hasOnlyAir() & section.maybeHas(AgrarianCropRules::isCropBlock) |
| Plafond d'obsolescence | Entrées de plus de 30 jours réels ($51,840,000\text{ ticks}$) purgées à la sauvegarde |
| Plantes prises en charge | Toutes cultures, canne à sucre, cactus, verrues du Nether, cacao, lianes, pousses, baies |
⚙️ Fonctionnement du Continuum
┌─────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────┘📐 Formules mathématiques de rattrapage
$$\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. Cultures courantes (CropBlock & 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. Hauteur de la canne à sucre et des cactus
$$\Delta \text{age} = \left\lfloor \frac{\Delta t_{\text{effective}}}{1365} \right\rfloor$$
3. Intervalles de stades d'autres plantes
| Type de plante | Ticks par stade | Probabilité par tick | Stade max / Limite |
|---|---|---|---|
| Verrue du Nether | 13,650 ticks (~11.37 min) | 10% par tick aléatoire | Stade 3 |
| Gousses de cacao | 6,825 ticks (~5.68 min) | 20% par tick aléatoire | Stade 2 |
| Baies sucrées | 6,825 ticks (~5.68 min) | 20% par tick aléatoire | Stade 3 |
| Lianes | 13,650 ticks (~11.37 min) | 10% par tick aléatoire | Propagation vers le bas |
| Pousses d'arbres | 95,550 ticks (~79.6 min) | 1.4% par tick aléatoire | Stade 2 (Arbre formé) |
⚡ Optimisations de performances
1. Sub-Chunk Palette-Level Pre-Filtering
Au lieu de scanner $98,304$ blocs, CropScanner interroge la palette de chaque section $16 \times 16 \times 16$ LevelChunkSection :
section.hasOnlyAir(): Ignore les sous-chunks vides en $0.0001\mu\text{s}$.section.maybeHas(AgrarianCropRules::isCropBlock): Vérifie la palette. Sans cultures, les 4 096 blocs sont directement passés. Cela élimine 85% à 95% des sections non agricoles sans charge de calcul.
2. Purge des horodatages obsolètes après 30 jours
$$\text{Max Timestamp Age} = 30\text{ days} \times 86,400\text{ s/day} \times 20\text{ ticks/s} = 51,840,000\text{ ticks}$$ Lors des sauvegardes automatiques (BEFORE_SAVE), les données vieilles de plus de 30 jours sont supprimées.
💾 Persistance des chunks et 0 écriture sur disque
- Horodatage découplé : Enregistré dans
ContinuumData, laissantLevelChunk.unsavedàfalse. - Scan en lecture seule : Analyse les blocs sans marquer le chunk comme modifié.
- Écriture sélective :
level.setBlock()n'est appelé que si la plante a réellement évolué ($\Delta \text{age} > 0$), évitant des écritures disque ($0\text{ I/O disque}$).
See also: Performances et régulation de file d'attente, Registre des plantes et cultures universelles, and Architecture et cibles Mixin.
