Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ For the generated parameter-level reference pulled straight from the annotated s

Lua authoring note: `rimbridge/run_lua` is intentionally a lowered Lua subset, not general-purpose Lua. Start with `rimbridge/get_lua_reference` and `rimbridge/compile_lua`; prefer `local` bindings, `rb.call`/`rb.poll`, static field access, and static one-based indexes such as `names[1]`. Dynamic indexing such as `names[i]`, arbitrary global assignment, and most broader Lua features are rejected in v1.

Playback default: RimBridgeServer enables RimWorld's private `TickManager.UltraSpeedBoost` process-wide at startup. Speed 4 (`Ultrafast`) therefore uses the boosted debug rate by default until `rimworld/set_time_speed` changes the flag; the next RimWorld startup enables it again.
Playback default: RimWorld's private `TickManager.UltraSpeedBoost` is left off, so having the mod installed does not change speed 4 (`Ultrafast`) for normal play. `rimworld/play_for` and `rimworld/play_until_letter` turn the boost on for the duration of an `Ultrafast` run (or a `forceRequestedSpeed` run) and restore the previous value afterwards; `rimworld/set_time_speed` with `ultraSpeedBoost` sets it explicitly for the rest of the session.

<!-- BEGIN GENERATED:tool-surface -->

Expand Down Expand Up @@ -339,9 +339,9 @@ Playback default: RimBridgeServer enables RimWorld's private `TickManager.UltraS
### Debug Actions And Mods

