Como lidar com computadores mais rápidos em um videogame cliente / servidor em tempo real

15

Estou criando meu primeiro jogo online usando o socket.io e gostaria que fosse um jogo multiplayer em tempo real como agar.io ou diep.io.

Mas me deparei com a questão de tentar descobrir como fazer com que todos os computadores funcionem na mesma velocidade.

Eu tenho três idéias para modelos, mas nenhuma delas parece certa, e me pergunto como os videogames normais fazem isso. (Você pode pular a leitura das minhas idéias; elas apenas fornecem uma maneira de ver os problemas que estou tendo.)

  1. O servidor permite que os clientes executem por conta própria e transmitam atualizações ao servidor, que as transmite para o restante dos clientes. Isso tem o problema de que alguns computadores rodam mais rápido que outros, permitindo que eles sejam atualizados mais rapidamente e se movam pela tela mais rapidamente.

  2. Peça ao servidor que informe aos clientes quando atualizar. Posso então esperar até o último cliente responder (uma péssima idéia no caso de uma pessoa ter um computador lento), esperar até o primeiro cliente responder (novamente, aguardando a comunicação antes de cada quadro) ou apenas enviá-lo o mais rápido possível (o que parece ter o mesmo problema que o número 1).

  3. No início do jogo, peça ao servidor que informe aos clientes a velocidade de atualização. Isso significaria que o cliente seria responsável por restringir o movimento entre esse período. Por exemplo, se alguém conseguisse pressionar um botão duas vezes nesse período, enviaria apenas um evento de pressionamento de botão. Isso tem o problema de que algumas ações seriam ignoradas (como pressionar o botão duplo) e que a interação dependeria do relógio do cliente, que pode não corresponder ao relógio do servidor. O servidor precisará acompanhar cada cliente e garantir que suas atualizações sejam enviadas no momento correto.

Eu fiz algumas pesquisas , mas os artigos que li não parecem abordar especificamente o que fazer se um cliente enviar atualizações mais rapidamente do que outros clientes.

No meu caso particular, estou lidando com pessoas que possuem velocidades de teclado mais rápidas (o computador deles enviaria mais atualizações de teclado do que outros computadores).

Como os programadores geralmente lidam com isso?

Pro Q
fonte
1
Na minha experiência, eles não. É por isso que existem máquinas de jogos; os que pagam 5 mil por um estado da chama arte atirador têm automaticamente uma vantagem dos mais ainda usando um Commodore 64.
Robert Harvey
1
Então, meu jogo vai parecer lento porque temos que jogar com o menor denominador comum? Parece que o servidor do jogo deve definir o ritmo e cabe aos clientes acompanhar ou você terá que ficar para trás.
Jeffo
3
Talvez eu esteja entendendo mal a pergunta, mas é provável que você esteja usando um modelo de cliente e servidor baseado em ticks. gamedev.stackexchange.com/questions/81608/... Basicamente, você só apenas a entrada processo e lógica em cada X quantidade de tempo (geralmente 1 / N segundos, como 1/60 para a lógica 60Hz)
Christopher Wirt
4
Depois de ler a pergunta um pouco mais de perto, parece que você está muito focado nos aspectos do cliente do jogo. Se você quer um jogo multiplayer "justo", seu servidor terá que ter autoridade. Significando que tudo o que acontece com os clientes é verificado ou feito pelo servidor. Então você restringe as coisas a partir daqui, provavelmente através de um sistema baseado em ticks, como acima.
Christopher Wirt
1
Ah, netcode em tempo real. O lugar mais profundo e sombrio do desenvolvimento de jogos. Bem-vindo ao navio, companheiro!
T. Sar - Restabelece Monica

Respostas:

8

Sua terceira ideia parece ser a mais próxima do que penso ser a solução da indústria para esse tipo de problema.

O que você está descrevendo é chamado de carrapatos . Em cada marca, um número fixo de ações seria processado para cada cliente em série. Muitas vezes, os servidores de jogos terão algumas ações paralelas quando capazes, mas esse é um problema muito mais complicado.

Um tick provavelmente terá a forma de 1 / N segundos, sendo N o número de ticks por segundo ou Tickrate. Esse tickrate pode ser muito frequente ou pouco frequente, dependendo do seu caso de uso. Minha sugestão pessoal seria evitar uma taxa de ticks acima de 60 ticks / segundo, a menos que você tenha certeza de que precisa de mais. Você provavelmente não :)

As ações devem ser atômicas. Por exemplo, no slither.io, uma ação como mover-se não deve processar imediatamente algo como quebrar sua corrente, a menos que o jogador que você acertar já tenha feito a jogada. Isso pode parecer trivial para algo no nível de pixels, mas se você estiver lidando com movimentos baseados em blocos, torna-se muito mais óbvio e garante justiça. Se o Jogador A se mover para o bloco X, Y e o Jogador B estiver nesse bloco, você deve garantir que, até o final do tique, o jogador B ainda esteja nesse bloco para que todas as ações ocorram entre eles.

Além disso, eu diria que evite fazer seus cálculos no lado do cliente, a menos que sejam feitos independentemente no lado do servidor. Isso fica complicado e caro com a física (muitos jogos optam por uma taxa de tick mais baixa para a física do que muitas outras ações e eventos por causa disso)

Para referência, aqui está um bom link para complementar seu próprio entendimento de servidores de jogos e redes multiplayer.

