DUXPLIMA Documentation

uxmlib-update

A notify-only release checker with GitHub, Modrinth and JSON providers. It never self-downloads.

Compares the running build against the latest published release, logs it once, and shows a permission-gated clickable notice on join. It never downloads anything, which is the point: a plugin that updates itself is a plugin that changes under an operator without their say-so.

Wiring it up

UpdateChecker checker = new UpdateChecker(
        scheduler, new GitHubReleaseProvider("UXPLIMA", "uxmLib"), getPluginMeta().getVersion());

new UpdateNotifier(plugin, scheduler, checker, "myplugin.update.notify")
        .start(Duration.ofSeconds(40), Duration.ofHours(6));

start(initialDelay, period) and stop() are both idempotent, and start may be called again after a stop, so a reload command can restart the poll safely.

The initial delay exists so the check does not compete with everything else enabling at startup.

Providers

new GitHubReleaseProvider("owner", "repo");
new ModrinthReleaseProvider("projectId");
new JsonUrlReleaseProvider("https://example.com/latest.json");
new JsonUrlReleaseProvider(uri, List.of("version", "tag_name"));

JsonUrlReleaseProvider reads a version from your own endpoint, with the field names you name. This is the one to use for a private or paid plugin whose releases are not on a public host.

UpdateProvider is a one-method interface returning CompletableFuture<Optional<Release>>, so anything else is a small class.

provider.endpoint() reports the URI it will call, which is worth logging once at startup so an operator can see what the plugin talks to.

Checking manually

checker.check().thenAccept(outcome -> {
    switch (outcome.status()) {
        case OUTDATED -> logger.info("Update available: " + outcome.release().orElseThrow().version());
        case UP_TO_DATE -> logger.info("Up to date");
        case DEV_BUILD -> logger.info("Running a build newer than any release");
        case FAILED -> logger.warning("Update check failed");
    }
});

checker.lastOutcome();
checker.currentVersion();
StatusMeaning
UP_TO_DATEThe running build matches or exceeds the latest release
OUTDATEDA newer release exists
DEV_BUILDThe running build is newer than anything published
FAILEDThe check could not complete

DEV_BUILD is a real state, not an error. A developer running a local build should not be told they are out of date.

Announce once

checker.checkAndAnnounce(outcome -> logger.warning("Update: " + outcome.release().orElseThrow().url()));

The callback fires at most once per distinct newer release, not once per poll. A six-hourly timer therefore produces one console line, and a later release announces again because it is a new version.

Threading

The provider fetch runs on scheduler.async, so the network call never touches a server thread. The version comparison itself is pure, using SemanticVersion, which is what makes it able to say a stable release is newer than a beta of the same number.

The join notice

UpdateNotifier registers a listener that shows a clickable message to joining players who hold the permission you passed. It is a permission node, not an operator check, so a server can show it to a developer role without giving them the world.

Notify only, deliberately

There is no self-download and no plan for one. The notice carries the release URL and the operator decides.