invalid argument #3 (Instance expected, got string)

Viewed 349

I was looking for a solution of this issue in search engines, but can't find one.

Though there's one that is almost identical, which is 'Invalid Argument #2 (String Expected, got Instance)', I tried looking ways to fix it in reverse, but it uses tostring(), and my issue is about the newest feature: Instance Attributes, which it doesn't support Instances as value, and the only thing I could do is to convert it as a string, now that the issue is about the Part.Parent, which uses Adornee/Instance or something on which I don't know the workaround for this

local LocalPlayer   =   Players.LocalPlayer;
local Mouse         =   LocalPlayer:GetMouse();

local function Part_Hide()
    if (Toggle_MouseHover == true and Mouse.Target ~= nil) then
        if (ReplicatedStorage:FindFirstChild("[Part_Storage]") == nil) then
            local Model         =   Instance.new ("Model", ReplicatedStorage);
            Model.Name          =   "[Part_Storage]";
            --Mouse.Target:SetAttribute("Source", Mouse.Target:GetFullName())
            Mouse.Target:SetAttribute("Source", Mouse.Target.Name)
            Mouse.Target.Parent =   Model;
        else
            local Model         =   ReplicatedStorage:FindFirstChild("[Part_Storage]");
            --Mouse.Target:SetAttribute("Source", Mouse.Target:GetFullName())
            Mouse.Target:SetAttribute("Source", Mouse.Target.Name)
            Mouse.Target.Parent =   Model;
        end
    end
end

local function Part_Unhide()
    if (ReplicatedStorage:FindFirstChild("[Part_Storage]") ~= nil) then
        local Storage   =   ReplicatedStorage:FindFirstChild("[Part_Storage]");
        for _, Child in pairs (Storage:GetChildren()) do
            --local Source  =   Child:GetAttribute("Source");
            --Child.Parent  =   Source
            Child.Parent    =   Child:GetAttribute("Source");   --  <-- Cause of the issue
        end
    end
end

I don't know if there's a way for an Attribute to accept Instances as value

Child.Parent = Child:GetAttribute("Source");

Is there a way to either convert String into an Instance, or an alternative way of saving an Instance as a value, or any kinds of workarounds for this issue? If so, answers are appreciated, thanks in advance!

2 Answers

As you've pointed out, Instances aren't acceptable as an Attribute type.

One workaround might be to create an ObjectValue instead, and use that to hold onto the Instance reference. You can stuff it into the object before moving it over to ReplicatedStorage, and then just delete it when you pull it out of storage.

local LocalPlayer = Players.LocalPlayer
local Mouse = LocalPlayer:GetMouse()

-- if the container for part storage doesn't exist, create it
local PartStorage = ReplicatedStorage:FindFirstChild("[Part_Storage]")
if (PartStorage == nil) then
    PartStorage = Instance.new("Model", ReplicatedStorage)
    PartStorage.Name = "[Part_Storage]"
end

local function Part_Hide()
    if (Toggle_MouseHover == true and Mouse.Target ~= nil) then
        -- alias the Mouse Target
        local Part = Mouse.Target
        local PartSource = Part.Parent

        -- move the part to storage
        Part.Parent = PartStorage

        -- hold onto the reference to where this object came from
        local ParentRef = Instance.new("ObjectValue")
        ParentRef.Value = PartSource
        ParentRef.Name = "ParentRef"
        ParentRef.Parent = Part
    end
end

local function Part_Unhide()
    -- move all parts out of storage
    for _, Child in pairs (PartStorage:GetChildren()) do
        -- remove the ObjectValue from the child
        local PartSource = Child:FindFirstChild("ParentRef")
        PartSource.Parent = nil

        -- place the child back into the world
        Child.Parent = PartSource.Value

        -- clean up
        PartSource:Destroy()
    end
end

As a small note, unless this is intended, you shouldn't do these kinds of world changing operations in a LocalScript, as those changes will only show up for the player that owns the LocalScript. The changes will be replicated to all players if you perform the work in a serverside Script.

I finally managed to make it work, thank you @Kylaaa

Turns out that your method of ObjectValue really works! I was looking for something like that, which holds a value of an instance, though I end up finding Roblox's newest feature and began focusing on it instead (which was my first mistake)

The second mistake was me not using aliases, because I thought using Instance properties/attributes directly would just work same as one being aliased and the alias part would be just for decoration or something that just adds for a line(something like making it easy for reference)

This is now the complete code:

local function Part_Hide()
    if (Toggle_MouseHover == true and Mouse.Target ~= nil) then
        local Part          =   Mouse.Target;
        local Part_Source   =   Part.Parent;
        local Source        =   Instance.new ("ObjectValue", Part);
        Source.Value        =   Part_Source;
        Source.Name         =   "Source";
        if (ReplicatedStorage:FindFirstChild("[Part_Storage]") == nil) then
            local Model =   Instance.new ("Model", ReplicatedStorage);
            Model.Name  =   "[Part_Storage]";
            Part.Parent =   Model;
        else
            local Model =   ReplicatedStorage:FindFirstChild("[Part_Storage]");
            Part.Parent =   Model;
        end
    end
end

local function Part_Unhide()
    if (ReplicatedStorage:FindFirstChild("[Part_Storage]") ~= nil) then
        local Storage   =   ReplicatedStorage:FindFirstChild("[Part_Storage]");
        for _, Child in pairs (Storage:GetChildren()) do
            local Source    =   Child:FindFirstChild("Source");
            Child.Parent    =   Source.Value;
            Source:Destroy();
            Storage:Destroy();
        end
    end
end
Related