Disconnect all remoteevents when unequipping a tool

I’ve modified the script to be modular, so if you change the names at the top to whatever your gun’s name and the names of the remote events, it will work. I’ve also cleaned up some code to help you out:

local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local LogService = game:GetService("LogService")
local RunService = game:GetService("RunService")

local GUN_EVENT_NAME = "MP5SD RE"
local GUN_NAME = "MP5SD"

local FireEvent = ReplicatedStorage[GUN_EVENT_NAME]:WaitForChild(GUN_NAME .. "Fire")
local FireLastEvent = ReplicatedStorage[GUN_EVENT_NAME]:WaitForChild(GUN_NAME .. "FireLast")
local ReloadNotEmptyEvent = ReplicatedStorage[GUN_EVENT_NAME]:WaitForChild("Reload" .. GUN_NAME .. "NotEmpty")
local ReloadEmptyEvent = ReplicatedStorage[GUN_EVENT_NAME]:WaitForChild("Reload" .. GUN_NAME ..  "Empty")

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()

local connections = {}

local function setupViewModel()
	-- Destroy existing viewmodel if it exists
	if workspace.CurrentCamera:FindFirstChild("ViewModelMP5SD") then
		workspace.CurrentCamera.ViewModelMP5SD:Destroy()
	end

	-- Get the camera
	local camera = workspace.CurrentCamera

	-- Clone the viewmodel from ReplicatedStorage
	local viewmodel = ReplicatedStorage:WaitForChild("ViewModelMP5SD"):Clone()
	viewmodel.Parent = workspace.CurrentCamera

	-- Get the Animator
	local animator = viewmodel:WaitForChild("Humanoid"):WaitForChild("Animator")

	-- Load the animations
	local Ranimation = Instance.new("Animation")
	Ranimation.AnimationId = "rbxassetid://121937746273937" -- Replace with your animation ID
	local RanimationTrack = animator:LoadAnimation(Ranimation)
	RanimationTrack.Priority = Enum.AnimationPriority.Action2
	local REanimation = Instance.new("Animation")
	REanimation.AnimationId = "rbxassetid://75766370456110" -- Replace with your animation ID
	local REanimationTrack = animator:LoadAnimation(REanimation)
	REanimationTrack.Priority = Enum.AnimationPriority.Action4
	local REEmptyAnimation = Instance.new("Animation")
	REEmptyAnimation.AnimationId = "rbxassetid://111792193575216" -- Replace with your animation ID for empty reload
	local REEmptyAnimationTrack = animator:LoadAnimation(REEmptyAnimation)
	REEmptyAnimationTrack.Priority = Enum.AnimationPriority.Action4
	local RanimationL = Instance.new("Animation")
	RanimationL.AnimationId = "rbxassetid://118944095797406" -- Replace with your animation ID for last shot
	local RanimationLTrack = animator:LoadAnimation(RanimationL)
	local WalkAnimation = Instance.new("Animation")
	WalkAnimation.AnimationId = "rbxassetid://88135397333059" -- Replace with your animation ID
	local WalkAnimationTrack = animator:LoadAnimation(WalkAnimation)
	WalkAnimationTrack.Looped = true

	-- Function to stop all playing animations
	local function stopAllAnimations()
		for _, track in ipairs(animator:GetPlayingAnimationTracks()) do
			track:Stop()
		end
	end

	-- Recoil for camera animation
	local function moveCameraUp()
		local currentCFrame = camera.CFrame
		local newCFrame = currentCFrame * CFrame.Angles(math.rad(0.8), 0, 0)
		camera.CFrame = newCFrame
	end

	-- Function to play the animations
	local function RecoilAnimation()
		stopAllAnimations()
		RanimationTrack:Play()
	end

	local function ReloadAnimation()
		stopAllAnimations()
		REanimationTrack.Priority = Enum.AnimationPriority.Action4
		REanimationTrack:Play()
	end

	local function ReloadEmptyAnimation()
		stopAllAnimations()
		REEmptyAnimationTrack:Play()
	end

	local function LastShotAnimation()
		stopAllAnimations()
		RanimationLTrack:Play()
	end

	-- Lock position at the end of the last shot animation
	RanimationLTrack.Stopped:Connect(function()
		local lastFrameCFrame = viewmodel.PrimaryPart.CFrame -- Assuming PrimaryPart is set
		viewmodel:SetPrimaryPartCFrame(lastFrameCFrame)
	end)
	
	FireEvent.OnClientEvent:Connect(function()
		moveCameraUp()
		RecoilAnimation()
	end)
	
	FireLastEvent.OnClientEvent:Connect(function()
		moveCameraUp()
		LastShotAnimation()
	end)

	ReloadNotEmptyEvent.OnClientEvent:Connect(function()
		ReloadAnimation()
	end)

	ReloadEmptyEvent.OnClientEvent:Connect(function()
		ReloadEmptyAnimation()
	end)
	
	-- Function to handle walking animation
	local function updateWalkingAnimation()
		if character.Humanoid.MoveDirection.Magnitude > 0 then
			if not WalkAnimationTrack.IsPlaying then
				WalkAnimationTrack:Play()
			end
		else
			if WalkAnimationTrack.IsPlaying then
				WalkAnimationTrack:Stop()
			end
		end
	end
	-- Connect the walking animation update to the RenderStepped event
	local runConnection = RunService.RenderStepped:Connect(updateWalkingAnimation)

	local FireConnection = FireEvent.OnClientEvent:Connect(function() -- IMPORTANT PART
		moveCameraUp()
		RecoilAnimation()
	end)

	local FireLastConnection = FireLastEvent.OnClientEvent:Connect(function() -- IMPORTANT PART
		moveCameraUp()
		LastShotAnimation()
	end)
	local ReloadNotEmptyConnection = ReloadNotEmptyEvent.OnClientEvent:Connect(function() -- IMPORTANT PART
		ReloadAnimation()
	end)
	local ReloadDEmptyConnection = ReloadEmptyEvent.OnClientEvent:Connect(function() -- IMPORTANT PART
		ReloadEmptyAnimation()
	end)

	table.insert(connections, runConnection)
	table.insert(connections, FireConnection)
	table.insert(connections, FireLastConnection)
	table.insert(connections, ReloadNotEmptyConnection)
	table.insert(connections, ReloadDEmptyConnection)
