ModNet
Library for multiplayer mods: modded players exchange messages through the game's own network, with a handshake that lists each player's mods and versions. Other mods use it for shared emotes, models and version checks.
By helvetica
| Last updated | 6 hours ago |
| Total downloads | 0 |
| Total rating | 0 |
| Categories | Libraries |
| Dependency string | helvetica-ModNet-0.1.3 |
| Dependants | 0 other packages depend on this package |
This mod requires the following mods to function
BepInEx-BepInExPack
BepInEx pack for Mono Unity games. Preconfigured and ready to use.
Preferred version: 5.4.2305StolenRealmModding-StolenRealmModAPI
Community modding API for Stolen Realm - fluent builders for skills, enemies, quests, UI, combat events, and persistent data
Preferred version: 0.2.0README
ModNet
Lets mods send messages to the other modded players in a multiplayer game, through the game's own network. Other mods use it to share what only they know about, such as a player's emote or custom model, and to check that everyone has the same mod version.
ModNet is a library. On its own it does nothing you can see in play. Every player who wants a mod's shared features needs ModNet and that mod.
- Hello handshake. Each modded player announces their installed mods and versions when they join.
- Players card. With Mods Menu installed, Options → Mods has a ModNet card listing who runs which mods, with version differences highlighted.
A game where some players don't have ModNet hasn't been tested yet.
Settings
| Setting | Default | |
|---|---|---|
| Enabled | on | Exchange messages with other modded players. Off: mods behave as if nobody else had them |
| Log Traffic | off | Log every message sent and received (turn on when reporting a multiplayer problem) |
| Test Tools | off | Ping and payload-size test buttons on the ModNet card |
The file is BepInEx\config\com.helvetica.modnet.cfg.
How it works
Each message is a string passed to the game's replicated Root.SetAnimationTrigger(Character, string). The host
relays it to every client. On a modded player, a Harmony prefix catches the string before it reaches the
Animator.
For mod developers
- Add a soft dependency, so your mod still loads without ModNet:
[BepInDependency("com.helvetica.modnet", BepInDependency.DependencyFlags.SoftDependency)] - Reference
ModNet.dllwithPrivate="false", so it isn't copied next to your mod. - Put every ModNet call in one nested class, and only call into it after checking that ModNet is installed: the
runtime loads ModNet's types only when that class's code runs.
internal static class MySync { static bool _available; internal static void Init() { _available = true; Bridge.Hook(); } // from OnModLoaded, if installed internal static void Tell(Character c, string what) { if (_available) Bridge.Send(c, what); } static class Bridge { internal static void Send(Character c, string p) => ModNetApi.Send("mymod", p, c); internal static void Hook() { ModNetApi.Register("mymod", m => Handle(m.Character, m.Payload)); ModNetApi.PlayerHello += _ => ResendState(); // late joiners } } } // OnModLoaded: if (Chainloader.PluginInfos.ContainsKey("com.helvetica.modnet")) MySync.Init();
API (ModNetApi)
| Member | |
|---|---|
Send(channel, payload, subject) |
To every other modded player, not to yourself. subject = one of your characters; null = any. Returns false if not sent (true when queued) |
Register(channel, handler) |
NetMessage: Channel, Payload, Character (the subject), SenderOwnerId, Sender |
Ready, IsMultiplayer, MyOwnerId |
Sending starts about 3 s after your characters appear in the world |
BecameReady |
Raised once per session when sending starts |
PlayerHello |
Raised when a modded player is first heard from in a session. Re-send your state here |
PlayerLeft |
Raised when a player leaves |
Players, GetPlayer(ownerId) |
PlayerInfo: Name, IsMe, HasModNet, SteamId, Mods (GUID → version), Has(guid, version) |
EveryoneHas(guid, version) |
Gate for features that need every player to have the mod |
Rules
- Events only. Send events, never per-frame data.
- Size and rate. Payloads up to 1000 characters. Sending is throttled to 10 per second, with bursts of 20.
- Sending from a handler is fine. A message sent while another one is being received (from a handler, or from
PlayerHello) is queued and goes out on the next frame. - Separator. Use
|inside payloads. Channel names can't contain it. - Don't trust the sender. A message's
Characteris the sender's own character. Ignore messages that would change your own characters. - Protocol version.
ModNetApi.Protocolis part of the wire marker. Players on another protocol version ignore each other.