Help requested - Learn how the networking in C++ works

Hi guys,

I have to code the multiplayer part of our project, for that I have read several posts and documentations, and now I would like to test what I have read.

First step : Replication of a simple variable. I would like to sync a simple int32 from the server to the client.

I have created a new test project based on the third person template. I have tested the generated code and it’s works fine. I see both players, sync on the server and on the client.

To test variable replication I have added this in my TestNetworkCharacter.h


UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category = Testing)
	int32 Test;

And this to my TestNetworkCharacter.cpp


void ATestNetworkCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(ATestNetworkCharacter, Test);
}

I have added #include “Net/UnrealNetwork.h” in the TestNetwork.h

To test if the client receive the new value when the server change it, I have added this 2 actions in the .cpp.

This to change the value


void ATestNetworkCharacter::Action2()
{
	if (Role == ROLE_Authority)
		Test = 2;
}

And this to log the current value


void ATestNetworkCharacter::Action1()
{
	FString MyString = FString::FromInt(Test);

	if (Role == ROLE_Authority)
	{
		UE_LOG(LogSomething, Log, TEXT("Server %s"), *MyString);
	}
	else
	{
		UE_LOG(LogSomething, Log, TEXT("Client %s"), *MyString);
	}
}

Test procedure :

  1. I launch the server and next the client.
  2. I hit the key to execute Action1() on server and next on the client, this is to log the current value of my variable.
  3. On the server I hit the key to execute Action2(), this normaly change the value to 2, and replicated it to the client.
  4. I repeat the step 2 to log the new value.

But when I read the server’s log I see


[2014.04.21-14.47.18:015][315]LogSomething: Serveur 1
[2014.04.21-14.47.24:864][579]LogSomething: Serveur 2

And I see this in the client’s log


[2014.04.21-14.47.08:804][233]LogSomething: Client 1
[2014.04.21-14.47.28:621][981]LogSomething: Client 1

The value is still on 1 on the client. Replication dont have to change the value of my variable to 2 on the client ?
Where is the error ?

Thanks you for your reply.

I think you missed a Server Function.

you ever call a function that is reliable/unreliable, server, withvalidation?

Defined in header like this:



UFUNCTION(reliable, server, WithValidation) void ServerAction();


think this is what you miss.

Actually what you want to do is the same as in the tutorial here.

Cheers,
Jack

Are you telling me that I have to add a PlayerController class to my TestNetwork project and add in the .h this


bool bSomeBool;
void SetSomeBool(bool bNewSomeBool);

UFUNCTION(reliable, server, WithValidation)
	void ServerSetSomeBool(bool bNewSomeBool);

and in the .cpp this


void ATestNetworkPlayerController::SetSomeBool(bool bNewSomeBool)
{
	bSomeBool = bNewSomeBool;

	if (Role < ROLE_Authority)
	{
		ServerSetSomeBool(bNewSomeBool);
	}
}

bool ATestNetworkPlayerController::ServerSetSomeBool_Validate(bool bNewSomeBool)
{
	return true;
}

void ATestNetworkPlayerController::ServerSetSomeBool_Implementation(bool bNewSomeBool)
{
	SetSomeBool(bNewSomeBool);
}

And finally call the SetSomeBool function when I change the value of my variable inside de Action2 function ?

You dont need a playercontroller for this.
Your Characterclass is also able to do it.

You only have to add the “server version” of your actions. Call them in the “normal version”. Check if you are the server. If not, call the server function.
Like the sample you just posted.

He recalls the function as server, if you are a client. So the Server will run it and spread to everyone the change of the bSomeBool.

Hope I could help :smiley:

It does not work.

I have added this code in TestNetworkCharacter.h


void ChangeTest(int32 Value);

UFUNCTION(reliable, server, WithValidation)
      void ServerChangeTest(int32 Value);

And this code to TestNetworkCharacter.cpp


void ATestNetworkCharacter::ChangeTest(int32 Value)
{
	UE_LOG(LogSomething, Log, TEXT("Entering function"));
	Test = Value;

	if (Role < ROLE_Authority)
	{
		UE_LOG(LogSomething, Log, TEXT("Call the server"));
		ServerChangeTest(Value);
	}
}

bool ATestNetworkCharacter::ServerChangeTest_Validate(int32 Value)
{
	UE_LOG(LogSomething, Log, TEXT("Validate"));
	return true;
}

void ATestNetworkCharacter::ServerChangeTest_Implementation(int32 Value)
{
	UE_LOG(LogSomething, Log, TEXT("Implementation"));
	ChangeTest(Value);
}

I have changed the code of the function Action2() by this one


