Astral Realms Documentation Help

Developer API

AstralDungeons exposes a small surface for other plugins: one Bukkit event, one AstralSync snapshot type, one AstralCore item supplier, and the registered actions and requirements. There is no static DungeonsAPI facade — the services are reached through the plugin instance.

DungeonCompletedEvent

com.astralrealms.dungeons.event.DungeonCompletedEvent — a PlayerEvent, fired on the bridge, on the main thread, once per claimed run. It is the intended hook for changing what a dungeon run pays out.

It fires inside PlayerDataService#claimRewards, after the dungeon key has been consumed and before the reward-actions run, so a listener sees the run's real numbers and can rewrite them before anything acts on them.

Member

Type

Mutable

Description

getPlayer()

Player

—

The returning player.

blueprint()

DungeonBlueprint

no

The bridge blueprint for the run.

killedMobs()

int

no

Mobs this player personally killed.

duration()

long

no

Run length in milliseconds.

completed()

boolean

no

Whether the boss was defeated while this player was alive.

experience()/experience(double)

double

yes

The experience payout. The setter is fluent (Lombok lombok.accessors.fluent = true across the whole plugin), so it is experience(v), not setExperience(v).

commands()

List<String>

yes (the list is mutable)

The queued console commands. Add, remove or rewrite entries in place.

It is not cancellable, and it carries no isCancelled flag — a listener that wants to withhold a payout must zero the experience and clear the command list.

@EventHandler public void onDungeonCompleted(DungeonCompletedEvent event) { // Double XP weekend, but only for a real clear if (event.completed()) event.experience(event.experience() * 2); // Bonus for a fast run if (event.completed() && event.duration() < Duration.ofMinutes(5).toMillis()) event.commands().add("crate give %player_name% speedrun 1"); }

%player_name% in a command is substituted at dispatch time; nothing else is.

After the event, event.toPlayerData() rebuilds a DungeonPlayerData from the (possibly edited) values, and that object is what the reward-actions placeholders, the analytics event and the leaderboard updates all read. Note that toPlayerData() stamps a fresh createdAt, which is unused past this point.

DungeonInventory snapshot

DungeonInventorySnapshotAdapter registers a SnapshotAdapter<DungeonInventory> with AstralSync under the key dungeons:inventory, from both modules. Any plugin on either tier can read a player's gear:

Optional<DungeonInventory> inventory = SyncAPI.findData(playerId, DungeonInventory.class); inventory.ifPresent(inv -> { ItemStack[] worn = inv.equippedContent(); // preset content, or the inventory's own array ItemStack helmet = inv.getItem(0); // the inventory's own array, always });

Mutating the returned object is how the bundled actions work, and is how AstralSync picks changes up — there is no explicit save call. getItem/setItem throw IndexOutOfBoundsException outside 0..23; the array returned by content()/equippedContent() is live, not a copy.

See Dungeon Inventory & Presets for the slot layout and the ownership rules that make equippedContent() the array to read.

dungeons.keys item supplier

The bridge registers DungeonKeysStackSupplier under the ItemStackSupplier namespace dungeons.keys, so any AstralCore config that accepts an item can produce a working, PDC-stamped dungeon key (dungeons.keys-<blueprint id>), and any lookup can identify one. See Keys.

// Or programmatically, through the plugin's own service: AstralDungeons plugin = JavaPlugin.getPlugin(AstralDungeons.class); ItemStack key = plugin.blueprints() .findById("crypt_of_ashes") .map(plugin.blueprints()::buildKey) .orElse(null); Optional<DungeonBlueprint> which = plugin.blueprints().findByKey(someStack);

Registered actions

All bridge-side. Full descriptions in Dungeon Inventory & Presets.

Id

Arguments

set-inventory-slot

<slot> [item]

sync-inventory-slot

<slot> [item]

take-held-item

[amount]

create-preset

<name>

delete-preset

<preset>

rename-preset

<preset> <name>

set-preset-slot

<preset> <slot> [item]

sync-preset-slot

<preset> <slot> [item]

toggle-preset

<preset>

Registered requirements

Id

Arguments

Passes when

can-equip-item

<slot> <item>

The worn grid holds no other item of the same AstralItems blueprint.

can-equip-preset-item

<preset> <slot> <item>

The named preset's grid holds no other item of the same blueprint.

See Equip conflicts.

Messaging channels

Both are RPC exchanges on the shared MessagingService, with packets registered by DungeonPacketRegistry (registered by both modules, so the packet types decode on either side).

Constant

Channel

Request → Response

DungeonConstants.SERVERS_MESSAGING_CHANNEL

dungeons.servers

CreateDungeonRequestPacket{playerId, partyId, blueprintId} → CreateDungeonResponsePacket{serverId, success}

DungeonConstants.REQUIREMENTS_MESSAGING_CHANNEL

dungeons.requirements

CheckRequirementsRequestPacket{playerId, blueprintId} → CheckRequirementsResponsePacket{present}

The bridge is the requester on both; the dungeon servers answer dungeons.servers and other bridge/lobby servers answer dungeons.requirements for players they hold. A responder that cannot handle a request (server full, blueprint unknown, player not here) returns null and simply drops out rather than replying with a failure. See Cross-server creation handshake and Checking requirements across servers.

Cache keys

Constant

Redis key

Holds

DungeonConstants.SERVERS_CACHE_KEY

dungeons:servers

DungeonServer{uniqueId, name, instances, maxInstances, availableBlueprints}, one per dungeon server, 1-minute TTL, rewritten every 30s.

DungeonConstants.REWARDS_CACHE_KEY

dungeons:players_data

DungeonPlayerData per player uuid — an unclaimed run. Written on quit from the dungeon server, read and deleted by the bridge on the next PlayerDataLoadedEvent.

Unused types

DungeonQueue (common, model/queue) and QueueRepository exist in the source but are not referenced by either module — no queueing is wired up. Entry is immediate or refused; there is no waiting list. Don't build against them.

Last modified: 25 September 2026