Skip to content

Slot animations, typed registries and API cleanup - #273

Open
1robie wants to merge 46 commits into
Maxlego08:developfrom
1robie:develop
Open

1robie wants to merge 46 commits into
Maxlego08:developfrom
1robie:develop

Conversation

@1robie

@1robie 1robie commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds slot animations and typed registries, and reorganises the API around them. Everything that existed in 1.1.2.0 keeps working: old methods are deprecated (since = "1.1.2.1", forRemoval = true) and delegate to their replacements. Only code that was never released was moved or renamed outright.

Slot animations (engine and API)

  • Menus can reveal their items when they open and take them away when they close, buttons can loop their own idle animation, and an animation can be played on an open menu at any time.
  • 11 built-in reveal orders (sequential, spiral, wave, matrix, custom, ...). Addons add their own with @AutoSlotAnimationLoader / registerSlotAnimation.
  • Named animations live in the reloadable Registries.ANIMATION registry. Plugins add their own with AnimationManager#registerAnimation and listen to AnimationStartEvent / AnimationEndEvent.
  • AnimationManager#getHistory() records which animations a player has seen, in a new database table; /zm resetanimation <player> [menu] clears it.

Typed registries

  • RegistryManager / Registries: namespaced keys, an owner for every entry, aliases.
    • Static registries are frozen once the server has loaded.
    • Reloadable registries are rebuilt into a snapshot and swapped in all at once.
    • Open registries drop a plugin's entries when it disables.
  • These now live in registries: buttons, actions, requirements, item rules, item comparisons, material loaders, item components, command argument types, custom item mechanics, and button and inventory options. The old register* methods still work: they register the entry for the plugin that provides it, and log a name clash instead of throwing.
  • ZMenuKeys.parse turns any name into a key, so names registered before this change still work: characters a key cannot hold become _ (Shop+Buy is zmenu:shop_buy). RegistryNameTest checks this for every registry.
  • Menus are a reloadable registry keyed plugin:file. A file that breaks on reload keeps its previous version, and a YAML error no longer loads an empty menu.

API reorganisation

  • New ItemStackService (load, convert, serialize, player heads, item comparisons), SoundService and MenuPlugin#readYamlFile. The menu page of a player moved to PaginationManager.
  • InventoryManager methods that belong elsewhere are deprecated and delegate to their new home, as are API types zMenu never uses, the pre-1.9 Potion methods and Spigot platform detection.

Cleanup and fixes

  • Removed code for versions older than 1.20.6 (including the pre-1.14 NMS dupe manager), the unused NBT-API dependency, unused classes, commented-out code and the legacy ClassicMeta formatter.
  • New features.custom-items option in config.yml to turn custom items off.
  • Fixes:
    • lore item comparison was never registered;
    • /zm reload messages were missing;
    • escaped % signs were not restored in some placeholder paths;
    • the discord webhook avatar was wrong;
    • the abandoned map-based item reader was removed;
    • all PlaceholderAPI lookups share one result cache.

Compatibility

  • Addons built against 1.1.2.0 keep compiling and running; they get deprecation warnings pointing to the replacements.

1robie added 30 commits October 22, 2026 12:08
Slot animations
- Reveal/conceal animations for inventories (open, close, actions), per-button
  settings, idle animations, temporary items with keyframes, variants, seeds.
- Built-in reveal orders in api.animation.slot.order; each type is a
  SlotAnimationLoader registered through @AutoSlotAnimationLoader, addons use
  InventoryManager#registerSlotAnimation.
- The compiler is internal (Common fr.maxlego08.menu.animation.compile); the
  API keeps the entry points, settings and order implementations.

Registries
- Typed registries (RegistryType, KeyedRegistry, WritableRegistry,
  RegistryManager) with namespaced keys, owners, aliases, and static
  registries frozen once the server has loaded.
- Reloadable registries rebuilt into a snapshot and swapped atomically
  (ReloadContext, ReloadReport, ReloadValidator).

Inventories
- zMenu's own menus live in a reloadable registry keyed zmenu:<file>: a file
  that fails on reload keeps its previous version, load events fire once the
  new snapshot is live, YAML errors no longer load an empty menu.
