package sander; import java.util.ArrayList; import static java.util.Collections.addAll; import java.util.List; import static org.bukkit.Bukkit.getScheduler; import org.bukkit.plugin.Plugin; public class MainThreadPlanner { private AccumulativeRunnable doProcess; private final AccumulativeRunnable doSubmit; protected final Plugin plugin; private final Processor processor; public MainThreadPlanner(Plugin plugin,Processor processor) { this.doSubmit = new DoSubmitAccumulativeRunnable(plugin); this.plugin = plugin; this.processor = processor; } private void process(List chunks) { this.processor.process(chunks); } /** * Sends data chunks to the {@link #process} method. This method is to be * used from inside the {@code doInBackground} method to deliver * intermediate results for processing on the Bukkit Main Server * Thread inside the {@code process} method. * *

* Because the {@code process} method is invoked asynchronously on the * Bukkit Main Server Thread * multiple invocations to the {@code publish} method might occur before the * {@code process} method is executed. For performance purposes all these * invocations are coalesced into one invocation with concatenated * arguments. * *

* For example: * *

     * publish("1");
     * publish("2", "3");
     * publish("4", "5", "6");
     * 
* * might result in: * *
     * process("1", "2", "3", "4", "5", "6")
     * 
* * * @param chunks intermediate results to process * * @see #process * */ public final void publish(T... chunks) { synchronized (this) { if (this.doProcess == null) { this.doProcess = new AccumulativeRunnable() { @Override public void run(List args) { MainThreadPlanner.this.process(args); } @Override protected void submit() { MainThreadPlanner.this.doSubmit.add(this); } }; } } this.doProcess.add(chunks); } private static abstract class AccumulativeRunnable implements Runnable { private List arguments = null; protected abstract void run(List paramList); @Override public final void run() { this.run(this.flush()); } public final synchronized void add(T... toAdd) { boolean mustSubmit = false; if (this.arguments == null) { mustSubmit = true; this.arguments = new ArrayList<>(); } addAll(this.arguments, toAdd); if (mustSubmit) { this.submit(); } } abstract protected void submit(); private synchronized List flush() { List localList = this.arguments; this.arguments = null; return localList; } } public interface Processor { public void process(List process); } private class DoSubmitAccumulativeRunnable extends AccumulativeRunnable implements Runnable { /** * Time in ticks between 2 invokings of the schedular */ private final static int DELAY = 1; /** * The plugin, used to schedule tasks */ private final Plugin plugin; public DoSubmitAccumulativeRunnable(Plugin plugin) { this.plugin = plugin; } @Override protected void run(List args) { for (Runnable runnable : args) { runnable.run(); } } @Override protected void submit() { if (this.plugin.isEnabled()) { getScheduler().scheduleSyncDelayedTask(this.plugin, this, DELAY); } else { // What do do, scheduling an task would throw an IllegalArgumentException, and enablking this plugin for a little time may cause bugs inside the plugin? // Mayby use reflection to access the enabled field of JavaPlugin, but this wont work whit plugins that dont exend that class throw new IllegalStateException("Plugin not enabled, unable to schadule task"); } } } }