Writing a Custom Adapter
On this page9
Your system is not in the list. That is fine — every selector takes 'custom', and
every bridge/*.lua file is open and stays open after escrow.
How to start
Set the selector in
config.luato'custom':luaConfig.InventorySystem = 'custom'Open the matching file in
bridge/.Find the branches marked
-- setup custom here. Each one is a template with a comment saying exactly what to return.Fill in the ones your system actually needs. The ones you skip keep answering the neutral value.
Restart and check the boot summary.
Which file for which selector
| Selector | File | Side |
|---|---|---|
Config.Framework | bridge/framework_server.lua, bridge/framework_client.lua | both |
Config.MulticharSystem | bridge/framework_server.lua | server |
Config.InventorySystem | bridge/inventory.lua | server |
Config.NotifySystem | bridge/notify.lua | both |
Config.TextUISystem | bridge/textui.lua | client |
Config.ProgressSystem | bridge/progress.lua | client |
Config.TargetSystem | bridge/target.lua | client |
Config.EmoteSystem | bridge/emote.lua | client |
Config.VoiceSystem | bridge/voice_client.lua, bridge/voice_server.lua | both |
Config.AppearanceSystem | bridge/appearance_client.lua, bridge/appearance_server.lua | both |
Config.TattooSystem | bridge/appearance_server.lua | server |
Config.DeathSystem | bridge/death_client.lua, bridge/death_server.lua | both |
Config.PoliceSystem | bridge/police_server.lua | server |
Config.MDTSystem | bridge/mdt.lua | server |
Config.EvidenceSystem | bridge/evidence.lua | server |
Config.ScreenshotSystem | bridge/screenshot_client.lua, bridge/screenshot_server.lua | both |
Config.HudSystem | bridge/hud.lua | client |
Config.NametagSystem | bridge/nametag_client.lua | client |
Config.WeatherSyncSystem | bridge/studio_client.lua | client |
ServerConfig.PhotoHost = 'custom' | bridge/screenshot_server.lua | server |
An empty custom branch is not an error
Leaving a branch as a template does not break anything: every call answers the neutral value — nothing is given, nothing is taken, nothing is counted — and the script logs one line naming the first function that was actually asked for. For example:
Config.InventorySystem = 'custom' and the branches marked "setup custom here" in
bridge/inventory.lua are still empty, so every inventory call answers the neutral
value (nothing is given, taken or counted). Fill in the ones you use -
Inventory.GetItemCount was the first one asked for.That message tells you which function to write next. Work down the list until it stops appearing.
Rules for a custom branch
Hooks — the other extension point
bridge/hooks.lua is a separate file of optional callbacks. They are not adapters:
they are places to say yes, no or instead at a decision the core is about to
make. Leave one empty and the core's own answer stands.
Gates — return false to refuse
| Hook | Called when |
|---|---|
Hooks.CanStartScan | Someone tries to start a scan. |
Hooks.CanShowPrompt | The prompt is about to be drawn. |
Hooks.CanUseDevice | A device is being switched on or used. |
Hooks.CanIntroduce | Someone introduces themselves. |
Hooks.CanStartImplant | A face implant is being started. |
Hooks.CanReadAsPolice | A camera report is being read as police. |
Hooks.ShouldProcessDeath | A death session is about to be opened. |
Hooks.IsPlayerBusy | The core asks whether the player is mid-something. |
Money and standalone
| Hook | Purpose |
|---|---|
Hooks.ChargeImplant | Take the implant price your way. |
Hooks.RefundImplant | Give it back if the surgery fails. |
Hooks.StandaloneCharge | Money on a server with no framework. |
Hooks.GetCustomCharacterId | What a "character" is, with ServerConfig.Standalone.CharacterKey = 'custom'. |
Hooks.GetCustomJob | What job a player has, with no framework. |
Presentation
| Hook | Purpose |
|---|---|
Hooks.FormatAutoLabel | Write the clothing words admins see in the panel and on Discord your own way. Players always see the memory line (where and when they met). |
Hooks.OnHudVisibility | React when our HUD shows or hides. |
Hooks.FilterDiscordExport | Change or drop a Discord export before it is sent. |
Notifications — react, never block
| Hook | Fires on |
|---|---|
Hooks.OnScanStateChanged | A scan starts, pauses, resumes or ends. |
Hooks.OnScanFinished | A scan produced a result. |
Hooks.OnRecognized | A recognition succeeded. |
Hooks.OnProof | A proof was written. |
Hooks.OnLabIdentified | A Lab ID was granted. |
Hooks.OnIdentityReset | A face implant or identity reset completed. |
Hooks.OnDeathSessionClosed | A death session was decided. |
Full signatures: Hooks.
Writing to us from your own resource
If your script needs to tell us something rather than answer a question, use the write exports:
| Export | For |
|---|---|
ReportDown, ReportRevived, ReportRespawned | Death scripts. |
ReportDeathSignal (client export) | The client side of the same, for death scripts that only run on the client. |
GrantLabIdentification | Evidence scripts. |
RecordIntroduction | ID cards, phones, business cards. |
LogAction | Your own cuff, rob, search, escort or custom action. |
ResetIdentity, StartFaceImplant | Surgeon shops and story events. |
GiveItem, TriggerItemUse | Standalone item handling. |
OpenAdminPanel | Admin menus. |
ServerConfig.Api = {
TrustedResources = { 'my_surgeon_shop', 'my_idcard' },
}LogAction also has a per-minute limit for each calling resource
(Config.Proof.ExportCallsPerMinute). A normal script never comes near it.
Read exports need nothing
IsRecognized, GetRecognitionLabel, GetRecognition, GetRecognizers,
HasRecognized, GetReliability, IsFaceHidden, GetConcealment are open to any
resource. No trust list, no permission.
See Nametags & Chat for the pattern, and Server Exports for the signatures.
Testing your adapter
Boot summary — does your system show up where you expect?
Code[5code-recognition] Integrations: ... inventory=custom ...Self-test —
recognition:selftestin the server console. Every line PASS.recognition:status— running scans, detected scripts, voice trust.Config.Debug = true— client bridges then print their choices to F8, and the boot summary adds the job whitelists.The five-minute two-player test — First Test.
Keeping your adapter across updates
bridge/*.lua stays open, but it is also where new adapters land, so the shipped
files change between versions.
- Take the new bridge file.
- Re-apply only your
'custom'branch into it — the branches are written to be self-contained for exactly this reason. - Never keep an old bridge file wholesale: it may be missing functions the new core calls, and the resource will error at load.
See Updating.