Developer API
AstralSkyblock exposes its services through the AstralSkyblock.get() singleton, fires eight custom Bukkit events around island/world/member/co-op lifecycle, and registers eleven actions plus three ACF completions and two context resolvers that other menus, dialogs and commands can reuse. Cross-server coherency (island load routing, membership sync, cache invalidation) flows over RabbitMQ through ASPacketRegistry.
Maven coordinates
Repository: https://maven.astralrealms.fr/repository/maven-public/.
Accessing the plugin
AstralSkyblock is @Getter-annotated (Lombok), so every field constructed in onEnable is exposed as a same-named getter off the singleton:
Getter | Type | Use |
|---|---|---|
|
| Island CRUD, cross-server spawn resolution, settings flush, cache warmup. |
|
| Membership CRUD (add/kick/leave/promote/demote/transfer), cross-server member sync. |
|
| Co-op grant CRUD, cross-server sync. |
|
| Invitation lifecycle (create/accept/decline/cancel), periodic pruning of expired invites. |
|
| Role CRUD and permission-flag updates. |
|
| Loaded |
|
| Loaded |
|
| Loaded |
|
| ASP |
|
| Emptiest-island-server lookup, per-island host-server mapping. |
|
| Records/loads last-seen player rows. |
AstralSkyblock also exposes configuration(), rolesConfiguration(), blockValuesConfiguration(), database(), cache(), messaging(), menus() (AstralCore MenuContainer) and dialogs() (AstralCore DialogContainer) through the same Lombok getters — see Overview for the full service table.
Custom Bukkit events
AstralSkyblock fires eight custom events, none of them Cancellable — every one is a post-fact notification fired after its underlying database write has already succeeded. They live under event/island, event/island/world, event/member and event/coop.
Island lifecycle
Both events in this group compute their own async flag from the calling thread at construction time (super(!Bukkit.isPrimaryThread())), because they're fired from inside a CompletableFuture callback chain that isn't guaranteed to land back on the main thread. They are the only two events in this catalogue that behave this way — check Event#isAsynchronous() (or simply write your handler to be thread-safe) before touching Bukkit API from a listener.
IslandCreateEvent
Field | Type | Description |
|---|---|---|
|
| The player who created the island. |
|
| The newly created island. |
|
| The Bukkit world the island's |
Fired by IslandService#create once the island row (with seeded roles/owner) is persisted and its world has finished loading — after the player has already been teleported and the ISLAND_CREATED message sent.
IslandDeletedEvent
Field | Type | Description |
|---|---|---|
|
| The player who requested the deletion. |
|
| The deleted island (its row is already gone from the database). |
Fired by IslandService#delete after the island row is removed, before the world-delete cleanup and the ISLAND_DELETED message. No permission or ownership check happens before this fires — see Commands for the caller-side gap.
World lifecycle
Both events use Lombok's default (synchronous) Event() constructor and are always dispatched with .callEvent() from inside a Bukkit.getScheduler().runTask(plugin, ...) block in WorldService, so listeners can always touch Bukkit API directly.
IslandWorldLoadedEvent
Field | Type | Description |
|---|---|---|
|
| The island whose world was loaded. |
|
| The now-loaded Bukkit world. |
Fired by WorldService#loadWorld right after the world is registered in loadedWorlds, its environment settings applied (IslandSettingsListener#applyEnvironment), and its host-server mapping set — but before any player is necessarily present.
IslandWorldUnloadedEvent
Field | Type | Description |
|---|---|---|
|
| The island whose world was unloaded. |
|
| The Bukkit world that was just unloaded. |
Fired by WorldService#unload after Bukkit.unloadWorld succeeds and the host-server mapping is cleared. Only fires if the island can still be resolved from the world-service cache — a warning is logged instead if it can't.
Membership & co-op
All four events in this group also use the synchronous Event() constructor, but the services that fire them run inside asynchronous repository callbacks, so each call site explicitly hops back to the main thread first: Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getPluginManager().callEvent(...)).
IslandMemberJoinEvent
Field | Type | Description |
|---|---|---|
|
| The island the player joined. |
|
| The joining player. |
|
| The player who sent the accepted invitation. |
Fired by MemberService#addMember after the membership row is persisted, and again locally on every other server by MemberService's MEMBER_SYNC_CHANNEL packet handler when it receives the MemberJoinPacket broadcast from the originating server — so a listener registered on any server sees every join, not just the one that happened locally.
IslandMemberLeaveEvent
Field | Type | Description |
|---|---|---|
|
| The island the player left. |
|
| The departing player. |
|
|
|
Fired by MemberService#kick (Reason.KICKED) and MemberService#leave (Reason.VOLUNTARY), and relayed network-wide the same way as IslandMemberJoinEvent via MemberLeavePacket. Reason.BANNED is defined on the enum but no code path in this build currently fires it — ban enforcement isn't wired into MemberService yet (there is no BanRepository), even though IslandBan exists as a model and island-bans/island-confirm-ban menus ship in resources/menus/.
IslandCoopAddEvent
Field | Type | Description |
|---|---|---|
|
| The island granting co-op access. |
|
| The player granted co-op. |
|
| The player who sent the accepted co-op invitation. |
Fired by CoopService#add after the IslandCoop row is persisted and the local island snapshot is updated. CoopAddPacket is also broadcast on COOP_SYNC_CHANNEL so other servers update their own cache/snapshot, but — unlike the member-join/leave events below — the event itself is not re-fired on those other servers; CoopService's packet handler explicitly skips it (its Javadoc: "Does NOT fire an event (already fired on the originating server)"). Listen for it only where you expect the originating action to happen, not network-wide.
IslandCoopRemoveEvent
Field | Type | Description |
|---|---|---|
|
| The island revoking co-op access. |
|
| The player whose co-op grant was removed. |
Fired by CoopService#remove. Same cache-only cross-server relay as IslandCoopAddEvent — CoopRemovePacket syncs state on other servers but does not re-fire this event there.
Key service methods
The full method surface is longer than what's listed here — these are the entry points other plugins are most likely to call.
IslandService
Method | Returns | Behavior |
|---|---|---|
|
| Validates the name is free, inserts the island (with seeded roles + owner) and its world in one flow, teleports the player, and fires |
|
| Deletes the island row, then the world (best-effort), then fires |
|
| Resolves where to teleport a visitor: checks the live host-server mapping, falls back to the emptiest island server, loads the world locally or requests a remote load, and times out after 15 seconds. |
|
| Every island currently in the local L1 cache — not a full database scan. |
|
| Re-cascades a cached island's members/roles/co-ops after a membership or role write. |
|
| Rebuilds a cached island's upgrade-level snapshot after an upgrade purchase. |
|
| Flushes the island's dirty |
|
| Pages every island into the L1 cache on startup; failures are logged, not fatal. |
MemberService
Method | Returns | Behavior |
|---|---|---|
|
| Synchronous cached lookup — the island the player is a member of, if any. This is the resolver behind the bare-argument form of |
|
| Adds the player under the island's default role. No validation — callers ( |
|
| Requires |
|
| Moves the target one step up the non-owner role ladder (roles sorted ascending by |
|
| Moves the target one step down the ladder. Requires |
|
| Only the current owner may call this. Demotes the ex-owner to the island's highest-weight |
WorldService
Method | Returns | Behavior |
|---|---|---|
|
| Loads the island's |
|
| Clones the blueprint's source world for the island, loads it, and saves it. Tears down whatever was created if any step fails. |
|
| Saves the currently-loaded |
|
| Unloads the world from this server (optionally without saving — used by the delete path so a deleted island's world can't be re-persisted). Clears the host-server mapping and fires |
|
| Resolves the island that owns a loaded Bukkit world, by world name. |
|
| The loaded |
Registered actions
AstralSkyblock registers eleven actions with registerAction (its own local action registry — see Actions for the general [id] args syntax). They resolve inside any actions:/close-actions:/view-requirements:-adjacent action list parsed by AstralSkyblock's own menus/dialogs, and any other plugin can trigger them programmatically through the same registry. Five of them (invite-member, coop-player, promote-member, demote-member, transfer-ownership) aren't referenced by any menu shipped in resources/menus/ in this build — they exist for custom menus/dialogs (or other plugins) that want a menu-click equivalent of the matching /is subcommand.
Every action's first argument is a PlaceholderWrapper<Island> — in shipped menus this resolves from either a direct Island placeholder (e.g. %parameters_island%) or an id string that's looked up (e.g. %parameters_island_id%); both forms are used interchangeably in the examples below because both appear in the shipped resource files.
toggle-role-permission
Toggles one IslandPermission on a role. Requires IslandPermission#SET_PERMISSION and Island#canEditRole (the caller must outrank the role being edited, or be the owner/skyblock.admin). Plays a pickup-orb sound on enable, a button-click sound on disable.
Arg | Type | Description |
|---|---|---|
|
| The island the role belongs to. |
|
| The role being edited. |
|
| The permission to toggle. |
update-role-permissions
Flushes a role's pending permission toggles (accumulated by toggle-role-permission, held in IslandRole#dirtyPermissions until this fires) to the database. Same permission/rank checks as toggle-role-permission. Typically wired as a menu's close-actions: so edits persist when the player exits.
Arg | Type | Description |
|---|---|---|
|
| The island the role belongs to. |
|
| The role whose dirty permissions should be persisted. |
toggle-island-setting
Toggles one IslandSettings flag. Requires IslandPermission#SET_SETTINGS. Time-lock and weather-lock settings are mutually exclusive within their group (see IslandSettings#conflictingSettings) — enabling one silently clears the conflicting one. Immediately re-applies world environment state if the island is hosted on this server, since time/weather locks are world state rather than cancellable events.
Arg | Type | Description |
|---|---|---|
|
| The island being edited. |
|
| The setting to toggle. |
update-island-settings
Flushes an island's pending setting toggles to the database via IslandService#updateSettings. Same permission check and environment re-apply as toggle-island-setting.
Arg | Type | Description |
|---|---|---|
|
| The island whose dirty settings should be persisted. |
invite-member
Sends a member invitation. Requires IslandPermission#INVITE_MEMBER; silently returns (no message) if the caller lacks the island entirely. Delegates to InvitationService#create with InvitationType.MEMBER.
Arg | Type | Description |
|---|---|---|
|
| The inviting island. |
|
| The player to invite. |
No shipped menu wires this action — build the second argument from whatever placeholder in your menu/dialog resolves to a MinecraftPlayer (e.g. a name-entry dialog result).
kick-member
Kicks a member — see MemberService#kick for the outrank rules. From resources/menus/confirm/confirm-kick.yml:
Arg | Type | Description |
|---|---|---|
|
| The island to kick from. |
|
| The member to kick. |
promote-member
Promotes a member one step up the role ladder — see MemberService#promote.
Arg | Type | Description |
|---|---|---|
|
| The island the target belongs to. |
|
| The member to promote. |
No shipped menu wires this action — see kick-member above for the shipped pattern a menu would follow to supply a member's playerId.
demote-member
Demotes a member one step down the role ladder — see MemberService#demote.
Arg | Type | Description |
|---|---|---|
|
| The island the target belongs to. |
|
| The member to demote. |
No shipped menu wires this action either — same shape as promote-member.
transfer-ownership
Transfers ownership to an existing member — see MemberService#transfer. Only the current owner may invoke this.
Arg | Type | Description |
|---|---|---|
|
| The island to transfer. |
|
| The member becoming the new owner. |
No shipped menu wires this action — the second argument needs a placeholder resolving to a full IslandMember (not just a playerId), e.g. a member picked from an island-members-style layout.
coop-player
Sends a co-op invitation. Requires IslandPermission#COOP_MEMBER. Delegates to InvitationService#create with InvitationType.COOP.
Arg | Type | Description |
|---|---|---|
|
| The inviting island. |
|
| The player to invite as co-op. |
No shipped menu wires this action — same argument shape as invite-member.
uncoop-player
Removes a co-op grant. Requires IslandPermission#COOP_MEMBER — note this checks COOP_MEMBER, not a separate UNCOOP_MEMBER node, despite /is uncoop (the command) gating on IslandPermission#UNCOOP_MEMBER. From resources/menus/coops.yml:
Arg | Type | Description |
|---|---|---|
|
| The island to remove the co-op from. |
|
| The co-op player to remove. |
Command extension points
AstralSkyblock registers its ACF completions and context resolvers on its own PaperCommandManager (AstralPaperPlugin#commands()), so any command class registered by this plugin — including one added by a dependent plugin sharing the same manager — can reference them via @CommandCompletion/@Values.
Registered as | Handler | Behavior |
|---|---|---|
|
| Every loaded blueprint id ( |
|
| Every island's name on this server ( |
|
| Names of the caller's island's members, excluding the caller; empty if the caller has no island or isn't a |
Type | Resolver | Behavior |
|---|---|---|
|
| Pops the first argument, resolves via |
|
| Pops the first argument. If absent and the parameter is |
See Commands for how SkyblockCommand/MemberCommand /etc. use these resolvers in practice.
Cross-server messaging
ASPacketRegistry (registered on MessagingService in onEnable) is the RabbitMQ packet catalog for everything that has to stay consistent across servers. Understanding it is useful for tracing how island state propagates, even though most plugins should prefer the service methods and events above over sending these packets directly.
ID range | Packet | Fields | Purpose |
|---|---|---|---|
|
|
| Sent by |
|
|
| Reply to a load request. |
|
|
| Broadcast by |
|
|
| Broadcast by |
|
|
| Broadcast by |
|
|
| Broadcast by |
|
|
| Broadcast by |
|
|
| Generic UUID-keyed cache sync, used by |
|
|
| Same, Long-keyed — used by |
|
|
| Same, composite-keyed — used by |
|
|
| Same, island+string-keyed — used by |
|
|
| Registered in the catalog but not currently sent by any repository — |
Each *SyncedRepository registers its own exchange handler on construction (plugin.messaging().registerExchange(exchangeChannel, ...)), keyed by a channel constant from ASConstants (e.g. ISLAND_MANAGEMENT_CHANNEL, MEMBER_SYNC_CHANNEL, COOP_SYNC_CHANNEL, and one *_UPDATE_CHANNEL per repository) — these are the "three-tier cache" (local Caffeine → Redis → MySQL) invalidation paths described on SyncedRepository.
Model types worth knowing
Island
@Entity("islands"), implements ComplexPlaceholder (namespace island). Persisted fields: uniqueId, name, locked, level, spawn pose (spawnX/Y/Z/spawnYaw/spawnPitch), updatedAt/createdAt. Transient (hydrated relationships, not persisted): owner (IslandMember), members(), roles(), coops(), settings (EnumSet<IslandSettings>), upgrades() (Map<UpgradeType, Integer>).
Method | Behavior |
|---|---|
| Owner and |
| Owner/ |
|
|
| Stage a setting toggle (respecting mutual-exclusion groups) / commit staged toggles and clear the dirty map. |
| The island's current level for that upgrade track, |
IslandRole
@Entity("island_roles"), implements ComplexPlaceholder (namespace role). Fields: id (Long), islandId, kind (IslandRole.Type: MEMBER/VISITOR/COOP), name, weight (int, seniority — higher outranks lower), isDefault, createdAt. Transient: permissions (EnumSet<IslandPermission>).
Method | Behavior |
|---|---|
|
|
| Stage a permission toggle / commit staged toggles. |
IslandUpgrade/UpgradeType
IslandUpgrade is a @ConfigSerializable record (type, levels: Map<Integer, Level>) loaded from upgrades/*.yml, implementing ComplexPlaceholder (namespace upgrade). Each nested Level record carries level, price, currency, currencyDisplay, value, and unlockActions (a PaperActionList run once when that level is purchased).
UpgradeType is a plain enum with nine tracks: WORLDBORDER_SIZE, MEMBERS_LIMIT, COOP_LIMIT, GENERATOR, HOPPERS_LIMIT, CROP_GROWTH_SPEED, SPAWNERS_RATE, MOB_DROPS, MINECART_LIMITS.
IslandSettings
Enum of 18 togglable environment flags (ALWAYS_DAY, PVP, CREEPER_EXPLOSION, TNT_EXPLOSION, …), implementing ConfigurationEnum<ItemStackWrapper> (each value carries a menu icon loaded from settings.yml). ALWAYS_DAY/ALWAYS_MIDDLE_DAY/ALWAYS_NIGHT/ALWAYS_MIDDLE_NIGHT form a mutually exclusive time-lock group; ALWAYS_RAIN/ALWAYS_SHINY form a mutually exclusive weather-lock group (see conflictingSettings()).
IslandPermission
Enum of 52 in-island permission nodes plus the ALL wildcard, also ConfigurationEnum<ItemStackWrapper> (icons from permissions.yml). Checked via Island#hasPermission/IslandRole#hasPermission — these are not Bukkit permission nodes; see Roles, Permissions & Settings for the full catalogue and how roles.yml seeds them onto default roles.
Placeholders
Every model above doubles as a ComplexPlaceholder, so %island_*%, %role_*%, %member_*%, %upgrade_*% and the menu-scoped %setting_*%/%permission_*% chains resolve without extra wiring wherever an Island/IslandRole/IslandMember/IslandUpgrade is in scope. SkyblockPlaceholders additionally exposes %skyblock_player_island_*% — a chain point that resolves MemberService#findPlayerIsland for the placeholder's Player context, then continues into Island's own namespace (e.g. %skyblock_player_island_hasPermission_KICK_MEMBER%, used throughout the shipped menus as a view-requirements gate). See Placeholders for the full reference.