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

时间:2014-09-14 20:47:59

标签: java runnable minecraft bukkit

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

我的代码的最底部是randomDelayrandomPeriodrun()函数基于这两个变量以给定间隔重复运行。在runnable启动后,我不知道如何动态更改这些内容。我想让runnable的每个句点的长度不同,但问题是一旦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);
    }
}

3 个答案:

答案 0 :(得分:2)

让我们来回答!哦,是的!

您的代码不是动态的,因为当Bukkit RandomResponse构建PluginClassLoader时,初始化这些字谜。这会创建一个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代替Runnable中的java.lang类。

答案 1 :(得分:1)

将句点设置为每个刻度。

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;
    }
}

答案 2 :(得分:0)

虽然使用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依赖。