Les comptes de joueurs

Donnez à vos joueurs un compte qu'ils gardent d'un jeu Aukimi à l'autre, avec des sauvegardes qui les suivent d'un appareil au suivant, sur le service d'Aukimi ou sur un serveur à vous.

En une ligne#

Un joueur crée son compte une fois sur play.aukimi.com et le retrouve dans tous les jeux faits avec Aukimi — sauvegardes comprises. Votre jeu demande le compte ; il ne touche jamais au mot de passe.

Pourquoi votre jeu ne voit jamais le mot de passe#

N'importe qui peut publier un jeu. Un jeu qui demanderait un mot de passe Aukimi serait une page d'hameçonnage parfaitement convaincante, et vos bonnes intentions à vous ne changeraient rien à ce qu'un autre développeur peut livrer.

Le mot de passe se saisit donc sur play.aukimi.com, et nulle part ailleurs. Votre jeu appelle PlayerLoginAsync(), une fenêtre s'ouvre aux couleurs d'Aukimi, le joueur accepte, et la fenêtre rend un jeton à votre jeu.

Ce jeton vaut pour votre jeu seulement. Un jeton du jeu A présenté au jeu B est refusé — pas ignoré, refusé. C'est tout l'enjeu : le compte est partagé entre les jeux, l'accès ne l'est pas. Sans cette règle, un jeu malveillant lirait les sauvegardes d'un joueur dans tous les autres.

Ce que vous recevez d'un joueur#

Deux choses, et rien de plus :

Vous recevezVous ne recevez jamais
Un pseudonyme (PlayerName())L'adresse e-mail
Un identifiant opaque, différent dans chaque jeu (PlayerID())De quoi le reconnaître ailleurs

L'identifiant change d'un jeu à l'autre volontairement. Deux développeurs qui compareraient leurs listes de joueurs ne pourraient pas savoir qu'il s'agit de la même personne. C'est aussi ce qui vous garde sous-traitant plutôt que responsable de ces données — une distinction juridique, pas un ornement.

Rien n'arrête la boucle de jeu#

Toute commande qui parle au réseau rend un numéro de tâche, jamais un résultat. Votre boucle continue à soixante images par seconde pendant que la requête voyage — c'est toute la raison pour laquelle un jeu ne peut pas simplement « attendre le serveur ».

job = PlayerLoginAsync()

Puis, une fois par image :

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

Relâchez la tâche dès que vous l'avez lue. Une tâche jamais relâchée garde son résultat en mémoire pour toute la partie. Rien ne casse, rien ne vous prévient, et la fuite ne se voit que sur une longue session.

Jouer sans compte#

Tout le monde n'a pas envie de s'inscrire avant d'essayer un jeu. PlayerGuestAsync(nom$) crée un compte sans e-mail, gardé dans ce navigateur.

C'est un vrai compte : il sauvegarde, il recharge, et il peut être réclamé plus tard — le joueur ajoute une adresse et un mot de passe, et ses sauvegardes suivent sans être déplacées.

Le revers, à dire honnêtement : vider les données du site perd ce compte, et rien ne le récupère. C'est le prix du « sans inscription », et votre jeu devrait le dire plutôt que de le laisser découvrir. PlayerIsGuest() existe exactement pour cette phrase-là.

Sauvegarder#

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

La sauvegarde est du JSON de la forme que vous voulez, rangé pour le couple (ce joueur, ce jeu). Elle le suit dans un autre navigateur, sur une autre machine, sur une autre plateforme.

Pour quelques valeurs, SetCloudDataVariable() et CloudDataVariable() sont plus simples — pas de tâche, pas de JSON. Vérifiez d'abord CloudDataAllowed() : il rend 0 quand personne n'est identifié, et écrire quand même ne mènerait nulle part, en silence.

Dessiner l'écran#

Aukimi ne dessine aucun formulaire de connexion. Un écran composé par le moteur aurait toujours l'air d'une page web posée sur un jeu en pixel art, et ce serait la seule partie de votre jeu que vous ne pourriez pas rhabiller.

C'est vous qui le dessinez. La seule pièce qui doit venir du navigateur est la saisie de texte : CreateEditBox() vous donne un vrai champ, que vous placez où vous voulez.

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

La démo Player Account, dans la liste d'exemples de l'Engine, est un écran complet et fonctionnel en une centaine de lignes. Ouvrez-la, lisez-la, puis remplacez chaque Print() par vos propres images.

Avant que tout cela fonctionne#

Le jeu doit être déclaré depuis votre compte Aukimi, dans Mes jeux (aukimi.com/app/games) : un titre, l'adresse où l'on peut y jouer, et les adresses vers lesquelles votre jeu a le droit de revenir. Tant que ce n'est pas fait, PlayerAvailable() rend 0 — et un bon jeu annonce « les comptes ne sont pas disponibles » au lieu de donner l'impression de s'être figé.

