From 6f2c52170c0dec5fc2de70d7aa122916022d5507 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 16 Jun 2026 07:41:29 +0100 Subject: [PATCH] Route LiteNetLib sends through transport adapter --- docs/cultnet-transport-parity.md | 4 +- src/GameCult.Networking/CultNetTransport.cs | 66 ++++++++++++++++++++ src/GameCult.Networking/NetPeerExtensions.cs | 5 +- src/GameCult.Networking/README.md | 4 +- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/docs/cultnet-transport-parity.md b/docs/cultnet-transport-parity.md index 15b0e3d9..2f792d33 100644 --- a/docs/cultnet-transport-parity.md +++ b/docs/cultnet-transport-parity.md @@ -150,7 +150,9 @@ Current progress: `Client`/`Server` expose profiles for the LiteNetLib lane. The profile names both the modern reliable ordered `schema` channel and the legacy reliable ordered `legacy` union-message channel, so LiteNetLib is described as an - adapter instead of remaining the hidden default. + adapter instead of remaining the hidden default. `NetPeer` send helpers now + route through `LiteNetLibTransportConnection`, so outbound schema and legacy + messages share one channel-aware ReliableOrdered adapter surface. - TypeScript has the first narrow transport connection port: `TcpFramedTransportConnection` owns length-prefixed frame delivery, exposes frame/close/error events, `send(channel, payload)`, `close`, and transfer diff --git a/src/GameCult.Networking/CultNetTransport.cs b/src/GameCult.Networking/CultNetTransport.cs index 4aa4719c..9c0f44ca 100644 --- a/src/GameCult.Networking/CultNetTransport.cs +++ b/src/GameCult.Networking/CultNetTransport.cs @@ -8,6 +8,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using LiteNetLib; namespace GameCult.Networking { @@ -2044,6 +2045,71 @@ namespace GameCult.Networking } } + /// + /// Channel-aware send adapter for the C# LiteNetLib production lane. + /// + public sealed class LiteNetLibTransportConnection + { + private readonly NetPeer _peer; + private readonly CultNetTransportStats _stats = new CultNetTransportStats(); + + /// + /// Initializes a new channel-aware adapter around a LiteNetLib peer. + /// + public LiteNetLibTransportConnection(NetPeer peer, CultNetTransportProfile? profile = null) + { + _peer = peer ?? throw new ArgumentNullException(nameof(peer)); + Profile = profile ?? CultNetTransportProfiles.CreateLiteNetLib("csharp-litenetlib-peer"); + } + + /// + /// Gets the transport profile this adapter implements. + /// + public CultNetTransportProfile Profile { get; } + + /// + /// Gets a snapshot of outbound transfer counters. + /// + public CultNetTransportStats Stats => _stats.Snapshot(); + + /// + /// Sends a payload over a supported LiteNetLib transport channel. + /// + public void Send(string channelId, byte[] payload) + { + if (payload == null) throw new ArgumentNullException(nameof(payload)); + if (!string.Equals(channelId, "schema", StringComparison.Ordinal) + && !string.Equals(channelId, "legacy", StringComparison.Ordinal)) + { + throw new InvalidOperationException($"litenetlib transport only supports schema and legacy channels, got \"{channelId}\"."); + } + + _peer.Send(payload, DeliveryMethod.ReliableOrdered); + _stats.BytesSent += payload.Length; + _stats.FramesSent++; + } + + /// + /// Serializes and sends a legacy GameCult.Networking union message. + /// + public void SendLegacy(T message) + where T : Message + { + if (message == null) throw new ArgumentNullException(nameof(message)); + Send("legacy", MessageSerialization.Serialize(message)); + } + + /// + /// Serializes and sends a CultNet schema-v0 message. + /// + public void SendSchema(T message) + where T : ICultNetSchemaMessage + { + if (message == null) throw new ArgumentNullException(nameof(message)); + Send("schema", CultNetSchemaMessageSerialization.Serialize(message)); + } + } + /// /// Transport connection for the current TCP length-prefixed CultNet schema lane. /// diff --git a/src/GameCult.Networking/NetPeerExtensions.cs b/src/GameCult.Networking/NetPeerExtensions.cs index 4b4470c8..6f691a28 100644 --- a/src/GameCult.Networking/NetPeerExtensions.cs +++ b/src/GameCult.Networking/NetPeerExtensions.cs @@ -1,5 +1,4 @@ using LiteNetLib; -using MessagePack; namespace GameCult.Networking { @@ -16,7 +15,7 @@ namespace GameCult.Networking /// The message to serialize and send. public static void Send(this NetPeer peer, T message) where T : Message { - peer.Send(MessageSerialization.Serialize(message), DeliveryMethod.ReliableOrdered); + new LiteNetLibTransportConnection(peer).SendLegacy(message); } /// @@ -27,7 +26,7 @@ namespace GameCult.Networking /// The schema-v0 message to serialize and send. public static void SendCultNet(this NetPeer peer, T message) where T : ICultNetSchemaMessage { - peer.Send(CultNetSchemaMessageSerialization.Serialize(message), DeliveryMethod.ReliableOrdered); + new LiteNetLibTransportConnection(peer).SendSchema(message); } } } diff --git a/src/GameCult.Networking/README.md b/src/GameCult.Networking/README.md index 08f68cd1..4bde0919 100644 --- a/src/GameCult.Networking/README.md +++ b/src/GameCult.Networking/README.md @@ -369,7 +369,9 @@ The client: `Client.TransportProfile` and `Server.TransportProfile` describe the production LiteNetLib lane as a `litenetlib` transport adapter with reliable ordered `schema` and `legacy` channels. Discovery and operators can inspect the active -pipe without treating LiteNetLib as an implicit protocol truth. +pipe without treating LiteNetLib as an implicit protocol truth. `NetPeer` +send helpers route through `LiteNetLibTransportConnection`, keeping outbound +schema and legacy messages behind that channel-aware adapter surface. The native RUDP socket path also exposes `CultNetRudpReconnectLoop` for caller-owned game or service loops. The caller reports closure, calls