Цю сторінку ще не перекладено — показано англійську версію.
Profile
Профіль — це подання, і платформі в ньому майже нічого не належить. Про гравця платформа зберігає лише player_id і системний профіль за ним: облікові записи, сесії, прив’язки до провайдерів — усе це в Auth & Players. Усе, що гравець має, — це ваші власні сутності, якими він володіє. Профіль — це набір таких сутностей, який оголошує ваш проєкт і який читають для одного власника за один прохід.
Коли це потрібно
- Екрану потрібні дані одного гравця за один виклик. Оголошений набір охоплює всі сутності (entity) цього гравця, тож клієнтові не доводиться зшивати кілька запитів.
- Екрани платформи мають показувати людину, а не ідентифікатор: таблиця лідерів, черга модерації й тікет підтримки зберігають лише
player_id, доки проєкт не вкаже запис, яким відображати гравця. - Потрібна картка іншого гравця. Це те саме читання, але для іншого власника, звужене предикатом рядків (row predicate) і маскою полів, які вже оголошено в Access & Roles.
- HUD має стежити за станом гравця наживо: читання — це вибірка (selection), а на вибірку можна підписатися.
- Не використовуйте його, якщо дані не належать гравцеві. Спільні й глобальні рядки читають звичайною вибіркою Entity: у них немає власника, від якого можна було б розгорнути читання.
Хто що робить
| Актор | Що робить на цій сторінці |
|---|---|
schema-author | позначає, які сутності належать гравцеві, і оголошує, які з них входять до профілю |
player | читає власний профіль, а записує безпосередньо в самі сутності |
room-visitor | читає профіль іншого гравця в межах, які дозволяють предикат і маска цього гравця |
Коротко
Належність до профілю оголошують для сутності цілком, а не для окремих полів. Сутність повідомляє, що входить до профілю, а що з неї побачить інший гравець, визначає маска полів (column mask) ролі, яка її читає (Access & Roles).
loadout і progress належать гравцеві й входять до набору профілю[Entity("loadout"), OwnedBy(Owner.Player), InProfile]
public class Loadout { public string Primary = ""; }
[Entity("progress"), OwnedBy(Owner.Player), InProfile]
public class Progress
{
public int Level;
public string Title = "";
public int SecretMmr; // no reading role's mask names it: it stays server-side
}@Entity('loadout') @OwnedBy(Owner.player) @InProfile()
export class Loadout { primary = ''; }
@Entity('progress') @OwnedBy(Owner.player) @InProfile()
export class Progress {
level = 0;
title = '';
secretMmr = 0; // no reading role's mask names it: it stays server-side
}@entity("loadout")
@owned_by(Owner.PLAYER)
@in_profile
class Loadout:
primary: str = ""
@entity("progress")
@owned_by(Owner.PLAYER)
@in_profile
class Progress:
level: int = 0
title: str = ""
secret_mmr: int = 0 # no reading role's mask names it: it stays server-sideЯк оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
UCLASS(PSEntity = "loadout", PSOwnedBy = "Player", PSInProfile)
class ULoadout : public UObject
{
GENERATED_BODY()
UPROPERTY() FString Primary;
};
UCLASS(PSEntity = "progress", PSOwnedBy = "Player", PSInProfile)
class UProgress : public UObject
{
GENERATED_BODY()
UPROPERTY() int32 Level;
UPROPERTY() FString Title;
UPROPERTY() int32 SecretMmr; // no reading role's mask names it: it stays server-side
};
// declarations compile into the same pushed model — playserv push from the UE project or CI[Entity("loadout"), OwnedBy(Owner.Player), InProfile]
public class Loadout { public string Primary = ""; }
[Entity("progress"), OwnedBy(Owner.Player), InProfile]
public class Progress
{
public int Level;
public string Title = "";
public int SecretMmr; // no reading role's mask names it: it stays server-side
}Читання профілю — це вибірка за власником: той самий інтерфейс запитів Entity, лише власника зафіксовано, а список сутностей узято з оголошення. Profile — це назва такого читання, а не модуль, що стоїть за ним. Права ті самі, предикати ті самі, фільтри ті самі, підписка (subscription) та сама, бо це та сама операція.
var mine = playserv.Profile.Mine(); // a selection, not a record
var rows = await mine.Query(); // loadout + progress, one pass
mine.Subscribe(changed => Hud.Refresh(changed)); // the selection stays live
var rival = await playserv.Profile.Of(rivalId).Query(); // only what the mask leavesconst mine = playserv.profile.mine(); // a selection, not a record
const rows = await mine.query(); // loadout + progress, one pass
mine.subscribe((changed) => hud.refresh(changed)); // the selection stays live
const rival = await playserv.profile.of(rivalId).query(); // only what the mask leavesmine = playserv.profile.mine() # a selection, not a record
rows = await mine.query() # loadout + progress, one pass
mine.subscribe(lambda changed: hud.refresh(changed)) # the selection stays live
rival = await playserv.profile.of(rival_id).query() # only what the mask leavesЯк оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
TPSSelection<UPSProfile> MyProfile = Client->Entities->Of<UPSProfile>()->Select().GetMine(); // a selection, not a record
MyProfile.Then(TPSOnResult<FPSProfileRows>::CreateWeakLambda(this, [this](const TPSResult<FPSProfileRows>& Result)
{
if (!Result.HasValue()) { return; }
Hud->ShowProfile(Result.Value()); // loadout + progress, one pass
}));
TPSSubscription ProfileWatch = MyProfile.Subscribe(
[this](const FPSProfileChange& Changed) { Hud->Refresh(Changed); });
Client->Entities->Of<UPSProfile>()->Get(RivalId,
TPSOnResult<FPSProfileRows>::CreateWeakLambda(this, [this](const TPSResult<FPSProfileRows>& Rival)
{
if (!Rival.HasValue()) { return; }
Hud->ShowRival(Rival.Value());
}));
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.var mine = playserv.Profile.Mine(); // a selection, not a record
var rows = await mine.Query(); // loadout + progress, one pass
mine.Subscribe(changed => Hud.Refresh(changed)); // the selection stays live
var rival = await playserv.Profile.Of(rivalId).Query(); // only what the mask leavesМодель
| Поняття | Що це |
|---|---|
player_id | усе, що платформа знає про гравця, плюс системний профіль за ним — див. Auth & Players |
| набір профілю | сутності гравця, які проєкт оголосив своїм профілем. Оголошувати набір не обов’язково |
| вибірка за власником | саме читання: на вході один власник, на виході його рядки з усього набору. Права, предикати, фільтри й підписка ті самі, що в будь-якої вибірки Entity |
| публічне читання | та сама вибірка для іншого власника, звужена предикатом рядків і маскою полів тієї ролі (role), яка читає, — див. Access & Roles |
Що справджується для будь-якого читання профілю.
| Завжди | Що це |
|---|---|
окремого запису профілю немає | профіль не має ні власного ідентифікатора, ні ревізії, ні історії, ні життєвого циклу, бо це подання (view) над рядками, у яких усе це є |
писати треба туди, де лежать дані | оновіть рядок progress, і кожне читання профілю, що його охоплює, побачить нове значення під час наступного проходу |
публічно записати не можна | у подання нема чого записувати, а спільний стан, який можуть змінювати різні гравці, змінюють через серверний код |
володіння — це предикат | а не стовпець, який додає платформа (Access & Roles). owner == caller.player — лише один варіант цього механізму, «один з учасників» — інший |
похідне поле — спр ава хука | коли Level перетинає поріг, Progress.Title проставляє саме спостерігач після зміни. Він іде слідом за записом і відхилити його не може |
Помилки
- Якщо проєкт не оголосив набір профілю, читати профіль нема чого, і виклик відхиляють як unavailable. Порожнього результату не буде: він означав би, що профіль у гравця є, просто порожній.
- Рядок, який приховує предикат, дає
not found, так само як рядок, якого не існує: публічне читання ніколи не допоможе дізнатися, що саме існує, але приховане. - Поля поза маскою ролі, яка читає, у відповіді просто немає — воно не приходить порожнім.
- Запис від імені гравця, у якому гравця не вказано, — відмова валідації.
- Оголосити набір профілю — це дія над схемою (schema):
fnабоadm. Спробу з ключем гравця (player key) відхиляють як forbidden, а завантаження не приймають навіть частково — його відхиляють цілком.
Обмеження
Конкретні числа з’являться разом із розділом про ліміти платформи, а що відбувається на кожній стелі, визначено вже зараз.
- Розмір сторінки вибірки за власником: результат обрізають до ліміту, а прапорець «є ще» лишається ввімкненим. Повертати менше без цього прапорця заборонено.
- Розмір вкладеного набору на рядок: обрізають за тим самим правилом, а прапорець ставлять на вкладенні.
- Частота змін одного екземпляра: відмова з категорії rate limit із часом, після якого можна повторити. Читання профілю — така сама вибірка, як будь-яка інша, тож воно успадковує ліміти сутності, а не оголошує власні.