Astral Realms Documentation Help

Developer API

AstralEssentials exposes a small integration surface: two actions registered into its own AstralCore action registry, and one AstralSync snapshot adapter for cross-server player time/weather. It defines no custom Bukkit events and no placeholder namespace — RecipeService and SitService are reachable off the plugin instance but exist for the plugin's own commands, not as a supported third-party API.

Maven coordinates

<dependency> <groupId>com.astralrealms</groupId> <artifactId>essentials</artifactId> <version>1.0-SNAPSHOT</version> <scope>provided</scope> </dependency>

Repository: https://maven.astralrealms.fr/repository/maven-public/.

Registered actions

AstralEssentials#onEnable calls the plugin-scoped registerAction (not registerActionGlobally), so both actions are registered into AstralEssentials' own PlatformActionRegistry instance rather than the shared global registry:

this.registerAction("confirm-trash", ConfirmTrashAction.class); this.registerAction("refund-trash", RefundTrashAction.class);

Action resolution (DefaultPaperActionProxy#initialize) looks the name up on plugin.actions(), where plugin is whichever AstralPaperPlugin owns the configuration file being parsed — so [confirm-trash] and [refund-trash] only resolve inside YAML parsed by AstralEssentials itself (its own menus/*.yml, or a PaperActionList field in config.yml such as the portal actions list), not from another plugin's action lists. In practice the only place they're used is the trash-confirmation menu below.

Both actions implement PaperAction directly (a no-arg constructor, no parsed argument), so they take no argument in YAML:

ID

Class

Description

confirm-trash

ConfirmTrashAction

Discards the executor's pending trashed items.

refund-trash

RefundTrashAction

Returns the executor's pending trashed items to their inventory.

confirm-trash

- "[confirm-trash]"

Calls TrashListener.clearPendingRefunds(executor.getUniqueId()), dropping the executor's entry from the pending-refunds map without giving the items back — the items are permanently discarded.

refund-trash

- "[refund-trash]"

Calls TrashListener.refundPlayer(executor), removing the executor's entry from the pending-refunds map and adding each stashed ItemStack back to their inventory via Inventory#addItem.

Trash bin flow

TrashListener is what wires the two actions above together with the /trash GUI. It keeps a static Map<UUID, ItemStack[]> pendingRefunds and listens for InventoryCloseEvent:

  1. On close, it checks the closed inventory's holder is a TrashInventory (the /trash GUI's holder class) and collects every non-empty ItemStack from getStorageContents(). If nothing was left in the bin, the listener returns and nothing further happens.

  2. Otherwise the collected items are stored in pendingRefunds keyed by the player's UUID.

  3. The listener then calls plugin.menus().computeAndOpen(player, "trash-confirmation"). If that future completes exceptionally (for example, no trash-confirmation menu blueprint exists under the plugin's menus/ data folder) or resolves to null, refundPlayer(player) is called immediately as a fallback — items are never silently lost even if the confirmation menu is missing or misconfigured.

  4. Inside the trash-confirmation menu, an item bound to [confirm-trash] discards the stash; an item bound to [refund-trash] returns it. Both are static-map lookups keyed by the acting player, so either action works regardless of which menu instance or session triggers it, as long as it runs for the same player.

clearPendingRefunds(UUID) and refundPlayer(Player) are also public static methods on TrashListener itself — the two actions are thin wrappers that just supply the executor from the PaperActionContext.

AstralSync adapter

PlayerTimeSnapshotAdapter registers PlayerTimeData (per-player time/weather override) with AstralSync under the key astralessentials:player_time:

SyncAPI.registerAdapter(new PlayerTimeSnapshotAdapter());

Method

Behavior

key()

astralessentials:player_time.

create(Player)

A fresh PlayerTimeData(-1, PlayerWeatherType.NONE) for a player with no prior snapshot — -1 and NONE are the "no override" sentinels.

deserialize(DataHolder, BinaryMessage)

Reads a long time then a PlayerWeatherType enum off the wire.

serialize(DataHolder, PlayerTimeData, BinaryMessage)

Writes time() then weather() back in the same order.

update(Player, PlayerTimeData)

No-op — returns the data unchanged.

apply(Player, PlayerTimeData)

Delegates to PlayerTimeData#apply(Player), described below.

PlayerTimeData#apply(Player) is permission-gated and sentinel-gated:

public void apply(Player player) { if (player.hasPermission("essentials.ptime") && this.time >= 0) player.setPlayerTime(this.time, false); if (player.hasPermission("essentials.pweather") && this.weather() != PlayerWeatherType.NONE) player.setPlayerWeather(this.weather.bukkitWeatherType()); }

A player without essentials.ptime (respectively essentials.pweather) never has their client-side time (respectively weather) overridden on load, even if a non-sentinel value is stored in their synced snapshot.

Other plugins can read or mutate the same snapshot directly through SyncAPI, the same way PlayerTimeCommand/PlayerWeatherCommand do internally:

import com.astralrealms.essentials.storage.PlayerTimeData; import com.astralrealms.sync.SyncAPI; SyncAPI.findData(player.getUniqueId(), PlayerTimeData.class) .ifPresent(data -> { long time = data.time(); // -1 == no override PlayerWeatherType weather = data.weather(); // NONE == no override data.time(6000); // mutate directly; picked up on next apply() });

PlayerWeatherType

PlayerWeatherType is the enum PlayerTimeData stores its weather override as, mapping onto Bukkit's WeatherType:

Constant

bukkitWeatherType()

Meaning

CLEAR

WeatherType.CLEAR

Force clear weather for the player.

DOWNFALL

WeatherType.DOWNFALL

Force rain/storm for the player.

NONE

WeatherType.CLEAR

No override — the sentinel apply() checks against; the mapped WeatherType.CLEAR is unused since apply() never reaches bukkitWeatherType() for NONE.

PlayerWeatherType.fromBukkit(@Nullable WeatherType) is the reverse lookup used by PlayerWeatherCommand when writing a snapshot from a vanilla WeatherType: it returns NONE for a null input or for any WeatherType that doesn't match CLEAR/DOWNFALL (which, given the two mapped constants above, is unreachable for real Bukkit WeatherType values).

Internal services

SitService is exposed via a Lombok-generated getter on the AstralEssentials plugin instance (plugin.sit()), but it isn't registered with AstralPaperAPI or documented as a stable extension point — treat it as reference, not a supported API surface for other plugins.

SitService

Method

Returns

Use

register(UUID playerId, ArmorStand armorStand)

void

Track an armor stand as the seat backing a sitting player.

unregister(UUID playerId)

void

Remove and delete the tracked armor stand for a player.

findByPlayer(UUID playerId)

Optional<ArmorStand>

The armor stand a player is currently sitting on, if any.

findByLocation(Location location)

Optional<ArmorStand>

The armor stand registered at a block location, if any.

A repeating task (every 100 ticks) sweeps every tracked armor stand and calls unregister on it (which removes the entity and clears both maps) in two cases: it has no passengers (the player stood up or disconnected), or it's already dead / no longer valid (removed some other way, e.g. by another plugin).

Registering your own actions

AstralEssentials' actions use the same AstralPaperPlugin#registerAction mechanism any plugin can call — see Custom Actions for the full PaperAction implementation and registration walkthrough.

Last modified: 25 July 2026