Skip to content
This repository has been archived by the owner on Jun 15, 2023. It is now read-only.

ParticleTasks

ByteZ edited this page Sep 14, 2021 · 11 revisions

Quick Explanation With Example

ParticleTasks are the most efficient and elegant way to repeatedly display particles. As an example, let's look at a very inefficient method which even lags the server when used excessively:

public void displayCube(World world, Vector start, Vector end) {
  for (int x = start.getBlockX(); x <= end.getBlockX(); ++x) {
    for (int y = start.getBlockY(); y <= end.getBlockY(); ++y) {
      for (int z = start.getBlockZ(); z <= end.getBlockZ(); ++z) {
        new ParticleBuilder(ParticleEffect.FLAME, new Location(world, x, y, z)).display();
      }
    }
  }
}

To progressively display this cube, this method has to be called every few ticks. This will lead to high CPU time and lag because the packets are rebuilt for every run. Also, the PlayerConnection has to be retrieved for every particle which also causes delay (Even with the ConnectionCache).

Instead of the above code, we can start a new WorldTask. A WorldTask is a repeating task that sends the given particles to every player in a specific world.

public int startCubeTask(World world, Vector start, Vector end) {
  List<Object> packets = new ArrayList<>();
  ParticleBuilder particle = new ParticleBuilder(ParticleEffect.FLAME);
  for (int x = start.getBlockX(); x <= end.getBlockX(); ++x) {
    for (int y = start.getBlockY(); y <= end.getBlockY(); ++y) {
      for (int z = start.getBlockZ(); z <= end.getBlockZ(); ++z) {
        packets.add(particle.setLocation(new Location(world, x, y, z)).toPacket());
      }
    }
  }
  return TaskManager.startWorldTask(packets, 5, world); // Start a new task and return the id
}

Or the old fashioned way:

ParticleTask task = new WorldTask(packets, 5, world); // Create a new WorldTask with a tick delay of 5
return TaskManager.getTaskManager().startTask(task); // Start the new task and return the id

This method will start a new ParticleTask that displays a cube of flame particles every 5 ticks. The return value is the id of the task, which can be used to cancel the timer at any time (see TaskManager#stopTask).

Currently built-in ParticleTask implementations

Please note that only WorldTasks has access to the world. Every other ParticleTask will display the particles in every world since Mojang doesn't save the world id in the packet

Class name Short explanation
GlobalTask Displays the particle to every player that is currently online.
WorldTask Displays the particle to every player in a specific world.
TargetedTask Displays the particle to the provided list of players.
SingularTask Only displays the particles to one specific player.
FilteredTask Only displays the particles to players that match the given filter.
SuppliedTask Uses a Supplier to evaluate which players should see particles.

If you have any suggestions for more task implementations, please open an issue with a detailed explanation.

GlobalTask

A GlobalTask is the simplest implementation of the ParticleTask class. It displays the particles to every player that is currently online. The constructor only needs the packets and the tick delay.

WorldTask

WorldTasks are the same as GlobalTasks except they only send the packets to players in a specific world instead of every player that is online.

TargetedTask

TargetedTasks are tasks that only target a predefined list of players. Please note that this implementation currently saves a collection of players, not UUIDS! So if a player rejoins, they won't be able to see the particles. However, because of the JVMs reference implementation, this can be used to directly link to the reference of an object.

SingularTask

A SingularTask only sends the particles to one specific player. But unlike TargetedTasks, the UUID of the player is saved instead of the player object. Because of this, the targeted player will still see the particle, even after rejoining.

FilteredTask

A FilteredTask applies a given Filter to all online players and only displays the particle to matches.

As an example, let's create a task that only sends the particles to server operators every 10 ticks: Lambda:

ParticleTask task = new FilteredTask(packets, 10, player -> player.isOp());

or by using a method reference:

ParticleTask task = new FilteredTask(packets, 10, Player::isOp);

SuppliedTask

SuppliedTasks use a Supplier to retrieve the list of targeted players. The supplier is called every time the task is called.

e.g. with a TeamManager that has getters for each team.

ParticleTask task = new SuppliedTask(packets, 5, () -> teamManager.getRedPlayers());