mirror of
https://github.com/zDevelopers/ImageOnMap.git
synced 2025-01-05 23:37:43 +01:00
* NEW: Worker runnables can now have callbacks.
These are executed in the Bukkit main thread, therefore allowing to use the Bukkit API in a thread-safe manner.
This commit is contained in:
parent
7a8ec83d6a
commit
15e5c5457f
@ -20,7 +20,9 @@ package fr.moribus.imageonmap;
|
||||
|
||||
import fr.moribus.imageonmap.commands.Commands;
|
||||
import fr.moribus.imageonmap.image.ImageIOExecutor;
|
||||
import fr.moribus.imageonmap.image.ImageRendererExecutor;
|
||||
import fr.moribus.imageonmap.image.MapInitEvent;
|
||||
import fr.moribus.imageonmap.map.MapManager;
|
||||
import java.io.File;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@ -62,8 +64,11 @@ public final class ImageOnMap extends JavaPlugin
|
||||
}
|
||||
}
|
||||
|
||||
//Init all the things !
|
||||
MetricsLite.startMetrics();
|
||||
ImageIOExecutor.start();
|
||||
ImageRendererExecutor.start();
|
||||
MapManager.init();
|
||||
Commands.init(this);
|
||||
getServer().getPluginManager().registerEvents(new MapInitEvent(), this);
|
||||
}
|
||||
@ -72,6 +77,8 @@ public final class ImageOnMap extends JavaPlugin
|
||||
public void onDisable()
|
||||
{
|
||||
ImageIOExecutor.stop();
|
||||
ImageRendererExecutor.stop();
|
||||
MapManager.exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ import fr.moribus.imageonmap.commands.Command;
|
||||
import fr.moribus.imageonmap.commands.CommandException;
|
||||
import fr.moribus.imageonmap.commands.CommandInfo;
|
||||
import fr.moribus.imageonmap.commands.Commands;
|
||||
import fr.moribus.imageonmap.image.ImageRendererExecutor;
|
||||
import fr.moribus.imageonmap.worker.WorkerCallback;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -36,7 +38,7 @@ public class NewCommand extends Command
|
||||
@Override
|
||||
protected void run() throws CommandException
|
||||
{
|
||||
Player player = playerSender();
|
||||
final Player player = playerSender();
|
||||
URL url;
|
||||
|
||||
if(args.length < 1) throwInvalidArgument("You must give an URL to take the image from.");
|
||||
@ -55,7 +57,21 @@ public class NewCommand extends Command
|
||||
|
||||
}
|
||||
|
||||
info("Not implemented.");
|
||||
info("Working ...");
|
||||
ImageRendererExecutor.Test(new WorkerCallback()
|
||||
{
|
||||
@Override
|
||||
public void finished(Object... args)
|
||||
{
|
||||
player.sendMessage("Long task finished !");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void errored(Throwable exception)
|
||||
{
|
||||
player.sendMessage("Whoops, an error occured !");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Moribus
|
||||
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package fr.moribus.imageonmap.image;
|
||||
|
||||
import fr.moribus.imageonmap.worker.Worker;
|
||||
import fr.moribus.imageonmap.worker.WorkerCallback;
|
||||
import fr.moribus.imageonmap.worker.WorkerRunnable;
|
||||
|
||||
public class ImageRendererExecutor extends Worker
|
||||
{
|
||||
static private ImageRendererExecutor instance;
|
||||
|
||||
static public void start()
|
||||
{
|
||||
if(instance != null) stop();
|
||||
instance = new ImageRendererExecutor();
|
||||
instance.init();
|
||||
}
|
||||
|
||||
static public void stop()
|
||||
{
|
||||
instance.exit();
|
||||
instance = null;
|
||||
}
|
||||
|
||||
private ImageRendererExecutor()
|
||||
{
|
||||
super("Image IO");
|
||||
}
|
||||
|
||||
static public void Test(WorkerCallback callback)
|
||||
{
|
||||
instance.submitQuery(new WorkerRunnable()
|
||||
{
|
||||
@Override
|
||||
public void run() throws Throwable
|
||||
{
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
|
||||
}
|
@ -42,6 +42,7 @@ public abstract class Worker
|
||||
PluginLogger.LogWarning("Restarting " + name + " thread.");
|
||||
exit();
|
||||
}
|
||||
callbackManager.init();
|
||||
thread = createThread();
|
||||
thread.start();
|
||||
}
|
||||
@ -49,6 +50,7 @@ public abstract class Worker
|
||||
public void exit()
|
||||
{
|
||||
thread.interrupt();
|
||||
callbackManager.exit();
|
||||
thread = null;
|
||||
}
|
||||
|
||||
@ -74,10 +76,11 @@ public abstract class Worker
|
||||
try
|
||||
{
|
||||
currentRunnable.run();
|
||||
callbackManager.callback(currentRunnable);
|
||||
}
|
||||
catch(Throwable ex)
|
||||
{
|
||||
PluginLogger.LogError("Exception from thread " + name, ex);
|
||||
callbackManager.callback(currentRunnable, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,6 +94,12 @@ public abstract class Worker
|
||||
}
|
||||
}
|
||||
|
||||
protected void submitQuery(WorkerRunnable runnable, WorkerCallback callback, Object... args)
|
||||
{
|
||||
callbackManager.setupCallback(runnable, callback, args);
|
||||
submitQuery(runnable);
|
||||
}
|
||||
|
||||
|
||||
private Thread createThread()
|
||||
{
|
||||
|
@ -18,19 +18,23 @@
|
||||
|
||||
package fr.moribus.imageonmap.worker;
|
||||
|
||||
import fr.moribus.imageonmap.ImageOnMap;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
class WorkerCallbackManager implements Runnable
|
||||
{
|
||||
static private final int WATCH_LOOP_DELAY = 5;
|
||||
static private final int WATCH_LOOP_DELAY = 40;
|
||||
|
||||
private final HashMap<WorkerRunnable, WorkerCallback> callbacks;
|
||||
private final ArrayDeque<WorkerCallback> callbackQueue;
|
||||
private final HashMap<WorkerRunnable, WorkerRunnableInfo> callbacks;
|
||||
private final ArrayDeque<WorkerRunnableInfo> callbackQueue;
|
||||
|
||||
private final String name;
|
||||
|
||||
private BukkitTask selfTask;
|
||||
|
||||
public WorkerCallbackManager(String name)
|
||||
{
|
||||
callbacks = new HashMap<>();
|
||||
@ -40,16 +44,104 @@ class WorkerCallbackManager implements Runnable
|
||||
|
||||
public void init()
|
||||
{
|
||||
//Bukkit.getScheduler().runTaskTimer(null, this, 0, WATCH_LOOP_DELAY);
|
||||
selfTask = Bukkit.getScheduler().runTaskTimer(ImageOnMap.getPlugin(), this, 0, WATCH_LOOP_DELAY);
|
||||
}
|
||||
|
||||
public void setupCallback(WorkerRunnable runnable, WorkerCallback callback, Object[] args)
|
||||
{
|
||||
synchronized(callbacks)
|
||||
{
|
||||
callbacks.put(runnable, new WorkerRunnableInfo(callback, args));
|
||||
}
|
||||
}
|
||||
|
||||
public void callback(WorkerRunnable runnable)
|
||||
{
|
||||
callback(runnable, null);
|
||||
}
|
||||
|
||||
public void callback(WorkerRunnable runnable, Throwable exception)
|
||||
{
|
||||
WorkerRunnableInfo runnableInfo;
|
||||
synchronized(callbacks)
|
||||
{
|
||||
runnableInfo = callbacks.get(runnable);
|
||||
}
|
||||
if(runnableInfo == null) return;
|
||||
runnableInfo.setRunnableException(exception);
|
||||
|
||||
enqueueCallback(runnableInfo);
|
||||
}
|
||||
|
||||
public void exit()
|
||||
{
|
||||
|
||||
if(selfTask != null) selfTask.cancel();
|
||||
}
|
||||
|
||||
private void enqueueCallback(WorkerRunnableInfo runnableInfo)
|
||||
{
|
||||
synchronized(callbackQueue)
|
||||
{
|
||||
callbackQueue.add(runnableInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
WorkerRunnableInfo currentRunnableInfo;
|
||||
synchronized(callbackQueue)
|
||||
{
|
||||
if(callbackQueue.isEmpty()) return;
|
||||
currentRunnableInfo = callbackQueue.pop();
|
||||
}
|
||||
|
||||
currentRunnableInfo.runCallback();
|
||||
}
|
||||
|
||||
private class WorkerRunnableInfo
|
||||
{
|
||||
private final WorkerCallback callback;
|
||||
private final Object[] args;
|
||||
private Throwable runnableException;
|
||||
|
||||
public WorkerRunnableInfo(WorkerCallback callback, Object[] args)
|
||||
{
|
||||
this.callback = callback;
|
||||
this.args = args;
|
||||
this.runnableException = null;
|
||||
}
|
||||
|
||||
public WorkerCallback getCallback()
|
||||
{
|
||||
return callback;
|
||||
}
|
||||
|
||||
public void runCallback()
|
||||
{
|
||||
if(runnableCrashed())
|
||||
{
|
||||
callback.errored(runnableException);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.finished(args);
|
||||
}
|
||||
}
|
||||
|
||||
public Throwable getRunnableException()
|
||||
{
|
||||
return runnableException;
|
||||
}
|
||||
|
||||
public void setRunnableException(Throwable runnableException)
|
||||
{
|
||||
this.runnableException = runnableException;
|
||||
}
|
||||
|
||||
public boolean runnableCrashed()
|
||||
{
|
||||
return this.runnableException != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user