RustPlus Client
RustPlus is the core client. It opens a WebSocket to the server's companion port and exposes a
typed, task-based API.
Construct and connect
using var rustPlus = new RustPlus(new RustPlusConnection(server, port, playerId, playerToken, UseFacepunchProxy: false));
await rustPlus.ConnectAsync();
RustPlusConnection is a record grouping the connection identity:
| Property | Meaning |
|---|---|
Server |
The server's IP address. |
Port |
The Rust+ companion port (not the in-game connect port). |
PlayerId |
Your Steam ID. |
PlayerToken |
The player token from pairing. |
UseFacepunchProxy |
Route through the Facepunch proxy instead of connecting directly. |
RustPlusConnection.ToString()redactsPlayerToken(it prints as***) so the credential is not accidentally leaked through logs or exceptions.
Connection lifecycle
The client progresses through the following states. ErrorOccurred can fire from both the
Connecting and Connected states (for example, a WebSocket connection failure or a dead-socket
receive error).
stateDiagram-v2
[*] --> Disconnected
Disconnected --> Connecting : ConnectAsync()
Connecting --> Connected : handshake OK
Connecting --> Disconnected : ErrorOccurred
Connected --> Disconnecting : DisconnectAsync() / Dispose
Connected --> Disconnected : ErrorOccurred (transport lost)
Disconnecting --> Disconnected : close complete
After DisconnectAsync() the instance can reconnect by calling ConnectAsync() again — it opens a
fresh socket without leaking the previous one.
The Response<T> contract
Every request returns a Response<T>:
public sealed record Response<T>
{
public bool IsSuccess { get; init; }
public ErrorMessage? Error { get; init; }
public T? Data { get; init; }
}
Always check IsSuccess before reading Data:
var time = await rustPlus.GetTimeAsync();
if (time.IsSuccess)
Console.WriteLine(time.Data!.Time);
else
Console.WriteLine(time.Error!.Message);
Error.Code is a machine-readable RustPlusErrorCode parsed from the raw server identifier, so you
can branch on failure type without string comparisons. A successful server reply never surfaces as a
thrown exception: when the server answered fine but the library could not map the payload (e.g.
reading an alarm through the strict GetSmartSwitchInfoAsync — the server replies with the entity's
actual type), the call returns a failed Response with RustPlusErrorCode.ClientMappingFailed
and the mapper's message.
Method reference
Server & world
| Method | Returns | Notes |
|---|---|---|
GetInfoAsync() |
Response<ServerInfo?> |
Name, player counts, map size, wipe time, logo URL. |
GetTimeAsync() |
Response<TimeInfo?> |
In-game time, sunrise/sunset, day-length parameters. |
GetMapAsync() |
Response<ServerMap?> |
Map image bytes and monument list. Large response — cache it. |
GetMapMarkersAsync() |
Response<MapMarkers?> |
Typed marker dictionaries: players, cargo ship, patrol heli, CH-47, travelling vendor, vending machines, explosions, crates, generic-radius overlays. Event movers carry Rotation (degrees, null when omitted); unrecognized types land in UnknownMarkers (full raw surface incl. RawType) instead of failing. |
Entities (smart devices)
| Method | Returns | Notes |
|---|---|---|
GetSmartDeviceInfoAsync(entityId) |
Response<SmartDeviceInfo?> |
Type-agnostic read for any binary-state device (smart switch or smart alarm) — use this for mixed device sets. |
GetSmartSwitchInfoAsync(entityId) |
Response<SmartDeviceInfo?> |
Current on/off state. Strict: fails with ClientMappingFailed when the entity is not a switch. |
GetAlarmInfoAsync(entityId) |
Response<SmartDeviceInfo?> |
Current state (protocol-level identical to smart switch). Strict: fails with ClientMappingFailed when the entity is not an alarm. |
GetStorageMonitorInfoAsync(entityId) |
Response<StorageMonitorInfo?> |
Item list and protection state. |
SetSmartSwitchValueAsync(entityId, value) |
Response<SmartDeviceInfo?> |
true = on, false = off. Reply is the EntityChanged broadcast. |
ToggleSmartSwitchAsync(entityId) |
Response<SmartDeviceInfo?> |
Reads current state then flips it. |
StrobeSmartSwitchAsync(entityId, timeoutMilliseconds, value) |
Response<SmartDeviceInfo?> |
Pulses to value, waits timeoutMilliseconds, then reverts. Default: 1 s on. |
CheckSubscriptionAsync(alarmId) |
Response<SubscriptionInfo?> |
Whether you are subscribed to a smart alarm. |
SetSubscriptionAsync(entityId, doSubscribe) |
Response |
Subscribe (true) or unsubscribe (false) from push notifications. |
Team
| Method | Returns | Notes |
|---|---|---|
GetTeamInfoAsync() |
Response<TeamInfo?> |
All members, statuses, map notes. |
GetTeamChatAsync() |
Response<TeamChatInfo?> |
Recent team chat history. |
SendTeamMessageAsync(message) |
Response<TeamMessage?> |
Sends text; reply is the echo broadcast for your own message. |
PromoteToLeaderAsync(steamId) |
Response |
Promotes the specified team member to leader. |
Clan
| Method | Returns | Notes |
|---|---|---|
GetClanInfoAsync() |
Response<ClanInfo?> |
Full clan snapshot: roles, members, invites, MOTD. |
GetClanChatAsync() |
Response<ClanChatInfo?> |
Recent clan chat history. |
SendClanMessageAsync(message) |
Response |
Posts a message to clan chat. |
SetClanMotdAsync(message) |
Response |
Sets the clan message of the day. |
Nexus
| Method | Returns | Notes |
|---|---|---|
GetNexusAuthAsync(appKey) |
Response<NexusAuth?> |
Cross-server auth token for Nexus-enabled servers. |
Cameras
| Method | Returns | Notes |
|---|---|---|
SubscribeToCameraAsync(cameraId) |
Response<CameraInfo?> |
Starts the OnCameraRaysReceived stream; returns width/height/flags. |
SendCameraInputAsync(buttons, mouseDeltaX, mouseDeltaY) |
Response |
Sends movement/action input to the subscribed camera. |
UnsubscribeFromCameraAsync() |
Response |
Stops the camera stream. |
Tip
For anything beyond a quick look, use CameraController from the RustPlusApi.Camera
package instead of calling these directly — it renews the subscription (the server stops
streaming rays for unrenewed subscriptions), detects the device kind, and wraps the
press-and-release turret/PTZ gestures. See Cameras.
Low-level
| Method | Returns | Notes |
|---|---|---|
SendRequestAsync(request, broadcastReplyMatcher) |
Task<AppMessage> |
Raw protobuf request. Use for operations not covered by typed methods. |
Events
The complete set of public events across RustPlusSocket and RustPlus:
| Event | Payload type | Fires when |
|---|---|---|
Connecting |
EventArgs |
ConnectAsync is called, before the WebSocket handshake. |
Connected |
EventArgs |
The WebSocket handshake completed successfully. |
SendingRequest |
EventArgs |
A request is about to be written to the send channel. |
RequestSent |
AppRequest |
A request was serialised and sent over the socket. |
MessageReceived |
AppMessage |
Any message (response or broadcast) was received. |
NotificationReceived |
AppMessage |
An unsolicited broadcast was received. |
ResponseReceived |
AppMessage |
A seq-bearing response was received. |
Disconnecting |
EventArgs |
DisconnectAsync started the close handshake. |
Disconnected |
EventArgs |
The WebSocket close completed. |
ErrorOccurred |
Exception |
A transport or receive error occurred. Fires from Connecting or Connected state. |
OnEntityChanged |
EntityChangedEventArg |
Every EntityChanged broadcast, raw and before any device-type heuristic (Id, Value, Capacity, HasProtection, ProtectionExpiry, Items). The broadcast carries no entity type — route on Id when you know your paired entities; this is the reliable channel. |
OnSmartDeviceTriggered |
SmartDeviceEventArg |
An EntityChanged broadcast classified as a binary-state device: no items, no capacity, no protection in the payload. A storage broadcast carrying only value is indistinguishable from a switch and lands here too. |
OnStorageMonitorTriggered |
StorageMonitorEventArg |
An EntityChanged broadcast classified as a storage monitor: items, capacity or tool-cupboard protection present (TC broadcasts are sometimes partial — capacity may be absent). Item-less value == true storage broadcasts carry no contents snapshot and are not raised here (observe them via OnEntityChanged). |
OnTeamChatReceived |
TeamMessageEventArg |
A team chat message arrived. |
OnClanChatReceived |
ClanMessageEventArg |
A clan chat message arrived. |
OnTeamChanged |
TeamChangedEventArg |
The team snapshot changed (members, map notes, leader, …); carries the triggering PlayerId. |
OnClanChanged |
ClanChangedEventArg |
The clan snapshot changed (roles, members, MOTD, …). |
OnCameraRaysReceived |
CameraRaysEventArg |
A camera frame broadcast arrived for the subscribed camera. |
Note
To receive OnEntityChanged, OnSmartDeviceTriggered or OnStorageMonitorTriggered broadcasts
for a given entity, you must first make at least one request on that entity (e.g.
GetSmartDeviceInfoAsync), which registers it with the server — the registration happens even
when the read itself fails on a type mismatch. Camera frame events start after
SubscribeToCameraAsync.
Disposal
RustPlus is IDisposable; dispose it (or await DisconnectAsync()) when done.
await rustPlus.DisconnectAsync();
DisposeAsync() is also available and drains background I/O loops before releasing the socket.