Hi guys, I’ve spent quite some time researching the architecture patterns for multiplayer games but still not sure if I’m going in the right direction. I want to discuss how to implement the stuff correctly.
Description
I have a multiplayer cooperative game for up to 4 players with standard listen server-authoritative design. Player can start a multiplayer game, other players can join it anytime and start playing coop.
The genre can be described as a sandbox so every player should have their unique info persist between sessions.
Players can have many different attributes, equipment and inventory. Players can have buffs/debuffs affecting their attributes so I need to have “actual” calculated attributes.
Problems to solve
NOTE: should be implemented with the ability to Save/Load data in mind
Where to store persistent data like raw attributes? Raw attributes - the base for actual attributes calculation, they persist everywhere. Load once per level. PlayerState is replicated across clients so it would be an overhead to use it I guess? This info is needed only by server-side with replication to the Client.
Where to store actual player data like attributes? I assume that I load raw attributes, calculate the actual (amplified) attributes according to equipment and buffs. So now I need to store it, be able to modify it on request and be able to use it for different player actions. For example, I can store it in an Actor Component attached to player Pawn which can also track all buffs/debuffs and update attributes accordingly. The Pawn and any of its components can request actual attributes using some Interface on the Pawn. It will be lost on Pawn death and can be recalculated on respawn.
Where to store persistent player data like inventory? Inventory will be lost on death. Probably can store inventory in a Actor Component of the Pawn too.
So I struggle to sum this all up to make it right. General question is where should I store described player data to be able to modify/access/save/load it easily?
Take into account that players may separate and never play again with each other opting for looking for a new game. In this case you can’t save player persistent data on the host.
One option is a centralized server that would hold persistent data in a database and be the “memory” of your project.
Each player would logon and register and be given a unique id as which you could later retrieve this information. All player updates would be periodically updated on the database.
The downside is that this requires upkeep. The server has to be running which costs money and you have to have good internet access
All data retaining to the current game session would probably be volatile. The session data (quests / tasks / challenges) could be saved on the hosts pc (maybe with a list of attendees) or periodically to the database.
(In case of having the database you could save the session in the database instead of the hosts pc).
=======
Option two if you don’t want central control and have less intensive to prevent hacking
Player persistent data would be stored locally on that persons pc, so that when they change sessions their characters data remains saved.
Session could also be stored on hosts pc.
You could go the route of some sort of CRC check of data and then save on all pc’s and upon load compare the CRC from all players to see if someone edited their save file and cheated.
Yeah, I’m quite new to unreal so I will stick to a simplier version. It is a good idea to store and maintain characters locally when no serious cheat prevention logic is needed.
The question that rises concerns though is how to better maintain player data during the session. I plan to use mostly blueprints.
Go with KISS in this case (Keep It Simple Stupid). Base the properties around simple structs. They will be easy to serialize and save / load. Keep the more complex operations on mutators (buffs, debufs etc).
Save out base stats, current modifiers if gameplay critical
(most stat modifiers are based on gear and worlds version of spells, tech or other implementation)
Gear depends if you want crafting / modifiying.
Keep the main gear information in a datatable and only save out the gear id that points to the datatable and maybe added modifiers, durability, amount - also just kept as structs.
Try to push it to be data driven then it will be lightweight to save / load too
I have created a “SaveGameBP” Blueprint which has the parent of “SaveGame”. I am making a 4 player PC game with a listener server. Using Steam for the lobby and sessions for clients to join. Currently have the host and the clients saving their character data on their own computers. I feel this “works”; however, I am not sure this is the best way.
My game is a first person game. As for where this save data resides in game, I have this data in the “FirstPersonCharacter” Blueprint. Also unsure if this is the best idea; however, that is currently how I am making it work.
My game is an adventure like game where there are quests and a main storyline. Thus, I have the host owning the progress of the quests and the storyline. Clients manage their own inventories and equipment and can help, or hinder, the progression of the hosts quests and storyline.
Finally, I am not sure there is a “right” way to implement your game. I believe there may be a “best”, or a possible “good”, way to implement your game. For me, this is my first Unreal Engine game so my game development journey is definitely evolutionary. I am also solo making my game. So, my strategy is to make things “work” and attempt to minimize really bad choices such that I can cobble together a gaming framework where my main focus can be on world building, quest development and story development.
SaveGames work great (they are option #6 in my compendium), but they have their own use. For example I would use them to save some game settings/preferences the player edited.
Note that using SaveGames to store data client-side, i.e, on the player computer, can be really dangerous, as they are able to cheat by changing these files with a few clicks.
Take the time to check that compendium I linked, and you will have a good overview of how to handle saving/persisting your own data.
For where data should reside, that is a different question. Take for example a ‘Health’ property. Should it persist a pawn death? If the answer is no, which is usually that, then place it in pawn(Character). If the answer is yes, then it should be placed in either PlayerState or PlayerController (depending on replication visibility). Now take for example ‘Kills’ property which keeps count of kills. By the previous reasoning you will usually save it in PlayerState.
I would also suggest looking at the different actor GameFramework classes and seeing what properties they entail to have a better idea on what further properties can be saved there.
I can see cheating as a big problem if you are attempting to monetize in game microtransactions.
I believe the lobby needs to be solid, or how clients join the host’s game, such that people cannot randomly join the host’s game and trash their game experience.
Otherwise, I feel if people are going to cheat to get around some quest or story element because it is “difficult”, they are just hurting themselves and reducing the overall intended gaming experience.