void ATestNetworkCharacter::Action2()
{
	ChangeTest(2);		
}

For me, the function ChangeTest have to be call from the client, to go inside the if. But it’s look not good, because important change must be run from the server, not from the client. The server must stay the king.

By the way, here come my test procedure :

  1. Launch the server and next the client
  2. Hit the key to log value on server and after on client
  3. Hit the key to call Action2() from the client
  4. Repeat the step 2

Inside server logs I have


[2014.04.21-23.23.31:304][391]LogSomething: Server 1
[2014.04.21-23.23.35:068][533]LogSomething: Validate
[2014.04.21-23.23.35:069][533]LogSomething: Implementation
[2014.04.21-23.23.35:069][533]LogSomething: Entering function
[2014.04.21-23.23.39:180][688]LogSomething: Server 1

And inside de client log I have


[2014.04.21-23.23.33:178][768]LogSomething: Client 1
[2014.04.21-23.23.35:056][839]LogSomething: Entering function
[2014.04.21-23.23.35:056][839]LogSomething: Call the server side
[2014.04.21-23.23.37:266][923]LogSomething: Client 2

Then, the client change the value, call the server-side function, the server seems to execute the same function but finaly does not change the value because le final log show Server 1 in place of server 2.

Where is my mistake ?

Hello! Hopefully I can help you out. Going back to your first post, what are you trying to accomplish? Do you want the variable to be something that only the server ever asks to change, or is it something that the client can also ask the server to change as well?

If you only want a replicated value the server is the only one to modify (like health), then you seemed on the right track in your initial post, but you might have been getting mixed up with multiple actors, if I’m reading it right. If you have 2 characters running around, let’s call the one controlled by the server “A” and the one controlled by the client “B.” This is what probably happened, if I’m understanding correctly:

  1. You pressed the key to call Action1 on the server. It called the function on A and logged 1.
  2. You pressed the key to call Action1 on the client machine. It called the function locally on B and logged 1.
  3. You pressed the key to call Action2 on the server. This set the value of Test to 2 on A, which will also be replicated to the client (for A!).
  4. You pressed the key to call Action1 on the server. It called the function on A and logged 2.
  5. You pressed the key to call Action1 on the client machine. It called the function locally on B, which has never had its value changed (you changed A), and logged 1.

It’s likely your replication worked, but you might be logging out values from the wrong machine on the client.

