Skip to content
RitomasssCommunity
Embeddable sign input and native dialogs

NexaGUIAPI Documentation

NexaGUIAPI combines the established callback-based Sign API with reusable native Paper dialogs. The Sign API keeps its original package and backend selection; Dialog support is capability-checked and remains optional on older supported servers.

Java 21Compile target
Paper 1.21.x26.1–26.3 support
Paper API firstPacket fallback for early 1.21
Folia awarePlayer-scoped dispatch

Compatibility#

Backend behavior depends on the Paper API exposed by the server version.

NexaGUIAPI is not a standalone Bukkit plugin. It must be shaded into the consumer plugin that uses it.

Paper 1.21.5+ and 26.1–26.3#

Prefers Paper's native Player#openVirtualSign path.

Paper 1.21.0–1.21.4#

Uses the reflection-based packet backend first, then ProtocolLib if needed.

Physical fallback#

Used only when virtual packet backends are unavailable; restores the original block state.

Consumer Dependency#

Keep Paper provided, ProtocolLib optional and NexaGUIAPI relocated inside the final plugin jar. This example uses the checked local source version, 1.2.3; use the version supplied with your library release.

<dependency>
  <groupId>net.ritomasss</groupId>
  <artifactId>NexaGUIAPI</artifactId>
  <version>1.2.3</version>
</dependency>

<relocation>
  <pattern>net.ritomasss.nexasignapi</pattern>
  <shadedPattern>net.ritomasss.yourplugin.libs.signapi</shadedPattern>
</relocation>
Do not shade ProtocolLib. Declare it as optional/provided and add it as a soft dependency only when supporting the packet backend.

Quick Start#

Create one API instance in the plugin lifecycle and close it during disable.

Initialize#

private NexaSignApi signApi;

@Override
public void onEnable() {
    signApi = NexaSignApi.create(this);
}

@Override
public void onDisable() {
    if (signApi != null) signApi.close();
}

Open input#

signApi.open(player, SignInputOptions.builder()
    .line(0, "Enter price")
    .line(1, "")
    .line(2, "-------------")
    .line(3, "NexaAuctions")
    .build(), result -> {
        if (!result.submitted()) return;
        String input = result.line(1);
    });

Input Options#

SignInputOptions describes the visual prompt and the temporary session environment.

line(index, text)

Set the four displayed lines. Keep prompts short enough for the client sign UI.

color / glow

Configure dye color and glowing text where the selected backend supports it.

material / side

Choose the temporary sign material and active side for supported backends.

timeout / location

Control expiry and the fallback placement location.

Backend Selection#

The API reports support so a consumer can disable a feature cleanly instead of failing while opening a sign.

if (!signApi.isSupported()) {
    getLogger().warning(signApi.supportReason());
    return;
}

Native virtual#

Preferred on Paper 1.21.5+ and 26.1–26.3; no physical block is placed.

Packet virtual#

Reflection-based packet path for Paper 1.21.0–1.21.4, with ProtocolLib as a compatibility fallback.

Physical fallback#

Uses a nearby air block only when virtual backends are unavailable, then restores it for submit, cancel, timeout, quit or close.

Session Lifecycle#

Only one active sign session is allowed per player. Completion must be treated as a result state, not as an exception.

Result handling#

Check submitted() before reading lines. Cancelled, timed-out and unsupported flows should return without applying a price, name or command.

Cleanup guarantees#

  • Submit
  • Cancel
  • Timeout
  • Player quit
  • Consumer plugin disable

Always call close() from your plugin disable path.

Folia & Threading#

Opening and closing are dispatched through player/entity-scoped scheduling where the runtime supports Folia.

Call the API from async code only as an input request; do not manipulate Bukkit world/player state yourself from an async callback.

Consumer rule#

Parse the returned text in the callback, then schedule any inventory, economy or world operation with your own server-safe scheduler.

Thread-safe expectation#

The library makes the sign session dispatch safe, but it cannot make arbitrary consumer code safe. Keep external plugin calls on the thread required by that API.

Integration Rules#

NexaGUIAPI is intentionally small so auction, shop, claim and admin plugins can own validation and business logic.

Validate input#

Trim, parse, range-check and permission-check the returned lines inside the consumer plugin.

Use one instance#

Create one instance per consumer plugin and let it own all sessions.

Keep ProtocolLib optional#

List it as a soft dependency for early Paper versions; do not bundle its classes.

Troubleshooting#

Most issues come from backend expectations or a missing cleanup call.

Include the Paper build, ProtocolLib version, backend support reason and the consumer plugin version in reports.

API reports unsupported#

Confirm the server is a supported Paper branch. Do not assume Spigot or an unrecognized fork exposes Paper virtual-sign APIs.

Packet backend is not selected#

On Paper 1.21.0–1.21.4, NexaGUIAPI first checks its reflective packet backend. ProtocolLib is the compatibility fallback if that backend is unavailable; otherwise a temporary physical sign is used.

Temporary sign remains#

Check that the consumer calls close() on disable and does not terminate the JVM/server thread during a callback.

Player opens two prompts#

Only one active session is supported. Disable or cancel the existing session before starting another.

Callback changes inventory asynchronously#

Move inventory/economy/world operations to the correct player or server scheduler; the callback is not permission to call Bukkit APIs from any thread.

Input is accepted incorrectly#

SignAPI returns text, not domain validation. Parse and validate in the consumer before applying the business action.

Support#

NexaGUIAPI is a library, so include both library and consumer context.

  • API version and consumer plugin version
  • Paper build, Java version and server fork
  • Whether native, ProtocolLib or physical backend was selected
  • ProtocolLib version if applicable
  • Session lifecycle and full stack trace

Native Dialog API#

Build a vanilla form with a title, rich text or item body, checkbox, stable-id selector, text input, or number slider. Paper and the connecting client must support native dialogs.

Paper's Dialog API was introduced in 1.21.7. On unsupported runtimes the Dialog service reports unsupported while the existing Sign API remains available.
DialogDefinition form = DialogDefinition.builder(Component.text("Crate » Preview Template"))
    .body(Component.text("Select a preview GUI template."))
    .input(DialogInputDefinition.checkbox("enabled", Component.text("Enabled"), true))
    .input(DialogInputDefinition.singleOption("preview", Component.text("Preview"), options, "default"))
    .action("apply", Component.text("OK"), DialogActionRole.SUBMIT)
    .action("back", Component.text("Back"), DialogActionRole.CANCEL)
    .build();

guiApi.dialogs().open(player, form, result -> {
    if (result.status() == DialogResultStatus.SUBMITTED) {
        boolean enabled = result.values().booleanValue("enabled");
        String template = result.values().optionValue("preview");
    }
});

Typed controls#

Checkbox, text, single-option selection, and bounded number-range inputs.

One-shot actions#

Session-scoped buttons return Submit or Cancel results. Back never applies form values.

Bounded lifecycle#

Sessions expire and clear on replacement, disconnect, consumer disable, or service close.

Paper does not expose an immediate callback for Escape dismissal. An Escape close is cleaned up at timeout or disconnect; no values are applied unless the consumer handles a submitted result. Programmatic cancellation invalidates a session without closing another plugin's currently visible dialog.