-
-
Notifications
You must be signed in to change notification settings - Fork 29
ParticleTasks
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).
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.
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.
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.
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.
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.
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);
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());