PubSubLib.Router 1.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package PubSubLib.Router --version 1.1.0
                    
NuGet\Install-Package PubSubLib.Router -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PubSubLib.Router" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PubSubLib.Router" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="PubSubLib.Router" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PubSubLib.Router --version 1.1.0
                    
#r "nuget: PubSubLib.Router, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PubSubLib.Router@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PubSubLib.Router&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=PubSubLib.Router&version=1.1.0
                    
Install as a Cake Tool

Router — PubSubLib.Router

Package cầu nối giữa game client (kết nối TCP qua MyConnection) và PubSub server (qua NATS). Router là một IServerModule cắm vào MyConnection server.

Mục lục


Vai trò

Game Client ◄── TCP (MyConnection) ──► Router ◄── NATS ──► PubSub Server

Router làm 2 nhiệm vụ chính:

Hướng Nhiệm vụ
Client → Server Nhận PubSub.Cmd từ client qua UDP → gán watcherId từ connection → forward lên NATS topic PubSub.Cmd
Server → Client Nhận PubSub.Evt từ NATS → demux (phân phối) đến đúng client TCP dựa trên watcherId trong message

Router không chứa logic game. Nó chỉ là cầu nối — map connection ↔ watcherId và forward dữ liệu.


Cài đặt

<PackageReference Include="PubSubLib.Router" Version="1.1.0" />

Hoặc project reference:

<ProjectReference Include="..\PubSubLib.Router\PubSubLib.Router.csproj" />

Dependencies:

  • PubSubLib — dùng các kiểu proto message
  • Natify — giao tiếp NATS với PubSub server
  • MyConnection — giao tiếp TCP/UDP với game client
  • System.Threading.Channels

Kiến trúc

                          PubSubRouterModule
┌──────────────┐         ┌──────────────────────────────────────┐       ┌──────────────┐
│ Game Client  │  TCP    │  IServerModule                       │ NATS  │ PubSub Server│
│              │◄──────►│                                      │◄─────►│              │
│ watcherId=1  │         │  _connections: {watcherId → conn}    │       │              │
│              │         │  _watcherIds:  {conn → watcherId}    │       │              │
└──────────────┘         │                                      │       └──────────────┘
                          │  ┌──────────────────────┐           │
                          │  │  IPubSubNatifyClient  │           │
                          │  │  ├─ SendAddWatcher()  │── NATS ──► PubSub.Cmd
                          │  │  ├─ SendRemoveWatcher()│         │
                          │  │  ├─ SendMoveWatcher() │          │
                          │  │  ├─ SendPingUnits()   │          │
                          │  │  └─ SendPublishEvent()│          │
                          │  │                      │           │
                          │  │  OnBatchEnter() ◄───── NATS ──── PubSub.Evt
                          │  │  OnBatchLeave()      │           │
                          │  │  OnSyncEnter()       │           │
                          │  │  OnSyncLeave()       │           │
                          │  │  OnUnitEvent()       │           │
                          │  └──────────────────────┘           │
                          └──────────────────────────────────────┘

API Reference

IPubSubRouterModule

public interface IPubSubRouterModule : IServerModule
{
    static IPubSubRouterModule Create(NatifyServer server, string regionId);
}

Kế thừa IServerModule từ MyConnection — tự động tích hợp vào vòng đời server.

Tham số Mô tả
server NatifyServer — kết nối NATS
regionId Region identifier (vd: "VN", "US") — prefix cho NATS subject

IPubSubNatifyClient

Interface nội bộ dùng để giao tiếp NATS:

public interface IPubSubNatifyClient : IDisposable
{
    static IPubSubNatifyClient Create(NatifyServer server, string regionId);

    // Gửi command → PubSub.Cmd
    void SendAddWatcher(AddWatcherCmd cmd);
    void SendRemoveWatcher(RemoveWatcherCmd cmd);
    void SendMoveWatcher(MoveWatcherCmd cmd);
    void SendPingUnits(PingUnitsCmd cmd);
    void SendPublishEvent(PublishEventCmd cmd);

