# 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`

```lua
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:

```lua
Framework.SetSkin = function(skinData, isMale, model)
    local ped = PlayerPedId()
    local plyModel 
    if (Framework.AppereanceResource == 'illenium-appearance' or Framework.AppereanceResource == 'fivem-appearance' or Framework.AppereanceResource == 'rcore_clothing' or Framework.AppereanceResource == 'tgiann-clothing') and skinData ~= nil and skinData.model ~= nil then
        plyModel = type(skinData.model) == 'string' and joaat(skinData.model) or skinData.model
    else
        if not model or model == '' then
            plyModel = isMale and GetHashKey("mp_m_freemode_01") or GetHashKey("mp_f_freemode_01")
        else
            plyModel = type(model) ~= 'number' and tonumber(model) or model
        end
    end
    
    RequestModel(plyModel)
    debugPrint('[^3SKIN^7] Requesting model [/]')
    while not HasModelLoaded(plyModel) do
        RequestModel(plyModel)
        Citizen.Wait(0)
    end
    debugPrint('[^3SKIN^7] Model Loaded')
    SetPlayerModel(PlayerId(), plyModel)
    ped = PlayerPedId()
    SetPedDefaultComponentVariation(PlayerPedId())
    SetEntityVisible(PlayerPedId(), true)
    SetEntityAlpha(PlayerPedId(), 255)
    
    debugPrint('[ADDITIONAL OUTPUTS] Appearance selected: '..Framework.AppereanceResource)
    if skinData == nil then return debugPrint('Could not find a skin for user. Setting default values') end
    
    local tattooData = false
    
    if skinData.tattoo then
        tattooData = skinData.tattoo
        skinData.tattoo = nil -- preventing overlapping
    end
    
    if Framework.AppereanceResource == 'skinchanger' then
        TriggerEvent('skinchanger:loadSkin', skinData)
    elseif Framework.AppereanceResource == 'fivem-appearance' then
        exports['fivem-appearance']:setPedAppearance(ped, skinData)
    elseif Framework.AppereanceResource == 'illenium-appearance' then
        exports['illenium-appearance']:setPedAppearance(ped, skinData)
    elseif Framework.AppereanceResource == 'qb-clothing' then
        TriggerEvent('qb-clothing:client:loadPlayerClothing', skinData, ped)
    elseif Framework.AppereanceResource == 'crm-appearance' then
        exports['crm-appearance']:crm_set_player_appearance(skinData)
    elseif Framework.AppereanceResource == 'bl_appearance' then
        exports.bl_appearance:SetPlayerPedAppearance(skinData)
    elseif Framework.AppereanceResource == 'tgiann-clothing' then
        exports["tgiann-clothing"]:SetPedAppearance(ped, skinData.skin)
    elseif Framework.AppereanceResource == 'rcore_clothing' then
        exports['rcore_clothing']:setPedSkin(ped, skinData)
    elseif Framework.AppereanceResource == 'dx_clothing' then
        exports['dx_clothing']:setPedAppearance(ped, skinData)
    elseif Framework.AppereanceResource == 'karma_clothing' then
        TriggerEvent('qb-clothing:client:loadPlayerClothing', skinData.skin, PlayerPedId())
    end

    if GetResourceState('rcore_tattoos') == 'started' then
        if tattooData then
            TriggerEvent('rcore_tattoos:setPedTattoos', PlayerPedId(), tattooData, true)
        end
    end
