API and Integration
Third-party plugins reach EmakiAccessory through emaki-accessory-api.
Maven Coordinates
<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:
dependencies:
server:
EmakiAccessory:
load: BEFORE
required: false
join-classpath: trueFacade
emaki.jiuwu.craft.accessory.api.EmakiAccessoryApi is the static facade:
| Method | Description |
|---|---|
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.
// 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.
| Method | Thread | Description |
|---|---|---|
List<AccessoryPartView> parts() | Any thread | List every part definition. |
Optional<AccessoryPartView> part(String partId) | Any thread | Look up a part by id. |
List<String> slotInstanceIds() | Any thread | List every slot instance id. |
List<String> pageIds() | Any thread | List every accessory page id, in page order. Since 1.0.3. |
String enabledPage(UUID playerId) | That player's owner thread | The 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 thread | What 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 thread | What the player stores on one specific page. Since 1.0.3. |
int equippedSetPieces(UUID playerId, String setId) | That player's owner thread | How 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. UseequippedOnPage()to inspect any other page.- When the player lacks the enabled page's permission,
enabledPage()returns an empty string andequipped()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 withEquippedAccessoryView#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:
EmakiCoreLibApi.addModuleListener(myPlugin, "EmakiAccessory", phase -> {
if (phase == ModuleReadinessPhase.READY) {
rebuildMyCache();
}
});Model Types
The emaki.jiuwu.craft.accessory.api.model package:
AccessoryPartView
public record AccessoryPartView(String partId, int count, String displayName, List<String> slotInstanceIds)| Member | Description |
|---|---|
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
public record EquippedAccessoryView(String pageId, String slotInstanceId, String partId, ItemStack item, boolean orphaned)| Member | Description |
|---|---|
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.