Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
-1

How to save tools in DataStore?

Asked by 7 years ago
Edited 7 years ago

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.

1 answer

Log in to vote
0
Answered by 7 years ago

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:

  • Since you want different default values for different stats, I made stats a dictionary which includes the name of each "stat" and also its default value. This took out your strange 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.)
  • I created a TryGet function which wraps the datastore call in 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).
  • You should generally set the parent of an object last (recommended by Roblox for maximum performance, so it's a good habit to get into, though it won't make a noticeable difference here), so I moved stat.Parent = a down
  • I renamed a to inv because that's what it is
  • I merged your GetAsync for loop into the stat creation one. Since I have TryGet 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:

  • You should only call GetAsync or SetAsync once (for each time a player joins/leaves), not once per child! You can save a table of strings to a single datastore-key to improve this.
  • I have no idea what the 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)
0
(I know this has been awhile) The -local wep = inv:GetChildren()- was supposed to save the weapon that you bought, but I think instead of using a tool I will just use values and when your inventory GUI shows it will just get the item from the value. So thank you for the help! possiblyInSync 1 — 6y
Ad

Answer this question