Skip to content

Lifecycle and threading

ClientResource and ServerResource give you the SDK’s services as properties (Vehicles, Players, World, Hud, Network, Storage, …) and call your overrides:

Override Client Server Called
OnStartAsync ✓ ✓ Once, when the resource loads.
OnStopAsync ✓ ✓ Once, when it unloads.
OnTick(TimeSpan delta) ✓ ✓ Every engine frame (client) or server loop iteration.
OnPlayerJoined / OnPlayerLeft ✓ A player appears or leaves.
OnPlayerConnected / OnPlayerDisconnected ✓ A player joins or leaves the server.
OnVehicleSpawned / OnVehicleDespawned ✓ ✓ A bus is placed in or removed from the world.
OnWeatherChanged ✓ ✓ The authority changes the weather.
OnSessionChanged ✓ The player joins or leaves a multiplayer server.
OnMapLoaded / OnMapUnloaded ✓ A map finishes loading, or is unloaded.

For anything else, subscribe directly: input changes, vehicle triggers, metadata changes and named network events all return an IDisposable. Every subscription belongs to the resource and ends when it unloads, even if you do not keep the handle.

protected override ValueTask OnStartAsync(CancellationToken cancellationToken)
{
Input.Changed.Subscribe(change => Log.Info($"{change.Command} {(change.IsDown ? "down" : "up")}"));
return ValueTask.CompletedTask;
}
  • Reads return an immutable snapshot that OMX publishes once per game frame or server loop.
  • Writes are checked when you call them, so an invalid one throws right there, and are applied by OMX at its next safe point on the engine or server loop. Writes to the local bus land before that frame’s vehicle scripts run.
  • Your code runs on the resource’s own thread. Each resource has one serial dispatcher and SynchronizationContext. Event handlers, timer callbacks and await continuations all run there, one at a time and in order, never on the simulation or network thread.
  • Ticks are coalesced. A resource that falls behind receives one tick covering the time it missed, not a backlog.
  • Failures are contained. An exception in a handler is logged against your resource and does not stop other handlers or resources.

Every SDK member is safe to call from any thread. Use Runtime.Post(action) to queue work back onto your resource’s thread.

Joining or leaving a multiplayer server is a new session: every player and vehicle leaves, and the new session’s ones join with the new session’s ids. Old IPlayer and IVehicle views report IsValid == false and keep their last snapshot for diagnostics, even if a later session happens to reuse a numeric id.