Classes
A class is a single YAML file in plugins/AstralClasses/classes/ (sub-folders are scanned recursively) that defines a playable class: its display name, weapon, progression curve, abilities, and subclass-specific actions (stat modifiers and effects applied on join).
Each file is deserialized directly into an AstralClass object, keyed by its id.
Minimal example
Top-level fields
Field | Type | Required | Description |
|---|---|---|---|
| String | Yes | Class identifier. Used as the lookup key in commands, menus, and player data. |
| MiniMessage | Yes | Display name of the class, shown in menus and selection UIs. |
| ItemStackWrapper | Yes | The equippable weapon item — matched back to a class via |
| ItemStackWrapper | Yes | Cosmetic item shown in the class selection menu (decoupled from the actual weapon). |
| int or PlaceholderWrapper | Yes | XP required to reach each level. Supports class-level/skill-level/boosted context. |
|
| Yes | Map of subclass id → list of abilities for that subclass. Every class must have a |
|
| No | Map of subclass id → actions run on player join for that subclass (stat modifiers, effects, etc.). |
Weapon item and display
weapon-item is the actual ItemStack a player carries when they select this class. It is matched back to the class via ClassService.findByWeaponItem(ItemStack) — internally, this comparison uses ItemStackUtils.areSimilar(), which requires the same material and then falls back to a full ItemMeta equality check (persistent data, custom model data, enchantments, name, lore, and any other meta fields all have to match).
weapon-display is a separate cosmetic item shown only in the class selection menu. This decoupling allows the menu to show a distinct visual representation without forcing players to hold an identical item.
Both fields use the standard ItemStackWrapper from AstralCore: material, item-model, name, lore, enchantments, custom-model-data, durability, glow, etc. See Menu Items for the full schema.
Experience and progression
experience-to-level-up is the XP required to progress to each successive level. It is stored as a PlaceholderWrapper<Integer>, which allows the value to be a literal integer or a placeholder expression that resolves with context:
When a player gains experience, the placeholder is resolved with context including class-level, skill-level, and boosted flags.
Subclasses and abilities
Subclasses are alternate ability sets within a single class. Every class ships a default subclass, and players fall back to default unless they explicitly select another one (stored in ClassProgressData.selectedSubClassId).
sub-classes-abilities is a map of subclass id → list of SkillEntry objects. Each entry defines one ability a player can unlock and use:
Field | Type | Required | Description |
|---|---|---|---|
| String | Yes | Ability identifier (e.g. |
| MiniMessage | Yes | Display name shown in menus (e.g. "Slash"). |
| MiniMessage | No | Description or flavor text shown in ability tooltips. |
| MiniMessage | No | Cosmetic damage range or formula shown in menus (e.g. "50 / 75" or "50 + 10 per level"). Display-only; actual damage comes from the skill. |
| String | Yes | ID of the underlying |
| List\<int\> | Yes | Class levels at which this ability unlocks (e.g. |
| int | Yes | Class level at which a boosted version of this ability becomes available. |
| long (ms) or PlaceholderWrapper | Yes | Cooldown in milliseconds between casts. Supports context: |
| InputType | No | The input binding that triggers this ability by default (e.g. |
| boolean | No | If |
Ability example
Cooldown context: When a player triggers an ability, the cooldown is resolved with a PlaceholderContainer populated with:
class-level: the player's current class levelskill-level: the player's level in that specific ability (fromClassProgressData.skillLevels)boosted: whether the ability is currently in boosted state (fromClassProgressData.boostedSkills)
This allows cooldowns to scale with progression:
Subclass actions
sub-classes-actions is a map of subclass id → PaperActionList executed when a player joins (or logs in with that subclass active). Actions are registered in PlayerListener#onPlayerJoin() and run immediately after the player's data loads.
Typical use: applying stat modifiers (via AstralStats) and status effects:
Actions use AstralCore's action DSL — see Actions for the full catalogue. Every action receives access to player placeholders (%player_name%, etc.).
Full example: Knight class
Loading and resolution
Class files are loaded by ClassService.load(), which:
Recursively scans
plugins/AstralClasses/classes/for.yml/.yamlfiles (via AstralCore'sConfigurationManager.loadFolder()).Deserializes each file as an
AstralClass.Registers each class by its
idin an internal map.
No skill-id validation at load time. skill-id is a plain string field — AstralClasses does not load or index AstralSkill's skill files itself, so it cannot verify at class-load time that a referenced skill actually exists. An ability whose skill-id doesn't resolve to a real AstralSkill skill will simply fail to cast (or no-op) the first time a player triggers it.
Per-file error isolation: loadFolder() catches deserialization exceptions per file and logs Failed to load configuration file: <path> without aborting the rest of the scan — so a malformed class file (missing required fields, invalid YAML, etc.) does not prevent other classes from loading.
Hot reload
/class reload (requires class.command.reload permission) re-reads every class file from disk and rebuilds the class registry, alongside the double-jump config, menus, and the weapon item supplier. Player data is preserved.
Integration: menus and placeholders
The classes selection menu (see Configuration) renders all classes and their abilities. It queries the class registry via ClassService.all() and exposes ability lore by chaining through the classes/class/skill-entry placeholder namespaces (e.g. %classes_class_raw% bound to a variable, then %variables_selected_skill-entry_attack-1_name%). See Placeholders for the complete placeholder API.
When a player equips a class, the equip-class action updates their ClassPlayerData.selectedClassId (after confirming the class is in unlockedClassIds) — it does not itself give the weapon-item to their inventory. The classes.weapon itemstack supplier (registered via WeaponClassItemSupplier) makes each class's weapon resolvable by class id for menus and [give-item]-style actions to hand out explicitly. See Actions for the full equip-class behavior.
Cross-product: skills
Each ability references an underlying AstralSkill skill (by skill-id). The skill DSL defines the actual behavior — damage, projectiles, effects, etc. — and is documented separately at Skills (AstralSkill). Classes do not define skill behavior; they only consume skills by id and wrap them with unlock levels, cooldowns, and input bindings.
The display-damage, name, and info fields are cosmetic labels for menus; they do not affect the skill's runtime behavior.
See also
Configuration — main class configuration file and menu setup.
Installation — plugin setup and dependencies.
Placeholders — all available player/class/ability placeholders for menus.
Abilities & Input — input types and how ability triggers work.
Commands — class-related commands (reload, toggle, reset, modifier).
Developer API — programmatic class access and player progression.