UE4 Equipamento para Multiplayer # 5 | Transferência de informações entre servidor e cliente


Neste artigo, consideraremos a transferência de dados entre o servidor e o cliente no Unreal Engine 4 implementada em C ++ . No começo, para uma pessoa que não tem educação especializada, isso parece ser algo incompreensivelmente complicado. Apesar do grande número de exemplos e análises, foi pessoalmente muito difícil para mim montar uma imagem completa desse processo. Mas, quando a quantidade crítica de informações obtidas através da leitura e dos testes realizados foi alcançada, chegou-se a um entendimento de como tudo isso funciona.




Primeiro de tudo, você precisa entender claramente que todos os objetos no jogo (com raras exceções) podem ter várias cópias. O objeto original está localizado no servidor. O original está sempre lá. Podem existir cópias nos Clientes, mas não existem, ou seja, eles podem não ser. Todos os eventos significativos ocorrem no servidor, e é ele quem decide quem precisa saber e quem não sabe.

Tudo o que acontece no cliente não é conhecido por ninguém, exceto o cliente.

Exemplo

Começamos o jogo com dois clientes. Há um cubo no palco.O original deste cubo está localizado no servidor. Ele é o mais importante. A primeira cópia estará localizada no primeiro cliente e a segunda - no segundo cliente. Se fizermos algo com uma cópia do objeto em qualquer um de seus clientes, o original não sofrerá . A mudança será puramente local, apenas para este cliente. Se você alterar o original, existem 2 cenários principais:


  1. As cópias dos clientes permanecerão inalteradas.
  2. Cópias dos clientes serão sincronizadas com o original.



Agora que as regras básicas do jogo são claras, podemos considerar quais opções de transmissão de informações entre o Servidor e o Cliente estão disponíveis para nós. Conheço três maneiras, mas consideraremos apenas as duas primeiras, porque o terceiro permite transferir qualquer coisa e em qualquer lugar, e só se aplica se você estiver limitado pelas limitações dos dois primeiros.


  1. Replicação
  2. RPC ( chamadas de procedimento remoto ).
  3. TCP


    Replicação


A primeira coisa a aceitar incondicionalmente:

A replicação é uma via de mão única e funciona apenas do servidor para o cliente.
A segunda coisa que você precisa saber:
Somente objetos ou variáveis ​​de classe podem ser replicados.
E terceiro, uma condição importante:
A replicação ocorre apenas quando uma alteração ocorre no servidor.

Se no Blueprint marcamos os lugares certos, o C ++ não é muito mais complicado. O principal é não esquecer de incluir #include "UnrealNetwork.h" .

Primeiro, considere a replicação do objeto.
No construtor, escrevemos:


bReplicates = true; 

Se queremos replicar o movimento:


 bReplicateMovement = true; 

Se você precisar replicar o componente conectado:


 Component->SetReplicates(true); 

Uma descrição completa pode ser encontrada aqui .


Com a replicação variável, as coisas são um pouco mais interessantes.
Vamos começar com o arquivo de cabeçalho .h .
Você pode simplesmente replicar a variável:


 UPROPERTY(Replicated) bool bMyReplicatedVariable; 

Ou você pode executar alguma função no lado do cliente se a variável tiver sido replicada. Não importa qual valor a variável assume. O fato de sua mudança é importante.


 UPROPERTY(ReplicatedUsing = OnRep_MySomeFunction) TArray<float> MyReplicatedArray; UFUNCTION() void OnRep_MySomeFunction(); 

A execução de uma função é apenas uma das maneiras de enviar comandos do servidor. É muito importante saber que a replicação de matrizes não ocorre totalmente, mas apenas a parte alterada. Assim, com apenas uma variável, você pode enviar muitos comandos do servidor, analisando a matriz em elementos e elementos em bits.


Agora vamos para .cpp
Escrevemos condições de replicação:


 void AMySuperActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(AMySuperActor, bMyReplicatedVariable); DOREPLIFETIME_CONDITION(AMySuperActor, MyReplicatedArray, COND_OwnerOnly); } 

A primeira variável bMyReplicatedVariable foi replicada incondicionalmente a todos os clientes de uma só vez, enquanto a segunda, MyReplicatedArray , foi atualizada apenas para o cliente que possui o objeto AMySuperActor , a menos que, é claro, tenha sido declarado.


Uma lista completa de possíveis condições pode ser encontrada aqui .




RPC ( chamadas de procedimento remoto )


Esse método de transferência de dados, diferentemente da replicação, funciona nos dois sentidos, mas é mais caro. Para usá-lo, você só precisa conectar #include "UnrealNetwork.h" .

