Hi! I have a couple of questions about the use case of a custom player properties module. The goal would be to be able to easily access information about the player client-side. I have thought about creating a server-side profile as well.
In your opinion, what would be the best way to set up an easy way to get specific player values on both the client and server? (My thought was a custom player module, but I’m not sure if that’s the most efficient way).
I have it setup like this currently (Code is not final):
Data.lua (Script)
-- ServerScriptService
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage.Remotes
local updatePlayerEvent = remotes.UpdatePlayer
Players.PlayerAdded:Connect(function(player)
local playerData = {} -- Would be retrieved through datastore, etc.
updatePlayerEvent:FireClient(player, playerData)
end)
Player.lua (ModuleScript)
-- StarterPlayerScripts
local Player = {}
-- Basic info
Player.Name = ""
Player.Age = 0
Player.Gender = ""
-- Values
Player.Money = 0
-- Moods
Player.Health = 100
Player.Hygiene = 100
Player.Hunger = 100
Player.Hydration = 100
Player.Fun = 100
Player.Social = 100
-- There would be more values in the future
return Player
Data.lua (LocalScript)
-- StartPlayerScripts
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlayerModule = require(script:WaitForChild("Player"))
local remotes = ReplicatedStorage:WaitForChild("Remotes")
local updatePlayerEvent = remotes:WaitForChild("UpdatePlayer")
updatePlayerEvent.OnClientEvent:Connect(function(playerData: {})
for i, v in playerData do
PlayerModule[i] = v
end
end)
For efficiency, security, and replication, you’d need to make an immutable datastructure. Immutable because you’d want the same reference + avoid unwanted changes to your data.
I’d look into existing packages for these types of systems, otherwise you could also make your own. It’s basically a dictionary with a wrapper module that sends and receives changes through signals.
An easy way to achieve this is by using roblox’s “Value” containers.
However, keep in mind that…
This approach causes a significant memory burden if your instances number into the thousands.
If storing an inventory using this method, it is recommended to store it as a single json string inside one container, and opting to use pattern matching as opposed to decoding the entire thing for maximum efficiency.
Exposes your data structure to exploiters, making certain containers easier to target through your own systems’ potential vulnerabilities. This is because most exploits expose the working tree, which would also include your value containers. Lua tables are ofcourse not immune to exploits either, but they’re a tougher nut to crack given that the exploiters would actually need to do some memory digging in order to find the values.
When speaking of “containers”, I mean things like:
…and the many other containers Roblox provides you.
Remember, instances created by the server are replicated.
Meaning that any changes the server makes to them, the clients will be able to respond to!
So how would this work in your case?
When loading in the player’s data, instance new containers according to the type of value that is stored.
By doing this, you create a streamlined way in which the server and even other clients can refer to your data whenever necessary.
Write data to the containers’ .Value property.
On the server/client, you can then hook up to the containers’ .Changed event.
But what about data that’s not specific to players?
For this, I recommend you parent your container instances under a folder in workspace.
Using this method, I was able to very easily create a dueling system.
I highly recommend you write two separate modules that you can reuse anywhere:
A player container manager.
The server can use “setters” given a constant string and value to write player data.
The client can use “getters” given a constant string to retrieve player data.
A shared container manager.
The server can use “setters” given a constant string and value to write data that’s not unique to any player. (For example, keeping track of round timers, or server statistics)
The client can use “getters” given a constant string to retrieve shared data.
This is also how I was able to implement the aforementioned dueling system with minimal overhead.