The data does not save

I wanna make that the boolvalues save for other places
but the datasave does not work correctly

Did I maybe do something wrong?

--// SERVICES
local DataStoreService = game:GetService("DataStoreService")

--// VARS

local ds = DataStoreService:GetDataStore("datatest")
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local RadiopassID = 663864800


local function checkGamePass(player, passID)
	local hasPass = false
	local success, message = pcall(function()
		hasPass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, passID)
	end)
	if not success then
		warn("Error while checking if player has game pass: " .. tostring(message))
		return false
	end
	return hasPass
end

game.Players.PlayerAdded:Connect(function(plr)
	print("executed")


	local FolderOwned = Instance.new("Folder",plr)
	FolderOwned.Name = "ItemsOwned"

	local RadioOwned = Instance.new("BoolValue",FolderOwned)
	RadioOwned.Name = "RadioOwned"
	----------------------------------------------------

	local FolderEquiped = Instance.new("Folder", plr)
	FolderEquiped.Name = "ItemsEquiped"

	local RadioEquiped = Instance.new("BoolValue",FolderEquiped)
	RadioEquiped.Name = "RadioEquiped"


	local dataofuser = ds:GetAsync(plr.UserId)
	if dataofuser ~= nil then -- anything but nil
		print("Found data for " .. plr.Name)
		RadioOwned.Value = ds[1] 
		--------------------------------
		RadioEquiped.Value = ds[1] 
	else
		print("Replacing no data with new data.")
	end

	if checkGamePass(plr, RadiopassID) then
		-- Player owns the game pass
		print("Player owns the game pass")
		RadioOwned.Value = true
	else
		-- Player does not own the game pass
		print("Player does not own the game pass")
		RadioOwned.Value = false
	end
end)

game.Players.PlayerRemoving:Connect(function(plr)
	local datasave = {}
	local folderitemsowned = plr:WaitForChild("ItemsOwned")
	local folderitemequiped = plr:WaitForChild("ItemsOwned")
	table.insert(datasave, folderitemsowned:WaitForChild("RadioOwned").Value)
	--------------------------
	table.insert(datasave, folderitemequiped:WaitForChild("RadioEquiped").Value)

	local success,response = pcall(function()
		ds:SetAsync(plr.UserId, datasave)
	end)

	if success then
		print("succesfully saved data of " .. plr.Name)
	else
		warn(response)
	end
end)

Before I read through all your code, I don’t see a BindToClose() function. If you already have it in another script, great! Basically, when a server shuts down, using this function you have 30 seconds to finish saving data.

game:BindToClose(function()
-- Save all player data
end)

It also appears that you’re saving the player data under the player themself. If this is the case, when a player leaves, that data is going to be destroyed. Because of this, I recommend saving player data on the server in a folder somewhere, under workspace or ServerStorage even. (Just be mindful, the player cannot see anything in ServerStorage).

image

When you successfully finish saving that player’s data, you can then destroy their folder.

In short, the data you are currently trying to save is being destroyed when the player leaves the game. Additionally, the server tends to shutdown faster than you can save data which is why you should use BindToClose().

I agree with @1Minecraft0954 that you should add game:BindToClose() to ensure that any data you need to save gets saved. Studio often behaves like it would in-game but not 100% of the time. Plus its best practice to remove any race conditions to prevent unintended behavior.

Anyways…

I found that ds[1] throws an error… I think you meant dataofuser[1]

local dataofuser = ds:GetAsync(plr.UserId)
if dataofuser ~= nil then -- anything but nil
	print("Found data for " .. plr.Name)
	RadioOwned.Value = ds[1]  -- This throws an error.
	RadioOwned.Value = dataofuser[1]  -- I believe this is what you intended.
	--------------------------------
	RadioEquiped.Value = ds[1] -- 
else
	print("Replacing no data with new data.")
end

After fixing this bug the code seems to work just fine for me. But I did make the following changes for testing so if you’re still having issues let us know and I’ll set up the environment correctly.

game.Players.PlayerRemoving:Connect(function(plr)
	print(plr.Name .. " is leaving!!")
	local datasave = {}
	--local folderitemsowned = plr:WaitForChild("ItemsOwned")	-- I commented this stuff out as I didn't feel like the error was here
	--local folderitemequiped = plr:WaitForChild("ItemsOwned")
	table.insert(datasave, checkGamePass(plr, RadiopassID)) -- I replaced this line for testing
	--------------------------
	--table.insert(datasave, folderitemequiped:WaitForChild("RadioEquiped").Value)
	print("Found all important data")
	local success,response = pcall(function()
		ds:SetAsync(plr.UserId, datasave)
	end)

	if success then
		print("succesfully saved data of " .. plr.Name)
	else
		print("failed to save data of " .. plr.Name)
		warn(response)
	end
	print("Done.") -- Lets me know the function ended! Very important for a function to do this...
end)

