Skip to content

Custom Items

Custom content supports stock-template items. A custom item reuses one registered stock item template and can change its display name, description, and optional PNG icon.

using Anomaly.Server.Api.Content;
CustomItems.Register(new CustomItemDefinition
{
Key = "mymod:field_kit",
BaseTemplate = ItemType.Medkit,
DisplayName = "Field Kit",
Description = "A server-authoritative custom medkit.",
IconPath = "items/field_kit.png",
});

Register definitions in your LabAPI plugin’s Enable() before the catalog freezes. Registration copies the definition, so later edits to that object do not change the catalog. Keys use lowercase colon-separated modid:name grammar. The mod ID uses dotted namespace segments and cannot use anomaly or anomaly.*; the name is a single segment starting with a letter and containing lowercase letters, digits, or underscores. The server sorts keys canonically and allocates IDs as one transaction, starting above the current stock maximum and ending at 127. Invalid keys, duplicate registrations, and unsupported stock templates are rejected before registration changes state. Keys, display names, and descriptions must be valid Unicode and fit 128, 512, and 4096 UTF-8 bytes respectively; a display name is required. Invalid definition arguments throw before changing the catalog, preserving earlier valid registrations. ID allocation, collisions, and icon materialization are validated transactionally before the catalog can be offered.

The stock base owns the model, pickup prefab, behavior plumbing, serialization, and weight. The definition customizes only presentation text and an optional icon.

The authenticated flow is offer, consent decision, missing-file request, verified transfer, staged materialization, then activation acknowledgement.

  • Consent is bound to server fingerprint, catalog SHA-256, and manifest SHA-256.
  • Decline sends zero file requests and activates nothing.
  • Changing any element of the tuple requires new consent.
  • Server readiness advances through Offered, Approved, Transferring, and Transferred before the client can acknowledge Activated.
  • The client stages every template clone, PNG decode, icon assignment, text entry, and collision check before publishing.
  • Any failure rolls back the whole catalog.
  • The server grants only after the exact catalog digest is acknowledged.

Pending consent can remain open without occupying transfer slots or bytes. Approval starts the transfer inactivity deadline. Declining optional content leaves the authenticated connection available for ordinary gameplay and custom messages; transfer or activation failure revokes the grant and disconnects.

using Anomaly.Server.Api.Content;
using Anomaly.Server.Api.Events.Arguments;
using Anomaly.Server.Api.Events.Handlers;
void OnContentReady(ClientEventArgs ev)
{
if (CustomItems.GetPlayerReadiness(ev.Player) == ContentReadiness.Activated)
CustomItems.TryGiveItem(ev.Player, "mymod:field_kit", out _);
}
ClientEvents.ContentReady += OnContentReady;

Use TryGetItemId, TryGetItemKey, and IsCustomItem to recognize definitions without persisting assigned IDs. Register the handler in Enable() and remove the same handler with ClientEvents.ContentReady -= OnContentReady in Disable(); the quickstart shows the complete plugin pattern. ContentReady fires once per player-instance activation. A native round restart replaces the player instance, so the new player must activate again even when the authenticated connection survives. Matching persisted consent reuses cached bytes without another prompt; undecided or declined offers may prompt again.

Transferred means the approved missing files have arrived and passed transfer validation. It is not grant-ready: materialization and the exact-digest activation acknowledgement still have to succeed. Declined and Failed never become grant-ready.

Subscribe to LabAPI PlayerEvents.UsedItem and recognize the item with CustomItems.TryGetItemKey(ev.UsableItem.Type, out var key). Choose a stock base that actually has a use action; a passive base such as Coin does not become usable merely because logic is attached.

IconPath is relative to the operator’s configured AssetDirectory and must identify a PNG. The transfer envelope permits 64 MiB per file and 256 MiB per manifest, but PNG decoding admits at most 16 MiB encoded, 4096 pixels per dimension, 16 Mi pixels, and 64 MiB retained decoded data. Header, peak-memory, and aggregate-memory limits are checked before activation.

Registered definitions freeze for the server process; restart to change or remove them. Catalog offers are immutable for an active player instance. File changes rebuild the offer for future player instances; reconnect or native round restart receives the current tuple.

Continue to Managed Modules.