Cuentas de jugador
Dale a los jugadores de tu juego una cuenta que conservan en todos los juegos de Aukimi, con partidas guardadas que los siguen de un dispositivo a otro, en el servicio de Aukimi o en uno propio.
En una frase#
Un jugador inicia sesión una vez en play.aukimi.com y encuentra esa cuenta en todos los juegos hechos con Aukimi, partidas guardadas incluidas. Tu juego pide la cuenta; nunca maneja la contraseña.
Por qué tu juego nunca ve la contraseña#
Cualquiera puede publicar un juego. Un juego que pidiera una contraseña de Aukimi sería una página de phishing perfectamente convincente, y ninguna cantidad de buenas intenciones de tu parte cambiaría lo que un desarrollador distinto podría publicar.
Así que la contraseña se escribe en play.aukimi.com y en ningún otro sitio.
Tu juego llama a PlayerLoginAsync(), se abre una ventana del navegador con los
colores propios de Aukimi, el jugador aprueba, y la ventana le entrega a tu
juego un token.
Ese token se emite solo para tu juego. Un token del juego A presentado al juego B se rechaza, no se ignora, se rechaza. Ese es el punto: la cuenta se comparte entre juegos, el acceso no. Sin esto, un juego malicioso podría leer las partidas guardadas de un jugador en todos los demás.
Qué recibes sobre un jugador#
Dos cosas, y nada más:
| Recibes | Nunca recibes |
|---|---|
Un nombre para mostrar (PlayerName()) | La dirección de correo |
Un id opaco, distinto en cada juego (PlayerID()) | Nada que lo identifique en otro sitio |
El id cambia de un juego a otro a propósito. Dos desarrolladores comparando sus listas de jugadores no pueden saber que están viendo a la misma persona. Eso también te mantiene como procesador y no como responsable de esos datos, lo cual es una distinción legal y no decorativa.
Nada bloquea el bucle del juego#
Todo comando que habla con la red devuelve un número de trabajo, nunca un resultado. Tu bucle sigue corriendo a sesenta fotogramas por segundo mientras la petición está en curso, que es la razón entera por la que un juego no puede simplemente "esperar al servidor".
job = PlayerLoginAsync()
Luego, una vez por fotograma:
etat$ = PlatformJobState(job)
if etat$ = "done"
resultat$ = PlatformJobResult(job)
PlatformJobRelease(job)
endif
if etat$ = "error"
Print(PlatformJobError(job))
PlatformJobRelease(job)
endif
Libera el trabajo en cuanto lo hayas leído. Un trabajo que nunca liberas mantiene su resultado en memoria durante el resto de la sesión. Nada se rompe, nada te avisa, y la fuga solo aparece en una sesión de juego larga.
Jugar sin cuenta#
No todo el mundo quiere registrarse antes de probar un juego.
PlayerGuestAsync(name$) crea una cuenta sin correo, guardada en este
navegador.
Es una cuenta real: guarda, carga, y se puede reclamar más tarde, el jugador añade un correo y una contraseña, y las partidas existentes siguen sin moverse.
La trampa honesta: borrar los datos del sitio del navegador pierde esa cuenta,
y no hay forma de recuperarla. Ese es el precio de "sin registro", y tu juego
debería decirlo en lugar de dejar que el jugador lo descubra. PlayerIsGuest()
está ahí exactamente para esa frase.
Guardar#
job = PlayerSaveAsync(donnees$)
job = PlayerLoadAsync()
La partida guardada es JSON con la forma que tú decidas, almacenado contra el par (este jugador, este juego). Los sigue a otro navegador, otra máquina, otra plataforma.
Para un puñado de valores, SetCloudDataVariable() y CloudDataVariable() son
más simples, sin job, sin JSON. Comprueba CloudDataAllowed() primero: devuelve
0 cuando no hay ningún jugador con sesión iniciada, y escribir de todas formas
no llegaría a ningún sitio en silencio.
Dibujar la pantalla#
Aukimi no dibuja ningún formulario de inicio de sesión. Un formulario compuesto por el motor siempre se parecería a una página web soltada sobre un juego de pixel art, y sería la única parte de tu juego que no podrías restilizar.
Lo dibujas tú. La única pieza que tiene que venir del navegador es la entrada
de texto, así que CreateEditBox() te da un campo de entrada real que colocas
tú mismo:
CreateEditBox(1)
EditBoxPosition(1, 20, 42)
EditBoxSize(1, 60, 8)
nom$ = EditBoxText(1)
La demo Player Account en la lista de ejemplos del Engine es una pantalla
completa y funcional en un centenar de líneas. Ábrela, léela, y luego
reemplaza cada Print() con tu propio arte.
Antes de que nada de esto funcione#
El juego debe estar declarado desde tu cuenta de Aukimi, en My games
(aukimi.com/app/games): un título, la dirección donde se puede jugar, y las
direcciones a las que tu juego tiene permitido volver. Hasta entonces
PlayerAvailable() devuelve 0, y un buen juego dice "las cuentas no están
disponibles" en lugar de parecer que se ha colgado.
Esa misma pantalla es donde ves quién está jugando. Lee los números por lo que son: cuenta jugadores que iniciaron sesión y sesiones iniciadas, nunca partidas jugadas. Un juego que nunca le pide a nadie iniciar sesión no muestra nada ahí, y la pantalla lo dice en lugar de mostrar un cero silencioso.
Usar tu propio servidor de cuentas#
Todo lo anterior habla con el servicio de cuentas de Aukimi. Si alojas tu juego en tu propio servidor, los mismos doce comandos pueden hablar con tu servicio en su lugar, y tu script no cambia ni una sola línea.
En el Engine: panel Multiplayer → sección Player accounts → elige My own server → rellena un campo, la dirección de tu servidor. Esa dirección es un ajuste de la escena, así que dos juegos pueden usar dos servicios distintos.
Dos formas de iniciar sesión a un jugador#
La elección se hace en tu script, no en un ajuste. Ambas formas existen sea cual sea el servidor al que apuntes.
La ventana (la que ya conoces)#
job = PlayerLoginAsync()
Se abre una ventana, el jugador inicia sesión ahí, y le entrega a tu juego el
derecho a actuar en su nombre. Con el servicio de Aukimi esa ventana es
play.aukimi.com. Con tu propio servidor, esa página es tuya, para
construirla tú.
Directamente, con un usuario y una contraseña#
job = PlayerLoginAsync(username$, password$)
Sin ventana. Tu juego lee lo que el jugador escribió en tu propia pantalla y se lo envía a tu servidor. Más simple de escribir, y te deja conservar el aspecto de tu juego de principio a fin.
Advertencia: en esta forma, la contraseña viaja de tu juego a tu servidor tal como se escribió. Dos consecuencias, ambas firmes: la dirección de tu servidor debe empezar por
https://(solo una prueba en tu propia máquina está exenta), y esta forma solo está pensada para un servidor que sea tuyo.
play.aukimi.comla rechaza, a propósito. Aukimi nunca quiere estar en posición de ver la contraseña de un jugador, y tampoco debería un juego que tú no escribiste.
Las dos opciones son independientes del ajuste: un servidor casero puede perfectamente ofrecer solo la ventana, y nunca aceptar una contraseña directamente.
Un ejemplo concreto#
Un juego con su propia pantalla de "Sign in": dos campos de texto dibujados por el juego, un botón, y esto detrás del botón.
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
Todo lo que viene después del inicio de sesión es idéntico al resto de esta
página: PlayerSaveAsync, PlayerLoadAsync, PlayerName y los demás no saben,
y no les importa, qué servidor respondió.
Qué tiene que responder tu servidor#
Se necesitan cuatro direcciones, sea cual sea el inicio de sesión que ofrezcas:
| Para qué sirve | Qué espera el juego |
|---|---|
| Jugar como invitado | Devolver una cuenta sin correo |
| Quién es este jugador | El nombre para mostrar, el id, si es invitado |
| Leer la partida | Los datos guardados |
| Escribir la partida | Guardar lo que envía el juego |
Luego, según lo que decidas ofrecer:
- La forma de ventana necesita una página de inicio de sesión en tu servidor, que le diga al juego quién inició sesión una vez terminado.
- La forma directa necesita una dirección más, que toma un usuario y una contraseña y responde sí o no.
Las direcciones exactas, la forma de cada respuesta y los códigos de error
están en el repositorio, en docs/engine-player-accounts.md. Ese archivo es el
contrato; esta página es el mapa.
Nota: nada de esto funciona en una exportación nativa. Los comandos
Player*necesitan un servidor con el que hablar y un navegador a través del cual hablar, y una build nativa no tiene ninguno de los dos.
Los comandos#
| Comando | Qué hace |
|---|---|
PlayerAvailable() | Si el servicio de cuentas está configurado para este juego |
PlayerLoginAsync() | Abre la ventana de inicio de sesión. Devuelve un job |
PlayerLoginAsync(user, pass) | Inicia sesión directamente, solo en tu propio servidor. Devuelve un job |
PlayerGuestAsync() | Crea una cuenta sin correo. Devuelve un job |
PlayerLoggedIn() | Si alguien tiene sesión iniciada ahora mismo |
PlayerName() | Su nombre para mostrar |
PlayerID() | Su id opaco, específico de este juego |
PlayerIsGuest() | Si es una cuenta sin correo |
PlayerSaveAsync() | Escribe la partida guardada. Devuelve un job |
PlayerLoadAsync() | La lee de vuelta. Devuelve un job |
PlayerRefreshAsync() | Renueva el token antes de que expire |
PlayerLogout() | Cierra sesión en este dispositivo |
PlayerLastError() | Por qué falló la última llamada |