This crate provides a safe ABI with minimal overhead to interact with the core gateway. Every plugin must import this crate to interact with the core gateway.
To create a Rust plugin, the user is just responsible for defining a struct that implements the [GCPluginInstance] trait and also includes the [gc_plugin] macro. The following is the simplest example of a working minimal plugin.
use ;
use Value;
The [gc_plugin] macro will be responsible for generating the necessary unsafe C ABI functions and callbacks. The plugin just needs to provide an implementation of [GCPluginInstance], this instance will exist for the entire lifetime of the plugin instance (until the core calls [raw::gc_plugin_shutdown]). This is will be the object used by the core gateway to interact with the plugin.
It's important to remember that the lifetime of a plugin instance is different then the lifetime of the plugin (or dynamic library). A plugin may contain multiple plugin instances that run inside the same process, it's important to keep thin in mind
as it can affect the overall functionality (eg: handling global and static variables).
If the plugin needs to preform any additional cleanup, it can implement the Drop trait on the [GCPluginInstance] object which will be called when the plugin instance is shutdown.
It is also important to note that the callbacks functions might be invoked by different threads at the same time, thus the Send + Sync restriction in the [GCPluginInstance] trait.
Using additional threads and async runtimes
Spawning an additional thread at startup or using an asynchronous runtime is common practice, although due to the possibility of the core unloading or shuting down the plugin, this introduces some additional complexity which the ABI abstractions cannot handle on it's own.
Unfortunately, is not possible to safely pass the GCPluginInterface to a new thread due to it requiring a 'static lifetime. This is by design to avoid leaking the interface which is invalid after the plugin is dropped.
When working with async runtime, there is no mechanism to scope the lifetime, thus it is recommended to transform the GCPluginInterface to a 'static lifetime. This requires an unsafe operation, but as long as
the Runtime object is added to the [GCPluginInstance] object as a field, it will be safely joined when the plugin is unloaded/shutdown.
The following is an example of how to achieve this using Tokio:
use ;
use ;
The plugin is then responsible for ensuring [GCPluginInterface] is not used after [raw::gc_plugin_shutdown] is called.
Plugin template
To create a new plugin it is recommended to use the script that will create a new plugin with the necessary boilerplate code.
Important notes
Examples
Examples of plugins can be found in our repository directory.
Testing Plugins
Since testing plugins independently from the core gateway can be difficult, we provide a submodule to simulate the Gateway and help with integration tests. Head over to the [testing] module for more information.