⏳ 컨티넘: 오프라인 작물 성장 영구 시뮬레이션 엔진
**The Continuum(시공간 연속체)**은 핵심 시뮬레이션 엔진입니다. 바닐라에서는 청크가 언로드되면 성장이 멈추지만, 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{ ticks}$) 이상 지난 기록은 저장 시 자동 삭제 |
| 지원 식물 | 범용 작물, 사탕수수, 선인장, 네더 와트, 코코아, 덩굴, 묘목, 달콤한 열매 |
⚙️ 컨티넘 작동 원리
┌─────────────────────────────────────────────────────────────┐
│ 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 ticks (~11.37분) | 틱당 10% | 3단계 |
| 코코아 콩 | 6,825 ticks (~5.68분) | 20% | 2단계 |
| 달콤한 열매 | 6,825 ticks (~5.68분) | 20% | 3단계 |
| 덩굴 | 13,650 ticks (~11.37분) | 10% | 아래로 번식 |
| 묘목 | 95,550 ticks (~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일 이상 지난 데이터는 메모리와 저장 파일에서 자동 삭제됩니다.
💾 청크 영구 저장 및 0-디스크 기록 보장
- 분리된 타임스탬프 저장:
ContinuumData에 기록되므로 청크 자체의unsaved플래그는false로 유지됩니다. - 읽기 전용 검사: 팔레트만 사전 검사하여 청크를 수정됨 상태로 만들지 않습니다.
- 조건부 블록 쓰기: 실제 단계 변화($\Delta \text{age} > 0$)가 있을 때만
setBlock()을 호출하여 디스크 쓰기를 아낍니다($0\text{ 디스크 I/O}$).
See also: 성능 최적화 및 대기열 조절, 작물 레지스트리 및 범용 작물, and 아키텍처 및 Mixin 주입 대상.
