GetInstanceAddedSignal Firing Twice - Need a workaround!

Edit: It turns out my issue is not to do with GetInstanceAddedSignal . Rather, the issue is a Script with RunContext set to Local running twice. Check the marked solution & my reply for more info.

Old post (click to open)

Hi, I’d like to stop GetInstanceAddedSignal from firing twice or find a way to only make my code run once, not twice. The issue is that this is a reported bug and is affecting my workflow; the workarounds I’ve researched don’t seem to work for me:

Double firing of CollectionService:GetInstanceAddedSignal when applying tag in same frame that object is added to DataModel - #11 by Reditect


Notes:

Many of my scripts use the same system of GetInstanceAddedSignal and GetInstanceRemovedSignal. All the Models are Atomic, the game has Streaming Enabled. The scripts are all local scripts (client-sided) under StarterGui. The code runs fine and works, apart from the fact that it is firing twice because GetInstanceAddedSignal is.

This is what the Console prints:

It prints twice, despite there being only one Lava Model:
image


Code:

Click to open dropdown
local CollectionService = game:GetService("CollectionService")
local resetPlayer_BindableEvent = game.Players.LocalPlayer.PlayerGui:WaitForChild("BindableEventsFolder"):WaitForChild("resetPlayer_BindableEvent")

local objectsLoadedIn_Table = {}
local tagForThisScript = "LavaTag"
local localPlayer = game.Players.LocalPlayer

local function makeObjectModelWhatItIs(objectModel_v)
	
	local objectDebounce = false
	local hitbox
	local WorldThisObjectIsIn_StringValue
	
	local success, errorMessage = pcall(function()
		hitbox = objectModel_v.hitbox
		WorldThisObjectIsIn_StringValue = objectModel_v.WorldThisObjectIsIn_StringValue
	end)
	--print(success,errorMessage)
	
	if success then
		
		hitbox.Touched:Connect(function(hit)
			local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
			if player and player == game.Players.LocalPlayer  then
				
				if objectDebounce == false then
					objectDebounce = true
					
					resetPlayer_BindableEvent:Fire()
					wait(0.5)
					objectDebounce = false
					
				end
				
			end
		end)
		
	end
	
	
end

-- to get future parts:
CollectionService:GetInstanceAddedSignal(tagForThisScript):Connect(function(newpart: Part)
	print("Check GetInstanceAddedSignal!")
	print(newpart:GetFullName())
	if table.find(objectsLoadedIn_Table, newpart) == nil then
		table.insert(objectsLoadedIn_Table, newpart)
		makeObjectModelWhatItIs(newpart)
	end
end)

-- to remove parts as they are unloaded:
CollectionService:GetInstanceRemovedSignal(tagForThisScript):Connect(function(oldpart: Part)
	--print("Check GetInstanceRemovedSignal!")
	local index = table.find(objectsLoadedIn_Table, oldpart)
	if index then
		table.remove(objectsLoadedIn_Table, index)
	end
end)

Links relevant to this:


Can anyone help with this?

try adding a debounce to stop it from firing twice

Does that if-condition checking if it’s already in the connection not work? As in even if the same item is being added multiple times, it still goes through the condition? It may be hairy having to add if-conditions like that in all your scripts but idk how else to handle it firing multiple times

1 Like

Like this?

Click to open dropdown
local lastAddedSignalObject

-- to get future parts:
CollectionService:GetInstanceAddedSignal(tagForThisScript):Connect(function(newpart: Part)
	
	if lastAddedSignalObject ~= newpart then
		lastAddedSignalObject = newpart
		
		print("Check GetInstanceAddedSignal T2!")
		--print(newpart:GetFullName())
		
		if table.find(objectsLoadedIn_Table, newpart) == nil then
			table.insert(objectsLoadedIn_Table, newpart)
			makeObjectModelWhatItIs(newpart)
		end
		
	else
		print("duplicate?")
	end
	
end)

Adding a wait or task.wait anywhere in the code doesn’t help either. It still fires twice as Roblox is firing it twice, I assume at the same time.


If I use debounceTest = false and check that, I risk missing an object that is tagged if there are multiple objects that are tagged. I plan on adding more Lava Models around my game, so that doesn’t work. Either way, it still fires twice.


Also, would this not be a debounce in of itself?

if table.find(objectsLoadedIn_Table, newpart) == nil then
   table.insert(objectsLoadedIn_Table, newpart)
   makeObjectModelWhatItIs(newpart)
end
1 Like

yes, that can work nicely as a debounce

I’m not sure what the problem is as your code looks like it should work fine (it avoids calling makeObjectModelWhatItIs twice, which was the problem caused by the bug).

A possible alternative solution that fits your purpose can be to make an existence table where it stores what objects are current streamed in.

local exists = {}

-->> not real event names; just for clarity
AddedSignal:Connect(function(Object)
    if exists[Object] then
        return
    end
    exists[Object] = true
    -->> rest of code
end)
RemovedSignal:Connect(function(Object)
    exists[Object] = nil
    -->> rest of code
end)

This solution only works for one tag. If you want to extend it to multiple tags, you can create multiple tables or have some sort of lookup method to see if something exists or not.

However, if you’re trying to find a more convenient workaround without having to repeat this same code over and over again, you can try to make a class that handles this for you:

--|| Example idea

local InstanceLoader = require(InstanceLoader)

