Fixes #305 //sel extend bug
Translate a few messages
Add option `store-redo: true`, disabling will result in smaller history
but cannot use redo
Add option `small-edits: false` will reduce history by assuming edits <
4096x256x4096
Change FaweStreamChangeSet to use new settings, but be backwards
compatible (uses mode byte)
Add auto generated semantic version (may not be accurate)
Add recursive brush
Optimize undo/redo using mutable change objects
Tweak several classes to implement HasFaweQueue
Fix wrong cancel message for entity/biome changes
Optimized offset mask
Fixed masks not being reset after edit
Added interface ResettableMask used to reset masks after being used for
an EditSession
Added MaskTraverser for traversing and resetting inherited masks after
use
Added angle mask \#,#
Added id,data,combined mask #id #data #iddata (Must match starting block
id/data)
Added x,y,z axis mask #xaxis #yaxis #zaxis
Add mode 4 FAWE format (short location prefix + no combined from bytes)
Fix `/frb` command not respecting regions
Tweak permission for `/frb` -> worldedit.history.rollback
Translated ToolUtil commands
This commit is contained in:
Jesse Boyd 2016-09-23 17:11:41 +10:00
parent b2ef1cbc50
commit 2cb1485553
43 changed files with 1746 additions and 272 deletions

View File

