Datastore not saving new objects when added

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

I am trying to create new Numbervalues into the PlrStats folder and save them when this happens/a player leaves the game. This is because players will be getting the Age of a bone, and this will in turn create a new object for them with the bones age as the value.

  1. What is the issue? Include screenshots / videos if possible!

The issue is that the scripts do this, they work as intended, EXCEPT the datastore. It says it’s being saved, says it’s updating, but it in fact is not saving as intended. As per the GIFs.

  1. What solutions have you tried so far? Did you look for solutions on the Creator Hub?

I’ve looked this up and have yet to find a solution that is what I am looking for. I have provided my datastore code below that runs on the player leaving to save the stats, and provided gyazo gifs of it not working.

game.Players.PlayerRemoving:Connect(function(player)

	local playerKey = "Player-ID:"..player.UserId

	local Bools = player.Bools
	local PlrStats = player.PlrStats
	
	local tableToSave = {};
	
	for _, object in ipairs(Bools:GetChildren()) do
		if object:IsA("BoolValue") then
			tableToSave[object.Name] = object.Value
		end
	end
	for _, object in ipairs(PlrStats:GetChildren()) do
		if object:IsA("StringValue") or object:IsA("NumberValue") then
			tableToSave[object.Name] = object.Value
		end
	end

	local success, message = pcall(function()
		CrossfireDataStore:SetAsync(playerKey, tableToSave)
	end)
	
	if success then
		if message then
			print(message)
		end
		print("saved success for "..player.Name)
	end
end)

I appreciate any and all help regarding this, as this is CRUCIAL for my games continued development!!!

This is the GIFS;

It generating the new object in the respective folder

It saving successfully

It not re-appearing on join

Again thank you for all your help and support!!

1 Like

Hey! It’s certainly possible the datastore saving is working as you intend it to. If the issue is the NumberValue instances not being created on join, perhaps the script which is responsible for creating those values is not working?

2 Likes

Hello,

Yeah so when it saves these values, it’s supposed to re-call that table which it saved, which I’m sure you know,

The table it saves is whatever is present in those folders of “Bools” and “PlrStats” as stated before, this is the code that calls this when a player joins the game. It’s possible I am just misunderstanding and not seeing something obvious, I’ve been at this a while thinking it’s a more complex issue than it might be.

Code on join to create folders/fetch values;

game.Players.PlayerAdded:Connect(function(player)
	local playerKey = "Player-ID:"..player.UserId
	
	local Bools = Instance.new("Folder", player)
	Bools.Name = "Bools"
	local stats = Instance.new("Folder",player)
	stats.Name = "PlrStats"
	
	for name, object in pairs(PlayerDataTemplate) do
		local newValue = Instance.new(object[1])
		newValue.Name = name
		newValue.Value = object[2]
		
		if object[1] == "NumberValue" or object[1] == "StringValue" then
			newValue.Parent = stats
		else
			newValue.Parent = Bools
		end
	end
	
	local GetSave;
	local success, message = pcall(function()
		GetSave = CrossfireDataStore:GetAsync(playerKey)
	end)
	
	if success then
		if GetSave then
			for _, object in ipairs(Bools:GetChildren()) do
				if object:IsA("BoolValue") then
					print(object.Name, GetSave[object.Name])
					object.Value = GetSave[object.Name]
				end
			end
			for _, object in ipairs(stats:GetChildren()) do
				if object:IsA("NumberValue") or object:IsA("StringValue") then
					object.Value = GetSave[object.Name]
				end
			end
			

			print("Data loaded for "..player.Name)
		else
			print("Test")
		end
	else
		print("Problem loading data!")
	end

end)

I’m assuming PlayerDataTemplate is a constant defined elsewhere. In that case, it looks like your code first creates all the values as defined in PlayerDataTemplate, gets player save data, and then assigns the save data to those Instances. If bone age is not defined in PlayerDataTemplate, your code does not ever look inside GetSave to create an instance to represent it.

Is it possible that the bone age value is not being created because it 's not defined there?

3 Likes

Yeah that makes sense and is how it works,

Thank you for the secondary perspective, just a question though to follow up, calling that saved data table instead of going off the default player data template would solve this issue? How would I go about calling that per the specific player when they join? As I’ve always just done this as creating new assets the way I am intending too is uncharted territory for me.

Tbh just do instance data to tables and it’ll save u a lot of time

1 Like

I’ve thought about changing my datastoring methods; this is just the way I know best on how to effectively and cleanly store data, with ease of editing for some programmers I have work under me during times I am away irl for things.

Since GetSave is just an object (or I guess lua calls them tables?) You can convert each Key, Value pair in the object into an iterable array the same way you use PlayerDataTemplate.

