# Integracja CXG-GTFAT z innymi zasobami

Pliki `bridge/*.lua` są edytowalnymi punktami adaptacji uprawnień, trwałości, powiadomień, stanowisk i formatów wyglądu. Poprawki menu to ręczne zmiany przeznaczone dla konkretnych wersji; zasób nie modyfikuje innych zasobów podczas instalacji.

## Menu wyglądu

Poprawki przygotowano dla tych repozytoriów i rewizji:

| Menu | Rewizja źródłowa | Dodatkowe działanie |
| --- | --- | --- |
| illenium-appearance | 1b003ec169b15f145d519438ba7c6454bf746f27 | Zastosuj poprawkę Lua dotyczącą zapisu komponentów. |
| fivem-appearance | b06da32881ed49042909b38a778c10dd9bd9eaed | Zastosuj poprawkę i przebuduj grę TypeScript z użyciem komendy `build:game`. |
| qb-clothing | 8cca4009dd473ab30c30e443ecad50830b816ed7 | Zastosuj poprawkę w miejscach zapisu skina i stroju. |
| esx_skin / skinchanger | fe59ca0bd6da59e2ec6eb4a8d06ece312e96ae7a | Ścieżka poprawki jest względna wobec katalogu głównego repozytorium esx_core. |

Folder `integrations` pakietu zawiera pliki `illenium-appearance.patch`, `fivem-appearance.patch`, `qb-clothing.patch` i `skinchanger.patch`. Nie są stosowane automatycznie. W deweloperskim checkoutcie menu sprawdź właściwą poprawkę przed jej zastosowaniem:

```sh
git apply --check /ruta/al/parche-correcto.patch
git apply /ruta/al/parche-correcto.patch
```

Jeśli sprawdzenie się nie powiedzie, przerwij i dostosuj zmianę do zainstalowanej wersji; nie wymuszaj poprawki. Forki i nowsze rewizje mogą się różnić. Poprawka fivem-appearance wymaga przebudowania pakietu komendą `build:game` zgodnie z instrukcją menu. Poprawki nie poświadczają wszystkich wersji ani nie zastępują testów na serwerze.

Podczas zapisu techniczny decal jest oczyszczany, aby menu nie zapisało go jako zwykłego wyboru gracza. Menu z własną tabelą wyglądu musi wywołać oczyszczanie w miejscu zapisu. Waga jest przechowywana oddzielnie od strojów.

## Zapis własnego wyglądu

Kliencki export `SanitizeAppearance` zwraca kopię wyglądu. Obsługiwane formaty to components, qb i esx:

```lua
local appearanceCopy = exports['CXG_GTFAT']:SanitizeAppearance(
    ped,
    appearance,
    'components'
)

GuardarApariencia(appearanceCopy)
```

`GuardarApariencia` to przykładowe wywołanie; zastąp je funkcją zapisu menu. components akceptuje tablicę komponentów albo obiekt z właściwością `components`; każdy komponent używa `component_id`, `drawable`, `texture` i opcjonalnie `palette`. Format qb używa pola `decals` z `item` i `texture`, a esx używa `decals_1` i `decals_2`. Export zwraca głęboką kopię, zachowuje pozostałe pola i nie zmienia tymczasowo peda. Jeśli adapter formatu zawiedzie albo format jest nieznany, zwraca niezmienioną kopię i zapisuje diagnostykę.

## Rejestracja peda podglądu

Jeśli menu używa peda podglądu oddzielnego od postaci, zarejestruj podgląd aktywnej postaci. Wyrejestruj go przed usunięciem encji:

```lua
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` i `EliminarPreview` to przykładowe wywołania; zastąp je funkcjami menu. Rejestruj tylko podgląd aktywnej postaci, nie pedy świata ani podglądy innych postaci. Alternatywnie ustaw `Config.GetPreviewPed`, aby zwracał bieżącego peda lub nil. `RefreshAppearance(previewPed)` żąda ponownej oceny już zarządzanego peda i samo nie włącza Fat.

