What’s up bros. I have a datastore script that saves the player’s backpack to a datastore. The problem is, it doesn’t save a tool if it is equipped, because when a tool is equipped it goes to the player’s character instead of their backpack. The same is when a player dies with the tool equiped, it doesn’t save. I’m trying to save the player’s items at all times.
I’ve tried using Humanoid:UnequipTools() but it always breaks the script. I would really appreciate any help on this issue! If I solve this issue then my game is pretty much perfect lol.
Here’s my current datastore script, I have a folder called “Items” in serverstorage:
local folders_to_save = {
-- 'Backpack' is the name of player's folder where items are stored
-- And Items are the folder for where the items are originated
['Backpack'] = game.ServerStorage.Items
}
-- Reload the saved items every time player respawns
local reload_items_on_reset = true
local DataStoreService = game:GetService("DataStoreService")
local ItemStore = DataStoreService:GetDataStore("ItemStore")
-- This is where we store all the loaded data's
local PlayerData = {}
local function LoadItemsFromData(player: Player, data, onReset: boolean)
local folders = {}
for storeFolderName, itemsFolder in pairs(folders_to_save) do
local folder = player:FindFirstChild(storeFolderName)
if not folder then
-- the folder does not exist, so we create it
folder = Instance.new("Folder")
folder.Name = storeFolderName
folder.Parent = player
end
-- keeping track of the folder
folders[storeFolderName] = folder
end
-- now we're going to make a function to add items in data
local function AddItem(item: Instance, folder: Folder, index: number)
-- the data doesn't have the directory for the folder
if not data[folder.Name] then
-- we create it
data[folder.Name] = {}
end
-- if the item wasn't exist in data
if not index then
-- getting the index of next to the very last index
index = #data[folder.Name] + 1
-- inserting the item name to data
table.insert(data[folder.Name], index, item.Name)
end
-- detecting item parent change
item.AncestryChanged:Connect(function(_, newParent)
-- if item is not in the folder anymore
-- and its not because player is resetting
if newParent ~= folder and folder.Parent ~= nil then
data[folder.Name][index] = nil
end
end)
end
-- iterating every item directory of the data
for storeFolderName, savedItems in pairs(data) do
-- if player is respawning, and folder is not Backpack
-- we cancel the iteration
--
-- this is because only Backpack is being resetted
-- after player respawn
--
-- other folder doesnt
-- (btw this script supports not only backpack to save)
-- (it also supports saving other kind of folders)
-- (as long it is inside player)
if onReset == true and storeFolderName ~= 'Backpack' then
continue
end
local itemsFolder: Folder = folders_to_save[storeFolderName]
-- checking if the directory has the items folder
-- (where the items originated)
if itemsFolder then
-- getting the folder from player
local folder = folders[storeFolderName]
for index, itemName in pairs(savedItems) do
-- checking if theres an item in the original items folder
local item = itemsFolder:FindFirstChild(itemName)
if item then
-- cloning the item and put it the player's folder
local playerItem = item:Clone()
playerItem.Parent = folder
-- since the item is exist in data, and has an index
-- we pass that index in the parameter. and we will modify
-- the additem function again
AddItem(playerItem, folder, index)
else
-- if the item is not valid in the items folder
warn(player, 'has unknown item that is said to be in folder', storeFolderName, ":", itemName)
end
end
else
warn(player, 'has unknown directory saved in data:', storeFolderName)
end
end
-- now we loaded saved items to player's folder
-- next is we detect of new items added in player folders
for storeFolderName, folder in pairs(folders) do
-- we dont need to track the folder anymore
folders[storeFolderName] = nil
-- we also need this here
-- so the item added detector dont run twice
if onReset == true and storeFolderName ~= 'Backpack' then
continue
end
folder.ChildAdded:Connect(function(item)
-- we dont have an index, so we leave it empty
-- it means that the item will be added in data
-- if it doesnt have an index
AddItem(item, folder)
end)
end
end
-- a function to load player's data
local function Load(player: Player)
-- if player's data is already loaded, cancel the function
if PlayerData[player] then
return warn(player, 'data is already loaded')
end
-- The key to access player's data
local dataKey = 'key_' .. player.UserId
-- always use pcall for async functions
local loadedData = nil
local success, result = pcall(function()
-- getting the data from ItemStore Datastore
loadedData = ItemStore:GetAsync(dataKey)
end)
-- error occured when loading data, and logging that error in the output
if not success then
error(success)
end
-- if player is new to the game, they dont have a saved data so its nil
if loadedData == nil then
loadedData = {} -- we give it empty data
end
warn(player, 'items have been loaded')
return loadedData
end
local function Save(player: Player)
local dataKey = 'key_' .. player.UserId
-- get the tracked loaded data of the player
local data = PlayerData[player]
local success, result = pcall(function()
ItemStore:SetAsync(dataKey, data, {player.UserId})
end)
-- something wrong/error happened
if not success then
error(result)
end
warn('Successfully saved', player, 'items to Datastore')
end
-- handling joining players
game.Players.PlayerAdded:Connect(function(player: Player)
-- loading the data
local data = Load(player)
-- keep track of the data in PlayerData
PlayerData[player] = data
-- now we use the LoadItemsFromData function here
-- waiting until character is loaded
local character = player.Character or player.CharacterAdded:Wait()
LoadItemsFromData(player, data)
if reload_items_on_reset == true then
player.CharacterAdded:Connect(function(character: Model)
-- we pass true here to tell that the character respawned
-- so we going to modify the LoadItemsFromData function
LoadItemsFromData(player, data, true)
end)
end
end)
game.Players.PlayerRemoving:Connect(function(player: Player)
Save(player)
-- player is leaving, so we dont need their data anymore
-- get rid of it to prevent memory leak
if PlayerData[player] then
table.clear(PlayerData[player])
end
PlayerData[player] = nil
end)
-- at very last
game:BindToClose(function()
-- if the game is closing, we will force save all player data
for _, player in pairs(game.Players:GetPlayers()) do
Save(player)
end
end)