StatsManager | Store player stats

StatsManager

GitHub | Roblox | Sponsor
StatsManager is a simplified system for storing a player's stats. It doubles as a data-saving solution method as well as a stats container for managing savable and non savable stats for players. Whether or not data is saved can be decided on a case-by-case basis.

You can think of it a bit like a leaderboard, except more user-friendly.


Why use this module?

  • Choice between savable and non-savable stats
  • Detection for stat changes
  • Set who can access stats
  • Easily retrieve and modify stats
  • Works with ProfileService and DataStore2

StatsManager is incredibly easy to use, which makes it a great solution for your project. Additional functionality is provided for those who want it, but it is NOT required.

Updating stats is easy, here is a demo!

print(stats.Cash)
stats.Cash = stats.Cash + 10


Functions

StatsManager
StatsProfile - GetData ( player , timeout )
Get Data for a specified player. Leave blank for LocalPlayer. If no timeout is set, the script will yield until player stats are found
local stats = StatsManager:GetData(player)
stats.Cash = stats.Cash + 10

void - SetTemplate ( dictionary )
Set default value and whether data saves. Customize functionality for each stat.
StatsManager:SetTemplate({
	Cash = {
		Visibility = "Public"
		DefaultValue = 10
		Save = true
	},
})

void - EditOfflineData ( userId , callback)
Edit stats for a user who is not currently in-game. This function will error if the user is currently in the same game, or if it is not called from the server
StatsManager:EditOfflineData(69896869, function(stats)
	stats.Cash = stats.Cash + 100
end)

void - SetWrapper ( wrapper )
Set data saving wrapper, can be either "DataStore2" or "ProfileService". Leave blank for to use ProfileService (recommended)
StatsManager:SetWrapper("ProfileService")

void - SetTag ( tag )
This method does not need to be called unless you know what you are doing. Combine a tag (datastore key) for saved stats. Useful for porting from one data-saving solution to another.
StatsManager:SetTag("TagGoesHere")



StatsProfile
RbxScriptSignal - GetUpdateSignal ( statName )
Returns a RbxScriptSignal, that fires when the specified stat is changed.
stats:GetUpdateSignal("Cash"):Connect(function(newCashValue)
	print(newCashValue)
end)


What’s actually happening

Under the hood, StatsManager currently uses DataStore2 and ProfileService for saving, however you can choose which wrapper you would like to use. Each wrapper has unique functionality that you can read up on. One main benefit of using ProfileService is that you will have session locking, which is why it is currently a more popular option.


Template Settings

Here is a reference list for changing attributes for each player stat:

Setting Value
Visibility "Public" / "Private" / "Server"
DefaultValue any
Save true / false
ClientEditBehaviour "Modify" / "None"
ClientPrivilegeCheck function
ClientAllowedValues Array
More information
  • Visibility - "Public" / "Private" / "Server"

    • Public - Every user can see your stats, as well as the server
    • Private - Only the LocalPlayer and the server can see the stats
    • Server - Only the server can see the stats
  • DefaultValue - any
    This can be any value, and it will be the default value of the stat

  • Save - true / false
    Set’s whether the stat will be saved, for next time the user log’s in. This value defaults to true, so set it to false if you don’t want to save a particular stat.

  • ClientEditBehaviour - "Modify" / "None"
    Default’s to none, however if modify is set to “Modify”, a list of allowed value must also be set for the ClientAllowedValues setting.

  • ClientPrivilegeCheck - function
    If a function is defined, and the value returned from it is true, the client will be given permission to edit the specified stat.

  • ClientAllowedValues - Array
    A list of values that the client is allowed to change the value of the stat to.


25 Likes

As a helpful tip to anyone using this module, I tend to store the stats template in a ModuleScript so that the code is more organized.

2 Likes

looks epic i bet it will help alot of people cause roblox datastore has alot of issues

2 Likes

While definitely a useful module, there’s a couple of pretty major issues.

For one, in the module is a PackageLink owned by you, which prevents people from uploading/publishing games we use this in. It’s easily removable, but still serves as an inconvenience for people who don’t know it exists.

Secondly, leaving and rejoining a server will not restore the saved data, as DataTokens for users are not removed upon leaving. This should fix itself when the player joins a new server, but serves as an issue if a user needs to rejoin a server.

Thirdly, adding a new value to the template, or altering a new value, will break the data stores in their entirety, refusing to update or even load the data. There isn’t a way around this as far as I know, but I’d recommend making use of ProfileService’s :Reconcile() function to fix this.

If these issues were to be fixed, I’d say this is the best and easiest data module out there. It’s simplicity and surprisingly level of flexibility serves as a good introduction to people who are just beginning data stores, while also serving it’s purpose for people who don’t decide to bother (like me). Overall a great module with a few major issues that need sorting out.

5 Likes

Thanks for the feedback, I’ll jump right onto those issues!

2 Likes

Cant you do all of this yourself? It isn’t hard to make stats

1 Like

StatsManager makes it easy to load and change your stats, without the use of value objects. It has the additional benefit of being compatible with two wrapper modules which are DataStore2 and ProfileService, and replication is handled for values that you might not want to share between different clients.

Overall, this results in less lines of code

3 Likes

Hey everyone, I’ve updated the module and applied some major fixes, so if you were having any issues, they shouldn’t be present anymore after updating the module!

