⏳ Континуум: Движок офлайн-роста растений
Континуум (The Continuum) — ведущий движок симуляции. В ванильном Minecraft рост останавливается при выгрузке чанка. The Continuum сохраняет временные метки выгрузки и плавно симулирует прошедшее время без лагов.
📊 Инфоблок технических характеристик
| 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) |
| Бюджет очереди | CROPS_PER_TICK = 5 (Глобальная очередь без блокировок) |
| Palette Pre-Filter | section.hasOnlyAir() & section.maybeHas(AgrarianCropRules::isCropBlock) |
| Предел устаревания | Записи старше 30 реальных дней ($51,840,000\text{ тактов}$) удаляются |
| Поддерживаемые культуры | Все культуры, тростник, кактус, адский нарост, какао, лианы, саженцы, ягоды |
⚙️ Как работает Континуум
┌─────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────┘📐 Математические формулы догоняющего роста
$$\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. Стандартные культуры (CropBlock и моды)
$$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. Высота тростника и кактусов
$$\Delta \text{age} = \left\lfloor \frac{\Delta t_{\text{effective}}}{1365} \right\rfloor$$
3. Интервалы стадий других растений
| Тип растения | Тактов на стадию | Вероятность за такт | Макс. стадия |
|---|---|---|---|
| Адский нарост | 13,650 тактов (~11.37 мин) | 10% за случайный такт | Стадия 3 |
| Какао-бобы | 6,825 тактов (~5.68 мин) | 20% за случайный такт | Стадия 2 |
| Сладкие ягоды | 6,825 тактов (~5.68 мин) | 20% за случайный такт | Стадия 3 |
| Лианы | 13,650 тактов (~11.37 мин) | 10% за случайный такт | Рост вниз |
| Саженцы | 95,550 тактов (~79.6 мин) | 1.4% за случайный такт | Стадия 2 (Рост дерева) |
⚡ Оптимизация производительности
1. Sub-Chunk Palette-Level Pre-Filtering
Вместо перебора всех $98,304$ блоков CropScanner использует фильтрацию палитры для каждой секции $16 \times 16 \times 16$ LevelChunkSection:
section.hasOnlyAir(): Пропускает пустые подчанки за $0.0001\mu\text{s}$.section.maybeHas(AgrarianCropRules::isCropBlock): Проверяет массив палитры. Если культур нет, весь блок на 4 096 вокселей пропускается. Это отсекает от 85% до 95% несельскохозяйственных подчанков без накладных расходов.
2. Очистка записей старше 30 дней
$$\text{Max Timestamp Age} = 30\text{ days} \times 86,400\text{ s/day} \times 20\text{ ticks/s} = 51,840,000\text{ ticks}$$ Во время автосохранения (BEFORE_SAVE) данные старше 30 реальных дней удаляются из памяти и диска.
💾 Сохранение чанков и нулевая запись на диск
- Независимое сохранение времени: Временные метки хранятся в
ContinuumData, сохраняя флагLevelChunk.unsavedвfalse. - Чтение без изменения чанков: Проверка палитр не помечает чанки измененными.
- Запись только при росте: Вызов
level.setBlock()происходит только при реальном приросте ($\Delta \text{age} > 0$), защищая диск от лишней записи ($0\text{ Disk I/O}$).
See also: Производительность и ограничение очереди, Реестр растений и универсальные культуры, and Архитектура и миксины.
