Astral Realms Documentation Help

World & Server Management

Alongside its player-facing commands, AstralEssentials runs a set of always-on listeners and repeating tasks that shape how a server group behaves: mob and trading controls, entity/spawner caps, spawn protection, custom portals, enchantment caps, beehive regeneration, ender dragon handling, world rules, and command gating. Every key below lives in config.yml; see Configuration for the full file reference and Installation for which of these are set up once at onEnable() versus re-applied by /essentials reload.

Mob & Trading Controls

Key

Type

Default

Description

disabled-mob-types

Set<EntityType>

[VILLAGER, WANDERING_TRADER]

Entity types blocked network-wide.

disabled-mob-spawning-groups

Map<String, Set<EntityType>>

build: [PHANTOM]

Additional entity types blocked only on the matching server group.

disable-trading

boolean

true

Disables villager/wandering-trader/piglin trading entirely.

disable-phantoms-until

String (permission)

"group.serfs"

Permission node a player must hold for phantoms to spawn for them.

wither-skip-invulnerability-phase-group

Map<String, Boolean>

(not shipped)

Per-group toggle to skip a built wither's invulnerability phase.

disabled-mob-types: - VILLAGER - WANDERING_TRADER disabled-mob-spawning-groups: build: - PHANTOM disable-trading: true disable-phantoms-until: "group.serfs"

MainConfiguration#shouldNotSpawn(EntityType) is the shared check: an entity type is blocked if it's in the global disabled-mob-types set, or in the current server group's entry in disabled-mob-spawning-groups. MobsListener uses it in four places:

  • EntitySpawnEvent — cancels the spawn outright.

  • PlayerChunkLoadEvent — removes any already-present entity of a blocked type from a chunk as it's sent to a player.

  • PhantomPreSpawnEvent — cancels the phantom's spawn outright if PHANTOM is disabled (checked before, and independently of, the disable-phantoms-until permission gate below).

  • The armor-stand placement guard (below) also short-circuits if ARMOR_STAND itself is blocked.

disable-trading cancels four separate events: PiglinBarterEvent, a piglin picking up a dropped item (EntityPickupItemEvent), PlayerTradeEvent (the villager/wandering-trader trade GUI), and PlayerInteractEntityEvent against a VILLAGER, WANDERING_TRADER, or PIGLIN — the last of these stops the trade GUI from ever opening on right-click.

disable-phantoms-until is checked on PhantomPreSpawnEvent: if the spawn's target is a player, the spawn is cancelled unless that player holds the configured permission — phantoms stay disabled for a player until they reach that permission node. This runs independently of, and in addition to, the PHANTOM type check from shouldNotSpawn.

wither-skip-invulnerability-phase-group is not present in the shipped default config.yml — add it to opt in. It's keyed by server group and checked only for a wither summoned by a player (CreatureSpawnEvent.SpawnReason.BUILD_WITHER): when the active group's entry is true, the new wither has setInvulnerableTicks(0) and setInvulnerable(false) applied immediately, so it's damageable from the instant it spawns instead of after its vanilla invulnerability phase.

Entity Limits

entity-limits caps how many of a given EntityType may exist in a single chunk, per server group:

entity-limits: resource: armor_stand: 20 item_frame: 30 painting: 20

MainConfiguration#entityLimitsForGroup() resolves entity-limits.<active group>, falling back to an empty map (no limits) if the active group has no entry. A limit of 0 or less is treated as disabled. The resolved map is enforced at four points:

  • MobsListener#onEntitySpawn (EntitySpawnEvent) — cancels a spawn if the type already has ≥ limit entities in that chunk.

  • MobsListener#onArmorStandPlace (PlayerInteractEvent, right-clicking an armor stand item) — a dedicated check that blocks placement once the chunk's armor stand count reaches the limit (and blocks it outright if ARMOR_STAND is a disabled mob type, before the count is even checked).

  • ChunkListener#onChunkLoad (ChunkLoadEvent) — trims any type over its limit back down to the limit the moment a chunk loads.

  • EntityLimitTask — a repeating task (Bukkit.getScheduler().runTaskTimer(this, new EntityLimitTask(this), 1800L, 1800L), a 90s initial delay then every 90s) that walks every loaded chunk in every loaded world and applies the same trim, catching entities that accumulated some other way (e.g. natural breeding, dispensers).

Spawner Limits

Key

Type

Default

Description

spawners-limits.enabled

boolean

true

Whether the spawner-placement listener is registered at all.

spawners-limits.limit

int

12

Max spawners per chunk.

disabled-spawners-groups

Set<String>

[resource, spawn]

Server groups where placing a spawner is blocked outright.

spawners-limits: enabled: true limit: 12 disabled-spawners-groups: - resource - spawn

