如何动态更改重复Java Runnable的周期



首先,这是与Minecraft/Bukkit相关的,但我相信我的问题不是Bukkit特有的,只是忽略了我认为(我希望)的一些小问题。

在我的代码的最底部是一个randomDelayrandomPeriodrun()函数基于这两个变量以给定的间隔重复运行。我不知道如何在可运行程序启动后动态更改这些。我想让可运行的每个周期的长度不同,但问题是一旦run()函数开始,它似乎就使用了分配的第一个值。

package code.malon;
import java.util.Random;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;
public final class RandomResponse extends JavaPlugin {
    Random rand = new Random();
    int min = 20;
    int max = 200;
    long randomDelay = rand.nextInt((max - min) + 1) + min;
    long randomPeriod = rand.nextInt((max - min) + 1) + min;
    public void onEnable() {
        BukkitScheduler scheduler = Bukkit.getServer().getScheduler();
        scheduler.scheduleSyncRepeatingTask(this, new Runnable() {
            @Override
            public void run() {
                randomDelay = rand.nextInt((max - min) + 1) + min;
                randomPeriod = rand.nextInt((max - min) + 1) + min;
            }
        }, randomDelay, randomPeriod);
    }
}

让我们递归!哦,是的!

您的代码不是动态的,因为当RandomResponse由Bukkit的PluginClassLoader构造时,您初始化felds。这将创建一个new Random(),生成一次随机值,并为"randomDelay"安排延迟。这不是动态的。让我们用一个小黑客来解决这个问题:

public final class RandomResponse extends JavaPlugin {
    final Random rand = new Random();
    final int min = 20;
    final int max = 200;
    // I made these final for arbitrary reasons.
    private RandomResponse randomResponse;
    @Override
    public void onEnable() {
        randomResponse = this; // To use in anonymous class.
        // The delay should be random, so we compute it within onEnable() method.
        // We do not leave it in the class because then it's initialized by constructing.
        long randomDelay = rand.nextInt((max - min) + 1) + min;
        getServer().getScheduler().runTaskLater(this, new Runnable() {
            @Override
            public void run() {
                /*
                 * Stuff to do
                 */
                // Call itself again some time later.
                long randomDelay = rand.nextInt((max - min) + 1) + min;
                getServer().getScheduler().runTaskLater(randomResponse, this, randomDelay);
            }
        }, randomDelay);
    }
}

请注意,scheduleSyncRepeatingTask在开始运行后不能更改任务的周期长度。因此,您必须递归调用。要允许取消并重新运行任务,请使用以下命令:

BukkitTask task;
void runTask() {
     cancelTask();
     task = getServer().getScheduler().runTaskLater(this, new Runnable() {
        @Override
        public void run() {
            /*
             * Stuff to do
             */
            // Call itself again some time later.
            long randomDelay = rand.nextInt((max - min) + 1) + min;
            task = getServer().getScheduler().runTaskLater(randomResponse, this, randomDelay);
        }
    }, randomDelay);
}
void cancelTask() {
    if (task != null) try {
        task.cancel();
    } catch(Throwable ex) {
        // Ignore.
    }
}

您可能没有想到,您可能希望使用BukkitRunnable,而不是java.lang中的Runnable类。

将周期设置为每个刻度。

Task task = new Task();
task.runTaskTimer(Plugin, 1L, 1L);

然后使用索引,并在每次达到随机延迟时将其重置。

public class Task extends BukkitRunnable() {
    private Random random = new Random();
    private int index;
    public Task() {
        setRandom();
    }
    @Override
    public void run() {
        if (index == delay) {
            // Work goes here
            setRandom();
        } else {
            index++;
        }
    }
    private void setRandom() {
        index = random.nextInt(201) + 20;
    }
}

虽然使用Bukkit API无法做到这一点,但您可以在CraftBukkit和Reflection的帮助下动态更新任务的周期。

public class TaskUtil {
    public static Field CRAFTTASK_PERIOD;
    static {
        try {
            CRAFTTASK_PERIOD = CraftTask.class.getDeclaredField("period");
            CRAFTTASK_PERIOD.setAccessible(true);
        } catch (Exception e) {
            e.printStackTrace()
        }
    }
    public static void updateTaskPeriod(int id, long period) {
        CraftScheduler scheduler = (CraftScheduler) Bukkit.getScheduler();
        scheduler.getPendingTasks().forEach(task -> {
            if (task.getTaskId() == id) {
                try {
                    CRAFTTASK_PERIOD.setLong(task, period);
                } catch (Exception e) {
                    e.printStackTrace()
                }
            }
        });
    }
}

您还可以更进一步,通过利用Reflection从调度器中获取排队任务,完全消除CraftBukkit依赖关系。

最新更新