Mark children for destruction after Destroying is fired instead of before

Currently, Destroy works in the following way:

  1. Mark children for destruction.
  2. Fire Destroying signal.
  3. Destroy marked children.

This can be proved with the following script:

local A = Instance.new("Folder")
A.Name = "A"
local B = Instance.new("Folder", A)
B.Name = "B"

A.Destroying:Connect(function()
	print("DESTROYING A")
	-- Remove children before they are destroyed?
	for _, child in A:GetChildren() do
		child.Parent = nil
		print("SALVAGED", child)
	end
end)
B.Destroying:Connect(function()
	print("DESTROYING B")
end)
A:Destroy()
--> DESTROYING A
--> SALVAGED B
--> DESTROYING B (didn't work)

To further prove this behavior, if Destroying is used to add children to the destroyed instance, those children are not destroyed:

local A = Instance.new("Folder")
A.Name = "A"
local B = Instance.new("Folder")
B.Name = "B"

A.Destroying:Connect(function()
	print("DESTROYING A")
	B.Parent = A
	print("ADDED B")
end)
B.Destroying:Connect(function()
	print("DESTROYING B")
end)
A:Destroy()
--> DESTROYING A
--> ADDED B (was not destroyed)

Notably, other descendants can be salvaged, because their parents have not yet been destroyed:

local A = Instance.new("Folder")
A.Name = "A"
local B = Instance.new("Folder", A)
B.Name = "B"
local C = Instance.new("Folder", B)
C.Name = "C"

A.Destroying:Connect(function()
	print("DESTROYING A")
	for _, descendant in A:GetDescendants() do
		descendant.Parent = nil
		print("SALVAGED", descendant)
	end
end)
B.Destroying:Connect(function()
	print("DESTROYING B")
end)
C.Destroying:Connect(function()
	print("DESTROYING C")
end)
A:Destroy()
--> DESTROYING A
--> SALVAGED B
--> SALVAGED C
--> DESTROYING B

I want to be able to use Destroying to salvage child instances by setting their Parent to nil before they are actually destroyed. I cannot do this because children are marked before Destroying is fired instead of after.

10 Likes

Doesn’t the current behavior make more sense? You first mark an object as, for example, “eatable”, then you tell everyone you’re gonna eat it, and after that you eat it. As far as I know the Engine cannot fire the .Destroying event if it’s not for destruction, or marked.

5 Likes

To clarify, I’m proposing the following change:

Current behavior:

  1. Mark children for destruction.
  2. Fire Destroying signal.
  3. Destroy marked children.

New behavior:

  1. Fire Destroying signal.
  2. Mark children for destruction.
  3. Destroy marked children.
1 Like

Well I think they mark children for deletion then fire due to conflicts with something else. Possibly to prevent the signal from firing if it could not mark the part for destruction. Just as @SomeFedoraGuy mentioned. You should fire the signal once the action has been done, because if it fails then you just fired a false signal.

3 Likes

What happens regarding parent locking

Does the instance get moved, Destroying gets fired, then its locked

Or Destroying gets fired, then the instance is moved and locked

1 Like

Personally I can’t see a use case here so if you could provide me one that would be great! (I couldn’t see a use for your original example)

Additionally, it doesn’t really flow, if you’re doing to do something you go:

  1. ‘I’m going to do this!’
  2. You tell everyone that you’re doing to do this
  3. You do it

I also feel as though you could pool instances for destroying and create a hacky function for salvaging.

1 Like

It seems unlikely that this would break anything; you can’t really rely on a locked parent property in your code, so in the interest of expanding use cases this sounds good to me.

Meantime you could use a different mechanism to achieve this. E.g. setting a “destroying” attribute before destroying it, and connecting to the attribute-changed. You could util wrap this. I expect that the attribute changed fires before destroy happens so you can re-parent the needed children before the destroy happens, but obviously this does not work for non-developer controlled destruction, like falling out of the world.

2 Likes