## Exporty zasobów serwerowych

Exporty serwerowe to uprzywilejowane API dla innych zasobów serwera. Nie przekazuj ich bezpośrednio z eventu wywoływalnego przez klienta.

```lua
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
```

Argument `source` to ID gracza po stronie serwera, a nie wartość wysłana przez klienta. Wszystkie wartości są w kg. `GetWeight` zwraca wagę albo nil, error. `SetWeight` i `AddWeight` zwracają znormalizowaną zastosowaną wagę w kg albo nil, error; `SetWeight` odrzuca wartości poza zakresem i zaokrągla do najbliższego kroku, a remisy w górę. `AddWeight` dopuszcza ujemne przyrosty. `ResetWeight` zapisuje `defaultKg` i zwraca zastosowaną wagę w kg; nie usuwa zapisanej wartości.

Aby otworzyć UI gracza z innego zasobu serwerowego:

```lua
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
```

Jako drugi argument można opcjonalnie przekazać ID stanowiska: `OpenWeightUI`(source, 'gimnasio'). Bez stanowiska obowiązuje polityka dostępu komend, nawet jeśli rejestracja komend jest wyłączona. Wynik true potwierdza autoryzację i wysłanie żądania otwarcia, ale nie potwierdza, że klient wyrenderował interfejs.

### Exporty serwera

| Export | Zastosowanie |
| --- | --- |
| `GetWeight(source)` | Zwraca kg albo nil, error. |
| `SetWeight(source, kg)` | Ustawia zweryfikowaną wagę i zwraca zastosowane kg albo nil, error. |
| `AddWeight(source, deltaKg)` | Dodaje różnicę i zwraca zastosowane kg albo nil, error. |
| `ResetWeight(source)` | Zapisuje i zwraca skonfigurowaną wagę domyślną w kg albo nil, error. |
| `GetWeightSettings()` | Zwraca kopię aktywnych limitów. |
| `OpenWeightUI(source, stationId?)` | Żąda otwarcia wagi zgodnie z obowiązującą polityką. |
| `RefreshCharacter`(source) | Wczytuje ponownie tożsamość i wagę po przełączeniu frameworka na właściwą postać. |
| `ReconcileStorage`(source) | Uzgadnia zapis w niepewnym stanie, jeśli adapter potrafi potwierdzić wcześniejsze zapisy. |

`RefreshCharacter` jest opcjonalny dla integracji wielopostaciowych; wywołaj go, gdy framework udostępni właściwą tożsamość. Zasób nie łączy się automatycznie z Qbox.

Błędy mogą obejmować `not_ready`, `invalid_source`, `player_unavailable`, `character_unavailable`, `invalid_weight`, `out_of_range`, `permission_denied`, `context_denied`, `busy`, `storage_failed`, `storage_timeout`, `storage_unknown` lub `not_synced`. Nie traktuj błędu zapisu jako potwierdzonej wagi i nie ponawiaj automatycznie zapisu z wynikiem `storage_timeout` lub `storage_unknown`.

## Exporty klienta

```lua
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` zwraca nil, '`not_synced`' do czasu otrzymania wartości potwierdzonej przez serwer. `GetFatStatus(ped?)` i `GetReservedDecals(ped)` udostępniają tylko informacje do odczytu; nie potwierdzają wyrenderowania geometrii. `GetReservedDecals` może służyć do ukrycia zarezerwowanych decalów w menu. `RefreshAppearance(previewPed?)` odświeża wyłącznie zarządzany wygląd gracza lub zarejestrowany podgląd.

`RegisterPreviewPed(ped)` i `UnregisterPreviewPed(ped)` pozwalają przekazać selektorowi encję podglądu. Peda może wyrejestrować tylko zasób, który go zarejestrował. Zarejestrowane podglądy są czyszczone po zatrzymaniu zasobu właściciela.

