Astral Realms Documentation Help

Developer API

AstralClasses exposes its services through the ClassAPI static façade and the plugin instance. Integration typically falls into three categories: reading class/ability data, accessing player progression state via AstralSync, or building custom plugins around the ClassService and SkillService.

Maven coordinates

<dependency> <groupId>com.astralrealms</groupId> <artifactId>classes</artifactId> <version>2.0-SNAPSHOT</version> <scope>provided</scope> </dependency>

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

ClassAPI

Static lookups for reading a player's current class:

import com.astralrealms.classes.ClassAPI; AstralClass playerClass = ClassAPI.selectedClass(player);

Method

Returns

Use

selectedClass(Player player)

AstralClass

Resolves the player's currently selected class. Falls back to the default-class if no selection is found or if player data is unavailable.

ClassAPI is bootstrapped once from AstralClasses#onEnable — do not call init() from your plugin.

The AstralClass model

Represents a loaded class definition from the YAML configuration. Provides both static metadata and helper methods for computing player-specific ability data.

@ConfigSerializable @Getter public class AstralClass implements ComplexPlaceholder { String id; ComponentWrapper name; ItemStackWrapper weaponItem; ItemStackWrapper weaponDisplay; PlaceholderWrapper<Integer> experienceToLevelUp; Map<String, List<SkillEntry>> subClassesAbilities; Map<String, PaperActionList> subClassesActions; // Helper methods public List<Object> getSkillData(Player player, InputType input); public List<Object> getSkillData(Player player, String abilityId, ClassPlayerData data, InputType input); public Optional<SkillEntry> findSkillEntry(String subClassId, String abilityId); public SkillEntry findSkillEntry(String abilityId); }

Component

Type

Purpose

id()

String

The class's registry key — used in config files and as a reference everywhere.

name()

ComponentWrapper

MiniMessage-formatted display name of the class. Render via ComponentWrapper#get(PlaceholderContainer) in a placeholder context.

weaponItem()

ItemStackWrapper

The weapon/item associated with this class. Resolve it for display via the placeholder wrapper.

weaponDisplay()

ItemStackWrapper

Display variant of the weapon item (may differ from the actual weapon used in gameplay).

experienceToLevelUp()

PlaceholderWrapper<Integer>

Experience required to advance one level. Resolve it per-player via PlaceholderWrapper#get(PlaceholderContainer).

subClassesAbilities()

Map<String, List<SkillEntry>>

Map of subclass id → list of abilities available in that subclass. The key "default" always exists.

subClassesActions()

Map<String, PaperActionList>

Actions to run when a subclass is equipped (keyed by subclass id).

SkillEntry

A nested class representing a single ability within a subclass:

@ConfigSerializable @Getter public static class SkillEntry implements ComplexPlaceholder { String id; ComponentWrapper name; ComponentWrapper info; String skillId; // The underlying skill id (from AstralSkill) List<Integer> unlockableLevels; // Levels at which this ability unlocks (e.g. [1, 10, 20]) Integer unlockableBoostLevel; // Level at which boost variant unlocks PlaceholderWrapper<Long> cooldown; // Cooldown in milliseconds, player-dependent ComponentWrapper displayDamage; InputType defaultInput; // Default input binding (e.g. LEFT_CLICK) boolean forceInputBinding; // If true, input binding cannot be changed }

Helper methods

getSkillData(Player player, InputType input)List<Object>

Computes the active skill data for a player, given an input type. Returns a list of 5 elements (or fewer if no ability is bound):

// Returns: [skillId, cooldownMillis, classLevel, skillLevel, isBoosted] List<Object> data = astralClass.getSkillData(player, InputType.LEFT_CLICK); if (data.size() == 5) { String skillId = (String) data.get(0); long cooldown = (Long) data.get(1); int classLevel = (Integer) data.get(2); int skillLevel = (Integer) data.get(3); boolean boosted = (Boolean) data.get(4); }

If no ability is bound to that input, returns List.of("", 0L).

findSkillEntry(String subClassId, String abilityId)Optional<SkillEntry>

Looks up an ability within a specific subclass only. Returns empty if the subclass or ability does not exist:

Optional<SkillEntry> entry = astralClass.findSkillEntry("default", "fireball"); entry.ifPresent(skill -> { System.out.println("Ability: " + skill.name()); System.out.println("Underlying skill: " + skill.skillId()); });

findSkillEntry(String abilityId)SkillEntry

Searches all subclasses for an ability by id. Returns the first match or null if not found anywhere in the class.

Reading player progression via AstralSync

Per-player class state is stored and synced through AstralSync. Query it via SyncAPI:

import com.astralrealms.classes.storage.ClassPlayerData; import com.astralrealms.sync.SyncAPI; SyncAPI.findData(player.getUniqueId(), ClassPlayerData.class) .ifPresent(data -> { // Read player's selected class and progression String selectedClassId = data.selectedClassId(); List<String> unlockedClasses = data.unlockedClassIds(); Map<InputType, String> inputBindings = data.inputBindings(); // Class-specific progress ClassProgressData progress = data.classProgressDataMap().get(selectedClassId); if (progress != null) { int level = progress.level(); int experience = progress.experience(); boolean isSubClassActive = progress.asSelectedSubClass(); String activeSubClass = progress.selectedSubClassId(); Map<String, Integer> skillLevels = progress.skillLevels(); // ability id -> level Map<String, Boolean> boostedSkills = progress.boostedSkills(); // ability id -> boosted } // Settings boolean toggleAutoAttack = data.classSettings().toggleAutoAttack(); });

ClassPlayerData

A mutable Lombok class (fluent @Getter/@Setter, not a Java record) with a reset() method that clears progress back to the default class:

Field

Type

Purpose

selectedClassId()

String

The class currently active for this player.

unlockedClassIds()

List<String>

All classes the player has unlocked.

classProgressDataMap()

Map<String, ClassProgressData>

Progress per class (keyed by class id).

inputBindings()

Map<InputType, String>

Custom input → ability id bindings.

classSettings()

ClassSettings

Player-specific toggles (e.g. toggleAutoAttack).

ClassProgressData record

Nested inside the map above — one entry per class the player has interacted with:

Field

Type

Purpose

level()

int

Current class level.

experience()

int

Current experience toward next level.

asSelectedSubClass()

boolean

If true, player is actively using a subclass other than "default".

selectedSubClassId()

String

The active subclass id (only meaningful if asSelectedSubClass() == true).

skillLevels()

Map<String, Integer>

Per-ability level (ability id → level). Unlocked abilities have a level >= 1.

boostedSkills()

Map<String, Boolean>

Which abilities are currently in boost state.

AstralSync adapter

The plugin registers a SnapshotAdapter with AstralSync to persist class state across servers:

public class ClassSnapshotAdapter implements SnapshotAdapter<ClassPlayerData> { @Override public Key key() { return Key.key("astralclasses", "player_data"); } @Override public ClassPlayerData create(Player player) { // Seed default class at configured starting level return new ClassPlayerData( plugin.configuration().defaultClass(), plugin.configuration().startingLevel() ); } }

Method

Behavior

key()

Returns Key.key("astralclasses", "player_data") — the sync adapter's identity.

create()

Seeds a new player snapshot with the default-class from config and the starting-level. No plugin code should manually call this — it is part of AstralSync's initialization contract.

update()

Pass-through (no modification during sync).

apply()

Currently a no-op. Handlers that need to react to synced class changes should use AstralClass placeholders or query via ClassService directly.

serialize() / deserialize()

Read/write all fields (selectedClassId, unlockedClassIds, classProgressDataMap, inputBindings, classSettings) to the binary protocol.

Note: Treat this adapter as a persistence contract, not for manual registration. It is registered once by AstralClasses itself at startup.

ItemStack supplier (weapon items)

The plugin registers a stack supplier under the id classes.weapon, allowing other plugins to resolve class weapon items via the standard item supplier API:

import com.astralrealms.core.paper.AstralPaperAPI; import net.kyori.adventure.key.Key; // Get an itemstack by class id ItemStack weapon = AstralPaperAPI.itemStack("classes.weapon", classId, 1); // Resolve a held weapon back to its class AstralClass classe = plugin.classes().findByWeaponItem(player.getInventory().getItemInMainHand());

The supplier is keyed as Key.key("classes.weapon", classId). Use it in any context that accepts an itemstack key (e.g., [give-item] action in menus).

Plugin instance and services

Access the plugin and its services:

import com.astralrealms.classes.AstralClasses; AstralClasses plugin = AstralClasses.getInstance(); // Class lookups ClassService classes = plugin.classes(); classes.findById(classId); // Optional<AstralClass> classes.findByPlayer(player); // AstralClass (falls back to default) classes.findByWeaponItem(itemStack); // Optional<AstralClass> classes.ids(); // Set<String> of all class ids classes.all(); // Collection<AstralClass> of all loaded classes classes.selectedSubClass(player); // String — the player's active subclass id

SkillService

Only available when skills are enabled for the server group (see configuration.skill-enable-groups):

SkillService skills = plugin.skills(); // May be null if skills are not enabled if (skills != null) { // Trigger a skill skills.tryTriggerSkill(player, InputType.LEFT_CLICK, false); // Cooldown manager CooldownManager cooldowns = skills.cooldownManager(); boolean canUse = cooldowns.canUse(player, "fireball"); long remaining = cooldowns.getRemainingCooldown(player, "fireball"); // Trigger manager (internal rate-limiting) SkillTriggerManager triggers = skills.triggerManager(); boolean canTriggerThisTick = triggers.canTrigger(player); }

Important: plugin.skills() returns null if skills are disabled. Always null-check before use.

Registered helpers

The plugin registers two helpers with AstralCore for command/menu integration:

Completion: inputType

Tab completion for InputType enum values in ACF commands:

@CommandCompletion("inputType") public void bindAbility(CommandSender sender, InputType input) { // input is one of: SPACE, SNEAK, LEFT_CLICK, RIGHT_CLICK, DROP, SWAP_HANDS, HOTBAR_1…8 }

Context resolver: net.kyori.adventure.key.Key

Parses a string argument into a Key for use in commands or menus:

@CommandCompletion("@nothing") public void doSomething(CommandSender sender, Key key) { // key is parsed from the string argument // Used internally for the itemstack supplier key resolution }

Skill casting integration

When a skill ability is triggered, the underlying SkillService.tryTriggerSkill method calls into AstralSkill via:

SkillAPI.service().castAtPos( player, casterUuid, skillId, eyeLocation, direction, placeholderContainer );

For details on writing skill implementations and how classes invoke them, see the AstralSkill developer documentation — AstralClasses is a consumer of the AstralSkill engine and does not define the skill DSL itself.

See also

Last modified: 25 July 2026