SpawnerListener is only registered in onEnable() if spawners-limits.enabled is true — when disabled, neither check below runs, spawner placement is unrestricted, and disabled-spawners-groups has no effect.

  • Per-chunk cap — on BlockPlaceEvent for a SPAWNER block, the listener counts CreatureSpawner tile entities already in the chunk (the just-placed one included, since the block is already set when the event fires). If that count exceeds spawners-limits.limit, the placement is cancelled and the player is sent the spawners-limit-reached message.

  • Group ban — a second handler, registered at EventPriority.LOWEST, cancels any spawner placement outright when the active server group is in disabled-spawners-groups, independent of the per-chunk cap, sending the spawnwers-disabled-in-world message (shipped key spelling).

Spawn Protection

Key

Type

Default

Description

spawn-protection.radius

int (blocks)

64

Protected radius around each protected world's spawn location.

spawn-protection.enabled-groups

Map<String, Set<String>>

resource: [world, world_nether, world_the_end]

Worlds protected on each server group.

spawn-protection: radius: 64 enabled-groups: resource: - "world" - "world_nether" - "world_the_end"

SpawnProtection#isProtected(world) looks up enabled-groups.<active group> and checks whether it contains the given world name. SpawnProtectionListener applies it to four events:

  • BlockBreakEvent/BlockPlaceEvent — skipped for players with spawnprotection.bypass; otherwise cancelled (with the spawn-protection message) if the block is within radius blocks (distance-squared) of the world's spawn location in a protected world.

  • BlockExplodeEvent/EntityExplodeEvent — cancelled (no message, since there's no player to message) if any block in the explosion's block list falls within radius of spawn in a protected world. These two checks have no permission bypass — they apply regardless of who or what caused the explosion.

Custom Portals

portals.nether and portals.end each replace vanilla portal travel with a config-driven AstralCore action list, gated by an AstralCore requirement list:

portals: nether: enabled: true actions: - "[connect-world] resource world_nether" requirements: requirements: - "[permission] world.nether" deny-actions: - "[message] You do not have permission to enter the Nether." end: enabled: false actions: - "[connect-world] resource world_end" requirements: requirements: - "[permission] world.end" deny-actions: - "[message] You do not have permission to enter The End."

Field

Type

Description

enabled

boolean

Whether entering this portal type does anything. nether defaults true, end defaults false.

actions

PaperActionList

Run once the requirements pass and the dwell delay elapses. See Actions.

requirements

PaperRequirementList

Evaluated once per portal stay; its own deny-actions fire on failure. See Requirements.

PortalListener handles everything: EntityPortalEvent is always cancelled (non-player entities never travel through these portals) and PlayerPortalEvent is always cancelled (vanilla teleport is fully suppressed). Detection instead runs off PlayerMoveEvent block changes:

  1. The player must be standing in a NETHER_PORTAL or END_PORTAL block (feet or head). Re-entering after stepping out restarts the sequence; while a stay is already being handled, further moves are ignored (no restarted timer, no repeated deny-action spam).

  2. If portal.enabled() is false, nothing happens.

  3. requirements.run(...) is evaluated once. On failure it fires its own deny-actions and the sequence stops there.

  4. A teleport is scheduled after the vanilla dwell delay — for nether portals, read live from the player's world PLAYERS_NETHER_PORTAL_DEFAULT_DELAY game rule (vanilla default 80 ticks / 4s), or PLAYERS_NETHER_PORTAL_CREATIVE_DELAY (vanilla default 1 tick) for Creative/Spectator players; end portals teleport instantly (0 ticks).

  5. When the delay elapses, requirements are re-checked (in case something changed during the dwell), then actions.run(...) executes with the player as executor.

The shipped [connect-world] resource world_nether action is ConnectWorldAction, registered globally by AstralHomes (registerActionGlobally("connect-world", ...), not by AstralEssentials itself): its first argument is the target server group (here resource) and its second is the destination world name within that group.

Enchantment Level Caps

max-enchantment-levels: efficiency: 10 unbreaking: 5 fortune: 5 sharpness: 7

max-enchantment-levels is a Map<Enchantment, Integer> of bare enchantment ids to a maximum level. EnchantmentListener hooks AstralItems' custom-anvil AnvilResultEvent (fired by the /anvil GUI, not the vanilla anvil block). If the map is empty the listener no-ops. Otherwise, for every capped enchantment: if either input book's stored level already exceeds the cap, or the level the combine would produce (equal input levels → +1, otherwise the higher of the two) would exceed the cap, the entire anvil result is cancelled — the combine is blocked outright rather than clamped to the cap.

Beehive Regeneration

beehive-regeneration-times: resource: worlds: - "world_nether" - "world_the_end" min: "20s" max: "1m" build: worlds: - "world" min: "10s" max: "30s"

beehive-regeneration-times is keyed by server group; each entry lists the worlds it applies to and a min/max Duration range (DurationParser shorthand, e.g. 20s, 1m). At onEnable(), one tick after startup, AstralEssentials looks up the active group's entry and starts one BeeHiveTask per already loaded world whose name is in that entry's worlds set (new BeeHiveTask(beehiveConfig, world).runTaskTimer(this, 100L, 5L) — 5s initial delay, then every 5 ticks / 0.25s). Worlds loaded after that point are not picked up until a restart.

Each run of BeeHiveTask processes up to 5 of the world's loaded chunks (round-robin across ticks). For every BEE_NEST tile entity not already at max honey level, it stores a next-regeneration timestamp in the block's PersistentDataContainer (key astralessentials:beehive_regeneration); once that time is reached, the honey level is incremented by one and a new delay is rolled uniformly between min and max (inclusive) for the next increment.

Ender Dragon

Key

Type

Default

Description

ender-dragon-drops

Set<ItemStackWrapper>

[minecraft:stone]

Items granted to the dragon's killer.

disable-dragon-egg

boolean

(not shipped)

Cancels dragon egg formation when true.

ender-dragon-drops: - "minecraft:stone"

EnderDragonListener#onEnderDragonDeath (EntityDeathEvent for ENDER_DRAGON with a non-null killer) adds every configured ItemStackWrapper in ender-dragon-drops directly to the killer's inventory; anything that doesn't fit is added to the event's normal ground drops instead of being lost. The shipped default (a single minecraft:stone) is a placeholder — replace it with real loot.

disable-dragon-egg is not present in the shipped default config.yml; add it explicitly to enable. When true, DragonEggListener cancels Paper's DragonEggFormEvent, preventing the egg from forming after a (re)kill.

Separately and unconditionally (not config-gated), EnderDragonPacketListener is registered against PacketEvents in onEnable() — see PacketEvents integration — and cancels the outgoing Play.Server.EFFECT packet with effect id 1028 (the vanilla ender-dragon-death effect) at MONITOR priority, suppressing the vanilla death animation/sound broadcast.

World Rules

keep-inventory and disable-locator-bar are two independent, top-level config.yml booleans (both default true) that shape the whole server's world behavior; see Configuration for the key table.

  • disable-locator-bar is the only one of the two that is a genuine world game rule. AstralEssentials#onEnable() schedules a one-tick-delayed task (Bukkit.getScheduler().runTask(this, () -> Bukkit.getWorlds().forEach(...))) that, when the flag is true, calls world.setGameRule(GameRules.LOCATOR_BAR, false) on every world that is already loaded at that point. Worlds that load afterward, and /essentials reload, do not re-apply it.

  • keep-inventory is not a game rule at all — it's an event-level override. PlayerListener#onPlayerDeath checks it on every PlayerDeathEvent and, when true, calls e.setKeepInventory(true) and clears e.getDrops(). The same handler also always suppresses the death message and schedules an instant respawn back to the spawn group one tick later.

Command Restrictions

blocked-commands, unregistered-commands, and groups-blocked-commands are documented in full on Configuration; this is how CommandListener enforces them:

  • isCommandBlocked(cmd)true if the command label starts with, or ends with : followed by, any blocked-commands entry (case-insensitive). Enforced on PlayerCommandPreprocessEvent (cancels the command, showing vanilla's "unknown command" message), TabCompleteEvent (strips matches from completions), and PlayerCommandSendEvent (strips matches — and any command containing : — from the client's known-command list).

  • isCommandBlockedInGroup(cmd)true if the active server group has a groups-blocked-commands entry and the label matches one of its commands (same starts-with/ends-with rule). Enforced on PlayerCommandPreprocessEvent, sending command-disabled-in-world and cancelling the command.

  • Both checks are skipped entirely for players holding astralessentials.bypass.commandblock.

  • unregistered-commands is unrelated to CommandListener: at load time (onEnable() and /essentials reload), each name is removed from the server's CommandMap directly, so it isn't a runnable command at all (not just hidden).

Server-Group Scoping

Most of the systems above key off AstralPaperAPI.serverInformation().group() and only take effect on server groups that appear in their config section — installing the plugin does not by itself activate them everywhere:

  • entity-limits (via entityLimitsForGroup())

  • disabled-mob-spawning-groups

  • wither-skip-invulnerability-phase-group

  • disabled-spawners-groups

  • spawn-protection.enabled-groups (via SpawnProtection#isProtected)

  • beehive-regeneration-times

  • groups-blocked-commands

disable-phantoms-until is the exception among the group-related keys — it's checked against a permission node, not the active server group. See Installation for how this interacts with plugin startup (some of these, like beehive regeneration, are only resolved once against the worlds loaded at enable time).

Last modified: 25 July 2026