Skip to content

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 ​

ItemValue
Module version1.1.5
API Jaremaki-storage-api-1.1.3.jar
Main classemaki.jiuwu.craft.storage.EmakiStoragePlugin
Main command/emakistorage
Alias/estorage
Required dependencyEmakiCoreLib
Optional dependencyPlaceholderAPI
Descriptor baselineapi-version: "26.1", folia-supported: true
JavaJava 25
Main permissionsemakistorage.use (default true), emakistorage.unlock.purchase (default true), emakistorage.autopickup (default true), emakistorage.reload (op), emakistorage.debug (op), emakistorage.admin (op)

Feature Pages ​

PageContents
Commands and PermissionsSubcommands, permission nodes and examples for /emakistorage and /estorage
ConfigurationThe unlock, behavior, search, persistence and logging keys of config.yml
CapacityThe capacity section, the four capacity sources, the three-level stackLimit and the overflow policies
GUI and DisplayThe gui and display sections, the gui/storage_gui.yml template structure and click semantics
Unlock CostsThe tiers, per-slot pricing and payment ordering of unlock_costs.yml
CoreLib ActionsThe 5 actions EmakiStorage registers into the CoreLib ActionRegistry
API and IntegrationCalling the warehouse through emaki-storage-api and the result model
Event APIThe StorageDepositEvent, StorageWithdrawEvent, StorageUnlockEvent and StorageBatchEvent Bukkit events
PlaceholderAPI PlaceholdersThe 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-level stackLimit configuration 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: effectiveSlots is the sum of base_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_slot port 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 item amount field expresses occupancy as 1–99 and the exact figure goes into lore. The valid range of the max_stack_size component 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 drop or delete option
  • Write-only flow log: business logic never reads the log files, and the three admin operation kinds ADMIN_SET / ADMIN_GIVE / ADMIN_CLEAR are always recorded

Persistence Design ​

TopicImplementation
Entry dataSingle 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 strategyFull 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 orderingWrite 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 framingEach record is framed separately using Paper's ItemStack#serializeAsBytes() / deserializeBytes(byte[]), which carry DataVersion and run through the DataFixer on read
Corruption isolationRecords that fail to decode are written to corrupt/<uuid>-<timestamp>.dat and the remaining records load normally
Hand-editable datameta.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/
PathDescription
config.ymlMain configuration: GUI, capacity, unlock/overflow, display, behaviour, search, persistence, logging
unlock_costs.ymlPaid expansion tiers. A business data file, released only when missing; version upgrades never overwrite admin content
gui/storage_gui.ymlWarehouse window template. The function row only declares offset; slot numbers are derived from gui.storage_rows
lang/*.ymlGUI, command, unlock and console message text
data/<uuid>/storage.datThat player's entry data, binary, rewritten in full
data/<uuid>/meta.ymlThat player's slot counts, stackLimit values, sort mode and auto-pickup switch; hand-editable
logs/<uuid>/<yyyy-MM-dd>.logFlow 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 ​

  1. Place EmakiCoreLib-*.jar and EmakiStorage-*.jar into the server's plugins/
  2. Do not place emaki-storage-api-*.jar into plugins/; the API Jar is a compile dependency for developers only
  3. After the first start, review plugins/EmakiStorage/config.yml and unlock_costs.yml
  4. Open your own warehouse:
text
/estorage
  1. After editing configuration, reload config, language and GUI templates:
text
/estorage reload
  1. Grant slots to an online player and inspect the capacity breakdown:
text
/estorage slot grant Steve 10
/estorage info Steve

Usage Notes ​

  • Setting gui.deposit_feedback to none is 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> and emakistorage.stacklimit.<n> take the highest matching tier and do not support wildcards
  • To shrink capacity, lowering capacity.base_slots never 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 /storage written in config.yml comments 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