Fix two live simulation bugs: profiler crash + regions never updating

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>
This commit is contained in:
George
2026-06-07 16:42:11 +01:00
parent 6e6de00f0d
commit 881f716115
4 changed files with 33 additions and 2 deletions
@@ -1,5 +1,6 @@
package com.livingworld.core.simulation;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@@ -30,6 +31,11 @@ public interface RegionManager {
*/
List<Region> resolveAll(List<RegionCoordinate> coordinates);
/**
* Returns all currently active (cached/loaded) regions.
*/
Collection<Region> getActiveRegions();
/**
* Marks a region as dirty, indicating unsaved changes.
*
@@ -1,5 +1,6 @@
package com.livingworld.core.simulation;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -63,6 +64,11 @@ public final class SimulationManager {
this.scheduler.onMinecraftTick();
if (this.scheduler.shouldRunSimulationCycle()) {
long tick = this.timeService.getSimulationTick();
for (Region r : this.regionManager.getActiveRegions()) {
this.scheduler.queueRegion(new RegionUpdateJob(
r.getCoordinate(), 0, tick, null, UpdateReason.NORMAL_ROLLING_UPDATE));
}
runSimulationCycle();
}
}
@@ -209,7 +215,16 @@ public final class SimulationManager {
long tick = getSimulationTickCounter();
RegionUpdateJob job = new RegionUpdateJob(
coordinate, 100, tick, null, UpdateReason.FORCED_DEBUG_COMMAND);
runModulesForRegion(region.get(), job, tick);
if (this.profiler != null) {
this.profiler.startCycle(tick);
}
try {
runModulesForRegion(region.get(), job, tick);
} finally {
if (this.profiler != null) {
this.profiler.endCycle(0);
}
}
}
/** Returns a profile snapshot of the last completed simulation cycle. */
@@ -233,6 +233,11 @@ class SimulationManagerTest {
return coordinates.stream().map(regions::get).filter(java.util.Objects::nonNull).toList();
}
@Override
public java.util.Collection<Region> getActiveRegions() {
return regions.values();
}
@Override
public void markDirty(Region region) {
markDirtyCount++;
@@ -158,6 +158,11 @@ class LongRunSimulationTest {
.toList();
}
@Override
public Collection<Region> getActiveRegions() {
return orderedRegions;
}
@Override
public void markDirty(Region region) {
region.markDirty();