Simulação Autoritativa
Última atualização: 15 de setembro de 2026
Uma vez que os jogadores estão em uma sala, o servidor decide o que de fato acontece — onde cada um está, quem acertou quem, qual é o placar. Fazer isso no servidor, e não no cliente, é o que torna um jogo autoritativo e difícil de trapacear. O SDK de sala oferece um loop de tick de passo fixo, uma forma de compor a lógica como sim systems e snapshots que transmitem o estado resultante aos clientes de forma eficiente.
A simulação autoritativa é opcional. Um jogo por turnos ou pouco sincronizado pode ignorar o loop de tick por completo e usar apenas o roster e as mensagens da sala. Recorra a isto quando o servidor precisar avançar um mundo contínuo — movimento, física, combate em tempo real.
O loop de tick
Uma sala avança em passos fixos, o que mantém o comportamento determinístico independentemente da variação do timing de rede.
OnSimStep(float dt)executa um passo fixo de simulação.SimStepSecondsdefine o tamanho do passo (padrão 1/30 s).OnTick(float dt)roda uma vez por tick do host e conduz os passos, recuperando os perdidos atéMaxCatchUpSteps(padrão 6), para que uma pausa momentânea não deixe o mundo permanentemente atrasado.
protected override float SimStepSeconds => 1f / 60f; // sim a 60 Hz
protected override void OnSimStep(float dt)
{
// avançar movimento, resolver combate, contar timers — tudo autoritativo no servidor
}
A recuperação é limitada de propósito: se o servidor não consegue acompanhar, ele roda no máximo alguns passos extras por tick e então aceita um mundo mais lento, em vez de entrar em uma espiral de backlog cada vez maior.
Sim systems
Em vez de um único OnSimStep gigante, você pode compor o comportamento a partir de sim systems — pequenas unidades que fazem uma coisa cada (movimento, depois colisão, depois respawn) e rodam em uma ordem definida. Há duas formas: um system que roda uma vez para a sala inteira (ISimSystem) e um que roda por jogador (IPlayerSimSystem), cada um vinculado a um SimPhase para que a ordem seja explícita. Registre um system por jogador com AddPlayerSystem(...).
Isso mantém um jogo grande legível: cada regra fica isolada e testável, e a ordem em que rodam é dado, não um emaranhado de chamadas. O OnSimStep padrão simplesmente roda os systems que você registrou.
Snapshots
O servidor guarda a verdade; os clientes precisam de uma visão dela estável e barata. Um snapshot é uma projeção do estado da sala que você declara uma vez e o SDK transmite aos clientes em um cronograma.
protected override void Setup()
{
DeclareSnapshot<MySnapshot>(plan =>
{
plan.Group = "room";
plan.Cadence = SnapshotCadence.EveryN(2); // a cada 2 ticks
plan.Assemble = ctx => BuildSnapshot(ctx); // montar a partir do estado atual
});
}
A cadência controla com que frequência um snapshot sai — SnapshotCadence.EveryTick para ação rápida, EveryN(n) para enviar com menos frequência em jogos mais calmos ou economizar banda. Snapshots são independentes da taxa de simulação, então você pode simular a 60 Hz e transmitir a 20. Um snapshot é entregue aos clientes por um grupo e, por padrão, também publica imediatamente quando um evento relevante ocorre (ForcePublishOnEvent).
Cadência adaptativa
Para salas cuja intensidade varia, uma AdaptiveCadencePolicy permite que a taxa de broadcast suba e desça conforme a atividade — transmitir com frequência durante um tiroteio, recuar quando a sala está calma — limitada por um WsOpsBudget e presa entre MinHz (padrão 1) e MaxHz (padrão 15). Uma taxa fixa pode ser fixada por ambiente via PLAYSERV_BROADCAST_HZ. O SnapshotContext dá ao plano o tick e quaisquer eventos drenados de que ele precisa para montar cada frame.
Snapshots descrevem o que enviar e com que frequência, não como o cliente desenha. Interpolação, predição e reconciliação vivem no cliente; o trabalho do servidor é emitir um fluxo autoritativo e bem ritmado.
Blocos de construção de gameplay
Para mecânicas comuns, você compõe o seu tipo de jogador ou entidade a partir de pequenas interfaces de papel (uma posição, um corpo com velocidade, vida, uma sequência de input, e assim por diante), para que os systems embutidos operem sobre qualquer jogo que as adote. Não é obrigatório usá-las — um jogo com outra mecânica define as suas — elas são apenas o vocabulário que os systems prontos falam.
Persistindo resultados
Quando uma partida termina, geralmente você quer que algo sobreviva a ela — um placar, uma estatística, uma mudança de inventário. Duas declarações ligam a sala aos seus dados de jogo: DeclareDataMirror(...) mantém o estado da sala espelhado em uma tabela conforme ele muda, e DeclarePersistedCounter(...) acumula um valor corrente (como um placar) e o grava. Como o SDK de servidor age como sujeito servidor, essas gravações não estão sujeitas às permissões de tabela do cliente — veja Controle de Acesso.
Próximos passos
- Hospedagem de Game Server — a sala e o host dentro dos quais essas simulações rodam
- Trabalhando com Dados de Jogo — para onde vão o estado espelhado e os contadores persistidos
- Assinaturas de Dados — como os clientes observam dados que mudam