Plataforma multijogador - as correções do servidor normalmente são necessárias com um único cliente no servidor?


10

Atualmente, estou trabalhando em um jogo multiplayer bastante simples. Li vários artigos sobre as técnicas usadas para ocultar a latência, mas ainda não consegui entender alguns dos conceitos. Acho o tópico muito interessante e gosto de tentar idéias sozinho, mas acho que pedir gamedev stackexchange será mais eficiente para minha pergunta. Vou tentar o meu melhor para descrever minha situação atual e que pergunta surgiu ao longo do caminho.

No momento, eu quero apenas ter um único jogador sincronizado com o servidor. Teoricamente, presumi que um jogador sozinho com previsão do lado do cliente não exigiria correções do servidor, pois não existem fatores externos que influenciam seu movimento. Portanto, meu protótipo atualmente possui apenas um player sincronizado com um servidor sem que as correções do servidor sejam enviadas.

Se você conhece as redes de jogos, acho que pode pular as seções de contexto, embora eu tenha feito algo errado ao longo do caminho também.

O loop do cliente (uma vez por quadro, uma vez a cada ~ 16.67ms)

O loop simplificado do cliente se parece com:

  1. Verifique a entrada local (WASD) e empacote-a como ações (por exemplo Type=MoveLeft, Time=132.0902, ID=15). Mantemos as ações empacotadas para eventualmente enviá-las mais tarde. Além disso, aplicamos diretamente a ação desejada na simulação física local do jogo. Por exemplo, se tivermos uma MoveLeftação, aplicamos uma força à esquerda na velocidade do jogador.

  2. Marque para enviar ações. Para evitar abusar da largura de banda do cliente, envie apenas as ações empacotadas em determinados intervalos (por exemplo, 30 ms).

  3. Aplique modificações no servidor. Em um determinado momento, ele irá lidar com deltas e correções recebidas pelo servidor e aplicá-los à simulação local do jogo. Para esta questão em particular, isso não é usado.

  4. Atualize a física local. Execute o loop de física no player principal. Basicamente, isso faz a previsão do lado do cliente do movimento do jogador. Isso adiciona gravidade à velocidade do jogador, aplica a velocidade do jogador à sua posição, corrige colisões ao longo do caminho etc. Devo especificar que a simulação da física é sempre executada com um delta segundos fixo (chamado várias vezes, dependendo dos segundos delta reais) .

Estou pulando alguns detalhes específicos sobre a física e outras seções porque sinto que eles não são necessários para a pergunta, mas fique à vontade para me informar se eles seriam relevantes para a questão.

O loop do servidor (a cada 15ms)

O loop simplificado do servidor se parece com:

  1. Lidar com ações. Verifique os pacotes de ação recebidos dos clientes e aplique-os à simulação de física do servidor. Por exemplo, poderíamos receber 5 MoveLeftações e aplicaríamos a força à velocidade 5 vezes . É importante observar que um pacote de ação inteiro é executado em um "quadro" , ao contrário do cliente em que é aplicado assim que a ação acontece.

  2. Atualize a lógica do jogo. Atualizamos a física do jogo, movemos jogadores e corrigimos colisões, etc. Também empacotamos todos os eventos importantes que foram enviados aos jogadores (por exemplo, a saúde de um jogador caiu, um jogador morreu etc.) posteriormente.

  3. Envie correções. Nós regularmente (por exemplo, uma vez a cada 35ms) enviamos deltas para outros jogadores (por exemplo, posições, saúde, etc.) se eles mudaram recentemente. Esta parte não está implementada no momento, pois desejo que a simulação de um único player forneça os mesmos resultados no cliente e no servidor sem correções, para garantir que a previsão do lado do cliente funcione bem.

O problema

O sistema atual funciona bem em circunstâncias simples, e fiquei surpreso ao ver que deu resultados muito semelhantes com movimentos horizontais simples (as imprecisões são devido a erros de precisão de ponto flutuante, acredito):

A sincronização funciona bem com colisões / movimentos simples

Por favor, ignore os gráficos do protótipo. Retângulo branco = jogador, Retângulos vermelhos = obstáculos, Azul = fundo

No entanto, estou recebendo erros de sincronização depois de fazer movimentos sensíveis ao tempo, como pular e me aproximar de um obstáculo isolado:

A sincronização não funciona porque pulei o obstáculo especificado em momentos sensíveis ao tempo

Em teoria, eu esperaria que ambos terminassem sempre com os mesmos resultados, pois não há fatores externos para o cliente influenciando sua posição. Na prática, porém, acho que entendi o problema.

