简体   繁体   中英

Repeating task every x minutes for every player

I am working on a Java/Spigot plugin for minecraft where every 15 minutes I'd like to send the player a message. It needs to be every 15 minutes since they joined not every 15 minutes all together, so every player would be on a different timer.

What I have tried

@SuppressWarnings("deprecation")
public static void setup()
{
    Bukkit.getScheduler().scheduleAsyncRepeatingTask(MyPlugin.getPlugin(), new BukkitRunnable()
    {
        public void run() {
            for (final String string : online.keySet()) {
                Utils.sendMessage(string, "It's been 15 minutes")
            }
        }
    }, 1L, 1200L);
}

but that is doing every 15 minutes since the server started. I can't find a way to do it for every player that won't lag the server if there are 50+ players online.

Edit:

Second attempt

private static boolean taskStarted = false;
private static HashMap<UUID, Long> map = new HashMap<UUID, Long>();

@EventHandler
public void onJoin(PlayerJoinEvent event) {
    map.put(event.getPlayer().getUniqueId(), Calendar.getInstance().getTime().getTime());
}

@EventHandler
public void onQuit(PlayerQuitEvent event) {
    map.remove(event.getPlayer().getUniqueId());
}



@SuppressWarnings("deprecation")
public static void startPowerTask() {
    if(taskStarted)
        return;
    taskStarted = true;
    Bukkit.getScheduler().scheduleSyncRepeatingTask(ServerCore.getPlugin(), new BukkitRunnable() {

        @Override
        public void run() {
            for(UUID uuid : map.keySet()) {
                long time = map.get(uuid);
                if(time == ???)
                    Utils.sendMessage(uuid, "It's been 15 minutes");
            }
        }
    }, 20, 20);
}

I don't know how to check if it's been 15 minutes

Scheduling a task per player is not a good idea, also why are you using an async task? These are especially costly to setup and may led to unintentional behaviour unless you know what you're doing. Always aim at using a sync task instead if possible.

For the least lag I'm using a single scheduler, called the global scheduler.

Listen for the PlayerJoinEvent , we need to know each player and when they joined. I'm using a hashmap to store the player UUID as well as a long representing their join date.

private HashMap<UUID, Long> map = new HashMap<UUID, Long>();

@EventHandler
public void onJoin(PlayerJoinEvent event) {
    map.put(event.getPlayer().getUniqueID(), Calendar.getInstance().getTime().getTime());
}

You can store the player name or even the player object instead, but UUIDs are most robust as they are consistent.

Now we need to check if the hashmap is empty or not, this can be done right after setting values to the map:

  • if the map isn't empty and the global scheduler isn't running, start it.
  • if the map is empty and the scheduler is running, stop it.

This makes it only run when necessary, reducing lag.

In the scheduler we want to loop through all players in the map and compare their join date with the current date, if above 15 minutes, do something and set the player join date as the current date. This scheduler should preferably run as rarely as possible, but to achieve most accurate results, aim at around a second (20 ticks)

I am not personally familiar with Bukkit, however, the documentation provides a getLastPlayed() method which should return the date when the user last was seen on the server. I believe you could use this date to calculate the delay in the BukkitScheduler , like this:

@SuppressWarnings("deprecation")
public static void setup()
{
    // calculate delay amount
    static long delay = somePlayer.getLastPlayed();
    // figure out remaining time post calculation
    delay = (System.currentTimeMillis() - delay) % 1200;
    // proceed with your code as before
    Bukkit.getScheduler().scheduleAsyncRepeatingTask(MyPlugin.getPlugin(), new BukkitRunnable()
    {
        public void run() {
            for (final String string : online.keySet()) {
                Utils.sendMessage(string, "It's been 15 minutes")
            }
        }
    }, delay, 1200L);
}

Note that I'm assuming 1200 ticks gives you the amount of time you want, but if not you can change that value after delay as well as in the modular arithmetic accordingly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM