Repository navigation
API: OxySync
OxySync is a Unity Mirror-style high-level networking layer built into the API. Derive from Shared.OxySync.NetworkBehaviour and use attributes to replicate fields and call remote methods — no manual packet code required.
Namespaces:
-
Shared.OxySync→NetworkBehaviour -
Shared.OxySync.Attributes→SyncVarAttribute,CommandAttribute,ClientRpcAttribute,TargetRpcAttribute,ServerAttribute,ClientAttribute,InterestGroupAttribute,FixedInterestGroupAttribute
The
Sharedassembly is merged intoONI_Together_API.dll, so this works directly from a mod that references the API.
public abstract class NetworkBehaviour : KMonoBehaviour
{
public float SyncInterval = 0.5f; // seconds between SyncVar ticks
public int InterestGroup { get; set; } = -1; // -1 = every player
public int BehaviourId { get; set; } = -1; // assigned automatically on spawn
public virtual int NetId { get; set; } // backed by the GameObject's NetworkIdentity
protected bool isServer { get; }
protected bool isClient { get; }
protected bool inSession { get; }
protected void CallCommand(string methodName, params object[] args);
protected void CallClientRpc(string methodName, params object[] args);
protected void CallTargetRpc(ulong targetPlayer, string methodName, params object[] args);
}
// Other members (send delegates, SyncVarField/CachedMethod collections, timestamp fields,
// static event hooks) are internal plumbing and aren't needed to use the class.-
NetIdresolves through the object'sNetworkIdentity. The behaviour must live on an entity that has aNetworkIdentity(e.g. spawned viaSpawnUtilsAPI.KNetInstantiate). -
BehaviourIdis derived from the type name and is set automatically inOnSpawn. -
SyncIntervalis the host tick rate for[SyncVar]replication (seconds). -
isServer/isClient/inSessionare the same checks asSessionInfoAPI, exposed for convenience.
[SyncVar(Hook = nameof(OnCounterChanged), Epsilon = 0.01f, InterestGroup = -1, SendMode = 8)]
private int _counter;| Property | Default | Meaning |
|---|---|---|
Hook |
null |
Method name called when the value changes: void Hook(T oldValue, T newValue)
|
Epsilon |
0.01f |
Float change threshold before a sync is sent |
InterestGroup |
-1 |
Override the class default group (-1 = all) |
SendMode |
0 (Unreliable) |
PacketSendMode value |
- Maximum 64
[SyncVar]fields per behaviour. - The host replicates changed SyncVars to clients on each
SyncIntervaltick or if the value changes using a dirty bit mask.
[Command(RequiresHost = false, SendMode = (int) PacketSendMode.Reliable)]
public void CmdSetCounter(int value) { ... }Method name should start with Cmd. RequiresHost (default false) rejects the call if the receiver isn't the host.
[ClientRpc(IncludeHost = false, InterestGroup = -1, SendMode = (int) PacketSendMode.Reliable)]
private void RpcOnCounterUpdated(int newValue) { ... }Method name should start with Rpc. IncludeHost also runs it locally on the host.
[TargetRpc(SendMode = (int) PacketSendMode.Reliable]
private void TargetSendDirectMessage(string message) { ... }Method name should start with Target.
Markers that are enforced when a method is dispatched through OxySync RPCs. For Unity lifecycle methods (Update, etc.) still guard manually with if (!isServer) return;.
Class-level [InterestGroup(Group = n)] sets the default group for SyncVars and ClientRpcs. -1 means "everyone". [FixedInterestGroup] marks a group as non-dynamic.
Call the generated helpers rather than invoking RPC methods directly:
You must be in a session for these to work.
CallCommand(nameof(CmdSetCounter), 5);
CallCommand(() => CmdSetCounter(5)); // expression overload
CallCommand((Action)MyCommandMethod, arg1); // delegate overload
CallClientRpc(nameof(RpcOnCounterUpdated), 5);
CallClientRpc(interestGroup: 3, nameof(RpcOnCounterUpdated), 5);
CallTargetRpc(targetPlayerId, nameof(TargetSendDirectMessage), "hi");CallCommand runs locally when you're the host, otherwise sends to the host. CallClientRpc / CallTargetRpc are host-only.
Primitives (int, float, bool, byte, long, ulong, short, ushort, uint, sbyte, char, double, decimal, string), enums, Vector2, Vector3, Color, Quaternion, byte[], HashedString, KAnimHashedString, Nullable<T>, arrays, List<T>, HashSet<T>, Queue<T>, Stack<T> and Dictionary<K,V> of supported types.
using Shared.OxySync;
using Shared.OxySync.Attributes;
using UnityEngine;
public class CounterBehaviour : NetworkBehaviour
{
[SyncVar(Hook = nameof(OnCounterChanged), SendMode = (int) PacketSendMode.Reliable)]
private int _counter;
[SyncVar]
private float _temperature = 25f;
[Command]
public void CmdSetCounter(int value)
{
_counter = value;
CallClientRpc(nameof(RpcOnCounterUpdated), value);
}
[Command(RequiresHost = true)]
public void CmdReset() => _counter = 0;
[ClientRpc]
private void RpcOnCounterUpdated(int newValue)
{
Debug.Log($"Counter is now {newValue}");
}
[TargetRpc]
private void TargetSendDirectMessage(string message)
{
Debug.Log($"Direct: {message}");
}
private void OnCounterChanged(int oldValue, int newValue)
{
Debug.Log($"{oldValue} -> {newValue}");
}
}Attach CounterBehaviour to an object that has a NetworkIdentity (or spawn it via SpawnUtilsAPI.KNetInstantiate) so its NetId resolves.
Mod development environment setup
ONI Together API