Configuration
AstralSkill's main configuration file is config.yml in the plugin data folder (plugins/AstralSkill/). On first enable, saveDefaultConfig() creates it with defaults. The plugin loads skills from a separate folder (see Skill Folder below).
config.yml: Skill Service
The skill-service block tunes the skill update loop — the background thread pool that runs active skills every millisecond.
Field | Type | Default | Description |
|---|---|---|---|
| int |
| Number of update shards (concurrent threads). Each shard owns a fixed-rate update loop running at 1ms per tick and a subset of active skills (partitioned by entity UUID hash). The shard count is fixed at construction and is not changed by |
| long |
| Duration (milliseconds) that a tick must exceed to be counted toward the console warning. Only gates the |
| double |
| Minimum percentage of ticks in a 10-second window that must exceed |
Update threads and shards
Each shard runs as a single daemon thread named AstralSkill-Skills-<n> (where <n> is the shard index, 0-indexed). Skills are routed to shards by their author's entity UUID hash so each author's active skills always run on the same shard, keeping each shard's work thread-confined and eliminating lock contention.
The shard count is determined once when the plugin enables and remains fixed for the server's lifetime — even if you call /skill reload, the existing shards continue running and no new ones are created. To change the shard count, restart the server.
Performance metrics
/skill metrics (permission skill.metrics) displays per-shard performance counters across all time:
Total ticks: how many times each shard's update loop has run.
Over budget: count of ticks exceeding the hard 1ms tick budget (regardless of
warn-threshold-ms).Peak duration: the longest single tick observed on that shard.
Started / finished: total skills activated and deactivated on each shard.
The console warning (emitted every 10 seconds when conditions are met) is tuned by warn-threshold-ms and warn-min-overrun-percent, but the metrics counters always measure against the fixed 1ms budget, giving you the full picture without noise.
Skill Folder
Every YAML file under <plugin-data-folder>/skills/ is loaded as a skill. Folders are scanned recursively, so nesting is allowed — skills/knight/attack-1/cast.yml, skills/mobs/tower/boss/, etc. are all valid. Folder organization is purely for humans; it has no semantic meaning to the plugin.
On enable and on /skill reload, SkillService.load() walks the skill folder and calls configurationManager().loadFolder(skillFolder, WrappedSkill.class) to parse each file into a WrappedSkill, deserializing the YAML into the skill's concrete Skill type (e.g. ProjectileSkill, AreaSkill) via the configured type adapter.
Skill identity and collisions
Each skill file must declare an id field at the top level:
The id field is the map key — skills are indexed by their id, not by filename. This means two skill files with the same id will silently collide: whichever file is loaded last will overwrite the earlier one, with no warning logged.
To avoid collisions:
Keep filenames aligned with their skill ids (e.g.
knight-attack-1-cast.ymlforid: "knight-attack-1-cast").Use namespace/class prefixes in skill ids across your network (e.g.
knight-attack-1-cast,archer-volley-burst,tower-guardian-smash).
Skill initialization
After loading all skill files, each is initialized via its init() method, which:
Parses the effect specs (under
skill-rootand target phases) into effect instances viaEffectFactory.Parses the
paramssection according to the skill type's expected fields.Validates that all referenced effects, targeting strategies, and other dependencies resolve correctly.
If initialization fails for a skill, an error is logged with the skill id and exception message, but the plugin continues loading the remaining skills. The failed skill will be unavailable at runtime.
Reload Behavior
/skill reload (permission skill.reload) invokes SkillService.smartLoad(), which implements a graceful reload cycle:
If skills are already running,
smartLoad()deactivates all active skills asynchronously.Once all skills are deactivated, all existing shard threads are stopped and the skill registry is cleared.
The folder is re-scanned, each skill is re-parsed and re-initialized, and each shard's update loop is restarted.
The console prints the count of skills loaded and initialized.
If no skills are running (e.g., this is the first load on plugin enable), load() is called directly without the shutdown sequence.
Importantly:
The shard count is never changed — the same threads continue running before and after reload.
Running skill instances are deactivated — they stop updating and their effects are applied to targets before they're removed.
Configuration changes take effect immediately — reloading picks up new/edited/deleted skill files.
Plugin Disable
When the server stops or the plugin is disabled, onDisable() shuts down gracefully:
All shard threads are stopped (the
ScheduledFuturefor each shard's update loop is cancelled).The chunk and entity bounding-box caches are cleared.
The executor services are shut down.
Any active skills are terminated without running their target-effect phases.