P2P Triggering Event From One Player Controller/Client and Propegate to All Others

Hello,

I am currently stumped on a quick proof of concept using a listen server doing the one thing we usually avoid: having the clients affect other clients.

What I need is to have the events and a data struct from any given client to be sent to all the others: excluding itself.

To test, I have tried setting it up using the player controller, player state, and game state.

  • Player Controller: Event on space bar, calling an increment event on the Game State, passing in the controller/playerstate to be evaluated on the other side.

  • Player State: Contains increment int which is being read from and displayed to the UI to make sure replication is working correctly. It also contains a string variable called Alias, which I added to make testing on a single device easier (eventually this will just be device ID).

  • Game State: On increment, loop through all player states and check to see if their aliases match up. If they are the same, do nothing. If they are different, increment the counter variable on the respective Player State.

I do not know if it is because I do not have any actors in the level, as I’m just working with pure data/inputs, but I’m not having any luck getting this to work with any combo of RPC’s. (referenced How to replicate an event to a specific client only? - #2 by Chyros)

Do I need an intermediary variable or event?

At best, I have been able to get whichever client is acting as the server to modify the other clients’ increment variable within their player states, but when it comes to the clients modifying anything else, no dice.

I take it event Increment is a “run on server” replicated event, in GameState ?

That’s not gonna work, to replicate from client to server you need to call an RPC within a class the client owns, ie. playercontroller or playerstate or character.

Additionally, there’s no need to check for matching player state one by one. You can directly pass the playerstate (or a way to reach it directly) to the “other side”.

Move your event Increment (run on server) to the player state class. Increment your variable directly in there. You can call that event from player controller or anywhere else, whether you are server or client, all should work.

Considering you’re already using a variable, you probably don’t want to use events to replicate the variable to others. Replicate the variable instead. Changing a replicated variable value on server will automatically flag it for replication, you don’t have to do anything. If you need to trigger an event on clients (eg. to update UI) you can use RepNotify in variable replication setting, that’ll automatically create a function OnRep_Variable which is automatically called whenever the variable changes.

Generally you don’t have to worry about variable being replicated back to client himself, but if really needed you can also use replication condition SkipOwner to prevent exactly that.

Thanks.

I completely forgot about GS ownership when I was going through the other day.

Definitely closer, but I think I am still messing something up.

Player State Code:

Player Controller Code:

The only way I have been able to get this to work is by having the Player State event be a multi cast; however, this closes out with a Nullptr error.

One thing I have been noticing, is that the server and client 1 have the same palyer ID.

Also, to clarify, I’m not trying to replicate the variables to others. The goal in this exact example is to have when Key 1 is pressed, it increments the counter on all other clients.

So in a two player example, if player 1 presses the key, it affects everyone else. Player 2 affects player 1.

Additionally, for the end purposes, OnRepNotify is too slow and I need them to be executed in the order that they were originally called.

Ok I misunderstood some of this.

In this case you need to iterate all playerstates to increment variable, except for the one triggering the event. You can access all playerstates via GameState->PlayerArray.

Also, note you should probably never use methods like GetPlayerState(0), GetPlayerController(0) or GetPlayerCharacter(0) on server side code (unless you’re trying to reach the host of listen server). Those are static context-less methods that will return the first object available and not the one you’re looking for. Instead, use the variable PlayerState from controller class to get the playerstate of this controller.


While this should work for incrementing the counter as intended, this unfortunately doesn’t guarantee original order across players. This is because each game object has its own replication channel, and reliable calls are order-guaranteed only within the same channel. To guarantee order (at least the order received by the server, you can’t guarantee the order in which clients pressed the key), you’ll have to funnel all those multicasts through the same channel (actor). To achieve that you can move (and adapt) code into GameState instead of PlayerState :

Adapt the playercontroller code :

image


Additionally, don’t forget events/multicasts are basically fire-and-forget. If a player joins the game late he won’t get any of the events that were triggered before, so all his counters are gonna be wrong. You can remedy that by replicating the counter variable with condition InitialOnly - the result is that it will only replicate once initially when the actor is created on client, with whatever value it has on server at that time, then it’ll stop replicating and you can continue updating it relying on events.
image

Thank you!

After I had posted my responses, I started to realize I wasnt using the player state variable as I was debugging my other UI widgets.

The Game state code you had is indeed what I had tried in my original—however—with the wrong player state reference.

The above code works as intended.

This is what happens when auto piloting through the editor and just typing blindly.