How do we fix this specific memory loss? (script data)

Hello, I have composed a datastore script recently who work, except that I see a problem. When the player enters the game, if the loading of his data is not a success then he will be kicked, the problem is that if the player is kicked from the game and he comes back then all his old memory will be erased. Is there a way to create something that would allow the player not to lose all his data in which case there is a problem loading his data when he enters the game? My data is composed of a single file in which new boolvalue (with different names) are added when the player find an object → Player find object “coolcool” then player.object.coolcool (coolcool is a boolvalue created). Thank you for helping me !! I only put the problematic part of the script, but I can put all the script (if it can help you to help me maybe). Thank you very much hoping someone will know how to fix this

if success and data then
			local newval = Instance.new("BoolValue")
			newval.Name = cName
			newval.Parent = folder
			print("dataok")
		else
			print("joueursansdata")
			wait(5)
			if success and data then
				local newval = Instance.new("BoolValue")
				newval.Name = cName
				newval.Parent = folder
				print("dataok")
			else	
				player:Kick("Your data failed to load ! We are sorry, Please rejoin")

			end	
		end
	end 

I understand what the problem is but I don’t know how to fix it yet. If the player arrives in the game and his data does not load then he will have no data, but when the player will be kicked, the playerremoving will take effect and the new data (which is thus virgin) will be saved and thus the player will lose his old data. If someone can help me to correct this

I don’t know how good my method is but whenever a player joins I make sure that his data is actually loaded by adding the player to the “save” table if :GetAsync() successfully returned the data.

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

local saveXP = DataStoreService:GetDataStore("XP") -- We're gonna use XP for this example
local isLoaded = {} -- The "save" table that I mentioned
local data = {}

Players.PlayerAdded:Connect(function(player)
	local success, response = pcall(function()
		return saveXP:GetAsync(player.UserId)
	end)

	if success then
		data[player] = response
		isLoaded[player] = true -- Adding the player to the "save" table
	else
		player:Kick("Error: "..response)
	end
end)

Players.PlayerRemoving:Connect(function(player)
	if isLoaded[player] then -- Checks if player has actual data
		isLoaded[player] = nil -- This could also be used to avoid double saving in case of shutdown
		saveXP:SetAsync(player.UserId, data[player])
	end
end)

I think it can be written more efficiently but you get the general idea.

2 Likes

Thank you so much for taking the time to try to help me, right now I’m eating but afterwards I’ll try to put your advice into practice and I’ll let you know if I’ve managed to get through it. Thank you again

so you need to keep track of players that have loaded and if a player never loaded then when they are removed you don’t save here is some demo code

local playerLoaded = {}

game.Players.PlayerAdded:Connect(function(player)
    -- when the player enters the game try to load there data
    local success, value = pcall(dataStore.GetAsync, dataStore, player.UserId)

    -- if the datastore fails to load kick the player and return out of the function early
    if success == false then
        player:Kick("Your data failed to load ! We are sorry, Please rejoin")
        return
    end
   
    -- if the value = nil then set value to a empty table
    value = value or {}

    -- clone the data folder inside this script with all its values
    local folder = script.Data:Clone()

    -- loop all values and set them to match what was last saved in the datastore
    for i, child in ipairs(folder) do child.Value = value[child.Name] or child.Value end

    -- place the folder into the player
    folder.Parent = player

    -- set the playerloaded to true so we can keep track if there are players loaded so that bind to close can wait
    playerLoaded[player] = true
end)

game.Players.PlayerRemoving:Connect(function(player)
    -- if player loaded == nil meaning the player failed to load when they entered we simple exit because we dont want to overwrite the old data
    if playerLoaded[player] == nil then return end
 
    -- create a table and set the the values from there data folder
    local playerData = {}
    for i, child in ipairs(player.Data:GetChildren()) do
        playerData[child.Name] = child.Value
    end

    -- now save this table to the datastore
    local success, value = pcall(dataStore.SetAsync, dataStore, player.UserId, playerData)

    -- now set playerLoaded to nil so that bindtoclose can stop looping and allow the server to close
    playerLoaded[player] = nil
end)

