I want my code to run once every 20t when a player touches the water
package me.pgk.Listeners;
import me.pgk.PGK;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
public class WaterLavaDamage implements Listener {
private final PGK plugin;
public WaterLavaDamage(PGK plugin) {
this.plugin = plugin;
}
@EventHandler
public void OnWater(PlayerMoveEvent e) {
Player p = e.getPlayer();
if (!(p.getLocation().getBlock().getType().equals(Material.WATER))) return;
Bukkit.getScheduler().runTaskLater(plugin, () -> {
//here is the code I wanted to run
p.sendMessage("Water Damage");
}, 20L);
}
}
the OnWater(PlayerMoveEvent e) event; checks for player movement every tick (20 times every second) and I want the code inside of it to run once every 20 ticks when the player touches the water.
however when the player does so, the code runs every tick (20 times every second) after 20 ticks.
CodePudding user response:
The PlayerMoveEvent
fires many times a second when the player moves. Even if they're moving their head, this is why the answer given by Blue Dev is making your message print many more times than expected.
Instead of creating a new task every time the player moves in water. You should use a runTaskTimer
scheduler and only create it if there is not one already running for the player.
// Create a map to store the player's UUID and their task
public final Map<UUID, BukkitTask> playersBeingDamaged = new HashMap<>();
@EventHandler
public void OnWater(PlayerMoveEvent e) {
Player p = e.getPlayer();
// Create an optional which may contain the BukkitTask
Optional<BukkitTask> bukkitTaskOptional = Optional.ofNullable(playersBeingDamaged.get(p.getUniqueId()));
if (!(p.getLocation().getBlock().getType().equals(Material.WATER))) {
// If the player is not in water and they have a task, cancel and remove it from the map
bukkitTaskOptional.ifPresent(it -> {
it.cancel();
playersBeingDamaged.remove(p.getUniqueId());
});
} else {
// If they're in water and have a task already, return; don't do anything more
if(bukkitTaskOptional.isPresent())
return;
// Create the task for the player which will have a delay of 0 ticks and run every 20
BukkitTask task = Bukkit.getScheduler().runTaskTimer(
plugin,
() -> p.sendMessage("Water Damage"),
0L,
20L
);
// Add the UUID and the task to the map
playersBeingDamaged.put(p.getUniqueId(), task);
}
}
If these tasks will be created often, a more efficient way would be to have a runTaskTimer
running continually that iterates over a map of UUID
's, get the player and their location, and then perform the action if they meet the conditions. They get removed from the map if they don't meet the conditions (e.g. in water). The PlayerMoveEvent
is only used to add them to this map when they're in water.
Here's an example of that:
public class MyRunnable extends BukkitRunnable {
@Override
public void run() {
for(UUID uuid : playersBeingDamaged){
Player player = Bukkit.getPlayer(uuid);
if(player == null || player.getLocation().getBlock().getType().equals(Material.WATER)){
// Player is offline OR they're not currently in water, remove them and continue
playersBeingDamaged.remove(uuid);
continue;
}
player.sendMessage("Water damage");
}
}
}
And this task can be started in your onEnable like so:
BukkitTask myTask = new MyRunnable().runTaskTimer(plugin, 0L, 20L);
P.S. playersBeingDamaged
will need to be somewhere accessible to your runnable class and your listener.
CodePudding user response:
Try using scheduler#runTaskTimer instead of scheduler#runTaskLater.
boolean isTouchingWater = true;
Bukkit.getScheduler().runTaskTimer(plugin, new Runnable() {
@Override
public void run() {
if(isTouchingWater) {
p.sendMessage("Water Damage");
}else {
//cancel the task
}
}
}, 0, 20);
You'll want to have a boolean as described so that you can cencel the task when the player is no longer touching water.