Skip to content

Add the packet capture service and the connection sources - #901

Open
sven-n wants to merge 1 commit into
claude/network-packet-capturefrom
claude/packet-capture-service
Open

Add the packet capture service and the connection sources#901
sven-n wants to merge 1 commit into
claude/network-packet-capturefrom
claude/packet-capture-service

Conversation

@sven-n

@sven-n sven-n commented Aug 25, 2026

Copy link
Copy Markdown
Member

Phase 2 of #895. Targets the phase 1 branch of #897, so it can be merged after that one — please review/merge #897 first, this PR then rebases onto master automatically.

The service is the layer between the capture hook of phase 1 and the admin panel page of phase 3: it collects the connections of all servers which run in this process and captures their traffic on request. Nothing uses it yet.

How the servers provide their connections

A server which can provide the connections of its clients implements IConnectionSource — the game server, the connect server and the chat server do. The proxies of a distributed deployment don't, so their connections are simply not listed, which is exactly the graceful degradation we want.

Each connection is described by an ICapturedConnectionInfo, implemented by the servers themselves. That keeps the IConnection inside the server which owns it — the info exposes what the UI needs (account name, character name, endpoint, client version, the packet definition set which applies) and attaches or detaches a capture sink on request.

Server Connections from Names Version
Game server IGameContext.GetPlayersAsync(), RemotePlayer with a connection Account.LoginName, SelectedCharacter.Name — read live, so a login or character selection shows up right away RemotePlayer.ClientVersion
Connect server ClientListener.Clients none, the clients aren't logged in ConnectServer.ClientVersion from its settings
Chat server its connected clients ChatClient.Nickname not applicable, the chat protocol has no versions

The PacketCaptureService finds those servers through the already registered IServerProvider. That means no extra registration plumbing, and it covers game servers which are created or removed at runtime by the GameServerContainer for free. It's registered in the all-in-one startup only, like the live map page.

Capturing

StartCaptureAsync(connectionId) returns a LiveCapturedConnection and registers it as a sink. A second caller for the same connection gets the same capture; the capture is only detached when the last interested party called StopCapture — so two admins watching the same player don't disturb each other.

The captured packets go into a capped buffer (default 5,000, oldest dropped), so a busy connection can't fill the memory. LiveCapturedConnection implements the existing ICapturedConnection, which means an admin panel page and the WinForms tool can consume the same model — and a capture can be saved as .mucap with the extensions we already have.

Direction: a packet sent on a server connection goes to the client, a received one goes to the server. The existing Packet.ToServer is set accordingly.

Deviations from the plan in #895

  • No PlayerAdded/PlayerRemoved events on GameContext. The plan wanted them so the connection list could update push-based. With the servers being queried on demand, they aren't needed, and adding unused public events felt speculative. If phase 3 wants push updates instead of refreshing on demand, they're easy to add then.
  • SetObservationAsync and IsObserved are not in the service yet. They belong to the account flag, which arrives with the migration in phase 5.
  • The three server projects now reference the analyzer library, and the analyzer library references MUnique.OpenMU.Interfaces (for ServerType). Interfaces itself stays free of project references. If you'd rather have the capture abstractions in a separate small project instead of the analyzer library, that's a straightforward move.

Tests

8 new tests in PacketCaptureServiceTest: connections collected from several sources (and a server which isn't a source being skipped), lookup by id, lookup by account or character name (case insensitive, server scoped), a capture being attached and detached, a capture being shared by two parties and only stopped by the last one, an unknown connection returning null, the direction of captured packets, and the oldest packets being dropped when the buffer is full.

Verification

  • dotnet build MUnique.OpenMU.sln -p:ci=true → 0 errors; no warning originates in a file this PR adds or changes.
  • The complete test suite passes: 1559 tests, 0 failures (MUnique.OpenMU.Network.Tests is at 59 passed, 4 skipped).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Pb82LmoaUVdZtBtQs7xrtA


Generated by Claude Code

The service collects the connections of all servers which run in this
process and captures their traffic on request. It's the layer between
the capture hook of the connections and the admin panel page.

* A server which can provide the connections of its clients implements
  IConnectionSource. That's the game server, the connect server and the
  chat server; the proxies of a distributed deployment don't, so their
  connections are simply not listed.
* Each connection is described by an ICapturedConnectionInfo, which is
  implemented by the servers themselves. That way the network connection
  stays inside the server which owns it: the info exposes the account
  and character name, the endpoint, the client version, the packet
  definition set which applies - and it attaches or detaches a capture
  sink on request.
* The PacketCaptureService finds those servers over the already
  registered IServerProvider, so no additional registration is needed
  when servers are created or removed at runtime.
* A capture is shared by all interested parties and stops when the last
  one is gone. Its packets are kept in a capped buffer, so a busy
  connection can't fill the memory.

Registered in the all-in-one startup only, like the live map page.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Pb82LmoaUVdZtBtQs7xrtA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants