EmakiStorage
EmakiStorage is the GUI warehouse module of the Emaki series. It stores player items in logical slots where a single slot can hold a long-scale amount while preserving the original ItemStack in full (components, enchantments and PDC are all kept as-is).
The warehouse window is described by the gui/storage_gui.yml template, capacity comes from config.yml combined with permission tiers, command grants and paid purchases, and deposit/withdraw behaviour is exposed through CoreLib actions, a public API and Bukkit events.
Basic Information
| Item | Value |
|---|---|
| Module version | 1.0.1 |
| API Jar | emaki-storage-api-1.0.1.jar |
| Main class | emaki.jiuwu.craft.storage.EmakiStoragePlugin |
| Main command | /emakistorage |
| Alias | /estorage |
| Required dependency | EmakiCoreLib |
| Optional dependency | PlaceholderAPI |
| Descriptor baseline | api-version: "1.21.8", folia-supported: true |
| Java | Java 25 |
| Main permissions | emakistorage.use (default true), emakistorage.unlock.purchase (default true), emakistorage.reload (op), emakistorage.debug (op), emakistorage.admin (op) |
Feature Pages
| Page | Contents |
|---|---|
| Commands and Permissions | Subcommands, permission nodes and examples for /emakistorage and /estorage. |
| Configuration | The unlock, behavior, search, persistence and logging keys of config.yml. |
| Capacity | The capacity section, the four capacity sources, the three-level stackLimit and the overflow policies. |
| GUI and Display | The gui and display sections, the gui/storage_gui.yml template structure and click semantics. |
| Unlock Costs | The tiers, per-slot pricing and payment ordering of unlock_costs.yml. |
| CoreLib Actions | The 5 actions EmakiStorage registers into the CoreLib ActionRegistry. |
| API and Integration | Calling the warehouse through emaki-storage-api and the result model. |
| Event API | The StorageDepositEvent, StorageWithdrawEvent and StorageUnlockEvent Bukkit events. |
| PlaceholderAPI Placeholders | The 10 placeholders of the emakistorage extension and their resolution constraints. |
Core Capabilities
long-scale per-slot storage: one logical slot holds one kind of item, and its ceiling comes from the three-levelstackLimitconfiguration rather than the vanilla 64/99 stack size.- Original item preserved in full: deduplication uses full
ItemStack#equals(components, enchantments and custom PDC all take part). No hash signature layer is introduced, so two items of the same material with different components are never merged into one entry. - Slot count is the single source of truth for capacity:
effectiveSlotsis the sum ofbase_slots, the permission tier, command grants and paid purchases, then clamped. Page count is derived from it (ceil(effectiveSlots / slots per page)); there is no reverse derivation from a configured page count. - Mixed deposit: clicking any display slot with a loaded cursor and using the fixed
deposit_slotport both funnel into one transaction implementation. Capacity checks, the filter, event publication and log emission exist exactly once. - 1–99 occupancy rendering: with
display.amount_mode: percent, the itemamountfield expresses occupancy as 1–99 and the exact figure goes into lore. The valid range of themax_stack_sizecomponent is exactly 1–99, so no packet trickery is involved. - Search and sort: search is substring matching and never compiles a regular expression; sorting is an explicit "tidy up" action that rewrites entry order.
- Zero-loss shrinking: when lowered capacity leaves occupancy over the limit, four policies are available and none of them causes irreversible data loss. There is no
dropordeleteoption. - Write-only flow log: business logic never reads the log files, and the three admin operation kinds
ADMIN_SET/ADMIN_GIVE/ADMIN_CLEARare always recorded.
Persistence Design
| Topic | Implementation |
|---|---|
| Entry data | Single binary file storage.dat, file magic EMSTOR, format version 1. |
| Write strategy | Full rewrite, not append. Every save produces an already-compact file, so there is no compactor, no garbage-ratio bookkeeping and no dangling-id repair. |
| Write ordering | Write a sibling .tmp first, re-read and validate the record count, then replace atomically. A failed write keeps the previous file and removes the tmp. |
| Per-record framing | Each record is framed separately using Paper's ItemStack#serializeAsBytes() / deserializeBytes(byte[]), which carry DataVersion and run through the DataFixer on read. |
| Corruption isolation | Records that fail to decode are written to corrupt/<uuid>-<timestamp>.dat and the remaining records load normally. |
| Hand-editable data | Slot counts, stackLimit values and the sort mode live in meta.yml (text YAML). Entry payloads stay binary because YAML cannot express a full ItemStack. |
File Structure
After the server runs, EmakiStorage mainly uses the following files:
text
plugins/EmakiStorage/
├─ config.yml
├─ unlock_costs.yml
├─ gui/
│ └─ storage_gui.yml
├─ lang/
│ ├─ zh_CN.yml
│ └─ en_US.yml
├─ data/
│ └─ <uuid>/
│ ├─ storage.dat
│ └─ meta.yml
├─ logs/
│ └─ <uuid>/
│ └─ <yyyy-MM-dd>.log
├─ corrupt/
└─ exports/| Path | Description |
|---|---|
config.yml | Main configuration: GUI, capacity, unlock/overflow, display, behaviour, search, persistence, logging. |
unlock_costs.yml | Paid expansion tiers. A business data file, released only when missing; version upgrades never overwrite admin content. |
gui/storage_gui.yml | Warehouse window template. The function row only declares offset; slot numbers are derived from gui.storage_rows. |
lang/*.yml | GUI, command, unlock and console message text. |
data/<uuid>/storage.dat | That player's entry data, binary, rewritten in full. |
data/<uuid>/meta.yml | That player's slot counts, stackLimit values and sort mode; hand-editable. |
logs/<uuid>/<yyyy-MM-dd>.log | Flow log, one directory per player and one file per day, write-only. |
corrupt/ | Quarantine directory for records that failed to decode. |
exports/ | Readable YAML dumps produced by /estorage export. |
Quick Start
- Place
EmakiCoreLib-*.jarandEmakiStorage-*.jarinto the server'splugins/. - Do not place
emaki-storage-api-*.jarintoplugins/; the API Jar is a compile dependency for developers only. - After the first start, review
plugins/EmakiStorage/config.ymlandunlock_costs.yml. - Open your own warehouse:
text
/estorage- After editing configuration, reload config, language and GUI templates:
text
/estorage reload- Grant slots to an online player and inspect the capacity breakdown:
text
/estorage slot grant Steve 10
/estorage info SteveUsage Notes
- Setting
gui.deposit_feedbacktononeis not recommended. Under mixed deposit an item lands where the entry order puts it, possibly on another page; without feedback the player will believe the item vanished. emakistorage.slots.<n>andemakistorage.stacklimit.<n>take the highest matching tier and do not support wildcards.- Lowering
capacity.base_slotsnever consumes slots a player was granted or purchased; the three sources are persisted separately. - Overflow state is not persisted. It is derived from capacity versus occupancy and recomputed on login, reload, permission change and command grant.
- Write-type admin commands require the target player to be online, see Commands and Permissions.