1 Like

Update v1.2

Hey everyone, I’ve just added support for a new settings for stats called ClientPrivilegeCheck! This allows you to specify a function which checks if the client input from a player is valid, rather than relying on a list of pre-determined values. This can be useful in more arbitrary cases, where you are checking multiple conditions.

Basic example usage:

TeamPreference = {
	Visibility = "Public",
	DefaultValue = "Survivor",
	
	ClientEditBehaviour = "Modify",
	ClientPrivilegeCheck = function(value)
		if value == "Survivor" or value == "Zombie" then
			return true
		end
		return false
	end,
};

Update v1.3 - More detailed error messages

What’s up again, I’ve made some more useful changes to the module to improve the workflow! This includes more detailed context for error messages.

Also a welcome addition, stats which are given permission to be modified by the client, have relevant data sent to the client upon initialization. This helps reduce network traffic as well as provide context to more error messages.

Error messages

[STATS MANAGER] Invalid method ':FindFirstChild()'. Try index stats using 'stat.StatName'
[STATS MANAGER] Invalid method ':WaitForChild()'. Try index stats using 'stat.StatName'
Trying to call methods on a player’s stats such as :WaitForChild() or :FindFirstChild() will now error. Instead you will be told to use stats.StatName instead.

ERROR: [STATS MANAGER] Cannot edit data not owned by LocalPlayer
Attempting to edit the value of another player’s stats from the client will return an error. If you want a player to be able to modify another player’s stats, do this from the server instead!

ERROR: [STATS MANAGER] Client does not have access to stat statName
This error will appear when trying to change a value of the local player’s stats and the ClientEditBehaviour setting is not set to “Modify”. This setting defaults to “None” so ensure you have set it up correctly. Refer to Tutorial.

ERROR: [STATS MANAGER] Stat 'StatName' does not exist. Not present within stats template
In the table where you have configured the stats, the specified stat’s functionality has not been defined. Because of this the data cannot be edited because it does not exist in the stats profile.

As a reminder, I can’t stress enough, how much this will lower the amount of code you have to write, as well as being easy to use. If you are a new user and you’re looking for a data saving solution, please take the time to check out StatsManager.

How to use it on client and connect it to GUI?

1 Like

It’s actually quite simple! All you have to do it check when the stat is updated and apply those changes to the UI.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StatsManager = require(ReplicatedStorage.StatsManager)

local stats = StatsManager:GetData()

script.Parent.CashStat.Text = "Cash: "..tostring(stats.Cash)
stats:GetUpdateSignal("Cash"):Connect(function(newCashValue)
	script.Parent.CashStat.Text = "Cash: "..tostring(newCashValue)
end)

I did recently make improvements to the functionality of the module, you may have to update it if the following code does not work.

If you’re looking for an example of what you just asked for, I’ve linked an example place with StatsManager already fully set-up. Let me know if you have questions.
https://www.roblox.com/games/9104201134/StatsManager-example

Your Module is very good, but can I ask what TeamPreference mean?

1 Like

That’s just an example I used if a player wants to set their own team from the client. If you want to know about it, under-the-hood, the server handles the requests and makes sure the are legitimate and match the type of values you expect.

How to modified the value in-game or with script? I mean if player is A the value is this else value is using default value.

1 Like

I’m a bit confused about the phrasing of this question, but if you’re asking about reading each player’s stats on a case by case basis, I can definitely help you there.

Use the target player as the parameter for the person who’s stats you are trying to read/write from. Optionally, you can leave it blank and it will automatically fetch the stats for the LocalPlayer if the code is being run on the client.

local stats = StatsManager:GetData(player)
1 Like

Update v2 - Ease of use + Tag support

Been a while since the StatsManager system was last updated. I’ve added support for DataStore tags, which are useful if you are converting from an existing DataStore solution!

I’ve realised that most people don’t really need or want to complete extra setup to use the module, therefore the following API Methods, SetTemplate, and GetData are the only ones required!

Additionally, some values in the template do not to be set. Therefore the bare minimum required is Visibility, DefaultValue, and Save.


Also, it’s crucial that you update your module if you are having trouble saving, a bug was fixed, where the save value in a template is always seen as false, even if users had set it to true.


Porting over to StatsManager

Some DataStore solutions require the user to supply a tag to combine their data. If this is the case, StatsManager includes an optional :SetTag() method.

Simply write in Lua code:

StatsManager:SetTag("YourTagGoesHere")

This will combine the defined tag to your data, and you will be able to access old save-data.

As always, updating the module won’t affect any save data, so it is highly encouraged!

hey gamma its me adam, im pretty sure you remember me.

but is this a DataStore module or a Stats/Leaderboard Maker & Datastore? im pretty confused on this even though roblox has major issues with datastores, thats why i came here.

nvm figured it out

1 Like

Wassup Adam! StatsManager can be used for creating leaderboards, however it also lets you chose whether or not you want specific stats to be saved. Hopefully this clears that up for you.

ProfileService and DataStore2 are incorporated into this module, which means that backups are automatically created. It’s highly unlikely for data to be lost unless you are mis-using datastores. You can trust that this module won’t corrupt any player’s data, and I am currently using it in some of my own games. I’m hoping that I can build trust with you and other developers that can safely rely on this module.