Skip to content

Launcher

Start and stop the Resonite client through umu-launcher — pure host process control, no gRPC. launch spawns the umu-run chain and PID-diffs the engine and renderer processes into existence; terminate stages SIGTERMSIGKILL over the two PIDs (or auto-detects the single running instance). The cooperative gRPC quit is resoio.shutdown. See the CLI resoio launch / resoio terminate commands.

Pass Resonite's own command-line launch options as a typed LaunchOptions via launch(options=...) (-DataPath, -CachePath, -Screen, -Verbose, …) instead of hand-assembling raw flag strings. Anything LaunchOptions does not model can still be passed through launch(extra_args=...).

resoio.launcher.launch

launch(
    *,
    resonite_exe: str | None = None,
    mod_path: str | None = None,
    vanilla: bool = False,
    options: LaunchOptions | None = None,
    extra_args: Sequence[str] = (),
    prefix: str | None = None,
    proton_path: str | None = None,
    name: str | None = None,
    wait_timeout: float = 60.0,
    poll_interval: float = 0.5,
) -> LaunchResult

Launch Resonite (engine + renderer) via umu-launcher and return both PIDs.

Spawns the umu-run chain detached (the caller can exit without taking Resonite down), then PID-diffs the engine and renderer processes into existence and returns their host PIDs. Pure process control — no gRPC.

PARAMETER DESCRIPTION
resonite_exe

Path to Resonite.exe. None resolves it from the RESONITE_EXE env var, then the default Steam install path.

TYPE: str | None DEFAULT: None

mod_path

Gale profile directory holding the ResoniteIO mod. None resolves it from MOD_PATH; with neither set this raises (unless vanilla). The profile must already have the mod deployed.

TYPE: str | None DEFAULT: None

vanilla

Launch without loading any mod (skips the mod-profile checks).

TYPE: bool DEFAULT: False

options

Typed Resonite launch options (-DataPath, -Screen, …). None uses the defaults, which still skip the intro tutorial. See :class:LaunchOptions.

TYPE: LaunchOptions | None DEFAULT: None

extra_args

Extra arguments forwarded to Resonite.exe after options. The raw escape hatch for anything options does not model; reconciling overlaps with options is the caller's job.

TYPE: Sequence[str] DEFAULT: ()

prefix

Wine prefix directory (WINEPREFIX). None lets umu use its default (~/Games/umu/$GAMEID).

TYPE: str | None DEFAULT: None

proton_path

Proton build (PROTONPATH) — a compat-tools name like GE-Proton or a path. None resolves it from PROTONPATH, then GE-Proton.

TYPE: str | None DEFAULT: None

name

Convenience label for multi-launch. Purely auto-allocates an isolated data tree under RESONITE_IO_INSTANCES_DIR (default ~/.resonite-io/instances/<name>/): the WINEPREFIX and the -DataPath / -CachePath / -LogsPath slots are auto-filled from it (explicit prefix / options values still win). It does not gate launching. None simply leaves those paths at their defaults — run several instances yourself by passing a distinct --data-path (and prefix) per launch. Either way launch always injects a unique per-instance Camera IPC queue token (so the engine and renderer agree on the queue and concurrent instances do not cross-talk), and never refuses just because another instance is already running.

TYPE: str | None DEFAULT: None

wait_timeout

Seconds to wait for both processes to appear.

TYPE: float DEFAULT: 60.0

poll_interval

Seconds between process-table polls.

TYPE: float DEFAULT: 0.5

RETURNS DESCRIPTION
The

class:LaunchResult with the engine and renderer host PIDs.

TYPE: LaunchResult

RAISES DESCRIPTION
LauncherError

Resonite.exe / the mod / umu-run is missing, more than one new engine/renderer appears from this launch, or a process did not appear before wait_timeout. Launch never refuses because another instance is already running — that is the caller's choice.

