Konta graczy

Daj graczom twojej gry konto, które zachowują we wszystkich grach aukimi, z zapisami, które podążają za nimi z urządzenia na urządzenie, na usłudze aukimi albo na twoim własnym serwerze.

W jednym zdaniu#

Gracz loguje się raz na play.aukimi.com i znajduje to konto w każdej grze zrobionej w aukimi, razem z zapisami. Twoja gra prosi o konto; nigdy nie obsługuje hasła.

Dlaczego twoja gra nigdy nie widzi hasła#

Każdy może opublikować grę. Gra, która prosiłaby o hasło do aukimi, byłaby całkowicie przekonującą stroną phishingową, i żadna ilość dobrych intencji z twojej strony nie zmieniłaby tego, co mógłby wypuścić inny deweloper.

Dlatego hasło wpisuje się na play.aukimi.com i nigdzie indziej. Twoja gra wywołuje PlayerLoginAsync(), otwiera się okno przeglądarki we własnych barwach aukimi, gracz to zatwierdza, a okno przekazuje twojej grze token.

Ten token jest wydany tylko dla twojej gry. Token gry A pokazany grze B jest odrzucany, nie ignorowany, tylko odrzucany. O to właśnie chodzi: konto jest współdzielone między grami, dostęp nie jest. Bez tego jedna złośliwa gra mogłaby odczytać zapisy gracza we wszystkich pozostałych.

Co dostajesz o graczu#

Dwie rzeczy, nie więcej:

DostajeszNigdy nie dostajesz
Wyświetlaną nazwę (PlayerName())Adres e-mail
Nieprzejrzysty identyfikator, inny w każdej grze (PlayerID())Cokolwiek, co identyfikuje go gdzie indziej

Identyfikator zmienia się celowo z gry na grę. Dwaj deweloperzy porównujący swoje listy graczy nie mogą stwierdzić, że patrzą na tę samą osobę. To także sprawia, że jesteś procesorem, a nie administratorem tych danych, co jest rozróżnieniem prawnym, a nie ozdobnym.

Nic nie blokuje pętli gry#

Każde polecenie, które rozmawia z siecią, zwraca numer zadania, nigdy wynik. Twoja pętla działa dalej w sześćdziesięciu klatkach na sekundę, podczas gdy żądanie jest w locie, co jest całym powodem, dla którego gra nie może po prostu „czekać na serwer”.

job = PlayerLoginAsync()

Potem, raz na klatkę:

etat$ = PlatformJobState(job)
if etat$ = "done"
    resultat$ = PlatformJobResult(job)
    PlatformJobRelease(job)
endif
if etat$ = "error"
    Print(PlatformJobError(job))
    PlatformJobRelease(job)
endif

Zwolnij zadanie, gdy już je odczytasz. Zadanie, którego nigdy nie zwolnisz, trzyma swój wynik w pamięci do końca sesji. Nic się nie psuje, nic cię nie ostrzega, a przeciek ujawnia się dopiero w długiej sesji gry.

Granie bez konta#

Nie każdy chce się rejestrować, zanim wypróbuje grę. PlayerGuestAsync(name$) tworzy konto bez adresu e-mail, przechowywane w tej przeglądarce.

To prawdziwe konto: zapisuje, wczytuje, i może zostać przejęte później: gracz dodaje e-mail i hasło, a istniejące zapisy podążają za nim, nie przenosząc się.

Uczciwy haczyk: wyczyszczenie danych witryny w przeglądarce traci to konto, i nie ma sposobu, żeby je odzyskać. To cena „braku rejestracji”, i twoja gra powinna to powiedzieć wprost, zamiast pozwolić graczowi odkryć to samemu. PlayerIsGuest() istnieje dokładnie po to zdanie.

Zapisywanie#

job = PlayerSaveAsync(donnees$)
job = PlayerLoadAsync()

Zapis to JSON w twoim własnym kształcie, przechowywany względem pary (ten gracz, ta gra). Podąża za nim do innej przeglądarki, innej maszyny, innej platformy.

Dla garści wartości SetCloudDataVariable() i CloudDataVariable() są prostsze: bez zadania, bez JSON-a. Sprawdź najpierw CloudDataAllowed(): zwraca 0, gdy żaden gracz nie jest zalogowany, a zapis mimo to poszedłby po cichu donikąd.

Rysowanie ekranu#

aukimi nie rysuje żadnego formularza logowania. Formularz złożony przez silnik zawsze wyglądałby jak strona internetowa wrzucona do gry pixel-art, i byłby jedyną częścią twojej gry, której nie mógłbyś przestylizować.

Rysujesz go sam. Jedynym elementem, który musi pochodzić z przeglądarki, jest wpisywanie tekstu, więc CreateEditBox() daje ci prawdziwe pole wejściowe, które sam pozycjonujesz:

CreateEditBox(1)
EditBoxPosition(1, 20, 42)
EditBoxSize(1, 60, 8)
nom$ = EditBoxText(1)

Demo Player Account na liście przykładów Engine to kompletny, działający ekran w około stu liniach. Otwórz je, przeczytaj, a potem zamień każde Print() na własną grafikę.

Zanim to wszystko zadziała#

Gra musi zostać zadeklarowana z twojego konta aukimi, w My games (aukimi.com/app/games): tytuł, adres, pod którym można w nią zagrać, i adresy, na które twoja gra ma prawo wracać. Do tego czasu PlayerAvailable() zwraca 0, a dobra gra mówi „konta nie są dostępne”, zamiast sprawiać wrażenie, że się zawiesiła.

