G1R:MP Forum Alpha
Log in Join
Gothic 1 Remake Multiplayer

The Colony Forum

News, support, development discussions, server communities, and stories from beyond the Barrier.

Colony discussion

# Feature request: per-observer nameplate state

Started by JoePL in Suggestions

  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
09-06-2026, 12:02 PM (This post was last modified: 09-06-2026, 12:03 PM by JoePL.)
#1
# 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

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
Forum Jump: