Skip to content

Public API ​

Compile-time dependency ​

xml
<repositories><repository><id>jiuwu-releases</id><url>https://repo.crypticlib.com/repository/maven-public/</url></repository></repositories>
<dependency><groupId>emaki.jiuwu.craft</groupId><artifactId>emaki-item-api</artifactId><version>2.8.15</version><scope>provided</scope></dependency>
kotlin
repositories { maven("https://repo.crypticlib.com/repository/maven-public/") }
dependencies { compileOnly("emaki.jiuwu.craft:emaki-item-api:2.8.15") }

Compile only — never install, shade, or relocate this jar

EmakiItem embeds the same un-relocated classes. Duplicating them breaks Bukkit event delivery and bridge identity

Prerequisite: EmakiItemApi.status().usable() returns true. Once it does, the non-null layers are catalog(), operations(), repair(), experimental migration(), extensions(), and state(). install, uninstall, and Bridge belong to the runtime only: third-party code must neither call nor implement them

operations().create(id, amount) must run on the global-region owner thread and fires the synchronous cancellable EmakiItemCreateEvent before returning; a veto becomes FailureKind.CANCELLED. Refresh methods obey their update policies; player and set refresh use the player owner thread. openRepairGui returns EmakiResult<Unit>

repair() owns disabled flags, economy repair quotes, and economy repair(player, item). A quote can succeed while affordable() is false; repair performs the event, charge, compensation, durability commit, and post actions in one runtime flow. Material repair remains a GUI/inventory transaction and exposes no standalone simplified API

Experimental migration() provides worker-thread filesystem preview/apply, player-owner migrateInventory, and current-thread-owned migrateAllOnline; partial results preserve changed work when a batch cannot fully complete. extensions() registers owner-scoped item-layer preview providers and custom effect types

state() is the typed read/write layer for custom persistent item state, addressed by ItemStateKey<T> (namespace + partition + field name + ItemStateType). snapshot(item) returns an immutable ItemStateSnapshot supporting get(key), contains(key), and repaired(), with item() handing back a clone; repair(item) repairs metadata and reads the snapshot back. get(item, key) returns Optional<T> and is empty on a type mismatch. set(item, key, value), add(item, key, amount), and remove(item, key) return ItemStateMutation<T> carrying oldValue, newValue, delta, plus committed, changed, clamped, rejected, and reason, so clamping and rejection are readable from the result instead of thrown

Use Success, Partial, Failure, and optionalValue(). Failure kinds are UNAVAILABLE, NOT_FOUND, INVALID_INPUT, REJECTED, CANCELLED, TARGET_OFFLINE, WRONG_THREAD, INTERNAL_ERROR

Custom effects types ​

registerEffectType(Plugin owner, ItemEffectType type) returns an ItemEffectRegistration; unregisterEffectTypes(Plugin owner) removes every custom effect type a plugin registered. Once registered, an item definition may declare its own effects: - type: "<your-id>" next to the six built-in types (variables, ea_attribute, es_skill, accessory_slot, name_action, lore_action). The built-in ids are reserved: registering one is rejected and yields an inactive handle

ItemEffectType has exactly two methods:

  • typeId(): the id matched against effects[].type; a blank id makes the registration inactive
  • parse(ItemEffectParseContext): turns one raw effect entry into an applicable effect; returning null skips that entry, for example when the configuration does not apply to the current item

The parsed result is an ItemEffect, where only apply(ItemEffectApplyContext) is abstract and clear(ItemEffectApplyContext) defaults to empty. EmakiItem always calls clear before apply on the same item: clear removes data this effect wrote on a previous build (it must tolerate an item that carries no such data) and apply rewrites it. Store your own data under your own namespace; never rewrite or delete another module's persistent data

The context types are supplied by EmakiItem and must not be implemented by third parties:

  • ItemEffectParseContext: definitionId() is the id of the definition declaring the effect, and effect() returns the raw entry including its type as a defensive copy of plain data that must not be mutated
  • ItemEffectApplyContext extends it and adds itemStack() (the live stack being built — write to it directly and do not retain it), variables() (the definition's resolved expression variables, ready for templating), and equipSlot() (the definition's normalized equip slot, for example main_hand)

Behaviour notes:

  • Both the type field and typeId() are normalized before matching: trimmed, lower-cased with Locale.ROOT, and with spaces replaced by underscores
  • An entry whose type has no registered handler is skipped silently and never rejects the definition, so an item may reference a provider that is not installed
  • Registering or unregistering discards EmakiItem's built-item prototype cache, so the affected definitions are re-parsed on their next build
  • Registration is internally synchronized and may be called from any thread; typeId() is read at registration time, while parse and the ItemEffect callbacks run on the thread building the item, so they must be non-blocking, must not schedule work, and must not retain the supplied stack
  • The raw effect configuration only enters the item definition signature when at least one custom effect exists, so an item without custom effects keeps its previous signature; editing a custom effect's configuration is enough to trigger an item update, with no extra signature hook

Readiness and reload ​

status().ready() means "definitions are loaded", not "components were constructed". The components are non-null as soon as the plugin enables, but the definition table is rebuilt during a reload, so:

  • Inside a reload window status() reports loading and every EmakiResult-returning method on catalog(), operations(), repair(), and migration() returns unavailable() rather than NOT_FOUND. Treat unavailable() as "retry shortly", not "no such item"
  • definitionIds(), exists(id), and typeIds() cannot express unavailability: during a reload they return pre-reload stale results or empty results. When you must act only on loaded definitions, use EmakiCoreLibApi.whenReady(plugin, "EmakiItem", callback) instead of inferring absence from an empty result
  • A plugin that caches EmakiItem definitions should refresh them after every reload via EmakiCoreLibApi.addModuleListener(plugin, "EmakiItem", phase -> ...): invalidate on LOADING, rebuild on READY. whenReady fires only once and is not suited to this

A plugin that depends on EmakiItem definitions at startup should hook whenReady rather than querying the table directly in its own onEnable: module load order does not guarantee EmakiItem's first load happens before your onEnable. The callback fires once, and the thread it runs on is not guaranteed to be an owner thread, so schedule explicitly before touching players or inventories