Again… I didn’t set up the environment to be identical to yours (as I’m lazy) so if you’re still having issues let us know and we will start there.

1 Like

The first property of SetAsync ALWAYS needs to be a key.
I normally do
DataStore:SetAsync(tostring(player.UserId), Data)

Just like @1Minecraft0954 said, you should use game:BindToClose() because the server might shut down before Players.PlayerRemoving fires, which can lead to saving issues. I recommend using the onShutdown function from GEILER123456’s DataStore tutorial.

local function onShutdown()
	if RunService:IsStudio() then
		task.wait(2)
	else
		local finished = Instance.new("BindableEvent")
		local allPlayers = Players:GetPlayers()
		local leftPlayers = #allPlayers

		for _,player in ipairs(allPlayers) do
			coroutine.wrap(function()
				save(player, true)
				leftPlayers -= 1
				if leftPlayers == 0 then
					finished:Fire()
				end
			end)()
		end

		finished.Event:Wait()
	end
end

game:BindToClose(onShutdown)

Just like what @tycolt3 said, you meant to use dataofuser instead of ds.

game.Players.PlayerAdded:Connect(function(plr)
	print("executed")


	local FolderOwned = Instance.new("Folder",plr)
	FolderOwned.Name = "ItemsOwned"

	local RadioOwned = Instance.new("BoolValue",FolderOwned)
	RadioOwned.Name = "RadioOwned"
	----------------------------------------------------

	local FolderEquiped = Instance.new("Folder", plr)
	FolderEquiped.Name = "ItemsEquiped"

	local RadioEquiped = Instance.new("BoolValue",FolderEquiped)
	RadioEquiped.Name = "RadioEquiped"


	local dataofuser = ds:GetAsync(plr.UserId)
	if dataofuser ~= nil then -- anything but nil
		print("Found data for " .. plr.Name)
		RadioOwned.Value = dataofuser[1] 
		--------------------------------
		RadioEquiped.Value = dataofuser[1] 
	else
		print("Replacing no data with new data.")
	end

	if checkGamePass(plr, RadiopassID) then
		-- Player owns the game pass
		print("Player owns the game pass")
		RadioOwned.Value = true
	else
		-- Player does not own the game pass
		print("Player does not own the game pass")
		RadioOwned.Value = false
	end
end)

Just like what @3F1VE said, the first parameter of DataStore:SetAsync() key should be a string, though the function automatically converts the UserId to a string. The same thing applies to DataStore:GetAsync()

game.Players.PlayerRemoving:Connect(function(plr)
	local datasave = {}
	local folderitemsowned = plr:WaitForChild("ItemsOwned")
	local folderitemequiped = plr:WaitForChild("ItemsOwned")
	table.insert(datasave, folderitemsowned:WaitForChild("RadioOwned").Value)
	--------------------------
	table.insert(datasave, folderitemequiped:WaitForChild("RadioEquiped").Value)

	local success,response = pcall(function()
		ds:SetAsync(tostring(plr.UserId), datasave)
	end)

	if success then
		print("succesfully saved data of " .. plr.Name)
	else
		warn(response)
	end
end)

Aside from the things mentioned above, I noticed some problems in your script.

I think you meant RadioEquipped instead of RadioEquiped.

In the dataofuser part, I think you meant to set RadioEquipped.Value to dataofuser[2].

In making the table for saving data, I recommend listing them instead of using table.insert().

local datasave = {
	folderitemsowned:WaitForChild("RadioOwned").Value,
	folderitemequiped:WaitForChild("RadioEquiped").Value
}

I don’t think it’s necessary to save RadioOwned because you’re already checking if the player has bought the gamepass.

I recommend making the functions connected to the events as variables instead so that it will be easier to read, and use it in other ways.

Speaking of the ‘other ways’ I mentioned above, you can make the function connected to Player.PlayerAdded as a variable named loadData and use it when iterating every player in the game once the server starts, just in case Player.PlayerAdded didn’t fire for the players that are already in the server.

for _, Player in ipairs(Players:GetPlayers()) do
	task.spawn(loadData, Player) -- similar to coroutine.wrap(loadData)(Player)
end

This also applies in the function connected to Player.PlayerRemoving which can be made as a variable and be used in the onShutdown() function mentioned above.

