This has been brought up in the past by a since-anonymized user, but never got any replies.
I assume Destroy() is not called on characters when the player leave? Because I keep a reference to their characters and I was able to reparent their character, after they left, despite having this new setting enabled.
Since destroy can now replicate to clients which is great, how would I destroy it on the server after itâs deleted in the client?
also itâs pretty simple to make this system
Clients donât replicate to the server. Unless the instance that it destroyed was inside their character⌠But in all other cases, a client destroying something wonât affect the server.
Sure, thatâs definitely correct that such a check will throw a false positive on a newly created instance, and on paper thatâs a problem. But the thing i have to ask is when would this actually matter? Iâm having trouble thinking of a case of where you specifically need to see when an object was called via :Destroy().
Furthermore, while I understand this wonât fit every case it should fit most of them, which is a change in approach; instead of checking if the instance is destroyed yet, you can switch to a more linear design and hook onto the .Destroyed
event and then run whatever behavior was meant to happen after checking if it was destroyed or not.
I will give you an example, I literally donât like when people come at me and say that a feature is just NOT needed just because they donât see the use case for it. When developers donât see why it would be useful to have it is because they havenât come across a situation where is needed.
This property in my opinion should be easy to implement and is better to have it available than the developer itself connecting to destroying and then changing a variable to true to know when the object is destroyed.
Here Are Some of The Use Cases:
- You have a loop that uses a certain Instance, this loop runs every 60 seconds. You want to terminate this loop when the object is destroyed. (Currently you will have to check if the parent is nil which is not accurate)
- You have a table on client full of objects that can be destroyed at any time from the server. Then you have a for loop on the client that iterates through this table and is required to check if the object is destroyed before executing some code, else just remove the object from the table. (Weak tables donât work with Instances and I am also not willing to connect to Destroying for a bunch of objects, assume you have like 500)
- You want to update how a Gui looks like if is not destroyed, if is destroyed then do nothing.(Currently you will have to check if the parent is nil which is not accurate)
- Do some stuff to an object while is not destroyed, for example, you want an arrow to point at the object. You make this arrow point at it while is parented to Workspace, if not then remove, if is added back then point at it again. (If you were to check if this object is parented to nil to assume is destroyed it will stop the arrow from pointing at the object when is parented back to workspace, you might say, focasds just parent the object somewhere else, yes I can do that, but I prefer just parenting to nil).
Now I wonât make any feature request because I canât create topics and because this is something that should have been done when Destroy() was first added.
I use the IsDestroyed method (a function I use to know if an object is destroyed which basically is a wrapper to check if is not a descendant of game) 167 times in my game:
That said I will really appreciate a property provided by default to know when an Instance is already destroyed.
I honestly thought this was already a thing. I was thinking only a few hours ago on how I could utilize this for one of my games, and now I know it will be a thing!
Oh man, I didnât know this wasnât already the case. That explains some memory problems I was having before
Sure, exploiters will take advantage of the ability for the server to free up some of its bandwidth. To that exploiter.
I found a bug with this new feature, previously had this bug happening when calling :Destroy() on the character on the client and we fixed it by moving the :Destroy() over to the server (shouldâve done that in the first place anyways i guess)
but now that it replicates the char:Destroy() to the client the warning is back
Player:Move called, but player currently has no humanoid.
how to replicate:
disable characterautoloads and then on a server script:
game.Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(char)
wait(10)
char:Destroy()
wait(10)
player:LoadCharacter()
end)
wait()
player:LoadCharacter()
end)
it would spam the warning every 10 seconds
Just use a closure
task.delay(1, function()
obj:Destroy();
end);
Someone correct me if Iâm wrong but using an anonymous function for something like this in Luau is supposedly cheap compared to regular Lua. The only reason I could ever think of doing what youâre suggesting is to cut down on file size ⌠which should not be an issue if youâre modularizing properly
Youâre correct. The Luau implementation does a substantially better job of making small lambdas like that cheap. Using such a lambda will generally only incur a single memory allocation compared to it incurring several in vanilla Lua.
Awesome! The moment I first heard that this behavior existed my immediate thought was why it hadnât already been addressed as it didnât seem like the type of behavior that developers really depended on or knew of in the first place and Iâm pretty glad that Iâm not part of the team responsible for rolling out these sorts of changes as I would have definitely inadvertently squandered a lot of peopleâs hard work put into making custom replication. Really awesome to see you guys considering even the most obscure use cases here and a big kudos to how carefully this change is being rolled out. Great job.
I realize that this reply isnât entirely necessary but I felt that the praise was needed.
I do.
Iâm curious, would removing the upvalue and using a function argument instead have a smaller cost? (i.e. task.delay(1, function(obj) obj:Destroy() end, obj)
)
Yes indeed, that will allow the Luau VM to avoid any memory allocation in most cases, but itâs generally not worth the reduced readability.
At this point itâs probably better if you circle back and just do task.delay(1, obj.Destroy, obj)
as the namecall optimization wonât be saving you any time when using a closure means youâd be doing 2 function calls anyways.
Benchmark time. (Note: this benchmarks task.spawn
, but task.delay
should be about the same, minus the scheduling cost.)
local n = 500
local instance = Instance.new('Folder')
instance:Destroy()
function spawnLookup()
for i = 1, n do
task.spawn(instance.Destroy, instance)
end
end
local destroy = game.Destroy
function spawnCached()
for i = 1, n do
task.spawn(destroy, instance)
end
end
function closure()
for i = 1, n do
task.spawn(function() instance:Destroy() end)
end
end
function closureNoUpvalues()
for i = 1, n do
task.spawn(function(instance) instance:Destroy() end, instance)
end
end
Caching game.Destroy
yields the best performance, while looking up game.Destroy
every time results in the slowest performance. However everything overlaps so heavily that in practice it doesnât really matter.
Try me.
Hereâs what happens if you call namecall directly using some implementation-specific Hot Garbage. Please note: this is scary as hell. You donât want to see how I did this.
So namecall
is a normal instance:Destroy()
call, without using the task library. __namecall
is a direct __namecall
metamethod call, without using the task library, and without setting the namecall method at all (it is set once, to Destroy, using a newproxy __namecall, and then not set again).
cursed
uses the task library, and is the most efficient way to schedule a (precisely) delayed destruction of an Instance.
Alright so scheduling namecall ~directly using the task library is absolutely possible and it has huge performance benefits. Thatâs literally what Iâm doing.
Okay Iâll be honest this is funny. I was complaining about how this wasnât possible and here it is, right in front of me.
I might make a community resource, youâll see how I did this soon.
There is also an edge case where connections connected to an instance after the instance is destroyed are not disconnected, this should be fixed with this new update as well.
local b = workspace.Baseplate
local c1 = b.Touched:Connect(function()
end)
b:Destroy()
task.defer(function()
print(c1.Connected) --> false
end)
local c2 = b.Touched:Connect(function()
end)
while c2.Connected do
print("connection still connected...")
task.wait(1)
end
Note that in certain cases (garbage collection, etc) you wonât observe the Connected
property turn false until the next time the event is fired. Your callback wonât get called and the Connected
property will get set to false. That may be what youâre observing.
Of course, you wonât be able to trigger the Touched event after something is destroyed, try with GetPropertyChangedSignal.
I misread your reply. After something is destroyed, connections that are added should not be disconnected. Some Instances are still somewhat functional after being destroyed, so sometimes this is wanted for one reason or another.
Connections linked to an instance still being kept alive even if the instance is destroyed is bad and unexpected behavior in an almost every single case. Once an instance is destroyed, there is no reason for you to use it in almost any cases (except clean up references to it, etcâŚ). Maids and janitors make this process easy and cleaning up in general.
Even the developer hub recommends so and this isnât surprising anyways: