Pioneer species (oak, birch) dominate at YOUNG_WOODLAND (intensity ~0.5).
Spruce and dark oak join at mid intensity. At MATURE_FOREST (intensity ~1.0)
the full mix includes cherry and jungle saplings for a diverse canopy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tuning:
- simulationIntervalTicks: 100→50 (faster sim feedback)
- FURNACE_SCAN_INTERVAL: 100→50 (matches sim interval for pollution equilibrium)
- PollutionModule.BASE_DECAY_RATE: 0.02→0.008 (slower natural decay)
- SoilModule.POLLUTION_CONTAMINATION_THRESHOLD: 30→10, CONTAMINATION_FERTILITY_DRAIN: 0.002→0.005
- VegetationModule.DIEOFF_POLLUTION_THRESHOLD: 60→30
- WorldEffectsModule: lower grass-degrade/pollution-indicator thresholds so effects
trigger from realistic furnace-driven pollution levels
- NeoForgeWorldEffectExecutor.BLOCK_ATTEMPTS: 8→20
New features:
- Campfire pollution source (0.2 air, 0.05 ground per lit campfire)
- Cross-region air pollution spreading (2% of gradient per sim cycle)
- SimulationManager.queueRegionForUpdate() for priority enqueueing
- LivingWorldBootstrap.notifyPlayerInRegion() boosts priority when player enters region
- Player region tracking in LivingWorldMod: checks every 20 MC ticks, queues update
on region change
- NeoForgeWorldEffectExecutor: implement SAPLING_GROWTH_BOOSTED (oak sapling placement);
VEGETATION_SPREADS also places short grass and flowers; GRASS_DEGRADES_TO_DIRT clears
plants and converts dirt→coarse_dirt at intensity>0.5
All 400 tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous thresholds (grass=60, smoke=70) required pollutionScore > 60.
pollutionScore is a weighted average, so even if air pollution caps at 100
the score only reaches 40 from air alone — the thresholds were physically
unreachable from furnace burning.
New thresholds: grass degradation fires at pollutionScore > 15 (soilQuality
gate raised to 75 so it doesn't block — soil defaults to 60), smoke
particles fire at > 10. With 4 furnaces the score reaches ~15 in about
20 real simulation ticks (~2 minutes of play).
Also added acid-rain water pollution (0.1/furnace/scan) — coal burning
emits SO₂ which contributes to the weighted score alongside the existing
air and ground components.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PollutionModule only decayed pollution; no source ever fed it. Every 100
server ticks (one simulation interval), LivingWorldMod now scans each
active region's loaded chunks for lit AbstractFurnaceBlockEntity instances
(covers standard furnace, blast furnace, smoker) using getChunkNow so
unloaded chunks are skipped cheaply.
Per lit furnace per scan: +0.5 air, +0.1 ground pollution. With 4 furnaces
this drives air pollution to an equilibrium of ~55 (decay rate 3.6%/tick),
giving a clearly visible pollution score in /lw region info.
Bootstrap gains getActiveRegions() and handleFurnaceActivity() for the
platform layer to call without needing Minecraft imports.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
forceUpdateRegion crashed with "no profiling cycle is active" because
runModulesForRegion calls profiler.beginModule() which requires an active
cycle. Wrapped the call in startCycle/endCycle in a try/finally.
Regions showed tick=0 and no module data because SimulationScheduler's
priority queue was never populated for normal rolling updates — only
explicit queueRegion() calls worked. Fixed by auto-enqueueing all active
regions in onMinecraftServerTick() just before each simulation cycle fires.
Auto-enqueueing is placed there (not in runSimulationCycle()) so unit tests
that drive cycles directly are unaffected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Track E — commands:
/lw region info — now shows all 7 module data classes (air/ground/water pollution,
soil fertility/moisture/contamination, succession stage, depletion levels, etc.)
/lw region info <x> <z> — inspect any region by block coordinates
/lw region force-update — runs full module pipeline on current region immediately
/lw stats — shows last cycle duration, per-module timings, budget overrun flag
Track F — weather feedback:
Each simulation tick, active overworld regions receive a moisture boost when it
rains (+0.3 waterAvailability, -0.15 droughtRisk) and a drought increase when
dry (+0.05 droughtRisk). The rain state is supplied by LivingWorldMod via a
BooleanSupplier set on ServerStartedEvent and cleared on stop.
400 tests, all passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NeoForgeWorldEffectExecutor translates WorldEffectRequests into actual Minecraft
block operations: GRASS_DEGRADES_TO_DIRT replaces grass with dirt, VEGETATION_SPREADS
spreads grass onto lit dirt, POLLUTION_VISUAL_INDICATOR spawns smoke particles.
SAPLING_GROWTH_SLOWED/BOOSTED are stubs pending mixin hooks.
Block writes are guarded by isLoaded() checks so no chunks are force-loaded.
Intensity scales the number of block candidates attempted per tick (max 8).
MinecraftServer is captured in LivingWorldMod on ServerStartedEvent and cleared
on stop; the executor receives it via Supplier to stay null-safe across restarts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eight simulation modules (pollution, soil, water, vegetation, resource depletion,
recovery, ecosystem, world effects) form the full ecosystem pipeline. Each module
owns typed RegionData and writes summary metrics back to RegionMetrics.
Player block-break events are wired through BlockBreakInfo across the platform
boundary; the NeoForge adapter translates BreakEvent and routes it to the
bootstrap handler which records mining/logging/farming depletion on the
affected region.
Module state now survives server restarts: FileRegionPersistenceService accepts
per-module codecs (via PropertiesPersistenceWriter/Reader) that serialise every
RegionData instance alongside the region's core state. Bootstrap registers codecs
for all seven data-bearing modules at startup.
395 tests, all passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>