mirror of
https://github.com/ND-Framework/ND_Core.git
synced 2026-08-17 13:56:02 +01:00
718 lines
23 KiB
Lua
718 lines
23 KiB
Lua
local avoidSavingLastLocations = {}
|
|
local moneyLogs = {}
|
|
|
|
local function removeCharacterFunctions(character)
|
|
local newData = {}
|
|
for k, v in pairs(character) do
|
|
if type(v) ~= "function" then
|
|
newData[k] = v
|
|
end
|
|
end
|
|
return newData
|
|
end
|
|
|
|
local function logMoney(charId, action, account, amount, reason, trace)
|
|
if not trace or (action == "set" and not reason) then return end
|
|
local traceText = trace:match("%((.-)%)")
|
|
traceText = traceText and traceText:gsub("%^%d", "") or traceText or ""
|
|
table.insert(moneyLogs, { charId, action, account, amount, reason or "[NO REASON GIVEN]", traceText })
|
|
end
|
|
|
|
local function createCharacterTable(info)
|
|
local playerInfo = PlayersInfo[info.source] or {}
|
|
|
|
local self = {
|
|
id = info.id,
|
|
source = info.source,
|
|
identifier = info.identifier,
|
|
user_id = info.user_id,
|
|
identifiers = playerInfo.identifiers or {},
|
|
discord = playerInfo.discord or {},
|
|
name = info.name,
|
|
firstname = info.firstname,
|
|
lastname = info.lastname,
|
|
fullname = ("%s %s"):format(info.firstname, info.lastname),
|
|
dob = info.dob,
|
|
gender = info.gender,
|
|
cash = info.cash,
|
|
bank = info.bank,
|
|
phonenumber = info.phonenumber,
|
|
groups = info.groups,
|
|
metadata = info.metadata,
|
|
inventory = info.inventory
|
|
}
|
|
|
|
---@param account string
|
|
---@param amount number
|
|
---@param reason string|nil
|
|
---@return boolean
|
|
function self.deductMoney(account, amount, reason)
|
|
local amount = tonumber(amount)
|
|
if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end
|
|
self[account] -= amount
|
|
if NDCore.players[self.source] then
|
|
self.triggerEvent("ND:updateMoney", self.cash, self.bank)
|
|
TriggerEvent("ND:moneyChange", self.source, account, amount, "remove", reason)
|
|
end
|
|
|
|
logMoney(self.id, "deduct", account, amount, reason, Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()))
|
|
|
|
return true
|
|
end
|
|
|
|
---@param account string
|
|
---@param amount number
|
|
---@param reason string|nil
|
|
---@return boolean
|
|
function self.addMoney(account, amount, reason)
|
|
local amount = tonumber(amount)
|
|
if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end
|
|
self[account] += amount
|
|
if NDCore.players[self.source] then
|
|
self.triggerEvent("ND:updateMoney", self.cash, self.bank)
|
|
TriggerEvent("ND:moneyChange", self.source, account, amount, "add", reason)
|
|
end
|
|
|
|
logMoney(self.id, "add", account, amount, reason, Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()))
|
|
|
|
return true
|
|
end
|
|
|
|
---@param amount number
|
|
---@return boolean
|
|
function self.depositMoney(amount)
|
|
local amount = tonumber(amount)
|
|
if not amount or self.cash < amount or amount <= 0 then return end
|
|
return self.deductMoney("cash", amount, locale("deposit")) and self.addMoney("bank", amount, locale("deposit"))
|
|
end
|
|
|
|
---@param amount number
|
|
---@return boolean
|
|
function self.withdrawMoney(amount)
|
|
local amount = tonumber(amount)
|
|
if not amount or self.bank < amount or amount <= 0 then return end
|
|
return self.deductMoney("bank", amount, locale("withdraw")) and self.addMoney("cash", amount, locale("withdraw"))
|
|
end
|
|
|
|
---@param data string
|
|
---@return any
|
|
function self.getData(data)
|
|
return self[data]
|
|
end
|
|
|
|
---@param metadata string|table
|
|
---@return any
|
|
function self.getMetadata(metadata)
|
|
if type(metadata) ~= "table" then
|
|
return self.metadata[metadata]
|
|
end
|
|
local returnData = {}
|
|
for i=1, #metadata do
|
|
local data = metadata[i]
|
|
returnData[data] = self.metadata[data]
|
|
end
|
|
return returnData
|
|
end
|
|
|
|
---@param key string|table
|
|
---@param value any
|
|
function self.setData(key, value, reason)
|
|
if type(key) == "table" then
|
|
for k, v in pairs(key) do
|
|
self[k] = v
|
|
if k == "cash" or k == "bank" then
|
|
logMoney(self.id, "set", k, value, reason, Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()))
|
|
TriggerEvent("ND:moneyChange", self.source, k, v, "set", reason)
|
|
end
|
|
end
|
|
else
|
|
self[key] = value
|
|
if key == "cash" or key == "bank" then
|
|
logMoney(self.id, "set", key, value, reason, Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()))
|
|
TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason)
|
|
end
|
|
end
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), key)
|
|
TriggerEvent("ND:updateCharacter", self, key)
|
|
end
|
|
|
|
---@param key string|table
|
|
---@param value any
|
|
---@return table
|
|
function self.setMetadata(key, value)
|
|
if type(key) == "table" then
|
|
for k, v in pairs(key) do
|
|
self.metadata[k] = v
|
|
end
|
|
else
|
|
self.metadata[key] = value
|
|
end
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "metadata")
|
|
TriggerEvent("ND:updateCharacter", self, "metadata")
|
|
return self.metadata
|
|
end
|
|
|
|
-- Mark character deleted
|
|
function self.delete()
|
|
local result = MySQL.update.await("UPDATE nd_characters SET deleted_at = NOW() WHERE charid = ?", {self.id})
|
|
if result and NDCore.players[self.source] then
|
|
NDCore.players[self.source] = nil
|
|
end
|
|
return result
|
|
end
|
|
|
|
--save last location
|
|
function self.saveLastLocation()
|
|
local ped = GetPlayerPed(self.source)
|
|
if not ped or ped == 0 then return end
|
|
|
|
local coords = GetEntityCoords(ped)
|
|
local heading = GetEntityHeading(ped)
|
|
local saveLocation = true
|
|
|
|
for i=1, #avoidSavingLastLocations do
|
|
local loc = avoidSavingLastLocations[i]
|
|
if #(loc.coords-coords) < loc.dist then
|
|
saveLocation = false
|
|
end
|
|
end
|
|
|
|
if not saveLocation then return end
|
|
|
|
self.setMetadata("location", {
|
|
x = coords.x,
|
|
y = coords.y,
|
|
z = coords.z,
|
|
w = heading
|
|
})
|
|
|
|
return true
|
|
end
|
|
|
|
-- Unload and save character
|
|
function self.unload()
|
|
if not NDCore.players[self.source] then return end
|
|
|
|
for name, _ in pairs(self.groups) do
|
|
lib.removePrincipal(self.source, ("group.%s"):format(name))
|
|
end
|
|
|
|
self.saveLastLocation()
|
|
self.triggerEvent("ND:characterUnloaded")
|
|
TriggerEvent("ND:characterUnloaded", self.source, self)
|
|
|
|
local saved = self.save()
|
|
NDCore.players[self.source] = nil
|
|
|
|
return saved
|
|
end
|
|
|
|
-- Save character information to database
|
|
function self.save(key)
|
|
if key then
|
|
local charData = self[key]
|
|
if not charData or type(charData) == "function" then
|
|
return lib.print.error(key, "Is not valid character data.")
|
|
end
|
|
|
|
if type(charData) == "table" then
|
|
charData = json.encode(charData)
|
|
end
|
|
|
|
local query = ("UPDATE nd_characters SET `%s` = ? WHERE charid = ?"):format(key)
|
|
local affectedRows = MySQL.update.await(query, {charData, self.id})
|
|
return affectedRows > 0
|
|
end
|
|
|
|
local affectedRows = MySQL.update.await("UPDATE nd_characters SET user_id = ?, name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, phonenumber = ?, `groups` = ?, metadata = ? WHERE charid = ?", {
|
|
self.user_id,
|
|
self.name,
|
|
self.firstname,
|
|
self.lastname,
|
|
self.dob,
|
|
self.gender,
|
|
self.cash,
|
|
self.bank,
|
|
self.phonenumber,
|
|
json.encode(self.groups),
|
|
json.encode(self.metadata),
|
|
self.id
|
|
})
|
|
return affectedRows > 0
|
|
end
|
|
|
|
---Create a license/permit for the character
|
|
---@param licenseType string
|
|
---@param expire number
|
|
function self.createLicense(licenseType, expire)
|
|
local expireIn = tonumber(expire) or 2592000
|
|
local time = os.time()
|
|
local licenses = self.metadata.licenses
|
|
local identifier = {}
|
|
|
|
for i=1, 16 do
|
|
identifier[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9)
|
|
end
|
|
|
|
local license = {
|
|
type = licenseType,
|
|
status = "valid",
|
|
issued = time,
|
|
expires = time+expireIn,
|
|
identifier = table.concat(identifier)
|
|
}
|
|
|
|
if licenses then
|
|
local found = false
|
|
|
|
for i=1, #licenses do
|
|
if licenses[i].type == licenseType then
|
|
self.metadata.licenses[i] = license
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if not found then
|
|
self.metadata.licenses[#licenses+1] = license
|
|
end
|
|
else
|
|
self.metadata.licenses = {license}
|
|
end
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "metadata")
|
|
TriggerEvent("ND:updateCharacter", self, "metadata")
|
|
end
|
|
|
|
function self.getLicense(identifier)
|
|
local licenses = self.metadata.licenses or {}
|
|
for i=1, #licenses do
|
|
local data = licenses[i]
|
|
if data.identifier == identifier then
|
|
return data, i
|
|
end
|
|
end
|
|
end
|
|
|
|
function self.updateLicense(identifier, newData)
|
|
local data, i = self.getLicense(identifier)
|
|
if not data then return end
|
|
for k, v in pairs(newData) do
|
|
data[k] = v
|
|
end
|
|
self.save()
|
|
end
|
|
|
|
---@param coords vector3|vector4
|
|
---@return boolean
|
|
function self.setCoords(coords)
|
|
if not self.source or not coords then return end
|
|
local ped = GetPlayerPed(self.source)
|
|
if not DoesEntityExist(ped) then return end
|
|
SetEntityCoords(ped, coords.x, coords.y, coords.z)
|
|
if coords.w then
|
|
SetEntityHeading(ped, coords.w)
|
|
end
|
|
return true
|
|
end
|
|
|
|
---@param eventName string
|
|
---@param ... any
|
|
---@return boolean
|
|
function self.triggerEvent(eventName, ...)
|
|
if not self.source then return end
|
|
TriggerClientEvent(eventName, self.source, ...)
|
|
return true
|
|
end
|
|
|
|
function self.notify(...)
|
|
if not self.source then return end
|
|
TriggerClientEvent("ox_lib:notify", self.source, ...)
|
|
return true
|
|
end
|
|
|
|
function self.revive()
|
|
self.triggerEvent("ND:revivePlayer")
|
|
self.setMetadata({
|
|
dead = false,
|
|
deathInfo = false,
|
|
})
|
|
end
|
|
|
|
---@param reason string
|
|
function self.drop(reason)
|
|
if not self.source then return end
|
|
DropPlayer(self.source, reason)
|
|
end
|
|
|
|
-- Set the character as the players active character/currently playing character
|
|
function self.active()
|
|
local char = NDCore.players[self.source]
|
|
if char and char.id == self.id then return end
|
|
if char then char.unload() end
|
|
for identifierType, identifier in pairs(self.identifiers) do
|
|
if lib.table.contains(Config.admins, ("%s:%s"):format(identifierType, identifier)) then
|
|
self.addGroup("admin")
|
|
end
|
|
end
|
|
|
|
local roles = self.discord.roles
|
|
if roles then
|
|
local hasAdmin = false
|
|
for i=1, #Config.adminDiscordRoles do
|
|
local role = Config.adminDiscordRoles[i]
|
|
if lib.table.contains(roles, role) then
|
|
hasAdmin = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if hasAdmin then
|
|
self.addGroup("admin")
|
|
elseif self.groups["admin"] then
|
|
self.removeGroup("admin")
|
|
end
|
|
|
|
local function hasRole(groupRoles)
|
|
for i=1, #groupRoles do
|
|
local role = groupRoles[i]
|
|
if lib.table.contains(roles, role) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
for group, groupRoles in pairs(Config.groupRoles) do
|
|
if hasRole(groupRoles) then
|
|
self.addGroup(group)
|
|
elseif self.groups[group] then
|
|
self.removeGroup(group)
|
|
end
|
|
end
|
|
end
|
|
|
|
for name, _ in pairs(self.groups) do
|
|
lib.addPrincipal(self.source, ("group.%s"):format(name))
|
|
end
|
|
NDCore.players[self.source] = self
|
|
TriggerEvent("ND:characterLoaded", self)
|
|
self.triggerEvent("ND:characterLoaded", removeCharacterFunctions(self))
|
|
end
|
|
|
|
---@param name string
|
|
---@param rank number
|
|
---@param isJob boolean
|
|
---@return boolean
|
|
function self.addGroup(name, rank, customGroup, isJob)
|
|
local groupRank = tonumber(rank) or 1
|
|
local groupInfo = Config.groups?[name] or {}
|
|
local rankData = groupInfo?.ranks?[groupRank]
|
|
|
|
local groupLabel = groupInfo?.label or name
|
|
local rankName = rankData?.label or groupRank
|
|
local isBoss = rankData?.isBoss or false
|
|
|
|
if customGroup then
|
|
if customGroup.label then groupLabel = customGroup.label end
|
|
if customGroup.rankName then rankName = customGroup.rankName end
|
|
if customGroup.isBoss ~= nil then isBoss = customGroup.isBoss end
|
|
end
|
|
|
|
if isJob then
|
|
for _, group in pairs(self.groups) do
|
|
group.isJob = nil
|
|
end
|
|
end
|
|
|
|
self.groups[name] = {
|
|
name = name,
|
|
label = groupLabel,
|
|
rankName = rankName,
|
|
rank = groupRank,
|
|
isJob = isJob,
|
|
isBoss = isBoss
|
|
}
|
|
|
|
if not isJob then
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "groups")
|
|
TriggerEvent("ND:updateCharacter", self, "groups")
|
|
end
|
|
|
|
TriggerEvent("ND:groupAdded", self, self.groups[name])
|
|
lib.addPrincipal(self.source, ("group.%s"):format(name))
|
|
return self.groups[name]
|
|
end
|
|
|
|
---@param name string
|
|
---@return table
|
|
function self.getGroup(name)
|
|
return self.groups[name]
|
|
end
|
|
|
|
---@param name string
|
|
function self.removeGroup(name)
|
|
local group = self.groups[name]
|
|
if not group then return end
|
|
|
|
if self.job == name then
|
|
self.job = nil
|
|
self.jobInfo = nil
|
|
end
|
|
|
|
self.groups[name] = nil
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "groups")
|
|
TriggerEvent("ND:updateCharacter", self, "groups")
|
|
TriggerEvent("ND:groupRemoved", self, group)
|
|
lib.removePrincipal(self.source, ("group.%s"):format(name))
|
|
return group
|
|
end
|
|
|
|
---@param name string
|
|
---@param rank number
|
|
---@return boolean
|
|
function self.setJob(name, rank, customGroup, keepGroup)
|
|
if not keepGroup then
|
|
self.removeGroup(self.job)
|
|
end
|
|
|
|
local job = self.addGroup(name, rank, customGroup, true)
|
|
|
|
if job then
|
|
self.job = job.name
|
|
self.jobInfo = job
|
|
end
|
|
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self), "job")
|
|
TriggerEvent("ND:updateCharacter", self, "job")
|
|
return job
|
|
end
|
|
|
|
---@param job string
|
|
---@return boolean
|
|
function self.getJob()
|
|
for name, group in pairs(self.groups) do
|
|
if group.isJob then
|
|
return name, group
|
|
end
|
|
end
|
|
end
|
|
|
|
local jobName, jobInfo = self.getJob()
|
|
if jobInfo then
|
|
self.job = jobName
|
|
self.jobInfo = jobInfo
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
function NDCore.avoidSavingLocation(coords, dist)
|
|
avoidSavingLastLocations[#avoidSavingLastLocations+1] = { coords = coords, dist = dist}
|
|
end
|
|
|
|
function NDCore.validateCharacterData(charInfo)
|
|
local errors = {}
|
|
|
|
local function checkLength(field, value, maxLength)
|
|
if type(value) ~= "string" then return end
|
|
if #value > maxLength then
|
|
table.insert(errors, string.format("%s is too long (max %d, got %d)", field, maxLength, #value))
|
|
end
|
|
end
|
|
|
|
if charInfo.name then checkLength("game name", charInfo.name, 50) end
|
|
if charInfo.firstname then checkLength("firstname", charInfo.firstname, 50) end
|
|
if charInfo.lastname then checkLength("lastname", charInfo.lastname, 50) end
|
|
if charInfo.dob then checkLength("dob", charInfo.dob, 50) end
|
|
if charInfo.gender then checkLength("gender", charInfo.gender, 50) end
|
|
if type(charInfo.cash) ~= "number" then table.insert(errors, "cash must be a number") end
|
|
if type(charInfo.bank) ~= "number" then table.insert(errors, "bank must be a number") end
|
|
|
|
if #errors > 0 then
|
|
return false, errors
|
|
else
|
|
return true, {}
|
|
end
|
|
end
|
|
|
|
---@param src number
|
|
---@param info table
|
|
---@return table
|
|
function NDCore.newCharacter(src, info)
|
|
local identifier = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
|
|
if not identifier then return end
|
|
|
|
local userId = PlayersInfo[src] and PlayersInfo[src].userId
|
|
|
|
local charInfo = {
|
|
source = src,
|
|
identifier = identifier,
|
|
user_id = userId,
|
|
name = GetPlayerName(src) or "",
|
|
firstname = info.firstname or "",
|
|
lastname = info.lastname or "",
|
|
dob = info.dob or "",
|
|
gender = info.gender or "",
|
|
cash = info.cash or 0,
|
|
bank = info.bank or 0,
|
|
phonenumber = info.phonenumber,
|
|
groups = info.groups or {},
|
|
metadata = info.metadata or {},
|
|
inventory = info.inventory or {}
|
|
}
|
|
|
|
local valid, issues = NDCore.validateCharacterData(charInfo)
|
|
if not valid then
|
|
for _, err in ipairs(issues) do
|
|
Wait(100)
|
|
TriggerClientEvent("ND:notifyClient", src, {
|
|
description = err,
|
|
type = "error"
|
|
})
|
|
end
|
|
return
|
|
end
|
|
|
|
charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, user_id, name, firstname, lastname, dob, gender, cash, bank, phonenumber, `groups`, metadata, inventory) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", {
|
|
identifier,
|
|
userId,
|
|
charInfo.name,
|
|
charInfo.firstname,
|
|
charInfo.lastname,
|
|
charInfo.dob,
|
|
charInfo.gender,
|
|
charInfo.cash,
|
|
charInfo.bank,
|
|
charInfo.phonenumber,
|
|
json.encode(charInfo.groups),
|
|
json.encode(charInfo.metadata),
|
|
json.encode(charInfo.inventory)
|
|
})
|
|
|
|
return createCharacterTable(charInfo)
|
|
end
|
|
|
|
---@param id number
|
|
---@return table
|
|
function NDCore.fetchCharacter(id, src)
|
|
local result
|
|
if src then
|
|
local identifier = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
|
|
local userId = PlayersInfo[src] and PlayersInfo[src].userId
|
|
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ? AND (identifier = ? OR user_id = ?)", {id, identifier, userId})
|
|
else
|
|
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id})
|
|
end
|
|
|
|
local info = result?[1]
|
|
if not info then return end
|
|
|
|
-- Check if character has user_id, if not assign it
|
|
if not info.user_id and src then
|
|
local userId = PlayersInfo[src] and PlayersInfo[src].userId
|
|
if userId then
|
|
MySQL.update.await("UPDATE nd_characters SET user_id = ? WHERE charid = ?", {userId, info.charid})
|
|
info.user_id = userId
|
|
end
|
|
end
|
|
|
|
return createCharacterTable({
|
|
source = src,
|
|
id = info.charid,
|
|
identifier = info.identifier,
|
|
user_id = info.user_id,
|
|
name = info.name,
|
|
firstname = info.firstname,
|
|
lastname = info.lastname,
|
|
dob = info.dob,
|
|
gender = info.gender,
|
|
cash = info.cash,
|
|
bank = info.bank,
|
|
phonenumber = info.phonenumber,
|
|
groups = json.decode(info.groups),
|
|
metadata = json.decode(info.metadata),
|
|
inventory = json.decode(info.inventory)
|
|
})
|
|
end
|
|
|
|
---@param src number
|
|
---@return table
|
|
function NDCore.fetchAllCharacters(src)
|
|
local userId = PlayersInfo[src] and PlayersInfo[src].userId
|
|
local identifier = NDCore.getPlayerIdentifierByType(src, Config.characterIdentifier)
|
|
local characters = {}
|
|
|
|
-- Query by either identifier OR user_id, excluding deleted characters
|
|
local result = MySQL.query.await("SELECT * FROM nd_characters WHERE (identifier = ? OR user_id = ?) AND deleted_at IS NULL", {identifier, userId})
|
|
|
|
for i=1, #result do
|
|
local info = result[i]
|
|
|
|
-- Check if character has user_id, if not assign it
|
|
if not info.user_id and userId then
|
|
MySQL.update.await("UPDATE nd_characters SET user_id = ? WHERE charid = ?", {userId, info.charid})
|
|
info.user_id = userId
|
|
end
|
|
|
|
characters[info.charid] = createCharacterTable({
|
|
source = src,
|
|
id = info.charid,
|
|
identifier = info.identifier,
|
|
user_id = info.user_id,
|
|
name = info.name,
|
|
firstname = info.firstname,
|
|
lastname = info.lastname,
|
|
dob = info.dob,
|
|
gender = info.gender,
|
|
cash = info.cash,
|
|
bank = info.bank,
|
|
phonenumber = info.phonenumber,
|
|
groups = json.decode(info.groups),
|
|
metadata = json.decode(info.metadata),
|
|
inventory = json.decode(info.inventory)
|
|
})
|
|
end
|
|
return characters
|
|
end
|
|
|
|
---@param src number
|
|
---@param id number
|
|
---@return table
|
|
function NDCore.setActiveCharacter(src, id)
|
|
local char = NDCore.players[src]
|
|
if not src or char and char.id == id then return end
|
|
|
|
local character = NDCore.fetchCharacter(id, src)
|
|
if not character then return end
|
|
|
|
character.name = GetPlayerName(src)
|
|
character.active()
|
|
return character
|
|
end
|
|
|
|
-- runs every 10 minutes, saves all characters and money logs.
|
|
lib.cron.new("*/10 * * * *", function()
|
|
local players = NDCore.getPlayers()
|
|
for _, player in pairs(players) do
|
|
player.saveLastLocation()
|
|
player.save()
|
|
end
|
|
|
|
if #moneyLogs == 0 then return end
|
|
|
|
local placeholders = {}
|
|
local params = {}
|
|
local logs = moneyLogs
|
|
moneyLogs = {}
|
|
|
|
for i=1, #logs do
|
|
local row = logs[i]
|
|
for j = 1, 6 do
|
|
table.insert(params, row[j])
|
|
end
|
|
table.insert(placeholders, "(?, ?, ?, ?, ?, ?)")
|
|
end
|
|
|
|
local query = "INSERT INTO nd_money_log (character_id, action, account, amount, reason, trace) VALUES " .. table.concat(placeholders, ",")
|
|
MySQL.prepare(query, params)
|
|
end)
|