Skip to content

One internal grant primitive, grown only by real consumers ​

Status: Accepted (2026-06-24)

Context ​

Giving a player something (a weapon, health, armor) is a server decision delivered to that player's client. Weapon grants came first, as a server-owned command; pickup rewards were known to follow and would need the same delivery. Two questions had to be settled before either was built: how the grant is typed, so rewards do not grow a second delivery path, and how it travels to the client. This builds on the reliable event lane (ADR 0007) and the server facade (ADR 0009).

Decision ​

  • A grant is a typed enum with only the variants that have a caller. Grant (src/roster/mod.rs) has Weapon { weapon_type, ammo, set_as_current }, Health { amount, max } and Armor { amount, max }. Each variant came with its consumer and its packet: GiveWeapon for weapons, GiveHealth / GiveArmor for rewards (ADR 0014). There are no dead variants and no wire for them. Grant is internal: the facade exposes named verbs (give_weapon, add_health, add_armor) that build it (ADR 0019), and all of them share one validation and one delivery funnel.
  • Delivery is reliable and recipient-implicit. Grant packets travel on the reliable event lane to the address of the target only and carry no subject id: the receiving client applies the grant to its own player. Other players see the result through normal sync (the held weapon in the player's frames, health and armor in the relayed values).
  • The client applies grants in order and drops none. A grant is an event, so it is not latest-wins. A teleport, by contrast, is latest-wins on the client: only the final destination matters.
  • The server validates before sending. A weapon id must be one a player can hold (1 to 46, except 19, 20 and 21) and allowed by the gamemode (WeaponNotAllowed otherwise); ammo must not exceed 65535, so every legal total fits the frame's ammo field. set_as_current equips the weapon (the parachute is never equipped by a grant). Health and armor amounts and ceilings must be finite, positive and at most 200. An invalid grant never reaches the wire; the client checks the values again, because a wire value is untrusted on both sides. The server owns the weapon inventory and commits a weapon grant into it (ADR 0024).
  • Teleport is a general facade action; destinations are gamemode data. teleport(player, position, heading, interior) (dlmp.teleport(player, { x, y, z, heading, interior })) knows nothing of named places. Any catalog of destinations, including interiors, belongs to the gamemode that offers it, not to the core.

Consequences ​

  • Pickup rewards reused the weapon grant's validation and delivery instead of adding a second path.
  • A new kind of grant arrives with its consumer, its packet and its validation together; its encoding is designed when it is needed, not frozen in advance.

Alternatives considered ​

  • A full reward enum up front (weapon, health, armor, money). Rejected: three variants with no consumer and unrelated payloads, whose packet layout would have been fixed before anyone designed it.
  • A standalone weapon command outside a grant primitive. Rejected: rewards would then need a second delivery path for the same job.
  • One shared command queue on the client for grants and teleports. Rejected: they need different semantics (every grant applies; only the last teleport matters).

Documentation licensed under CC BY 4.0. DarqlineMP is not affiliated with Rockstar Games or Take-Two Interactive. Grand Theft Auto is a trademark of Take-Two Interactive Software, Inc.