Help updating an actor for each client?

I’m making a chess-like game where both players see themselves as the blue team, and they see each other as red (like in most multiplayer shooters), and I’m also very new to networking.

Every game piece (actors) has a variable that’s a pointer to that piece’s current tile. Every tile (also actors) has 2 variables: the current state of the tile (empty or occupied) and a pointer to that tile’s current piece (if it has one). Each title also has a static mesh that indicates its current state: white for empty, red for “occupied by an enemy piece”, and blue for “occupied by a friendly piece.” These are the only things that change during runtime. Everything else (like each tile’s coordinates or a piece’s owning player index) stays the same from start to finish.

Right now, everything works as it should when the game begins: pieces’ tiles change colors for each player to reflect the piece occupying them:

But there’s a problem when one of the variables I talked about earlier changes. When I try to move a piece, the piece itself is updated for both clients (because it is set to replicate movement), but the tile does not change colors:

While trying to figure out why, I discovered that the piece’s and tile’s variables update for the client who changes them (“player 1”), but not for the other player. So the function that updates the tiles is called for both players, but the second player doesn’t know that the piece has moved.

I’m doing most of this code inside of the player controller class, but the important variables are being stored in each instance of the piece and tile classes, and those are the variables that aren’t updating for both players. I’m not sure if they’re being updated for the server either.

I’m very new to networking and I’ve tried everything but haven’t been able to fix this. I thought that maybe it was because the variables weren’t replicating, but when I made all of the variables replicated, nothing changed.

Any help would really be appreciated!

Edit: Another problem I’ve just discovered is that when playtesting in the viewport and de-possessing player 1, the tiles’ materials return to their previous states. I don’t know the the viewport playtesting works, but my guess is that the color of the tile is only changing for the first player.

I’m a little confused because you say " the piece itself is updated for both clients", then you say “the second player doesn’t know that the piece has moved”. Maybe you could clarify exactly what’s happening and post some screenshot of your code

but regardless, it’s not enough to just set a variable to replicated. Clients have to tell the server that they moved, so that the server can tell all the other clients. This is done with an RPC.

Otherwise, the server will only tell clients about it’s own moves, but it wont tell anything about other clients.

1 Like

Hi, yeah, I guess my wording was kind of weird. The piece actually moves for both clients, in that it changes location, but the instance variables of that piece do not change for each client. I tried making the actual function that moves the piece and updates its variables a client, server, and net multicast ufunction, but none of them changed anything.

post some screenshots of your code

1 Like

I wasn’t sure what to share. I think I have all of the important code here (explanation at the very bottom, but I hope my comments are a little comprehensive):

AMatch_PlayerController - The base player controller that each connected player gets. Once the game begins, each player controller is assigned a number that’s stored in a variable called PlayerIndex that’s either 0 or 1 depending on which player they are. The player index helps differentiate the two controllers:




Header file for the player controller class:

Header file for the board tile class, which each have replicated variables to store their current state (occupied or not) and the piece currently on them:

Replication code for the board tile in its implementation file:

Header file for the parent piece class, which has a replicated pointer to its current tile:

Replication code for the parent piece in its implementation file:

So the player can click a tile or a piece on the tile to execute the “interact” code. If the player is selecting a piece to move, then it will select that piece and give them the option to move it, entering the “move” state. If the player is already in the “move” state, then the piece or tile they clicked is actually the target of their move, not the piece that they want to move, so the “move” function will be called: MovePiece(). I think that the move function is replicated for the server and all the clients, and it updates everything. It resets the variables of the piece’s old tile, updates the variables for the new tile, and updates the piece’s variables. Then, it calls the UpdateAllTileHighlights() function for every player controller, which is supposed to update the color of every tile depending don’t the player (blue for friendly, red for enemy). I think that UpdateTileHighlights() works, but the wrong tiles update for the other player because the variables aren’t updating for them.

I think the problem might start at MovePiece()

From the client’s machine, you are sending the server a reference to “NewTile”, and it updates CurrentTile on the server.

CurrentTile is replicated, so it updates all the other clients about CurrentTile, but that’s all it does.

It doesnt set the material and all that stuff because Replication will only update the pointer to CurrentTile, it won’t do anything else. So, only the server sees the color change because it doesn’t tell other clients about that.