When creating instances, it’s recommended to parent it after setting its properties instead of using the second parameter of Instance.new() to parent it because when setting the properties of a parented instance, the events of the instance will fire, resulting in memory being used.

Lastly, I don’t think you should pcall() MarketplaceService#UserOwnsGamePassAsync() because it won’t error. this means that checkGamePass() function is not needed anymore and you should use MarketplaceService#UserOwnsGamePassAsync() directly in the function.


Summary

When applying everything above, the final code should look like this.

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")

local DataStore = DataStoreService:GetDataStore("datatest")
local RADIO_PASS_ID = 663864800

local function loadData(Player: Player) -- player joins
	local userId = Player.UserId
	
	local FolderOwned = Instance.new("Folder")
	FolderOwned.Name = "ItemsOwned"
	FolderOwned.Parent = Player
	
	local RadioOwned = Instance.new("BoolValue")
	RadioOwned.Name = "RadioOwned"
	RadioOwned.Parent = FolderOwned
	
	local FolderEquipped = Instance.new("Folder")
	FolderEquipped.Name = "ItemsEquipped"
	FolderEquipped.Parent = Player
	
	local RadioEquipped = Instance.new("BoolValue")
	RadioEquipped.Name = "RadioEquipped"
	RadioEquipped.Parent = FolderEquipped
	
	RadioOwned.Value = MarketplaceService:UserOwnsGamePassAsync(userId, RADIO_PASS_ID)
	
	local data = DataStore:GetAsync(tostring(userId))
	
	if data ~= nil then -- if the player has data
		RadioEquipped.Value = data[1]
	else -- if the player has no data
		RadioEquipped.Value = false
	end
end

local function saveData(Player: Player) -- player leaving
	local FolderEquipped = Player:FindFirstChild("ItemsEquipped")
	
	if FolderEquipped ~= nil then
		local RadioEquipped = FolderEquipped:FindFirstChild("RadioEquipped")
		
		if RadioEquipped ~= nil then
			pcall(DataStore.SetAsync, DataStore, tostring(Player.UserId), {RadioEquipped.Value})
		end
	end
end

local function onShutdown() -- server shut down or studio playtest close
	if RunService:IsStudio() == true then
		task.wait(2)
	else
		local finished = Instance.new("BindableEvent")
		local allPlayers = Players:GetPlayers()
		local leftPlayers = #allPlayers
		
		for _, Player in ipairs(allPlayers) do
			task.spawn(function()
				saveData(Player)
				leftPlayers -= 1
				
				if leftPlayers == 0 then
					finished:Fire()
				end
			end)
		end
		
		finished.Event:Wait()
	end
end

for _, Player in ipairs(Players:GetPlayers()) do
	task.spawn(loadData, Player)
end

Players.PlayerAdded:Connect(loadData) -- player joins
Players.PlayerRemoving:Connect(saveData) -- player leaving
game:BindToClose(onShutdown) -- server shut down or studio playtest close
4 Likes

well somehow it still does not work.
Thanks for rewriting a whole script for me!

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")

local DataStore = DataStoreService:GetDataStore("datatest")
local RADIO_PASS_ID = 663864800


local function checkGamePass(player, passID)
	local hasPass = false
	local success, message = pcall(function()
		hasPass = MarketplaceService:UserOwnsGamePassAsync(player.UserId, passID)
	end)
	if not success then
		warn("Error while checking if player has game pass: " .. tostring(message))
		return false
	end
	return hasPass
end

local function loadData(Player: Player) -- player joins
	local userId = Player.UserId

	local FolderOwned = Instance.new("Folder")
	FolderOwned.Name = "ItemsOwned"
	FolderOwned.Parent = Player

	local RadioOwned = Instance.new("BoolValue")
	RadioOwned.Name = "RadioOwned"
	RadioOwned.Parent = FolderOwned

	local FolderEquipped = Instance.new("Folder")
	FolderEquipped.Name = "ItemsEquipped"
	FolderEquipped.Parent = Player

	local RadioEquipped = Instance.new("BoolValue")
	RadioEquipped.Name = "RadioEquipped"
	RadioEquipped.Parent = FolderEquipped

	if checkGamePass(Player, RADIO_PASS_ID) then
		print("Player owns the game pass")
		RadioOwned.Value = true
	else
		-- Player owns the game pass
		print("Player owns the game pass")
		RadioOwned.Value = false
	end

	local data = DataStore:GetAsync(tostring(userId))

	if data ~= nil then -- if the player has data
		RadioEquipped.Value = data[1]
		RadioOwned.Value = data[1]
	else -- if the player has no data
		RadioEquipped.Value = false
		RadioOwned.Value = false
	end
