Best approach for Multiplayer Data thing

This game I’m making is an online Hero-Shooter FPS MOBA, where each player have data for stuff, specifically Owned Heroes/Classes (An example would be like in League of Legends) and Equipment Inventory (Example would be like in Destiny 2) for this question. I would be using something like EOS for accounts and have the player data be stored in a database.

My idea is that when the player starts up the game and enters the Menu Level, the Menu Level will send a request to the database to get the player data for Owned Heroes and Equipment Inventory and store it in the Game Instance. Then, when the player starts the gamemode, the gamemode would directly get the player data from the Game Instance for Character Selection and Player Inventory. Or the gamemode will get the player data from the Game Instance and store it in the Player State for use during the match, then reupload back to the Game Instance after the match/session. Then just before the player ends runtime, the game will store all the player data into a Save Game, then upload it back to the database.

I’m not exactly sure that this is the most efficient way to do things, or if is a better way to do things, so I just want to ask and make sure:

  • Where should the code retrieving the player data go during runtime?
  • Where should the variables for player data be stored during runtime after retrieval from databased? In the Game Instance, Player State or somewhere else?
  • It is even necessary to put the information in the Save Game before upload, or can I just upload directly from Game Instance (Without compiling it all in a Save Game)?
  • How often should I be updating the database for player data?

Also, I apologize if some parts isn’t clear enough, let me know and I’ll try to elaborate as best as I can. This is all the questions I can think of for now. Thank you in advanced.

Note: I did also post this on Reddit, but didn’t get much answers.

1 Like

Clients should not be allowed to modify data locally. This is guaranteed to lead to cheating, especially in the MOBA genre.

You must send a request to the server (Server RPC) every time you want to change any value that affects gameplay.

The server then checks and modifies it, and then either raises a Client/Multicast event, or the variable should be Replicated/RepNotify.

So I’m assuming this advice is meant for anything that relates to the player owning something, like inventory and weapons or owned heroes.

Does this apply to stuff like character selection during matches?

1 Like

Even if we assume that at any moment you can choose any character - ultimately you need to notify the server of your choice via RPC.

If a check is required to determine whether a player can select a specific character, it must be performed on the server.

There is also a method that I named RPC-tunnel.
For example, when the ability to select a character appears on the level, BP_CharacterSwitcher actors are created for each character, and each client becomes the owner of one of them. Only now will the client be able to honestly change the character.
Then BP_CharacterSwitcher will be deleted, or the client can be deleted as the owner, and it will no longer be able to send RPC to the server.

1 Like

So RPC is used for anything that is related to if the player is allowed to do a certain thing, like if the player is allowed to choose this hero because they have it or not allowed to choose it because they don’t have it, correct?

1 Like

RPCs are needed to transmit any message from the client to the server, or from the server to the client (one or all).
Including to ask the server to check some data, and to receive a response from the server - the result of the check (callback).
It is the server that must store information about which characters are available to the client.
But this is not their only application, but only one of the options.

You need to build network interaction in such a way that all important data is on the server. And then you distribute this data to clients when necessary.

1 Like

Sorry to bother you about this again but would this methodology make more sense:

The player, when checking or selecting a owned hero or inventory, sends an RPC to the server to check and/or modify the database as required because this data should only be handled on server authority. Then the server, after finishing the checking or modification, sends a RepNotify back to the client to notify for UI changes and whatnot.

1 Like

Something like that.

  1. All important data is stored on the server
  2. Clients send RPC to the server if they want to change something
  3. The server will check and change the data if it is allowed

If you need to launch, for example, an explosion effect, use RPC Multicast.
Then you need to use a replicated variable (RepNotify) that will store the state of the object that was exploded to correctly display the object to those players who came in later and missed the explosion event.

1 Like

Sorry for the late response, but I do have one more question.

What if I’m using a listen server instead of a dedicated server, but I still want to use a database to hold the player data? Would there be any conflict with Server Authority?

Do you mean that when the player starts the listen server, some data will still be loaded from another place (database)?

Yeah that, just wanted to know if there would anything different from the stuff you told me if the net mode were a listen server instead of dedicated.

From the point of view of network code architecture, there is no difference between server types.

If you will create a visual visible only to a specific player based on the data from the database, then you can make requests to the database directly from the client.

If the data will affect the gameplay, then the client must call to the server, which then requests data from the database and does something based on it.

On a dedicated server, unlike a listening server, widgets are not created (in some cases) therefore, you should not place logic in them that affects the gameplay, and there are no local player controllers. These are all the changes I detected.

If I’m understanding this correctly, the UserWidget for Selection would have to have functions that cast to something like the GameMode class, which would have the actual logic for Equipping/Selection and whatnot, instead of having the logic in itself?

Yes, the widget should be able to run logic, but the logic itself should be located elsewhere.

It’s like separating frontend and backend.

Ok, understood. But this does bring up another few questions, if you don’t mind. If the Logic should be located somewhere else, should it belong on the Game Instance, the Game Mode or some other class?

Also regarding the Game Instance, should the interaction of player data be direct (Inventory Change → Change Player Data in Database), or should there be an intermediary (Inventory Change → Change Player Data in Game Instance → Change Player Data in Database)?

If we talk specifically about inventory, then it is better (in my opinion) to create a separate replicated actor for it.
Storing inventory data in Game Instance or Game Mode is clearly not worth it, at least for the reason that they are not replicated as well as widgets, and Game Mode only exists on the server at all.

As for interaction, it should look something like this:

The player presses the “E” button → an “interaction” RPC is sent to the server → the server checks whether there is an item in front of the player ON THE SERVER, and if so, it adds information about the object to the inventory and removes the object from the level (or moves the object to a non-game zone if FPS drops when creating/removing the object), and sends the changed data to the database → the RepNotify function is triggered in the client’s inventory → the inventory widget changes in accordance with the current data.

When a player takes something out of the inventory, an RPC is sent to the server indicating from which cell of the inventory the item should be taken → the server checks whether the item is in the specified cell, deletes the information from the inventory actor, sends new data to the database, creates a new actor at the level → NetLoadOnClient of the created actor automatically creates its copy on the client, the RepNotify function is triggered in the inventory actor on the client - the inventory widget changes.

If the database is a kind of save file, then for optimization purposes it is better to take information from it after starting the game server, and send the latest relevant data before closing it.

By separate replicated actor, do you mean an actor component or something else?

If inventory is an abstract storage that the player has, then it can be a component added to Pawn. If it is an item, such as a backpack, and the player can remove it or put on another one, then it is a separate actor.

So just to recount everything you’ve told me in regards to the player data database:

  • The interactions regarding the database should be direct, meaning there’s no need for using a GameInstance or anything else as an intermediate
  • The logic of these interactions should not exist on the UserWidget, but on something like an ActorComponent (if the inventory is abstract, that’s what I’m going for)
  • Interactions need to happen on the server so that players cannot cheat. So it must use ServerRPC, then have a RepNotify to update the UserWidget UI
  • I’m assuming the ActorComponent would go in something like the PlayerState or PlayerController. It doesn’t need a GameInstance because the data is on a database, thus doesn’t need something to persist. (This does bring up another question of what happens if I need the inventory to persist between levels?).

Am I missing anything?

After level change you can request player data from the database again.
Or you can cache it in GameInstance. But you shouldn’t store it there permanently, it should only be temporary storage when level change.