I have to rejoin a lot to get my data to save!

So I’m making a One Piece game, and I created a system for saving data/loading tools. Right now I’m working on the Stats/Attributes part of the game using the new feature Roblox provides.

So here’s my CoreScript which is a ServerScript in ServerScriptService:

local PlayerDataModule = require(script[".DataService"])
local Http = game:GetService("HttpService")
local Utils = game.ServerStorage.Utils
local Tools = Utils.Tools

local PlayerAttributes = {
	Race = "None";
	Job = "None";
	Style = "None";
	Skin = "None";
	Fruit = "None";
	Banned = false;
	InCombat = false;
	Marine = false;
	Revolutionary = false;
	Crew = 0;
	Bounty = 0;
	Beli = 0;
	SwordExp = 0;
	FistExp = 0;
}

game.Players.PlayerAdded:Connect(function(Player)
	local PlayerInventory, PlayerStats
	if PlayerDataModule:GetData(Player) then
		local PlayersData = PlayerDataModule:GetData(Player)
		if PlayersData.Inventory then
			PlayerInventory = Http:JSONDecode(PlayersData.Inventory)
		end
		if PlayersData.Stats then
			PlayerStats = Http:JSONDecode(PlayersData.Stats)
		else
			PlayerStats = PlayerAttributes
		end
		for AttributeName, AttributeValue in pairs(PlayerStats) do
			print(AttributeName, AttributeValue)
			Player:SetAttribute(AttributeName, AttributeValue)
		end
	end
	Player.CharacterAdded:Connect(function(Character)
		if PlayerInventory then
			for _,ToolName in pairs(PlayerInventory) do
				local PlayerTool = Tools:FindFirstChild(ToolName)
				if PlayerTool then
					PlayerTool:Clone().Parent = Player.Backpack
				end
			end
		end
	end)
end)
game.Players.PlayerRemoving:Connect(function(Player)
	local UserId = Player.UserId
	PlayerDataModule:SaveToDataStore(UserId)
end)
PlayerDataModule:SavePlayerData()

This script runs fine, up until it tries to get the player’s Data. It always returns nil, and creates new Attributes for the player instead of loading the saved ones.

Here is the ModuleScript I created for tweaking Data:

local module = {}

local Data = game:GetService("DataStoreService")
local Http = game:GetService("HttpService")
local DataKey = Data:GetDataStore("Nami")

local Queue = {}

local PlayerAttributes = {
	"Race";
	"Job";
	"Style";
	"Skin";
	"Fruit";
	"Banned";
	"InCombat";
	"Marine";
	"Revolutionary";
	"Crew";
	"Bounty";
	"Beli";
	"SwordExp";
	"FistExp";
}

local ItemsToSave = {
	Cutlass = true;
	Katana = true;
	Jitte = true;
	Geppo = true;
	Flintlock = true;
}

function AddToQueue(UserId)
	local Player = game.Players:GetPlayerByUserId(UserId)
	if Player then
		Queue[UserId] = {
		}
		Queue[UserId].Stats = {}
		Queue[UserId].Inventory = {}
		local Inv = Queue[UserId].Inventory
		for _ , AttributeName in pairs(PlayerAttributes) do
			Queue[UserId].Stats[AttributeName] = Player:GetAttribute(AttributeName)
		end
		for _ , Item in pairs(Player.Backpack:GetChildren()) do
			Inv[#Inv+1] = tostring(Item)
		end
		if Player.Character and Player.Character:FindFirstChildWhichIsA("Tool") then
			local Item = Player.Character:FindFirstChildWhichIsA("Tool")
			Inv[#Inv+1] = tostring(Item)
		end
	end
end

function module:GetData(Player)
	local UserId = Player.UserId
	local PlrData = {}
	if DataKey:GetAsync(UserId .. "- Inventory") then
		PlrData.Inventory = DataKey:GetAsync(UserId .. "- Inventory")
		print(PlrData.Inventory)
	end
	if DataKey:GetAsync(UserId .. "- Stats") then
		print("Player's Stats have been acquired!")
		PlrData.Stats = DataKey:GetAsync(UserId .. "- Stats")
		print(PlrData.Stats)
	end
	return PlrData
end

function module:SaveToDataStore(UserId)
	if Queue[UserId] then
		print("Saving Player's Data ...")
		local Inventory = Http:JSONEncode(Queue[UserId].Inventory)
		local Stats = Http:JSONEncode(Queue[UserId].Stats)
		DataKey:SetAsync(UserId .. "- Inventory", Inventory)
		DataKey:SetAsync(UserId .. "- Stats", Stats)
		print(DataKey:GetAsync(UserId .. "- Stats"))
	end
end

function module:SavePlayerData()
	spawn(function()
		while wait(1) do
			for _,Player in pairs(game.Players:GetPlayers()) do
				local Success, Fail = pcall(function()
					AddToQueue(Player.userId)
				end)
				if Fail then
					warn(Fail)
				end
			end
			for UserId,_ in pairs(Queue) do
				local Success, Fail = pcall(function()
					local Player = game.Players:GetPlayerByUserId(UserId)
					if Player then
					else
						Queue[UserId]=nil
					end
				end)
				if Fail then
					warn(Fail)
				end
			end
		end
	end)
end

return module

So the part that is confusing for me is that when I print ‘Saving Player’s Data’, it prints perfectly. When I try to print the data of what I just saved, it doesn’t print anything at all not even nil.

But THE MAIN PROBLEM is that the script does start saving player data, after the player has joined SEVERAL TIMES. Does anyone know why that is?

When the player leaves the game, their character gets destroyed. You shouldn’t get data from the player when they are about to leave. This will result in data loss because the character may exist sometimes or not. The best way would be to cache the data continuously for the player. When the player leaves, just get their UserId and save the already cached data. This way you won’t need to access anything from with the player and can directly access the cached data to save.

1 Like

@GrayzcaIe gave the best answer to this but I want to give a little advice: you should really add :BindToClose to your script. If the game were to crash for some reason or if the player is removing in studio, sometimes the event doesn’t fire, causing that data loss. :BindToClose is just an extra safety measure I highly recommend taking.

1 Like