Events and Error Handling
Understand the event model and exceptions to build resilient networking code.
Client events
client.OnLobbyCreated += (s, e) => { /* e.Lobby */ };
client.OnLobbyJoined += (s, e) => { /* e.Lobby */ };
client.OnLobbyLeft += (s, e) => { /* e.LobbyId, e.Reason */ };
client.OnMemberJoined += (s, e) => { /* e.Member */ };
client.OnMemberLeft += (s, e) => { /* e.Member, e.Reason */ };
client.OnLobbyDataChanged += (s, e) => { /* e.Key, e.OldValue, e.NewValue, e.ChangedBy */ };
client.OnMemberDataChanged += (s, e) => { /* e.MemberId, e.Key, e.OldValue, e.NewValue */ };
client.OnP2PMessageReceived += (s, e) =>
{
// e.Message (P2PMessage), e.SenderId, e.Channel
};
client.OnVersionMismatch += (s, e) =>
{
// e.LocalVersion, e.PlayerVersions, e.IncompatiblePlayers
};
Advanced P2P events exist on SteamP2PManager (packet-level and session events) if you need low-level control.
Exceptions
SteamNetworkException: Base exception for library errors.LobbyException: Lobby-specific failures (creation, join, invalid IDs).P2PException: P2P send/receive/session issues (target, channel, session error).SyncException: SyncVar and Steam data synchronization failures.SyncSerializationExceptionandSyncValidationExceptionalso derive from this family.
All SteamNetworkLib exceptions expose structured diagnostics:
ErrorKind: broad failure reason, such asSteamUnavailable,NotInLobby,InvalidSteamId,PacketTooLarge,SerializationFailed,ValidationFailed, orMessageFormatInvalid.Operation: API operation or lifecycle step that failed, when available.IsRetryable: whether retrying later may succeed.
Specialized exceptions include additional context. LobbyException can expose LobbyId64, MemberId64, DataKey, SteamResult, and RequiresHost. P2PException can expose TargetId64, MessageType, PacketSize, MaxPacketSize, Channel, and SessionError. SyncException exposes SyncKey.
try
{
var lobby = await client.CreateLobbyAsync();
}
catch (LobbyException ex)
{
MelonLogger.Error($"Lobby error in {ex.Operation}: {ex.Message} ({ex.ErrorKind})");
if (!string.IsNullOrEmpty(ex.DataKey))
{
MelonLogger.Error($"Data key: {ex.DataKey}");
}
}
catch (SteamNetworkException ex)
{
MelonLogger.Error($"Steam error in {ex.Operation}: {ex.Message} ({ex.ErrorKind})");
}
Initialization failures
Initialize() throws a SteamNetworkException when Steamworks is not available. That usually means Steam is not running, the game was launched outside Steam, SteamAPI has not initialized yet, or another loader/runtime problem prevented Steamworks from attaching to the process.
For consumer mods, prefer TryInitialize() and retry later:
if (!client.TryInitialize(out var error))
{
MelonLogger.Warning($"Steam networking unavailable: {error?.Message} ({error?.ErrorKind})");
multiplayerAvailable = false;
return;
}
multiplayerAvailable = true;
Do not treat a failed initialization as a reason to disable the whole mod unless networking is the mod's only feature. Keep local behavior active, skip calls to SyncVars/P2P/lobby data, and retry after the menu or main scene has loaded.
IL2CPP specifics
client.ProcessIncomingMessages() internally calls SteamAPI.RunCallbacks() on IL2CPP, which is required to drive Steam callbacks. Ensure you call it every frame (e.g., OnUpdate).