Source code in src/resoio/launcher.py
def launch(
    *,
    resonite_exe: str | None = None,
    mod_path: str | None = None,
    vanilla: bool = False,
    options: LaunchOptions | None = None,
    extra_args: Sequence[str] = (),
    prefix: str | None = None,
    proton_path: str | None = None,
    name: str | None = None,
    wait_timeout: float = 60.0,
    poll_interval: float = 0.5,
) -> LaunchResult:
    """Launch Resonite (engine + renderer) via umu-launcher and return both
    PIDs.

    Spawns the umu-run chain detached (the caller can exit without taking
    Resonite down), then PID-diffs the engine and renderer processes into
    existence and returns their host PIDs. Pure process control — no gRPC.

    Args:
        resonite_exe: Path to ``Resonite.exe``. ``None`` resolves it from the
            ``RESONITE_EXE`` env var, then the default Steam install path.
        mod_path: Gale profile directory holding the ResoniteIO mod. ``None``
            resolves it from ``MOD_PATH``; with neither set this raises (unless
            ``vanilla``). The profile must already have the mod deployed.
        vanilla: Launch without loading any mod (skips the mod-profile checks).
        options: Typed Resonite launch options (``-DataPath``, ``-Screen``, …).
            ``None`` uses the defaults, which still skip the intro tutorial.
            See :class:`LaunchOptions`.
        extra_args: Extra arguments forwarded to ``Resonite.exe`` after
            ``options``. The raw escape hatch for anything ``options`` does not
            model; reconciling overlaps with ``options`` is the caller's job.
        prefix: Wine prefix directory (``WINEPREFIX``). ``None`` lets umu use its
            default (``~/Games/umu/$GAMEID``).
        proton_path: Proton build (``PROTONPATH``) — a compat-tools name like
            ``GE-Proton`` or a path. ``None`` resolves it from ``PROTONPATH``,
            then ``GE-Proton``.
        name: Convenience label for **multi-launch**. Purely auto-allocates an
            isolated data tree under ``RESONITE_IO_INSTANCES_DIR`` (default
            ``~/.resonite-io/instances/<name>/``): the WINEPREFIX and the
            ``-DataPath`` / ``-CachePath`` / ``-LogsPath`` slots are auto-filled
            from it (explicit ``prefix`` / ``options`` values still win). It does
            **not** gate launching. ``None`` simply leaves those paths at their
            defaults — run several instances yourself by passing a distinct
            ``--data-path`` (and prefix) per launch. Either way ``launch`` always
            injects a unique per-instance Camera IPC queue token (so the engine and
            renderer agree on the queue and concurrent instances do not cross-talk),
            and never refuses just because another instance is already running.
        wait_timeout: Seconds to wait for both processes to appear.
        poll_interval: Seconds between process-table polls.

    Returns:
        The :class:`LaunchResult` with the engine and renderer host PIDs.

    Raises:
        LauncherError: Resonite.exe / the mod / ``umu-run`` is missing, more than
            one new engine/renderer appears from this launch, or a process did not
            appear before ``wait_timeout``. Launch never refuses because another
            instance is already running — that is the caller's choice.
    """
    exe = _resolve_resonite_exe(resonite_exe)
    install_dir = os.path.dirname(os.path.realpath(exe))

    # Always inject a unique Camera IPC queue token so the engine and its renderer
    # bind the same queue. A named launch derives it from the label data tree; an
    # unnamed launch gets a random one. The token MUST be in the env before exec:
    # the engine mod would otherwise self-generate one at runtime, which the
    # renderer (a Wine child) never inherits, so the two bind different queues and
    # the client freezes after both processes start.
    camera_queue: str
    if name is not None:
        _validate_instance_name(name)
        prefix, options, camera_queue = _resolve_instance_paths(name, prefix, options)
    else:
        camera_queue = f"resonite-io-camera-{secrets.token_hex(8)}"

    argv, env, log_path = _build_command(
        exe,
        install_dir,
        mod_path,
        vanilla,
        extra_args,
        options=options,
        prefix=prefix,
        proton_path=proton_path,
        camera_queue=camera_queue,
    )
    _logger.info("launching Resonite: %s (cwd=%s)", " ".join(argv), install_dir)

    # Snapshot existing engine/renderer PIDs so _wait_for_new can pick out the
    # ones this launch spawns by set difference (argv0 matching cannot tell two
    # instances of the same install apart).
    before_engines = set(_find_engine_pids(install_dir))
    before_renderers = set(_find_renderer_pids(install_dir))

    # Detach fully: new session + closed stdio so the parent (CLI / caller) can
    # exit without signalling Resonite. PROTON_SET_GAME_DRIVE=0 (set in
    # _build_command) suppresses umu's game-drive mapping of $HOME onto a Wine
    # drive letter, so even a $HOME-resident install resolves the renderer's
    # absolute Unix paths (renderer exe / /dev/shm IPC) correctly.
    stdout: object = subprocess.DEVNULL
    log_handle = None
    if log_path is not None:
        log_handle = open(log_path, "ab")
        stdout = log_handle
    try:
        subprocess.Popen(  # noqa: S603 - argv built from validated paths
            argv,
            cwd=install_dir,
            env=env,
            stdin=subprocess.DEVNULL,
            stdout=stdout,  # pyright: ignore[reportArgumentType]
            stderr=subprocess.STDOUT,
            start_new_session=True,
        )
    finally:
        if log_handle is not None:
            log_handle.close()

    deadline = time.monotonic() + wait_timeout
    resonite_pid = _wait_for_new(
        lambda: _find_engine_pids(install_dir),
        before_engines,
        "engine",
        deadline,
        poll_interval,
    )
    renderer_pid = _wait_for_new(
        lambda: _find_renderer_pids(install_dir),
        before_renderers,
        "renderer",
        deadline,
        poll_interval,
    )
    _logger.info(
        "Resonite launched: resonite_pid=%d renderer_pid=%d", resonite_pid, renderer_pid
    )
    return LaunchResult(resonite_pid=resonite_pid, renderer_pid=renderer_pid)