`SanitizeAppearance(ped, appearance, format)` zwraca kopię wyglądu; nil, error nie jest wskaźnikiem błędu. `RegisterPreviewPed` i `UnregisterPreviewPed` zwracają true po powodzeniu i false, jeśli nie można zarejestrować lub usunąć peda.

## Edytowalne bridge

| Plik | Adaptacja |
| --- | --- |
| `bridge/server.lua` | `CanAccess`, tożsamość, ładowanie/zapis wagi i serwerowe hooki. |
| `bridge/client.lua` | Ped podglądu, powiadomienia i hooki zmian wagi/interfejsu. |
| `bridge/interaction.lua` | Rejestracja stanowisk w target albo zastąpienie TextUI. |
| `bridge/appearance.lua` | Odczyt/zapis decalów w niestandardowym formacie wyglądu. |

Domyślnie `CanAccess` rozpoznaje tylko ace i everyone. Dla jobs, groups lub custom zaimplementuj sprawdzanie przez właściwe API serwera i zwracaj dokładnie true po przyznaniu dostępu. Wyjątki i pozostałe wartości odmawiają dostępu. Pola polityki job, group i custom są interpretowane przez adapter.

Niestandardowy dostawca zapisu implementuje sygnatury `LoadWeight`(`characterId`, context, done), `SaveWeight`(`characterId`, kg, context, done) oraz, jeśli trzeba uzgadniać niepewne zapisy, `ReconcileWeight`(`characterId`, context, done). Odczyt wywołuje done(true, kg) (użyj kg = nil, gdy nie ma zapisanej wartości) albo done(false, '`storage_failed`'). Zapis wywołuje done(true) dopiero po potwierdzeniu albo done(false, '`storage_failed`'). Uzgodnienie wywołuje done(true, true) tylko wtedy, gdy wcześniejszy zapis nie może zakończyć się później; w przeciwnym razie done(false, '`storage_unknown`'). Dla rzeczywiście asynchronicznych zapisów ustaw `AsyncStorage` = true; callback musi zakończyć się w kontekście FiveM obsługującym oczekiwanie. API czeka do 10 sekund przed zgłoszeniem limitu czasu. Po `storage_timeout` nie ponawiaj automatycznie; najpierw uzgodnij zapis przez dostawcę gwarantującego, że wcześniejszy zapis nie zostanie później zastosowany. Nie deklaruj trybu asynchronicznego, jeśli dostawca zwraca sterowanie przed rozpoczęciem lub zakolejkowaniem zapisu.

Hooki `OnWeightChanged`, `OnCharacterChanged`, `OnUIOpened`, `OnUIClosed` i Log służą do obserwacji lub rejestrowania zmian. Hooki nie przyznają dostępu ani nie cofają potwierdzonych operacji.

## Kontrola przed włączeniem

Poprawki i adaptery należy zweryfikować z rzeczywistymi wersjami i zasobami serwera. Przed udostępnieniem integracji graczom:

1. Powtórz zmianę Normal/Fat na męskiej i żeńskiej postaci freemode.
2. Przy aktywnym Fat zapisz i ponownie wczytaj wygląd oraz strój. Potwierdź, że techniczny decal nie zapisuje się jako zwykły wybór, a waga pozostaje niezależna od stroju.
3. Przy włączonej trwałości postaci uruchom ponownie zasób i połącz się ponownie. Potwierdź, że ta sama postać odzyskuje wagę, a inna jej nie dziedziczy.
4. Otwórz menu wyglądu z zarejestrowanym podglądem i sprawdź, czy pokazuje bieżącą postać. Przy anulowaniu lub zamykaniu menu wyrejestruj go przed usunięciem encji.

Wykonanie tych kroków na serwerze jest konieczne do sprawdzenia konkretnego zestawu menu, frameworka i ubrań. Sama obecność poprawki nie potwierdza tych wyników.