-- while players are loaded dont close the server (only works upto 30 seconds)
game:BindToClose(function()
    while next(playerLoaded) ~= nil do
        task.wait()
    end
end)
1 Like

Thank you very much for taking the time to help me. Honestly you have a much deeper level of knowledge than I do, I’m still trying to figure out how to adapt your script to mine and will take the opportunity to post my entire script in case you come by again. Thank you again, same for @scriptjerk ```

local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("Creatures1")

local function saveData(player)
	local cTable = {}

	for _, creature in pairs(player:WaitForChild("Creatures"):GetChildren()) do
		table.insert(cTable,creature.Name)
	end

	local success, errorMsg = pcall(function()
		DataStore:SetAsync(player.UserId, cTable)
	end)
end
game.Players.PlayerAdded:Connect(function(player)
	local folder = Instance.new("Folder")
	folder.Name = "Creatures"
	folder.Parent = player
	local value = Instance.new("BoolValue")
	value.Name = "playerok"
	value.Parent = player
	local data
	local success, errorMsg = pcall(function()
		data = DataStore:GetAsync(player.UserId)
	end)

	if success and data then
		for _, cName in pairs(data) do
			if workspace.Creatures:FindFirstChild(cName) then 
				local newval = Instance.new("BoolValue")
				newval.Name = cName
				newval.Parent = folder
			else
				wait(5)
				player:Kick("Your data failed to load ! We are sorry, Please rejoin")

			end	
		end
	end 


	local Bool = true
	game.ReplicatedStorage.Nametag.OnServerEvent:Connect(function(plr)
		local success, errorMsg = pcall(function()
			if Bool then
				Bool = false
				saveData(player)
				wait(6)
				Bool = true		
			else
				wait(5)
				saveData(player)
				Bool = true		
			end
		end)
	end)
end)

game.Players.PlayerRemoving:Connect(function(player)
	local success, errorMsg = pcall(function()
		saveData(player)
	end)
end)


game:BindToClose(function()
	for _, player in pairs(game.Players:GetPlayers()) do
		local success, errorMsg = pcall(function()
			saveData(player)
		end)
	end
end)

Do you think this should be work ? I don’t know how can i test a success fail with this

game.Players.PlayerAdded:Connect(function(player)
	local folder = Instance.new("Folder")
	folder.Name = "Creatures"
	folder.Parent = player
	local value = Instance.new("BoolValue")
	value.Name = "playerok"
	value.Parent = player
	local data
	local success, value = pcall(function()
		data = DataStore:GetAsync(player.UserId)
	end)
	if success == false then
		player:Kick("Your data failed to load ! We are sorry, Please rejoin")
		return
	end
	if success and data then
		for _, cName in pairs(data) do
			if workspace.Creatures:FindFirstChild(cName) then 
				local newval = Instance.new("BoolValue")
				newval.Name = cName
				newval.Parent = folder

			end	
		end
	end 

Hmm, already noticed issues in your code here.

You should separate data from the if statement, since new player data will always be nil. Here’s the following code that I wrote, which I hope will explain every detail better

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

local saveCreatures = DataStoreService:GetDataStore("Creatures1")
local isLoaded = {}

local function onLoad(player)
	local data
	local success = pcall(function()
		data = saveCreatures:GetAsync(player.UserId)
	end)

	-- By separating the data from the if statement, we can now check whether the
	-- player is a newbie or not, otherwise it would assume that newbie is a
	-- failed load
	if success then
		-- Checking if player has any data, if not - assign newbie data
		if data == nil then
			data = {}
		end

		-- Create the folder and values here, cause there's no point in creating those
		-- earlier in case the data is invalid
		local creatures = Instance.new("Folder")
		creatures.Name = "Creatures"
		creatures.Parent = player

		local value = Instance.new("BoolValue")
		value.Name = "playerok"
		value.Parent = player

		for _, creature in pairs(data) do
			if workspace.Creatures:FindFirstChild(creature) then
				local bool = Instance.new("BoolValue")
				bool.Name = creature
				bool.Parent = creatures
			end
		end

		-- Remember this? We're basically telling the script later that data
		-- for this player is not invalid and is good to save
		isLoaded[player] = true
	else
		player:Kick("Your data failed to load ! We are sorry, Please rejoin")
		-- Notice how in case of error the player isn't added to the table:
		-- because we weren't able to retrieve data, which makes it invalid
	end
end

local function onSave(player)
	-- Checking if the player is on this table, if it is, then continue saving,
	-- otherwise assume that the player data is in process of saving or invalid
	if isLoaded[player] then
		isLoaded[player] = nil

		local creatures = player.Creatures
		local data = {}

		for _, creature in pairs(creatures:GetChildren()) do
			table.insert(data, creature.Name)
		end

		local success, response = pcall(function()
			saveCreatures:SetAsync(player.UserId, data)
		end)

		if not success then
			warn("DataStore not working, advise against leaving (Error: "..response..")")
		end
	end
end

-- Connecting load/save functions
Players.PlayerAdded:Connect(onLoad)
Players.PlayerRemoving:Connect(onSave)

-- In case of a shutdown
game:BindToClose(function()
	for _, player in pairs(Players:GetPlayers()) do
		onSave(player)
	end
end)

This code of course doesn’t contain - what I presume - an AutoSave function, which you’ll have to write it in yourself. I’m not really good at commenting, but I hope that this post will be enough for you to understand the idea behind each task. Any improvements upon this are welcome.

2 Likes

I’m trying it right now, and my autosave is the server event, it’s better than saving the 60 seconds for my game. Thanks a lot, I try and I tell you. Thank you

Sorry, I just noticed an error - I edited my post. Apply the changes and test again.

it’s strange because in the first part it erased my data but now with a new data the backup works again, I think it’s related to the script change

Yes, there were 3 little mistakes (one forgot ) , one ) too many behind the function, and creatues instead of creatures)

Is it possible with this script to voluntarily cause a data loading error?

If you mean causing a voluntary error to test if kicking works, then yeah - just add a letter to :GetAsync():

data = saveCreatures:GetAsync(player.UserId)

to

data = saveCreatures:GetAsynccc(player.UserId) -- Added extra 2 c's at the end

Don’t forget to change it back though :wink:

1 Like

There is something really strange, the backup works only sometimes, but not every time, one time out of 3 there is no backup

So the save data after a player is kicked works well (thank you very much), however there is still the concern of, sometimes when I leave, the “hi” is not displayed (which represents the save when the player leaves that you have put), and when I rejoin it puts me my old data (without erasing everything either)

local function onSave(player, autoSave)
	-- Checking if the player is on this table, if it is, then continue saving,
	-- otherwise assume that the player data is in process of saving or invalid
	if isLoaded[player] then
		isLoaded[player] = nil

		local creatures = player.Creatures
		local data = {}

		for _, creature in pairs(creatures:GetChildren()) do
			table.insert(data, creature.Name)
		end

		local success, response = pcall(function()
			saveCreatures:SetAsync(player.UserId, data)
		end)
		if success then 
			print("DataSave")
		end
		if not success then
			warn("DataStore not working, advise against leaving (Error: "..response..")")
		end
	end
end

here the print (“DataSave”) work only sometimes, really strange?

Error is for sur in link with theses 2 lines

if isLoaded[player] then
		isLoaded[player] = nil

What does the output say?

twenty characters

I made a video but unfortunately roblox does not want to upload it even though it is only 30 seconds long. In the video we can see me getting 1 “object”, so in Creatures folder there is a value, then I stop the game, nothing is displayed in the output, I join the game and my item has disappeared from the creature file