end

local tool = player.Backpack:WaitForChild(GUN_NAME)
tool.Unequipped:Connect(function()
	for _, connection in connections do
		-- We verify that the connection isn't nil or already disconnected
		if connection then
			connection:Disconnect()
		end
	end

	-- Clean the table's memory pointers
	connections = {}
end)

local function onToolEquipped(toolEquipped)
	if toolEquipped.Name == GUN_NAME then
		setupViewModel()
	end
end

-- Connect the tool equipped event
player.Character.ChildAdded:Connect(function(child)
	if child:IsA("Tool") then
		onToolEquipped(child)
	end
end)

-- Handle tool replacement
player.Character.ChildRemoved:Connect(function(child)
	if child:IsA("Tool") and child.Name == GUN_NAME then
		setupViewModel()
	end
end)

i just have no idea why but it is still not working. theres nothing in the output.

Okay, so now we’ll do some troubleshooting after the changes we’ve made.

  1. Add a print statement inside the onToolEquipped function.
  2. Add a print statement inside the tool.Unequipped function.
  3. Add a print statement at the end of setupAnimations function.

We’re basically adding these prints to trace how far the script goes, and we’re isolating the location of the issue. If it doesn’t print all 3, then there’s a problem before the suspected print.

do u mean setupviewmodel i dont have a setupanimations

Yeah sorry, setupViewModel. In fact, add a print at the beginning and the end of that function, so we know if it’s even being called.

equip
image
unequip
image

I see, then it seems like something outside of the script was changed. Are the RemoteEvent names still the same? This is to ensure that the events are being fired when you click or reload.

1 Like

yup, they are the same. (this is just to make it over 30 )

What I like to do is have a table called connections.
Every connection I make will be stored inside the table.
Once I’m done with it, just loop through all of the connections and disconnect.
I also sometimes use Trove by Sleitnik that does it for me when I need more complex systems that need cleanup.