--> will have the same functionality as the mentioned solution above, but in class form
local TagA = InstanceLoader.new("TagA")
TagA.InstanceAdded:Connect(function(Object)
    -->> code
end
1 Like

Hi, apologies for the delayed response. Anyhow, is what you describe not the same to what I already have?

Code (click to open)
local CollectionService = game:GetService("CollectionService")
local addOrSubtractPlayersHP_RemoteEvent = game.Workspace.RemoteEventsFolder.HP_SystemFolder.addOrSubtractPlayersHPonServer

local objectsLoadedIn_Table = {}
local tagForThisScript = "LavaTag"
local lastAddedSignalObject

local function makeObjectModelWhatItIs(objectModel_v)
	
	local objectDebounce = false
	local hitbox
	local WorldThisObjectIsIn_StringValue
	
	local success, errorMessage = pcall(function()
		hitbox = objectModel_v.hitbox
		WorldThisObjectIsIn_StringValue = objectModel_v.WorldThisObjectIsIn_StringValue
	end)
	--print(success,errorMessage)
	
	if success then
		
		hitbox.Touched:Connect(function(hit)
			local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
			if player and player == game.Players.LocalPlayer  then
				
				if objectDebounce == false then
					objectDebounce = true
										
					local numToAddOrSubToHp = -1
					addOrSubtractPlayersHP_RemoteEvent:FireServer(numToAddOrSubToHp)
					
					wait(0.5)
					objectDebounce = false
					
				end
				
			end
		end)
		
	end
	
	
end

-- to get future parts:
CollectionService:GetInstanceAddedSignal(tagForThisScript):Connect(function(newpart)
	
	if lastAddedSignalObject ~= newpart then
		lastAddedSignalObject = newpart
		
		--print("Check GetInstanceAddedSignal T2!")
		print(newpart:GetFullName())
		
		if table.find(objectsLoadedIn_Table, newpart) == nil then
			table.insert(objectsLoadedIn_Table, newpart)
			makeObjectModelWhatItIs(newpart)
		else
			print("duplicate check 2?")
			-- This doesn't even fire
		end
		
	elseif lastAddedSignalObject == newpart then
		print("duplicate check 1?")
		-- This doesn't even fire
	end
	
	
end)

-- to remove parts as they are unloaded:
CollectionService:GetInstanceRemovedSignal(tagForThisScript):Connect(function(oldpart: Part)
	--print("Check GetInstanceRemovedSignal!")
	local index = table.find(objectsLoadedIn_Table, oldpart)
	if index then
		table.remove(objectsLoadedIn_Table, index)
	end
end)

Line 4 → Table of things loaded in
Line 46 to 68 → AddedSignal Function w/ respect to table lookup
Line 71 to 77 → RemovedSignal Function w/ respect to table lookup


To recap: This isn’t working as GetInstanceAddedSignal is firing twice on the Client despite the table lookup. Thus, all code is done twice.

I’m confused about what the actual issue is. It was stated that GetInstanceAddedSignal was firing twice, but, what exactly is the issue being caused by the double signal fire? Is makeObjectModelWhatItIs running twice every single time an object is loaded?

1 Like

Sorry if I’m restating what you already understand. For everyone one object in the game, GetInstanceAddedSignal fires twice.

image
Note that there is only one Model here.


Note that there is only one print statement here.


Note that there are two lines of code printed, despite one Model and one line of code.


Yes. I will add a print statement under the function to show this:

image


Note that this print statement is being fired twice despite only one print statement.


makeObjectModelWhatItIs is being fired twice and print(newpart:GetFullName()) are being fired twice because CollectionService:GetInstanceAddedSignal is being fired twice by Roblox internally.

After extensive testing, I found that the event was firing twice… because the script itself was being run twice. I have a Script with RunContext set to Client in StarterPlayerScripts. This was the code:

print(game["Run Service"]:IsClient(), script:GetFullName())

local CS = game.CollectionService
CS:GetInstanceAddedSignal("test"):Connect(function(a)
	print("added", a)
end)
CS:GetInstanceRemovedSignal("test"):Connect(function(a)
	print("removed", a)
end)

print("a")
task.wait(3)
print("b")
Instance.new("Part", workspace):AddTag("test")

Indeed, GetInstanceAddedSignal runs twice. However, that’s not due to a faulty connection, but rather (more interestingly) because… the script itself runs twice, in two different locations. One in StarterPlayerScripts, and another instance in PlayerScripts:

This is most definitely unintentional behavior and should be reported as a bug.

Try printing script:GetFullName() to see if this is the case. This explains why makeObjectModelWhatItIs is running twice despite there being checks.

1 Like

You are correct. Interestingly, it does print twice:

I will update the OP.

Also, I was able to workaround the issue by doing the following:

if script.Parent.Parent.Parent.Name == "PlayerGui" then
   -- code here
else
	print("Checking if this fired elsewhere!")
	print(script.Parent.Parent.Parent.Name)
end

I’ll report the bug via @Bug-Support, but that might take some time. Thank you so much, this issue has been quite the annoyance. :smile: :pray:

1 Like

Just to clarify that this is not a bug but rather (unexpected) expected behavior. For the sake of simplicity we didn’t want to have any exceptions to where scripts using the new RunContext could run. Unfortunately that also resulted in some quirks such as the one you see here where the script runs in both StarterPlayerScripts and PlayerScripts.

The correct solution is to parent your script elsewhere or use a LocalScript instead.

Late last year we added some warnings to Studio that should make this more clear, if those aren’t showing then let me know because that certainly would be a bug :smile:

3 Likes

Hi, thanks for the response.

Could you share what the “warnings” would look like? I don’t see anything show up in the Output. I also do not see anything in the Properties menu on-hover:

image

When hovering over a localscript in the object list, there is a message, but it does not mention RunContext:

The warning would show when you run the place file. We could also look at adding them to the properties widget if that would make it more clear.

1 Like

Adding to the property widget would be good, yes. I assume a message would show up when hovering over the RunContext property itself.

That in combination with the warning in the console would be great at telling people the info. But, I wouldn’t want things to get spammed in the console of course.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.