I created a plugin that is meant to sync scripts to the filesystem, and vice versa. This plugin exists as a single button that you can find in the “Plugins” tab. I never use Plugin:Activate. The idea is that the plugin always runs regardless of what other tools you’re using.
When I save it as a local plugin, studio refreshes all plugins, but it still seems like my old plugin is still running in the background – particularly the threads spawned with spawn. I want to kill these threads when my plugin refreshes.
Does anyone know how to detect when plugins are refreshed? Or another good way to solve this particular problem?
For those interested in reproing...
Create a script in workspace with the following source, then just right click twice on it, selecting “Save as Local Plugin”. Overwrite the plugin when you save it the second time.
local ID = game:GetService("HttpService"):GenerateGUID(false):sub(10, 13);
print("plugin " .. ID .. " starting");
spawn(function()
while true do
print("plugin " .. ID .. " running")
wait(5)
end
end)
Things I've tried...
plugin.Changed event
Button.Changed event
plugin.Deactivation event
polling plugin properties, like RobloxLocked (always false, even after unloading) or Parent (always nil).
Edit: this applies to threads spawned with spawn, and all individual scripts’ threads.
In the case of Rojo, which I’m the most familiar with, the plugin sets a unique value which the old plugin will see. Once the old plugin sees it, it stops. An example of this would be a value in shared.MyPluginName.version. It could be set to a unique number or a unique table, function, or userdata. The old plugin will compare this version to its own internal version and unload if different.
Ah, that works. I guess the only shortcoming is that if you clean up your plugins folder (meaning delete everything you don’t need) and refresh, it won’t necessarily fix the problem, but that’s not my use case. That’ll be someone else’s problem.
Edit: and regarding the other solutions, they are pretty much all filesystem → studio, not vice versa. I tried RbxSync (which is the only one that purports to try), but it was pretty glitchy. Not to suggest my solution will be any better, but it’ll be mine.
A simple way to do this is to check the Parent property of any DockWidgetPluginGui instances created by the plugin - when the plugin is unloaded, the Parent property is equal to nil (otherwise it’s equal to game.PluginGui). This may or may not be an ugly hack, but it seems to work reliably for me
Built-in plugins using dock widgets listen for AncestryChanged to know when to destroy the UI and disconnect connections (specifically they use Roact and call Roact.unmount(handle)).
For other plugins, I’ve seen methods like what IdiomicLanguage said where you create something outside the plugin (such as a gui in CoreGui), but if it already exists then you Destroy() the previous instance first. The previous instance of the plugin listens for that destroy to know to cleanup.
@HaxHelper
Aw, so my system does utilize DockWidgetPluginGuis, but not all of the time. I’m trying to keep that complicated GUI away from the user if a one-button solution will be sufficient for them.
This does look like a good solution for some use cases, and even captures the shortcoming I mentioned up above in reference to @IdiomicLanguage’s suggestion.
@dr01d3k4
I’ve gone with a Maid in _G for this. I’m frankly surprised I didn’t think of that solution before, as that’s what I did back when I tested my plugin(s) with a command line script, for which I don’t get the magic auto-cleanup that HotSwap provides.
The DockWidgetPluginGui doesn’t necessarily need its Enabled property to be true - I typically just keep all of the dock widgets parented to PluginGui at all times and toggle DockWidgetPluginGui.Enabled when I want to change visibility
this does mean that there are DockWidgetPluginGui instances just sitting there when not in use, but imo this isn’t really a big deal for a plugin interface
it does (blegh), and that’s why I have toolbar buttons on my plugins to toggle the visibility of dock widgets that they create. Plugins need some hardcore TLC
edit: however, note that only one DockWidgetPluginGui instance needs to be set up this way to treat its ancestry as a “plugin loaded” condition