GuidesUI
GUI Hooks Reference
Complete reference for guihooks — sending events to the UI, toast notifications, streaming data, state changes, and common UI event patterns.
guihooks is how your Lua code talks to the UI. Whether you're showing a notification, updating a dashboard, or navigating to a new screen, it all goes through this bridge. This reference covers every method and the most common event patterns.
Overview
guihooks is the primary interface for communicating from GE Lua to the HTML/JavaScript UI. It supports one-way events, request/response patterns, and high-frequency data streaming.
| Method | Direction | Pattern |
|---|---|---|
guihooks.trigger(event, ...) | GE → UI | Fire-and-forget event |
guihooks.triggerClient(targetId, event, ...) | GE → specific UI | Targeted event |
guihooks.triggerStream(name, data) | GE → UI | Immediate stream send |
guihooks.queueStream(key, value) | GE → UI (batched) | High-frequency data |
guihooks.sendStreams() | GE → UI | Flush queued streams |
guihooks.message(msg, ttl, category, icon) | GE → UI | Simple popup message |
guihooks.triggerRawJS(event, jsonStr) | GE → UI | Pre-formatted JSON event |
Triggering Events
Basic Events
-- One-way event to UI - most common pattern
guihooks.trigger('EventName', data)
-- String data
guihooks.trigger('MenuOpenModule', 'vehicleselect')
-- Table data (auto-serialized to JSON)
guihooks.trigger('MissionStateChanged', {active = true, id = "mission_01"})
-- No data
guihooks.trigger('MenuHide')
guihooks.trigger('ClearTasklist')State Changes
-- Navigate to a UI state
guihooks.trigger('ChangeState', {state = 'play', params = {}})
guihooks.trigger('ChangeState', {state = 'menu'})
guihooks.trigger('ChangeState', {state = 'vehicleInventory'})
guihooks.trigger('ChangeState', {state = 'painting', params = {}})
guihooks.trigger('ChangeState', {state = 'tuning', params = {}})
-- Navigate back
guihooks.trigger('UINavigation', 'back', 1)Notifications (Toast Messages)
-- Standard notification
guihooks.trigger('Notification', {
type = 'info', -- 'info', 'warning', 'error', 'success'
title = 'Title',
message = 'Description text'
})
-- Toast-style (shorter, less intrusive)
guihooks.trigger("toastrMsg", {
type = "success",
title = "Game Saved",
msg = ""
})
guihooks.trigger("toastrMsg", {
type = "error",
title = "Update required",
msg = "Please update to the latest version."
})
guihooks.trigger("toastrMsg", {
type = "info",
title = "Vehicle sold",
msg = vehicleInfo.Name .. " for $" .. string.format("%.2f", price)
})
guihooks.trigger("toastrMsg", {
type = "warning",
label = "vehStored",
title = "Vehicle stored",
msg = "Damaged vehicles have been moved to storage."
})Data Responses
Send data back to the UI in response to UI-initiated queries:
-- Respond with career save slots
guihooks.trigger("allCareerSaveSlots", resultTable)
-- Respond with vehicle inventory data
guihooks.trigger("vehicleInventoryData", data)
-- Respond with purchase data
guihooks.trigger("vehiclePurchaseData", data)
-- Respond with current save slot name
guihooks.trigger("currentSaveSlotName", {saveSlot = slotName})
-- Respond with shopping cart data
guihooks.trigger("sendPaintingShoppingCartData", data)
guihooks.trigger("sendTuningShoppingData", shoppingData)
-- Signal completion
guihooks.trigger("saveFinished")
guihooks.trigger("onCareerPaintingStarted")Streaming Data
For high-frequency data (e.g., telemetry, live dashboards):
Queued Streams (Batched)
-- Queue data for batch send (call in onUpdate)
local function onUpdate(dtReal, dtSim, dtRaw)
guihooks.queueStream('vehicleSpeed', currentSpeed)
guihooks.queueStream('engineRPM', currentRPM)
guihooks.queueStream('fuelLevel', fuelPercent)
-- Streams are flushed automatically by main.lua's render loop
end
M.onUpdate = onUpdateImmediate Streams
-- Send stream data immediately (bypass queue)
guihooks.triggerStream('missionProgress', {
completed = 3,
total = 10,
currentObjective = "Deliver package"
})Common UI Events Reference
Game State Events
| Event | Data | Purpose |
|---|---|---|
ChangeState | {state = 'play'} | Navigate UI to state |
MenuOpenModule | moduleName (string) | Open UI module |
MenuHide | - | Hide menu overlay |
UINavigation | 'back', count | Navigate back |
HideCareerTasklist | - | Hide career task UI |
ClearTasklist | - | Clear task list |
Notification Events
| Event | Data | Purpose |
|---|---|---|
Notification | {type, title, message} | Full notification |
toastrMsg | {type, title, msg} | Toast message |
Career Events
| Event | Data | Purpose |
|---|---|---|
allCareerSaveSlots | table | Save slot list |
vehicleInventoryData | table | Inventory data |
vehiclePurchaseData | table | Purchase info |
vehicleShopDelta | table | Shop changes |
gameContextPlayerVehicleDamageInfo | {needsRepair} | Damage status |
onAnyMissionChanged | state, missionId | Mission state change |
saveFinished | - | Save completed |
addListing | {inventoryId} | Add marketplace listing |
Vehicle/Tuning Events
| Event | Data | Purpose |
|---|---|---|
onCareerPaintingStarted | - | Paint mode entered |
sendPaintingShoppingCartData | table | Paint shop data |
sendTuningShoppingData | table | Tuning shop data |
updateTuningVariable | table | Tuning var update |
Common Patterns
State + UI Sync (No Ghost Values!)
local isActive = false
-- Always update UI when state changes
local function setActive(active)
isActive = active
guihooks.trigger('MyModStateChanged', {active = isActive})
end
-- DON'T do this (ghost value - UI out of sync):
-- isActive = true
-- (forgot to trigger UI update)Confirm Dialog Pattern
-- Show confirmation then execute
guihooks.trigger('ConfirmDialog', {
title = "Are you sure?",
message = "This will delete your save.",
confirmLabel = "Delete",
cancelLabel = "Cancel",
onConfirm = "myExtension.confirmDelete()",
onCancel = "myExtension.cancelDelete()"
})Progressive UI Updates
local isActive = false
local uiTimer = 0
local function onUpdate(dtReal, dtSim, dtRaw)
if not isActive then return end
uiTimer = uiTimer + dtReal
if uiTimer > 0.1 then -- 10Hz UI update
uiTimer = 0
guihooks.trigger('MyModUpdate', getState())
end
end
M.onUpdate = onUpdateReload UI
-- Force full UI reload (after mounting/unmounting files)
reloadUI()guihooks.trigger vs be:queueJS
| Feature | guihooks.trigger | be:queueJS |
|---|---|---|
| Serialization | Automatic (JSON) | Manual |
| Safety | High | Low (raw JS) |
| Use case | Standard events | Low-level JS calls |
| Recommendation | Prefer this | Avoid unless needed |
-- Prefer this:
guihooks.trigger('MyEvent', {key = "value"})
-- Avoid this (raw JavaScript):
be:queueJS("angular.element(document).scope().$broadcast('MyEvent', {key: 'value'})")Gotchas
- Events are async - UI receives them next frame, not immediately.
- Data is JSON-serialized - Functions, metatables, and circular refs will be lost.
- No confirmation of delivery -
triggeris fire-and-forget. Use a callback pattern (VE→GE or UI→GE) if you need a response. - UI must be loaded - Events sent before UI initialization are dropped silently.
- String vs table data - Some events expect a string, others a table. Check existing usage.
reloadUI()is expensive - Only call when necessary (e.g., after mounting filesystem changes).
Quick Reference
-- One-way events
guihooks.trigger('EventName', data)
guihooks.trigger('ChangeState', {state = 'play'})
guihooks.trigger('toastrMsg', {type="info", title="Hi", msg="Hello"})
-- Simple message popup
guihooks.message("Hello!", 5, "info")
-- Targeted event (specific UI window)
guihooks.triggerClient(targetDsmId, 'EventName', data)
-- Streaming (high frequency)
guihooks.queueStream('key', value)
guihooks.sendStreams()
guihooks.triggerStream('name', data)
-- Reload
reloadUI()See Also
- Cross-VM Comms - Full cross-VM communication patterns
- UI Bridge - Lua to UI communication details
- UI Apps - Creating HUD widgets
- Common Patterns - UI data flow pattern