PlayerDataTemplate seems to include some metadata to ensure your types are right, but lua has some handy functions which let us determine types of a value completely automagically.

As an aside-- your method actually has an advantage of forcing strict types in your database, which is a worthy endeavor! We would lose some of that strict typing with this method, but oh well!

Here’s an example of how you can create the Instances directly from the GetSave data rather than a pre-defined template. (This is somewhat puesdo-code, take with a grain of salt syntaxtically)

for key, value in pairs(GetSave) do
    if typeof(value) === "boolean" then
        local newValue = Instance.new("BoolValue")
        newValue.Name = key
        newValue.Value = value
        newValue.Parent = Bools
    elseif typeof(value) === "number" then
        local newValue = Instance.new("NumberValue")
        newValue.Name = key
        newValue.Value = value
        newValue.Parent = stats
    elseif typeof(value) === "string" then
        local newValue = Instance.new("StringValue")
        newValue.Name = key
        newValue.Value = value
        newValue.Parent = stats
    end 
end
2 Likes

This looks great, I will give this a try, and mark your solution if it functions as intended.

Thank you for your time and your wealth of knowledge on the matter, I will for sure reply back here if things fail/I can’t figure it out!!

1 Like

What I’m talking about is the same thing you’re doing just slightly modified. Because you only have folders, Number Values, and String Values, everything should be far simpler.

Example code:

local function InstanceToTable(Player:Model) : {any}
	local Result = {}
	
	for Index, Object in Player:GetChildren() do
		if not Object:IsA("Folder") then continue end
		
		local ChildrenResult = {}
		
		for I, O in Object:GetChildren() do
			if not O:IsA("StringValue") and not O:IsA("NumberValue") then continue end
			
			ChildrenResult[O.Name] = O.Value
		end
		
		Result[Object.Name] = ChildrenResult	
	end
	
	return Result
end

local function TableToInstance(T:{any}, Parent:Instance)
	for Name, Children in T do
		local Folder = Instance.new("Folder")
		Folder.Name = Name
		Folder.Parent = Parent
		
		for N, Value in Children do
			local Type = typeof(Value) == "string" and "StringValue" or "NumberValue"
			
			local Obj = Instance.new(Type)
			Obj.Name = N
			Obj.Value = Value
			Obj.Parent = Folder
		end
	end
end

--Edit + example use
local PlayerData = {
["Stats"] = {
 Speed = 0,
 Strength = 0
},
["Colors"]  = {
 CarColor = "Red"
}
}

TableToInstance(PlayerData, Player)

With this, you can easily turn your table data into instances and vice versa without too many if’s

Hello,

So it isn’t working as intended, as when I try to use this method instead of the method of using the PlayerDataTemplate, it simply doesn’t get my saved data within the datastore. I will reply this with the entire data template code, and I am curious as to why this isn’t fetching what is required for the datastore? AS it will only work as intended when that PlayerDataTemplate is generated.

Thank you!

local DSS = game:GetService("DataStoreService")
local RepStorage = game:GetService("ReplicatedStorage")

local CrossfireDataStore = DSS:GetDataStore("v1.2_AchaeologistStorage")

local PlayerDataTemplate = {
	--// Prior Values \\--
	["Cosmetic"] = {"StringValue", "None"},
	["Uniform"] = {"StringValue", "None"},
	
	--//\\ Number Values //\\--
	["Coins"] = {"NumberValue", 15}, -- Base money, awarded for gameplay
	["XP"] = {"NumberValue", 0}, -- Determines level
	["XP_Floor"] = {"NumberValue", 75}, -- Determines level floor
	["Level"] = {"NumberValue", 1}, -- lvl
	["Serial"] = {"NumberValue", 1}, -- Bone Adjustment Serial, adds every time you generate a new aged/enchanted bone
	
	
	--//\\ Stackable Items //\\--
	
	
	--// Bones
	["Bones"] = {"NumberValue", 0}, 
	["Large Bones"] = {"NumberValue", 0}, 
	["Dinosaur Bones"] = {"NumberValue", 0}, 
	["Wing Bones"] = {"NumberValue", 0}, 
	["Fish Bones Small"] = {"NumberValue", 0}, 
	["Fish Bones Large"] = {"NumberValue", 0},
	["Tail Bones"] = {"NumberValue", 0},
	["Ribcage"] = {"NumberValue", 0},
	["Shark Skulls"] = {"NumberValue", 0}
	
	--// Rare Bones
	["TRex Skull"] = {"BoolValue", false},-- [Rarity = Prehistoric], BONE REQUIREMENTS{}
	["Pterodactyl Skull"] = {"BoolValue", false},-- [Rarity = Prehistoric], BONE REQUIREMENTS{}
	
	
												
	--Fish Small
	["Small Fish - Bass"] = {"BoolValue", false},
	["Small Fish - Trout"] = {"BoolValue", false},
	["Small Fish - Pike"] = {"BoolValue", false},
	["Small Fish - Carp"] = {"BoolValue", false},
	["Small Fish - Perch"] = {"BoolValue", false},
	["Small Fish - Pickerel"] = {"BoolValue", false},
	
	--Fish Large
	["Large Fish - Catfish"] = {"BoolValue", false},
	["Large Fish - Nile Perch"] = {"BoolValue", false},
	["Large Fish - Salmon"] = {"BoolValue", false},
	["Large Fish - Sturgeon"] = {"BoolValue", false},
	--Fish Dino
	["Greatwhite Shark"] = {"BoolValue", false}, 
	["Bull Shark"] = {"BoolValue", false},
	["Tiger Shark"] = {"BoolValue", false},
	["Mako Shark"] = {"BoolValue", false},
	["Megladon"] = {"BoolValue", false}, 
	["Mosasaurs"] = {"BoolValue", false}, 
	["Dunkleosteus"] = {"BoolValue", false}, 
	
	
	 
	
	-----------------------------------------------------------------
	--//\\ First time in game assets //\\--
	["FirstTime"] = {"BoolValue", false}

	
}


