Hi! It’s been a while since I last used Roblox to program some stuff so I do apologize if I misuse some terms or have trouble understanding certain things. Anyway, I’m trying to program a general light pole script that’s inserted into a folder consisting of all the towns in the game I’m working on. The script simply establishes connections for toggling the lights whenever it’s night as well as turning all the light poles off whenever the town’s transformer has been destroyed. However, given that players can regenerate destroyed towns, I wish to disconnect these events upon being destroyed to improve the game’s performance. I found about Destroying which is apparently supposed to call whatever function you have connected, however, nothing seems to print whenever I destroy the script. Here’s a piece of my code that contains it:
---- CLEAN UP ----
-- Removes connections for better game performance
script.Destroying:Connect(function() -- [FIX]
print("Before: ", lightChangeConnection, " ", transformerValConnection);
lightChangeConnection:Disconnect();
transformerValConnection:Disconnect();
print("Disconnected!");
print(lightChangeConnection, " ", transformerValConnection);
powerTrafficLights(false, true); -- Will destroy connections
end)
I originally was using AncestoryChanged and detecting if the script’s Parent was set to nil (indicating it’s been destroyed), however, it didn’t seem to work. Am I doing something wrong? Thanks in advance! : )
No, I get that but I’m referring to Destroying not working. Though I doubt the approach of Destroying will be effective. Probably going to use a BindableEvent for the cleanup process unless someone else has a far more optimal/effective approach.
I believe since you are deleting the self (script) that the code will not execute. I would assume script.Parent.ChildRemoved would also net the same effect. (just tested in a new place file and yea.)
There are alternatives such as having a controller script or some sort of bindable.
The way I was originally planning on using this script was by having them inserted in models (i.e. cloned and reparented), though now that I think about it, this method does seem pretty flawed and more complicated to manage. Is it recommended to have a “controller” or “manager” like script in ServerScriptService that detects and handles the cleanup? In other words, instead of having scripts that are cloned and reparented, I was thinking of creating tables and in these tables, store the connections where upon deletion of a model (indicating that it’s be regenerated), it would just handle the clean up as necessary and reestablish a connection with the new model (not sure if this idea is that clear).
The ‘correct’ implementation of this code, which reliably runs cleanup, and avoids all aspects of memory leakage, is actually quite nuanced—involving unassociated threads, and the concept of auxiliary references. However, this pattern is really a last resort. As, if at all possible, it’s best to handle cleanup externally.
To elaborate, when a script creates signal connections, or interfaces with the task and coroutine libraries, the result are threads which are ‘associated’ with that script instance. When this instance is parented outside of a container where the script is intended to run (as is what happens when a script is destroyed or removed), the engine attempts to forcefully close all of these associated threads. Threads that have not yet yielded however, cannot be closed in this fashion. Thus a script that destroys itself, can in niche cases, still remain active indefinitely after being unparented. Your example is not one such case. Thus, the cleanup code never runs.
However, attempting to intentionally immortalize the script to get around this, is—at best, a hackish solution that would be far from future proof. The better approach, would be to have the cleanup code be unassociated. But as one might suspect, doing this from within the architecture of a single script (internally as opposed to externally), is also risky without diligent planning. Afterall, the entire reason why the engine associates threads to relevant script instances, is to avoid (as much as is reasonably possible) memory leakage and unintentional behavior. So adopting practices which defy these intended measures, would not only be against standard, but incredibly questionable.
Can you elaborate on what you mean by this? I’m kind of interpreting this like the approach I mentioned above, though I feel as if this is not what you mean. Many thanks! : )
If the light pole script is the one creating all of these connections, they should already be severed when it is destroyed. The problem, I’m assuming, is that you presumably need to run other cleanup operations alongside the script’s removal. For the reasons I outlined, this can’t reliably be done from within the very script that’s being destroyed (atleast not within conventions). This is what I mean when I say the cleanup code should be unassociated. You can organize around this constraint in any handful of ways. All I mean to explain, is that you can’t depend on these operations to run in a thread that is intended to close when the script is disabled.
Ahh I see, yeah my initial approach was not well thought out lol. I’ll definitely divide some functionality further to mitigate this issue, thanks for the feedback though! : )