Hello everyone,
For the last few projects of mine I have been relying on IntValues and StringValue instances to keep track of the player’s stats. However, this has limitations because storing an Inventory as a StringValue means it is extremely hard to give attributes to the items.
So, I changed my architecture to a ModuleScript in the Server, and with every update to the player’s stats sends a remote event to a localscript in StarterCharacterScripts with the updated stats and a bindableEvent is used to communicate these stats with different scripts.
But I am very paranoid that what I am doing is inefficient or not how people would usually go about storing data. And also sending a RemoteEvent every stat update for each player could activate some sort of rate limit.
while task.wait(1) do
for _, players in Players:GetPlayers() do
local vt = get_data_version_time(player)
if vt == version_times[player] then
continue
end
version_times[player] = vt
--- dealing with confidential data
RemoteEvent:FireClient(player, get_player_data(player))
--- dealing with public data or something
player:SetAttribute("Data", to_json(get_player_data(player)))
--task.wait()
end
end
It could drop the request sending errors, but only if you continuosly try to fire remote events.
So if you update the data every 5 seconds or every time a player data is changed you should be fine, as long as you don’t use one remote event for many stats every second.
I don’t have any previous games, but I’m working on one.
I think I didn’t use one singular attribute for all.
I would store them at the ReplicatedStorage like this:
Module scripts seem the better option.
I recently created my own replication system, using buffers which help you to reduce the paycheck of remotes too.
If you are intrested I can show you my module (it’s not as good as other community resources).
To replicate data I would consider using “Replica” or “ByteNet”, two very fast modules to replicate your data.
And for data saving I would recommend either “ProfileStore” or either “Suphi’s Data Store Module”, again two of the best data saving modules in the forum.
All the resources are free.
A DataType created for compressing strings and numbers, you can compress other objects too but you will have to learn serialization.
You can create a single remote and send data throught it.
Example:
local Remote = path.to.remote
local Players = game:GetService("Players")
local stats = {} --your stats here
local function send(player: Player, stats: {any})
-- code here
remote:FireServer(player, "StatUpdater", stats) --or FireClient
end
Players.PlayerAdded:Connect(function(player: Player)
task.spawn(send, player, stats)
end
In your case, optimization of stress is useless, because players will have HUD and inventory opened very often, this mean that gain from possible: Update only when value is visible gives you nothing
The most important thing is what you send, you should always have One Remote per task, otherwise it will be very inefficient, if you have already remote called ReplicateData, you should think about where you need to send data
For instance, if you have HUD data, like coins or level or smth, this is possible solution:
Create HUDReplicator
Assign numerical IDs to every HUD component (strings aren’t that efficient, you can pack numbers into buffers to gain advantage)
Send ID and Value to the client, by packing them into 3 byte buffer you’ll reduce their size from 16 bytes, which is a lot
Decompress value and use maps to find correct elements & perform functions
local function onClientEvent(Data: buffer)
local ElementID = buffer.readu8(Data, 0)
local Value = buffer.readu16(Data, 1)
local Gui = ElementIDToGuiObject[ElementID]
if not Gui then
return
end
Gui.Text = Value
end
ReplicateHUD.OnClientEvent:Connect(onClientEvent)