game.Players.PlayerAdded:Connect(function(player)
	local playerKey = "Player-ID:"..player.UserId
	
	local Bools = Instance.new("Folder", player)
	Bools.Name = "Bools"
	local stats = Instance.new("Folder",player)
	stats.Name = "PlrStats"

	for name, object in pairs(PlayerDataTemplate) do
		local newValue = Instance.new(object[1])
		newValue.Name = name
		newValue.Value = object[2]
		
		if object[1] == "NumberValue" or object[1] == "StringValue" then
			newValue.Parent = stats
		else
			newValue.Parent = Bools
		end
	end

	
	
	local GetSave;
	local success, message = pcall(function()
		GetSave = CrossfireDataStore:GetAsync(playerKey)
	end)

	
	if success then
		if GetSave then

			for _, object in ipairs(Bools:GetChildren()) do
				if object:IsA("BoolValue") then
					print(object.Name, GetSave[object.Name])
					object.Value = GetSave[object.Name]
				end
			end
			for _, object in ipairs(stats:GetChildren()) do
				if object:IsA("NumberValue") or object:IsA("StringValue") then
					object.Value = GetSave[object.Name]
				end
			end



			--[[
			--// getsave method, didnt work with above commented out/not
			for key,value in pairs(GetSave) do
				if typeof(value) == "BoolValue" then
					local newValue = Instance.new("BoolValue")
					newValue.Name = key
					newValue.Value = value
					newValue.Parent = Bools

				elseif typeof(value) == "NumberValue" then
					local newValue = Instance.new("NumberValue")
					newValue.Name = key
					newValue.Value = value
					newValue.Parent = stats

				elseif typeof(value) == "StringValue" then
					local newValue = Instance.new("StringValue")
					newValue.Name = key
					newValue.Value = value
					newValue.Parent = stats
				end
			end
			]]--
			

			print("Data loaded for "..player.Name)
		else
			print("Test")
		end
	else
		print("Problem loading data!")
	end

end)

game.Players.PlayerRemoving:Connect(function(player)

	local playerKey = "Player-ID:"..player.UserId

	local Bools = player.Bools
	local PlrStats = player.PlrStats
	
	local tableToSave = {};
	
	for _, object in ipairs(Bools:GetChildren()) do
		if object:IsA("BoolValue") then
			tableToSave[object.Name] = object.Value
		end
	end
	for _, object in ipairs(PlrStats:GetChildren()) do
		if object:IsA("StringValue") or object:IsA("NumberValue") then
			tableToSave[object.Name] = object.Value
		end
	end

	local success, message = pcall(function()
		CrossfireDataStore:SetAsync(playerKey, tableToSave)
	end)
	
	if success then
		if message then
			print(message)
		end
		print("saved success for "..player.Name)
	end
end)

game:BindToClose(function()
    wait(5)
end)

You’re intentionally making ur data static by using the data template… The objects get created whether the name of the saved object is in the template or not. if the name is not in the table, then the saved object doesn’t get created. If it doesn’t get created, then it never saves. U could modify the functions I gave you to fit your table yk?

Marking this as the solution; as with your code, and help from this link, I was able to figure out my problem with ProfileStore!!

Thanks all for your help!

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.