I need help with a dataloss issue

Hello all, i’ve been having a issue with data being wiped/reset with no error. Here is the script;

--// Variables
local Datastore = game:GetService("DataStoreService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local Players = game:GetService("Players")

local Player_Data = ReplicatedStorage:WaitForChild("PlayerData")
local Events = ReplicatedStorage:WaitForChild("Events")

local NotificationEvent = Events:WaitForChild("Notification")

local PlayerDatastore = Datastore:GetDataStore("PlayerData")

local ServerModules = ServerScriptService:WaitForChild("ServerModules")

local IssueTracking = require(ServerModules:WaitForChild("Issue Tracking"))

local module = {}

function CreateInventoryData(Inventory) -- Create a table for the "Inventory"
	local InventoryTable = {
			PremiumBlocks = {},
			Weapons = {},
	}
	
	local PremiumBlocks = Inventory:WaitForChild("PremiumBlocks")
	local Weapons = Inventory:WaitForChild("Weapons")
	if PremiumBlocks ~= nil and Weapons ~= nil then 
		-- Create PremiumBlocks Table
		for _,Block in pairs(PremiumBlocks:GetChildren()) do
			if Block:IsA("IntValue") then
				InventoryTable.PremiumBlocks[Block.Name] = Block.Value
			end
			wait()
		end
		-- Create Weapons Table
		for _,Weapon in pairs(Weapons:GetChildren()) do
			if Weapon:IsA("IntValue") then
				InventoryTable["Weapons"][Weapon.Name] = Weapon.Value
			end
			wait()
		end
	end
	return InventoryTable
end

function CreateSlotsData(Slots) -- Get all slots and put them into a table
	local Data = {}
	for i,v in pairs(Slots:GetChildren()) do
		Data[v.Name] = v.Value
	end
	return Data
end

function CreateDataTable(DataFolder) -- Create a table for the Data Folder
	local DataTable = {}
	for _,instance in pairs(DataFolder:GetChildren()) do 
		if instance.ClassName ~= "Folder" then
			DataTable[instance.Name] = instance.Value
			wait()
		end
	end -- created the basic data
	DataTable.Inventory = CreateInventoryData(DataFolder.Inventory)
	DataTable.Slots = CreateSlotsData(DataFolder.Slots)
	return DataTable
end

function module:Save(Player)
	local Data = Player_Data:FindFirstChild(tostring(Player.UserId))
	if Data == nil then 
		return print("Unable to find "..Player.Name.."'s data folder")
	end
	local Key = "Player_"..Player.UserId
	local GeneratedData = CreateDataTable(Data)
	local success,err = pcall(function()
		return PlayerDatastore:SetAsync(Key, GeneratedData)
	end)
	if success then
		NotificationEvent:FireClient(Player,{"Your Data Has Been Saved!",5})
	else
		NotificationEvent:FireClient(Player, {"Error Saving Your Data!",5})
		IssueTracking:AddIssue("Error", "Error Saving data for "..Player.Name.." Error:" ..err)
	end
end

function module:Load(Player)
	local Data = Player_Data:FindFirstChild(tostring(Player.UserId))
	if Data == nil then 
		return print("Unable to find "..Player.Name.."'s data folder")
	end
	local Key = "Player_"..Player.UserId
	
	local Inventory = Data:WaitForChild("Inventory")
	local PremBlockFolder = Inventory:WaitForChild("PremiumBlocks")
	local WeaponsFolder = Inventory:WaitForChild("Weapons")
	
	local SlotsFolder = Data:WaitForChild("Slots")
	
	local GetData
	
	local success,err = pcall(function()
		GetData = PlayerDatastore:GetAsync(Key)
	end)
	
	if success then 
		-- actual loading
		if GetData == nil then return end -- Don't want to load from nil
		for i, v in pairs(GetData) do
			if i == "Inventory" then 
				for BlockName, BlockID in pairs(v.PremiumBlocks) do -- Load Premium Blocks
					if PremBlockFolder:FindFirstChild(BlockName) then 
					else
					local NewInstance = Instance.new("IntValue")
						NewInstance.Name = BlockName
						NewInstance.Value = BlockID
						NewInstance.Parent = PremBlockFolder
					end
					wait()
				end
				for WeaponName, WeaponID in pairs(v.Weapons) do -- Load Weapons
					if WeaponsFolder:FindFirstChild(WeaponName) then 
					else
					local NewInstance = Instance.new("IntValue")
						NewInstance.Name = WeaponName
						NewInstance.Value = WeaponID
						NewInstance.Parent = WeaponsFolder
					end
					wait()
				end
			elseif i == "Slots" then -- Loading slots
				for SlotName, SlotValue in pairs(v) do
					if SlotsFolder:FindFirstChild(SlotName) then
						SlotsFolder[SlotName].Value = SlotValue
					else
						local NewInstance = Instance.new("StringValue")
						NewInstance.Name = SlotName
						NewInstance.Value = SlotValue
						NewInstance.Parent = SlotsFolder
					end
				end
			else
				if Data:FindFirstChild(i) ~= nil then 
					Data[i].Value = v
				end
			end
		end
		NotificationEvent:FireClient(Player,{"Your Data Has Been Loaded!",5})
		--print("Loaded Data for "..Player.Name.."["..Player.UserId.."]")
	else
		print(err)
	end
end

return module

Some Information about the data itself;
The data is stored in a folder in ReplicatedStorage and inside another folder named after the player’s userid.
Saving is done when a player leaves + autosave on a 600 second interval.

2 Likes

Just as a quick tip, if you are expecting there to already be data in a player’s key, it is best practice to use UpdateAsync not SetAsync, because the latter wipes any exhisting data before overwriting, which is risky at best.

Just a little quick change, probably won’t solve the whole issue though.

1 Like

Gotcha, i don’t really understand UpdateAsync even with the wiki, so i haven’t been using it. Same with the Datastore2. Guess this is a good time to learn :smiley:

UpdateAsync is actually fairly simple. It takes a function to update instead of a value like SetAsync. You just have to return the new value to be put in the DataStore. The function also cannot yield. It passes the old value as an argument.

DataStore:UpdateAsync("some_key", function(oldValue)
    local newValue = something
    return newValue -- Required to return a value
end)
1 Like