Custom Networking
Custom messages use the authenticated Anomaly channel. IAnomalyMessage includes direction, authentication-phase, and payload-bound policy. A message that does not declare those policies is not a valid public contract.
Directional shared messages
Section titled “Directional shared messages”using System.IO;using Anomaly.Shared.Networking;
namespace MyMod.Shared;
public sealed class PingRequest : AuthenticatedClientToServerMessage, IAnomalyMessage{ public string MessageName => "mymod:ping_request"; public MessageChannel TransportChannel => MessageChannel.ReliableOrdered; public override int MaxPayloadBytes => sizeof(long);
public long ClientTicks { get; set; }
public void Serialize(BinaryWriter writer) => writer.Write(ClientTicks); public void Deserialize(BinaryReader reader) => ClientTicks = reader.ReadInt64();}
public sealed class PingResponse : AuthenticatedServerToClientMessage, IAnomalyMessage{ public string MessageName => "mymod:ping_response"; public MessageChannel TransportChannel => MessageChannel.ReliableOrdered; public override int MaxPayloadBytes => sizeof(long) * 2;
public long ClientTicks { get; set; } public long ServerTicks { get; set; }
public void Serialize(BinaryWriter writer) { writer.Write(ClientTicks); writer.Write(ServerTicks); }
public void Deserialize(BinaryReader reader) { ClientTicks = reader.ReadInt64(); ServerTicks = reader.ReadInt64(); }}Register the same names, exact message types, fixed channels, and policies on both sides. Undefined channels and changed factory contracts are rejected; sending a different type under a registered name is rejected before a replay counter is consumed:
using Anomaly.Shared.Networking;using MyMod.Shared;
AnomalyMessageRegistry.Register( "mymod:ping_request", () => new PingRequest());
AnomalyMessageRegistry.Register( "mymod:ping_response", () => new PingResponse());Sending
Section titled “Sending”Client code uses Anomaly.Client.Api.Networking.AnomalyMessaging.Send and subscribes to its MessageReceived event. Send throws if the message cannot be delivered. Register messages during mod initialization and send only while connected to an authenticated server.
Server code uses Anomaly.Server.Api.Networking.ClientMessaging.Send, TrySend, SendToAll, or SendToAllExcept. Single-recipient calls take a native LabAPI Player; SendToAllExcept takes the excluded player. TrySend returns false for an unavailable recipient or rejected message policy. Send throws on delivery failure; broadcasts attempt each recipient independently. Receive through ClientEvents.MessageReceived and its ClientMessageReceivedEventArgs.Player and .Message properties. Subscribe in your LabAPI plugin’s Enable and unsubscribe in Disable.
The runtime rejects wrong-direction traffic before invoking mod handlers. Custom messages are available only after the authenticated session is established. Ordinary custom messages do not require optional-content approval.
Bounds and decoding
Section titled “Bounds and decoding”MaxPayloadBytes counts only the bytes produced by Serialize; it excludes the 1-byte Anomaly channel ID, 4-byte counter, and 4-byte message hash. It must match the real serialized format. The complete encoded/encrypted frame must also fit the deployed transport’s reported maximum for the chosen channel.
- Bound strings and collection counts before allocation.
- Reject malformed lengths, duplicate keys, trailing bytes, and non-canonical forms.
- Do not use a custom message for arbitrary files or large objects.
- Use the supported content transfer for item PNGs and allowed OGG audio.
The runtime also applies per-peer message and byte rate limits before deserialization, samples invalid-envelope logs, isolates broadcast recipients, and disconnects abusive peers.
Compatibility
Section titled “Compatibility”The application protocol is v5. Client and server protocol versions must match; mismatches are rejected during connection. Keep client, server, and the shared message assembly from one tested release set.
Continue to Assets and Localization.