Um recurso importante é que você pode transferir variáveis ​​usando o método RPC .
Primeiro, deve-se dizer que os RPCs vêm com o aviso de recebimento de Confiável e sem Confiável . Se, no primeiro caso, o Remetente não se acalmar até que esteja convencido de que a encomenda foi entregue (e a enviará repetidamente se não houver notícias recíprocas), então, no segundo, o Remetente não se importará se alguém recebeu a encomenda ou não. Enviado e esquecido.
Sempre que possível - use Não confiável . Normalmente, esse método é adequado para informações não muito importantes ou para dados atualizados com freqüência. Onde é impossível, no caso de enviar do servidor para o cliente, tentamos fazer a replicação com uma chamada de função, como mostrado acima.

Portanto, para enviar nosso pacote do cliente para o servidor, é necessário registrar três funções:


 UFUNCTION(Reliable, Server, WithValidation) void ServerTestFunction(float MyVariable); void ServerTestFunction_Implementation(float MyVariable); bool ServerTestFunction_Validate(float MyVariable); 

Confiável - um pacote com aviso de recebimento.
Servidor - enviando do cliente para o servidor.
WithValidation - o pacote é aberto pelo destinatário somente se as condições descritas na função booleana ServerTestFunction_Validate (float MyVariable) forem atendidas . Ou seja, se a função retornar verdadeira . Este parâmetro é necessário apenas para funções executadas no servidor.
ServerTestFunction (float MyVariable) - essa função é chamada pelo cliente se ele deseja enviar algo ao servidor. Em geral, nem é necessário descrevê-lo em .cpp .
ServerTestFunction_Implementation (float MyVariable) - esta função será chamada diretamente no servidor apenas se ...
ServerTestFunction_Validate (float MyVariable) - essa função é executada no servidor e, se true for retornado, ServerTestFunction_Implementation (float MyVariable) será chamado.


Para enviar um pacote do servidor para o cliente, se não estivermos categoricamente satisfeitos com o uso da replicação, em essência, apenas o servidor mudará para o cliente :


 UFUNCTION(Reliable, Client, WithValidation) 

Os nomes das funções, em princípio, podem ser arbitrários, mas geralmente devem refletir para onde o pacote está indo. Para nossa comodidade.


 UFUNCTION(Reliable, Client, WithValidation) void ClientTestFunction(float MyVariable); void ClientTestFunction_Implementation(float MyVariable); bool ClientTestFunction_Validate(float MyVariable); 

Caso contrário, ele funciona exatamente da mesma maneira que no exemplo anterior, levando em consideração o fato de que desta vez o pacote vai do servidor para o cliente.


Há outra opção para enviar do servidor, quando o pacote vai para todos os clientes ao mesmo tempo.


 UFUNCTION(Reliable, NetMulticast, WithValidation) void NetMulticastTestFunction(); void NetMulticastTestFunction_Implementation(); bool NetMulticastTestFunction_Validate(); 

Não abuse desta opção. Pense em como você pode gerenciar a replicação.

Para Client e NetMulticast, a validação é opcional



Exemplo de implementação de solicitação do servidor
 /*       ,     */ void ADreampaxActor::DoSomethingWithOtherActor(ADreampaxOtherActor * SomeOtherActor) { /*  ,      */ if (Role < ROLE_Authority) { /*    ,    ,      */ ServerDoSomethingWithOtherActor(SomeOtherActor); /*   ,        */ return; } /*         */ SomeOtherActor->Destroy(true); } /*      ,    ServerDoSomethingWithOtherActor(SomeOtherActor)     */ void ADreampaxCharacter::ServerDoSomethingWithOtherActor_Implementation(ADreampaxOtherActor * SomeOtherActor) { /*   ,       */ DoSomethingWithOtherActor(SomeOtherActor); } /*     ,    ServerDoSomethingWithOtherActor_Implementation(ADreampaxOtherActor * SomeOtherActor) */ bool ADreampaxCharacter::ServerDoSomethingWithOtherActor_Validate(ADreampaxOtherActor * SomeOtherActor) { /*      true,   ,   -    fasle.       */ return true; } 

E, finalmente, um link para o manual, que deve ser lido.
Compêndio de Rede 'Unreal Engine 4'


É tudo o que você precisa saber sobre a comunicação entre o Servidor e o Cliente para prosseguir para a próxima seção, onde escreveremos a replicação do inventário e consideraremos como fazer alterações corretamente.


PS Se você notar alguma imprecisão ou erro, escreva nos comentários.

Source: https://habr.com/ru/post/pt420233/


All Articles