Eu tenho uma pergunta sobre o UDP. Por contexto, estou trabalhando em um jogo de ação em tempo real.
Eu li bastante sobre as diferenças entre UDP e TCP e sinto que as entendo muito bem, mas há uma que nunca pareceu correta, e que é a confiabilidade e os reconhecimentos específicos . Entendo que o UDP não oferece confiabilidade por padrão (ou seja, os pacotes podem ser descartados ou chegar fora de ordem). Quando alguma confiabilidade é necessária, a solução que eu vi (que faz sentido conceitualmente) é usar confirmações (ou seja, o servidor envia um pacote ao cliente e, quando o cliente recebe essa mensagem, envia de volta uma confirmação ao servidor) .
O que acontece quando o reconhecimento é descartado?
No exemplo acima (um servidor enviando um pacote para um cliente), o servidor lida com a perda potencial de pacotes reenviando pacotes a cada quadro até que sejam recebidos reconhecimentos por esses pacotes. Você ainda pode encontrar problemas de largura de banda ou mensagens fora de ordem, mas, do ponto de vista da perda de pacotes, o servidor é coberto.
No entanto, se o cliente enviar uma confirmação que nunca chega, o servidor não terá outra opção a não ser parar de enviar a mensagem, o que poderia interromper o jogo se as informações contidas nesse pacote fossem necessárias. Você pode adotar uma abordagem semelhante ao servidor (ou seja, continuar enviando agradecimentos até receber uma confirmação pela confirmação?), Mas essa abordagem o faria alternar para sempre (já que você precisaria de uma confirmação para a confirmação para a confirmação) e assim por diante).
Sinto que minha lógica básica está correta aqui, o que me deixa com duas opções.
- Envie um único pacote de reconhecimento e espere o melhor.
- Envie um punhado de pacotes de reconhecimento (talvez 3-4) e espere o melhor, assumindo que nem todos serão descartados.
Existe uma resposta para esse problema? Estou fundamentalmente interpretando algo errado? Existe alguma garantia de usar UDP que eu não conheço? Sinto-me hesitante em seguir em frente com muito código de rede até me sentir confortável ao saber que minha lógica está correta.
fonte
Respostas:
Essa é uma forma do problema dos dois generais e você está certo - não há número de tentativas suficientes para garantir perfeitamente o recebimento.
Na prática nos jogos, geralmente há um horizonte de tempo além do qual as informações realmente não importam, mesmo que elas cheguem tecnicamente de maneira confiável. Como descobrir que você tinha um tiro na cabeça perfeito alinhado 2 segundos atrás - é tarde demais para o jogador usar essa informação agora.
Se sua perda de pacotes é tão alta que você não consegue obter as informações necessárias rotineiramente dentro de uma janela de reação apertada, então para um jogo em tempo real, é melhor chutar o jogador e tentar encontrar uma melhor correspondência para ele em outro lugar, em vez de continue tentando enviar o pacote para emular uma conexão confiável.
Por esse motivo, alguns sistemas de replicação de jogos ignoram completamente o reconhecimento e as novas tentativas e optam por enviar apenas spam a atualização mais recente sempre que possível. Se alguém cair ou chegar atrasado, muito ruim, pule, pegue o próximo e continue, contando com os sistemas de previsão e interpolação para suavizar a lacuna e minimizar os soluços visíveis para o jogador.
De repente, quero começar a chamar isso de "Replicação Simba" por desconsiderar problemas do passado e tentar viver no momento presente. ;)
Uma solução híbrida é seguir em frente enviando a nova atualização AND (já que as atualizações do estado do jogo geralmente podem ser bem pequenas / compactáveis ) também incluem a última atualização e talvez a anterior antes ... Portanto, caso o cliente as perca , você não precisa esperar um tempo completo de ida e volta para descobrir e consertá-lo. Na maioria das vezes o cliente já viu isso, então há dados redundantes dessa maneira, mas a latência para corrigir uma mensagem perdida é menor. As atualizações do cliente podem incluir o número de índice da atualização consecutiva mais recente que eles viram, para que você possa ser minimamente conservador com quantas atualizações antigas você incluirá no próximo pacote de atualização.
Você também pode implementar um sistema de duas camadas como outro tipo de híbrido, em que o estado de curta duração é replicado de maneira não confiável e o estado de longo prazo é sincronizado de maneira confiável, usando TCP ou sua própria implementação de confiabilidade com uma nova tentativa contagem. Isso fica mais complexo de gerenciar, porque você tem dois sistemas de mensagens para manter, e os dois instantâneos podem estar fora de sincronia, adicionando uma nova classe de casos extremos.
fonte
A abordagem utilizada pelo TCP é que o remetente continuará reenviando o pacote até receber uma confirmação. O destinatário ignorará pacotes duplicados, mas ainda enviará agradecimentos por eles. O remetente ignorará as confirmações duplicadas.
Se um pacote for perdido, o remetente o reenvia, como você já sabe.
Se uma confirmação for perdida, o remetente reenvia o pacote original, o que faz com que o destinatário reenvie a confirmação.
Se um reconhecimento não for recebido dentro de um determinado período (talvez 60 segundos ou 20 tentativas), o jogador será considerado desconectado do jogo. Você deve implementar algum tipo de regra de tempo limite, caso contrário, um jogador que desconecte o cabo de rede amarrará os recursos no servidor para sempre.
fonte
Se você deseja reinventar o TCP, faz sentido olhar primeiro para o TCP , que lida com o problema exato que você descreve (parte da solução é usar valores definidos pelo usuário para novas tentativas e tempos limite).
Soluções que usam 2 canais, um canal TCP (para comunicação confiável) e um canal UDP (para comunicação de baixa latência) não são incomuns.
Algumas soluções detectam quando um cliente está perdendo algumas informações por muito tempo e iniciam uma ressincronização, que pode usar UDP ou TCP.
Outra abordagem comum é projetar a comunicação de forma que ela não dependa de reconhecimentos, mas isso está fora do escopo da questão.
fonte
Em um RTS, você realmente não pode usar um protocolo como o TCP e também não pode tornar o UDP confiável. Se você tentar, o jogo irá congelar sempre que houver um problema na rede.
Em vez disso, você cria o protocolo para que os pacotes perdidos não importem muito.
A versão curta é que você não se importa onde os outros jogadores estiveram no último quadro, desde que saiba onde eles estão agora . A versão longa é mais complicada.
A pergunta então é: o que você faz quando um pacote desaparece? E a resposta é ... você adivinha. O jogador provavelmente está se movendo em linha reta, certo? Basta movê-los um passo adiante nessa linha. ... Exceto que nenhum jogador RTS se mova em linha reta. E então há detecção de colisão.
Isto é difícil. Muitos jogos entendem errado. Pode-se argumentar que não há resposta certa para isso, apenas vários erros que podem ser trocados.
A razão pela qual esses jogos funcionam muito bem não é apenas porque eles pensaram muito sobre esses problemas, mas também que a Internet se tornou bastante confiável. Quase todos os pacotes UDP chegam ao seu destino em tempo hábil. (A menos que haja um problema permanente como um firewall)
fonte