Outward
You are viewing a potentially older version of this package. View Latest Version
Install

Details

Date Uploaded
13 hours ago
Downloads
111
Size
167KB
Dependency string
CeruleanCutlass-NetKit-0.2.2

NetKit

📖 Full documentation: NetKit wiki page

The shared Photon co-op transport layer for the Outward mod kits. It owns the transport shell CompanionKit's NetBus and SpawnKit's SpawnNet used to duplicate (~65–75% line-identical): relay attach, send helpers, the one hello + peer ledger + absence detector, per-channel+verb counters and trace, join/leave hooks, heartbeat, and the PUN diagnostics. Message semantics (verbs, codecs, authorization, flush policies) stay in the consumer mods.

Requires: BepInEx 5 (BepInExPack_Outward) and ForgeKit — a hard dependency ([BepInDependency]), so BepInEx refuses to load NetKit if ForgeKit/ isn't in BepInEx/plugins/. No SideLoader; NetKit builds on a clean runner.

Full design + decisions: docs/netkit-plan.md. Background: docs/photon-layer-research-2026-07-18.md.

What it owns

  • Channels, not per-mod buses. One shared NK_Bus wire envelope (channel, verb, seq, extra, payload) carries every channel; the demux is channel → verb.
  • One hello / one absence detector. nk.hello on join/connect/guest-scene-ready carries the proto version + the registered channel→version map + optional per-channel extension strings. Peer state surfaces per channel (OnPeerReady / OnPeerLost / IsPeerReady / ReadyCount). The 10 s "peer appears UNMODDED" warning lives here once.
  • Diagnostics under the owning channel's log tag: counters, ring-buffer trace, heartbeat (all PhotonNetwork.time-stamped), the PUN log-signature watcher + per-id unknown-view table. netdump reports transport/attach/peers/counters; selftest runs the loopback + Core checks. The per-channel heartbeat line carries cumulative tx=/rx=/drops= totals (after pt=, before the consumer fragment) so the passive stream survives when the interactive dumps are unreachable.

Dev verbs (BepInEx/config/NetKit_cmd.txt)

  • netdump — co-op census on this machine (transport/attach, per-channel verbs/peers/counters/ ring buffer, hello ledger, PUN-signature + unknown-view tables, helloMuted).
  • selftest[SELFTEST] PASS/FAIL … DONE: hello codec, compat calc, peer-ledger timing, counters, and (in a room) the transport loopback.
  • netmute [on|off|status] (bare = status) — ON suppresses OUTGOING nk.hello sends so this box reads as UNMODDED to peers; the staging tool for incompatible-peer testplan rows without renaming a DLL mid-session. Incoming handling unchanged. Session-only (not persisted), default off.
  • Pure half in core/NetKit.Core (unit-tested, zero game refs): hello codec (lossless escaping), channel-version compat, the peer-ledger timing, counters/trace models, heartbeat formatter, unknown-view table.

API sketch (game side)

var ch = Net.RegisterChannel("sk", "0.4.0", new ChannelOptions {
    LogTag = "SKNET", HelloExtension = () => …, HeartbeatFragment = () => … });

ch.Register("spawn", msg => …);          // msg: Verb, Payload, Extra, SenderActor,
                                         //      SenderIsMaster, SenderIsSelf
ch.SendToMaster("hit", payload, extra);  // bool; also SendToOthers/SendToAll/SendToPlayer
ch.SendToAllLoopback("nk.test", …);      // selftest
ch.OnPeerReady += info => …;             // info: Actor, ChannelVersion, Extension, IsMaster
ch.CountDrop("spawn", "no-row");         // consumer-reported drops

Net.Attached / Net.InRoom / Net.IsMaster / Net.IsGuestInRoom

Send guards return false + drop-count (never throw). Unknown verb on receive = warn-once + counter. Handlers run inside try/catch with per-verb error counters.

Two backends (one internal transport interface)

  • Rpc (default): the NK_Bus [PunRPC] relay piggybacked on CharacterManager's PhotonView (viewID 999). RefreshRpcMonoBehaviourCache() after attach is a harmless no-op precaution — the shipped UseRpcMonoBehaviourCache is false, so it is not "mandatory".
  • Event (opt-in): PhotonNetwork.RaiseEvent on one configurable code ([Net] EventCode, default 177) — no GameObject, no view. Implemented + selftest-covered but config-gated OFF ([Net] Transport = Rpc) until its offline loopback + two-box behavior are live-verified.

Example configuration

BepInEx/config/cobalt.netkit.cfg — created on first launch. Excerpt (defaults):

[Net]
## Transport backend: Rpc (default) or Event.
Transport = Rpc
## RaiseEvent code used by the Event backend.
EventCode = 177
## Seconds between per-channel heartbeat lines.
HeartbeatSeconds = 30
## Seconds before a silent peer is warned as "appears UNMODDED".
HelloWarnSeconds = 10
## Per-message send/receive logging under each channel's tag.
VerboseNet = true
## Photon DisconnectTimeout override in ms; 0 = leave the game's default.
DisconnectTimeoutMs = 0

The dev command channel is BepInEx/config/NetKit_cmd.txt (the verbs above). NetKit ships no config-override data tables.

Cutover note

New builds speak NK_Bus only. An old-build peer (CK_Bus/SK_Bus) looks unmodded to the new hello and is refused co-op cooperation by the existing incompatible-peer machinery — acceptable because the mods ship as one bundle (Outward-Mods-latest.zip). W2/W3 refactor CompanionKit's NetBus and SpawnKit's SpawnNet onto channels ck / sk.

Verification state (docs/netkit-testplan.md). The core transport surface is live-proven — relay attach, the hello/peer ledger, channel demux and the counters all PASSed across real two-box sessions between 2026-07-27 and 07-31. Still open: the RaiseEvent transport backend (V10/V11 — which is why [Net] Transport ships as Rpc), plus V9/V9b/V9c and NK-F2b. Don't read "NetKit is verified" as covering the Event backend, and don't read the open items as covering the RPC path.

Thunderstore development is made possible with ads. Please consider making an exception to your adblock.