If you wanted clients to see all those changes, you would have to call a “NetMulticast” function, but you cant call NetMulticast strait from a client. You would need to call a “Server” function from the clients, then the server would call the NetMulticast function which would update all clients, and itself.

So change MovePiece() to NetMulticast, then make a new “Server” function, like
UFUNCTION(Server, Reliable)
TellServerYouMoved(ABoardTile* NewTile);

inside TellServerYouMoved, you simply call MovePiece and give it NewTile as the parameter.

now, instead of calling MovePiece, you would call TellServerYouMoved when a player moves

1 Like

I just tried what you explain but nothing changed for me. I changed MovePiece(ABoardTile* NewTile) to MovePiece_Client(ABoardTile* NewTile) and made it a net multicast function. Then, I made a new server function called MovePiece_Server(ABoardTile* NewTile) that just calls MovePiece_Client(NewTile). Then, I replaced the call for when the player interacts from MovePiece_Client(NewTile) to MovePiece_Server(NewTile). Is that right? Thanks for taking the time to help me out!

That should be working.

Could you post just the stuff related to move piece, like the header declaration, cpp definition, and the function you call it originally

Also, make sure that all of your board’s tiles are replicated because when you send a pointer over the network, that pointer need to be replicated, or else the server doesnt know which tile you’re talking about

1 Like

Sure. Here’s the declaration:

Here’s the definition:

And the call:

You sure that all your board’s tiles are replicated?

1 Like

Also, CurrentlySelectedPiece would need to be replicated as well, and you would also need to send that to the server from the client, as a parameter

1 Like

What exactly about the tiles needs to be replicated? Currently they have bReplicates set to true and their variables are all replicated too.

CurrentlySelectedPiece is actually a declared private variable inside of the Match_PlayerController class. Should I change that?

Okay, so I changed CurrentlySelectedPiece to be public, I replicated it, and I replaced where it’s called in both MovePiece functions with a new parameter called PieceToMove, which I pass CurrentlySelectedPiece as when I called the server MovePiece function. Nothing has changed though.

having bReplicates set to true is good enough.

declaring CurrentlySelectedPiece as private shouldn’t have an effect on any of this.

the problem is that, you reference CurrentlySelectedPiece in MovePiece(), but the server doesn’t know which piece is currently selected unless you tell it.

So you have to add another parameter to MovePiece, (for exampe, NewlySelectedPiece), then pass it along to MovePiece_Client

Then, on MovePiece_Client, you would set CurrentlySelectedPiece = NewlySelectedPiece;
before you start referencing CurrentlySelectedPiece. Otherwise it doesnt know what piece youre talking about

Then it should work in theory, although I don’t see exactly how the board changes color, but as long as you’re not referencing anything that has changed since the objects construction, it should work

that’s the problem, you are referencing things that changed since the object was created, and now the server doesn’t know what you’re referencing.

1 Like

Is what I did in my last reply what you describe? Instead of just setting CurrentlySelectedPiece to be the passed parameter, I just used the NewlySelectedPiece parameter as the target of the function calls, instead of CurrentlySelectedPiece.

yes, you could just make the calls directly on NewlySelectedPiece

I’m having trouble seeing how the tile actually changes color because you have a Highlight function, then you have UpdateAllTileHighlights.

Try to find out exactly where this is breaking

Put some print screen functions like this:

// put this in MovePiece_Server
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, TEXT("Sent to server"));

// put this in MovePiece_Client
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, TEXT("Multicast to clients"));

then, when you move a piece, does both of the messages appear on screen? the second message should appear twice

1 Like

“Multicast to clients” only appeared once. What does that mean?

it seems like your multicast is only happening on the server

delete the other 2 message functions

on MovePiece_Client try this:

	FString LocalRoleEnumString = UEnum::GetValueAsString(GetLocalRole());
	GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, LocalRoleEnumString);

what does it say now?

1 Like

“ROLE_Authority”

I’m about to go to sleep, i can help more tomorrow, but you should be getting 2 messages from your NetMulticast function. Once when it executes on server, then another when it executes on client.

I set up a quick example in blueprints just to double check.

This is how it should look:


Capture2

so when you press 5 on your keyboard, it should send 3 messages total,

1 from the server function and 2 from the multicast

for some reason you’re not getting the 3rd message

1 Like