Sincronização de cliente de baixo tráfego com servidor no MMO

22

Estou implementando o MMO, onde o jogador voa no espaço em sua nave estelar, controlando-o com as setas e cooperando com outros jogadores.

Eu quero implementá-lo para que o jogador seja capaz de desviar do navio de foguetes ou qualquer outra coisa, então estou tentando prever todo o estado do jogo no lado do cliente usando o mesmo algoritmo de simulação do mundo usado pelo servidor. Esse mundo de jogo é escrito em C # e será chamado diretamente no cliente (escrito em Unity3D) e através do CLR no servidor C ++ (no Linux). Conexão através de UDP.

O problema é como manter, por exemplo, 1000 jogadores em um único mapa (excluindo todos os outros objetos de jogo, mobs ...): Digamos que eu:

  • sincronizar servidor com clientes 50 vezes por segundo
  • envia a cada cliente estados apenas dos objetos do jogo (e jogadores) que ele é capaz de ver (dentro de um raio)
  • tem que enviar 100 objetos para cada jogador dentro do seu raio de visão
  • deve enviar em média 50 bytes por objeto de jogo (é id, x, y cordas, rotação, estado ...)

portanto, ele precisará ter essa largura de banda de rede: 1000 (clientes) * 50 (vezes por segundo) * 100 (objetos a serem enviados para cada jogador) * 50 (bytes por objeto) = 250 000 000 bytes por segundo! É impossível!

É possível reduzir esse valor de alguma forma? Por exemplo, permita que os clientes simulem completamente seus mundos de jogo (por um longo período de tempo) e enviem a eles apenas entradas de outros clientes e sincronizem mundos de jogo, digamos, a cada vários segundos, mas causará estranhos problemas de dessincronização na trela devido a cálculos flutuantes .

Enfim, como esses jogos são programados de maneira comum? Obrigado.

eslavo
fonte
1
Envio apenas informações lógicas sobre objetos (posição do mundo, estado atual (que é um byte) e assim por diante) - sem gráficos.
Slav
1
@Slav: Legal! Toda essa mudança me lembra meus dias de programação em ASM.
Randolf Richardson
1
Por que não "hoje dias"? :) Quando escrevo em AS3, Java, Lua, C # e enfrento um desempenho tão ruim, sinto falta do C ++ e lembro do ASM.
Slav
1
@Slav: Heheh, não fiz muito ASM recentemente. A maioria das coisas para mim está em Java e Perl (principalmente mod_perl2) atualmente, mas também gosto muito dessas linguagens.
Randolf Richardson
2
@Slav, você escreveu: "Quando escrevo em AS3, Java, Lua, C # e enfrento um desempenho tão ruim, sinto falta do C ++ e lembro do ASM". Você deve aprender a usar Lua e C # corretamente, talvez ache o desempenho menos abismal. Além disso, reclamar da (supostamente) linguagem de script mais rápida do mercado é, na melhor das hipóteses, singular ... É um jogo de análise do genoma humano em tempo real?
Raine

Respostas:

20

Você precisa apenas de 30 atualizações (ou menos, talvez 10 ou 20) por segundo. interpolar as posições dos objetos em movimento do lado do cliente. Em geral, você só deve enviar dados quando for realmente necessário. No WoW, você pode receber mais atualizações dos jogadores com quem está em um grupo do que dos jogadores que estão no mesmo local. Além disso, se outro jogador estiver longe de você, você não receberá tantas atualizações por segundo sobre ele.

Em seguida, envie apenas um instantâneo completo para cada jogador quando ele se conectar. Depois disso, envie apenas as alterações dos objetos do jogo. Se nenhuma alteração ocorreu, não a envie.

Em seguida, faça uso intenso de BitVectors ou, no entanto, você poderá chamá-los para reduzir a quantidade de dados desnecessários! Exemplo: Você também pode tentar escrever um float usando apenas um byte (em um intervalo de 0 a 1 ou -1 a 1) para ter apenas 256 ou 128 valores diferentes. Mas o jogador não notará movimentos bruscos graças às interpolações.

Veja aqui um exemplo com a LidgrenLibrary sobre como compactar dados: http://code.google.com/p/lidgren-network-gen3/wiki/Optimization

Próximo: Tente reduzir o raio de visão dos jogadores à medida que eles se movem e transmitir apenas informações importantes nesse período. Então, quando eles pararem, aumente o raio de visão novamente. Você pode usar um sistema de hash espacial ou uma árvore bsp para reduzir a sobrecarga de procurar objetos que estão "dentro do alcance". Esta é uma boa leitura para o tópico: http://en.wikipedia.org/wiki/Collision_detection

Comprima também os dados, VOCÊ MESMO, VOCÊ CONHECE a estrutura dos dados e a coerência temporal nos dados (que podem e devem ser explorados). Um algoritmo geral como Bzip2, Deflate, qualquer que seja, deve ser usado, mas apenas como o estágio final da compactação!

Além disso, para informações não críticas ao jogo, você também pode empregar técnicas P2P adicionais. Exemplo: Um jogador reproduz a animação "Olá". (Apenas um efeito gráfico) O jogador envia essas informações para o servidor, mas o servidor não retransmite as informações para os outros jogadores. Em vez disso, esse efeito não crítico é enviado pelo próprio jogador para os outros clientes no intervalo.

EDIT (por causa do comentário):