end

local function saveData(Player: Player) -- player leaving
	local FolderEquipped = Player:FindFirstChild("ItemsEquipped")

	if FolderEquipped ~= nil then
		local RadioEquipped = FolderEquipped:FindFirstChild("RadioEquipped")

		if RadioEquipped ~= nil then
			pcall(DataStore.SetAsync, DataStore, tostring(Player.UserId), {FolderEquipped:FindFirstChild("RadioEquipped").Value})
		end
	end
	
	local FolderOwned = Player:FindFirstChild("ItemsEquipped")

	if FolderOwned ~= nil then
		local FolderOOwned = FolderOwned:FindFirstChild("RadioOwned")

		if FolderOOwned ~= nil then
			pcall(DataStore.SetAsync, DataStore, tostring(Player.UserId), {FolderOwned:FindFirstChild("RadioOwned").Value})
		end
	end
end

local function onShutdown() -- server shut down or studio playtest close
	if RunService:IsStudio() == true then
		task.wait(2)
	else
		local finished = Instance.new("BindableEvent")
		local allPlayers = Players:GetPlayers()
		local leftPlayers = #allPlayers

		for _, Player in ipairs(allPlayers) do
			task.spawn(function()
				saveData(Player)
				leftPlayers -= 1

				if leftPlayers == 0 then
					finished:Fire()
				end
			end)
		end

		finished.Event:Wait()
	end
end

for _, Player in ipairs(Players:GetPlayers()) do
	task.spawn(loadData, Player)
end

Players.PlayerAdded:Connect(loadData) -- player joins
Players.PlayerRemoving:Connect(saveData) -- player leaving
game:BindToClose(onShutdown) -- server shut down or studio playtest close

yea…

wait let me just look maybe I did something wrong or I read it wrong

Are you sure Enable Studio Access to API Services is enabled?

1 Like

yea still having problem

I tried it with a simpler version:


local DataStoreService = game:GetService("DataStoreService")
local MarketplaceService = game:GetService("MarketplaceService")
local myDataStore = DataStoreService:GetDataStore("PlayerLocker")
local RADIO_PASS_ID = 663864800

game.Players.PlayerAdded:Connect(function(Player)
	local userId = Player.UserId

	local FolderOwned = Instance.new("Folder")
	FolderOwned.Name = "ItemsOwned"
	FolderOwned.Parent = Player

	local RadioOwned = Instance.new("BoolValue")
	RadioOwned.Name = "RadioOwned"
	RadioOwned.Parent = FolderOwned

	local FolderEquipped = Instance.new("Folder")
	FolderEquipped.Name = "ItemsEquipped"
	FolderEquipped.Parent = Player

	local RadioEquipped = Instance.new("BoolValue")
	RadioEquipped.Name = "RadioEquipped"
	RadioEquipped.Parent = FolderEquipped
	
	RadioOwned.Value = MarketplaceService:UserOwnsGamePassAsync(userId, RADIO_PASS_ID)
	
	local data
	local success, errorMessage = pcall(function()
		data = myDataStore:GetAsync(userId.."_RadioEquipped")
	end)

	if success then
		RadioEquipped.Value = data or false
		print("Successfully loaded the data for "..Player.Name.." | UserID: "..userId)
	else
		print("There was an error loading the data for "..Player.Name.." | UserID: "..userId)
		warn(errorMessage)
	end
end)

game.Players.PlayerRemoving:Connect(function(Player)
	local userId = Player.UserId
	
	local success, errormessage = pcall(function()
		myDataStore:SetAsync(userId.."_RadioEquipped", Player.ItemsEquipped.RadioEquipped.Value)
	end)
	
	if success then
		print("Data successfully saved! for "..Player.Name.." | UserID: "..userId)
	else
		print("There was an error saving the data for "..Player.Name.." | UserID: "..userId)	
		warn(errormessage)
	end
end)

I still don’t know really how to implement the bindtoclose function

1 Like

yea it is for sure xd
Like idk why but it prints out that the values/value saved but then when I rejoin the data loaded succesfully but it didn’t

@T3_MasterGamer @tycolt3 @1Minecraft0954 @3F1VE

Here is the model with everything I tried to do
and the “locker” gui where you equip stuff

thedatasavingproblem.rbxm (104.5 KB)

I got it now after like 3 whole days of fixing and re-writing the code.
Thanks to all of you! for helping me @1Minecraft0954 @T3_MasterGamer @tycolt3 @3F1VE

2 Likes

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