Strange error in code; I'm completely lost

I’ve got two functions, one that stores offsets of parts in a model relative to a given anchorpoint, another that resets all parts to the offset they originally had. (So if the anchor would move, they’d be updated and it would look like it all moves at once

I have an empty table in which I cache all the offsets.

local partOffsetsMain = {}

local function RecalculateParts(Model,Anchorpoint,datastore)
	for _, v in pairs (Model:GetChildren()) do
		if v ~= Anchorpoint then
			v.CFrame = Anchorpoint.CFrame * datastore[v]
		end
	end
end

local function StoreOffsets(Model,Anchorpoint,datastore)
	for _, v in pairs (Model:GetChildren()) do
		if v ~= Anchorpoint then
			datastore[v] = Anchorpoint.CFrame:toObjectSpace(v.CFrame)
		end
	end
	return true
end

I then call it as soon as an event is fired from the server to the client. I begin with clearing out the cache from previous data and then refill it with the new model.

Remotes["InteractDoor"].onClientEvent:connect(function(args)
    -- args[2] refers to the model.
	partOffsetsMain = {}
	repeat wait() until StoreOffsets(args[2].Door,args[2].Door.Hinge,partOffsetsMain) == true
	Run.RenderStepped:connect(function()
		if RecalculationEnabled.Door == true then
			RecalculateParts(args[2].Door,args[2].Door.Hinge,partOffsetsMain)
		end
	end)
	if args[1] == "Open" then
		local origin1 = args[2].Door.Hinge.CFrame
		local OpenDoorAnimation = TweenService:Create(args[2].Door.Hinge,TweenInfos.Door.Open,{CFrame = origin1 * CFrame.Angles(0,math.rad(-108),0)})
		RecalculationEnabled.Door = true
		OpenDoorAnimation:Play()
		OpenDoorAnimation.Completed:connect(function()
			RecalculationEnabled.Door = false
		end)
	elseif args[1] == "Close" then
		local origin1 = args[2].Door.Hinge.CFrame
		local CloseDoorAnimation = TweenService:Create(args[2].Door.Hinge,TweenInfos.Door.Close,{CFrame = origin1 * CFrame.Angles(0,math.rad(108),0)})
		RecalculationEnabled.Door = true
		CloseDoorAnimation:Play()
		CloseDoorAnimation.Completed:connect(function()
			RecalculationEnabled.Door = false
		end)
	end
end)

The problem is that the first time I approach a door and press G, which triggers the event, it works fine. But when I approach a new door, and thus a new model is given to the functions, I get the error:

01:17:46.428 - Players.thelolguy301.PlayerScripts.Interaction:34: bad argument #2 to ‘?’ (Vector3 expected, got nil)

Thus stating that the data does not exist.
After doing some testing I found out that the data DOES in fact exist; I had my script return the amount of values in partOffsetsMain and it always resulted in 5, being the amount of parts in my door models.

1 Like

How are you clearing out the cache before refilling it?

Right when the event is called

partOffsetsMain = {}
repeat wait() until StoreOffsets(args[2].Door,args[2].Door.Hinge,partOffsetsMain) == true

What’s the line of code that the error occurs on?

This:

v.CFrame = Anchorpoint.CFrame * datastore[v]

Which is called through this:

RecalculateParts(args[2].Door,args[2].Door.Hinge,partOffsetsMain)

Actually I see some little things in this code that might be causing issues. You make quite a few connections without disconnecting them.

You can replace

OpenDoorAnimation.Completed:connect(function()
	RecalculationEnabled.Door = false
end)

with

OpenDoorAnimation.Completed:Wait()
RecalculationEnabled.Door = false

And every time the remote InteractDoor is fired, a connection to RenderStepped is made that is never disconnected, which might be the cause of your issue. This code will run an additional time for each time you fire this remote, concurrently.

Run.RenderStepped:connect(function()
		if RecalculationEnabled.Door == true then
			RecalculateParts(args[2].Door,args[2].Door.Hinge,partOffsetsMain)
		end
end)

How do I best disconnect the RenderStepped function?

Can you move the connection outside of the function?

No because it requires arguments passed through the remove event.

Also it only happens when a new model is passed through to the StoreOffsets function and RecalculateParts function

Try something like this maybe.

spawn(function()
    repeat wait() until RecalculationEnabled.Door
    RecalculateParts(args[2].Door,args[2].Door.Hinge,partOffsetsMain)
end)

This will spawn another Lua thread that waits until the condition is true, then runs your function and exits. Variables should be in scope.

Should I do this within the event?

You should replace the connection to RenderStepped within the event’s function.

Aside, if ever args[1] is not “Open” or “Close”, a Lua thread will be left dangling in an infinite yield. If an exploiter were to spam this event, the server would explode. This code is not robust or reliable in any way. You should make an escape variable that will also trigger the repeat wait() until in a final else block, if invalid data is passed through the event.

This will only replicate the parts’ positions once though. While the renderstepped was to make sure the parts were all offsetted in every frame.

Sorry, you’re right. This code is really not a good design for what you’re trying to do lmao.
Your problems are most likely caused by your events never being disconnected though. Solving your problem looks more like a major refactor than a quick patch.
Let’s see what other people have to say.

Does the second door actually fail to animate?

The partOffsetsMain variable is not local to the onClientEvent handler, and this event connection, while nonfunctional while RecalculationEnabled.Door is false, is never disconnected:

    Run.RenderStepped:connect(function()
		if RecalculationEnabled.Door == true then
			RecalculateParts(args[2].Door,args[2].Door.Hinge,partOffsetsMain)
		end
	end)

The second time the RemoteEvent is fired, RecalculationEnabled.Door is set to true again, and the RenderStepped connection from the first door becomes active again, tries to run RecalculateParts for its door, and errors because the partOffsetsMain variable has been replaced with a table that doesn’t contain values for that door.

All doors animate regardless. What happens is it will begin to lag more and more and the errors occur starting from the second door.

Then it appears that what I described in the rest of my post is indeed what is happening.

I’d suggest modifying the code as follows:

Remotes["InteractDoor"].onClientEvent:connect(function(args)
    -- args[2] refers to the model.
	-- make this local:
	local partOffsetsMain = {}
	repeat wait() until StoreOffsets(args[2].Door,args[2].Door.Hinge,partOffsetsMain) == true
	local connection = Run.RenderStepped:connect(function()
		-- toggling this with a variable is no longer necessary
		RecalculateParts(args[2].Door,args[2].Door.Hinge,partOffsetsMain)
	end)
	if args[1] == "Open" then
		local origin1 = args[2].Door.Hinge.CFrame
		local OpenDoorAnimation = TweenService:Create(args[2].Door.Hinge,TweenInfos.Door.Open,{CFrame = origin1 * CFrame.Angles(0,math.rad(-108),0)})
		OpenDoorAnimation:Play()
		OpenDoorAnimation.Completed:connect(function()
			connection:disconnect()
		end)
	elseif --...
		-- same thing for this branch
	end
end)
3 Likes

Woohoo! That worked! My god, I am forever thankful for that. It works like a charm now. :slight_smile: Should I use this method more where I disconnect a renderstepped connection? Is this because it stacks up multiple threads of the same function? In other words like doing:

game:GetService("RunService").RenderStepped:connect(function()
    print("thread1")
end)
game:GetService("RunService").RenderStepped:connect(function()
    print("thread2")
end)
game:GetService("RunService").RenderStepped:connect(function()
    print("thread3")
end)
1 Like

Yes, exactly.

2 Likes