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:
Linkadiciona 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
bannedque 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
authnã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ê
| Ator | O que faz nesta página |
|---|---|
player | faz login, vincula ou desvincula identidades, renova a credencial, faz logout |
moderator | revoga sessões; bane, suspende ou reativa jogadores |
backend-service | filtra o login por região; cria as primeiras linhas de um jogador novo; lê e revoga sessões |
Resumo
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 identitiesAinda 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.// 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 identitiesCada ponto é personalizado onde é declarado; as formas que um handler pode assumir estão reunidas em Extensibility:
[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)Ainda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
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.
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:
| Tipo | Quando o próprio handler falha | Ao recusar |
|---|---|---|
| uma barreira | a etapa é recusada — uma verificação de região que não respondeu não conta como aprovada | um 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 observador | a etapa continua concluída: se o pacote inicial não chegou, perde-se um baú, não o login | nã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
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.
| Sempre | O 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 jogador | uma segunda conta do mesmo provedor é um conflito |
um sujeito externo | nunca é 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 acesso | sã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 credencial | sã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âneas | cada uma é revogada de forma independente |
as claims de uma credencial | são contexto declarado — região, idioma — e apenas contexto. Uma claim nunca carrega autoridade |
uma impressão digital do dispositivo | nã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.
| De | Estados |
|---|---|
| o tipo de identidade | anonymous → registered, e a transição é de mão única |
| o status de acesso | active ⇄ suspended, e active → banned → active para desbanir |
| o jogador | alive → merged, em que merged é terminal: um jogador mesclado não faz login de novo |
O que quem usa o módulo declara.
| Declara | O que é |
|---|---|
política de login | se 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ão | o 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ões | o 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ão | como 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.
| Sempre | O que é |
|---|---|
não é autopromoção | conceder 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 é idempotente | conceder 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ânea | ela 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.