Fix entity mask + More work on rollback

This commit is contained in:
Jesse Boyd 2016-08-04 19:46:38 +10:00
parent e5ebccd053
commit 895de11a09
18 changed files with 589 additions and 78 deletions

View File

@ -219,6 +219,12 @@ public class FaweBukkit implements IFawe, Listener {
return world.getName();
}
@Override
public String getUserName(UUID uuid) {
String name = Bukkit.getOfflinePlayer(uuid).getName();
return name != null ? name : uuid.toString();
}
/**
* A mask manager handles region restrictions e.g. PlotSquared plots / WorldGuard regions
*/

View File

@ -32,6 +32,7 @@ import com.sk89q.worldedit.command.NavigationCommands;
import com.sk89q.worldedit.command.RegionCommands;
import com.sk89q.worldedit.command.SchematicCommands;
import com.sk89q.worldedit.command.ScriptingCommands;
import com.sk89q.worldedit.command.ToolCommands;
import com.sk89q.worldedit.command.composition.SelectionCommand;
import com.sk89q.worldedit.command.tool.brush.GravityBrush;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
@ -277,6 +278,7 @@ public class Fawe {
LocalSession.inject(); // Add remember order / queue flushing
// Commands
BrushCommands.inject(); // Translations + heightmap
ToolCommands.inject(); // Translations + inspect
ClipboardCommands.inject(); // Translations + lazycopy + paste optimizations
SchematicCommands.inject(); // Translations
ScriptingCommands.inject(); // Translations

View File

@ -16,6 +16,7 @@ import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.CuboidClipboard;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.awt.Color;
import java.lang.reflect.Field;
import java.util.ArrayList;
@ -507,6 +508,18 @@ public class FaweCache {
}
}
public static String getMaterialName(int combined) {
return getMaterialName(getId(combined), getData(combined));
}
public static String getMaterialName(int id, int data) {
BundledBlockData.BlockEntry entry = BundledBlockData.getInstance().findById(id);
if (entry == null) {
return data != 0 ? id + ":" + data : id + "";
}
return data != 0 ? entry.id.replace("minecraft:","") + ":" + data : entry.id.replace("minecraft:","");
}
public static Map<String, Object> asMap(Object... pairs) {
HashMap<String, Object> map = new HashMap<String, Object>(pairs.length >> 1);
for (int i = 0; i < pairs.length; i+=2) {

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer;
@ -27,6 +28,24 @@ public class Rollback extends FaweCommand {
@Override
public boolean execute(final FawePlayer player, final String... args) {
if (!Settings.HISTORY.USE_DATABASE) {
BBC.SETTING_DISABLE.send(player, "history.use-database");
return false;
}
if (args.length != 3) {
BBC.COMMAND_SYNTAX.send(player, "/frb u:<uuid> r:<radius> t:<time>");
return false;
}
switch (args[0]) {
case "i":
case "info":
case "undo":
case "revert":
BBC.COMMAND_SYNTAX.send(player, "/frb u:<uuid> r:<radius> t:<time>");
return false;
}
if (args.length < 1) {
BBC.COMMAND_SYNTAX.send(player, "/frb <info|undo> u:<uuid> r:<radius> t:<time>");
return false;

View File

@ -41,6 +41,7 @@ public enum BBC {
WORLDEDIT_RESTRICTED("&6Your WorldEdit is now restricted.", "Info"),
WORLDEDIT_OOM_ADMIN("&cPossible options:\n&8 - &7//fast\n&8 - &7Do smaller edits\n&8 - &7Allocate more memory\n&8 - &7Disable this safeguard", "Info"),
COMPRESSED("History compressed. Saved ~ %s0b (%s1x smaller)", "Info"),
ACTION_COMPLETE("Action completed in %s0 seconds", "Info"),
GENERATING_LINK("Uploading %s, please wait...", "Web"),
GENERATING_LINK_FAILED("&cFailed to generate download link!", "Web"),
@ -90,7 +91,23 @@ public enum BBC {
BRUSH_HEIGHT_INVALID("Invalid height map file (%s0)", "WorldEdit.Brush"),
BRUSH_SMOOTH("Smooth brush equipped (%s0 x %s1 using %s2).", "WorldEdit.Brush"),
BRUSH_SPHERE("Sphere brush shape equipped (%s0).", "WorldEdit.Brush"),
BRUSH_INSPECT("Inspect brush shape equipped (%s0).", "WorldEdit.Brush"),
TOOL_INSPECT("Inspect tool bound to %s0.", "WorldEdit.Tool"),
TOOL_INSPECT_INFO("&7%s0 changed %s1 to %s2 %s3 ago","Info"),
TOOL_INSPECT_INFO_FOOTER("&6Total: &7%s0 changes","Info"),
TOOL_NONE("Tool unbound from your current item.", "WorldEdit.Tool"),
TOOL_INFO("Info tool bound to %s0.", "WorldEdit.Tool"),
TOOL_TREE("Tree tool bound to %s0.", "WorldEdit.Tool"),
TOOL_TREE_ERROR("Tree type %s0 is unknown.", "WorldEdit.Tool"),
TOOL_REPL("Block replacer tool bound to %s0.", "WorldEdit.Tool"),
TOOL_CYCLER("Block data cycler tool bound to %s0.", "WorldEdit.Tool"),
TOOL_FLOOD_FILL("Block flood fill tool bound to %s0.", "WorldEdit.Tool"),
TOOL_FLOOD_FILL_RANGE_ERROR("Maximum range: %s0.", "WorldEdit.Tool"),
TOOL_DELTREE("Floating tree remover tool bound to %s0.", "WorldEdit.Tool"),
TOOL_FARWAND("Far wand tool bound to %s0.", "WorldEdit.Tool"),
TOOL_LRBUILD_BOUND("Long-range building tool bound to %s0.", "WorldEdit.Tool"),
TOOL_LRBUILD_INFO("Left-click set to %s0; right-click set to %s1.", "WorldEdit.Tool"),
SCHEMATIC_DELETE("%s0 has been deleted.", "Worldedit.Schematic"),
SCHEMATIC_FORMAT("Available clipboard formats (Name: Lookup names)", "Worldedit.Schematic"),
@ -116,9 +133,11 @@ public enum BBC {
COMMAND_SYNTAX("&cUsage: &7%s0", "Error"),
NO_PERM("&cYou are lacking the permission node: %s0", "Error"),
SETTING_DISABLE("&cLacking setting: %s0","Error"),
SCHEMATIC_NOT_FOUND("&cSchematic not found: &7%s0", "Error"),
NO_REGION("&cYou have no current WorldEdit region", "Error"),
NOT_PLAYER("&cYou must be a player to perform this action!", "Error"),
PLAYER_NOT_FOUND("&cPlayer not found:&7 %s0", "Error"),
OOM(
"&8[&cCritical&8] &cDetected low memory i.e. < 1%. FAWE will take the following actions:\n&8 - &7Terminate WE block placement\n&8 - &7Clear WE history\n&8 - &7Unload non essential chunks\n&8 - &7Kill entities\n&8 - &7Garbage collect\n&cIgnore this if trying to crash server.\n&7Note: Low memory is likely (but not necessarily) caused by WE",
"Error"),

View File

@ -4,6 +4,8 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class DBHandler {
public final static DBHandler IMP = new DBHandler();
private Map<String, RollbackDatabase> databases = new ConcurrentHashMap<>();
public RollbackDatabase getDatabase(String world) {

View File

@ -5,9 +5,9 @@ import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.change.MutablePlayerBlockChange;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.World;
import java.io.File;
import java.io.IOException;
@ -30,8 +30,9 @@ public class RollbackDatabase {
private String INSERT_EDIT;
private String CREATE_TABLE;
private String GET_EDITS_POINT;
// private String GET_EDITS_POINT;
private String GET_EDITS;
private String GET_EDITS_USER;
private String PURGE;
private ConcurrentLinkedQueue<RollbackOptimizedHistory> historyChanges = new ConcurrentLinkedQueue<>();
@ -40,13 +41,15 @@ public class RollbackDatabase {
public RollbackDatabase(final String world) throws SQLException, ClassNotFoundException {
this.prefix = "";
this.world = world;
this.dbLocation = new File(Fawe.imp().getDirectory(), "history" + File.separator + world);
this.dbLocation = new File(Fawe.imp().getDirectory(), "history" + File.separator + world + File.separator + "summary.db");
connection = openConnection();
CREATE_TABLE = "CREATE TABLE IF NOT EXISTS `" + prefix + "edits` (`player` BLOB(16) NOT NULL,`id` INT NOT NULL,`x1` INT NOT NULL,`y1` INT NOT NULL,`z1` INT NOT NULL,`x2` INT NOT NULL,`y2` INT NOT NULL,`z2` INT NOT NULL,`time` INT NOT NULL, PRIMARY KEY (player, id))";
INSERT_EDIT = "INSERT INTO `" + prefix + "edits` (`player`,`id`,`x1`,`y1`,`z1`,`x2`,`y2`,`z2`,`time`) VALUES(?,?,?,?,?,?,?,?,?)";
PURGE = "DELETE FROM `" + prefix + "edits` WHERE `time`<?";
GET_EDITS = "SELECT `player`,`id`,`x1`,`y1`,`z1`,`x2`,`y2`,`z2`,`time` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>?";
GET_EDITS_POINT = "SELECT `player`,`id`,`time` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?";
// GET_EDITS_POINT = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?";
GET_EDITS = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>?";
GET_EDITS_USER = "SELECT `player`,`id` FROM `" + prefix + "edits` WHERE `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=? AND `time`>? AND `player`=?";
init();
purge((int) TimeUnit.DAYS.toMillis(Settings.HISTORY.DELETE_AFTER_DAYS));
TaskManager.IMP.async(new Runnable() {
@Override
@ -72,6 +75,14 @@ public class RollbackDatabase {
});
}
public void init() {
try (PreparedStatement stmt = connection.prepareStatement(CREATE_TABLE)) {
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void addFinishTask(Runnable run) {
notify.add(run);
}
@ -92,32 +103,40 @@ public class RollbackDatabase {
});
}
public void getPotentialEdits(final int x, final int y, final int z, final RunnableVal<DiskStorageHistory> onEach, final Runnable onFail) {
public void getPotentialEdits(final UUID uuid, final long minTime, final Vector pos1, final Vector pos2, final RunnableVal<DiskStorageHistory> onEach, final Runnable whenDone) {
final World world = FaweAPI.getWorld(this.world);
addTask(new Runnable() {
@Override
public void run() {
try (PreparedStatement stmt = connection.prepareStatement(GET_EDITS_POINT)) {
stmt.setInt(1, x);
stmt.setInt(2, x);
stmt.setInt(3, y);
stmt.setInt(4, y);
stmt.setInt(5, z);
stmt.setInt(6, z);
try (PreparedStatement stmt = connection.prepareStatement(uuid == null ? GET_EDITS : GET_EDITS_USER)) {
stmt.setInt(1, pos1.getBlockX());
stmt.setInt(2, pos2.getBlockX());
stmt.setByte(3, (byte) (pos1.getBlockY() - 128));
stmt.setByte(4, (byte) (pos2.getBlockY() - 128));
stmt.setInt(5, pos1.getBlockZ());
stmt.setInt(6, pos2.getBlockZ());
stmt.setInt(7, (int) (minTime / 1000));
if (uuid != null) {
byte[] uuidBytes = ByteBuffer.allocate(16).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()).array();
stmt.setBytes(8, uuidBytes);
}
ResultSet result = stmt.executeQuery();
if (!result.next()) {
TaskManager.IMP.taskNow(onFail, false);
TaskManager.IMP.taskNow(whenDone, false);
return;
}
do {
byte[] uuid = result.getBytes(1);
byte[] uuidBytes = result.getBytes(1);
int index = result.getInt(2);
long time = 1000l * result.getInt(3);
DiskStorageHistory history = new DiskStorageHistory(world, UUID.nameUUIDFromBytes(uuid), index);
ByteBuffer bb = ByteBuffer.wrap(uuidBytes);
long high = bb.getLong();
long low = bb.getLong();
DiskStorageHistory history = new DiskStorageHistory(world, new UUID(high, low), index);
if (history.getBDFile().exists()) {
onEach.run(history);
}
} while (result.next());
TaskManager.IMP.taskNow(whenDone, false);
} catch (SQLException e) {
e.printStackTrace();
}
@ -125,10 +144,6 @@ public class RollbackDatabase {
});
}
public int getBlocks(int originX, int originZ, int radius, UUID uuid, long timeDiff, RunnableVal<MutablePlayerBlockChange> result) {
return 0;
}
public void logEdit(RollbackOptimizedHistory history) {
historyChanges.add(history);
}

View File

@ -1,4 +0,0 @@
package com.boydti.fawe.logging.rollback;
public class RollbackDatabase {
}

View File

@ -1,5 +1,8 @@
package com.boydti.fawe.logging.rollback;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.sk89q.worldedit.world.World;
import java.io.IOException;
@ -57,6 +60,8 @@ public class RollbackOptimizedHistory extends DiskStorageHistory {
public boolean flush() {
if (super.flush()) {
// Save to DB
RollbackDatabase db = DBHandler.IMP.getDatabase(Fawe.imp().getWorldName(getWorld()));
db.logEdit(this);
return true;
}
return false;

View File

@ -1,24 +1,117 @@
package com.boydti.fawe.object.brush;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.command.tool.brush.Brush;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.change.MutableFullBlockChange;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldVector;
import com.sk89q.worldedit.WorldVectorFace;
import com.sk89q.worldedit.command.tool.BrushTool;
import com.sk89q.worldedit.command.tool.DoubleActionTraceTool;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.world.World;
import java.io.IOException;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public class InspectBrush implements Brush {
public class InspectBrush extends BrushTool implements DoubleActionTraceTool {
private final Player player;
private final int radius;
public InspectBrush(Player player, double radius) {
this.player = player;
this.radius = (int) radius;
/**
* Construct the tool.
*/
public InspectBrush() {
super("worldedit.brush.inspect");
}
@Override
public void build(EditSession editSession, Vector position, Pattern pattern, double size) throws MaxChangedBlocksException {
// TODO
public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
return perform(player, session, false);
}
@Override
public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
return perform(player, session, true);
}
public WorldVector getTarget(Player player, boolean adjacent) {
WorldVector target = null;
int range = this.range > -1 ? getRange() : MAX_RANGE;
if (adjacent) {
WorldVectorFace face = player.getBlockTraceFace(range, true);
return face.getFaceVector();
} else {
return player.getBlockTrace(getRange(), true);
}
}
public boolean perform(final Player player, LocalSession session, boolean rightClick) {
if (!session.isToolControlEnabled() || !player.hasPermission("worldedit.brush.inspect")) {
BBC.NO_PERM.send(player, "worldedit.brush.inspect");
return false;
}
if (!Settings.HISTORY.USE_DATABASE) {
BBC.SETTING_DISABLE.send(player, "history.use-disk");
return false;
}
WorldVector target = getTarget(player, rightClick);
final int x = target.getBlockX();
final int y = target.getBlockY();
final int z = target.getBlockZ();
World world = player.getWorld();
final FawePlayer fp = FawePlayer.wrap(player);
EditSessionBuilder editSession = new EditSessionBuilder(world).player(fp);
String worldName = Fawe.imp().getWorldName(world);
RollbackDatabase db = DBHandler.IMP.getDatabase(worldName);
final AtomicInteger count = new AtomicInteger();
db.getPotentialEdits(null, 0, target, target, new RunnableVal<DiskStorageHistory>() {
@Override
public void run(DiskStorageHistory value) {
try {
Iterator<MutableFullBlockChange> iter = value.getFullBlockIterator(false);
while (iter.hasNext()) {
MutableFullBlockChange change = iter.next();
if (change.x != x || change.y != y || change.z != z) {
continue;
}
int from = change.from;
int to = change.to;
UUID uuid = value.getUUID();
String name = Fawe.imp().getName(uuid);
int index = value.getIndex();
long age = System.currentTimeMillis() - value.getBDFile().lastModified();
String ageFormatted = MainUtil.secToTime(age / 1000);
BBC.TOOL_INSPECT_INFO.send(fp, name, FaweCache.getMaterialName(from), FaweCache.getMaterialName(to), ageFormatted);
count.incrementAndGet();
return;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}, new Runnable() {
@Override
public void run() {
BBC.TOOL_INSPECT_INFO_FOOTER.send(fp, count);
}
});
return true;
}
@Override
public boolean canUse(Actor actor) {
return actor.hasPermission("worldedit.brush.inspect");
}
}

View File

@ -0,0 +1,49 @@
package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.util.ExtentTraverser;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
public class MutableFullBlockChange implements Change {
public int z;
public int y;
public int x;
public int from;
public int to;
public MutableFullBlockChange(int x, int y, int z, int combinedFrom, int combinedTo) {
this.x = x;
this.y = y;
this.z = z;
this.from = combinedFrom;
this.to = combinedTo;
}
@Override
public void undo(UndoContext context) throws WorldEditException {
create(context);
}
@Override
public void redo(UndoContext context) throws WorldEditException {
create(context);
}
public void create(UndoContext context) {
Extent extent = context.getExtent();
ExtentTraverser<FastWorldEditExtent> find = new ExtentTraverser(extent).find(FastWorldEditExtent.class);
if (find != null) {
FastWorldEditExtent fwee = find.get();
fwee.getQueue().setBlock(x, y, z, FaweCache.getId(from), FaweCache.getData(from));
} else {
Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
}
}
}

View File

@ -1,16 +0,0 @@
package com.boydti.fawe.object.change;
import java.util.UUID;
public class MutablePlayerBlockChange extends MutableBlockChange {
private final UUID uuid;
public MutablePlayerBlockChange(UUID uuid, int x, int y, int z, short id, byte data) {
super(x, y, z, id, data);
this.uuid = uuid;
}
public UUID getUIID() {
return uuid;
}
}

View File

@ -1,9 +1,11 @@
package com.boydti.fawe.object.changeset;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.change.MutableBlockChange;
import com.boydti.fawe.object.change.MutableEntityChange;
import com.boydti.fawe.object.change.MutableFullBlockChange;
import com.boydti.fawe.object.change.MutableTileChange;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.jnbt.CompoundTag;
@ -226,6 +228,65 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
};
}
public Iterator<MutableFullBlockChange> getFullBlockIterator(final boolean dir) throws IOException {
final FaweInputStream is = new FaweInputStream(getBlockIS());
if (is == null) {
return new ArrayList<MutableFullBlockChange>().iterator();
}
final MutableFullBlockChange change = new MutableFullBlockChange(0, 0, 0, 0, 0);
return new Iterator<MutableFullBlockChange>() {
private MutableFullBlockChange last = read();
public MutableFullBlockChange read() {
try {
int read0 = is.read();
if (read0 == -1) {
return null;
}
int x = ((byte) read0 & 0xFF) + ((byte) is.read() << 8) + originX;
int z = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8) + originZ;
int y = is.read() & 0xff;
change.x = x;
change.y = y;
change.z = z;
change.from = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
change.to = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
return change;
} catch (Exception ignoreEOF) {
MainUtil.handleError(ignoreEOF);
}
return null;
}
@Override
public boolean hasNext() {
if (last == null) {
last = read();
}
if (last != null) {
return true;
}
try {
is.close();
} catch (IOException e) {
MainUtil.handleError(e);
}
return false;
}
@Override
public MutableFullBlockChange next() {
MutableFullBlockChange tmp = last;
last = null;
return tmp;
}
@Override
public void remove() {
throw new IllegalArgumentException("CANNOT REMOVE");
}
};
}
public Iterator<MutableEntityChange> getEntityIterator(final NBTInputStream is, final boolean create, final boolean dir) {
if (is == null) {
return new ArrayList<MutableEntityChange>().iterator();

View File

@ -33,7 +33,16 @@ public class ProcessedWEExtent extends FaweRegionExtent {
if (!limit.MAX_ENTITIES() || entity == null) {
return null;
}
return super.createEntity(location, entity);
if (WEManager.IMP.maskContains(this.mask, location.getBlockX(), location.getBlockZ())) {
if (!limit.MAX_CHANGES()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
return null;
}
return super.createEntity(location, entity);
} else if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return null;
}
@Override

View File

@ -22,7 +22,7 @@ public class WEManager {
public final ArrayDeque<FaweMaskManager> managers = new ArrayDeque<>();
public void cancelEdit(Extent parent, BBC reason) throws WorldEditException {
public void cancelEditSafe(Extent parent, BBC reason) throws FaweException {
try {
final Field field = AbstractDelegateExtent.class.getDeclaredField("extent");
field.setAccessible(true);
@ -33,6 +33,10 @@ public class WEManager {
throw new FaweException(reason);
}
public void cancelEdit(Extent parent, BBC reason) throws WorldEditException {
cancelEditSafe(parent, reason);
}
public boolean maskContains(final HashSet<RegionWrapper> mask, final int x, final int z) {
for (final RegionWrapper region : mask) {
if ((x >= region.minX) && (x <= region.maxX) && (z >= region.minZ) && (z <= region.maxZ)) {

View File

@ -27,7 +27,6 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.CommandBrush;
import com.boydti.fawe.object.brush.CopyBrush;
import com.boydti.fawe.object.brush.HeightBrush;
import com.boydti.fawe.object.brush.InspectBrush;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions;
@ -80,24 +79,6 @@ public class BrushCommands {
this.worldEdit = worldEdit;
}
@Command(
aliases = { "inspect", "i" },
usage = "<radius>",
desc = "Inspect edits within a radius",
help =
"Chooses the inspect brush",
min = 0,
max = 1
)
@CommandPermissions("worldedit.brush.inspect")
public void inspectBrush(Player player, LocalSession session, EditSession editSession, @Optional("1") double radius) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
BrushTool tool = session.getBrushTool(player.getItemInHand());
tool.setSize(radius);
tool.setBrush(new InspectBrush(player, radius), "worldedit.brush.inspect");
BBC.BRUSH_INSPECT.send(player, radius);
}
@Command(
aliases = { "sphere", "s" },
usage = "<pattern> [radius]",

View File

@ -19,12 +19,22 @@
package com.sk89q.worldedit.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.WorldVector;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.world.World;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
@ -45,6 +55,34 @@ public class HistoryCommands {
this.worldEdit = worldEdit;
}
@Command(
aliases = { "/frb", "frb", "fawerollback", "/fawerollback" },
usage = "<user> <radius> <time>",
desc = "Undo edits within a radius",
min = 3,
max = 3
)
@CommandPermissions("worldedit.history.undo")
public void faweRollback(Player player, LocalSession session, String user, int radius, String time) throws WorldEditException {
UUID other = Fawe.imp().getUUID(user);
if (other == null) {
BBC.PLAYER_NOT_FOUND.send(player, user);
return;
}
long timeDiff = MainUtil.timeToSec(time) * 1000;
if (timeDiff == 0) {
BBC.COMMAND_SYNTAX.send(player, "/frb " + user + " " + radius + " <time>");
return;
}
radius = Math.max(Math.min(500, radius), 0);
World world = player.getWorld();
WorldVector origin = player.getPosition();
Vector bot = origin.subtract(radius, radius, radius);
Vector top = origin.add(radius, radius, radius);
// TODO
}
@Command(
aliases = { "/undo", "undo" },
usage = "[times] [player]",

View File

@ -0,0 +1,215 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.command;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.brush.InspectBrush;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.ItemType;
import com.sk89q.worldedit.command.tool.BlockDataCyler;
import com.sk89q.worldedit.command.tool.BlockReplacer;
import com.sk89q.worldedit.command.tool.DistanceWand;
import com.sk89q.worldedit.command.tool.FloatingTreeRemover;
import com.sk89q.worldedit.command.tool.FloodFillTool;
import com.sk89q.worldedit.command.tool.LongRangeBuildTool;
import com.sk89q.worldedit.command.tool.QueryTool;
import com.sk89q.worldedit.command.tool.TreePlanter;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.patterns.Pattern;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.util.command.parametric.Optional;
public class ToolCommands {
private final WorldEdit we;
public ToolCommands(WorldEdit we) {
this.we = we;
}
@Command(
aliases = { "inspect", "i" },
usage = "",
desc = "Inspect edits within a radius",
help =
"Chooses the inspect brush",
min = 0,
max = 0
)
@CommandPermissions("worldedit.brush.inspect")
public void inspectBrush(Player player, LocalSession session, EditSession editSession, @Optional("1") double radius) throws WorldEditException {
session.setTool(player.getItemInHand(), new InspectBrush());
BBC.TOOL_INSPECT.send(player, ItemType.toHeldName(player.getItemInHand()));
}
@Command(
aliases = { "none" },
usage = "",
desc = "Unbind a bound tool from your current item",
min = 0,
max = 0
)
public void none(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
session.setTool(player.getItemInHand(), null);
BBC.TOOL_NONE.send(player);
}
@Command(
aliases = { "info" },
usage = "",
desc = "Block information tool",
min = 0,
max = 0
)
@CommandPermissions("worldedit.tool.info")
public void info(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
session.setTool(player.getItemInHand(), new QueryTool());
BBC.TOOL_INFO.send(player, ItemType.toHeldName(player.getItemInHand()));
}
@Command(
aliases = { "tree" },
usage = "[type]",
desc = "Tree generator tool",
min = 0,
max = 1
)
@CommandPermissions("worldedit.tool.tree")
@SuppressWarnings("deprecation")
public void tree(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
TreeGenerator.TreeType type = args.argsLength() > 0 ?
type = TreeGenerator.lookup(args.getString(0))
: TreeGenerator.TreeType.TREE;
if (type == null) {
BBC.TOOL_TREE_ERROR.send(player, args.getString(0));
return;
}
session.setTool(player.getItemInHand(), new TreePlanter(new TreeGenerator(type)));
BBC.TOOL_TREE.send(player, ItemType.toHeldName(player.getItemInHand()));
}
@Command(
aliases = { "repl" },
usage = "<block>",
desc = "Block replacer tool",
min = 1,
max = 1
)
@CommandPermissions("worldedit.tool.replacer")
public void repl(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
BaseBlock targetBlock = we.getBlock(player, args.getString(0));
session.setTool(player.getItemInHand(), new BlockReplacer(targetBlock));
BBC.TOOL_REPL.send(player, ItemType.toHeldName(player.getItemInHand()));
}
@Command(
aliases = { "cycler" },
usage = "",
desc = "Block data cycler tool",
min = 0,
max = 0
)
@CommandPermissions("worldedit.tool.data-cycler")
public void cycler(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
session.setTool(player.getItemInHand(), new BlockDataCyler());
BBC.TOOL_CYCLER.send(player, ItemType.toHeldName(player.getItemInHand()));
}
@Command(
aliases = { "floodfill", "flood" },
usage = "<pattern> <range>",
desc = "Flood fill tool",
min = 2,
max = 2
)
@CommandPermissions("worldedit.tool.flood-fill")
public void floodFill(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
LocalConfiguration config = we.getConfiguration();
int range = args.getInteger(1);
if (range > config.maxSuperPickaxeSize) {
BBC.TOOL_FLOOD_FILL_RANGE_ERROR.send(player, config.maxSuperPickaxeSize);
return;
}
Pattern pattern = we.getBlockPattern(player, args.getString(0));
session.setTool(player.getItemInHand(), new FloodFillTool(range, pattern));
BBC.TOOL_FLOOD_FILL.send(player, ItemType.toHeldName(player.getItemInHand()));
}
@Command(
aliases = { "deltree" },
usage = "",
desc = "Floating tree remover tool",
min = 0,
max = 0
)
@CommandPermissions("worldedit.tool.deltree")
public void deltree(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
session.setTool(player.getItemInHand(), new FloatingTreeRemover());
BBC.TOOL_DELTREE.send(player, ItemType.toHeldName(player.getItemInHand()));
}
@Command(
aliases = { "farwand" },
usage = "",
desc = "Wand at a distance tool",
min = 0,
max = 0
)
@CommandPermissions("worldedit.tool.farwand")
public void farwand(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
session.setTool(player.getItemInHand(), new DistanceWand());
BBC.TOOL_FARWAND.send(player, ItemType.toHeldName(player.getItemInHand()));
}
@Command(
aliases = { "lrbuild", "/lrbuild" },
usage = "<leftclick block> <rightclick block>",
desc = "Long-range building tool",
min = 2,
max = 2
)
@CommandPermissions("worldedit.tool.lrbuild")
public void longrangebuildtool(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
BaseBlock secondary = we.getBlock(player, args.getString(0));
BaseBlock primary = we.getBlock(player, args.getString(1));
session.setTool(player.getItemInHand(), new LongRangeBuildTool(primary, secondary));
BBC.TOOL_LRBUILD_BOUND.send(player, ItemType.toHeldName(player.getItemInHand()));
BBC.TOOL_LRBUILD_INFO.send(player, ItemType.toName(secondary.getType()), ItemType.toName(primary.getType()));
}
public static Class<?> inject() {
return ToolCommands.class;
}
}