UI and content

Register tracked panels, admin tools, phone apps, Toolgun modes, items, NPCs, localization and status effects.

Screen panels

1public void OnClientLoaded( LitherarpAddonContext context )
2{
3 context.RegisterScreenPanel(
4 objectName: "Courier HUD",
5 zIndex: 120,
6 attach: go => go.GetOrAddComponent<CourierHud>() );
7
8 context.RegisterPhoneApp(
9 appId: "courier",
10 label: "Courier",
11 icon: "local_shipping",
12 content: builder => builder.AddContent( 0, "No active delivery." ) );
13}

The UI bootstrap creates a dedicated object with ScreenPanel, then calls the attach factory. Z-index is clamped from 0 to 590 so an addon cannot cover the loading screen at 600. A failed factory is retried up to five times, and a half-created object is destroyed.

SetCorePanelEnabled<T> can replace normal HUD surfaces. It cannot hide the loading screen, escape menu, community warning, rules or chat. Multiple addon hides are tracked independently and teardown restores visibility only when no remaining addon hides the panel.

Admin panels and permissions

RegisterAdminPanel adds a page under Addon Tools and declares its addon.<id>.<node> metadata. This controls Admin Console access to the page. It does not authorize a host mutation by itself. A network or RPC handler must still check the same permission on the host.

RegisterPermissionNode adds a discoverable node to rank editing. HasPermission checks that addon-scoped node for a player.

Phone apps

New apps join the native home grid. Input handlers receive Up, Down, Left, Right, Select and Back through LitherarpPhoneAppInputContext. Set Handled when consumed. An unhandled Back returns home.

Core app overrides can replace any combination of label, icon, order, content and input. Layers unwind in activation order. Core app hides are owner-tracked.

Chat commands and autocomplete

1public void OnHostLoaded( LitherarpAddonContext context )
2{
3 context.RegisterChatCommand(
4 "help",
5 "Shows help for this addon.",
6 new LitherarpChatCommandOptions
7 {
8 Usage = "/help <topic>",
9 PermissionNode = "support"
10 },
11 invocation => invocation.Reply( BuildHelp( invocation.RawArgs ) ) );
12}
13
14public void OnClientLoaded( LitherarpAddonContext context )
15{
16 context.RegisterPermissionNode(
17 "support",
18 "Use /help",
19 "Allows the player to use the addon help command." );
20}

RegisterChatCommand always installs the handler on the host. The standard overload also publishes safe metadata for slash autocomplete. When a player types /he, a registered help command can appear beside matching native commands. Tab or a click inserts /help . The !help form remains executable, but autocomplete only opens for a single slash before the first space.

The host sends command metadata through a bounded, chunked snapshot. Handlers never enter that snapshot. A joining player receives the current catalog after activation. Registration, addon disable and re-apply update open chat panels without requiring the player to change the text already entered.

OptionDefaultContract
AllowWhileRestrictedfalseAllows execution while the sender is cuffed or jailed. This is the options equivalent of the boolean overload.
SuggestInChattrueIncludes command metadata in slash autocomplete. Set it to false when the command must stay out of the public metadata snapshot.
Usage/nameText shown in the suggestion row. A custom value must start with the same slash command name, matched case-insensitively, followed by either the end of the string or a space.
PermissionNodeemptyAccepts an addon-local node such as support, or the full node for the same addon, with a 160-character full-node limit. The standard client hides the suggestion without it and the host rejects direct execution before the handler.

Validation and display rules

  • Names use lowercase letters, digits, underscore or hyphen and contain at most 24 characters.
  • Descriptions are normalized to one line and at most 160 characters. They may be literal text or a localization key registered with RegisterLocalization.
  • Usage is normalized to one line and at most 96 characters. Invalid usage rejects registration.
  • Each addon can register 64 commands. The host accepts at most 512 commands across all active addons.
  • The panel shows at most eight matches. Native commands keep their established order, addon commands follow alphabetically, and names are deduplicated case-insensitively.
  • Arguments split on whitespace, have no quoting syntax and are capped at 16. Parse RawArgs yourself when the command needs a richer grammar.
  • A permission filter improves the normal UI, but it is not a secrecy boundary because suggested metadata is public. Use SuggestInChat = false when the name or description must not be published.