Como pular um obstáculo como esse depende muito do tempo do jogador, pequenas variações de quando a velocidade é aplicada à posição terão repercussões no resultado (por exemplo, o cliente pode se afastar a tempo de evitar uma colisão com o jogador). obstáculo, enquanto o servidor faria isso ao receber todo o pacote de ações posteriormente e permanecer preso no obstáculo por um pequeno período de tempo, alterando o resultado final). A diferença entre a maneira como o cliente e o servidor lidam com isso é principalmente que o cliente executa todas as suas ações à medida que elas acontecem, enquanto o servidor faz todas elas em massa à medida que as recebe.

A questão

Esse longo contexto finalmente leva à minha pergunta (obrigado por ler até aqui): É normal exigir correções do servidor, mesmo quando há apenas um jogador sincronizado com o servidor, ou devo usar certas técnicas para evitar a dessincronização em situações sensíveis ao tempo ?

Pensei em certas soluções possíveis, algumas das quais me sinto menos à vontade:

  1. Implemente a correção do servidor. Simplesmente assuma que esse é um comportamento normal e corrija os erros à medida que eles acontecem. Eu queria implementá-lo de qualquer maneira, mas só queria ter certeza de que o que fiz até agora é aceitável.

  2. Use o tempo do cliente fornecido para aplicar as ações desejadas. Eu acho que isso seria semelhante à compensação de atraso, exigindo "voltar no tempo" e verificar se há movimento. É como aplicar correções no servidor, voltar no tempo e reaplicar as próximas ações depois disso. Eu realmente não gosto da ideia. Parece complexo, dispendioso em recursos e requer confiança no tempo determinado do cliente (embora eu pretenda realmente verificar se o tempo parece relativamente legítimo).

  3. Peça à GameDevelopment StackExchange uma ótima nova idéia que resolverá todos os meus problemas.

Estou apenas começando no mundo das redes de jogos, portanto, sinta-se à vontade para corrigir / criticar / insultar qualquer um dos conceitos acima ou fornecer idéias / recursos que possam me ajudar ao longo de minha jornada no Maravilhoso Mundo das Redes. Perdoe-me se eu pudesse encontrar minha resposta em outro lugar, eu falhei nisso.

Muito obrigado pelo seu precioso tempo.


Seu servidor e cliente executam quadros em taxas diferentes. O que acontece se o cliente executar duas ações em quadros consecutivos, mas o servidor perceber uma diferença de um quadro entre eles?
user253751

@immibis me baseei em gafferongames.com/game-physics/fix-your-timestep para minimizar esse efeito.
Jesse Emond

Respostas:


11

Nesses casos, é melhor deixar o cliente ser um pouco autoritário. Para controles tão precisos, é extremamente improvável que você tenha um bom comportamento, mesmo com correção e previsão realmente avançadas.

O cliente precisa passar do envio de mensagens "eu pulei" para o envio de mensagens "eu pulei de X, Y na hora T". O servidor então verifica se o local está próximo do que ele achava que estava no momento T (que você pode limitar a um tempo razoavelmente pequeno no passado) para se proteger contra trapaças e simular o salto da posição que o cliente enviei. O servidor só corrige o cliente quando está muito fora de sintonia (geralmente devido a atrasos ou similares).

Esse tipo de técnica é usada em conjunto com correção e interpolação para fazer com que o jogo pareça responsivo ao cliente local e pareça suave para clientes remotos.


Muito interessante. Vou tentar implementar isso e ver como vai. Muito obrigado por reservar um tempo para responder. A propósito, você mencionou "bem próximo" e "tempo razoavelmente pequeno no passado", você simplesmente verifica com uma distância e tempo constantes, respectivamente? Ou você usaria técnicas mais sofisticadas, como manter um histórico de posições e usar o tempo médio de ida e volta do cliente (ou qualquer outra coisa, realmente)?
Jesse Emond

Tudo o que funciona para o seu jogo. Comece simples, complique-o apenas na medida em que achar necessário. Alguns servidores mantêm uma espécie de histórico de snapshots para ~max(RTT)ticks de servidor no passado, mas se você precisa especificamente do seu jogo, não sei. Pode ser ainda mais útil para jogos no estilo de atirador, onde você também deseja realizar algum nível de detecção de acerto / tiro no cliente e precisa não apenas saber onde um jogador estava há 46ms, mas também onde seu alvo estava há 46ms e onde plataformas oclusoras em movimento onde.
Sean Middleditch

Perfeito. Vou experimentar e ver o que funciona melhor. Marcando-a como a resposta aceita, obrigado novamente!
Jesse Emond
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.