Astral Realms Documentation Help

Overview

AstralDungeons adds instanced, party-based procedural dungeons to the AstralRealms network. A party walks into a portal on the lobby; the request is broadcast to the dedicated dungeon servers, one of which generates a fresh instanced world for that blueprint and pulls the party in. Inside the instance the party explores procedurally arranged rooms, fights mobs and a boss for experience and loot, and — once they return to the lobby — the loot and experience they earned are handed to them there.

Players fight with a dungeon inventory: a separate, network-synced 24-slot gear grid edited from a menu on the lobby, optionally organised into named presets. Their normal survival inventory is never carried into a dungeon.

Two-module architecture

The plugin ships as two separate builds of the same com.astralrealms.dungeons.AstralDungeons main class, each targeting a different tier of the network:

Module

plugin.yml name

Runs on

Depends on

Role

Bridge

AstralDungeons-Bridge

Lobby / hub servers

AstralCore, AstralParty, AstralSync, AstralItems (soft: AstralMailbox)

Owns the portals and dungeon keys, the dungeon-inventory and preset UI, /donjons, tracks which dungeon servers exist and what they can host, brokers instance creation over RPC, and pays out the rewards a party collected once a player reconnects to the lobby.

Paper

AstralDungeons

Dedicated dungeon servers

AstralCore, AstralMobs, AstralSync

Owns dungeon generation, gameplay (room population, mob/boss spawning, decorated-pot loot, boss guidance), the in-dungeon ruleset, and per-player reward bookkeeping while a party is inside an instance.

Both modules are load: POSTWORLD and target api-version: 26.1.2. The bridge only arms its portal listener and portal-particle task when the server's group matches portal-group, so the same build can be deployed network-wide and stays inert outside the hub.

Entering a dungeon

The normal path is the portal — a Cuboid region declared on a bridge-side blueprint. Walking into it runs, in order (see Portals & Keys):

Player steps into a portal cuboid (bridge, PortalListener) │ ├─ no dungeon server with a free slot for this blueprint → NO_AVAILABLE_SERVER, bumped back out ├─ not in a party → NO_PARTY, bumped back out ├─ not the party leader → NOT_PARTY_LEADER, bumped back out ├─ party size outside [minimum-players, maximum-players] → INVALID_PARTY_SIZE, bumped back out ▼ RequirementsService#checkForRequirements(every member, blueprint) │ each member must hold the blueprint's key AND pass its `requirements` list; members on │ another server are asked over the "dungeons.requirements" RPC exchange │ → FAILED_REQUIREMENTS ▼ ServerService#createInstance(player, party, blueprintId) │ RPC request over the "dungeons.servers" exchange (fanout — every subscribed │ dungeon server receives it): CreateDungeonRequestPacket{playerId, partyId, blueprintId} ▼ Every dungeon server: DungeonCreationListener │ ├─ ignores the request (no reply) if it is already at max-instances │ ├─ ignores the request (no reply) if it doesn't have that blueprint loaded │ ├─ replies success=false if that party already has an instance on it, or the │ │ party can't be resolved via PartyAPI │ └─ otherwise calls DungeonService#create(party, blueprint) — generates the layout, │ creates and paints the world, pastes the rooms — and replies success=true ▼ Bridge: acts on whichever CreateDungeonResponsePacket arrives first (1-minute RPC timeout); on success, TeleportationService sends the requesting player and every other party member to that response's serverId, and an "dungeons_instance_created" analytics event is logged.

/donjons create <blueprint> (permission dungeons.create) is the staff/testing shortcut for the same createInstance call — it only checks that the player is in a party, skipping the portal's key, requirement and party-size gates.

Because the create request fans out to every eligible dungeon server and each one races to reply, capacity is balanced implicitly: only servers with a free instance slot and the blueprint attempt generation at all, and the first one to finish wins. ServerService#findEmptiest(String) is used by the portal as an up-front availability check, not to pick the winner.

Server discovery (dungeons:servers)

Each dungeon server heartbeats its own state into the shared dungeons:servers Redis cache every 30 seconds (starting 1 second after enable), writing a DungeonServer record: its unique ID, name, current instance count, max-instances, and the set of blueprint IDs it has loaded. The bridge keeps a Caffeine cache of this data (refreshed every minute, 1‑minute repository TTL) and reads it through ServerService#findEmptiest and #availableBlueprints() — the union of blueprint IDs offered by every server that still has a free instance slot.

Inside the dungeon

Once the party is teleported in, the dungeon server takes over. On join, EquipmentListener clears the player's inventory outright and re-dresses them from their synced dungeon inventory (or the preset they have equipped), applies the inventory.items default hotbar from config.yml, strips potion effects, heals them to full and revokes flight. DungeonService#registerPlayer then drops each player at the blueprint's spawn room component and PathGuidanceService starts their private guide stream to the boss.

