Help needed with saving every existing tool in the player's backpack

Can you tell me what is the tools folder with the testtools for in reaplicated storage?

Yes, those are the cloneables that the actual tool represents. For example, you might have two Steel Axes with different durability and rarity values (maybe even different display names). But they are both a “Steel Axe”. So you clone the Steel Axe cloneable, and modify it in accordance.

Wait a second i have been developing my own saving, can you look please at this and tell me how to fix the Argument 1 missing or nil - Server - ToolSaves:190 error in this script.

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local CollectionService = game:GetService("CollectionService")
local ToolStore = DataStoreService:GetDataStore("AdvancedDynamicToolSave")

local function vec3ToTable(v) return {v.X, v.Y, v.Z} end
local function tableToVec3(t) return Vector3.new(t[1], t[2], t[3]) end

local function cframeToTable(cf) return {cf:GetComponents()} end
local function tableToCFrame(t) return CFrame.new(unpack(t)) end

local function color3ToTable(c) return {c.R, c.G, c.B} end
local function tableToColor3(t) return Color3.new(t[1], t[2], t[3]) end

local function serializeInstance(obj)
	local HttpService = game:GetService("HttpService")

	local data = {
		ClassName = obj.ClassName,
		Name = obj.Name,
		UniqueId = HttpService:GenerateGUID(false),
		Properties = {},
		Children = {}
	}


	local props = data.Properties
	if obj:IsA("BasePart") then
		-- Handle WeldConstraint linking
		if obj:IsA("WeldConstraint") and obj.Part1 then
			local part1Name = Instance.new("StringValue")
			part1Name.Name = "Part1Name"
			part1Name.Value = obj.Part1.Name
			part1Name.Parent = obj
		end
	
		props.Size = vec3ToTable(obj.Size)
		props.Color = color3ToTable(obj.Color)
		props.Material = obj.Material.Name
		props.CFrame = cframeToTable(obj.CFrame)
		props.Anchored = obj.Anchored
		props.Orientation = vec3ToTable(obj.Orientation)
		props.Transparency = obj.Transparency
		props.Reflectance = obj.Reflectance
		props.CanCollide = obj.CanCollide
		props.CastShadow = obj.CastShadow
	end

	if obj:IsA("Model") then
		props.PrimaryPart = obj.PrimaryPart and obj.PrimaryPart.Name or nil
	end

	if obj:IsA("MeshPart") then
		props.MeshId = obj.MeshId
		props.TextureID = obj.TextureID
	end

	local mesh = obj:FindFirstChildOfClass("SpecialMesh")
	if mesh then
		props.Mesh = {
			MeshType = mesh.MeshType.Name,
			MeshId = mesh.MeshId,
			TextureId = mesh.TextureId,
			Scale = vec3ToTable(mesh.Scale),
			Offset = vec3ToTable(mesh.Offset)
		}
	end

	if obj:IsA("Weld") or obj:IsA("WeldConstraint") or obj:IsA("Motor6D") then
		props.Part0 = obj.Part0 and obj.Part0:GetAttribute("__UniqueId") or nil
		props.Part1 = obj.Part1 and obj.Part1:GetAttribute("__UniqueId") or nil

		if obj:IsA("Weld") or obj:IsA("Motor6D") then
			props.C0 = cframeToTable(obj.C0)
			props.C1 = cframeToTable(obj.C1)
		end
	end

	for _, child in ipairs(obj:GetChildren()) do
		if not child:IsA("Script") and not child:IsA("LocalScript") then
			table.insert(data.Children, serializeInstance(child))
		end
	end
	obj:SetAttribute("__UniqueId", data.UniqueId)

	return data
end

local function serializeTool(tool)

	local toolData = {
		Name = tool.Name,
		Attributes = tool:GetAttributes(),
		Values = {},
		Structure = {},
		Tags = CollectionService:GetTags(tool)
	}

	for _, child in ipairs(tool:GetDescendants()) do
		if child:IsA("ValueBase") then
			table.insert(toolData.Values, {
				ClassName = child.ClassName,
				Name = child.Name,
				Value = child.Value
			})
		end
	end

	for _, child in ipairs(tool:GetChildren()) do
		if not child:IsA("Script") and not child:IsA("LocalScript") then
			table.insert(toolData.Structure, serializeInstance(child))
		end
	end

	return toolData