Ten sam ekran to miejsce, gdzie widzisz, kto gra. Czytaj te liczby takimi, jakimi są: liczy graczy, którzy się zalogowali i rozpoczęte sesje, nigdy rozegranych gier. Gra, która nigdy nikogo nie prosi o zalogowanie, nie pokazuje tam niczego, i ekran mówi to wprost, zamiast pokazywać ciche zero.

Używanie własnego serwera kont#

Wszystko powyżej rozmawia z usługą kont aukimi. Jeśli hostujesz swoją grę na własnym serwerze, te same dwanaście poleceń może rozmawiać zamiast tego z twoją usługą, a twój skrypt nie zmienia się choćby o jedną linię.

W Engine: panel Multiplayer → sekcja Player accounts → wybierz My own server → wypełnij jedno pole, adres twojego serwera. Ten adres to ustawienie sceny, więc dwie gry mogą używać dwóch różnych usług.

Dwa sposoby zalogowania gracza#

Wybór jest dokonywany w twoim skrypcie, nie w ustawieniu. Obie formy istnieją niezależnie od tego, na jaki serwer wskazujesz.

Okno (to, co już znasz)#

job = PlayerLoginAsync()

Otwiera się okno, gracz loguje się w nim, i oddaje twojej grze prawo do działania w jego imieniu. Z usługą aukimi tym oknem jest play.aukimi.com. Z twoim własnym serwerem tę stronę budujesz sam.

Bezpośrednio, nazwą użytkownika i hasłem#

job = PlayerLoginAsync(username$, password$)

Bez okna. Twoja gra odczytuje to, co gracz wpisał na twoim własnym ekranie, i wysyła to do twojego serwera. Prostsze w napisaniu, i pozwala zachować wygląd twojej gry od początku do końca.

Ostrzeżenie: w tej formie hasło podróżuje z twojej gry do twojego serwera tak, jak zostało wpisane. Dwie konsekwencje, obie twarde: adres twojego serwera musi zaczynać się od https:// (wyjątkiem jest tylko test na twojej własnej maszynie), i ta forma jest przeznaczona wyłącznie dla serwera, który należy do ciebie.

play.aukimi.com odmawia jej, celowo. aukimi nigdy nie chce znaleźć się w sytuacji, w której widzi hasło gracza, i tak samo nie powinna gra, której nie napisałeś ty.

Te dwa wybory są niezależne od ustawienia: domowej roboty serwer może całkiem dobrze oferować tylko okno i nigdy nie przyjmować hasła bezpośrednio.

Konkretny przykład#

Gra z własnym ekranem „Zaloguj się”: dwa pola tekstowe narysowane przez grę, jeden przycisk, i to za przyciskiem.

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

Wszystko po zalogowaniu jest identyczne z resztą tej strony: PlayerSaveAsync, PlayerLoadAsync, PlayerName i pozostałe nie wiedzą i nie obchodzi ich, który serwer odpowiedział.

Na co musi odpowiadać twój serwer#

Cztery adresy są wymagane, niezależnie od tego, jakie logowanie oferujesz:

Do czego służyCzego oczekuje gra
Graj jako gośćOddaje konto bez adresu e-mail
Kim jest ten graczWyświetlaną nazwę, identyfikator, czy jest gościem
Odczytaj zapisZapisane dane
Zapisz zapisPrzechowuje to, co wysyła gra

Potem, zależnie od tego, co zdecydujesz się oferować:

  • Forma z oknem potrzebuje strony logowania na twoim serwerze, która mówi grze, kto się zalogował, gdy to się skończy.
  • Forma bezpośrednia potrzebuje jeszcze jednego adresu, który przyjmuje nazwę użytkownika i hasło i odpowiada tak albo nie.

Dokładne adresy, kształt każdej odpowiedzi i kody błędów są w repozytorium, w docs/engine-player-accounts.md. Ten plik to kontrakt; ta strona to mapa.

Uwaga: nic z tego nie działa w eksporcie natywnym. Polecenia Player* potrzebują serwera, z którym mogą rozmawiać, i przeglądarki, przez którą mogą to robić, a build natywny nie ma ani jednego, ani drugiego.

Polecenia#

PolecenieCo robi
PlayerAvailable()Czy usługa kont jest skonfigurowana dla tej gry
PlayerLoginAsync()Otwiera okno logowania. Zwraca zadanie
PlayerLoginAsync(user, pass)Loguje bezpośrednio, tylko na twoim własnym serwerze. Zwraca zadanie
PlayerGuestAsync()Tworzy konto bez adresu e-mail. Zwraca zadanie
PlayerLoggedIn()Czy ktoś jest teraz zalogowany
PlayerName()Jego wyświetlana nazwa
PlayerID()Jego nieprzejrzysty identyfikator, właściwy tej grze
PlayerIsGuest()Czy to konto bez adresu e-mail
PlayerSaveAsync()Zapisuje stan gry. Zwraca zadanie
PlayerLoadAsync()Odczytuje go z powrotem. Zwraca zadanie
PlayerRefreshAsync()Odnawia token, zanim wygaśnie
PlayerLogout()Wylogowuje na tym urządzeniu
PlayerLastError()Dlaczego ostatnie wywołanie zawiodło