Datastore pet system saving issues

I’ve been trying to find a way to make my pet system save. After finding out that you could only save values on Roblox I realized this would be so easy and still attempted it. I attempted to make the values in my UnlockedPets folder(towards the bottom) save however it doesnt work and I need some guidance. This is also how my folders are setup for reference.
image

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local MarketplaceService = game:GetService("MarketplaceService")
local ServerScriptService = game:GetService("ServerScriptService")
local HTTPService = game:GetService("HttpService")

local function InstanceObject(class, parent, name, value)
	local object = Instance.new(class, parent)
	object.Name = name
	if value then object.Value = value end
	return object
	
end


local function onPlayerJoin(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = leaderstats

	local gems = Instance.new("IntValue")
	gems.Name = "Gems"
	gems.Parent = leaderstats
	
	local slimed = Instance.new("IntValue")
	slimed.Name = "Slimed"
	slimed.Parent = leaderstats
	
	local backpackMax = Instance.new("IntValue")
	backpackMax.Name = "BackpackMax"
	backpackMax.Parent = player
	
	local Management = InstanceObject("Folder", player, "Management", nil)
	local MaxPets = InstanceObject("IntValue", Management, "MaxPets", 20)
	local UnlockedPets = InstanceObject("Folder", Management, "UnlockedPets", nil)
	local Inventory = InstanceObject("Folder", player, "Inventory", nil)
	local Pets = InstanceObject("Folder", Inventory, "Pets", nil)
	local character = player.Character or player.CharacterAdded:Wait()
	repeat wait() until character:FindFirstChild("HumanoidRootPart")
	repeat wait() until character:FindFirstChild("Head")
	repeat wait() until character:FindFirstChild("Humanoid")
	local EquippedPets = InstanceObject("Folder", character, "EquippedPets", nil)
	
	
	local playerUserId = 'Player_'..player.UserId
	local data = playerData:GetAsync(playerUserId)
	if data then
		coins.Value = data["Coins"]
		gems.Value = data["Gems"]
		slimed.Value = data["Slimed"]
		backpackMax.Value = data["BackpackMax"]
			
	else
		coins.Value = 10000
		gems.Value = 0
		slimed.Value = 0
		backpackMax.Value = 10
		
	end
		
local at = require(ReplicatedStorage.Modules.Handlers.TableOfAssets)
	local owns_extra_max_pets = MarketplaceService:UserOwnsGamePassAsync(player.UserId, at.gamepasses["Extra Max Equipped Pets"].id)
	print(owns_extra_max_pets)
	local MaxEquipped = InstanceObject("IntValue", Management, "MaxEquippedPets", owns_extra_max_pets and at.game_settings.extra_max_equipped_pets or at.game_settings.max_equipped_pets)
		
end

local function create_table(player)
	local player_stats = {}
	
	for _, stat in pairs(player.leaderstats:GetChildren()) do
		player_stats[stat.Name] = stat.Value
	end
	
	return player_stats
	
end

local function create_table2(player)
	local unlocked_pets = {}
	
		for _, unlockedP in pairs(player.Management.UnlockedPets:GetChildren()) do
		unlocked_pets[unlockedP.Name] = unlocked_pets.Value
		
	end
	
	return unlocked_pets
end

local function onPlayerExit(player)
	local player_stats  = create_table(player)
	local unlocked_pets = create_table2(player)
	local success, err = pcall(function()
		local playerUserId = 'Player_'..player.UserId
		playerData:SetAsync(playerUserId, player_stats)
		playerData:SetAsync(playerUserId, unlocked_pets)
		
	end)

	if not success then 
		warn("Could not save data")
	end
end
game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)

2 Likes

Where are all of the other pets stored? (Includes locked pets)

Either way, I think the easiest way to do it (as you already attempted) is to make a table of the pets’ names. You would then SetAsync with that table, then find the pet in the overall pets’ folder based on the name.

Also, I would highly not recommend not using SetAsync twice (as you did in the end). This not only overwrites data and only saves the unlocked pets, but could fill up the datastore queue. Instead, you would either make one table for both the player stats, or two separate datastores for the player stats and unlocked pets.

--for saving both unlocked pets and player stats in one table. 
--The player stats are the first value of the table, and the unlocked pets are the second value

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")

local pets_folder = game.ReplicatedStorage.Pets --change this to match pets folder
 

