Turfwar/server/cash.lua

305 lines
7.9 KiB
Lua

-- server/cash.lua
print("^2[turfwar]^7 cash.lua loaded")
Cash = {}
local cache = {}
local function PushMoney(src)
TriggerClientEvent('turfwar:money:update', src, Cash.Get(src), Cash.GetBank(src))
end
-- Safe notify helper (won't crash if you don't have turfwar:notify hooked)
local function Notify(src, msg)
TriggerClientEvent('chat:addMessage', src, { args = { "ATM", tostring(msg) } })
end
local function getIdentifier(src)
local ids = GetPlayerIdentifiers(src)
if not ids or #ids == 0 then return nil end
-- Prefer license:
for _, id in ipairs(ids) do
if id:sub(1, 8) == "license:" then
return id
end
end
-- Fallback (steam/discord/etc.)
return ids[1]
end
local function ensureLoaded(src)
local identifier = getIdentifier(src)
if not identifier then return nil end
if not cache[identifier] then
Cash.Load(src)
end
return identifier
end
function Cash.Load(src)
local identifier = getIdentifier(src)
if not identifier then return end
local rows = MySQL.query.await(
'SELECT cash, bank FROM player_money WHERE identifier = ?',
{ identifier }
)
local row = (rows and rows[1]) or nil
if not row then
MySQL.insert.await(
'INSERT INTO player_money (identifier, cash, bank) VALUES (?, 0, 0)',
{ identifier }
)
row = { cash = 0, bank = 0 }
end
cache[identifier] = {
cash = tonumber(row.cash) or 0,
bank = tonumber(row.bank) or 0
}
PushMoney(src)
end
function Cash.Get(src)
local identifier = ensureLoaded(src)
if not identifier then return 0 end
return cache[identifier].cash or 0
end
function Cash.GetBank(src)
local identifier = ensureLoaded(src)
if not identifier then return 0 end
return cache[identifier].bank or 0
end
function Cash.Add(src, amount)
amount = tonumber(amount) or 0
if amount == 0 then return end
local identifier = ensureLoaded(src)
if not identifier then return end
local data = cache[identifier]
data.cash = math.max(0, (data.cash or 0) + amount)
MySQL.update(
'UPDATE player_money SET cash = ? WHERE identifier = ?',
{ data.cash, identifier }
)
PushMoney(src)
end
function Cash.Remove(src, amount)
Cash.Add(src, -(tonumber(amount) or 0))
end
function Cash.Set(src, amount)
amount = math.max(0, tonumber(amount) or 0)
local identifier = ensureLoaded(src)
if not identifier then return end
cache[identifier].cash = amount
MySQL.update(
'UPDATE player_money SET cash = ? WHERE identifier = ?',
{ amount, identifier }
)
PushMoney(src)
end
function Cash.SetBank(src, amount)
amount = math.max(0, tonumber(amount) or 0)
local identifier = ensureLoaded(src)
if not identifier then return end
cache[identifier].bank = amount
MySQL.update(
'UPDATE player_money SET bank = ? WHERE identifier = ?',
{ amount, identifier }
)
PushMoney(src)
end
AddEventHandler('playerJoining', function()
Cash.Load(source)
end)
AddEventHandler('playerDropped', function()
local src = source
local identifier = getIdentifier(src)
if identifier and cache[identifier] then
local data = cache[identifier]
MySQL.update(
'UPDATE player_money SET cash = ?, bank = ? WHERE identifier = ?',
{ data.cash or 0, data.bank or 0, identifier }
)
cache[identifier] = nil
end
end)
RegisterNetEvent('turfwar:money:request', function()
local src = source
Cash.Load(src)
PushMoney(src)
end)
-- =========================================================
-- Ped cash drops -> claim once -> add to player's cash
-- =========================================================
local CashDrops = {}
local NextDropId = 1
RegisterNetEvent("environment:pedCashDrop", function(amount, coords)
amount = tonumber(amount) or 0
if amount < 1 or amount > 1000 then return end
if type(coords) ~= "table" or coords.x == nil or coords.y == nil or coords.z == nil then return end
local dropId = NextDropId
NextDropId = NextDropId + 1
CashDrops[dropId] = {
amount = amount,
taken = false,
createdAt = os.time()
}
TriggerClientEvent("environment:spawnCashPickup", -1, dropId, amount, coords)
SetTimeout(5 * 60 * 1000, function()
if CashDrops[dropId] and not CashDrops[dropId].taken then
CashDrops[dropId] = nil
TriggerClientEvent("environment:removeCashPickup", -1, dropId)
end
end)
end)
RegisterNetEvent("environment:pickupCash", function(dropId)
local src = source
dropId = tonumber(dropId)
if not dropId then return end
local drop = CashDrops and CashDrops[dropId]
if not drop or drop.taken then return end
drop.taken = true
local amount = tonumber(drop.amount) or 0
CashDrops[dropId] = nil
if amount > 0 then
Cash.Add(src, amount)
end
TriggerClientEvent("environment:removeCashPickup", -1, dropId)
TriggerClientEvent("environment:pickupFeedback", src, amount)
end)
-- =========================================================
-- ATM-Gated Bank Transactions (server authoritative)
-- Requires: server/atm_access.lua exporting RequireATM
-- =========================================================
local function RequireATM(src, token)
return exports[GetCurrentResourceName()]:RequireATM(src, token, true)
end
local function clampAmount(x)
x = tonumber(x)
if not x then return nil end
x = math.floor(x)
if x <= 0 then return nil end
return x
end
RegisterNetEvent("turfwar:bank:deposit", function(token, amount)
local src = source
local ok, err = RequireATM(src, token)
if not ok then
print(("^1[ATM BLOCK]^7 deposit src=%s reason=%s"):format(src, err))
return
end
amount = clampAmount(amount)
if not amount then return end
local cash = Cash.Get(src)
if cash < amount then
Notify(src, "Not enough cash.")
return
end
Cash.Set(src, cash - amount)
Cash.SetBank(src, Cash.GetBank(src) + amount)
end)
RegisterNetEvent("turfwar:bank:withdraw", function(token, amount)
local src = source
local ok, err = RequireATM(src, token)
if not ok then
print(("^1[ATM BLOCK]^7 withdraw src=%s reason=%s"):format(src, err))
return
end
amount = clampAmount(amount)
if not amount then return end
local bank = Cash.GetBank(src)
if bank < amount then
Notify(src, "Not enough money in bank.")
return
end
Cash.SetBank(src, bank - amount)
Cash.Set(src, Cash.Get(src) + amount)
end)
RegisterNetEvent("turfwar:atm:balance", function(token)
local src = source
local ok = RequireATM(src, token)
if not ok then return end
PushMoney(src)
end)
-- =========================================================
-- Admin
-- =========================================================
RegisterCommand("cash", function(src)
if src == 0 then
print("[turfwar] /cash can only be used in-game.")
return
end
TriggerClientEvent('chat:addMessage', src, {
args = { "Money", ("Cash: $%d | Bank: $%d"):format(Cash.Get(src), Cash.GetBank(src)) }
})
end, false)
RegisterCommand("addcash", function(src, args)
if src == 0 then
print("[turfwar] Use in-game: /addcash <amount>")
return
end
Cash.Add(src, tonumber(args[1]) or 0)
end, false)
RegisterCommand("setcash", function(src, args)
if src == 0 then
print("[turfwar] Use in-game: /setcash <amount>")
return
end
Cash.Set(src, tonumber(args[1]) or 0)
end, false)