Astral Realms Documentation Help

Overview

AstralStats is the RPG stat and resource framework for the AstralRealms network. Its main class, AstralStats, extends AstralPaperPlugin and depends on AstralCore and PlaceholderAPI (plugin.yml). It gives every player two building blocks that other plugins can read and drive through StatsAPI: a stat system (attack damage, armor, critical chance, …) and a resource system (health, mana, stamina, shield, charge).

What it does

  • Stats are a fixed catalog of 34 StatType keys under the astralstats namespace (e.g. astralstats:attack_damage, astralstats:critical_chance). A stat only exists for a player if it has a matching blueprint — a YAML file under blueprints/ giving it a display name and a base/min/max range; BlueprintService walks that folder recursively at load time.

  • Resources are a fixed ResourceType enum — HEALTH, MANA, STAMINA, SHIELD, CHARGE — each configured per-type in resources.yml (regeneration source stat, max-value stat, off-combat-only flag).

  • Attribute-backed stats take effect on vanilla Attributes via a single astralstats:main AttributeModifier; a handful of other stats are read directly by plugin listeners/resources instead of going through an Attribute at all.

  • Modifiers (FLAT, RELATIVE, PERCENTAGE) stack onto a stat's base value to produce the value the game actually sees. They can be permanent or timed; timed stat modifiers and resource effects survive a player disconnecting and reconnecting.

  • Combat mechanics — critical strikes, percentage damage reduction, and shield absorption — are built on top of those stat/resource values, gated by a combat-state cooldown.

  • Integration — other plugins call StatsAPI directly; anything that resolves PlaceholderAPI can read live values through %stats_...%.

Requirements

Dependency

Required

Notes

Paper (api-version: 1.21)

Yes

AstralCore

Yes

Configuration loading, command registration, placeholder framework

PlaceholderAPI

Yes

Backs the %stats_...% placeholder namespace

Architecture at a glance

plugins/AstralStats/ ├── config.yml ← combat-cooldown, silent-command-success ├── resources.yml ← per-ResourceType configuration ├── messages.yml ← localised strings └── blueprints/ ← one YAML per stat; scanned recursively └── shield/ ← subfolders are just organisation (e.g. max-shield.yml, shield-regeneration.yml)

The repository ships four example blueprints — attack-damage, attack-speed, shield/max-shield, and shield/shield-regeneration — any other StatType a server wants active needs its own blueprint file added to that folder.

Services

Getter

Class

Responsibility

blueprints()

BlueprintService

Loads every .yml/.yaml under blueprints/ (recursively) into a Key → StatBlueprint map.

combat()

CombatService

Tracks per-player last-damage timestamps to answer isInCombat(UUID).

stats()

StatService

Holds each online player's StatContainer, computes stat values, applies/removes modifiers, ticks dirty stats every 20 ticks (1s).

resources()

ResourceService

Holds each online player's ResourceContainer, initializes/reads/writes resource amounts, ticks regeneration every 5 ticks (async).

temporaryEffects()

TemporaryEffectService

Times out StatModifiers and resource deltas; re-applies non-expired ones on reconnect.

Listeners

Listener

Trigger

PlayerListener

PlayerJoinEvent loads stats, resources, and pending temporary effects; PlayerQuitEvent (LOW) unloads them in reverse order.

CriticalDamageListener

EntityDamageByEntityEvent (HIGH) — rolls critical_chance, scales the MAGIC damage modifier by critical_damage.

DamageReductionListener

EntityDamageEvent — scales incoming player damage down by damage_reduction%.

ShieldListener

EntityDamageEvent (HIGHEST, ignoreCancelled) — absorbs damage from the player's SHIELD resource before it reaches health.

CombatListener

EntityDamageEvent (HIGH) records a damage timestamp for combat state; PlayerQuitEvent clears it.

How stats take effect

Most StatType keys are backed by a vanilla Attribute. StatRegistry statically maps 26 of the 34 keys (e.g. ATTACK_DAMAGEAttribute.ATTACK_DAMAGE, ARMORAttribute.ARMOR, MAX_HEALTHAttribute.MAX_HEALTH) to an AttributeStatHandler(Attribute, playerBaseValue). On every update, the handler removes any AttributeModifier it previously applied under the key astralstats:main and — if the computed value differs from the handler's own playerBaseValue by more than a small epsilon — adds one fresh ADD_NUMBER modifier for the difference. Exactly one modifier is ever present per attribute; recomputing a stat replaces it rather than stacking another one.

Five keys are not attribute-backed and are instead read directly by plugin code wherever they're needed: critical_chance and critical_damage by CriticalDamageListener, damage_reduction by DamageReductionListener, and max_shield/shield_regeneration by the SHIELD resource's blueprint (max-stat/regeneration-stat, consumed in PlayerResource.regeneration). Any StatType (or arbitrary custom key) whose blueprint has no attribute mapping and isn't read by a listener falls back to a no-op DummyStatHandler — currently health_regeneration, movement_speed, and speed_malus_reduction are declared in the catalog but not wired to anything.

See Stats & Modifiers for the full key list and blueprint format.

Modifier model

A stat's live value is computed from its base value (the blueprint's base, or an overridden base set via /stats set or StatsAPI) plus every StatModifier currently attached to it, combined in this order:

value = (base + Σ FLAT) × (1 + Σ RELATIVE / 100) × Π (1 + PERCENTAGE / 100)

...then clamped to [min, max] if the blueprint defines bounds (min < max). Modifiers are added/removed by key (stats().addModifier(...)/removeModifier(...)), or added as timed effects through TemporaryEffectService — a timed StatModifier or resource delta expires automatically after a duration in milliseconds. If the player is offline when it expires, or the server restarts, the effect is simply gone (no stuck modifiers, no persistence across a crash); if the player is offline and reconnects before expiry, the modifier/resource effect is re-applied on PlayerJoinEvent and continues counting down.

See Stats & Modifiers and Developer API for the programmatic surface.

Combat mechanics

  • Critical strikes — on EntityDamageByEntityEvent, a direct player attacker rolls against critical_chance (0–100); on success, the event's MAGIC damage modifier is set to originalDamage × (critical_damage / 100).

  • Damage reduction — every EntityDamageEvent against a player is scaled down by 1 - (damage_reduction / 100) once damage_reduction > 0.

  • Shield absorption — the SHIELD resource absorbs damage before it reaches health: fully if the shield amount covers the hit, otherwise it's drained to 0 and the remainder passes through; breaking the shield plays ITEM_SHIELD_BREAK.

  • Combat stateCombatService marks a player "in combat" for combat-cooldown (config.yml, default 10s) after they take any damage; resources whose blueprint sets off-combat-only: true (the shipped default for SHIELD) stop regenerating while the player is in combat.

See Combat Mechanics for the full mechanics and formulas.

Where to go next

Page

Covers

Configuration

config.yml, messages.yml

Stats & Modifiers

Full StatType catalog, blueprint format, modifier types

Resources

ResourceType, resources.yml, regeneration formulas

Combat Mechanics

Critical strikes, damage reduction, shields, combat state

Commands

/stats and its subcommands

Placeholders

%stats_...%

Developer API

StatsAPI static accessors

Last modified: 25 July 2026