Repository navigation
API: Game Events
ONI Together raises events on Klei's global Game event bus using hashed identifiers in Shared.MP_HASHES. Subscribe with Game.Instance.Subscribe(...) and unsubscribe with Game.Instance.Unsubscribe(...).
MP_HASHES lives in the Shared assembly, which is merged into ONI_Together_API.dll, so you can use it directly from a mod that references the API. Each ModHashes implicitly converts to GameHashes / int, so it can be passed straight to the game's event API. Hashes are derived from a stable string name, so the same MP_HASHES constant resolves to the same id on every peer.
These are local process events, not network broadcasts. Each peer raises them from its own session state, so keep your handler null-safe and guard with
inSession/isServerwhere appropriate.
Namespaces:
-
Shared→MP_HASHES
using Shared;
using UnityEngine;
public class MyEventHandler : KMonoBehaviour
{
public override void OnSpawn()
{
base.OnSpawn();
Game.Instance?.Subscribe(MP_HASHES.OnPlayerJoined, OnPlayerJoined);
Game.Instance?.Subscribe(MP_HASHES.OnInSessionChanged, OnSessionChanged);
}
public override void OnCleanUp()
{
Game.Instance?.Unsubscribe(MP_HASHES.OnPlayerJoined, OnPlayerJoined);
Game.Instance?.Unsubscribe(MP_HASHES.OnInSessionChanged, OnSessionChanged);
base.OnCleanUp();
}
private void OnPlayerJoined(object data)
{
ulong playerId = Boxed<ulong>.Unbox(data);
// ...
}
private void OnSessionChanged(object data)
{
bool inSession = ((Boxed<bool>)data).value;
// ...
}
}Handlers take a single object argument and must be the exact same method reference used for both Subscribe and Unsubscribe.
| Constant | Raised in | Payload | When |
|---|---|---|---|
GameServer_OnStateChanged |
GameServer.SetState |
none | Host: GameServer.State changed |
GameServer_OnServerStarted |
GameServer.Start |
none | Host: server finished starting |
OnConnected |
GameServer.Start, client transports |
none | Transport connected (host or client) |
GameClient_OnConnectedInGame |
GameClient |
none | Client: finished joining and loading into the game |
OnMultiplayerGameSessionInitialized |
NetworkConfig |
none | Host: Steam/LAN session finished initialising |
OnInSessionChanged |
MultiplayerSession.InActiveSession |
Boxed<bool> |
Session active state changed |
OnDisconnected |
NetworkConfig.Stop |
none | Session stopped / disconnected |
OnPlayerJoined |
SteamLobby, RiptideServer, RiptideClient
|
Boxed<ulong> player id |
A peer joins |
OnPlayerLeft |
SteamLobby, RiptideServer, RiptideClient
|
Boxed<ulong> player id |
A peer leaves |
OnPlayerCursorCreated |
MultiplayerSession.CreateNewPlayerCursor |
Boxed<ulong> player id |
A player's cursor object is created |
Payloads are pooled boxed values from the game's assemblies:
-
Boxed<ulong>— a player/user id. Read it withBoxed<ulong>.Unbox(data). (Producers useBoxed<ulong>.Get(id)and.Release(); consumers only need to unbox.) -
Boxed<bool>— used byOnInSessionChanged.BoxedBools.True/BoxedBools.FalseareBoxed<bool>, read via((Boxed<bool>)data).value. - Several events carry no payload; their
dataargument isnull.
- Always
Unsubscribe(e.g. inOnCleanUp) to avoid leaked handlers across session/game reloads. - Use the
MP_HASHESconstants — never hardcode integer hashes. - In-repo examples:
UnityChatBoxUIsubscribes toOnInSessionChanged;AnimResyncRequestertoGameClient_OnConnectedInGameandOnMultiplayerGameSessionInitialized;OxySyncChattoOnPlayerJoined.
Mod development environment setup
ONI Together API