Multicharacter (Optional)
Quality of life scripts for your FiveM server. Performance optimized resources that install in minutes, with free updates and support that actually answers.
Cad Characters
Works out of the box ( just start
cad-queuebeforecad-characters)
QB Multicharacter
Replace below function in
qb-multicharacter/server/main.lua
QBCore.Functions.CreateCallback('qb-multicharacter:server:setupCharacters', function(source, cb)
local license = QBCore.Functions.GetIdentifier(source, 'license')
local discord = GetPlayerIdentifierByType(source, 'discord')
if not discord then return cb({}) end
local data = exports['cad-queue']:GetPrio(discord)
local jobName = (data and data.name) or 'all'
local plyChars = {}
local result = MySQL.query.await('SELECT * FROM players WHERE license = ?', { license })
if result then
for i = 1, (#result), 1 do
result[i].charinfo = json.decode(result[i].charinfo)
result[i].money = json.decode(result[i].money)
result[i].job = json.decode(result[i].job)
local job = result[i].job
if (jobName == 'all') or (job?.name ~= jobName) then
plyChars[#plyChars + 1] = result[i]
end
end
end
cb(plyChars)
end)Snappy Characters - ESX/QB/QBOX
ESX: Replace below function in
snappy-characters/bridge/framework/server/esx.lua
function GetAllCharacters(src)
local identifier = ESX.GetIdentifier(src)
local discord = GetPlayerIdentifierByType(src, 'discord') -- add the identifier which you are using in `Config.Identifier` in cad-queue
if not discord then return {} end
local data = (GetResourceState('cad-queue') == 'started') and exports['cad-queue']:GetPrio(discord)
local jobName = (data and data.name) or 'all'
local characters = {}
local result = MySQL.query.await("SELECT identifier, job, firstname, lastname, dateofbirth, sex, skin, disabled FROM users WHERE identifier LIKE ?", { ("%s%%:%s"):format('char', identifier) })
if result and result[1] then
for _, v in pairs(result) do
local job = v.job
local gender = v.sex == 'm' and 'Male' or 'Female'
characters[#characters+1] = {
charInfo = {
firstName = v.firstname,
lastName = v.lastname,
dateOfBirth = v.dateofbirth,
nationality = v.nationality,
gender = gender,
citizenId = v.identifier
},
skinData = GetSkinData(v.identifier),
isLocked = (jobName ~= 'all' and job ~= jobName) or (v.disabled ~= 0),
businessName = job
}
end
end
return characters
endQB: Replace below function in
snappy-characters/bridge/framework/server/qb.lua
function GetAllCharacters(src)
local license = GetPlayerIdentifierByType(src, 'license')
local discord = GetPlayerIdentifierByType(src, 'discord') -- add the identifier which you are using in `Config.Identifier` in cad-queue
if not discord then return {} end
local data = (GetResourceState('cad-queue') == 'started') and exports['cad-queue']:GetPrio(discord)
local jobName = (data and data.name) or 'all'
local characters = {}
local result = MySQL.query.await('SELECT citizenid, charinfo, job FROM players WHERE license = ?', { license })
if result and result[1] then
for _, v in pairs(result) do
local jobInfo = (v.job and json.decode(v.job)) or { name = 'none' }
local charInfo = json.decode(v.charinfo)
local gender = (charInfo.gender ~= 1 and 'Female') or 'Male'
characters[#characters+1] = {
charInfo = {
firstName = charInfo.firstname,
lastName = charInfo.lastname,
dateOfBirth = charInfo.birthdate,
nationality = charInfo.nationality,
gender = gender,
citizenId = v.citizenid
},
skinData = GetSkinData(v.citizenid),
isLocked = (jobName ~= 'all' and jobInfo.name ~= jobName),
businessName = jobInfo.label
}
end
end
return characters
endQBOX: Replace below function in
snappy-characters/bridge/framework/server/qbox.lua
function GetAllCharacters(src)
local license, license2 = GetPlayerIdentifierByType(src, 'license2'), GetPlayerIdentifierByType(src, 'license')
local discord = GetPlayerIdentifierByType(src, 'discord') -- add the identifier which you are using in `Config.Identifier` in cad-queue
if not discord then return {} end
local data = (GetResourceState('cad-queue') == 'started') and exports['cad-queue']:GetPrio(discord)
local jobName = (data and data.name) or 'all'
local characters = {}
local result = MySQL.query.await('SELECT citizenid, charinfo, job FROM players WHERE license IN (?, ?)', { license, license2 })
if result and result[1] then
for _, v in pairs(result) do
local jobInfo = (v.job and json.decode(v.job)) or { name = 'none' }
local charInfo = json.decode(v.charinfo)
local gender = (charInfo.gender ~= 1 and 'Female') or 'Male'
characters[#characters+1] = {
charInfo = {
firstName = charInfo.firstname,
lastName = charInfo.lastname,
dateOfBirth = charInfo.birthdate,
nationality = charInfo.nationality,
gender = gender,
citizenId = v.citizenid
},
skinData = GetSkinData(v.citizenid),
isLocked = (jobName ~= 'all' and jobInfo.name ~= jobName),
businessName = jobInfo.label
}
end
end
return characters
endForge Multicharacter (ESX/QB/QBOX)
ESX: Replace below function in
forge-multicharacter/server/frameworks/ESX.lua
function ST.ESX:LoadCharacters(source, cb)
local discord = GetPlayerIdentifierByType(source, 'discord')
local data = (GetResourceState('cad-queue') == 'started') and exports['cad-queue']:GetPrio(discord)
local jobName = (data and data.name) or 'all'
local roles = {}
if Config.bot.use then
roles = GetDiscordRoles(source)
if roles == nil then
DropPlayer(source, "Missing discord Identifier")
end
end
local characters = {}
local queryIdentifier = FormatIdentifier(IdentifierFormatTable(source, "%%"))
local query = "SELECT * FROM `users` WHERE `identifier` LIKE '" .. tostring(queryIdentifier) .. "'"
MySQL.Async.fetchAll(query, {}, function(results)
local addSlots = 0
if results then
if #results > 0 then
for _, char in ipairs(results) do
debug("char" .. tostring(_) .. ": " .. json.encode(char))
local location = json.decode(char.position)
local accounts = json.decode(char.accounts)
local sex = 0
-- cache update!
if char.sex == "m" then
sex = 1
end
local jobLabel = self:GetJobLabel(char.job)
if (jobName == 'all') or (char.job == jobName) then
table.insert(characters, {
identifier = char.identifier,
name = char.firstname .. " " .. char.lastname,
lastSpawnPoint = location,
cash = accounts.money,
bank = accounts.bank,
gender = sex,
job = jobLabel,
skin = json.decode(char.skin),
grade = char.job_grade,
playTime = FormatPlayTime(char.playtime),
nationality = char.nationality
})
end
end
end
end
addSlots = (MySQL.query.await('SELECT `slots` FROM `additionalSlots` WHERE `identifier`=?', {
GetPlayerIdentifierByType(source, 'license')
})[1] or {}).slots or 0
cb(source, characters, roles, addSlots)
end)
endQB/QBOX: Replace below function in
forge-multicharacter/server/frameworks/QB.lua
function ST.QB:LoadCharacters(source, cb)
local license = self:GetIdentifier(source)
local discord = GetPlayerIdentifierByType(source, 'discord')
local data = (GetResourceState('cad-queue') == 'started') and exports['cad-queue']:GetPrio(discord)
local jobName = (data and data.name) or 'all'
-- QBOX/QBCore compatibility check
if QBCore and QBCore.Players then
QBCore.Players[license] = true
else
debug("Warning: QBCore.Players is nil - possible QBOX compatibility issue")
end
local roles = {}
if Config.bot.use then
roles = GetDiscordRoles(source)
if roles == nil then
DropPlayer(source, "Missing discord Identifier")
end
end
local characters = {}
local query =
"SELECT * FROM `players`, `playerskins` WHERE `license` LIKE @license AND `players`.`citizenid`=`playerskins`.`citizenid`"
MySQL.Async.fetchAll(query, {
['@license'] = '%' .. license .. '%'
}, function(results)
local addSlots = 0
if results then
if #results > 0 then
for _, char in ipairs(results) do
local location = json.decode(char.position)
local accounts = json.decode(char.money)
local job = json.decode(char.job)
local charinfo = json.decode(char.charinfo)
local sex = charinfo.gender == 0 and 1 or 0
local shareChar = {
identifier = char.citizenid,
name = charinfo.firstname .. " " .. charinfo.lastname,
lastSpawnPoint = location,
cash = accounts.cash,
bank = accounts.bank,
gender = sex,
job = job.label,
skin = json.decode(char.skin),
grade = job.grade.level,
playTime = FormatPlayTime(char.playtime),
nationality = charinfo.nationality
}
shareChar.skin.model = char.model
if (jobName == 'all') or (job?.name == jobName) then
table.insert(characters, shareChar)
end
end
addSlots = (MySQL.query.await('SELECT `slots` FROM `additionalSlots` WHERE `identifier`=?', {
license
})[1] or {}).slots or 0
end
end
cb(source, characters, roles, addSlots)
end)
endZSX Multicharacter
Replace below function in
ZSX_Multicharacter/server/functions/characters.lua
Characters.Get = function(src)
local identifier = Characters.GetIdentifier(src)
local PREFIX = Config.Prefix or 'char'
local query
if FrameworkSelected == 'ESX' then
query = "SELECT * FROM `users` WHERE identifier LIKE '%"..identifier.."'"
elseif FrameworkSelected == 'QBCore' then
query = "SELECT * FROM `players` WHERE `license` = '"..identifier.."'"
end
local discord = GetPlayerIdentifierByType(src, 'discord')
local queue_data = (GetResourceState('cad-queue') == 'started') and exports['cad-queue']:GetPrio(discord) -- added
local queue_job_name = (queue_data and queue_data.name) or 'all' -- added
local response = MySQL.query.await(query)
local characters = {}
local startTime = GetGameTimer()
debugPrint("[^2CHARACTERS.GET^7] Gathering NUI Data [/]")
for k,v in ipairs(response) do
if FrameworkSelected == 'ESX' then
local checkMulticharacterMode = string.sub(v.identifier, 1, string.len(PREFIX)) == PREFIX
assert(checkMulticharacterMode, '^5Your es_extended does not use Config.Multichar. Player identifier: ^3'..v.identifier..'\n^2Please refer to the docs: https://zsx-development.gitbook.io/docs/multicharacter/common-issues/esx\n\n')
end
local id = FrameworkSelected == 'ESX' and tonumber(string.sub(v.identifier, #PREFIX+1, string.find(v.identifier, ':')-1)) or FrameworkSelected == 'QBCore' and tonumber(v.cid)
if FrameworkSelected == 'ESX' then
local job = v.job
local grade = v.job_grade
if GetResourceState('origen_masterjob') == 'started' then
if not Characters.Jobs[job] then
local business = exports["origen_masterjob"]:GetBusiness(job)
if business then
local gradeObject = business.Functions.GetGrade(grade)
if gradeObject then
if not Characters.JobGrades[job] then
Characters.JobGrades[job] = {}
end
Characters.Jobs[job] = business.Data.label
Characters.JobGrades[job][tonumber(grade)] = gradeObject.label or ''
debugPrint('[ORIGEN_MASTERJOB] Applied changes to the Jobs and JobGrades objects. Job: ['..job..']')
else
debugPrint('[ORIGEN_MASTERJOB] No business grade has been found for: ['..job..']')
end
else
debugPrint('[ORIGEN_MASTERJOB] No business has been found for: ['..job..']')
end
end
end
assert(v.position ~= nil and v.position ~= '', '^5Failed to get player position. Player identifier: ^3'..v.identifier..'\n^2Please refer to the docs: https://zsx-development.gitbook.io/docs/multicharacter/common-issues/esx\n\n')
v.job = {
name = job,
label = Characters.Jobs[job] or 'Unemployed',
grade_label = Characters.JobGrades[job] and Characters.JobGrades[job][tonumber(grade)] or ''
}
if (queue_job_name == 'all' or (v.job and v.job.name == queue_job_name)) then -- added
local coords = json.decode(v.position)
v.position = coords
v.id = id
v.skin = Characters.ConvertSkin(v.identifier)
table.insert(characters, v)
end -- added
elseif FrameworkSelected == 'QBCore' then
local skinResponse = MySQL.query.await("SELECT * FROM `playerskins` WHERE `citizenid` = '"..v.citizenid.."' AND active = 1")
local responseCharData = json.decode(v.charinfo)
local responseJobData = json.decode(v.job)
if (queue_job_name == 'all' or (responseJobData and responseJobData.name == queue_job_name)) then -- added
local characterData = {
sex = tonumber(response[1].gender) == 0 and 'm' or 'f',
skin = Characters.ConvertSkin(v.citizenid),
model = skinResponse[1] and tonumber(skinResponse[1].model) or false,
position = json.decode(v.position),
firstname = responseCharData.firstname,
lastname = responseCharData.lastname,
job = {
name = responseJobData.name,
label = responseJobData.label,
grade_label = responseJobData.grade and responseJobData.grade.name or ''
},
id = id
}
table.insert(characters, characterData)
end -- added
end
end
if Config.DebugTimers then
print('[CHARACTERS.GET] Took: ^3'..(GetGameTimer() - startTime)..'ms^7')
end
debugPrint("[^2CHARACTERS.GET^7] Gathered NUI Data")
return characters
end