Anti combat log & restrictions
Players should not escape a fight, a death or a sentence by disconnecting or relogging. Everything on this page is decided on the server.
How it works
-
Combat is tracked
When a player damages another player (any weapon or melee), both are marked as in combat forConfig.AntiCombatLog.CombatWindowseconds. Other scripts can mark players too — cuffing, robbing, a police chase — withMarkCombat. -
Leaving is recorded
On disconnect (drop) and on a character switch (relog), the server stores a drop record for the character: where they were, whether they were dead, whether they were in combat, and the disconnect reason. -
Combat loggers are exposed
When the player was in combat:- Players within
Display.Radiussee a red marker where they left, with their name, server id and whether they were dead, forDisplay.Durationseconds. - A Discord log is sent to
Config.AntiCombatLog.Webhook. ServerEditable.OnCombatLogand the eventkoja-spawnselector:server:combatLogrun, so you can jail, fine or ban.
- Players within
-
The next spawn is limited
A forced spawn is the only one that can be picked; the rest of the list stays visible but locked with the reason, the selector opens on it and ESC is disabled. Cancelling from the client is refused by the server and the selector opens again.Situation Default result Combat logout or relog "Last location" is locked ("Combat logout") for LockMinutesDied, then left "Last location" is locked ("You died here") and the hospital is forced Dead on login (QB isdead/inlaststand, ESXusers.is_dead)The hospital is forced In jail (QB injail, ESXusers.jail_timeorusers.jail)The prison is forced Your own rules See below -
The record is cleared
After the player lands, the drop record is removed. Locks you set withLockLastLocationstay until their own time runs out.
Config
Config.AntiCombatLog = {
Enabled = true,
CombatWindow = 60,
LastLocation = 'lock',
LockMinutes = 15,
Display = { Enabled = true, Duration = 120, Radius = 80.0 },
Webhook = '',
}
Config.Death = {
Enabled = true,
LastLocation = 'lock',
ForceHospital = true,
Hospital = { id = 'hospital', label = 'Pillbox Medical Center', category = 'job', icon = 'hospital', coords = vec4(298.62, -584.58, 43.26, 70.0) },
}
Config.Jail = {
Enabled = true,
Spawn = { id = 'prison', label = 'Bolingbroke Penitentiary', category = 'job', icon = 'lock', coords = vec4(1846.4, 2585.9, 45.67, 270.0) },
}| Option | What it does |
|---|---|
CombatWindow | Seconds a player stays "in combat" after the last damage |
LastLocation | 'lock' shows it locked, 'hide' removes it, 'none' leaves it alone |
LockMinutes | How long after the logout the combat lock applies |
Display | The red marker for nearby players |
Webhook | Discord webhook URL, empty = off |
Death.ForceHospital | Force Death.Hospital after dying / when dead on login |
Jail.Spawn | The spawn forced while jailed |
Death and jail detection live in server/editable.lua (IsDead, IsDeadOnLogin, IsJailed). Change them if your ambulance or jail script stores this somewhere else.
Server API
local spawn = exports['koja-spawnselector']
Combat
spawn:MarkCombat(source, seconds) -- seconds defaults to CombatWindow
spawn:IsInCombat(source)
AddEventHandler('police:server:cuffed', function(officer, target)
exports['koja-spawnselector']:MarkCombat(officer, 120)
exports['koja-spawnselector']:MarkCombat(target, 300)
end)Drop record
local record = spawn:GetDropRecord(sourceOrIdentifier)
-- { kind = 'drop' | 'relog', reason = 'Exiting', at = 1793532461, dead = false, combat = true, coords = { x, y, z, w } } or nil
spawn:ClearDropRecord(sourceOrIdentifier)
Lock the last location
spawn:LockLastLocation(sourceOrIdentifier, 'Inside the vault', 1800) -- seconds, nil = until unlocked
spawn:UnlockLastLocation(sourceOrIdentifier)
Use it from a heist, an event island or an interior so nobody logs back in there.
Last location rules
A rule is asked every time the selector opens, after the built-in combat and death rules:
spawn:AddLastLocationRule('heist', function(source, ctx)
if ctx.combat and ctx.sinceDrop and ctx.sinceDrop < 3600 then
return { action = 'hide' }
end
if ctx.coords and #(vector3(ctx.coords.x, ctx.coords.y, ctx.coords.z) - vector3(255.0, 225.0, 101.0)) < 30.0 then
return { action = 'lock', reason = 'Bank vault' }
end
end)
spawn:RemoveLastLocationRule('heist')
ctx field | Description |
|---|---|
identifier | Character identifier |
coords, updated | Saved last location and when it was saved |
drop | The drop record table (or empty) |
dead, combat | From the drop record |
kind, reason, droppedAt, sinceDrop | Disconnect type, reason, unix time, seconds since |
now | Server time |
Return { action = 'lock' | 'hide', reason = '...' } or nil.
Restrictions
A restriction limits the whole list. Persisted ones survive restarts and are stored per character:
spawn:SetRestriction(sourceOrIdentifier, 'community_service', {
reason = 'Community service',
spawn = { id = 'cs', label = 'Community service', category = 'job', coords = vec4(170.6, -991.3, 30.1, 160.0) },
duration = 3600,
})
spawn:ClearRestriction(sourceOrIdentifier, 'community_service')
| Field | Description |
|---|---|
reason | Shown on the banner and on every locked spot |
spawn | A spawn definition (forced and added to the list) or the id of an existing spot |
allow | Extra spawn ids that stay pickable |
duration / until | Seconds from now / unix time. Omit for "until cleared" |
Live restrictions are computed on every open:
spawn:AddRestrictionHandler('wanted', function(source, ctx)
if Player(source).state.wanted then
return { reason = 'You are wanted', allow = { 'sandy', 'paleto' } }
end
end)
spawn:RemoveRestrictionHandler('wanted')
Only the first active restriction applies (jail, then hospital, then saved ones, then handlers).
Events
| Event | Side | Arguments |
|---|---|---|
koja-spawnselector:server:combatLog | Server | identifier, record (name, character, kind, reason, dead, coords, source) |
koja-spawnselector:client:combatLogged | Client | payload of the marker shown to this player |
AddEventHandler('koja-spawnselector:server:combatLog', function(identifier, record)
exports['koja-spawnselector']:SetRestriction(identifier, 'combatlog', {
reason = 'Combat logout — 10 minutes in prison',
spawn = 'prison',
duration = 600,
})
end)