GuidesArchitecture
GE Architecture Quick Reference
10-second refresher on Game Engine extension lifecycle, hooks, and core rules. Pin this page while coding.
A 10-second refresher on GE (Game Engine) core rules. Read this before any coding.
1. Extension Lifecycle (When Code Runs)
| Hook | Args | Timing | Use For |
|---|---|---|---|
onExtensionLoaded | (deserializedData) | GE boot / VM reload | Setup vars, one-time init. Called only on the extension itself with deserialized state. Return false to abort loading. |
onInit | (deserializedData) | After onExtensionLoaded (GE only) | Second-phase init, after all batch-loaded extensions are ready |
onClientStartMission | (levelPath) | Level loading begins | Early level setup (before objects ready) |
onClientPostStartMission | (levelPath) | Level Lua loaded, objects available | Mission logic, trigger setup, entity queries |
onWorldReadyState | (state) | World objects ready (1=loading, 2=fully loaded) | Post-spawn setup, deferred entity queries. state=2 is the safe point. |
onUiReady | none | UI (CEF) fully loaded | Trigger UI-dependent logic |
onFirstUpdate | none | Very first frame after init | One-shot startup logic |
onUpdate | (dtReal, dtSim, dtRaw) | Every frame (before physics) | Game logic, timers, state machines |
onPreRender | (dtReal, dtSim, dtRaw) | Every frame (after physics, before render) | Visual updates, debug drawing |
onGuiUpdate | (dtReal, dtSim, dtRaw) | Every frame (during onPreRender) | UI-specific per-frame updates |
onVehicleSpawned | (vid, vehObj) | Vehicle spawned | Track vehicles, attach vehicle hooks |
onVehicleDestroyed | (vid) | Vehicle removed | Cleanup vehicle-specific data |
onVehicleSwitched | (oldId, newId, player) | Player switches vehicle | Camera, UI, context updates |
onVehicleResetted | (vehicleID) | Vehicle reset (R key) | Re-sync state after reset |
onResetGameplay | (playerID) | Gameplay reset | Clean state, stop timers/modes |
onClientEndMission | (levelPath) | Leaving level | Level-specific cleanup |
onPreExit | none | Before game shutdown | Save data, cleanup |
onExit | none | Game shutting down | Final cleanup |
onPreWindowClose | none | Window close requested | Prompt save, confirm exit |
onScreenFadeState | (state) | Screen fade transitions | 1=fading to black, 2=fully black, 3=fading from black. Use state 2 to do work while screen is hidden (swap vehicles, teleport, etc.) |
2. Hook System (How to Extend)
Core Principle: Hook > Override
Always prefer hooks over function overrides. Hooks chain; overrides replace.
-- GOOD: local function + export pattern (chains with other mods)
local function onExtensionLoaded()
-- Your init logic
end
M.onExtensionLoaded = onExtensionLoaded
-- Note: onExtensionLoaded is only called on the extension itself (with deserialized data).
-- There is no broadcast to other extensions when an extension loads.
-- AVOID: Direct overrides unless absolutely necessary
-- someModule.func = function() ... end -- Breaks other mods!Safe Override Pattern (When You Must)
Always call original FIRST to let game logic finish, then apply your changes.
local orgFunc = target.func
target.func = function(...)
local res = orgFunc(...) -- Game does its work FIRST
-- Your mod logic here (won't be overwritten)
return res
end3. No Ghost Values (Keep State & UI in Sync)
Rule: If you change internal state, you MUST update the UI/reporters.
A "Ghost Value" = state changed but UI shows old data. Confusing UX!
Example: Freeroam Mission State
-- After modifying mission state, update UI
freeroam_missions.state = newState
freeroam_missions.saveMissionProgress() -- Persist!
guihooks.trigger("MissionStateChanged", newState) -- Sync UI!Example: Safe State Getter Override
local orgGetState = module.getState
module.getState = function()
local state = orgGetState() -- Get base state FIRST
-- Modify returned data for UI visibility
state.modified = true
return state
end4. Architectural > Hacks
| Do This | Avoid This |
|---|---|
Use onClientPostStartMission for level setup | Polling map.getMap().nodes in onUpdate |
Use onVehicleSpawned to track vehicles | Polling map.vehicles every frame |
Use guihooks.trigger() for UI updates | Directly modifying UI DOM from GE |
Namespace your settings: myMod_setting | Generic names that collide: setting |
Use extensions.hook() for cross-module comms | Direct function calls across extensions |
5. Quick Discovery
be:getPlayerVehicle(0) -- Get player vehicle object
map.getMap() -- Current level data
scenetree.findObject("name") -- Find entity by name
extensions.hook("onResetGameplay") -- Trigger all reset hooks
jsonEncode({data = true}) -- Serialize to JSON
jsonDecode('{"key": "value"}') -- Parse JSONSummary Checklist
- Hook into lifecycle at the right timing
- Prefer hooks over overrides
- Call original first when overriding
- Update UI/reporters when changing state
- Namespace your settings to avoid collisions
See Also
- Architecture - Full extension system reference
- Boot Sequence - Startup order details
- Hook Catalog - All available hooks
- Common Patterns - Proven patterns