Table of Contents

Class CameraController

Namespace
RustPlusApi.Camera
Assembly
RustPlusApi.Camera.dll

Managed session for one subscribed camera: keeps the subscription alive by periodically re-sending the subscribe request (the server stops streaming rays for stale subscriptions), forwards ray frames via OnFrameReceived, and exposes the press-and-release input helpers (ZoomAsync(CancellationToken), ShootAsync(CancellationToken), ReloadAsync(CancellationToken)) used by PTZ cameras and auto-turrets. Mirrors the Camera class of liamcottle/rustplus.js. Dispose to stop the keep-alive and unsubscribe.

public sealed class CameraController : IAsyncDisposable
Inheritance
CameraController
Implements
Inherited Members

Remarks

The server tracks a single camera subscription per connection, so create at most one live controller per IRustPlus client at a time.

Fields

DefaultMoveDuration

How long MoveAsync(CameraButtons, TimeSpan?, CancellationToken) holds the buttons when no duration is supplied. 500 ms moves a live drone roughly 2 m at cruise speed.

public static readonly TimeSpan DefaultMoveDuration

Field Value

TimeSpan

DefaultResubscribeInterval

The keep-alive renewal period used when none is supplied (mirrors rustplus.js).

public static readonly TimeSpan DefaultResubscribeInterval

Field Value

TimeSpan

Properties

CameraId

The identifier this controller is subscribed to (e.g. CAM01).

public string CameraId { get; }

Property Value

string

Info

Camera description from the most recent successful subscribe.

public CameraInfo Info { get; }

Property Value

CameraInfo

IsAutoTurret

Whether the camera is an auto-turret. Detected via the Reload flag — only turrets expose a reload input. (rustplus.js checks Crosshair instead, but a drone may also render a crosshair, so Reload is the safer discriminator.)

public bool IsAutoTurret { get; }

Property Value

bool

IsDrone

