Easy to Use player stat module

Hello! I haven’t made many community resources on this account, or in general for that fact so bear with me here.

I’ve recently made a very simply programmed and simple to use module to make player datastores easily! It will require some very basic knowledge on using modules and metadata but its fairly simplistic.

-Its 7:35 am as im writing this, theres going to be typos / grammar mistakes or whatever, just lmk in the comments, im also probably repeating myself in several places, sorry!

and if someone is better at naming than me, help me come up with something better than “statHandler”

Warnings:

  • This was written in <2 days and all though I havent found any bugs, there probably are some, so please let me know if you find any!
  • This is intentionally simple (and maybe I was tired), there is no queue for backed up requests or anything of the sorts, once you learn more about datastores with experience feel free to add your own features, and share them below!

Module Download: statHandler - Roblox

Example Usage Download (place): statHandler - Roblox


Notes:

  • .new() should only be called once per player, on join preferably, do not call it per stat, or on character created, please :skull:
    -use statHandler:changeAmount(statName:string, amountChange:number), do not edit your leaderstats int instance values if you are using them as leaderstats

API:
Config:

Setup:

  • loadPlayerDataOnInit: should it auto load the player data on statHandler.new(), if set to false you will have to manually write a reference to the :initialize() function, default: true
  • primary_datastore_name: the name for the datastore to use, changing this will remove all saved data under that store, default: “PrimaryDataStore”

real quick, a key in datastores should be a unique string to access certain stores

  • key: this string will be added infront of the players userid in the datasaving function
  • debug_logging: whether or not it should print debug messages to the output, default: true, but I recommend turning this off for live games or adding your own specific debug messages
  • functionInStudio: pretty self explanatory, toggles whether or not to save / load data in studio, default: true

Stat Data Table:

the stat array in “config” is as stated, and array of data, use it as such

  • defaultAmount: how much of the stat to give to players with no save data for the stat
  • maxAmount: the maximum amount the player can get of the stat, set to anything below 0 (-1, -124731, -139458495, -81, etc) to allow inf amount
  • statName: the string to reference the stat, will also show up on leaderboard if isLeaderstat is true, must be unique from other stats, it will assert this if it finds several stats with the same names
  • isLeaderstat: toggle to show the store on the players leaderboard, will not effect datastores, its just a visual thing (In-Experience Leaderboards | Documentation - Roblox Creator Hub)
IsAPIEnabled

This is a function I got from a dev forum post and I just needed because I knew this module would be used by beginners so they might mess this up, it just checks whether or not the API toggle is enabled in game settings (it must be enabled for this module to function and will assert this)

Types

statData:

stored data (entered in config stat list)

liveStatData:

stored data with 1 change, it adds the “currentAmount” property, used in live stores during runtime

Version

Not used or referenced anywhere in the code, just a static string of the version, and a comment telling you which version it was on when I initially released it (init release was v. 0.6, I used it a little and added/removed a couple things before release)

Functions

-the internal module functions
-all functions will assert that they must only be called from the server
(in no specific order)

statHandler.new(plr:Player)

requires the plr (Player) parameter to setup, this is the player that the datastores will be loaded to and saved to
creates the metadata for a player, only call once per player, on join, dont call it several times or on character loaded or something like that

statHandler:debugMessage(message:string)

it was created for internal use to log messages and includes the use of debug.traceback(), you can call it from the metadata reference in your server handler I guess

statHandler:initialize()

loads the players stats in the “stores” table, sets them to default, and then fires the :loadPlayerDataOntoStores() function to load data

statHandler:loadPlayerDataOntoStores()

pretty self explanatory, loads the player data, will it is generally supposed to be used only internally it should be fine to use from your server handler

statHandler:savePlayerStores()

well, yk, saves the players data :shock:

saves the players data under the key with the players user id added onto the end

statHandler:changeAmount(statName:string, amountChange:number)