Métodos adicionais para diminuir a contagem média de bits por segundo para cada jogador:

  1. Você escreveu que envia "O objeto não foi alterado". Não há razão para fazer isso. Se você se preocupa com a perda de pacotes (e com a simulação fora de sincronia por causa disso), considere o seguinte: A cada passo fixo do tempo (por exemplo, 100, 200, 300, 400 ...) faça o hash do estado da simulação e envie-o ao servidor . o servidor confirma ou envia um instantâneo completo de todos os dados de volta.

  2. Para coisas como foguetes ou até jogadores, você pode empregar não apenas interpolação, mas também extrapolação para tornar a simulação mais realista. Exemplo 'Foguete': em vez de atualizar com mensagens como "Agora está na posição x", basta enviar uma mensagem contendo o seguinte: "Foguete gerado: posição (vetor), tempo (em que etapa de simulação o foguete foi gerado), velocidade ( vetor)". Portanto, você nem precisa incluir a rotação, porque a ponta sempre estará na direção da "velocidade".

  3. Combine vários comandos em uma mensagem e nunca envie mensagens menores que 16 a 20 bytes, porque o cabeçalho do udp será maior que a própria mensagem. Também não envie pacotes maiores que o MTU do seu protocolo, pois a fragmentação diminuirá a velocidade da transmissão.

Riki
fonte
Ah, é uma boa idéia atualizar alguns objetos com mais frequência do que outros, usar P2P, degradar a precisão do ponto flutuante, enviar apenas alterações (o que não é trivial para mim porque eu pretendia sincronizar objetos periodicamente, mas "o objeto não mudou" é a informação também). Com todas essas modificações, a imagem toda parece mais realista!
Slav
1
O envio de avisos do tipo "objeto não foi alterado" pode ser uma técnica útil para fins de teste, onde você deseja ver o desempenho do jogo quando os jogadores estão em horário de pico, pois tem o potencial de exigir demandas de processamento e da rede, mas ainda existem soluções melhores do que isso (como criar um daemon independente que controla um personagem real no jogo e depois executar o daemon várias vezes em máquinas diferentes).
Randolf Richardson
5

Aqui estão duas abordagens:

Primeiro:
mude para a física determinística, envie comandos ao jogador, ações ai, objetos que aparecem e tudo o que não pode ser determinado do lado do cliente. Isso deve incluir não comandos, uma confirmação de que até um determinado momento, apenas os comandos enviados e recebidos se aplicam.

O cliente deve executar duas ou três simulações simultâneas.
1: Pára sempre que faltam dados para a próxima etapa.
2: Continue usando dados de adivinhação e forneça o estado usado para renderização. 3: Sempre que o 1 parar, essa simulação copia o estado do no 1, atualiza o horário atual e assume o no 2, que é descartado.

Se a recuperação for rápida o suficiente, você poderá deixar de diferir entre 2 e 3 e simplesmente soltar os dados antigos imediatamente.

Segundo:
não use física determinística, faça o mesmo que acima, mas envie "quadros completos" uma vez a cada poucos segundos. Você pode facilmente deixar de lado a transferência de coisas temporárias, como balas.

Nos dois casos, você pode ter cuidado com o fato de o cliente prever que alguém está morrendo, é meio bobo ver um oponente sem explodir.

E +1 por fazer as contas, muitas pessoas não conseguem fazer estimativas simples de uso de recursos.

aaaaaaaaaaaa
fonte
2
"Física determinística" significa que não posso usar valores de ponto flutuante ou diferentes etapas de simulação? Eu estou imaginando que a dessincronização crítica pode acontecer se, por exemplo, o foguete passar por alguma torre inimiga no cliente, mas atingir o servidor (devido a alguma imprecisão do ponto flutuante), o que fará com que o jogador continue lutando com essa torre até o próximo pacote de sincronização do servidor de entrada. (alguns segundos).
Slav
3
Significa números inteiros e tempo fixo. Teoricamente, você pode zombar de pontos flutuantes para se comportar, mas usar números inteiros é muito mais simples. Você tem razão com o exemplo de míssil que falta, se você usar a física não determinística, é provavelmente melhor deixar o servidor lidar completamente com a morte e transmitir casos de morte / destruição rapidamente.
Aaaaaaaaaaaa
5

Algumas perguntas primeiro.

Os 'foguetes ou algo mais' são inteligentes ou burros? Se eles são burros, tudo o que você precisa é o registro de data e hora do fogo, a origem e o vetor para simular seu caminho. Se são inteligentes, quão inteligentes são? Você pode calcular no momento do incêndio que eles vão acertar ou errar? Nesse caso, você pode simular todo o caminho no cliente. ("No T13, o míssil atingirá o navio porque a jogada perdeu o teste de esquiva / o atirador marcou um golpe crítico.")

Em geral, embora não haja praticamente nenhuma razão para: A) ter uma taxa de clock de 50Hz, (a maioria dos atiradores se diverte com 15-20 e MMOs a menos que isso.) B) envia estado completo a cada quadro. (A rotação de um míssil no espaço é importante? Ou você pode simplesmente supor que sua 'frente' está orientada ao longo do vetor que está viajando?)

Gaste tempo com previsão e interpolação e você verá sua largura de banda despencar. Um projeto em que trabalhei tinha uma taxa de atualização de 10Hz e uma representação de estado de objeto de 14 bytes. (Comprima tudo o que puder! Acredito que usamos 6 bits para definir a rotação em torno do plano xe outros 6 bits para uma inclinação acima / abaixo desse plano, parecia indistinguível o envio de uma matriz / quaternion rotacional real.)

Outra coisa que você pode fazer é priorizar objetos. Show, talvez haja 100 objetos no conjunto relevante, mas você conhece o ponto de vista dele no servidor? Se algo não está na visão dele, você pode reduzir a frequência de atualização por uma ordem de magnitude?

A idéia geral não é fazer uma simulação perfeita no cliente, isso é impossível, a idéia é criar um jogo divertido, onde os jogadores não notem que não é uma simulação perfeita.

Doug-W
fonte