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! local resourceName = GetCurrentResourceName() function QBCore.Player.Login(source, citizenid, newData) if source and source ~= '' then if citizenid then local license = QBCore.Functions.GetIdentifier(source, 'license') local PlayerData = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenid }) if PlayerData and license == PlayerData.license then PlayerData.money = json.decode(PlayerData.money) PlayerData.job = json.decode(PlayerData.job) PlayerData.gang = json.decode(PlayerData.gang) PlayerData.position = json.decode(PlayerData.position) PlayerData.metadata = json.decode(PlayerData.metadata) PlayerData.charinfo = json.decode(PlayerData.charinfo) QBCore.Player.CheckPlayerData(source, PlayerData) else DropPlayer(source, Lang:t('info.exploit_dropped')) TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Joining Exploit', false) end else QBCore.Player.CheckPlayerData(source, newData) end return true else QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.LOGIN - NO SOURCE GIVEN!') return false end end function QBCore.Player.GetOfflinePlayer(citizenid) if citizenid then local PlayerData = MySQL.prepare.await('SELECT * FROM players where citizenid = ?', { citizenid }) if PlayerData then PlayerData.money = json.decode(PlayerData.money) PlayerData.job = json.decode(PlayerData.job) PlayerData.gang = json.decode(PlayerData.gang) PlayerData.position = json.decode(PlayerData.position) PlayerData.metadata = json.decode(PlayerData.metadata) PlayerData.charinfo = json.decode(PlayerData.charinfo) return QBCore.Player.CheckPlayerData(nil, PlayerData) end end return nil end function QBCore.Player.GetPlayerByLicense(license) if license then local source = QBCore.Functions.GetSource(license) if source > 0 then return QBCore.Players[source] else return QBCore.Player.GetOfflinePlayerByLicense(license) end end return nil end function QBCore.Player.GetOfflinePlayerByLicense(license) if license then local PlayerData = MySQL.prepare.await('SELECT * FROM players where license = ?', { license }) if PlayerData then PlayerData.money = json.decode(PlayerData.money) PlayerData.job = json.decode(PlayerData.job) PlayerData.gang = json.decode(PlayerData.gang) PlayerData.position = json.decode(PlayerData.position) PlayerData.metadata = json.decode(PlayerData.metadata) PlayerData.charinfo = json.decode(PlayerData.charinfo) return QBCore.Player.CheckPlayerData(nil, PlayerData) end end return nil end local function applyDefaults(playerData, defaults) for key, value in pairs(defaults) do if type(value) == 'function' then playerData[key] = playerData[key] or value() elseif type(value) == 'table' then playerData[key] = playerData[key] or {} applyDefaults(playerData[key], value) else playerData[key] = playerData[key] or value end end end function QBCore.Player.CheckPlayerData(source, PlayerData) PlayerData = PlayerData or {} local Offline = not source if source then PlayerData.source = source PlayerData.license = PlayerData.license or QBCore.Functions.GetIdentifier(source, 'license') PlayerData.name = GetPlayerName(source) end local validatedJob = false if PlayerData.job and PlayerData.job.name ~= nil and PlayerData.job.grade and PlayerData.job.grade.level ~= nil then local jobInfo = QBCore.Shared.Jobs[PlayerData.job.name] if jobInfo then local jobGradeInfo = jobInfo.grades[tostring(PlayerData.job.grade.level)] if jobGradeInfo then PlayerData.job.label = jobInfo.label PlayerData.job.grade.name = jobGradeInfo.name PlayerData.job.payment = jobGradeInfo.payment PlayerData.job.grade.isboss = jobGradeInfo.isboss or false PlayerData.job.isboss = jobGradeInfo.isboss or false validatedJob = true end end end if validatedJob == false then -- set to nil, as the default job (unemployed) will be added by `applyDefaults` PlayerData.job = nil end local validatedGang = false if PlayerData.gang and PlayerData.gang.name ~= nil and PlayerData.gang.grade and PlayerData.gang.grade.level ~= nil then local gangInfo = QBCore.Shared.Gangs[PlayerData.gang.name] if gangInfo then local gangGradeInfo = gangInfo.grades[tostring(PlayerData.gang.grade.level)] if gangGradeInfo then PlayerData.gang.label = gangInfo.label PlayerData.gang.grade.name = gangGradeInfo.name PlayerData.gang.payment = gangGradeInfo.payment PlayerData.gang.grade.isboss = gangGradeInfo.isboss or false PlayerData.gang.isboss = gangGradeInfo.isboss or false validatedGang = true end end end if validatedGang == false then -- set to nil, as the default gang (unemployed) will be added by `applyDefaults` PlayerData.gang = nil end applyDefaults(PlayerData, QBCore.Config.Player.PlayerDefaults) if GetResourceState('qb-inventory') ~= 'missing' then PlayerData.items = exports['qb-inventory']:LoadInventory(PlayerData.source, PlayerData.citizenid) end 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 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 = grade or '0' if not QBCore.Shared.Jobs[job] then return false end self.PlayerData.job = { name = job, label = QBCore.Shared.Jobs[job].label, onduty = QBCore.Shared.Jobs[job].defaultDuty, type = QBCore.Shared.Jobs[job].type or 'none', grade = { name = 'No Grades', level = 0, payment = 30, isboss = false } } local gradeKey = tostring(grade) local jobGradeInfo = QBCore.Shared.Jobs[job].grades[gradeKey] if jobGradeInfo then self.PlayerData.job.grade.name = jobGradeInfo.name self.PlayerData.job.grade.level = tonumber(gradeKey) self.PlayerData.job.grade.payment = jobGradeInfo.payment self.PlayerData.job.grade.isboss = jobGradeInfo.isboss or false self.PlayerData.job.isboss = jobGradeInfo.isboss or 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.SetGang(gang, grade) gang = gang:lower() grade = grade or '0' if not QBCore.Shared.Gangs[gang] then return false end self.PlayerData.gang = { name = gang, label = QBCore.Shared.Gangs[gang].label, grade = { name = 'No Grades', level = 0, isboss = false } } local gradeKey = tostring(grade) local gangGradeInfo = QBCore.Shared.Gangs[gang].grades[gradeKey] if gangGradeInfo then self.PlayerData.gang.grade.name = gangGradeInfo.name self.PlayerData.gang.grade.level = tonumber(gradeKey) self.PlayerData.gang.grade.isboss = gangGradeInfo.isboss or false self.PlayerData.gang.isboss = gangGradeInfo.isboss or 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.Notify(text, type, length) TriggerClientEvent('QBCore:Notify', self.PlayerData.source, text, type, length) end function self.Functions.HasItem(items, amount) return QBCore.Functions.HasItem(self.PlayerData.source, items, amount) end function self.Functions.GetName() local charinfo = self.PlayerData.charinfo return charinfo.firstname .. ' ' .. charinfo.lastname end function self.Functions.SetJobDuty(onDuty) self.PlayerData.job.onduty = not not onDuty 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.AddRep(rep, amount) if not rep or not amount then return end local addAmount = tonumber(amount) local currentRep = self.PlayerData.metadata['rep'][rep] or 0 self.PlayerData.metadata['rep'][rep] = currentRep + addAmount self.Functions.UpdatePlayerData() end function self.Functions.RemoveRep(rep, amount) if not rep or not amount then return end local removeAmount = tonumber(amount) local currentRep = self.PlayerData.metadata['rep'][rep] or 0 if currentRep - removeAmount < 0 then self.PlayerData.metadata['rep'][rep] = 0 else self.PlayerData.metadata['rep'][rep] = currentRep - removeAmount end self.Functions.UpdatePlayerData() end function self.Functions.GetRep(rep) if not rep then return end return self.PlayerData.metadata['rep'][rep] or 0 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 local isMoneyItem, moneyItemName = exports["tgiann-inventory"]:IsMoneyItem(moneytype) if isMoneyItem then exports["tgiann-inventory"]:SetItem(self.PlayerData.source, moneyItemName, self.PlayerData.money[moneytype]) end if not self.Offline then self.Functions.UpdatePlayerData() if amount > 100000 then TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') added, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason, true) else TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'AddMoney', 'lightgreen', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') added, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason) end 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 local isMoneyItem, moneyItemName = exports["tgiann-inventory"]:IsMoneyItem(moneytype) if isMoneyItem then exports["tgiann-inventory"]:SetItem(self.PlayerData.source, moneyItemName, self.PlayerData.money[moneytype]) end if not self.Offline then self.Functions.UpdatePlayerData() if amount > 100000 then TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') removed, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason, true) else TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'RemoveMoney', 'red', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') removed, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. reason) end 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, forInventory) 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 forInventory then local isMoneyItem, moneyItemName = exports["tgiann-inventory"]:IsMoneyItem(moneytype) if isMoneyItem then exports["tgiann-inventory"]:SetItem(self.PlayerData.source, moneyItemName, amount) end end if not self.Offline then self.Functions.UpdatePlayerData() TriggerEvent('qb-log:server:CreateLog', 'playermoney', 'SetMoney', 'green', '**' .. GetPlayerName(self.PlayerData.source) .. ' (citizenid: ' .. self.PlayerData.citizenid .. ' | id: ' .. self.PlayerData.source .. ')** $' .. amount .. ' (' .. moneytype .. ') set, new ' .. moneytype .. ' balance: ' .. self.PlayerData.money[moneytype] .. ' reason: ' .. 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.GetMoney(moneytype) if not moneytype then return false end moneytype = moneytype:lower() return self.PlayerData.money[moneytype] 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 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) 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) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', { 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) }) if GetResourceState('qb-inventory') ~= 'missing' then exports['qb-inventory']:SaveInventory(source) end QBCore.ShowSuccess(resourceName, PlayerData.name .. ' PLAYER SAVED!') else QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.SAVE - PLAYERDATA IS EMPTY!') end end function QBCore.Player.SaveOffline(PlayerData) if PlayerData then MySQL.insert('INSERT INTO players (citizenid, cid, license, name, money, charinfo, job, gang, position, metadata) VALUES (:citizenid, :cid, :license, :name, :money, :charinfo, :job, :gang, :position, :metadata) ON DUPLICATE KEY UPDATE cid = :cid, name = :name, money = :money, charinfo = :charinfo, job = :job, gang = :gang, position = :position, metadata = :metadata', { 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) }) if GetResourceState('qb-inventory') ~= 'missing' then exports['qb-inventory']:SaveInventory(PlayerData, true) end QBCore.ShowSuccess(resourceName, PlayerData.name .. ' OFFLINE PLAYER SAVED!') else QBCore.ShowError(resourceName, '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', '**' .. GetPlayerName(source) .. '** ' .. license .. ' deleted **' .. citizenid .. '**..') end end) else DropPlayer(source, Lang:t('info.exploit_dropped')) TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' Has Been Dropped For Character Deletion Exploit', true) 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', 'Character **' .. citizenid .. '** got deleted') end end) end end -- Inventory Backwards Compatibility function QBCore.Player.SaveInventory(source) if GetResourceState('qb-inventory') == 'missing' then return end exports['qb-inventory']:SaveInventory(source, false) end function QBCore.Player.SaveOfflineInventory(PlayerData) if GetResourceState('qb-inventory') == 'missing' then return end exports['qb-inventory']:SaveInventory(PlayerData, true) end function QBCore.Player.GetTotalWeight(items) if GetResourceState('qb-inventory') == 'missing' then return end return exports['qb-inventory']:GetTotalWeight(items) end function QBCore.Player.GetSlotsByItem(items, itemName) if GetResourceState('qb-inventory') == 'missing' then return end return exports['qb-inventory']:GetSlotsByItem(items, itemName) end function QBCore.Player.GetFirstSlotByItem(items, itemName) if GetResourceState('qb-inventory') == 'missing' then return end return exports['qb-inventory']:GetFirstSlotByItem(items, itemName) end -- Util Functions function QBCore.Player.CreateCitizenId() local CitizenId = tostring(QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(5)):upper() local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE citizenid = ?) AS uniqueCheck', { CitizenId }) if result == 0 then return CitizenId end return QBCore.Player.CreateCitizenId() end function QBCore.Functions.CreateAccountNumber() local AccountNumber = 'US0' .. math.random(1, 9) .. 'QBCore' .. math.random(1111, 9999) .. math.random(1111, 9999) .. math.random(11, 99) local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.account")) = ?) AS uniqueCheck', { AccountNumber }) if result == 0 then return AccountNumber end return QBCore.Functions.CreateAccountNumber() end function QBCore.Functions.CreatePhoneNumber() local PhoneNumber = math.random(100, 999) .. math.random(1000000, 9999999) local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(charinfo, "$.phone")) = ?) AS uniqueCheck', { PhoneNumber }) if result == 0 then return PhoneNumber end return QBCore.Functions.CreatePhoneNumber() end function QBCore.Player.CreateFingerId() local 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 result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.fingerprint")) = ?) AS uniqueCheck', { FingerId }) if result == 0 then return FingerId end return QBCore.Player.CreateFingerId() end function QBCore.Player.CreateWalletId() local WalletId = 'QB-' .. math.random(11111111, 99999999) local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.walletid")) = ?) AS uniqueCheck', { WalletId }) if result == 0 then return WalletId end return QBCore.Player.CreateWalletId() end function QBCore.Player.CreateSerialNumber() local SerialNumber = math.random(11111111, 99999999) local result = MySQL.prepare.await('SELECT EXISTS(SELECT 1 FROM players WHERE JSON_UNQUOTE(JSON_EXTRACT(metadata, "$.phonedata.SerialNumber")) = ?) AS uniqueCheck', { SerialNumber }) if result == 0 then return SerialNumber end return QBCore.Player.CreateSerialNumber() end PaycheckInterval() -- This starts the paycheck system