@ -18,20 +18,48 @@ group = 'com.boydti.fawe'
def revision = "" def revision = ""
def buildNumber = "" def buildNumber = ""
final def date = new Date().format("yy.MM.dd") def semver = ""
def date = ""
ext { ext {
git = org.ajoberstar.grgit.Grgit.open(file(".git")) git = org.ajoberstar.grgit.Grgit.open(file(".git"))
date = git.head().date.format("yy.MM.dd")
revision = "-${git.head().abbreviatedId}" revision = "-${git.head().abbreviatedId}"
parents = git.head().parentIds; parents = git.head().parentIds;
index = -45; // Offset to mach CI index = -45; // Offset to mach CI
int major, minor, patch;
major = minor = patch = 0;
for (;parents != null && !parents.isEmpty();index++) { for (;parents != null && !parents.isEmpty();index++) {
int majorCount, minorCount, patchCount;
patchCount = 1;
commit = git.getResolve().toCommit(parents.get(0)); commit = git.getResolve().toCommit(parents.get(0));
parents = commit.getParentIds(); for (String line : commit.fullMessage.tokenize("\n")) {
switch (line.replaceAll("- ", "").split(" ")[0].toLowerCase()) {
case "minor":
case "added":
case "add":
case "change":
case "changed":
case "changes":
if (majorCount == 0) {minorCount = 1; patch = patchCount = 0;}
break;
case "refactor":
case "remove":
case "major":
patch = minor = minorCount = patchCount = 0;
majorCount = 1;
break;
}
}
major += majorCount;
minor += minorCount;
patch += patchCount;
parents = commit.getParentIds()
} }
buildNumber = "-${index}" buildNumber = "-${index}"
semver = "-${major}.${minor}.${patch}"
} }
version = date + revision + buildNumber version = date + revision + buildNumber + semver
if ( project.hasProperty("lzNoVersion") ) { // gradle build -PlzNoVersion if ( project.hasProperty("lzNoVersion") ) { // gradle build -PlzNoVersion
version = "unknown"; version = "unknown";
} }

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.bukkit.wrapper;
import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.StringMan;
@ -57,7 +58,7 @@ import org.bukkit.util.Vector;
* @see #wrap(org.bukkit.World) * @see #wrap(org.bukkit.World)
* @see #create(org.bukkit.WorldCreator) * @see #create(org.bukkit.WorldCreator)
*/ */
public class AsyncWorld implements World { public class AsyncWorld implements World, HasFaweQueue {
private World parent; private World parent;
private FaweQueue queue; private FaweQueue queue;

View File

@ -33,10 +33,12 @@ import com.sk89q.worldedit.command.RegionCommands;
import com.sk89q.worldedit.command.SchematicCommands; import com.sk89q.worldedit.command.SchematicCommands;
import com.sk89q.worldedit.command.ScriptingCommands; import com.sk89q.worldedit.command.ScriptingCommands;
import com.sk89q.worldedit.command.ToolCommands; import com.sk89q.worldedit.command.ToolCommands;
import com.sk89q.worldedit.command.ToolUtilCommands;
import com.sk89q.worldedit.command.composition.SelectionCommand; import com.sk89q.worldedit.command.composition.SelectionCommand;
import com.sk89q.worldedit.command.tool.LongRangeBuildTool; import com.sk89q.worldedit.command.tool.LongRangeBuildTool;
import com.sk89q.worldedit.command.tool.brush.GravityBrush; import com.sk89q.worldedit.command.tool.brush.GravityBrush;
import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
import com.sk89q.worldedit.extension.platform.CommandManager; import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.extension.platform.PlatformManager;
import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.AbstractDelegateExtent;
@ -48,6 +50,8 @@ import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.entity.ExtentEntityCopy; import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
import com.sk89q.worldedit.function.mask.BlockMask; import com.sk89q.worldedit.function.mask.BlockMask;
import com.sk89q.worldedit.function.mask.FuzzyBlockMask; import com.sk89q.worldedit.function.mask.FuzzyBlockMask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.mask.OffsetMask;
import com.sk89q.worldedit.function.mask.SolidBlockMask; import com.sk89q.worldedit.function.mask.SolidBlockMask;
import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.pattern.ClipboardPattern; import com.sk89q.worldedit.function.pattern.ClipboardPattern;
@ -337,6 +341,7 @@ public class Fawe {
HistoryCommands.inject(); // Translations HistoryCommands.inject(); // Translations
NavigationCommands.inject(); // Translations + thru fix NavigationCommands.inject(); // Translations + thru fix
ParametricBuilder.inject(); // Translations ParametricBuilder.inject(); // Translations
ToolUtilCommands.inject(); // Fixes + Translations
// Schematic // Schematic
SchematicReader.inject(); SchematicReader.inject();
SchematicWriter.inject(); SchematicWriter.inject();
@ -379,6 +384,9 @@ public class Fawe {
BlockMask.inject(); // Optimizations BlockMask.inject(); // Optimizations
SolidBlockMask.inject(); // Optimizations SolidBlockMask.inject(); // Optimizations
FuzzyBlockMask.inject(); // Optimizations FuzzyBlockMask.inject(); // Optimizations
OffsetMask.inject(); // Optimizations
DefaultMaskParser.inject(); // Add new masks
Masks.inject(); //
// Operations // Operations
Operations.inject(); // Optimizations Operations.inject(); // Optimizations
// BlockData // BlockData

View File

@ -1,7 +1,7 @@
package com.boydti.fawe; package com.boydti.fawe;
public class FaweVersion { public class FaweVersion {
public final int year, month, day, hash, build; public final int year, month, day, hash, build, major, minor, patch;
public FaweVersion(String version) { public FaweVersion(String version) {
String[] split = version.substring(version.indexOf('=') + 1).split("-"); String[] split = version.substring(version.indexOf('=') + 1).split("-");
@ -11,6 +11,10 @@ public class FaweVersion {
this.day = Integer.parseInt(date[2]); this.day = Integer.parseInt(date[2]);
this.hash = Integer.parseInt(split[1], 16); this.hash = Integer.parseInt(split[1], 16);
this.build = Integer.parseInt(split[2]); this.build = Integer.parseInt(split[2]);
String[] semver = split[3].split("\\.");
this.major = Integer.parseInt(semver[0]);
this.minor = Integer.parseInt(semver[1]);
this.patch = Integer.parseInt(semver[2]);
} }
@Override @Override

View File

@ -95,7 +95,11 @@ public enum BBC {
BRUSH_BLEND_BALL("Blend ball brush equipped (%s0).", "WorldEdit.Brush"), BRUSH_BLEND_BALL("Blend ball brush equipped (%s0).", "WorldEdit.Brush"),
BRUSH_ERODE("Erode brush equipped (%s0).", "WorldEdit.Brush"), BRUSH_ERODE("Erode brush equipped (%s0).", "WorldEdit.Brush"),
BRUSH_PASTE_NONE("Nothing to paste", "WorldEdit.Brush"), BRUSH_PASTE_NONE("Nothing to paste", "WorldEdit.Brush"),
BRUSH_SIZE("Brush size set", "WorldEdit.Brush"),
BRUSH_RANGE("Brush size set", "WorldEdit.Brush"),
BRUSH_MASK_DISABLED("Brush mask disabled", "WorldEdit.Brush"),
BRUSH_MASK("Brush mask set", "WorldEdit.Brush"),
BRUSH_MATERIAL("Brush material set", "WorldEdit.Brush"),
ROLLBACK_ELEMENT("Undoing %s0", "WorldEdit.Rollback"), ROLLBACK_ELEMENT("Undoing %s0", "WorldEdit.Rollback"),
@ -114,6 +118,8 @@ public enum BBC {
TOOL_FARWAND("Far wand 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_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"), TOOL_LRBUILD_INFO("Left-click set to %s0; right-click set to %s1.", "WorldEdit.Tool"),
SUPERPICKAXE_ENABLED("Super Pickaxe enabled.", "WorldEdit.Tool"),
SUPERPICKAXE_DISABLED("Super Pickaxe disabled.", "WorldEdit.Tool"),
SCHEMATIC_DELETE("%s0 has been deleted.", "Worldedit.Schematic"), SCHEMATIC_DELETE("%s0 has been deleted.", "Worldedit.Schematic"),

View File

@ -154,6 +154,17 @@ public class Settings extends Config {
" - Use of the FAWE API will not be effected" " - Use of the FAWE API will not be effected"
}) })
public static boolean ENABLE_FOR_CONSOLE = true; public static boolean ENABLE_FOR_CONSOLE = true;
@Comment({
"Should redo information be stored:",
" - History is about 20% larger",
" - Enables use of /redo",
})
public static boolean STORE_REDO = true;
@Comment({
"Assumes all edits are smaller than 4096x256x4096:",
" - Reduces history size by ~10%",
})
public static boolean SMALL_EDITS = false;
} }
public static class QUEUE { public static class QUEUE {

View File

@ -6,6 +6,7 @@ import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID; import java.util.UUID;
public class RollbackOptimizedHistory extends DiskStorageHistory { public class RollbackOptimizedHistory extends DiskStorageHistory {
@ -101,13 +102,13 @@ public class RollbackOptimizedHistory extends DiskStorageHistory {
} }
@Override @Override
public void writeHeader(int x, int y, int z) throws IOException { public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
minX = x; minX = x;
maxX = x; maxX = x;
minY = y; minY = y;
maxY = y; maxY = y;
minZ = z; minZ = z;
maxZ = z; maxZ = z;
super.writeHeader(x, y, z); super.writeHeader(os, x, y, z);
} }
} }

View File

@ -76,6 +76,26 @@ public class FaweLimit {
return MAX_ENTITIES-- > 0; return MAX_ENTITIES-- > 0;
} }
public boolean isUnlimited() {
return MAX_CHANGES == Integer.MAX_VALUE &&
MAX_FAILS == Integer.MAX_VALUE &&
MAX_CHECKS == Integer.MAX_VALUE &&
MAX_ITERATIONS == Integer.MAX_VALUE &&
MAX_BLOCKSTATES == Integer.MAX_VALUE &&
MAX_ENTITIES == Integer.MAX_VALUE &&
MAX_HISTORY == Integer.MAX_VALUE;
}
public void set(FaweLimit limit) {
MAX_ACTIONS = limit.MAX_ACTIONS;
MAX_CHANGES = limit.MAX_CHANGES;
MAX_BLOCKSTATES = limit.MAX_BLOCKSTATES;
MAX_CHECKS = limit.MAX_CHECKS;
MAX_ENTITIES = limit.MAX_ENTITIES;
MAX_FAILS = limit.MAX_FAILS;
MAX_ITERATIONS = limit.MAX_ITERATIONS;
MAX_HISTORY = limit.MAX_HISTORY;
}
public FaweLimit copy() { public FaweLimit copy() {
FaweLimit limit = new FaweLimit(); FaweLimit limit = new FaweLimit();

View File

@ -145,7 +145,7 @@ public abstract class FawePlayer<T> {
ConcurrentLinkedDeque<Runnable> adder = getMeta("fawe_action_v2"); ConcurrentLinkedDeque<Runnable> adder = getMeta("fawe_action_v2");
if (adder == null) { if (adder == null) {
adder = new ConcurrentLinkedDeque(); adder = new ConcurrentLinkedDeque();
ConcurrentLinkedDeque<Runnable> previous = (ConcurrentLinkedDeque<Runnable>) setMeta("fawe_action_v2", adder); ConcurrentLinkedDeque<Runnable> previous = (ConcurrentLinkedDeque<Runnable>) getAndSetMeta("fawe_action_v2", adder);
if (previous != null) { if (previous != null) {
setMeta("fawe_action_v2", adder = previous); setMeta("fawe_action_v2", adder = previous);
} }
@ -397,11 +397,18 @@ public abstract class FawePlayer<T> {
* @param value * @param value
* @return previous value * @return previous value
*/ */
public Object setMeta(String key, Object value) { public void setMeta(String key, Object value) {
if (this.meta == null) { if (this.meta == null) {
this.meta = new ConcurrentHashMap<>(8, 0.9f, 1); this.meta = new ConcurrentHashMap<>(8, 0.9f, 1);
} }
return this.meta.put(key, value); this.meta.put(key, value);
}
public <T> T getAndSetMeta(String key, T value) {
if (this.meta == null) {
this.meta = new ConcurrentHashMap<>(8, 0.9f, 1);
}
return (T) this.meta.put(key, value);
} }
/** /**

View File

@ -0,0 +1,5 @@
package com.boydti.fawe.object;
public interface HasFaweQueue {
FaweQueue getQueue();
}

View File

@ -0,0 +1,56 @@
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.blocks.BaseBlock;
import com.sk89q.worldedit.command.tool.BrushTool;
import com.sk89q.worldedit.command.tool.brush.Brush;
import com.sk89q.worldedit.function.block.BlockReplace;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
public class RecurseBrush implements Brush {
private final BrushTool tool;
public RecurseBrush(BrushTool tool) {
this.tool = tool;
}
@Override
public void build(final EditSession editSession, final Vector position, Pattern to, double size) throws MaxChangedBlocksException {
Mask mask = tool.getMask();
if (mask == null) {
mask = Masks.alwaysTrue();
}
final int radius = (int) size;
BaseBlock block = editSession.getBlock(position);
if (block.getId() == 0) {
return;
}
final BlockReplace replace = new BlockReplace(editSession, to);
editSession.setMask((Mask) null);
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace) {
@Override
public boolean isVisitable(Vector from, Vector to) {
if (super.isVisitable(from, to)) {
int dx = Math.abs((int) (position.x - to.x));
if (dx > radius) return false;
int dz = Math.abs((int) (position.z - to.z));
if (dz > radius) return false;
int dy = Math.abs((int) (position.y - to.y));
if (dy > radius) return false;
return true;
} else {
return false;
}
}
};
visitor.visit(position);
Operations.completeBlindly(visitor);
}
}

View File

@ -1,8 +1,8 @@
package com.boydti.fawe.object.change; package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext; import com.sk89q.worldedit.history.UndoContext;
@ -35,14 +35,21 @@ public class MutableBlockChange implements Change {
create(context); create(context);
} }
private FaweQueue queue;
private boolean checkedQueue;
public void create(UndoContext context) { public void create(UndoContext context) {
Extent extent = context.getExtent(); if (queue != null) {
ExtentTraverser<FastWorldEditExtent> find = new ExtentTraverser(extent).find(FastWorldEditExtent.class); queue.setBlock(x, y, z, id, data);
if (find != null) { }
FastWorldEditExtent fwee = find.get(); if (!checkedQueue) {
fwee.getQueue().setBlock(x, y, z, id, data); checkedQueue = true;
} else { Extent extent = context.getExtent();
Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)"); if (extent instanceof HasFaweQueue) {
(queue = ((HasFaweQueue) extent).getQueue()).setBlock(x, y, z, id, data);
} else {
Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
}
} }
} }
} }

View File

@ -2,8 +2,8 @@ package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext; import com.sk89q.worldedit.history.UndoContext;
@ -29,18 +29,29 @@ public class MutableChunkChange implements Change {
create(context, false); create(context, false);
} }
private FaweQueue queue;
private boolean checkedQueue;
public void create(UndoContext context, boolean undo) { public void create(UndoContext context, boolean undo) {
Extent extent = context.getExtent(); if (queue != null) {
ExtentTraverser<FastWorldEditExtent> find = new ExtentTraverser(extent).find(FastWorldEditExtent.class); perform(queue, undo);
if (find != null) { }
FastWorldEditExtent fwee = find.get(); if (!checkedQueue) {
if (undo) { checkedQueue = true;
fwee.getQueue().setChunk(from); Extent extent = context.getExtent();
if (extent instanceof HasFaweQueue) {
perform(queue = ((HasFaweQueue) extent).getQueue(), undo);
} else { } else {
fwee.getQueue().setChunk(to); Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
} }
}
}
public void perform(FaweQueue queue, boolean undo) {
if (undo) {
queue.setChunk(from);
} else { } else {
Fawe.debug("FAWE doesn't support: " + context + " for " + getClass()); queue.setChunk(to);
} }
} }
} }

View File

@ -1,6 +1,8 @@
package com.boydti.fawe.object.change; package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.ExtentTraverser;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
@ -72,19 +74,30 @@ public class MutableEntityChange implements Change {
} }
} }
private FaweQueue queue;
private boolean checkedQueue;
public void create(UndoContext context) { public void create(UndoContext context) {
Extent extent = context.getExtent(); if (queue != null) {
ExtentTraverser<FastWorldEditExtent> find = new ExtentTraverser(extent).find(FastWorldEditExtent.class); perform(queue);
if (find != null) { }
FastWorldEditExtent fwee = find.get(); if (!checkedQueue) {
Map<String, Tag> map = tag.getValue(); checkedQueue = true;
List<DoubleTag> pos = (List<DoubleTag>) map.get("Pos").getValue(); Extent extent = context.getExtent();
int x = (int) Math.round(pos.get(0).getValue()); if (extent instanceof HasFaweQueue) {
int y = (int) Math.round(pos.get(1).getValue()); perform(queue = ((HasFaweQueue) extent).getQueue());
int z = (int) Math.round(pos.get(2).getValue()); } else {
fwee.getQueue().setEntity(x, y, z, tag); Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
} else { }
Fawe.debug("FAWE doesn't support: " + context + " for " + getClass() + " (bug Empire92)");
} }
} }
public void perform(FaweQueue queue) {
Map<String, Tag> map = tag.getValue();
List<DoubleTag> pos = (List<DoubleTag>) map.get("Pos").getValue();
int x = (int) Math.round(pos.get(0).getValue());
int y = (int) Math.round(pos.get(1).getValue());
int z = (int) Math.round(pos.get(2).getValue());
queue.setEntity(x, y, z, tag);
}
} }

View File

@ -2,8 +2,8 @@ package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext; import com.sk89q.worldedit.history.UndoContext;
@ -36,14 +36,25 @@ public class MutableFullBlockChange implements Change {
create(context); create(context);
} }
private FaweQueue queue;
private boolean checkedQueue;
public void create(UndoContext context) { public void create(UndoContext context) {
Extent extent = context.getExtent(); if (queue != null) {
ExtentTraverser<FastWorldEditExtent> find = new ExtentTraverser(extent).find(FastWorldEditExtent.class); perform(queue);
if (find != null) { }
FastWorldEditExtent fwee = find.get(); if (!checkedQueue) {
fwee.getQueue().setBlock(x, y, z, FaweCache.getId(from), FaweCache.getData(from)); checkedQueue = true;
} else { Extent extent = context.getExtent();
Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)"); if (extent instanceof HasFaweQueue) {
perform(queue = ((HasFaweQueue) extent).getQueue());
} else {
Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
}
} }
} }
public void perform(FaweQueue queue) {
queue.setBlock(x, y, z, FaweCache.getId(from), FaweCache.getData(from));
}
} }

View File

@ -1,8 +1,8 @@
package com.boydti.fawe.object.change; package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
@ -36,18 +36,29 @@ public class MutableTileChange implements Change {
} }
} }
private FaweQueue queue;
private boolean checkedQueue;
public void create(UndoContext context) { public void create(UndoContext context) {
Extent extent = context.getExtent(); if (queue != null) {
ExtentTraverser<FastWorldEditExtent> find = new ExtentTraverser(extent).find(FastWorldEditExtent.class); perform(queue);
if (find != null) { }
FastWorldEditExtent fwee = find.get(); if (!checkedQueue) {
Map<String, Tag> map = tag.getValue(); checkedQueue = true;
int x = ((IntTag) map.get("x")).getValue(); Extent extent = context.getExtent();
int y = ((IntTag) map.get("y")).getValue(); if (extent instanceof HasFaweQueue) {
int z = ((IntTag) map.get("z")).getValue(); perform(queue = ((HasFaweQueue) extent).getQueue());
fwee.getQueue().setTile(x, y, z, tag); } else {
} else { Fawe.debug("FAWE doesn't support: " + extent + " for " + getClass() + " (bug Empire92)");
Fawe.debug("FAWE doesn't support: " + context + " for " + getClass()); }
} }
} }
public void perform(FaweQueue queue) {
Map<String, Tag> map = tag.getValue();
int x = ((IntTag) map.get("x")).getValue();
int y = ((IntTag) map.get("y")).getValue();
int z = ((IntTag) map.get("z")).getValue();
queue.setTile(x, y, z, tag);
}
} }

View File

@ -199,26 +199,11 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (osBD != null) { if (osBD != null) {
return osBD; return osBD;
} }
writeHeader(x, y, z);
return osBD;
}
public void writeHeader(int x, int y, int z) throws IOException {
bdFile.getParentFile().mkdirs(); bdFile.getParentFile().mkdirs();
bdFile.createNewFile(); bdFile.createNewFile();
osBD = getCompressedOS(new FileOutputStream(bdFile)); osBD = getCompressedOS(new FileOutputStream(bdFile));
// Mode writeHeader(osBD, x, y, z);
osBD.write((byte) MODE); return osBD;
// Origin
setOrigin(x, z);
osBD.write((byte) (x >> 24));
osBD.write((byte) (x >> 16));
osBD.write((byte) (x >> 8));
osBD.write((byte) (x));
osBD.write((byte) (z >> 24));
osBD.write((byte) (z >> 16));
osBD.write((byte) (z >> 8));
osBD.write((byte) (z));
} }
@Override @Override
@ -271,12 +256,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
return null; return null;
} }
InputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile)); InputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile));
// skip mode readHeader(is);
is.skip(1);
// origin
int x = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
setOrigin(x, z);
return is; return is;
} }

View File

@ -8,11 +8,13 @@ import com.boydti.fawe.object.change.MutableEntityChange;
import com.boydti.fawe.object.change.MutableFullBlockChange; import com.boydti.fawe.object.change.MutableFullBlockChange;
import com.boydti.fawe.object.change.MutableTileChange; import com.boydti.fawe.object.change.MutableTileChange;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -21,18 +23,197 @@ import java.util.Iterator;
public abstract class FaweStreamChangeSet extends FaweChangeSet { public abstract class FaweStreamChangeSet extends FaweChangeSet {
public static final int MODE = 3;
public static final int HEADER_SIZE = 9; public static final int HEADER_SIZE = 9;
private int mode;
private final int compression; private final int compression;
private FaweStreamIdDelegate idDel;
private FaweStreamPositionDelegate posDel;
public FaweStreamChangeSet(World world) { public FaweStreamChangeSet(World world) {
this(world, Settings.HISTORY.COMPRESSION_LEVEL); this(world, Settings.HISTORY.COMPRESSION_LEVEL, Settings.HISTORY.STORE_REDO, Settings.HISTORY.SMALL_EDITS);
} }
public FaweStreamChangeSet(World world, int compression) { public FaweStreamChangeSet(World world, int compression, boolean storeRedo, boolean smallLoc) {
super(world); super(world);
this.compression = compression; this.compression = compression;
if (storeRedo) {
if (smallLoc) {
mode = 4;
} else {
mode = 3;
}
} else if (smallLoc) {
mode = 1;
} else {
mode = 2;
}
}
public interface FaweStreamPositionDelegate {
void write(OutputStream out, int x, int y, int z) throws IOException;
int readX(InputStream in) throws IOException;
int readY(InputStream in) throws IOException;
int readZ(InputStream in) throws IOException;
}
public interface FaweStreamIdDelegate {
void writeChange(OutputStream out, int from, int to) throws IOException;
void readCombined(InputStream in, MutableBlockChange change, boolean dir) throws IOException;
void readCombined(InputStream in, MutableFullBlockChange change, boolean dir) throws IOException;
}
private void setupStreamDelegates(int mode) {
this.mode = mode;
if (mode == 3 || mode == 4) {
idDel = new FaweStreamIdDelegate() {
@Override
public void writeChange(OutputStream stream, int combinedFrom, int combinedTo) throws IOException {
stream.write((combinedFrom) & 0xff);
stream.write(((combinedFrom) >> 8) & 0xff);
stream.write((combinedTo) & 0xff);
stream.write(((combinedTo) >> 8) & 0xff);
}
@Override
public void readCombined(InputStream is, MutableBlockChange change, boolean dir) throws IOException {
if (dir) {
is.skip(2);
int to1 = is.read();
int to2 = is.read();
change.id = (short) ((to2 << 4) + (to1 >> 4));
change.data = (byte) (to1 & 0xf);
} else {
int from1 = is.read();
int from2 = is.read();
is.skip(2);
change.id = (short) ((from2 << 4) + (from1 >> 4));
change.data = (byte) (from1 & 0xf);
}
}
@Override
public void readCombined(InputStream is, MutableFullBlockChange change, boolean dir) throws IOException {
change.from = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
change.to = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
}
};
} else {
idDel = new FaweStreamIdDelegate() {
@Override
public void writeChange(OutputStream stream, int combinedFrom, int to) throws IOException {
stream.write((combinedFrom) & 0xff);
stream.write(((combinedFrom) >> 8) & 0xff);
}
@Override
public void readCombined(InputStream in, MutableBlockChange change, boolean dir) throws IOException {
int from1 = in.read();
int from2 = in.read();
change.id = (short) ((from2 << 4) + (from1 >> 4));
change.data = (byte) (from1 & 0xf);
}
@Override
public void readCombined(InputStream is, MutableFullBlockChange change, boolean dir) throws IOException {
change.from = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8);
change.to = 0;
}
};
}
if (mode == 1 || mode == 4) { // small
posDel = new FaweStreamPositionDelegate() {
@Override
public void write(OutputStream out, int x, int y, int z) throws IOException {
byte b1 = (byte) y;
byte b2 = (byte) (x);
byte b3 = (byte) (z);
int x16 = (x >> 8) & 0xF;
int z16 = (z >> 8) & 0xF;
byte b4 = MathMan.pair16(x16, z16);
out.write(b1);
out.write(b2);
out.write(b3);
out.write(b4);
}
byte[] buffer = new byte[4];
@Override
public int readX(InputStream in) throws IOException {
if (in.read(buffer) == -1) {
throw new EOFException();
}
return (((buffer[1] & 0xFF) + ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20;
}
@Override
public int readY(InputStream in) {
return buffer[0] & 0xFF;
}
@Override
public int readZ(InputStream in) throws IOException {
return (((buffer[2] & 0xFF) + ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20;
}
};
} else {
posDel = new FaweStreamPositionDelegate() {
byte[] buffer = new byte[5];
@Override
public void write(OutputStream stream, int x, int y, int z) throws IOException {
stream.write((x) & 0xff);
stream.write(((x) >> 8) & 0xff);
stream.write((z) & 0xff);
stream.write(((z) >> 8) & 0xff);
stream.write((byte) y);
}
@Override
public int readX(InputStream is) throws IOException {
if (is.read(buffer) == -1) {
throw new EOFException();
}
return (buffer[0] & 0xFF) + (buffer[1] << 8);
}
@Override
public int readY(InputStream is) throws IOException {
return buffer[4] & 0xFF;
}
@Override
public int readZ(InputStream is) throws IOException {
return (buffer[2] & 0xFF) + (buffer[3] << 8);
}
};
}
}
public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
os.write(mode);
setOrigin(x, z);
os.write((byte) (x >> 24));
os.write((byte) (x >> 16));
os.write((byte) (x >> 8));
os.write((byte) (x));
os.write((byte) (z >> 24));
os.write((byte) (z >> 16));
os.write((byte) (z >> 8));
os.write((byte) (z));
setupStreamDelegates(mode);
}
public void readHeader(InputStream is) throws IOException {
// skip mode
int mode = is.read();
// origin
int x = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + (is.read() << 0));
setOrigin(x, z);
setupStreamDelegates(mode);
} }
public FaweOutputStream getCompressedOS(OutputStream os) throws IOException { public FaweOutputStream getCompressedOS(OutputStream os) throws IOException {
@ -93,21 +274,8 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
try { try {
OutputStream stream = getBlockOS(x, y, z); OutputStream stream = getBlockOS(x, y, z);
//x //x
x-=originX; posDel.write(stream, x - originX, y, z - originZ);
stream.write((x) & 0xff); idDel.writeChange(stream, combinedFrom, combinedTo);
stream.write(((x) >> 8) & 0xff);
//z
z-=originZ;
stream.write((z) & 0xff);
stream.write(((z) >> 8) & 0xff);
//y
stream.write((byte) y);
//from
stream.write((combinedFrom) & 0xff);
stream.write(((combinedFrom) >> 8) & 0xff);
//to
stream.write((combinedTo) & 0xff);
stream.write(((combinedTo) >> 8) & 0xff);
} }
catch (Throwable e) { catch (Throwable e) {
MainUtil.handleError(e); MainUtil.handleError(e);
@ -172,43 +340,25 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
private MutableBlockChange last = read(); private MutableBlockChange last = read();
public MutableBlockChange read() { public MutableBlockChange read() {
try { try {
int read0 = is.read(); change.x = posDel.readX(is) + originX;
if (read0 == -1) { change.y = posDel.readY(is);
return null; change.z = posDel.readZ(is) + originZ;
} idDel.readCombined(is, change, dir);
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;
if (dir) {
is.skip(2);
int to1 = is.read();
int to2 = is.read();
change.id = (short) ((to2 << 4) + (to1 >> 4));
change.data = (byte) (to1 & 0xf);
} else {
int from1 = is.read();
int from2 = is.read();
is.skip(2);
change.id = (short) ((from2 << 4) + (from1 >> 4));
change.data = (byte) (from1 & 0xf);
}
return change; return change;
} catch (Exception ignoreEOF) { } catch (EOFException ignoreOEF) {
MainUtil.handleError(ignoreEOF); return null;
} catch (Exception e) {
MainUtil.handleError(e);
} }
return null; return null;
} }
@Override @Override
public boolean hasNext() { public boolean hasNext() {
if (last == null) {
last = read();
}
if (last != null) { if (last != null) {
return true; return true;
} else if ((last = read()) != null) {
return true;
} }
try { try {
is.close(); is.close();
@ -242,21 +392,15 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
private MutableFullBlockChange last = read(); private MutableFullBlockChange last = read();
public MutableFullBlockChange read() { public MutableFullBlockChange read() {
try { try {
int read0 = is.read(); change.x = posDel.readX(is) + originX;
if (read0 == -1) { change.y = posDel.readY(is);
return null; change.z = posDel.readZ(is) + originZ;
} idDel.readCombined(is, change, dir);
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; return change;
} catch (Exception ignoreEOF) { } catch (EOFException ignoreOEF) {
MainUtil.handleError(ignoreEOF); return null;
} catch (Exception e) {
MainUtil.handleError(e);
} }
return null; return null;
} }

View File

@ -113,9 +113,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet {
setOrigin(x, z); setOrigin(x, z);
idsStream = new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE); idsStream = new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE);
idsStreamZip = getCompressedOS(idsStream); idsStreamZip = getCompressedOS(idsStream);
idsStreamZip.write(FaweStreamChangeSet.MODE); writeHeader(idsStreamZip, x, y, z);
idsStreamZip.writeInt(x);
idsStreamZip.writeInt(z);
return idsStreamZip; return idsStreamZip;
} }
@ -125,7 +123,7 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet {
return null; return null;
} }
FaweInputStream result = MainUtil.getCompressedIS(new FastByteArraysInputStream(ids)); FaweInputStream result = MainUtil.getCompressedIS(new FastByteArraysInputStream(ids));
result.skip(FaweStreamChangeSet.HEADER_SIZE); readHeader(result);
return result; return result;
} }

View File

@ -2,6 +2,7 @@ package com.boydti.fawe.object.extent;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
@ -24,9 +25,9 @@ import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class FastWorldEditExtent extends AbstractDelegateExtent { public class FastWorldEditExtent extends AbstractDelegateExtent implements HasFaweQueue {
private final FaweQueue queue; private FaweQueue queue;
private final int maxY; private final int maxY;
public FastWorldEditExtent(final World world, FaweQueue queue) { public FastWorldEditExtent(final World world, FaweQueue queue) {

View File

@ -2,6 +2,7 @@ package com.boydti.fawe.object.extent;
import com.boydti.fawe.jnbt.anvil.MCAFile; import com.boydti.fawe.jnbt.anvil.MCAFile;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.Vector2D;
@ -21,7 +22,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class MCAExtent extends AbstractDelegateExtent { public class MCAExtent extends AbstractDelegateExtent implements HasFaweQueue {
private final FaweQueue queue; private final FaweQueue queue;
private final File folder; private final File folder;

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.WEManager; import com.boydti.fawe.util.WEManager;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
@ -38,7 +39,7 @@ public class ProcessedWEExtent extends FaweRegionExtent {
} }
if (WEManager.IMP.maskContains(this.mask, location.getBlockX(), location.getBlockZ())) { if (WEManager.IMP.maskContains(this.mask, location.getBlockX(), location.getBlockZ())) {
if (!limit.MAX_ENTITIES()) { if (!limit.MAX_ENTITIES()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES); WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_ENTITIES);
return null; return null;
} }
return super.createEntity(location, entity); return super.createEntity(location, entity);
@ -63,11 +64,14 @@ public class ProcessedWEExtent extends FaweRegionExtent {
return super.getEntities(region); return super.getEntities(region);
} }
int count = 0;
@Override @Override
public BaseBlock getLazyBlock(int x, int y, int z) { public BaseBlock getLazyBlock(int x, int y, int z) {
count++;
if (WEManager.IMP.maskContains(this.mask, x, z)) { if (WEManager.IMP.maskContains(this.mask, x, z)) {
if (!limit.MAX_CHECKS()) { if (!limit.MAX_CHECKS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES); WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
return EditSession.nullBlock; return EditSession.nullBlock;
} else { } else {
return extent.getLazyBlock(x, y, z); return extent.getLazyBlock(x, y, z);

View File

@ -7,20 +7,20 @@ import javax.annotation.Nullable;
public class AbstractDelegateMask implements Mask { public class AbstractDelegateMask implements Mask {
private final Mask parent; private final Mask mask;
public AbstractDelegateMask(Mask parent) { public AbstractDelegateMask(Mask parent) {
this.parent = parent; this.mask = parent;
} }
@Override @Override
public boolean test(Vector vector) { public boolean test(Vector vector) {
return parent.test(vector); return mask.test(vector);
} }
@Nullable @Nullable
@Override @Override
public Mask2D toMask2D() { public Mask2D toMask2D() {
return parent.toMask2D(); return mask.toMask2D();
} }
} }

View File

@ -0,0 +1,186 @@
package com.boydti.fawe.object.mask;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import java.util.HashMap;
import javax.annotation.Nullable;
public class AngleMask implements Mask, ResettableMask {
private final Extent extent;
private final int max;
private final int min;
private int maxY;
public AngleMask(Extent extent, int min, int max) {
this.extent = extent;
this.maxY = extent.getMaximumPoint().getBlockY();
this.min = min;
this.max = max;
}
private HashMap<Long, Boolean> angles = new HashMap<>();
private long tick = 0;
@Override
public boolean test(Vector vector) {
long currentTick = Fawe.get().getTimer().getTick();
if (tick != (tick = currentTick)) {
angles.clear();
tick = currentTick;
}
long pair = MathMan.pairInt(vector.getBlockX(), vector.getBlockZ());
if (!angles.isEmpty()) {
Boolean value = angles.get(pair);
if (value != null) {
return value;
}
}
boolean result = getAngle(vector);
angles.put(pair, result);
return result;
}
@Override
public void reset() {
this.angles.clear();
}
public boolean getAngle(Vector vector) {
int x = vector.getBlockX();
int z = vector.getBlockZ();
boolean n = false;
int o = getHighestTerrainBlock(x, z, 0, maxY, n);
if (getHighestTerrainBlock(x - 1, z, o - min, o - max, n) != -1) {
return true;
}
if (getHighestTerrainBlock(x + 1, z, o - min, o - max, n) != -1) {
return true;
}
if (getHighestTerrainBlock(x, z - 1, o - min, o - max, n) != -1) {
return true;
}
if (getHighestTerrainBlock(x, z + 1, o - min, o - max, n) != -1) {
return true;
}
return false;
}
private Vector mutable = new Vector();
private int getHighestTerrainBlock(final int x, final int z, int minY, int maxY, final boolean naturalOnly) {
maxY = Math.min(this.maxY, Math.max(0, maxY));
minY = Math.max(0, minY);
mutable.x = x;
mutable.z = z;
for (int y = maxY; y >= minY; --y) {
mutable.y = y;
BaseBlock block = extent.getLazyBlock(mutable);
final int id = block.getId();
int data;
switch (id) {
case 0: {
continue;
}
case 2:
case 4:
case 13:
case 14:
case 15:
case 20:
case 21:
case 22:
case 25:
case 30:
case 32:
case 37:
case 39:
case 40:
case 41:
case 42:
case 45:
case 46:
case 47:
case 48:
case 49:
case 51:
case 52:
case 54:
case 55:
case 56:
case 57:
case 58:
case 60:
case 61:
case 62:
case 7:
case 8:
case 9:
case 10:
case 11:
case 73:
case 74:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 84:
case 85:
case 87:
case 88:
case 101:
case 102:
case 103:
case 110:
case 112:
case 113:
case 117:
case 121:
case 122:
case 123:
case 124:
case 129:
case 133:
case 138:
case 137:
case 140:
case 165:
case 166:
case 169:
case 170:
case 172:
case 173:
case 174:
case 176:
case 177:
case 181:
case 182:
case 188:
case 189:
case 190:
case 191:
case 192:
return y;
default:
data = 0;
}
if (naturalOnly ? BlockType.isNaturalTerrainBlock(id, data) : !BlockType.canPassThrough(id, data)) {
return y;
}
}
return -1;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
}

View File

@ -0,0 +1,39 @@
package com.boydti.fawe.object.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import javax.annotation.Nullable;
public class DataMask implements Mask, ResettableMask {
private final Extent extent;
public DataMask(Extent extent) {
this.extent = extent;
}
int data = -1;
@Override
public boolean test(Vector vector) {
if (data != -1) {
return extent.getLazyBlock(vector).getData() == data;
} else {
data = extent.getLazyBlock(vector).getData();
return true;
}
}
@Override
public void reset() {
this.data = -1;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
}

View File

@ -0,0 +1,40 @@
package com.boydti.fawe.object.mask;
import com.boydti.fawe.FaweCache;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import javax.annotation.Nullable;
public class IdDataMask implements Mask, ResettableMask {
private final Extent extent;
public IdDataMask(Extent extent) {
this.extent = extent;
}
int combined = -1;
@Override
public boolean test(Vector vector) {
if (combined != -1) {
return FaweCache.getCombined(extent.getLazyBlock(vector)) == combined;
} else {
combined = FaweCache.getCombined(extent.getLazyBlock(vector));
return true;
}
}
@Override
public void reset() {
this.combined = -1;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
}

View File

@ -0,0 +1,39 @@
package com.boydti.fawe.object.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import javax.annotation.Nullable;
public class IdMask implements Mask, ResettableMask {
private Extent extent;
public IdMask(Extent extent) {
this.extent = extent;
}
int id = -1;
@Override
public boolean test(Vector vector) {
if (id != -1) {
return extent.getLazyBlock(vector).getId() == id;
} else {
id = extent.getLazyBlock(vector).getId();
return true;
}
}
@Override
public void reset() {
this.id = -1;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
}

View File

@ -0,0 +1,5 @@
package com.boydti.fawe.object.mask;
public interface ResettableMask {
void reset();
}

View File

@ -0,0 +1,33 @@
package com.boydti.fawe.object.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import javax.annotation.Nullable;
/**
* Restricts the
*/
public class XAxisMask implements Mask, ResettableMask {
private int layer = -1;
@Override
public boolean test(Vector vector) {
if (layer == -1) {
layer = vector.getBlockX();
}
return vector.getBlockX() == layer;
}
@Override
public void reset() {
this.layer = -1;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
}

View File

@ -0,0 +1,33 @@
package com.boydti.fawe.object.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import javax.annotation.Nullable;
/**
* Restricts the
*/
public class YAxisMask implements Mask, ResettableMask {
private int layer = -1;
@Override
public boolean test(Vector vector) {
if (layer == -1) {
layer = vector.getBlockY();
}
return vector.getBlockY() == layer;
}
@Override
public void reset() {
this.layer = -1;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
}

View File

@ -0,0 +1,33 @@
package com.boydti.fawe.object.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import javax.annotation.Nullable;
/**
* Restricts the
*/
public class ZAxisMask implements Mask, ResettableMask {
private int layer = -1;
@Override
public boolean test(Vector vector) {
if (layer == -1) {
layer = vector.getBlockZ();
}
return vector.getBlockZ() == layer;
}
@Override
public void reset() {
this.layer = -1;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
}

View File

@ -84,115 +84,121 @@ public class FaweFormat implements ClipboardReader, ClipboardWriter {
BlockArrayClipboard clipboard; BlockArrayClipboard clipboard;
int ox, oy, oz; int ox, oy, oz;
oy = 0; oy = 0;
boolean from = false; boolean from = false;
boolean small = true; boolean small = false;
switch (mode) { boolean knownSize = false;
default: switch(mode) {
return null; case 0:
knownSize = true;
break;
case 1:
small = true;
break;
case 2:
break;
case 3: case 3:
from = true; from = true;
case 2: break;
small = false; case 4:
case 1: { // Unknown size small = true;
ox = in.readInt(); from = true;
oz = in.readInt(); break;
FaweOutputStream tmp = new FaweOutputStream(new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE)); }
int width = 0; if (knownSize) {
int height = 0; int width = in.readUnsignedShort();
int length = 0; int height = in.readUnsignedShort();
while (true) { int length = in.readUnsignedShort();
ox = in.readShort();
oy = in.readShort();
oz = in.readShort();
Vector origin = new Vector(0, 0, 0);
CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE));
clipboard = new BlockArrayClipboard(region, clipboardId);
try {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
for (int z = 0; z < length; z++) {
int combined = in.readUnsignedShort();
int id = FaweCache.getId(combined);
int data = FaweCache.getData(combined);
BaseBlock block = FaweCache.getBlock(id, data);
clipboard.setBlock(x, y, z, block);
}
}
}
} catch (WorldEditException e) {
e.printStackTrace();
return null;
}
} else {
ox = in.readInt();
oz = in.readInt();
FaweOutputStream tmp = new FaweOutputStream(new FastByteArrayOutputStream(Settings.HISTORY.BUFFER_SIZE));
int width = 0;
int height = 0;
int length = 0;
while (true) {
int x, y, z;
if (small) {
tmp.write(x = in.read());
tmp.write(y = in.read());
tmp.write(z = in.read());
} else {
tmp.writeShort((short) (x = in.readUnsignedShort()));
tmp.write(y = in.read());
tmp.writeShort((short) (z = in.readUnsignedShort()));
}
if (from) {
in.skip(2);
}
short combined;
tmp.writeShort(combined = in.readShort());
if (combined == 0 || y == -1) {
break;
}
if (x > width) {
width = x;
}
if (y > height) {
height = y;
}
if(z > length) {
length = z;
}
}
Vector origin = new Vector(0, 0, 0);
CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length));
clipboard = new BlockArrayClipboard(region, clipboardId);
width++;
height++;
length++;
byte[] array = ((ByteArrayOutputStream) tmp.getParent()).toByteArray();
FaweInputStream part = new FaweInputStream(new FastByteArrayInputStream(array));
try {
for (int i = 0; i< array.length; i+= 9) {
int x, y, z; int x, y, z;
if (small) { if (small) {
tmp.write(x = in.read()); x = in.read();
tmp.write(y = in.read()); y = in.read();
tmp.write(z = in.read()); z = in.read();
} else { } else {
tmp.writeShort((short) (x = in.readUnsignedShort())); x = in.readUnsignedShort();
tmp.write(y = in.read()); y = in.read();
tmp.writeShort((short) (z = in.readUnsignedShort())); z = in.readUnsignedShort();
} }
if (from) { if (from) {
in.skip(2); in.skip(2);
} }
short combined; int combined = in.readShort();
tmp.writeShort(combined = in.readShort()); int id = FaweCache.getId(combined);
if (combined == 0 || y == -1) { int data = FaweCache.getData(combined);
break; BaseBlock block = FaweCache.getBlock(id, data);
} clipboard.setBlock(x, y, z, block);
if (x > width) {
width = x;
}
if (y > height) {
height = y;
}
if(z > length) {
length = z;
}
} }
Vector origin = new Vector(0, 0, 0); } catch (WorldEditException e) {
CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length)); e.printStackTrace();
clipboard = new BlockArrayClipboard(region, clipboardId); return null;
width++;
height++;
length++;
byte[] array = ((ByteArrayOutputStream) tmp.getParent()).toByteArray();
FaweInputStream part = new FaweInputStream(new FastByteArrayInputStream(array));
try {
for (int i = 0; i< array.length; i+= 9) {
int x, y, z;
if (small) {
x = in.read();
y = in.read();
z = in.read();
} else {
x = in.readUnsignedShort();
y = in.read();
z = in.readUnsignedShort();
}
if (from) {
in.skip(2);
}
int combined = in.readShort();
int id = FaweCache.getId(combined);
int data = FaweCache.getData(combined);
BaseBlock block = FaweCache.getBlock(id, data);
clipboard.setBlock(x, y, z, block);
}
} catch (WorldEditException e) {
e.printStackTrace();
return null;
}
break;
}
case 0: {
int width = in.readUnsignedShort();
int height = in.readUnsignedShort();
int length = in.readUnsignedShort();
ox = in.readShort();
oy = in.readShort();
oz = in.readShort();
Vector origin = new Vector(0, 0, 0);
CuboidRegion region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE));
clipboard = new BlockArrayClipboard(region, clipboardId);
try {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
for (int z = 0; z < length; z++) {
int combined = in.readUnsignedShort();
int id = FaweCache.getId(combined);
int data = FaweCache.getData(combined);
BaseBlock block = FaweCache.getBlock(id, data);
clipboard.setBlock(x, y, z, block);
}
}
}
} catch (WorldEditException e) {
e.printStackTrace();
return null;
}
break;
} }
} }
try { try {

View File

@ -0,0 +1,48 @@
package com.boydti.fawe.util;
import com.boydti.fawe.object.mask.ResettableMask;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import java.lang.reflect.Field;
import java.util.Collection;
public class MaskTraverser {
private final Mask mask;
public MaskTraverser(Mask start) {
this.mask = start;
}
public void reset(Extent newExtent) {
reset(mask, newExtent);
}
private void reset(Mask mask, Extent newExtent) {
if (mask instanceof ResettableMask) {
((ResettableMask) mask).reset();
}
Class<?> current = mask.getClass();
while(current.getSuperclass() != null) {
try {
Field field = current.getDeclaredField("extent");
field.setAccessible(true);
field.set(mask, newExtent);
} catch (NoSuchFieldException | IllegalAccessException ignore) {}
try {
Field field = current.getDeclaredField("mask");
field.setAccessible(true);
Mask next = (Mask) field.get(mask);
reset(next, newExtent);
} catch (NoSuchFieldException | IllegalAccessException ignore) {}
try {
Field field = current.getDeclaredField("masks");
field.setAccessible(true);
Collection<Mask> masks = (Collection<Mask>) field.get(mask);
for (Mask next : masks) {
reset(next, newExtent);
}
} catch (NoSuchFieldException | IllegalAccessException ignore) {}
current = current.getSuperclass();
}
}
}

View File

@ -31,6 +31,7 @@ import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.HistoryExtent; import com.boydti.fawe.object.HistoryExtent;
import com.boydti.fawe.object.NullChangeSet; import com.boydti.fawe.object.NullChangeSet;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
@ -44,7 +45,9 @@ import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.object.extent.FaweRegionExtent; import com.boydti.fawe.object.extent.FaweRegionExtent;
import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.object.extent.NullExtent;
import com.boydti.fawe.object.extent.ProcessedWEExtent; import com.boydti.fawe.object.extent.ProcessedWEExtent;
import com.boydti.fawe.object.mask.ResettableMask;
import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.ExtentTraverser;
import com.boydti.fawe.util.MaskTraverser;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.Perm; import com.boydti.fawe.util.Perm;
import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.SetQueue;
@ -149,7 +152,7 @@ import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
* {@link Extent}s that are chained together. For example, history is logged * {@link Extent}s that are chained together. For example, history is logged
* using the {@link ChangeSetExtent}.</p> * using the {@link ChangeSetExtent}.</p>
*/ */
public class EditSession extends AbstractWorld { public class EditSession extends AbstractWorld implements HasFaweQueue {
/** /**
* Used by {@link #setBlock(Vector, BaseBlock, Stage)} to * Used by {@link #setBlock(Vector, BaseBlock, Stage)} to
* determine which {@link Extent}s should be bypassed. * determine which {@link Extent}s should be bypassed.
@ -232,9 +235,6 @@ public class EditSession extends AbstractWorld {
if (allowedRegions == null) { if (allowedRegions == null) {
if (player != null && !player.hasWorldEditBypass()) { if (player != null && !player.hasWorldEditBypass()) {
allowedRegions = player.getCurrentRegions(); allowedRegions = player.getCurrentRegions();
if (allowedRegions.length == 1 && allowedRegions[0].isGlobal()) {
allowedRegions = null;
}
} }
} }
if (autoQueue == null) { if (autoQueue == null) {
@ -292,7 +292,7 @@ public class EditSession extends AbstractWorld {
if (allowedRegions.length == 0) { if (allowedRegions.length == 0) {
this.extent = new NullExtent(this.extent, BBC.WORLDEDIT_CANCEL_REASON_NO_REGION); this.extent = new NullExtent(this.extent, BBC.WORLDEDIT_CANCEL_REASON_NO_REGION);
} else { } else {
this.extent = new ProcessedWEExtent(this.extent, allowedRegions, limit); this.extent = new ProcessedWEExtent(this.extent, allowedRegions, this.limit);
} }
} }
this.extent = wrapExtent(this.extent, bus, event, Stage.BEFORE_HISTORY); this.extent = wrapExtent(this.extent, bus, event, Stage.BEFORE_HISTORY);
@ -577,9 +577,15 @@ public class EditSession extends AbstractWorld {
public void setMask(Mask mask) { public void setMask(Mask mask) {
if (mask == null) { if (mask == null) {
mask = Masks.alwaysTrue(); mask = Masks.alwaysTrue();
} else {
new MaskTraverser(mask).reset(this);
} }
ExtentTraverser<MaskingExtent> maskingExtent = new ExtentTraverser(this.extent).find(MaskingExtent.class); ExtentTraverser<MaskingExtent> maskingExtent = new ExtentTraverser(this.extent).find(MaskingExtent.class);
if (maskingExtent != null) { if (maskingExtent != null && maskingExtent.get() != null) {
Mask oldMask = maskingExtent.get().getMask();
if (oldMask instanceof ResettableMask) {
((ResettableMask) oldMask).reset();
}
maskingExtent.get().setMask(mask); maskingExtent.get().setMask(mask);
} else if (mask != Masks.alwaysTrue()) { } else if (mask != Masks.alwaysTrue()) {
this.extent = new MaskingExtent(this.extent, mask); this.extent = new MaskingExtent(this.extent, mask);
@ -1159,6 +1165,8 @@ public class EditSession extends AbstractWorld {
BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS.send(player); BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS.send(player);
} }
} }
// Reset limit
limit.set(originalLimit);
// Enqueue it // Enqueue it
if (queue == null || queue.size() == 0) { if (queue == null || queue.size() == 0) {
queue.dequeue(); queue.dequeue();

View File

@ -31,6 +31,8 @@ import com.boydti.fawe.object.brush.BlendBall;
import com.boydti.fawe.object.brush.DoubleActionBrushTool; import com.boydti.fawe.object.brush.DoubleActionBrushTool;
import com.boydti.fawe.object.brush.ErodeBrush; import com.boydti.fawe.object.brush.ErodeBrush;
import com.boydti.fawe.object.brush.LineBrush; import com.boydti.fawe.object.brush.LineBrush;
import com.boydti.fawe.object.brush.RecurseBrush;
import com.boydti.fawe.object.mask.IdMask;
import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.CommandPermissions;
@ -117,6 +119,25 @@ public class BrushCommands {
BBC.BRUSH_SPHERE.send(player, radius); BBC.BRUSH_SPHERE.send(player, radius);
} }
@Command(
aliases = { "recursive", "recurse", "r" },
usage = "<pattern-to> [radius]",
desc = "Choose the recursive brush",
help = "Chooses the recursive brush",
min = 0,
max = 2
)
@CommandPermissions("worldedit.brush.recursive")
public void recursiveBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, @Optional("2") double radius) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
BrushTool tool = session.getBrushTool(player.getItemInHand());
tool.setSize(radius);
tool.setBrush(new RecurseBrush(tool), "worldedit.brush.recursive");
tool.setMask(new IdMask(editSession));
tool.setFill(fill);
BBC.BRUSH_SPHERE.send(player, radius);
}
@Command( @Command(
aliases = { "line", "l" }, aliases = { "line", "l" },
usage = "<pattern> [radius]", usage = "<pattern> [radius]",
@ -152,8 +173,7 @@ public class BrushCommands {
max = 2 max = 2
) )
@CommandPermissions("worldedit.brush.sphere") @CommandPermissions("worldedit.brush.sphere")
public void sphereBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, public void sphereBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, @Optional("2") double radius, @Switch('h') boolean hollow) throws WorldEditException {
@Optional("2") double radius, @Switch('h') boolean hollow) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius); worldEdit.checkMaxBrushRadius(radius);
BrushTool tool = session.getBrushTool(player.getItemInHand()); BrushTool tool = session.getBrushTool(player.getItemInHand());

View File

@ -26,9 +26,11 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.database.DBHandler; import com.boydti.fawe.database.DBHandler;
import com.boydti.fawe.database.RollbackDatabase; import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory; import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandContext;
@ -72,7 +74,7 @@ public class HistoryCommands {
min = 3, min = 3,
max = 3 max = 3
) )
@CommandPermissions("worldedit.history.undo") @CommandPermissions("worldedit.history.rollback")
public void faweRollback(final Player player, LocalSession session, final String user, int radius, String time) throws WorldEditException { public void faweRollback(final Player player, LocalSession session, final String user, int radius, String time) throws WorldEditException {
if (!Settings.HISTORY.USE_DATABASE) { if (!Settings.HISTORY.USE_DATABASE) {
BBC.SETTING_DISABLE.send(player, "history.use-database"); BBC.SETTING_DISABLE.send(player, "history.use-database");
@ -136,7 +138,7 @@ public class HistoryCommands {
return; return;
} }
radius = Math.max(Math.min(500, radius), 0); radius = Math.max(Math.min(500, radius), 0);
World world = player.getWorld(); final World world = player.getWorld();
WorldVector origin = player.getPosition(); WorldVector origin = player.getPosition();
Vector bot = origin.subtract(radius, radius, radius); Vector bot = origin.subtract(radius, radius, radius);
bot = bot.setY(Math.max(0, bot.getY())); bot = bot.setY(Math.max(0, bot.getY()));
@ -144,10 +146,20 @@ public class HistoryCommands {
top = top.setY(Math.min(255, top.getY())); top = top.setY(Math.min(255, top.getY()));
RollbackDatabase database = DBHandler.IMP.getDatabase(world); RollbackDatabase database = DBHandler.IMP.getDatabase(world);
final AtomicInteger count = new AtomicInteger(); final AtomicInteger count = new AtomicInteger();
final FawePlayer fp = FawePlayer.wrap(player);
database.getPotentialEdits(other, System.currentTimeMillis() - timeDiff, bot, top, new RunnableVal<DiskStorageHistory>() { database.getPotentialEdits(other, System.currentTimeMillis() - timeDiff, bot, top, new RunnableVal<DiskStorageHistory>() {
@Override @Override
public void run(DiskStorageHistory edit) { public void run(DiskStorageHistory edit) {
EditSession session = edit.toEditSession(null); EditSession session = new EditSessionBuilder(world)
.player(fp)
.autoQueue(false)
.fastmode(false)
.checkMemory(false)
.changeSet(edit)
.limitUnlimited()
.queue(fp.getMaskedFaweQueue(false))
.build();
session.setSize(1);
session.undo(session); session.undo(session);
edit.deleteFiles(); edit.deleteFiles();
BBC.ROLLBACK_ELEMENT.send(player, Fawe.imp().getWorldName(edit.getWorld()) + "/" + user + "-" + edit.getIndex()); BBC.ROLLBACK_ELEMENT.send(player, Fawe.imp().getWorldName(edit.getWorld()) + "/" + user + "-" + edit.getIndex());

View File

@ -152,7 +152,6 @@ public class ToolCommands {
) )
@CommandPermissions("worldedit.tool.flood-fill") @CommandPermissions("worldedit.tool.flood-fill")
public void floodFill(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { public void floodFill(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
LocalConfiguration config = we.getConfiguration(); LocalConfiguration config = we.getConfiguration();
int range = args.getInteger(1); int range = args.getInteger(1);

View File

@ -0,0 +1,130 @@
package com.sk89q.worldedit.command;
import com.boydti.fawe.config.BBC;
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.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.util.command.parametric.Optional;
/**
* Tool commands.
*/
public class ToolUtilCommands {
private final WorldEdit we;
public ToolUtilCommands(WorldEdit we) {
this.we = we;
}
@Command(
aliases = { "/", "," },
usage = "[on|off]",
desc = "Toggle the super pickaxe function",
min = 0,
max = 1
)
@CommandPermissions("worldedit.superpickaxe")
public void togglePickaxe(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
String newState = args.getString(0, null);
if (session.hasSuperPickAxe()) {
if ("on".equals(newState)) {
player.printError("Super pick axe already enabled.");
BBC.SUPERPICKAXE_ENABLED.send(player);
return;
}
session.disableSuperPickAxe();
player.print("Super pick axe disabled.");
} else {
if ("off".equals(newState)) {
player.printError("Super pick axe already disabled.");
BBC.SUPERPICKAXE_DISABLED.send(player);
return;
}
session.enableSuperPickAxe();
player.print("Super pick axe enabled.");
BBC.SUPERPICKAXE_ENABLED.send(player);
}
}
@Command(
aliases = { "mask" },
usage = "[mask]",
desc = "Set the brush mask",
min = 0,
max = -1
)
@CommandPermissions("worldedit.brush.options.mask")
public void mask(Player player, LocalSession session, EditSession editSession, @Optional CommandContext context) throws WorldEditException {
if (context == null || context.argsLength() == 0) {
session.getBrushTool(player.getItemInHand()).setMask(null);
BBC.BRUSH_MASK_DISABLED.send(player);
} else {
ParserContext parserContext = new ParserContext();
parserContext.setActor(player);
parserContext.setWorld(player.getWorld());
parserContext.setSession(session);
parserContext.setExtent(editSession);
Mask mask = we.getMaskFactory().parseFromInput(context.getJoinedStrings(0), parserContext);
session.getBrushTool(player.getItemInHand()).setMask(mask);
BBC.BRUSH_MASK.send(player);
}
}
@Command(
aliases = { "mat", "material" },
usage = "[pattern]",
desc = "Set the brush material",
min = 1,
max = 1
)
@CommandPermissions("worldedit.brush.options.material")
public void material(Player player, LocalSession session, EditSession editSession, Pattern pattern) throws WorldEditException {
session.getBrushTool(player.getItemInHand()).setFill(pattern);
BBC.BRUSH_MATERIAL.send(player);
}
@Command(
aliases = { "range" },
usage = "[pattern]",
desc = "Set the brush range",
min = 1,
max = 1
)
@CommandPermissions("worldedit.brush.options.range")
public void range(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
int range = args.getInteger(0);
session.getBrushTool(player.getItemInHand()).setRange(range);
BBC.BRUSH_RANGE.send(player);
}
@Command(
aliases = { "size" },
usage = "[pattern]",
desc = "Set the brush size",
min = 1,
max = 1
)
@CommandPermissions("worldedit.brush.options.size")
public void size(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
int radius = args.getInteger(0);
we.checkMaxBrushRadius(radius);
session.getBrushTool(player.getItemInHand()).setSize(radius);
BBC.BRUSH_SIZE.send(player);
}
public static Class<?> inject() {
return ToolUtilCommands.class;
}
}

View File

@ -0,0 +1,187 @@
package com.sk89q.worldedit.extension.factory;
import com.boydti.fawe.object.mask.AngleMask;
import com.boydti.fawe.object.mask.DataMask;
import com.boydti.fawe.object.mask.IdDataMask;
import com.boydti.fawe.object.mask.IdMask;
import com.boydti.fawe.object.mask.XAxisMask;
import com.boydti.fawe.object.mask.YAxisMask;
import com.boydti.fawe.object.mask.ZAxisMask;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.NoMatchException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.BiomeMask2D;
import com.sk89q.worldedit.function.mask.BlockMask;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
import com.sk89q.worldedit.function.mask.ExpressionMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.MaskIntersection;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.mask.NoiseFilter;
import com.sk89q.worldedit.function.mask.OffsetMask;
import com.sk89q.worldedit.function.mask.RegionMask;
import com.sk89q.worldedit.function.mask.SolidBlockMask;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.noise.RandomNoise;
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.session.request.RequestSelection;
import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.biome.Biomes;
import com.sk89q.worldedit.world.registry.BiomeRegistry;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Parses mask input strings.
*/
public class DefaultMaskParser extends InputParser<Mask> {
public DefaultMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
List<Mask> masks = new ArrayList<Mask>();
for (String component : input.split(" ")) {
if (component.isEmpty()) {
continue;
}
Mask current = getBlockMaskComponent(masks, component, context);
masks.add(current);
}
switch (masks.size()) {
case 0:
return null;
case 1:
return masks.get(0);
default:
return new MaskIntersection(masks);
}
}
private Mask getBlockMaskComponent(List<Mask> masks, String component, ParserContext context) throws InputParseException {
Extent extent = Request.request().getEditSession();
final char firstChar = component.charAt(0);
switch (firstChar) {
case '#':
switch (component.toLowerCase()) {
case "#existing":
return new ExistingBlockMask(extent);
case "#solid":
return new SolidBlockMask(extent);
case "#dregion":
case "#dselection":
case "#dsel":
return new RegionMask(new RequestSelection());
case "#selection":
case "#region":
case "#sel":
try {
return new RegionMask(context.requireSession().getSelection(context.requireWorld()).clone());
} catch (IncompleteRegionException e) {
throw new InputParseException("Please make a selection first.");
}
case "#xaxis":
return new XAxisMask();
case "#yaxis":
return new YAxisMask();
case "#zaxis":
return new ZAxisMask();
case "#id":
return new IdMask(extent);
case "#data":
return new DataMask(extent);
case "#iddata":
return new IdDataMask(extent);
default:
throw new NoMatchException("Unrecognized mask '" + component + "'");
}
case '\\':
case '/': {
String[] split = component.substring(1).split(",");
if (split.length != 2) {
throw new InputParseException("Unknown angle '" + component + "' (not in form /#,#)");
}
try {
int y1 = Integer.parseInt(split[0]);
int y2 = Integer.parseInt(split[1]);
return new AngleMask(extent, y1, y2);
} catch (NumberFormatException e) {
throw new InputParseException("Unknown angle '" + component + "' (not in form /#,#)");
}
}
case '>':
case '<':
Mask submask;
if (component.length() > 1) {
submask = getBlockMaskComponent(masks, component.substring(1), context);
} else {
submask = new ExistingBlockMask(extent);
}
OffsetMask offsetMask = new OffsetMask(submask, new Vector(0, firstChar == '>' ? -1 : 1, 0));
return new MaskIntersection(offsetMask, Masks.negate(submask));
case '$':
Set<BaseBiome> biomes = new HashSet<BaseBiome>();
String[] biomesList = component.substring(1).split(",");
BiomeRegistry biomeRegistry = context.requireWorld().getWorldData().getBiomeRegistry();
List<BaseBiome> knownBiomes = biomeRegistry.getBiomes();
for (String biomeName : biomesList) {
BaseBiome biome = Biomes.findBiomeByName(knownBiomes, biomeName, biomeRegistry);
if (biome == null) {
throw new InputParseException("Unknown biome '" + biomeName + "'");
}
biomes.add(biome);
}
return Masks.asMask(new BiomeMask2D(context.requireExtent(), biomes));
case '%':
int i = Integer.parseInt(component.substring(1));
return new NoiseFilter(new RandomNoise(), ((double) i) / 100);
case '=':
try {
Expression exp = Expression.compile(component.substring(1), "x", "y", "z");
WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(
Request.request().getEditSession(), Vector.ONE, Vector.ZERO);
exp.setEnvironment(env);
return new ExpressionMask(exp);
} catch (ExpressionException e) {
throw new InputParseException("Invalid expression: " + e.getMessage());
}
case '!':
if (component.length() > 1) {
return Masks.negate(getBlockMaskComponent(masks, component.substring(1), context));
}
default:
ParserContext tempContext = new ParserContext(context);
tempContext.setRestricted(false);
tempContext.setPreferringWildcard(true);
return new BlockMask(extent, worldEdit.getBlockFactory().parseFromListInput(component, tempContext));
}
}
public static Class<?> inject() {
return DefaultMaskParser.class;
}
}

View File

@ -0,0 +1,234 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalPlayer;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.session.request.Request;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Various utility functions related to {@link Mask} and {@link Mask2D}.
*/
public final class Masks {
private static final AlwaysTrue ALWAYS_TRUE = new AlwaysTrue();
private static final AlwaysFalse ALWAYS_FALSE = new AlwaysFalse();
private Masks() {
}
/**
* Return a 3D mask that always returns true;
*
* @return a mask
*/
public static Mask alwaysTrue() {
return ALWAYS_TRUE;
}
/**
* Return a 2D mask that always returns true;
*
* @return a mask
*/
public static Mask2D alwaysTrue2D() {
return ALWAYS_TRUE;
}
/**
* Negate the given mask.
*
* @param finalMask the mask
* @return a new mask
*/
public static Mask negate(final Mask finalMask) {
if (finalMask instanceof AlwaysTrue) {
return ALWAYS_FALSE;
} else if (finalMask instanceof AlwaysFalse) {
return ALWAYS_TRUE;
}
checkNotNull(finalMask);
return new AbstractMask() {
private Mask mask = finalMask;
@Override
public boolean test(Vector vector) {
return !mask.test(vector);
}
@Nullable
@Override
public Mask2D toMask2D() {
Mask2D mask2d = mask.toMask2D();
if (mask2d != null) {
return negate(mask2d);
} else {
return null;
}
}
};
}
/**
* Negate the given mask.
*
* @param mask the mask
* @return a new mask
*/
public static Mask2D negate(final Mask2D mask) {
if (mask instanceof AlwaysTrue) {
return ALWAYS_FALSE;
} else if (mask instanceof AlwaysFalse) {
return ALWAYS_TRUE;
}
checkNotNull(mask);
return new AbstractMask2D() {
@Override
public boolean test(Vector2D vector) {
return !mask.test(vector);
}
};
}
/**
* Return a 3-dimensional version of a 2D mask.
*
* @param mask the mask to make 3D
* @return a 3D mask
*/
public static Mask asMask(final Mask2D mask) {
return new AbstractMask() {
@Override
public boolean test(Vector vector) {
return mask.test(vector.toVector2D());
}
@Nullable
@Override
public Mask2D toMask2D() {
return mask;
}
};
}
/**
* Wrap an old-style mask and convert it to a new mask.
*
* <p>Note, however, that this is strongly not recommended because
* {@link com.sk89q.worldedit.masks.Mask#prepare(LocalSession, LocalPlayer, Vector)}
* is not called.</p>
*
* @param mask the old-style mask
* @param editSession the edit session to bind to
* @return a new-style mask
* @deprecated Please avoid if possible
*/
@Deprecated
@SuppressWarnings("deprecation")
public static Mask wrap(final com.sk89q.worldedit.masks.Mask mask, final EditSession editSession) {
checkNotNull(mask);
return new AbstractMask() {
@Override
public boolean test(Vector vector) {
return mask.matches(editSession, vector);
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
};
}
/**
* Wrap an old-style mask and convert it to a new mask.
*
* <p>As an {@link EditSession} is not provided in this case, one will be
* taken from the {@link Request}, if possible. If not possible, then the
* mask will return false.</p>
*
* @param mask the old-style mask
* @return a new-style mask
*/
@SuppressWarnings("deprecation")
public static Mask wrap(final com.sk89q.worldedit.masks.Mask mask) {
checkNotNull(mask);
return new AbstractMask() {
@Override
public boolean test(Vector vector) {
EditSession editSession = Request.request().getEditSession();
return editSession != null && mask.matches(editSession, vector);
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
};
}
/**
* Convert a new-style mask to an old-style mask.
*
* @param mask the new-style mask
* @return an old-style mask
*/
@SuppressWarnings("deprecation")
public static com.sk89q.worldedit.masks.Mask wrap(final Mask mask) {
checkNotNull(mask);
return new com.sk89q.worldedit.masks.AbstractMask() {
@Override
public boolean matches(EditSession editSession, Vector position) {
Request.request().setEditSession(editSession);
return mask.test(position);
}
};
}
private static class AlwaysTrue implements Mask, Mask2D {
@Override
public boolean test(Vector vector) {
return true;
}
@Override
public boolean test(Vector2D vector) {
return true;
}
@Nullable
@Override
public Mask2D toMask2D() {
return this;
}
}
private static class AlwaysFalse implements Mask, Mask2D {
@Override
public boolean test(Vector vector) {
return false;
}
@Override
public boolean test(Vector2D vector) {
return false;
}
@Nullable
@Override
public Mask2D toMask2D() {
return this;
}
}
public static Class<?> inject() {
return Masks.class;
}
}

View File

@ -0,0 +1,92 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Checks whether another mask tests true for a position that is offset
* a given vector.
*/
public class OffsetMask extends AbstractMask {
private Mask mask;
private Vector offset;
private Vector mutable = new Vector();
/**
* Create a new instance.
*
* @param mask the mask
* @param offset the offset
*/
public OffsetMask(Mask mask, Vector offset) {
checkNotNull(mask);
checkNotNull(offset);
this.mask = mask;
this.offset = offset;
}
/**
* Get the mask.
*
* @return the mask
*/
public Mask getMask() {
return mask;
}
/**
* Set the mask.
*
* @param mask the mask
*/
public void setMask(Mask mask) {
checkNotNull(mask);
this.mask = mask;
}
/**
* Get the offset.
*
* @return the offset
*/
public Vector getOffset() {
return offset;
}
/**
* Set the offset.
*
* @param offset the offset
*/
public void setOffset(Vector offset) {
checkNotNull(offset);
this.offset = offset;
}
@Override
public boolean test(Vector vector) {
mutable.x = vector.x + offset.x;
mutable.y = vector.y + offset.y;
mutable.z = vector.z + offset.z;
return getMask().test(mutable);
}
@Nullable
@Override
public Mask2D toMask2D() {
Mask2D childMask = getMask().toMask2D();
if (childMask != null) {
return new OffsetMask2D(childMask, getOffset().toVector2D());
} else {
return null;
}
}
public static Class<?> inject() {
return OffsetMask.class;
}
}

View File

@ -120,6 +120,9 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
* Clamps the cuboid according to boundaries of the world. * Clamps the cuboid according to boundaries of the world.
*/ */
private void recalculate() { private void recalculate() {
if (pos1 == null || pos2 == null) {
return;
}
pos1 = pos1.clampY(0, world == null ? 255 : world.getMaxY()); pos1 = pos1.clampY(0, world == null ? 255 : world.getMaxY());
pos2 = pos2.clampY(0, world == null ? 255 : world.getMaxY()); pos2 = pos2.clampY(0, world == null ? 255 : world.getMaxY());
Vector min = getMinimumPoint(); Vector min = getMinimumPoint();

View File

@ -88,7 +88,6 @@ public class CuboidRegionSelector extends com.sk89q.worldedit.regions.CuboidRegi
position1 = oldRegion.getMinimumPoint().toBlockVector(); position1 = oldRegion.getMinimumPoint().toBlockVector();
position2 = oldRegion.getMaximumPoint().toBlockVector(); position2 = oldRegion.getMaximumPoint().toBlockVector();
} }
region.setPos1(position1); region.setPos1(position1);
region.setPos2(position2); region.setPos2(position2);
} }