- /lw speed now accepts 1-100 (was 1-20)
- Vegetation growth rates 4x faster: grass 0.015→0.06, flowers 0.008→0.03,
shrubs 0.005→0.02, trees 0.002→0.008. Previously grass alone took ~200
cycles to unlock shrub growth; now ~50 cycles.
- Recovery base progress 0.5→2.0/cycle, health bonus 0.02→0.05/cycle.
Each succession stage now advances in ~50 good cycles instead of ~200.
At /lw speed 100 with good soil+water conditions set via /lw set, full
BARREN→MATURE_FOREST succession completes in well under a minute.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/lw speed <1-20> — run N full sim cycles (module pipeline + all
post-sim hooks) per normal tick interval.
/lw speed reset — return to real-time (1x).
The multiplier is stored on LivingWorldBootstrap and reset to 1 on
server stop. Extra cycles queue all active regions and call the full
post-sim hook chain (pollution spread, seasonal effects, climate,
warming, water runoff, dynamic cap) so ecosystem state advances
coherently rather than just running modules in isolation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
VegetationModule:
- All living tiers now die under bad conditions, not just grass. Rates:
grass 1.0, flowers 0.5, shrubs 0.25, trees 0.10 per sim cycle (trees
only die under severe pollution >60 or near-zero soil <10).
- Dead accumulation raised 0.20 → 0.50 per bad cycle.
- Previously only grass died, keeping vegetation pressure artificially
high and preventing regression even when soil/pollution were terrible.
RecoveryModule:
- DAMAGE_PER_BAD_TICK raised 3.0 → 5.0 so regression fires in ~14 bad
cycles (~35s) once conditions trip the threshold, down from ~23 cycles.
- DAMAGE_DECAY_RATE lowered 0.05 → 0.03 so damage sticks around during
oscillating conditions instead of washing away on good ticks.
- Fixed bug: 3-arg RecoveryRegionData constructor (used during damage
decay) defaulted maxSuccessionStage to MATURE_FOREST, silently
overwriting any biome or dynamic cap set by setRegionBiomeCap() /
applyDynamicCapUpdate(). Now uses 4-arg constructor to preserve cap.
Net result: a heavily polluted forest regresses a stage in ~2 minutes
of sustained bad conditions instead of 10-15+ minutes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds RegionSetCommand under /lw set with sub-commands to directly mutate
simulation data in the player's current region:
/lw set soil fertility <0-100> — soil fertility
/lw set soil moisture <0-100> — soil moisture
/lw set water availability <0-100> — water availability
/lw set water drought <0-100> — drought risk
/lw set pollution <0-100> — all pollution layers (proportional)
/lw set stage <STAGE> — force succession stage (resets progress)
/lw set cap <STAGE> — force succession ceiling
/lw set rain <0.0-1.0> — override regional rain level
Stage names have tab-completion. Forcing /lw set stage to a higher stage
than the current cap auto-raises the cap. All changes are immediately
persisted via markDirty.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dynamic succession cap: desert regions (initially capped at SPARSE_GRASS)
can now advance to MATURE_FOREST if water and soil conditions improve
sustainably. applyDynamicCapUpdate() raises the cap each sim cycle;
maxSuccessionStage is now persisted in the region codec so progress
survives server restarts.
- Elevation-based water runoff: LivingWorldMod samples average surface
height once per region on first entry. applyWaterRunoff() uses height
differences to transfer water availability from high regions to low
neighbours when it is raining, simulating drainage basin physics —
valleys collect water, hills drain.
- Water pool formation (WATER_POOL_FORMS): WorldEffectsModule emits the
new effect when a BARREN/SPARSE_GRASS region has regional rain > 0.3.
NeoForgeWorldEffectExecutor finds the lowest sampled surface point and
places water source blocks there; at high intensity the pool widens to
adjacent blocks. These physical water blocks are then picked up by the
periodic hasWaterBody() scan and feed back a hydration boost, enabling
succession.
- Player water management: water bucket placement now invalidates the
water-body scan cache for that region so player-built pools are detected
within one player-check cycle. The scan itself is now periodic (every
~10 min) rather than one-shot, so natural pools from puddle formation
are also re-evaluated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GlobalClimateTracker accumulates carbonPpm each sim cycle from total regional
air pollution (emissions) minus total tree-canopy pressure (carbon sink). The
resulting warmingLevel (0–1, representing 0–5°C anomaly) feeds three effects:
- AtmosphereModule subtracts getRainPenalty() (up to -0.15) from every
region's rain target, creating planet-wide drought as warming rises.
- Bootstrap post-sim pass raises drought risk by warming * 0.02 per cycle,
giving warming a second, persistent drought pathway.
- Carbon state persists across sessions in living_world/global_climate.dat
so climate change accumulates over a playthrough.
/lw climate shows: Carbon: 342 ppm (+62 ppm) | Warming: +1.1°C |
Biodiversity: 73% | Rain penalty: -3%
Deforestation accelerates warming; reforestation reverses it — the mod now
rewards long-term forest stewardship at a planetary scale.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Seasons (Step 1)
- Season enum (SPRING/SUMMER/AUTUMN/WINTER) derived from gameTime / 24000 / 8.
- AtmosphereModule applies seasonal rain modifier: spring +10%, summer -15%,
winter -25%. Cascades naturally through water → soil → succession.
- Bootstrap post-sim pass: spring boosts soil fertility (+0.003/cycle),
winter draws moisture out of topsoil (-0.002/cycle).
Player pollution effects (Step 2)
- Poll > 40: nausea (MobEffects.CONFUSION in 1.21.1); > 60: slowness; > 80: weakness.
- Duration 100 ticks, refreshed each PLAYER_CHECK_INTERVAL (1 s). Expires
5 s after the player leaves the polluted region.
Wildfire (Step 3)
- WILDFIRE added to WorldEffectType.
- WorldEffectsModule emits WILDFIRE when droughtRisk > 70 AND thunderLevel > 0.5
with 1% probability per sim cycle.
- NeoForgeWorldEffectExecutor places fire blocks on surface grass/leaves/logs;
Minecraft fire spreading takes over from there.
Fog (Step 4)
- When pollutionScore > 40, 1–3 SMOKE particles sent per check interval at
player eye-level via ServerLevel.sendParticles(player, ...) — per-player only,
not broadcast to everyone.
Water body passive boost (Step 5)
- On first entry to a region, hasWaterBody() samples 30 surface positions.
If ≥5 are water, applyWaterBodyBoost() permanently raises purification
capacity +15, water availability +10, and lowers drought risk -10.
/lw atmosphere command (Step 6)
- Shows: Region (x,z) | Season: Spring | Rain: 72% | Storm: 18%
- Wired via Function<CommandSourceStack, String> atmosphereStatus parameter
added to LivingWorldCommandRoot.registerDeferred().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New AtmosphereModule (9th in pipeline) derives per-region rain/thunder from
ecosystem health and pollution score, smoothly interpolating each sim cycle.
- Tree canopy scrubs air pollution: treePressure × 0.003 removed per cycle,
creating a direct incentive to maintain forests.
- Rain level (0–1) targets ecosystem health / 100 × 0.85; global MC rain adds
a 0.15 bias so natural weather still matters.
- Thunder level targets pollutionScore / 100 × 0.8; acid rain fires when
thunder > 0.4 AND pollution > 20, draining soil fertility and water.
- Regional drought: rain < 0.2 raises drought risk proportionally.
- Per-player ClientboundGameEventPacket (RAIN_LEVEL_CHANGE, THUNDER_LEVEL_CHANGE,
START_RAINING, STOP_RAINING) sent each player-check cycle so each region has
an independent sky without client-side mixins.
- HUD extended with Rain% and Storm% fields when atmosphere data is present.
- Removed global applyWeatherFeedback() from bootstrap; AtmosphereModule owns
all rain/drought/acid-rain simulation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/lw hud toggles a persistent per-player flag stored in LivingWorldBootstrap.
When enabled, the action-bar HUD (Eco/Poll/Soil/Wat) is shown every 20 ticks
alongside the existing compass-based trigger. Flag is cleared on server stop.
Requires operator permission level 2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 1 – Mob spawn feedback: FinalizeSpawnEvent suppresses passive mobs (Animal)
in regions with ecosystem health < 30 (up to 70% cancellation) and hostile mobs
(Monster) in regions > 60 health (up to 50% cancellation).
Step 2 – Compass HUD: holding a compass shows a colour-coded action-bar overlay
with Eco/Poll/Soil/Wat scores for the current region (green/yellow/red banded).
Step 3 – Resource loop closure: logging depletion recovery is already tied to
vegetation pressure in ResourceDepletionModule; sapling-to-tree growth continues
to close the loop via the existing vegetation feedback path.
Step 4 – Agriculture: bone meal (PlayerInteractEvent.RightClickBlock) adds +2
soil fertility; harvesting fully-grown (age=7) crops (BlockEvent.BreakEvent)
drains -0.5 fertility and adds 0.3 farming depletion.
Step 5 – Acid rain: when raining AND region pollution > 20, rainfall drains soil
fertility, raises soil contamination, and reduces water availability proportional
to excess pollution. Implemented in applyWeatherFeedback().
Step 6 – Biome-aware succession: RecoveryRegionData gains a maxSuccessionStage
cap (default MATURE_FOREST). deriveBiomeCap() samples the biome at the region
centre on first player entry and calls setRegionBiomeCap(); deserts/badlands →
SPARSE_GRASS, savannas/mountains/cold → SCRUBLAND, beaches/oceans → GRASSLAND,
forests/taiga/jungle → MATURE_FOREST, plains/swamp → YOUNG_WOODLAND.
Step 7 – Directional wind: spreadPollutionAcrossRegions() drifts a wind angle by
±0.05 rad per sim cycle; each cardinal neighbour's spread rate is multiplied by
(1 + alignment×0.5) where alignment = cos(windAngle − offsetAngle), creating
downwind (×1.5) and upwind (×0.5) asymmetry.
All 400 tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>