C'est sur cet écran que vous voyez qui joue. Lisez les chiffres pour ce qu'ils sont : ils comptent les joueurs identifiés et les sessions démarrées, jamais les parties jouées. Un jeu qui ne demande jamais de compte n'y affiche rien, et l'écran le dit plutôt que de montrer un zéro muet.

Brancher son propre serveur de comptes#

Tout ce qui précède parle au service de comptes d'Aukimi. Si vous hébergez votre jeu sur votre propre serveur, les mêmes douze commandes peuvent parler au vôtre, et votre script ne change pas d'une ligne.

Dans l'Engine : panneau Multiplayer → section Player accounts → choisir My own server → remplir un seul champ, l'adresse de votre serveur. Cette adresse est un réglage de la scène : deux jeux peuvent donc utiliser deux services différents.

Deux façons de connecter un joueur#

Le choix se fait dans votre script, pas dans un réglage. Les deux formes existent quel que soit le serveur visé.

La fenêtre, celle que vous connaissez déjà#

job = PlayerLoginAsync()

Une fenêtre s'ouvre, le joueur s'y connecte, et elle rend à votre jeu le droit d'agir en son nom. Avec le service d'Aukimi, cette fenêtre est play.aukimi.com. Avec votre propre serveur, cette page est à vous de la construire.

Directement, avec un identifiant et un mot de passe#

job = PlayerLoginAsync(identifiant$, motDePasse$)

Aucune fenêtre. Votre jeu lit ce que le joueur a tapé sur votre propre écran et l'envoie à votre serveur. Plus simple à écrire, et cela vous laisse garder l'allure de votre jeu du début à la fin.

Attention : sous cette forme, le mot de passe voyage de votre jeu à votre serveur tel qu'il a été tapé. Deux conséquences, fermes toutes les deux : l'adresse de votre serveur doit commencer par https:// (seul un essai sur votre propre machine y échappe), et cette forme n'a de sens que pour un serveur que vous possédez.

play.aukimi.com la refuse, exprès. Aukimi ne veut jamais être en position de voir le mot de passe d'un joueur, et un jeu que vous n'avez pas écrit ne le devrait pas non plus.

Les deux choix sont indépendants du réglage : un serveur maison peut très bien ne proposer que la fenêtre, et n'accepter aucun mot de passe en direct.

Un exemple concret#

Un jeu avec son propre écran « Se connecter » : deux champs dessinés par le jeu, un bouton, et ceci derrière le bouton.

job = PlayerLoginAsync(nomTape$, motDePasseTape$)
DO
    etat$ = PlatformJobState(job)
    if etat$ = "done"
        PlatformJobRelease(job)
        Print("Bienvenue " + PlayerName())
    endif
    if etat$ = "error"
        PlatformJobRelease(job)
        Print(PlayerLastError())
    endif
    Sync()
LOOP

Tout ce qui suit la connexion est identique au reste de cette page : PlayerSaveAsync, PlayerLoadAsync, PlayerName et les autres ne savent pas, et n'ont pas besoin de savoir, quel serveur a répondu.

Ce que votre serveur doit répondre#

Quatre adresses sont obligatoires, quelle que soit la connexion proposée :

À quoi ça sertCe que le jeu attend
Jouer en invitéRendre un compte sans courriel
Qui est ce joueurLe nom affiché, l'identifiant, s'il est invité
Lire la sauvegardeLes données enregistrées
Écrire la sauvegardeRanger ce que le jeu envoie

Ensuite, selon ce que vous choisissez d'offrir :

  • La forme fenêtre demande une page de connexion sur votre serveur, qui dit au jeu qui s'est connecté une fois que c'est fait.
  • La forme directe demande une adresse de plus, qui reçoit un identifiant et un mot de passe et répond oui ou non.

Les adresses exactes, la forme de chaque réponse et les codes d'erreur sont dans le dépôt, dans docs/engine-player-accounts.md. Ce fichier est le contrat ; cette page est la carte.

À savoir : rien de tout cela ne fonctionne dans un export natif. Les commandes Player* ont besoin d'un serveur à qui parler et d'un navigateur pour lui parler, et un build natif n'a ni l'un ni l'autre.

Les commandes#

CommandeCe qu'elle fait
PlayerAvailable()Le service de comptes est-il configuré pour ce jeu
PlayerLoginAsync()Ouvre la fenêtre de connexion. Rend une tâche
PlayerLoginAsync(id, mdp)Connecte directement, sur votre serveur seulement. Rend une tâche
PlayerGuestAsync()Crée un compte sans e-mail. Rend une tâche
PlayerLoggedIn()Quelqu'un est-il identifié en ce moment
PlayerName()Son pseudonyme
PlayerID()Son identifiant opaque, propre à ce jeu
PlayerIsGuest()Est-ce un compte sans e-mail
PlayerSaveAsync()Écrit la sauvegarde. Rend une tâche
PlayerLoadAsync()La relit. Rend une tâche
PlayerRefreshAsync()Renouvelle le jeton avant qu'il expire
PlayerLogout()Déconnecte sur cet appareil
PlayerLastError()Pourquoi le dernier appel a échoué