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
StatTypekeys under theastralstatsnamespace (e.g.astralstats:attack_damage,astralstats:critical_chance). A stat only exists for a player if it has a matching blueprint — a YAML file underblueprints/giving it a display name and abase/min/maxrange;BlueprintServicewalks that folder recursively at load time.Resources are a fixed
ResourceTypeenum —HEALTH,MANA,STAMINA,SHIELD,CHARGE— each configured per-type inresources.yml(regeneration source stat, max-value stat, off-combat-only flag).Attribute-backed stats take effect on vanilla
Attributes via a singleastralstats:mainAttributeModifier; a handful of other stats are read directly by plugin listeners/resources instead of going through anAttributeat 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
StatsAPIdirectly; anything that resolves PlaceholderAPI can read live values through%stats_...%.
Requirements
Dependency | Required | Notes |
|---|---|---|
Paper ( | Yes | — |
AstralCore | Yes | Configuration loading, command registration, placeholder framework |
PlaceholderAPI | Yes | Backs the |
Architecture at a glance
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 |
|---|---|---|
|
| Loads every |
|
| Tracks per-player last-damage timestamps to answer |
|
| Holds each online player's |
|
| Holds each online player's |
|
| Times out |
Listeners
Listener | Trigger |
|---|---|
|
|
|
|
|
|
|
|
|
|
How stats take effect
Most StatType keys are backed by a vanilla Attribute. StatRegistry statically maps 26 of the 34 keys (e.g. ATTACK_DAMAGE → Attribute.ATTACK_DAMAGE, ARMOR → Attribute.ARMOR, MAX_HEALTH → Attribute.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:
...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 againstcritical_chance(0–100); on success, the event'sMAGICdamage modifier is set tooriginalDamage × (critical_damage / 100).Damage reduction — every
EntityDamageEventagainst a player is scaled down by1 - (damage_reduction / 100)oncedamage_reduction > 0.Shield absorption — the
SHIELDresource absorbs damage before it reaches health: fully if the shield amount covers the hit, otherwise it's drained to0and the remainder passes through; breaking the shield playsITEM_SHIELD_BREAK.Combat state —
CombatServicemarks a player "in combat" forcombat-cooldown(config.yml, default10s) after they take any damage; resources whose blueprint setsoff-combat-only: true(the shipped default forSHIELD) stop regenerating while the player is in combat.
See Combat Mechanics for the full mechanics and formulas.
Where to go next
Page | Covers |
|---|---|
| |
Full | |
| |
Critical strikes, damage reduction, shields, combat state | |
| |
| |
|