resoio.launcher.terminate

terminate(
    resonite_pid: int | None = None,
    renderer_pid: int | None = None,
    *,
    timeout: float = 3.0,
) -> LaunchResult

Stop Resonite by signalling its engine and renderer processes.

Each target is sent SIGTERM, given timeout seconds to exit, then SIGKILL if still alive. Idempotent: a PID that is already gone is skipped. With no arguments, the single running instance is auto-detected from the process table (RESONITE_EXE's install dir); detecting more than one engine or renderer is an error (pass explicit PIDs to disambiguate).

PARAMETER DESCRIPTION
resonite_pid

Engine host PID to kill (e.g. LaunchResult.resonite_pid).

TYPE: int | None DEFAULT: None

renderer_pid

Renderer host PID to kill.

TYPE: int | None DEFAULT: None

timeout

Seconds to wait after SIGTERM before SIGKILL.

TYPE: float DEFAULT: 3.0

RETURNS DESCRIPTION
The

class:LaunchResult of PIDs actually signalled (0 for a role

TYPE: LaunchResult

LaunchResult

that was not running).

RAISES DESCRIPTION
LauncherError

more than one instance was detected in the no-argument path, or a given PID is alive but is not the expected Resonite process.

Source code in src/resoio/launcher.py
def terminate(
    resonite_pid: int | None = None,
    renderer_pid: int | None = None,
    *,
    timeout: float = 3.0,
) -> LaunchResult:
    """Stop Resonite by signalling its engine and renderer processes.

    Each target is sent ``SIGTERM``, given ``timeout`` seconds to exit, then
    ``SIGKILL`` if still alive. Idempotent: a PID that is already gone is
    skipped. With **no** arguments, the single running instance is auto-detected
    from the process table (``RESONITE_EXE``'s install dir); detecting more than
    one engine or renderer is an error (pass explicit PIDs to disambiguate).

    Args:
        resonite_pid: Engine host PID to kill (e.g. ``LaunchResult.resonite_pid``).
        renderer_pid: Renderer host PID to kill.
        timeout: Seconds to wait after ``SIGTERM`` before ``SIGKILL``.

    Returns:
        The :class:`LaunchResult` of PIDs actually signalled (``0`` for a role
        that was not running).

    Raises:
        LauncherError: more than one instance was detected in the no-argument
            path, or a given PID is alive but is not the expected Resonite process.
    """
    if resonite_pid is None and renderer_pid is None:
        install_dir = _install_dir_for_detection()
        engines = _find_engine_pids(install_dir)
        renderers = _find_renderer_pids(install_dir)
        if len(engines) > 1 or len(renderers) > 1:
            raise LauncherError(
                f"multiple Resonite instances detected (engine={engines} "
                f"renderer={renderers}); pass explicit PIDs to choose which to stop."
            )
        resonite_pid = engines[0] if engines else None
        renderer_pid = renderers[0] if renderers else None

    killed_engine = (
        _kill_pid(resonite_pid, _ENGINE_ARGV0_SUFFIX, "engine", timeout)
        if resonite_pid is not None
        else 0
    )
    killed_renderer = (
        _kill_pid(renderer_pid, _RENDERER_ARGV0_SUFFIX, "renderer", timeout)
        if renderer_pid is not None
        else 0
    )
    return LaunchResult(resonite_pid=killed_engine, renderer_pid=killed_renderer)

resoio.launcher.LaunchResult dataclass

LaunchResult(resonite_pid: int, renderer_pid: int)

Host PIDs of the Resonite engine and renderer processes.

Returned by :func:launch (the freshly started pair) and :func:terminate (the pair it actually signalled; 0 for a role that was not running). resonite_pid matches the engine PID the mod reports as ServerInfo.resonite_pid; renderer_pid matches ServerInfo.renderer_pid.

resoio.launcher.LauncherError

Bases: RuntimeError

A resoio launch / terminate operation could not be completed.

Raised for actionable conditions: Resonite.exe / the mod not found, no umu-run on PATH, more than one new engine / renderer appearing from a single launch, terminate finding more than one instance where exactly one is expected, or the engine / renderer not appearing before the timeout.

resoio.launcher.LaunchOptions dataclass

LaunchOptions(
    skip_intro_tutorial: bool = True,
    force_intro_tutorial: bool = False,
    do_not_auto_load_home: bool = False,
    screen: bool = False,
    device: Device | None = None,
    legacy_steamvr_input: bool = False,
    force_sranipal: bool = False,
    force_babble: bool = False,
    force_reticle_above_horizon: bool = False,
    cubemap_resolution: int | None = None,
    join: str | None = None,
    open: str | None = None,
    bootstrap: str | None = None,
    scratchspace: int | None = None,
    announce_home_on_lan: bool = False,
    camera_biggest_group: bool = False,
    camera_timelapse: bool = False,
    camera_stay_behind: bool = False,
    camera_stay_in_front: bool = False,
    use_resonite_camera: bool = False,
    data_path: str | None = None,
    cache_path: str | None = None,
    logs_path: str | None = None,
    repair_database: bool = False,
    generate_precache: bool = False,
    export_database_all: str | None = None,
    export_database_machine: str | None = None,
    reset_dash: bool = False,
    never_save_settings: bool = False,
    never_save_dash: bool = False,
    kiosk: bool = False,
    no_ui: bool = False,
    force_lan_only: bool = False,
    invisible: bool = False,
    disable_platform_interfaces: bool = False,
    force_no_voice: bool = False,
    force_april_fools: bool = False,
    load_assembly: tuple[str, ...] = (),
    verbose: bool = False,
    validate_types: bool = False,
    watchdog: str | None = None,
    engine_config: str | None = None,
    cloud_profile: CloudProfile | None = None,
    delete_unsynced_cloud_records: bool = False,
    force_sync_conflicting_cloud_records: bool = False,
    background_workers: int | None = None,
    priority_workers: int | None = None,
)

Typed Resonite command-line launch options for :func:launch.

Resonite's client accepts a large set of command-line launch arguments (documented at https://wiki.resonite.com/Command_line_arguments). Each field maps to one of them, so callers get autocomplete and type checking instead of hand-assembling raw -Flag strings into extra_args. :meth:to_args renders the argv fragment that :func:launch weaves in just ahead of extra_args (so a raw extra_args entry can still override an option).

Booleans render as a bare flag when True and are omitted when False; value-bearing fields are omitted when None (or empty), and fields render in declaration order. All fields default to Resonite's own default behaviour except skip_intro_tutorial, which defaults to True to preserve :func:launch's historical behaviour of always skipping the intro tutorial.

The renderer-overriding arguments -Renderer and -AttachRenderer are intentionally not modelled: they break :func:launch's renderer-PID detection (which pins <install>/Renderer/Renderite.Renderer.exe) and the doorstop preloader lookup, so the launch would time out waiting for the renderer. Pass them through launch(extra_args=...) only if you accept that PID detection will not work.

Path-valued options are passed through verbatim; :meth:to_args does not absolutise them. :func:launch starts the process with cwd at the Resonite install dir, so a relative path resolves against that directory.

skip_intro_tutorial class-attribute instance-attribute

skip_intro_tutorial: bool = True

Skip the intro tutorial world (-SkipIntroTutorial).

Defaults to True to preserve :func:launch's historical behaviour.

force_intro_tutorial class-attribute instance-attribute

force_intro_tutorial: bool = False

Force the intro tutorial to run (-Forceintrotutorial; note Resonite's irregular casing).

do_not_auto_load_home class-attribute instance-attribute

do_not_auto_load_home: bool = False

Do not auto-load the cloud Home on start (-DoNotAutoLoadHome).

screen class-attribute instance-attribute

screen: bool = False

Force desktop screen mode instead of VR (-Screen).

device class-attribute instance-attribute

device: Device | None = None

Force a specific headset / display device (-Device).

legacy_steamvr_input class-attribute instance-attribute

legacy_steamvr_input: bool = False

Use legacy SteamVR input handling (-LegacySteamVRInput).

force_sranipal class-attribute instance-attribute

force_sranipal: bool = False

Force SRAnipal init for HTC eye/lip tracking (-ForceSRAnipal).

force_babble class-attribute instance-attribute

force_babble: bool = False

Force the Project Babble face-tracking driver (-ForceBabble).

force_reticle_above_horizon class-attribute instance-attribute

force_reticle_above_horizon: bool = False

Disallow looking below the horizon in desktop first-person view (-ForceReticleAboveHorizon).

cubemap_resolution class-attribute instance-attribute

cubemap_resolution: int | None = None

Cubemap resolution for 360° equirectangular rendering (-CubemapResolution).

join class-attribute instance-attribute

join: str | None = None

Join a session on start: Auto for active LAN sessions, or a session URI (-Join).

open class-attribute instance-attribute

open: str | None = None

Open a world at the given resrec URL on start (-Open).

bootstrap class-attribute instance-attribute

bootstrap: str | None = None

Run a custom bootstrap function in the named class (-Bootstrap).

scratchspace class-attribute instance-attribute

scratchspace: int | None = None

Start a scratchspace world on the given port (-Scratchspace; legacy).

announce_home_on_lan class-attribute instance-attribute

announce_home_on_lan: bool = False

Make Home and userspace accessible from LAN (-AnnounceHomeOnLAN).

camera_biggest_group class-attribute instance-attribute

camera_biggest_group: bool = False

Init the static camera with the biggest-group preset (-CameraBiggestGroup).

camera_timelapse class-attribute instance-attribute

camera_timelapse: bool = False

Init the static camera with the timelapse preset (-CameraTimelapse).

camera_stay_behind class-attribute instance-attribute

camera_stay_behind: bool = False

Init the static camera with the stay-behind preset (-CameraStayBehind).

camera_stay_in_front class-attribute instance-attribute

camera_stay_in_front: bool = False

Init the static camera with the stay-in-front preset (-CameraStayInFront).

use_resonite_camera class-attribute instance-attribute

use_resonite_camera: bool = False

Spawn the static camera as a Resonite Camera with zoom controls (-UseResoniteCamera).

data_path class-attribute instance-attribute

data_path: str | None = None

Database directory location (-DataPath).

cache_path class-attribute instance-attribute

cache_path: str | None = None

Cache directory location (-CachePath).

logs_path class-attribute instance-attribute

logs_path: str | None = None

Log files directory location (-LogsPath).

repair_database class-attribute instance-attribute

repair_database: bool = False

Repair the local database on start (-RepairDatabase).

generate_precache class-attribute instance-attribute

generate_precache: bool = False

Cache cloud records to RuntimeData/PreCache (-GeneratePrecache).

export_database_all class-attribute instance-attribute

export_database_all: str | None = None

Export all local database records to a directory (-ExportDatabaseAll).

export_database_machine class-attribute instance-attribute

export_database_machine: str | None = None

Export records owned by this machine to a directory (-ExportDatabaseMachine).

reset_dash class-attribute instance-attribute

reset_dash: bool = False

Reset the dashboard layout to default (-ResetDash).

never_save_settings class-attribute instance-attribute

never_save_settings: bool = False

Do not save/sync settings — testing only (-NeverSaveSettings).

never_save_dash class-attribute instance-attribute

never_save_dash: bool = False

Do not save/sync dashboard changes — testing only (-NeverSaveDash).

kiosk class-attribute instance-attribute

kiosk: bool = False

Run in Kiosk mode: hide userspace UI, disable guest teleport (-Kiosk).

no_ui class-attribute instance-attribute

no_ui: bool = False

Hide the userspace UI (-NoUI).

force_lan_only class-attribute instance-attribute

force_lan_only: bool = False

Announce sessions on LAN only, not the internet (-ForceLANOnly).

invisible class-attribute instance-attribute

invisible: bool = False

Force online status to invisible on login (-Invisible).

disable_platform_interfaces class-attribute instance-attribute

disable_platform_interfaces: bool = False

Disable all platform interfaces — Discord/Steam/clipboard (-DisablePlatformInterfaces).

force_no_voice class-attribute instance-attribute

force_no_voice: bool = False

Do not set up avatars with voice (-ForceNoVoice).

force_april_fools class-attribute instance-attribute

force_april_fools: bool = False

Activate April Fools mode regardless of date (-ForceAprilFools).

load_assembly class-attribute instance-attribute

load_assembly: tuple[str, ...] = ()

Extra CLR assemblies / DLLs to load into the process (-LoadAssembly, one occurrence per path).

verbose class-attribute instance-attribute

verbose: bool = False

Produce detailed engine-initialisation logs (-Verbose).

validate_types class-attribute instance-attribute

validate_types: bool = False

Check and log DataModel type validation (-ValidateTypes).

watchdog class-attribute instance-attribute

watchdog: str | None = None

Periodically write the current time to this file for restart detection (-Watchdog).

engine_config class-attribute instance-attribute

engine_config: str | None = None

Use a custom engine config file (-EngineConfig).

cloud_profile class-attribute instance-attribute

cloud_profile: CloudProfile | None = None

Cloud API server set to use, for debugging (-CloudProfile).

delete_unsynced_cloud_records class-attribute instance-attribute

delete_unsynced_cloud_records: bool = False

WARNING: irreversibly deletes local unsynced cloud records and re-downloads cloud copies (-DeleteUnsyncedCloudRecords).

force_sync_conflicting_cloud_records class-attribute instance-attribute

force_sync_conflicting_cloud_records: bool = False

WARNING: forces conflicting local records to overwrite their cloud copies (-ForceSyncConflictingCloudRecords).

background_workers class-attribute instance-attribute

background_workers: int | None = None

WARNING: overrides the background worker process count; wrong values can destabilise the engine (-BackgroundWorkers).

priority_workers class-attribute instance-attribute

priority_workers: int | None = None

WARNING: overrides the priority worker process count; wrong values can destabilise the engine (-PriorityWorkers).

to_args

to_args() -> list[str]

Render the Resonite argv fragment for these options.

Walks the fields in declaration order so the output is deterministic. Booleans emit a bare flag when True; None / empty values are omitted; enums emit their wire value; load_assembly emits one -LoadAssembly <path> pair per entry.

Source code in src/resoio/launcher.py
def to_args(self) -> list[str]:
    """Render the Resonite argv fragment for these options.

    Walks the fields in declaration order so the output is deterministic.
    Booleans emit a bare flag when ``True``; ``None`` / empty values are
    omitted; enums emit their wire ``value``; ``load_assembly`` emits one
    ``-LoadAssembly <path>`` pair per entry.
    """
    args: list[str] = []
    for field in fields(self):
        arg = _ARG_NAMES[field.name]
        value = getattr(self, field.name)
        # bool before int: bool is a subclass of int.
        if isinstance(value, bool):
            if value:
                args.append(arg)
        elif isinstance(value, tuple):
            for item in cast("tuple[str, ...]", value):
                args.extend((arg, item))
        elif isinstance(value, enum.Enum):
            args.extend((arg, value.value))
        elif value is not None:
            args.extend((arg, str(value)))
    return args

resoio.launcher.Device

Bases: Enum

Headset / display device to force (-Device <value>).

resoio.launcher.CloudProfile

Bases: Enum

Cloud API server set to target (-CloudProfile <value>).