721 lines
31 KiB
Lua
721 lines
31 KiB
Lua
|
QBCore.Players = {}
|
||
|
QBCore.Player = {}
|
||
|
|
||
|
-- On player login get their data or set defaults
|
||
|
-- Don't touch any of this unless you know what you are doing
|
||
|
-- Will cause major issues!
|
||
|
|
||
|
function QBCore.Player.Login(source, citizenid, newData)
|
||
|
if source and source ~= '' then
|
||
|
if citizenid then
|
||
|
local license = QBCore.Functions.GetIdentifier(source, 'license')
|
||
|
local isOnline = QBCore.Functions.GetPlayerByCitizenId(citizenid)
|
||
|
local PlayerData = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenid })
|
||
|
if isOnline then exports['qb-core']:ExploitBan(source, 'You Have Been Banned For Exploitation') return end
|
||
|
if PlayerData and license == PlayerData.license then
|
||
|
PlayerData.money = json.decode(PlayerData.money)
|
||
|
PlayerData.job = json.decode(PlayerData.job)
|
||
|
PlayerData.position = json.decode(PlayerData.position)
|
||
|
PlayerData.metadata = json.decode(PlayerData.metadata)
|
||
|
PlayerData.charinfo = json.decode(PlayerData.charinfo)
|
||
|
if PlayerData.gang then
|
||
|
PlayerData.gang = json.decode(PlayerData.gang)
|
||
|
else
|
||
|
PlayerData.gang = {}
|
||
|
end
|
||
|
PlayerData.phoneNumber = PlayerData.phone_number
|
||
|
QBCore.Player.CheckPlayerData(source, PlayerData)
|
||
|
else
|
||
|
DropPlayer(source, Lang:t("info.exploit_dropped"))
|
||
|
TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', string.format("%s Has Been Dropped For Character Joining Exploit", GetPlayerName(source)), false)
|
||
|
end
|
||
|
else
|
||
|
QBCore.Player.CheckPlayerData(source, newData)
|
||
|
end
|
||
|
return true
|
||
|
else
|
||
|
QBCore.ShowError(GetCurrentResourceName(), 'ERROR QBCORE.PLAYER.LOGIN - NO SOURCE GIVEN!')
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.GetOfflinePlayer(citizenid)
|
||
|
if citizenid then
|
||
|
local PlayerData = MySQL.Sync.prepare('SELECT * FROM players where citizenid = ?', {citizenid})
|
||
|
if PlayerData then
|
||
|
PlayerData.money = json.decode(PlayerData.money)
|
||
|
PlayerData.job = json.decode(PlayerData.job)
|
||
|
PlayerData.position = json.decode(PlayerData.position)
|
||
|
PlayerData.metadata = json.decode(PlayerData.metadata)
|
||
|
PlayerData.charinfo = json.decode(PlayerData.charinfo)
|
||
|
if PlayerData.gang then
|
||
|
PlayerData.gang = json.decode(PlayerData.gang)
|
||
|
else
|
||
|
PlayerData.gang = {}
|
||
|
end
|
||
|
PlayerData.phoneNumber = PlayerData.phone_number
|
||
|
return QBCore.Player.CheckPlayerData(nil, PlayerData)
|
||
|
end
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.CheckPlayerData(source, PlayerData)
|
||
|
PlayerData = PlayerData or {}
|
||
|
local Offline = true
|
||
|
if source then
|
||
|
PlayerData.source = source
|
||
|
PlayerData.license = PlayerData.license or QBCore.Functions.GetIdentifier(source, 'license')
|
||
|
PlayerData.name = GetPlayerName(source)
|
||
|
Offline = false
|
||
|
end
|
||
|
|
||
|
PlayerData.citizenid = PlayerData.citizenid or QBCore.Player.CreateCitizenId()
|
||
|
PlayerData.cid = PlayerData.cid or 1
|
||
|
PlayerData.money = PlayerData.money or {}
|
||
|
PlayerData.optin = PlayerData.optin or true
|
||
|
for moneytype, startamount in pairs(QBCore.Config.Money.MoneyTypes) do
|
||
|
PlayerData.money[moneytype] = PlayerData.money[moneytype] or startamount
|
||
|
end
|
||
|
|
||
|
-- Charinfo
|
||
|
PlayerData.charinfo = PlayerData.charinfo or {}
|
||
|
PlayerData.charinfo.firstname = PlayerData.charinfo.firstname or 'Fornavn'
|
||
|
PlayerData.charinfo.lastname = PlayerData.charinfo.lastname or 'Efternavn'
|
||
|
PlayerData.charinfo.birthdate = PlayerData.charinfo.birthdate or '19/07/1998'
|
||
|
PlayerData.charinfo.gender = PlayerData.charinfo.gender or 0
|
||
|
PlayerData.charinfo.backstory = PlayerData.charinfo.backstory or 'Baghistorie'
|
||
|
PlayerData.charinfo.nationality = PlayerData.charinfo.nationality or 'Danmark'
|
||
|
PlayerData.charinfo.phone = PlayerData.charinfo.phone or QBCore.Functions.CreatePhoneNumber()
|
||
|
PlayerData.charinfo.account = PlayerData.charinfo.account or QBCore.Functions.CreateAccountNumber()
|
||
|
-- Metadata
|
||
|
PlayerData.metadata = PlayerData.metadata or {}
|
||
|
PlayerData.metadata['hunger'] = PlayerData.metadata['hunger'] or 100
|
||
|
PlayerData.metadata['thirst'] = PlayerData.metadata['thirst'] or 100
|
||
|
PlayerData.metadata['stress'] = PlayerData.metadata['stress'] or 0
|
||
|
PlayerData.metadata['isdead'] = PlayerData.metadata['isdead'] or false
|
||
|
PlayerData.metadata['inlaststand'] = PlayerData.metadata['inlaststand'] or false
|
||
|
PlayerData.metadata['armor'] = PlayerData.metadata['armor'] or 0
|
||
|
PlayerData.metadata['ishandcuffed'] = PlayerData.metadata['ishandcuffed'] or false
|
||
|
PlayerData.metadata['tracker'] = PlayerData.metadata['tracker'] or false
|
||
|
PlayerData.metadata['injail'] = PlayerData.metadata['injail'] or 0
|
||
|
PlayerData.metadata['jailitems'] = PlayerData.metadata['jailitems'] or {}
|
||
|
PlayerData.metadata['status'] = PlayerData.metadata['status'] or {}
|
||
|
PlayerData.metadata['phone'] = PlayerData.metadata['phone'] or {}
|
||
|
PlayerData.metadata['fitbit'] = PlayerData.metadata['fitbit'] or {}
|
||
|
PlayerData.metadata['commandbinds'] = PlayerData.metadata['commandbinds'] or {}
|
||
|
PlayerData.metadata['bloodtype'] = PlayerData.metadata['bloodtype'] or QBCore.Config.Player.Bloodtypes[math.random(1, #QBCore.Config.Player.Bloodtypes)]
|
||
|
PlayerData.metadata['dealerrep'] = PlayerData.metadata['dealerrep'] or 0
|
||
|
PlayerData.metadata['craftingrep'] = PlayerData.metadata['craftingrep'] or 0
|
||
|
PlayerData.metadata['attachmentcraftingrep'] = PlayerData.metadata['attachmentcraftingrep'] or 0
|
||
|
PlayerData.metadata['reporep'] = PlayerData.metadata['reporep'] or 0
|
||
|
PlayerData.metadata['currentapartment'] = PlayerData.metadata['currentapartment'] or nil
|
||
|
PlayerData.metadata['jobrep'] = PlayerData.metadata['jobrep'] or {}
|
||
|
PlayerData.metadata['garbage'] = PlayerData.metadata['garbage'] or 0
|
||
|
PlayerData.metadata['delivery'] = PlayerData.metadata['delivery'] or 0
|
||
|
PlayerData.metadata['jobrep']['tow'] = PlayerData.metadata['jobrep']['tow'] or 0
|
||
|
PlayerData.metadata['jobrep']['trucker'] = PlayerData.metadata['jobrep']['trucker'] or 0
|
||
|
PlayerData.metadata['jobrep']['taxi'] = PlayerData.metadata['jobrep']['taxi'] or 0
|
||
|
PlayerData.metadata['jobrep']['hotdog'] = PlayerData.metadata['jobrep']['hotdog'] or 0
|
||
|
PlayerData.metadata['callsign'] = PlayerData.metadata['callsign'] or 'NO CALLSIGN'
|
||
|
PlayerData.metadata['fingerprint'] = PlayerData.metadata['fingerprint'] or QBCore.Player.CreateFingerId()
|
||
|
PlayerData.metadata['walletid'] = PlayerData.metadata['walletid'] or QBCore.Player.CreateWalletId()
|
||
|
PlayerData.metadata['health'] = PlayerData.metadata['health'] or 200
|
||
|
PlayerData.metadata['criminalrecord'] = PlayerData.metadata['criminalrecord'] or {
|
||
|
['hasRecord'] = false,
|
||
|
['date'] = nil
|
||
|
}
|
||
|
PlayerData.metadata['crypto'] = PlayerData.metadata['crypto'] or {
|
||
|
["shung"] = 0,
|
||
|
["gne"] = 0,
|
||
|
["xcoin"] = 0,
|
||
|
["lme"] = 0
|
||
|
}
|
||
|
PlayerData.metadata['licences'] = PlayerData.metadata['licences'] or {
|
||
|
['driver'] = true,
|
||
|
['business'] = false,
|
||
|
['weapon'] = false
|
||
|
}
|
||
|
PlayerData.metadata['inside'] = PlayerData.metadata['inside'] or {
|
||
|
house = nil,
|
||
|
apartment = {
|
||
|
apartmentType = nil,
|
||
|
apartmentId = nil,
|
||
|
}
|
||
|
}
|
||
|
PlayerData.metadata['phonedata'] = PlayerData.metadata['phonedata'] or {
|
||
|
SerialNumber = QBCore.Player.CreateSerialNumber(),
|
||
|
InstalledApps = {},
|
||
|
}
|
||
|
-- Job
|
||
|
if PlayerData.job and PlayerData.job.name and not QBCore.Shared.Jobs[PlayerData.job.name] then PlayerData.job = nil end
|
||
|
PlayerData.job = PlayerData.job or {}
|
||
|
PlayerData.job.name = PlayerData.job.name or 'unemployed'
|
||
|
PlayerData.job.label = PlayerData.job.label or 'Civil'
|
||
|
PlayerData.job.payment = PlayerData.job.payment or 10
|
||
|
PlayerData.job.type = PlayerData.job.type or 'none'
|
||
|
if QBCore.Shared.ForceJobDefaultDutyAtLogin or PlayerData.job.onduty == nil then
|
||
|
PlayerData.job.onduty = QBCore.Shared.Jobs[PlayerData.job.name].defaultDuty
|
||
|
end
|
||
|
PlayerData.job.isboss = PlayerData.job.isboss or false
|
||
|
PlayerData.job.grade = PlayerData.job.grade or {}
|
||
|
PlayerData.job.grade.name = PlayerData.job.grade.name or 'Arbejdsløs'
|
||
|
PlayerData.job.grade.level = PlayerData.job.grade.level or 0
|
||
|
-- Gang
|
||
|
if PlayerData.gang and PlayerData.gang.name and not QBCore.Shared.Gangs[PlayerData.gang.name] then PlayerData.gang = nil end
|
||
|
PlayerData.gang = PlayerData.gang or {}
|
||
|
PlayerData.gang.name = PlayerData.gang.name or 'Ingen bande'
|
||
|
PlayerData.gang.label = PlayerData.gang.label or 'Ingen banderelationer'
|
||
|
PlayerData.gang.isboss = PlayerData.gang.isboss or false
|
||
|
PlayerData.gang.grade = PlayerData.gang.grade or {}
|
||
|
PlayerData.gang.grade.name = PlayerData.gang.grade.name or 'Ingen stilling'
|
||
|
PlayerData.gang.grade.level = PlayerData.gang.grade.level or 0
|
||
|
-- Other
|
||
|
PlayerData.playtime = PlayerData.playtime or 0
|
||
|
PlayerData.position = PlayerData.position or QBConfig.DefaultSpawn
|
||
|
PlayerData.playerPhone = PlayerData.playerPhone or PlayerData.charinfo.phone
|
||
|
PlayerData.items = GetResourceState('ps-inventory') ~= 'missing' and exports['ps-inventory']:LoadInventory(PlayerData.source, PlayerData.citizenid) or {}
|
||
|
return QBCore.Player.CreatePlayer(PlayerData, Offline)
|
||
|
end
|
||
|
|
||
|
-- On player logout
|
||
|
|
||
|
function QBCore.Player.Logout(source)
|
||
|
TriggerClientEvent('QBCore:Client:OnPlayerUnload', source)
|
||
|
TriggerEvent('QBCore:Server:OnPlayerUnload', source)
|
||
|
TriggerClientEvent('QBCore:Player:UpdatePlayerData', source)
|
||
|
Wait(200)
|
||
|
QBCore.Players[source] = nil
|
||
|
end
|
||
|
|
||
|
-- Create a new character
|
||
|
-- Don't touch any of this unless you know what you are doing
|
||
|
-- Will cause major issues!
|
||
|
|
||
|
function QBCore.Player.CreatePlayer(PlayerData, Offline)
|
||
|
local self = {}
|
||
|
self.Functions = {}
|
||
|
self.PlayerData = PlayerData
|
||
|
self.Offline = Offline
|
||
|
|
||
|
function self.Functions.UpdatePlayerData()
|
||
|
if self.Offline then return end -- Unsupported for Offline Players
|
||
|
TriggerEvent('QBCore:Player:SetPlayerData', self.PlayerData)
|
||
|
TriggerClientEvent('QBCore:Player:SetPlayerData', self.PlayerData.source, self.PlayerData)
|
||
|
end
|
||
|
|
||
|
function self.Functions.SetJob(job, grade)
|
||
|
job = job:lower()
|
||
|
grade = tostring(grade) or '0'
|
||
|
if not QBCore.Shared.Jobs[job] then return false end
|
||
|
self.PlayerData.job.name = job
|
||
|
self.PlayerData.job.label = QBCore.Shared.Jobs[job].label
|
||
|
self.PlayerData.job.onduty = QBCore.Shared.Jobs[job].defaultDuty
|
||
|
self.PlayerData.job.type = QBCore.Shared.Jobs[job].type or 'none'
|
||
|
if QBCore.Shared.Jobs[job].grades[grade] then
|
||
|
local jobgrade = QBCore.Shared.Jobs[job].grades[grade]
|
||
|
self.PlayerData.job.grade = {}
|
||
|
self.PlayerData.job.grade.name = jobgrade.name
|
||
|
self.PlayerData.job.grade.level = tonumber(grade)
|
||
|
self.PlayerData.job.payment = jobgrade.payment or 30
|
||
|
self.PlayerData.job.isboss = jobgrade.isboss or false
|
||
|
else
|
||
|
self.PlayerData.job.grade = {}
|
||
|
self.PlayerData.job.grade.name = 'Ingen stilling'
|
||
|
self.PlayerData.job.grade.level = 0
|
||
|
self.PlayerData.job.payment = 30
|
||
|
self.PlayerData.job.isboss = false
|
||
|
end
|
||
|
|
||
|
if not self.Offline then
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
TriggerEvent('QBCore:Server:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
|
||
|
TriggerClientEvent('QBCore:Client:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
|
||
|
end
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function self.Functions.UpdateNumber(newnumber)
|
||
|
self.PlayerData.charinfo.phone = newnumber
|
||
|
self.PlayerData.playerPhone = newnumber
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
end
|
||
|
|
||
|
function self.Functions.SetGang(gang, grade)
|
||
|
gang = gang:lower()
|
||
|
grade = tostring(grade) or '0'
|
||
|
if not QBCore.Shared.Gangs[gang] then return false end
|
||
|
self.PlayerData.gang.name = gang
|
||
|
self.PlayerData.gang.label = QBCore.Shared.Gangs[gang].label
|
||
|
if QBCore.Shared.Gangs[gang].grades[grade] then
|
||
|
local ganggrade = QBCore.Shared.Gangs[gang].grades[grade]
|
||
|
self.PlayerData.gang.grade = {}
|
||
|
self.PlayerData.gang.grade.name = ganggrade.name
|
||
|
self.PlayerData.gang.grade.level = tonumber(grade)
|
||
|
self.PlayerData.gang.isboss = ganggrade.isboss or false
|
||
|
else
|
||
|
self.PlayerData.gang.grade = {}
|
||
|
self.PlayerData.gang.grade.name = 'Ingen grad'
|
||
|
self.PlayerData.gang.grade.level = 0
|
||
|
self.PlayerData.gang.isboss = false
|
||
|
end
|
||
|
|
||
|
if not self.Offline then
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
TriggerEvent('QBCore:Server:OnGangUpdate', self.PlayerData.source, self.PlayerData.gang)
|
||
|
TriggerClientEvent('QBCore:Client:OnGangUpdate', self.PlayerData.source, self.PlayerData.gang)
|
||
|
end
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function self.Functions.SetJobDuty(onDuty)
|
||
|
self.PlayerData.job.onduty = not not onDuty -- Make sure the value is a boolean if nil is sent
|
||
|
TriggerEvent('QBCore:Server:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
|
||
|
TriggerClientEvent('QBCore:Client:OnJobUpdate', self.PlayerData.source, self.PlayerData.job)
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
end
|
||
|
|
||
|
function self.Functions.SetPlayerData(key, val)
|
||
|
if not key or type(key) ~= 'string' then return end
|
||
|
self.PlayerData[key] = val
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
end
|
||
|
|
||
|
function self.Functions.SetMetaData(meta, val)
|
||
|
if not meta or type(meta) ~= 'string' then return end
|
||
|
if meta == 'hunger' or meta == 'thirst' then
|
||
|
val = val > 100 and 100 or val
|
||
|
end
|
||
|
self.PlayerData.metadata[meta] = val
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
end
|
||
|
|
||
|
function self.Functions.GetMetaData(meta)
|
||
|
if not meta or type(meta) ~= 'string' then return end
|
||
|
return self.PlayerData.metadata[meta]
|
||
|
end
|
||
|
|
||
|
function self.Functions.AddJobReputation(amount)
|
||
|
if not amount then return end
|
||
|
amount = tonumber(amount)
|
||
|
self.PlayerData.metadata['jobrep'][self.PlayerData.job.name] = self.PlayerData.metadata['jobrep'][self.PlayerData.job.name] + amount
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
end
|
||
|
|
||
|
|
||
|
function self.Functions.GetMoney(moneytype)
|
||
|
if not moneytype then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
moneytype = moneytype:lower()
|
||
|
|
||
|
return self.PlayerData.money[moneytype] or 0 -- Return 0 if the moneytype is not found
|
||
|
end
|
||
|
|
||
|
|
||
|
function self.Functions.AddMoney(moneytype, amount, reason)
|
||
|
reason = reason or 'unknown'
|
||
|
moneytype = moneytype:lower()
|
||
|
amount = tonumber(amount)
|
||
|
if amount < 0 then return end
|
||
|
if not self.PlayerData.money[moneytype] then return false end
|
||
|
self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] + amount
|
||
|
|
||
|
if not self.Offline then
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', string.format("** %s (citizenid: %s | id: %s) ** $%s (%s) added, new %s balance: %s reason: %s",GetPlayerName(self.PlayerData.source),self.PlayerData.citizenid,self.PlayerData.source,amount,moneytype,moneytype,self.PlayerData.money[moneytype],reason))
|
||
|
TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, amount, false)
|
||
|
TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, "add", reason)
|
||
|
TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, "add", reason)
|
||
|
end
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function self.Functions.RemoveMoney(moneytype, amount, reason)
|
||
|
reason = reason or 'unknown'
|
||
|
moneytype = moneytype:lower()
|
||
|
amount = tonumber(amount)
|
||
|
if amount < 0 then return end
|
||
|
if not self.PlayerData.money[moneytype] then return false end
|
||
|
for _, mtype in pairs(QBCore.Config.Money.DontAllowMinus) do
|
||
|
if mtype == moneytype then
|
||
|
if (self.PlayerData.money[moneytype] - amount) < 0 then
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
self.PlayerData.money[moneytype] = self.PlayerData.money[moneytype] - amount
|
||
|
|
||
|
if not self.Offline then
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', string.format("** %s (citizenid: %s | id: %s)** $%s (%s) removed, new %s balance: %s reason: %s",GetPlayerName(self.PlayerData.source),self.PlayerData.citizenid,self.PlayerData.source,amount,moneytype,moneytype,self.PlayerData.money[moneytype],reason))
|
||
|
TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, amount, true)
|
||
|
if moneytype == 'bank' then
|
||
|
TriggerClientEvent('qb-phone:client:RemoveBankMoney', self.PlayerData.source, amount)
|
||
|
end
|
||
|
TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, "remove", reason)
|
||
|
TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, "remove", reason)
|
||
|
end
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function self.Functions.SetMoney(moneytype, amount, reason)
|
||
|
reason = reason or 'unknown'
|
||
|
moneytype = moneytype:lower()
|
||
|
amount = tonumber(amount)
|
||
|
if amount < 0 then return false end
|
||
|
if not self.PlayerData.money[moneytype] then return false end
|
||
|
local difference = amount - self.PlayerData.money[moneytype]
|
||
|
self.PlayerData.money[moneytype] = amount
|
||
|
|
||
|
if not self.Offline then
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'SetMoney', 'green', string.format("** %s (citizenid: %s | id: %s)** $%s (%s) set, new %s balance: %s reason: %s",GetPlayerName(self.PlayerData.source),self.PlayerData.citizenid,self.PlayerData.source,amount,moneytype,moneytype,self.PlayerData.money[moneytype],reason))
|
||
|
TriggerClientEvent('hud:client:OnMoneyChange', self.PlayerData.source, moneytype, math.abs(difference), difference < 0)
|
||
|
TriggerClientEvent('QBCore:Client:OnMoneyChange', self.PlayerData.source, moneytype, amount, "set", reason)
|
||
|
TriggerEvent('QBCore:Server:OnMoneyChange', self.PlayerData.source, moneytype, amount, "set", reason)
|
||
|
end
|
||
|
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function self.Functions.SetCreditCard(cardNumber)
|
||
|
self.PlayerData.charinfo.card = cardNumber
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
end
|
||
|
|
||
|
function self.Functions.GetCardSlot(cardNumber, cardType)
|
||
|
local item = tostring(cardType):lower()
|
||
|
local slots = exports['ps-inventory']:GetSlotsByItem(self.PlayerData.items, item)
|
||
|
for _, slot in pairs(slots) do
|
||
|
if slot then
|
||
|
if self.PlayerData.items[slot].info.cardNumber == cardNumber then
|
||
|
return slot
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
function self.Functions.Save()
|
||
|
if self.Offline then
|
||
|
QBCore.Player.SaveOffline(self.PlayerData)
|
||
|
else
|
||
|
QBCore.Player.Save(self.PlayerData.source)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function self.Functions.Logout()
|
||
|
if self.Offline then return end -- Unsupported for Offline Players
|
||
|
QBCore.Player.Logout(self.PlayerData.source)
|
||
|
end
|
||
|
|
||
|
function self.Functions.AddMethod(methodName, handler)
|
||
|
self.Functions[methodName] = handler
|
||
|
end
|
||
|
|
||
|
function self.Functions.AddField(fieldName, data)
|
||
|
self[fieldName] = data
|
||
|
end
|
||
|
|
||
|
if self.Offline then
|
||
|
return self
|
||
|
else
|
||
|
QBCore.Players[self.PlayerData.source] = self
|
||
|
QBCore.Player.Save(self.PlayerData.source)
|
||
|
|
||
|
-- At this point we are safe to emit new instance to third party resource for load handling
|
||
|
TriggerEvent('QBCore:Server:PlayerLoaded', self)
|
||
|
self.Functions.UpdatePlayerData()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Add a new function to the Functions table of the player class
|
||
|
-- Use-case:
|
||
|
--[[
|
||
|
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
|
||
|
QBCore.Functions.AddPlayerMethod(Player.PlayerData.source, "functionName", function(oneArg, orMore)
|
||
|
-- do something here
|
||
|
end)
|
||
|
end)
|
||
|
]]
|
||
|
|
||
|
function QBCore.Functions.AddPlayerMethod(ids, methodName, handler)
|
||
|
local idType = type(ids)
|
||
|
if idType == "number" then
|
||
|
if ids == -1 then
|
||
|
for _, v in pairs(QBCore.Players) do
|
||
|
v.Functions.AddMethod(methodName, handler)
|
||
|
end
|
||
|
else
|
||
|
if not QBCore.Players[ids] then return end
|
||
|
|
||
|
QBCore.Players[ids].Functions.AddMethod(methodName, handler)
|
||
|
end
|
||
|
elseif idType == "table" and table.type(ids) == "array" then
|
||
|
for i = 1, #ids do
|
||
|
QBCore.Functions.AddPlayerMethod(ids[i], methodName, handler)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Add a new field table of the player class
|
||
|
-- Use-case:
|
||
|
--[[
|
||
|
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
|
||
|
QBCore.Functions.AddPlayerField(Player.PlayerData.source, "fieldName", "fieldData")
|
||
|
end)
|
||
|
]]
|
||
|
|
||
|
function QBCore.Functions.AddPlayerField(ids, fieldName, data)
|
||
|
local idType = type(ids)
|
||
|
if idType == "number" then
|
||
|
if ids == -1 then
|
||
|
for _, v in pairs(QBCore.Players) do
|
||
|
v.Functions.AddField(fieldName, data)
|
||
|
end
|
||
|
else
|
||
|
if not QBCore.Players[ids] then return end
|
||
|
|
||
|
QBCore.Players[ids].Functions.AddField(fieldName, data)
|
||
|
end
|
||
|
elseif idType == "table" and table.type(ids) == "array" then
|
||
|
for i = 1, #ids do
|
||
|
QBCore.Functions.AddPlayerField(ids[i], fieldName, data)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Save player info to database (make sure citizenid is the primary key in your database)
|
||
|
|
||
|
function QBCore.Player.Save(source)
|
||
|
local ped = GetPlayerPed(source)
|
||
|
local pcoords = GetEntityCoords(ped)
|
||
|
local PlayerData = QBCore.Players[source].PlayerData
|
||
|
if PlayerData then
|
||
|
MySQL.insert('INSERT INTO players (citizenid, cid, license, name, money, charinfo, job, gang, position, metadata, playtime) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata, :playtime) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata, playtime = :playtime', {
|
||
|
citizenid = PlayerData.citizenid,
|
||
|
cid = tonumber(PlayerData.cid),
|
||
|
license = PlayerData.license,
|
||
|
name = PlayerData.name,
|
||
|
money = json.encode(PlayerData.money),
|
||
|
charinfo = json.encode(PlayerData.charinfo),
|
||
|
job = json.encode(PlayerData.job),
|
||
|
gang = json.encode(PlayerData.gang),
|
||
|
position = json.encode(pcoords),
|
||
|
metadata = json.encode(PlayerData.metadata),
|
||
|
playtime = PlayerData.playtime
|
||
|
})
|
||
|
if GetResourceState('ps-inventory') ~= 'missing' then exports['ps-inventory']:SaveInventory(source) end
|
||
|
-- QBCore.ShowSuccess(GetCurrentResourceName(), PlayerData.name .. ' PLAYER SAVED!')
|
||
|
else
|
||
|
QBCore.ShowError(GetCurrentResourceName(), 'ERROR QBCORE.PLAYER.SAVE - PLAYERDATA IS EMPTY!')
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.SaveOffline(PlayerData)
|
||
|
if PlayerData then
|
||
|
MySQL.Async.insert('INSERT INTO players (citizenid, cid, license, name, money, charinfo, job, gang, position, metadata, playtime) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata, :playtime) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata, playtime = :playtime', {
|
||
|
citizenid = PlayerData.citizenid,
|
||
|
cid = tonumber(PlayerData.cid),
|
||
|
license = PlayerData.license,
|
||
|
name = PlayerData.name,
|
||
|
money = json.encode(PlayerData.money),
|
||
|
charinfo = json.encode(PlayerData.charinfo),
|
||
|
job = json.encode(PlayerData.job),
|
||
|
gang = json.encode(PlayerData.gang),
|
||
|
position = json.encode(PlayerData.position),
|
||
|
metadata = json.encode(PlayerData.metadata),
|
||
|
playtime = PlayerData.playtime
|
||
|
})
|
||
|
if GetResourceState('ps-inventory') ~= 'missing' then exports['ps-inventory']:SaveInventory(PlayerData, true) end
|
||
|
-- QBCore.ShowSuccess(GetCurrentResourceName(), PlayerData.name .. ' OFFLINE PLAYER SAVED!')
|
||
|
else
|
||
|
QBCore.ShowError(GetCurrentResourceName(), 'ERROR QBCORE.PLAYER.SAVEOFFLINE - PLAYERDATA IS EMPTY!')
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Delete character
|
||
|
|
||
|
local playertables = { -- Add tables as needed
|
||
|
{ table = 'players' },
|
||
|
{ table = 'apartments' },
|
||
|
{ table = 'bank_accounts' },
|
||
|
{ table = 'crypto_transactions' },
|
||
|
{ table = 'phone_invoices' },
|
||
|
{ table = 'phone_messages' },
|
||
|
{ table = 'playerskins' },
|
||
|
{ table = 'player_contacts' },
|
||
|
{ table = 'player_houses' },
|
||
|
{ table = 'player_mails' },
|
||
|
{ table = 'player_outfits' },
|
||
|
{ table = 'player_vehicles' }
|
||
|
}
|
||
|
|
||
|
function QBCore.Player.DeleteCharacter(source, citizenid)
|
||
|
local license = QBCore.Functions.GetIdentifier(source, 'license')
|
||
|
local result = MySQL.scalar.await('SELECT license FROM players where citizenid = ?', { citizenid })
|
||
|
if license == result then
|
||
|
local query = "DELETE FROM %s WHERE citizenid = ?"
|
||
|
local tableCount = #playertables
|
||
|
local queries = table.create(tableCount, 0)
|
||
|
|
||
|
for i = 1, tableCount do
|
||
|
local v = playertables[i]
|
||
|
queries[i] = {query = query:format(v.table), values = { citizenid }}
|
||
|
end
|
||
|
|
||
|
MySQL.transaction(queries, function(result2)
|
||
|
if result2 then
|
||
|
TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Character Deleted', 'red', string.format("** %s ** %s deleted ** %s **",GetPlayerName(source),license,citizenid))
|
||
|
end
|
||
|
end)
|
||
|
else
|
||
|
DropPlayer(source, Lang:t("info.exploit_dropped"))
|
||
|
riggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', string.format("%s Has Been Dropped For Character Deletion Exploit",GetPlayerName(source)))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.ForceDeleteCharacter(citizenid)
|
||
|
local result = MySQL.scalar.await('SELECT license FROM players where citizenid = ?', { citizenid })
|
||
|
if result then
|
||
|
local query = "DELETE FROM %s WHERE citizenid = ?"
|
||
|
local tableCount = #playertables
|
||
|
local queries = table.create(tableCount, 0)
|
||
|
local Player = QBCore.Functions.GetPlayerByCitizenId(citizenid)
|
||
|
|
||
|
if Player then
|
||
|
DropPlayer(Player.PlayerData.source, "An admin deleted the character which you are currently using")
|
||
|
end
|
||
|
for i = 1, tableCount do
|
||
|
local v = playertables[i]
|
||
|
queries[i] = {query = query:format(v.table), values = { citizenid }}
|
||
|
end
|
||
|
|
||
|
MySQL.transaction(queries, function(result2)
|
||
|
if result2 then
|
||
|
TriggerEvent('qb-log:server:CreateLog', 'joinleave', 'Character Force Deleted', 'red', string.format("Character ** %s ** got deleted",citizenid))
|
||
|
end
|
||
|
end)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Inventory Backwards Compatibility
|
||
|
|
||
|
function QBCore.Player.SaveInventory(source)
|
||
|
if GetResourceState('ps-inventory') == 'missing' then return end
|
||
|
exports['ps-inventory']:SaveInventory(source, false)
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.SaveOfflineInventory(PlayerData)
|
||
|
if GetResourceState('ps-inventory') == 'missing' then return end
|
||
|
exports['ps-inventory']:SaveInventory(PlayerData, true)
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.GetTotalWeight(items)
|
||
|
if GetResourceState('ps-inventory') == 'missing' then return end
|
||
|
return exports['ps-inventory']:GetTotalWeight(items)
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.GetSlotsByItem(items, itemName)
|
||
|
if GetResourceState('ps-inventory') == 'missing' then return end
|
||
|
return exports['ps-inventory']:GetSlotsByItem(items, itemName)
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.GetFirstSlotByItem(items, itemName)
|
||
|
if GetResourceState('ps-inventory') == 'missing' then return end
|
||
|
return exports['ps-inventory']:GetFirstSlotByItem(items, itemName)
|
||
|
end
|
||
|
|
||
|
-- Util Functions
|
||
|
|
||
|
function QBCore.Player.CreateCitizenId()
|
||
|
local UniqueFound = false
|
||
|
local CitizenId = nil
|
||
|
while not UniqueFound do
|
||
|
CitizenId = tostring(QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(5)):upper()
|
||
|
local result = MySQL.prepare.await('SELECT COUNT(*) as count FROM players WHERE citizenid = ?', { CitizenId })
|
||
|
if result == 0 then
|
||
|
UniqueFound = true
|
||
|
end
|
||
|
end
|
||
|
return CitizenId
|
||
|
end
|
||
|
|
||
|
function QBCore.Functions.CreateAccountNumber()
|
||
|
local UniqueFound = false
|
||
|
local AccountNumber = nil
|
||
|
while not UniqueFound do
|
||
|
AccountNumber = 'DK0' .. math.random(1, 9) .. 'HPReb' .. math.random(1111, 9999) .. math.random(1111, 9999) .. math.random(11, 99)
|
||
|
local query = '%' .. AccountNumber .. '%'
|
||
|
local result = MySQL.prepare.await('SELECT COUNT(*) as count FROM players WHERE charinfo LIKE ?', { query })
|
||
|
if result == 0 then
|
||
|
UniqueFound = true
|
||
|
end
|
||
|
end
|
||
|
return AccountNumber
|
||
|
end
|
||
|
|
||
|
function QBCore.Functions.CreatePhoneNumber()
|
||
|
local UniqueFound = false
|
||
|
local PhoneNumber = nil
|
||
|
while not UniqueFound do
|
||
|
PhoneNumber = "45"..math.random(10000000,99999999)
|
||
|
local query = '%' .. PhoneNumber .. '%'
|
||
|
local result = MySQL.prepare.await('SELECT COUNT(*) as count FROM players WHERE charinfo LIKE ?', { query })
|
||
|
if result == 0 then
|
||
|
UniqueFound = true
|
||
|
end
|
||
|
end
|
||
|
return PhoneNumber
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.CreateFingerId()
|
||
|
local UniqueFound = false
|
||
|
local FingerId = nil
|
||
|
while not UniqueFound do
|
||
|
FingerId = tostring(QBCore.Shared.RandomStr(2) .. QBCore.Shared.RandomInt(3) .. QBCore.Shared.RandomStr(1) .. QBCore.Shared.RandomInt(2) .. QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(4))
|
||
|
local query = '%' .. FingerId .. '%'
|
||
|
local result = MySQL.prepare.await('SELECT COUNT(*) as count FROM `players` WHERE `metadata` LIKE ?', { query })
|
||
|
if result == 0 then
|
||
|
UniqueFound = true
|
||
|
end
|
||
|
end
|
||
|
return FingerId
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.CreateWalletId()
|
||
|
local UniqueFound = false
|
||
|
local WalletId = nil
|
||
|
while not UniqueFound do
|
||
|
WalletId = 'HP-' .. math.random(11111111, 99999999)
|
||
|
local query = '%' .. WalletId .. '%'
|
||
|
local result = MySQL.prepare.await('SELECT COUNT(*) as count FROM players WHERE metadata LIKE ?', { query })
|
||
|
if result == 0 then
|
||
|
UniqueFound = true
|
||
|
end
|
||
|
end
|
||
|
return WalletId
|
||
|
end
|
||
|
|
||
|
function QBCore.Player.CreateSerialNumber()
|
||
|
local UniqueFound = false
|
||
|
local SerialNumber = nil
|
||
|
while not UniqueFound do
|
||
|
SerialNumber = math.random(11111111, 99999999)
|
||
|
local query = '%' .. SerialNumber .. '%'
|
||
|
local result = MySQL.prepare.await('SELECT COUNT(*) as count FROM players WHERE metadata LIKE ?', { query })
|
||
|
if result == 0 then
|
||
|
UniqueFound = true
|
||
|
end
|
||
|
end
|
||
|
return SerialNumber
|
||
|
end
|
||
|
|
||
|
PaycheckInterval() -- This starts the paycheck system
|