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.
Config.NametagSystem = 'none' -- off by defaultSupport
| System | Support | Notes |
|---|---|---|
| our exports and event | Supported | Any nametag or chat script can use them. |
| cxc_gamertag | Stub | Built-in mask driver, plus a snippet for the per-viewer name. |
| zeno-rpchatid | Snippet | Edit their own Config.IsPlayerMasked. |
| any other | Snippet | The push-cache pattern. |
Ready-made snippets
Ready-made snippets, one page each:
| Page | For |
|---|---|
| Any nametag script | Any overhead-nametag resource — the push-cache pattern. |
| cxc_gamertag | The mask driver plus the per-viewer name patch. |
| zeno-rpchatid | Config.IsPlayerMasked. |
| Nobody NameTags | Config.CustomRequestScript. |
| Default chat | The default chat resource: a per-recipient /me. |
| CodeM Supreme Chat | showPlayerData. |
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:
| Key | Default | Allowed | What 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.LabInNameApis | false | true / false | Whether Lab IDs also count for name display. They always count for proofs and HasRecognized. |
What you may call
| Side | Export | Signature |
|---|---|---|
| server | IsRecognized | (viewerSrc, targetSrc, minPct?, opts?) → boolean |
| server | GetRecognitionLabel | (viewerSrc, targetSrc, minPct?, opts?) → string|nil |
| server | GetRecognition | (viewerSrc, targetSrc, opts?) → nil or an info table |
| server | GetRecognizers | (targetSrc, minPct?, opts?) → list of server ids |
| server | HasRecognized | (observerSrc, targetSrc, minPct?, opts?) → boolean ("may act"; always counts a Lab ID) |
| server | GetReliability | (observerSrc, targetSrc, opts?) → 0–100 |
| server | IsFaceHidden | (targetSrc) → boolean (viewer-independent) |
| server | GetConcealment | (targetSrc) → a table describing what is visible |
| client | IsRecognized | (targetServerId, minPct?) → boolean |
| client | GetRecognitionLabel | (targetServerId, minPct?) → string|nil |
| client | GetRecognition | (targetServerId) → nil or an info table |
| client | IsFaceHidden | () → 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
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:
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:
- Listen to
5code-recognition:client:onRecognitionChanged. - Store
{ pct, label, introducedName }in a local table, keyed by server id. - In your draw loop, read that table. No exports, no calls, no allocation.
- Apply
minPctbefore 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:
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.