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:
| Menu | Source revision | Additional action |
|---|---|---|
| illenium-appearance | 1b003ec169b15f145d519438ba7c6454bf746f27 | Apply the Lua component-save patch. |
| fivem-appearance | b06da32881ed49042909b38a778c10dd9bd9eaed | Apply the patch and rebuild the game's TypeScript bundle with the build:game workflow. |
| qb-clothing | 8cca4009dd473ab30c30e443ecad50830b816ed7 | Apply the patch at the skin and outfit save boundaries. |
| esx_skin / skinchanger | fe59ca0bd6da59e2ec6eb4a8d06ece312e96ae7a | The 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:
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:
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:
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.
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:
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
| Export | Use |
|---|---|
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
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
| File | Adaptation |
|---|---|
bridge/server.lua | CanAccess, identity, weight loading/saving, and server hooks. |
bridge/client.lua | Preview ped, notifications, and weight/interface change hooks. |
bridge/interaction.lua | Station registration with a target or replacement of TextUI. |
bridge/appearance.lua | Read/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:
- Repeat the Normal/Fat change with male and female freemode characters.
- 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.
- With per-character persistence enabled, restart the resource and reconnect. Confirm the same character recovers its weight and another character does not inherit it.
- 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.