A neat trick you can use to help check some of these values while the game is running w/o having to use log statements is the “displayall” console command. If you open the console with the tilde (`) key, you can type “displayall <NameOfClass> <NameOfUPropertyOnClass>” and see the property value displayed in the upper left corner. I like to use that for debugging things like this or verifying that they change. In your case, you’d want to type “displayall TestNetworkCharacter test” without the quotes. On the client, you should see multiple entries, one for the client’s character and one for the server’s character. When you press Action2 on the server, you should see the value update on the client, for the server’s character.

Yes, for this first try, my Test variable is like the health, I want only the server can change the value and when the server changes the value all clients got the updated value.

If I understand right, you mean that the server has in memory “A” and “B”. The client has in memory only its own “B”. When I modify a replicated variable on the server, the “A” is normaly updated, and with the replication, the “B”, hosted on the server is also updated. But the “B” hosted on the client machine is out of range of the replication.

Is that correct ? I finaly understand how replication works ?

You say that :

You mean : On the server, you should see multiple entries, one for … ?

But then, how I update the “B” hosted on the client ? How to acces to the “B” hosted by the server to send the value to the “B” hosted on client machine ? Have you some examples of that kind of function ?

I will test your “displayall” in few minutes. This way to check variables will be very helpful for me ! Thanks.

Okay some news … and new questions.

The DisplayAll give me some new informations … to confuse me more.

  1. I launch the server and type the DisplayAll command. –> I can read on server window “…Character_C_0.Test = 1” (Got the server character hosted on the server)
  2. I launch the client –> A new line appears on the server window “…Character_C_1.Test = 1” (Here is our client character hosted on the server)
  3. I type the DisplayAll command on client –> I can read on client window “…Character_C_1.Test = 1” (probably the client character hosted on the client machine) AND “…Character_C_2.Test = 1” (Character_C_2 ? Intruder ?)
  4. On the server I hit the key to change the value of Test to 2 –> I can read on server window “…Character_C_0.Test = 2” (Perfect) AND on client window “…Character_C_2.Test = 2” (The Chracter_C_2 is the server character hosted on the client, and its Test variable is replicated !!!)
  5. On the client I hit the key to change the value of Test to 3 –> I can read on client window “…Character_C_1.Test = 3” (Normal). On server window no change “…Character_C_1.Test = 1”, the change from the client is not replicated to the server (Good, the server stay the king).

Now I have to find, on the server, a way to modify the Test variable of the client character, and normaly it will be replicated to the client machine … I hope.

Any suggestion ?

If your game has two players (one controlled by the server and one controlled by the client), then both the server and the client should have versions of both characters (they both see each other). So there is a server version of A (controlled by server) and B (controlled by client), as well as a client version of each. In your example, you were probably modifying the test variable on the server for character A, which then replicated to the client’s version of A. When you were logging out the information on the client though, you were probably logging character B.

Based on your number 4, that does sound like your replication is working correctly. In the general case, you want to avoid writing code that allows the client machine to modify a replicated variable locally or you can get the server and client out of sync, like you can see in your number 5. As for modifying the variable of the client’s characters, there are a few different things you could do. One is to find the character as a result of some gameplay operation…so as an example with health, a weapon might do a line trace and hit a particular character and then you could modify variables on the hit character. Alternatively, you could iterate over all of the characters on the server and modify each one at once if you just want to test things. Take a look at uses of UWorld::GetPawnIterator(). You could try to cast each pawn to your test character class, then set the variable on the server there. The third option would be if you wanted the client to initiate the change on its own character, then you could write one of those server functions that in its implementation changes the value and then have the client call that server function, which will request that the server execute the function, which will change the value.

Yes, it works ! I used the last option to test quickly and it works, the client variable hosted on the server is modified and replicated on the client machine !

A bit more complex than expected but it works. Thanks to you two Billy and Jack !

Last question, do you have any informations about setup a dedicated server ? Because finaly, our project will be a dedicated server with several client connected on it.

For now I have 2 shortcuts on desktop, one for the listening server (basic it’s : “…\UE4Editor.exe” “…\Project.uproject” Map_Name?listen -game) and the other for the client (“…\UE4Editor.exe” “…\Project.uproject” 127.0.0.1 -game).
For dedicated server I have tried “…\UE4Editor.exe” “…\Project.uproject” Map_Name -server -log. But it seems to be wrong, I have saw “Game class is ‘GameMode’” in place of ‘TestNetworkGameMode’ and when I try to connect on it with the unchanged client, it does not works, the client switch itself to server.

Any suggestion to setup dedicated server, with log and even better with console access (DisplayAll is really useful !!) ?

Thanks you again.

There are a couple ways to handle dedicated server. You can test dedicated server using a command-line very similar to what you proposed, in addition to eventually also just building a server-specific executable that requires cooked content.

You can explicitly say which game mode to use in your shortcut if you want. In your server shortcut where you have Map_Name -server, you could do Map_Name?game=PathToGameHere -server -log. As an example, to specify to use the “MyGame” blueprint located in my game’s blueprints folders, I might put ?game=/Game/Blueprints/MyGame.MyGame_C. The _C is for a generated class from the blueprint and isn’t necessary for native game mode classes. Additionally, you can set up a simple alias for the mode inside an INI if you prefer. For this example with the blueprint mode, you’d set the alias up in the DefaultGame.ini in your project’s config folder and add something like this:


[/Script/Engine.GameMode]
+GameModeClassAliases=(ShortName="TG",GameClassName="/Game/Blueprints/MyGame.MyGame_C")

You can make the short name whatever you want. I made it “TG” for “TestGame.” This allows you to do ?game=TG in the shortcut instead of needing the whole path. I think ShooterGame uses these aliases to a native class in its INI file.


[/Script/Engine.GameMode]
+GameModeClassAliases=(ShortName="FFA",GameClassName="ShooterGame.ShooterGame_FreeForAll")
+GameModeClassAliases=(ShortName="TDM",GameClassName="ShooterGame.ShooterGame_TeamDeathMatch")

I’ll see if I can poke one of our other engineers who deals with the dedicated servers more often than I do to also pop into this thread and offer any advice/correct any mistakes.

I have made several test to find which file I have to point to.
I have created a BP based on my TestNetworkGameMode.cpp and using the full path “” <– No success.
I have tested to point to the souce folder <– Not better.
Etc …

Finally, juste before post the message here, I have made a last test, I have just copy/paste your path, even if I have not found my “Game/Blueprints” folder. And it works ! Now, I have a dedicated server and my client can join the game as a real client.

Can you tell me, where on my HDD, is located this “Game/Blueprints/” folder ? Not in Unreal projects folder, not even in Unreal Engine folder.

Thanks you again.

Ah woops, I realize that could be confusing. The “Game” folder is basically just your root content directory for your project. So in my example, I had just made a blueprints folder in there, just like you did.

If you look at the content browser, you’ll see it shows “Game” as the root: