Astral Realms Documentation Help

Discovery & Matching

A player discovers an entry whenever the plugin sees them in possession of its item. Discovery is automatic — there is no command for it, and YAML authors do not need to wire anything up.

What triggers a discovery

Event

Source

When it fires

PlayerItemGiveEvent

AstralCore

An action or service hands an item to the player (e.g. [give-item], shop purchase, quest reward).

EntityPickupItemEvent

Bukkit

The player picks up an item entity from the ground.

InventoryClickEvent

Bukkit

The player clicks on an item in any inventory other than an AstralCore menu.

All three listeners run at MONITOR priority with ignoreCancelled = true, so they never interfere with other plugins and they only see interactions that actually succeeded.

Matching rules

When the plugin sees an item it asks the BlueprintService whether that item is registered with any collection. The lookup uses two paths:

Fast path — vanilla items

If the item has no metadata or no persistent data, it goes through a normalised hash lookup:

  1. Clone the item.

  2. Set the amount to 1.

  3. Look up the result in a Map<ItemStack, CollectionEntry> built at load time.

This means enchantments, custom names, NBT, and amount are all considered for matching — an unenchanted diamond and a sharp-V diamond are two different lookups.

Slow path — custom items

When the item carries metadata, AstralCollections walks through every registered item supplier (skipping the vanilla and HeadDatabase suppliers) and asks each one for the item's key. If a supplier recognises the item, that key is looked up in a separate Map<Key, CollectionEntry>.

This makes it possible to use CraftEngine items, AstralItems items, or items registered by any other plugin that integrates an ItemStackSupplier with AstralCore.

The supplier-key index is built 100 ticks after server start so that all suppliers have time to register their items.

What happens on a discovery

DiscoveryListener detects an item │ ▼ BlueprintService.findByItem(itemStack) │ matching entry? │ ▼ CollectionService.markDiscovered(player, entry) │ ├── add entry ID to the player's progress data │ (returns false if it was already recorded — no further action) │ ├── send the `entry-discovered` message │ └── if progress == total entries: ├── add collection ID to completedCollections └── run completion-actions on the main thread

The "already recorded" check uses a Multimap with HashSet values, so the same entry can never trigger the discovery message twice for the same player.

Edge cases

  • Air items are never matched.

  • Items with empty metadata go through the fast path even if they look custom; if your custom item never matches, double-check that the item supplier returns a non-null key for it.

  • Multiple entries with the same item — the first one returned by the lookup wins. Avoid defining the same item under two different entry IDs.

  • Completed collections still fire the entry-discovered message for any new entry — but the player already discovered them all, so by definition there are none left. Re-running discovery on a completed collection is a no-op.

Last modified: 25 July 2026