Skip to content
CXGdocs.GUIDES
Cxg gtfatIntegrate CXG-GTFAT with other resources
CXG DOCUMENTATION

Integrate CXG-GTFAT with other resources

The bridge/*.lua files are editable adaptation points for permissions, persistence, notifications, stations, and appearance formats. Menu patches are manual changes for specific versions; the resource does not modify other resources when installed.

Appearance menus

Patches are prepared for these repositories and revisions:

MenuSource revisionAdditional action
illenium-appearance1b003ec169b15f145d519438ba7c6454bf746f27Apply the Lua component-save patch.
fivem-appearanceb06da32881ed49042909b38a778c10dd9bd9eaedApply the patch and rebuild the game's TypeScript bundle with the build:game workflow.
qb-clothing8cca4009dd473ab30c30e443ecad50830b816ed7Apply the patch at the skin and outfit save boundaries.
esx_skin / skinchangerfe59ca0bd6da59e2ec6eb4a8d06ece312e96ae7aThe patch is relative to the esx_core repository root.

The package's integrations folder contains illenium-appearance.patch, fivem-appearance.patch, qb-clothing.patch, and skinchanger.patch. They are not applied automatically. In a development checkout of the menu, check the matching patch before applying it:

Code
git apply --check /path/to/the-correct-patch.patch
git apply /path/to/the-correct-patch.patch

If the check fails, stop and adapt the change to the installed version; do not force the patch. Forks and newer revisions may differ. The fivem-appearance patch requires rebuilding the bundle with that menu's build:game workflow. These patches do not certify every version or replace testing on your server.

Saving sanitizes the technical decal so a menu does not persist it as the player's ordinary selection. Menus with their own appearance table must call the sanitizer at their save point. Weight is stored separately from outfits.

Save a custom appearance

The client export SanitizeAppearance returns a copy of the appearance. Supported formats are components, qb, and esx:

Code
local appearanceCopy = exports['CXG_GTFAT']:SanitizeAppearance(
    ped,
    appearance,
    'components'
)

GuardarApariencia(appearanceCopy)

GuardarApariencia is an example call; replace it with your menu's save function. components accepts an array of components or an object with a components property; each component uses component_id, drawable, texture, and optionally palette. The qb format uses the decals field with item and texture; esx uses decals_1 and decals_2. The export returns a deep copy and preserves other fields without temporarily changing the ped. If the format adapter fails or the format is unrecognized, it returns the unchanged copy and logs a diagnostic.

Register a preview ped

If the menu uses a preview ped separate from the character, register the preview for the active character. Unregister it before deleting the entity:

Code
local previewPed = ObtenerPreviewActual()

if previewPed and DoesEntityExist(previewPed) then
    exports['CXG_GTFAT']:RegisterPreviewPed(previewPed)
end

-- When the menu closes or is cancelled, before deleting the ped:
exports['CXG_GTFAT']:UnregisterPreviewPed(previewPed)
EliminarPreview(previewPed)

ObtenerPreviewActual and EliminarPreview are example calls; replace them with your menu's functions. Register only the active character's preview, not world peds or previews for other characters. Alternatively, set Config.GetPreviewPed to return the current ped or nil. RefreshAppearance(previewPed) requests a refresh of a ped that is already managed; it does not activate Fat by itself.

Server-resource exports

Server exports are privileged APIs for other server resources. Do not forward them directly from an event that a client can invoke.

Code
local playerSource = source -- Player ID on the server
local kg, err = exports['CXG_GTFAT']:GetWeight(playerSource)
if kg == nil then
    print('No confirmed weight:', err)
end

local appliedKg, setError = exports['CXG_GTFAT']:SetWeight(playerSource, 92.5)
if appliedKg == nil then
    print('Could not set weight:', setError)
end

local addedKg, addError = exports['CXG_GTFAT']:AddWeight(playerSource, -2.0)
if addedKg == nil then
    print('Could not add weight:', addError)
end

local resetKg, resetError = exports['CXG_GTFAT']:ResetWeight(playerSource)
if resetKg == nil then
    print('Could not reset weight:', resetError)
end

The source argument is the server player ID, not a value sent by the client. All values are in kg. GetWeight returns the weight or nil, error. SetWeight and AddWeight return the normalized applied weight in kg or nil, error; SetWeight rejects out-of-range values and rounds to the nearest step, with ties rounded up. AddWeight accepts negative increments. ResetWeight writes defaultKg and also returns the applied weight in kg; it does not delete the stored value.

To open the UI for a player from another server resource:

Code
local playerSource = source -- Player ID on the server
local opened, err = exports['CXG_GTFAT']:OpenWeightUI(playerSource)
if not opened then
    print('Could not open the scale:', err)
end

An optional station ID can be passed as the second argument: OpenWeightUI(source, 'gym'). Without a station, it applies the command access policy even if command registration is disabled. A true result confirms authorization and that the open request was sent; it does not confirm that the client has rendered the interface.

Server exports

ExportUse
GetWeight(source)Returns kg or nil, error.
SetWeight(source, kg)Sets a validated weight and returns the applied kg or nil, error.
AddWeight(source, deltaKg)Adds a delta and returns the applied kg or nil, error.
ResetWeight(source)Saves and returns the configured default weight in kg or nil, error.
GetWeightSettings()Returns a copy of the active limits.
OpenWeightUI(source, stationId?)Requests to open the scale under the applicable policy.
RefreshCharacter(source)Reloads identity and weight after the framework switches to the intended character.
ReconcileStorage(source)Reconciles storage after an uncertain state when the adapter can confirm earlier writes.

RefreshCharacter is optional for multicharacter integrations; call it after the framework has made the intended identity available. The resource does not connect to Qbox automatically.

Errors may include not_ready, invalid_source, player_unavailable, character_unavailable, invalid_weight, out_of_range, permission_denied, context_denied, busy, storage_failed, storage_timeout, storage_unknown, or not_synced. Do not treat a storage error as confirmed weight or automatically retry a write with a storage_timeout or storage_unknown result.

Client exports

Code
local kg, err = exports['CXG_GTFAT']:GetWeight()
local settings = exports['CXG_GTFAT']:GetWeightSettings()
local isOpen = exports['CXG_GTFAT']:IsWeightUIOpen()
local closed = exports['CXG_GTFAT']:CloseWeightUI()
local status, statusError = exports['CXG_GTFAT']:GetFatStatus()
local reserved, reservedError = exports['CXG_GTFAT']:GetReservedDecals(PlayerPedId())

GetWeight returns nil, 'not_synced' until a server-confirmed value arrives. GetFatStatus(ped?) and GetReservedDecals(ped) provide read-only information; they do not prove that the geometry rendered. GetReservedDecals can be used to hide reserved decals in menus. RefreshAppearance(previewPed?) only refreshes the managed player appearance or a registered preview.

RegisterPreviewPed(ped) and UnregisterPreviewPed(ped) let a menu hand its preview entity to the selector. Only the resource that registered the ped can unregister it. Registered previews are cleaned up when their owning resource stops.

SanitizeAppearance(ped, appearance, format) returns a copy of the appearance; nil, error is not its failure indicator. RegisterPreviewPed and UnregisterPreviewPed return true when the operation succeeds and false when the ped cannot be registered or removed.

Editable bridges

FileAdaptation
bridge/server.luaCanAccess, identity, weight loading/saving, and server hooks.
bridge/client.luaPreview ped, notifications, and weight/interface change hooks.
bridge/interaction.luaStation registration with a target or replacement of TextUI.
bridge/appearance.luaRead/write decals in a custom appearance format.

By default, CanAccess recognizes only ace and everyone. For jobs, groups, or custom access, implement the check with the server's actual API and return exactly true when access is granted. Exceptions and other values deny access. Job, group, and custom policy fields are data interpreted by your adapter.

A custom storage provider implements the signatures LoadWeight(characterId, context, done), SaveWeight(characterId, kg, context, done), and, if it needs to reconcile uncertain writes, ReconcileWeight(characterId, context, done). Loading calls done(true, kg) (use kg = nil when no value exists) or done(false, 'storage_failed'). Saving calls done(true) only after confirmation, or done(false, 'storage_failed'). Reconciliation calls done(true, true) only when no earlier write can complete later; if that cannot be guaranteed, call done(false, 'storage_unknown'). For genuinely asynchronous saves, set AsyncStorage = true; the callback must complete in a FiveM context that supports waiting. The API allows up to 10 seconds before reporting a timeout. Do not automatically retry after storage_timeout; first reconcile with a provider that guarantees an earlier write will not apply later. Do not mark a provider asynchronous if it returns before starting or ordering the write.

The OnWeightChanged, OnCharacterChanged, OnUIOpened, OnUIClosed, and Log hooks are for observing or logging changes. Hooks do not grant access or undo confirmed operations.

Checks before enabling it

Patches and adapters need validation against the actual server resources and versions. Before enabling the integration for players:

  1. Repeat the Normal/Fat change with male and female freemode characters.
  2. With Fat active, save and reload the appearance and outfit. Confirm that the technical decal is not saved as an ordinary selection and that weight remains separate from the outfit.
  3. With per-character persistence enabled, restart the resource and reconnect. Confirm the same character recovers its weight and another character does not inherit it.
  4. Open the appearance menu with a registered preview and confirm it represents the current character. When cancelling or closing the menu, unregister it before deleting the entity.

Completing these steps on your server is necessary to validate your specific combination of menu, framework, and clothing. The existence of a patch alone does not certify those results.

CXG · DocumentationA good foundation. Endless possibilities.
Integrate CXG-GTFAT with other resources · cxg-gtfat · CXG Docs