helvetica-ModNet icon

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.

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-5.4.2305 icon
BepInEx-BepInExPack

BepInEx pack for Mono Unity games. Preconfigured and ready to use.

Preferred version: 5.4.2305
StolenRealmModding-StolenRealmModAPI-0.2.0 icon
StolenRealmModding-StolenRealmModAPI

Community modding API for Stolen Realm - fluent builders for skills, enemies, quests, UI, combat events, and persistent data

Preferred version: 0.2.0

README

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

  1. Add a soft dependency, so your mod still loads without ModNet:
    [BepInDependency("com.helvetica.modnet", BepInDependency.DependencyFlags.SoftDependency)]
    
  2. Reference ModNet.dll with Private="false", so it isn't copied next to your mod.
  3. 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 Character is the sender's own character. Ignore messages that would change your own characters.
  • Protocol version. ModNetApi.Protocol is part of the wire marker. Players on another protocol version ignore each other.