Skip to content

Architecture Choices

Anomaly supports three distinct integration shapes. Picking the right one on day one saves refactor pain later: going from client-only to shared-protocol means adding a shared message assembly and server-side code paths; going the other direction means deleting code. This chapter walks through each shape with its use cases, the DLL references it needs, and the decision criteria.

Your mod runs entirely on the modded client. No server-side component and no custom network messages.

  • What you reference: Anomaly.Client.Api.dll, Anomaly.Shared.dll.
  • What you build: a single MelonLoader mod DLL.
  • What you hook: events on ClientEvents, PlayerEvents, RoleEvents, etc. — the 14 handler classes under Anomaly.Client.Api.Events.Handlers.
  • Typical use cases: HUD overlays, personal QoL features (auto-reload indicators, custom hotkey sets), client-side stat trackers, pure-cosmetic local effects.
  • What the server knows: nothing. The server cannot distinguish a client running your mod from one that isn’t.

A client mod paired with a separate LabAPI server plugin. The two use Anomaly’s custom-messaging layer to cooperate, but they do not define their own shared message types.

  • What you reference client-side: Anomaly.Client.Api.dll, Anomaly.Shared.dll.
  • What you reference server-side: Anomaly.Server.dll, Anomaly.Shared.dll, plus LabApi.dll for the plugin entrypoint.
  • What you build: two DLLs — a MelonLoader mod for the client and a LabAPI plugin for the server.
  • Typical use cases: round-start role reassignments, server-driven cosmetic announcements, admin tools that surface UI on the client.

Client and server share custom message types, defined once in a shared assembly and registered on both sides.

  • What you reference: Anomaly.Client.Api.dll + Anomaly.Shared.dll on the client, Anomaly.Server.dll + Anomaly.Shared.dll on the server, plus a third shared project (yours — often named YourMod.Shared) that carries the message definitions.
  • What you build: three DLLs — client, server, and your shared protocol.
  • What you hook: NetworkMessageEvents.AnomalyMessageReceived on the client; AnomalyMessaging.MessageReceived on the server.
  • Typical use cases: authoritative game logic extensions, custom inventory systems, server-driven content delivery, anything where the client has to present state the server owns.
QuestionIf yes →
Does your feature run entirely on the local client and need no knowledge the server could block or audit?Client-only
Do you need the server to authoritatively trigger something on clients, but the messages are simple events you can express with built-in Anomaly messages?Client + server companion
Do you need clients and the server to exchange structured, custom data types?Shared-protocol
Do you need to add new content (custom items, custom SCPs, custom procs)?Shared-protocol (usually)
Do you only need to replace existing game visuals, not add new behaviour?Client-only, possibly combined with server-sent AssetOverrides (no custom messages needed).

When in doubt, start client-only. You can add a server companion later if you find you need authoritative state or cross-player behaviour. Going the other direction — removing a server companion and its shared-protocol assembly after the fact — is harder because you’ll have cemented message contracts in every deployed version.

  • Referencing Anomaly.Client.dll to get at an internal type. If the API you need isn’t in Anomaly.Client.Api, open an issue or a PR to expose it. Don’t bind to the internal implementation.
  • Sharing a message type by copying the class file into both client and server projects. Messages must come from a single shared assembly so both sides stay compatible. Duplicating a message class is a bug-generator — use a shared project.
  • Writing to %APPDATA%\Anomaly\ from your mod. That’s the Launcher’s shared identity, cache, and metadata directory. User-facing settings should use MelonPreferences categories, and ClientConfig / ClientPersistence remain MelonPreferences-backed compatibility facades for typed key/value state.

Environment Setup.