    // Nhận event ← PubSub.Evt
    void OnBatchEnter(Action<BatchEnterMsg> callback);
    void OnBatchLeave(Action<BatchLeaveMsg> callback);
    void OnSyncEnter(Action<SyncEnterMsg> callback);
    void OnSyncLeave(Action<SyncLeaveMsg> callback);
    void OnUnitEvent(Action<UnitEventMsg> callback);
}

Cách dùng

using MyConnection;
using Natify;
using PubSubLib.Router;

// 1. Tạo MyConnection server (TCP cho client)
var server = IServer.Create(new ServerConfig
{
    tcpPort = 9090,
    udpPort = 9091,
    jwtSecret = "your-secret-key-here-32chars-min",
    jwtAudience = "game-audience",
    jwtIssuer = "game-issuer",
    restEndpoint = "/api",
    websocketEndpoint = "/ws"
});

// 2. Xác thực user — trả về IUser với Id = watcherId
server.OnLogin<LoginBody>(body =>
{
    // body.Value chứa dữ liệu login từ client
    // Id của IUser sẽ được dùng làm watcherId
    return Task.FromResult<IUser>(new GameUser(body.UserId));
});

// 3. Tạo NATS bridge
var natifyServer = new NatifyServer(
    "nats://localhost:4222",  // NATS URL
    "Router",                  // tên router
    "RouterGroup",             // group
    "PubSubServer"             // tên server để gửi command đến
);

// 4. Gắn router module
server.AddModule(IPubSubRouterModule.Create(natifyServer, "VN"));

Yêu cầu:

  • NATS server chạy ở nats://localhost:4222
  • PubSub server đã đăng ký với tên "PubSubServer" trong NATS

Luồng xử lý nội bộ

1. Client connect

Client ── TCP connect ──► MyConnection Server
                              │
                              ▼ server.OnConnect callback
                          PubSubRouterModule.SetServer()
                              │
                              ├─ Parse watcherId = long.Parse(conn.User.Id)
                              ├─ _connections[watcherId] = conn
                              ├─ _watcherIds[conn] = watcherId
                              └─ _natifyClient.SendAddWatcher(new AddWatcherCmd
                                  {
                                      WatcherId = watcherId,
                                      PosX = 0, PosY = 0, Radius = 0
                                  })
                                  │
                                  ▼ NATS PubSub.Cmd ──────► PubSub Server
                                                              pubSub.AddWatcher(watcherId, ...)
  • watcherId được lấy từ conn.User.Id (kết quả của OnLogin callback)
  • Router gửi AddWatcher với vị trí/bán kính mặc định (0,0,0) — client sẽ gửi MoveWatcher để cập nhật sau
  • Router đăng ký SubscribeUdp<PubSubCommand>("PubSub.Cmd", ...) để nhận command từ client

2. Client disconnect

Client ── TCP disconnect ──► MyConnection Server
                                │
                                ▼ server.OnDisconnect callback
                            PubSubRouterModule
                                │
                                ├─ _watcherIds.TryRemove(conn, out watcherId)
                                ├─ _connections.TryRemove(watcherId, out _)
                                └─ _natifyClient.SendRemoveWatcher(new RemoveWatcherCmd
                                    {
                                        WatcherId = watcherId
                                    })
                                    │
                                    ▼ NATS PubSub.Cmd ──────► PubSub Server
                                                                pubSub.RemoveWatcher(watcherId)

3. Client gửi command

Client gửi PubSubCommand qua UDP topic PubSub.Cmd. Router xử lý:

