Lua-defined entity properties and actions (RPC system) #36

Open
opened 2026-03-19 06:44:16 +00:00 by kit · 0 comments
Owner

Summary

Design and implement a system where the server-side Lua addon can define custom entity properties and actions that are advertised to the WebUI client. This makes the viewer extensible — addon authors can expose game-specific data and controls without modifying the WebUI code.

Two concepts

Properties (readable + writable values)

Things with a current value that can be displayed and optionally edited.

Built-in examples: position, angles, health, color, frozen, on fire, asleep, alive

Custom Lua examples: a money printer's stored cash, a door's lock state, an NPC's aggression level

Properties have:

  • Name/label for display
  • Type — number, string, boolean (checkbox), vector, color
  • Getter — Lua function called to read current value (for custom properties not in the snapshot)
  • Setter — Lua function called when the WebUI edits the value (optional — omit for read-only)
  • Scope — which entity classes this property applies to (or * for all)

Boolean properties render as checkboxes (frozen, on fire, etc.). Number/string properties render as editable fields.

Actions (fire-and-forget RPCs)

One-shot operations with no persistent state.

Examples: delete, disintegrate, break, ignite, extinguish, respawn, strip weapons

Actions have:

  • Name/label for display
  • Callback — Lua function executed on the server when triggered
  • Scope — which entity classes this action applies to
  • Confirmation — optional, requires user to confirm before executing (for destructive actions like delete)

Actions render as buttons in the properties panel.

Protocol design

Server → Client: advertise schema

On handshake (or as a separate message when schema changes):

MSG_PROPERTY_SCHEMA:
  propertyCount: u16
  for each property:
    name: string
    label: string
    type: u8 (0=number, 1=string, 2=bool, 3=vector, 4=color)
    writable: u8
    scope: string (classname filter, "*" for all)

MSG_ACTION_SCHEMA:
  actionCount: u16
  for each action:
    name: string
    label: string
    scope: string
    confirm: u8

Server → Client: custom property values

Sent periodically (or on demand) for entities that have custom Lua-defined properties:

MSG_CUSTOM_PROPERTIES:
  entityId: u16
  propCount: u8
  for each:
    propIndex: u16 (index into schema)
    value: (type-dependent encoding)

Client → Server: set property / invoke action

MSG_SET_PROPERTY:
  entityId: u16
  propIndex: u16
  value: (type-dependent)

MSG_INVOKE_ACTION:
  entityId: u16
  actionIndex: u16

Lua API (server-side)

-- Register a custom property
WebStream:RegisterProperty({
    name = "money",
    label = "Stored Money",
    type = "number",
    scope = "money_printer",
    get = function(ent) return ent:GetMoney() end,
    set = function(ent, value) ent:SetMoney(value) end,
})

-- Register an action
WebStream:RegisterAction({
    name = "disintegrate",
    label = "Disintegrate",
    scope = "*",
    confirm = true,
    callback = function(ent) ent:Disintegrate() end,
})

-- Built-in properties (position, health, color, etc.)
-- are registered automatically with standard getters/setters

Client-side rendering

The properties panel dynamically renders based on the schema:

  • Properties matching the selected entity's class appear as fields (numbers, text, checkboxes)
  • Actions matching the entity's class appear as buttons
  • Grouped into sections: "Properties" and "Actions"
  • #32 — Entity property editing UI (client-side, consumes this system)
  • #35 — Extended entity properties in protocol (the built-in property set)

Labels

feature, design

## Summary Design and implement a system where the server-side Lua addon can define custom entity properties and actions that are advertised to the WebUI client. This makes the viewer extensible — addon authors can expose game-specific data and controls without modifying the WebUI code. ## Two concepts ### Properties (readable + writable values) Things with a current value that can be displayed and optionally edited. **Built-in examples:** position, angles, health, color, frozen, on fire, asleep, alive **Custom Lua examples:** a money printer's stored cash, a door's lock state, an NPC's aggression level Properties have: - **Name/label** for display - **Type** — number, string, boolean (checkbox), vector, color - **Getter** — Lua function called to read current value (for custom properties not in the snapshot) - **Setter** — Lua function called when the WebUI edits the value (optional — omit for read-only) - **Scope** — which entity classes this property applies to (or `*` for all) Boolean properties render as checkboxes (frozen, on fire, etc.). Number/string properties render as editable fields. ### Actions (fire-and-forget RPCs) One-shot operations with no persistent state. **Examples:** delete, disintegrate, break, ignite, extinguish, respawn, strip weapons Actions have: - **Name/label** for display - **Callback** — Lua function executed on the server when triggered - **Scope** — which entity classes this action applies to - **Confirmation** — optional, requires user to confirm before executing (for destructive actions like delete) Actions render as buttons in the properties panel. ## Protocol design ### Server → Client: advertise schema On handshake (or as a separate message when schema changes): ``` MSG_PROPERTY_SCHEMA: propertyCount: u16 for each property: name: string label: string type: u8 (0=number, 1=string, 2=bool, 3=vector, 4=color) writable: u8 scope: string (classname filter, "*" for all) MSG_ACTION_SCHEMA: actionCount: u16 for each action: name: string label: string scope: string confirm: u8 ``` ### Server → Client: custom property values Sent periodically (or on demand) for entities that have custom Lua-defined properties: ``` MSG_CUSTOM_PROPERTIES: entityId: u16 propCount: u8 for each: propIndex: u16 (index into schema) value: (type-dependent encoding) ``` ### Client → Server: set property / invoke action ``` MSG_SET_PROPERTY: entityId: u16 propIndex: u16 value: (type-dependent) MSG_INVOKE_ACTION: entityId: u16 actionIndex: u16 ``` ## Lua API (server-side) ```lua -- Register a custom property WebStream:RegisterProperty({ name = "money", label = "Stored Money", type = "number", scope = "money_printer", get = function(ent) return ent:GetMoney() end, set = function(ent, value) ent:SetMoney(value) end, }) -- Register an action WebStream:RegisterAction({ name = "disintegrate", label = "Disintegrate", scope = "*", confirm = true, callback = function(ent) ent:Disintegrate() end, }) -- Built-in properties (position, health, color, etc.) -- are registered automatically with standard getters/setters ``` ## Client-side rendering The properties panel dynamically renders based on the schema: - Properties matching the selected entity's class appear as fields (numbers, text, checkboxes) - Actions matching the entity's class appear as buttons - Grouped into sections: "Properties" and "Actions" ## Related - #32 — Entity property editing UI (client-side, consumes this system) - #35 — Extended entity properties in protocol (the built-in property set) ## Labels `feature`, `design`
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kit/gmod-web-stream#36
No description provided.