Skip to content

Lich Character Watchdog ‐ Windows

Ryan P. McKinnon edited this page May 20, 2026 · 6 revisions

Lich Character Watchdog (Windows)

A PowerShell-based watchdog that ensures a specific Lich character session stays running on Windows. Detects whether the character is currently active using two signals (session file + process check) and relaunches Lich if either is missing. Scheduled via Task Scheduler.

Note

Linux and macOS instructions live on separate wiki pages:

Table of Contents

How it works

The watchdog considers a character "running" only if both of these are true:

  1. Session file present — Lich writes %TEMP%\simutronics\sessions\<CharacterName>.session while connected.
  2. Matching process alive — A rubyw.exe process exists whose command line references lich and --login <CharacterName>. If either signal is missing, the watchdog launches Lich with the configured arguments.

Why both checks?

Scenario Session file Process Watchdog action
Normal running session Do nothing
Clean disconnect Relaunch
Lich crashed hard (stale session file) Relaunch
Lich starting up (process before session) Relaunch*

Note

The startup race window is small; with a 10-minute schedule it's very unlikely to trigger a duplicate launch. If you see duplicate launches in the log, that's the cause.

The script

Save this as C:\Lich5\watchdog.ps1 (or wherever you prefer).

<#
.SYNOPSIS
  Ensures a specific Lich character session is running. Launches if not.
.DESCRIPTION
  Considers the character "running" only if BOTH are true:
    1. Lich's session file exists at %TEMP%\simutronics\sessions\<Char>.session
    2. A rubyw.exe process is alive whose command line references lich + --login <Char>
  Designed to run on a recurring schedule via Task Scheduler.
#>
 
# ---- Config ----------------------------------------------------------------
$CharacterName = 'Mycharname'
$RubyExe       = 'C:\Ruby34-x64\bin\rubyw.exe'
$LichDir       = 'C:\Lich5'
$LichScript    = 'lich.rbw'
$LichArgs      = @('--login', $CharacterName, '--gemstone', '--wizard')
$LogFile       = 'C:\Lich5\logs\watchdog.log'
 
$SessionFile   = Join-Path $env:TEMP "simutronics\sessions\$CharacterName.session"
# ---------------------------------------------------------------------------
 
function Write-Log {
    param([string]$Message)
    $stamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
    $line  = "[$stamp] $Message"
    try {
        $dir = Split-Path -Parent $LogFile
        if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
        Add-Content -Path $LogFile -Value $line
    } catch { }
}
 
function Test-SessionFile {
    param([string]$Path)
    Test-Path -LiteralPath $Path
}
 
function Test-LichProcess {
    param([string]$Character)
 
    # Match rubyw.exe whose command line references lich AND --login <Character>.
    # Case-insensitive; tolerates either "--login Name" or "--login=Name".
    $escaped = [regex]::Escape($Character)
    $pattern = "(?i)lich.*--login[\s=]+$escaped\b"
 
    $procs = Get-CimInstance Win32_Process -Filter "Name = 'rubyw.exe'" -ErrorAction SilentlyContinue |
             Where-Object { $_.CommandLine -and $_.CommandLine -match $pattern }
 
    return [bool]$procs
}
 
try {
    $sessionOk = Test-SessionFile -Path $SessionFile
    $processOk = Test-LichProcess -Character $CharacterName
 
    if ($sessionOk -and $processOk) {
        Write-Log "OK: $CharacterName running (session file + process both present)."
        exit 0
    }
 
    # Log which check failed, useful for diagnosing flapping
    $reason = @()
    if (-not $sessionOk) { $reason += 'no session file' }
    if (-not $processOk) { $reason += 'no matching rubyw.exe' }
    Write-Log "Not running for $CharacterName ($($reason -join ', ')). Launching..."
 
    if (-not (Test-Path $RubyExe)) {
        Write-Log "ERROR: rubyw.exe not found at $RubyExe"; exit 2
    }
    if (-not (Test-Path (Join-Path $LichDir $LichScript))) {
        Write-Log "ERROR: $LichScript not found in $LichDir"; exit 2
    }
 
    $allArgs = @($LichScript) + $LichArgs
    Start-Process -FilePath $RubyExe `
                  -ArgumentList $allArgs `
                  -WorkingDirectory $LichDir | Out-Null
 
    Write-Log "Launched: $RubyExe $($allArgs -join ' ')"
    exit 0
}
catch {
    Write-Log "ERROR: $($_.Exception.Message)"
    exit 1
}

Configuration

Edit the variables at the top of the script:

Variable Description Example
$CharacterName Character name as it appears in --login 'Mycharname'
$RubyExe Full path to rubyw.exe 'C:\Ruby34-x64\bin\rubyw.exe'
$LichDir Lich install directory 'C:\Lich5'
$LichScript Lich entry script 'lich.rbw'
$LichArgs Launch arguments (must match how you normally launch) @('--login', $CharacterName, '--gemstone', '--wizard')
$LogFile Where to write watchdog activity 'C:\Lich5\logs\watchdog.log'

