Expected ':' not '.' calling member function WaitForChild (wrapper/metatables)

Hello!

I’ve run into this weird issue where I can’t use :WaitForChild on a wrapper for some reason. No idea why but it’s kind of frustrating.

['CreateDummy'] = function(self)
		local currentDummy = dummy:Clone()
		local humanoidRootPart = currentDummy:WaitForChild('HumanoidRootPart')
		local humanoid = currentDummy:WaitForChild('Humanoid')
		currentDummy.Parent = dummyFolder
		local pathContainer = currentDummy:WaitForChild('Path')
		pathContainer.Value = pathfindingService:CreatePath()
		local funcs = { -- this is just the added functionality, nothing here should be causing the issue as it works smoothly aside from the error
			['MoveToActive'] = false;
			['Status'] = 'Roaming';
			['GenerateRandomPoint'] = function(self, radius)
				local currentPos = humanoidRootPart.Position
				local atts = 0
				repeat 
					local randomPos = currentPos + Vector3.new(math.random(-radius, radius), 0, math.random(-radius, radius))
					atts += 1
					print(atts)
					pathContainer.Value:ComputeAsync(currentPos, randomPos)
				until pathContainer.Value.Status == Enum.PathStatus.Success
			end;
			['PlayPath'] = function(self)
				self.MoveToActive = true
				for i,v in ipairs(pathContainer.Value:GetWaypoints()) do
					local currentWaypoint = Instance.new('Part')
					currentWaypoint.Parent = debugFolder
					currentWaypoint.Material = Enum.Material.Neon
					currentWaypoint.Size = Vector3.new(1,1,1)
					currentWaypoint.CanCollide = false
					currentWaypoint.Shape = Enum.PartType.Ball
					currentWaypoint.Anchored = true
					currentWaypoint.Position = v.Position
					currentWaypoint.CanCollide = false
					self.Dots[#self.Dots + 1] = currentWaypoint
				end
				for i,v in ipairs(pathContainer.Value:GetWaypoints()) do
					if self.MoveToActive then
						humanoid:MoveTo(v.Position)
						print('mt', v.Position)
						humanoid.MoveToFinished:Wait()
						print('mtf')
					else
						break
					end
				end
				pathContainer.Value = pathfindingService:CreatePath()
			end;
			['CancelPath'] = function(self)
				self.MoveToActive = false
			end;
			['DestroyDots'] = function(self)
				for i,v in pairs(self.Dots) do
					v:Destroy()
				end
			end;
			['Dots'] = {}
		}
		local meta = { -- this is the metatable
			__index = dummy
		}
		return setmetatable(funcs, meta)
	end;

Now, from the other script that actually requires the dummy,

local currentDummy = dummyHandler:CreateDummy()
local dummyHumanoidRootPart = currentDummy:WaitForChild('HumanoidRootPart') -- this is the line that errors

TIA for any help! :smile:

1 Like

Probably because it does not know what currentDummy is you need to return the dummy

1 Like

I’m returning the metatable whose __index is dummy, that metatable is assigned to the currentDummy value. It recognizes that WaitForChild is a function (otherwise it would error with attempt to call a nil value) so that’s what is causing the issue. I just don’t know how to fix it.

1 Like

Do

currentDummy.HumanoidRootPart
--it may not know it is an dummy and to make a new dummy just use the plugin
1 Like

I want to use :WaitForChild as a sanity check. It probably won’t ever error but I just want to keep it just in case.

Well it is making waitforchild a nil value because it does not know what it is for the var currentDummy just do the code I did before

1 Like

Tell me what happens :grinning_face_with_smiling_eyes:

It’s not going to error, :WaitForChild is essentially the same as indexing it just with an added safety measure if the child didn’t replicate instantly for whatever reason. currentDummy is also already being assigned as the metatable. It also doesn’t matter if I haven’t predefined metatable.WaitForChild, it should inherit all members from the actual dummy model instance.

Also, as I told you before, if metatable.WaitForChild didn’t exist, it would give the “attempt to index nil” error.

If you do type(metatable.WaitForChild) it will return ‘function’ because it does exist, the self variable just isn’t being passed for whatever reason.

1 Like

Is it a localscript if not it is not needed

Not sure if this post could help the issue? It does seem a bit relevant to what you could be facing

1 Like

No, it’s a server script.

@JackscarIitt Yeah, I took a look into that post but it isn’t what I was looking for unfortunately. The self variable just isn’t being passed and I can’t figure out why. Basically on the backend it’s probably something like function module.WaitForChild(self, name) if not self then error('expected : not . (insert rest of error message lol)'). Using a : just automatically passes the self variable which is why game:GetService(‘Workspace’) is the same as game.GetService(game, ‘Workspace’)

The problem is, you’re passing currentDummy as the first argument to the WaitForChild method, this is essentially doing dummy.WaitForChild(currentDummy, "HumanoidRootPart"), not dummy.WaitForChild(dummy, "HumanoidRootPart"), because x:y(...) is the sugar syntax for x.y(x, ...) (I guess you already knew this but the x in that example doesn’t refer to the __index metafield). __index metafield doesn’t really mean it’ll inherit the value from the field, it means it’ll index whatever the field is set for __index as a fallback for indexing.

2 Likes

Gotcha.

Do you have any suggestions for how I could go about solving it though? I was thinking like

__index = function(tbl, key)
    if currentDummy[key] then

then if I did

tbl:WaitForChild('str')

it would return that it exists but I don’t know how I would get the argument passed.

Another problem is that there’s no protection if it errors which is kind of where I’m stuck. It would probably be easily fixable if I could get the args passed but I’m not really sure. I could probably use pcall but I don’t really want to use that method since it’s kind of clunky. It also runs me back into the problem of not knowing how to get the arguments passed.

I’m not sure exactly which argument you mean when you say “get the argument passed”?
Are you referring to the original dummy table being used as the index?

1 Like

The colon operator is syntactic sugar to call a function in a table. Doing object:func(...) is the same thing as doing object.func(object, ...).

When you call the function WaitForChild with a colon, it’s essentially doing currentDummy.WaitForChild(currentDummy, 'HumanoidRootPart'). currentDummy is not an instance so that is why the error occurs.

Try setting this as your __index metamethod:

__index = function(_, index, value)
    local member = dummy[index]
    if typeof(member) == "function" then
        return function(_, ...)
            return member(dummy, ...)
        end
    end
    return member
end