From there the gameplay loop is:

  1. Explore the procedurally generated layout — rooms are pasted from schematics and stitched together from the blueprint's start/end/normal/cap room lists. See Dungeon & Room Blueprints.

  2. Fight mobs that spawn as participants walk within a room's detection/activation range and can see the spawn spot (or stand within 25 blocks of it); killing one awards the configured experience for that mob id to every alive participant, and its AstralMobs drops become reward items. See Loot & Rewards.

  3. Smash decorated pots scattered through the instance for a chance at loot from the blueprint's pots loot table; each pot holds three rewards before it breaks.

  4. Reach and defeat the boss, spawned once a participant comes within activation range of the blueprint's boss location — killing it ends the instance with cause WON and marks every alive participant completed (an instance also ends early, with cause NO_ALIVE_PARTICIPANTS, if every participant dies or disconnects). See Dungeon Instances for the full lifecycle and state machine.

The dungeon world is a locked-down sandbox: block breaking/placing, item dropping, off-hand swapping, inventory clicks, opening containers, hunger, and every block interaction except right-clicking a decorated pot are cancelled outright; chat is scoped to the instance; join/quit and death messages are suppressed.

Instances are looked up and keyed three ways at once — by party ID, by world, and by player ID — so gameplay listeners (mob kills, pot interactions, player movement) can resolve "which dungeon is this?" from whichever context they fire in.

Reward carry-over

Rewards are not granted directly in the dungeon; they accumulate on a per-player DungeonPlayerData (experience, mobs killed, duration, completion flag, and a list of queued command strings) and are claimed back on the lobby:

  1. On join, if the player's party has an active instance, the dungeon server adds a fresh DungeonPlayerData for that player, stamped with the blueprint id and the current time.

  2. Killing a mob adds its configured experience to every alive participant and increments the killer's killedMobs. Picking up a loot item that carries commands (an AstralMobs drop or a pot reward) cancels the pickup and adds those commands to every alive participant's queued commands, with a toast and a level-up sound each.

  3. On quit (including the forced kick after an instance ends), the dungeon server stamps the run's duration, flushes the player's data to the dungeons:players_data:<uuid> Redis key, and drops it from its own cache.

  4. Back on the lobby, once AstralSync fires PlayerDataLoadedEvent for the player, the bridge's PlayerDataService#claimRewards reads that key on the main thread, consumes one of the blueprint's keys from their inventory, fires DungeonCompletedEvent so other plugins can adjust the payout, runs the configured reward-actions, dispatches the queued commands from the console, deletes the Redis entry, logs a dungeon_completed analytics event and updates the three configured leaderboards.

Feature map

Area

Page

Portals, dungeon keys, entry requirements

Portals & Keys

Dungeon inventory, presets, the menu/dialog UI

Dungeon Inventory & Presets

Dungeon & room blueprints, generation

Dungeon & Room Blueprints

Pot loot tables, mob drops, reward carry-over

Loot & Rewards

Instance lifecycle, state machine, timeouts

Dungeon Instances

config.yml keys for both modules

Configuration

Command tree, permissions

Commands

Placeholders exposed by the plugin

Placeholders

Events, actions, requirements, the key supplier

Developer API

Requirements

Dependency

Required by

Notes

AstralCore

Both modules

Configuration, messaging, cache, menus/dialogs, placeholder framework, AstralPaperPlugin base.

AstralParty

Bridge

A party is required to enter a dungeon (PartyAPI.findByPlayer), and the instance is keyed by party id.

AstralSync

Both modules

Stores the dungeon inventory as a synced snapshot (dungeons:inventory), and fires PlayerDataLoadedEvent — the trigger for both reward claiming (bridge) and re-dressing the player (paper).

AstralItems

Bridge

Resolves gear to ItemInstances for the equip-conflict rules and the %dungeons_inventory_stat_*% totals.

AstralMailbox

Bridge (soft)

Overflow target when preset gear is handed back and the player's inventory is full. Preset equip/delete fails if the service is unavailable.

AstralMobs

Paper

Spawns room/boss mobs, resolves a killed entity back to its MobBlueprint id, and supplies the drops that become dungeon rewards.

AstralRoomEditor

Paper

Provides the RoomBlueprint model and type serializers for the rooms/ folder.

Pathetic (de.bsommerfeld.pathetic)

Paper

A* pathfinding behind the per-player boss guidance streams.

RabbitMQ

Both modules

Backs MessagingService — the dungeons.servers and dungeons.requirements RPC exchanges.

Redis

Both modules

Backs CacheService — the dungeons:servers server-advertisement cache and dungeons:players_data reward cache.

Last modified: 25 September 2026