Интеграция CXG-GTFAT с другими ресурсами
Файлы bridge/*.lua — это редактируемые точки для адаптации разрешений, хранения данных, уведомлений, станций и форматов внешности. Патчи меню — ручные изменения для конкретных версий; при установке ресурс не изменяет другие ресурсы.
Меню внешности
Патчи подготовлены для следующих репозиториев и ревизий:
| Меню | Исходная ревизия | Дополнительное действие |
|---|---|---|
| illenium-appearance | 1b003ec169b15f145d519438ba7c6454bf746f27 | Примените Lua-патч сохранения компонентов. |
| fivem-appearance | b06da32881ed49042909b38a778c10dd9bd9eaed | Примените патч и пересоберите игровой TypeScript bundle с помощью build:game. |
| qb-clothing | 8cca4009dd473ab30c30e443ecad50830b816ed7 | Примените патч к сохранению скина и комплекта. |
| esx_skin / skinchanger | fe59ca0bd6da59e2ec6eb4a8d06ece312e96ae7a | Путь патча отсчитывается от корня репозитория esx_core. |
В папке integrations пакета находятся illenium-appearance.patch, fivem-appearance.patch, qb-clothing.patch и skinchanger.patch. Они не применяются автоматически. В checkout меню для разработки проверьте нужный патч перед применением:
git apply --check /ruta/al/parche-correcto.patch
git apply /ruta/al/parche-correcto.patch
Если проверка не прошла, остановитесь и адаптируйте изменение к установленной версии; не применяйте патч принудительно. Форки и новые ревизии могут отличаться. Для патча fivem-appearance требуется пересобрать bundle по инструкции этого меню с помощью build:game. Патчи не сертифицируют все версии и не заменяют проверку на сервере.
При сохранении технический decal очищается, чтобы меню не записало его как обычный выбор игрока. Меню со своей таблицей внешности должны вызывать очистку в точке сохранения. Вес хранится отдельно от комплектов.
Сохранение собственной внешности
Клиентский export SanitizeAppearance возвращает копию внешности. Поддерживаются форматы components, qb и esx:
local appearanceCopy = exports['CXG_GTFAT']:SanitizeAppearance(
ped,
appearance,
'components'
)
GuardarApariencia(appearanceCopy)
GuardarApariencia — пример вызова; замените его функцией сохранения меню. components принимает массив компонентов или объект со свойством components; каждый компонент использует component_id, drawable, texture и необязательно palette. Формат qb использует поле decals с item и texture; esx использует decals_1 и decals_2. Export возвращает глубокую копию, сохраняет остальные поля и не меняет ped временно. Если адаптер формата не сработал или формат не распознан, он возвращает неизменённую копию и записывает диагностическое сообщение.
Регистрация preview ped
Если меню использует ped для предварительного просмотра отдельно от персонажа, зарегистрируйте preview активного персонажа. Снимите регистрацию до удаления сущности:
local previewPed = ObtenerPreviewActual()
if previewPed and DoesEntityExist(previewPed) then
exports['CXG_GTFAT']:RegisterPreviewPed(previewPed)
end
-- Al cerrar o cancelar el menú, antes de eliminar el ped:
exports['CXG_GTFAT']:UnregisterPreviewPed(previewPed)
EliminarPreview(previewPed)
ObtenerPreviewActual и EliminarPreview — примеры вызовов; замените их функциями своего меню. Регистрируйте только preview активного персонажа, не мировые ped и не preview других персонажей. Вместо этого можно настроить Config.GetPreviewPed, чтобы он возвращал текущий ped или nil. RefreshAppearance(previewPed) просит повторно обработать уже управляемый ped, но сам по себе не включает Fat.
Exports для серверных ресурсов
Серверные exports — это привилегированные API для других ресурсов сервера. Не перенаправляйте их напрямую из события, которое может вызвать клиент.
local playerSource = source -- ID del jugador en el servidor
local kg, err = exports['CXG_GTFAT']:GetWeight(playerSource)
if kg == nil then
print('No hay un peso confirmado:', err)
end
local appliedKg, setError = exports['CXG_GTFAT']:SetWeight(playerSource, 92.5)
if appliedKg == nil then
print('No se pudo fijar el peso:', setError)
end
local addedKg, addError = exports['CXG_GTFAT']:AddWeight(playerSource, -2.0)
if addedKg == nil then
print('No se pudo sumar peso:', addError)
end
local resetKg, resetError = exports['CXG_GTFAT']:ResetWeight(playerSource)
if resetKg == nil then
print('No se pudo restablecer el peso:', resetError)
end
Аргумент source — ID игрока на сервере, а не значение, присланное клиентом. Все значения указываются в кг. GetWeight возвращает вес либо nil, error. SetWeight и AddWeight возвращают нормализованный применённый вес в кг либо nil, error; SetWeight отклоняет значения за пределами диапазона и округляет до ближайшего шага, округляя середины вверх. AddWeight допускает отрицательное изменение. ResetWeight записывает defaultKg и возвращает применённый вес в кг; сохранённое значение при этом не удаляется.
Чтобы открыть интерфейс игроку из другого серверного ресурса:
local playerSource = source -- ID del jugador en el servidor
local opened, err = exports['CXG_GTFAT']:OpenWeightUI(playerSource)
if not opened then
print('No se pudo abrir la báscula:', err)
end
Вторым аргументом можно передать ID станции: OpenWeightUI(source, 'gimnasio'). Если станция не указана, действует политика доступа команд, даже если регистрация команд отключена. Результат true подтверждает авторизацию и отправку запроса на открытие, но не отображение интерфейса на клиенте.
Серверные exports
| Export | Использование |
|---|---|
GetWeight(source) | Возвращает кг либо nil, error. |
SetWeight(source, kg) | Устанавливает проверенный вес и возвращает применённое значение в кг либо nil, error. |
AddWeight(source, deltaKg) | Прибавляет изменение и возвращает применённое значение в кг либо nil, error. |
ResetWeight(source) | Сохраняет и возвращает настроенный начальный вес в кг либо nil, error. |
GetWeightSettings() | Возвращает копию действующих ограничений. |
OpenWeightUI(source, stationId?) | Запрашивает открытие весов согласно действующей политике. |
RefreshCharacter(source) | Перезагружает идентификатор и вес после переключения фреймворка на нужного персонажа. |
ReconcileStorage(source) | Сверяет хранилище после неопределённого состояния, если адаптер может подтвердить предыдущие записи. |
RefreshCharacter необязателен для интеграций с несколькими персонажами; вызывайте его после того, как фреймворк предоставит нужный идентификатор. Ресурс не подключается к Qbox автоматически.
Ошибки могут включать not_ready, invalid_source, player_unavailable, character_unavailable, invalid_weight, out_of_range, permission_denied, context_denied, busy, storage_failed, storage_timeout, storage_unknown или not_synced. Не считайте ошибку хранилища подтверждённым весом и не повторяйте автоматически запись, завершившуюся с storage_timeout или storage_unknown.
Клиентские 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 возвращает nil, 'not_synced'. GetFatStatus(ped?) и GetReservedDecals(ped) возвращают информацию только для чтения и не доказывают, что геометрия отобразилась. GetReservedDecals можно использовать, чтобы скрывать зарезервированные decal в меню. RefreshAppearance(previewPed?) обновляет только управляемую внешность игрока или зарегистрированный preview.
RegisterPreviewPed(ped) и UnregisterPreviewPed(ped) позволяют меню передать селектору сущность preview. Снять регистрацию может только ресурс, который её установил. Зарегистрированные preview удаляются при остановке ресурса-владельца.
SanitizeAppearance(ped, appearance, format) возвращает копию внешности; nil, error не используется для обозначения ошибки. RegisterPreviewPed и UnregisterPreviewPed возвращают true при успехе и false, если ped не удалось зарегистрировать или удалить.
Редактируемые bridges
| Файл | Адаптация |
|---|---|
bridge/server.lua | CanAccess, идентификатор, загрузка/сохранение веса и серверные hooks. |
bridge/client.lua | Preview ped, уведомления и hooks изменений веса/интерфейса. |
bridge/interaction.lua | Регистрация станций в target или замена TextUI. |
bridge/appearance.lua | Чтение/запись decal в собственном формате внешности. |
По умолчанию CanAccess распознаёт только ace и everyone. Для jobs, groups или custom реализуйте проверку через реальный API сервера и возвращайте строго true при разрешённом доступе. Исключение или любое другое значение означает отказ. Поля политик job, group и custom интерпретирует ваш адаптер.
Собственный поставщик хранилища должен реализовать сигнатуры LoadWeight(characterId, context, done), SaveWeight(characterId, kg, context, done) и, если требуется согласование неопределённых записей, ReconcileWeight(characterId, context, done). При загрузке вызывается done(true, kg) (если данных нет, используйте kg = nil) либо done(false, 'storage_failed'). Сохранение вызывает done(true) только после подтверждения или done(false, 'storage_failed'). Согласование вызывает done(true, true), только если предыдущая запись уже не сможет завершиться позже; если это нельзя гарантировать, вызовите done(false, 'storage_unknown'). Для действительно асинхронного сохранения установите AsyncStorage = true; callback должен завершиться в контексте FiveM, поддерживающем ожидание. API ждёт до 10 секунд и затем сообщает о тайм-ауте. После storage_timeout не повторяйте запись автоматически; сначала выполните согласование через поставщика, гарантирующего, что предыдущая запись не применится позже. Не объявляйте поставщика асинхронным, если он возвращает управление до запуска или постановки записи в очередь.
Hooks OnWeightChanged, OnCharacterChanged, OnUIOpened, OnUIClosed и Log нужны для наблюдения и ведения журнала. Hooks не выдают доступ и не отменяют подтверждённые операции.
Проверки перед включением
Патчи и адаптеры необходимо проверить с фактическими версиями и ресурсами сервера. Перед включением интеграции для игроков:
- Проверьте переключение Normal/Fat на мужском и женском персонаже freemode.
- При включённом Fat сохраните и снова загрузите внешность и комплект. Убедитесь, что технический decal не записался как обычный выбор и вес хранится отдельно от комплекта.
- При включённом сохранении по персонажу перезапустите ресурс и подключитесь снова. Убедитесь, что тот же персонаж получил свой вес, а другой его не унаследовал.
- Откройте меню внешности с зарегистрированным preview и проверьте, что он отображает текущего персонажа. При отмене или закрытии меню снимите регистрацию preview до удаления сущности.
Выполните эти проверки на своём сервере, чтобы проверить конкретное сочетание меню, фреймворка и одежды. Наличие патча само по себе не подтверждает результат.