Astral Realms Documentation Help

Overview

AstralTower adds an instanced, party-based "dimensional tower" arena to the AstralRealms network. A party requests entry from a lobby-side portal or command; a dedicated tower server generates a fresh instanced world of procedurally sequenced rooms and pulls the party in. Inside the instance the party fights through rooms room-by-room, opening doors for artifacts, coins, a shop, or a boss, and — once they return to the lobby — any accumulated rewards are handed to them there.

Two-module architecture

The plugin ships as two separate builds of largely-parallel com.astralrealms.tower.AstralTower/com.astralrealms.tower.bridge.AstralTowerBridge main classes, each targeting a different tier of the network:

Module

plugin.yml name

Runs on

Depends on

Role

Bridge

AstralTower-Bridge

Lobby / proxy-facing servers

AstralCore, AstralSync, AstralParty, PlaceholderAPI, LuckPerms

Owns /tower (lobby), the entry portal, daily ticket issuance and spending, the instance-creation handshake, and the reward carry-over that pays a party's loot out once they reconnect to the lobby. Disables itself automatically if AstralTower (the paper module) is also enabled on the same server, to prevent conflicts.

Paper

AstralTower

Dedicated tower servers

AstralCore, AstralSync, AstralClasses, AstralMobs, AstralParty, FastAsyncWorldEdit, packetevents, PacketFramework, CraftEngine, PlaceholderAPI

Owns tower generation, gameplay (room sequencing, mob spawning/scaling, artifacts, coins, shop, doors), and per-instance reward bookkeeping while a party is inside the tower.

Both modules load config.yml and messages.yml through AstralPaperPlugin#loadConfiguration, and both register a tower placeholder namespace and messaging exchanges over the shared TowerPacketRegistry (RabbitMQ) and CacheService (Redis). See Configuration for both modules' keys and Placeholders for the two tower_* namespaces.

Creating a tower instance

Player enters the portal cuboid (checks access-permission and the paused queue first), or runs /tower create (skips those two checks) (bridge, on the lobby) │ ▼ Party + capacity checks (bridge InstanceService#createInstance) │ ├─ player must be in an AstralParty party, and be its owner │ ├─ party size (members + 1) must be ≤ maximum-party-size │ ├─ party must not already have an instance │ └─ the owner AND every member must have ≥ 1 daily ticket ▼ AstralPaperAPI.servers().findEmptiestByGroup(target-server-group) │ picks the least-loaded server in the configured group (default "tower") ▼ InstanceCreateRequestPacket sent over the "tower.instances" exchange to that server ▼ Paper module: InstancePacketListener → InstanceService#create(playerId, partyId) │ ├─ resolves the party via PartyAPI; fails with PARTY_NOT_FOUND if it can't │ ├─ generates a weighted room sequence from rooms.yml (see below) │ ├─ creates and pastes the world, then a RoomInstance per pasted room │ └─ replies InstanceCreateResponsePacket{ SUCCESS | PARTY_NOT_FOUND | SERVER_FULL | UNEXPECTED_ERROR } ▼ Bridge: on SUCCESS, TeleportationService sends the requesting player and every party member to that tower server.

See Commands for /tower create's exact failure messages and Tower Instances for room generation, phases, and doors.

Inside the tower

Once teleported in, the party spawns in the lobby room (RoomType.LOBBY, always position 0 per rooms.yml) and moves through the generated sequence:

  1. Fight mobs — a room's mobs.yml set spawns over time and scales in health per room depth and per participant. Killing the last mob transitions the room to REWARDS. See Tower Instances: mob scaling.

  2. Collect the room's reward — depending on which door the party took to reach it, a room offers artifacts, coins, a shop, or (on the boss room) nothing extra: see Loot, Shop & Rewards.

  3. Walk through a door to advance — each cleared room opens doors tagged with a reward type for the next room, so the party picks which reward they want before moving on. See Tower Instances: doors.

  4. Reach the last room and claim the end-of-run chest, or die/disconnect/time out — any of these ends the instance. See Tower Instances: instance lifecycle.

Player state inside the instance (session coins, mobs killed) lives only in memory for the duration of the instance — PlayerDataService loads it on PlayerJoinTowerInstanceEvent and drops it on PlayerQuitTowerInstanceEvent. See Placeholders for %tower_coins%.

Cosmetic hand slot

Rather than giving players a real starter item, the paper module's InventoryPacketListener intercepts inventory/held-item packets and substitutes the inventory slots configured in config.yml — the player's actual inventory is untouched; what they see (and the item they appear to be holding at hand-slot) is purely a packet-level illusion. See Configuration.

Reward carry-over

Rewards claimed inside the tower (room-reward chests, the end-of-run chest) are not granted directly; they are cached per-player (RewardService, a 14-day-TTL Redis-backed CachedRewards record of an action list and the room index they were earned at) and claimed back on the lobby:

  1. Reaching RoomPhase.END_REWARDS (or interacting with a room-reward chest) queues a CachedRewards entry and marks the player as already rewarded for that room.

  2. Back on the lobby, once AstralSync fires PlayerDataLoadedEvent for the player, the bridge's RewardsListener reads every cached reward for that player, runs each entry's PaperAction list, fires a TowerFinishedEvent(player, room) per reward, deletes the cache entry, and deducts one daily ticket per reward whose room index is ≥ 1.

See Loot, Shop & Rewards for the full sequence and the TowerFinishedEvent shape.

Daily tickets

Entering the tower costs a ticket per party member. Tickets are AstralSync player data (TowerPlayerData, snapshot key tower:player_data) refilled once per day (Europe/Paris calendar day) on PlayerDataLoadedEvent, to daily-tickets.default plus a bonus per matching daily-tickets.permissions entry the player holds. A LuckPerms NodeAddEvent listener also grants the configured bonus immediately when a group.* permission node is added to a player. See Commands: /tower tickets and Configuration.

Feature map

Area

Page

Room generation, phases, doors, mob scaling, custom events

Tower Instances

Artifacts, coins, shop, room/end rewards, reward carry-over

Loot, Shop & Rewards

config.yml/messages.yml keys for both modules

Configuration

/tower command trees, permissions

Commands

Placeholders exposed by both modules

Placeholders

Requirements

Dependency

Required by

Notes

AstralCore

Both modules

Configuration, messaging, cache, placeholder framework, AstralPaperPlugin base.

AstralSync

Both modules

TowerPlayerData snapshot adapter (bridge); PlayerDataLoadedEvent triggers ticket refill and reward carry-over (bridge).

AstralParty

Both modules

A party is required to create an instance (PartyAPI.findByPlayer/findById). Bridge also depends on it at the plugin.yml level.

AstralClasses, AstralMobs

Paper

Room mob/boss spawning (MobsAPI.spawnMob).

PlaceholderAPI

Both modules

Declared as a hard plugin.yml dependency by both modules; backs the tower placeholder namespace.

PacketEvents, PacketFramework

Paper

Display entities (artifacts, coins, holograms) and the cosmetic hand-slot packet illusion.

CraftEngine

Paper

Custom item/pack integration.

FastAsyncWorldEdit

Paper

Pastes room schematics when generating a tower world.

LuckPerms

Bridge

NodeAddEvent listener grants bonus daily tickets when a group.* node is added.

RabbitMQ

Both modules

Backs MessagingService — the instance-creation and queue-status exchanges.

Redis

Both modules

Backs CacheService — instance state, the reward cache, and the paused-queue flag.

Last modified: 25 July 2026