So Im in that stage of development where I need to start storing data, in this case, player data, some global values, and possibly enemy data (we’ll see about this part).
All I need to do is send the data from the server to the client, but I was wondering the best way to go about this.
For starters how it works now, is every time it detects a change in the data, it just sends the new info to every client.
One change I’ve thought of making, is instead of sending every players data to every player, just send the players their individual data.
Now this data could update pretty frequently, so making it run fast and making it performant are one of my highest priorities!
So yeah, I was wondering how I should go about making this, and any common issues to avoid.
Updating a player’s local data when it changes on the server shouldn’t be incredibly performance-intensive to begin with. As you stated, you should be only updating a single person’s data whenever that person’s data changes if the changes don’t need to be reflected to other clients. In that case, a global update is probably a good call. Use FireClient and FireAllClients to accomplish this.
Leveraging UnreliableRemoteEvents for data that isn’t significant or is emphereal (i.e. quickly or constantly updating values such as time elapsed or steps taken, or effects such as VFX or damage counters) is probably a good idea.
For a simple way to store and replicate data from the server to the client, you could opt to store data in an instance hierarchy accessible to both the server and the client, for example as a Folder under the Player instance. A big benefit of this approach is that replication of data is handled automatically. You can either inform the client when there has been a change to their data with a RemoteEvent, or the client can simply listen for changes to their data with a .Changed event connection.
This approach works well for data easily representable by things like numbers, strings, booleans (or other value types that can be stored in a -Value (ValueBase) instance). This is also how default leaderboards work.
This is an issue with ProfileService I solved by storing a copy of the player’s data locally.
Assuming each player’s data is a table on the server, simply retrieve that data when the player joins via RemoteFunction. Then to sync the client with the server, fire a RemoteEvent passing the key of the data and its new value to the client when their data changes on the server.
Extra
The caveat of handling data fully inside scripts is that the data cannot directly work with leaderstats. To listen to data changes, you’ll need to create a listener function, like :subscribe(key, callback) on both the client and server, which basically acts like GetPropertyChangedSignal() but for a table. Then you can just change the value objects in leaderstats to update the leaderboard. Though you’ll need to work with a Signal module to do this.
This way, leaderstats is completely independant from the data, so you should deal with data 100% through code and not through the physical leaderstats instances.
Personally this is why I use the ProfileStore module instead.
You can use a Data Manager that has functions that simply update the save table directly as well as changing a player descendant value or leaderstat a value.
Lets say for example you are storing player level and you also have it on the leaderstats. All you have to do is change the
Profile.Data.Wins = newValue
when the player levels up and then the line below set:
Player.leaderstats.Wins = Profile.Data.Wins
That way data is fully handled and controller on the server but if you ever need to access that data on the client side you can easily.
I recently completely reworked my datastore system and management. I spent hours trying out every possible way to replicate data from the server to the client, and ultimately chose the classic folders + attributes method.
I selected this method because it seems to be the simplest and most efficient approach to me. Attributes are automatically replicated, making data change detection straightforward. Additionally, it allows for easy access to other players’ data, which can be useful if I need to include multiplayer features such as a custom player list or profile stats. It also does not use additional network traffic unlike remote event.
On the other hand, using Remote Events can be more complicated and overwhelming. It requires firing a client event every time a player’s data changes, and you must ensure that the event is received correctly and that the number of events fired per minute does not exceed the limit. Additionally, it necessitates an entire client-side data management system with a good set of bindable events to communicate changes and update GUIs, which can be complex to manage and maintain over game updates.
Therefore, if you’re looking for a simple yet performant and straightforward way to handle data replication, I strongly recommend to use folders and attributes.