Save table to DataStore on player leave

Hi. I’m not gonna explain every detail, but I have a system to save a ‘thing’ when they hover over a button and press a certain key, to a DataStore. But this would never work because it’s unnecessarily sending DataStore requests over and over, which ROBLOX doesn’t allow anyway because;

DataStore request was added to queue. If request queue fills, further requests will be dropped. Try sending fewer requests

I want it so it adds this ‘thing’ to a table beforehand and have it save the table when they leave, but idrk how to do that. I thought about just making the table a variable, but then wouldn’t that just overwrite a player’s data with someone elses? idk I’m pretty crap at datastores and if anyone could help me that’d be great. Sorry for the bad explanation, I’m not comfortable with sharing what I’m working on exactly

function save.OnServerInvoke(player, thing)
	local dict
	
	local success, erm = pcall(function()
		dict = fstore:GetAsync(player.UserId)
	end)
	
	if success then
		local check1 = table.find(dict, button)
		
		if not check1 then
			dict[#dict + 1] = button
		else
			table.remove(dict, check1)
		end
		
		dataSave(player, dict)
		return true
		
	else 
		warn(erm)
		return false
	end
end

Hi, here is an example saving backpack items (In a table) When the player leaves.
Note: Please test it in-game not studio.

local DataStoreService = game:GetService("DataStoreService")
local ToolsDataStore = DataStoreService:GetDataStore("ToolsDataStore")


function PlayerJoined(Player)
	local Success, KeyInfo
	local ToolData


	if ToolData ~= nil then
		for i, data in ipairs(ToolData) do

			local PlayerOwnsTool = Player.Character:FindFirstChild(data[1]) or Player.Backpack:FindFirstChild(data[1]) 
			if ShopItems:FindFirstChild(data[1]) and PlayerOwnsTool == nil then

				local ItemClone = ShopItems[data[1]]:Clone()
				ItemClone.Handle.Quantity.Value = data[2]
				ItemClone.Parent = Player.Backpack

			elseif ShopItems:FindFirstChild(data[1]) and PlayerOwnsTool then
				PlayerOwnsTool.Handle.Quantity.Value = data[2]
			end
		end
	end
end

function PlayerLeft(Player)
local ToolTable = {}

for i,v in pairs(Player.Backpack:GetChildren()) do
		table.insert(ToolTable,{v.Name, v.Handle.Quantity.Value})
	end
	if ToolTable ~= nil then
		local Success, errorMessage = pcall(function()
			ToolsDataStore:SetAsync(Player.UserId, ToolTable)
		end)

		if errorMessage then
			warn(errorMessage)
		end
	end
end

game.Players.PlayerAdded:Connect(PlayerJoined)
game.Players.PlayerRemoving:Connect(PlayerLeft)

This was a great solution, but in my case I’m trying to get something in the player’s PlayerGui when they leave, which doesn’t work in server scripts. I tried using remotefunctions. Any alternatives??

You could probably use a Remote event and fire it when the Player leaves (Get the Name of the GUI) and save it into a table.

When the Player joins check the name of gui in a serverstorage folder (ServerStorage folder contains all your guis in game) if there was then clone it into the PlayerGui.

So I do :FireServer when the player leaves?

You could do like you said, and basically save their data to a dictionary every time it updates with a remote event so that you can update it when they leave. It would just have to be a dictionary of dictionaries, i.e.

data = {
    ['plrName'] = {},
    ['otherPlr'] = {}
1 Like

Actually, I’ve thought a better idea you don’t have to use RemoteEvents just loop through the PlayerGui when the Player leaves and save the name of the Gui in a table…

Also add a folder inside of ServerStorage called “GUIS” with all your GUIS in game
When the Player joins back loop through the table and check if there name of an instance in the table is equal to a child of the GUIS Folder (In ServerStorage) and if they are equal then Clone the GUI to the Player Gui.

You cannot access PlayerGui with a server script.

1 Like

Incorrect, you can.

But I literally tried it and it said it couldn’t find PlayerGui, which is the whole point in me asking

I mean unless you have another way Im all ears

It is possible, it’s not working for you probably because you aren’t using game:BindToClose()…
But you could use the Remote event way as I mentioned before.

I tried the game:BindToClose() thing

image

The RemoteEvent one seems really hacky but I’ll try

Use :FindFirstChild("") if it doesn’t work just use the RemoteEvent way.

Done, but now it’s giving me a new error. It is now telling me that this:
104: Cannot store Array in data store. Data stores can only accept valid UTF-8 characters.

local data = {}

local function dataSetup(player)
	local data1

	local success, erm = pcall(function()
		data1 = fstore:GetAsync(player.UserId)
	end)

	if success then
		if not data1 then 
			data[player.UserId] = {}
		else
			data[player.UserId] = data1
			load:FireClient(player, data1)
		end
	end
end

local function onSave(player, button)
	local playerfavs = data[player.UserId]
	
	if playerfavs then
		if playerfavs[button] then 
			playerfavs[button] = nil
		else
			playerfavs[#playerfavs + 1] = button
		end
	end
end

local function playerLeft(player)
	local dict = {}
	local playerfavs = data[player.UserId]
	
	for i, button in ipairs(playerfavs) do
		dict[i] = button
	end
	
	if playerfavs then
		local success, erm = pcall(function()
			fstore:SetAsync(player.UserId, dict)
		end)
		
		if not success then
			print(erm)
		end
		
		data[player.UserId] = nil
	end
end

save.OnServerEvent:Connect(onSave)

Players.PlayerAdded:Connect(dataSetup)
Players.PlayerRemoving:Connect(playerLeft)

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

Nevermind, it was a mistake on my part. Thanks everyone.