Цю сторінку ще не перекладено — показано англійську версію.
Extensibility
Кожен сценарій платформи — це ланцюжок зареєстрованих функцій: замініть ланку або обгорніть її. Поведінку платформи ви змінюєте, підміняючи її кроки, а не форкаючи її код.
Коли це потрібно
- Крок платформи має виконувати вашу логіку: оголосіть заміну іменованої ланки через
[Override(…)]. - Навколо кроку потрібні перевірки чи побічні ефекти: упорядковані проміжні обробники (middleware)
Before/After, які можуть заборонити крок або повідомити про нього. - Код має запускатися за розкладом, за подією чи через вебхук: тригери передають вам уже розібраний типізований контекст.
- Треба знати, що насправді виконається, ще до деплою: проженіть ланцюжок у тестовому режимі (dry run) і прочитайте остаточний порядок.
- Не потрібно, якщо правило стосується записів однієї сутності (entity). Для цього є легший варіант — хук (hook) у Data & Subscriptions.
Хто що робить
| Актор | Що робить на цій сторінці |
|---|---|
backend-service | перевизначає ланки, обгортає кроки проміжними обробниками, пише обробники тригерів |
operator | переглядає ланцюжки, задає порядок, читає секрети, перевіряє розв’язання порядку в тестовому режимі |
Коротко
SignIn, проміжний обробник навколо grant і код за cron-розкладом// gate one named step of the auth scenario — a before hook may refuse, fail-closed
[Before(Auth.SignIn)]
public static Task<Verdict> GateRegion(SignInAttempt a) =>
a.Region == "sanctioned"
? Hook.Reject(Problem.Forbidden, "region not served")
: Hook.Continue(a);
// wrap a step with ordered middleware
PlayServ.Extend.Scenario("commerce.purchase")
.Before("grant", LogPurchaseIntent)
.After("grant", NotifySquad, order: 10);
// customer code on a trigger
[OnSchedule("0 4 * * *")]
public static async Task NightlyCleanup() { ... }// gate one named step of the auth scenario — a before hook may refuse, fail-closed
export const gateRegion = before(Auth.signIn, (a: SignInAttempt) =>
a.region === 'sanctioned'
? Hook.reject(Problem.forbidden, 'region not served')
: Hook.continue(a));
// wrap a step with ordered middleware
playserv.extend.scenario('commerce.purchase')
.before('grant', logPurchaseIntent)
.after('grant', notifySquad, { order: 10 });
// customer code on a trigger
export const nightlyCleanup = onSchedule('0 4 * * *', async () => { /* ... */ });# gate one named step of the auth scenario — a before hook may refuse, fail-closed
@before(auth.sign_in)
async def gate_region(a):
if a.region == "sanctioned":
return hook.reject(problem.FORBIDDEN, "region not served")
return hook.cont(a)
# wrap a step with ordered middleware
playserv.extend.scenario("commerce.purchase") \
.before("grant", log_purchase_intent) \
.after("grant", notify_squad, order=10)
# customer code on a trigger
@on_schedule("0 4 * * *")
async def nightly_cleanup(): ...Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
Перевизначення, проміжні обробники й тригери виконуються як хмарні функції на платформі, а не в рушії. Пишіть їх на C#, TypeScript або Python, а Unreal підписується на події, які вони породжують. Згодом можна буде писати функції і в рушії: графами Blueprint і на C++ (з трансляцією в скриптовий формат, який перевіряє платформа). І те, й інше аналізують і публікують, як будь-яке оголошення, а виконується код однаково на платформі.
Перевизначення, проміжні обробники й тригери виконуються як хмарні функції на платформі, а не в рушії. Пишіть їх на C#, TypeScript або Python, а Unity підписується на події, які вони породжують.
Модель
До чого платформа дає під’єднатися.
| Термін | Що це |
|---|---|
зареєстрована функція | один крок платформи, який можна перевизначити, — «створити профіль», «визначити ціну» |
scenario | упорядкований ланцюжок, який виконує процес платформи: автентифікація, вхід, покупка |
overridability | чи можна ланку замінити, лише обгорнути або її змінювати не можна |
middleware | упорядкований обробник до чи після ланки |
trigger | те, що запускає ваш код: подія, розклад, вебхук |
secret | значення, яке може читати ваш обробник |
invocation | один запуск разом із його трасуванням |
Що оголошує хук.
| Оголошує | Що це |
|---|---|
position | іменований крок, до якого під’єднується хук |
kind | gatekeeper — перевірка допуску чи валідація. У разі збою він блокує: якщо зламався сам хук, крок не виконується. Або observer — лог, сповіщення, лічильник. У разі збою він пропускає: крок виконується, а про збій однаково повідомляють, а не замовчують його. Значення за замовчуванням немає |
moment | before — до валідації: хук отримує типізовані дані й може змінити їх або відхилити. Або after — після того, як крок зафіксовано: хук отримує запит і результат, дає лише побічні ефекти й ніколи не може зірвати операцію чи змінити відповідь |
effect | що хук робить, а не лише де він стоїть. Тоді порядок описують через саму роботу, а не через числа, і він не зламається, коли хтось додасть свій хук поруч із вашим |
версія і умова | обробник, що діє лише для одного середовища чи аудиторії, — це оголошена версія хука, а не гілка в його тілі. Саме її вмикає й вимикає перемикач у панелі |
Три способи під’єднати код.
| Спосіб | Для чого |
|---|---|
атрибут [Before(Step)] / [After(Step)] | одне правило на одному іменованому кроці — так пишуть більшість хуків |
атрибут [Override(Link)] | повна заміна реалізації ланки |
Extend.Scenario("…").Before("link", fn, order: n) | обгортка ланки всередині ланцюжка, коли важливий порядок відносно інших проміжних обробників |
| Завжди | Що це |
|---|---|
усі три способи | деплояться через playserv push |
два способи з атрибутами | їх показує панель, бо оголошення передає назву кроку чи ланки в опубліковану модель |
спосіб із проміжним обробником | натомість несе порядок, а саме він і потрібен ланцюжку |
призначення під час запуску | (Scenario.OnX = fn) і далі доступне для обробника, якому не треба з’являтися в дереві адмін-панелі |
заміна однієї ланки | сусідні ланки лишаються недоторканими, і жодна з них не знає, яка реалізація відповіла — власний крок платформи чи ваш |
Куди йде виклик і що справджується для будь-якого маршруту.
| Завжди | Що це |
|---|---|
чотири напрямки | хмарна функція · зовнішній бекенд споживача · ігровий сервер · інший оголошений напрямок |
маршрутизатор | працює на повідомленнях і сигналах; request-response — лише один з адаптерів над ним, а не його суть |
зіставлення | відбувається за оголошеною назвою операції чи сигналу і ні за чим іншим: ні за формою даних, ні за тим, хто викликає, ні за навантаженням |
напрямок | не входить до контракту операції: якщо перенести обробник в інший напрямок, сумісність не зламається |
«ігровий сервер» | визначається тим, чим він є, а не тим, хто його хостить: наш парк серверів і власний хостинг студії — це один напрямок, і в оголошенні немає позначки, чия це інфраструктура |
RPC ігрового сервера | реєструються в тому самому маршрутизаторі: оголосити RPC й означає зареєструвати його, іншого способу немає |
порядок | проміжні обробники виконуються згори донизу. Якщо крок має кілька реалізацій, маршрутизатор перебирає їх зліва направо за умовою, а коли жодна не підійшла, відповідає версія, позначена як версія за замовчуванням |
Ланцюжок із перевизначеннями й проміжними обробниками можна розв’язати й прочитати, ще нічого не запускаючи. Остаточний порядок видно в панелі та з коду.
Що таке обмеження між хуками і коли його перевіряють.
| Що це | |
|---|---|
назване обмеження | точка розширення може назвати ефекти, які вона обмежує, — перевірка античиту має йти перед розміщенням, чек вимагає списання коштів у цій точці, — і більше нічого не обмежувати |
неназваний ефект | нічим не обмежений, але й не заборонений |
порушення | дефект конфігурації. Відмова називає обидва хуки й обмеження, яке вони порушили; це не попередження і не мовчазна зміна порядку |
коли перевіряють | під час кожної дії, яка може змінити, що виконується в точці: реєстрації, деплою, зміни розстановки. Тож розстановка, яка дійшла до виконання, уже пройшла перевірку |
без повторної перевірки під час виконання | прийняту розстановку під час виконання вдруге не перевіряють |
| Завжди | Що це |
|---|---|
обробники | типізовані на вході й виході: жодних dynamic і жодних «мішків» контексту. Дескриптор платформи доступний неявно, а контекст виклику — хто викликав, тригер, трасування — приходить уже розібраним |
що бачить збірка рушія | події, які сценарій надсилає потім, бо перевизначення чи проміжний обробник виконується на платформі, а середовище виконання рушія для цього не призначене. Саме це показують вкладки рушіїв у прикладах на цій сторінці |
[Rpc("resolve_price", Default = true)]
public static Price ResolvePrice(Sku sku) => Pricing.Base(sku);
[Rpc("resolve_price", When = "env == 'staging'")]
public static Price ResolvePriceStaging(Sku sku) => Pricing.WithDiscount(sku, 0.5f);
// a hook can carry a version too, gated by its own condition
[After("grant", When = "audience == 'beta'")]
public static void NotifySquadBeta(GrantResult r) => Messaging.PingBeta(r.Squad);export const resolvePrice = rpc('resolve_price', { default: true },
(sku: Sku) => Pricing.base(sku));
export const resolvePriceStaging = rpc('resolve_price', { when: "env == 'staging'" },
(sku: Sku) => Pricing.withDiscount(sku, 0.5));
// a hook can carry a version too, gated by its own condition
export const notifySquadBeta = after('grant', { when: "audience == 'beta'" },
(r: GrantResult) => Messaging.pingBeta(r.squad));@rpc("resolve_price", default=True)
def resolve_price(sku: Sku) -> Price:
return pricing.base(sku)
@rpc("resolve_price", when="env == 'staging'")
def resolve_price_staging(sku: Sku) -> Price:
return pricing.with_discount(sku, 0.5)
# a hook can carry a version too, gated by its own condition
@after("grant", when="audience == 'beta'")
def notify_squad_beta(r: GrantResult):
messaging.ping_beta(r.squad)Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
Перевизначення, проміжні обробники й тригери виконуються як хмарні функції на платформі, а не в рушії. Пишіть їх на C#, TypeScript або Python, а Unreal підписується на події, які вони породжують. Згодом можна буде писати функції і в рушії: графами Blueprint і на C++ (з трансляцією в скриптовий формат, який перевіряє платформа). І те, й інше аналізують і публікують, як будь-яке оголошення, а виконується код однаково на платформі.
Перевизначення, проміжні обробники й тригери виконуються як хмарні функції на платформі, а не в рушії. Пишіть їх на C#, TypeScript або Python, а Unity підписується на події, які вони породжують.
Власну логіку платформи можна завантажити, відредагувати й задеплоїти знову. Це такі самі функції, як ваші:
playserv functions pull matchmaking.match # the platform's implementation, as source
# edit: widen the skill window for the weekend event
playserv push # your version registers; the default stays as fallback
Тут ви налаштовуєте логіку: перевизначення, проміжні обробники й версії, про які йдеться на цій сторінці.
До сутностей платформи не можна додавати поля
Гравець, кімната, запис у таблиці лідерів і замовлення — це системний стан із власними машинами станів, а не основа вашої моделі даних. Ваші дані — це ваша власна сутність, оголошена так, як описано на сторінці Схема в коді і прив’язана до стану платформи предикатом: власник — цей гравець, область — ця кімната. Так дані лишаються вашими, навіть коли змінюється модель самої платформи.
Помилки
- Назва, якій не відповідає жоден зареєстрований обробник, отримує
not found: виклик ніколи не зникає тихо лише тому, що його ніхто не слухав. - Назву, зареєстровану двічі, і набір, де дві реалізації претендують на ту саму умову, відхиляють ще під час оголошення набору: на деплої, а не під час виклику.
- Відмова хука несе власний код і причину хука, тож «правило гри сказало „ні“» ніколи не виглядає як збій транспорту.
- Хук, що зазнав збою, поводиться згідно з оголошеним видом —
fail-openабоfail-closed, — і цей вид оголошено заздалегідь, а не виведено з того, що сталося. - Хук не може змінити те, що вже заявлено: ні власника, ні ціль, ні таблицю чи розмову, якій адресовано виклик. Він виправляє вхідні дані й повертає вердикт.
Обмеження
Конкретні числа з’являться разом із розділом про обмеження платформи, а що стається на кожній межі, визначено вже зараз.
- Граничний час виконання хука: коли його перевищено, діє поведінка у разі збою, яку оголошує вид хука.
- Кількість хуків на одній позиції і реалізацій одного методу: реєстрацію ще одного відхиляють.
- Глибина вкладеності «хук викликає операцію, у якої теж є хуки»: оголошена відмова, а не вичерпання ресурсів.
- Розмір контексту, який передають хукові: обрізати його заборонено, тож відхиляють реєстрацію, а не передають обробникові половину контексту.
Уроки й рецепти: таблиця лідерів у Tanks використовує хуки цього модуля, а щоденний турнір цілком побудовано на ньому.