Files
ND_Core/server/player.lua

487 lines
15 KiB
Lua
Raw Permalink Normal View History

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
2023-07-09 21:19:35 +02:00
local function createCharacterTable(info)
2023-07-11 00:35:11 +02:00
local playerInfo = PlayersInfo[info.source] or {}
2023-07-09 21:19:35 +02:00
local self = {
2023-07-16 16:33:37 +02:00
id = info.id,
2023-07-09 21:19:35 +02:00
source = info.source,
2023-07-10 03:33:01 +02:00
identifier = info.identifier,
2023-07-11 00:35:11 +02:00
identifiers = playerInfo.identifiers or {},
2023-07-12 04:53:35 +02:00
discord = playerInfo.discord or {},
2023-07-09 21:19:35 +02:00
name = info.name,
firstname = info.firstname,
lastname = info.lastname,
fullname = ("%s %s"):format(info.firstname, info.lastname),
2023-07-09 21:19:35 +02:00
dob = info.dob,
gender = info.gender,
cash = info.cash,
bank = info.bank,
2023-07-10 02:45:32 +02:00
groups = info.groups,
2023-07-09 21:19:35 +02:00
metadata = info.metadata,
inventory = info.inventory
}
2023-07-10 02:45:32 +02:00
---@param account string
---@param amount number
---@param reason string|nil
---@return boolean
2023-07-09 21:19:35 +02:00
function self.deductMoney(account, amount, reason)
local amount = tonumber(amount)
2023-08-23 00:33:21 +02:00
if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end
2023-07-09 21:19:35 +02:00
self[account] -= amount
if NDCore.players[self.source] then
2023-07-13 01:23:23 +02:00
self.triggerEvent("ND:updateMoney", self.cash, self.bank)
2023-07-09 21:19:35 +02:00
TriggerEvent("ND:moneyChange", self.source, account, amount, "remove", reason)
end
return true
2023-07-09 03:07:46 +02:00
end
2023-07-09 21:19:35 +02:00
2023-07-10 02:45:32 +02:00
---@param account string
---@param amount number
---@param reason string|nil
---@return boolean
2023-07-09 21:19:35 +02:00
function self.addMoney(account, amount, reason)
local amount = tonumber(amount)
2023-08-23 00:33:21 +02:00
if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end
2023-07-09 21:19:35 +02:00
self[account] += amount
if NDCore.players[self.source] then
2023-07-13 01:23:23 +02:00
self.triggerEvent("ND:updateMoney", self.cash, self.bank)
2023-07-09 21:19:35 +02:00
TriggerEvent("ND:moneyChange", self.source, account, amount, "add", reason)
end
return true
2023-07-09 03:37:46 +02:00
end
2023-07-10 02:45:32 +02:00
---@param amount number
---@return boolean
2023-07-09 21:19:35 +02:00
function self.depositMoney(amount)
local amount = tonumber(amount)
if not amount or self.cash < amount or amount <= 0 then return end
2023-07-10 02:45:32 +02:00
return self.deductMoney("cash", amount, "Deposit") and self.addMoney("bank", amount, "Deposit")
2023-07-09 03:07:46 +02:00
end
2023-07-09 21:19:35 +02:00
2023-07-10 02:45:32 +02:00
---@param amount number
---@return boolean
2023-07-09 21:19:35 +02:00
function self.withdrawMoney(amount)
local amount = tonumber(amount)
if not amount or self.bank < amount or amount <= 0 then return end
2023-07-10 02:45:32 +02:00
return self.deductMoney("bank", amount, "Withdraw") and self.addMoney("cash", amount, "Withdraw")
2023-07-09 21:19:35 +02:00
end
2023-07-16 12:31:20 +02:00
---@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 = {}
2023-08-26 01:37:15 +02:00
for i=1, #metadata do
local data = metadata[i]
returnData[data] = self.metadata[data]
end
return returnData
end
---@param key string|table
2023-07-16 12:31:20 +02:00
---@param value any
2023-08-26 04:35:34 +02:00
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
2023-08-26 04:35:34 +02:00
TriggerEvent("ND:moneyChange", self.source, k, v, "set", reason)
end
end
else
self[key] = value
if key == "cash" or key == "bank" then
2023-08-26 04:35:34 +02:00
TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason)
end
end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
2023-07-16 12:31:20 +02:00
end
2023-07-09 21:19:35 +02:00
---@param key string|table
2023-07-10 02:45:32 +02:00
---@param value any
---@return table
2023-07-09 21:19:35 +02:00
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))
2023-07-09 21:19:35 +02:00
return self.metadata
end
2023-07-10 02:45:32 +02:00
-- Completely delete character
function self.delete()
local result = MySQL.query.await("DELETE FROM nd_characters WHERE charid = ?", {self.id})
if result and NDCore.players[self.source] then
NDCore.players[self.source] = nil
2023-07-10 02:45:32 +02:00
end
return result
end
2023-07-09 21:19:35 +02:00
2023-07-10 02:45:32 +02:00
-- 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
2023-07-10 02:45:32 +02:00
local ped = GetPlayerPed(self.source)
if ped then
local coords = GetEntityCoords(ped)
local heading = GetEntityHeading(ped)
self.setMetadata("location", {
x = coords.x,
y = coords.y,
z = coords.z,
2023-07-10 02:45:32 +02:00
w = heading
})
end
self.triggerEvent("ND:characterUnloaded")
2023-07-10 02:45:32 +02:00
TriggerEvent("ND:characterUnloaded", self.source, self)
local saved = self.save()
NDCore.players[self.source] = nil
return saved
2023-07-09 21:19:35 +02:00
end
2023-07-10 02:45:32 +02:00
-- Save character information to database
function self.save()
2023-12-09 19:44:12 +01:00
local affectedRows = MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ? WHERE charid = ?", {
2023-07-09 21:19:35 +02:00
self.name,
self.firstname,
self.lastname,
self.dob,
self.gender,
self.cash,
self.bank,
2023-07-10 02:45:32 +02:00
json.encode(self.groups),
2023-07-09 21:19:35 +02:00
json.encode(self.metadata),
self.id
2023-07-09 21:19:35 +02:00
})
return affectedRows > 0
2023-07-09 21:19:35 +02:00
end
2023-07-10 02:45:32 +02:00
---Create a license/permit for the character
---@param licenseType string
---@param expire number
2023-07-09 21:19:35 +02:00
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
self.metadata.licenses[#licenses+1] = license
2023-07-13 01:23:23 +02:00
else
self.metadata.licenses = {license}
2023-07-09 21:19:35 +02:00
end
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
2023-07-09 21:19:35 +02:00
end
2023-08-23 03:29:22 +02:00
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
2023-07-09 21:19:35 +02:00
2023-07-10 02:45:32 +02:00
---@param coords vector3|vector4
---@return boolean
2023-07-09 21:19:35 +02:00
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)
2023-07-10 02:45:32 +02:00
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, ...)
2023-07-09 21:19:35 +02:00
return true
2023-07-09 03:07:46 +02:00
end
2023-07-12 04:53:35 +02:00
function self.notify(...)
if not self.source then return end
2023-08-23 03:30:15 +02:00
if GetResourceState("ModernHUD") == "started" then
TriggerClientEvent("ModernHUD:notify", self.source, ...)
elseif GetResourceState("ox_lib") == "started" then
TriggerClientEvent("ox_lib:notify", self.source, ...)
end
2023-07-12 04:53:35 +02:00
return true
end
2023-09-24 11:19:22 +02:00
function self.revive()
self.triggerEvent("ND:revivePlayer")
self.setMetadata({
dead = false,
deathInfo = false,
2023-09-24 11:19:22 +02:00
})
end
2023-07-10 02:45:32 +02:00
---@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
2023-07-13 01:23:23 +02:00
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
2023-10-21 21:44:17 +02:00
local roles = self.discord.roles
if roles then
for i=1, #Config.adminDiscordRoles do
local role = Config.adminDiscordRoles[i]
if lib.table.contains(roles, role) then
self.addGroup("admin")
end
end
end
for name, _ in pairs(self.groups) do
lib.addPrincipal(self.source, ("group.%s"):format(name))
end
NDCore.players[self.source] = self
2023-07-10 02:45:32 +02:00
TriggerEvent("ND:characterLoaded", self)
self.triggerEvent("ND:characterLoaded", removeCharacterFunctions(self))
2023-07-10 02:45:32 +02:00
end
---@param name string
---@param rank number
---@param isJob boolean
2023-07-10 03:17:32 +02:00
---@return boolean
2023-07-22 23:59:39 +02:00
function self.addGroup(name, rank, isJob)
2023-08-19 22:09:23 +02:00
local groupRank = tonumber(rank) or 1
2023-07-10 03:17:32 +02:00
local groupInfo = Config.groups[name]
-- if not groupInfo then return end
2023-07-22 23:59:39 +02:00
if isJob then
for _, group in pairs(self.groups) do
group.isJob = nil
end
end
2023-07-10 02:45:32 +02:00
self.groups[name] = {
label = groupInfo and groupInfo.label or name,
rankName = groupInfo and groupInfo.ranks[groupRank] or groupRank,
2023-08-19 22:09:23 +02:00
rank = groupRank,
2023-07-22 23:59:39 +02:00
isJob = isJob
2023-07-10 02:45:32 +02:00
}
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
lib.addPrincipal(self.source, ("group.%s"):format(name))
return self.groups[name]
2023-07-10 03:17:32 +02:00
end
---@param name string
---@return table
function self.getGroup(name)
return self.groups[name]
2023-07-10 02:45:32 +02:00
end
---@param name string
function self.removeGroup(name)
local group = self.groups[name]
if not group then return end
2023-07-10 02:45:32 +02:00
self.groups[name] = nil
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
lib.removePrincipal(self.source, ("group.%s"):format(name))
return group
2023-07-10 02:45:32 +02:00
end
2023-07-22 23:59:39 +02:00
---@param name string
---@param rank number
---@return boolean
function self.setJob(name, rank)
self.removeGroup(self.job)
local job = self.addGroup(name, rank, true)
local jobName, jobInfo = self.getJob()
if jobInfo then
self.job = jobName
self.jobInfo = jobInfo
end
return job
2023-07-22 23:59:39 +02:00
end
---@param job string
---@return boolean
2023-08-26 01:37:15 +02:00
function self.getJob()
2023-07-22 23:59:39 +02:00
for name, group in pairs(self.groups) do
2023-08-26 01:37:15 +02:00
if group.isJob then
return name, group
2023-07-22 23:59:39 +02:00
end
end
end
2023-08-26 01:37:15 +02:00
local jobName, jobInfo = self.getJob()
if jobInfo then
self.job = jobName
self.jobInfo = jobInfo
end
2023-07-09 21:19:35 +02:00
return self
2023-07-09 03:07:46 +02:00
end
2023-07-10 03:17:32 +02:00
---@param src number
---@param info table
---@return table
2023-07-09 03:07:46 +02:00
function NDCore.newCharacter(src, info)
2023-07-10 03:33:01 +02:00
local identifier = GetPlayerIdentifierByType(src, Config.characterIdentifier)
if not identifier then return end
2023-07-09 03:07:46 +02:00
2023-07-16 16:33:37 +02:00
local charInfo = {
2023-07-09 03:07:46 +02:00
source = src,
2023-07-10 03:33:01 +02:00
identifier = identifier,
2023-07-09 03:07:46 +02:00
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,
2023-07-10 02:45:32 +02:00
groups = info.groups or {},
2023-07-09 03:07:46 +02:00
metadata = info.metadata or {},
inventory = info.inventory or {},
2023-07-16 16:33:37 +02:00
}
2023-07-09 03:07:46 +02:00
2023-07-16 12:31:20 +02:00
charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, groups, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", {
2023-07-10 03:33:01 +02:00
identifier,
2023-07-09 03:07:46 +02:00
charInfo.name,
charInfo.firstname,
charInfo.lastname,
charInfo.dob,
charInfo.gender,
charInfo.cash,
charInfo.bank,
2023-07-16 12:31:20 +02:00
json.encode(charInfo.groups),
2023-07-10 02:45:32 +02:00
json.encode(charInfo.metadata)
2023-07-09 21:19:35 +02:00
})
2023-07-09 03:07:46 +02:00
2023-07-16 16:33:37 +02:00
return createCharacterTable(charInfo)
2023-07-09 03:07:46 +02:00
end
2023-07-10 03:17:32 +02:00
---@param id number
---@return table
function NDCore.fetchCharacter(id, src)
local result
if src then
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ? and identifier = ?", {id, GetPlayerIdentifierByType(src, Config.characterIdentifier)})
else
result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id})
end
2023-07-09 03:07:46 +02:00
if not result then return end
local info = result[1]
2023-07-09 21:19:35 +02:00
return createCharacterTable({
source = src,
2023-07-16 16:33:37 +02:00
id = info.charid,
2023-07-10 03:33:01 +02:00
identifier = info.identifier,
2023-07-09 03:07:46 +02:00
name = info.name,
firstname = info.firstname,
lastname = info.lastname,
dob = info.dob,
gender = info.gender,
cash = info.cash,
bank = info.bank,
2023-07-10 02:45:32 +02:00
groups = json.decode(info.groups),
2023-07-09 03:07:46 +02:00
metadata = json.decode(info.metadata),
2023-07-10 02:45:32 +02:00
inventory = json.decode(info.inventory)
2023-07-09 03:07:46 +02:00
})
end
2023-07-10 03:17:32 +02:00
---@param src number
---@return table
2023-07-11 00:35:11 +02:00
function NDCore.fetchAllCharacters(src)
2023-07-09 03:07:46 +02:00
local characters = {}
2023-07-10 03:33:01 +02:00
local result = MySQL.query.await("SELECT * FROM nd_characters WHERE identifier = ?", {GetPlayerIdentifierByType(src, Config.characterIdentifier)})
2023-07-09 03:07:46 +02:00
2023-07-11 00:35:11 +02:00
for i=1, #result do
2023-07-09 03:07:46 +02:00
local info = result[i]
2023-07-09 21:19:35 +02:00
characters[info.charid] = createCharacterTable({
2023-07-16 12:31:20 +02:00
source = src,
2023-07-09 03:07:46 +02:00
id = info.charid,
2023-07-10 03:33:01 +02:00
identifier = info.identifier,
2023-07-09 03:07:46 +02:00
name = info.name,
firstname = info.firstname,
lastname = info.lastname,
dob = info.dob,
gender = info.gender,
cash = info.cash,
bank = info.bank,
2023-07-10 02:45:32 +02:00
groups = json.decode(info.groups),
2023-07-09 03:07:46 +02:00
metadata = json.decode(info.metadata),
inventory = json.decode(info.inventory),
})
end
return characters
end
2023-07-10 03:17:32 +02:00
---@param src number
---@param id number
---@return table
2023-07-09 03:07:46 +02:00
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
2023-07-09 03:07:46 +02:00
character.name = GetPlayerName(src)
2023-07-16 16:33:37 +02:00
character.active()
return character
2023-07-09 03:07:46 +02:00
end