Pular para o conteúdo principal
Serviços em torno do jogo

Esta página ainda não foi traduzida: mostrando a versão em inglês.

Auth & Players

O login é uma etapa que você pode sobrescrever, não uma caixa-preta. Provedores, sessões, vínculo de identidades, banimentos. Cada ponto do fluxo — antes e depois do login, antes e depois de um vínculo, antes e depois de uma mesclagem, na mudança de status — é um ponto de extensão declarado, com um tipo declarado: uma barreira, que pode recusar a etapa, ou um observador, que não pode.

Quando usar

  • Os jogadores (player) precisam fazer login (dispositivo, e-mail, Apple, Google, Steam ou provedor próprio), e criar a conta no primeiro login é uma flag, não um segundo fluxo.
  • Uma conta de convidado precisa virar conta completa mais tarde: Link adiciona a Steam sem perder o progresso, e a mesclagem (merge) junta duas contas em um só jogador.
  • As regras precisam rodar onde ninguém consegue pulá-las: uma barreira (gate) por região antes do login, um pacote inicial depois do login que criou o jogador.
  • A moderação precisa agir na hora: revogar sessões, suspender, banir o dispositivo, com um evento banned que todos os sistemas em execução recebem.
  • O contexto declarado (região, plataforma, build) precisa chegar a todos os hooks seguintes sem que cada um tenha de ler o jogador de novo para descobri-lo.
  • Não existe alternativa mais leve. Todos os outros módulos identificam quem os chama por meio deste, e o auth não pode ser desligado enquanto algum deles precisar de um ator jogador: o configurador de módulos recusa e lista os módulos dependentes.

Quem faz o quê

AtorO que faz nesta página
playerfaz login, vincula ou desvincula identidades, renova a credencial, faz logout
moderatorrevoga sessões; bane, suspende ou reativa jogadores
backend-servicefiltra o login por região; cria as primeiras linhas de um jogador novo; lê e revoga sessões

Resumo

Uma chamada SignIn por provedor, criação no primeiro login como flag; Link adiciona a Steam
// client — one call per provider; create-on-first-sign-in is a flag
var session = await PlayServ.Auth.SignIn(Provider.Device, create: true);
await PlayServ.Auth.Link(Provider.Steam);          // one player, many identities
// client — one call per provider; create-on-first-sign-in is a flag
const session = await PlayServ.auth.signIn(Provider.Device, { create: true });
await PlayServ.auth.link(Provider.Steam);    // one player, many identities
# client — one call per provider; create-on-first-sign-in is a flag
session = await playserv.auth.sign_in(Provider.DEVICE, create=True)
await playserv.auth.link(Provider.STEAM)     # one player, many identities
Em breve — Go

Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.

// client — one call per provider; create-on-first-sign-in is a flag
Client->Auth->SignInWithProvider(FPSProviderId::Device, Credential,
    TPSOnResult<FPSSession>::CreateWeakLambda(this, [this](const TPSResult<FPSSession>& Result)
    {
        if (!Result.HasValue()) { return; }
        // one player, many identities — add Steam to the same account
        Client->Auth->Providers->Link(FPSProviderId::Steam, SteamCredential);
    }));
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.
Unity C# is the same C# API here — roda sem alterações na Unity com os tipos gerados
// client — one call per provider; create-on-first-sign-in is a flag
var session = await PlayServ.Auth.SignIn(Provider.Device, create: true);
await PlayServ.Auth.Link(Provider.Steam);          // one player, many identities

Cada ponto é personalizado onde é declarado; as formas que um handler pode assumir estão reunidas em Extensibility:

Uma barreira antes do login recusa uma região; um observador depois do login que criou o jogador concede um pacote inicial
[Before(Auth.SignIn)]                          // a gate: it may refuse, and it is fail-closed
public static Verdict GateRegion(SignInAttempt a) =>
    a.Region == "sanctioned"
        ? Hook.Reject(Problem.Forbidden, "region not served")
        : Hook.Continue(a);

