-- server/atm_access.lua print("^2[turfwar]^7 atm_access.lua loaded (server-side gatekeeper)") Config = Config or {} Config.Finance = Config.Finance or {} local USE_RADIUS = tonumber(Config.Finance.ATM_USE_RADIUS) or 1.8 local TTL_MS = tonumber(Config.Finance.ATM_SESSION_TTL_MS) or 15000 local sessions = {} local function dist3(a, b) local dx = (a.x - b.x) local dy = (a.y - b.y) local dz = (a.z - b.z) return math.sqrt(dx*dx + dy*dy + dz*dz) end local function newToken() return ("%08x%08x"):format(math.random(0, 0xffffffff), math.random(0, 0xffffffff)) end local function getPlayerCoords(src) local ped = GetPlayerPed(src) if not ped or ped == 0 then return nil end local c = GetEntityCoords(ped) return { x = c.x, y = c.y, z = c.z } end RegisterNetEvent("turfwar:atm:open", function(payload) local src = source if type(payload) ~= "table" then TriggerClientEvent("turfwar:atm:openDenied", src, "bad_payload") return end local x, y, z = tonumber(payload.x), tonumber(payload.y), tonumber(payload.z) local model = payload.model if not x or not y or not z then TriggerClientEvent("turfwar:atm:openDenied", src, "bad_coords") return end local okModel = false for _, m in ipairs(Config.Finance.ATM_MODELS or {}) do if m == model then okModel = true break end end if not okModel then TriggerClientEvent("turfwar:atm:openDenied", src, "bad_model") return end local pc = getPlayerCoords(src) if not pc then TriggerClientEvent("turfwar:atm:openDenied", src, "no_ped") return end local atmC = { x = x, y = y, z = z } if dist3(pc, atmC) > (USE_RADIUS + 0.75) then TriggerClientEvent("turfwar:atm:openDenied", src, "too_far") return end local token = newToken() sessions[src] = { token = token, expires = GetGameTimer() + TTL_MS, atm = { x = x, y = y, z = z, model = model } } TriggerClientEvent("turfwar:atm:openGranted", src, token) end) local function RequireATM(src, token, verbose) local s = sessions[src] if not s then return false, "no_session" end if token ~= s.token then return false, "bad_token" end if GetGameTimer() > (s.expires or 0) then sessions[src] = nil return false, "expired" end local pc = getPlayerCoords(src) if not pc then return false, "no_ped" end local atmC = { x = s.atm.x, y = s.atm.y, z = s.atm.z } if dist3(pc, atmC) > (USE_RADIUS + 0.75) then if verbose then print(("^1[ATM BLOCK]^7 src=%d moved away from ATM"):format(src)) end sessions[src] = nil return false, "moved_away" end return true end exports("RequireATM", RequireATM) AddEventHandler("playerDropped", function() sessions[source] = nil end)