Skip to content

Asset Distribution and Late-Join Behaviour

If you point AssetDirectory at a folder of files, Anomaly can provide those files to connecting clients and keep late joiners aligned with the current visual state.

  1. Server starts and scans the configured asset folder.
  2. A client joins and completes Anomaly authentication.
  3. The server tells the client which assets are available.
  4. The client checks its local cache and requests only missing files.
  5. The server transfers the missing files.
  6. Client-side mods can load the assets through Anomaly’s public asset APIs.
  7. Late joiners receive the current spawned-asset and override state after their asset check finishes.

On startup, Anomaly reads AssetDirectory and records the files it can offer to clients. If the directory is empty or missing, the server continues with no distributable assets.

Use clear folder structure and file names. Mod companion plugins can use the public asset registry APIs to resolve files by path.

When a client joins, Anomaly compares the server’s available assets with the client’s local cache. Files the client already has are reused; only missing files are transferred.

First-time joins can take longer on asset-heavy servers. Returning clients are usually faster because the cache survives across sessions and reinstalls.

Transfers are paced to reduce gameplay impact, but large asset packs still affect join time and server bandwidth. For production servers:

  • Keep the asset folder as small as practical.
  • Prefer compressed formats where appropriate.
  • Test cold-client joins with several players at once.
  • Avoid large mid-round replacements unless the server is in a maintenance or event window.

If a specific @anomaly ID repeatedly causes excessive transfer load, handle it like any other abusive connection: warn, kick, block, or ban the identity.

Server companion plugins can use Anomaly APIs to spawn custom assets, move spawned objects, toggle visibility, and apply supported visual overrides. Anomaly remembers the active state and replays it to late joiners after their asset transfer finishes.

Plugin authors do not need to write separate late-join replay logic if they use Anomaly’s asset APIs.

Anomaly can detect some changes inside the configured asset folder while the server is running. This is useful for development, but production servers should prefer planned restarts for large asset changes.

Server companion plugins can register custom CASSIE / Announcer words that synchronise to every connected client. Custom-word audio is transferred through the existing initial asset package and replayed to clients after file transfer completes.

using Anomaly.Server.Features;
using PlayerRoles.Voice;
public override void OnEnabled()
{
// Path is relative to AssetDirectory; the audio file is transferred to clients
// as part of the initial asset package on connect.
Announcer.AddWord(
apiName: "MYMOD_ALERT",
assetPath: "audio/cassie/mymod_alert.ogg",
durationSeconds: 1.85f,
type: CassieClipCategory.Word);
}
public static IReadOnlyCollection<Word> Words { get; }
public static Word AddWord(string apiName, string assetPath, float durationSeconds,
CassieClipCategory type = CassieClipCategory.Word);
public static Word AddWord(string apiName, byte[] audioHash, float durationSeconds,
CassieClipCategory type = CassieClipCategory.Word);

The durationSeconds value is developer-provided and is what the announcer scheduler uses to pace subsequent words on the line — it must match the actual length of your audio clip or surrounding lines will overlap.

apiName is the identifier mods use when triggering the line ("MYMOD_ALERT" becomes the same kind of word a vanilla "ALERT" is — case-insensitive). Use a MODID_ prefix to avoid collisions with vanilla CASSIE vocabulary and other mods.

type selects the CassieClipCategory (Word, Number, Caption, etc.). Most custom announcements are Word; numeric inserts are Number; on-screen captions only are Caption.

The matching client API is Anomaly.Client.Api.Features.Announcer.AddWord(...), which registers a local-only word backed by an AudioClip you already have in memory. For server-synchronised vocabulary used by every connected client, register on the server side — the client’s announcer vocabulary is populated automatically when the asset package finishes transferring on AnomalyMessaging.ClientReady.

Troubleshooting and Known Limitations.