Astral Realms Documentation Help

Developer API

AstralCrates exposes its state through the CrateService hung off the main plugin instance (plugin.crates()) and, more commonly, through an item-supplier registration that lets any other plugin resolve a crate's key as an ordinary AstralCore item reference. There is no static CratesAPI façade and no custom Bukkit events — integration is service calls and the item-supplier registry only.

Maven coordinates

<dependency> <groupId>com.astralrealms</groupId> <artifactId>crates</artifactId> <version>1.0-SNAPSHOT</version> <scope>provided</scope> </dependency>

Repository: https://maven.astralrealms.fr/repository/maven-public/.

crates:<id> item supplier

On onEnable, AstralCrates registers itself with AstralCore's item-supplier registry under the name crates:

AstralPaperAPI.registerItemStackSupplier("crates", new CrateItemSupplier(this));

This means any AstralCore call site that accepts an item-supplier key ([give-item] actions, /give, menu item definitions, etc.) also accepts a crates:<crate-id> key, resolving to that crate's tagged key ItemStack — the same item CrateConfiguration#createItemStack would build. CrateItemSupplier implements AstralCore's ItemStackSupplier contract:

Method

Behavior

get(String id)

plugin.crates().findById(id) then crate.createItemStack(1); null if the id doesn't resolve.

get(Player player, Key key, int amount)

Same lookup using key.value() (the part after the :), building the stack with amount copies instead of 1.

completions()

Every loaded crate's id, as Key.key("crates", crate.id()) — feeds tab-completion for any command/menu editor that lists item-supplier keys.

keyOf(ItemStack itemStack)

plugin.crates().getCrateKey(itemStack) mapped to Key.key("crates", id), or null if the stack carries no astralcrates:crate PDC value. Lets other plugins normalise an AstralCrates key stack back to a registry key.

# anywhere an item-supplier key is accepted actions: - "[give-item] crates:example"

There is no built-in command to give a key for this reason — see Commands.

CrateService

Accessed via AstralCrates#crates().

import com.astralrealms.crates.AstralCrates; import com.astralrealms.crates.service.CrateService; CrateService crates = AstralCrates.getPlugin(AstralCrates.class).crates();

Method

Returns

Behavior

findById(String id)

Optional<CrateConfiguration>

Case-insensitive match against every loaded crate's id.

findByLocation(Location location)

Optional<CrateConfiguration>

Block-coordinate match (world name + block X/Y/Z) against every crate's locations map; Optional.empty() if location is null.

isKey(ItemStack itemStack)

boolean

true if the stack has item meta and its PDC contains CrateService.CRATE_KEY at all (any crate). false for a null /meta-less stack.

isKeyFor(ItemStack itemStack, CrateConfiguration configuration)

boolean

Reads the PDC string under CRATE_KEY and compares it case-insensitively to configuration.id(). false for a null /meta-less stack or a mismatched id.

getCrateKey(ItemStack itemStack)

Optional<String>

The crate id stored on the stack's PDC, or Optional.empty() if the stack is null /meta-less or carries no CRATE_KEY value.

pickRandomReward(Player player, CrateConfiguration configuration)

CrateReward (nullable)

Builds a RandomCollection<CrateReward> from every reward whose requirements() pass for player (a failed ExecutableRunException is logged and the reward is skipped, not selected), weighted by each reward's chance, and draws one. Returns null if no reward is eligible.

pickMultipleRewards(Player player, CrateConfiguration configuration, int amount)

List<CrateReward>

Calls pickRandomReward amount times, keeping only non-null draws — the result can be shorter than amount if some draws found no eligible reward.

openCratePreview(Player player, CrateConfiguration configuration)

void

No-op if the crate has no menu: configured; otherwise opens the crate's preview menu via plugin.menus().computeAndOpen(player, menu.id(), Map.of("crate", configuration)).

crates()

Set<CrateConfiguration> (unmodifiable copy)

Every currently loaded crate.

crates.findByLocation(clickedBlock.getLocation()).ifPresent(crate -> { boolean holdingKey = crates.isKeyFor(player.getInventory().getItemInMainHand(), crate); if (holdingKey) { CrateReward reward = crates.pickRandomReward(player, crate); } });

