I’m saving players’ items when they leave, including the item that they currently have equipped (Items go to the player’s model when equipped).
I got to do this by just making the player unequip all items when their character is being removed
game.Players.PlayerAdded:Connect(function(player)
-- Player's items are loaded here
-- When character is being removed, unequip their tools so that it goes to backpack
player.CharacterRemoving:Connect(function(char)
char:WaitForChild("Humanoid"):UnequipTools()
end)
end)
game.Players.PlayerRemoving:Connect(function(player)
local playerID = player.UserId
local playerChar = player.Character
local playerBackpack = player.Backpack
local leaderstats = player:FindFirstChild("leaderstats")
if not leaderstats then
warn(playerID .. "'s leaderstats does not exist for data save!")
return
else
local items = {}
-- Get all items from player backpack to save
for i, v in pairs(playerBackpack:GetChildren()) do
if v.ClassName == "Tool" then
table.insert(items, v.Name)
end
end
local data = {
Pets = leaderstats.Pets.Value,
Items = items
}
local success, response = pcall(function()
DataStore:SetAsync(playerID, data)
end)
if success then
print(playerID .. " data successfully saved")
else
warn(playerID .. " data failed to save: " .. response)
end
end
end)
On the original place, this all works, but on a different place the item that the player has equipped while leaving won’t be saved, so they’ll lose it when they rejoin. The CharacterRemoving event definitely triggered before the PlayerRemoving event, but the item the player has supposedly unequipped isn’t found in the backpack, why?
If the leaderstats doesn’t exist it should print out a warning, but it’s not doing that so it should be there, btw the place I transferred the script to has team create enabled, I don’t know if that changes anything…
Try putting a return inside before the SetAsync function and also put print statements in every line to make sure it actually ran. Also, take note of what was being saved by printing the names of the tools.
Since you have print calls helping you understand if data was successfully saved, and are not making any statements against that, I’ll assume your script is working in that regard. Your data-persistence implementation is very elementary, but it is ultimately incomplete. I noticed you’re missing a game:BindToClose callback. This method of the DataModel allows developers to run code whenever a Roblox server shuts down.
A Roblox server shuts down either
Unexpectedly
Forcefully by the hand of the game’s owner or an authorized developer
When the last client disconnects (applicable to solo Roblox Studio test sessions)
When a Roblox server shuts down it is no longer a priority to operate the engine, leading to multiple processes being cancelled. The firing of the Players.PlayerRemoving event is no longer reliable, so you’ll need to handle data-saving in that new callback. Begin by converting your event listener to a named function so its code may be re-used:
local function onPlayerRemoving(player: Player)
-- ...
end
Players.PlayerRemoving:Connect(onPlayerRemoving)
game:BindToClose(function()
for _, player in Players:GetPlayers() do
onPlayerRemoving(player)
end
end)
I’ve implemented your idea in the script, but the same problem persists, it’s saving items in the backpack but not the item the player has equipped, which I made the player unequip so that it goes into the backpack
I had 2 items here, a Fusion Coil and a Gravity Coil, but it only saved the Gravity Coil because the Fusion Coil didn’t go to my backpack even though it’s supposed to be unequipped and be put back in my backpack
local Players = game:GetService("Players")
local function onPlayerRemoving(player: Player)
local playerID = player.UserId
local playerChar = player.Character
local playerBackpack = player.Backpack
local leaderstats = player:FindFirstChild("leaderstats")
if not leaderstats then
warn(playerID .. "'s leaderstats does not exist for data save!")
return
else
local items = {}
for i, v in pairs(playerBackpack:GetChildren()) do
if v.ClassName == "Tool" then
table.insert(items, v.Name)
print("Saved: " .. v.Name)
end
end
local data = {
Pets = leaderstats.Pets.Value,
Items = items
}
local success, response = pcall(function()
DataStore:SetAsync(playerID, data)
end)
if success then
print(playerID .. " data successfully saved")
else
warn(playerID .. " data failed to save: " .. response)
end
end
end
Players.PlayerRemoving:Connect(onPlayerRemoving)
game:BindToClose(function()
for _, player in Players:GetPlayers() do
onPlayerRemoving(player)
end
end)
Why not just save in StarterGear instead if you have just put all your items in there as well? If not, make sure that the playerbackpack is cloned with the same weapon when it dies.
EDIT: If the player has tools when it dies, parent it into the backpack instead of unequipping, maybe that’ll work.
you make dedicated modules so its easily useable in any scripts for example you could have a module that handles all data saving and another module that handles gamepasses,and etc hardcoding stuff can get messy quickly and cause issues in the future like having code thats hard to update
good code structure :
Bad code structure :
similar design structure can be seen outside of roblox as well one of them being minecraftt
also when saving player data you should add retry logic so if it fails once it retries for certain amount of time and you should handle unexpected exceptions as well