🇬🇧 English | 🇺🇦 Українська
13. Профілі на практиці: сценарії гри¶
Розділ про те, як ассети S3 Storage Profile розкладаються по реальних конфігураціях проєкту — однокористувацька гра, listen-сервер, виділений сервер, настільний застосунок — і звідки в кожній з них беруться ключі.
Попередні розділи описують ці речі окремо: 2. Налаштування — що таке профіль, 3. Облікові дані — де тримати ключі, 10. Сценарії розгортання — хто звертається до сховища. Тут — їхній перетин, на конкретних ассетах із конкретними значеннями.
Головне правило¶
Профіль зберігає не ключі, а джерело, з якого їх узяти. Тому питання «який профіль зробити» завжди зводиться до питання «який процес ним користуватиметься» — бо джерело має бути доступним саме тому процесу.
З цього випливає наслідок, який економить найбільше часу:
Профіль із
Environment variables— це серверний профіль. Якщо він поїде у збірку до гравців і ті його викличуть, змінних оточення там не буде, і кожен запит завершитьсяAuthentication Error. Це не помилка плагіна — це джерело, обране не для того процесу.
Якщо і клієнт, і сервер працюють з одним бакетом, але з різними правами — це два профілі з однаковими адресою й бакетом і різними джерелами, а не один спільний.
Чи потрібен вам профіль узагалі¶
| Ситуація | Що брати |
|---|---|
| Одне сховище на весь проєкт | Project Settings і Get Default S3 Client. Профіль не потрібен |
| Два й більше сховищ | Профіль на кожне |
| Конфігурація відома лише в рантайм (прийшла від бекенда) | Get Named S3 Client — профіль тут не допоможе, бо ассет фіксується на етапі розробки |
Профілі починають окупатися саме з другого сховища: спільна пара редакторських ключів одна на проєкт і може описувати тільки одне з них — див. «Ключі профілю».
Як профіль отримує ключі: чотири джерела в дії¶
| Credential Source | Хто постачає ключі | Доступно з Blueprint | Типовий процес |
|---|---|---|---|
| Environment variables | Середовище запуску процесу | — (нічого викликати не треба) | Виділений сервер, бекенд, збірковий агент |
| Local user store | Кінцевий користувач, через Save S3 Credentials |
✅ так | Настільний застосунок |
| Anonymous | Ніхто, запити не підписуються | — | Публічний бакет, підписані посилання |
| Supplied in code | Ваш код, нодою Set Static Credentials на клієнті |
✅ так | Клієнт гри з короткоживучими ключами від бекенда |
Дві схожі ноди, які діють на різні клієнти — найважливіше місце цього розділу.
Нода На що діє Set Runtime Credentials (на підсистемі) Тільки на клієнта за замовчуванням Set Static Credentials (на клієнті) На той клієнт, на якому викликана — зокрема на клієнта профілю Клієнти з
Get S3 Client For Profileберуть ключі виключно зCredential Sourceсвого ассета, іSet Runtime Credentialsвони не бачать. Це тиха невдача: сама нода не помиляється, просто профіль поводиться так, ніби ключів йому не давали. Для профілю потрібна самеSet Static Credentials.
Для профілю з Supplied in code ключі встановлюються так:
On Login Response (ваш бекенд повернув короткоживучі ключі)
│
└─► Get S3 Subsystem → Get S3 Client For Profile (SP_PlayerSaves)
│
└─► Set Static Credentials
Access Key Id : (від бекенда)
Secret Access Key : (від бекенда)
Session Token : (від бекенда)
Expires In Seconds : 3600
Ключі лишаються тільки в пам'яті: на диск нічого не пишеться, у збірці нічого немає.
Плагін припиняє ними користуватися трохи раніше за вказаний строк, щоб запит, підписаний під
самий кінець, не прибув уже після його завершення. Expires In Seconds = 0 — ключі без
терміну дії.
З C++ доступний ще й провайдер, який сам ходить по нові ключі, коли старі спливають:
UDemoS3Subsystem* Subsystem = GetGameInstance()->GetSubsystem<UDemoS3Subsystem>();
UDemoS3Client* Client = Subsystem->GetClientForProfile(ArchiveProfile);
Client->SetCredentialsProvider(
MakeShared<FDemoS3CredentialsProvider_Callback, ESPMode::ThreadSafe>(/* ваш запит до бекенда */));
Сценарій 1. Однокористувацька гра¶
Хмарні збереження плюс публічний контент — два сховища з різними правами.
Ассети:
| Ассет | Provider / бакет | Credential Source | Навіщо |
|---|---|---|---|
SP_PlayerSaves |
ваш основний, my-game-saves |
Supplied in code |
Збереження гравця: запис, приватно |
SP_PublicContent |
той самий чи інший, my-game-content |
Anonymous |
Патчі й ассети: лише читання, бакет публічний |
Граф після входу гравця (бекенд віддав короткоживучі ключі):
On Login Response
│
└─► Get S3 Subsystem → Get S3 Client For Profile (SP_PlayerSaves)
│
└─► Set Static Credentials
Access Key Id : (від бекенда)
Secret Access Key : (від бекенда)
Session Token : (від бекенда)
Expires In Seconds : 3600
Далі — звичайне збереження:
Event Save Game
│
├─► Save Game to Slot (спершу локально)
│
└─► Get S3 Subsystem → Get S3 Client For Profile (SP_PlayerSaves) → S3 Upload File
│
├─ On Success → «збережено у хмару»
└─ On Failure → лишити локальне збереження, спробувати пізніше
Публічний контент не потребує нічого:
Get S3 Subsystem → Get S3 Client For Profile (SP_PublicContent) → S3 Download File
Чому так. Anonymous для публічного бакета означає, що у збірці немає взагалі нічого, що
можна видобути. А Supplied in code для збережень — що ключі живуть лише в пам'яті процесу:
на диск не пишуться, у збірку не потрапляють, і після виходу гравця зникають разом із сесією.
Не плутайте з
Local user store. Той навмисно зберігає ключі на диск (зашифровано) — це правильно для настільного застосунку, де ключі належать користувачеві й мають пережити перезапуск, і зайве для гри, де бекенд щоразу видає нові.
Set Static Credentials діє на клієнта одразу — Forget Profile Client тут не потрібен.
Він потрібен в іншому випадку: коли ключі змінилися за джерелом (наприклад, у локальному
сховищі користувача), бо тоді клієнт продовжить користуватися вже отриманими.
Сценарій 2. Мережева гра: listen-сервер¶
Хост — і сервер, і гравець. Головне рішення — хто звертається до сховища.
Ассети:
| Ассет | Credential Source | Хто ним користується |
|---|---|---|
SP_WorldState |
Supplied in code — ключі хост отримує від вашого бекенда |
Тільки хост |
SP_PlayerUploads |
Supplied in code — свої ключі в кожного гравця |
Кожен клієнт своїм |
Стан світу — лише хост:
Event Save World State
│
└─► Switch Has Authority
├─ Authority → Get S3 Client For Profile (SP_WorldState) → S3 Upload File
│ └─ On Success → Multicast: «світ збережено»
└─ Remote → нічого не робити
Без Switch Has Authority подія виконається у всіх: сесія на вісьмох дасть вісім однакових
завантажень одного файлу, вісім оплачених запитів і непередбачуваний порядок запису.
Власні дані гравця — кожен сам. Тут кожен клієнт має отримати свої ключі з політикою,
обмеженою його префіксом (users/${user_id}/*). Спільний ключ на всіх — це можливість
перезаписати чужі дані.
Сценарій 3. Виділений сервер¶
Випадок, заради якого профілі й варто заводити: бінарник сервера у гравців не буває, тож
довгоживучі ключі тут доречні, а Environment Variable Prefix дає кілька сховищ в одному
оточенні.
Ассети:
| Ассет | Credential Source | Environment Variable Prefix | Читає змінні |
|---|---|---|---|
SP_Saves |
Environment variables |
SAVES |
SAVES_ACCESS_KEY_ID, SAVES_SECRET_ACCESS_KEY |
SP_Replays |
Environment variables |
REPLAYS |
REPLAYS_ACCESS_KEY_ID, REPLAYS_SECRET_ACCESS_KEY |
Запуск сервера:
# systemd unit, Docker, скрипт запуску - будь-що, що ставить оточення
export SAVES_ACCESS_KEY_ID=...
export SAVES_SECRET_ACCESS_KEY=...
export REPLAYS_ACCESS_KEY_ID=...
export REPLAYS_SECRET_ACCESS_KEY=...
./MyGameServer -log
Два сховища, дві пари ключів, кожна з власною політикою — і жодного рядка коду, який би їх розрізняв: профіль сам знає, який префікс читати.
Значення читаються перед кожним запитом, тож ротація ключів діє без перезапуску процесу.
А що на клієнтах. Вони до сховища не звертаються взагалі — або звертаються лише за підписаними посиланнями, які видає сервер:
Клієнт: RPC до сервера — «хочу залити збереження»
│
Сервер: перевіряє право, далі Make Presigned URL (PUT, 900 секунд)
│
└─► повертає адресу клієнтові
│
Клієнт: звичайний HTTP PUT за цією адресою
Клієнт при цьому налаштований як Anonymous — підписувати йому нічого.
Не кладіть
SP_Savesу клієнтську збірку в надії, що «там просто не спрацює». Воно справді не спрацює, але помилка виглядатиме якAuthentication Errorбез очевидної причини. Якщо клієнту потрібен доступ до того самого бакета — заведіть окремий профіль зAnonymousабоLocal user store.
Сценарій 4. Настільний застосунок¶
Бакет належить користувачеві, тож ключі — його, а не ваші.
Ассет: SP_UserStorage, Credential Source = Local user store,
Local Store Profile Name = default.
(натиснуто «Підключитися» на екрані налаштувань)
│
└─► Save S3 Credentials (Profile Name: default, ключі з полів вводу)
│
└─► Forget Profile Client (SP_UserStorage)
│
└─► Get S3 Client For Profile (SP_UserStorage) → S3 List Objects (Max Keys 1)
├─ On Success → до основного екрана
└─ On Failure → показати помилку одразу, поки користувач ще тут
Перевірка одразу після введення — те, що економить найбільше підтримки: та сама помилка через півгодини під час першого справжнього збереження вже нічого користувачеві не пояснює.
Кілька застосунків на одній машині — дайте кожному своє Local Store Profile Name.
Сховище до того ж солиться назвою проєкту, тож два різні застосунки не прочитають ключі один
одного навіть за однакового імені профілю.
Зведена таблиця¶
| Сценарій | Профіль | Credential Source | Де насправді лежать ключі | Blueprint? |
|---|---|---|---|---|
| Сингл: збереження | SP_PlayerSaves |
Supplied in code | Лише в пам'яті, Set Static Credentials |
✅ |
| Сингл: публічний контент | SP_PublicContent |
Anonymous | Ніде — підпису немає | ✅ |
| Listen: стан світу | SP_WorldState |
Supplied in code | Лише в пам'яті, у хоста | ✅ |
| Виділений сервер | SP_Saves, SP_Replays |
Environment variables | Оточення процесу, свій префікс на профіль | ✅ |
| Клієнт при виділеному сервері | SP_PublicRead |
Anonymous | Ніде; доступ через підписані посилання | ✅ |
| Настільний застосунок | SP_UserStorage |
Local user store | Зашифрований файл у теці користувача | ✅ |
Типові помилки¶
| Помилка | Що видно | Що робити |
|---|---|---|
Environment variables у збірці для гравців |
Authentication Error на кожній операції |
Це серверне джерело. Клієнту — Anonymous, Local user store або підписані посилання |
Set Runtime Credentials разом із профілем |
Профіль поводиться так, ніби ключів не давали | Ця нода діє лише на клієнта за замовчуванням. Для профілю — Set Static Credentials на його клієнті |
| Ключі змінили в локальному сховищі, а працюють старі | Стара відмова 403 повторюється | Forget Profile Client — клієнт кешує вже отримане. Для Set Static Credentials не потрібен: вона замінює ключі одразу |
| Два профілі, ключі лише у спільній редакторській секції | Запит до одного сховища підписаний ключем від іншого | Заповніть Editor Access Key Id на самому профілі — див. 3. Облікові дані |
Порожній Local Store Profile Name у двох профілів |
Обидва читають одні й ті самі ключі | Дайте кожному своє ім'я |