Global Floors DataStore

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    Save this to a datastore and on rejoining give the floors to the player again.
  2. What is the issue? Include screenshots / videos if possible!
    I have no clue how i would do it.
  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    Devfourms and my own datastore system (that failed badly)

Current Code:

--Important VARs
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

--ReplicatedStorage VARs
local PlayerDataFolder = ReplicatedStorage.PlayerData

local GlobalFloorFolder = ReplicatedStorage.GlobalLeaderboards.Floors

--UI VARs
local Template = script.RowTemplate

local ElevatorButton = script.Parent.Parent.ElevatorJiggle
local ServerFrame = script.Parent.ServerFrame
local GlobalFrame = script.Parent.GlobalFrame
local ServerButton = script.Parent.ServerButton
local GlobalButton = script.Parent.GlobalButton

--Leaderboard functions
local function serverSort()
	for i, child in ipairs(ServerFrame:GetChildren()) do
		if child.ClassName == "Frame" then
			child:Destroy()
		end
	end
	
	for i, child in ipairs(PlayerDataFolder:GetChildren()) do
		local newRow = Template:Clone()
		newRow.Name = child.Name
		newRow.PlayerName.Text = child.Name
		newRow.PlayerImage.Image = Players:GetUserThumbnailAsync(child.Value,  Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size60x60)
		newRow.NumLabel.Text = child.TotalFloors.Value
		
		if newRow.Name == "TyK214" then
			newRow.Background.Visible = true
		end
		
		newRow.LayoutOrder = -child.TotalFloors.Value
		newRow.Parent = ServerFrame
	end
	
	ServerFrame.CanvasSize = UDim2.new(0, 0, 0, Template.Size.Y.Offset * #PlayerDataFolder:GetChildren())
end

local function globalSort()
	for i, child in ipairs(GlobalFrame:GetChildren()) do
		if child.ClassName == "Frame" then
			child:Destroy()
		end
	end

	for i, child in ipairs(GlobalFloorFolder:GetChildren()) do
		local newRow = Template:Clone()
		newRow.Name = child.Name
		newRow.PlayerName.Text = child.Name
		newRow.PlayerImage.Image = Players:GetUserThumbnailAsync(child.Value,  Enum.ThumbnailType.HeadShot, Enum.ThumbnailSize.Size60x60)
		newRow.NumLabel.Text = child.Num.Value

		if newRow.Name == "TyK214" then
			newRow.Background.Visible = true
		end

		newRow.LayoutOrder = -child.Num.Value
		newRow.Parent = GlobalFrame
		
		local amount = newRow.NumLabel.Text

		game.ReplicatedStorage.Remotes.ChangeFloorValue:FireServer(amount)
	end
	
	GlobalFrame.CanvasSize = UDim2.new(0, 0, 0, Template.Size.Y.Offset * #GlobalFloorFolder:GetChildren())
end

--Toggle visibility
ElevatorButton.Activated:Connect(function()
	script.Parent.Visible = not script.Parent.Visible
	script.Ding:Play()
end)

local function toggleServerGlobal()
	script.Click:Play()
	ServerFrame.Visible = not ServerFrame.Visible
	GlobalFrame.Visible = not GlobalFrame.Visible
	
	if ServerFrame.Visible then
		ServerButton.ImageColor3 = Color3.new(1, 1, 1)
		GlobalButton.ImageColor3 = Color3.new(0, 0, 0)
	else
		ServerButton.ImageColor3 = Color3.new(0, 0, 0)
		GlobalButton.ImageColor3 = Color3.new(1, 1, 1)
	end
end

ServerButton.Activated:Connect(toggleServerGlobal)
GlobalButton.Activated:Connect(toggleServerGlobal)

--Update Leaderboards
while true do
	for i=5, 60 do
		wait(5)
		serverSort()
		if #GlobalFrame:GetChildren() == 1 then
			globalSort()
		end
	end
	
	globalSort()
end

image

It saves your value globally, but on rejoining your floors value resets to 0. How would i save the value and apply it on rejoining?

What you’ll need to do first in order to use DataStores is actually create one. Super simple to do. Its just getting the DataStoreService and then creating a global DataStore like this.

local DataStoreService = game:GetService("DataStoreService") -- We get the service.

local NewDataStore = DataStoreService:GetDataStore("WhateverNameYouWantHere")
-- We create the GlobalDataStore here. Name can be whatever you wish for it to be.

A simple setup with DataStores you can try is by loading player data the moment they join initially. You can check for new players joining via using the “PlayerAdded” event from the Players service.

local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(Player)
	-- Whatever code here.
end)

Within that function, you can simply use the “GetAsync” method of your new GlobalDataStore in order to request the data of any Player. The GetAsync method requires a specific “Key”, for simplicity you should use the Player user Id. Something like this:

local PlayerData = NewDataStore:GetAsync(Player.UserId) 
-- We simply just use that method with the Player UserId in order to request the data of that specific Key.
-- Do note that BY DEFAULT the data you initially get will be nil.
-- It will be nil since by default there will be nothing written to that key.

For actually saving that data, you can do a similar setup but for whenever the player is leaving! Simply use the Players.PlayerRemoving event of the Player service. And within that function, you can basically use the “SetAsync” method in order to actually save their data when they leave. Like this:

NewDataStore:SetAsync(Player.UserId, DataToSave)
-- We finally save something to our GlobalDataStore. The data of the player!

And this is much it. This is the quickest, simplest and fastest way you can start using DataStores however there are still a couple of details you should keep in mind.

DataStore/GlobalDataStore methods (such as SetAsync and GetAsync) are NOT 100% reliable! This meaning that they can FAIL. And trust me, they can fail pretty often and at unpredictable times.
So something to consider is encapsulating the SetAsync and GetAsync methods into a “pcall”.
I cant fully go over what a pcall is and what it does so here is the documentation for them. But the general idea is that they let you handle code in cases it errors out without killing execution of your entire script/thread so you can handle any errors out in case it did actually fail. A simple setup looks like this:

local PlayerData -- We declare the PlayerData variable first.
local Success, Error = pcall(function() -- We create the pcall!
-- The "Success" variable is a boolean determining whenever or not the code ran and finished successfully!
-- The "Error" variable is simply the string of the actual error. Its the same one and kind you would see within the Output window whenever your code errors out.
	PlayerData = NewDataStore:GetAsync(Player.UserId)
end)
if Success then
	-- Only runs if the pcall itself (the GetAsync method) was successful!
	-- Do whatever you need to do with the PlayerData!
else
	-- Only runs if the pcall itself (the GetAsync method) failed!
	-- You can add code here that would retry the GetAsync process!
end

You can also do the same thing with the SetAsync method since that method also suffers from the same unreliability as the GetAsync method. As ive said previously, all DataStore/GlobalDataStore methods CAN and WILL (eventually) fail so your game should be ready to handle the times in which failures can happen!

And this is much it. Not much more to it other than some more complicated things (like the UpdateAsync method) which are probably of not much relevance to you yet (if youre learning to use data stores for the first time at least). Have fun scripting!

You’ll need to use the DataStoreService to save and load data. You can make a basic one which gets the job done like the example Xeno showed. If you don’t want to spend time learning on how to make a fool proof DataStore then I recommend using a DataStore module like Suphi’s DataStore Module which is probably the best option right now.

Hey there, thanks! I eventually got it working and it works without errors. Real life saver, been on this for weeks (ik).

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.