Humanoid:MoveToFinished function can't be Disconnected

I have no idea what I could be doing wrong, but for some reason I can’t seem to disconnect the function from the :MoveToFinished event, so whenever I use Humanoid:MoveTo, it fires even when it shouldn’t.

I’m working on a changing rooms system; when the player enters a store, the script starts checking for the distance between the player and the room, using RunService. Once the player is close enough and if the room is available, the player is automatically pulled inside the room using the :MoveTo function. Once the player is inside the room, the script opens the UI to customize the character, changes the camera position for an upclose view, etc.

The issue is that when I press the exist button to leave the changing room, I use :MoveTo to move the character to the previous position outside the room, but for some reason it also fires the MoveToFinished function, even if I disconnected it right after the player initially walked into the changing room.

--SERVICES--
local Players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
local RunS = game:GetService("RunService")
local ZonePlus = require(RS:WaitForChild("Zone"))

--PLAYER INSTANCES--
local Plr = Players.LocalPlayer
local Character = Plr.Character or Plr.CharacterAdded:Wait()
local Controls = require(Plr.PlayerScripts:WaitForChild("PlayerModule")):GetControls()
local Dummy

--REMOTES--
local RE = RS:WaitForChild("RoomsRemote")
local ShopData = RE:WaitForChild("RoomsInfo"):InvokeServer("GetData","Shops")
local RoomsData = RE.RoomsInfo:InvokeServer("GetData","ChangingRooms")

--OVERLAP PARAMS--
local OP = OverlapParams.new()
OP.FilterType = Enum.RaycastFilterType.Whitelist
OP.FilterDescendantsInstances = {Character}
OP.CollisionGroup = "Default"

--VARIABLES--
local DistanceChecks = {}
local inArea = false
local AreaName
local MT
local usingRoom = false

----FUNCTIONS----
local function startTryon(Background)
--Removed this part of the code 'cause it's not relevant to the problem. 
--This part simply opens the UI and changes the camera position, nothing else.
end

local function enterRoom(Room,Background)
	local Curtain = Room:FindFirstChild("Curtain")
	Curtain.Transparency = 1
	Curtain.CanCollide = false
	
	Character:SetAttribute("ogPos",Character.HumanoidRootPart.Position)
	
	Controls:Disable()
	Character.Humanoid:MoveTo(Room.Platform.Position)
	
	MT = Character.Humanoid.MoveToFinished:Connect(function(reached)
		if not reached then
			local charSize = Character:GetExtentsSize()
			local cf = CFrame.new(0,charSize.Y/2,0)
			local dummyCF = Room.Platform.CFrame:ToWorldSpace(cf)
			Character.PrimaryPart.CFrame = dummyCF
		end
		
		task.wait(0.5)
		if not usingRoom then
			
			startTryon(Background)
			Curtain.Transparency = 0.5
		
			if MT ~= nil then
				MT:Disconnect()
				MT = nil
			end --Tried to disconnect, but it doesn't seem to work.
			--I also tried without this if statement, 
			--but for some reason it errors because MT is nil??

		usingRoom = true
		end
	end)
end



local function checkDistanceRooms(room)
	local Model = room.Model
	DistanceChecks["Room"..Model:GetAttribute("ID")] = RunS.Stepped:Connect(function(time, delta)
		if Character.Humanoid:GetState() ~= Enum.HumanoidStateType.Dead and Plr:DistanceFromCharacter(Model.Platform.Position) <= 20 then
			
--I think this part runs more than once even if i put a conditional debounce with usingRoom??
			if not Model:GetAttribute("isBusy") and not usingRoom then
				task.wait()
				local isBusy = RE.RoomsInfo:InvokeServer("isBusy",room.ID)
				if isBusy then return 
				else
					Model:SetAttribute("isBusy",true)
					RE:FireServer(Model)
					enterRoom(Model,room.Background)
					print("near changing room ID: "..Model:GetAttribute("ID"))
				end
			end
		end
	end)
end


local function zoneCheck(Shop)
	local Area = Shop.Area
	local Bound = ZonePlus.new(Area)
	
	Bound.localPlayerEntered:Connect(function()
		inArea = true
		AreaName = Area.Name
		warn("player joined: "..Area.Name)
		
		for i,room in pairs(RoomsData) do
			task.spawn(function()
				checkDistanceRooms(room)
			end)
		end
	end)
	
	Bound.localPlayerExited:Connect(function()
		print("player left: "..Shop.ShopName)
		
		for i,room in pairs(Shop.ChangingRooms) do
			local ID = room:GetAttribute("ID")
			DistanceChecks["Room"..ID]:Disconnect()
			DistanceChecks["Room"..ID] = nil
		end
	end)
end

--This is where the problem is I believe, as you can see this function used :MoveTo.
--The problem is, I don't want the :MoveToFinished part above to fire, hence why I tried to use :Disconnect.
--Because it doesn't work, I tried to override it with another (empty) :MoveToFinished, but instead both functions are ran.

