Is there a way to make this datastore script even MORE reliable?

A friend of mine made this, and im worried about its ability to reliably save and get data. Ive had issues with it and im scared to launch the game with this broken datastore. is there anyway i can make it more reliable? (Idk how to use datastore 2)

local RS = game:GetService("ReplicatedStorage") local SendsAndGets = RS:WaitForChild("SetVals") local DSS = game:GetService("DataStoreService") local SaveToDS = DSS:GetDataStore("InvisLeaderstatsDS1") local ValsToSaveAndRetrieve = {"OverheadName", "PlayerFindable", "NotifLocal"} local function GetData(player)	local invisleaderstats = Instance.new("Folder")	invisleaderstats.Name = "invisleaderstats"	invisleaderstats.Parent = player	local OverheadName = Instance.new("StringValue")	OverheadName.Name = "OverheadName"	OverheadName.Value = ""	OverheadName.Parent = invisleaderstats	local PlayerFindable = Instance.new("BoolValue")	PlayerFindable.Name = "PlayerFindable"	PlayerFindable.Value = false	PlayerFindable.Parent = invisleaderstats	local notif = Instance.new("StringValue")	notif.Name = "NotifLocal"	notif.Value = "BR"	notif.Parent = invisleaderstats	local data = nil	local success, errormessage = pcall(function()	data = SaveToDS:GetAsync(player.UserId.." | ILSdataVAL")	if data ~= nil then	print(data)	else	print("No data there welp!")	end	end)	if success then	for i = 1, #ValsToSaveAndRetrieve do	if typeof(data) == 'table' then	if data[ValsToSaveAndRetrieve[i]] ~= nil then	player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]).Value = data[ValsToSaveAndRetrieve[i]]	print("ILS - "..player.Name.." ("..player.DisplayName..", "..player.UserId.."): The data of "..ValsToSaveAndRetrieve[i].." was retrieved!")	else	warn("ILS - "..player.Name.." ("..player.DisplayName..", "..player.UserId.."): The data of "..ValsToSaveAndRetrieve[i].." was not retrieved!")	if player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("IntValue") then	player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]).Value = 0	elseif player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("StringValue") then	player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]).Value = ""	elseif player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("BoolValue") then	player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]).Value = false	end	end	else	warn("ILS - "..player.Name.." ("..player.DisplayName..", "..player.UserId.."): The data of "..ValsToSaveAndRetrieve[i].." was not retrieved!")	if player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("IntValue") then	player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]).Value = 0	elseif player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("StringValue") then	player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]).Value = ""	elseif player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("BoolValue") then	player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]).Value = false	end	end	end	else	print("ILS - "..player.Name.." ("..player.DisplayName..", "..player.UserId.."): The data was not retrieved!")	warn("ILS - "..errormessage)	player:Kick("ILS - Player data failed to load or is corrupted. Please rejoin!")	end	for i = 1, #ValsToSaveAndRetrieve do	if player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("IntValue") then	local Newval = Instance.new("IntValue")	Newval.Parent = RS	Newval.Name = ValsToSaveAndRetrieve[i].."val | "..player.UserId	elseif player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("StringValue") then	local Newval = Instance.new("StringValue")	Newval.Parent = RS	Newval.Name = ValsToSaveAndRetrieve[i].."val | "..player.UserId	elseif player:WaitForChild("invisleaderstats"):WaitForChild(ValsToSaveAndRetrieve[i]):IsA("BoolValue") then	local Newval = Instance.new("BoolValue")	Newval.Parent = RS	Newval.Name = ValsToSaveAndRetrieve[i].."val | "..player.UserId	end	end end local function SaveData(player)	local SavedData = {}	for i = 1, #ValsToSaveAndRetrieve do	SavedData[ValsToSaveAndRetrieve[i]] = RS:WaitForChild(ValsToSaveAndRetrieve[i].."val | "..player.UserId).Value	print(ValsToSaveAndRetrieve[i].. " = ".. tostring(RS:WaitForChild(ValsToSaveAndRetrieve[i].."val | "..player.UserId).Value))	end	print(SavedData)	local success, errormessage = pcall(function()	SaveToDS:SetAsync(player.UserId.." | ILSdataVAL", SavedData)	end)	if success then	local RSChildren = RS:GetChildren()	print("ILS - "..player.Name.." ("..player.DisplayName..", "..player.UserId..") has saved their data!")	for i = 1, #RSChildren do	if string.find(RSChildren[i].Name, player.UserId) then	RSChildren[i]:Destroy()	print("killed "..RSChildren[i].Name)	end	end	else	print("ILS - There was nothing saved!")	warn(errormessage)	end end game:GetService("Players").PlayerAdded:Connect(GetData) game:GetService("Players").PlayerRemoving:Connect(SaveData) game:BindToClose(function()	for _, v in pairs(game:GetService("Players"):GetPlayers()) do	SaveData(v)	end end) local event = game.ReplicatedStorage.Geodude event.OnServerEvent:Connect(function(plr)	GetData(plr) end) 

