There are three major challenges with plugins:
- Developing them is an arduous process because you need to publish and update your plugin whenever you want to test changes
- Plugins run in every place
- Coordinating collaborators to install plugins necessary for the development of a game (e.g. zone editor) and making sure they have up-to-date versions
To alleviate this, I am releasing a plugin that addresses those issues in the following ways:
Per-place/game plugins for collaborators
Any developer that has this plugin installed will have scripts in TestService.LocalPlugins automatically loaded as plugins. You can sync these scripts across the entire game using the new packages feature, or even have place-specific plugins by not using packages. This means collaborators for a game only need to install this one plugin, and they will have access to all plugins in your game/place.
If you add, update, or remove a local plugin, everyone else will receive those changes without needing to install, update, or uninstall the plugin manually. You no longer need to publish private plugins to the library so collaborators can use them. And any potential issues that would arise from each collaborator managing their plugins (e.g. version 2 of a plugin fixed a bug which caused data corruption, but collaborator A is still running version 1 and corrupting data) are no longer a problem.
Per-place/game plugins for solo developers
Local plugins can be used regardless of whether you’re working on a collaborative or solo project.
Plugins editable in the explorer
As mentioned previously, plugins local to the game/place are in the explorer which gives you the ability to add, modify, or remove them without having to open the publish window or Manage Plugins. This plugin also allows you to do the same with plugins that are not place-specific by inserting scripts into TestService.GlobalPlugins. Global plugins are saved to the main plugin’s settings, and are loaded every time you open a new place.
Once you edit a plugin in the explorer, all you need to do is disable/enable the script to run the new version. Global plugins are automatically saved whenever the source changes.
Run in any DataModel
Unlike plugins like Hotswap that rely on loadstring, this plugin runs scripts as modules, so it works in client DataModels like the client view in APS or test players.
Use Cases:
- Visual editor to place resource spawners in your game without adding models that get published with the game
- Visual zone editor
- DataStore editor specific to your game
- Add utility functions to _G/shared so you can do
_G.insert(assetId)
or similar from the command bar in any place - Test plugins you are going to publish to public domain immediately after making changes, without having to
Plugin Link
Notes
- Scripts in local/global plugins must be Scripts (or LocalScripts – there is no difference), and NOT ModuleScripts. ModuleScripts do not have a Disabled property, which is used to control whether plugins run or not
- GlobalPlugins only have their sources serialized to plugin settings, so they cannot have any children. LocalPlugins may have children, as they are saved with the place/game.
- Because these scripts aren’t technically plugins, they do not have the plugin variable defined. If you need this in your script (mainly if you are creating toolbars/buttons, which you may not always need), return a table containing a
Start
function from your script. This will be called when the plugin is loaded with theplugin
variable passed as an argument. Example:
local t = {}
function t.Start(plugin)
-- start doing stuff in my script with the 'plugin' variable
end
return t
- Because of the above, all local/global plugins are part of the same actual plugin. This should mostly not be noticeable, but be careful for conflicts in plugin settings as it may conflict with others (this plugin reserves “PluginData”)
- Similarly to Start, you can define a Stop function which is also passed a plugin argument and is run whenever the script is deleted or disabled. Note that this is not run when Studio reloads all plugins, in which the active events from both local/global and real plugins will continue to run
- Because the plugin converts your global/local plugins to modules to run them, it needs to guarantee the script returns. It can’t unconditionally add a
return nil
since Roblox will throw an error if the script has already returned before then, so it needs to check if it already returns. I didn’t want to add a full Lua interpreter, so it naively matches “^return” on the last line of the script – if your script returns, make sure it is done on the last line. - When deleting global scripts, the plugin will prompt you with a confirmation since this will permanently delete them from saved data without any hope of recovery. Sometimes you can undo the deletion, but this is not always the case (e.g. accidentally deleted in play solo or changehistoryservice was disabled by a plugin like the animation editor)
- Use the waitForObjectUntilGameLoad module in the plugin’s source if any of your plugins need to wait-for-object-and-create-if-not-exist. If you use FindFirstChild, the object may have not finished loading on client DataModels (play solo) or when loading TeamCreate places. If you use WaitForChild with a timeout, you’ll be waiting longer than you need to be