[After(Auth.SignIn, created: true)]            // an observer: it watches, it cannot refuse
public static async Task GrantStarterPack(Player player)
{
    await player.Inventory.Grant("chest.gold", count: 1);
}
// a gate: it may refuse, and it is fail-closed
export const gateRegion = before(Auth.signIn, (a: SignInAttempt) =>
  a.region === 'sanctioned'
    ? Hook.reject(Problem.forbidden, 'region not served')
    : Hook.continue(a));

// an observer: it watches, it cannot refuse
export const grantStarterPack = after(Auth.signIn, { created: true },
  async (player: Player) => {
    await player.inventory.grant('chest.gold', { count: 1 });
  });
@before(auth.sign_in)                          # a gate: it may refuse, and it is fail-closed
def gate_region(a: SignInAttempt) -> Verdict:
    if a.region == "sanctioned":
        return Hook.reject(Problem.FORBIDDEN, "region not served")
    return Hook.continue_(a)

@after(auth.sign_in, created=True)             # an observer: it watches, it cannot refuse
async def grant_starter_pack(player: Player):
    await player.inventory.grant("chest.gold", count=1)
Em breve — Go

Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.

Executa fora da engine

Tanto uma sobrescrita quanto um hook são cloud functions: rodam na plataforma, não na engine. Escreva-os em C#, TypeScript ou Python — a Unreal assina os eventos que eles geram. Estão previstas funções escritas na própria engine: grafos de Blueprint e C++ (traduzido para um script que a plataforma valida). As duas formas são analisadas e publicadas como qualquer declaração; a execução continua na plataforma.

Executa fora da engine

Tanto uma sobrescrita quanto um hook são cloud functions: rodam na plataforma, não na engine. Escreva-os em C#, TypeScript ou Python — a Unity assina os eventos que eles geram.

O painel mostra exatamente a forma declarada: cada ponto exibe seus handlers, o tipo de cada um e a ordem resolvida. O tipo decide o que acontece quando um handler falha:

TipoQuando o próprio handler falhaAo recusar
uma barreiraa etapa é recusada — uma verificação de região que não respondeu não conta como aprovadaum código do catálogo da plataforma e um motivo legível. Quem chama decide pelo código; o texto do motivo pode mudar e ser traduzido
um observadora etapa continua concluída: se o pacote inicial não chegou, perde-se um baú, não o loginnão pode recusar

Um hook nunca decide quem fez login

Uma barreira responde sim ou não sobre uma identidade que a plataforma já estabeleceu. Ela não identifica o jogador, não atribui uma identidade e não substitui a confirmação do provedor.

O modelo

device · subject (the guest)Steam · subject (via Link)another player, same persona player — one stable id, never reused linklink: anonymous → registered, one waymerge: terminal; its id keeps resolving
Um vínculo é provedor · sujeito · jogador: vincular transforma um convidado em um só sentido, e depois de uma mesclagem o id mesclado continua resolvendo

Um jogador é o portador de uma identidade, não uma linha do seu esquema, e o identificador dele é estável e nunca reaproveitado — nem numa mesclagem: o id de um jogador mesclado continua resolvendo, em vez de virar uma referência solta. Um vínculo (link) é uma tripla: provedor · sujeito externo · jogador.

SempreO que é
provedor + sujeitoé único, e essa unicidade gera um conflito, não uma proibição: a resposta para “esta conta já está em uso” é escolher uma mesclagem
no máximo um vínculo por provedor por jogadoruma segunda conta do mesmo provedor é um conflito
um sujeito externonunca é o identificador de um jogador: ele pertence ao provedor, e usá-lo como nosso amarraria os nossos ids aos deles
tipo de identidade e status de acessosão eixos diferentes: anonymous contra registered é um; active / suspended / banned é outro. Por isso dá para expressar tanto “anônimo banido” quanto “registrado suspenso”
uma sessão e uma credencialsão coisas diferentes: a sessão é o registro; a credencial é o que você apresenta. Revogar uma sessão invalida todas as credenciais dela e encerra as assinaturas (subscription) abertas
várias sessões simultâneascada uma é revogada de forma independente
as claims de uma credencialsão contexto declarado — região, idioma — e apenas contexto. Uma claim nunca carrega autoridade
uma impressão digital do dispositivonão é uma identidade: nunca serve de motivo para admitir, só para recusar, e é armazenada e comparada numa forma irreversível

