Hi, I’ve been wondering how does data stores work, how to make it, and what it’s used for.
It would be very nice if someone gave me an explanation about datastores so I could use them for my games.
Data stores are a data saving service that Roblox provides at no cost for you to implement in your game. The way they work is in each universe, you can create multiple data stores and assign keys to them, like a dictionary. You can then edit and get these keys to return data, and then set them to save data. I would recommend looking at ProfileService, the official documentation, or my own module for looking into how it works internally with code.
I would like to see your own module to see how to make it, but how does ProfileService work and link up to datastores?
Profile service is just a wrapper for the regular data store api, it adds extra features such as session locking, and is just more feature packed. My own module relies on ProfileService, so look into the docs of profile service and then you can get an idea of how to script it by looking at my method (or use it of course).
Ok so I like your module it’s really great but this really looks just complex and hard to learn, and I did just start coding since 5 months ago. Are you sure this is more of a simpler way to manage datastores?
Yes, it’s way simpler. The way Roblox’s api works, you have to implement checks yourself, and add session locking yourself as well. Profile service eliminates this problem by already including these features, as well as global updates. If you’re having trouble when trying to implement my module, go to the GitHub linked, and look at the gameTesting.lua file in the testing folder. You can also follow YouTube tutorials for profile service like I did.
It’s also better for beginners, because I know a lot of them that will use multiple keys for esch type of currency for example assigned to a user id, which is a bad practice.
Alright seems like I just gotta get used to how datastores are lol, but I’ll check out your gametesting lua file.
I know a pretty simple method for DSS (a method used for saving data in games) that most don’t use but imo is so much simpler (keep in mind you can compact datastores by putting info in tables and saving the table).
So first, you call DataStoreService and you make a DataStore with it:
local DSS = game:GetService("DataStoreService")
local YourDS = DSS:GetDataStore("UniqueNameOfDS")
After that, you make a variable in the player that you change using the given data in the DS:
game.Players.PlayerAdded:Connect(function(player)
local NewVariable = Instance.new("IntValue") -- An example, can be whatever value you want
NewVariable.Name = "VariableName"
NewVariable.Value = YourDS:GetAsync(player.UserId) or 0 -- UserId is the key used, since it's unique for each player it's really helpful and commonly used in this case for data, also 0 is the default value if there is no data
NewVariable.Parent = player
end)
Lastly, we need to save the data when the player leaves the game:
game.Players.PlayerRemoving:Connect(function(player)
YourDS:SetAsync(player.UserId,player.VariableName.Value) -- For context, it means to "get a sync" and "set a sync", meaning to set the value to the key under the datastore basically
end)
To everyone reading this after the date it was posted: all of the information in my post below works, but it is extremely low performance and a bad way to write data stores. Please look elsewhere for DataStore information.
Hello!
This is all going to be basic DataStore stuff:
DataStores are tools provided to developers to save a players stats in their games. It’s very useful, but a little complicated at times.
Let me go over some important stuff:
You cannot save objects to DataStores (only tables, strings, numbers, and booleans)
It is required to use them on the server (via server script, or by module script with a server script executing the code)
Saving data on the client is easily exploitable and will probably fail most of the time.
Once you start to save more than 5 things using SetAsync()
, you should store all of the data in a table and save the table instead. Calling SetAsync()
too much can cause serious issues.
And that’s about it for the extremely important things
Here is some example code on how to set things up:
First, start by creating a new script in ServerScriptService and name it whatever you like. “Leaderstats” is a good name for it if you intend to use multiple server scripts in ServerScriptService.
After this, follow along with this code to get things started:
You need to set up some basic every-script stuff first, such as variables. Do it like this:
local Players = game:GetService("Players") --// Gets the Players service (same as game.Players)
local dataStoreService = game:GetService("DataStoreService") --// Gets the DataStore service to use the DataStore servers
local dataStore = dataStoreService:GetDataStore("MyDataStore") --// Gets a specific DataStore with this name ("MyDataStore" can be changed to whatever you want!)
After this you need to setup an event called PlayerAdded
local Players = game:GetService("Players") --// Gets the Players service (same as game.Players)
local dataStoreService = game:GetService("DataStoreService") --// Gets the DataStore service to use the DataStore servers
local dataStore = dataStoreService:GetDataStore("MyDataStore") --// Gets a specific DataStore with this name ("MyDataStore" can be changed to whatever you want!)
Players.PlayerAdded:Connect(function(player) --// Runs every time a player joins the game
end)
For creating leaderstats, you need to create a folder via the script, like so:
local Players = game:GetService("Players") --// Gets the Players service (same as game.Players)
local dataStoreService = game:GetService("DataStoreService") --// Gets the DataStore service to use the DataStore servers
local dataStore = dataStoreService:GetDataStore("MyDataStore") --// Gets a specific DataStore with this name ("MyDataStore" can be changed to whatever you want!)
Players.PlayerAdded:Connect(function(player) --// Runs every time a player joins the game
local leaderstats = Instance.new("Folder") --// Creates a new folder
leaderstats.Name = "leaderstats" --// Names the folder "leaderstats". It is VERY important you DO NOT change this. If it is spelled slightly different, it will not show up on the player leaderboard!
leaderstats.Parent = player --// Sets the parent to the player that joined the game
end)
To create stats, it’s very similar!
local Players = game:GetService("Players") --// Gets the Players service (same as game.Players)
local dataStoreService = game:GetService("DataStoreService") --// Gets the DataStore service to use the DataStore servers
local dataStore = dataStoreService:GetDataStore("MyDataStore") --// Gets a specific DataStore with this name ("MyDataStore" can be changed to whatever you want!)
Players.PlayerAdded:Connect(function(player) --// Runs every time a player joins the game
local leaderstats = Instance.new("Folder") --// Creates a new folder
leaderstats.Name = "leaderstats" --// Names the folder "leaderstats". It is VERY important you DO NOT change this. If it is spelled slightly different, it will not show up on the player leaderboard!
leaderstats.Parent = player --// Sets the parent to the player that joined the game
local cash = Instance.new("IntValue") --// Creates a new integer value
cash.Name = "Cash" --// Names it to "Cash"
cash.Value = 0 --// Sets its value to 0
cash.Parent = leaderstats --// Parents it to the leaderstats folder to be displayed on the player leaderboard!
local wins = Instance.new("IntValue") --// Creates a new integer value
wins.Name = "Wins" --// Names it to "Wins"
wins.Value = 0 --// Sets its value to 0
wins.Parent = leaderstats --// Parents it to the leaderstats folder to be displayed on the player leaderboard!
end)
If you want stats that do not appear on the player leaderboard, then you simply parent the stat to the player instead of the leaderstats folder, like this:
local Players = game:GetService("Players") --// Gets the Players service (same as game.Players)
local dataStoreService = game:GetService("DataStoreService") --// Gets the DataStore service to use the DataStore servers
local dataStore = dataStoreService:GetDataStore("MyDataStore") --// Gets a specific DataStore with this name ("MyDataStore" can be changed to whatever you want!)
Players.PlayerAdded:Connect(function(player) --// Runs every time a player joins the game
local leaderstats = Instance.new("Folder") --// Creates a new folder
leaderstats.Name = "leaderstats" --// Names the folder "leaderstats". It is VERY important you DO NOT change this. If it is spelled slightly different, it will not show up on the player leaderboard!
leaderstats.Parent = player --// Sets the parent to the player that joined the game
local cash = Instance.new("IntValue") --// Creates a new integer value
cash.Name = "Cash" --// Names it to "Cash"
cash.Value = 0 --// Sets its value to 0
cash.Parent = leaderstats --// Parents it to the leaderstats folder to be displayed on the player leaderboard!
local wins = Instance.new("IntValue") --// Creates a new integer value
wins.Name = "Wins" --// Names it to "Wins"
wins.Value = 0 --// Sets its value to 0
wins.Parent = leaderstats --// Parents it to the leaderstats folder to be displayed on the player leaderboard!
local gems = Instance.new("IntValue") --// Creates a new integer value
gems.Name = "Gems" --// Names it to "Gems"
gems.Value = 0 --// Sets its value to 0
gems.Parent = leaderstats --// Parents it to the player that joined so it is NOT displayed on the leaderboard
end)
For saving the data, this is where it gets complicated.
When saving data, you need to save it with a specific key. This can be a string concatenated with the player’s UserId.
We do this inside of a PlayerRemoving
event, which is similar to PlayerAdded
but runs when a player leaves the game.
Here is some code on how to setup the data saving:
The PlayerRemoving event is the exact same as PlayerAdded in the way you set it up.
Players.PlayerRemoving:Connect(function(player) --// Runs when a player leaves the game
end)
Setting up the saving isn’t the worst thing ever, but can be complicated if you don’t understand what it means. That’s why I’m adding comments to explain!
The next step is to add a pcall to prevent the script from breaking there’s an error.
Players.PlayerRemoving:Connect(function(player) --// Runs when a player leaves the game
local success, errorMessage = pcall(function() --// A pcall is where it wraps code that can fail easily (especially in DataStores. i.e. DataStore servers are down) and it prevents the entire script from breaking if it errors.
end)
end)
Now for actually saving!
Players.PlayerRemoving:Connect(function(player) --// Runs when a player leaves the game
local success, errorMessage = pcall(function() --// A pcall is where it wraps code that can fail easily (especially in DataStores. i.e. DataStore servers are down) and it prevents the entire script from breaking if it errors.
dataStore:SetAsync("cash-"..player.UserId, player.leaderstats.Cash.Value) --// Calls SetAsync to save data to Roblox's servers. It calls the specific key, then saves the value of the stat
dataStore:SetAsync("wins-"..player.UserId, player.leaderstats.Wins.Value) --// Calls SetAsync to save data to Roblox's servers. It calls the specific key, then saves the value of the stat
dataStore:SetAsync("gems-"..player.UserId, player.Cash.Value) --// Calls SetAsync to save data to Roblox's servers. It calls the specific key, then saves the value of the stat
end)
end)
Loading isn’t much more complicated. Just a little more time consuming.
Back up in the PlayerAdded event, and under where you create the stats, youwant to start writing some empty variables and another pcall, like this:
local cashData, winsData, gemsData
local success, errorMessage = pcall(function()
end)
Then you can start pulling the data off the Roblox servers using GetAsync()
:
local cashData, winsData, gemsData --// Writes an empty variable for each bit of saved data. These variables get set in the pcall below with the respective data
local success, errorMessage = pcall(function()
cashData = dataStore:GetAsync("cash-"..player.UserId) --// Loads the data off the Roblox servers with the specific key used below
winsData = dataStore:GetAsync("wins-"..player.UserId) --// Loads the data off the Roblox servers with the specific key used below
gemsData = dataStore:GetAsync("gems-"..player.UserId) --// Loads the data off the Roblox servers with the specific key used below
end)
Then after that, all you have to do is make a fairly basic if statement to set the values to the stats you created! Like this:
if success then
cash.Value = cashData
wins.Value = winsData
gems.Value = gemsData
print("Loaded data for "..player.Name.."!")
else
warn("No data for "..player.Name.."!")
if errorMessage then
warn(errorMessage)
end
end
Full script!
local Players = game:GetService("Players") --// Gets the Players service (same as game.Players)
local dataStoreService = game:GetService("DataStoreService") --// Gets the DataStore service to use the DataStore servers
local dataStore = dataStoreService:GetDataStore("MyDataStore") --// Gets a specific DataStore with this name ("MyDataStore" can be changed to whatever you want!)
Players.PlayerAdded:Connect(function(player) --// Runs every time a player joins the game
local leaderstats = Instance.new("Folder") --// Creates a new folder
leaderstats.Name = "leaderstats" --// Names the folder "leaderstats". It is VERY important you DO NOT change this. If it is spelled slightly different, it will not show up on the player leaderboard!
leaderstats.Parent = player --// Sets the parent to the player that joined the game
local cash = Instance.new("IntValue") --// Creates a new integer value
cash.Name = "Cash" --// Names it to "Cash"
cash.Value = 0 --// Sets its value to 0
cash.Parent = leaderstats --// Parents it to the leaderstats folder to be displayed on the player leaderboard!
local wins = Instance.new("IntValue") --// Creates a new integer value
wins.Name = "Wins" --// Names it to "Wins"
wins.Value = 0 --// Sets its value to 0
wins.Parent = leaderstats --// Parents it to the leaderstats folder to be displayed on the player leaderboard!
local gems = Instance.new("IntValue") --// Creates a new integer value
gems.Name = "Gems" --// Names it to "Gems"
gems.Value = 0 --// Sets its value to 0
gems.Parent = leaderstats --// Parents it to the player that joined so it is NOT displayed on the leaderboard
local cashData, winsData, gemsData --// Writes an empty variable for each bit of saved data. These variables get set in the pcall below with the respective data
local success, errorMessage = pcall(function()
cashData = dataStore:GetAsync("cash-"..player.UserId) --// Loads the data off the Roblox servers with the specific key used below
winsData = dataStore:GetAsync("wins-"..player.UserId) --// Loads the data off the Roblox servers with the specific key used below
gemsData = dataStore:GetAsync("gems-"..player.UserId) --// Loads the data off the Roblox servers with the specific key used below
end)
if success then
cash.Value = cashData
wins.Value = winsData
gems.Value = gemsData
print("Loaded data for "..player.Name.."!")
else
warn("No data for "..player.Name.."!")
if errorMessage then
warn(errorMessage)
end
end
end)
Players.PlayerRemoving:Connect(function(player) --// Runs when a player leaves the game
local success, errorMessage = pcall(function() --// A pcall is where it wraps code that can fail easily (especially in DataStores. i.e. DataStore servers are down) and it prevents the entire script from breaking if it errors.
dataStore:SetAsync("cash-"..player.UserId, player.leaderstats.Cash.Value) --// Calls SetAsync to save data to Roblox's servers. It calls the specific key, then saves the value of the stat
dataStore:SetAsync("wins-"..player.UserId, player.leaderstats.Wins.Value) --// Calls SetAsync to save data to Roblox's servers. It calls the specific key, then saves the value of the stat
dataStore:SetAsync("gems-"..player.UserId, player.Gems.Value) --// Calls SetAsync to save data to Roblox's servers. It calls the specific key, then saves the value of the stat
print("Saved data for "..player.Name.."!")
end)
end)
Oh yeah, and you want a BindToClose
in the script as well. If there’s only one player left in the server and they leave, the server will close before the player’s data saves.
BindToClose will cause the server to wait before closing, allowing the script to finish running and save the data before the server closes.
You can do it like this (at the bottom of your script):
game:BindToClose(function() --// Will create a function that runs before the server shuts down
task.wait(45) --// Waits 45 seconds for data of the last player to save then closes the server
end)
All in all, if you’re just starting out, DataStores are hard!
I really hope this helps you out!
Oh, and if you want to learn more, check out Roblox’s documentation on the matter!
Best regards,
Amora
I also have documentation hosted inside the main module if that will help you too!
Basically just do what this guy did lol, using pcall can definitely prevent errors, so you can add that to what I showed you (I do think mine was simpler though, but overall this still makes sense and is good for those that know what they’re doing in scripting other things)
“guy”
I’m only joking this doesn’t offend me lol
in my defense your username says RoboBoy- Lol sorry XD
Nahh, it’s alright!
I kinda wanna change my username, but I’m tryna save robux. 1k robux would leave a fat dent in my profit/savings
These are really good methods but which is better suited for storing and showing data from a currency system as an example.
I’m saying this because I’m getting 2 different types of methods, one from @AmoraFolf and @2jammers.
I would say the other persons method, but if you want to make your game secure, efficient and add more stuff in the long run, use my method or ProfileService. It’s honestly just a better practice to use ProfileService when too can anyways, but that’s my opinion.
Alright I’ll look into their method more but thank you guys for sharing your opinions about datastores.
RoboBoy’s is simpler to understand, at least to me, and it can be perfected using other ways, I never taught myself ProfileService though since I never could quite grasp it, so I go for simple and most efficient, but if you find ProfileService easier to understand then use that
This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.