Various minor

JavaScript-API
Allow command replacing
Tweak primary thread checks
Tab completion
Schematic resolving
help tweaks
optimize change set
fix color brush
This commit is contained in:
Jesse Boyd 2018-09-04 05:40:06 +10:00
parent 0021ab0d61
commit beb2e0e1b0
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
45 changed files with 1609 additions and 595 deletions

View File

@ -53,7 +53,6 @@ apply plugin: 'com.github.johnrengelman.shadow'
shadowJar { shadowJar {
dependencies { dependencies {
include(dependency('com.github.luben:zstd-jni:1.1.1')) include(dependency('com.github.luben:zstd-jni:1.1.1'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0')) include(dependency('co.aikar:fastutil-lite:1.0'))
include(dependency(':core')) include(dependency(':core'))
} }

View File

@ -627,6 +627,11 @@ public class FaweBukkit implements IFawe, Listener {
return ((BlocksHubBukkit) blocksHubPlugin).getApi(); return ((BlocksHubBukkit) blocksHubPlugin).getApi();
} }
@Override
public boolean isMainThread() {
return Bukkit.isPrimaryThread();
}
private Version version = null; private Version version = null;
public Version getVersion() { public Version getVersion() {

View File

@ -68,7 +68,7 @@ public class AsyncChunk implements Chunk {
@Override @Override
public ChunkSnapshot getChunkSnapshot(boolean includeMaxblocky, boolean includeBiome, boolean includeBiomeTempRain) { public ChunkSnapshot getChunkSnapshot(boolean includeMaxblocky, boolean includeBiome, boolean includeBiomeTempRain) {
if (Thread.currentThread() == Fawe.get().getMainThread()) { if (Fawe.isMainThread()) {
return world.getChunkAt(x, z).getChunkSnapshot(includeMaxblocky, includeBiome, includeBiomeTempRain); return world.getChunkAt(x, z).getChunkSnapshot(includeMaxblocky, includeBiome, includeBiomeTempRain);
} }
return whenLoaded(new RunnableVal<ChunkSnapshot>() { return whenLoaded(new RunnableVal<ChunkSnapshot>() {
@ -80,7 +80,7 @@ public class AsyncChunk implements Chunk {
} }
private <T> T whenLoaded(RunnableVal<T> task) { private <T> T whenLoaded(RunnableVal<T> task) {
if (Thread.currentThread() == Fawe.get().getMainThread()) { if (Fawe.isMainThread()) {
task.run(); task.run();
return task.value; return task.value;
} }

View File

@ -201,6 +201,11 @@ public class Fawe {
* @param s * @param s
*/ */
public static void debug(Object s) { public static void debug(Object s) {
Actor actor = Request.request().getActor();
if (actor != null && actor.isPlayer()) {
actor.print(BBC.color(BBC.PREFIX.original() + " " + s));
return;
}
debugPlain(BBC.PREFIX.original() + " " + s); debugPlain(BBC.PREFIX.original() + " " + s);
} }
@ -729,7 +734,7 @@ public class Fawe {
} }
public static boolean isMainThread() { public static boolean isMainThread() {
return INSTANCE != null ? INSTANCE.thread == Thread.currentThread() : true; return INSTANCE != null ? imp().isMainThread() : true;
} }
/** /**

View File

@ -65,4 +65,8 @@ public interface IFawe {
public default FormBuilder getFormBuilder() { public default FormBuilder getFormBuilder() {
return null; return null;
} }
default boolean isMainThread() {
return Fawe.get().getMainThread() == Thread.currentThread();
}
} }

View File

@ -6,11 +6,8 @@ import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.Dispatcher;
import java.util.AbstractMap;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public abstract class FaweParser<T> extends InputParser<T> { public abstract class FaweParser<T> extends InputParser<T> {
protected FaweParser(WorldEdit worldEdit) { protected FaweParser(WorldEdit worldEdit) {
@ -28,27 +25,13 @@ public abstract class FaweParser<T> extends InputParser<T> {
public abstract Dispatcher getDispatcher(); public abstract Dispatcher getDispatcher();
public List<String> suggestRemaining(String input, String... expected) throws InputParseException {
List<String> remainder = StringMan.split(input, ':');
int len = remainder.size();
if (len != expected.length - 1) {
if (len <= expected.length - 1 && len != 0) {
if (remainder.get(len - 1).endsWith(":")) {
throw new SuggestInputParseException(null, StringMan.join(expected, ":"));
}
throw new SuggestInputParseException(null, expected[0] + ":" + input + ":" + StringMan.join(Arrays.copyOfRange(expected, len + 1, 3), ":"));
} else {
throw new SuggestInputParseException(null, StringMan.join(expected, ":"));
}
}
return remainder;
}
protected static class ParseEntry { protected static class ParseEntry {
public boolean and; public boolean and;
public String input; public String input;
public String full;
public ParseEntry(String input, boolean type) { public ParseEntry(String full, String input, boolean type) {
this.full = full;
this.input = input; this.input = input;
this.and = type; this.and = type;
} }
@ -59,66 +42,54 @@ public abstract class FaweParser<T> extends InputParser<T> {
} }
} }
public List<Map.Entry<ParseEntry, List<String>>> parse(String command) throws InputParseException { public static List<Map.Entry<ParseEntry, List<String>>> parse(String toParse) throws InputParseException {
List<Map.Entry<ParseEntry, List<String>>> keys = new ArrayList<>(); List<Map.Entry<ParseEntry, List<String>>> keys = new ArrayList<>();
List<String> args = new ArrayList<>(); List<String> inputs = new ArrayList<>();
int len = command.length(); List<Boolean> and = new ArrayList<>();
String current = null; int last = 0;
int end = -1; outer:
boolean newEntry = true; for (int i = 0; i < toParse.length(); i++) {
for (int i = 0; i < len; i++) { char c = toParse.charAt(i);
int prefix = 0;
boolean or = false;
char c = command.charAt(i);
if (i < end) continue;
switch (c) { switch (c) {
case ',':
case '&': case '&':
or = true; String result = toParse.substring(last, i);
case ',': { if (!result.isEmpty()) {
prefix = 1; inputs.add(result);
if (current == null) { and.add(c == '&');
throw new InputParseException("Duplicate separator"); } else {
throw new InputParseException("Invalid dangling character " + c);
} }
newEntry = true; last = i + 1;
break; continue outer;
} default:
case '[': { if (StringMan.getMatchingBracket(c) != c) {
int depth = 0; int next = StringMan.findMatchingBracket(toParse, i);
end = len; if (next != -1) {
loop: i = next;
for (int j = i + 1; j < len; j++) { } else {
char c2 = command.charAt(j); toParse += "]";
switch (c2) { i = toParse.length();
case '[':
depth++;
continue;
case ']':
if (depth-- <= 0) {
end = j;
break loop;
}
} }
continue outer;
} }
String arg = command.substring(i + 1, end);
args.add(arg);
break;
}
}
if (newEntry) {
newEntry = false;
int index = StringMan.indexOf(command, Math.max(i, end) + prefix, '[', '&', ',');
if (index < 0) index = len;
end = index;
current = command.substring(i + prefix, end);
if (prefix == 1) args = new ArrayList<>();
ParseEntry entry = new ParseEntry(current, or);
keys.add(new AbstractMap.SimpleEntry<>(entry, args));
} }
} }
for (int i = 0; i < keys.size() - 1; i++) { // Apply greedy and inputs.add(toParse.substring(last, toParse.length()));
if (keys.get(i + 1).getKey().and) { for (int i = 0; i < inputs.size(); i++) {
keys.get(i).getKey().and = true; String full = inputs.get(i);
String command = full;
List<String> args = new ArrayList<>();
while (!command.isEmpty() && command.charAt(command.length() - 1) == ']') {
int startPos = StringMan.findMatchingBracket(command, command.length() - 1);
if (startPos == -1) break;
String arg = command.substring(startPos + 1, command.length() - 1);
args.add(arg);
command = full.substring(0, startPos);
} }
Collections.reverse(args);
ParseEntry entry = new ParseEntry(full, command, i > 0 ? and.get(i - 1) : false);
keys.add(new AbstractMap.SimpleEntry<>(entry, args));
} }
return keys; return keys;
} }

View File

@ -2,87 +2,87 @@ package com.boydti.fawe.command;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.StringMan;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.InputParseException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull;
public class SuggestInputParseException extends InputParseException { public class SuggestInputParseException extends InputParseException {
private final String message; private final InputParseException cause;
private final SuggestSupplier<List<String>> getSuggestions;
private String prefix; private String prefix;
private ArrayList<String> suggestions = new ArrayList<>();
public SuggestInputParseException(String input, Collection<String> inputs) { public SuggestInputParseException(String msg, String prefix, SuggestSupplier<List<String>> getSuggestions) {
super(""); this(new InputParseException(msg), prefix, getSuggestions);
this.message = "Suggested input: " + StringMan.join(suggestions = getSuggestions(input, inputs), ", ");
this.prefix = "";
} }
public SuggestInputParseException(String input, String... inputs) { public static SuggestInputParseException of(Throwable other, String prefix, SuggestSupplier<List<String>> getSuggestions) {
super(""); InputParseException e = find(other);
this.message = "Suggested input: " + StringMan.join(suggestions = getSuggestions(input, inputs), ", "); if (e != null) return of(e, prefix, getSuggestions);
this.prefix = ""; return of(new InputParseException(other.getMessage()), prefix, getSuggestions);
}
public static SuggestInputParseException of(InputParseException other, String prefix, SuggestSupplier<List<String>> getSuggestions) {
if (other instanceof SuggestInputParseException) return (SuggestInputParseException) other;
return new SuggestInputParseException(other, prefix, getSuggestions);
}
public SuggestInputParseException(InputParseException other, String prefix, SuggestSupplier<List<String>> getSuggestions) {
super(other.getMessage());
checkNotNull(getSuggestions);
checkNotNull(other);
this.cause = other;
this.getSuggestions = getSuggestions;
this.prefix = prefix;
}
public interface SuggestSupplier<T> {
T get() throws InputParseException;
}
public static InputParseException find(Throwable e) {
do {
if (e instanceof InputParseException) return (InputParseException) e;
e = e.getCause();
}
while (e != null);
return null;
}
public static SuggestInputParseException get(Throwable e) {
Throwable t = e;
while (t.getCause() != null) {
t = t.getCause();
if (t instanceof SuggestInputParseException) return (SuggestInputParseException) t;
}
return null;
}
@Override
public synchronized Throwable getCause() {
return cause.getCause();
} }
@Override @Override
public String getMessage() { public String getMessage() {
return message; return cause.getMessage();
} }
public List<String> getSuggestions() {
return MainUtil.prepend(prefix, suggestions); public List<String> getSuggestions() throws InputParseException {
return getSuggestions.get();
} }
public SuggestInputParseException prepend(String input) { public SuggestInputParseException prepend(String input) {
this.prefix = input + prefix; this.prefix = input + prefix;
return this; return this;
} }
public static SuggestInputParseException get(Throwable e) {
if (e instanceof SuggestInputParseException) {
return (SuggestInputParseException) e;
}
Throwable cause = e.getCause();
if (cause == null) {
return null;
}
return get(cause);
}
private static ArrayList<String> getSuggestions(String input, Collection<String> inputs) {
ArrayList<String> suggestions = new ArrayList<>();
if (input != null) {
String tmp = input.toLowerCase();
for (String s : inputs) {
if (s.startsWith(tmp)) {
suggestions.add(s);
}
}
}
if (suggestions.isEmpty()) {
suggestions.addAll(inputs);
}
return suggestions;
}
private static ArrayList<String> getSuggestions(String input, String... inputs) {
ArrayList<String> suggestions = new ArrayList<>();
if (input != null) {
String tmp = input.toLowerCase();
for (String s : inputs) {
if (s.startsWith(tmp)) {
suggestions.add(s);
}
}
}
if (suggestions.isEmpty()) {
for (String s : inputs) {
suggestions.add(s);
}
}
return suggestions;
}
} }

View File

@ -30,6 +30,47 @@ public class Commands {
} }
} }
public static Command fromArgs(String[] aliases, String usage, String desc, int min, int max, String flags, String help, boolean queued /* ignored */) {
return new Command() {
@Override
public Class<? extends Annotation> annotationType() {
return Command.class;
}
@Override
public String[] aliases() {
return aliases;
}
@Override
public String usage() {
return usage;
}
@Override
public String desc() {
return desc;
}
@Override
public int min() {
return min;
}
@Override
public int max() {
return max;
}
@Override
public String flags() {
return flags;
}
@Override
public String help() {
return help;
}
@Override
public boolean anyFlags() {
return !(flags.isEmpty() || flags.matches("[a-z]+"));
}
};
}
public static Command translate(Class clazz, final Command command) { public static Command translate(Class clazz, final Command command) {
if (cmdConfig == null || command instanceof TranslatedCommand) { if (cmdConfig == null || command instanceof TranslatedCommand) {
return command; return command;

View File

@ -76,8 +76,6 @@ public class Settings extends Config {
@Comment({ @Comment({
"Put any minecraft or mod jars for FAWE to be aware of block textures", "Put any minecraft or mod jars for FAWE to be aware of block textures",
}) })
public String PATTERNS = "patterns";
public String MASKS = "masks";
public String TEXTURES = "textures"; public String TEXTURES = "textures";
public String HEIGHTMAP = "heightmap"; public String HEIGHTMAP = "heightmap";
public String HISTORY = "history"; public String HISTORY = "history";
@ -88,6 +86,7 @@ public class Settings extends Config {
public String CLIPBOARD = "clipboard"; public String CLIPBOARD = "clipboard";
@Comment("Each player has their own sub directory for schematics") @Comment("Each player has their own sub directory for schematics")
public boolean PER_PLAYER_SCHEMATICS = true; public boolean PER_PLAYER_SCHEMATICS = true;
public String COMMANDS = "commands";
} }
@Comment("Region restriction settings") @Comment("Region restriction settings")

View File

@ -21,10 +21,9 @@ import com.sk89q.worldedit.blocks.BlockMaterial;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.registry.BundledBlockData; import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.util.ArrayDeque;
import java.util.Collection; import java.lang.reflect.InvocationTargetException;
import java.util.HashSet; import java.util.*;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -409,7 +408,7 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, CHUNKSECTIONS, SECTION> impl
if (chunk != null) { if (chunk != null) {
return chunk; return chunk;
} }
boolean sync = Thread.currentThread() == Fawe.get().getMainThread(); boolean sync = Fawe.isMainThread();
if (sync) { if (sync) {
return loadChunk(getWorld(), cx, cz, true); return loadChunk(getWorld(), cx, cz, true);
} else if (getSettings().HISTORY.CHUNK_WAIT_MS > 0) { } else if (getSettings().HISTORY.CHUNK_WAIT_MS > 0) {

View File

@ -265,30 +265,34 @@ public abstract class FaweChangeSet implements ChangeSet {
int bz = cz << 4; int bz = cz << 4;
synchronized (FaweChangeSet.this) { synchronized (FaweChangeSet.this) {
// Biome changes // Biome changes
if (previous.getBiomeArray() != null) { {
byte[] previousBiomes = previous.getBiomeArray();
byte[] nextBiomes = next.getBiomeArray(); byte[] nextBiomes = next.getBiomeArray();
int index = 0; if (nextBiomes != null) {
for (int z = 0; z < 16; z++) { byte[] previousBiomes = previous.getBiomeArray();
int zz = bz + z; if (previousBiomes != null) {
for (int x = 0; x < 16; x++) {
byte idFrom = previousBiomes[index]; int index = 0;
byte idTo = nextBiomes[index]; for (int z = 0; z < 16; z++) {
if (idFrom != idTo && idTo != 0) { int zz = bz + z;
addBiomeChange(bx + x, zz, FaweCache.getBiome(idFrom & 0xFF), FaweCache.getBiome(idTo & 0xFF)); for (int x = 0; x < 16; x++) {
byte idFrom = previousBiomes[index];
byte idTo = nextBiomes[index];
if (idFrom != idTo && idTo != 0) {
addBiomeChange(bx + x, zz, FaweCache.getBiome(idFrom & 0xFF), FaweCache.getBiome(idTo & 0xFF));
}
index++;
}
} }
index++;
} }
} }
// TODO
} }
// Block changes // Block changes
for (int layer = 0; layer < layers; layer++) { for (int layer = 0; layer < layers; layer++) {
char[] currentLayer = next.getIdArray(layer); char[] currentLayer = next.getIdArray(layer);
char[] previousLayer = previous.getIdArray(layer);
if (currentLayer == null) { if (currentLayer == null) {
continue; continue;
} }
char[] previousLayer = previous.getIdArray(layer);
int startY = layer << 4; int startY = layer << 4;
int index = 0; int index = 0;
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {

View File

@ -24,7 +24,7 @@ public class SummedColorTable {
this.hasAlpha = new int[raw.length]; this.hasAlpha = new int[raw.length];
this.alpha = calculateAlpha ? new long[raw.length] : null; this.alpha = calculateAlpha ? new long[raw.length] : null;
this.alphaInverse = calculateAlpha ? new float[256] : null; this.alphaInverse = calculateAlpha ? new float[256] : null;
this.areaInverses = new float[Character.MAX_VALUE]; this.areaInverses = new float[1024 * 1024];
for (int i = 0; i < areaInverses.length; i++) { for (int i = 0; i < areaInverses.length; i++) {
areaInverses[i] = 1f / (i + 1); areaInverses[i] = 1f / (i + 1);
} }

View File

@ -14,6 +14,8 @@ import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -123,6 +125,12 @@ public class NullExtent extends FaweRegionExtent {
return null; return null;
} }
@Nullable
@Override
public Operation commit() {
return null;
}
@Override @Override
public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) { public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
throw new FaweException(reason); throw new FaweException(reason);

View File

@ -28,8 +28,7 @@ public class StripNBTExtent extends AbstractDelegateExtent {
*/ */
public StripNBTExtent(Extent extent, Set<String> strip) { public StripNBTExtent(Extent extent, Set<String> strip) {
super(extent); super(extent);
this. this.strip = strip.toArray(new String[strip.size()]);
strip = strip.toArray(new String[strip.size()]);
} }
@Override @Override

View File

@ -48,7 +48,7 @@ public class AngleColorPattern extends DataAnglePattern {
int height = extent.getNearestSurfaceTerrainBlock(x, z, y, 0, maxY); int height = extent.getNearestSurfaceTerrainBlock(x, z, y, 0, maxY);
if (height > 0) { if (height > 0) {
BaseBlock below = extent.getLazyBlock(x, height - 1, z); BaseBlock below = extent.getLazyBlock(x, height - 1, z);
if (FaweCache.canPassThrough(block.getId(), block.getData())) { if (FaweCache.canPassThrough(below.getId(), below.getData())) {
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
} }
} }

View File

@ -139,7 +139,7 @@ public class StructureFormat implements ClipboardReader, ClipboardWriter {
Map<String, Tag> entityEntryMap = entityEntry.getValue(); Map<String, Tag> entityEntryMap = entityEntry.getValue();
ListTag posTag = (ListTag) entityEntryMap.get("pos"); ListTag posTag = (ListTag) entityEntryMap.get("pos");
CompoundTag nbtTag = (CompoundTag) entityEntryMap.get("nbt"); CompoundTag nbtTag = (CompoundTag) entityEntryMap.get("nbt");
String id = ((StringTag) entityEntryMap.get("Id")).getValue(); String id = nbtTag.getString("Id");
Location location = NBTConversions.toLocation(clipboard, posTag, nbtTag.getListTag("Rotation")); Location location = NBTConversions.toLocation(clipboard, posTag, nbtTag.getListTag("Rotation"));
if (!id.isEmpty()) { if (!id.isEmpty()) {
BaseEntity state = new BaseEntity(id, nbtTag); BaseEntity state = new BaseEntity(id, nbtTag);

View File

@ -585,7 +585,7 @@ public class MainUtil {
long ratio = total / compressedSize; long ratio = total / compressedSize;
long saved = total - compressedSize; long saved = total - compressedSize;
if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null) { if (ratio > 3 && !Fawe.isMainThread() && actor != null) {
BBC.COMPRESSED.send(actor, saved, ratio); BBC.COMPRESSED.send(actor, saved, ratio);
} }
} catch (Exception e) { } catch (Exception e) {

View File

@ -10,6 +10,8 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.IntConsumer;
import java.util.function.IntFunction;
public class StringMan { public class StringMan {
public static String replaceFromMap(final String string, final Map<String, String> replacements) { public static String replaceFromMap(final String string, final Map<String, String> replacements) {
@ -33,6 +35,85 @@ public class StringMan {
return sb.toString(); return sb.toString();
} }
public static boolean containsAny(CharSequence sequence, String any) {
for (int i = 0; i < sequence.length(); i++) {
if (any.indexOf(sequence.charAt(i)) != -1) return true;
}
return false;
}
public static int findMatchingBracket(CharSequence sequence, int index) {
char startC = sequence.charAt(index);
char lookC = getMatchingBracket(startC);
if (lookC == startC) return -1;
boolean forward = isBracketForwards(startC);
int increment = forward ? 1 : -1;
int end = forward ? sequence.length() : -1;
int count = 0;
for (int i = index + increment; i != end; i += increment) {
char c = sequence.charAt(i);
if (c == startC) {
count++;
} else if (c == lookC && count-- == 0) {
return i;
}
}
return -1;
}
public static String prettyFormat(double d) {
if (d == Double.MIN_VALUE) return "-∞";
if (d == Double.MAX_VALUE) return "";
if(d == (long) d) return String.format("%d",(long)d);
else return String.format("%s",d);
}
public static boolean isBracketForwards(char c) {
switch (c) {
case '[':
case '(':
case '{':
case '<':
return true;
default: return false;
}
}
public static char getMatchingBracket(char c) {
switch (c) {
case '[': return ']';
case '(': return ')';
case '{': return '}';
case '<': return '>';
case ']': return '[';
case ')': return '(';
case '}': return '{';
case '>': return '<';
default: return c;
}
}
public static int parseInt(CharSequence string) {
int val = 0;
boolean neg = false;
int numIndex = 1;
int len = string.length();
outer:
for (int i = len - 1; i >= 0; i--) {
char c = string.charAt(i);
switch (c) {
case '-':
val = -val;
break;
default:
val = val + (c - 48) * numIndex;
numIndex *= 10;
break;
}
}
return val;
}
public static String removeFromSet(final String string, final Collection<String> replacements) { public static String removeFromSet(final String string, final Collection<String> replacements) {
final StringBuilder sb = new StringBuilder(string); final StringBuilder sb = new StringBuilder(string);
int size = string.length(); int size = string.length();
@ -189,7 +270,7 @@ public class StringMan {
return true; return true;
} }
public static boolean isAlphanumericUnd(final String str) { public static boolean isAlphanumericUnd(final CharSequence str) {
for (int i = 0; i < str.length(); i++) { for (int i = 0; i < str.length(); i++) {
final char c = str.charAt(i); final char c = str.charAt(i);
if ((c < 0x30) || ((c >= 0x3a) && (c <= 0x40)) || ((c > 0x5a) && (c <= 0x60)) || (c > 0x7a) || (c == '_')) { if ((c < 0x30) || ((c >= 0x3a) && (c <= 0x40)) || ((c > 0x5a) && (c <= 0x60)) || (c > 0x7a) || (c == '_')) {

View File

@ -188,7 +188,7 @@ public abstract class TaskManager {
if (r == null) { if (r == null) {
return; return;
} }
if (Thread.currentThread() == Fawe.get().getMainThread()) { if (Fawe.isMainThread()) {
r.run(); r.run();
} else { } else {
task(r); task(r);
@ -336,7 +336,7 @@ public abstract class TaskManager {
* @return * @return
*/ */
public <T> T syncWhenFree(final RunnableVal<T> function, int timeout) { public <T> T syncWhenFree(final RunnableVal<T> function, int timeout) {
if (Fawe.get().getMainThread() == Thread.currentThread()) { if (Fawe.isMainThread()) {
function.run(); function.run();
return function.value; return function.value;
} }
@ -389,7 +389,7 @@ public abstract class TaskManager {
} }
public <T> T sync(final Supplier<T> function, int timeout) { public <T> T sync(final Supplier<T> function, int timeout) {
if (Fawe.get().getMainThread() == Thread.currentThread()) { if (Fawe.isMainThread()) {
return function.get(); return function.get();
} }
final AtomicBoolean running = new AtomicBoolean(true); final AtomicBoolean running = new AtomicBoolean(true);

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.util.image;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.StringMan;
import com.sk89q.worldedit.util.command.parametric.ParameterException; import com.sk89q.worldedit.util.command.parametric.ParameterException;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.RenderingHints; import java.awt.RenderingHints;
@ -16,6 +17,13 @@ import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ImageUtil { public class ImageUtil {
public static BufferedImage getScaledInstance(BufferedImage img, public static BufferedImage getScaledInstance(BufferedImage img,
@ -208,7 +216,6 @@ public class ImageUtil {
} }
} }
public static URI getImageURI(String arg) throws ParameterException { public static URI getImageURI(String arg) throws ParameterException {
try { try {
if (arg.startsWith("http")) { if (arg.startsWith("http")) {

View File

@ -363,19 +363,6 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue,
this(world, null, null, null, null, null, true, null, null, null, blockBag, eventBus, event); this(world, null, null, null, null, null, true, null, null, null, blockBag, eventBus, event);
} }
/**
* Lazily copy a region
*
* @param region
* @return
*/
public BlockArrayClipboard lazyCopy(Region region) {
WorldCopyClipboard faweClipboard = new WorldCopyClipboard(this, region);
BlockArrayClipboard weClipboard = new BlockArrayClipboard(region, faweClipboard);
weClipboard.setOrigin(region.getMinimumPoint());
return weClipboard;
}
/** /**
* The limit for this specific edit (blocks etc) * The limit for this specific edit (blocks etc)
* *
@ -2865,9 +2852,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue,
distribution.add(new Countable<BaseBlock>(FaweCache.CACHE_BLOCK[i], count)); distribution.add(new Countable<BaseBlock>(FaweCache.CACHE_BLOCK[i], count));
} }
} }
Collections.sort(distribution);
// Collections.reverse(distribution); // Collections.reverse(distribution);
return distribution; return distribution;
} }

View File

@ -36,6 +36,7 @@ import com.boydti.fawe.object.extent.ResettableExtent;
import com.boydti.fawe.util.*; import com.boydti.fawe.util.*;
import com.boydti.fawe.util.cui.CUI; import com.boydti.fawe.util.cui.CUI;
import com.boydti.fawe.wrappers.WorldWrapper; import com.boydti.fawe.wrappers.WorldWrapper;
import com.intellectualcrafters.plot.object.PlotArea;
import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Chronic;
import com.sk89q.jchronic.Options; import com.sk89q.jchronic.Options;
import com.sk89q.jchronic.utils.Span; import com.sk89q.jchronic.utils.Span;
@ -1170,7 +1171,7 @@ public class LocalSession implements TextureHolder {
if (hasCUISupport) { if (hasCUISupport) {
actor.dispatchCUIEvent(event); actor.dispatchCUIEvent(event);
} else { } else if (actor.isPlayer()) {
CUI cui = Fawe.get().getCUI(actor); CUI cui = Fawe.get().getCUI(actor);
if (cui != null) cui.dispatchCUIEvent(event); if (cui != null) cui.dispatchCUIEvent(event);
} }

View File

@ -8,7 +8,8 @@ import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.CommandManager; import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.util.command.*; import com.sk89q.worldedit.util.command.*;
import com.sk89q.worldedit.util.command.parametric.ParametricCallable; import com.sk89q.worldedit.util.command.parametric.AParametricCallable;
import java.util.*; import java.util.*;
public abstract class HelpBuilder implements Runnable { public abstract class HelpBuilder implements Runnable {
@ -69,13 +70,12 @@ public abstract class HelpBuilder implements Runnable {
if (c instanceof DelegateCallable) { if (c instanceof DelegateCallable) {
c = ((DelegateCallable) c).getParent(); c = ((DelegateCallable) c).getParent();
} }
if (c instanceof ParametricCallable) { if (c instanceof AParametricCallable) {
Object obj = ((ParametricCallable) c).getObject(); Command command = ((AParametricCallable) c).getCommand();
Command command = obj.getClass().getAnnotation(Command.class);
if (command != null && command.aliases().length != 0) { if (command != null && command.aliases().length != 0) {
group = command.aliases()[0]; group = command.aliases()[0];
} else { } else {
group = obj.getClass().getSimpleName().replaceAll("Commands", "").replaceAll("Util$", ""); group = ((AParametricCallable) c).getGroup();
} }
} else if (c instanceof Dispatcher) { } else if (c instanceof Dispatcher) {
group = mapping.getPrimaryAlias(); group = mapping.getPrimaryAlias();

View File

@ -1,5 +1,8 @@
package com.sk89q.worldedit.command; package com.sk89q.worldedit.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.DataAnglePattern; import com.boydti.fawe.object.DataAnglePattern;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.clipboard.MultiClipboardHolder; import com.boydti.fawe.object.clipboard.MultiClipboardHolder;
@ -8,11 +11,11 @@ import com.boydti.fawe.object.pattern.*;
import com.boydti.fawe.object.random.SimplexRandom; import com.boydti.fawe.object.random.SimplexRandom;
import com.boydti.fawe.util.ColorUtil; import com.boydti.fawe.util.ColorUtil;
import com.boydti.fawe.util.TextureUtil; import com.boydti.fawe.util.TextureUtil;
import com.boydti.fawe.wrappers.LocationMaskedPlayerWrapper;
import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.worldedit.EmptyClipboardException; import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
@ -26,11 +29,13 @@ import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
import com.sk89q.worldedit.scripting.RhinoCraftScriptEngine;
import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Range;
import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.util.command.parametric.Optional;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import java.awt.Color; import java.awt.Color;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;

View File

@ -45,6 +45,7 @@ import com.sk89q.worldedit.event.extent.PlayerSaveClipboardEvent;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.ClipboardFormats;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.operation.Operations;
@ -54,6 +55,8 @@ import com.sk89q.worldedit.util.command.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.util.command.parametric.Optional;
import com.sk89q.worldedit.util.io.file.FilenameException; import com.sk89q.worldedit.util.io.file.FilenameException;
import com.sk89q.worldedit.world.registry.WorldData; import com.sk89q.worldedit.world.registry.WorldData;
import javax.annotation.Nullable;
import java.io.*; import java.io.*;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@ -69,6 +72,7 @@ import java.util.regex.Pattern;
import static com.boydti.fawe.util.ReflectionUtils.as; import static com.boydti.fawe.util.ReflectionUtils.as;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* Commands that work with schematic files. * Commands that work with schematic files.
@ -187,16 +191,26 @@ public class SchematicCommands extends MethodCommands {
player.print(BBC.getPrefix() + "Remapped schematic"); player.print(BBC.getPrefix() + "Remapped schematic");
} }
private File resolve(File dir, String filename, @Nullable ClipboardFormat format) {
if (format != null) {
if (!filename.matches(".*\\.[\\w].*")) {
filename = filename + "." + format.getExtension();
}
return MainUtil.resolveRelative(new File(dir, filename));
}
for (ClipboardFormat f : ClipboardFormat.values()) {
File file = MainUtil.resolveRelative(new File(dir, filename + "." + f.getExtension()));
if (file.exists()) return file;
}
return null;
}
@Command(aliases = {"load"}, usage = "[<format>] <filename>", desc = "Load a schematic into your clipboard") @Command(aliases = {"load"}, usage = "[<format>] <filename>", desc = "Load a schematic into your clipboard")
@Deprecated @Deprecated
@CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load", "worldedit.schematic.upload", "worldedit.schematic.load.other"}) @CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load", "worldedit.schematic.upload", "worldedit.schematic.load.other"})
public void load(final Player player, final LocalSession session, @Optional("schematic") final String formatName, String filename) throws FilenameException { public void load(final Player player, final LocalSession session, @Optional() final String formatName, String filename) throws FilenameException {
final LocalConfiguration config = this.worldEdit.getConfiguration(); final LocalConfiguration config = this.worldEdit.getConfiguration();
final ClipboardFormat format = ClipboardFormat.findByAlias(formatName); ClipboardFormat format = formatName == null ? null : ClipboardFormat.findByAlias(formatName);
if (format == null) {
BBC.CLIPBOARD_INVALID_FORMAT.send(player, formatName);
return;
}
InputStream in = null; InputStream in = null;
try { try {
URI uri; URI uri;
@ -220,8 +234,14 @@ public class SchematicCommands extends MethodCommands {
File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working; File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working;
File f; File f;
if (filename.startsWith("#")) { if (filename.startsWith("#")) {
f = player.openFileOpenDialog(new String[] { format.getExtension() }); String[] extensions;
if (!f.exists()) { if (format != null) {
extensions = new String[] { format.getExtension() };
} else {
extensions = ClipboardFormats.getFileExtensionArray();
}
f = player.openFileOpenDialog(extensions);
if (f == null || !f.exists()) {
player.printError("Schematic " + filename + " does not exist! (" + f + ")"); player.printError("Schematic " + filename + " does not exist! (" + f + ")");
return; return;
} }
@ -230,32 +250,30 @@ public class SchematicCommands extends MethodCommands {
BBC.NO_PERM.send(player, "worldedit.schematic.load.other"); BBC.NO_PERM.send(player, "worldedit.schematic.load.other");
return; return;
} }
if (!filename.matches(".*\\.[\\w].*")) { if (format == null && filename.matches(".*\\.[\\w].*")) {
filename += "." + format.getExtension(); String extension = filename.substring(filename.lastIndexOf('.') + 1, filename.length());
format = ClipboardFormat.findByExtension(extension);
} }
f = MainUtil.resolveRelative(new File(dir, filename)); f = resolve(dir, filename, format);
} }
if (f.getName().replaceAll("." + format.getExtension(), "").isEmpty()) { if (f == null || !f.exists()) {
File directory = f.getParentFile();
if (directory.exists()) {
int max = MainUtil.getMaxFileId(directory) - 1;
f = new File(directory, max + "." + format.getExtension());
} else {
f = new File(directory, "1." + format.getExtension());
}
}
if (!f.exists()) {
if (!filename.contains("../")) { if (!filename.contains("../")) {
dir = this.worldEdit.getWorkingDirectoryFile(config.saveDir); dir = this.worldEdit.getWorkingDirectoryFile(config.saveDir);
f = this.worldEdit.getSafeSaveFile(player, dir, filename, format.getExtension(), format.getExtension()); f = resolve(dir, filename, format);
} }
} }
if (!f.exists() || !MainUtil.isInSubDirectory(working, f)) { if (f == null || !f.exists() || !MainUtil.isInSubDirectory(working, f)) {
player.printError("Schematic " + filename + " does not exist! (" + f.exists() + "|" + f + "|" + (!MainUtil.isInSubDirectory(working, f)) + ")"); player.printError("Schematic " + filename + " does not exist! (" + f.exists() + "|" + f + "|" + (!MainUtil.isInSubDirectory(working, f)) + ")");
return; return;
} }
if (format == null) {
format = ClipboardFormat.findByFile(f);
if (format == null) {
BBC.CLIPBOARD_INVALID_FORMAT.send(player, f.getName());
return;
}
}
in = new FileInputStream(f); in = new FileInputStream(f);
uri = f.toURI(); uri = f.toURI();
} }
format.hold(player, uri, in); format.hold(player, uri, in);

View File

@ -46,8 +46,8 @@ public class AreaPickaxe implements BlockTool {
editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop); editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop);
for (int x = ox - range; x <= ox + range; ++x) { for (int x = ox - range; x <= ox + range; ++x) {
for (int y = oy - range; y <= oy + range; ++y) { for (int z = oz - range; z <= oz + range; ++z) {
for (int z = oz - range; z <= oz + range; ++z) { for (int y = oy + range; y >= oy - range; --y) {
if (editSession.getLazyBlock(x, y, z).getId() != initialType) { if (editSession.getLazyBlock(x, y, z).getId() != initialType) {
continue; continue;
} }

View File

@ -1,8 +1,10 @@
package com.sk89q.worldedit.extension.factory; package com.sk89q.worldedit.extension.factory;
import com.boydti.fawe.command.FaweParser; import com.boydti.fawe.command.FaweParser;
import com.boydti.fawe.command.SuggestInputParseException;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.CommandLocals;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
@ -22,11 +24,13 @@ import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.SimpleDispatcher; import com.sk89q.worldedit.util.command.SimpleDispatcher;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DefaultMaskParser extends FaweParser<Mask> { public class DefaultMaskParser extends FaweParser<Mask> {
private final Dispatcher dispatcher; private final Dispatcher dispatcher;
@ -52,23 +56,27 @@ public class DefaultMaskParser extends FaweParser<Mask> {
@Override @Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException { public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
if (input.isEmpty()) return null; if (input.isEmpty()) {
throw new SuggestInputParseException("No input provided", "", () -> Stream.of("#", ",", "&").map(n -> n + ":").collect(Collectors.toList())
// TODO namespaces
);
}
Extent extent = Request.request().getExtent(); Extent extent = Request.request().getExtent();
if (extent == null) extent = context.getExtent(); if (extent == null) extent = context.getExtent();
HashSet<BaseBlock> blocks = new HashSet<BaseBlock>(); List<List<Mask>> masks = new ArrayList<>();
List<Mask> intersection = new ArrayList<>(); masks.add(new ArrayList<>());
List<Mask> union = new ArrayList<>();
final CommandLocals locals = new CommandLocals(); final CommandLocals locals = new CommandLocals();
Actor actor = context != null ? context.getActor() : null; Actor actor = context != null ? context.getActor() : null;
if (actor != null) { if (actor != null) {
locals.put(Actor.class, actor); locals.put(Actor.class, actor);
} }
//
try { try {
List<Map.Entry<ParseEntry, List<String>>> parsed = parse(input); List<Map.Entry<ParseEntry, List<String>>> parsed = parse(input);
for (Map.Entry<ParseEntry, List<String>> entry : parsed) { for (Map.Entry<ParseEntry, List<String>> entry : parsed) {
ParseEntry pe = entry.getKey(); ParseEntry pe = entry.getKey();
String command = pe.input; final String command = pe.input;
String full = pe.full;
Mask mask = null; Mask mask = null;
if (command.isEmpty()) { if (command.isEmpty()) {
mask = parseFromInput(StringMan.join(entry.getValue(), ','), context); mask = parseFromInput(StringMan.join(entry.getValue(), ','), context);
@ -79,101 +87,116 @@ public class DefaultMaskParser extends FaweParser<Mask> {
if (charMask && input.charAt(0) == '=') { if (charMask && input.charAt(0) == '=') {
return parseFromInput(char0 + "[" + input.substring(1) + "]", context); return parseFromInput(char0 + "[" + input.substring(1) + "]", context);
} }
if (mask == null) { if (char0 == '#') {
// Legacy syntax throw new SuggestInputParseException(new NoMatchException("Unkown mask: " + full + ", See: //masks"), full,
if (charMask) { () -> {
switch (char0) { if (full.length() == 1) return new ArrayList<>(dispatcher.getPrimaryAliases());
case '\\': // return dispatcher.getAliases().stream().filter(
case '/': // s -> s.startsWith(command.toLowerCase())
case '{': // ).collect(Collectors.toList());
case '$': //
case '%': {
command = command.substring(1);
String value = command + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]");
if (value.contains(":")) {
if (value.charAt(0) == ':') value.replaceFirst(":", "");
value = value.replaceAll(":", "][");
}
mask = parseFromInput(char0 + "[" + value + "]", context);
break;
} }
case '|': );
case '~': }
case '<': // Legacy syntax
case '>': if (charMask) {
case '!': switch (char0) {
input = input.substring(input.indexOf(char0) + 1); case '\\': //
mask = parseFromInput(char0 + "[" + input + "]", context); case '/': //
if (actor != null) { case '{': //
BBC.COMMAND_CLARIFYING_BRACKET.send(actor, char0 + "[" + input + "]"); case '$': //
} case '%': {
return mask; String value = command.substring(1) + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]");
if (value.contains(":")) {
if (value.charAt(0) == ':') value.replaceFirst(":", "");
value = value.replaceAll(":", "][");
}
mask = parseFromInput("#" + char0 + "[" + value + "]", context);
break;
} }
case '|':
case '~':
case '<':
case '>':
case '!':
input = input.substring(input.indexOf(char0) + 1);
mask = parseFromInput(char0 + "[" + input + "]", context);
if (actor != null) {
BBC.COMMAND_CLARIFYING_BRACKET.send(actor, char0 + "[" + input + "]");
}
return mask;
} }
if (mask == null) { }
if (command.startsWith("[")) { if (mask == null) {
int end = command.lastIndexOf(']'); if (command.startsWith("[")) {
mask = parseFromInput(command.substring(1, end == -1 ? command.length() : end), context); int end = command.lastIndexOf(']');
} else { mask = parseFromInput(command.substring(1, end == -1 ? command.length() : end), context);
try { } else {
context.setPreferringWildcard(true); context.setPreferringWildcard(true);
context.setRestricted(false); context.setRestricted(false);
BaseBlock block = worldEdit.getBlockFactory().parseFromInput(command, context); BaseBlock block = worldEdit.getBlockFactory().parseFromInput(command, context);
if (pe.and) { mask = new BlockMask(extent, block);
mask = new BlockMask(extent, block);
} else {
blocks.add(block);
continue;
}
} catch (NoMatchException e) {
throw new NoMatchException(e.getMessage() + " See: //masks");
}
}
} }
} }
} else { } else {
List<String> args = entry.getValue(); List<String> args = entry.getValue();
if (!args.isEmpty()) { String cmdArgs = ((args.isEmpty()) ? "" : " " + StringMan.join(args, " "));
command += " " + StringMan.join(args, " "); try {
mask = (Mask) dispatcher.call(command + cmdArgs, locals, new String[0]);
} catch (Throwable e) {
throw SuggestInputParseException.of(e, full, () -> {
try {
List<String> suggestions = dispatcher.get(command).getCallable().getSuggestions(cmdArgs, locals);
if (suggestions.size() <= 2) {
for (int i = 0; i < suggestions.size(); i++) {
String suggestion = suggestions.get(i);
if (suggestion.indexOf(' ') != 0) {
String[] split = suggestion.split(" ");
suggestion = BBC.color("[" + StringMan.join(split, "][") + "]");
suggestions.set(i, suggestion);
}
}
}
return suggestions;
} catch (CommandException e1) {
throw new InputParseException(e1.getMessage());
} catch (Throwable e2) {
e2.printStackTrace();
throw new InputParseException(e2.getMessage());
}
});
} }
mask = (Mask) dispatcher.call(command, locals, new String[0]);
} }
if (pe.and) { // & if (pe.and) {
intersection.add(mask); masks.add(new ArrayList<>());
} else {
if (!intersection.isEmpty()) {
if (intersection.size() == 1) {
throw new InputParseException("Error, floating &");
}
union.add(new MaskIntersection(intersection));
intersection.clear();
}
union.add(mask);
} }
masks.get(masks.size() - 1).add(mask);
} }
} catch (InputParseException rethrow) {
throw rethrow;
} catch (Throwable e) { } catch (Throwable e) {
InputParseException ips = SuggestInputParseException.find(e);
if (ips != null) throw ips;
e.printStackTrace();
throw new InputParseException(e.getMessage(), e); throw new InputParseException(e.getMessage(), e);
} }
if (!blocks.isEmpty()) { List<Mask> maskUnions = new ArrayList<>();
union.add(new BlockMask(extent, blocks)); for (List<Mask> maskList : masks) {
} if (maskList.size() == 1) {
if (!intersection.isEmpty()) { maskUnions.add(maskList.get(0));
if (intersection.size() == 1) { } else if (maskList.size() != 0) {
throw new InputParseException("Error, floating &"); maskUnions.add(new MaskUnion(maskList));
} }
union.add(new MaskIntersection(intersection));
intersection.clear();
} }
if (union.isEmpty()) { if (maskUnions.size() == 1) {
return null; return maskUnions.get(0);
} else if (union.size() == 1) { } else if (maskUnions.size() != 0) {
return union.get(0); return new MaskIntersection(maskUnions);
} else { } else {
return new MaskUnion(union); return null;
} }
} }
public static Class<?> inject() { public static Class<DefaultMaskParser> inject() {
return DefaultMaskParser.class; return DefaultMaskParser.class;
} }
} }

View File

@ -1,6 +1,8 @@
package com.sk89q.worldedit.extension.factory; package com.sk89q.worldedit.extension.factory;
import com.boydti.fawe.command.FaweParser; import com.boydti.fawe.command.FaweParser;
import com.boydti.fawe.command.SuggestInputParseException;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.random.TrueRandom; import com.boydti.fawe.object.random.TrueRandom;
import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandException;
@ -16,13 +18,15 @@ import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.internal.command.ActorAuthorizer; import com.sk89q.worldedit.internal.command.ActorAuthorizer;
import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.internal.command.WorldEditBinding;
import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.SimpleDispatcher; import com.sk89q.worldedit.util.command.SimpleDispatcher;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class HashTagPatternParser extends FaweParser<Pattern> { public class HashTagPatternParser extends FaweParser<Pattern> {
private final Dispatcher dispatcher; private final Dispatcher dispatcher;
@ -47,7 +51,10 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
@Override @Override
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
if (input.isEmpty()) return null; if (input.isEmpty()) {
throw new SuggestInputParseException("No input provided", "", () -> Stream.of("#", ",", "&").map(n -> n + ":").collect(Collectors.toList()));
// TODO namespace
}
List<Double> chances = new ArrayList<>(); List<Double> chances = new ArrayList<>();
List<Pattern> patterns = new ArrayList<>(); List<Pattern> patterns = new ArrayList<>();
final CommandLocals locals = new CommandLocals(); final CommandLocals locals = new CommandLocals();
@ -58,7 +65,8 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
try { try {
for (Map.Entry<ParseEntry, List<String>> entry : parse(input)) { for (Map.Entry<ParseEntry, List<String>> entry : parse(input)) {
ParseEntry pe = entry.getKey(); ParseEntry pe = entry.getKey();
String command = pe.input; final String command = pe.input;
String full = pe.full;
Pattern pattern = null; Pattern pattern = null;
double chance = 1; double chance = 1;
if (command.isEmpty()) { if (command.isEmpty()) {
@ -70,11 +78,22 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
if (charMask && input.charAt(0) == '=') { if (charMask && input.charAt(0) == '=') {
return parseFromInput(char0 + "[" + input.substring(1) + "]", context); return parseFromInput(char0 + "[" + input.substring(1) + "]", context);
} }
if (char0 == '#') {
throw new SuggestInputParseException(new NoMatchException("Unkown pattern: " + full + ", See: //patterns"), full,
() -> {
if (full.length() == 1) return new ArrayList<>(dispatcher.getPrimaryAliases());
return dispatcher.getAliases().stream().filter(
s -> s.startsWith(command.toLowerCase())
).collect(Collectors.toList());
}
);
}
if (charMask) { if (charMask) {
switch (char0) { switch (char0) {
case '$': { case '$': {
command = command.substring(1); String value = command.substring(1) + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]");
String value = command + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]");
if (value.contains(":")) { if (value.contains(":")) {
if (value.charAt(0) == ':') value.replaceFirst(":", ""); if (value.charAt(0) == ':') value.replaceFirst(":", "");
value = value.replaceAll(":", "]["); value = value.replaceAll(":", "][");
@ -92,15 +111,15 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
int percentIndex = command.indexOf('%'); int percentIndex = command.indexOf('%');
if (percentIndex != -1) { // Legacy percent pattern if (percentIndex != -1) { // Legacy percent pattern
chance = Expression.compile(command.substring(0, percentIndex)).evaluate(); chance = Expression.compile(command.substring(0, percentIndex)).evaluate();
command = command.substring(percentIndex + 1); String value = command.substring(percentIndex + 1);
if (!entry.getValue().isEmpty()) { if (!entry.getValue().isEmpty()) {
if (!command.isEmpty()) command += " "; if (!value.isEmpty()) value += " ";
command += StringMan.join(entry.getValue(), " "); value += StringMan.join(entry.getValue(), " ");
} }
pattern = parseFromInput(command, context); pattern = parseFromInput(value, context);
} else { // legacy block pattern } else { // legacy block pattern
try { try {
pattern = worldEdit.getBlockFactory().parseFromInput(command, context); pattern = worldEdit.getBlockFactory().parseFromInput(pe.full, context);
} catch (NoMatchException e) { } catch (NoMatchException e) {
throw new NoMatchException(e.getMessage() + " See: //patterns"); throw new NoMatchException(e.getMessage() + " See: //patterns");
} }
@ -109,18 +128,45 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
} }
} else { } else {
List<String> args = entry.getValue(); List<String> args = entry.getValue();
if (!args.isEmpty()) { String cmdArgs = ((args.isEmpty()) ? "" : " " + StringMan.join(args, " "));
command += " " + StringMan.join(args, " "); try {
pattern = (Pattern) dispatcher.call(command + cmdArgs, locals, new String[0]);
} catch (Throwable e) {
throw SuggestInputParseException.of(e, full, () -> {
try {
List<String> suggestions = dispatcher.get(command).getCallable().getSuggestions(cmdArgs, locals);
if (suggestions.size() <= 2) {
for (int i = 0; i < suggestions.size(); i++) {
String suggestion = suggestions.get(i);
if (suggestion.indexOf(' ') != 0) {
String[] split = suggestion.split(" ");
suggestion = BBC.color("[" + StringMan.join(split, "][") + "]");
suggestions.set(i, suggestion);
}
}
}
return suggestions;
} catch (CommandException e1) {
throw new InputParseException(e1.getMessage());
} catch (Throwable e2) {
e2.printStackTrace();
throw new InputParseException(e2.getMessage());
}
});
} }
pattern = (Pattern) dispatcher.call(command, locals, new String[0]);
} }
if (pattern != null) { if (pattern != null) {
patterns.add(pattern); patterns.add(pattern);
chances.add(chance); chances.add(chance);
} }
} }
} catch (CommandException | ExpressionException e) { } catch (InputParseException rethrow) {
throw new RuntimeException(e); throw rethrow;
} catch (Throwable e) {
InputParseException ips = SuggestInputParseException.find(e);
if (ips != null) throw ips;
e.printStackTrace();
throw new InputParseException(e.getMessage(), e);
} }
if (patterns.isEmpty()) { if (patterns.isEmpty()) {
return null; return null;
@ -135,7 +181,8 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
} }
} }
public static Class<?> inject() {
public static Class<HashTagPatternParser> inject() {
return HashTagPatternParser.class; return HashTagPatternParser.class;
} }
} }

View File

@ -48,17 +48,14 @@ import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.function.factory.Deform; import com.sk89q.worldedit.function.factory.Deform;
import com.sk89q.worldedit.function.factory.Deform.Mode; import com.sk89q.worldedit.function.factory.Deform.Mode;
import com.sk89q.worldedit.internal.command.*; import com.sk89q.worldedit.internal.command.*;
import com.sk89q.worldedit.scripting.CommandScriptLoader;
import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.auth.AuthorizationException; import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.*;
import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.InvalidUsageException;
import com.sk89q.worldedit.util.command.composition.ProvidedValue; import com.sk89q.worldedit.util.command.composition.ProvidedValue;
import com.sk89q.worldedit.util.command.fluent.CommandGraph; import com.sk89q.worldedit.util.command.fluent.CommandGraph;
import com.sk89q.worldedit.util.command.fluent.DispatcherNode; import com.sk89q.worldedit.util.command.fluent.DispatcherNode;
import com.sk89q.worldedit.util.command.parametric.ExceptionConverter; import com.sk89q.worldedit.util.command.parametric.*;
import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.util.eventbus.Subscribe;
import com.sk89q.worldedit.util.logging.DynamicStreamHandler; import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
import com.sk89q.worldedit.util.logging.LogFormat; import com.sk89q.worldedit.util.logging.LogFormat;
@ -184,13 +181,13 @@ public final class CommandManager {
* @param clazz The class containing all the sub command methods * @param clazz The class containing all the sub command methods
* @param aliases The aliases to give the command * @param aliases The aliases to give the command
*/ */
public void registerCommands(Object clazz, Object processor, String... aliases) { public void registerCommands(Object clazz, CallableProcessor processor, String... aliases) {
if (platform != null) { if (platform != null) {
if (aliases.length == 0) { if (aliases.length == 0) {
builder.registerMethodsAsCommands(dispatcher, clazz); builder.registerMethodsAsCommands(dispatcher, clazz, processor);
} else { } else {
DispatcherNode graph = new CommandGraph().builder(builder).commands(); DispatcherNode graph = new CommandGraph().builder(builder).commands();
graph = graph.registerMethods(clazz); graph = graph.registerMethods(clazz, processor);
dispatcher.registerCommand(graph.graph().getDispatcher(), aliases); dispatcher.registerCommand(graph.graph().getDispatcher(), aliases);
} }
platform.registerCommands(dispatcher); platform.registerCommands(dispatcher);
@ -244,6 +241,9 @@ public final class CommandManager {
} }
} }
commandMap.clear();
methodMap.clear();
dispatcher = graph dispatcher = graph
.group("/anvil") .group("/anvil")
.describeAs("Anvil command") .describeAs("Anvil command")
@ -295,6 +295,13 @@ public final class CommandManager {
public void register(Platform platform) { public void register(Platform platform) {
log.log(Level.FINE, "Registering commands with " + platform.getClass().getCanonicalName()); log.log(Level.FINE, "Registering commands with " + platform.getClass().getCanonicalName());
this.platform = null;
try {
new CommandScriptLoader().load();
} catch (Throwable e) {
e.printStackTrace();
}
LocalConfiguration config = platform.getConfiguration(); LocalConfiguration config = platform.getConfiguration();
boolean logging = config.logCommands; boolean logging = config.logCommands;
@ -403,6 +410,7 @@ public final class CommandManager {
// exceptions without writing a hook into every dispatcher, we need to unwrap these // exceptions without writing a hook into every dispatcher, we need to unwrap these
// exceptions and rethrow their converted form, if their is one. // exceptions and rethrow their converted form, if their is one.
try { try {
Request.request().setActor(finalActor);
Object result = dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]); Object result = dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]);
} catch (Throwable t) { } catch (Throwable t) {
// Use the exception converter to convert the exception if any of its causes // Use the exception converter to convert the exception if any of its causes
@ -485,6 +493,16 @@ public final class CommandManager {
TaskManager.IMP.taskNow(new Runnable() { TaskManager.IMP.taskNow(new Runnable() {
@Override @Override
public void run() { public void run() {
// int space0 = args.indexOf(' ');
// String arg0 = space0 == -1 ? args : args.substring(0, space0);
// CommandMapping cmd = dispatcher.get(arg0);
// if (cmd != null && cmd.getCallable() instanceof AParametricCallable) {
// Command info = ((AParametricCallable) cmd.getCallable()).getDefinition();
// if (!info.queued()) {
// handleCommandOnCurrentThread(finalEvent);
// return;
// }
// }
if (!fp.runAction(new Runnable() { if (!fp.runAction(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -522,7 +540,8 @@ public final class CommandManager {
return commandLog; return commandLog;
} }
public static Class<?> inject() {
public static Class<CommandManager> inject() {
return CommandManager.class; return CommandManager.class;
} }
} }

View File

@ -1,6 +1,8 @@
package com.sk89q.worldedit.extension.platform; package com.sk89q.worldedit.extension.platform;
import com.boydti.fawe.util.ReflectionUtils;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.sk89q.util.ReflectionUtil;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.WorldVector; import com.sk89q.worldedit.WorldVector;
@ -13,7 +15,13 @@ import com.sk89q.worldedit.internal.cui.CUIEvent;
import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class PlayerProxy extends AbstractPlayerActor { public class PlayerProxy extends AbstractPlayerActor {

View File

@ -213,9 +213,7 @@ public class AbstractDelegateExtent implements LightingExtent {
} }
@Override @Override
public final public @Nullable Operation commit() {
@Nullable
Operation commit() {
Operation ours = commitBefore(); Operation ours = commitBefore();
Operation other = null; Operation other = null;
if (extent != this) other = extent.commit(); if (extent != this) other = extent.commit();

View File

@ -0,0 +1,97 @@
/*
* 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.extent.clipboard;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import javax.annotation.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
public class ClipboardFormats {
/**
* Find the clipboard format named by the given alias.
*
* @param alias
* the alias
* @return the format, otherwise null if none is matched
*/
@Nullable
public static ClipboardFormat findByAlias(String alias) {
return ClipboardFormat.findByAlias(alias);
}
/**
* Detect the format of given a file.
*
* @param file
* the file
* @return the format, otherwise null if one cannot be detected
*/
@Nullable
public static ClipboardFormat findByFile(File file) {
checkNotNull(file);
for (ClipboardFormat format : ClipboardFormat.values()) {
if (format.isFormat(file)) {
return format;
}
}
return null;
}
/**
* @return a multimap from a file extension to the potential matching formats.
*/
public static Multimap<String, ClipboardFormat> getFileExtensionMap() {
HashMultimap<String, ClipboardFormat> map = HashMultimap.create();
for (ClipboardFormat format : ClipboardFormat.values()) {
map.put(format.getExtension(), format);
}
return map;
}
public static Collection<ClipboardFormat> getAll() {
return Arrays.asList(ClipboardFormat.values());
}
/**
* Not public API, only used by SchematicCommands.
* It is not in SchematicCommands because it may rely on internal register calls.
*/
public static String[] getFileExtensionArray() {
List<String> exts = new ArrayList<>();
HashMultimap<String, ClipboardFormat> map = HashMultimap.create();
for (ClipboardFormat format : ClipboardFormat.values()) {
exts.add(format.getExtension());
}
return exts.toArray(new String[exts.size()]);
}
private ClipboardFormats() {}
}

View File

@ -45,6 +45,7 @@ import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.extent.PlayerSaveClipboardEvent;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
@ -540,6 +541,18 @@ public enum ClipboardFormat {
return aliasMap.get(alias.toLowerCase().trim()); return aliasMap.get(alias.toLowerCase().trim());
} }
@Nullable
public static ClipboardFormat findByExtension(String extension) {
checkNotNull(extension);
extension = extension.toLowerCase();
for (ClipboardFormat format : values()) {
if (format.getExtension().equalsIgnoreCase(extension)) {
return format;
}
}
return null;
}
/** /**
* Detect the format given a file. * Detect the format given a file.
* *

View File

@ -0,0 +1,102 @@
package com.sk89q.worldedit.scripting;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.command.FaweParser;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.MainUtil;
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.BrushProcessor;
import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
import com.sk89q.worldedit.extension.factory.HashTagPatternParser;
import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.util.command.ProcessedCallable;
import com.sk89q.worldedit.util.command.parametric.FunctionParametricCallable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class CommandScriptLoader {
private final NashornCraftScriptEngine engine;
private final String loader;
public CommandScriptLoader() throws IOException {
this.engine = new NashornCraftScriptEngine();
try (InputStream inputStream = Fawe.class.getResourceAsStream("/cs_adv.js")) {
this.loader = CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));
}
}
/**
* Load all file commands
* @throws Throwable
*/
public void load() throws Throwable {
File commands = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.COMMANDS);
if (commands.exists()) {
for (File file : commands.listFiles()) add(new String[0], file);
}
}
private void add(String[] aliases, File file) throws Throwable {
if (file.isDirectory()) {
if (aliases.length == 0) {
String[] newAliases = new String[] {file.getName()};
for (File newFile : file.listFiles()) {
add(newAliases, newFile);
}
} else {
Fawe.debug("Ignoring nested directory: " + file);
}
} else {
String name = file.getName();
if (name.endsWith(".js")) {
Fawe.debug("Loading script: " + name);
List<FunctionParametricCallable> cmds = getCommands(file, Collections.emptyMap());
FaweParser parser = null;
if (aliases.length == 1) {
switch (aliases[0]) {
case "brush":
if (!cmds.isEmpty()) {
BrushProcessor processor = new BrushProcessor(WorldEdit.getInstance());
for (FunctionParametricCallable cmd : cmds) {
ProcessedCallable processed = new ProcessedCallable(cmd, processor);
CommandManager.getInstance().registerCommand(aliases, cmd.getCommand(), processed);
}
}
return;
case "patterns":
parser = FaweAPI.getParser(HashTagPatternParser.class);
break;
case "masks":
parser = FaweAPI.getParser(DefaultMaskParser.class);
break;
}
if (parser != null) {
for (FunctionParametricCallable cmd : cmds) {
parser.getDispatcher().registerCommand(cmd, cmd.getCommand().aliases());
}
return;
}
}
for (FunctionParametricCallable cmd : cmds) {
CommandManager.getInstance().registerCommand(aliases, cmd.getCommand(), cmd);
}
}
}
}
private List<FunctionParametricCallable> getCommands(File file, Map<String, Object> vars) throws Throwable {
String script = new String(Files.readAllBytes(file.toPath())) + loader;
return (List) engine.evaluate(script, file.getPath(), vars);
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.scripting;
import com.boydti.fawe.Fawe;
import com.sk89q.worldedit.WorldEditException;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import java.util.Map;
public class NashornCraftScriptEngine implements CraftScriptEngine {
private static NashornScriptEngineFactory FACTORY;
private int timeLimit;
@Override
public void setTimeLimit(int milliseconds) {
timeLimit = milliseconds;
}
@Override
public int getTimeLimit() {
return timeLimit;
}
@Override
public Object evaluate(String script, String filename, Map<String, Object> args) throws Throwable {
ClassLoader cl = Fawe.get().getClass().getClassLoader();
Thread.currentThread().setContextClassLoader(cl);
synchronized (NashornCraftScriptEngine.class) {
if (FACTORY == null) FACTORY = new NashornScriptEngineFactory();
}
;
ScriptEngine engine = FACTORY.getScriptEngine("--language=es6");
SimpleBindings bindings = new SimpleBindings();
for (Map.Entry<String, Object> entry : args.entrySet()) {
bindings.put(entry.getKey(), entry.getValue());
}
try {
Object result = engine.eval(script, bindings);
return result;
} catch (Error e) {
e.printStackTrace();
throw new ScriptException(e.getMessage());
} catch (Throwable e) {
e.printStackTrace();
while (e.getCause() != null) {
e = e.getCause();
}
if (e instanceof WorldEditException) {
throw e;
}
throw e;
} finally {
}
}
}

View File

@ -169,7 +169,6 @@ public class SessionManager {
checkNotNull(owner); checkNotNull(owner);
LocalSession session = getIfPresent(owner); LocalSession session = getIfPresent(owner);
LocalConfiguration config = worldEdit.getConfiguration();
SessionKey sessionKey = owner.getSessionKey(); SessionKey sessionKey = owner.getSessionKey();
// No session exists yet -- create one // No session exists yet -- create one
@ -182,12 +181,14 @@ public class SessionManager {
session = new LocalSession(); session = new LocalSession();
} }
LocalConfiguration config = worldEdit.getConfiguration();
session.setConfiguration(config); session.setConfiguration(config);
session.setBlockChangeLimit(config.defaultChangeLimit); session.setBlockChangeLimit(config.defaultChangeLimit);
sessions.put(getKey(owner), new SessionHolder(sessionKey, session)); sessions.put(getKey(owner), new SessionHolder(sessionKey, session));
} }
LocalConfiguration config = worldEdit.getConfiguration();
// Set the limit on the number of blocks that an operation can // Set the limit on the number of blocks that an operation can
// change at once, or don't if the owner has an override or there // change at once, or don't if the owner has an override or there
// is no limit. There is also a default limit // is no limit. There is also a default limit

View File

@ -21,7 +21,10 @@ package com.sk89q.worldedit.session.request;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.util.command.parametric.ParametricCallable;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -30,19 +33,16 @@ import javax.annotation.Nullable;
*/ */
public final class Request { public final class Request {
private static final ThreadLocal<Request> threadLocal = private static final ThreadLocal<Request> threadLocal = ThreadLocal.withInitial(Request::new);
new ThreadLocal<Request>() {
@Override
protected Request initialValue() {
return new Request();
}
};
private private
@Nullable @Nullable
World world; World world;
private private
@Nullable @Nullable
Actor actor;
private
@Nullable
LocalSession session; LocalSession session;
private private
@Nullable @Nullable
@ -87,6 +87,15 @@ public final class Request {
return null; return null;
} }
@Nullable
public Actor getActor() {
return actor;
}
public void setActor(@Nullable Actor actor) {
this.actor = actor;
}
/** /**
* Get the request session. * Get the request session.
* *
@ -143,7 +152,8 @@ public final class Request {
threadLocal.remove(); threadLocal.remove();
} }
public static Class<?> inject() {
public static Class<Request> inject() {
return Request.class; return Request.class;
} }
} }

View File

@ -72,6 +72,7 @@ public class SimpleDispatcher implements Dispatcher {
continue; continue;
} else { } else {
Fawe.debug("Replacing commands is currently undefined behavior: " + StringMan.getString(alias)); Fawe.debug("Replacing commands is currently undefined behavior: " + StringMan.getString(alias));
commands.put(lower, mapping);
continue; continue;
} }
} }

View File

@ -0,0 +1,310 @@
package com.sk89q.worldedit.util.command.parametric;
import com.boydti.fawe.command.SuggestInputParseException;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.util.chat.UsageMessage;
import com.sk89q.minecraft.util.commands.*;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.util.command.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
public abstract class AParametricCallable implements CommandCallable {
// private final ParametricBuilder builder;
// private ParameterData[] parameters;
// private Set<Character> valueFlags = new HashSet<Character>();
// private boolean anyFlags;
// private Set<Character> legacyFlags = new HashSet<Character>();
// private SimpleDescription description = new SimpleDescription();
// private String permission;
// private Command command;
public abstract ParameterData[] getParameters();
public abstract Set<Character> getValueFlags();
public abstract Set<Character> getLegacyFlags();
public abstract SimpleDescription getDescription();
public abstract String[] getPermissions();
public abstract ParametricBuilder getBuilder();
public abstract boolean anyFlags();
public abstract Command getCommand();
public Command getDefinition() {
return getCommand();
}
public abstract String getGroup();
@Override
public abstract String toString();
/**
* Get the right {@link ArgumentStack}.
*
* @param parameter the parameter
* @param existing the existing scoped context
* @return the context to use
*/
public static ArgumentStack getScopedContext(Parameter parameter, ArgumentStack existing) {
if (parameter.getFlag() != null) {
CommandContext context = existing.getContext();
if (parameter.isValueFlag()) {
return new StringArgumentStack(context, context.getFlag(parameter.getFlag()), false);
} else {
String v = context.hasFlag(parameter.getFlag()) ? "true" : "false";
return new StringArgumentStack(context, v, true);
}
}
return existing;
}
/**
* Get whether a parameter is allowed to consume arguments.
*
* @param i the index of the parameter
* @param scoped the scoped context
* @return true if arguments may be consumed
*/
public boolean mayConsumeArguments(int i, ContextArgumentStack scoped) {
CommandContext context = scoped.getContext();
ParameterData parameter = getParameters()[i];
// Flag parameters: Always consume
// Required non-flag parameters: Always consume
// Optional non-flag parameters:
// - Before required parameters: Consume if there are 'left over' args
// - At the end: Always consumes
if (parameter.isOptional()) {
if (parameter.getFlag() != null) {
return !parameter.isValueFlag() || context.hasFlag(parameter.getFlag());
} else {
int numberFree = context.argsLength() - scoped.position();
for (int j = i; j < getParameters().length; j++) {
if (getParameters()[j].isNonFlagConsumer() && !getParameters()[j].isOptional()) {
// We already checked if the consumed count was > -1
// when we created this object
numberFree -= getParameters()[j].getConsumedCount();
}
}
// Skip this optional parameter
if (numberFree < 1) {
return false;
}
}
}
return true;
}
/**
* Get the default value for a parameter.
*
* @param i the index of the parameter
* @param scoped the scoped context
* @return a value
* @throws ParameterException on an error
* @throws CommandException on an error
*/
public Object getDefaultValue(int i, ContextArgumentStack scoped) throws ParameterException, CommandException, InvocationTargetException {
CommandContext context = scoped.getContext();
ParameterData parameter = getParameters()[i];
String[] defaultValue = parameter.getDefaultValue();
if (defaultValue != null) {
try {
return parameter.getBinding().bind(parameter, new StringArgumentStack(context, defaultValue, false), false);
} catch (MissingParameterException e) {
throw new ParametricException(
"The default value of the parameter using the binding " +
parameter.getBinding().getClass() + " in the method\n" +
toString() + "\nis invalid");
}
}
return null;
}
/**
* Check to see if all arguments, including flag arguments, were consumed.
*
* @param scoped the argument scope
* @throws UnconsumedParameterException thrown if parameters were not consumed
*/
public void checkUnconsumed(ContextArgumentStack scoped) throws UnconsumedParameterException {
CommandContext context = scoped.getContext();
String unconsumed;
String unconsumedFlags = getUnusedFlags(context);
if ((unconsumed = scoped.getUnconsumed()) != null) {
throw new UnconsumedParameterException(unconsumed + " " + unconsumedFlags);
}
if (unconsumedFlags != null) {
throw new UnconsumedParameterException(unconsumedFlags);
}
}
/**
* Get any unused flag arguments.
*
* @param context the command context
*/
public String getUnusedFlags(CommandContext context) {
if (!anyFlags()) {
Set<Character> unusedFlags = null;
for (char flag : context.getFlags()) {
boolean found = false;
if (getLegacyFlags().contains(flag)) {
break;
}
for (ParameterData parameter : getParameters()) {
Character paramFlag = parameter.getFlag();
if (paramFlag != null && flag == paramFlag) {
found = true;
break;
}
}
if (!found) {
if (unusedFlags == null) {
unusedFlags = new HashSet<Character>();
}
unusedFlags.add(flag);
}
}
if (unusedFlags != null) {
StringBuilder builder = new StringBuilder();
for (Character flag : unusedFlags) {
builder.append("-").append(flag).append(" ");
}
return builder.toString().trim();
}
}
return null;
}
@Override
public boolean testPermission(CommandLocals locals) {
String[] perms = getPermissions();
if (perms != null && perms.length != 0) {
for (String perm : perms) {
if (getBuilder().getAuthorizer().testPermission(locals, perm)) {
return true;
}
}
return false;
} else {
return true;
}
}
@Override
public List<String> getSuggestions(String arguments, CommandLocals locals) throws CommandException {
String[] split = ("ignored" + " " + arguments).split(" ", -1);
// &a<current> &f<next>
// &cerrors
CommandContext context = new CommandContext(split, getValueFlags(), !arguments.endsWith(" "), locals);
ContextArgumentStack scoped = new ContextArgumentStack(context);
SuggestionContext suggestable = context.getSuggestionContext();
List<String> suggestions = new ArrayList<>(2);
ParameterData parameter = null;
ParameterData[] parameters = getParameters();
String consumed = "";
boolean hasSuggestion = false;
int maxConsumedI = 0; // The maximum argument index
int minConsumedI = 0; // The minimum argument that has been consumed
// Collect parameters
try {
for (;maxConsumedI < parameters.length; maxConsumedI++) {
parameter = parameters[maxConsumedI];
if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) {
// Parse the user input into a method argument
ArgumentStack usedArguments = getScopedContext(parameter, scoped);
usedArguments.mark();
try {
parameter.getBinding().bind(parameter, usedArguments, false);
minConsumedI = maxConsumedI + 1;
} catch (Throwable e) {
while (e.getCause() != null && !(e instanceof ParameterException || e instanceof InvocationTargetException)) e = e.getCause();
consumed = usedArguments.reset();
// Not optional? Then we can't execute this command
if (!parameter.isOptional()) {
if (!(e instanceof MissingParameterException)) minConsumedI = maxConsumedI;
throw e;
}
}
}
}
if (minConsumedI >= maxConsumedI && (parameter == null || parameter.getType() == CommandContext.class)) checkUnconsumed(scoped);
} catch (MissingParameterException ignore) {
} catch (UnconsumedParameterException e) {
suggestions.add(BBC.color("&cToo many parameters! Unused parameters: " + e.getUnconsumed()));
} catch (ParameterException e) {
String name = parameter.getName();
suggestions.add(BBC.color("&cFor parameter '" + name + "': " + e.getMessage()));
} catch (InvocationTargetException e) {
Throwable tmp = e;
SuggestInputParseException suggestion = SuggestInputParseException.get(tmp);
try {
if (suggestion != null && !suggestion.getSuggestions().isEmpty()) {
hasSuggestion = true;
suggestions.addAll(suggestion.getSuggestions());
} else {
Throwable t = tmp;
while (t.getCause() != null) t = t.getCause();
String msg = t.getMessage();
String name = parameter.getName();
if (msg != null && !msg.isEmpty())
suggestions.add(BBC.color("&cFor parameter '" + name + "': " + msg));
}
} catch (InputParseException e2) {
throw new WrappedCommandException(e2);
}
} catch (Throwable t) {
t.printStackTrace();
throw new WrappedCommandException(t);
}
// If there's 1 or less suggestions already, then show parameter suggestion
if (!hasSuggestion && suggestions.size() <= 1) {
StringBuilder suggestion = new StringBuilder();
outer:
for (String prefix = ""; minConsumedI < parameters.length; minConsumedI++) {
parameter = parameters[minConsumedI];
if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) {
suggestion.append(prefix);
List<String> argSuggestions = parameter.getBinding().getSuggestions(parameter, consumed);
switch (argSuggestions.size()) {
case 0:
break;
case 1:
suggestion.append(argSuggestions.iterator().next());
break;
default:
suggestion.setLength(0);
suggestions.addAll(argSuggestions);
break outer;
}
consumed = "";
prefix = " ";
}
}
if (suggestion.length() != 0) suggestions.add(suggestion.toString());
}
return suggestions;
}
}

View File

@ -139,7 +139,7 @@ public class ContextArgumentStack implements ArgumentStack {
*/ */
@Override @Override
public String reset() { public String reset() {
String value = context.getString(markedIndex, index - 1); String value = (index - 1 > markedIndex) ? context.getString(markedIndex, index - 1) : "";
index = markedIndex; index = markedIndex;
return value; return value;
} }

View File

@ -0,0 +1,328 @@
package com.sk89q.worldedit.util.command.parametric;
import com.boydti.fawe.util.StringMan;
import com.google.common.primitives.Chars;
import com.sk89q.minecraft.util.commands.*;
import com.sk89q.worldedit.util.command.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Function;
public class FunctionParametricCallable extends AParametricCallable {
private final ParametricBuilder builder;
private final ParameterData[] parameters;
private final Set<Character> valueFlags = new HashSet<Character>();
private final boolean anyFlags;
private final Set<Character> legacyFlags = new HashSet<Character>();
private final SimpleDescription description = new SimpleDescription();
private final String permission;
private final Command command;
private final Function<Object[], ?> function;
private final String group;
public FunctionParametricCallable(ParametricBuilder builder, String group, Command command, String permission, List<String> arguments, Function<Object[], ?> function) {
this.command = command;
this.permission = permission;
this.builder = builder;
this.function = function;
this.group = group;
List<Object[]> paramParsables = new ArrayList<>();
{
Map<Type, Binding> bindings = builder.getBindings();
Map<String, Type> unqualified = new HashMap<>();
for (Map.Entry<Type, Binding> entry : bindings.entrySet()) {
Type type = entry.getKey();
String typeStr = type.getTypeName();
unqualified.put(typeStr, type);
unqualified.put(typeStr.substring(typeStr.lastIndexOf('.') + 1), type);
}
{
Object[] param = null; // name | type | optional value
boolean checkEq = false;
int checkEqI = 0;
for (int i = 0; i < arguments.size(); i++) {
String arg = arguments.get(i);
if (arg.equals("=")) {
checkEqI++;
checkEq = true;
} else if (param == null || !checkEq) {
if (param != null) paramParsables.add(param);
param = new Object[3];
param[0] = arg;
if (arg.length() == 1 && command.flags().contains(arg)) {
param[1] = Boolean.class;
} else {
param[1] = String.class;
}
param[2] = null;
checkEqI = 0;
checkEq = false;
} else {
if (checkEqI == 1) {
param[1] = unqualified.getOrDefault(arg, String.class);
checkEq = false;
}
else if (checkEqI == 2) {
char c = arg.charAt(0);
if (c == '\'' || c == '"') arg = arg.substring(1, arg.length() - 1);
param[2] = arg;
checkEqI = 0;
checkEq = false;
}
}
}
if (param != null) paramParsables.add(param);
}
}
parameters = new ParameterData[paramParsables.size()];
List<Parameter> userParameters = new ArrayList<Parameter>();
// This helps keep tracks of @Nullables that appear in the middle of a list
// of parameters
int numOptional = 0;
//
// Go through each parameter
for (int i = 0; i < paramParsables.size(); i++) {
Object[] parsable = paramParsables.get(i);
String paramName = (String) parsable[0];
Type type = (Type) parsable[1];
String optional = (String) parsable[2];
ParameterData parameter = new ParameterData();
parameter.setType(type);
parameter.setModifiers(new Annotation[0]);
boolean flag = paramName.length() == 1 && command.flags().contains(paramName);
if (flag) {
parameter.setFlag(paramName.charAt(0), type != boolean.class && type != Boolean.class);
}
if (optional != null) {
parameter.setOptional(true);
if (!optional.equalsIgnoreCase("null")) parameter.setDefaultValue(new String[]{optional});
}
parameter.setName(paramName);
// Track all value flags
if (parameter.isValueFlag()) {
valueFlags.add(parameter.getFlag());
}
// No special @annotation binding... let's check for the type
if (parameter.getBinding() == null) {
parameter.setBinding(builder.getBindings().get(type));
// Don't know how to parse for this type of value
if (parameter.getBinding() == null) {
throw new ParametricException("Don't know how to handle the parameter type '" + type + "' in\n" + StringMan.getString(command.aliases()));
}
}
// Do some validation of this parameter
parameter.validate(() -> StringMan.getString(command.aliases()), i + 1);
// Keep track of optional parameters
if (parameter.isOptional() && parameter.getFlag() == null) {
numOptional++;
} else {
if (numOptional > 0 && parameter.isNonFlagConsumer()) {
if (parameter.getConsumedCount() < 0) {
throw new ParametricException(
"Found an parameter using the binding " +
parameter.getBinding().getClass().getCanonicalName() +
"\nthat does not know how many arguments it consumes, but " +
"it follows an optional parameter\nMethod: " + StringMan.getString(command.aliases()));
}
}
}
parameters[i] = parameter;
// Make a list of "real" parameters
if (parameter.isUserInput()) {
userParameters.add(parameter);
}
}
// Gather legacy flags
anyFlags = command.anyFlags();
legacyFlags.addAll(Chars.asList(command.flags().toCharArray()));
// Finish description
description.setDescription(!command.desc().isEmpty() ? command.desc() : null);
description.setHelp(!command.help().isEmpty() ? command.help() : null);
description.overrideUsage(!command.usage().isEmpty() ? command.usage() : null);
description.setPermissions(Arrays.asList(permission));
if (command.usage().isEmpty() && (command.min() > 0 || command.max() > 0)) {
boolean hasUserParameters = false;
for (ParameterData parameter : parameters) {
if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) {
hasUserParameters = true;
break;
}
}
if (!hasUserParameters) {
description.overrideUsage("(unknown usage information)");
}
}
// Set parameters
description.setParameters(userParameters);
}
@Override
public String getGroup() {
return group;
}
@Override
public Command getCommand() {
return command;
}
@Override
public ParameterData[] getParameters() {
return parameters;
}
public Set<Character> getValueFlags() {
return valueFlags;
}
@Override
public Set<Character> getLegacyFlags() {
return legacyFlags;
}
@Override
public Object call(String stringArguments, CommandLocals locals, String[] parentCommands) throws CommandException {
// Test permission
if (!testPermission(locals)) {
throw new CommandPermissionsException();
}
if (locals.get(CommandCallable.class) == null) locals.put(CommandCallable.class, this);
String calledCommand = parentCommands.length > 0 ? parentCommands[parentCommands.length - 1] : "_";
String[] split = (calledCommand + " " + stringArguments).split(" ", -1);
CommandContext context = new CommandContext(split, getValueFlags(), false, locals);
// Provide help if -? is specified
if (context.hasFlag('?')) {
throw new InvalidUsageException(null, this, true);
}
Object[] args = new Object[parameters.length];
ContextArgumentStack arguments = new ContextArgumentStack(context);
ParameterData parameter = null;
try {
// preProcess handlers
{
}
// Collect parameters
for (int i = 0; i < parameters.length; i++) {
parameter = parameters[i];
if (mayConsumeArguments(i, arguments)) {
// Parse the user input into a method argument
ArgumentStack usedArguments = getScopedContext(parameter, arguments);
try {
usedArguments.mark();
args[i] = parameter.getBinding().bind(parameter, usedArguments, false);
} catch (ParameterException e) {
// Not optional? Then we can't execute this command
if (!parameter.isOptional()) {
throw e;
}
usedArguments.reset();
args[i] = getDefaultValue(i, arguments);
}
} else {
args[i] = getDefaultValue(i, arguments);
}
}
// Check for unused arguments
checkUnconsumed(arguments);
// preInvoke handlers
{
if (context.argsLength() < command.min()) {
throw new MissingParameterException();
}
if (command.max() != -1 && context.argsLength() > command.max()) {
throw new UnconsumedParameterException(context.getRemainingString(command.max()));
}
}
// Execute!
Object result = function.apply(args);
// postInvoke handlers
{
}
return result;
} catch (MissingParameterException e) {
throw new InvalidUsageException("Too few parameters!", this, true);
} catch (UnconsumedParameterException e) {
throw new InvalidUsageException("Too many parameters! Unused parameters: " + e.getUnconsumed(), this, true);
} catch (ParameterException e) {
assert parameter != null;
String name = parameter.getName();
throw new InvalidUsageException("For parameter '" + name + "': " + e.getMessage(), this, true);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof CommandException) {
throw (CommandException) e.getCause();
}
throw new WrappedCommandException(e);
} catch (Throwable t) {
throw new WrappedCommandException(t);
}
}
@Override
public boolean testPermission(CommandLocals locals) {
return permission != null ? (builder.getAuthorizer().testPermission(locals, permission)) : true;
}
@Override
public SimpleDescription getDescription() {
return description;
}
@Override
public String[] getPermissions() {
return new String[]{permission};
}
@Override
public ParametricBuilder getBuilder() {
return builder;
}
@Override
public boolean anyFlags() {
return anyFlags;
}
@Override
public String toString() {
return command.aliases()[0];
}
}

View File

@ -23,9 +23,13 @@ import com.sk89q.worldedit.util.command.SimpleParameter;
import com.sk89q.worldedit.util.command.binding.PrimitiveBindings; import com.sk89q.worldedit.util.command.binding.PrimitiveBindings;
import com.sk89q.worldedit.util.command.binding.Range; import com.sk89q.worldedit.util.command.binding.Range;
import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.util.command.binding.Text;
import javax.xml.ws.Provider;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
/** /**
* Describes a parameter in detail. * Describes a parameter in detail.
@ -155,6 +159,10 @@ public class ParameterData extends SimpleParameter {
* Validate this parameter and its binding. * Validate this parameter and its binding.
*/ */
public void validate(Method method, int parameterIndex) throws ParametricException { public void validate(Method method, int parameterIndex) throws ParametricException {
validate(() -> method.toGenericString(), parameterIndex);
}
public void validate(Supplier<String> method, int parameterIndex) throws ParametricException {
// We can't have indeterminate consumers without @Switches otherwise // We can't have indeterminate consumers without @Switches otherwise
// it may screw up parameter processing for later bindings // it may screw up parameter processing for later bindings
BindingBehavior behavior = getBinding().getBehavior(this); BindingBehavior behavior = getBinding().getBehavior(this);
@ -166,7 +174,7 @@ public class ParameterData extends SimpleParameter {
getBinding().getClass().getCanonicalName() + getBinding().getClass().getCanonicalName() +
"\nmay or may not consume parameters (isIndeterminateConsumer(" + type + ") = true)" + "\nmay or may not consume parameters (isIndeterminateConsumer(" + type + ") = true)" +
"\nand therefore @Switch(flag) is required for parameter #" + parameterIndex + " of \n" + "\nand therefore @Switch(flag) is required for parameter #" + parameterIndex + " of \n" +
method.toGenericString()); method.get());
} }
// getConsumedCount() better return -1 if the BindingBehavior is not CONSUMES // getConsumedCount() better return -1 if the BindingBehavior is not CONSUMES
@ -176,7 +184,7 @@ public class ParameterData extends SimpleParameter {
getBinding().getClass().getCanonicalName() + getBinding().getClass().getCanonicalName() +
"\neven though its behavior type is " + behavior.name() + "\neven though its behavior type is " + behavior.name() +
"\nfor parameter #" + parameterIndex + " of \n" + "\nfor parameter #" + parameterIndex + " of \n" +
method.toGenericString()); method.get());
} }
// getConsumedCount() should not return 0 if the BindingBehavior is not PROVIDES // getConsumedCount() should not return 0 if the BindingBehavior is not PROVIDES
@ -186,7 +194,7 @@ public class ParameterData extends SimpleParameter {
getBinding().getClass().getCanonicalName() + getBinding().getClass().getCanonicalName() +
"\nwhen its behavior type is " + behavior.name() + " and not PROVIDES " + "\nwhen its behavior type is " + behavior.name() + " and not PROVIDES " +
"\nfor parameter #" + parameterIndex + " of \n" + "\nfor parameter #" + parameterIndex + " of \n" +
method.toGenericString()); method.get());
} }
} }

View File

@ -49,7 +49,7 @@ import java.util.Set;
/** /**
* The implementation of a {@link CommandCallable} for the {@link ParametricBuilder}. * The implementation of a {@link CommandCallable} for the {@link ParametricBuilder}.
*/ */
public class ParametricCallable implements CommandCallable { public class ParametricCallable extends AParametricCallable {
private final ParametricBuilder builder; private final ParametricBuilder builder;
private final Object object; private final Object object;
@ -60,6 +60,7 @@ public class ParametricCallable implements CommandCallable {
private final Set<Character> legacyFlags = new HashSet<Character>(); private final Set<Character> legacyFlags = new HashSet<Character>();
private final SimpleDescription description = new SimpleDescription(); private final SimpleDescription description = new SimpleDescription();
private final CommandPermissions commandPermissions; private final CommandPermissions commandPermissions;
private final Command definition;
/** /**
* Create a new instance. * Create a new instance.
@ -179,6 +180,22 @@ public class ParametricCallable implements CommandCallable {
// Get permissions annotation // Get permissions annotation
commandPermissions = method.getAnnotation(CommandPermissions.class); commandPermissions = method.getAnnotation(CommandPermissions.class);
this.definition = definition;
}
@Override
public Command getCommand() {
return object.getClass().getAnnotation(Command.class);
}
@Override
public Command getDefinition() {
return definition;
}
@Override
public String getGroup() {
return object.getClass().getSimpleName().replaceAll("Commands", "").replaceAll("Util$", "");
} }
@Override @Override
@ -187,6 +204,7 @@ public class ParametricCallable implements CommandCallable {
if (!testPermission(locals)) { if (!testPermission(locals)) {
throw new CommandPermissionsException(); throw new CommandPermissionsException();
} }
if (locals.get(CommandCallable.class) == null) locals.put(CommandCallable.class, this);
String calledCommand = parentCommands.length > 0 ? parentCommands[parentCommands.length - 1] : "_"; String calledCommand = parentCommands.length > 0 ? parentCommands[parentCommands.length - 1] : "_";
String[] split = (calledCommand + " " + stringArguments).split(" ", -1); String[] split = (calledCommand + " " + stringArguments).split(" ", -1);
@ -279,94 +297,8 @@ public class ParametricCallable implements CommandCallable {
} }
@Override @Override
public List<String> getSuggestions(String arguments, CommandLocals locals) throws CommandException { public ParameterData[] getParameters() {
String[] split = ("ignored" + " " + arguments).split(" ", -1); return parameters;
CommandContext context = new CommandContext(split, getValueFlags(), !arguments.endsWith(" "), locals);
ContextArgumentStack scoped = new ContextArgumentStack(context);
SuggestionContext suggestable = context.getSuggestionContext();
// For /command -f |
// For /command -f flag|
if (suggestable.forFlag()) {
for (int i = 0; i < parameters.length; i++) {
ParameterData parameter = parameters[i];
if (parameter.getFlag() == suggestable.getFlag()) {
String prefix = context.getFlag(parameter.getFlag());
if (prefix == null) {
prefix = "";
}
return parameter.getBinding().getSuggestions(parameter, prefix);
}
}
// This should not happen
return new ArrayList<String>();
}
int consumerIndex = 0;
ParameterData lastConsumer = null;
String lastConsumed = null;
for (int i = 0; i < parameters.length; i++) {
ParameterData parameter = parameters[i];
if (parameter.getFlag() != null) {
continue; // We already handled flags
}
try {
scoped.mark();
parameter.getBinding().bind(parameter, scoped, true);
if (scoped.wasConsumed()) {
lastConsumer = parameter;
lastConsumed = context.getString(scoped.position() - 1);
consumerIndex++;
}
} catch (MissingParameterException e) {
// For /command value1 |value2
// For /command |value1 value2
if (suggestable.forHangingValue()) {
return parameter.getBinding().getSuggestions(parameter, "");
} else {
// For /command value1| value2
if (lastConsumer != null) {
return lastConsumer.getBinding().getSuggestions(lastConsumer, lastConsumed);
// For /command| value1 value2
// This should never occur
} else {
throw new RuntimeException("Invalid suggestion context");
}
}
} catch (ParameterException | InvocationTargetException e) {
SuggestInputParseException suggestion = SuggestInputParseException.get(e);
if (suggestion != null) {
return suggestion.getSuggestions();
}
if (suggestable.forHangingValue()) {
String name = getDescription().getParameters().get(consumerIndex).getName();
throw new InvalidUsageException("For parameter '" + name + "': " + e.getMessage(), this);
} else {
return parameter.getBinding().getSuggestions(parameter, "");
}
}
}
// For /command value1 value2 |
if (suggestable.forHangingValue()) {
// There's nothing that we can suggest because there's no more parameters
// to add on, and we can't change the previous parameter
return new ArrayList<String>();
} else {
// For /command value1 value2|
if (lastConsumer != null) {
return lastConsumer.getBinding().getSuggestions(lastConsumer, lastConsumed);
// This should never occur
} else {
throw new RuntimeException("Invalid suggestion context");
}
}
} }
/** /**
@ -378,179 +310,34 @@ public class ParametricCallable implements CommandCallable {
return valueFlags; return valueFlags;
} }
@Override
public Set<Character> getLegacyFlags() {
return legacyFlags;
}
@Override @Override
public SimpleDescription getDescription() { public SimpleDescription getDescription() {
return description; return description;
} }
@Override @Override
public boolean testPermission(CommandLocals locals) { public String[] getPermissions() {
if (commandPermissions != null) { return commandPermissions != null ? commandPermissions.value() : new String[0];
for (String perm : commandPermissions.value()) {
if (builder.getAuthorizer().testPermission(locals, perm)) {
return true;
}
}
return false;
} else {
return true;
}
} }
/** @Override
* Get the right {@link ArgumentStack}. public ParametricBuilder getBuilder() {
* return builder;
* @param parameter the parameter
* @param existing the existing scoped context
* @return the context to use
*/
public static ArgumentStack getScopedContext(Parameter parameter, ArgumentStack existing) {
if (parameter.getFlag() != null) {
CommandContext context = existing.getContext();
if (parameter.isValueFlag()) {
return new StringArgumentStack(context, context.getFlag(parameter.getFlag()), false);
} else {
String v = context.hasFlag(parameter.getFlag()) ? "true" : "false";
return new StringArgumentStack(context, v, true);
}
}
return existing;
} }
/** @Override
* Get whether a parameter is allowed to consume arguments. public boolean anyFlags() {
* return anyFlags;
* @param i the index of the parameter
* @param scoped the scoped context
* @return true if arguments may be consumed
*/
public boolean mayConsumeArguments(int i, ContextArgumentStack scoped) {
CommandContext context = scoped.getContext();
ParameterData parameter = parameters[i];
// Flag parameters: Always consume
// Required non-flag parameters: Always consume
// Optional non-flag parameters:
// - Before required parameters: Consume if there are 'left over' args
// - At the end: Always consumes
if (parameter.isOptional()) {
if (parameter.getFlag() != null) {
return !parameter.isValueFlag() || context.hasFlag(parameter.getFlag());
} else {
int numberFree = context.argsLength() - scoped.position();
for (int j = i; j < parameters.length; j++) {
if (parameters[j].isNonFlagConsumer() && !parameters[j].isOptional()) {
// We already checked if the consumed count was > -1
// when we created this object
numberFree -= parameters[j].getConsumedCount();
}
}
// Skip this optional parameter
if (numberFree < 1) {
return false;
}
}
}
return true;
} }
/** @Override
* Get the default value for a parameter. public String toString() {
* return method.toGenericString();
* @param i the index of the parameter
* @param scoped the scoped context
* @return a value
* @throws ParameterException on an error
* @throws CommandException on an error
*/
public Object getDefaultValue(int i, ContextArgumentStack scoped) throws ParameterException, CommandException, InvocationTargetException {
CommandContext context = scoped.getContext();
ParameterData parameter = parameters[i];
String[] defaultValue = parameter.getDefaultValue();
if (defaultValue != null) {
try {
return parameter.getBinding().bind(parameter, new StringArgumentStack(context, defaultValue, false), false);
} catch (MissingParameterException e) {
throw new ParametricException(
"The default value of the parameter using the binding " +
parameter.getBinding().getClass() + " in the method\n" +
method.toGenericString() + "\nis invalid");
}
}
return null;
}
/**
* Check to see if all arguments, including flag arguments, were consumed.
*
* @param scoped the argument scope
* @throws UnconsumedParameterException thrown if parameters were not consumed
*/
public void checkUnconsumed(ContextArgumentStack scoped) throws UnconsumedParameterException {
CommandContext context = scoped.getContext();
String unconsumed;
String unconsumedFlags = getUnusedFlags(context);
if ((unconsumed = scoped.getUnconsumed()) != null) {
throw new UnconsumedParameterException(unconsumed + " " + unconsumedFlags);
}
if (unconsumedFlags != null) {
throw new UnconsumedParameterException(unconsumedFlags);
}
}
/**
* Get any unused flag arguments.
*
* @param context the command context
*/
public String getUnusedFlags(CommandContext context) {
if (!anyFlags) {
Set<Character> unusedFlags = null;
for (char flag : context.getFlags()) {
boolean found = false;
if (legacyFlags.contains(flag)) {
break;
}
for (ParameterData parameter : parameters) {
Character paramFlag = parameter.getFlag();
if (paramFlag != null && flag == paramFlag) {
found = true;
break;
}
}
if (!found) {
if (unusedFlags == null) {
unusedFlags = new HashSet<Character>();
}
unusedFlags.add(flag);
}
}
if (unusedFlags != null) {
StringBuilder builder = new StringBuilder();
for (Character flag : unusedFlags) {
builder.append("-").append(flag).append(" ");
}
return builder.toString().trim();
}
}
return null;
} }
/** /**
@ -573,7 +360,8 @@ public class ParametricCallable implements CommandCallable {
} }
} }
public static Class<?> inject() {
public static Class<ParametricCallable> inject() {
return ParametricCallable.class; return ParametricCallable.class;
} }
} }

View File

@ -146,7 +146,7 @@ public class StringArgumentStack implements ArgumentStack {
*/ */
@Override @Override
public String reset() { public String reset() {
String value = context.getString(markedIndex, index - 1); String value = (index - 1 > markedIndex) ? context.getString(markedIndex, index - 1) : "";
index = markedIndex; index = markedIndex;
return value; return value;
} }

View File

@ -0,0 +1,51 @@
{
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
var a = func.toString().replace(STRIP_COMMENTS, '');
var r = a.slice(a.indexOf('(')+1, a.indexOf(')')).match(ARGUMENT_NAMES);
var l = new java.util.ArrayList();
if(r !== null) {
for (var i = 0; i < r.length; i++) {
l.add(r[i]);
}
}
return l;
}
function getAllFunctions(){
var a = new java.util.ArrayList();
for (var f in this){
if (this.hasOwnProperty(f) && this[f] instanceof Function && !/a/i.test(f)){
a.add(this[f]);
}
}
return a;
}
var functions = getAllFunctions();
var commands = new java.util.ArrayList()
for (var i = 0; i < functions.length; i++) {
var f = functions[i];
if (f.hasOwnProperty('desc'))
{
if (!f.hasOwnProperty('permission')) f.permission = "fawe.use";
if (!f.hasOwnProperty('aliases')) f.aliases = [f.name];
if (!f.hasOwnProperty('queued')) f.queued = true;
var cmd = com.boydti.fawe.config.Commands.fromArgs(f.aliases, f.usage, f.desc, f.min, f.max, f.flags, f.help, f.queued);
var man = com.sk89q.worldedit.extension.platform.CommandManager.getInstance();
var builder = man.getBuilder();
var args = getParamNames(f);
var wrap = Java.extend(java.util.function.Function, {
apply: function(a) {
return f.apply(null, a);
}
});
var w2 = new wrap();
var callable = new com.sk89q.worldedit.util.command.parametric.FunctionParametricCallable(builder, "", cmd, f.permission, args, w2);
commands.add(callable);
}
}
commands;
}