Appearance

Creating custom compatibility for appearance.

What's needed?

To create custom compatibility for your appearance, you need to firstly get:

  • table where player skins is saved;

  • export for opening up appearance menu;

  • export for setting up player skin;

If you already got this data you're good to go!

Gathering data from database

Headover to /server/functions/characters.lua and find Characters.ConvertSkin

Characters.ConvertSkin = function(convertedIdentifier)
    local skin = nil
    local startTime = GetGameTimer()
    
    debugPrint("[^2CHARACTERS.CONVERTSKIN^7] Checking player skin [/]")
    local appearanceResource = Config.ForceAppereance ~= false and Config.ForceAppereance or (GetResourceState('skinchanger') == 'started' and 'skinchanger' or GetResourceState('skinchanger') == 'fivem-appearance' and 'fivem-appearance' or GetResourceState('illenium-appearance') == 'started' and 'illenium-appearance' or GetResourceState('qb-clothing') == 'started' and 'qb-clothing' or GetResourceState('crm-appearance') == 'started' and 'crm-appearance' or GetResourceState('bl_appearance') == 'started' and 'bl_appearance' or GetResourceState('tgiann-clothing') == 'started' and 'tgiann-clothing' or GetResourceState('rcore_clothing') == 'started' and 'rcore_clothing')
    if appearanceResource == 'skinchanger' then
        if FrameworkSelected == 'ESX' then
            local result = MySQL.query.await("SELECT `skin` FROM `users` WHERE `identifier` = ?", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end
        elseif FrameworkSelected == 'QBCore' then
            local result = MySQL.query.await("SELECT * FROM `playerskins` WHERE `citizenid` = ? AND active = 1", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end
        end
    elseif appearanceResource == 'fivem-appearance' then
        if FrameworkSelected == 'ESX' then
            local result = MySQL.query.await("SELECT `skin` FROM `users` WHERE `identifier` = ?", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end
        elseif FrameworkSelected == 'QBCore' then
            local result = MySQL.query.await("SELECT * FROM `playerskins` WHERE `citizenid` = ? AND active = 1", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end
        end
    elseif appearanceResource == 'illenium-appearance' then
        if FrameworkSelected == 'ESX' then
            local result = MySQL.query.await("SELECT `skin` FROM `users` WHERE `identifier` = ?", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end
        elseif FrameworkSelected == 'QBCore' then
            local result = MySQL.query.await("SELECT * FROM `playerskins` WHERE `citizenid` = ? AND active = 1", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end 
        end
    elseif appearanceResource == 'qb-clothing' then
        if FrameworkSelected == 'ESX' then
            debugPrint('Tried to access qb-clothing in ESX. Are you sure you added proper resource to your framework?')
        elseif FrameworkSelected == 'QBCore' then
            local result = MySQL.query.await("SELECT * FROM `playerskins` WHERE `citizenid` = ? AND active = 1", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end
        end
    elseif appearanceResource == 'crm-appearance' then
        if FrameworkSelected == 'ESX' then
            local result = MySQL.query.await("SELECT `skin` FROM `users` WHERE `identifier` = ?", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end
        elseif FrameworkSelected == 'QBCore' then
            local result = MySQL.query.await("SELECT * FROM `playerskins` WHERE `citizenid` = ? AND active = 1", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin = json.decode(result[1].skin)
            end
        end
    elseif appearanceResource == 'bl_appearance' then
        local result = MySQL.query.await("SELECT * FROM `appearance` WHERE `id` = ?", {convertedIdentifier})
        if result and result[1] and result[1].skin then
            local decodedSkin = json.decode(result[1].skin)
            skin = mergeTables(json.decode(result[1].skin), json.decode(result[1].clothes), json.decode(result[1].tattoos))
        end
    elseif appearanceResource == 'tgiann-clothing' then
        local result = MySQL.query.await("SELECT * FROM tgiann_skin WHERE citizenid = ?", { convertedIdentifier })
        if result and result[1] and result[1].skin then
            skin = {
                skin = json.decode(result[1].skin),
                model = tonumber(result[1].model)
            }
        end
    elseif appearanceResource == 'rcore_clothing' then
        local rcoreSkin = exports["rcore_clothing"]:getSkinByIdentifier(convertedIdentifier)
        skin = {
            skin = rcoreSkin.skin,
            model =  rcoreSkin.ped_model,
        }

    elseif appearanceResource == 'dx_clothing' then
        skin = {
            skin = false,
            tattoos = false
        }
        if FrameworkSelected == 'ESX' then
            local result = MySQL.query.await("SELECT `skin` FROM `users` WHERE `identifier` = ?", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin.skin = json.decode(result[1].skin)
            end
        elseif FrameworkSelected == 'QBCore' then
            local result = MySQL.query.await("SELECT * FROM `playerskins` WHERE `citizenid` = ? AND active = 1", {convertedIdentifier})
            if result and result[1] and result[1].skin then
                skin.skin = json.decode(result[1].skin)
            end
        end
        skin.tattoos = exports['dx_clothing']:getTattoosByIdentifier(convertedIdentifier) 
    elseif appearanceResource == 'karma_clothing' then
        local result = MySQL.query.await("SELECT * FROM `playerskins` WHERE `citizenid` = ? AND active = 1", {convertedIdentifier})
        if result and result[1] and result[1].skin then
            skin = {
                skin = {
                    newSkin = json.decode(result[1].skin),
                    newHead = {
                        headblend = {},
                        features = {},
                        overlays = {},
                        eyeColor = 0,
                        fade = 0,
                        tattoos = {}
                    }
                }
            }
            local headDataQuery = MySQL.single.await('SELECT citizenid, model, head_blend, head_features, head_overlays, fade, tattoos, eye_color FROM karma_head_clothing WHERE citizenid = ? LIMIT 1', { convertedIdentifier })
            if headDataQuery then
                skin.skin.newHead = {
                    headblend = json.decode(headDataQuery.head_blend),
                    features = json.decode(headDataQuery.head_features),
                    overlays = json.decode(headDataQuery.head_overlays),
                    eyeColor = headDataQuery.eye_color,
                    fade = headDataQuery.fade,
                    tattoos = json.decode(headDataQuery.tattoos)
                }
            end
        end
    end

    if GetResourceState('rcore_tattoos') == 'started' then
        local rcoreTattooPromise = promise:new()
        debugPrint('[^2CHARACTERS.CONVERTSKIN^7] Awaiting rcore tattoo [/]')
        TriggerEvent('rcore_tattoos:getPlayerTattoosByIdentifier', convertedIdentifier, function(tattoos)
            if not skin then 
                skin = {} 
            end
            skin.tattoo = tattoos
            rcoreTattooPromise:resolve()
            debugPrint('[^2CHARACTERS.CONVERTSKIN^7] Rcore tattoo loaded!')
        end)

        Citizen.CreateThread(function()
            Wait(100)
            local startTime = GetGameTimer()
            local endTime = startTime + 3000
            local currentTime = startTime
            while rcoreTattooPromise and rcoreTattooPromise.state == 0 do
                currentTime = GetGameTimer()
                if currentTime >= endTime then
                    rcoreTattooPromise:resolve()
                    debugPrint('[RCORE_TATTOO] Force resolved promise.')
                    break
                end
                Wait(100)
            end
        end)
       
        Citizen.Await(rcoreTattooPromise)
    end


    if Config.DebugTimers then
        print('[CHARACTERS.CONVERTSKIN] Took: ^3'..(GetGameTimer() - startTime)..'ms^7')
    end
    
    debugPrint("[^2CHARACTERS.CONVERTSKIN^7] Checked player skin.")
    return skin
end

This function represents how data should look like, inside that function make sure to add your auto detection for your appearance (Framework.AppearanceResource variable) (not exactly needed though) and apply your custom logic inside the statement to return variable skin after all.

Setting up player skin

Headover to the /client/framework/framework_functions.lua and find Framework.SetSkin function:

Inside that function add your statement for your specific appearance and add event/export for your appearance to set player skin.

Opening up appearance menu

Headover to the /client/framework/framework_functions.lua and find Framework.OpenSkinMenu function:

Here as above you can add auto detection on Framework.ApperanceResource (it's not needed). Create your statement for your appearance and inside of it add your export to toggle up menu

Last updated