Skip to content

database

Jan Boon edited this page Jul 21, 2026 · 2 revisions

title: Database, Links and Expressions description: The interface database, reactive elements and the interface expression language published: true editor: markdown

The interface database is the reactive state store of the UI: a tree of named 64-bit values. Widgets bind to leaves, sliders and pickers write them, <link> elements watch them and push computed results into element properties. Most "logic" in a NeL interface is this triangle — no code involved.

The sliders window: the slider writes a database leaf, the gauges and numbers read it, and a link shows the warning icon above a threshold The same window with the slider below the threshold — the link hid the icon

The database

  • Paths are colon-separated: UI:TEMP:SLIDERH, SERVER:USER:HP. Nodes are created on demand.
  • A leaf stores one sint64. Everything else is a view of those bits: booleans, RGBA packed as R | G<<8 | B<<16 | A<<24, doubles bit-cast. Expressions read leaves as integers — don't @-read a double-typed leaf.
  • The root branches are conventions, not magic: UI: is interface state (UI:SAVE: the persisted part), and in the client SERVER: is the network-synchronized tree and LOCAL: the prediction layer. Writes to SERVER:/LOCAL: from interface data are refused hard (both by the set handler and @ link targets).

Declare and seed your state with <variable>:

<variable entry="UI:TEMP:SLIDERH" type="sint32" value="220" />

A fresh database is all zeros. Values nothing declared read 0 — and several engine bindings are alpha values, so the classic failure is a fully transparent interface: declare UI:SAVE:CONTENT_ALPHA, UI:SAVE:CONTAINER_ALPHA (255) and friends in your config XML. The *_ROLLOVER_FACTOR entries are inverted: 255 = no fade when the mouse is elsewhere, 0 = invisible. {.is-warning}

Widgets that display leaves directly (text_number, bar, …) poll with a change cache — they're cheap and always current. Two-way widgets (scroll sliders, colpick, combo_box, select_number) write leaves and follow external writes.

— reactive rules

<link expr="gt(@UI:TEMP:SLIDERH, 200)"
      target="ui:sample:sliders:content:warn:active" />

A <link> evaluates expr whenever any database leaf it reads changes, and writes the result to its targets. Attributes:

attribute meaning
expr the expression; its @ reads define the trigger set
target comma-separated targets: an element property (path:property — the last segment is the reflected property, e.g. :active, :w, :color), or @db:path to write a database leaf
action / params an action handler to run on trigger (instead of or besides targets)
cond expression gating the action

Target paths resolve relative to the enclosing group first, then as absolute ids. Writable properties are the reflected properties — the same set Lua sees: active, x, y, w, h, color, hardtext, texture, and per-widget extras.

Links evaluate lazily: a leaf change only marks the link; evaluation happens on the widget manager's observer flush each frame. Two embedding-level requirements make this pipeline run at all — a link updater instance and one updateAllLinks() after parsing; if links only fire at startup and never again, read Embedding.

addOnDbChange is the Lua-side equivalent (run a script when leaves change) — see Lua scripting.

The expression language

Interface expressions appear in <link expr>, cond= attributes, the set handler's value=, <define value_from_code>, and menu/proc conditions.

The grammar is minimal — there are no infix operators; everything is a function call:

add(mul(@UI:TEMP:A, 2), 1)      arithmetic
eq(@UI:TEMP:MODE, 3)            comparison
ifthenelse(cond, a, b)          selection
'a string'                      single quotes only, C escapes
@UI:PATH:LEAF                   database read (and trigger registration)
@UI:VAL[UI:INDEX]               indirection: value of UI:INDEX spliced into the path

Literals: integers, decimals (float precision), true/false, 'strings'. Function names are case-sensitive. %name inside an expression attribute is define substitution — resolved before the expression parser runs.

Function catalog

Arithmetic and logic: add, sub, mul, div, mod, abs, min, max, eq, ne, lt, le, gt, ge, and, or, not, ifthenelse(cond,a[,b]), switch(index,v0,v1,…), int (force integer), identity, rand([lo,hi]), ilinear(i,start,end).

Bits: band, bor, bxor, bnot, shl, shr, sal, sar, getbit, extSign8To64, extSign11To64.

Strings and colors: str(...) (concatenate), localize(id) (translation lookup), makeRGB(r,g,b[,a]), intToColor, getRed/getGreen/getBlue/getAlpha, secondsToTimeString/secondsToTimeStringShort.

Database and UI: getprop('ui:abs:path:prop') (read a reflected property; absolute ids only), oldvalue('UI:PATH') (a leaf's previous value), dbcount('UI:LIST:$:X') (count consecutive entries), depends(...) (evaluate args only to register triggers — subscribe a link to leaves it doesn't otherwise read).

The Ryzom client registers ~100 more game-side functions (item names, skill values, …) — those are not available in a bare-library interface. Notably lua('...') and the lua: expression prefix are client extensions: in a standalone embedding they work only if the application registers a lua expression function alongside its lua action handler.

Imperative writes: the set handler

The set action handler is the workhorse counterpart to links — evaluate once, write now:

onclick_l="set" params_l="dblink=UI:TEMP:HELLO|value=add(@UI:TEMP:HELLO,1)"
onclick_l="set" params_l="target_property=ui:sample:views:active|value=not(getprop('ui:sample:views:active'))"

value= is the expression; dblink= writes a leaf, target_property= a reflected property (relative targets resolve against the caller's window), target= an expression evaluating to a target name. See Action handlers.

Clone this wiki locally