Strange errors when getting and setting data stores

So I’ve been trying to make a custom inventory system to advance my knowledge of Roblox scripting and data stores and I feel like I’m done with all the necessary code but when I run it either doesn’t save sometimes or sometimes the data I try to get always returns null.

For some reason, it keeps changing whenever I change something (I’m not sure what because I don’t see anything wrong with it) I might just be missing something though.

local DataStoreService = game:GetService("DataStoreService")
local InventoryDatastore = DataStoreService:GetDataStore("InvStore")
local UIS = game:GetService("UserInputService")
local pickupItemEvent = game.ReplicatedStorage.PickupItem



local saveData = game.ReplicatedStorage:WaitForChild("SaveData")

local Players = game:GetService("Players")


Players.PlayerAdded:Connect(function(player)
	--Adds player data folder
	local playerData = Instance.new("Folder", game.ServerStorage.Inventory)
	playerData.Name = player.Name
	
	local success, err = pcall(function()
		--Get the player's data store and clone the pickable partsR objects to the players inventory from their names
		local invItemsSave = InventoryDatastore:GetAsync(player.Name)
		print(invItemsSave)
		if invItemsSave ~= nil then
			for i, v in pairs(invItemsSave) do
				print(">>>Debug: Got data from store")
				local invItem = game.ReplicatedStorage.PickablePartsR:FindFirstChild(v)
				if invItem then
					invItem:Clone().Parent  = game.ServerStorage.Inventory:WaitForChild(player.Name)
				end
			end	
		end
	end)
	if success then
		print("Data successfully got")
	else
		print("Error getting data!")
		warn(err)
	end
end)


--When called, destroys the item and adds it to the players inventory folder
local function pickupItem(player, item)
	--Add the item to the Player inventory folder
	item:Destroy()
	local invItem = item:Clone()
	invItem.Parent = game.ServerStorage.Inventory:WaitForChild(player.Name)
	--Fires a mudulescript that updates the UI
end
pickupItemEvent.OnServerEvent:Connect(pickupItem)


game.Players.PlayerRemoving:Connect(function(player)
	local success, err = pcall(function()
		--Create a table with all the current inventory object names
		local invSave = {}
		--Add that table to the local players data store: InventoryDataStore:SetAsync(Player.Name, "the table with all the inventory objects in text form")
		for i, v in pairs(game.ServerStorage.Inventory:WaitForChild(player.Name):GetChildren()) do
			if v then
				table.insert(invSave, v.Name)
			end
		end
		InventoryDatastore:SetAsync(player.Name, invSave)
		print(">>>Debug: "..invSave)
	end)
	if success then
		print("Data successfully saved")
	else
		print("Error saving data!")
		warn(err)
	end
end)

First problem: This is the one that happened first. Whenever I join the game for the first time it does nothing as (if invItemsSave ~= nil). So when I pick up some items, leave the game and join back the “invItemsSave” is still empty(so it never goes past if invItemsSave ~= nil) even though it showed that the saving was successful with the pcall() and even the invSave table in the player removing event had the names of the items that were picked up.

Second problem: I messed around with it trying to fix it but now with the code attached it does not even print “data successfully saved” in the player leave pcall. I think that this might be because the game might have already quit and it’s not printing (i think it had shown earlier though) and if that’s the case then I guess the first problem is the only one

I don’t know what I am doing wrong I’ve checked other posts with the same problem and they all have different solutions such as typing the wrong key in the GetAsync or something. Also if there’s anything else that could be neater/faster/optimized then lmk too!

Thank You, this is my first forum post so if something is formatted wrong ill try to fix it.

Heya I can give you an instant fix probably if you tell me, did you test this out in studio?

Yes I did, you’re probably gonna tell me to try it out in the real game xD so if that works ill let you know thanks(cant believe i didn’t think of that)

I can also give you a solution of how youll make it work in studio if that was your fix, so just lemme know if that was your problem!

In your case it is probably not saving as the game is shutting down before the PlayerRemoving function has a chance to fire. To fix this try adding this at the bottom of you data script. This will cause the game to wait a few seconds before shutting down to avoid missed saving.

game:BindToClose(function()
   wait(3)
end
1 Like

I’ll also write it down rn cause you’ll need it if you wanna avoid data loss, game:BindToClose, check if the runservice is studio and then wait 3 seconds in there. And don’t forget to save your data with a coroutine in there for every player! If you need examples I can give you some. Basically what BindToClose does is to keep the server open for up to 30 secs iirc. In this time you’ll have enough time to save everything until the server shuts down!

1 Like

But does the wait actually yield and stop the game?

The 30 seconds is just a MAXIUMUM value. It means if the function you have in the BindToClose takes over 30 seconds then it will shutdown regardless however if it is under 30 seconds will finish the function and then shut down even if the full 30 seconds have not passed.

It does not yield the script, all it does is give the game time to run the functions it needs to before shutting down (What is inside the function).

Yup, thanks. Thats why I said it keeps it UP to 30 secs open

I don’t think that works? Plus the game doesn’t immediately shutdown anyways.

No problem, and ya just wanted to make sure the OP understood.

Oh it does, often datastores don’t get enough time to save it especially in studio that’s why a bind to close is so important when dealing with datastores!

Should have checked if his API was not disabled first.

Once all players have left the game will in fact shut down “immediately” in terms of the scripts within it. Obviously it takes a few miliseconds-seconds to run all of Roblox’s own code to shut down the instance, remove it from listing, ect but all the in game code stops functioning the moment the last player in the server leaves.

The game:BindToClose() function however gives the server that extra 30 seconds maximum to finish what it was doing such as saving player data, ect.

Thank you for all the replies, It worked!
I just needed to give the server a few more seconds to save using the BindToClose.
Thanks!

1 Like

Im sure i heard something about datastores being saved in studio or something?

Alright glad we could help! be sure to ask if youve got anymore questions!