this is used to increment the players stat, statName is pretty self explanatory however amountChange is well, the amount to change by, it will subtract it (stopping at 0) if the number is negative, and it will add it (stopping at max) if the number is positive
USE THIS FUNCTION, DO NOT EDIT YOUR LEADERSTATS VALUES

statHandler:setAmount(statName:string, nextAmount:number)

very similar in use to statHandler:changeAmount(…), the difference being that it does increment the value of the stat, it just sets it to a certain number, and will set itself to 0 if it is set below 0, and will set itself to the max if it is set above the max

statHandler:_checkMaxAmount(statName:string)

-only intended to be called by internal functions, but would also work fine called by your server handler if you needed to for whatever reasons

checks for

  • if stat is higher than the max, sets it to the max
  • if stat is below 0, sets it to 0

as well as it updates the physical leaderstats values for the players leaderboard


Tutorial

settings up your stats (adding stats)

the properties for config are explained in the config section of “api”, above, I suggest you read them

in config statslist is an array [list] of stats that youd use in your game, you can add as many stats as you want

just keep adding tables under the array
for example:

statslist = {
    {
        defaultAmount = 0,
        maxAmount = -1,
        statName = "coins",
        isLeaderstat = true,
    },
    {
        defaultAmount = 15,
        maxAmount = 999,
        statName = "gems",
        isLeaderstat = true,
    },
}

setting up your server handler (aka making it work)

Firstly, download the module from above (duh). Now while it is up to you where to put it for ease I personally put it in ReplicatedStorage, that way the client and the server can get at the type definitions, however it is only supposed to be ran from the server and will assert this upon function calling.

The config options will be under the module, please read the config section of the api section above this and edit them to your needs

Because its a module script, we now have to use another (in this case server) script to control it, so im going to make a script under ServerScriptService and call it “server” for simplicity.
(ignore the “updateClientCoinUI” RemoteEvent below, its from the example usage file and I was too lazy to delete it for this screenshot)
2023-07-09 07_17_23-server - Roblox Studio

now in the “server” script we’re going to add a reference to the module, using require(module_pathg)

local repstorage = game:GetService("ReplicatedStorage")

local statHandler = require(repstorage:WaitForChild("statHandler"))

I should say, theres probably a better way to do this, or this is actually a good way and im just questioning myself a ton, either way, this is how im going to do it for the example
Im then going to add a table to a variable in naming “plrList” like so local plrList = {}

We’re going to use plrList to store a reference to each players metadata so we can access it at a later time through the players user id (see below)

then we can add a Players reference somewhere above the statHandler reference local players = game:GetService("Players")

and then a function for .PlayerAdded

players.PlayerAdded:Connect(function(plr:Player)
    --here we put an index with the players user id in the plrList table with its value being the metadata for a statHandler creation
	plrList[plr.UserId] = statHandler.new(plr)
end)

now this section assumes you left loadPlayerDataOnInit equal to true in config, if not you have to add plrList[plr.UserId]:initialize() for any of the rest of the module to function and to load data

we are also going to want to save the players data upon them leaving the game, so we can use the .PlayerRemoved event

players.PlayerRemoving:Connect(function(plr:Player)
	plrList[plr.UserId]:savePlayerStores() -- save data
	
	plrList[plr.UserId] = nil -- remove the players reference from the players list
end)

from this player list reference you will be able to use all the modules functions (read about them in the api section)
for example and testing in my workspace I have a part with a click detector, it works like this

workspace:WaitForChild("addition_example"):WaitForChild("ClickDetector").MouseClick:Connect(function(plr)
	--"25" here is positive, so it will add 25 from the currency
	plrList[plr.UserId]:changeAmount("coins", 25)
end)
  • im aware I teach by example and that this wasnt very in depth on an explanation, if anyone has suggestions for additions to the tutorial please let me know!

Please ask any questions in the comments, and feel free to correct me on anything! theres probably typos all over this or if you find any bugs please report them below!

4 Likes

A cool idea, but there are already better alternatives out there like ProfileService and Datastore2.

3 Likes

There’s always going to be alternatives for any resource made, just a fact of open sourcing work

3 Likes