Skip to content

API Reference

DooDesch edited this page Jun 25, 2026 · 9 revisions

API Reference

For mods that want to appear as a gamemode in Side Hustle's main-menu list. Register early (in your OnInitializeMelon); registration is load-order independent, so it does not matter whether Side Hustle or your mod loads first.

Setting up the project

Reference SideHustle.dll and declare it as an optional dependency, so the hub loads first and your mod still loads cleanly if Side Hustle is absent:

[assembly: MelonOptionalDependencies("SideHustle")]

Reference these from a normal modded install:

  • Mods/SideHustle.dll - the API
  • MelonLoader/net6/MelonLoader.dll, Il2CppInterop.Runtime.dll
  • UnityEngine.CoreModule.dll etc. only if your own gamemode needs them
  • For Thunderstore, list DooDesch-SideHustle as a dependency.

SideHustle.API

namespace SideHustle
{
    public static class API
    {
        // Register (or replace, by Id) a gamemode.
        public static void Register(GamemodeDescriptor descriptor);

        // Remove a previously registered gamemode. Returns true if one was removed.
        public static bool Unregister(string id);

        // True once Side Hustle's own OnInitializeMelon has run.
        public static bool IsReady { get; }

        // The currently registered gamemodes.
        public static IReadOnlyList<GamemodeDescriptor> Registered { get; }
    }
}

Registering with an existing Id replaces it, so re-registering (hot reload, double init) is safe.

GamemodeDescriptor

Field Meaning
Id Stable, unique id (e.g. "you.yourmode"). Required. Used for de-dup and lobby filtering.
DisplayName Shown in the list (falls back to Id).
Description One-line description under the name.
Author Shown in the list (optional).
Icon / IconTex Optional Sprite / Texture2D for the list row. If Icon is null, the hub builds one from IconTex.
Support Singleplayer | Multiplayer | Hybrid - decides whether selecting launches straight into SP or shows Singleplayer / Host / Join.
Surface MenuSpace (overlay on the menu, no save) | World (Side Hustle boots a throwaway save first).
OnLaunchSingleplayer Action<LaunchContext> - start singleplayer. Required for Singleplayer / Hybrid.
OnHostMultiplayer Optional - start hosting a multiplayer session.
OnJoinMultiplayer Optional - join a multiplayer session.
OnExitToHub Optional - called when Side Hustle tears the gamemode down (the player backs out).

Helper properties: AllowsSingleplayer and AllowsMultiplayer derive from Support.

LaunchContext

Passed to your launch callbacks.

Member Meaning
Descriptor The descriptor being launched.
IsHost true = host, false = client, null = singleplayer.
LobbyId Steam lobby id for multiplayer; 0 for singleplayer.
IsSingleplayer true when this is a singleplayer launch (IsHost == null).
PlayerCount Players currently in the lobby (1 for singleplayer).
HostName Steam persona of the host (null for singleplayer).
HasPassword true if the lobby was opened with a password.
Multiplayer The full multiplayer payload (max players, gamemode name, host name, and a free-form ConfigBlob the host published). Null for singleplayer.
ReturnToHub() Call when your gamemode finishes; the hub restores the menu and re-shows the list. Safe to call once.

Enums

public enum GamemodeSupport { Singleplayer, Multiplayer, Hybrid }
public enum GamemodeSurface { MenuSpace, World }
  • MenuSpace gamemodes build their own overlay on top of the main menu and never load a save (e.g. an in-game editor).
  • World gamemodes need the loaded game world, so Side Hustle boots a throwaway save before handing over.

When to register (important)

Register early - in your OnInitializeMelon. The menu list is built from the live registry each time the menu scene loads, so a gamemode registered before the first menu shows up immediately. Registering later is fine too; it appears the next time the menu is opened.

Minimal example

using SideHustle;
using MelonLoader;

[assembly: MelonOptionalDependencies("SideHustle")]

public sealed class Core : MelonMod
{
    public override void OnInitializeMelon()
    {
        SideHustle.API.Register(new GamemodeDescriptor
        {
            Id = "you.yourmode",
            DisplayName = "Your Mode",
            Description = "What your gamemode does.",
            Author = "You",
            Support = GamemodeSupport.Singleplayer,
            Surface = GamemodeSurface.MenuSpace,
            OnLaunchSingleplayer = ctx =>
            {
                // build your overlay / start your mode
                // when finished: ctx.ReturnToHub();
            }
        });
    }
}

Multiplayer gamemodes

Set Support to Multiplayer or Hybrid and add OnHostMultiplayer / OnJoinMultiplayer. When the player picks your gamemode, Side Hustle shows a Singleplayer / Host / Join choice (and, for Host, a player-count picker), then handles the lobby for you:

  • Host - Side Hustle creates a public, joinable Steam lobby, tags it so the server browser can find it, and (for World gamemodes) boots a throwaway world. It then calls OnHostMultiplayer(ctx) with IsHost = true, the LobbyId, the player count and the host options.
  • Join - the player picks a session in the built-in server browser (filtered to your gamemode). Side Hustle joins the lobby; the game streams the host's world in, then it calls OnJoinMultiplayer(ctx) with IsHost = false and the lobby info read back from the host.

Your job is to run the gamemode once the callback fires; networking (lobby + the game's co-op session) is already up. Call ctx.ReturnToHub() to end the session - Side Hustle leaves the lobby and returns to the menu.

SideHustle.API.Register(new GamemodeDescriptor
{
    Id = "you.yourmode",
    DisplayName = "Your Mode",
    Support = GamemodeSupport.Multiplayer,   // or Hybrid to also allow solo
    Surface = GamemodeSurface.World,         // most multiplayer gamemodes need the world
    OnHostMultiplayer = ctx => { /* start the round - you are the host (ctx.LobbyId) */ },
    OnJoinMultiplayer = ctx => { /* sync into the host's session (ctx.HostName, ctx.PlayerCount) */ },
    OnExitToHub = ctx => { /* tear down when the session ends */ }
});

Bigger lobbies (more than the vanilla 4 players) work when the player also has BiggerLobbies installed.

Mod policy (optional)

A gamemode can declare which other mods may stay loaded while it runs, so unrelated mods do not interfere. Set GamemodeDescriptor.Policy:

public sealed class ModPolicy
{
    public string[] AllowedMods;   // extra mods allowed to stay loaded (mod name OR DLL file name)
    public string[] RequiredMods;  // mods this gamemode needs (enabled if disabled; warned if not installed)
}

When the player selects your gamemode, Side Hustle shows a confirmation listing exactly which mods it will disable and enable, then - on confirmation - applies the change and restarts the game into your gamemode (MelonLoader cannot unload a mod at runtime). When the player leaves your gamemode, their original mods are restored. You never list the essentials - MelonLoader, S1API, Side Hustle, your own mod, the Mod Manager & Phone App, and (for multiplayer gamemodes) the multiplayer libraries are always kept, as is anything a kept mod depends on. Match mods by their MelonLoader display name or their DLL file name (case-insensitive).

SideHustle.API.Register(new GamemodeDescriptor
{
    Id = "you.yourmode",
    DisplayName = "Your Mode",
    // ... callbacks ...
    Policy = new ModPolicy
    {
        AllowedMods = new[] { "Some Compatible Mod" },   // everything else loaded gets disabled
        RequiredMods = new[] { "A Mod You Depend On" }   // enabled if present-but-disabled
    }
});

A gamemode with no Policy (the default) never changes which mods are loaded.

Clone this wiki locally