Events, state bags & commands
Events
Public
| Event | Side | Arguments | Notes |
|---|---|---|---|
np_helicam:missionReport | server local | report | Fired once when a mission ends. See below |
np_helicam:evidence | server local | { mission, src, t, data } | One evidence screenshot. See below |
np_helicam:notify | client net | payload | Shows a toast through np_helicam's notify provider. See below |
np_helicam:contextMap | client local | taken | true when np_helicam takes the minimap (camera inset or Air Support map), false right after it restored it. See below |
np_helicam:fullscreen | client local | on | true while the camera or a live feed fills the screen. See below |
The server events are local server events (TriggerEvent), so only server scripts can listen with AddEventHandler. They are not net events. The same goes for np_helicam:contextMap and np_helicam:fullscreen on the client.
np_helicam:missionReport
AddEventHandler('np_helicam:missionReport', function(report)
print(('Mission %s by %s, %d events'):format(report.id, report.meta.operator or '?', #report.events))
exports.my_mdt:AttachAirReport(report.id, report.text)
end)| Field | Type | Description |
|---|---|---|
id | string | Mission id, e.g. 261004-2214-3 (UTC date-time + counter) |
meta | table | callsign, model, operator (player name), operatorCallsign, startedAt, endedAt (Unix time), endReason, durationMs, screenshots |
events | table[] | { kind, text, t, data? }, t = ms since start. data may hold x, y, z, plate, netId, label |
track | table[] | { t, x, y, z, spd }, sampled on the server every Config.Mission.trackIntervalMs |
dropped | number | Events trimmed because of Config.Mission.maxEvents |
durationMs | number | Length of the mission |
distanceM | number | Ground distance of the track |
counts | table | Events per kind, e.g. { lock = 3, bolo = 1 } |
text | string | The plain-text report (also used for the webhook and "Copy report") |
endReason is 'stop' (R), 'idle' (camera closed longer than Config.Mission.idleEndMs), 'restart' (a new mission started), 'dropped' (operator disconnected) or 'shutdown' (resource stopped). Event kinds are listed in Operations → Mission recording.
Trivial missions don't fire the event: a mission shorter than 10 s with at most one real event (the start, note and end lines don't count) is discarded without a report, event or webhook post.
np_helicam:evidence
AddEventHandler('np_helicam:evidence', function(ev)
-- ev.mission: mission id, ev.src: operator server id, ev.t: ms since mission start
-- ev.data: 'data:image/jpeg;base64,...' (typically 100-400 KB)
exports.my_evidence:Store(ev.mission, ev.src, ev.data)
end)Fired once per screenshot when B is pressed with Config.Mission.screenshots on and screenshot-basic running. np_helicam does not keep or upload the image. If nobody listens, it is gone.
np_helicam:notify
TriggerClientEvent('np_helicam:notify', src, {
tint = 'red', -- neutral red orange rust pink olive lilac green blue aqua (amber → orange, grey → neutral)
icon = 'helicopter', -- Font Awesome solid icon name, without 'fa-'
app = 'AIR SUPPORT', -- small caption, max 32 chars
title = 'BOLO', -- max 96 chars
message = 'Plate **46EEK572**', -- max 512 chars, **bold** only
ttl = 6000, -- ms, clamped to 1000-30000
})The payload is sanitised on the client (control characters, GTA ~ codes and ^n colour codes are removed) and shown through Config.Notify.provider. At least a title or a message is required.
np_helicam:contextMap
While the camera is up, the context map moves the minimap to the top right. While the Air Support panel is open, the minimap sits under the panel's map window. Both share one owner of the radar, so the event fires once when the first of them takes the minimap (true) and once after the last one let go and the layout from Config.ContextMap.restore (GTA's default unless you change it) was put back (false). Switching between the camera inset and the panel doesn't fire it. A HUD that places the minimap itself can listen for this event and re-apply its own layout:
AddEventHandler('np_helicam:contextMap', function(taken)
if not taken then
MyHud.applyMinimapLayout() -- np_helicam just restored Config.ContextMap.restore
end
end)np_hud handles this already: it leaves the minimap alone while np_helicam has it and re-applies its own layout on false. See Configuration → Context map.
To check the state from a resource that starts later, use the client export IsMinimapHeld.
np_helicam:fullscreen
true when the operator camera or a ground unit's live feed opens, false when the last of them closes. Both draw their own full-screen overlay, so a HUD in its own NUI frame should hide for it. np_hud does this already.
AddEventHandler('np_helicam:fullscreen', function(on)
MyHud.setVisible(not on)
end)To check the state from a resource that starts later, use the client export IsFullscreen.
Internal
These are used between np_helicam's own client and server. Don't trigger or rely on them. Every client → server event is rate-limited and validated on the server.
| Event | Direction | Purpose |
|---|---|---|
np_helicam:rpc:req / np_helicam:rpc:res | client ↔ server | Request / response transport for client actions (markers, BOLO check, mission, feed, share, orbit request, units, air:sub, auth) |
np_helicam:cam:state | client → server | Operator's gimbal state, ≤ 5 Hz. The server writes helicam_cam |
np_helicam:cam:status | client → server | Operator's 1 Hz status summary. Kept in server memory |
np_helicam:light:state | client → server | Searchlight state, ≤ 10 Hz. The server writes helicam_light |
np_helicam:airUnits | server → client | (list, ended): every live air unit, about 1 Hz, to ground units with the Air Support panel open |
np_helicam:airStatus | server → client | (netId, status | nil): status of the heli a ground unit is watching |
np_helicam:auth | server → client | The player's role flags after a job or duty change |
np_helicam:markers | server → client | Full marker list after every change |
np_helicam:units | server → client | (list, ended): friendly unit positions (with veh for units in a vehicle, used for the radar blip), to subscribers only. ended = true when the server dropped the subscription (role lost) |
np_helicam:crewShare | server → client | A lock shared by a crew mate |
The client also listens to framework events to refresh its role: QBCore:Client:OnPlayerLoaded, QBCore:Client:OnJobUpdate, QBCore:Client:SetDuty, qbx_core:client:playerLoggedIn, esx:playerLoaded, esx:setJob.
State bags
Clients write no state bags. The operator sends rate-limited events to the server. The server checks that the sender has the air role and sits in a camera seat of a whitelisted helicopter, cleans the payload, and writes the bag itself. This also makes the copilot's camera and light work, because only the entity's network owner (usually the pilot) could write its bags.
All bags are entity bags on the helicopter, replicated to every client in scope.
| Bag | Written by | Rate | Content |
|---|---|---|---|
helicam_cam | server | ≤ 5 Hz, threshold-gated | Camera gimbal only: { on, op, rx, rz, fov, vision, palette } |
helicam_light | server | ≤ 10 Hz, threshold-gated | Searchlight: { on, dx, dy, dz, lux, beam?, strobe? }. See Searchlight sync |
helicam_viewers | server | on change | Number of ground units watching the feed |
There is no helicam_status bag any more. See Air unit status.
helicam_cam
| Field | Description |
|---|---|
on | true while the camera is up and the operator shares the feed |
op | Operator's server id, set by the server |
rx, rz | Camera pitch and heading in degrees (rotation order 2) |
fov | Field of view in degrees |
vision | 0 normal, 1 night vision, 2 FLIR |
palette | Thermal palette id |
The bag deliberately carries no target, position lock or aim point, because it replicates to everyone in scope.
One owner per heli. The first crew member whose camera sends a state owns the bag. A second operator in the same heli doesn't overwrite it. When the owner closes the camera, turns Share feed off or leaves, the bag goes off, and the next crew member to send takes it over.
Air unit status
The operator's status (mode, vision, zoom, light, fuel, street and zone, the target with model, plate, colour, speed, heading and position, and the aim point) is not replicated. The server keeps the latest one per heli in memory and pushes it only to:
- ground units with the Air Support panel open (
np_helicam:airUnits, about 1 Hz, with every live air unit and its server-side position), and - the heli's feed viewers (
np_helicam:airStatus).
The callsign and operator id in the status are set by the server, not taken from the client.
Cleanup
When the camera closes or the operator turns Share feed off, the server sets helicam_cam to { on = false } and drops the status. Every second the server releases feeds whose owner left the seat, lost the air role, disconnected, or whose heli is gone, and ends their viewers' feeds. A 10 s sweep also switches off any lit helicam_light or live helicam_cam bag whose owner is no longer valid. On resource stop, the server turns off every bag it wrote.
Commands
| Command | Who | Description |
|---|---|---|
/bolo add <plate> <reason...> | Config.Bolo.commandJobs role (default bolo), or the server console | Add a BOLO. The callsign is stored as by |
/bolo remove <plate> | same | Remove a BOLO |
/bolo list | same | The 10 newest BOLOs (and how many more) as a toast, or printed in the console |
The command is not ACE-restricted. The role check is the gate, so it works with job-based permissions. One /bolo per 2 s per player. The registry holds at most 500 BOLOs; the oldest is dropped.
Key mapping commands
These are registered as commands so they can be bound in FiveM's key settings. They are listed with their default keys under Controls:
helicam, helicam_vision, helicam_palette, helicam_overlay, helicam_lock, helicam_heat, helicam_mark, helicam_record, helicam_bookmark, helicam_share, helicam_light, helicam_luxup, helicam_luxdown, helicam_beam, helicam_strobe, helicam_hover, helicam_settings, helicam_air.