Client ── UDP "PubSub.Cmd" ──► server.SubscribeUdp → OnCommand(conn, cmd)
                                    │
                                    ▼
                                Kiểm tra conn.Connected?
                                Lấy watcherId từ _watcherIds
                                    │
                                    ▼ switch cmd.CmdCase
                                ┌───────────────────────────────┐
                                │ MoveWatcherCmd                │
                                │   → gán cmd.MoveWatcher.WatcherId = watcherId
                                │   → _natifyClient.SendMoveWatcher(cmd)
                                │                               │
                                │ PingUnitsCmd                  │
                                │   → gán cmd.PingUnits.WatcherId = watcherId
                                │   → _natifyClient.SendPingUnits(cmd)
                                │                               │
                                │ PublishEventCmd               │
                                │   → _natifyClient.SendPublishEvent(cmd)
                                │   (watcherId không cần thiết) │
                                └───────────────────────────────┘
                                    │
                                    ▼ NATS PubSub.Cmd ──────► PubSub Server

Quan trọng: Router ghi đè WatcherId trong MoveWatcherCmdPingUnitsCmd bằng watcherId từ connection. Client không cần (và không nên) tự gửi watcherId — router đảm bảo tính xác thực.

4. Server gửi event

Server publish event lên NATS PubSub.Evt. Router nhận và demux:

NATS PubSub.Evt ──────► IPubSubNatifyClient callbacks
                            │
                            ▼ switch msg type
                        ┌───────────────────────────────────────────────────┐
                        │ BatchEnterMsg / BatchLeaveMsg                   │
                        │   → foreach watcherId in msg.WatcherIds        │
                        │       → if _connections.TryGetValue(watcherId) │
                        │       → if conn.Connected                      │
                        │       → _server.SendOnTcp("PubSub.Evt", conn, evt)│
                        │                                                │
                        │ UnitEventMsg                                   │
                        │   → foreach watcherId in msg.WatcherIds        │
                        │       → if _connections.TryGetValue(watcherId) │
                        │       → if conn.Connected                      │
                        │       → if msg.UseUdp:                         │
                        │           _server.SendOnUdp("PubSub.Evt", conn, evt)│
                        │         else:                                  │
                        │           _server.SendOnTcp("PubSub.Evt", conn, evt)│
                        │                                                │
                        │ SyncEnterMsg / SyncLeaveMsg                    │
                        │   → watcherId = msg.WatcherId (đơn lẻ)         │
                        │   → if _connections.TryGetValue(watcherId)     │
                        │   → if conn.Connected                          │
                        │   → _server.SendOnTcp("PubSub.Evt", conn, evt) │
                        └───────────────────────────────────────────────────┘
                            │
                            ▼ TCP "PubSub.Evt" ──────► Client

Demux logic:

  • BatchEnter/BatchLeave: message chứa repeated watcher_ids — duyệt từng watcherId, tìm connection, gửi qua TCP
  • UnitEvent: message chứa repeated watcher_ids. Nếu msg.UseUdp == true → gửi qua UDP (best-effort), ngược lại gửi qua TCP (reliable)
  • SyncEnter/SyncLeave: message chứa 1 watcherId duy nhất — gửi trực tiếp qua TCP

Cấu trúc dữ liệu nội bộ

// Map watcherId → connection (để demux event từ server)
private readonly ConcurrentDictionary<long, IConnection> _connections = new();

// Map connection → watcherId (để tra cứu watcherId khi nhận command từ client)
private readonly ConcurrentDictionary<IConnection, long> _watcherIds = new();

Cả 2 dictionary đều là ConcurrentDictionary — thread-safe, phù hợp với môi trường multi-thread của MyConnection server.


Ví dụ hoàn chỉnh

Full setup Router + Server + Client: xem PubSubTestAll.cs — 3 integration test mô phỏng toàn bộ luồng thực tế.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.4 0 7/6/2026
1.4.3 0 7/6/2026
1.4.2 0 7/6/2026
1.4.1 92 6/23/2026
1.4.0 100 6/21/2026
1.3.0 98 6/19/2026
1.2.0 98 6/19/2026
1.1.0 110 6/18/2026