Error trying to make Datastore

I’m trying to make a script that saves the player’s tools when added, and yesterday it was working perfectly fine. But I wanted to create a new one, so I changed the name and now I keep getting the same error, preventing me from even making the datastore work. I can’t change the name back either, and It’s stressing me out a ton. I’ve spent 7 hours trying to figure out what’s wrong, and nothings help. Someone please help me w this, thanks!

Error:

Code:

local DataStoreService = game:GetService('DataStoreService') or game:FindService('DataStoreService')
local Players = game:GetService("Players") or game:FindService("Players")
local Tools = game.ReplicatedStorage:WaitForChild("Tools")

do 
	local DS = DataStoreService:GetDataStore('PlrToolSaved')
	local PlrToolSaves = {}
	
	Players.PlayerAdded:Connect(function(plr)
		repeat
			task.wait()
		until DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.GetAsync) > 0
		local Data = DS:GetAsync(plr.UserId)
		if Data and typeof(Data) == 'table' then
			PlrToolSaves[plr] = Data
		else
			PlrToolSaves[plr] = {}
		end
		
		local savedTool = Instance.new('Folder', plr)
		savedTool.Name = 'AllToolSaved'
		for _, v in ipairs(Data) do
			Instance.new('Configuration', savedTool).Name = v
		end
	end)
	
	Players.PlayerRemoving:Connect(function(plr)
		local Data = PlrToolSaves[plr]
		PlrToolSaves[plr] = nil
		repeat
			task.wait()
		until DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.SetIncrementAsync) > 0
		DS:SetAsync(plr.UserId, Data)
	end)
	
	game:GetService('ReplicatedStorage').ShopBuy.OnServerEvent:Connect(function(player, tool, price)
		if player.leaderstats.Points.Value >= price then
			Instance.new('Configuration', player.AllToolSaved).Name = tool
			player.leaderstats.Points.Value -= price
		end
	end)
end
1 Like

The error is telling you that the argument for ipairs is invalid and it got nil (which means Data is nil, and that the player is new). Since you already have PlrToolSaves[plr] set to Data or an empty table if not found, you can simply replace Data here with PlrToolSaves[plr]

1 Like

So, you’re telling me I do this? :

1 Like

Will it still save all the players data or is it different?

1 Like

It’s different, you already assigned each player’s data seperately to a table. I have a feeling you didn’t make this code but it’s fine

My friend did awhile back, but I’m re-using it for another Shop GUI

I have one question. I wanna make it so if you own a gamepass, and the tool has a certain name, it’ll add it to the table; If you don’t, then it won’t add it to the table.

	game:GetService('ReplicatedStorage').ShopBuy.OnServerEvent:Connect(function(player, tool, price)
		if player.leaderstats.Points.Value >= price then
			Instance.new('Configuration', player.PlrToolSaved).Name = tool
			player.leaderstats.Points.Value -= price
		end
	end)
end

A little bit off-topic, but still ties in with how I want the datastore to work. How could I make it do that with

			table.insert(PlrToolSaved[player],tool)

I’ll detract from the post’s topic for a minute, just to improve on this further:

  • You don’t actually need to wait for the budget to be more than 0 to do anything.
    Roblox’s limits are generous enough that you will not hit the queue limit if you are sending requests to datastores just once like in your code.

  • You should also wrap GetAsync() and SetAsync() in a pcall; as with most HTTP calls, they have a chance to drop and cause your script to error.

If you need help figuring out how to use pcall(), this is a good start: Pcalls - When and how to use them

also, just use ValueObjects, why are you using Configurations

What are ValueObjects? I’ve never seen them

IntValues, StringValue, and in your case, ObjectValue are considered to be ValueObjects.

The way I see it, Configurations are just an archaic construct that developers can use to enable easily change settings for something, such as the stats of a weapon, etc.

It’s largely been superseded by Attributes though, and using ValueObjects also ensures type safety.

SO, If I change it to a stringvalue, it’ll be much better?

and in your case, ObjectValue

You’re trying to set a Folder object as your value of your Configuration, so use that.

And it’s not much better per say, it’s me just nitpicking, just use what you like.

All you need is a conditional:

-- put this section at start of script
local MarketplaceService = game:GetService("MarketplaceService") 
local GamepassId = 0 -- replace with your gamepass id
-------
game:GetService('ReplicatedStorage').ShopBuy.OnServerEvent:Connect(function(player, tool, price)
		if player.leaderstats.Points.Value >= price then
			Instance.new('Configuration', player.PlrToolSaved).Name = tool
			player.leaderstats.Points.Value -= price
if tool = "the name of the specific tool" and MarketplaceService:UserOwnsGamepassAsync(player.UserId, gamepassId) then -- checks if player has gamepass with above id and matches the name you want
table.insert(PlrToolSaved[player],tool)
end
		end
	end)
end

I have one more request for help before I close this; How do I make it so it checks if you own, or have bought said gamepass, and if it checks that you own it when you join or when you bought it, it’ll add it to the Table.

But how do I make it so it doesn’t keep adding it to the table everytime you join.

Just use UserOwnsGamepassAsync like above everytime the player joins and if it returns true it adds to the table.
To check if the tool is in the table use table.find(PlrToolSaved[player], "name of tool") and add the tool to the table if it doesn’t return anything

Speculation ;-;

Before I say anything, I’d say it’s probably because of this very common issue that’s easy to fix:


Data might not be saving in your game, because the server closes too fast, so data isn’t even able to save in the first place. You need a delay.

A quick fix is to tell the server to give a little delay before actually closing, to let data save. You can put this one line of code in a script, and it should do the job:

game:BindToClose(function() task.wait(5) end) --Delays 5 seconds before shutting down.

Or, you can literally do the data-saving process in that BindToClose function, so it does save the data, then close the game. Either way, it can work.

Now let’s see what’s actually going on-

One thing I’d do is put some print()s in a script and see where goes wrong. It helps you see where the script actually pauses, and to give you a better understanding of what is yielding the script in an infinite timelapse… or a little stupid typo in a variable. Try that out before doing anything else.