I am a few months into learning Unreal Engine, but I am brand new to the networking and replication aspect of the engine. I am just curious on your thoughts for the best method to solve the following problem:
If for example, I had a game which was turn based and involved a game board (This would be something like simulating a board game), where is the best spot to store the board data and how would I go about utilizing replication to ensure each client is able to know where game pieces are on the board such that the client can draw them to the screen.
The current idea I am struggling to work through is using a game board object containing an array representing the possible squares of the board (Imagine a checkers game), however I am completely unsure of where this object should be stored and how it should be replicated. I originally had it stored in the gamemode object, but quickly realized it won’t replicate to clients like this. Then, I tried to move it to a game instance class, but I am still struggling to get the game board object to replicate properly.
Am I using the right idea or is there a better place to store the game board? The game board was also based on a UObject, so I am not sure if this could be interfering with its ability to replicate. Is this something which is better suited to being based on an Actor object?
Any guidance and advice would be much appreciated.
I did go through that set of tutorials before I began trying to piece this together, but I think my lack of understanding lies in the scenario. I need one global game board which only the server can manipulate, but clients can send requests for manipulation and read the data at any time. I understand how to implement each of these points by themselves, but I think what I am struggling to grasp most is where I should be attempting to do this. IE. What is the best place to store this gameboard (actor or object)? The choices which I can see are GameMode, GameState, and GameInstance, but I struggle to understand the differences in replication between those 3 classes. I also struggle to understand the difference in replication between a regular Object and an Actor object.
One last question is where are the best suited places to having functions which read/request/manipulate the gameboard. Obviously, the player controller (And its UIs) would be most advantageous to store the functions required for requesting game board manipulation. However, which class is best suited to reading and drawing the gameboard for the clients. Right now I believe I have that code stored in my GameInstance class. As well, would GameInstance be the best place to add code to manipulate the gameboard? My last question is where do I go about initializing the gameboard? The gameboard has member objects which need to be constructed on its initial run time.
Thanks in advance
(When I say ‘code’ what I really mean to say is ‘Blueprint Nodes’)
Do you have a “game board” actor blueprint? Set it to replicate and then replicate whatever variables on that blueprint need to stay in sync. Alternately, I think the GameState would be a good place to keep that information, but if you have a game board actor already, why not use that?
Your GameState could have a variable to keep track of whose turn it is and only accept PlayerController commands from the correct PlayerController. Run all input commands on the server and then multicast the outcomes to the client(s).
Ahh I think a light bulb just went off on a huge misconception I’ve had. For some reason I was picturing I had to store the game board inside of another container such as game state. Something about your comment helped me realize I only need to spawn a gameboard actor into the level and then all should work well. Is this the correct idea?
It wouldn’t be all too hard at the stage at which I am at with the game to simply transfer the contents of the game board actor to the game state class instead. Right now it simply holds a single array for testing purposes. Would this be more advantageous to having a game board actor?
Yes, correct. Have the server spawn the replicated actor (game board) so that each client gets a copy.
It may be unnecessary to move that information to your Game State; it might work just as well where it is on the game board actor. You can set your single array to replicate, so that server and clients have the same value(s). Or probably better yet, set them to “RepNotify” so that when the server changes the value of the array, the clients get “notified” to run a function and react to that change (e.g. animate game pieces to move to their new location). It’s hard to say what would be best without knowing your game, but that should give you some really basic options to test.
Thank you so much for the replies! I have gone and kept the GameBoard as an actor and I spawn it in the level via the level blueprint using authority switches. While that all seems to be working okay I am running into a weird problem which I cannot seem to solve for the life of me. As I mentioned the GameBoard actor contains a single array at the moment containing the board pieces. I used the strategy of setting this array to RepNotify in order to animate the game piece and it does work. However the array itself does not seem to replicate to clients.
The RepNotify function calls the draw board event which is set as a reliable Multicast as you can see. I setup a debug switch after that to ensure it was being called on both server and client which it is. However, at the bottom of the blueprint you can see a function labeled ‘DrawLane’ and I have a basic isValid check to ensure each index of the array has something there before attempting to draw its pieces to the board. This is where it all fails though. Only the host machine passes these isValid checks meanwhile each client machine fails them and thus never draws/animates any pieces to the screen.
I am at a complete loss as to how that can be possible if the RepNotify function is being called and makes it through to the client.
What is contained in your Lanes array and how is it getting populated/modified? I don’t think you need to make your “DrawBoard” event a Multicast, since it’s being called from RepNotify. If you’re changing the Lanes array on the server (Run on Server and validate with switch has authority), then the “RepNotify” function should trigger on the clients without needing a Multicast.
If you run the “Is Valid” call after the ForEachLoop on “DrawLanes”, prior to “DrawLane”, do you get valid data out of the Lanes array on the client(s)?
I tried taking off multicast from the ‘DrawBoard’ event, but it seems to only call it on the server then.
I also moved the Is Valid call into the ‘DrawLanes’ event and noticed all of the lanes were valid on the server and none were valid on the client.
As for the systems which modify this array its pretty basic. I have a function which initiailizes it. By this I mean the Lanes array should never hold more than 5 indices. Each index is a Lane object and thus the initializing function simply constructs 5 Lane objects on “GameBoard BeginPlay.” I have this initializing function running with switch has authority and right at the end of it I go ahead and send a call to “Set Lanes” in order to trigger the RepNotify. I would expect this to replicate all the initial values and do an initial board draw. The function which I use to add pieces into a Lane is stored in the ‘Level’ blueprint. It’s temporary and simply creates a dummy piece and stores it into a Lane. At the end of the function I have it set to once again call ‘Set Lanes’ in order to trigger the RepNotify.
However, I suspect that since the is valid call is failing and without it I receive ‘accessed none’ error on the client side, the initializing function must not be working correctly or it isn’t replicating the Lane object at all. Is it possible that it is trying to replicate the Lanes array and thus copies the size of the array, but the object contained in the array has a problem and thus isn’t being replicated?
Ahh okay so it’s likely a bug! I thought I was losing it a bit. Your solution seems like a good work around. Curious on one thing - when you run a multicast function that takes parameters does it take the parameters from the server version which is calling it or the client version?
Gah I don’t know what to do with this anymore. I’ve been struggling with this for a week now. I spent the last few hours removing the array and instead simply storing 5 separate lane variables into the game state. It still won’t replicate to the client. This has got to be one of the most frustrating things I have ever had to do in my decade of programming.
In a last ditch effort I’ll leave the significant portions of my blueprint here in the hopes I just did something really stupid that I am blind to.
There’s a bit of learning curve getting all these things to talk together, but it’s rewarding once it’s working! Hang in there.
I’d probably recommend starting with getting some smaller replication working and then ramping up the components. From these screenshots, one thing I’d be concerned with is that you’re running server logic on the “Event BeginPlay”. The server is going to run this before the client connects. For testing, instead of using the “Event BeginPlay”, what if you tried a Key Press, like SpaceBar or something? Press the key on the server and see if things are in sync. You could try replicating something easier like an integer and making sure that value gets synced correctly.
I’ve never used the “Construct” object node; what benefits does it have to the “Spawn Actor from Class” node?
Also, I don’t think you should be switching authority on the RepNotify. The RepNotify should be informing the clients to do something, but you’re only allowing the result to run on the server. Maybe I am thinking about it wrong, but I would think that defeats the purpose. I would use “switch authority” on events that need to be ran on the server only, and if it needs replicated to clients, call a Multicast event AFTER that.
So the “Construct Object” node is used for creating instances of objects which aren’t actors. The way I have it setup is the Lane Handler class isn’t based off of actor. It’s just based off of ‘Object’ and thus it cannot be created by ‘Spawn Actor.’ This is also why I have it under ‘BeginPlay.’ I figured that I only need to create the Lane Handler object once and only the server should have to do that. What I expected to happen was for these created objects to be replicated to the client, but perhaps this assumption is what is throwing me off. I didn’t think to create Lane Handler as an actor simply because it’s only a container for two structs and an trigger box object.
I will try and test some of these things in a separate project and see what comes from it.