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 recevez | Vous 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.comla 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 sert | Ce que le jeu attend |
|---|---|
| Jouer en invité | Rendre un compte sans courriel |
| Qui est ce joueur | Le nom affiché, l'identifiant, s'il est invité |
| Lire la sauvegarde | Les données enregistrées |
| Écrire la sauvegarde | Ranger 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#
| Commande | Ce 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é |