Skip to the content
Docs

Nametags & Chat

On this page12

A name over someone's head, or a name in chat, should appear only for the people whose character actually recognises the speaker. That is what this integration does.

lua
Config.NametagSystem = 'none'   -- off by default

Support

SystemSupportNotes
our exports and eventSupportedAny nametag or chat script can use them.
cxc_gamertagStubBuilt-in mask driver, plus a snippet for the per-viewer name.
zeno-rpchatidSnippetEdit their own Config.IsPlayerMasked.
any otherSnippetThe push-cache pattern.

Ready-made snippets

Ready-made snippets, one page each:

PageFor
Any nametag scriptAny overhead-nametag resource — the push-cache pattern.
cxc_gamertagThe mask driver plus the per-viewer name patch.
zeno-rpchatidConfig.IsPlayerMasked.
Nobody NameTagsConfig.CustomRequestScript.
Default chatThe default chat resource: a per-recipient /me.
CodeM Supreme ChatshowPlayerData.

The rule the snippets implement

A name is shown only while the viewer has a live scene result of that player — they scanned them, it worked, and it was within the last ten minutes or so.

Never from a saved memory alone. Never from a Lab ID. And never a real character name: what we hand back is the name the target introduced themselves with, or the memory line ("Met him on Vinewood Blvd in the evening").

Two settings control it:

KeyDefaultAllowedWhat it does
Config.Api.RecognitionSource'scene''scene' · 'scene_or_memory'scene = only a live result counts. scene_or_memory = any saved memory counts too.
Config.Api.LabInNameApisfalsetrue / falseWhether Lab IDs also count for name display. They always count for proofs and HasRecognized.

What you may call

SideExportSignature
serverIsRecognized(viewerSrc, targetSrc, minPct?, opts?) → boolean
serverGetRecognitionLabel(viewerSrc, targetSrc, minPct?, opts?) → string|nil
serverGetRecognition(viewerSrc, targetSrc, opts?) → nil or an info table
serverGetRecognizers(targetSrc, minPct?, opts?) → list of server ids
serverHasRecognized(observerSrc, targetSrc, minPct?, opts?) → boolean ("may act"; always counts a Lab ID)
serverGetReliability(observerSrc, targetSrc, opts?) → 0–100
serverIsFaceHidden(targetSrc) → boolean (viewer-independent)
serverGetConcealment(targetSrc) → a table describing what is visible
clientIsRecognized(targetServerId, minPct?) → boolean
clientGetRecognitionLabel(targetServerId, minPct?) → string|nil
clientGetRecognition(targetServerId) → nil or an info table
clientIsFaceHidden() → boolean — your own face

minPct defaults to Config.Scoring.ActThreshold (80).

These are all read exports: no permission list, no TrustedResources entry needed. Full signatures and return shapes: Server Exports and Client Exports.

The client event

lua
AddEventHandler('5code-recognition:client:onRecognitionChanged', function(targetServerId, info)
    -- info = nil, or { pct, channel, live, label, introducedName, sceneEndsAt }
end)

Local to the client. Any resource on the same client may listen.

Three rules you must not break

cxc_gamertag

Built-in driver. With Config.NametagSystem = 'cxc_gamertag' we call their Mask function when a player's face becomes hidden or visible, so masked players lose their tag.

Their export name is renameable on their side; if you renamed it, adjust the branch in bridge/nametag_client.lua.

The per-viewer name (rather than just the mask) needs the patch in the cxc_gamertag snippet.

zeno-rpchatid

Snippet only, and the adapter reminds you at boot:

Code
zeno-rpchatid: edit their Config.IsPlayerMasked to call IsFaceHidden. Never write their rp_masked state bag.

Edit their Config.IsPlayerMasked to call our IsFaceHidden. We never write their state bag — that is their data, and writing it from a second resource is how two scripts end up fighting over one value.

Any other nametag script

Use the push-cache pattern from Any nametag script:

  1. Listen to 5code-recognition:client:onRecognitionChanged.
  2. Store { pct, label, introducedName } in a local table, keyed by server id.
  3. In your draw loop, read that table. No exports, no calls, no allocation.
  4. Apply minPct before you draw anything.

Chat

The same rule, one step further: a /me or an OOC line that names the speaker should be sent per recipient, so each player sees the name their character actually knows.

GetRecognizers(targetSrc, minPct) gives you the list of server ids who currently recognise that player. Send the named version to them and the anonymous version to everyone else.

Default chat does exactly that for the default chat resource.

ID cards, phones and business cards

An ID-card script that shows a name to somebody is really an introduction:

lua
exports['5code-recognition']:RecordIntroduction(speakerSrc, listenerSrc, name, opts)

Two players and a name, nothing more. It is a write export, so your resource must be in ServerConfig.Api.TrustedResources.

The core decides what happens to that name:

  • It records whether the speaker was concealed when the name was given.
  • The name attaches to whichever memory the listener's next scan of this meeting resolves to.
  • A visible face names the person's real memory. A hidden face can only ever name a disguise memory.

Related settings: Config.Introduce.SameEncounterOnly (a masked introduction only sticks through a scan of the same meeting), Config.Introduce.PendingHours, and Config.Introduce.MaxPending. See Devices & Reports.

Custom

Set Config.NametagSystem = 'custom' and fill the 'custom' branch in bridge/nametag_client.lua, or register a driver at runtime with NametagBridge.SetDriver. A driver that keeps its own table gets an onClear callback so it can empty it.

See Writing a Custom Adapter.