Add forced simulation command

This commit is contained in:
George
2026-06-07 12:59:25 +01:00
parent 0358c2e5bf
commit 11d8c2db49
4 changed files with 80 additions and 5 deletions
@@ -55,9 +55,14 @@ public final class LivingWorldCommandRoot {
.executes(context -> listModules( .executes(context -> listModules(
context.getSource(), moduleRegistry)))) context.getSource(), moduleRegistry))))
.then(Commands.literal("simulate") .then(Commands.literal("simulate")
.then(Commands.argument("ticks", IntegerArgumentType.integer(1, 1000)) .then(Commands.argument(
.executes(context -> placeholder( "ticks",
context.getSource(), "Forced simulation is not connected yet."))))); IntegerArgumentType.integer(
1, SimulationManager.MAX_FORCED_SIMULATION_TICKS))
.executes(context -> SimulateCommand.execute(
context.getSource(),
simulationManager,
IntegerArgumentType.getInteger(context, "ticks"))))));
} }
private static int showStatus( private static int showStatus(
@@ -0,0 +1,47 @@
package com.livingworld.commands;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import com.livingworld.core.simulation.SimulationManager;
/**
* Runs a bounded number of forced Living World simulation cycles.
*/
public final class SimulateCommand {
private SimulateCommand() {
}
public static int execute(
CommandSourceStack source,
SimulationManager simulationManager,
int ticks) {
if (source == null) {
throw new IllegalArgumentException("source must not be null");
}
if (simulationManager == null) {
throw new IllegalArgumentException("simulationManager must not be null");
}
if (ticks <= 0 || ticks > SimulationManager.MAX_FORCED_SIMULATION_TICKS) {
source.sendFailure(Component.literal(
"Ticks must be between 1 and "
+ SimulationManager.MAX_FORCED_SIMULATION_TICKS + "."));
return 0;
}
try {
simulationManager.runForcedSimulationTicks(ticks);
source.sendSuccess(
() -> Component.literal(
"Ran " + ticks + " Living World simulation ticks."),
true);
return ticks;
} catch (RuntimeException exception) {
source.sendFailure(Component.literal(
"Simulation failed: " + exception.getMessage()));
return 0;
}
}
}
@@ -23,6 +23,8 @@ import com.livingworld.regions.Region;
*/ */
public final class SimulationManager { public final class SimulationManager {
public static final int MAX_FORCED_SIMULATION_TICKS = 1000;
private final SimulationScheduler scheduler; private final SimulationScheduler scheduler;
private final RegionManager regionManager; private final RegionManager regionManager;
private final ModuleRegistry moduleRegistry; private final ModuleRegistry moduleRegistry;
@@ -152,9 +154,12 @@ public final class SimulationManager {
* Runs forced simulation ticks for debugging or testing. * Runs forced simulation ticks for debugging or testing.
*/ */
public void runForcedSimulationTicks(int ticks) { public void runForcedSimulationTicks(int ticks) {
if (ticks <= 0 || ticks > 100) { if (ticks <= 0 || ticks > MAX_FORCED_SIMULATION_TICKS) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format("forced ticks must be in range [1, 100], got: %d", ticks)); String.format(
"forced ticks must be in range [1, %d], got: %d",
MAX_FORCED_SIMULATION_TICKS,
ticks));
} }
for (int i = 0; i < ticks; i++) { for (int i = 0; i < ticks; i++) {
@@ -3,6 +3,8 @@ package com.livingworld.core.simulation;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -86,6 +88,22 @@ class SimulationManagerTest {
assertEquals(0, fixture.scheduler.getMinecraftTickCounter()); assertEquals(0, fixture.scheduler.getMinecraftTickCounter());
} }
@Test
void forcedTicksAcceptSafeMaximumAndRejectValuesOutsideRange() {
Fixture fixture = new Fixture(25);
assertDoesNotThrow(() -> fixture.manager.runForcedSimulationTicks(
SimulationManager.MAX_FORCED_SIMULATION_TICKS));
assertEquals(
SimulationManager.MAX_FORCED_SIMULATION_TICKS,
fixture.timeService.getSimulationTick());
assertThrows(IllegalArgumentException.class,
() -> fixture.manager.runForcedSimulationTicks(0));
assertThrows(IllegalArgumentException.class,
() -> fixture.manager.runForcedSimulationTicks(
SimulationManager.MAX_FORCED_SIMULATION_TICKS + 1));
}
@Test @Test
void timeBudgetRequeuesJobsNotYetProcessed() { void timeBudgetRequeuesJobsNotYetProcessed() {
Fixture fixture = new Fixture(1); Fixture fixture = new Fixture(1);