Skip to content

API: Custom Packets

Lyraedan edited this page Sep 23, 2026 · 1 revision

Send your own serialized messages over the ONI Together networking layer.

Namespaces:

  • ONI_Together.Networking.Packets.Architecture → IPacket, IPacketSkipsRegistration
  • ONI_Together.Networking → PacketSendMode
  • ONI_Together_API.Networking → PacketRegistryAPI, PacketSenderAPI

The IPacket interface

public interface IPacket
{
    void Serialize(BinaryWriter writer);
    void Deserialize(BinaryReader reader);
    void OnDispatched();   // called on the receiving side after Deserialize
}

Packets are identified by a hash of their type, so the same packet type must be registered on every peer. A packet that implements IPacketSkipsRegistration is ignored by auto-registration.

public class ExamplePacket : IPacket, IPacketSkipsRegistration { /* ... */ }

Registering packets

PacketRegistryAPI (in ONI_Together_API.Networking):

Method Description
TryRegister(Type packetType, string nameOverride = null) Register a single packet type
AutoRegisterAll(Assembly? assembly = null) Register every IPacket in an assembly (skips IPacketSkipsRegistration)

Important: register no earlier than the OnAllModsLoaded Harmony event, or the main mod type may not exist yet.

Important: AutoRegisterAll() defaults to the API assembly, not your mod. Always pass your own: PacketRegistryAPI.AutoRegisterAll(typeof(MyMod).Assembly);

Send modes — PacketSendMode

A [Flags] enum mapping to the underlying transport:

Value Int Meaning
Unreliable 0 Not guaranteed, may arrive out of order. Good for frequent position/state.
Immediate 1 Flush immediately, skip buffering.
UnreliableImmediate 2 Unreliable + immediate.
NoDelay 4 Drop if it can't be sent quickly (unreliable only).
UnreliableNoDelay 7 Unreliable + immediate + no-delay.
Reliable 8 Guaranteed and ordered. Good for events/state changes.
ReliableImmediate 9 Reliable + immediate.

Sending packets — PacketSenderAPI

Method Default send mode Notes
SendToAll(IPacket, ulong? exclude = null, PacketSendMode = Reliable) Reliable Broadcast to all connected players
SendToAllClients(IPacket, PacketSendMode = Reliable) Reliable Host only
SendToAllExcluding(IPacket, HashSet<ulong> excludedIds, PacketSendMode = Reliable) Reliable Broadcast except listed ids
SendToPlayer(ulong steamId, IPacket, PacketSendMode = ReliableImmediate) ReliableImmediate To one player
SendToHost(IPacket, PacketSendMode = ReliableImmediate) ReliableImmediate Client → host
SendToAllOtherPeers(IPacket) (fixed) Host → all clients; client → wrapped and relayed via host

All senders no-op if the packet type isn't registered or the mod isn't loaded.

Full example

using System.IO;
using ONI_Together.Networking;
using ONI_Together.Networking.Packets.Architecture;
using ONI_Together_API;
using ONI_Together_API.Networking;

public class ExamplePacket : IPacket
{
    private ulong _senderId;
    private string _message;

    public ExamplePacket(ulong senderId, string message)
    {
        _senderId = senderId;
        _message = message;
    }

    public void Serialize(BinaryWriter writer)
    {
        writer.Write(_senderId);
        writer.Write(_message);
    }

    public void Deserialize(BinaryReader reader)
    {
        _senderId = reader.ReadUInt64();
        _message = reader.ReadString();
    }

    public void OnDispatched()
    {
        UnityEngine.Debug.Log($"From {_senderId}: {_message}");
    }
}

public static class MyModPackets
{
    // Call from an OnAllModsLoaded Harmony postfix / UserMod2 setup.
    public static void Register()
    {
        PacketRegistryAPI.AutoRegisterAll(typeof(MyModPackets).Assembly);
    }

    public static void SendHello()
    {
        if (SessionInfoAPI.IsClient) return; // host-only broadcast

        var packet = new ExamplePacket(SessionInfoAPI.LocalUserID, "Hello ONI Together");
        PacketSenderAPI.SendToAllClients(packet, PacketSendMode.Reliable);
    }
}

Packets should ideally be no larger then 1000 bytes otherwise you may encounter issues.

Clone this wiki locally