# Developer > Developer API reference for ML Turret Category: TURRET · Source: https://miciomods.it/docs/ml-turret-developer · Last updated: 2026-07-28 ## Overview `ml_turret` does not expose FiveM `exports()`. All integration happens through hook functions in the `open/` folder (safe to edit, not escrowed) and through optional `Bridge.OnServerRequest` callbacks for custom UI. Internally the script uses `Bridge.*` from `ml_bridge` for every framework call, so no raw ESX/QBCore/Qbox reference appears in runtime code. ## Server Hooks Located in `open/server.lua`. Every hook receives enough context to run custom logic (XP, logging, integrations) without patching the main files. ### Hook_OnTurretPlaced **open/server.lua** ```lua ---@param src number player source ---@param turret table runtime turret (id, turretType, owner, coords, ...) function Hook_OnTurretPlaced(src, turret) -- exports.ml_skills:AddXp('defense', 50, src) end ``` Fires once when a placeable turret has been persisted to the DB and spawned in the world. ### Hook_OnTurretDestroyed **open/server.lua** ```lua ---@param turretId number ---@param attackerSrc number|nil src that dealt the killing blow, nil for environmental deaths function Hook_OnTurretDestroyed(turretId, attackerSrc) end ``` Fires on the same tick the turret reaches 0 HP. The rubble prop is spawned 1.5 seconds later; use this hook for instant side-effects (alarms, bounty payouts, dispatch calls). ### Hook_OnTurretRepaired **open/server.lua** ```lua ---@param src number ---@param turretId number ---@param healthBefore number ---@param healthAfter number function Hook_OnTurretRepaired(src, turretId, healthBefore, healthAfter) end ``` Fires after a repair item has been consumed and `t.health` updated. ### Hook_OnAmmoLoaded **open/server.lua** ```lua ---@param src number ---@param turretId number ---@param caliberKey string key from Config.Ammo ---@param qty number items consumed function Hook_OnAmmoLoaded(src, turretId, caliberKey, qty) end ``` ### Hook_BeforeFixedSpawn **open/server.lua** ```lua ---@param fixedDef table entry from Config.FixedTurrets ---@return boolean|nil return false to cancel the spawn function Hook_BeforeFixedSpawn(fixedDef) return true end ``` Called once per fixed turret at resource start, before `SpawnTurretEntity`. Return `false` to skip spawning this instance (e.g. based on a daily event or admin toggle). ## Client Hooks Located in `open/client.lua`. ### Hook_OnLocalTurretPlaced **open/client.lua** ```lua ---@param turret table local turret wrapper (id, data, config, props) function Hook_OnLocalTurretPlaced(turret) end ``` Fires on every client in scope when a new turret appears locally. ### Hook_OnLocalTurretDestroyed **open/client.lua** ```lua ---@param turret table function Hook_OnLocalTurretDestroyed(turret) end ``` ### Hook_OnEnterManualControl / Hook_OnExitManualControl **open/client.lua** ```lua ---@param turret table function Hook_OnEnterManualControl(turret) end function Hook_OnExitManualControl(turret) end ``` Fires on the client that takes the gunner seat, at start and end of manual control. ## Shared Hooks `open/shared.lua` runs on both client and server. Use it to register custom calibres without touching `shared/config.lua`: **open/shared.lua** ```lua Config.Ammo['12g_buckshot'] = { label = 'ammo_12g_buck_label', item = 'ammo_12g_buckshot', weaponHash = `WEAPON_PUMPSHOTGUN`, damage = 28.0, amountPerItem = 8, reloadTime = 3500, } ``` Then declare `acceptedCalibers = { '12g_buckshot' }` on a turret type. ## Server Callbacks Exposed via `Bridge.OnServerRequest` for client-side integrations. ### ml_turret:getMyIdentity ```lua local bundle = Bridge.RequestServer('ml_turret:getMyIdentity') -- returns { ownerKey = string, citizenid = string|nil, license = string } ``` Returns the caller's identity bundle. `ownerKey` is the primary id used in `ml_turrets.owner` (citizenid on QBCore/Qbox, license on ESX/standalone). Useful when custom UI needs to check ownership client-side. ### ml_turret:getNearbyInvitable ```lua local players = Bridge.RequestServer('ml_turret:getNearbyInvitable', turretId) -- returns { { id = serverId, name = displayName }, ... } ``` Returns nearby players who can be invited to this turret's group: within `Config.Ownership.nearbyPlayerRange`, not the owner, not already a member, not the caller. Owner-only, non-owners get an empty array. ## Admin Commands ### /turret_revive <fixed_config_id> Clears the `is_destroyed` flag on a fixed turret's DB row. Used with `stayDestroyed = true` to bring a permanently-dead instance back after admin intervention. ```lua /turret_revive paleto_police_roof ``` Gated by `Permissions.IsStaff` (ACE or fallback identifier). Console invocation always allowed. Restart the resource for the revived turret to respawn. ## Debug Commands Available only when `Config.Debug = true` (or the caller has the ACE defined in `Config.Server.admin.acePermission`). `/turret_compose `: Spawns the full composition (base + gunner + barrel) with XYZ axes 3m in front of the player `/turret_compose_clear`: Removes the active composition `/turret_muzzles `: Interactive muzzle-offset editor. TAB cycles muzzles, WASD/QE moves, ENTER exports `/turret_camera `: Manual-control camera offset editor `/turret_firetest `: Fires 20 test volleys from each muzzle to verify positions `/turret_inspect`: Live menu for the nearest turret (HP, ammo, calibre, fuel, target, ...) `/turret_aim_viz`: Renders the aim direction + AI target in 3D `/turret_ai_diag`: Full AI state diagnostic for the nearest turret ## Database Helpers Server-side globals, primarily for advanced integrations. ### Database_DeleteTurret ```lua Database_DeleteTurret(turretId) ``` Deletes a single turret row by `id`. Used internally by `permanentDestruction` and dismantle flows. ### Database_DeleteFixedByConfigId ```lua Database_DeleteFixedByConfigId(fixedConfigId) ``` Deletes the row for a fixed turret by its `fixed_config_id`. Called when `refillOnRestart = true` to reset state at boot. ### Database_ReviveFixedByConfigId ```lua Database_ReviveFixedByConfigId(fixedConfigId) ``` Clears `is_destroyed = 0` and sanitises health on the row. The underlying operation of `/turret_revive`. ### Database_MarkFixedDestroyed ```lua Database_MarkFixedDestroyed(t) ``` Persists `is_destroyed = 1` immediately for a fixed turret. Called on `permanentDestruction` death so the next boot's `BuildTurretFromFixed` sees the destroyed state and applies `stayDestroyed` logic. ## Events ### Server → Client - `ml_turret:client:turretPlaced`: broadcasts a new turret to every client in scope - `ml_turret:client:turretRemoved`: broadcasts a turret removal (dismantle, permanent destruction) - `ml_turret:client:turretUpdated`: partial update to `lt.data` (groupMembers changes, etc.) scoped to clients within 150m - `ml_turret:client:openPanel`: sends the tactical UI payload to one player - `ml_turret:client:updatePanel`: live panel refresh after an action - `ml_turret:client:receiveGroupInvite`: delivers an invite to the target player ### Client → Server - `ml_turret:server:placeTurret`: fires after the placement progress bar completes - `ml_turret:server:enterControl` / `ml_turret:server:exitControl` - `ml_turret:server:loadAmmo` / `ml_turret:server:unloadAmmo` - `ml_turret:server:refuel` / `ml_turret:server:repair` - `ml_turret:server:dismantleTurret` / `ml_turret:server:rebuildTurret` - `ml_turret:server:inviteGroupMember` / `ml_turret:server:acceptGroupInvite` - `ml_turret:server:removeGroupMember` - `ml_turret:server:toggleAI` - `ml_turret:server:closePanel` All client→server events are rate-limited and server-validated (zero trust). Forged calls raise security flags logged to the `exploits` webhook channel. ## Examples **Award defence XP when a player kills a raider near a turret** **open/server.lua** ```lua function Hook_OnTurretDestroyed(turretId, attackerSrc) if not attackerSrc then return end exports.ml_skills:AddXp('personal', 25, attackerSrc) end function Hook_OnTurretPlaced(src, turret) exports.ml_skills:AddXp('personal', 50, src) end ``` **Add a custom shotgun calibre** **open/shared.lua** ```lua Config.Ammo['12g_buckshot'] = { label = 'ammo_12g_buck_label', item = 'ammo_12g_buckshot', weaponHash = `WEAPON_PUMPSHOTGUN`, damage = 28.0, amountPerItem = 8, reloadTime = 3500, } ``` Then on the turret type: **shared/config.lua** ```lua acceptedCalibers = { '12g_buckshot', '762x39' }, defaultCaliber = '12g_buckshot', ``` **Block a fixed turret from spawning on weekends** **open/server.lua** ```lua function Hook_BeforeFixedSpawn(fixedDef) if fixedDef.id == 'city_centre_defense' then local day = os.date('*t').wday if day == 1 or day == 7 then return false end end return true end ```