- Key-based InventoryManager methods; the name-based ones are deprecated for
  removal and delegate to them.
Conflicts resolved:
- ZInventoryManager: inventories stay in the registry; develop's shared-name rule (zMenu first, then
  plugin name order), its collision warning and its same-name re-registration on delete are kept,
  over keys. deleteInventory only removes one of zMenu's own while it is still the live entry, so
  InventoryHotReload (load new, then delete old) keeps the new version.
- InventoryDefault: develop's WrappedTask refresh timers and cancelTimers(); the animation and idle
  tasks are stopped with them.
- Inventory: extends both Keyed and SectionSerializable.
- InventoryManager, Configuration, Button, ZMenuPlugin, VInventoryManager: both sides kept.
- InventoryLoader: develop removed save(); the animation validation helpers stay.
- Commands: develop's NameSuggestions, fed with the inventory keys.
- BedrockReplacementListener (from develop) uses the key-based lookup.
Menus write their open-animation and close-animation, buttons their animation
settings and idle-animation, and the animation action its file, stop, inline
animation, mode and freeze, in the form the loaders read back.

- Each built-in SlotAnimation writes its type (matrix its rows, custom its
  order); an animation built in code cannot be serialized.
- Groups write only what differs from the group they inherit from.
- Settings and idle animations that only name files are written back as
  file:, otherwise in full.
- RandomPitchSoundOption writes its pitch range.
- Round-trip test over every example menu, and fixtures for the animation
  action.
- Add Registries.ANIMATION; animation files and plugin-registered animations
  are provided to it, a broken file keeps its previous version
- Animations are keyed (zmenu:<file> or plugin:name); CustomAnimation takes a Key
- The animation action resolves a Key loaded once from file:
- Skip button idle animation files when loading inventory animations
Which animations a player has already seen now lives in AnimationHistory,
reached with AnimationManager#getHistory(), instead of eight methods on
InventoryManager. ZAnimationHistory holds the session and database memory
that ZInventoryManager used to keep.
The methods that only repeat MenuPlugin (scheduler, enchantments, font
image, meta updater) and the ones meant for zMenu itself (startup,
loaders, commands, messages) are deprecated for removal since 1.1.2.1.
MenuPlugin#isPaper, isPaperOrFolia and isSpigot are deprecated too, as
zMenu only runs on Paper.