Por fim, eu diria que não deixe a justiça arruinar seu servidor. Se houver explorações que façam com que seu jogo seja injusto, conserte-as. Se é uma questão de um computador melhor ter uma pequena vantagem, eu diria que pode não ser tão importante assim.

Christopher Wirt
fonte
3
A @ProQ vale a pena notar que escrever um bom código de rede não é trivial! Se seu aplicativo não funcionar bem desde o início e seu código de rede parecer ruim, não desista. Mesmo as pessoas que fazem isso para viver todos os dias têm problemas com isso. é apenas que dura!
T. Sar - Restabelece Monica
0

O sistema a seguir garante que todos os clientes e o servidor compartilhem quase o mesmo estado do jogo a qualquer momento.

Tenha o estado do jogo no cliente e no servidor.

Quando um cliente tenta usar um comando (mouse, teclado, etc. outra entrada), verifique o estado do jogo, se for válido.

Se for, envie o comando para o servidor, sem executá-lo no cliente remetente.

Quando o servidor receber o comando, verifique o estado do jogo, se for válido.

Se for, envie o comando de volta para TODOS os clientes com a data exata do futuro após a execução no servidor, depois execute as ações solicitadas pelo comando após um atraso igual ao tempo mínimo para enviar o comando aos clientes . depois grave a data, isso ajuda a fazer previsões futuras. Se o tempo variar muito, torne seu sistema de jogo mais determinístico.

Quando um cliente recebe um comando do servidor, verifique seu estado do jogo, se for válido, execute imediatamente as ações solicitadas pelo comando, verifique a data atual e compare-a com a previsão de data recebida. Se não for válido, o cliente está fora de sincronia. (Todos os clientes com uma conexão semelhante recebem ao mesmo tempo)

Se a data for anterior, você teve um problema na etapa anterior, corrija-o. Se a data for um pouco depois, não faça nada. Se a data for muito depois, o cliente está fora de sincronia, ou seja, o cliente fica muito atrás.

Quando um cliente estiver fora de sincronia, solicite uma cópia de todo o estado do jogo do servidor e use esse. Se isso acontecer com muita freqüência, corrija isso porque é mais caro do que enviar comandos.

Nos clientes, renderize SOMENTE as coisas na tela quando não houver mais nada a fazer. Em cenários simples, a função de renderização leva apenas o estado atual do jogo como entrada.

Além disso, você pode otimizar muito, usando sistemas preditivos, comandos de agrupamento, renderização somente diffs, etc ...

A validação deve diferir, por exemplo, cabe ao servidor limitar as solicitações de comando / unidade de tempo.

Walle Cyril
fonte
0

Não sei se isso é problema de bibliotecas ou ambiente que você está usando, mas acho que você está abordando isso totalmente errado.

Na grande maioria dos jogos multiplayer, é apenas o servidor que está fazendo cálculos reais. Os clientes são apenas máquinas de IO idiotas, onde apenas um problema real de desempenho é desenhar gráficos 3D. E, nesse caso, não importa se o cliente pode executar a 20 ou 200 FPS, porque isso afeta apenas os recursos visuais. Isso significa que "atualização do cliente" não tem absolutamente nenhum significado. O cliente pode tentar "prever" o que o servidor pode calcular, mas isso é apenas para suavizar a sensação da jogabilidade e não tem nenhum impacto real na própria jogabilidade.

velocidades de teclado mais rápidas

Eu nem sei o que isso significa. A maioria das pessoas nem consegue acompanhar a velocidade dos teclados de última geração, então como isso afetaria o desempenho dos jogadores?

Caso contrário, a questão parece muito ampla e você deve se concentrar em um único problema real, em vez de tentar inventar um problema "geral" que talvez nem tenha.

Eufórico
fonte
velocidades mais rápidas do teclado não são sobre digitação; trata-se de quantos comandos podem ser enviados se você pressionar a tecla "avançar" ou a tecla "atirar".
Gbjbaanb
@gbjbaanb E como isso é um problema? Você deve se preocupar apenas com comandos pressionados e liberados. Você não deve se preocupar com a rapidez com que a atualização é feita.
Euphoric
Você se importa se estiver lidando com todos os eventos como o OP esperado - no cliente. Se pressionar w, você avançasse um pouco, e seu teclado fosse mais rápido que o de outra pessoa, você iria mais rápido que eles. A última coisa que você quer é fazer um jogo de corrida, você deve continuar pressionando uma tecla para avançar!
Gbjbaanb
@gbjbaanb Não. Isso está errado. O servidor sabe que "o jogador está avançando" e, a cada 1/60 do segundo, atualiza as posições de TODOS os jogadores com base em se eles estão avançando. É assim que TODOS os jogos funcionam. O loop de atualização é o mesmo para todas as entidades no jogo e as atualizações NÃO são baseadas em eventos da interface do usuário.
Euphoric
1
Não, não era esse o entendimento do OP, e foi por isso que ele pediu, e foi por isso que você disse: "Eu nem sei o que isso significa". Se você entende a posição do OP, então a pergunta dele faz sentido. Se o OP codificou seu jogo para trabalhar com um loop de jogo baseado inteiramente na rapidez com que os eventos são entregues pelo cliente, é sobre isso que ele está perguntando - você está errado ao aplicar um design de jogo hipotético ao que o OP está realmente perguntando, independentemente de seu design estar errado para a maioria dos padrões.
Gbjbaanb