Network Calling Function from Client on Server Problem

Hey all,

My setup is a boardgame designed for 4 Players, most functionality already exists in single player and now I am trying to add the multiplayer funcionality ( which I admit was stupid to wait until now ). Generally, my first idea is to have the server set up mostly everything, like generating card actors, etc. which will then be simply replicated on the client. This part works almost on itself as far as I can tell through the bReplicates boolean.

The second idea would be, that any action performed on a client should simply be instead called on the server through a simple RPC, and there it’s result will be broadcasted back automatically, through the replication process mentioned in idea #1. Luckily, there are many tutorials on using RPCs tagged as “server” which should mean they are called from a client but executed only on the server ( which would be perfect for my case ). Although I got this compiled and running, the RPC doesn’t call my server function.

In my understanding, calling this RPC should always call the function on the server:



// APuzzleBlankBlock.h

// int to test networking
UPROPERTY(replicated)
		int32 networkId;

UFUNCTION(Server, Reliable, WithValidation)
		void Server_BlockClickedBroadcast();
		void Server_BlockClickedBroadcast_Implementation();
		bool Server_BlockClickedBroadcast_Validate();
...
// APuzzleBlankBlock.cpp

// called when block is clicked, server just increments the replicated variable, client calls the server RPC to do the same on the server
void APuzzleBlankBlock::BlockClicked(UPrimitiveComponent* ClickedComp)
{
	if (Role == ROLE_Authority)
	{ 
		IncreaseNetworkId(); 
	}
	else 
	{
// this call simply doesn't work, the function isn't being called.
		Server_BlockClickedBroadcast();
// however Server_BlockClickedBroadcast_Implementation() only increments on the local client, not on the server
	}
}
...

void APuzzleBlankBlock::IncreaseNetworkId(){
	networkId++;
}
...

//networkId is replicated on clients
void APuzzleBlankBlock::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
	DOREPLIFETIME(APuzzleBlankBlock, networkId);
}

// this calls the function to increment networkId, which in turn should be replicated to all clients.
void APuzzleBlankBlock::Server_BlockClickedBroadcast_Implementation()
{
	IncreaseNetworkId();
}


So with this code, clicking on the server perfectly replicates the value of networkId to all clients, however, I wouldn’t need an RPC for this. The same effect
works if the server simply increments the variable, since it is replicated through its UPROPERTY(). But I should be able to call the Server_BlockClickedBroadcast() function
from my clients and it should then do same. Any help or insight on this would be greatly appreciated!

To simplifly: I just want a way for my client to tell the server that it was clicked, and as a consequence have the server broadcast this information to the other two uninvolved clients.

################ EDIT:
So appearently server RPCs can only be called from Pawns that are owned by a local client PlayerController. So I simply change my PuzzleBlocks to be Pawns instead of Actors, but how can I assign them a playercontroller? SpawnDefaultController() causes a crash, and simply using GetWorld()->SpawnActor and then possess doesn’t work.

############# SOLUTION:
My solution now was to give my pawns a reference to the local Player controller like this:



// get local player controller
	for (TObjectIterator<APuzzleBlankPlayerController> Itr; Itr; ++Itr)
	{
		APuzzleBlankPlayerController* pc = Cast<APuzzleBlankPlayerController>(*Itr);
		if (pc && pc->IsLocalPlayerController()){
			//UE_LOG(LogTemp, Warning, TEXT("Local Controller found!"));
			PlayerController = pc;
		}
	}


and have my block on Click call a function in that PlayerController. That function is a RPC which then works exactly as I tried before, but now it is in the PlayerController. That function then passes the cards pointer on to my original Function on the server and almost everything works the same. All my Actor’s don’t have any replicated fields yet, so most functionality doesn’t work exactly, but for some reason with Client 2 (of 3) the pointer works as it does on the server, but not on the other two clients… The object’s field still aren’t replicated but the Server uses his own object when he is passed the pointer to replicated one ( like I said, only on Client 2, never on #1 or #3)… Anyhow, the RPC problem is solved then. Thanks again to all!

Is your PuzzleBlankBlok owned by the Player?
Do note that Server RPC can only be called by a Client-possessed Pawn.

For what it’s worth, it looks like you’ve set everything up correctly. A couple of additional things to verify:

  • You’ve implemented Server_BlockClickedBroadcast_Validate() and it is returning true.
  • The APuzzleBlankBlock actors are being spawned on the server and then replicated to the client - never spawned directly on the client.

Oh, good catch. I bet this is the problem. You’ll probably want to route the call through the client player’s controller or pawn. Here’s some relevant discussion @ ActionHub: [4.6.1] How to enable any Actor class to send RPCs like Player Controller Does? GameState for example - Programming & Scripting - Epic Developer Community Forums

Ah wow this part seems to be unmentioned in a lot of tutorials! So far my Blocks are just simple Actors. I will change them to Pawns…

#######EDIT:
updated Original Post accordingly

What’s the crash?
You should be able to possess a pawn by calling PlayerController->Possess(AActor*) Function

Although it’s not very noticeable it’s described in the the documentation in the “requirements” section Here

well, the crash doesn’t have any information. It occurs when I try to call “SpawnDefaultController()” in my Pawn’s Constructor. Does this happen because “DefaultPawnClass== NULL” in my gamemode? I want to call PlayerController->Possess() but to do this from inside my pawn I need a reference to it, which I don’t have. Do I call the Posses from outside my Pawn, in the class where I spawn it?

Well if you don’t want to deal with the craziness that his ownership you could just call the function from your PlayerController with a Replicated to server call.

But the whole Reason why I want a PlayerController on my Pawn is because otherwise I cannot call the server RPCs. I basically want my setup to be 1 server and 3 clients where the game runs on the server and all actions from the clients are simply forwarded through RPCs to the Server.

Well, as all input function are normally called from a PlayerController, you can just forward from there.
EG.
PC->DoSomething(Block) [Server]
Inside do Something -> Call Block->DoSomething() Server Side
And then Multicast the actions or use On_rep on variables

Ok thanks for your help so far! I think I’m getting close. I found many people have similar problems replicating movement and I also just found out that the DOREPLIFETIME Macro stops the bReplicatesMovement from having any effect. So, simply replicating variables works. Simply replicatingMovement works as well. Together the replicatesMovement stops working, and on top of all that I need the PlayerController to make all server RPCs as well as the entire game slowing down 50% whenever I run this with 3 clients + 1 server in PIE :D. I’ll continue having fun with this tomorrow. Thanks again :wink:

Glad to see it’s getting better!
Have fun! :slight_smile: