English
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-storage-api</artifactId><version>1.1.3</version><scope>provided</scope></dependency>kotlin
repositories { maven("https://repo.crypticlib.com/repository/maven-public/") }
dependencies { compileOnly("emaki.jiuwu.craft:emaki-storage-api:1.1.3") }Compile only — never install, shade, or relocate this jar
EmakiStorage embeds the un-relocated API. A bundled copy breaks event and static bridge identity
Facade
Availability is determined by EmakiStorageApi.status().usable(). The only business layer that is non-null is operations(); install, uninstall and Bridge belong to the runtime only and must not be relied on
All asynchronous methods
Method contract: except for openGui, every public read/write method carries the Async suffix and returns CompletableFuture<EmakiResult<...>>:
| Method | Semantics |
|---|---|
readSnapshotAsync(UUID) | Reads a detached StorageSnapshot, loading from the file lane when it is not cached |
depositAsync(UUID,ItemStack,long) | Adds items to storage without taking them from the caller's inventory |
withdrawAsync(UUID,ItemStack,long) | Debits storage and hands the items to the online owner |
countOfAsync(UUID,ItemStack) | Returns the real count, including a legitimate zero |
countAllAsync(UUID,Collection<ItemStack>) | Counts several templates in one pass, returning Map<ItemStack,Long>. A template absent from storage maps to 0L rather than being missing. Available for offline players |
applyBatchAsync(UUID,StorageBatchRequest) | Atomic signed increments that never route through the player's inventory. allOrNothing is declared inside StorageBatchRequest, not as a separate parameter; the payload is StorageBatchResult. Exceeding behavior.batch_max_ops returns INVALID_INPUT with batch_too_large and applies nothing |
reserveAsync(UUID,StorageBatchRequest,Duration) | Holds only the withdrawal side of the request; the payload is a ReservationHandle. Deposit ops in the request are ignored, because a reservation holds stock back rather than pre-booking capacity. A non-positive ttl is rejected. Reservations survive a restart and are released on load once ttl has elapsed |
commitAsync(ReservationHandle) | Applies a reserved batch; the payload is StorageBatchResult. It takes the handle only, with no UUID, and re-runs the deposit-side pre-checks |
releaseAsync(ReservationHandle) | Drops a hold; the payload is Unit. It takes the handle only. Idempotent: an unknown or already-released handle returns NOT_FOUND rather than failing loudly |
grantSlotsAsync(UUID,int) | Adjusts the admin-managed granted-slot pool |
setStackLimitAsync(UUID,long) | Sets the player-level default per-slot ceiling; 0 restores config inheritance |
setSlotStackLimitAsync(UUID,int,long) | Sets one logical entry's ceiling; 0 restores player-default inheritance |
Thread contract: these futures may be submitted from any thread and the runtime does the real work on the file lane or the player's owner thread, but a future callback is not guaranteed to run on the Bukkit owner thread. Re-dispatch before touching players, inventories or the world
Synchronous method requirement: openGui(Player) is the only synchronous operation. It requires the supplied player's owner thread and never schedules a later open; a call from any other thread returns WRONG_THREAD
Return model: StorageSnapshot is an immutable detached snapshot. StorageAmount reports requested and applied amounts plus remaining() and complete(); an under-capacity deposit can return a Partial carrying the amount actually applied
Event boundary
Trigger rules: the asynchronous API fires the corresponding synchronous events on its actual transaction path; never assume every underlying edit has an event just because two methods look similar. In particular, the current implementation path for a direct async withdrawal is whatever the runtime transaction service does, and the events page lists the exact coverage
Batch and reservation boundary: exactly one StorageBatchEvent fires for a whole batch; per-op deposit and withdraw events are deliberately not fired inside a batch. Reserved units stay visible through StorageEntrySnapshot#reservedAmount() but are excluded from what applyBatchAsync may take, so the same units cannot be promised twice
Source is fixed: every public API method records its source as api and the caller cannot choose one; the gui, command, action and auto_pickup sources are only produced by the plugin's own entry points
Result semantics
Result contract: use Success, Partial, Failure, and optionalValue(). Failure kinds are exactly UNAVAILABLE, NOT_FOUND, INVALID_INPUT, REJECTED, CANCELLED, TARGET_OFFLINE, WRONG_THREAD, INTERNAL_ERROR. Ordinary business failures are expressed by the result rather than by a failed future