Verifying the regex matches your real launch

Before scheduling, launch Lich the normal way for your character, then run this from a PowerShell window:

Get-CimInstance Win32_Process -Filter "Name = 'rubyw.exe'" |
  Select-Object ProcessId, CommandLine | Format-List

Look at the actual CommandLine. The regex (?i)lich.*--login[\s=]+<name>\b expects something like:

  • ... lich.rbw --login Mycharname --gemstone --wizard
  • ... lich.rbw --login=Mycharname ...
  • ... rubyw.exe lich.rbw --login "Mycharname" ... (quoted name won't match) If your real command quotes the name, change the pattern in Test-LichProcess from --login[\s=]+$escaped\b to --login[\s=]+"?$escaped"?\b.

Task Scheduler setup

Create the task

  1. Open Task Scheduler (taskschd.msc).
  2. In the right pane, click Create Task... (not "Create Basic Task" — you need the full dialog).

General tab

  • Name: Lich Watchdog - Mycharname (or similar)
  • Description: (optional) "Restarts Lich for Mycharname if it stops running"
  • Security options:
    • Select Run only when user is logged on — required, because WizardFE needs an interactive desktop
    • Leave Run with highest privileges unchecked unless your Lich install specifically requires it

Triggers tab

Click New... and configure:

  • Begin the task: At log on
  • Specific user: your user account
  • Advanced settings:
    • Check Repeat task every: 10 minutes
    • for a duration of: Indefinitely
    • Check Enabled Click OK.

Tip

"10 minutes" isn't in the default dropdown. Type it directly into the field — Task Scheduler accepts custom intervals like 10 minutes, 15 minutes, etc.

Actions tab

Click New... and configure:

  • Action: Start a program
  • Program/script: powershell.exe
  • Add arguments:
    -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "C:\Lich5\watchdog.ps1"
    
  • Start in: (leave blank — Start-Process uses -WorkingDirectory explicitly) Click OK.

Conditions tab

  • Uncheck Start the task only if the computer is on AC power if you're on a laptop and want it to run on battery too.
  • Leave the rest at defaults.

Settings tab

  • Check Allow task to be run on demand
  • Check If the running task does not end when requested, force it to stop
  • If the task is already running, the following rule applies: Do not start a new instance — important, prevents overlapping runs from stacking
  • Leave other defaults Click OK to save the task. You may be prompted for your Windows password.

Testing

Before trusting the schedule, verify all four scenarios behave correctly. Run the script manually each time with:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Lich5\watchdog.ps1"

Then check C:\Lich5\logs\watchdog.log for the result.

# Setup Expected log line Expected behavior
1 Lich already running for the character OK: ... both present No relaunch
2 Lich cleanly exited (no process, no session file) Not running (no session file, no matching rubyw.exe) Relaunch
3 Kill rubyw.exe from Task Manager (stale session file remains) Not running (no matching rubyw.exe) Relaunch
4 Delete the session file manually but leave process running Not running (no session file) Relaunch*

Warning

Scenario 4 will cause a duplicate launch, which is the expected tradeoff for catching scenario 3.

Verifying the scheduled task

After creating the task, right-click it in Task Scheduler and choose Run to fire it immediately. Check the History tab and the watchdog log to confirm it executed cleanly.

Troubleshooting

Log file isn't being created The script tries to create the log directory but the user account running the task needs write permission to the parent directory. If `C:\Lich5\logs\` doesn't appear, check permissions or change `$LogFile` to somewhere under your user profile (e.g., `"$env:USERPROFILE\lich-watchdog.log"`).
Watchdog always relaunches even though Lich is running The process regex isn't matching your real command line. Run the `Get-CimInstance` diagnostic shown above, look at the actual `CommandLine`, and adjust the pattern in `Test-LichProcess` to match.
Watchdog never relaunches even though Lich is dead Most likely `$env:TEMP` isn't resolving to the location Lich uses. The script assumes the task runs as your interactive user (which is why "Run only when user is logged on" matters). If you ever switch to "Run whether user is logged on or not" or run as SYSTEM, `$env:TEMP` becomes `C:\Windows\Temp` and the session-file check will always fail. Add a diagnostic line near the top of the script to confirm:
Write-Log "Looking for session file at: $SessionFile"
Task runs but no window appears The task is probably configured with "Run whether user is logged on or not" — switch to "Run only when user is logged on" so the launched WizardFE has a desktop to draw on.
Multiple Lich instances spawn Either (a) the regex isn't matching your real command line so it thinks nothing is running, or (b) you hit the startup race window where the process was alive but the session file wasn't written yet. The log will tell you which: look for "no matching rubyw.exe" (case a) vs "no session file" (case b).
## Running multiple characters

To watchdog more than one character, make a copy of the script per character (watchdog-charA.ps1, watchdog-charB.ps1), edit $CharacterName and $LichArgs in each, and create a separate scheduled task pointing to each script. The process regex matches on the specific character name, so they won't interfere with each other.

Clone this wiki locally