As três máquinas de estados.

DeEstados
o tipo de identidadeanonymous → registered, e a transição é de mão única
o status de acessoactive ⇄ suspended, e active → banned → active para desbanir
o jogadoralive → merged, em que merged é terminal: um jogador mesclado não faz login de novo

O que quem usa o módulo declara.

DeclaraO que é
política de loginse o login anônimo é permitido e as demais regras em torno do login. É declarada como um atributo no ponto de montagem do módulo — não num arquivo de configuração ao lado do código, nem montada em runtime
papel padrãoo conjunto de papéis (role) que um jogador novo recebe no primeiro login. O padrão não tem padrão: se você não declarar nada, os jogadores novos chegam sem nenhum papel, e isso é uma declaração legítima, não uma omissão
política de sessõeso que acontece quando se atinge o teto de sessões simultâneas — derrubar a mais antiga com um evento ou recusar a nova. Não há padrão
política de exclusãocomo a exclusão de um jogador chega aos dados que fazem referência a ele

Onde se configura um provedor. No plano do operador, não no código — credencial de loja não se guarda em repositório. O que é declarado aparece no console de administração somente para leitura.

O que conceder um papel permite. Papéis não são assunto só do operador: a interface oferece conceder e revogar para um jogador, então o jogo pode promover um oficial da guilda ou dar a quem organiza um torneio os poderes necessários, direto do próprio código.

SempreO que é
não é autopromoçãoconceder exige a permissão atômica declarada para isso, e um ator sem ela recebe um forbidden explícito, não uma operação que silenciosamente não faz nada
conceder é idempotenteconceder um papel que o jogador já tem é um sucesso, não um conflito: o estado é o conjunto de papéis, não o histórico de chamadas. Por isso, ao contrário do login, essa operação não precisa de chave de idempotência
a mudança de papel não é instantâneaela vale sem reemitir a credencial e fica visível no máximo dentro do limite de desatualização declarado para o cache de direitos. Um código que concede um papel e logo em seguida o verifica num cliente conectado precisa levar essa janela em conta

Do que os papéis são feitos e o que eles liberam está em Access & Roles.

Erros

  • Sem credencial, ou com uma credencial expirada, a resposta é not authenticated, e renovar a credencial resolve. Uma credencial revogada recebe a mesma resposta, mas renovar não resolve: só um novo login.
  • Apresentar de novo uma credencial já rotacionada é um conflito — é isso que torna a rotação detectável, em vez de tolerada em silêncio.
  • Um jogador banido ou suspenso e uma impressão digital (fingerprint) banida recebem forbidden, e não adianta repetir.
  • O par provedor+sujeito já está em uso: é um conflito, e dá para repetir depois de escolher uma mesclagem; uma segunda conta do mesmo provedor é um conflito que uma nova tentativa não muda.
  • Desvincular o último método de login é uma recusa de validação: deixaria uma conta que ninguém consegue acessar.
  • Mesclar um jogador já mesclado é um conflito — merged é terminal.
  • Provedor indisponível responde unavailable, e vale tentar de novo com backoff; provedor que rejeita a credencial responde not authenticated, e vale uma nova tentativa, não um loop.
  • Excesso de tentativas de login gera uma resposta da categoria rate limit, com um prazo.

Limites

Os números chegam com o capítulo de limites da plataforma; o que acontece em cada teto já está definido.

  • Sessões simultâneas por jogador: conforme a política declarada, a mais antiga é derrubada com um evento ou a nova é recusada. Não há padrão.
  • Tentativas de login por período e tentativas de vincular um par já em uso: um rate limit com prazo, e o contador de tentativas fica no histórico.
  • Vínculos por jogador: vincular mais um provedor é recusado como conflito.
  • Validade de uma credencial: not authenticated, e renovar resolve. Validade da credencial de renovação: só um novo login.
  • Retenção de um jogador anônimo sem logins: exclusão conforme a política declarada, com um evento. A política é declarada de forma explícita; não há padrão.
  • Entradas na lista de impressões digitais banidas: uma nova entrada é recusada, e as antigas nunca são removidas em silêncio.