[Urgent] Data store data loss

Yo, my game has been experience data loss issues. Usually when the data loss occurs, they get rolled back to when they last ate a devil fruit (which is when the game autosaves). This can happen (but not exclusively) when the game shutsdown.

Heres the datastore script:

--|| Services ||--

local DatastoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local Marketplaceservice = game:GetService("MarketplaceService")

--|| Variables ||--

local Data = DatastoreService:GetDataStore("DataTest99")

--|| Modules ||--

local Rarity = require(script.Rarity)
local RNG = require(ServerScriptService.Modules.Misc.RNG)
local Moderation = require(ServerScriptService.Modules.Moderation:WaitForChild("Moderation"))
local Weebhookmodule = require(game.ServerScriptService.Modules.Misc:WaitForChild("Webhook"))

--|| Script ||--

function getSaveableColor3(color)
	return {r = color.r, g = color.g, b = color.b}
end

function loadColorFromDataStore(t)
	return Color3.new(t.r, t.g, t.b)
end

function AddFolder(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local Bounty = Instance.new("IntValue")
	Bounty.Name = "Bounty"
	Bounty.Parent = leaderstats
	
	local PlayerFolder = Instance.new("Folder")
	PlayerFolder.Name = "Stats"
	PlayerFolder.Parent = player
	
	local Banned = Instance.new("BoolValue")
	Banned.Name = "Banned"
	Banned.Value = false
	Banned.Parent = PlayerFolder
	
	local Party = Instance.new("Folder")
	Party.Name = "Party"
	Party.Parent = player
	
	local Settings = Instance.new("Folder")
	Settings.Name = "Settings"
	Settings.Parent = player
	
	local PartyInvites = Instance.new("BoolValue")
	PartyInvites.Value = true
	PartyInvites.Name = "PartyInvites"
	PartyInvites.Parent = Settings
	
	local MusicToggle = Instance.new("BoolValue")
	MusicToggle.Value = true
	MusicToggle.Name = "Music"
	MusicToggle.Parent = Settings
	
	local Shake = Instance.new("BoolValue")
	Shake.Value = true
	Shake.Name = "Shake"
	Shake.Parent = Settings
end

function DataLoad(player)
	local leaderstats = player:FindFirstChild("leaderstats")
	local PlayerData = player:FindFirstChild("Stats")
	local ID = player.UserId
	local data 
	local success, errormessage = pcall(function()
		data = Data:GetAsync(ID)
	end)
	
	if success then
		if data == nil then
			for i,v in pairs(script.Template:GetChildren())do
				local Clone = v:Clone()
				Clone.Parent = PlayerData
			end
			PlayerData.Race.Value = RNG(Rarity)
			PlayerData.BusoColor.Value = Color3.new(math.random(1,255)/255, math.random(1,255)/255, math.random(1,255)/255)
		else
			for i,v in pairs(script.Template:GetChildren()) do
				local Clone = v:Clone()
				Clone.Parent = PlayerData
				if data[Clone.Name] then
					if Clone:IsA("Color3Value") then
						Clone.Value = loadColorFromDataStore(data[Clone.Name])
					else
						Clone.Value = data[Clone.Name]
					end
				end
			end
			leaderstats.Bounty.Value = data.Bounty
			PlayerData.Banned.Value = data.Banned
		end
	else
		local DataFail = Instance.new("Folder")
		DataFail.Parent = player
		player:Kick("Data didn't load properly")
	end
end

function save(player)
	if player:FindFirstChild("DataFail") then return end
	if player:FindFirstChild("Saving") then return end
	local Saving = Instance.new("Folder")
	Saving.Name = "Saving"
	Saving.Parent = player
	game.Debris:AddItem(Saving, 7)
	local leaderstats = player:FindFirstChild("leaderstats")
	local PlayerData = player:FindFirstChild("Stats")
	local ID = player.UserId

	local data = {}
	for i,v in pairs(PlayerData:GetChildren()) do
		if v:IsA("Color3Value") then
			data[v.Name] = getSaveableColor3(v.Value)
		else
			data[v.Name] = v.Value
		end
	end

	data.Banned = PlayerData.Banned.Value

	data.Bounty = leaderstats.Bounty.Value
	
	local success, errormessage = pcall(function()
		Data:SetAsync(ID, data)
	end)

	if success then
		print("Saved "..player.Name.."s Data")
	else
		warn("Data Failure")
	end
end

game.Players.PlayerAdded:Connect(function(player)
	AddFolder(player)
	DataLoad(player)
	player.Stats:WaitForChild("Quests").Value = 0
	local PlayerData = player:WaitForChild("Stats")
	
	PlayerData.DevilFruit.Changed:Connect(function()
		save(player)
	end)	
end)

game.Players.PlayerRemoving:Connect(function(player)
	save(player)
end)

game:BindToClose(function()
	for i,v in pairs(game.Players:GetChildren()) do
		save(v)
	end
end)

Add a wait statement at the end of the BindToClose function. Make the wait statement wait about 5-10 seconds, depending on how much data is needed to save.

Ill try this and see if it solves the issue, thank you

Data loss still occurs after a shutdown. Heres the updated code:

--|| Services ||--

local DatastoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local Marketplaceservice = game:GetService("MarketplaceService")

--|| Variables ||--

local Data = DatastoreService:GetDataStore("DataTest99")

--|| Modules ||--

local Rarity = require(script.Rarity)
local RNG = require(ServerScriptService.Modules.Misc.RNG)
local Moderation = require(ServerScriptService.Modules.Moderation:WaitForChild("Moderation"))
local Weebhookmodule = require(game.ServerScriptService.Modules.Misc:WaitForChild("Webhook"))

--|| Script ||--

function getSaveableColor3(color)
	return {r = color.r, g = color.g, b = color.b}
end

function loadColorFromDataStore(t)
	return Color3.new(t.r, t.g, t.b)
end

function AddFolder(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local Bounty = Instance.new("IntValue")
	Bounty.Name = "Bounty"
	Bounty.Parent = leaderstats

	local PlayerFolder = Instance.new("Folder")
	PlayerFolder.Name = "StatsLoading"
	PlayerFolder.Parent = player

	local Banned = Instance.new("BoolValue")
	Banned.Name = "Banned"
	Banned.Value = false
	Banned.Parent = PlayerFolder

	local Party = Instance.new("Folder")
	Party.Name = "Party"
	Party.Parent = player

	local Settings = Instance.new("Folder")
	Settings.Name = "Settings"
	Settings.Parent = player

	local PartyInvites = Instance.new("BoolValue")
	PartyInvites.Value = true
	PartyInvites.Name = "PartyInvites"
	PartyInvites.Parent = Settings

	local MusicToggle = Instance.new("BoolValue")
	MusicToggle.Value = true
	MusicToggle.Name = "Music"
	MusicToggle.Parent = Settings

	local Shake = Instance.new("BoolValue")
	Shake.Value = true
	Shake.Name = "Shake"
	Shake.Parent = Settings
end

function DataLoad(player)
	local leaderstats = player:FindFirstChild("leaderstats")
	local PlayerData = player:FindFirstChild("StatsLoading")
	local ID = player.UserId
	local data 
	local success, errormessage = pcall(function()
		data = Data:GetAsync(ID)
	end)

	if success then
		if data == nil then
			for i,v in pairs(script.Template:GetChildren())do
				local Clone = v:Clone()
				Clone.Parent = PlayerData
			end
			PlayerData.Race.Value = RNG(Rarity)
			PlayerData.BusoColor.Value = Color3.new(math.random(1,255)/255, math.random(1,255)/255, math.random(1,255)/255)
		else
			for i,v in pairs(script.Template:GetChildren()) do
				local Clone = v:Clone()
				if data[Clone.Name] then
					if Clone:IsA("Color3Value") then
						Clone.Value = loadColorFromDataStore(data[Clone.Name])
					else
						Clone.Value = data[Clone.Name]
					end
				end
				Clone.Parent = PlayerData
			end
			leaderstats.Bounty.Value = data.Bounty
			PlayerData.Banned.Value = data.Banned
		end
	else
		local DataFail = Instance.new("Folder")
		DataFail.Parent = player
		player:Kick("Data didn't load properly")
	end
	PlayerData.Name = "Stats"
end

function save(player)
	if player:FindFirstChild("DataFail") then return end
	if player:FindFirstChild("Saving") then return end
	local Saving = Instance.new("Folder")
	Saving.Parent = player
	game.Debris:AddItem(Saving, 7)
	local leaderstats = player:FindFirstChild("leaderstats")
	local PlayerData = player:FindFirstChild("Stats")
	local ID = player.UserId

	local data = {}
	for i,v in pairs(PlayerData:GetChildren()) do
		if v:IsA("Color3Value") then
			data[v.Name] = getSaveableColor3(v.Value)
		else
			data[v.Name] = v.Value
		end
	end

	data.Banned = PlayerData.Banned.Value

	data.Bounty = leaderstats.Bounty.Value

	local success, errormessage = pcall(function()
		--Data:SetAsync(ID, data)
		
		if data then
			Data:UpdateAsync(ID, function(oldValue)
				local previousData = oldValue or {DataId = 0}
				if data.DataId == previousData.DataId then
					data.DataId = data.DataId + 1
					return data
				else
					return nil
				end
			end)
		end
	end)

	if success then
		print("Saved "..player.Name.."s Data")
	else
		warn("Data Failure")
	end
end

game.Players.PlayerAdded:Connect(function(player)
	AddFolder(player)
	DataLoad(player)
	player.Stats:WaitForChild("Quests").Value = 0
	local PlayerData = player:WaitForChild("Stats")

	PlayerData.DevilFruit.Changed:Connect(function()
		save(player)
	end)
end)

game.Players.PlayerRemoving:Connect(function(player)
	save(player)
end)

game:BindToClose(function()
	local TimeToWait = 10
	for i,v in pairs(game.Players:GetChildren()) do
		save(v)
		TimeToWait = TimeToWait + 1
	end
	if game["Run Service"]:IsStudio() then
	else
		task.wait(TimeToWait)
	end
end)

I can’t do anything about this except save while the player is still remaining in the game.

This is harder to figure out than the meaning of life.

change this:

game:BindToClose(function()
	for i,v in pairs(game.Players:GetChildren()) do
		save(v)
	end
end

to this:

game:BindToClose(function()
	if game:GetService("RunService"):IsStudio() then
		wait(2)
	else
		for _, player in pairs(game.Players:GetPlayers()) do
			local finished = Instance.new("BindableEvent")
			save(player) 
			finished.Event:Wait()
		end		
	end
end)