QuickySitter is designed so that HUD addons attach as seamless adjustment modules over the same LinkMsg surface that the built-in helper uses. QuickyHUD is the reference HUD; third-party HUDs can speak the same protocol.
This page covers the integration contract: the message numbers, the presence-detection handshake, and the lifecycle. The HUD code itself lives in the separate QuickyHUD project.
Why a HUD addon at all
Stock AVsitter’s [HELPER] is a notecard-driven menu accessed by the creator via the prim’s blue-dialog menu. It works, but every adjustment requires:
- A round-trip through the dialog menu UI
- Chat-channel typing for unsigned-integer offsets
- Repeated
[SAVE]/[CANCEL]pressing
A wearable HUD with X+/Y+/Z+ buttons, Save/Reset, and a single visible offset display is dramatically faster for fine-tuning poses, especially when adjusting couple poses where both partners’ positions matter and the creator wants live feedback.
QuickyHUD attaches non-destructively: on uninstall the prim falls back to the stock [HELPER] flow with no leftover state, and no end user ever sees evidence the HUD was there.
Integration surface
Three components, three message numbers, plus the offset-storage protocol shared with [QS]offset.
| Component | Role | Talks via |
|---|---|---|
[QS]hudproxy |
In-prim presence + ADJUSTMODE state owner. Receives 90266; sends 90271 (Re-Sync to sitA) and 90262 (save offset to [QS]offset); writes QPP_CFG:ADJUSTMODE LSD. |
LinkMsg 90093 (presence), 90266 (mode flip), 90262 (save offset), 90271 (Re-Sync). |
[QS]hudadmin |
In-prim dynamic-prop attacher. Rezzes the QuickyHUD on sit / on manual “Quicky HUD” button. | LinkMsg 90280 (QSPROP_ATTACH). |
| QuickyHUD | The wearable. Talks to the in-prim hudproxy on its own comm channel. | Out-of-band; not part of this protocol. |
HUDPROXY presence: 90093
QuickyHUD’s [QS]hudproxy writes the QPP_CFG:ADJUSTMODE LSD key unprotected on its state_entry. [QS]sitB gates QuickyHUD-aware UI on key existence and value:
- sitB appends the
[HELPER HUD]button to the Adjust-dialog tail for callers that pass the Adjust ACL (qs:sec:adjust; owner-only by default since 1.25, widenable to GROUP/ALL via[SECURITY]), also gated onqs:alive:adjusterpresent, theQPP_CFG:ADJUSTMODEkey existing, andqs:hud:unlicensednot set.- Label vs wire token (sitB 1.256). The button reads
[HELPER HUD]— it is the HUD-flavored sibling of[HELPER]. The token it sends on 90100 is unchanged: sitB translates the label back to[QUICKYHUD]before broadcasting. Scripts listening on 90100 keep matching[QUICKYHUD]and need no change; never match the display label.
- Label vs wire token (sitB 1.256). The button reads
- sitB enriches the main pose menu (
[NEW]/[DUMP]/[SAVE]/[DONE]) ifvalue == "On".
Problem. LSD outlives script removal. If the creator removes hudproxy + hudadmin from the linkset after first install, the LSD key persists with whatever value it last had. sitB keeps showing [HELPER HUD] (clicks no-op because nobody handles 90266) and stays stuck in the qh_on-enriched menu forever if the key happened to be "On" at removal time, including a [DONE] exit that can’t clear the orphaned "On" state.
Fix. 90093 active-presence probe.
| Num | Direction | msg |
id |
Meaning |
|---|---|---|---|---|
| 90093 | [QS]adjuster → hudproxy |
"PROBE" |
"" |
“Are you still there?” |
| 90093 | hudproxy → [QS]adjuster |
"HELLO" |
<script_name> |
Presence reply. Also broadcast unsolicited from hudproxy’s state_entry. |
Adjuster side
integer QS_HUDPROXY_HELLO = 90093;
integer hudproxy_present;
state_entry()
{
hudproxy_present = FALSE;
llMessageLinked(LINK_SET, QS_HUDPROXY_HELLO, "PROBE", "");
llSetTimerEvent(1.0);
}
link_message(integer s, integer num, string msg, key id)
{
if (num == QS_HUDPROXY_HELLO && msg == "HELLO")
{
hudproxy_present = TRUE;
llSetTimerEvent(0.0);
return;
}
}
timer()
{
llSetTimerEvent(0.0);
if (!hudproxy_present)
llLinksetDataDelete("QPP_CFG:ADJUSTMODE");
}
changed(CHANGED_INVENTORY) already calls llResetScript() in adjuster, so a script removal triggers a fresh state_entry → re-probe automatically. No separate inventory-change probe path needed.
hudproxy side
integer QS_HUDPROXY_HELLO = 90093;
state_entry()
{
// ... existing init ...
llMessageLinked(LINK_SET, QS_HUDPROXY_HELLO, "HELLO", llGetScriptName());
}
link_message(integer s, integer num, string str, key id)
{
if (num == QS_HUDPROXY_HELLO && str == "PROBE")
{
llMessageLinked(LINK_SET, QS_HUDPROXY_HELLO, "HELLO", llGetScriptName());
return;
}
}
LSL suppresses self-delivery of llMessageLinked to the same script, so adjuster’s own "PROBE" doesn’t loop back into its 90093 handler. The msg == "HELLO" discriminator is defensive: if a future QuickyHUD script also writes to 90093, only HELLO messages set the flag.
ADJUSTMODE flip: 90266
| Num | Direction | msg |
id |
Meaning |
|---|---|---|---|---|
| 90266 | [QS]adjuster → hudproxy |
"On" / "Off" |
"On": the clicking operator. "Off": llGetOwner(). |
“Flip QuickyHUD ADJUSTMODE remotely.” |
On the "On" flip, id carries the operator who clicked (which, under the Adjust ACL, may be a non-owner) and hudproxy uses it: it forwards that key as ATTACH_FOR_ADJUST (90274) so the HUD lands on whoever entered ADJUSTMODE. On "Off", id is llGetOwner() and hudproxy ignores it.
"On" is sent from the [HELPER HUD] button in the [ADJUST] submenu, and from [QS]animesh’s seat list (which since 1.26 offers that jump only while [QS]adjuster is present, because every way back out runs through the adjuster). The settings confirm dialog in hudadmin that used to emit it as well was removed in 1.257 together with hudproxy’s own settings entry. "Off" comes from the pose menu’s [DONE] / [ADJUST OFF] exit and from end_helper_mode’s auto-Off (only when adjuster’s local helper_method == 1). There is no [STOP HELP] button.
Dynamic prop attach: QSPROP_ATTACH 90280
[QS]prop is a minimally-invasive fork of stock [AV]prop with one new link-message: a way to register and rez a prop dynamically without writing it into the AVpos notecard. Used by [QS]hudadmin to attach the QuickyHUD on sit / on the manual “Quicky HUD” button.
| Num | Direction | msg |
id |
Meaning |
|---|---|---|---|---|
| 90280 | any in-prim source → [QS]prop |
<object>\|<type>\|<point>\|<sitter>\|<post_rez_say> |
sitter UUID | “Register this dynamic prop for the given sitter slot and rez it now. If a prior 90280 with the same (sitter, object) exists, update point + post_rez_say and re-rez.” |
Payload fields (pipe-delimited, parse with llParseStringKeepNulls, since the empty attach-point field must not collapse):
| Field | Content |
|---|---|
| 0 | Object name in this prim’s inventory. Must be an INVENTORY_OBJECT. |
| 1 | Stock [AV]prop type: 0 = ground prop (COPY-OK NEXT), 1 = attachment prop (COPY-TRANSFER NEXT), 2 = attachment prop personal, 3 = special. The HUD case is type 1. |
| 2 | Attachment-point name (case-insensitive substring match into the attach-point table, where CHEST=1 … AVATAR CENTER=40). No match (including an empty string) returns 0, which means the object’s default attach point (llAttachToAvatarTemp(0)), not avatar center. hudadmin passes "" deliberately. For HUDs use e.g. "HUD center". |
| 3 | Sitter slot index (0-based). Must be < llGetListLength(SITTERS); out-of-range messages are silently dropped. |
| 4 | Optional post-rez say. Verbatim string [QS]prop will llSay on its comm_channel once the rezzed prop reports REZ back via the same channel. Empty = no extra message. hudadmin uses it to push "*QUICKYTEXTURE*\|<uuid>" to a freshly-rezzed QuickyHUD. |
The dynamic-prop entry is stored in the qs:prop:* LSD registry. Since the 1.25 lazy-load refactor [QS]prop no longer keeps the stock prop_triggers / prop_types / prop_objects parallel RAM lists; each prop is one qs:prop:<i> LSD row (11 tab-separated fields) with a qs:prop:trig:<trig> index for trigger lookup. The trigger string is <sitter>|<object>, the prop group is <sitter>|QSDYN. Dedup is by trigger: re-issuing 90280 for the same (sitter, object) pair updates the mutable fields (prop_update) and re-rezzes via the existing rez_prop(idx) path, with no growth in the registry.
No new linkmsg is needed for cleanup. Stock [AV]prop’s 90065 (stand-up) handler already calls remove_props_by_sitter(msg, FALSE), which wipes all non-type-3 entries matching the standing sitter, including dynamic ones.
Re-Sync trigger: 90271
The HUD also owns Re-Sync policy. [QS]sitA exposes a single trigger; the HUD decides when to fire it. See Re-Sync Protocol for the full design.
Lifecycle summary
- Install. Creator drops hudproxy + hudadmin into the prim. hudproxy’s
state_entrybroadcasts an unsolicited HELLO (90093). Adjuster latcheshudproxy_present = TRUE. sitA / sitB enable QuickyHUD menu items. - Sit. hudadmin sends 90280 to attach the wearable Pose-HUD. The user interacts with the HUD; X+/Y+/Z+ clicks go straight to
[QS]offsetvia 90262. - Stand-up. Stock 90065 sweeps dynamic props. ADJUSTMODE stays as last set.
- Uninstall. Creator removes hudproxy + hudadmin. Adjuster’s next reset (via
CHANGED_INVENTORY) probes 90093, gets no reply, deletesQPP_CFG:ADJUSTMODE. sitA / sitB stop offering QuickyHUD menu items.
The fact that ADJUSTMODE is unprotected (no LSD_PASS) and adjuster can delete it is a key design decision, because it makes uninstall fully reversible without coordinated removal scripts.
See also
- QSALIVE Discovery: sibling presence protocol for plugin gating.
- Re-Sync Protocol: the SYNC trigger HUD policy owns.
- Personal Pose Offsets: 90262 / 90264 lifecycle from the storage side.
