TGL's Plugin System was inspired by both the Skyrim Script Extender (SKSE) and Fallout Script Extender (FOSE), as well as Minecraft Forge, Neo Forge, Fabric and Quilt. The Plugin System is an integral part of TGL, as anything the engine can do, so can a plugin, hence the "Engine eats its own Cooking".
Creating a Plugin for TGL is quite simple. First include tgl-api into your gradle/maven file. An example plugin is provided below.
@TGLPlugin(
id = MOD_ID,
name = "Test Plugin",
version = "1.0.0"
)
public class TestPlugin implements IPlugin {
public static final String MOD_ID = "test_plugin";
public boolean initialized = false;
public boolean enabled = false;
private PluginContext context;
@Override
public void onPreInitialize(PluginContext context) {
this.context = context;
context.registryManager().addRecordRegistry("holotapes", new RecordRegistry<HolotapeRecord>("holotapes"));
context.registryManager().addSerializerRegistry("holotapes", new HolotapeRecord.HolotapeSerializer());
}
@Override
public void onInitialize() {
HolotapeRecord testTape = new HolotapeRecord.HolotapeBuilder()
.modId(MOD_ID)
.itemId("test_tape")
.build();
RecordRegistry<HolotapeRecord> holotapeReg = (RecordRegistry<HolotapeRecord>) context.registryManager().getRecordRegistry("holotapes").get();
holotapeReg.addRecord(testTape);
Logger.debug(MOD_ID + " has initialized");
initialized = true;
}
public void onPostInitialize() {
}
@Override
public void onEnable() {
enabled = true;
}
@Override
public boolean canDisable() {
return true;
}
@Override
public void onDisable() {
}
@Override
public void onShutdown() {
}
@Override
public void onDispose() {
}
}An example HolotapeRecord is shown below
public class HolotapeRecord extends AbstractItem {
private final String[] data;
public HolotapeRecord(HolotapeBuilder builder) {
super(builder);
this.data = builder.data;
}
@Override
public HolotapeBuilder toBuilder() {
HolotapeBuilder builder = new HolotapeBuilder();
fillBuilder(builder);
return builder;
}
public void fillBuilder(HolotapeBuilder builder) {
super.fillBuilder(builder);
builder.data(this.data);
}
public static class HolotapeBuilder extends AbstractItemBuilder<HolotapeBuilder, HolotapeRecord> {
private String[] data;
public HolotapeBuilder() {
this.recordType("holotapes");
}
public HolotapeBuilder data(String... data) {
this.data = new String[data.length];
System.arraycopy(data, 0, this.data, 0, data.length);
return self();
}
@Override
public HolotapeBuilder self() {
return this;
}
@Override
public HolotapeRecord build() {
return new HolotapeRecord(this);
}
}
public static class HolotapeSerializer extends AbstractItemSerializer<HolotapeRecord> {
@Override
public byte[] write(HolotapeRecord record) {
byte[] recordBytes;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
bos.write(super.write(record));
write(bos, record.data.length);
for (String d : record.data)
writeString(bos, d);
recordBytes = bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
return recordBytes;
}
protected void readFields(ByteArrayInputStream bis, HolotapeBuilder builder) throws IOException {
super.readFields(bis, builder);
int dataLength = bis.read();
String[] data = new String[dataLength];
for (int i = 0; i < dataLength; i++)
data[i] = getString(bis);
builder.data(data);
}
@Override
public HolotapeRecord read(byte[] recordBytes) throws IOException {
HolotapeBuilder builder = new HolotapeBuilder();
readFields(new ByteArrayInputStream(recordBytes), builder);
return builder.build();
}
}
}
The Plugin Architecture relies on what type of Plugin Interface you're implementing, and what you do in each hook.
There are 4 different types of plugins your plugin can be. IPlugin, ServerPlugin, LauncherPlugin, EditorPlugin. You can implement all 4 or just IPlugin it's up to you.
IPlugin has the following methods that must be implemented:
onPreInitialize(PluginContext context);- This is where you should replace systems, register Record types, and any other system that should exist before the engine fully loads. The engine has only created the default record registries at this point, and done some minor initialization.onInitialize();- This is where you should fill your record registries and where you have access to the OpenGL/Vulkan context as well as the ability to start interfacing with the graphics card.onPostInitialize();- This is where everything is done and loaded in the engine, and where you can query against the loaded mods inside the engine. All Plugins and Mods have finished their initialization and you can perform functionality based on mod/plugin ids that are present. This is also where you should do any cleanup if necessary.
ServerPlugin, LauncherPlugin, EditorPlugin all work the same way but they have their type in front of their declaration:
onServerPreInitialize(ServerContext context);onLauncherPreInitialize(LauncherContext context);onEditorPreInitialize(EditorContext context);
This follows suit for both the onInitialize() and onPostInitialize() methods.