- `rimworld/pause_game` - Pause or unpause the game
- `rimworld/set_time_speed` - Set RimWorld's current time speed directly; RimBridgeServer enables the boost for speed 4 (Ultrafast) at startup, and this tool can change that state
- `rimworld/play_for` - Unpause the current game at a requested time speed for a bounded real-time duration, then pause it again; speed 4 (Ultrafast) is boosted by default, and forced-normal-speed slowdown can optionally be suppressed during the run
- `rimworld/play_until_letter` - Unpause the current game at a requested time speed until a new right-side letter appears, then pause and return the new letter payload; speed 4 (Ultrafast) is boosted by default, and forced-normal-speed slowdown can optionally be suppressed during the run
- `rimworld/set_time_speed` - Set RimWorld's current time speed directly; the boost for speed 4 (Ultrafast) is off by default, and this tool can turn it on or off
- `rimworld/play_for` - Unpause the current game at a requested time speed for a bounded real-time duration, then pause it again; speed 4 (Ultrafast) is boosted for the duration of the run, and forced-normal-speed slowdown can optionally be suppressed during the run
- `rimworld/play_until_letter` - Unpause the current game at a requested time speed until a new right-side letter appears, then pause and return the new letter payload; speed 4 (Ultrafast) is boosted for the duration of the run, and forced-normal-speed slowdown can optionally be suppressed during the run
- `rimworld/step_game_ticks` - Advance the paused game by an exact number of ticks, one tick per Unity update frame, mirroring RimWorld's Dev_TickOnce path while preserving render-frame boundaries
- `rimworld/list_debug_action_roots` - List top-level RimWorld debug action roots using stable internal debug-action paths
- `rimworld/list_debug_action_children` - List direct children of a RimWorld debug action path
Expand Down
88 changes: 52 additions & 36 deletions Source/LifecycleCapabilityModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,9 @@ public object PlayFor(int durationMs, string speed = "Normal", int pollIntervalM
{
previousNeverForceNormalSpeed = DebugViewSettings.neverForceNormalSpeed;
DebugViewSettings.neverForceNormalSpeed = true;
previousUltraSpeedBoost = TrySetUltraSpeedBoost(true);
}
if (ShouldBoostPlayback(parsedSpeed, forceRequestedSpeed) && previousUltraSpeedBoost == null)
previousUltraSpeedBoost = TrySetUltraSpeedBoost(true);
stopwatch = Stopwatch.StartNew();
EnsurePlaybackRunning(parsedSpeed);
}, timeoutMs: 5000),
Expand Down Expand Up @@ -240,23 +241,11 @@ public object PlayFor(int durationMs, string speed = "Normal", int pollIntervalM
}
catch (Exception ex)
{
if (forceRequestedSpeed && previousNeverForceNormalSpeed != null)
{
try
{
restoredNeverForceNormalSpeed = RimBridgeMainThread.Invoke(
() => RestoreNeverForceNormalSpeed(previousNeverForceNormalSpeed),
timeoutMs: 5000);
restoredUltraSpeedBoost = RimBridgeMainThread.Invoke(
() => RestoreUltraSpeedBoost(previousUltraSpeedBoost),
timeoutMs: 5000);
}
catch
{
restoredNeverForceNormalSpeed = null;
restoredUltraSpeedBoost = null;
}
}
RestorePlaybackOverridesAfterFailure(
previousNeverForceNormalSpeed,
previousUltraSpeedBoost,
ref restoredNeverForceNormalSpeed,
ref restoredUltraSpeedBoost);

return new
{
Expand Down Expand Up @@ -382,8 +371,9 @@ public object PlayUntilLetter(int timeoutMs = 1800000, string speed = "Normal",
{
previousNeverForceNormalSpeed = DebugViewSettings.neverForceNormalSpeed;
DebugViewSettings.neverForceNormalSpeed = true;
previousUltraSpeedBoost = TrySetUltraSpeedBoost(true);
}
if (ShouldBoostPlayback(parsedSpeed, forceRequestedSpeed) && previousUltraSpeedBoost == null)
previousUltraSpeedBoost = TrySetUltraSpeedBoost(true);
EnsurePlaybackRunning(parsedSpeed);
}, timeoutMs: 5000);

Expand Down Expand Up @@ -435,6 +425,9 @@ public object PlayUntilLetter(int timeoutMs = 1800000, string speed = "Normal",
restoredNeverForceNormalSpeed = RimBridgeMainThread.Invoke(
() => RestoreNeverForceNormalSpeed(previousNeverForceNormalSpeed),
timeoutMs: 5000);
}
if (previousUltraSpeedBoost != null)
{
restoredUltraSpeedBoost = RimBridgeMainThread.Invoke(
() => RestoreUltraSpeedBoost(previousUltraSpeedBoost),
timeoutMs: 5000);
Expand Down Expand Up @@ -479,23 +472,11 @@ public object PlayUntilLetter(int timeoutMs = 1800000, string speed = "Normal",
}
catch (Exception ex)
{
if (forceRequestedSpeed && previousNeverForceNormalSpeed != null)
{
try
{
restoredNeverForceNormalSpeed = RimBridgeMainThread.Invoke(
() => RestoreNeverForceNormalSpeed(previousNeverForceNormalSpeed),
timeoutMs: 5000);
restoredUltraSpeedBoost = RimBridgeMainThread.Invoke(
() => RestoreUltraSpeedBoost(previousUltraSpeedBoost),
timeoutMs: 5000);
}
catch
{
restoredNeverForceNormalSpeed = null;
restoredUltraSpeedBoost = null;
}
}
RestorePlaybackOverridesAfterFailure(
previousNeverForceNormalSpeed,
previousUltraSpeedBoost,
ref restoredNeverForceNormalSpeed,
ref restoredUltraSpeedBoost);

try
{
Expand Down Expand Up @@ -1384,4 +1365,39 @@ private static void PauseIfNeeded()
UltraSpeedBoostField?.SetValue(null, previousUltraSpeedBoost.Value);
return TryReadUltraSpeedBoost();
}

// The boost is process-wide and also affects normal play, so playback only holds it for the
// duration of a run that asks for Ultrafast (or forces the requested speed) and then restores it.
private static bool ShouldBoostPlayback(TimeSpeed speed, bool forceRequestedSpeed)
{
return forceRequestedSpeed || speed == TimeSpeed.Ultrafast;
}

private static void RestorePlaybackOverridesAfterFailure(
bool? previousNeverForceNormalSpeed,
bool? previousUltraSpeedBoost,
ref bool? restoredNeverForceNormalSpeed,
ref bool? restoredUltraSpeedBoost)
{
try
{
if (previousNeverForceNormalSpeed != null)
{
restoredNeverForceNormalSpeed = RimBridgeMainThread.Invoke(
() => RestoreNeverForceNormalSpeed(previousNeverForceNormalSpeed),
timeoutMs: 5000);
}
if (previousUltraSpeedBoost != null)
{
restoredUltraSpeedBoost = RimBridgeMainThread.Invoke(
() => RestoreUltraSpeedBoost(previousUltraSpeedBoost),
timeoutMs: 5000);
}
}
catch
{
restoredNeverForceNormalSpeed = null;
restoredUltraSpeedBoost = null;
}
}
}
5 changes: 2 additions & 3 deletions Source/RimBridgeStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,8 @@ private static void TryStart()
typeof(DebugViewSettings)
.GetField("showTpsCounter", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
?.SetValue(null, true);
typeof(TickManager)
.GetField("UltraSpeedBoost", BindingFlags.Static | BindingFlags.NonPublic)
?.SetValue(null, true);
// TickManager.UltraSpeedBoost is left alone here: it changes speed 4 for normal play too
// (and for mods that mirror vanilla's boost branch). Playback tools enable it per run.
}

RimBridgeTools tools;
Expand Down
18 changes: 9 additions & 9 deletions Source/RimBridgeTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,33 +249,33 @@ public object PauseGame([ToolParameter(Description = "True to pause, false to un
return InvokeAlias(Arguments((nameof(pause), pause)));
}

[ReadmeTool("Debug Actions And Mods", "Set RimWorld's current time speed directly; RimBridgeServer enables the boost for speed 4 (Ultrafast) at startup, and this tool can change that state")]
[Tool("rimworld/set_time_speed", Description = "Set RimWorld's current time speed directly; RimBridgeServer enables the boost for speed 4 (Ultrafast) at startup, and this tool can change that state")]
[ReadmeTool("Debug Actions And Mods", "Set RimWorld's current time speed directly; the boost for speed 4 (Ultrafast) is off by default, and this tool can turn it on or off")]
[Tool("rimworld/set_time_speed", Description = "Set RimWorld's current time speed directly; the boost for speed 4 (Ultrafast) is off by default, and this tool can turn it on or off")]
public object SetTimeSpeed(
[ToolParameter(Description = "Desired time speed: Paused, Normal, Fast, Superfast, or Ultrafast")] string speed = "Normal",
[ToolParameter(Description = "When set, change RimWorld's private TickManager.UltraSpeedBoost flag. RimBridgeServer enables it at startup; leave null to preserve the current value.")] bool? ultraSpeedBoost = null)
[ToolParameter(Description = "When set, change RimWorld's private TickManager.UltraSpeedBoost flag. It is off by default and process-wide, so it also affects normal play until turned off; leave null to preserve the current value.")] bool? ultraSpeedBoost = null)
{
return InvokeAlias(Arguments((nameof(speed), speed), (nameof(ultraSpeedBoost), ultraSpeedBoost)));
}

[ReadmeTool("Debug Actions And Mods", "Unpause the current game at a requested time speed for a bounded real-time duration, then pause it again; speed 4 (Ultrafast) is boosted by default, and forced-normal-speed slowdown can optionally be suppressed during the run")]
[Tool("rimworld/play_for", Description = "Unpause the current game at a requested time speed for a bounded real-time duration, then pause it again; speed 4 (Ultrafast) is boosted by default, and forced-normal-speed slowdown can optionally be suppressed during the run")]
[ReadmeTool("Debug Actions And Mods", "Unpause the current game at a requested time speed for a bounded real-time duration, then pause it again; speed 4 (Ultrafast) is boosted for the duration of the run, and forced-normal-speed slowdown can optionally be suppressed during the run")]
[Tool("rimworld/play_for", Description = "Unpause the current game at a requested time speed for a bounded real-time duration, then pause it again; speed 4 (Ultrafast) is boosted for the duration of the run, and forced-normal-speed slowdown can optionally be suppressed during the run")]
public object PlayFor(
[ToolParameter(Description = "Real-time duration in milliseconds to keep the game unpaused before pausing it again")] int durationMs,
[ToolParameter(Description = "Desired play speed while the game is running: Normal, Fast, Superfast, or Ultrafast")] string speed = "Normal",
[ToolParameter(Description = "How often to poll playback state while waiting to repause")] int pollIntervalMs = 25,
[ToolParameter(Description = "When true, temporarily suppress RimWorld's forced-normal-speed slowdown and ensure TickManager.UltraSpeedBoost is enabled while preserving the normal TickManager update path, then restore both prior values. The boost is enabled at bridge startup by default.")] bool forceRequestedSpeed = false)
[ToolParameter(Description = "When true, temporarily suppress RimWorld's forced-normal-speed slowdown and ensure TickManager.UltraSpeedBoost is enabled while preserving the normal TickManager update path, then restore both prior values. Ultrafast runs enable the boost for the run even without this flag.")] bool forceRequestedSpeed = false)
{
return InvokeAlias(Arguments((nameof(durationMs), durationMs), (nameof(speed), speed), (nameof(pollIntervalMs), pollIntervalMs), (nameof(forceRequestedSpeed), forceRequestedSpeed)));
}

[ReadmeTool("Debug Actions And Mods", "Unpause the current game at a requested time speed until a new right-side letter appears, then pause and return the new letter payload; speed 4 (Ultrafast) is boosted by default, and forced-normal-speed slowdown can optionally be suppressed during the run")]
[Tool("rimworld/play_until_letter", Description = "Unpause the current game at a requested time speed until a new right-side letter appears, then pause and return the new letter payload; speed 4 (Ultrafast) is boosted by default, and forced-normal-speed slowdown can optionally be suppressed during the run")]
[ReadmeTool("Debug Actions And Mods", "Unpause the current game at a requested time speed until a new right-side letter appears, then pause and return the new letter payload; speed 4 (Ultrafast) is boosted for the duration of the run, and forced-normal-speed slowdown can optionally be suppressed during the run")]
[Tool("rimworld/play_until_letter", Description = "Unpause the current game at a requested time speed until a new right-side letter appears, then pause and return the new letter payload; speed 4 (Ultrafast) is boosted for the duration of the run, and forced-normal-speed slowdown can optionally be suppressed during the run")]
public object PlayUntilLetter(
[ToolParameter(Description = "Maximum real-time wait in milliseconds before pausing and timing out")] int timeoutMs = 1800000,
[ToolParameter(Description = "Desired play speed while waiting: Normal, Fast, Superfast, or Ultrafast")] string speed = "Normal",
[ToolParameter(Description = "How often to poll the letter stack while waiting")] int pollIntervalMs = 250,
[ToolParameter(Description = "When true, temporarily suppress RimWorld's forced-normal-speed slowdown and ensure TickManager.UltraSpeedBoost is enabled while preserving the normal TickManager update path, then restore both prior values. The boost is enabled at bridge startup by default.")] bool forceRequestedSpeed = false,
[ToolParameter(Description = "When true, temporarily suppress RimWorld's forced-normal-speed slowdown and ensure TickManager.UltraSpeedBoost is enabled while preserving the normal TickManager update path, then restore both prior values. Ultrafast runs enable the boost for the run even without this flag.")] bool forceRequestedSpeed = false,
[ToolParameter(Description = "When false, letters already present when the wait starts are ignored and only newly added letters complete the wait")] bool includeExistingLetters = false)
{
return InvokeAlias(Arguments((nameof(timeoutMs), timeoutMs), (nameof(speed), speed), (nameof(pollIntervalMs), pollIntervalMs), (nameof(forceRequestedSpeed), forceRequestedSpeed), (nameof(includeExistingLetters), includeExistingLetters)));
Expand Down
Loading