end

local function recreateInstance(data, parentLookup)
	local inst = Instance.new(data.ClassName)
	inst.Name = data.Name

	local props = data.Properties or {}

	if inst:IsA("BasePart") then
		if props.Size then inst.Size = tableToVec3(props.Size) end
		if props.Color then inst.Color = tableToColor3(props.Color) end
		if props.Material then inst.Material = Enum.Material[props.Material] end
		if props.CFrame then inst.CFrame = tableToCFrame(props.CFrame) end
		if props.Anchored ~= nil then inst.Anchored = props.Anchored end
		if props.Orientation then inst.Orientation = tableToVec3(props.Orientation) end
		if props.Transparency then inst.Transparency = props.Transparency end
		if props.Reflectance then inst.Reflectance = props.Reflectance end
		if props.CanCollide ~= nil then inst.CanCollide = props.CanCollide end
		if props.CastShadow ~= nil then inst.CastShadow = props.CastShadow end

		if props.Mesh then
			local mesh = Instance.new("SpecialMesh")
			mesh.MeshType = Enum.MeshType[props.Mesh.MeshType]
			mesh.MeshId = props.Mesh.MeshId
			mesh.TextureId = props.Mesh.TextureId
			mesh.Scale = tableToVec3(props.Mesh.Scale)
			mesh.Offset = tableToVec3(props.Mesh.Offset)
			mesh.Parent = inst
		end
	end

	if inst:IsA("MeshPart") then
		if props.MeshId then inst.MeshId = props.MeshId end
		if props.TextureID then inst.TextureID = props.TextureID end
	end

	if data.UniqueId then
		parentLookup[data.UniqueId] = inst
	end


	for _, childData in ipairs(data.Children or {}) do
		local child = recreateInstance(childData, parentLookup)
		child.Parent = inst
	end

	if inst:IsA("Model") and props.PrimaryPart then
		task.defer(function()
			local primary = inst:FindFirstChild(props.PrimaryPart)
			if primary and primary:IsA("BasePart") then
				inst.PrimaryPart = primary
				inst:SetPrimaryPartCFrame(primary.CFrame)
			end
		end)
	end
	

	if inst:IsA("Weld") or inst:IsA("WeldConstraint") or inst:IsA("Motor6D") then
		task.defer(function()
			local p0, p1 = parentLookup[props.Part0], parentLookup[props.Part1]
			if p0 and p0:IsA("BasePart") then inst.Part0 = p0 end
			if p1 and p1:IsA("BasePart") then inst.Part1 = p1 end

			if props.C0 then inst.C0 = tableToCFrame(props.C0) end
			if props.C1 then inst.C1 = tableToCFrame(props.C1) end
		end)
	end
	local weldsToFix = {}
	if inst:IsA("Weld") then
	table.insert(weldsToFix, {weld = inst, part0Name = props.Part0Name, part1Name = props.Part1Name})
end

	for _, weldInfo in ipairs(weldsToFix) do
		local weld = weldInfo.weld
		local part0 = inst:FindFirstChild(weldInfo.part0Name, true)
		local part1 = inst:FindFirstChild(weldInfo.part1Name, true)
		if part0 and part0:IsA("BasePart") then weld.Part0 = part0 end
		if part1 and part1:IsA("BasePart") then weld.Part1 = part1 end
	end

	return inst
end

