Editable
This document covers all files in the `editable` folders that can be safely modified to customize the script for your server needs.
Overview
Files in editable
folders are designed to be modified without breaking the core functionality. These files handle:
Compatibility with other scripts
Custom behaviors and checks
Database operations
UI interactions
Development tools
Client Editable Files
Located in client/editable/
compatibility.lua
Purpose: Ensures compatibility with other clothing/skin scripts like skinchanger.
Key Functions
function getClothingOptions(excludedComponents, excludedProps)
-- Gets clothing options excluding specified components/props
-- Parameters:
-- excludedComponents (table): Component IDs to exclude
-- excludedProps (table): Prop IDs to exclude
-- Returns: table with components and props
end
function setClothingOptions(clothingOptions)
-- Sets clothing options (excluding face components)
-- Parameters:
-- clothingOptions (table): Clothing data to apply
end
function getSkinOptionsDb()
-- Retrieves skin data from database
-- Returns: skin data table or nil
end
function saveSkinOptions(skinOptions)
-- Saves skin options to database
-- Parameters:
-- skinOptions (table, optional): Data to save (defaults to current)
-- Returns: boolean success status
end
Registered Events
RegisterNetEvent('skinchanger:loadClothes', setClothingOptions)
RegisterNetEvent('skinchanger:change', setComponentOption)
Exports
exports('getClothingOptions', getClothingOptions)
exports('setClothingOptions', setClothingOptions)
exports('getSkinOptionsDb', getSkinOptionsDb)
exports('getSkinOptions', getSkinOptions)
exports('saveSkinOptions', saveSkinOptions)
const/constants.lua
Purpose: Defines all constants and mappings used throughout the script.
Component IDs
constants.PED_COMPONENTS_IDS = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }
constants.PED_PROPS_IDS = { 0, 1, 2, 3, 4, 5, 6, 7 }
Component Name Mapping
constants.componentsId = {
["face"] = 0, -- Face structure
["masks"] = 1, -- Masks
["tshirts"] = 8, -- T-shirts/tops
["pants"] = 4, -- Pants/legs
["shoes"] = 6, -- Footwear
["hoodies"] = 11, -- Hoodies/torso
["jewelry"] = 7, -- Necklaces/accessories
["bags"] = 5, -- Bags/backpacks
["decals"] = 10, -- Decals
["vests"] = 9, -- Vests/armor
["arms"] = 3, -- Arms/gloves
}
Props Name Mapping
constants.propsId = {
["hats"] = 0, -- Hats/helmets
["glasses"] = 1, -- Glasses/eyewear
["ear"] = 2, -- Earpieces
["watch"] = 6, -- Watches
["bracelet"] = 7, -- Bracelets
}
Face Features
constants.FACE_FEATURES = {
"noseWidth", "nosePeakHigh", "nosePeakSize", "noseBoneHigh",
"nosePeakLowering", "noseBoneTwist", "eyeBrownHigh", "eyeBrownForward",
"cheeksBoneHigh", "cheeksBoneWidth", "cheeksWidth", "eyesOpening",
"lipsThickness", "jawBoneWidth", "jawBoneBackSize", "chinBoneLowering",
"chinBoneLenght", "chinBoneSize", "chinHole", "neckThickness",
}
Head Overlays
constants.HEAD_OVERLAYS = {
"blemishes", "beard", "eyebrows", "ageing", "makeUp", "blush",
"complexion", "sunDamage", "lipstick", "moleAndFreckles",
"chestHair", "bodyBlemishes",
}
Hair Decorations
Contains extensive mappings for male and female hair decorations with texture dictionaries.
devCommands.lua
Purpose: Development commands for testing and debugging (only active when debug mode is enabled).
if not fs.config.debug then
return -- Skip loading if not in debug mode
end
RegisterCommand('openskin', function()
openSkinMenu(false)
end, false)
RegisterCommand('closeskin', function()
closeSkinMenu()
end, false)
Default Available Commands (only in debug mode)
/openskin
- Opens the skin menu/closeskin
- Closes the skin menu
disabledClothings.lua
Purpose: Configuration for default clothing when items are toggled off.
Structure
defaultDisabledClothings = {
hats = {
enabled = true, -- Whether this option is available
propIndex = 0, -- Prop index (for accessories)
componentIndex = nil, -- Component index (for clothing)
drawable = 0, -- Default drawable ID
variant = 0 -- Default texture variant
},
-- ... other items
}
Configurable Items
hats
Prop
Hats and helmets
masks
Component
Face masks
glasses
Prop
Eyewear
hoodies
Component
Hoodies/torso (multi-component)
bags
Component
Backpacks and bags
pants
Component
Pants and legs
shoes
Component
Footwear
Multi-Component Items
Some items affect multiple components:
hoodies = {
enabled = true,
propIndex = nil,
componentIndex = { 11, 8, 3 }, -- Multiple components
default = { 91, 15, 15 }, -- Default values for males
defaultF = { 15, 15, 15 }, -- Default values for females
drawable = { 0, 0, 0 }, -- Drawable when disabled
variant = { 0, 0, 0 } -- Variants when disabled
}
handlers.lua
Purpose: Handles menu opening/closing events and player loading.
Event Handlers
function onSkinMenuOpened()
-- Called when skin menu opens
-- Default: Shows key hints for camera controls
showKeyhintsPermanent({
{
keyIcon = { "ph-fill ph-mouse-left-click", "ph-fill ph-arrows-horizontal" },
keyClass = "flex flex-col items-center align-center keyhint_iconsVertical",
info = locale('keyhint_rotateCharacter'),
},
-- ... more hints
}, 'bottom-left')
end
function onSkinMenuClosed()
-- Called when skin menu closes
clearKeyhints()
end
AddEventHandler('5s_lib:onPlayerLoaded', function()
-- Automatically load saved skin when player joins
local skin = lib.callback.await('skinMenu/getSkin', false)
if skin ~= nil and skin ~= {} then
setSkinOptions(skin)
end
end)
Customization Examples
Custom opening behavior:
function onSkinMenuOpened()
-- Your custom logic here
TriggerEvent('myserver:logSkinMenuOpen')
-- Still show keyhints
showKeyhintsPermanent(yourCustomHints, 'top-right')
end
Integration with other systems:
function onSkinMenuClosed()
clearKeyhints()
-- Example: Trigger outfit save in custom system
if isInClothingStore then
TriggerServerEvent('clothingstore:purchaseOutfit')
end
end
maskFix.lua
Purpose: Automatically adjusts facial features when wearing masks for better compatibility.
Key Features
Automatically saves face blend data when mask is equipped
Shrinks facial features to prevent clipping
Restores original features when mask is removed
Main Functions
function forceRestoreFace()
-- Manually restore saved facial features
-- Use when mask removal doesn't trigger automatically
end
function fixClothing()
-- Fix clothing-related issues
-- Calls mask fix if needed
end
Exports
exports("forceRestoreFace", forceRestoreFace)
exports("fixClothing", fixClothing)
prechecks.lua
Purpose: Validation checks before allowing menu access.
Main Function
function canOpenSkinMenu()
-- Add your custom checks here
-- Example: Check if player is morphed
-- if exports['other_script']:isPlayerMorphed() then
-- fs.utils.notify('Cannot change appearance right now', 'error')
-- return false
-- end
-- Example: Check player permissions
-- if not exports['permission_system']:hasPermission('use.skinmenu') then
-- return false
-- end
-- Example: Check if player is in jail
-- if exports['esx_jail']:isPlayerInJail() then
-- return false
-- end
return true
end
Common Use Cases
Permission-based access:
function canOpenSkinMenu()
local playerGroup = ESX.GetPlayerData().group
if playerGroup == 'user' and not isInClothingStore then
fs.utils.notify('You need to be in a clothing store', 'error')
return false
end
return true
end
Location-based restrictions:
function canOpenSkinMenu()
local playerCoords = GetEntityCoords(PlayerPedId())
local inRestrictedZone = IsPlayerInRestrictedZone(playerCoords)
if inRestrictedZone then
fs.utils.notify('Cannot change appearance in this area', 'error')
return false
end
return true
end
State-based checks:
function canOpenSkinMenu()
if IsPedInAnyVehicle(PlayerPedId()) then
fs.utils.notify('Exit vehicle first', 'error')
return false
end
if GetEntityHealth(PlayerPedId()) <= 0 then
return false
end
return true
end
Server Editable Files
Located in server/editable/
database.lua
Purpose: Database operations for saving and loading skin data.
Core Functions
function saveSkinData(source, skinOptions)
-- Saves player skin data to database
-- Parameters:
-- source (number): Player server ID
-- skinOptions (table): Skin data to save
-- Returns: boolean success status
local identifier = fs.framework.getIdentifier(source)
local existingData = retrieveSkinData(source)
if not existingData then
-- Insert new record
MySQL.update.await('INSERT INTO skin_data(identifier, skinData) VALUES (?, ?)', {
identifier,
json.encode(skinOptions)
})
else
-- Update existing record
success = MySQL.update.await('UPDATE skin_data SET skinData = ? WHERE identifier = ?', {
json.encode(skinOptions),
identifier
}) ~= 0
end
return success
end
function retrieveSkinData(source)
-- Retrieves player skin data from database
-- Parameters:
-- source (number): Player server ID
-- Returns: table with skin data or nil
local identifier = fs.framework.getIdentifier(source)
local result = MySQL.single.await('select skinData from skin_data where identifier = ?', { identifier })
return result?.skinData and json.decode(result.skinData) or nil
end
Last updated