diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index 0b7c47ae..53407192 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -262,7 +262,7 @@ public class Fawe { TaskManager.IMP.later(() -> { try { transformParser = new DefaultTransformParser(getWorldEdit()); - visualQueue = new VisualQueue(); + visualQueue = new VisualQueue(3); WEManager.IMP.managers.addAll(Fawe.this.IMP.getMaskManagers()); WEManager.IMP.managers.add(new PlotSquaredFeature()); Fawe.debug("Plugin 'PlotSquared' found. Using it now."); diff --git a/core/src/main/java/com/boydti/fawe/database/RollbackDatabase.java b/core/src/main/java/com/boydti/fawe/database/RollbackDatabase.java index cd6fbba0..b57707e8 100644 --- a/core/src/main/java/com/boydti/fawe/database/RollbackDatabase.java +++ b/core/src/main/java/com/boydti/fawe/database/RollbackDatabase.java @@ -6,6 +6,7 @@ import com.boydti.fawe.config.Settings; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.changeset.DiskStorageHistory; +import com.boydti.fawe.object.task.SingleThreadNotifyQueue; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.Vector; @@ -22,7 +23,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; -public class RollbackDatabase { +public class RollbackDatabase extends SingleThreadNotifyQueue { private final String prefix; private final File dbLocation; @@ -40,7 +41,7 @@ public class RollbackDatabase { private String PURGE; private ConcurrentLinkedQueue historyChanges = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue notify = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue tasks = new ConcurrentLinkedQueue<>(); public RollbackDatabase(String world) throws SQLException, ClassNotFoundException { this(FaweAPI.getWorld(world)); @@ -62,28 +63,24 @@ public class RollbackDatabase { DELETE_EDIT_USER = "DELETE FROM `" + prefix + "edits` WHERE `player`=? AND `id`=?"; init(); purge((int) TimeUnit.DAYS.toMillis(Settings.IMP.HISTORY.DELETE_AFTER_DAYS)); - TaskManager.IMP.async(new Runnable() { - @Override - public void run() { - long last = System.currentTimeMillis(); - while (true) { - if (connection == null) { - break; - } - if (!RollbackDatabase.this.sendBatch()) { - try { - while (!notify.isEmpty()) { - Runnable runnable = notify.poll(); - runnable.run(); - } - Thread.sleep(50); - } catch (final InterruptedException e) { - e.printStackTrace(); - } - } - } + } + + + @Override + public boolean hasQueued() { + return connection != null && (!historyChanges.isEmpty() || !tasks.isEmpty()); + } + + @Override + public void operate() { + synchronized (this) { + if (connection == null) { + return; } - }); + while (sendBatch()) { + // Still processing + } + } } public void init() { @@ -94,10 +91,6 @@ public class RollbackDatabase { } } - public void addFinishTask(Runnable run) { - notify.add(run); - } - public void delete(final UUID uuid, final int id) { addTask(new Runnable() { @Override @@ -186,16 +179,14 @@ public class RollbackDatabase { } public void logEdit(RollbackOptimizedHistory history) { - historyChanges.add(history); + queue(() -> historyChanges.add(history)); } - private final ConcurrentLinkedQueue tasks = new ConcurrentLinkedQueue<>(); - public void addTask(Runnable run) { - this.tasks.add(run); + queue(() -> tasks.add(run)); } - public void runTasks() { + private void runTasks() { Runnable task; while ((task = tasks.poll()) != null) { try { @@ -321,9 +312,14 @@ public class RollbackDatabase { if (connection == null) { return false; } - connection.close(); - connection = null; - return true; + synchronized (this) { + if (connection == null) { + return false; + } + connection.close(); + connection = null; + return true; + } } /** diff --git a/core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualQueue.java b/core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualQueue.java index 4f64496b..a5feb905 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualQueue.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/visualization/VisualQueue.java @@ -1,62 +1,35 @@ package com.boydti.fawe.object.brush.visualization; -import com.boydti.fawe.Fawe; import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.util.TaskManager; +import com.boydti.fawe.object.task.SingleThreadIntervalQueue; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.entity.Player; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -public class VisualQueue { +public class VisualQueue extends SingleThreadIntervalQueue { - private ConcurrentHashMap playerMap; + public VisualQueue(int interval) { + super(interval); + } - public VisualQueue() { - playerMap = new ConcurrentHashMap<>(); - Runnable task = new Runnable() { - @Override - public void run() { - long allowedTick = Fawe.get().getTimer().getTick() - 1; - Iterator> iter = playerMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - Long time = entry.getValue(); - if (time < allowedTick) { - FawePlayer fp = entry.getKey(); - iter.remove(); - LocalSession session = fp.getSession(); - Player player = fp.getPlayer(); - Tool tool = session.getTool(player); - Brush brush; - if (tool instanceof BrushTool) { - BrushTool brushTool = (BrushTool) tool; - if (brushTool.getVisualMode() != VisualMode.NONE) { - try { - brushTool.visualize(BrushTool.BrushAction.PRIMARY, player); - } catch (Throwable e) { - WorldEdit.getInstance().getPlatformManager().handleThrowable(e, player); - } - } - } - } + @Override + public void operate(FawePlayer fp) { + LocalSession session = fp.getSession(); + Player player = fp.getPlayer(); + Tool tool = session.getTool(player); + Brush brush; + if (tool instanceof BrushTool) { + BrushTool brushTool = (BrushTool) tool; + if (brushTool.getVisualMode() != VisualMode.NONE) { + try { + brushTool.visualize(BrushTool.BrushAction.PRIMARY, player); + } catch (Throwable e) { + WorldEdit.getInstance().getPlatformManager().handleThrowable(e, player); } - TaskManager.IMP.laterAsync(this, 3); } - }; - TaskManager.IMP.laterAsync(task, 3); - } - - public boolean dequeue(FawePlayer player) { - return playerMap.remove(player) != null; - } - - public void queue(FawePlayer player) { - playerMap.put(player, Fawe.get().getTimer().getTick()); + } } } \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java index a8cb8ce4..d594791c 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java @@ -123,6 +123,21 @@ public class NullExtent extends FaweRegionExtent { return null; } + @Override + public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { + throw new FaweException(reason); + } + + @Override + public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { + throw new FaweException(reason); + } + + @Override + public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) { + throw new FaweException(reason); + } + @Override public Extent getExtent() { return this; diff --git a/core/src/main/java/com/boydti/fawe/object/task/SingleThreadIntervalQueue.java b/core/src/main/java/com/boydti/fawe/object/task/SingleThreadIntervalQueue.java new file mode 100644 index 00000000..0b774c02 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/task/SingleThreadIntervalQueue.java @@ -0,0 +1,55 @@ +package com.boydti.fawe.object.task; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.util.TaskManager; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +public abstract class SingleThreadIntervalQueue { + private final ConcurrentHashMap objMap = new ConcurrentHashMap<>(); + private final Runnable task; + private AtomicBoolean queued = new AtomicBoolean(); + + public SingleThreadIntervalQueue(int interval) { + this.task = new Runnable() { + @Override + public void run() { + long allowedTick = Fawe.get().getTimer().getTick() - 1; + Iterator> iter = objMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + Long time = entry.getValue(); + if (time < allowedTick) { + T obj = entry.getKey(); + iter.remove(); + operate(obj); + } + } + synchronized (objMap) { + if (!objMap.isEmpty()) TaskManager.IMP.laterAsync(this, interval); + else queued.set(false); + } + } + }; + } + + public abstract void operate(T obj); + + public boolean dequeue(T obj) { + synchronized (objMap) { + return objMap.remove(obj) != null; + } + } + + public void queue(T obj) { + synchronized (objMap) { + objMap.put(obj, Fawe.get().getTimer().getTick()); + if (!queued.get()) { + queued.set(true); + TaskManager.IMP.laterAsync(task, 3); + } + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/task/SingleThreadNotifyQueue.java b/core/src/main/java/com/boydti/fawe/object/task/SingleThreadNotifyQueue.java new file mode 100644 index 00000000..e5643666 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/task/SingleThreadNotifyQueue.java @@ -0,0 +1,37 @@ +package com.boydti.fawe.object.task; + +import com.boydti.fawe.util.TaskManager; +import java.util.concurrent.atomic.AtomicBoolean; + +public abstract class SingleThreadNotifyQueue { + private Object lock = new Object(); + private final Runnable task; + private final AtomicBoolean running = new AtomicBoolean(); + + public SingleThreadNotifyQueue() { + this.task = new Runnable() { + @Override + public void run() { + operate(); + synchronized (lock) { + if (hasQueued()) TaskManager.IMP.async(this); + else running.set(false); + } + } + }; + } + + public abstract boolean hasQueued(); + + public void queue(Runnable queueTask) { + synchronized (lock) { + queueTask.run(); + if (!running.get()) { + running.set(true); + TaskManager.IMP.async(task); + } + } + } + + public abstract void operate(); +}