Whether the camera is a drone (advertises the SprintAndDuck flag, which carries the drone's vertical movement controls).

public bool IsDrone { get; }

Property Value

bool

IsPtzCamera

Whether the camera is a PTZ (pan-tilt-zoom) CCTV camera: it supports Mouse look but is neither an auto-turret nor a drone. Live-observed PTZ flags are Mouse | FireFire drives ZoomAsync(CancellationToken).

public bool IsPtzCamera { get; }

Property Value

bool

IsStaticCamera

Whether the camera is a fixed CCTV camera that accepts no input at all (ControlFlags is None).

public bool IsStaticCamera { get; }

Property Value

bool

Methods

DisposeAsync()

Stops the keep-alive loop and unsubscribes from the camera (when still connected).

public ValueTask DisposeAsync()

Returns

ValueTask

LookAsync(float, float, CancellationToken)

Turns the camera by sending a single mouse-delta frame (PTZ cameras, turrets and drones — anything advertising Mouse). Refused with NotSupported when the device does not support mouse look.

public Task<Response> LookAsync(float deltaX, float deltaY, CancellationToken cancellationToken = default)

Parameters

deltaX float

Horizontal mouse delta (positive looks right).

deltaY float

Vertical mouse delta (positive looks down).

cancellationToken CancellationToken

A token to observe for cancellation requests.

Returns

Task<Response>

The acknowledgement Response from the server, or the client-side refusal (nothing sent).

Remarks

Live-observed (2026-06): actuates on PTZ cameras (the frame's CameraRotation pans) and on drones while airborne — a parked drone ignores the input (acked with success regardless). Turret behaviour is unverified: the test server's turret was deactivated and ignored every input.

MoveAsync(CameraButtons, TimeSpan?, CancellationToken)

Moves a drone by streaming the given movement buttons as input frames for duration (default DefaultMoveDuration), then releasing. Movement only actuates while a continuous input stream is held — a single press-and-release is acknowledged by the server but never moves the drone (live-verified 2026-06). Buttons: Forward/ Backward/Left/ Right need Movement; Sprint (ascend) and Duck (descend) need SprintAndDuck — the drone's vertical controls, hence the flag's name (Jump is accepted but did nothing on a live drone). Refused with NotSupported when the device does not advertise the required flags.

public Task<Response> MoveAsync(CameraButtons buttons, TimeSpan? duration = null, CancellationToken cancellationToken = default)

Parameters

buttons CameraButtons

Movement buttons to hold.

duration TimeSpan?

How long to hold the buttons; DefaultMoveDuration when null.

cancellationToken CancellationToken

A token to observe for cancellation requests.

Returns

Task<Response>

The release acknowledgement from the server, the first failed send, or the client-side refusal (nothing sent).

Remarks

Live flight 2026-06-12: streaming Sprint for 1 s climbed ~4.7 m, Forward/Backward moved ~5 m and back, Duck landed the drone on its starting spot. The drone hovers when the stream stops. A parked drone responds to Sprint (take-off) only — planar movement and mouse look require being airborne. Fly gently: collisions with the ground or structures (and incoming fire) cost the drone HP; a destroyed drone fails subsequent subscribes with NoPlayer. A carried package (attached by a player) is dropped with a FirePrimary click via PressAsync(CameraButtons, CancellationToken).

Exceptions

ArgumentException

buttons is empty or contains non-movement buttons (use the action helpers or SendInputAsync(CameraButtons, float, float, CancellationToken) for those).

PressAsync(CameraButtons, CancellationToken)

Performs a discrete action by pressing buttons and then releasing them (a follow-up input with None), mirroring the press-then-release pair used for PTZ and auto-turret actions in rustplus.js.

public Task<Response> PressAsync(CameraButtons buttons, CancellationToken cancellationToken = default)

Parameters

buttons CameraButtons

Bitmask of buttons to press and then release.

cancellationToken CancellationToken

A token to observe for cancellation requests.

Returns

Task<Response>

The press response if it failed; otherwise the release response.

ReloadAsync(CancellationToken)

Reloads an auto-turret, sending the Reload press-and-release used by rustplus.js. Refused with NotSupported unless IsAutoTurret.

public Task<Response> ReloadAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

A token to observe for cancellation requests.

Returns

Task<Response>

The acknowledgement Response from the server, or the client-side refusal (nothing sent).

SendInputAsync(CameraButtons, float, float, CancellationToken)

Sends a single raw input frame (button bitmask plus mouse deltas) to the subscribed camera.

public Task<Response> SendInputAsync(CameraButtons buttons, float mouseDeltaX = 0, float mouseDeltaY = 0, CancellationToken cancellationToken = default)

Parameters

buttons CameraButtons

Bitmask of buttons to report as pressed.

mouseDeltaX float

Horizontal mouse delta.

mouseDeltaY float

Vertical mouse delta.

cancellationToken CancellationToken

A token to observe for cancellation requests.

Returns

Task<Response>

The acknowledgement Response from the server.

ShootAsync(CancellationToken)

Fires an auto-turret once, sending the FirePrimary press-and-release used by rustplus.js. Refused with NotSupported unless IsAutoTurret.

public Task<Response> ShootAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

A token to observe for cancellation requests.

Returns

Task<Response>

The acknowledgement Response from the server, or the client-side refusal (nothing sent).

SubscribeAsync(IRustPlus, string, TimeSpan?, CancellationToken)

Subscribes to cameraId and returns a controller that keeps the subscription alive until disposed.

public static Task<Response<CameraController?>> SubscribeAsync(IRustPlus rustPlus, string cameraId, TimeSpan? resubscribeInterval = null, CancellationToken cancellationToken = default)

Parameters

rustPlus IRustPlus

A connected client.

cameraId string

The camera identifier configured in game (case-sensitive).

resubscribeInterval TimeSpan?

How often to renew the subscription; DefaultResubscribeInterval when null, Zero or negative to disable renewal.

cancellationToken CancellationToken

A token to observe for cancellation requests.

Returns

Task<Response<CameraController>>

A Response<T> whose data is the live controller on success.

ZoomAsync(CancellationToken)

Advances a PTZ camera's zoom by one step (it cycles through its zoom levels and wraps), sending the FirePrimary press-and-release used by rustplus.js. Refused with NotSupported unless IsPtzCamera: zoom shares FirePrimary with turret fire, so zooming "on" a turret would shoot it — and the server acks unsupported inputs with success, giving no feedback of its own.

public Task<Response> ZoomAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

A token to observe for cancellation requests.

Returns

Task<Response>

The acknowledgement Response from the server, or the client-side refusal (nothing sent).

Events

OnFrameReceived

Occurs when a ray frame for the subscribed camera is received.

public event EventHandler<CameraRaysEventArg>? OnFrameReceived

Event Type

EventHandler<CameraRaysEventArg>

OnKeepAliveFailed

Occurs when a keep-alive renewal attempt fails: the server refused the re-subscribe (e.g. NoPlayer after the camera was destroyed in game) or the client was disconnected (reported as Unknown with the exception message). Without a renewal the server stops streaming, so frames going quiet after this event means the subscription is dead — Info keeps its last successful value. The loop keeps retrying, so a later reconnect recovers on its own.

public event EventHandler<ErrorMessage>? OnKeepAliveFailed

Event Type

EventHandler<ErrorMessage>