Esta página ainda não foi traduzida: mostrando a versão em inglês.
Profile
Um perfil é uma visão, e quase nada nele pertence à plataforma. Sobre um jogador, a plataforma guarda o player_id e o perfil de sistema por trás dele — identidades, sessões, vínculos com provedores, tudo isso em Auth & Players. Tudo o que o jogador tem é uma entidade sua, que pertence a esse jogador. O perfil é o conjunto dessas entidades que o seu projeto declara, lido para um dono de uma só vez.
Quando usar
- Uma tela precisa da fatia de um jogador numa só chamada: o conjunto declarado se distribui pelas entidades desse jogador, em vez de o cliente costurar várias consultas.
- As telas da plataforma precisam mostrar uma pessoa, não um identificador: um ranking (leaderboard), uma fila de moderação e um ticket de suporte guardam só um
player_idaté o projeto indicar qual registro exibe o jogador. - Outro jogador precisa de um cartão: é a mesma leitura para outro dono, restringida pelo predicado de linhas e pela máscara de campos já declarados em Access & Roles.
- Um HUD precisa acompanhar em tempo real o estado do jogador: a leitura é uma seleção, e uma seleção faz assinatura (subscription).
- Dispensável quando os dados não pertencem a um jogador. Linhas compartilhadas e globais são uma seleção comum de Entity, sem um dono a partir do qual distribuir a leitura.
Quem faz o quê
| Ator | O que faz nesta página |
|---|---|
schema-author | marca entidades como pertencentes ao jogador e declara quais delas formam o perfil |
player | lê o próprio perfil; as escritas vão direto para as entidades |
room-visitor | lê o perfil de outro jogador, até onde o predicado e a máscara desse jogador permitem |
Resumo
A participação no perfil é declarada por entidade, não por campo. A entidade diz que faz parte do perfil; o que outro jogador pode ver dela é definido pela máscara de campos do papel (role) que faz a leitura (Access & Roles).
loadout e progress marcados como pertencentes ao jogador e incluídos no conjunto do perfil[Entity("loadout"), OwnedBy(Owner.Player), InProfile]
public class Loadout { public string Primary = ""; }
[Entity("progress"), OwnedBy(Owner.Player), InProfile]
public class Progress
{
public int Level;
public string Title = "";
public int SecretMmr; // no reading role's mask names it: it stays server-side
}@Entity('loadout') @OwnedBy(Owner.player) @InProfile()
export class Loadout { primary = ''; }
@Entity('progress') @OwnedBy(Owner.player) @InProfile()
export class Progress {
level = 0;
title = '';
secretMmr = 0; // no reading role's mask names it: it stays server-side
}@entity("loadout")
@owned_by(Owner.PLAYER)
@in_profile
class Loadout:
primary: str = ""
@entity("progress")
@owned_by(Owner.PLAYER)
@in_profile
class Progress:
level: int = 0
title: str = ""
secret_mmr: int = 0 # no reading role's mask names it: it stays server-sideAinda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
UCLASS(PSEntity = "loadout", PSOwnedBy = "Player", PSInProfile)
class ULoadout : public UObject
{
GENERATED_BODY()
UPROPERTY() FString Primary;
};
UCLASS(PSEntity = "progress", PSOwnedBy = "Player", PSInProfile)
class UProgress : public UObject
{
GENERATED_BODY()
UPROPERTY() int32 Level;
UPROPERTY() FString Title;
UPROPERTY() int32 SecretMmr; // no reading role's mask names it: it stays server-side
};
// declarations compile into the same pushed model — playserv push from the UE project or CI[Entity("loadout"), OwnedBy(Owner.Player), InProfile]
public class Loadout { public string Primary = ""; }
[Entity("progress"), OwnedBy(Owner.Player), InProfile]
public class Progress
{
public int Level;
public string Title = "";
public int SecretMmr; // no reading role's mask names it: it stays server-side
}A leitura é uma seleção restrita a um dono: a interface de consulta de Entity, com o dono fixo e a lista de entidades tirada da declaração. Profile é o nome dessa leitura, não um módulo por trás dela: os mesmos direitos, os mesmos predicados, os mesmos filtros, a mesma assinatura, porque é a mesma operação.
var mine = playserv.Profile.Mine(); // a selection, not a record
var rows = await mine.Query(); // loadout + progress, one pass
mine.Subscribe(changed => Hud.Refresh(changed)); // the selection stays live
var rival = await playserv.Profile.Of(rivalId).Query(); // only what the mask leavesconst mine = playserv.profile.mine(); // a selection, not a record
const rows = await mine.query(); // loadout + progress, one pass
mine.subscribe((changed) => hud.refresh(changed)); // the selection stays live
const rival = await playserv.profile.of(rivalId).query(); // only what the mask leavesmine = playserv.profile.mine() # a selection, not a record
rows = await mine.query() # loadout + progress, one pass
mine.subscribe(lambda changed: hud.refresh(changed)) # the selection stays live
rival = await playserv.profile.of(rival_id).query() # only what the mask leavesAinda não está decidido como declarar com atributos em Go (O-1). Os exemplos chegam quando a forma de escrita for escolhida.
TPSSelection<UPSProfile> MyProfile = Client->Entities->Of<UPSProfile>()->Select().GetMine(); // a selection, not a record
MyProfile.Then(TPSOnResult<FPSProfileRows>::CreateWeakLambda(this, [this](const TPSResult<FPSProfileRows>& Result)
{
if (!Result.HasValue()) { return; }
Hud->ShowProfile(Result.Value()); // loadout + progress, one pass
}));
TPSSubscription ProfileWatch = MyProfile.Subscribe(
[this](const FPSProfileChange& Changed) { Hud->Refresh(Changed); });
Client->Entities->Of<UPSProfile>()->Get(RivalId,
TPSOnResult<FPSProfileRows>::CreateWeakLambda(this, [this](const TPSResult<FPSProfileRows>& Rival)
{
if (!Rival.HasValue()) { return; }
Hud->ShowRival(Rival.Value());
}));
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.var mine = playserv.Profile.Mine(); // a selection, not a record
var rows = await mine.Query(); // loadout + progress, one pass
mine.Subscribe(changed => Hud.Refresh(changed)); // the selection stays live
var rival = await playserv.Profile.Of(rivalId).Query(); // only what the mask leavesO modelo
| Conceito | O que é |
|---|---|
player_id | tudo o que a plataforma entende por jogador, mais o perfil de sistema por trás dele — Auth & Players |
| conjunto do perfil | as entidades do jogador que o projeto declara como perfil; declarar esse conjunto é opcional |
| seleção por dono | a leitura: entra um dono, saem as linhas dele em todo o conjunto — com os mesmos direitos, predicados, filtros e assinatura de qualquer seleção de Entity |
| leitura pública | essa mesma seleção para outro dono, restringida pelo predicado de linhas e pela máscara de campos do papel que lê (Access & Roles) |
O que vale para toda leitura de perfil.
| Sempre | O que é |
|---|---|
não existe registro de perfil | o perfil não tem identificador próprio, revisão, histórico nem ciclo de vida, porque é uma visão (view) sobre linhas que têm tudo isso |
a escrita vai para onde os dados estão | altere a linha progress, e toda leitura de perfil que a inclui vê o novo valor na próxima vez |
não existe escrita pública | uma visão não tem onde receber escrita, e o estado compartilhado que pode ser alterado passa pelo código do servidor |
a posse é um predicado | e não uma coluna que a plataforma acrescenta (Access & Roles): owner == caller.player é um caso desse mecanismo, “um dos participantes” é outro |
campo derivado é trabalho de hook | é um observador pós-alteração que grava Progress.Title quando Level passa de um limite. Ele vem depois da escrita; não pode recusá-la |
Erros
- Um projeto sem conjunto de perfil declarado não tem leitura de perfil, e a chamada é recusada como unavailable — e não respondida com um resultado vazio, que daria a entender que o jogador tem um perfil e ele só está em branco.
- Uma linha escondida por um predicado responde
not found, assim como uma que não existe: a leitura pública nunca vira um jeito de descobrir o que existe mas não é visível. - Um campo fora da máscara do papel que lê fica ausente da resposta, e não presente e vazio.
- Uma escrita em nome de um jogador que não indica o jogador é uma recusa de validação.
- Declarar o conjunto do perfil é um ato de esquema —
fnouadm. Se uma chave de jogador tentar fazer isso, recebe forbidden, e a publicação inteira é rejeitada, em vez de ser declarada pela metade.
Limites
Os números chegam com o capítulo de limites da plataforma; o que acontece em cada teto já está definido.
- Tamanho da página da seleção por dono: cortado no teto, com a flag “há mais” ainda verdadeira; devolver menos sem a flag é proibido.
- Tamanho de um conjunto incluído por linha: cortado pela mesma regra, com a flag na inclusão.
- Frequência de alterações numa instância: uma recusa de rate limit com prazo. A leitura de perfil é uma seleção como qualquer outra e herda os tetos de Entity, em vez de declarar os próprios.