game.Players.PlayerAdded:Connect(function(plr)
    local getData = playerData:GetAsync(plr.UserId(
    if getData ~= nil then
        local stats, pets = getData[1], getData[2]

        for name, val in pairs(stats) do
            plr.leaderstats[name].Value = val
        end
        for _, name in pairs(pets) do
            pets_folder[name]:Clone().Parent = plr.Inventory.Pets
        end
    else
        --load respected leaderstat values if no data exists
    end
end)

game.Players.PlayerRemoving:Connect(function(plr)
    local stats, pets = {}, {}

    for _, s in pairs(plr.leaderstats:GetChildren()) do
        stats[s.Name] = s.Value
    end
    for _, p in pairs(plr.Inventory.Pets:GetChildren()) do
        table.insert(pets, p.Name)
    end

    playerData:SetAsync(plr.UserId, {stats, pets})
end)

Try this sample code I typed out. Try to implement it with your current code, and see if it works. Let me know if you have any issues or questions.

2 Likes

Thanks for the assistance. I tried implementing the code and I ran into an error so im not sure if im doing it correctly here’s what I have

image

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local MarketplaceService = game:GetService("MarketplaceService")
local ServerScriptService = game:GetService("ServerScriptService")
local HTTPService = game:GetService("HttpService")

local pets_folder = ReplicatedStorage:WaitForChild("ReplicatedPets")

local function InstanceObject(class, parent, name, value)
	local object = Instance.new(class, parent)
	object.Name = name
	if value then object.Value = value end
	return object
	
end


local function onPlayerJoin(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = leaderstats

	local gems = Instance.new("IntValue")
	gems.Name = "Gems"
	gems.Parent = leaderstats
	
	local slimed = Instance.new("IntValue")
	slimed.Name = "Slimed"
	slimed.Parent = leaderstats
	
	local backpackMax = Instance.new("IntValue")
	backpackMax.Name = "BackpackMax"
	backpackMax.Parent = player
	
	local Management = InstanceObject("Folder", player, "Management", nil)
	local MaxPets = InstanceObject("IntValue", Management, "MaxPets", 20)
	local UnlockedPets = InstanceObject("Folder", Management, "UnlockedPets", nil)
	local Inventory = InstanceObject("Folder", player, "Inventory", nil)
	local Pets = InstanceObject("Folder", Inventory, "Pets", nil)
	local character = player.Character or player.CharacterAdded:Wait()
	repeat wait() until character:FindFirstChild("HumanoidRootPart")
	repeat wait() until character:FindFirstChild("Head")
	repeat wait() until character:FindFirstChild("Humanoid")
	local EquippedPets = InstanceObject("Folder", character, "EquippedPets", nil)
	
	--line with the error
	local getData = playerData:GetAsync(player.UserId())
		if getData ~= nil then
			local stats, pets = getData[1], getData[2]

			for name, val in pairs(stats) do
				player.leaderstats[name].Value = val
			end
			for _, name in pairs(pets) do
				pets_folder[name]:Clone().Parent = player.Inventory.Pets
			end
	else
		--load values if there is none
		coins.Value = 10000
		gems.Value = 0
		slimed.Value = 0
		backpackMax.Value = 10
		
	end
		-- this im not trying to save
local at = require(ReplicatedStorage.Modules.Handlers.TableOfAssets)
	local owns_extra_max_pets = MarketplaceService:UserOwnsGamePassAsync(player.UserId, at.gamepasses["Extra Max Equipped Pets"].id)
	print(owns_extra_max_pets)
	local MaxEquipped = InstanceObject("IntValue", Management, "MaxEquippedPets", owns_extra_max_pets and at.game_settings.extra_max_equipped_pets or at.game_settings.max_equipped_pets)
		
end


local function onPlayerExit(player)
	local stats, pets = {}, {}
	
	local success, err = pcall(function()
		
for _, s in pairs(player.leaderstats:GetChildren()) do
	stats[s.Name] = s.Value
end
for _, p in pairs(player.Inventory.Pets:GetChildren()) do
	table.insert(pets, p.Name)
end

playerData:SetAsync(player.UserId, {stats, pets})
end)

	if not success then 
		warn("Could not save data")
	end
end
game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)

Change this line
local getData = playerData:GetAsync(player.UserId())
to
local getData = playerData:GetAsync(player.UserId)

1 Like

it got rid of the error but sadly it still doesnt save

If you’re testing in studio, it sometimes terminates the game before calling PlayerRemoving, could be it?

1 Like

i was moving some stuff around and i think the problem is that well inside the pets I have two values for the pets tier and an object value so when it saves it doesnt save the 2 values in the pet which breaks the whole inventory. Do you know how i could save those also

image

image

It’s a data structures problem. Can people have more than one of the same pet type? If not then you could perhaps use the key to name dictionary/table and store values in a dictionary/table.

e.g.
{
pet_name_1 = {value1 = true, value2 = “hello”},
pet_name_2 = {value1 = true, value2 = “hello”, value4 = 30},

}

Or otherwise if multiple can be owned, you could move the key inside the dictionary/table and stor them as a array/table. e.g.
{
{petType = “pet_name_1”, value1 = true, value2 = “hello”},
{petType = "pet_name_2 ", value1 = true, value2 = “hello”, value4 = 30},

}

1 Like

I’m working on a similar project and I found it viable to just store the type of pet as a dictionary (players can own multiple pets of the same type) and then store their attributes such as rarity, level, etc in a table within said dictionary.

1 Like

the value inside the pet is its tier when you evolve it but what if you could evolve infinitely

The tier isn’t really relevant because you can still reference the pet from the data table and apply any logic to any value within said table.

local PlayerPets = {
["Dog"] = {id = 1, tier = 30},
["Cat"] = {id = 2, tier = 45}
}

local Dog = PlayerPets["Dog"].tier = 1000
1 Like

so should I add this to my module script I have with all my pets and their stats

image