local function recreateTool(data)
	local tool = Instance.new("Tool")
	tool.Name = data.Name

	for key, value in pairs(data.Attributes or {}) do
		tool:SetAttribute(key, value)
	end
	
	for _, oldVal in ipairs(tool:GetChildren()) do
		if oldVal:IsA("ValueBase") then
			oldVal:Destroy()
		end
	end

	for _, valData in ipairs(data.Values or {}) do
		local val = Instance.new(valData.ClassName)
		val.Name = valData.Name
		val.Value = valData.Value
		val.Parent = tool
	end


	local partLookup = {}
	for _, objData in ipairs(data.Structure or {}) do
		local part = recreateInstance(objData, partLookup)
		part.Parent = tool
	end
	

	
	local echoName = tool:FindFirstChild("EchoName")
	local weight = tool:FindFirstChild("Weight")
	if echoName and weight then
		tool.Name = echoName.Value .. " (" .. tostring(weight.Value) .. "kg)"
	end
	if data.Tags then
		for _, tag in ipairs(data.Tags) do
			CollectionService:AddTag(tool, tag)
		end
	end
	
	task.defer(function()
		for _, desc in ipairs(tool:GetDescendants()) do
			if desc:IsA("WeldConstraint") and typeof(desc.Part1) == "nil" and desc:FindFirstChild("Part1Name") then
				local part1Name = desc.Part1Name.Value
				local part1 = tool:FindFirstChild(part1Name, true)
				if part1 and part1:IsA("BasePart") then
					desc.Part1 = part1
				end
			end
		end
	end)


	return tool
end

local function saveTools(player)
	local backpack = player:FindFirstChild("Backpack")
	if not backpack then return end

	local serialized = {}
	for _, tool in ipairs(backpack:GetChildren()) do
		if tool:IsA("Tool") and CollectionService:HasTag(tool, "Echo") then
			table.insert(serialized, serializeTool(tool))
		end
	end

	pcall(function()
		ToolStore:SetAsync(tostring(player.UserId), serialized)
	end)
end

local function loadTools(player)
	local backpack = player:WaitForChild("Backpack")

	local success, data = pcall(function()
		return ToolStore:GetAsync(tostring(player.UserId))
	end)

	if success and data then
		for _, toolData in ipairs(data) do
			local tool = recreateTool(toolData)
			tool.Parent = backpack
		end
	else
		warn("No tools loaded for", player.Name)
	end
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function()
		task.delay(1, function()
			loadTools(player)
		end)
	end)
end)
game:BindToClose(function()
	for _, player in pairs(Players:GetPlayers()) do
		saveTools(player)
	end
end)

Players.PlayerRemoving:Connect(saveTools)


I know this isn’t relevant to the purpose of your post, but the visual style of your game so far is absolutely stunning. Are you considering doing beta testing soon? I would love to give feedback.

1 Like

Yea of course. I will notify you, actally at the same time I am making an FPS game. Oh and please tell me if you know how to fix it and rate the script as a data saver beacuse you know alot about it,

1 Like

That error is right - props.Part0Name does not exist, and it was never set in the first place when you serialized the data.

Select Part0Name and press Ctrl + f, you will see that it really is never defined

look:
image

you only set Part1Name

Oh yea my bad i forgot about it.

1 Like

Are you trying to deep-serialize an entire Instance? I will add that to DMOP since that is an obvious feature that is currently missing.

It is possible to do that with DMOP in the same way you are doing by customizing the serialization/deserialization code. Do you remember the modules in the DataTypes folder? That is where you can place custom serialization/deserialization code.

I think you will inevitably run into issues with this form of serialization if you ever need scripts to be included.

There is no way for you to create/modify/read the source of a script outside of a plugin, so you cannot uniquely identify a serialized script to even clone it.

(unless you give yourself some restriction like every single script in the game has a unique name)

Yea, but there is something called ID’s of script and all insances, will it work with this? Or giving the scripts unique ID’s or i have have idea, give the script and random number before saving to its name so it cloud save it by its name.

Btw I just finished the saving system for everything but not including the scripts.

1 Like

I don’t actually think it will work, because you can’t access the legitimate UniqueId property. The way you are doing it is by creating new UniqueGUID, which works perfectly if you need to reference those serialized items in the same way.

So yes, you can give these scripts a UniqueId in the same way you are doing, but that is it. There is no other information available to you about the script other than it’s name. This is why i think scripts are inherently incompatible with deep-serialization of Instances.

Consider you give a script a UniqueId. When you go to de-serialize the script, how are you gonna know which script to clone? That created UniqueId isn’t the same as the scripts actual UniqueId, you can’t even access that property.

