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 |
| Runs on | Depends on | Role |
|---|---|---|---|---|
Bridge |
| Lobby / hub servers | AstralCore, AstralParty, AstralSync, AstralItems (soft: AstralMailbox) | Owns the portals and dungeon keys, the dungeon-inventory and preset UI, |
Paper |
| 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):
/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:
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.
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.
Smash decorated pots scattered through the instance for a chance at loot from the blueprint's
potsloot table; each pot holds three rewards before it breaks.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
WONand marks every alive participantcompleted(an instance also ends early, with causeNO_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:
On join, if the player's party has an active instance, the dungeon server adds a fresh
DungeonPlayerDatafor that player, stamped with the blueprint id and the current time.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 queuedcommands, with a toast and a level-up sound each.On quit (including the forced kick after an instance ends), the dungeon server stamps the run's
duration, flushes the player's data to thedungeons:players_data:<uuid>Redis key, and drops it from its own cache.Back on the lobby, once AstralSync fires
PlayerDataLoadedEventfor the player, the bridge'sPlayerDataService#claimRewardsreads that key on the main thread, consumes one of the blueprint's keys from their inventory, firesDungeonCompletedEventso other plugins can adjust the payout, runs the configuredreward-actions, dispatches the queued commands from the console, deletes the Redis entry, logs adungeon_completedanalytics event and updates the three configured leaderboards.
Feature map
Area | Page |
|---|---|
Portals, dungeon keys, entry requirements | |
Dungeon inventory, presets, the menu/dialog UI | |
Dungeon & room blueprints, generation | |
Pot loot tables, mob drops, reward carry-over | |
Instance lifecycle, state machine, timeouts | |
| |
Command tree, permissions | |
Placeholders exposed by the plugin | |
Events, actions, requirements, the key supplier |
Requirements
Dependency | Required by | Notes |
|---|---|---|
AstralCore | Both modules | Configuration, messaging, cache, menus/dialogs, placeholder framework, |
AstralParty | Bridge | A party is required to enter a dungeon ( |
AstralSync | Both modules | Stores the dungeon inventory as a synced snapshot ( |
AstralItems | Bridge | Resolves gear to |
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 |
AstralRoomEditor | Paper | Provides the |
Pathetic ( | Paper | A* pathfinding behind the per-player boss guidance streams. |
RabbitMQ | Both modules | Backs |
Redis | Both modules | Backs |