Thank you if you can help me learn to fix this, i dont know how to use datastore, and i dont want to make the creator sad by basically saying “Your datastore is bad, fix it”

3 Likes

Your implementation looks fine. There’s a couple of notes I want to mention.

  1. I’d avoid kicking the player unless it’s absolutely necessary. If your :GetAsync fails, you should implement a block of code to keep trying until it succeeds. While this is happening, have your player sit in a “loading” screen. Once the data is properly loaded and passes all the checks, you can complete the loading screen and let the player play.
local function tryGetUntilSuccess(player, saveKey)	local MAX_TRIES = 100	while player.Parent do	local success, value = pcall(function()	return game:GetService("DataStoreService"):GetDataStore("PlayerDataStore"):GetAsync(saveKey)	end)	if success then	return value	end	wait(2)	end end 
  1. Do the same thing as 1 for :SetAsync. You need to keep trying to save until the bind to close limit is reached or your save file is successful. In your code you try only once and give up with a warning message if it fails. The player’s data is valuable! They will not be happy after spending hours playing and losing progress!
    If you do a game where your player manually clicks to leave the session, like Deepwoken. You can have the server not teleport the player until the data saves successfully, similar to loading in.
  1. Data validation is important. What happens when you join a new server then return back to an older server? The data structure might’ve changed or it could become corrupted. You need to figure out how you want to deal with issues like this.
    Some games apply aggressive server version checking and kick players, while others simply shut down all old servers. Regardless, always assume the data is corrupted until you’ve checked.

  2. Datastore2 is great if you’re a beginner and just want to use it and not worry about things. If you’re doing your own custom datastore system, you need to be aware of the big thing that DataStore2 is trying to solve: **two servers trying to save your data at the same time. **
    This is an extremely rare scenario if you implement points #1 and #2 in a game with rapid user rejoins. I haven’t had reports of this ever happening in my games but it’s possible. However, at worst, it just rolls back your data to the last X seconds or so, where X is how long the roblox data servers have been down for. I don’t recommend it, but if you’re paranoid, you could switch to DataStore2 and implement everything I’ve mentioned here today or build your own system (see below). If you’re interested in more info about it. This is a distributed systems problem called transaction concurrency control.

2 Likes

I never used datastore2, i tried to, but idk how to code really at all. thats why i wanted to fix this, because a friend made it and its been set up to be used with the game. id happily use datastore2, but i could not firgure out how to use it at ALL. How would i properly implement the snippets you gave me to improve the current script? Also, i DO have a loading screen, the data loads during, and reload after. im not sure if that makes it more reliable. Thanks tho mans

1 Like

Replace your GetAsync section with this:

local data = nil local success, errormessage = false, nil while not success and player.Parent do	success, errormessage = pcall(function()	data = SaveToDS:GetAsync(player.UserId.." | ILSdataVAL")	if data ~= nil then	print(data)	else	print("No data there welp!")	end	end)	task.wait(1) end 

Replace your SetAsync section with this:

print(SavedData) local success, errormessage = false, nil while not success and player.Parent do	success, errormessage = pcall(function()	SaveToDS:SetAsync(player.UserId.." | ILSdataVAL", SavedData)	end)	task.wait(1) end 

Good luck!

1 Like

Wait, where exactly? sorry im not very smart lol… can you tell me where the thing i should be replacing is?
Sorry again

1 Like

OHH i figured it out, THANK YOU i hope this works better mans!
Also, is the whole thing where i load the data 2 times a waste of time, or is it making the script more reliable?

For specifics, the proccess is like this:

Player joins
Load Player Data
Loading screen finishes (Load data again)

Don’t load the data 2 times, 1 is enough. Just load it the first time and remove the 2nd.

Do:
Player joins
Load Player Data
Loading screen finishes

1 Like

Prolly wont respond, but im working on adding mesages in the loading screen. decided to add you for your help mans

1 Like