(you have to Clone() it, because you also can’t create scripts)

The only other way to uniquely identify a script is by it’s source code, which you also do not have access to (outside of Plugin scripts which only run in Studio).

So with those points in consideration, the only way you’d realistically be able to do this is by giving yourself some arbitrary restriction like I mentioned before. I am confident that there is no good way to include Scripts with Instance deep-serialization.

I did spend a couple hours yesterday looking for a solution, because I really wanted Instances to be in DMOP. But i don’t think I will add them if I can’t support them completely without restriction.

If Roblox ever makes it so you can simply read the source code only from the server side, this would become no big issue.

Wait a second, is this a good idea to make a ID for the script, here is the reference:

SCRIPTNAME_19

The SCRIPTNAME is just a reference for the name of the script the is a _ and then the randomly generated ID so i think i have an idea before saving you give the _ and the randomly genereate ID then it will save it by its name like you said it cloud and then when it loads delete the ID and the _(ity extracts the SCRIPTNAME (so the name before the _ )and then it uses it as the script’s name).
This will continue at every leave of the game and make the same thing again @azavier123.

The issue I was mentioning will still persist. But yes, if you want to have this work in any form, you will need to save the Script Name and also a UniqueGUID to uniquely identify it in the serial.

The issue persists because unique scripts are allowed to have the same name in Roblox. If you want scripts to be included, you need to give yourself another restriction (on top of using more specific IDs) like “every unique script in the game has a unique name” so that you can actually re-construct the script properly.

I don’t think I have ever made a game before where every single unique script in the game has a unique name. But it’s not an outlandish restriction. So yes you can definitely do this, but make sure every unique script in the game has a unique identifier (otherwise you will not be able to differentiate them)

Imagine you have two tools that both have a script called “Controller” that run different code. There is no way for you to differentiate between the two :man_shrugging:

You could also do a cursed system where you give unique Attributes/Tags to the scripts instead, so you can name them whatever you want.

To be very clear. Every script that runs the same code should have the same Script ID. All scripts that run unique code should have unique script IDs. (by Script ID, I mean however you choose to identify these scripts, such as their Name)

Do keep in mind that you will have to be very deliberate to keep a system like this functional. You won’t be able to auto-populate those identifiers, so that’s just another thing you will have to do EVERY TIME you need a serializable script. And imagine if you ever mess up and give two scripts the same identifier. How would you ever fix the havoc that would create in the save structure? You’d have to delete those items from every player’s inventory.

Can I ask why you want to deep-serialize Instances in the first place? There is not a single case where it is necessary (which is why Roblox tells you not to do it), Cloneables are usually the way to go for your situation. Then you only have to worry about storing the actual data that you need rather than all of the bloat that comes with Instances.

For example, you deep serialize WeldConstraints. In reality, You only need to store an array which contains all descendant welds of the Instance (as in, their Part0 and Part1), and then re-construct the welds when de-serialization happens. Similar story for pretty much every piece of data you will need

First Post - I think i will deep seriazie it but can you tell me what do you mean bu the unique indenitficator please.
Second Post - A wise philosopher said: human stupidity has no limits.

Yes. What I mean by a unique identifier is a way to uniquely identify scripts. It does not really matter what the identifier is, but you certainly need a way to distinguish scripts apart from one another.

Roblox does not provide a way to distinguish scripts uniquely, so you have to come up with some rules for yourself to distinguish the scripts.

If you’re not following me still, you’re gonna end up having to clone() the scripts anyway. This is because you literally cannot create scripts at runtime. So you need a way to know exactly which script to clone. You don’t have access to the source code, or it’s actual UniqueId property, and using the Name property has the obvious issue I mentioned earlier.

So now you get to pick your poison. How are you gonna uniquely identify scripts to clone?

There are really only two “decent” options:

  1. Use the Name property and accept the fact that all unique code must correspond to a unique Name.

  2. Use an Attribute or a Tag, and just give them sequential numbered IDs.

In both cases, messing up and creating a duplicate identifier would be a disaster. But that’s what you’re accepting when you make a system like this.

maybe i will give each scipt an Attribute

1 Like