# Feature request: per-observer nameplate state
**Target:** G1R:MP scripting API (server-side)
**Requested by:** Eldoria RolePlay
**Verified against:** documentation dump 1.2.1 / platform update 0.1.2
**Status of workarounds:** all known alternatives verified and ruled out (§4)
---
## 1. What we are trying to build
A **stranger system**: a roleplay server where a character's name is not public
knowledge. You do not know who someone is until they tell you.
- Two players who have never met see each other as `Nieznajomy (147)`
("Stranger #147") — a stable per-character number, not a name.
- After an in-character introduction (`/przedstaw`), each side learns the
other's real name **permanently**, and only for themselves.
- The relation is directional and per-pair: A knowing B says nothing about
whether C knows B.
The chat side of this already works — we send chat messages per recipient, so
each observer can be given a different sender label.
**The overhead nameplate is the only thing we cannot do**, and it defeats the
whole feature: the name floats above the character's head for everyone
regardless of what the observer has learned.
---
## 2. What we are asking for
### 2.1 Preferred: per-observer nameplate text
Sets the nameplate text that **`observerId` sees above `targetId`**, overriding
the global value set by `setPlayerNameplateText`.
### 2.2 Minimal acceptable: per-observer nameplate visibility
If per-observer text is too invasive, a per-observer **visibility** toggle is
enough to unblock us:
We would then keep the global nameplate text as the character's **real name**
and hide it from observers who have not been introduced. Strangers see no
nameplate at all; the `Nieznajomy (147)` label is carried by chat only.
This is strictly smaller than 2.1 and reuses state the engine already
replicates. **Either one unblocks the feature — we do not need both.**
---
## 3. Semantics we need
These apply to both variants.
| Aspect | Required behaviour |
| --- | --- |
| **Precedence** | The per-observer value overrides the global one. With no override present, the observer sees exactly what `setPlayerNameplateText` / `setPlayerNameplateVisible` set. |
| **Default** | No overrides exist. Current behaviour is unchanged for every resource that does not call the new functions. |
| **Direction** | Strictly one-way. `setPlayerNameplateTextFor(A, B, ...)` must not affect what B sees above A. |
| **Replication** | Same guarantees as the existing nameplate state: reliable, server-authoritative, **replayed on relevance entry and late join**. Without this, an override silently disappears when the pair walks apart and back together. |
| **Lifetime** | Cleared automatically when **either** player disconnects. Player ids are reused; a leftover override would show one player's name above a different player. This is the single most important requirement in the table. |
| **Ownership** | Same model as other 0.1.2 state: only the creating resource may set or clear its overrides; they are removed when that resource stops. |
| **Return value** | `bool`, consistent with the other setters, so a rejection is detectable. Returning `true` on a no-op repeat is fine. |
| **Validation** | Same text rules as `setPlayerNameplateText` (48 code points / 96 UTF-8 bytes, control characters and HTML delimiters rejected). |
### Cost profile — why this should be cheap
Overrides change **only when two characters are introduced**, not per frame and
not per tick. In practice that is a handful of calls per player per session.
The natural shape is a sparse map keyed by `(observerId, targetId)`, replicated
only to the one observer. There is no per-frame work and no broadcast: the
delta goes to a single client.
---
## 4. Alternatives we tested and ruled out
We checked these against the 1.2.1 documentation before filing. Listing them so
the same ground is not covered twice.
**`setPlayerNameplateText`** — one value for everyone.
> *"Changes a player's server-authoritative overhead nameplate text."*
**`setPlayerNameplateVisible`** — global, explicitly so.
> *"Shows or hides a player's overhead nameplate **for all observers**."*
**`setPlayerNametagText` / `setPlayerNametagShowing`** — not a separate channel.
> *"Compatibility alias for `setPlayerNameplateText`."*
**`create3DText` with a viewer filter** — would not scale even if `options`
supported one (today it accepts only `drawDistance`, `scale` and `r/g/b/a`).
Overhead labels need one label per **(observer, target)** pair. Twenty players
standing together is 20 × 19 = 380 labels; the documented limits are **256 per
resource and 2048 server-wide**, and `serverconf.cfg` allows 500 slots. The
cap is exhausted by a single crowded camp.
*(A `visibleTo` filter on `create3DText` would still be useful for other things
— private quest markers, per-player hints — but it does not solve this.)*
**Client-side rendering** — not possible. The client API exposes only
`setCameraTarget` and `resetCamera`; there is no world-to-screen projection and
no world-space drawing, so a client cannot position labels itself.
---
## 5. Acceptance scenarios
Concrete cases we would test against.
1. **Baseline unchanged.** A resource that never calls the new functions
behaves exactly as today.
2. **One-way override.** `setPlayerNameplateTextFor(A, B, "Gorn")` → A sees
`Gorn` above B; C still sees the global text; B's view of A is unaffected.
3. **Survives distance.** A and B walk out of relevance range and back. A still
sees `Gorn`.
4. **Survives reconnect of the observer.** A reconnects; A still sees `Gorn`
above B *(assuming the resource re-applies it — see 6)*.
5. **Id reuse is safe.** B disconnects, a different player joins and receives
B's old player id. A must **not** see `Gorn` above the new player.
6. **Reset.** `resetPlayerNameplateTextFor(A, B)` → A falls back to the global
text, not to an empty plate.
7. **Rejection is visible.** Invalid text, unknown player id or a target owned
by another resource returns `false`.
---
## 6. What we do if this is not available
We hide overhead nameplates for **everyone** (`setPlayerNameplateVisible(id,
false)` for all players) and carry identity through chat alone.
This is consistent — nobody ever sees a name that contradicts what they know —
but it removes a piece of information the engine already renders well, and it
means players cannot identify even their own friends at a glance. We would
rather not do that, hence this request.
---
## 7. Summary
One capability is missing: **the nameplate state cannot be varied per
observer.**
- Smallest change that unblocks us: `setPlayerNameplateVisibleFor`.
- Better result for players: `setPlayerNameplateTextFor` (+ `reset…`).
- Everything else on our side is built and waiting: the `acquaintances` table,
the per-pair lookup, the in-character introduction command and the config
switch that turns it all on.
**Target:** G1R:MP scripting API (server-side)
**Requested by:** Eldoria RolePlay
**Verified against:** documentation dump 1.2.1 / platform update 0.1.2
**Status of workarounds:** all known alternatives verified and ruled out (§4)
---
## 1. What we are trying to build
A **stranger system**: a roleplay server where a character's name is not public
knowledge. You do not know who someone is until they tell you.
- Two players who have never met see each other as `Nieznajomy (147)`
("Stranger #147") — a stable per-character number, not a name.
- After an in-character introduction (`/przedstaw`), each side learns the
other's real name **permanently**, and only for themselves.
- The relation is directional and per-pair: A knowing B says nothing about
whether C knows B.
The chat side of this already works — we send chat messages per recipient, so
each observer can be given a different sender label.
**The overhead nameplate is the only thing we cannot do**, and it defeats the
whole feature: the name floats above the character's head for everyone
regardless of what the observer has learned.
---
## 2. What we are asking for
### 2.1 Preferred: per-observer nameplate text
Code:
bool setPlayerNameplateTextFor(int observerId, int targetId, string text)Code:
bool resetPlayerNameplateTextFor(int observerId, int targetId)Code:
string|false getPlayerNameplateTextFor(int observerId, int targetId)Sets the nameplate text that **`observerId` sees above `targetId`**, overriding
the global value set by `setPlayerNameplateText`.
### 2.2 Minimal acceptable: per-observer nameplate visibility
If per-observer text is too invasive, a per-observer **visibility** toggle is
enough to unblock us:
Code:
bool setPlayerNameplateVisibleFor(int observerId, int targetId, bool visible)Code:
bool resetPlayerNameplateVisibleFor(int observerId, int targetId)We would then keep the global nameplate text as the character's **real name**
and hide it from observers who have not been introduced. Strangers see no
nameplate at all; the `Nieznajomy (147)` label is carried by chat only.
This is strictly smaller than 2.1 and reuses state the engine already
replicates. **Either one unblocks the feature — we do not need both.**
---
## 3. Semantics we need
These apply to both variants.
| Aspect | Required behaviour |
| --- | --- |
| **Precedence** | The per-observer value overrides the global one. With no override present, the observer sees exactly what `setPlayerNameplateText` / `setPlayerNameplateVisible` set. |
| **Default** | No overrides exist. Current behaviour is unchanged for every resource that does not call the new functions. |
| **Direction** | Strictly one-way. `setPlayerNameplateTextFor(A, B, ...)` must not affect what B sees above A. |
| **Replication** | Same guarantees as the existing nameplate state: reliable, server-authoritative, **replayed on relevance entry and late join**. Without this, an override silently disappears when the pair walks apart and back together. |
| **Lifetime** | Cleared automatically when **either** player disconnects. Player ids are reused; a leftover override would show one player's name above a different player. This is the single most important requirement in the table. |
| **Ownership** | Same model as other 0.1.2 state: only the creating resource may set or clear its overrides; they are removed when that resource stops. |
| **Return value** | `bool`, consistent with the other setters, so a rejection is detectable. Returning `true` on a no-op repeat is fine. |
| **Validation** | Same text rules as `setPlayerNameplateText` (48 code points / 96 UTF-8 bytes, control characters and HTML delimiters rejected). |
### Cost profile — why this should be cheap
Overrides change **only when two characters are introduced**, not per frame and
not per tick. In practice that is a handful of calls per player per session.
The natural shape is a sparse map keyed by `(observerId, targetId)`, replicated
only to the one observer. There is no per-frame work and no broadcast: the
delta goes to a single client.
---
## 4. Alternatives we tested and ruled out
We checked these against the 1.2.1 documentation before filing. Listing them so
the same ground is not covered twice.
**`setPlayerNameplateText`** — one value for everyone.
> *"Changes a player's server-authoritative overhead nameplate text."*
**`setPlayerNameplateVisible`** — global, explicitly so.
> *"Shows or hides a player's overhead nameplate **for all observers**."*
**`setPlayerNametagText` / `setPlayerNametagShowing`** — not a separate channel.
> *"Compatibility alias for `setPlayerNameplateText`."*
**`create3DText` with a viewer filter** — would not scale even if `options`
supported one (today it accepts only `drawDistance`, `scale` and `r/g/b/a`).
Overhead labels need one label per **(observer, target)** pair. Twenty players
standing together is 20 × 19 = 380 labels; the documented limits are **256 per
resource and 2048 server-wide**, and `serverconf.cfg` allows 500 slots. The
cap is exhausted by a single crowded camp.
*(A `visibleTo` filter on `create3DText` would still be useful for other things
— private quest markers, per-player hints — but it does not solve this.)*
**Client-side rendering** — not possible. The client API exposes only
`setCameraTarget` and `resetCamera`; there is no world-to-screen projection and
no world-space drawing, so a client cannot position labels itself.
---
## 5. Acceptance scenarios
Concrete cases we would test against.
1. **Baseline unchanged.** A resource that never calls the new functions
behaves exactly as today.
2. **One-way override.** `setPlayerNameplateTextFor(A, B, "Gorn")` → A sees
`Gorn` above B; C still sees the global text; B's view of A is unaffected.
3. **Survives distance.** A and B walk out of relevance range and back. A still
sees `Gorn`.
4. **Survives reconnect of the observer.** A reconnects; A still sees `Gorn`
above B *(assuming the resource re-applies it — see 6)*.
5. **Id reuse is safe.** B disconnects, a different player joins and receives
B's old player id. A must **not** see `Gorn` above the new player.
6. **Reset.** `resetPlayerNameplateTextFor(A, B)` → A falls back to the global
text, not to an empty plate.
7. **Rejection is visible.** Invalid text, unknown player id or a target owned
by another resource returns `false`.
---
## 6. What we do if this is not available
We hide overhead nameplates for **everyone** (`setPlayerNameplateVisible(id,
false)` for all players) and carry identity through chat alone.
This is consistent — nobody ever sees a name that contradicts what they know —
but it removes a piece of information the engine already renders well, and it
means players cannot identify even their own friends at a glance. We would
rather not do that, hence this request.
---
## 7. Summary
One capability is missing: **the nameplate state cannot be varied per
observer.**
- Smallest change that unblocks us: `setPlayerNameplateVisibleFor`.
- Better result for players: `setPlayerNameplateTextFor` (+ `reset…`).
- Everything else on our side is built and waiting: the `acquaintances` table,
the per-pair lookup, the in-character introduction command and the config
switch that turns it all on.
Owner Eldoria RolePlay: https://discord.gg/NkS7S6VKEd