Astral Realms Documentation Help

Developer API

AstralSkill exposes its casting and lifecycle API through the SkillAPI static façade and the SkillService. Consumers (AstralClasses, AstralMobs, and other plugins) call cast methods with a skill id, a caster entity, and optionally a PlaceholderContainer for custom level/attribute bindings. The skill DSL and configuration are covered in Skill Files and Configuration; this page documents the entry points for programmatic skill casting.

Maven coordinates

<dependency> <groupId>com.astralrealms</groupId> <artifactId>skill</artifactId> <version>1.2-SNAPSHOT</version> <scope>provided</scope> </dependency>

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

Entry point

import com.astralrealms.skill.SkillAPI; SkillService skillService = SkillAPI.service();

SkillAPI.service() returns the SkillService singleton, which gates access to all casting and lookup methods. If called before the AstralSkill plugin has enabled, it throws IllegalStateException. Callers should guard with a soft dependency (check if AstralSkill is loaded via Bukkit.getPluginManager().getPlugin("AstralSkill")) or defer initialization to a later event.

Casting methods

The three primary cast methods differ in how the target is specified:

castAtPos(…) — direct position cast

void castAtPos( Entity caster, UUID casterUUID, String skillId, Location location, Vector3d direction, PlaceholderContainer container )

Casts a skill at an explicit location and direction. The direction vector is typically normalized (e.g. from a player's eye direction, or a computed path). If container is null and the caster is a Player, a player-aware placeholder container is auto-created via AstralPaperAPI.createPlaceholderContainer(player); otherwise a blank PlaceholderContainer() is used. If the skill id is unknown, a warning is logged and the cast is abandoned.

Before the cast initializes, the skill's PaperRequirementList (if present and the caster is a Player) is evaluated; if requirements fail, the cast is aborted.

Parameters:

  • caster: The entity triggering the skill (may be a Player, Mob, ArmorStand, etc.).

  • casterUUID: The UUID of the caster; carried through to caster effects and targeting.

  • skillId: The id of the skill file (must match a file under skills/).

  • location: The center/spawn point of the skill (interpreted in the location's world).

  • direction: A velocity/heading vector (typically normalized). For impact skills, points along the terrain normal; for projectiles, defines the launch direction.

  • container: Custom placeholder bindings (e.g. level, custom stats). If null and caster is a Player, a container is auto-built.

castThroughPos(…) — projectile auto-aim

void castThroughPos( Entity caster, UUID casterUUID, String skillId, Location startLocation, Location passLocation, PlaceholderContainer container ) void castThroughPos( Entity caster, UUID casterUUID, String skillId, Location startLocation, Location passLocation, PlaceholderContainer container, boolean forceCast )

Solves the launch pitch so a projectile skill's simulated arc passes through passLocation. Unlike castAtPos, the caller does not supply a direction; instead the projectile's full trajectory is simulated using its configured velocity, drag, gravity, and range, and the launch pitch is computed. The horizontal heading always points from startLocation toward passLocation.

This only works for projectile skills (see Cast Types). If the skill is not a projectile, or the target cannot be reached (out of range, too much drag, etc.), the cast is aborted with a warning.

When both a direct arc and a lobbed arc can reach the point, the flatter path is preferred.

Skills that enforce a fixed pitch cannot be aimed vertically, so arbitrary heights may not be reachable; in such cases the cast is aborted.

Variant without forceCast: If the target is unreachable, the cast is abandoned.

Variant with forceCast = true: If the target is unreachable, the projectile is still fired along the straight line from startLocation toward passLocation (it simply will not reach the point). This is useful for "best-effort" aiming (e.g. chase/homing skills that should always activate even if the geometry is challenging).

Parameters:

  • startLocation: Where the projectile spawns; defines the world the cast happens in. If passLocation is in a different world, it is re-interpreted in the start location's world and a warning is logged.

  • passLocation: The location the projectile should pass through.

  • container: Custom placeholder bindings. Null-handling is the same as castAtPos.

  • forceCast: When true, fire along a direct heading if the target is unreachable; when false, abort.

castAtTarget(…) — entity targeting

void castAtTarget( Entity caster, UUID casterUUID, String skillId, UUID entityUUID, PlaceholderContainer container )

Resolves an entity on the main thread (via world.getEntity(entityUUID)) and casts at its location. If the entity no longer exists, a warning is logged and the cast is abandoned. Otherwise, delegates to castAtPos(caster, casterUUID, skillId, target.getLocation().clone(), new Vector3d(0.0), container).

Lookup methods

Method

Returns

Use

findById(String id)

Optional<WrappedSkill>

Resolve a skill by id. Returns a defensive copy.

all()

Map<String, WrappedSkill>

Every loaded skill (unmodifiable snapshot).

skillService.findById("my_laser") .ifPresent(skill -> getLogger().info("Loaded " + skill.getId())); skillService.all().forEach((id, wrapped) -> { getLogger().info(id + ": " + wrapped.getType()); });

findById() returns a defensive copy of the skill, so modifications do not affect the shared state.

Lifecycle control

Active skills are tracked per caster and automatically updated each tick. Consumers rarely call these directly; most lifecycle management happens automatically:

Method

Returns

Use

activateSkill(Entity author, WrappedSkill skill)

void

Register a skill as active (queues it for update). Called internally by cast methods.

deactivateSkill(Entity author, WrappedSkill skill)

void

Unregister one active skill instance.

deactivateSkills(Entity author, Set<WrappedSkill> skills)

void

Unregister a set of skills.

deactivateAllSkills(Entity author)

void

Deactivate all active skills for a single entity.

deactivateAllSkills()

CompletableFuture<Void>

Deactivate all active skills across all casters. Returns a future that completes when all shards have cleared.

The deactivateAllSkills() variant (no argument) is called during plugin shutdown and reload.

Performance monitoring

String summary = skillService.metricsSummary(); getLogger().info(summary);

metricsSummary() returns a human-readable snapshot of every shard's update-loop performance, including tick counts, overruns, peak timing, and active skill counts. Safe to call from any thread. The output looks like:

Skill threads (4): 10,000 ticks, 150 over 1ms (1.500%), peak 3.245ms, 500 started, 490 finished shard 0: 2,500 ticks, 40 over (last 0.823ms, peak 2.100ms), 125 started, 123 finished shard 1: 2,500 ticks, 40 over (last 1.245ms, peak 3.245ms), 125 started, 122 finished …

Requirement gating

Before a skill cast initializes, if the caster is a Player and the skill has a PaperRequirementList defined in its configuration, the requirements are evaluated. If any requirement fails (e.g. not enough money, cooldown active, level too low), the cast is aborted silently and no effects fire.

See Skill Files for how to define requirements on a skill.

Threading model

AstralSkill uses a fixed pool of shards (update threads) to isolate skill state and avoid lock contention. Each caster is routed to the same shard via hash, so all of its active skills are updated on a single thread.

Utility scheduling

ScheduledExecutorService executor = skillService.getExecutor(); executor.schedule(() -> { // Utility work, e.g. delaying a follow-up cast }, 5, TimeUnit.SECONDS);

getExecutor() exposes shard 0's scheduled executor for callers that need to schedule utility work (e.g. delayed casts, cooldown timers). Callers must not touch shard state or access active skills from this thread. Only use it for work that does not depend on the skill update loop or shared caster state.

Cast dispatch

Casts are dispatched to the caster's assigned shard. The cast initializes and the skill is queued for updates. Each update tick (1ms budget per shard, across all active skills), the skill's update() method is called; when it returns true, the skill is marked finished and removed from the active set.

Automatic cleanup

The EntityListener deactivates all of a caster's active skills when the entity dies (EntityDeathEvent). The PlayerListener manages chunk snapshots and player-specific caches when players join, change worlds, or quit. As a consumer, you do not need to call cleanup methods when an entity disappears; it happens automatically.

Integration pattern

AstralClasses and AstralMobs call the cast methods directly with a skill id and optional custom PlaceholderContainer. They do not re-implement the skill DSL — they rely on this API to execute skills.

Example from a class ability:

import com.astralrealms.skill.SkillAPI; String skillId = "class_fireball"; SkillAPI.service().castAtPos( player, player.getUniqueId(), skillId, player.getEyeLocation(), player.getEyeLocation().getDirection(), null // auto-build container for the player );

For skill DSL details (cast types, targeting, effects, displays, hitbox shapes), see:

  • Skill Files — the overall structure and config schema.

  • Cast Typesprojectile, impact, laser, auto-cast, n-cast.

  • Targeting — collision detection and entity selection.

  • Effects — caster/target effect pipeline.

  • Displays — visual rendering (particles, sounds).

Last modified: 25 July 2026