Цю сторінку ще не перекладено — показано англійську версію.
Auth & Players
Вхід — це крок, який можна перевизначити, а не чорна скринька. Провайдери, сесії, прив’язка облікових записів, бани. Кожна точка сценарію — до і після входу, до і після прив’язки, до і після об’єднання, під час зміни статусу — оголошена як точка розширення певного виду. Це або перевірка, яка може відхилити крок, або спостерігач, який цього зробити не може.
Коли це потрібно
- Гравці (player) мають входити в гру: через пристрій, email, Apple, Google, Steam або власний провайдер. Щоб гравця створювало під час першого входу (sign-in), досить прапорця — окремий сценарій не потрібен.
- Гостьовий акаунт згодом треба перетворити на постійний:
Linkдодає Steam і зберігає прогрес, а об’єднання (merge) зводить два акаунти в одного гравця. - Правила мають виконуватися там, де їх не обійти: перевірка регіону перед входом, стартовий набір після того входу, що створив гравця.
- Модерація має діяти одразу: відкликати сесії, тимчасово блокувати, банити пристрій. Подію (event)
bannedчує кожна запущена система. - Оголошений контекст (регіон, платформа, збірка) має доходити до кожного наступного хука, щоб жоден із них не перечитував гравця лише заради цього.
- Полегшеної альтернативи немає. Ус і інші модулі визначають, хто їх викликає, саме через цей модуль, тому
authне вимкнути, поки хоч одному з них потрібен актор-гравець: конфігуратор модулів відмовить і назве модулі, які від нього залежать.
Хто що робить
| Актор | Що робить на цій сторінці |
|---|---|
player | входить, прив’язує й відв’язує облікові записи (identity), оновлює сесію, виходить |
moderator | відкликає сесії; банить, тимчасово блокує або відновлює гравців |
backend-service | не пускає до входу з певних регіонів; заповнює перші рядки даних нового гравця; читає й відкликає сесії |
Коротко
SignIn на провайдера, створення гравця під час першого входу вмикає прапорець, Link додає Steam// client — one call per provider; create-on-first-sign-in is a flag
var session = await PlayServ.Auth.SignIn(Provider.Device, create: true);
await PlayServ.Auth.Link(Provider.Steam); // one player, many identities// client — one call per provider; create-on-first-sign-in is a flag
const session = await PlayServ.auth.signIn(Provider.Device, { create: true });
await PlayServ.auth.link(Provider.Steam); // one player, many identities# client — one call per provider; create-on-first-sign-in is a flag
session = await playserv.auth.sign_in(Provider.DEVICE, create=True)
await playserv.auth.link(Provider.STEAM) # one player, many identitiesЯк оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
// client — one call per provider; create-on-first-sign-in is a flag
Client->Auth->SignInWithProvider(FPSProviderId::Device, Credential,
TPSOnResult<FPSSession>::CreateWeakLambda(this, [this](const TPSResult<FPSSession>& Result)
{
if (!Result.HasValue()) { return; }
// one player, many identities — add Steam to the same account
Client->Auth->Providers->Link(FPSProviderId::Steam, SteamCredential);
}));
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.// client — one call per provider; create-on-first-sign-in is a flag
var session = await PlayServ.Auth.SignIn(Provider.Device, create: true);
await PlayServ.Auth.Link(Provider.Steam); // one player, many identitiesКожну точку налаштовують там, де її оголошено. Усі форми, яких може набувати обробник, зібрано на сторінці Extensibility:
[Before(Auth.SignIn)] // a gate: it may refuse, and it is fail-closed
public static Verdict GateRegion(SignInAttempt a) =>
a.Region == "sanctioned"
? Hook.Reject(Problem.Forbidden, "region not served")
: Hook.Continue(a);
[After(Auth.SignIn, created: true)] // an observer: it watches, it cannot refuse
public static async Task GrantStarterPack(Player player)
{
await player.Inventory.Grant("chest.gold", count: 1);
}// a gate: it may refuse, and it is fail-closed
export const gateRegion = before(Auth.signIn, (a: SignInAttempt) =>
a.region === 'sanctioned'
? Hook.reject(Problem.forbidden, 'region not served')
: Hook.continue(a));
// an observer: it watches, it cannot refuse
export const grantStarterPack = after(Auth.signIn, { created: true },
async (player: Player) => {
await player.inventory.grant('chest.gold', { count: 1 });
});@before(auth.sign_in) # a gate: it may refuse, and it is fail-closed
def gate_region(a: SignInAttempt) -> Verdict:
if a.region == "sanctioned":
return Hook.reject(Problem.FORBIDDEN, "region not served")
return Hook.continue_(a)
@after(auth.sign_in, created=True) # an observer: it watches, it cannot refuse
async def grant_starter_pack(player: Player):
await player.inventory.grant("chest.gold", count=1)Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
І перевизначення, і хук — хмарні функції: вони виконуються на платформі, а не в рушії. Пишіть їх на C#, TypeScript або Python, а Unreal підписується на події, які вони породжують. Функції, написані в самому рушії, заплановано: графи Blueprint і C++ (з трансляцією в скриптовий формат, який перевіряє платформа). Їх аналізують і надсилають на платформу так само, як будь-яке оголошення, а виконуються вони все одно на платформі.
І перевизначення, і хук — хмарні функції: вони виконуються на платформі, а не в рушії. Пишіть їх на C#, TypeScript або Python, а Unity підписується на події, які вони породжують.
Панель показує саме оголошену форму: для кожної точки видно її обробники, їхній вид і остаточний порядок виклику. Від виду залежить, що станеться, коли обробник упаде:
| Вид | Коли падає сам обробник | У разі відмови |
|---|---|---|
| перевірка (gate) | платформа відхиляє крок: перевірка регіону, до якої не вдалося достукатися, не вважається пройден ою | код із каталогу платформи й зрозуміла людині причина. Код, що викликає, розгалужується за кодом, а текст причини може змінюватися й перекладатися |
| спостерігач (observer) | крок лишається виконаним: якщо стартовий набір не видано, гравець недорахується скрині, але вхід не зірветься | відмовити не може |
Хук ніколи не вирішує, хто саме ввійшов
Перевірка лише відповідає «так» чи «ні» щодо облікового запису, який платформа вже встановила. Вона не називає гравця, не видає обліковий запис і не заміняє підтвердження від провайдера.
Модель
Гравець — це носій облікових записів, а не рядок у вашій схемі, а його ідентифікатор стабільний, і його ніколи не видають повторно — навіть після об’єднання: id об’єднаного гравця й далі веде до гравця, а не перетворюється на висяче посилання. Прив’язка — це трійка: провайдер · зовнішній суб’єкт · гравець.
| Завжди | Що це |
|---|---|
провайдер + суб’єкт | унікальна пара, і ця унікальність породжує конфлікт, а не заборону: якщо акаунт уже зайнятий, вихід — обрати об’єднання |
щонайбільше одна прив’язка на провайдера в гравця | другий акаунт того самого провайдера — конфлікт |
зовнішній суб’єкт | ніколи не стає ідентифікатором гравця. Він належить провайдерові, і якби ми взяли його за свій, наші id залежали б від чужих |
тип облікового запису й статус доступу | — різні осі. anonymous чи registered — одна, active / suspended / banned — інша, тож можна описати і «забаненого аноніма», і «тимчасово заблокованого зареєстрованого» |
сесія й облікові дані | — різні речі. Сесія — це запис, а облікові дані (credential) — те, що ви пред’являєте. Коли сесію відкликають, усі її облікові дані стають недійсними, а її відкриті підписки (subscription) закриваються |
кілька одночасних сесій | кожну можна відкликати окремо |
твердження в облікових даних | — оголошений контекст (регіон, локаль) і лише контекст. Твердження (claim) ніколи не дає повноважень |
відбиток пристрою | — не обліковий запис. Він ніколи не буде підставою впустити, лише підставою відмовити. Пл атформа зберігає й порівнює його тільки в незворотному вигляді |
Три машини станів.
| Машина | Стани |
|---|---|
| тип облікового запису | anonymous → registered, і перехід односторонній |
| статус доступу | active ⇄ suspended, а коли бан знімають — active → banned → active |
| гравець | alive → merged, причому merged — кінцевий стан: об’єднаний гравець більше не входить |
Що оголошуєте ви.
| Оголошення | Що це |
|---|---|
політика входу | чи дозволено анонімний вхід, а також решта правил входу. Її оголошують атрибутом на точці монтування (mount point) модуля — не конфігом поруч із кодом і не під час виконання |
роль за замовчуванням | набір ролей (role), з яким новий гравець приходить під час першого входу. Для ролі за замовчуванням немає значення за замовчуванням: якщо нічого не оголосити, нові гравці прийдуть без ролей, і це повноцінне оголошення, а не пропуск |
політика сесій | що робити, коли досягнуто стелі одночасних сесій: витіснити найстарішу й надіслати подію або відмовити новій. Значення за замовчуванням немає |
політика видалення | як видалення гравця доходить до даних, що на нього посилаються |
Де налаштовують провайдера. На операторському рівні, а не в коді: обліковим даним магазину не місце в репозиторії. Те, що оголошено, видно в адмін-консолі лише для перегляду.
Що дає видача ролі. Ролі — справа не лише оператора. В інтерфейсі гравця є операції видачі й відкликання, тож гра може власним кодом підвищити гравця до офіцера гільдії або передати ведучому турніру його повноваження.
| Завжди | Що це |
|---|---|
це не самопризначення | щоб видати роль, потрібен оголошений для цього атомарний дозвіл (permission atom). Актор без нього отримає звичайний forbidden, а не мовчазну відсутність дії |
видача ідемпотентна | видати роль, яку гравець уже має, — успіх, а не конфлікт. Стан — це набір ролей, а не історія викликів, тож, на відміну від входу, цій операції не потрібен ключ ідемпотентності |
зміна ролей не миттєва | вона набирає чинності без перевипуску облікових даних і стає помітною не пізніше за оголошену межу застарівання кешу прав. Якщо код видає роль і одразу перевіряє її на підключеному клієнті, це вікно треба враховувати |
З чого складаються ролі й що вони відкривають, описано на сторінці Access & Roles.
Помилки
- Облікових даних немає або вони прострочені — відповідь not authenticated, і це виправляє оновлення. Відкликані облікові дані дають ту саму відповідь, але оновлення тут не допоможе — лише новий вхід.
- Облікові дані, які вже замінили під час ротації, пред’явили знову — це конфлікт. Саме завдяки цьому ротацію помічають, а не мовчки пропускають.
- Забанений або тимчасово заблокований гравець, а також забанений відбиток отримують forbidden, і повторювати спробу марно.
- Пара провайдер+суб’єкт зайнята — конфлікт; спробу можна повторити, коли оберете об’єднання. Другий акаунт того самого провайдера — конфлікт, який повтор не змінить.
- Спроба відв’язати останній спосіб входу — відмова валідації: після неї до акаунта ніхто не зможе дістатися.
- Об’єднати вже об’єднаного гравця не можна — це конфлікт, бо
merged— кінцевий стан. - Коли провайдер недоступний, відповідь — unavailable, і спробу варто повторювати зі зростаючою паузою (backoff). Коли провайдер відхиляє облікові дані, відповідь — not authenticated, і тут вистачить однієї повторної спроби, а не циклу.
- Перевищено частоту спроб входу — відповідь із категорії rate limit і час, після якого можна спробувати знову.
Обмеження
Конкретні числа з’являться разом із розділом про ліміти платформи, а що відбувається на кожній стелі, визначено вже зараз.
- Одночасні сесії на гравця: залежно від оголошеної політики — витіснення найстарішої з подією або відмова новій. Значення за замовчуванням немає.
- Спроби входу за період і спроби прив’язати зайняту пару: обмеження частоти з часом, після якого можна повторити, а лічильник спроб лишається в історії.
- Прив’язки на гравця: прив’язку ще одного провайдера платформа відхиляє як конфлікт.
- Строк дії облікових даних: not authenticated, повторити можна після оновлення. Строк дії облікових даних для оновлення: лише новий вхід.
- Зберігання (retention) анонімного гравця без жодного входу: видалення за оголошеною політикою, з подією. Політику оголошують явно, значення за замовчува нням немає.
- Записи в бан-списку відбитків: нове додавання платформа відхиляє, а старі записи ніколи не витісняє мовчки.