Цю сторінку ще не перекладено — показано англійську версію.
Leaderboards
Одна модель таблиці, осі якої складаються в будь-який вид таблиці лідерів: щоденні рейтинги, таблиці найкращого кола, суми по гільдіях, сезони, турніри.
Коли це потрібно
- Результати мають ранжувати гравців (щоденний рейтинг, найкраще коло, сума по гільдії) в межах однієї оголошеної моделі, а не окремої системи під кожну таблицю лідерів (leaderboard).
- Вам потрібні стандартні читання — топ-N, вікно навколо гравця, рядки для заданого списку власників — без додаткового моделювання даних.
- Цикли мають закриватися за розкладом, іти в архів (а не зникати) і викликати хук нагороди з фінальною таблицею.
- Підозрілі результати ніколи не повинні потрапляти в таблицю: хук перед відправкою перевіряє їх, обрізає або відхиляє з типізованою причиною.
- Турнір — це та сама таблиця, тільки з вікном реєстрації (entry window), максимальною кількістю учасників і кількістю спроб за цикл.
- Не знадобиться, якщо число ніколи не порівнюють між гравцями. Особистий лічильник чи підсумок за всю кар’єру — це звичайні дані. Модуль упорядковує результати, але ніколи їх не обчислює й не будує сіт ки на вибування.
Хто що робить
| Актор | Що робить на цій сторінці |
|---|---|
player | читає топ-N, вікно навколо себе й власне місце, підписується на зміни свого місця |
backend-service | надсилає результати; виправляє або відхиляє їх у хуку перед відправкою; видає нагороди, коли цикл закривається |
operator | оголошує таблиці; достроково закриває цикл, виправляє записи (з аудитом), стежить за частотою відправок |
Коротко
weekly-score: власник, агрегація, скидання в понеділок, відправка із сервера, ключ сортування[Leaderboard("weekly-score")]
public static class WeeklyScore
{
public static Owner Owner = Owner.Player;
public static Aggregation Agg = Aggregation.Best; // set · best · increment · decrement
public static Reset Reset = Reset.Weekly(DayOfWeek.Monday); // Monday 00:00 UTC
public static Submit Submit = Submit.ServerOnly; // the default — clients are refused
[Rank(1, Sort.Descending)] public static int Score; // ranks first, high to low
[Rank(2, Sort.Ascending)] public static int ElapsedMs; // equal scores: the faster run wins
[Display] public static string Map; // travels with the row, never ranks it
}@Leaderboard('weekly-score')
export class WeeklyScore {
static owner = Owner.Player;
static agg = Aggregation.Best; // set · best · increment · decrement
static reset = Reset.weekly(DayOfWeek.Monday); // Monday 00:00 UTC
static submit = Submit.ServerOnly; // the default — clients are refused
@rank(1, Sort.Descending) static score: number; // ranks first, high to low
@rank(2, Sort.Ascending) static elapsedMs: number; // equal scores: the faster run wins
@display() static map: string; // travels with the row, never ranks it
}@leaderboard("weekly-score")
class WeeklyScore:
owner = Owner.PLAYER
agg = Aggregation.BEST # set · best · increment · decrement
reset = Reset.weekly(DayOfWeek.MONDAY) # Monday 00:00 UTC
submit = Submit.SERVER_ONLY # the default — clients are refused
score: int = rank(1, Sort.DESCENDING) # ranks first, high to low
elapsed_ms: int = rank(2, Sort.ASCENDING) # equal scores: the faster run wins
map: str = display() # travels with the row, never ranks itЯк оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
USTRUCT(PSLeaderboard = (Name = "weekly-score", Owner = "Player", Aggregation = "Best",
Reset = "Weekly:Monday", Submit = "ServerOnly"))
struct FWeeklyScore
{
GENERATED_BODY()
UPROPERTY(PSRank = (Order = 1, Sort = "Descending")) int32 Score; // ranks first, high to low
UPROPERTY(PSRank = (Order = 2, Sort = "Ascending")) int32 ElapsedMs; // equal scores: the faster run wins
UPROPERTY(PSDisplay) FString Map; // travels with the row, never ranks it
};
// declarations compile into the same pushed model — playserv push from the UE project or CI[Leaderboard("weekly-score")]
public static class WeeklyScore
{
public static Owner Owner = Owner.Player;
public static Aggregation Agg = Aggregation.Best; // set · best · increment · decrement
public static Reset Reset = Reset.Weekly(DayOfWeek.Monday); // Monday 00:00 UTC
public static Submit Submit = Submit.ServerOnly; // the default — clients are refused
[Rank(1, Sort.Descending)] public static int Score; // ranks first, high to low
[Rank(2, Sort.Ascending)] public static int ElapsedMs; // equal scores: the faster run wins
[Display] public static string Map; // travels with the row, never ranks it
}Оголошення лежить поруч з рештою вашої схеми (schema) — у серверному проєкті або в проєкті UE чи Unity. playserv push компілює його й відправляє на платформу: таблиця з’являється в панелі порожньою, а її наступне скидання вже заплановано. Розклади задають в UTC, тому ця таблиця закривається в понеділок о 00:00 UTC, а Reset.Weekly(DayOfWeek.Monday, at: "03:00") зсуває годину. Скидання за місцевим часом гравця немає: одна таблиця не може закритися в двадцять чотири різні моменти.
Ключ сортування — це список, а не очки плюс правило для нічиєї. Поля ранжуються в тому порядку, у якому ви їх пронумерували, кожне зі своїм напрямом. Останній рівень додає платформа: за рівних ключів вище стоїть та відправка, що надійшла раніше. Тому два однакові забіги ніколи не поміняються місцями між двома читаннями. Поле поза ключем — тут це Map — лише показують, і воно ніколи не зсуває рядок.
Agg визначає, що друга відправка робить з єдиним записом власника в поточному циклі:
Agg | Друга відправка | Ідемпотентна |
|---|---|---|
Set | замінює запис надісланими значеннями | так |
Best | замінює його, лише якщо нові значення стоять вище за ключем сортування | так |
Increment | додає надіслані значення до запису — вбивства, кола, внесок у гільдію | ні — передавайте ключ ідемпотентності (idempotency key) |
Decrement | віднімає їх | ні — передавайте ключ ідемпотентності |
Відправка, яка не перевершує запис Best, — не помилка: вона повертається прийнятою, а порядок не змінюється. Increment і Decrement — саме ті два варіанти, які повторений виклик застосував би двічі. Тому вони приймають такий самий ключ ідемпотентності, як і будь-який інший запис, що його можна повторити.
Відправка — це один виклик, і для цієї таблиці він іде із серверного коду, бо так сказано в оголошенні:
Submit: два поля для ранжування й поле для показу — із функції, якій належить результатawait PlayServ.Leaderboards.Submit("weekly-score", playerId,
score: 4200, elapsedMs: 61230, map: "caves");await PlayServ.leaderboards.submit('weekly-score', playerId,
{ score: 4200, elapsedMs: 61230, map: 'caves' });await playserv.leaderboards.submit("weekly-score", player_id,
score=4200, elapsed_ms=61230, map="caves")Як оголошувати через атрибути в Go, поки не вирішено (O-1). Приклади з’являться, коли оберуть спосіб запису.
Виклик є і в Unreal. Ця таблиця лишає типове Submit.ServerOnly, тому платформа приймає відправку лише від хмарної функції або від хоста кімнати з його ключем хоста. Оголосіть Submit.Players — і той самий виклик запрацює з клієнта. Див. Access & Roles.
Виклик є і в Unity. Ця таблиця лишає типове Submit.ServerOnly, тому платформа приймає відправку лише від хмарної функції або від хоста кімнати з його ключем хоста. Оголосіть Submit.Players — і той самий виклик запрацює з клієнта. Див. Access & Roles.
У цьому виклику варто окремо розібрати три речі:
| У виклику | Що це |
|---|---|
PlayServ · playserv | дескриптор (handle) хмарної функції та екземпляр клієнта, які SDK видає вам під час запуску. API один, а тих, хто викликає, двоє — у Go це ps |