Contas de jogador
Dê aos jogadores do seu jogo uma conta que eles mantêm em todo jogo feito com a aukimi, com saves que os acompanham de um dispositivo para outro, no serviço da aukimi ou num servidor seu.
Em uma frase#
Um jogador entra uma vez em play.aukimi.com e encontra essa conta em todo jogo feito com a aukimi: saves incluídos. Seu jogo pede a conta; ele nunca lida com a senha.
Por que seu jogo nunca vê a senha#
Qualquer um pode publicar um jogo. Um jogo que pedisse uma senha da aukimi seria uma página de phishing perfeitamente convincente, e nenhuma quantidade de boas intenções da sua parte mudaria o que um desenvolvedor diferente poderia lançar.
Então a senha é digitada em play.aukimi.com e em nenhum outro lugar. Seu
jogo chama PlayerLoginAsync(), uma janela do navegador abre com as cores da
própria aukimi, o jogador aprova, e a janela entrega um token ao seu jogo.
Esse token é emitido só para o seu jogo. Um token do jogo A apresentado ao jogo B é recusado, não ignorado, recusado. Esse é o ponto: a conta é compartilhada entre jogos, o acesso não é. Sem isso, um jogo malicioso conseguiria ler os saves de um jogador em todos os outros.
O que você recebe sobre um jogador#
Duas coisas, e nada mais:
| Você recebe | Você nunca recebe |
|---|---|
Um nome de exibição (PlayerName()) | O endereço de e-mail |
Um id opaco, diferente em cada jogo (PlayerID()) | Qualquer coisa que o identifique em outro lugar |
O id muda de um jogo para outro de propósito. Dois desenvolvedores comparando suas listas de jogadores não conseguem saber que estão olhando para a mesma pessoa. Isso também mantém você um processador, e não um controlador desses dados, uma distinção legal, não decorativa.
Nada trava o loop do jogo#
Todo comando que fala com a rede retorna um número de job, nunca um resultado. Seu loop continua rodando a sessenta quadros por segundo enquanto a requisição está em trânsito, e essa é a razão inteira pela qual um jogo não pode simplesmente "esperar pelo servidor".
job = PlayerLoginAsync()
Depois, uma vez por quadro:
etat$ = PlatformJobState(job)
if etat$ = "done"
resultat$ = PlatformJobResult(job)
PlatformJobRelease(job)
endif
if etat$ = "error"
Print(PlatformJobError(job))
PlatformJobRelease(job)
endif
Libere o job assim que você o tiver lido. Um job que nunca é liberado mantém seu resultado na memória pelo resto da sessão. Nada quebra, nada te avisa, e o vazamento só aparece numa sessão de jogo longa.
Jogando sem conta#
Nem todo mundo quer se cadastrar antes de experimentar um jogo.
PlayerGuestAsync(name$) cria uma conta sem e-mail, mantida neste navegador.
É uma conta de verdade: ela salva, ela carrega, e pode ser reivindicada depois: o jogador adiciona um e-mail e uma senha, e os saves existentes seguem sem se mover.
A pegadinha honesta: limpar os dados do site do navegador perde essa conta, e
não há como recuperá-la. Esse é o preço de "sem cadastro", e o seu jogo
deveria dizer isso em vez de deixar o jogador descobrir sozinho.
PlayerIsGuest() está aí exatamente para essa frase.
Salvando#
job = PlayerSaveAsync(donnees$)
job = PlayerLoadAsync()
O save é um JSON no formato que você quiser, guardado contra o par (este jogador, este jogo). Ele acompanha o jogador para outro navegador, outra máquina, outra plataforma.
Para um punhado de valores, SetCloudDataVariable() e CloudDataVariable()
são mais simples: sem job, sem JSON. Confira CloudDataAllowed() primeiro:
ele retorna 0 quando nenhum jogador está logado, e escrever mesmo assim iria
silenciosamente para lugar nenhum.
Desenhando a tela#
A aukimi não desenha nenhum formulário de login. Um formulário composto pelo motor sempre pareceria uma página da web jogada em cima de um jogo em pixel art, e seria a única parte do seu jogo que você não conseguiria estilizar de novo.
Você o desenha. A única peça que precisa vir do navegador é a entrada de
texto, então CreateEditBox() te dá um campo de entrada de verdade que você
posiciona sozinho:
CreateEditBox(1)
EditBoxPosition(1, 20, 42)
EditBoxSize(1, 60, 8)
nom$ = EditBoxText(1)
A demo Player Account na lista de exemplos do Engine é uma tela funcional
completa em cerca de cem linhas. Abra, leia, depois troque cada Print()
pela sua própria arte.
Antes de qualquer coisa disso funcionar#
O jogo precisa ser declarado a partir da sua conta aukimi, em My games
(aukimi.com/app/games): um título, o endereço onde ele pode ser jogado, e
os endereços para os quais seu jogo tem permissão de retornar. Até lá,
PlayerAvailable() retorna 0, e um bom jogo diz "contas não estão
disponíveis" em vez de parecer travado.
Essa mesma tela é onde você vê quem está jogando. Leia os números pelo que eles são: conta jogadores que entraram e sessões iniciadas, nunca jogos jogados. Um jogo que nunca pede a ninguém para entrar não mostra nada ali, e a tela avisa isso em vez de mostrar um zero silencioso.
Usando seu próprio servidor de contas#
Tudo acima fala com o serviço de contas da aukimi. Se você hospeda seu jogo no seu próprio servidor, os mesmos doze comandos podem falar com o seu serviço em vez disso, e seu script não muda uma única linha.
No Engine: painel Multiplayer → seção Player accounts → escolha My own server → preencha um campo, o endereço do seu servidor. Esse endereço é uma configuração da cena, então dois jogos podem usar dois serviços diferentes.
Duas formas de logar um jogador#
A escolha é feita no seu script, não numa configuração. As duas formas existem seja qual for o servidor que você aponta.
A janela (o que você já conhece)#
job = PlayerLoginAsync()
Uma janela abre, o jogador entra ali, e ela entrega ao seu jogo o direito de
agir em nome dele. Com o serviço da aukimi essa janela é play.aukimi.com.
Com o seu próprio servidor, essa página é sua para construir.
Diretamente, com um usuário e uma senha#
job = PlayerLoginAsync(username$, password$)
Sem janela. Seu jogo lê o que o jogador digitou na sua própria tela e envia para o seu servidor. Mais simples de escrever, e permite manter a cara do seu jogo do início ao fim.
Aviso: nessa forma, a senha viaja do seu jogo até o seu servidor como foi digitada. Duas consequências, ambas firmes: o endereço do seu servidor precisa começar com
https://(só um teste na sua própria máquina é isento), e essa forma só serve para um servidor que você mesmo controla.
play.aukimi.coma recusa, de propósito. A aukimi nunca quer estar numa posição de ver a senha de um jogador, e nenhum jogo que você não escreveu deveria também.
As duas escolhas são independentes da configuração: um servidor caseiro pode perfeitamente oferecer só a janela, e nunca aceitar uma senha diretamente.
Um exemplo concreto#
Um jogo com sua própria tela de "Entrar": dois campos de texto desenhados pelo jogo, um botão, e isso atrás do botão.
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
Tudo depois do login é idêntico ao resto desta página: PlayerSaveAsync,
PlayerLoadAsync, PlayerName e os outros não sabem, e não se importam, com
qual servidor respondeu.
O que o seu servidor precisa responder#
Quatro endereços são exigidos, seja qual for o login que você oferece:
| Para que serve | O que o jogo espera |
|---|---|
| Jogar como convidado | Devolver uma conta sem e-mail |
| Quem é este jogador | O nome de exibição, o id, se é um convidado |
| Ler o save | Os dados salvos |
| Escrever o save | Guardar o que o jogo envia |
Depois, dependendo do que você escolher oferecer:
- A forma da janela precisa de uma página de login no seu servidor, que avisa o jogo quem entrou assim que termina.
- A forma direta precisa de mais um endereço, que recebe um usuário e uma senha e responde sim ou não.
Os endereços exatos, o formato de cada resposta e os códigos de erro estão no
repositório, em docs/engine-player-accounts.md. Esse arquivo é o contrato;
esta página é o mapa.
Nota: nada disso funciona numa exportação nativa. Os comandos
Player*precisam de um servidor para conversar e de um navegador para conversar através dele, e um build nativo não tem nenhum dos dois.
Os comandos#
| Comando | O que faz |
|---|---|
PlayerAvailable() | O serviço de conta está configurado para este jogo |
PlayerLoginAsync() | Abre a janela de login. Retorna um job |
PlayerLoginAsync(user, pass) | Loga diretamente, só no seu próprio servidor. Retorna um job |
PlayerGuestAsync() | Cria uma conta sem e-mail. Retorna um job |
PlayerLoggedIn() | Alguém está logado agora |
PlayerName() | O nome de exibição dele |
PlayerID() | O id opaco dele, específico deste jogo |
PlayerIsGuest() | É uma conta sem e-mail |
PlayerSaveAsync() | Escreve o save. Retorna um job |
PlayerLoadAsync() | Lê de volta. Retorna um job |
PlayerRefreshAsync() | Renova o token antes que ele expire |
PlayerLogout() | Sai neste dispositivo |
PlayerLastError() | Por que a última chamada falhou |