Skip to content

Running Effects

Use EffectExecutor to render an effect repeatedly with Bukkit’s scheduler.

EffectExecutor executor = new EffectExecutor(plugin);
EffectExecutionConfig config = EffectExecutionConfig.builder()
.originAnchor(new FixedAnchor(origin))
.viewerSource(new FixedViewerSource(viewers))
.periodTicks(1)
.maxRuns(40)
.build();
RunningEffect running = executor.start(effect, config);

RunningEffect wraps the Bukkit task. Call cancel() to stop it early.

running.cancel();

EffectExecutionConfig controls scheduling.

Builder MethodDefaultMeaning
delayTicks(long)0initial scheduler delay
periodTicks(long)1ticks between frames
maxRuns(long)-1frame count limit, or -1 for unlimited

Each rendered frame receives:

  • runIndex: starts at 0
  • elapsedTicks: runIndex * periodTicks
  • elapsedSeconds: elapsedTicks / 20.0
  • step: by default, equal to runIndex

Use a custom step function when render progression should not be the same as run index.

EffectExecutionConfig config = EffectExecutionConfig.builder()
.originAnchor(new FixedAnchor(origin))
.viewerSource(new FixedViewerSource(viewers))
.periodTicks(2)
.maxRuns(60)
.stepFunction(frame -> (int) (frame.elapsedTicks() / 4))
.build();

Shapes such as rotating spheres, helixes, moving points, and morphs use ShapeContext.step() or ShapeContext.timeSeconds() to animate.

Anchors resolve locations per frame.

EffectAnchor fixed = new FixedAnchor(location);
EffectAnchor entity = new EntityAnchor(entity);

FixedAnchor clones the provided location and returns clones on each resolve.

EntityAnchor resolves the entity’s current location and returns null once the entity is invalid.

Set a target anchor for effects that need an origin-to-target relationship.

EffectExecutionConfig config = EffectExecutionConfig.builder()
.originAnchor(new EntityAnchor(caster))
.targetAnchor(new FixedAnchor(targetLocation))
.viewerSource(new FixedViewerSource(viewers))
.maxRuns(20)
.build();

Target-aware components include:

  • LineShape
  • MovingPointShape
  • LookAtTransform
  • direction providers that use EffectContext.target()

These flags control how unavailable anchors are handled.

Builder MethodDefaultMeaning
cancelIfOriginUnavailable(boolean)truestop if origin resolves to null or has no world
cancelIfTargetUnavailable(boolean)truestop if target resolves to null or has no world
cancelIfWorldsDiffer(boolean)truestop if origin and target are in different worlds

When the flag is false, the executor skips that frame and tries again on the next scheduled run.

ViewerSource resolves players per frame.

ViewerSource source = () -> Bukkit.getOnlinePlayers().stream()
.filter(player -> player.getWorld().equals(origin.getWorld()))
.toList();

FixedViewerSource is useful when the viewer collection is known at start time.

new FixedViewerSource(viewers);

By default, frames with no viewers are skipped before shape sampling, transforms, modifiers, and emitters run. Use skipEmptyViewerFrames(false) if your custom components need to advance even when no one sees the effect.

PaperEffectHandler is a convenience wrapper around EffectExecutor.

EffectHandler handler = new PaperEffectHandler(new EffectExecutor(plugin));
RunningEffect running = handler.playBetweenPoints(
effect,
from,
to,
viewers,
EffectExecutionConfig.builder()
.originAnchor(new FixedAnchor(from))
.viewerSource(new FixedViewerSource(viewers))
.periodTicks(1)
.maxRuns(20)
.build()
);

The handler replaces the origin, target, and viewer source from the provided base config with the method arguments. Use EffectExecutor directly when you need fully custom anchors or viewer sources.