What changes have you made after we talked about re-using the code for the M4A1? I would revert back to the version that’s in the solution if it’s still not working. If the solution version isn’t working, then it’s most likely something that was changed outside of the script.

no changes after reusing the code. it just stopped forking for sum reason

now it works? idk what is happening lol

Ah okay, these types of bugs where it would intermittently stop working because of something missing is called Race Conditions. When a script sometimes works, it’s usually caused by a race condition. I’m assuming that the root of the problem is related to the reference of the script’s tool.

Can you send a screenshot of where your script is, and where you keep your tools? I suspect that something is wrong with the referencing in the script, but there could be other things as well. I’ll try to help you as much as possible.

image
this is the tools
image
scripts that we are talking about rn

Okay, looks good so far. Are there any warnings or errors in the output? Specifically anything to do with WaitForChild?

nothing in the output (for some reason it works now but i suspect it to stop working later) and waitforchild is used like once in the refrencing of the tool in the backpack thats how u use it ig

image

I see, well I’m scanning through the animation script and there doesn’t seem to be anything out of place. Is there some other LocalScript that handles the shooting and reloading? The script that handles your UserInputService stuff?

Here’s what I have for your animation script:

local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local LogService = game:GetService("LogService")
local RunService = game:GetService("RunService")

local GUN_EVENT_NAME = "MP5SD RE"
local GUN_NAME = "MP5SD"

local FireEvent = ReplicatedStorage[GUN_EVENT_NAME]:WaitForChild(GUN_NAME .. "Fire")
local FireLastEvent = ReplicatedStorage[GUN_EVENT_NAME]:WaitForChild(GUN_NAME .. "FireLast")
local ReloadNotEmptyEvent = ReplicatedStorage[GUN_EVENT_NAME]:WaitForChild("Reload" .. GUN_NAME .. "NotEmpty")
local ReloadEmptyEvent = ReplicatedStorage[GUN_EVENT_NAME]:WaitForChild("Reload" .. GUN_NAME ..  "Empty")

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()

local connections = {}

