Акаунти гравців
Дайте гравцям вашої гри акаунт, який вони зберігають у кожній грі на Aukimi, зі збереженнями, що йдуть за ними з одного пристрою на інший, на сервісі Aukimi або на вашому власному сервері.
Одним рядком#
Гравець входить один раз на play.aukimi.com і знаходить цей акаунт у кожній грі, зробленій на Aukimi (разом зі збереженнями). Ваша гра лише запитує акаунт, вона ніколи не бачить пароль.
Чому ваша гра ніколи не бачить пароль#
Опублікувати гру може будь-хто. Гра, що запитує пароль від Aukimi, стала б цілком переконливою фішинговою сторінкою, і жодні добрі наміри з вашого боку не змінили б того, що міг би зробити інший розробник.
Тож пароль вводиться на play.aukimi.com і більше ніде. Ваша гра викликає
PlayerLoginAsync(), відкривається вікно браузера в кольорах самого Aukimi,
гравець підтверджує вхід, і вікно передає вашій грі токен.
Цей токен видано лише для вашої гри. Токен гри A, пред'явлений грі B, відхиляється, не ігнорується, а саме відхиляється. У цьому й суть: акаунт спільний між іграми, а доступ ні. Без цього одна зловмисна гра могла б читати збереження гравця в усіх інших.
Що ви отримуєте про гравця#
Дві речі, і не більше:
| Ви отримуєте | Ви ніколи не отримуєте |
|---|---|
Відображуване ім'я (PlayerName()) | Адресу електронної пошти |
Непрозорий id, різний у кожній грі (PlayerID()) | Будь-що, що ідентифікує їх деінде |
Id навмисно змінюється від однієї гри до іншої. Два розробники, порівнюючи свої списки гравців, не зможуть визначити, що дивляться на одну й ту саму людину. Це також лишає вас обробником, а не розпорядником цих даних, а це юридична відмінність, а не декоративна.
Ніщо не блокує ігровий цикл#
Кожна команда, що звертається до мережі, повертає номер задачі, а не результат. Ваш цикл продовжує працювати на шістдесяти кадрах за секунду, доки запит у польоті (саме тому гра не може просто «зачекати на сервер»).
job = PlayerLoginAsync()
Потім, раз на кадр:
etat$ = PlatformJobState(job)
if etat$ = "done"
resultat$ = PlatformJobResult(job)
PlatformJobRelease(job)
endif
if etat$ = "error"
Print(PlatformJobError(job))
PlatformJobRelease(job)
endif
Звільніть задачу, щойно прочитали її результат. Задача, яку ви ніколи не звільните, тримає свій результат у пам'яті до кінця сесії. Нічого не ламається, ніщо не попереджає вас, і витік проявляється лише в довгій сесії гри.
Гра без акаунта#
Не кожен хоче реєструватися перед тим, як спробувати гру.
PlayerGuestAsync(name$) створює акаунт без пошти, збережений у цьому
браузері.
Це справжній акаунт: він зберігає, завантажує, і його можна підтвердити пізніше (гравець додає пошту й пароль, і наявні збереження йдуть за ним, не переносячись окремо).
Чесна застереження: очищення даних сайту в браузері губить цей акаунт, і
відновити його неможливо. Це ціна за «без реєстрації», і ваша гра має сказати
про це прямо, а не дати гравцю дізнатися самому. PlayerIsGuest() існує саме
для цього речення.
Збереження#
job = PlayerSaveAsync(donnees$)
job = PlayerLoadAsync()
Збереження це JSON вашої власної форми, збережений під парою (цей гравець, ця гра). Він слідує за ними на інший браузер, іншу машину, іншу платформу.
Для жменьки значень SetCloudDataVariable() і CloudDataVariable() простіші:
без задачі, без JSON. Спершу перевірте CloudDataAllowed(): вона повертає 0,
коли жоден гравець не увійшов, а запис попри це мовчки нікуди не потрапить.
Малювання екрана#
Aukimi не малює жодної форми входу. Форма, складена рушієм, завжди виглядала б як вебсторінка, кинута поверх піксель-арт гри, і це була б єдина частина вашої гри, яку ви не могли б перестилізувати.
Ви малюєте її самі. Єдина частина, яка мусить прийти з браузера, це введення
тексту, тож CreateEditBox() дає вам справжнє поле вводу, яке ви позиціонуєте
самі:
CreateEditBox(1)
EditBoxPosition(1, 20, 42)
EditBoxSize(1, 60, 8)
nom$ = EditBoxText(1)
Демо Player Account у списку прикладів Engine це повноцінний робочий
екран приблизно на сотню рядків. Відкрийте його, прочитайте, а потім заміните
кожен Print() власною графікою.
Перш ніж це запрацює#
Гру потрібно оголосити з вашого акаунта Aukimi, у розділі My games
(aukimi.com/app/games): назва, адреса, за якою в неї можна грати, і адреси,
куди вашій грі дозволено повертатися. До того PlayerAvailable() повертає 0,
і добра гра каже «акаунти недоступні» замість того, щоб виглядати завислою.
Той самий екран показує, хто грає. Читайте ці цифри як те, чим вони є: він рахує гравців, які увійшли і розпочаті сесії, а не зіграні партії. Гра, яка нікого ніколи не просить увійти, не покаже там нічого, і екран каже про це прямо, замість того щоб показувати мовчазний нуль.
Використання власного сервера акаунтів#
Усе вище звертається до сервісу акаунтів Aukimi. Якщо ви розміщуєте гру на власному сервері, ті самі дванадцять команд можуть звертатися до вашого сервісу замість цього, і ваш скрипт не зміниться ні на рядок.
В Engine: панель Multiplayer → розділ Player accounts → оберіть My own server → заповніть одне поле, адресу вашого сервера. Ця адреса це налаштування сцени, тож дві гри можуть використовувати два різні сервіси.
Два способи увійти гравцю#
Вибір робиться у вашому скрипті, а не в налаштуванні. Обидві форми існують незалежно від того, на який сервер ви вказали.
Вікно (те, що ви вже знаєте)#
job = PlayerLoginAsync()
Відкривається вікно, гравець входить у ньому, і воно передає вашій грі право
діяти від його імені. Із сервісом Aukimi це вікно, play.aukimi.com. З
вашим власним сервером цю сторінку будуєте ви самі.
Напряму, іменем користувача й паролем#
job = PlayerLoginAsync(username$, password$)
Жодного вікна. Ваша гра читає те, що гравець набрав на вашому власному екрані, і надсилає це на ваш сервер. Простіше написати, і це дозволяє зберегти вигляд вашої гри від початку до кінця.
Попередження: у цій формі пароль подорожує з вашої гри на ваш сервер у тому вигляді, як його набрали. Два наслідки, обидва тверді: адреса вашого сервера має починатися з
https://(звільнений від цього лише тест на власній машині), і ця форма призначена лише для сервера, яким володієте ви.
play.aukimi.comвідмовляє в цьому навмисно. Aukimi ніколи не хоче опинитися в позиції, де бачить пароль гравця, і так само не мала б грати у це гра, яку написали не ви.
Два варіанти незалежні від налаштування: саморобний сервер цілком може пропонувати лише вікно і ніколи не приймати пароль напряму.
Конкретний приклад#
Гра з власним екраном «Увійти»: два текстові поля, намальовані грою, одна кнопка, і ось що за нею стоїть.
job = PlayerLoginAsync(typedName$, typedPassword$)
DO
etat$ = PlatformJobState(job)
if etat$ = "done"
PlatformJobRelease(job)
Print("Welcome " + PlayerName())
endif
if etat$ = "error"
PlatformJobRelease(job)
Print(PlayerLastError())
endif
Sync()
LOOP
Усе після входу ідентичне решті цієї сторінки: PlayerSaveAsync,
PlayerLoadAsync, PlayerName та інші не знають і не переймаються тим,
який сервер відповів.
Що має вміти відповідати ваш сервер#
Чотири адреси обов'язкові, який би вхід ви не пропонували:
| Для чого | Що очікує гра |
|---|---|
| Грати як гість | Повернути акаунт без пошти |
| Хто цей гравець | Відображуване ім'я, id, чи це гість |
| Прочитати збереження | Збережені дані |
| Записати збереження | Зберегти те, що надсилає гра |
Далі, залежно від того, що ви вирішите пропонувати:
- Форма з вікном потребує сторінки входу на вашому сервері, яка повідомляє грі, хто увійшов, коли все завершено.
- Пряма форма потребує ще однієї адреси, яка приймає ім'я користувача й пароль і відповідає «так» чи «ні».
Точні адреси, форма кожної відповіді та коди помилок містяться в репозиторії, у
docs/engine-player-accounts.md. Той файл це контракт; ця сторінка це карта.
Примітка: ніщо з цього не працює в нативному експорті. Команди
Player*потребують сервера, з яким говорити, і браузера, крізь який говорити, а в нативній збірці немає ні того, ні іншого.
Команди#
| Команда | Що вона робить |
|---|---|
PlayerAvailable() | Чи налаштований сервіс акаунтів для цієї гри |
PlayerLoginAsync() | Відкриває вікно входу. Повертає задачу |
PlayerLoginAsync(user, pass) | Входить напряму, лише на вашому власному сервері. Повертає задачу |
PlayerGuestAsync() | Створює акаунт без пошти. Повертає задачу |
PlayerLoggedIn() | Чи хтось зараз увійшов |
PlayerName() | Його відображуване ім'я |
PlayerID() | Його непрозорий id, специфічний для цієї гри |
PlayerIsGuest() | Чи це акаунт без пошти |
PlayerSaveAsync() | Записує збереження. Повертає задачу |
PlayerLoadAsync() | Читає його назад. Повертає задачу |
PlayerRefreshAsync() | Оновлює токен до того, як він спливе |
PlayerLogout() | Виходить на цьому пристрої |
PlayerLastError() | Чому провалився останній виклик |