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.
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,
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:
you only set Part1Name
Oh yea my bad i forgot about it.
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.
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
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:
-
Use the
Name
property and accept the fact that all unique code must correspond to a uniqueName
. -
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