UE4 | Inventory for multiplayer # 5 | Information transfer between Server and Client

  • Tutorial

In this article we will look at the data transfer between the Server and Client in the Unreal Engine 4 implemented in C ++ . At the very beginning, for a person who does not have a profile education, this seems to be something incomprehensibly difficult. Despite the large number of examples and analyzes, I personally found it very difficult to put together a complete picture of this process. But, when the critical amount of information obtained from reading and tests made was reached, it came to an understanding of how this all works.




First of all, you need to clearly understand that all objects in the game (with rare exceptions) may have several copies. The original object is located on the server. The original is always there. Copies can exist on Clients, but not necessarily, i.e. they may not be. All significant events occur on the Server, and it is he who decides who needs to know about it, and who does not.

Everything that happens on the Client is not known to anyone except the Client.

Example

Запускаем игру с двумя Клиентами. На сцене кубик.Оригинал этого кубика расположен на Сервере. Он самый важный. Первая копия будет находится на первом Клиенте, а вторая — на втором Клиенте. Если мы сделаем что-то с копией объекта на любом их клиентов, то оригинал не пострадает. Изменение будет сугубо локальным, только для этого Клиента. Если же изменить оригинал, то возможны 2 основных сценария:


  1. Копии клиентов останутся без изменений.
  2. Копии клиентов будут синхронизированы с оригиналом.



Now that the basic rules of the game are clear, we can consider which options for transferring information between the Server and the Client are available to us. I know 3 ways, but we will consider only the first two, because the third allows you to transfer anything at all and anywhere, and applies only if you have rested against the limitations of the first two.


  1. Replication ( Replication ).
  2. RPC ( Remote Procedure Calls ).
  3. TCP.


    Replication


The first thing is to unconditionally accept:

Replication is a one-way road, and works only from Server to Client.
The second thing you need to know:
Only objects or class variables can be replicated.
And third, an important condition:
Replication occurs only when a change has occurred on the server.

If in Blueprint we just put checkmarks in the right places, then C ++ is not much more complicated. The main thing is not to forget to connect #include "UnrealNetwork.h" .

First consider the replication of objects.
In the constructor we write:


bReplicates = true;

If you want to replicate the movement:


bReplicateMovement = true;

If you need to replicate the connected component:


Component->SetReplicates(true);

A full description can be found here .


With variable replication, things are a little more interesting.
Let's start with the header file .h .
You can simply replicate the variable:


UPROPERTY(Replicated)
bool bMyReplicatedVariable;

And you can run any function on the side of the Client, if the variable has been replicated. It doesn't matter what kind of value the variable took. What is important is the fact of its change.


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

The launch of the function is nothing more than one of the ways to send commands from the Server. It is very important to know that the replication of arrays does not occur entirely, but only the changed part. Thus, using just one variable, you can transfer multiple commands from the server, parsing the array into elements, and elements into bits.


Now let's move on to .cpp. We
register replication conditions:


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

The first bMyReplicatedVariable variable was replicated without any condition to all Clients at once, whereas the second, MyReplicatedArray , was updated only for the Client of the owner of the AMySuperActor object , if, of course, it was announced.


A full list of possible conditions can be found here .




RPC ( Remote Procedure Calls )


This method of data transfer, in contrast to replication, works both ways, but is more expensive. To use it, you just need to include #include "UnrealNetwork.h" .

An important feature is that the RPC method can transfer variables.
First you need to say that RPCs come with confirmation of receipt of the package Reliable and without such Unreliable . If in the first case the Sender does not calm down until he is convinced that the package has been delivered (and will send it again and again, if there is no response news), then in the second, the Sender doesn’t care if someone received his package or not. Sent and forgot.
Where it is possible - we use Unreliable . Usually this method is suitable for not very important information, or for frequently updated data. Where it is impossible, in the case of sending from the Server to the Client, we try to do replication with the function call, as shown above.

So, to send our parcel from the Client to the Server, you need to register three functions:


UFUNCTION(Reliable, Server, WithValidation)
voidServerTestFunction(float MyVariable);
voidServerTestFunction_Implementation(float MyVariable);
boolServerTestFunction_Validate(float MyVariable);

Reliable - parcel with confirmation of receipt.
Server - sending from Client to Server.
WithValidation - the parcel is opened by the recipient only under the conditions described in the bool ServerTestFunction_Validate (float MyVariable) function . That is, if the function returns true . This parameter is required only for functions that run on the Server.
ServerTestFunction (float MyVariable) - this function is called by the client, if it wants, to send something to the server. In general, it is not even required to describe it in .cpp .
ServerTestFunction_Implementation (float MyVariable) - this function will be called directly on the Server only if ...
ServerTestFunction_Validate (float MyVariable) - this function is executed on the Server, and if true is returned, ServerTestFunction_Implementation (float MyVariable) will be called.


To send a parcel from the Server to the Client, if we are absolutely not satisfied with the use of replication, in fact only the Server changes to the Client :


UFUNCTION(Reliable, Client, WithValidation)

Names of functions, in principle, can be any, but usually they should reflect exactly where the package is going. For our convenience.


UFUNCTION(Reliable, Client, WithValidation)
voidClientTestFunction(float MyVariable);
voidClientTestFunction_Implementation(float MyVariable);
boolClientTestFunction_Validate(float MyVariable);

Otherwise, it works in the same way as in the previous example, taking into account the fact that this time the package goes from the Server to the Client.


There is another option to send from the Server, when the package goes to all clients at once.


UFUNCTION(Reliable, NetMulticast, WithValidation)
voidNetMulticastTestFunction();
voidNetMulticastTestFunction_Implementation();
boolNetMulticastTestFunction_Validate();

Do not abuse this option. Think, perhaps you will manage replication.

For Client and NetMulticast, validation is optional



An example implementation of a request to the server
/* Эта функция может выполняться как на Клиенте, так и на Сервере */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. Тогда функция на сервере не запустится */returntrue;
}

And finally, a link to the manual, which must be read.
'Unreal Engine 4' Network Compendium


That's all you need to know about communication between the Server and the Client in order to proceed to the next section, where we will register the replication of the inventory and consider how to make changes to it correctly.


PS If you notice any inaccuracies or errors, please write in the comments.


Also popular now: