-
Notifications
You must be signed in to change notification settings - Fork 9
Custom Plugin
Rawky is built to be easy to mod. This is so anyone can implement new features. Most of the important parts of Rawky are actually plugins, such as the pixel grid, that use parts of the core API. This is done so anyone can disable it or use a different implementation, or a fork, of any of the included plugins.
Plugins are placed in the /plugins
folder beside the Rawky JAR. They're then loaded by a/the launcher, by using methods from the core.
Values that have been mentioned before will be cut to ...
to save line count.
To create a plugin, all you need to do is create an object
(it has to be a singleton!) and annotate it with @Plugin
. All singletons with this annotation are found and loaded by a/the launcher.
import com.deflatedpickle.rawky.api.plugin.Plugin
@Plugin(
value = "my_plugin",
author = "Me",
version = "1.0.0"
)
object MyPlugin
After giving necessary arguments to the annotation (there are default arguments!), it's ready to be used.
But it currently doesn't do anything. We need to define what this kind of plugin is and what it uses.
The main way to invoke code in Rawky plugins is to use events. Core events are triggered by a/the launcher. There are a number of these provided with the Rawky core, however, you can create your own.
To make use of an event, you can add a listener in your plugin object
.
import com.deflatedpickle.rawky.api.plugin.Plugin
@Plugin(...)
object MyPlugin {
init {
MyEvent.addListener {
println(it)
}
}
}
To create an event, you just need to extend AbstractEvent
.
import com.deflatedpickle.rawky.event.AbstractEvent
object MyEvent : AbstractEvent<String>()
These don't have to be object
s if you don't want.
Events take a generic type for the data they can have passed in.
The event then needs to be triggered at some point.
@Plugin(...)
object MyPlugin {
init {
MyEvent.trigger("My String")
}
}
The string we pass in will then be accessible from any listeners of this event.
That's all for creating a custom event!
Plugins are able to have their own config. These are created with default values when it doesn't exist, or be read when they do. Configs are stored as JSON inside the /config
folder.
We first need a settings object.
import kotlinx.serialization.Serializable
@Serializable
data class MySettings(
var enabled: Boolean = true
)
This needs to be a data class
, and it has to be annotated with @Serializable
.
We then need to tell @Plugin
where our settings our.
import com.deflatedpickle.rawky.api.plugin.Plugin
@Plugin(
...
settings = MySettings::class
)
...
Now if we were to launch Rawky, it would serialize and load our settings.
In order to use our settings, we have to wait until Rawky has finished loading the config. To do this, we can add an event listener for EventRawkyInit
, which is triggered right after settings have been loaded.
import com.deflatedpickle.rawky.api.plugin.Plugin
import com.deflatedpickle.rawky.event.EventRawkyInit
@Plugin(
...
settings = MySettings::class
)
object MyPlugin {
init {
EventRawkyInit.addListener {
}
}
}
We can then get our settings by doing:
...
EventRawkyInit.addListener {
val settings = ConfigUtil.getSettings<MySettings>("my_plugin")
}
...
This will give us access to an instance of our config.
All changes to values in this instance will be serialized when the program is closed.
Incase our plugin needs to use events or other content from a different plugin, we need to add it as a dependency. During plugin loading, all plugin dependencies are checked and a plugin load order is sorted out, using the dependencies.
These are very easy to add, all we do is pass an array of string IDs to our @Plugin
.
import com.deflatedpickle.rawky.api.plugin.Plugin
@Plugin(
...
dependencies = ["other_plugin"]
)
If it exists, our plugin will now be loaded after other_plugin
, and any initial events it triggers, we can listen to from MyPlugin
.
These are plugins that provide a new component to be placed into the docking grid, like the pixel grid.
To make one, pass type = PluginType.COMPONENT
to @Plugin
.
import com.deflatedpickle.rawky.api.plugin.Plugin
@Plugin(
...
type = PluginType.COMPONENT
)
...
Next, you'll need to create a component to pass to it. Components have to extend RawkyPanel
and have to be singletons.
import com.deflatedpickle.rawky.ui.component.RawkyPanel
object MyComponent : RawkyPanel()
Then, add the component as an argument to @Plugin
.
import com.deflatedpickle.rawky.api.plugin.Plugin
@Plugin(
value = "my_plugin",
author = "Me",
version = "1.0.0",
type = PluginType.COMPONENT,
components = [MyComponent ::class]
)
object MyPlugin
Once run, this component will now be added to the docking grid. You're free to do use the panel however you want; add buttons to it, override it's paintComponent
method, whatever.
Making a dialog plugin is a bit easier than a component plugin.
First, we need to set the @Plugin
type.
import com.deflatedpickle.rawky.api.plugin.Plugin
@Plugin(
...
type = PluginType.DIALOG
)
...
Second, we need to make our dialog. Rawky uses Oxbow dialogs for ease.
object MyDialog : TaskDialog(Window, "My Dialog")
We can then open our dialog whenever we want. Though it's a good idea to add Custom Menu Item for it.
An API plugin is very arbitrary in how it works, it's up to you. Usually, they'll contain a util object
to access some pre-made data structure or custom types.