Skip to content

API and Integration

Third-party plugins reach EmakiAccessory through emaki-accessory-api.

Maven Coordinates

xml
<dependency>
    <groupId>emaki.jiuwu.craft</groupId>
    <artifactId>emaki-accessory-api</artifactId>
    <version>1.0.3</version>
    <scope>provided</scope>
</dependency>

Do not put emaki-accessory-api-*.jar into the server plugins/ folder. It is a compile-time artifact only.

Declare EmakiAccessory as a dependency in your paper-plugin.yml:

yaml
dependencies:
  server:
    EmakiAccessory:
      load: BEFORE
      required: false
      join-classpath: true

Facade

emaki.jiuwu.craft.accessory.api.EmakiAccessoryApi is the static facade:

MethodDescription
status()Availability and identity metadata, returning CoreLib's ApiStatus.
catalog()Query layer, see AccessoryCatalog below.
install(Bridge) / uninstall(Bridge)For EmakiAccessory's own lifecycle only.

Accessors never return null. While EmakiAccessory is absent the catalog returns empty answers, so callers must not treat a NullPointerException as an availability signal.

Bridge is annotated @ApiStatus.NonExtendable; third-party plugins must not implement it.

No Operation or Extension Layer

This is deliberate. Accessories grant attributes and skills by pushing contributions into EmakiAttribute and EmakiSkills through their existing owner-scoped extension points, so there is no accessory-specific combat or skill pipeline for a third party to hook. The module therefore exposes only a query layer.

To influence the attributes or skills accessories provide, go to the corresponding extension point in EmakiAttribute or EmakiSkills rather than asking EmakiAccessory for an interface.

Do Not Cache the Layer

Resolve catalog() at the point of use instead of storing it in a field: the backing bridge is replaced across a reload.

java
// Correct
EmakiAccessoryApi.catalog().parts();

// Wrong: this reference points at the old bridge after a reload
private final AccessoryCatalog cached = EmakiAccessoryApi.catalog();

AccessoryCatalog — Query Layer

Annotated @ApiStatus.NonExtendable.

MethodThreadDescription
List<AccessoryPartView> parts()Any threadList every part definition.
Optional<AccessoryPartView> part(String partId)Any threadLook up a part by id.
List<String> slotInstanceIds()Any threadList every slot instance id.
List<String> pageIds()Any threadList every accessory page id, in page order. Since 1.0.3.
String enabledPage(UUID playerId)That player's owner threadThe enabled page id; an empty string when unknown or currently not usable. Since 1.0.3.
Map<String, EquippedAccessoryView> equipped(UUID playerId)That player's owner threadWhat the player has equipped on the enabled page, keyed by slot instance id.
Map<String, EquippedAccessoryView> equippedOnPage(UUID playerId, String pageId)That player's owner threadWhat the player stores on one specific page. Since 1.0.3.
int equippedSetPieces(UUID playerId, String setId)That player's owner threadHow many pieces of one set the player has equipped, counting the enabled page only.

Part, slot, and page id queries are configuration facts and may be called from any thread.

Player queries read the in-memory session cache and must be called on that player's owner thread. They return an empty result rather than blocking when the player's data has not finished loading, so an empty result must not be read as "this player wears no accessories".

Three things to keep in mind about multi-page semantics:

  • equipped() reports the enabled page only. Use equippedOnPage() to inspect any other page.
  • When the player lacks the enabled page's permission, enabledPage() returns an empty string and equipped() returns an empty map, while the stored items stay untouched and remain retrievable.
  • equipped() includes orphaned slots so callers can see items pending retrieval; filter them with EquippedAccessoryView#orphaned() when not wanted. Orphaned slots never contribute to set piece counts.

These methods are pure queries with no readiness gate, so they may return stale data during a reload. To invalidate your own cache after a reload, use CoreLib's resident readiness listener:

java
EmakiCoreLibApi.addModuleListener(myPlugin, "EmakiAccessory", phase -> {
    if (phase == ModuleReadinessPhase.READY) {
        rebuildMyCache();
    }
});

Model Types

The emaki.jiuwu.craft.accessory.api.model package:

AccessoryPartView

java
public record AccessoryPartView(String partId, int count, String displayName, List<String> slotInstanceIds)
MemberDescription
partId()Part id.
count()Number of expanded slots, at least 1.
displayName()MiniMessage display name; may be an empty string.
slotInstanceIds()Expanded slot instance ids in index order, each <partId>_<index> with the index starting at 1. The list is immutable.

EquippedAccessoryView

java
public record EquippedAccessoryView(String pageId, String slotInstanceId, String partId, ItemStack item, boolean orphaned)
MemberDescription
pageId()Id of the page holding this accessory. Since 1.0.3.
slotInstanceId()Slot instance id.
partId()Owning part id, or the raw prefix when the part no longer exists.
item()A defensive copy of the stored item, or null when the slot carried none.
orphaned()Whether the slot is no longer declared by its owning page.

item() returns a fresh clone on every call, so callers cannot mutate stored state through the view.