Hi, ive written a datastore for an inventory that uses templates, and the items/information that are in them are in module scripts. my issue is that it doesnt seem that its saving, and its not loading any data.
Datastore script
local DataStoreService = game:GetService("DataStoreService")
local items = require(game.ReplicatedStorage.Items)
local player_data = DataStoreService:GetDataStore("player_data")
game.Players.PlayerRemoving:Connect(function(player)
local inventory = {}
for i, v in pairs(player:WaitForChild("PlayerGui").ScreenGui.background.inventory:GetChildren()) do
table.insert(inventory,v.Name)
local success, errorMessage = pcall(function()
player_data:SetAsync(player.UserId,inventory)
end)
if success then
print("Data Saved")
else
print("Error "..errorMessage)
end
end
end)
game.Players.PlayerAdded:Connect(function(player)
repeat
task.wait()
until player.Character
task.wait(3)
local success, inventory = pcall(function()
return player_data:GetAsync(player.UserId)
end)
if success then
if inventory then
for i, v in pairs(inventory) do
local item = items.Items(v)
if item then
local numb
if item == "Dark Bandana" then
numb = 1
elseif item == "Dark Shades" then
numb = 2
elseif item == "Fedora" then
numb = 4
elseif item == "Dramatic Mask" then
numb = 3
elseif item == "Old Boot" then
numb = 6
elseif item == "Red Bandana" then
numb = 5
elseif item == "Topaz Fedora" then
numb = 7
local clone = player.PlayerGui.ScreenGui.background.inventory.InventoryClient.Template:Clone()
clone.Parent = player.PlayerGui.ScreenGui.background.inventory
clone.Name = v
clone.itemImage.Image = items.Items[numb].imageID
clone.itemName.Text = items.Items[numb].name
end
end
end
end
end
end)
Have you considered if “inventory” doesn’t exist when you use :GetAsync() because it’s the player’s first time playing? You need to set a default.
Here’s a bunch of other suggestions I have to improve your code as well:
Changes
Replace this:
repeat
task.wait()
until player.Character
with:
player.CharacterAdded:Wait()
(I actually recommend just loading the data with :GetAsync() before waiting for the character because it’s asynchronous so you’d want it to be ready by the time they spawn in, you can take out the task.wait(3) as well)
local numb
if item == "Dark Bandana" then
numb = 1
elseif item == "Dark Shades" then
numb = 2
elseif item == "Fedora" then
numb = 4
elseif item == "Dramatic Mask" then
numb = 3
elseif item == "Old Boot" then
numb = 6
elseif item == "Red Bandana" then
numb = 5
elseif item == "Topaz Fedora" then
numb = 7
You can instead do a dictionary (lookup table) and assign the item strings to a number.
Look into UpdateAsync and you can quickly just return a default in the function instead of specifying a default after doing :GetAsync() if the previous value was nil.
PlayerGui gets destroyed pretty much instantly when a player leaves the game, it tends to work in Studio but not on the Roblox platform.
Like @colorblindcrayon said, you need to set default stats for new players.
the character might load before this line is run, but I don’t see any need for the character in @cordedphoenix54 's script. Could just do
if not player.Character then player.CharacterAdded:Wait()
--or
local character = player.Character or player.CharacterAdded:Wait()
You should definitely use UpdateAsync to save data - you can utilise it to prevent data loss, and it has certain safety features that SetAsync doesn’t.
Anyway, the main thing I noticed is that the code is missing a BindToClose function. This means the game might be closing before it has a chance to save player data.
local players = game:GetService("Players")
local runService = game:GetService("RunService")
--Convert saving function to this format:
local function save(player)
end
players.PlayerRemoving:Connect(save)
--now, we add the BindToClose:
local function onClose()
if runService:IsStudio() or #players:GetPlayers() <= 1 then task.wait(3) return nil end
for _, player in ipairs(players:GetPlayers()) do
save(player)
end
task.wait(3)
end
game:BindToClose(onClose)
TLDR:
PlayerGui is not a reliable place to read save data.
Try making a module inventory and keeping it on the server. That way, exploiters can’t modify it, you can control data the client receives, and you can take as long as you need to save the data. Just destroy it after you’re done to prevent outdated data saving.
Okay, i found a module inventory system and hooked it up to my system, but i still dont understand how to connect this to a datastore
module script
local Inventories = {}
local Inventory = {}
function Inventory:Remove(player, name, amount)
if not player or not name then
return -- Not going over this because I already did in the "new" function
end
local inventory = Inventory:Get(player)
if not inventory then
return -- The user doesn't have an inventory, we can't add an item
end
if inventory[name] then -- Check if the user has the item you're giving
if inventory[name] - (amount or 1) < 0 then -- Check if the amount your removing is more then the user has
inventory[name] = nil -- Delete the item completely
else
inventory[name] = inventory[name] - amount or 1 -- Remove the amount or one item.
end
end
end
function Inventory:Add(player, name, amount)
if not player or not name then
return -- Not going over this because I already did in the "new" function
end
local inventory = Inventory:Get(player)
if not inventory then
return -- The user doesn't have an inventory, we can't add an item
end
if inventory[name] then -- Check if the user has the item you're giving
inventory[name] = inventory[name] + amount or 1 -- They do, just add the amount or 1
else
inventory[name] = amount or 1 -- They don't, give them the amount of items or 1
end
end
function Inventory:Get(player)
if not player then
return -- Not going over this because I already did in the "new" function
end
return Inventories[player] -- Return the cached inventory!
end
function Inventory.new(player, contents) -- Hey! Our new function, it takes a player and contents.
if not player then
return -- The player that we specified doesn't exist.
end
if not contents then -- We have no contents, let's make some.
contents = { -- Setting our contents to a table
}
end
Inventories[player] = contents -- Assign the users contents to our cache
return Inventories[player] -- return the inventory, (semi useless b/c its just content but still)
end
return Inventory
–local script
local UserInputService = game:GetService("UserInputService")
local List = script.Parent
local module = require(game.ReplicatedStorage.Items)
local b = require(game.Players.LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("Modules"):WaitForChild("case"))
local inventory_module = require(game.ReplicatedStorage.ModuleInv)
local template = game.ReplicatedStorage.ModuleInv:WaitForChild("Template")
function addItem(player, name, amount)
inventory_module:Add(player, name, amount)
end
function removeItem(player, name, amounnt)
inventory_module:Remove(player, name, amounnt)
end
local player = game.Players
game.ReplicatedStorage.Cases.GetItem.OnClientEvent:Connect(function(unboxed)
print(unboxed)
addItem(player, unboxed, 1)
local button = template:Clone()
button.Name = module.Items[unboxed].name
button.Visible = true
button.Parent = List
button.ImageLabel.Image = "rbxassetid://"..module.Items[unboxed].imageID
end)
When getting the data to save, you can just call a function in your inventory module that will collect all the player’s data and return it. Then, just save it to the store.
Create a RemoteFunction in ReplicatedStorage. Let’s call it “GetData”.
On the client:
local rStorage = game:GetService("ReplicatedStorage")
local dataFunc = rStorage:WaitForChild("GetData")
--we can invoke the function to get the data.
local data = dataFunc:InvokeServer()
On the server:
local rStorage = game:GetService("ReplicatedStorage")
local ssService = game:GetService("ServerScriptService")
local dataFunc = rStorage.GetData
local function retrieve(player)
local inv = require(ssService.Inventories[player.Name])
return inv:Get()
end
dataFunc.OnServerInvoke = retrieve
okay, im really sorry im basically getting you to do this for me but now how would i use the data to create templates and store it once the player leaves