This commit is contained in:
Kakarot
2025-05-11 19:05:51 -05:00
parent fe3847d591
commit d88d91b9e3
8 changed files with 252 additions and 241 deletions
-2
View File
@@ -1,5 +1,3 @@
QBCore.Functions = {}
-- Callbacks
function QBCore.Functions.CreateClientCallback(name, cb)
+2 -1
View File
@@ -28,7 +28,8 @@ server_scripts {
'server/events.lua',
'server/commands.lua',
'server/exports.lua',
'server/debug.lua'
'server/debug.lua',
'server/editor.lua'
}
ui_page 'html/index.html'
-7
View File
@@ -1,10 +1,3 @@
QBCore.Commands = {}
QBCore.Commands.List = {}
QBCore.Commands.IgnoreList = { -- Ignore old perm levels while keeping backwards compatibility
['god'] = true, -- We don't need to create an ace because god is allowed all commands
['user'] = true -- We don't need to create an ace because builtin.everyone
}
CreateThread(function() -- Add ace to node for perm checking
local permissions = QBCore.Config.Server.Permissions
for i = 1, #permissions do
+14 -14
View File
@@ -1,12 +1,12 @@
RegisterCommand('qbeditor', function(source)
local files = {
config = LoadResourceFile('qb-core', 'shared/json/config.json'),
gangs = LoadResourceFile('qb-core', 'shared/json/gangs.json'),
items = LoadResourceFile('qb-core', 'shared/json/items.json'),
jobs = LoadResourceFile('qb-core', 'shared/json/jobs.json'),
playerdata = LoadResourceFile('qb-core', 'shared/json/player_defaults.json'),
vehicles = LoadResourceFile('qb-core', 'shared/json/vehicles.json'),
weapons = LoadResourceFile('qb-core', 'shared/json/weapons.json'),
config = LoadResourceFile('qb-core', 'shared/config.json'),
gangs = LoadResourceFile('qb-core', 'shared/gangs.json'),
items = LoadResourceFile('qb-core', 'shared/items.json'),
jobs = LoadResourceFile('qb-core', 'shared/jobs.json'),
playerdata = LoadResourceFile('qb-core', 'shared/player_defaults.json'),
vehicles = LoadResourceFile('qb-core', 'shared/vehicles.json'),
weapons = LoadResourceFile('qb-core', 'shared/weapons.json'),
}
local allData = {}
@@ -27,13 +27,13 @@ RegisterNetEvent('qb-core:server:configEditor', function(fileName, fileData)
if not Player then return end
if not QBCore.Functions.HasPermission('god') then
local filePaths = {
['config'] = 'shared/json/config.json',
['gangs'] = 'shared/json/gangs.json',
['items'] = 'shared/json/items.json',
['jobs'] = 'shared/json/jobs.json',
['playerdata'] = 'shared/json/player_defaults.json',
['vehicles'] = 'shared/json/vehicles.json',
['weapons'] = 'shared/json/weapons.json'
['config'] = 'shared/config.json',
['gangs'] = 'shared/gangs.json',
['items'] = 'shared/items.json',
['jobs'] = 'shared/jobs.json',
['playerdata'] = 'shared/player_defaults.json',
['vehicles'] = 'shared/vehicles.json',
['weapons'] = 'shared/weapons.json'
}
local filePath = filePaths[fileName]
if not filePath then
+98 -5
View File
@@ -1,8 +1,3 @@
QBCore.Functions = {}
QBCore.Player_Buckets = {}
QBCore.Entity_Buckets = {}
QBCore.UsableItems = {}
-- Getters
-- Get your player first and then trigger a function on them
-- ex: local player = QBCore.Functions.GetPlayer(source)
@@ -729,6 +724,104 @@ function QBCore.Functions.PrepForSQL(source, data, pattern)
return true
end
function QBCore.Functions.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.Functions.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.Functions.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.Functions.CreateFingerId()
end
function QBCore.Functions.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.Functions.CreateWalletId()
end
function QBCore.Functions.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.Functions.CreateSerialNumber()
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
for functionName, func in pairs(QBCore.Functions) do
if type(func) == 'function' then
exports(functionName, func)
+120 -198
View File
@@ -1,52 +1,43 @@
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
if not source or source == '' then
QBCore.ShowError(resourceName, 'ERROR QBCORE.PLAYER.LOGIN - NO SOURCE GIVEN!')
return false
end
if citizenid then
local license = QBCore.Functions.GetIdentifier(source, 'license')
local row = MySQL.prepare.await('SELECT * FROM players WHERE citizenid = ?', { citizenid })
if not row or license ~= row.license then
DropPlayer(source, Lang:t('info.exploit_dropped'))
TriggerEvent('qb-log:server:CreateLog', 'anticheat', 'Anti-Cheat', 'white', GetPlayerName(source) .. ' exploit attempt', false)
return false
end
row.money = json.decode(row.money) or {}
row.job = json.decode(row.job) or {}
row.gang = json.decode(row.gang) or {}
row.position = json.decode(row.position) or {}
row.metadata = json.decode(row.metadata) or {}
row.charinfo = json.decode(row.charinfo) or {}
QBCore.Player.CheckPlayerData(source, row)
else
QBCore.Player.CheckPlayerData(source, newData)
end
return true
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
if not citizenid then return nil end
local row = MySQL.prepare.await('SELECT * FROM players WHERE citizenid = ?', { citizenid })
if not row then return nil end
row.money = json.decode(row.money) or {}
row.job = json.decode(row.job) or {}
row.gang = json.decode(row.gang) or {}
row.position = json.decode(row.position) or {}
row.metadata = json.decode(row.metadata) or {}
row.charinfo = json.decode(row.charinfo) or {}
return QBCore.Player.CheckPlayerData(nil, row)
end
function QBCore.Player.GetPlayerByLicense(license)
@@ -77,15 +68,82 @@ function QBCore.Player.GetOfflinePlayerByLicense(license)
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)
-- JSON DEFAULTS
local DynamicDefaults = {
citizenid = QBCore.Functions.CreateCitizenId,
phone = QBCore.Functions.CreatePhoneNumber,
account = QBCore.Functions.CreateAccountNumber,
fingerprint = QBCore.Functions.CreateFingerId,
walletid = QBCore.Functions.CreateWalletId,
SerialNumber = QBCore.Functions.CreateSerialNumber,
bloodtype = function()
return QBCore.Shared.GetRandomElement(QBCore.Config.Player.Bloodtypes)
end
}
local function applyDynamicDefaults(tbl)
for k, v in pairs(tbl) do
if type(v) == 'table' then
applyDynamicDefaults(v)
elseif v == '__GENERATED__' then
local gen = DynamicDefaults[k]
if gen then
tbl[k] = gen()
end
end
end
end
local function mergeDefaults(dest, src)
for k, v in pairs(src) do
if type(v) == 'table' then
if type(dest[k]) ~= 'table' then
dest[k] = {}
end
mergeDefaults(dest[k], v)
else
playerData[key] = playerData[key] or value
if dest[k] == nil then
dest[k] = v
end
end
end
end
local function mergeWithGenerators(dest, src)
for k, v in pairs(src) do
if type(v) == 'table' then
if type(dest[k]) ~= 'table' then
dest[k] = {}
end
mergeWithGenerators(dest[k], v)
else
-- only act if dest is missing
if dest[k] == nil then
if v == '__GENERATED__' then
local gen = DynamicDefaults[k]
if gen then
dest[k] = gen()
else
-- fallback to literal, or warn
dest[k] = v
print(("Warning: no generator for '%s'"):format(k))
end
else
-- static default
dest[k] = v
end
end
end
end
end
local function pruneExtras(dest, src)
for k, v in pairs(dest) do
if src[k] == nil then
dest[k] = nil
elseif type(v) == 'table' and type(src[k]) == 'table' then
pruneExtras(v, src[k])
end
end
end
@@ -94,58 +152,22 @@ function QBCore.Player.CheckPlayerData(source, PlayerData)
PlayerData = PlayerData or {}
local Offline = not source
local defaults = json.decode(LoadResourceFile(resourceName, 'shared/player_defaults.json'))
if not PlayerData.citizenid then
applyDynamicDefaults(defaults)
mergeDefaults(PlayerData, defaults)
else
pruneExtras(PlayerData, defaults)
mergeWithGenerators(PlayerData, defaults)
end
if source then
PlayerData.source = source
PlayerData.license = PlayerData.license or QBCore.Functions.GetIdentifier(source, 'license')
PlayerData.name = GetPlayerName(source)
PlayerData.source = source
PlayerData.license = PlayerData.license or GetPlayerIdentifierByType(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
@@ -424,62 +446,6 @@ function QBCore.Player.CreatePlayer(PlayerData, Offline)
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)
@@ -619,48 +585,4 @@ function QBCore.Player.GetFirstSlotByItem(items, itemName)
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
+4
View File
@@ -21,6 +21,10 @@ function QBCore.Shared.RandomInt(length)
return QBCore.Shared.RandomInt(length - 1) .. NumberCharset[math.random(1, #NumberCharset)]
end
function QBCore.Shared.GetRandomElement(tbl)
return tbl[math.random(1, #tbl)]
end
function QBCore.Shared.SplitStr(str, delimiter)
local result = {}
local from = 1
+14 -14
View File
@@ -1,5 +1,5 @@
{
"citizenid": null,
"citizenid": "__GENERATED__",
"cid": 1,
"money": {
"cash": 500,
@@ -44,24 +44,24 @@
"hunger": 100,
"thirst": 100,
"stress": 0,
"armor": 0,
"isdead": false,
"inlaststand": false,
"armor": 0,
"ishandcuffed": false,
"tracker": false,
"injail": 0,
"jailitems": [],
"jailitems": {},
"status": {},
"phone": {},
"rep": {},
"currentapartment": null,
"currentapartment": false,
"callsign": "NO CALLSIGN",
"bloodtype": null,
"fingerprint": null,
"walletid": null,
"bloodtype": "__GENERATED__",
"fingerprint": "__GENERATED__",
"walletid": "__GENERATED__",
"criminalrecord": {
"hasRecord": false,
"date": null
"date": "00-00-0000"
},
"licences": {
"driver": true,
@@ -69,15 +69,15 @@
"weapon": false
},
"inside": {
"house": null,
"house": {},
"apartment": {
"apartmentType": null,
"apartmentId": null
"apartmentType": {},
"apartmentId": {}
}
},
"phonedata": {
"SerialNumber": null,
"InstalledApps": []
"SerialNumber": "__GENERATED__",
"InstalledApps": {}
}
},
"position": {
@@ -86,5 +86,5 @@
"z": 1000,
"h": 1000
},
"items": []
"items": {}
}