local function setupViewModel()
	-- Destroy existing viewmodel if it exists
	if workspace.CurrentCamera:FindFirstChild("ViewModelMP5SD") then
		workspace.CurrentCamera.ViewModelMP5SD:Destroy()
	end

	-- Get the camera
	local camera = workspace.CurrentCamera

	-- Clone the viewmodel from ReplicatedStorage
	local viewmodel = ReplicatedStorage:WaitForChild("ViewModelMP5SD"):Clone()
	viewmodel.Parent = workspace.CurrentCamera

	-- Get the Animator
	local animator = viewmodel:WaitForChild("Humanoid"):WaitForChild("Animator")

	-- Load the animations
	local Ranimation = Instance.new("Animation")
	Ranimation.AnimationId = "rbxassetid://121937746273937" -- Replace with your animation ID
	local RanimationTrack = animator:LoadAnimation(Ranimation)
	RanimationTrack.Priority = Enum.AnimationPriority.Action2
	local REanimation = Instance.new("Animation")
	REanimation.AnimationId = "rbxassetid://75766370456110" -- Replace with your animation ID
	local REanimationTrack = animator:LoadAnimation(REanimation)
	REanimationTrack.Priority = Enum.AnimationPriority.Action4
	local REEmptyAnimation = Instance.new("Animation")
	REEmptyAnimation.AnimationId = "rbxassetid://111792193575216" -- Replace with your animation ID for empty reload
	local REEmptyAnimationTrack = animator:LoadAnimation(REEmptyAnimation)
	REEmptyAnimationTrack.Priority = Enum.AnimationPriority.Action4
	local RanimationL = Instance.new("Animation")
	RanimationL.AnimationId = "rbxassetid://118944095797406" -- Replace with your animation ID for last shot
	local RanimationLTrack = animator:LoadAnimation(RanimationL)
	local WalkAnimation = Instance.new("Animation")
	WalkAnimation.AnimationId = "rbxassetid://88135397333059" -- Replace with your animation ID
	local WalkAnimationTrack = animator:LoadAnimation(WalkAnimation)
	WalkAnimationTrack.Looped = true

	-- Function to stop all playing animations
	local function stopAllAnimations()
		for _, track in ipairs(animator:GetPlayingAnimationTracks()) do
			track:Stop()
		end
	end

	-- Recoil for camera animation
	local function moveCameraUp()
		local currentCFrame = camera.CFrame
		local newCFrame = currentCFrame * CFrame.Angles(math.rad(0.8), 0, 0)
		camera.CFrame = newCFrame
	end

	-- Function to play the animations
	local function RecoilAnimation()
		stopAllAnimations()
		RanimationTrack:Play()
	end

	local function ReloadAnimation()
		stopAllAnimations()
		REanimationTrack.Priority = Enum.AnimationPriority.Action4
		REanimationTrack:Play()
	end

	local function ReloadEmptyAnimation()
		stopAllAnimations()
		REEmptyAnimationTrack:Play()
	end

	local function LastShotAnimation()
		stopAllAnimations()
		RanimationLTrack:Play()
	end

	-- Lock position at the end of the last shot animation
	RanimationLTrack.Stopped:Connect(function()
		local lastFrameCFrame = viewmodel.PrimaryPart.CFrame -- Assuming PrimaryPart is set
		viewmodel:SetPrimaryPartCFrame(lastFrameCFrame)
	end)
	
	FireEvent.OnClientEvent:Connect(function()
		moveCameraUp()
		RecoilAnimation()
	end)
	
	FireLastEvent.OnClientEvent:Connect(function()
		moveCameraUp()
		LastShotAnimation()
	end)

	ReloadNotEmptyEvent.OnClientEvent:Connect(function()
		ReloadAnimation()
	end)

	ReloadEmptyEvent.OnClientEvent:Connect(function()
		ReloadEmptyAnimation()
	end)
	
	-- Function to handle walking animation
	local function updateWalkingAnimation()
		if character.Humanoid.MoveDirection.Magnitude > 0 then
			if not WalkAnimationTrack.IsPlaying then
				WalkAnimationTrack:Play()
			end
		else
			if WalkAnimationTrack.IsPlaying then
				WalkAnimationTrack:Stop()
			end
		end
	end
	-- Connect the walking animation update to the RenderStepped event
	local runConnection = RunService.RenderStepped:Connect(updateWalkingAnimation)

	local FireConnection = FireEvent.OnClientEvent:Connect(function() -- IMPORTANT PART
		moveCameraUp()
		RecoilAnimation()
	end)

	local FireLastConnection = FireLastEvent.OnClientEvent:Connect(function() -- IMPORTANT PART
		moveCameraUp()
		LastShotAnimation()
	end)
	local ReloadNotEmptyConnection = ReloadNotEmptyEvent.OnClientEvent:Connect(function() -- IMPORTANT PART
		ReloadAnimation()
	end)
	local ReloadDEmptyConnection = ReloadEmptyEvent.OnClientEvent:Connect(function() -- IMPORTANT PART
		ReloadEmptyAnimation()
	end)

	table.insert(connections, runConnection)
	table.insert(connections, FireConnection)
	table.insert(connections, FireLastConnection)
	table.insert(connections, ReloadNotEmptyConnection)
	table.insert(connections, ReloadDEmptyConnection)
end

local tool = player.Backpack:WaitForChild(GUN_NAME)
tool.Unequipped:Connect(function()
	for _, connection in connections do
		-- We verify that the connection isn't nil or already disconnected
		if connection then
			connection:Disconnect()
		end
	end

	-- Clean the table's memory pointers
	connections = {}
end)

local function onToolEquipped(toolEquipped)
	if toolEquipped.Name == GUN_NAME then
		setupViewModel()
	end
end

-- Connect the tool equipped event
player.Character.ChildAdded:Connect(function(child)
	if child:IsA("Tool") then
		onToolEquipped(child)
	end
end)

-- Handle tool replacement
player.Character.ChildRemoved:Connect(function(child)
	if child:IsA("Tool") and child.Name == GUN_NAME then
		setupViewModel()
	end
end)

i use a fastcast gun so theres a localscript handling the inputs and a serverr doing everything else

the new one doesnt work but the solution one does now