Skip to content

Custom Plugin

DeflatedPickle edited this page Jun 30, 2020 · 3 revisions

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.

Table of Contents

Creating A Plugin

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.

Plugin Events

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)
		}
    }
}

Custom Event

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 objects 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!

Plugin Settings

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.

Dependencies

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.

Component Plugin

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.

Dialog Plugin

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.

API Plugin

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.