zMenu no longer calls any of them: ZMenuPlugin hands out ZInventoryManager,
and click type parsing and case-insensitive plugin lookup moved to
ClickTypeParser and PluginUtils in Common.
The page and page count of the inventory a player opened last, read by
the player_page placeholders, now live in PaginationManager
(getMenuPage, getMenuMaxPage, setMenuPage) and are dropped with the
player's other pagination states when they leave. The InventoryManager
methods are deprecated for removal and delegate to them.
MenuPlugin#getItemStackService() now loads menu items, converts an item
into a menu item, writes items as base64, applies player head skins and
holds the item comparisons. ZItemStackService takes that code out of
ZInventoryManager. The InventoryManager methods and
MenuPlugin#loadItemStack are deprecated for removal and delegate to it;
zMenu itself no longer calls them.
It only copied its arguments into a list for the List overload, which
stays the one way to open an inventory with its previous inventories.
zMenu requires 1.20.6, so the dupe manager is always PDCDupeManager.
NMSDupeManager was the last user of ItemStackCompound, NMSProvider and
the NMS handlers, so NMS/Base, NMS/v1_20_R3 (1.20.4) and NMS/v1_20_R4
go with it. NMS/v1_21_R1 keeps the anvil inventory and packet listener.
NmsVersion and NMSUtils read the version from the CraftBukkit package,
which has no version since 1.20.5, so every check returned a constant.
The NMS reflection in the item serializer, the title and action bar
helpers, the pre-1.13 numeric material table, the 1.16-1.20.4 toast
format and the unused legacy ItemStackLoader could never run on a
supported server.
ElapsedTime, ProgressBar and DyeColorVariantLoader had no users, and five
of the six LocationUtils methods were never called.
No source imports de.tr7zw; the library was only shaded and relocated.
The hand-written codec produced standard padded Base64 and skipped
whitespace when decoding; java.util.Base64's encoder and MIME decoder give
the same results, checked on 20000 random inputs, so stored items still
read back.
Drops the old firework reader and its unused colour list helper, the old
download call, the Persist save/load pair, and commented log and listener
lines. The commented messages of /zm reload command and the disabled
anti-dupe Discord notification are left for their own fixes.
LoreSimilar had no @AutoItemStackSimilar, so 'lore' was never registered
and every lookup fell back to a full item comparison.
They were commented out when the command moved to paper-dispatch, so the
command gave no feedback at all.
ZMenuItemStack#setTypeMapAccessor lost its last callers in 78b075b,
when the take_item action and the item permissible moved to
loadItemStack. It kept a third enchantment parser that had drifted from
the loader's (comma only, Enchantment.getByName), so MenuItemStackLoader
is now the only one.
Dialog inputs escape % as U+F000 so PlaceholderAPI leaves them alone.
Placeholder.Api restored it, Placeholder.Local did not, so without
PlaceholderAPI only ActionHelper and MenuPlugin#parse (which added their
own replace) gave the % back, and PapiUtils left the escape character in
the text. Local now restores it too and the duplicate replaces are gone.
PapiUtils kept its cache in instance fields, so every ZUtils subclass,
including each ZMenuItemStack, parsed the same placeholder for the same
player on its own. The cache is now shared, still keyed by text and
player and rebuilt when cache-placeholderapi changes.
PlatformType, @PaperOnly and @SpigotOnly are deprecated for removal since
zMenu only runs on Paper. VersionFilter keeps refusing @SpigotOnly
classes without asking PlatformType, the Paper client version provider
is always available, and ZMenuPlugin answers the deprecated
isPaper/isPaperOrFolia/isSpigot with their only possible values.
NoneButton, ClickAction, TrimHelper, ZPersistentDataType,
ZDamageReductionRecord, ZToolRule, ResolvablePlayerProfile,
ItemCreateException, StringConsumer, ReturnConsumer,
ItemComponentLoaderInterface and EnumInventory.INVENTORY_MARKETPLACE have
no user in zMenu; each is deprecated for removal and names its
replacement where there is one. ZDataManager uses Function instead of
ReturnConsumer.
BaseInventory#getSpigotInventory returned the same inventory as
getInventory; it is now a deprecated default and zMenu calls
getInventory. InventoryEngine#setMaxPage and the MenuItemStack cache
setters are deprecated since zMenu computes those values itself.
InventoryEngine#cancel loses its wrong @contract(pure = true).
fromDamage reads damage values that potions lost in 1.9 and looks up
type names that no longer exist; toDamageValue and getNameId always
return 0; the brewer is never set or read. All are deprecated for
removal.
57 'TODO Auto-generated constructor stub' lines in the API exceptions and
MenuEvent marked nothing left to do.
RegistryType.Kind.OPEN registries are never frozen; RegistryOwnerListener
removes a plugin's entries from them when it disables, so a plugin
reloaded at runtime can register again. WritableRegistry gains
unregister, unregisterAll(owner) and an all-or-nothing register with
aliases. RegistryOwners resolves the owner of entries handed to the old
register methods (the plugin that provides their class) and turns a
clash into a warning. ZMenuKeys.parse turns any name into a key, reading
spaces, '-' and every character a key cannot hold as underscores, so
names registered before the registries keep working; tryParse only
returns null for a blank name. Menus waiting for an element match its
name the same way.
PermissibleLoader is Keyed (its type read like a YAML name) and lives in
the open Registries.PERMISSIBLE registry. Registries notify listeners of
each new key and alias, so menus waiting for a requirement type load
whichever way it is registered. registerPermissible and getPermissibles
are deprecated; a type registered twice is now refused with a warning
instead of silently replacing the first one.
1robie added 16 commits October 23, 2026 16:07
ActionLoader is Keyed by its first name, with the others as aliases, and
lives in the open Registries.ACTION registry; names are read like YAML
types, so any case or space/dash/underscore spelling resolves. zMenu
registers its built-ins straight into the registry, registerAction is
deprecated, and the serialization test reads the registry instead of the
old private map.
ButtonLoader is Keyed by its name, with its aliases as keys, and lives in
the open Registries.BUTTON registry owned by ButtonLoader#getPlugin. The
O(n) name/alias clash scan and the linear lookup are gone. The
ButtonManager register/unregister methods, getLoaders(Plugin) and
ButtonLoaderRegisterEvent#register are deprecated; a clash is logged
instead of throwing ButtonAlreadyRegisterException, which is deprecated.
RuleLoader is Keyed by its type, with its aliases as keys, and lives in
the open Registries.RULE registry. ZRuleLoaderRegistry is no longer a
lazily created singleton on the deprecated Registry base: it is a static
helper that registers the built-ins and reads a rule through the
registry. The unreleased ButtonManager#registerRuleLoader is removed and
the published RuleLoaderRegistry is deprecated.
ItemStackSimilar is Keyed by its name and lives in the open
Registries.ITEM_VERIFICATION registry. ItemStackService keeps
getVerification(String) for names read from files and drops the
unreleased registerVerification/getVerifications; the deprecated
InventoryManager methods use the registry.
MaterialLoader is Keyed by its prefix and lives in the open
Registries.MATERIAL_LOADER registry, replacing the list scanned on every
item build. The InventoryManager register and lookup methods are
deprecated; zMenu reads the registry when it resolves a prefixed material.
ItemComponentLoader is Keyed: minecraft:<name> for Minecraft components,
with <name> as an alias, and <plugin>:<name> for a plugin's. They live
in the open Registries.ITEM_COMPONENT registry; ZComponentsManager reads
it and takes the registry manager in its constructor, so tests still use
an isolated one. registerComponent is deprecated and logs a clash; the
exception it used to throw is deprecated.
CommandArgumentValidator is Keyed by its type and lives in the open
Registries.COMMAND_ARGUMENT_VALIDATOR registry. The block validator was
created with the type "material", so it shadowed nothing and "block"
never resolved; MaterialArgumentValidator now has material() and
block() with their own types, and block only accepts blocks. The
CommandManager register, unregister and list methods are deprecated.
MechanicFactory is Keyed by its id and lives in the open
Registries.MECHANIC registry. A custom item records the factory's own id,
so a mechanic written with another spelling still matches its listeners.
ItemManager#registerMechanicFactory is deprecated.
Options are registered as classes, so they get keyed wrapper records,
ButtonOptionType and InventoryOptionType (<plugin>:<class name>), held
in the open BUTTON_OPTION and INVENTORY_OPTION registries. Buttons,
inventories, Bedrock forms, dialogs and inventory serialization read
the registries; the InventoryManager option methods are deprecated and
still register the same class only once.
The inventory type, Bedrock type, dialog type and dialog action
registries only ever registered and read entries, so they keep their own
EnumMap or HashMap instead of extending the deprecated Registry<T,R>.
RegistryNameTest checks that names a key cannot hold as they are
(symbols, accents, spaces in a namespace, a second ':') still register
and are found by the same name, through the old register methods and
the registries, for buttons, actions, requirements, material loaders,
item comparisons, command argument types, mechanics and rules.
The slot animation loader built sounds with the internal ZSoundOption and
RandomPitchSoundOption and read file references through Common's
YamlFileCache, which a plugin compiling against the API cannot reach.
SoundService creates fixed and random-pitch sounds and reads them in the
animation format; readYamlFile exposes the file cache. The loader, the
animation action and its loader now only use the API. The unreleased
InventoryManager#createSoundOption is removed.
zMenu keeps the animation engine, its API, the history, the built-in
reveal orders, /zm resetanimation and the animations folder with its
default files. Reading open-animation, close-animation, the button
animation and idle-animation, the animation action and the files of the
animations folder now belongs to zMenuPlus, like player-inventory, along
with the example menus and their tests. ContainerInventory gets
setSlotAnimation/setCloseAnimation in the API so an inventory option can
set them. The animation docs are kept out of git.
features.custom-items in config.yml disables the items folder, the
itemjoin and on-click mechanics, the item update on join and
/zm giveitem. Features are read once at startup; /zm reload warns when
one changed. When disabled, getItemManager() returns DisabledItemManager,
which knows no item and ignores addon registrations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant