Allow multiple actions at a time

Closes #287
This commit is contained in:
Jesse Boyd 2016-09-16 17:47:53 +10:00
parent 58a1fb183e
commit 35b37ac8e9
13 changed files with 154 additions and 152 deletions

View File

@ -59,6 +59,8 @@ public class Settings extends Config {
})
@BlockName("default") // The name for the default block
public static final class LIMITS extends ConfigBlock {
@Comment("Max actions that can be run concurrently (i.e. commands)")
public int MAX_ACTIONS = 1;
@Comment("Max number of block changes (e.g. by `//set stone`).")
public int MAX_CHANGES = 50000000;
@Comment("Max number of blocks checked (e.g. `//count stone` which doesn't change blocks)")
@ -275,6 +277,7 @@ public class Settings extends Config {
for (String key : keys) {
if (key.equals("default") || (player != null && player.hasPermission("fawe.limit." + key))) {
LIMITS newLimit = LIMITS.get(key);
limit.MAX_ACTIONS = Math.max(limit.MAX_ACTIONS, newLimit.MAX_ACTIONS != -1 ? newLimit.MAX_ACTIONS : Integer.MAX_VALUE);
limit.MAX_CHANGES = Math.max(limit.MAX_CHANGES, newLimit.MAX_CHANGES != -1 ? newLimit.MAX_CHANGES : Integer.MAX_VALUE);
limit.MAX_BLOCKSTATES = Math.max(limit.MAX_BLOCKSTATES, newLimit.MAX_BLOCKSTATES != -1 ? newLimit.MAX_BLOCKSTATES : Integer.MAX_VALUE);
limit.MAX_CHECKS = Math.max(limit.MAX_CHECKS, newLimit.MAX_CHECKS != -1 ? newLimit.MAX_CHECKS : Integer.MAX_VALUE);

View File

@ -124,6 +124,7 @@ public class DefaultFaweQueueMap implements IFaweQueueMap {
return !blocks.isEmpty();
}
boolean result = true;
// amount = 8;
for (int i = 0; i < amount && (result = iter.hasNext()); i++, added++) {
Map.Entry<Long, FaweChunk> item = iter.next();
FaweChunk chunk = item.getValue();
@ -131,17 +132,16 @@ public class DefaultFaweQueueMap implements IFaweQueueMap {
pool.submit(chunk);
iter.remove();
}
// if result, then submitted = amount
if (result) {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < time) {
for (int i = 0; i < amount && (iter.hasNext()); i++, added++) {
while (System.currentTimeMillis() - start < time && result) {
if (result = iter.hasNext()) {
Map.Entry<Long, FaweChunk> item = iter.next();
FaweChunk chunk = item.getValue();
parent.start(chunk);
pool.submit(chunk);
iter.remove();
}
for (int i = 0; i < amount; i++, added--) {
FaweChunk fc = ((FaweChunk) pool.take().get());
parent.end(fc);
}

View File

@ -56,6 +56,7 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
public void end(FaweChunk chunk) {
super.end(chunk);
if (Settings.LIGHTING.MODE == 0) {
refreshChunk(chunk);
return;
}
if (relighter == null) {

View File

@ -34,18 +34,15 @@ public abstract class FaweCommand<T> {
}
});
} else {
if (player.getMeta("fawe_action") != null) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(player);
return true;
}
player.setMeta("fawe_action", true);
TaskManager.IMP.async(new Runnable() {
if (!player.runAction(new Runnable() {
@Override
public void run() {
execute(player, args);
player.deleteMeta("fawe_action");
}
});
}, true, true)) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(player);
return true;
}
}
return true;
} catch (Throwable e) {

View File

@ -4,6 +4,7 @@ package com.boydti.fawe.object;
* Created by Jesse on 4/5/2016.
*/
public class FaweLimit {
public int MAX_ACTIONS = 0;
public int MAX_CHANGES = 0;
public int MAX_FAILS = 0;
public int MAX_CHECKS = 0;
@ -41,6 +42,7 @@ public class FaweLimit {
return true;
}
};
MAX.MAX_ACTIONS = Integer.MAX_VALUE;
MAX.MAX_CHANGES = Integer.MAX_VALUE;
MAX.MAX_FAILS = Integer.MAX_VALUE;
MAX.MAX_CHECKS = Integer.MAX_VALUE;
@ -77,6 +79,7 @@ public class FaweLimit {
public FaweLimit copy() {
FaweLimit limit = new FaweLimit();
limit.MAX_ACTIONS = MAX_ACTIONS;
limit.MAX_CHANGES = MAX_CHANGES;
limit.MAX_BLOCKSTATES = MAX_BLOCKSTATES;
limit.MAX_CHECKS = MAX_CHECKS;

View File

@ -33,6 +33,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class FawePlayer<T> {
@ -106,6 +107,61 @@ public abstract class FawePlayer<T> {
}
}
private AtomicInteger getActions() {
AtomicInteger adder = getMeta("fawe_action_v2");
if (adder == null) {
adder = new AtomicInteger();
AtomicInteger previous = (AtomicInteger) setMeta("fawe_action_v2", adder);
if (previous != null) {
setMeta("fawe_action_v2", adder = previous);
}
}
return adder;
}
public boolean runAsyncIfFree(Runnable r) {
return runAction(r, true, true);
}
public boolean runIfFree(Runnable r) {
return runAction(r, true, false);
}
public boolean runAction(final Runnable ifFree, boolean checkFree, boolean async) {
if (checkFree) {
FaweLimit limit = getLimit();
int actionLimit = limit.MAX_ACTIONS;
final AtomicInteger current = getActions();
int val = current.incrementAndGet();
if (val > actionLimit) {
current.decrementAndGet();
return false;
}
Runnable r = new Runnable() {
@Override
public void run() {
try {
ifFree.run();
} catch (Throwable e) {
FaweException faweException = FaweException.get(e);
if (faweException != null) {
BBC.WORLDEDIT_CANCEL_REASON.send(FawePlayer.this, faweException.getMessage());
} else {
throw new RuntimeException(e);
}
} finally {
current.decrementAndGet();
}
}
};
TaskManager.IMP.taskNow(r, async);
return true;
} else {
ifFree.run();
}
return false;
}
/**
* Loads any history items from disk:
* - Should already be called if history on disk is enabled
@ -321,12 +377,13 @@ public abstract class FawePlayer<T> {
* Set some session only metadata for the player
* @param key
* @param value
* @return previous value
*/
public void setMeta(String key, Object value) {
public Object setMeta(String key, Object value) {
if (this.meta == null) {
this.meta = new ConcurrentHashMap<>(8, 0.9f, 1);
}
this.meta.put(key, value);
return this.meta.put(key, value);
}
/**
@ -388,60 +445,6 @@ public abstract class FawePlayer<T> {
return WorldEdit.getInstance().getEditSessionFactory().getEditSession(getWorld(), -1, getPlayer());
}
/**
* Run a task if the player has no currently running action
* @param run
* @return If the task was run
*/
public boolean runIfFree(Runnable run) {
if (getMeta("fawe_action") != null) {
return false;
}
setMeta("fawe_action", true);
try {
run.run();
} catch (Throwable e) {
FaweException faweException = FaweException.get(e);
if (faweException != null) {
BBC.WORLDEDIT_CANCEL_REASON.send(FawePlayer.this, faweException.getMessage());
} else {
MainUtil.handleError(e);
}
} finally {
deleteMeta("fawe_action");
}
return true;
}
/**
* Run an async task if the player has no currently running action
* @param run
* @return If the task was run
*/
public boolean runAsyncIfFree(final Runnable run) {
if (getMeta("fawe_action") != null) {
return false;
}
setMeta("fawe_action", true);
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
try {
run.run();
} catch (Throwable e) {
FaweException faweException = FaweException.get(e);
if (faweException != null) {
BBC.WORLDEDIT_CANCEL_REASON.send(FawePlayer.this, faweException.getMessage());
} else {
MainUtil.handleError(e);
}
} finally {
deleteMeta("fawe_action");
}
}
});
return true;
}
/**
* Get the tracked EditSession(s) for this player<br>

View File

@ -401,10 +401,12 @@ public abstract class FaweQueue {
}
while (!tasks.isEmpty()) {
Runnable task = tasks.poll();
try {
task.run();
} catch (Throwable e) {
MainUtil.handleError(e);
if (task != null) {
try {
task.run();
} catch (Throwable e) {
MainUtil.handleError(e);
}
}
}
}

View File

@ -43,7 +43,6 @@ public class CommandBrush implements Brush {
position = face.getFaceVector();
}
FawePlayer<Object> fp = FawePlayer.wrap(player);
fp.deleteMeta("fawe_action");
fp.setSelection(selector);
PlayerWrapper wePlayer = new SilentPlayerWrapper(new LocationMaskedPlayerWrapper(player, position));
String[] cmds = replaced.split(";");
@ -51,6 +50,5 @@ public class CommandBrush implements Brush {
CommandEvent event = new CommandEvent(wePlayer, cmd);
CommandManager.getInstance().handleCommand(event);
}
FawePlayer.wrap(player).setMeta("fawe_action", true);
}
}

View File

@ -351,6 +351,7 @@ public class EditSession extends AbstractWorld {
*/
public FaweLimit getLimitUsed() {
FaweLimit newLimit = new FaweLimit();
newLimit.MAX_ACTIONS = originalLimit.MAX_ACTIONS - limit.MAX_ACTIONS;
newLimit.MAX_CHANGES = originalLimit.MAX_CHANGES - limit.MAX_CHANGES;
newLimit.MAX_FAILS = originalLimit.MAX_FAILS - limit.MAX_FAILS;
newLimit.MAX_CHECKS = originalLimit.MAX_CHECKS - limit.MAX_CHECKS;

View File

@ -151,7 +151,7 @@ public class SelectionCommand extends SimpleCommand<Operation> {
long start = System.currentTimeMillis();
BBC.OPERATION.send(actor, BBC.VISITOR_BLOCK.format(cuboid.getArea()));
queue.flush();
BBC.ACTION_COMPLETE.send(actor, (System.currentTimeMillis() - start) / 1000);
BBC.ACTION_COMPLETE.send(actor, (System.currentTimeMillis() - start) / 1000d);
return null;
}
} catch (Throwable e) {

View File

@ -246,36 +246,29 @@ public final class CommandManager {
return split;
}
@Subscribe
public void handleCommand(final CommandEvent event) {
Request.reset();
TaskManager.IMP.taskNow(new Runnable() {
public void handleCommandOnCurrentThread(final CommandEvent event, boolean checkLimit) {
Actor actor = platformManager.createProxyActor(event.getActor());
final String args = event.getArguments();
final String[] split = commandDetection(args.split(" "));
// No command found!
if (!dispatcher.contains(split[0])) {
return;
}
if (!actor.isPlayer()) {
actor = new FakePlayer(actor.getName(), actor.getUniqueId(), actor);
}
final LocalSession session = worldEdit.getSessionManager().get(actor);
LocalConfiguration config = worldEdit.getConfiguration();
final CommandLocals locals = new CommandLocals();
final FawePlayer fp = FawePlayer.wrap(actor);
if (fp == null) {
throw new IllegalArgumentException("FAWE doesn't support: " + actor);
}
locals.put(Actor.class, actor instanceof Player ? new PlayerWrapper((Player) actor) : actor);
final Actor finalActor = actor;
if (!fp.runAction(new Runnable() {
@Override
public void run() {
Actor actor = platformManager.createProxyActor(event.getActor());
String args = event.getArguments();
String[] split = commandDetection(args.split(" "));
// No command found!
if (!dispatcher.contains(split[0])) {
return;
}
if (!actor.isPlayer()) {
actor = new FakePlayer(actor.getName(), actor.getUniqueId(), actor);
}
final LocalSession session = worldEdit.getSessionManager().get(actor);
LocalConfiguration config = worldEdit.getConfiguration();
CommandLocals locals = new CommandLocals();
final FawePlayer fp = FawePlayer.wrap(actor);
if (fp != null) {
if (fp.getMeta("fawe_action") != null) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(fp);
return;
}
fp.setMeta("fawe_action", true);
locals.put(Actor.class, actor instanceof Player ? new PlayerWrapper((Player) actor) : actor);
} else {
locals.put(Actor.class, actor);
}
locals.put("arguments", args);
final long start = System.currentTimeMillis();
try {
@ -297,35 +290,35 @@ public final class CommandManager {
throw t;
}
} catch (CommandPermissionsException e) {
BBC.NO_PERM.send(actor, "worldedit.*");
BBC.NO_PERM.send(finalActor, "worldedit.*");
} catch (InvalidUsageException e) {
if (e.isFullHelpSuggested()) {
actor.printRaw(ColorCodeBuilder.asColorCodes(new CommandUsageBox(e.getCommand(), e.getCommandUsed("/", ""), locals)));
finalActor.printRaw(ColorCodeBuilder.asColorCodes(new CommandUsageBox(e.getCommand(), e.getCommandUsed("/", ""), locals)));
String message = e.getMessage();
if (message != null) {
actor.printError(message);
finalActor.printError(message);
}
} else {
String message = e.getMessage();
actor.print(BBC.getPrefix() + (message != null ? message : "The command was not used properly (no more help available)."));
BBC.COMMAND_SYNTAX.send(actor, e.getSimpleUsageString("/"));
finalActor.print(BBC.getPrefix() + (message != null ? message : "The command was not used properly (no more help available)."));
BBC.COMMAND_SYNTAX.send(finalActor, e.getSimpleUsageString("/"));
}
} catch (WrappedCommandException e) {
FaweException faweException = FaweException.get(e);
if (faweException != null) {
BBC.WORLDEDIT_CANCEL_REASON.send(actor, faweException.getMessage());
BBC.WORLDEDIT_CANCEL_REASON.send(finalActor, faweException.getMessage());
} else {
Throwable t = e.getCause();
actor.printError("Please report this error: [See console]");
actor.printRaw(t.getClass().getName() + ": " + t.getMessage());
finalActor.printError("Please report this error: [See console]");
finalActor.printRaw(t.getClass().getName() + ": " + t.getMessage());
log.log(Level.SEVERE, "An unexpected error while handling a WorldEdit command", t);
}
} catch (CommandException e) {
String message = e.getMessage();
if (message != null) {
actor.printError(e.getMessage());
finalActor.printError(e.getMessage());
} else {
actor.printError("An unknown error has occurred! Please see console.");
finalActor.printError("An unknown error has occurred! Please see console.");
log.log(Level.SEVERE, "An unknown error occurred", e);
}
} finally {
@ -333,25 +326,35 @@ public final class CommandManager {
boolean hasSession = false;
if (editSession != null) {
editSession.flushQueue();
worldEdit.flushBlockBag(actor, editSession);
worldEdit.flushBlockBag(finalActor, editSession);
session.remember(editSession);
hasSession = editSession.size() > 0;
}
if (fp != null) {
fp.deleteMeta("fawe_action");
if (editSession != null) {
final long time = System.currentTimeMillis() - start;
if (time > 5 && hasSession) {
BBC.ACTION_COMPLETE.send(actor, (time / 1000d));
ChangeSet fcs = editSession.getChangeSet();
if (fcs != null && fcs instanceof FaweStreamChangeSet) {
MainUtil.sendCompressedMessage((FaweStreamChangeSet) fcs, editSession.getPlayer());
}
if (editSession != null) {
final long time = System.currentTimeMillis() - start;
if (time > 5 && hasSession) {
BBC.ACTION_COMPLETE.send(finalActor, (time / 1000d));
ChangeSet fcs = editSession.getChangeSet();
if (fcs != null && fcs instanceof FaweStreamChangeSet) {
MainUtil.sendCompressedMessage((FaweStreamChangeSet) fcs, fp);
}
}
}
}
}
}, checkLimit, false)) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(fp);
}
}
@Subscribe
public void handleCommand(final CommandEvent event) {
Request.reset();
TaskManager.IMP.taskNow(new Runnable() {
@Override
public void run() {
handleCommandOnCurrentThread(event, true);
}
}, Fawe.get().isMainThread());
event.setCancelled(true);
}

View File

@ -368,12 +368,12 @@ public class PlatformManager {
final BlockTool superPickaxe = session.getSuperPickaxe();
if (superPickaxe != null && superPickaxe.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
fp.runAsyncIfFree(new Runnable() {
fp.runAction(new Runnable() {
@Override
public void run() {
superPickaxe.actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
}
});
}, true, true);
event.setCancelled(true);
return;
}
@ -382,12 +382,12 @@ public class PlatformManager {
if (tool != null && tool instanceof DoubleActionBlockTool) {
if (tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
fp.runAsyncIfFree(new Runnable() {
fp.runAction(new Runnable() {
@Override
public void run() {
((DoubleActionBlockTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
}
});
}, true, true);
event.setCancelled(true);
}
}
@ -414,12 +414,12 @@ public class PlatformManager {
if (tool != null && tool instanceof BlockTool) {
if (tool.canUse(player)) {
FawePlayer<?> fp = FawePlayer.wrap(player);
fp.runAsyncIfFree(new Runnable() {
fp.runAction(new Runnable() {
@Override
public void run() {
((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location);
}
});
}, true, true);
event.setCancelled(true);
}
}

View File

@ -10,6 +10,7 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.MaskedFaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.WEManager;
@ -119,17 +120,11 @@ public class Sniper {
*/
public boolean snipe(Action action, Material itemInHand, Block clickedBlock, BlockFace clickedFace) {
try {
// Added
{
Player player = getPlayer();
FawePlayer<Player> fp = FawePlayer.wrap(player);
if (fp.getMeta("fawe_action") != null) {
return false;
}
maskQueue = null;
if (clickedBlock != null) {
clickedBlock = getWorld().getBlockAt(clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
}
Player player = getPlayer();
FawePlayer<Player> fp = FawePlayer.wrap(player);
maskQueue = null;
if (clickedBlock != null) {
clickedBlock = getWorld().getBlockAt(clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
}
return snipe(action, itemInHand, getWorld(), clickedBlock, clickedFace);
} catch (Throwable e) {
@ -301,20 +296,16 @@ public class Sniper {
performerBrush.initP(snipeData);
}
final FawePlayer<Player> fp = FawePlayer.wrap(getPlayer());
fp.runAsyncIfFree(new Runnable() {
fp.runAction(new RunnableVal<Boolean>() {
@Override
public void run() {
try {
boolean result = brush.perform(snipeAction, snipeData, targetBlock, lastBlock);
if (result) {
MetricsManager.increaseBrushUsage(brush.getName());
}
world.commit();
} catch (Throwable e) {
e.printStackTrace();
public void run(Boolean value) {
boolean result = brush.perform(snipeAction, snipeData, targetBlock, lastBlock);
if (result) {
MetricsManager.increaseBrushUsage(brush.getName());
}
world.commit();
}
});
}, true, true);
return true;
}
}