Modules
Modules are the very basis of the delphi plugin’s menu loader and resource system.
To put it shortly, modules are namespaces that can be accessed by the Delphi
plugin to load resources such as XML, JSON, SCSS or any kind of text files.
Modules can either be directories or .zip
files inside the
plugins/Delphi/modules
directory, or they can be registered by other plugins
with the Java API.
If you’ve been using the Delphi plugin, you’ve almost definitely used modules.
When you type in the command to open a page (/delphi open <player> <path>
) you
use a module to know what page to open. For example /delphi open @s foobar
,
opens a page inside the foobar
module.
Registering custom modules
Custom modules can be registered with the Java API (See here for information on how to get started with the API)
Basic, template, example of how to create and register a module:
import net.arcadiusmc.delphi.*;
import net.arcadiusmc.delphi.resource.*;
import net.arcadiusmc.delphi.util.*;
public class Example extends JavaPlugin {
@Override
public void onEnable() {
Delphi delphi = DelphiService.get();
delphi.getResources().registerModule("example-module", new ExampleModule());
}
}
class ExampleModule implements ApiModule {
@Override
public Result<Document, String> loadDocument(ResourcePath path, DocumentContext context) {
return Result.err("Not yet implemented");
}
}
Note
Modules do not need to be manually unregistered, Delphi will automatically unregister all modules registered by a plugin when that plugin is disabled.Modules from jar resources
Modules can be created out of plugin jar resources, let’s assume we have the following resources in our plugin’s jar:
/resources/
├─/config.yml
├─/paper-plugin.yml
└─/page-data
├─/index.xml
└─/style.scss
and we want the /page-data
directory to be a module that pages can be loaded
from, we can do like this:
import net.arcadiusmc.delphi.*;
import net.arcadiusmc.delphi.resource.*;
import net.arcadiusmc.delphi.util.*;
public class Example extends JavaPlugin {
@Override
public void onEnable() {
Delphi delphi = DelphiService.get();
// We have to give the constructor a class loader that resources will be
// loaded from, as well as the name of the directory to load data from.
JarResourceModule module = new JarResourceModule(getClass().getClassLoader(), "page-data");
// This is optional, but it specifies a list of file names inside of the
// module.
// Used for command auto completions.
module.setFilePaths(List.of("index.xml", "style.scss"));
delphi.getResources().registerModule("jar-module", module);
}
}
Files from the jar-module
can now be loaded like any other module and can be
accessed with the /delphi open <player> <path>
command, like so:
/delphi open @s jar-module
.
Modules from .zip
files and directories
You can also create modules out of .zip
files and directories that aren’t
inside the Delphi plugin’s modules
folder. This is done with
DelphiResources#createDirectoryModule(Path)
and
DelphiResources#createZipModule(Path)
Directory example:
import net.arcadiusmc.delphi.*;
import net.arcadiusmc.delphi.resource.*;
import net.arcadiusmc.delphi.util.*;
public class Example extends JavaPlugin {
@Override
public void onEnable() {
Delphi delphi = DelphiService.get();
DelphiResources resources = delphi.getResources();
Path dirPath = getDataPath().resolve("menu-directory");
DirectoryModule module = resources.createDirectoryModule(dirPath);
resources.registerModule("plugin-dir-module", dirPath);
}
}
Zip module example:
import net.arcadiusmc.delphi.*;
import net.arcadiusmc.delphi.resource.*;
import net.arcadiusmc.delphi.util.*;
public class Example extends JavaPlugin {
@Override
public void onEnable() {
Delphi delphi = DelphiService.get();
DelphiResources resources = delphi.getResources();
Path zipPath = getDataPath().resolve("zipfile.zip");
Result<ZipModule, String> result = resources.createZipModule(zipPath);
result
.ifError(err -> getSLF4JLogger().error("Failed to create zip module: {}", err))
.ifSuccess(module -> resources.registerModule("zip-module", module));
}
}
Creating a basic page with an ApiModule
public class ExampleModule implements ApiModule {
@Override
public Result<Document, String> loadDocument(ResourcePath path, DocumentContext context) {
Document document = context.newDocument();
Element body = document.getBody();
Element title = document.createElement("h1");
title.setTextContent("Hello, world!");
body.appendChild(title);
return Result.ok(document);
}
}
Loading a page from plugin resources and invoking java objects from it
We’ll create a page with a simple button that counts up each time you press it and tells the player how many times its been clicked.
First let’s make the page’s XML. We’ll call it clicker-page/index.xml
:
<delphi>
<head></head>
<body>
<!-- Give the button an ID, so we can reference it easily later -->
<button id="clicker">Click me!</button>
</body>
</delphi>
Then we’ll write a java class to handle the button logic:
package net.arcadiusmc.example;
import net.arcadiusmc.dom.*;
import net.arcadiusmc.dom.event.*;
public class ClickerPage {
// This is the entry point called by the Document
public static void onDomInitialized(Document document) {
// Wait for the document to be fully loaded
document.addEventListener(EventTypes.DOM_LOADED, ClickerPage::onDomLoaded)
}
static void onDomLoaded(Event event) {
Document document = event.getDocument();
Element button = document.getElementById("clicker");
if (button == null) {
return;
}
button.addEventListener(EventTypes.CLICK, new ClickerListener());
}
static class ClickerListener implements EventListener {
int count = 0;
@Override
public void onEvent(Event event) {
count++;
Player player = event.getDocument().getView().getPlayer();
player.sendRichMessage("<yellow>You've clicked the button " + count + "times");
}
}
}
To tell the page to load the java class, we’ll go back to the XML file and change
the <head>
element to this:
<head>
<java-object class-name="net.arcadiusmc.example.ClickerPage"/>
</head>
And finally we register the page module
public class ExamplePlugin extends JavaPlugin {
@Override
public void onEnable() {
JarResourceModule module = new JarResourceModule(getClassLoader(), "clicker-page");
module.setFilePaths(List.of("index.xml"));
Delphi delphi = DelphiProvider.get();
delphi.getResources().registerModule("clicker-page", module);
}
}
Opening pages with the API
Pages can be opened for players with the following code:
String pagePath = "example-module:index.xml?param=false";
Player player = // ...
Delphi delphi = DelphiProvider.get();
delphi.newViewBuilder()
.setPath(pagePath)
.setPlayer(player)
.openOrLog();
The openOrLog
returns an Optional<DocumentView>
if you need to do anything
with the view.
If instead you want full control over the result and errors, you can use
open()
which returns a Result<DocumentView, DelphiException>
, or you can use
openOrThrow()
which will throw the DelphiException
instead of logging or
returning it.
By default, the spawned document will be spawned directly infront of the player, facing the player. If you want to spawn the document at a specific position.
String pagePath = "example-module:index.xml?param=false";
Player player = // ...
Location location = new Location(player.getWorld(), 100, 100, 100, 0, 0);
Delphi delphi = DelphiProvider.get();
delphi.newViewBuilder()
.setPath(pagePath)
.setPlayer(player)
.setSpawnLocation(location)
.openOrLog();