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:
| Dostajesz | Nigdy 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.comodmawia 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ży | Czego oczekuje gra |
|---|---|
| Graj jako gość | Oddaje konto bez adresu e-mail |
| Kim jest ten gracz | Wyświetlaną nazwę, identyfikator, czy jest gościem |
| Odczytaj zapis | Zapisane dane |
| Zapisz zapis | Przechowuje 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#
| Polecenie | Co 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 |