load() and createHolograms(CrateConfiguration) also exist on CrateService but are lifecycle/internal methods — called from AstralCrates#loadConfiguration() on enable/reload — not intended as an external integration surface.

CrateService.CRATE_KEY

public static final NamespacedKey CRATE_KEY = NamespacedKey.fromString("astralcrates:crate");

Every crate key ItemStack carries exactly one PDC entry, written by CrateConfiguration#createItemStack:

Key

Type

Value

astralcrates:crate

PersistentDataType.STRING

The owning crate's id.

Reading it directly (equivalent to getCrateKey):

String crateId = itemStack.getItemMeta() .getPersistentDataContainer() .get(CrateService.CRATE_KEY, PersistentDataType.STRING);

CrateConfiguration#createItemStack

public ItemStack createItemStack(int amount)

Builds the crate's key item from its key: ItemStackWrapper (see Crate Files), resolving any %placeholders% in that wrapper against RootPlaceholderContainer.get() — the global placeholder container, not a per-player one, so only server-wide placeholders (not player-scoped ones like %player_name%) resolve correctly here. It then tags the built stack's PDC with CRATE_KEY = this.id, sets the stack's amount to amount, and returns a clone. Both CrateItemSupplier#get overloads and any /give/[give-item] crates:<id> call ultimately go through this method, so every key ItemStack in the network — however it was obtained — is tagged identically.

CrateReward type serializer

AstralCrates registers a Configurate TypeSerializer for CrateReward on enable:

this.registerTypeSerializer(CrateReward.class, new CrateRewardTypeSerializer());

CrateRewardTypeSerializer reads a reward node as: an ItemStackWrapper at the node root (item: and its siblings), chance as a double, actions: as a PaperActionList, and requirements: as a (@Nullable) PaperRequirementList. Serialization back to config is unsupported (CrateRewardTypeSerializer#serialize throws UnsupportedOperationException) — rewards are read-only config, never written back by the plugin. CrateReward itself is a record CrateReward(ItemStackWrapper itemStack, double chance, PaperActionList actions, @Nullable PaperRequirementList requirements) and doubles as the reward placeholder namespace — see Placeholders.

Dependencies AstralCrates itself consumes

AstralCrates is a consumer, not just a provider, of two other plugins' APIs — relevant if you're tracing why a crate's holograms or placeholders behave a certain way:

  • AstralHologramAPI (from AstralHologram, a hard dependency) — CrateService#createHolograms registers one text hologram per crate location via AstralHologramAPI.registerTextHologram(id, param, location, true, BillboardType.FIXED), 1.2 blocks above the block, using the crate's hologram: lines as the text param. CrateService#load unregisters every previously-tracked hologram id via AstralHologramAPI.unregisterHologram(id) before reloading. Hologram creation/teardown is skipped entirely when crates-enabled: false (see Configuration).

  • AstralPaperAPI placeholder containersCrateConfiguration and CrateReward implement ComplexPlaceholder (namespaces crate and reward), and are resolved through the standard AstralPaperAPI.createPlaceholderContainer(player) chain like every other plugin's placeholders; no crate-specific placeholder wiring is needed by callers. createItemStack is the one place that deliberately uses the global RootPlaceholderContainer instead (see above).

No custom Bukkit events

AstralCrates fires no custom Bukkit events — there is no CrateOpenEvent, CrateWinEvent, or similar. Other plugins observe crate activity by:

  • Calling CrateService directly (findById, findByLocation, isKeyFor, pickRandomReward/pickMultipleRewards) for live lookups, or

  • Resolving key items through the crates:<id> item supplier, or

  • Hooking the side effects a crate's own open-actions/win-actions already produce (see Opening & Rewards) — e.g. an economy or logging action placed directly in a crate's YAML runs through the normal action pipeline and is observable the same way any other action is.

VaultDisplayItemEvent (cancelled/filled by CrateListener) and BlockPlaceEvent (cancelled by KeyListener for key items) are vanilla/Paper events AstralCrates listens to, not events it defines.

Last modified: 25 July 2026