end
```

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:

```lua
Framework.OpenSkinMenu = function(gender)
    if Framework.AppereanceResource == 'skinchanger' then
        TriggerEvent('esx_skin:openSaveableMenu', function()
            if UserInterfaceActive then
                exports[ZSX_UI]:HideUI(false)
            end
            if Config.Identity.SetInBucketOnAppearance then TriggerServerEvent('ZSX_Multicharacter:Event:SetPlayerState', 'LOG_IN_USER') end
            HandleHud(false)
        end)
    elseif Framework.AppereanceResource == 'fivem-appearance' then
        SetPedHeadBlendData(PlayerPedId(), 0, 0, 0, 0, 0, 0, 0, 0, 0, false)
        exports['fivem-appearance']:startPlayerCustomization(function (skin)
            if skin then
                TriggerServerEvent('ZSX_Multicharacter:Save:Appereance', skin)
            else
                local data = exports['fivem-appearance']:getPedAppearance(PlayerPedId())
                TriggerServerEvent('ZSX_Multicharacter:Save:Appereance', data)
            end
            if Config.Identity.SetInBucketOnAppearance then TriggerServerEvent('ZSX_Multicharacter:Event:SetPlayerState', 'LOG_IN_USER') end
            if UserInterfaceActive then
                exports[ZSX_UI]:HideUI(false)
            end
            HandleHud(false)
        end, {ped = true, headBlend = true, faceFeatures = true, headOverlays = true, components = true, componentConfig = { masks = true, upperBody = true, lowerBody = true, bags = true, shoes = true, scarfAndChains = true, bodyArmor = true, shirts = true, decals = true, jackets = true }, props = true, propConfig = { hats = true, glasses = true, ear = true, watches = true, bracelets = true }, tattoos = true, enableExit = true})
    elseif Framework.AppereanceResource == 'illenium-appearance' then
        SetPedHeadBlendData(PlayerPedId(), 0, 0, 0, 0, 0, 0, 0, 0, 0, false)
        exports['illenium-appearance']:startPlayerCustomization(function (skin)
            if skin then
                TriggerServerEvent('ZSX_Multicharacter:Save:Appereance', skin)
            else
                local data = exports['illenium-appearance']:getPedAppearance(PlayerPedId())
                TriggerServerEvent('ZSX_Multicharacter:Save:Appereance', data)
            end
            
            if Config.Identity.SetInBucketOnAppearance then TriggerServerEvent('ZSX_Multicharacter:Event:SetPlayerState', 'LOG_IN_USER') end
            if UserInterfaceActive then
                exports[ZSX_UI]:HideUI(false)
            end
            HandleHud(false)
        end, {ped = true, headBlend = true, faceFeatures = true, headOverlays = true, components = true, componentConfig = { masks = true, upperBody = true, lowerBody = true, bags = true, shoes = true, scarfAndChains = true, bodyArmor = true, shirts = true, decals = true, jackets = true }, props = true, propConfig = { hats = true, glasses = true, ear = true, watches = true, bracelets = true }, tattoos = true, enableExit = true, automaticFade = false})
    elseif Framework.AppereanceResource == 'qb-clothing' then
        if UserInterfaceActive then
            exports[ZSX_UI]:HideUI(false)
        end
        HandleHud(false)
        TriggerEvent('qb-clothes:client:CreateFirstCharacter', false, false)
    elseif Framework.AppereanceResource == 'crm-appearance' then
        TriggerEvent('crm-appearance:init-new-character', 'crm-'..gender, function() 
            if UserInterfaceActive then
                exports[ZSX_UI]:HideUI(false)
            end
            if Config.Identity.SetInBucketOnAppearance then TriggerServerEvent('ZSX_Multicharacter:Event:SetPlayerState', 'LOG_IN_USER') end
            HandleHud(false)
        end) 
    elseif Framework.AppereanceResource == 'bl_appearance' then
        exports.bl_appearance:InitialCreation(function()
            if UserInterfaceActive then
                exports[ZSX_UI]:HideUI(false)
            end
            if Config.Identity.SetInBucketOnAppearance then TriggerServerEvent('ZSX_Multicharacter:Event:SetPlayerState', 'LOG_IN_USER') end
            HandleHud(false)
        end)

    elseif Framework.AppereanceResource == 'tgiann-clothing' then
        if FrameworkSelected == 'ESX' then
            TriggerEvent("tgiann-clothing:esx:createNew")
        else
            TriggerEvent("qb-clothes:client:CreateFirstCharacter")
        end
        if Config.Identity.SetInBucketOnAppearance then TriggerServerEvent('ZSX_Multicharacter:Event:SetPlayerState', 'LOG_IN_USER') end
        if UserInterfaceActive then
            exports[ZSX_UI]:HideUI(false)
        end
        HandleHud(false)
    elseif Framework.AppereanceResource == 'rcore_clothing' then
        TriggerEvent('rcore_clothing:openCharCreator')
    elseif Framework.AppereanceResource == 'dx_clothing' then
        local result = exports['dx_clothing']:StartFirstCustomization()
        while not result do
            Wait(0)
        end
        if Config.Identity.SetInBucketOnAppearance then TriggerServerEvent('ZSX_Multicharacter:Event:SetPlayerState', 'LOG_IN_USER') end
        if UserInterfaceActive then
            exports[ZSX_UI]:HideUI(false)
        end
        HandleHud(false)
    elseif Framework.AppereanceResource == 'karma_clothing' then
        TriggerEvent('qb-clothes:client:CreateFirstCharacter', false, false)
        if UserInterfaceActive then
            exports[ZSX_UI]:HideUI(false)
        end
        HandleHud(false)
    end
end
```

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zsx-development.gitbook.io/docs/resources/multicharacter/configurating/creating-custom-compatibilities/appearance.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
