Skip to content

Configuration ​

In plugins/EmakiAccessory/config.yml and the pages/*.yml accessory pages, every key has a purpose, a default value, and an allowed range. In the main configuration, version is a configuration-structure marker maintained by the project resource sync flow; the plugin merges newly added default keys based on it

Top-Level Keys ​

Shipped defaults (full contents):

yaml
version: "1.1.8"
language: "zh_CN"
release_default_data: true
drop_on_death: false
unique: true
slot_sources:
  pdc:
    enabled: false
    key: ""
  lore:
    enabled: false
    patterns:
      - "^穿戴部位[::]\\s*(.+)$"
    separator: ","
    aliases:
      "戒指": "ring"
      "项链": "necklace"
      "腰带": "belt"
      "护符": "charm"
persistence:
  autosave_seconds: 60
  drain_timeout_seconds: 10
FieldTypeRequiredDefaultAllowed valuesDescription
versionstringNo"1.1.8"—Configuration-structure version maintained by the bundled resource sync flow; used to merge newly added default keys. Falls back to "1.0.0" when absent
languagestringNo"zh_CN"—Language file name, resolving to lang/<language>.yml. An empty or all-whitespace value falls back to zh_CN; a missing file loads with the default language
release_default_databooleanNotruetrue / falseWhether missing bundled data files are released on startup. While true, the missing parts.yml, pages/main.yml, pages/secondary.yml and sets/example_set.yml are created and existing files are never overwritten; while false the default-data write is skipped (config.yml, lang/*.yml and gui/accessory_gui.yml are not governed by this switch)
drop_on_deathbooleanNofalsetrue / falseWhether the enabled page's accessories are added to the death drop list
uniquebooleanNotruetrue / falseWhether two identical accessories may sit on the same page at once
slot_sourcesobjectNosee "slot_sources"—Additional sources declaring which slots an accessory may occupy; falls back to the built-in defaults
durabilityobjectNosee "durability"—Accessory durability switch and per-hit damage points; falls back to the built-in defaults (disabled)
persistenceobjectNosee "persistence"—Autosave and shutdown flush parameters

drop_on_death ​

When set to true, the dead player's enabled page accessories are appended to the death drop list and handled by the vanilla death flow rather than being pushed back into the inventory — pushing them back would leave the same accessory in both the drops and the inventory

  • The option has no effect when another plugin already enabled keepInventory (the inventory is kept on death); accessories are kept either way
  • Only the enabled page is cleared: accessories on other pages stay in the data and are neither cleared nor dropped. When the enabled page is empty (for example the player has no access to that page), nothing is dropped
yaml
# Allow the enabled page's accessories to drop on death
drop_on_death: true

unique ​

Deduplication is judged within one page: only the accessories the player already holds on that page are checked, so the same accessory on two different pages never conflicts

The preferred criterion is EmakiItem's item definition id (the item's identity string emakiitem:<definitionId>): two accessories of the same definition count as duplicates even when their levels or affixes differ. When EmakiItem is unavailable, or the item carries no definition id, the check falls back to vanilla material comparison (material:<lowercased material name>), so two accessories sharing a material are treated as duplicates

yaml
# Turn deduplication off and allow two of the same accessory on one page
unique: false

slot_sources ​

This section adds sources for the slots an accessory can occupy. The item-carried declaration comes from emakiitem:accessory_slots, written by EmakiItem; the two options below are extra sources you opt into on top of it, and any match allows the accessory to be worn. With both options disabled (the default) only the item-carried declaration counts. Note that EmakiAttribute's active_slot and the EmakiSkills skill payload no longer take part in this decision

FieldTypeRequiredDefaultAllowed valuesDescription
slot_sources.pdc.enabledbooleanNofalsetrue / falseWhether a slot list is appended from the item's PDC
slot_sources.pdc.keystringNo""—Full NamespacedKey, used verbatim without case rewriting. An empty value or one that does not parse as a NamespacedKey silently disables this source (nothing is read even when enabled is true). A string list is preferred; when absent, a single string is read and split by separator
slot_sources.lore.enabledbooleanNofalsetrue / falseWhether a slot list is appended from lore using regular expressions
slot_sources.lore.patternslist<string>Nosee the default snippet below—Regex list; capture group 1 of each pattern holds the slot list text, matched with find (a substring hit is enough). MiniMessage tags are stripped and whitespace collapsed before matching, so write plain-text patterns. A pattern that fails to compile logs accessory.invalid_slot_pattern and is skipped; an empty (or fully invalid) list silently disables this source
slot_sources.lore.separatorstringNo","—Slot list separator, also used by the pdc string fallback; an empty or all-whitespace value falls back to ","
slot_sources.lore.aliasesmap<string,string>Nosee the default snippet below—Maps lore slot names to part ids in parts.yml. Lookup tries the original text first, then the lowercase form; unlisted values are used as normalized (trimmed, lowercased, spaces turned into underscores). Entries whose key or value is blank are dropped while loading

Shipped default snippet:

yaml
slot_sources:
  pdc:
    enabled: false
    key: ""
  lore:
    enabled: false
    patterns:
      - "^穿戴部位[::]\\s*(.+)$"
    separator: ","
    aliases:
      "戒指": "ring"
      "项链": "necklace"
      "腰带": "belt"
      "护符": "charm"

Example: read 穿戴部位: 戒指,项链 from lore and map the localized slot names onto part ids

yaml
slot_sources:
  lore:
    enabled: true
    patterns:
      - "^穿戴部位[::]\\s*(.+)$"
    separator: ","
    aliases:
      "戒指": "ring"
      "项链": "necklace"

Accessory pages: pages/*.yml ​

Each file defines one page. The page id comes from page_id, independent of the file name

yaml
page_id: "main"
display_name: "<gold>Main Accessories</gold>"
order: 1
gui_template: "accessory_gui"
permission: ""
parts:
  - "necklace"
  - "ring"
  - "belt"
  - "charm"
FieldTypeRequiredDefaultAllowed valuesDescription
page_idstringYes——Page id, also the storage partition key inside player data; when it normalizes to blank the whole file is skipped
display_namestringNo""—Page display name with MiniMessage support, used by switch cells, the enable cell and /eacc pages; when blank the window falls back to showing page_id
orderintegerNo2147483647 (Integer.MAX_VALUE)—Page sort order, ascending; the lowest value is the default enabled page, ties broken by page_id ascending
gui_templatestringNo"accessory_gui"—Layout id used by this page (the id of gui/<id>.yml); several pages may share one layout. A missing layout makes the page impossible to open (accessory.gui_template_missing)
permissionstringNo""—Empty means available to everyone; a non-empty value is matched verbatim
partslist<string>Yes——Parts enabled on this page; every value must be a part id defined in parts.yml. Duplicates are removed and an empty list discards the whole page (accessory.page_without_parts)

Remember these three page rules:

  • Isolated storage: the same slot instance id holds a separate accessory on each page, with no overwriting
  • Only the enabled page contributes: accessories on other pages are kept but produce no attributes, skills, or set piece counts
  • Losing permission keeps data: when a player loses the enabled page's permission, nothing is cleared and no page switch happens. The page cannot be opened and its accessories stop contributing, and the player recovers items with /eacc retrieve <page>

Parts listed on a page expand into slot instances according to count in parts.yml; you must then place each instance in the layout referenced by gui_template, otherwise the slot is unusable and a warning is logged. A parts entry naming an undefined part does not appear on the page and logs accessory.page_unknown_part

durability ​

Accessories never occupy vanilla equipment slots, so vanilla durability events (PlayerItemDamageEvent) never reach them; durability wear is deducted by EmakiAccessory itself when the player takes damage. The feature is disabled by default

yaml
durability:
  enabled: false
  damage_per_hit: 1
FieldTypeRequiredDefaultRangeDescription
durability.enabledbooleanNofalsetrue / falseFeature switch. While disabled no durability is deducted and the durability data on items stays untouched (equivalent to vanilla durability never being consumed)
durability.damage_per_hitintegerNo1≥ 1 (values below 1 are clamped)Durability points deducted from every eligible accessory each time the player takes damage

Scope and criterion:

  • Only accessories on the enabled page are checked; other pages never take damage
  • Only accessories that carry a durability ceiling take damage: the ceiling comes from the item's max_damage component, falling back to the material's own durability when unset (the same criterion EmakiItem's repair system uses); items with a ceiling of 0 are never deducted
  • You can opt a part out in parts.yml with durability_deduction: false (allowed by default)
  • Deduction listens to EntityDamageEvent (MONITOR, cancelled events ignored); damage with finalDamage ≤ 0 does not deduct
  • Deduction never exceeds the ceiling and stops once it is reached

After it is exhausted: that accessory's attributes, skills and set piece count all stop applying, the item stays in its cell, a gui.durability_broken marker shows in the window and a single durability.broken_notify message is sent. Take the item out, restore durability through EmakiItem's repair flow and it becomes effective again. Wear is written by the autosave, so no extra save command is needed

persistence ​

FieldTypeRequiredDefaultAllowed valuesDescription
persistence.autosave_secondsintegerNo60≥ 0 (negatives are clamped to 0)Autosave interval in seconds. Setting it to 0 disables timed saving; the actual timer period is max(100, seconds × 20) ticks, i.e. at most once every 5 seconds
persistence.drain_timeout_secondsintegerNo10≥ 1 (smaller values are clamped to 1)How long, in seconds, shutdown waits for unflushed writes to finish
yaml
persistence:
  autosave_seconds: 120
  drain_timeout_seconds: 15

Once you disable timed saving, data is flushed only when the window closes, when the player quits, on /eacc admin save and on server shutdown

After drain_timeout_seconds elapses, the saved/failed/remaining-dirty counts are logged and shutdown continues; it never waits forever and blocks the server shutdown flow

Player data files ​

Each player's accessory data lives in plugins/EmakiAccessory/data/<player UUID>.yml. The structure currently written to disk (format_version: 2):

yaml
format_version: 2
player_name: "Steve"
enabled_page: "main"
pages:
  main:
    ring_1: "<Base64 item bytes>"
  combat:
    ring_1: "<Base64 item bytes>"
FieldTypeRequiredDefaultAllowed valuesDescription
format_versionintegerYes (on write)2always written as 2 todayOn-disk structure version. The value is not validated when reading; recognition is structural only (see the next two sections)
player_namestringNo""—Player name at the last write; when blank on read it is filled in from the online name
enabled_pagestringNothe default page (lowest order)—The page id the player currently enables; at runtime an unknown page falls back to the default page
pagesmap<string, map<string, string>>No{}—Main structure: pages.<pageId>.<slotInstanceId> = Base64 item bytes. The same slot instance holds a separate entry per page with no overwriting; empty pages are not written
pages.<pageId>.<slotInstanceId>stringNo——Text produced by Base64-encoding the result of ItemStack.serializeAsBytes; an entry that fails to decode is dropped with a warning
slotsmap<string, string>No——Legacy (v1) key, read during migration only and never written again

Automatic v1 → v2 upgrade ​

Legacy (v1) files store accessories under a top-level slots: section and have no pages. When the plugin loads such a file, it moves the items into the default enabled page (the page with the lowest order) and logs Migrated accessory data ... from format 1 to 2. The trigger is "no pages: and a top-level slots: exists"; the actual format_version value is not inspected. When no default page is available (no pages configured), migration does not happen and the file is left untouched

No overwrite when the structure is unrecognized ​

Two situations hit this protection (a file has neither pages: nor slots: -- for example after manual corruption or because it came from another structure; or a legacy slots: file has no default page to migrate into):

  • The file is left untouched and never overwritten; the player starts this session with empty accessories, and an Unrecognized accessory data structure or migration warning is logged
  • Every later save for that player is skipped, logging Skipped saving accessories ... because its data file structure was not recognized

So back up plugins/EmakiAccessory/data/ before upgrading or hand-editing: once a structure is unrecognized, the plugin will not overwrite the original file with empty data