Plr.PlayerGui.ChangingRoom.Base.Leave.TextButton.MouseButton1Down:Connect(function()
	usingRoom = false
	Plr.PlayerGui.ChangingRoom.Enabled = false
	workspace.CurrentCamera.CameraType = Enum.CameraType.Custom
	Character.Humanoid:MoveTo(Character:GetAttribute("ogPos") + Vector3.new(5,0,5))

	Dummy:Destroy()
	MT = Character.Humanoid.MoveToFinished:Connect(function(reached)
		print("pls do nothing")
	end)

	print("PLS WORK omg")
	Controls:Enable()
end)


for i,v in pairs(ShopData) do
	zoneCheck(v)
end

I commented the code with extra informations, so please check it before replying.
Thanks in advance for helping!

Sorry for bumping, but I really need help with this lol…

Your lines here in function enterRoom:

MT = Character.Humanoid.MoveToFinished:Connect(function(reached)

and here in event handler for mouse down:

MT = Character.Humanoid.MoveToFinished:Connect(function(reached)

You’re defining the event handler twice onto the same variable MT, so MT is getting clobbered with the new definition. Since the old definition is being lost, when you do the disconnect, it’s on the second definition only. The first definition cannot be disconnected at that point because it’s been lost. This will contribute to a memory leak which will chew through resources over time causing lag in increased memory usage until the point of memory exhaustion.

Yeah, I know, but that’s exactly how it “should” be. The problem is that it does NOT get “lost”, instead they both fire even if MT is replaced with the new definition. I want to disconnect the function from MT once the enterRoom part is done, so that when the player presses the leave button, the previous one does not fire.

The way your code currently is, there’s a memory leak and both event handlers are defined. When the first one fires, it will disconnect the second one, leaving the first one in enterRoom intact.

Looking at your comments, you can place a disconnect inside the mouse button event handler and it should be fine.

But, I think you might have what is known as a race condition going on. If the event fires before the other script disconnects, it’s going to execute the event regardless because the instruction pointer is already inside the event handler.

Okay, I see. How can I fix it? I don’t see how it could be a memory leak considering MT is being disconnected in the first function, but clearly I’m the one struggling here lol.

Well, only define the event handler once. That will stop the memory leak. If you need to define it again for some reason, assign the connection to a different variable. As to the rest, what are you trying to accomplish?

When I’m trying to troubleshoot weird problems like this, I put print statements everywhere so I can see how things are executing. The race condition that I’m talking about is the MoveToFinished event and the MouseButton1Down event. Depending on when something one fires, the interpreter can be executing inside the other handler when it’s interrupted. So if MoveToFinished fires and then is interrupted by MouseButton1Down which disconnects the MoveToFinished event handler, when MouseButton1Down returns, it goes back into MoveToFinished, and it will finish and return from that.

Add print statements here:

	MT = Character.Humanoid.MoveToFinished:Connect(function(reached)
		if not reached then
			print("MoveToFinished:", "Not Reached")
			local charSize = Character:GetExtentsSize()
			local cf = CFrame.new(0,charSize.Y/2,0)
			local dummyCF = Room.Platform.CFrame:ToWorldSpace(cf)
			Character.PrimaryPart.CFrame = dummyCF
		end
		
		task.wait(0.5)
		if not usingRoom then
			print("MoveToFinished:", "Not usingRoom")
			
			startTryon(Background)
			Curtain.Transparency = 0.5
		
			if MT ~= nil then
				MT:Disconnect()
				MT = nil
				print("MoveToFinished:", "Disconnect", MT)
			end --Tried to disconnect, but it doesn't seem to work.
			--I also tried without this if statement, 
			--but for some reason it errors because MT is nil??

			usingRoom = true
		end
	end)

And here:

Plr.PlayerGui.ChangingRoom.Base.Leave.TextButton.MouseButton1Down:Connect(function()
	usingRoom = false
	print("MouseButton1Down:", "Event Fired")
	Plr.PlayerGui.ChangingRoom.Enabled = false
	workspace.CurrentCamera.CameraType = Enum.CameraType.Custom
	Character.Humanoid:MoveTo(Character:GetAttribute("ogPos") + Vector3.new(5,0,5))

	Dummy:Destroy()
	MT = Character.Humanoid.MoveToFinished:Connect(function(reached)
		print("pls do nothing")
	end)

	print("PLS WORK omg")
	Controls:Enable()
end)

Hi, so I placed the print statements where you told me to, and this happened:
image

It seems to print multiple times the same thing, for example the “near changing room ID” part and later the print statements you told me to put. I believe it’s because of the checkDistanceRooms function which contains a RunService loop… it’s weird because it should only run the code inside once, since i put an if statement to prevent that in this part:

if not Model:GetAttribute("isBusy") and not usingRoom then
				task.wait()
				local isBusy = RE.RoomsInfo:InvokeServer("isBusy",room.ID)
				if isBusy then return 
				else
					Model:SetAttribute("isBusy",true)
					RE:FireServer(Model)
					enterRoom(Model,room.Background)
					
					print("near changing room ID: "..Model:GetAttribute("ID"))
				end
			end

Okay so, it seems like this happens because the loop keeps creating a new connection (roughly 3) before the isBusy attribute is set to true. How can I fix this?

Do not put that code in a run service loop. The run service loop is not meant for network activity because it runs about every 0.016 milliseconds. Network latency can be anywhere from that to several hundred milliseconds. Since you are expecting a value from the server, your best bet would be to use a RemoteFunction to do that. A remote function will wait for a value from the server before proceeding.