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ê recebeVocê 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.com a 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 serveO que o jogo espera
Jogar como convidadoDevolver uma conta sem e-mail
Quem é este jogadorO nome de exibição, o id, se é um convidado
Ler o saveOs dados salvos
Escrever o saveGuardar 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#

ComandoO 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