Why are there 3 controllers in a 2 player network game?

I have recently started playing with Replication and have watched the 6 part video series, as well as read multiple threads (such as this one) while trying to troubleshoot things and while I am starting to get a grasp on how Replication works, I am still having trouble understand the concept of Player Controllers when it comes to networked games.

The questions (sorry if any of them sound dumb):

  1. When launching a game with 2 players and no dedicated server (using the Top Down template), why are there 3 controllers in the MyController debug view? Is it because the server has a copy of both controllers (Server PC, Client PC), and the client has his own copy of his controller?

  2. As a test, I execute a Multicast event when clicking an object and print a string. If I click the object on the server, I see Server and Client1 print the string. If I click it on the client, I see nothing as I would expect, since he is not the server so it is ignored. If I set the event to Run on Owning Client and click the object on the client, I see Client2 print something. This is the only situation so far where I ever see a “Client 2”. In the other tests I’ve done, such as printing a string when simply hitting F, the client shows up as Client 1 even if there is no replication set up for that key press (it’s just a simple F → Print string in MyController).

Assuming I am correct about #1, why are Multicast events run on the server’s copy of the client’s controller, and not the client’s own version of his controller? What is the difference between Client1 and Client2?

Thanks for the help!

Hey there (:

  1. Yes. The server has ALL PlayerControllers. So if you play with 10 Players (no dedicated server), the Server would have 10 Controllers. A client itself only has its own Controller. You can see this by choosing something different than “All Worlds” inside the PlayerController Blueprint while debugging:

http://puu.sh/dH59U/f1d45fd2ec.png

  1. I tried recreating your issue but i got no Client 2.

If i use Multicast, the server tells all other Clients (and himself) to perform the event (like printing something).
So he forces everyone to do this.

If i use “Running on Client”, i get my Server and Client to perform this action. Normaly you would use a “Switch has Authority” node to make sure that the server calls this. If he does, he will only let the Client perform the action which is owning the controller. (Remember he has all Controllers). So for you, to better understand what this is doing, i set up a small test.

http://puu.sh/dH61u/60b84d2ad0.png

So let me explain what i do here. What you see is inside a custom PlayerController BP. So if you hit F, you will start a for loop. The Start Index is 0, since we count from 0 to n-1 when coding. The Last Index is not important. I took 10, because i wanted to show how to loop through the Controllers. You could move the “Switch has Authority” in front of the loop, so that the player won’t even call the loop, but this is not important here.

So the Server loops through the “Get Player Controller” Node. 0 is always its own Controller. Since Clients only have one (in Singleplayer too) they always use 0. But the server has all of them, so he can loop through them. Now i used a “Is Valid” node, to not call the event with an empty controller (because 2-10 is not valid, cause we only have 2 players 0 and 1). Now we check if we are the Server (like i said it would be better infront of the loop, but nvm) and cast the Controller to our custom BP. Now we call the Event again but plugin our Controller to “Target”. If you now press F, the server will call the Event on every PlayerController. You could test it with using a 1 in the Get Player Controller node, instead of the loop and it will let the first player print the string.

And the last thing is “Run on Server”. You use this if you want the Client to perform an action that he himself is not allowed to. If you use this node, the client can press F to just print. The server will print it for him.

You can test using some Inputs that you can define under the Replicated section to pass the server a variable or let the server pass a variable to the client.

If you have more questions, feel free to ask me (:

Thanks for the info. A few followup questions:

1 - Why would “Run on owning client” execute this on the server AND the client? I was under the impression that Run on Owning Client makes it execute only on the client that owns the actor. But the part I don’t understand is which actor is this referring to? If it’s being executed on a character that’s spawned in the world, then it makes sense, it will execute on whoever spawned or owns this actor. But what about using Run on Owning Client in a Player Controller? How does the server know who to execute this on?

Here’s an example of a specific problem I’m having. Maybe this will help me understand. The game is a top-down tactical game where you can control multiple units per turn (but only one moves at a time).

2 - I need all players to know which unit is currently being controlled. This happens when either player clicks on a unit (if its their turn). I try to store this value in the player controller. This is the sequence in the blueprint (triggered on a click on a unit). The blueprint itself is a Unit, so the Self reference here is the unit that is being clicked on. Note that the Unit is already spawned in the world on map load so it is owned by the Server.

(Too many characters - CONTINUED IN NEXT COMMENT)

The actual behavior in game when I print the Display Name value of Controlled Character after clicking on a unit on each player is:

  • If server clicks on a unit, the value is in sync on both players because the server set his value and then replicated it to the client.
  • If client clicks on a unit, his value is updated but it is not replicated back to the server.

My guess is because “Run on Server” seems to only work if the client triggering that event owns the actor it’s being triggered on. I’m starting to think this is the source of a lot of the problems I’ve been having, since a lot of the tests I’ve done were on the Units themselves, which are all placed in the editor and not spawned by controllers, so they’re all owned by the server.

Either way, even if it worked, this seems like a lot of work just to change the value of a variable that I want to keep in sync for everyone, so I imagine I should use GameState to keep track of the Controlled Unit, since that should just stay in sync as long as the variable is Replicated and set by the server every time (without needing to manually multicast it back to everyone every time it changes).

So I’ve solved most of my problems and a lot of them stemmed from the fact I used variables as shortcuts to access my GameState, controller, etc. Since I did not start doing replication right away, these always worked fine so it took me a while to figure out that was the problem.

I would store the gamestate and controller in variables on Begin Play in a lot of my BPs, and I was using those stored variables in my replicated functions instead of Get GameState → Cast, and so on the players other than the one the function was executed on, that variable didn’t refer to anything useful.

So I have instead created shortcut functions that get the gamestate/controller and cast it instead and all is well.