Files
community_bridge/modules/clothing/illenium-appearance/server.lua
T
MrNewb c71a261115 chore(intellisense): improve parameter annotations and add missing comments
Touched up incorrect or missing Intellisense parameter annotations across several functions. Added some missing descriptions to improve editor support. Still more to do, but this is a small step forward for overall developer workflow. Also small fix in picklexp
2025-06-07 22:50:13 -04:00

253 lines
10 KiB
Lua

---@diagnostic disable: duplicate-set-field
if GetResourceState('illenium-appearance') == 'missing' then return end
Clothing = Clothing or {}
Clothing.Players = {}
Callback = Callback or Require("lib/utility/shared/callbacks.lua")
Table = Table or Require('lib/utility/shared/tables.lua')
--- Internal function to get the full appearance data including skin, model, and converted format
---@param src number The server ID of the player
---@return table|nil The player's full appearance data or nil if not found
function Clothing.GetFullAppearanceData(src)
src = src and tonumber(src)
assert(src, "src is nil")
local citId = Bridge.Framework.GetPlayerIdentifier(src)
if not citId then return end
if Clothing.Players[citId] then return Clothing.Players[citId] end
local result = MySQL.query.await('SELECT * FROM playerskins WHERE citizenid = ? AND active = ?', { citId, 1 })
if result[1] == nil then return end
local model = result[1].model
local skinData = json.decode(result[1].skin)
local converted = skinData
-- Store complete data in the cache
Clothing.Players[citId] = {
model = model,
skin = skinData,
converted = converted
}
return Clothing.Players[citId]
end
--- Retrieves a player's converted appearance data for easy use across modules
---@param src number The server ID of the player
---@param fullData boolean Optional - If true, returns the full data object including skin and model
---@return table|nil The player's converted appearance data or full appearance data if fullData=true
function Clothing.GetAppearance(src, fullData)
if fullData then
return Clothing.GetFullAppearanceData(src)
end
local completeData = Clothing.GetFullAppearanceData(src)
if not completeData then return nil end
return completeData.converted
end
--- Sets a player's appearance based on the provided data
---@param src number The server ID of the player
---@param data table The appearance data to apply
---@param updateBackup boolean Whether to update the backup appearance data
---@return table|nil The updated player appearance data or nil if failed
function Clothing.SetAppearance(src, data, updateBackup, save)
src = src and tonumber(src)
assert(src, "src is nil")
local citId = Bridge.Framework.GetPlayerIdentifier(src)
if not citId then return end
local model = GetEntityModel(GetPlayerPed(src))
if not model then return end
local converted = data
-- Get full appearance data
local currentClothing = Clothing.GetFullAppearanceData(src)
if not currentClothing then return end
local currentSkin = currentClothing.skin
for k, v in pairs(converted) do
currentSkin[k] = v
end
if not Clothing.Players[citId].backup or updateBackup then
Clothing.Players[citId].backup = currentClothing.converted
end
Clothing.Players[citId] = Clothing.Players[citId] or {}
Clothing.Players[citId].skin = currentSkin
Clothing.Players[citId].model = model
Clothing.Players[citId].converted = converted
if save then
MySQL.update.await('UPDATE playerskins SET skin = ? WHERE citizenid = ? AND active = ?', {
json.encode(currentSkin),
citId,
1
})
end
TriggerClientEvent('community_bridge:client:SetAppearance', src, Clothing.Players[citId].converted)
return Clothing.Players[citId]
end
--- Sets a player's appearance based on gender-specific data
---@param src number The server ID of the player
---@param data table Table containing separate appearance data for male and female characters
---@return table|nil Appearance updated player appearance data or nil if failed
function Clothing.SetAppearanceExt(src, data)
local tbl = Clothing.IsMale(src) and data.male or data.female
Clothing.SetAppearance(src, tbl)
end
--- Reverts a player's appearance to their backup appearance
---@param src number The server ID of the player
---@return boolean|nil Returns true if successful or nil if failed
function Clothing.Revert(src)
src = src and tonumber(src)
assert(src, "src is nil")
local currentClothing = Clothing.GetFullAppearanceData(src)
if not currentClothing then return end
local backup = currentClothing.backup
print(src, backup)
if not backup then return end
return Clothing.SetAppearance(src, backup)
end
function Clothing.OpenMenu(src)
src = src and tonumber(src)
assert(src, "src is nil")
TriggerClientEvent('qb-clothing:client:openMenu', src)
end
--- Event handler for when a player loads into the server
--- Caches the player's appearance data
AddEventHandler('community_bridge:Server:OnPlayerLoaded', function(src)
src = src and tonumber(src)
assert(src, "src is nil")
-- Use GetFullAppearanceData to cache the complete appearance
Clothing.GetFullAppearanceData(src)
end)
--- Event handler for when a player disconnects from the server
--- Removes the player's appearance data from the cache
AddEventHandler('community_bridge:Server:OnPlayerUnload', function(src)
src = src and tonumber(src)
assert(src, "src is nil")
local strSrc = tostring(src)
Clothing.Players[strSrc] = nil
end)
--- Event handler for when the resource starts
--- Caches appearance data for all currently connected players
AddEventHandler('onResourceStart', function(resource)
if resource ~= GetCurrentResourceName() then return end
for _, playerId in ipairs(GetPlayers()) do
local src = tonumber(playerId)
local strSrc = tostring(src)
if not Clothing.Players[strSrc] then
Clothing.Players[strSrc] = Clothing.GetAppearance(src)
end
end
end)
--- Callback handler for retrieving a player's appearance data
Callback.Register('community_bridge:cb:GetAppearance', function(source)
local src = source
return Clothing.GetAppearance(src)
end)
-- The below Should go to unit test
RegisterCommand('clothing:debug', function(source, args, rawCommand)
local src = source
Clothing.SetAppearance(src, {
components ={
{component_id = 0, drawable = math.random(0, 50), texture = 0},
{component_id = 1, drawable = math.random(0, 50), texture = 0},
{component_id = 2, drawable = math.random(0, 50), texture = 0},
{component_id = 3, drawable = math.random(0, 50), texture = 0},
{component_id = 4, drawable = math.random(0, 50), texture = 0},
{component_id = 5, drawable = math.random(0, 50), texture = 0},
{component_id = 6, drawable = math.random(0, 50), texture = 0},
{component_id = 7, drawable = math.random(0, 50), texture = 0},
{component_id = 8, drawable = math.random(0, 50), texture = 0},
{component_id = 9, drawable = math.random(0, 50), texture = 0},
{component_id = 10, drawable = math.random(0, 50), texture = 0},
{component_id = 11, drawable = math.random(0, 50), texture = 0},
},
props = {
{prop_id = 0, drawable = math.random(0, 50), texture = 0},
{prop_id = 1, drawable = math.random(0, 50), texture = 0},
{prop_id = 2, drawable = math.random(0, 50), texture = 0},
{prop_id = 3, drawable = math.random(0, 50), texture = 0},
{prop_id = 4, drawable = math.random(0, 50), texture = 0},
{prop_id = 5, drawable = math.random(0, 50), texture = 0},
{prop_id = 6, drawable = math.random(0, 50), texture = 0},
{prop_id = 7, drawable = math.random(0, 50), texture = 0},
{prop_id = 8, drawable = math.random(0, 50), texture = 0},
{prop_id = 9, drawable = math.random(0, 50), texture = 0},
{prop_id = 10, drawable = math.random(0, 50), texture = 0},
{prop_id = 11, drawable = math.random(0, 50), texture = 0},
{prop_id = 12, drawable = math.random(0, 50), texture = 0},
{prop_id = 13, drawable = math.random(0, 50), texture = 0},
}
}, false, true)
end, false)
RegisterCommand('clothing:revert', function(source, args, rawCommand)
local src = source
Clothing.Revert(src)
end, false)
RegisterCommand('clothing:current', function(source, args, rawCommand)
local src = source
local currentClothing = Clothing.GetAppearance(src)
if not currentClothing then return end
print(json.encode(currentClothing, { indent = true }))
end, false)
RegisterCommand('clothing:openmenu', function(source, args, rawCommand)
local src = source
Clothing.OpenMenu(src)
end, false)
RegisterCommand('clothing:crowley', function(source, args, rawCommand)
local src = source
Clothing.SetAppearance(src, {
components = {
{drawable = 0, texture = 0, component_id = 0},
{drawable = 0, texture = 0, component_id = 1},
{drawable = 19, texture = 0, component_id = 2},
{drawable = 6, texture = 0, component_id = 3},
{drawable = 0, texture = 0, component_id = 4},
{drawable = 0, texture = 0, component_id = 5},
{drawable = 0, texture = 0, component_id = 6},
{drawable = 0, texture = 0, component_id = 7},
{drawable = 23, texture = 0, component_id = 8},
{drawable = 0, texture = 0, component_id = 9},
{drawable = 0, texture = 0, component_id = 10},
{drawable = 4, texture = 2, component_id = 11}
},
props = {
{drawable = 27, prop_id = 0, texture = 0},
{drawable = 0, prop_id = 1, texture = 0},
{drawable = 0, prop_id = 2, texture = 0},
{drawable = 0, prop_id = 3, texture = 0},
{drawable = 0, prop_id = 4, texture = 0},
{drawable = 0, prop_id = 5, texture = 0},
{drawable = 0, prop_id = 6, texture = 0},
{drawable = 0, prop_id = 7, texture = 0},
{drawable = 0, prop_id = 8, texture = 0},
{drawable = 0, prop_id = 9, texture = 0},
{drawable = 0, prop_id = 10, texture = 0},
{drawable = 0, prop_id = 11, texture = 0},
{drawable = 0, prop_id = 12, texture = 0},
{drawable = 0, prop_id = 13, texture = 0}
}
}, false, true)
end, false)