English
EmakiStorage
EmakiStorage is the GUI warehouse module of the Emaki series. You store items in logical slots where a single slot can hold a long-scale amount while the original ItemStack is preserved 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.1.5 |
| API Jar | emaki-storage-api-1.1.3.jar |
| Main class | emaki.jiuwu.craft.storage.EmakiStoragePlugin |
| Main command | /emakistorage |
| Alias | /estorage |
| Required dependency | EmakiCoreLib |
| Optional dependency | PlaceholderAPI |
| Descriptor baseline | api-version: "26.1", folia-supported: true |
| Java | Java 25 |
| Main permissions | emakistorage.use (default true), emakistorage.unlock.purchase (default true), emakistorage.autopickup (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, StorageUnlockEvent and StorageBatchEvent 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 you lower capacity and occupancy ends up 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, current format version 2. The same file carries a reservation record section at its tail so API holds survive a restart |
| 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 | meta.yml (text YAML, version: 1.0.0) holds player_name, granted_slots, purchased_slots, default_stack_limit, sort_mode and auto_pickup. Entry payloads stay binary because YAML cannot express a full ItemStack |
File Structure
Once the server is running, EmakiStorage mainly uses these 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, sort mode and auto-pickup switch; 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- To shrink capacity, 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 only the GUI view refresh path evaluates it today. It is therefore recomputed when the window opens and after every deposit, withdrawal, page change, sort or search refresh
- The
/storagewritten inconfig.ymlcomments is a historical form; that alias is not registered, see Commands and Permissions - Write-type admin commands require the target player to be online, see Commands and Permissions