Register permission metadata from the client lifecycle so remote administrators can see the node in rank editing. Permission enforcement does not depend on that metadata registration. The host checks addon.<id>.<node> directly before invoking the handler.

Built-in names cannot be shadowed. The first addon to claim another name wins. Every successful registration is removed by context teardown, including disable, failed activation cleanup, re-apply and scene shutdown.

Gameplay content

1public void OnHostLoaded( LitherarpAddonContext context )
2{
3 context.RegisterToolAction( "courier_tool", "place_marker", action =>
4 {
5 // Parse and validate the untrusted relay payload first.
6 if ( !TryValidateMarkerAction(
7 action.Payload,
8 out var target,
9 out var primaryTrace,
10 out var normalizedPayload ) )
11 return;
12
13 // This raises ToolgunActionRequested with the validated context.
14 // It must be the last check before the mutation.
15 if ( !action.TryAuthorize(
16 target: target,
17 primaryTrace: primaryTrace,
18 validatedPayload: normalizedPayload ) )
19 return;
20
21 var marker = CreateMarker( target, primaryTrace );
22 if ( !marker.IsValid() )
23 return;
24
25 // This marks success and emits ToolgunActionCommitted.
26 action.Commit( marker );
27 } );
28}
SurfaceContext membersNotes
ToolgunRegisterToolMode, RegisterToolActionThe relay validates owner, registered mode, permission, rate and payload bounds. The handler validates action-specific input, then uses TryAuthorize and Commit around the mutation.
Items and consumablesRegisterRuntimeItem, RegisterConsumableThe host owns effects and consumption. Package .item resources are available while their managed package is active.
Dealer productsRegisterDealerProductAdds an item to a native weed or meth dealer with host-side stack pricing.
NPCsRegisterNpc, RegisterNpcKind, override and decorator methodsBuilders return a GameObject or null to suppress. Runtime NPC objects are removed on teardown.
Status effectsApplyStatusEffect, ApplyDamageOverTimeContext methods are the public mutation path. Effect ids are namespaced per addon, reapply refreshes duration and teardown removes active effects. Use an inter-addon service when several addons must coordinate one effect.
Chat commandsRegisterChatCommandHost handlers receive parsed arguments and a reply helper. Handler-free slash suggestions sync to remote clients and disappear on teardown.
LocalizationRegisterLocalizationRuntime entries override language-pack entries. Between addons, the first owner of a key wins until teardown.
Callback payloadPublic members
LitherarpChatCommandInvocationPlayer, CommandName, RawArgs, parsed Args, and Reply(message).
LitherarpAddonConsumableUseContextPlayer, Stack, SlotIndex, mutable ConsumeItem, Notice, NoticeIcon and NoticeColor.
LitherarpAddonToolActionContextPlayer, Toolgun, ModeId, Action, raw PayloadJson, parsed Payload, readonly Results, TryAuthorize(target, targets, primaryTrace, secondaryTrace, validatedPayload) and Commit(params GameObject[] results). Treat relay payloads as untrusted. TryAuthorize can run once. Both mutators become inert as soon as the synchronous handler returns, so never retain this context for later work. Returning without authorization and commit is a rejected or no-op action with no post-hook.
LitherarpToolgunActionContextImmutable policy data for native and custom actions: nullable Target, captured TargetId and TargetName, readonly Targets, nullable PrimaryTrace and SecondaryTrace, and bounded normalized JSON Payload.
LitherarpToolgunTraceNullable Target, world Position and surface Normal.
LitherarpPhoneAppInputContextState, Action and mutable Handled.