Skip to content

Plugin API

Kyle Kreuter edited this page Sep 26, 2026 · 3 revisions

Plugin API

vistructum-api is the only dependency your plugin needs. The core jar contains the API classes, so declare the dependency as provided and depend on the core plugin.

Setup

The API is not published to a Maven repository yet. Install it locally from the v1.0.0 tag:

git checkout v1.0.0
mvn -pl vistructum-api -am install
<dependency>
    <groupId>de.kylekreuter.vistructum</groupId>
    <artifactId>vistructum-api</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
</dependency>
depend: [vistructum]

Entry point

Vistructum.get() returns the service and throws IllegalStateException when the core plugin is not enabled. The service has four parts: findings(), scans(), players(), and status().

Vistructum.get().findings()
        .find(FindingQuery.open().world("world").limit(10))
        .thenAccept(page -> page.items().forEach(finding -> staff.sendMessage("#" + finding.id())));

Threading

  • Every CompletableFuture from the API completes on the main thread. Callbacks can call Bukkit directly.
  • Never call join() or get() on the main thread. The future completes in a later tick, so the server blocks forever.
  • When the core plugin is disabled, a future completes on the thread that finishes it.

Findings

Method Result
get(id) Optional<Finding>
find(FindingQuery) Page<Finding>, newest first
count(FindingQuery) Long, the number of matches across all pages. limit and before are ignored.
review(id, verdict, reviewer) Optional<Finding> with the reviewed finding, empty for an unknown ID. Fires FindingReviewedEvent.
preview(id) Optional<Preview>, a grayscale raster
previewPng(id) Optional<byte[]>, a PNG with 4×4 pixels per raster cell

A Finding holds its ID, Source (MASK for live detection, FULLSCAN for the fullscan), world, BlockBox, score, players, model version, creation time, and an optional Review.

FindingQuery is an immutable record with builder-style methods: all(), open(), world, source, state (ANY, OPEN, REVIEWED), since, before(id), and limit (1 to 500, default 20). Paging uses the ID as cursor: Page.next() repeats the query with before(last id), so new findings never shift pages.

Scans

Method Result
request(world) Optional<ScanJob> with the queued job, empty if a scan of that world is active, failed future for an unknown world
active() List<ScanJob> of running and queued jobs
cancelAll() List<ScanJob> of the cancelled jobs. Fires ScanFinishedEvent for each.

Players

Method Result
face(uuid) PlayerFace with the account name, if known, and 8×8 RGB pixels in the form 0xRRGGBB, row by row from the top-left corner. The pixel list is empty without a skin.

The core fetches faces from the Minecraft account services and stores them in SQLite. It reuses a face with a skin for 24 hours and a result without a skin for one hour. When the services are unreachable, the future completes with the outdated stored face, or with an empty face that isn't stored. For offline-mode UUIDs, the core looks up the skin by the name the server knows.

Status

status() returns VistructumStatus: tracked block changes, open findings, active scans, and InferenceStatus (mode LOCAL or REMOTE, availability, loaded model versions, last error).

Events

All events are synchronous and fire on the main thread.

Event When Access
FindingCreateEvent Before a finding is stored. Cancellable. getCandidate()
FindingCreatedEvent After a finding is stored getFinding()
FindingReviewedEvent After a verdict getFinding()
ScanStartedEvent After tile planning getJob()
ScanProgressEvent After each tile getJob()
ScanFinishedEvent On DONE, CANCELLED, or FAILED getJob()

Cancelling FindingCreateEvent drops the candidate for good. The core logs the cancellation.

@EventHandler
public void onCreate(FindingCreateEvent event) {
    if (event.getCandidate().world().equals("creative")) {
        event.setCancelled(true);
    }
}

The Javadoc of vistructum-api is the full reference.

Clone this wiki locally