I'm making a murder game and I'm trying to make it where players can purchase items (skins, effects, etc.), but the way I want the weapons (there class name being tools) to save is by using a DataStore in a value named 'Inventory'. But the script I made doesn't save the tools. The error I get is: "ServerScriptService.Saveinv:18: bad argument #3 to 'Value' (string expected, got nil) Stack Begin Script 'ServerScriptService.Saveinv', Line 18 Stack End" The script is:
local ds = game:GetService("DataStoreService"):GetDataStore("stats") local stats={"Murderer","Knife","Revolver"} game.Players.PlayerAdded:connect(function(plyr) local a=Instance.new("BoolValue") a.Parent=plyr a.Name="Inventory" for i=1,#stats do local stat=Instance.new("StringValue") stat.Parent=a stat.Value= "Default" stat.Name=stats[i] if a:WaitForChild("Murderer") then a.Murderer.Value = "Identify" end end local child=plyr.Inventory:GetChildren() for i=1, #child do child[i].Value=ds:GetAsync(plyr.userId..child[i].Name) end local wep = a:GetChildren() if wep.ClassName == "Tool" then wep=ds:GetAsync(plyr.userId.child.wep.Name) end end) game.Players.PlayerRemoving:connect(function(plyr) local child=plyr.Inventory:GetChildren() for i=1, #child do child[i].Value=ds:SetAsync(plyr.userId..child[i].Name,child[i].Value) end end)
I'm not the best at scripting nor am I great with DataStores so all help is appreciated. EDIT: The StringValues Murderer, Knife, and Revolver are the skins or abilities equipped.
You are having an error because GetAsync
returned nil
and you attempted to assign that to the Value
property of a StringValue
. That is, you effectively attempted to do child[i].Value = nil
.
I will post an improved script below. Changes I've made:
if a:WaitForChild("Murderer")
block (it is strange because why "wait" when you just created it? Also, you run that assignment every time the loop runs; you could have just done a.Murderer.Value = "Identify"
after the loop.)pcall
, which is recommended because datastores can throw errors. Note that ideally, you should handle the case when it fails (ex by retrying a couple times and/or notifying the user that their data could not be loaded and then, later on, don't save over their old information unless it's just preferences or something).stat.Parent = a
downa
to inv
because that's what it isTryGet
return nil
on failure, lua will choose the or value
part only if GetAsync fails (or returns false
, which won't happen in this case since you don't save false
to the datastore). This fixes the error you were having.SetAsync
doesn't return a value, so you shouldn't have child[i].Value = ds:SetAsync
Not yet fixed:
local wep = inv:GetChildren()
part is about. I think you are trying to find the child whose ClassName is "Tool", but there is no such child - you just created all the children, and they are all StringValues. That part of the script isn't erroring because wep
is a table and wep.ClassName
simply returns nil
. Since I don't know what you want, I just commented it out.Resulting script:
local ds = game:GetService("DataStoreService"):GetDataStore("stats") --local stats={"Murderer","Knife","Revolver"} local stats = { Murderer = "Identify", Knife = "Default", Revolver = "Default", } local function TryGet(ds, key) local success, value = pcall(function() return ds:GetAsync(key) end) if success then return value end return nil end game.Players.PlayerAdded:connect(function(plyr) local inv = Instance.new("BoolValue") inv.Parent = plyr inv.Name = "Inventory" for name, value in pairs(stats) do local stat = Instance.new("StringValue") stat.Name = name stat.Value = TryGet(ds, plyr.UserId..child[i].Name) or value stat.Parent = inv end -- local wep = inv:GetChildren() -- if wep.ClassName == "Tool" then -- wep = ds:GetAsync(plyr.userId.child.wep.Name) -- end end) game.Players.PlayerRemoving:connect(function(plyr) local child = plyr.Inventory:GetChildren() for i = 1, #child do ds:SetAsync(plyr.userId..child[i].Name,child[i].Value) end end)