Can you help me understanding the Client-Server model better? I have some strange errors..

Hey, everyone.

I am currently writing my first own game and still have some trouble understanding the client-server model, or basically the whole GameFlow and Replication / RPC stuff.

When I, the client, want to spawn an actor (let’s say a ‘city’-actor) out of my PlayerController-Class. Therefor I currently have a UFUNCTION(Server,Reliable,WithValidation) and call “this->getWorld()->SpawnActor()” in the _Validation function.
So, the city spawns on the server and since I say bReplicates = true in the constructor, the city gets replicated to all the players. Is this the way I should do it?

A fundamental question: Does the server run a synchronously instance of the Game or does je just sit there and wait for RPC Calls to handle? Because currently, I am calculating everything without any RPC Calls on the client and only send RPC calls, when something would really affect the gameplay / other players. As an example: The city class calculates it’s ressources (like wood, iron, stone, some basic gameplay data) by itself, locally. Nobody gets hurt so far. May he cheats, but so what? It is just locally, basically just for cosmetic reasons. He may would see a button that he now could build the most expensive building, but as soon as he clicks this button, an RPC call would be send to the server, and he would determine, that the city hasn’t enough ressources. But: Therfor, that this mechanism works (and I hope it does), the server would also have to calculate the ressources for this city. But does he do it? I mean, the instance is existing on the server, so the tick() should also be called on the server, or doesn’t it? Is my ‘idea’ how to solve this the right way to do it?

Or should I only calculate it on the server and replicate the value to the client? Wouldn’t that be unneccesary network-usage, or is this required, or just the better way to do it?

Okay, so assumed, my way of doing this is correct so far. Now, there are some functions, that the player would like to avoid, functions that perform not positive influnce for him but negative. Unlike the functions, he WANTS to execute, where the server just can say, YOU SHALL NOT PASS, the server couldn’t do anything if the player would just bypass this function call by a hack. As an example: Every city has a population, which consums ressources over time. The player would love to bypass the function that substracts the depending value, so calling an RPC Call from the Client to the Server to execute this substraction would probably be a very very stupid way to do it, yes? So, I have to call the function on the server. Now, a similiar question as above appears: Can I just execute the function without any RPC Call and let replication do the rest? As far as I know, replication doesn’t work bidirectional, just Server->Client, so can I just do it that way? Or should I call a Client Function for this purpose?

Next step: The city now exists on every client. Later, a player clicks on it on the map. Now I want to locally validate if the PlayerController who clicked that city is the owner of that city, to prevend unnecessary network-usage (If something gameplay-importend is clicked this is also check on the server). But how do I properly and safely set/get/identify the owner of an Actor? Currently, I am spawning the city on the server and call SetOwner(this) in the _Implementation function, called by a client, where ‘this’ is the PlayerController. Then, when a click event is triggerd, I execute the following lines:




bool ACapitalCity::isPlayerOwner(APlayerControllerImplementation* controller){

	if (this->GetOwner() == controller) return true;
	else return false;

}

But strangely, only the first Client (Client1) succesfully writes it’s owner into the ACapitalCity instance. Better explained: When I spawn an instance with Client 1, I can access the correct data of his PlayerController on every other client and the Server. Everything is just fine.
But when I spawn an instance of the same class with the Server or any other client game, with exactly the same code( and yes, everything is called in the same order, I debugged through it step by step) the GetOwner() function returns a NULL-Pointer, on every game instance, the own, the other clients, and the server. :frowning:
The behaviour remains the same, when I (instead of SetOwner()) use a SpawnParameter…
Very strange: All other values are corrctly, for example the unique PlayerState ID I set, is on all games everywhere accessable…
I fear that I have some big misconceptions about the whole RPC/Server thingy and that all of my above ideas dont work in practice… Or do I just miss something small? And is this at all the way you would identify a gameplay-owner of an object? Or what is the most safe / best way to do it? However, I would like to understand why this doesn’t work, for understanding purposes.

If you need some more code, let me know!

Thanks in advance! :slight_smile:

Greetings from Germany!