This commit is contained in:
Jesse Boyd 2019-08-18 01:57:14 +01:00
commit b3601eb858
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
38 changed files with 1643 additions and 338 deletions

View File

@ -89,7 +89,7 @@ subprojects {
ivy {
url 'https://ci.athion.net/job'
layout 'pattern', {
artifact '/[organisation]/[revision]/artifact/[module].[ext]'
artifact '/[organisation]/[module]/artifact/[revision].[ext]'
}
}
}

View File

@ -13,6 +13,9 @@ configurations.all { Configuration it ->
it.resolutionStrategy { ResolutionStrategy rs ->
rs.force("com.google.guava:guava:21.0")
}
it.resolutionStrategy { ResolutionStrategy rs ->
rs.force("it.unimi.dsi:fastutil:8.2.1")
}
}
dependencies {
@ -21,6 +24,7 @@ dependencies {
api project(':worldedit-libs:core') // TODO remove once core can compile
api project(':worldedit-libs:bukkit')
compileOnly 'com.sk89q:dummypermscompat:1.10'
compile "it.unimi.dsi:fastutil:8.2.1"
testCompile 'org.mockito:mockito-core:1.9.0-rc1'
implementation('org.apache.logging.log4j:log4j-slf4j-impl:2.8.1'){transitive = false}
compile 'com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT'
@ -42,6 +46,12 @@ dependencies {
implementation('com.wasteofplastic:askyblock:3.0.8.2'){transitive = false}
}
shadowJar {
relocate("it.unimi.dsi.fastutil", "com.sk89q.worldedit.bukkit.fastutil") {
include("it.unimi.dsi:fastutil")
}
}
processResources {
from('src/main/resources') {
expand(

View File

@ -15,6 +15,9 @@ configurations.all { Configuration it ->
it.resolutionStrategy { ResolutionStrategy rs ->
rs.force("com.google.guava:guava:21.0")
}
it.resolutionStrategy { ResolutionStrategy rs ->
rs.force("it.unimi.dsi:fastutil:8.2.1")
}
}
dependencies {
@ -28,6 +31,7 @@ dependencies {
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.googlecode.json-simple:json-simple:1.1.1'
compile 'org.slf4j:slf4j-api:1.7.26'
compile "it.unimi.dsi:fastutil:8.2.1"
compileOnly project(':worldedit-libs:core:ap')
annotationProcessor project(':worldedit-libs:core:ap')
@ -46,6 +50,11 @@ dependencies {
compile 'com.mojang:datafixerupper:1.0.20'
compile 'com.github.luben:zstd-jni:1.1.1'
compile 'co.aikar:fastutil-lite:1.0'
testImplementation ("org.junit.jupiter:junit-jupiter-api:5.5.0")
testImplementation ("org.junit.jupiter:junit-jupiter-params:5.5.0")
testImplementation ("org.mockito:mockito-core:3.0.0")
testImplementation ("org.mockito:mockito-junit-jupiter:3.0.0")
testRuntime ("org.junit.jupiter:junit-jupiter-engine:5.5.0")
}
tasks.withType(JavaCompile).configureEach {

View File

@ -1,6 +1,7 @@
package com.boydti.fawe.util;
import com.sk89q.worldedit.world.block.BlockType;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileNotFoundException;
@ -80,7 +81,7 @@ public class RandomTextureUtil extends CachedTextureUtil {
@Override
public BlockType getNearestBlock(int color) {
int offsetColor = offsets.getOrDefault(color, 0);
int offsetColor = offsets.getOrDefault((Object) color, 0);
if (offsetColor != 0) {
offsetColor = addRandomColor(color, offsetColor);
} else {

View File

@ -168,73 +168,73 @@ public class ReflectionUtils {
}
}
public static Object getHandle(final Object wrapper) {
public static Object getHandle(Object wrapper) {
final Method getHandle = makeMethod(wrapper.getClass(), "getHandle");
return callMethod(getHandle, wrapper);
}
//Utils
public static Method makeMethod(final Class<?> clazz, final String methodName, final Class<?>... parameters) {
public static Method makeMethod(Class<?> clazz, String methodName, Class<?>... parameters) {
try {
return clazz.getDeclaredMethod(methodName, parameters);
} catch (final NoSuchMethodException ex) {
} catch (NoSuchMethodException ex) {
return null;
} catch (final Exception ex) {
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
public static <T> T callMethod(final Method method, final Object instance, final Object... parameters) {
public static <T> T callMethod(Method method, Object instance, Object... parameters) {
if (method == null) {
throw new RuntimeException("No such method");
}
method.setAccessible(true);
try {
return (T) method.invoke(instance, parameters);
} catch (final InvocationTargetException ex) {
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (final Exception ex) {
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
public static <T> Constructor<T> makeConstructor(final Class<?> clazz, final Class<?>... parameterTypes) {
public static <T> Constructor<T> makeConstructor(Class<?> clazz, Class<?>... parameterTypes) {
try {
return (Constructor<T>) clazz.getConstructor(parameterTypes);
} catch (final NoSuchMethodException ex) {
} catch (NoSuchMethodException ex) {
return null;
} catch (final Exception ex) {
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static <T> T callConstructor(final Constructor<T> constructor, final Object... paramaters) {
public static <T> T callConstructor(Constructor<T> constructor, Object... paramaters) {
if (constructor == null) {
throw new RuntimeException("No such constructor");
}
constructor.setAccessible(true);
try {
return constructor.newInstance(paramaters);
} catch (final InvocationTargetException ex) {
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (final Exception ex) {
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Field makeField(final Class<?> clazz, final String name) {
public static Field makeField(Class<?> clazz, String name) {
try {
return clazz.getDeclaredField(name);
} catch (final NoSuchFieldException ex) {
} catch (NoSuchFieldException ex) {
return null;
} catch (final Exception ex) {
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Field findField(final Class<?> clazz, final Class<?> type, int hasMods, int noMods) {
public static Field findField(Class<?> clazz, Class<?> type, int hasMods, int noMods) {
for (Field field : clazz.getDeclaredFields()) {
if (type == null || type.isAssignableFrom(field.getType())) {
int mods = field.getModifiers();
@ -246,7 +246,7 @@ public class ReflectionUtils {
return null;
}
public static Field findField(final Class<?> clazz, final Class<?> type) {
public static Field findField(Class<?> clazz, Class<?> type) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == type) {
return setAccessible(field);
@ -255,11 +255,11 @@ public class ReflectionUtils {
return null;
}
public static Method findMethod(final Class<?> clazz, final Class<?> returnType, Class... params) {
public static Method findMethod(Class<?> clazz, Class<?> returnType, Class... params) {
return findMethod(clazz, 0, returnType, params);
}
public static Method findMethod(final Class<?> clazz, int index, int hasMods, int noMods, final Class<?> returnType, Class... params) {
public static Method findMethod(Class<?> clazz, int index, int hasMods, int noMods, Class<?> returnType, Class... params) {
outer:
for (Method method : sortMethods(clazz.getDeclaredMethods())) {
if (returnType == null || method.getReturnType() == returnType) {
@ -296,49 +296,49 @@ public class ReflectionUtils {
return fields;
}
public static Method findMethod(final Class<?> clazz, int index, final Class<?> returnType, Class... params) {
public static Method findMethod(Class<?> clazz, int index, Class<?> returnType, Class... params) {
return findMethod(clazz, index, 0, 0, returnType, params);
}
public static <T extends AccessibleObject> T setAccessible(final T ao) {
public static <T extends AccessibleObject> T setAccessible(T ao) {
ao.setAccessible(true);
return ao;
}
@SuppressWarnings("unchecked")
public static <T> T getField(final Field field, final Object instance) {
public static <T> T getField(Field field, Object instance) {
if (field == null) {
throw new RuntimeException("No such field");
}
field.setAccessible(true);
try {
return (T) field.get(instance);
} catch (final Exception ex) {
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static void setField(final Field field, final Object instance, final Object value) {
public static void setField(Field field, Object instance, Object value) {
if (field == null) {
throw new RuntimeException("No such field");
}
field.setAccessible(true);
try {
field.set(instance, value);
} catch (final Exception ex) {
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Class<?> getClass(final String name) {
public static Class<?> getClass(String name) {
try {
return Class.forName(name);
} catch (final ClassNotFoundException ex) {
} catch (ClassNotFoundException ex) {
return null;
}
}
public static <T> Class<? extends T> getClass(final String name, final Class<T> superClass) {
public static <T> Class<? extends T> getClass(String name, Class<T> superClass) {
try {
return Class.forName(name).asSubclass(superClass);
} catch (ClassCastException | ClassNotFoundException ex) {
@ -353,7 +353,7 @@ public class ReflectionUtils {
* @param clazz class
* @return RefClass based on passed class
*/
public static RefClass getRefClass(final Class clazz) {
public static RefClass getRefClass(Class clazz) {
return new RefClass(clazz);
}
@ -363,7 +363,7 @@ public class ReflectionUtils {
public static class RefClass {
private final Class<?> clazz;
private RefClass(final Class<?> clazz) {
private RefClass(Class<?> clazz) {
this.clazz = clazz;
}
@ -382,7 +382,7 @@ public class ReflectionUtils {
* @param object the object to check
* @return true if object is an instance of this class
*/
public boolean isInstance(final Object object) {
public boolean isInstance(Object object) {
return this.clazz.isInstance(object);
}
@ -394,11 +394,11 @@ public class ReflectionUtils {
* @return RefMethod object
* @throws RuntimeException if method not found
*/
public RefMethod getMethod(final String name, final Object... types) throws NoSuchMethodException {
public RefMethod getMethod(String name, Object... types) throws NoSuchMethodException {
try {
final Class[] classes = new Class[types.length];
int i = 0;
for (final Object e : types) {
for (Object e : types) {
if (e instanceof Class) {
classes[i++] = (Class) e;
} else if (e instanceof RefClass) {
@ -409,10 +409,10 @@ public class ReflectionUtils {
}
try {
return new RefMethod(this.clazz.getMethod(name, classes));
} catch (final NoSuchMethodException ignored) {
} catch (NoSuchMethodException ignored) {
return new RefMethod(this.clazz.getDeclaredMethod(name, classes));
}
} catch (final Exception e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@ -424,11 +424,11 @@ public class ReflectionUtils {
* @return RefMethod object
* @throws RuntimeException if constructor not found
*/
public RefConstructor getConstructor(final Object... types) {
public RefConstructor getConstructor(Object... types) {
try {
final Class[] classes = new Class[types.length];
int i = 0;
for (final Object e : types) {
for (Object e : types) {
if (e instanceof Class) {
classes[i++] = (Class) e;
} else if (e instanceof RefClass) {
@ -439,10 +439,10 @@ public class ReflectionUtils {
}
try {
return new RefConstructor(this.clazz.getConstructor(classes));
} catch (final NoSuchMethodException ignored) {
} catch (NoSuchMethodException ignored) {
return new RefConstructor(this.clazz.getDeclaredConstructor(classes));
}
} catch (final Exception e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@ -454,10 +454,10 @@ public class ReflectionUtils {
* @return RefMethod object
* @throws RuntimeException if method not found
*/
public RefMethod findMethod(final Object... types) {
public RefMethod findMethod(Object... types) {
final Class[] classes = new Class[types.length];
int t = 0;
for (final Object e : types) {
for (Object e : types) {
if (e instanceof Class) {
classes[t++] = (Class) e;
} else if (e instanceof RefClass) {
@ -470,12 +470,12 @@ public class ReflectionUtils {
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
findMethod:
for (final Method m : methods) {
for (Method m : methods) {
final Class<?>[] methodTypes = m.getParameterTypes();
if (methodTypes.length != classes.length) {
continue;
}
for (final Class aClass : classes) {
for (Class aClass : classes) {
if (!Arrays.equals(classes, methodTypes)) {
continue findMethod;
}
@ -492,12 +492,12 @@ public class ReflectionUtils {
* @return RefMethod object
* @throws RuntimeException if method not found
*/
public RefMethod findMethodByName(final String... names) {
public RefMethod findMethodByName(String... names) {
final List<Method> methods = new ArrayList<>();
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
for (final Method m : methods) {
for (final String name : names) {
for (Method m : methods) {
for (String name : names) {
if (m.getName().equals(name)) {
return new RefMethod(m);
}
@ -513,7 +513,7 @@ public class ReflectionUtils {
* @return RefMethod
* @throws RuntimeException if method not found
*/
public RefMethod findMethodByReturnType(final RefClass type) {
public RefMethod findMethodByReturnType(RefClass type) {
return this.findMethodByReturnType(type.clazz);
}
@ -531,7 +531,7 @@ public class ReflectionUtils {
final List<Method> methods = new ArrayList<>();
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
for (final Method m : methods) {
for (Method m : methods) {
if (type.equals(m.getReturnType())) {
return new RefMethod(m);
}
@ -546,11 +546,11 @@ public class ReflectionUtils {
* @return RefConstructor
* @throws RuntimeException if constructor not found
*/
public RefConstructor findConstructor(final int number) {
public RefConstructor findConstructor(int number) {
final List<Constructor> constructors = new ArrayList<>();
Collections.addAll(constructors, this.clazz.getConstructors());
Collections.addAll(constructors, this.clazz.getDeclaredConstructors());
for (final Constructor m : constructors) {
for (Constructor m : constructors) {
if (m.getParameterTypes().length == number) {
return new RefConstructor(m);
}
@ -565,14 +565,14 @@ public class ReflectionUtils {
* @return RefField
* @throws RuntimeException if field not found
*/
public RefField getField(final String name) {
public RefField getField(String name) {
try {
try {
return new RefField(this.clazz.getField(name));
} catch (final NoSuchFieldException ignored) {
} catch (NoSuchFieldException ignored) {
return new RefField(this.clazz.getDeclaredField(name));
}
} catch (final Exception e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@ -584,7 +584,7 @@ public class ReflectionUtils {
* @return RefField
* @throws RuntimeException if field not found
*/
public RefField findField(final RefClass type) {
public RefField findField(RefClass type) {
return this.findField(type.clazz);
}
@ -602,7 +602,7 @@ public class ReflectionUtils {
final List<Field> fields = new ArrayList<>();
Collections.addAll(fields, this.clazz.getFields());
Collections.addAll(fields, this.clazz.getDeclaredFields());
for (final Field f : fields) {
for (Field f : fields) {
if (type.equals(f.getType())) {
return new RefField(f);
}
@ -617,7 +617,7 @@ public class ReflectionUtils {
public static class RefMethod {
private final Method method;
private RefMethod(final Method method) {
private RefMethod(Method method) {
this.method = method;
method.setAccessible(true);
}
@ -649,7 +649,7 @@ public class ReflectionUtils {
* @param e object to which the method is applied
* @return RefExecutor with method call(...)
*/
public RefExecutor of(final Object e) {
public RefExecutor of(Object e) {
return new RefExecutor(e);
}
@ -659,10 +659,10 @@ public class ReflectionUtils {
* @param params sent parameters
* @return return value
*/
public Object call(final Object... params) {
public Object call(Object... params) {
try {
return this.method.invoke(null, params);
} catch (final Exception e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@ -670,7 +670,7 @@ public class ReflectionUtils {
public class RefExecutor {
final Object e;
public RefExecutor(final Object e) {
public RefExecutor(Object e) {
this.e = e;
}
@ -681,10 +681,10 @@ public class ReflectionUtils {
* @return return value
* @throws RuntimeException if something went wrong
*/
public Object call(final Object... params) {
public Object call(Object... params) {
try {
return RefMethod.this.method.invoke(this.e, params);
} catch (final Exception e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@ -697,7 +697,7 @@ public class ReflectionUtils {
public static class RefConstructor {
private final Constructor constructor;
private RefConstructor(final Constructor constructor) {
private RefConstructor(Constructor constructor) {
this.constructor = constructor;
constructor.setAccessible(true);
}
@ -723,10 +723,10 @@ public class ReflectionUtils {
* @return new object
* @throws RuntimeException if something went wrong
*/
public Object create(final Object... params) {
public Object create(Object... params) {
try {
return this.constructor.newInstance(params);
} catch (final Exception e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@ -735,7 +735,7 @@ public class ReflectionUtils {
public static class RefField {
private final Field field;
private RefField(final Field field) {
private RefField(Field field) {
this.field = field;
field.setAccessible(true);
}
@ -767,14 +767,14 @@ public class ReflectionUtils {
* @param e applied object
* @return RefExecutor with getter and setter
*/
public RefExecutor of(final Object e) {
public RefExecutor of(Object e) {
return new RefExecutor(e);
}
public class RefExecutor {
final Object e;
public RefExecutor(final Object e) {
public RefExecutor(Object e) {
this.e = e;
}
@ -783,10 +783,10 @@ public class ReflectionUtils {
*
* @param param value
*/
public void set(final Object param) {
public void set(Object param) {
try {
RefField.this.field.set(this.e, param);
} catch (final Exception e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@ -799,7 +799,7 @@ public class ReflectionUtils {
public Object get() {
try {
return RefField.this.field.get(this.e);
} catch (final Exception e) {
} catch (Exception e) {
throw new RuntimeException(e);
}
}

View File

@ -80,6 +80,7 @@ public abstract class LocalConfiguration {
public int butcherMaxRadius = -1;
public boolean allowSymlinks = false;
public boolean serverSideCUI = true;
public boolean extendedYLimit = false;
protected String[] getDefaultDisallowedBlocks() {
List<BlockType> blockTypes = Lists.newArrayList(

View File

@ -95,6 +95,7 @@ import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.SingleBlockTypeMask;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.annotation.ClipboardMask;
import com.sk89q.worldedit.internal.annotation.Range;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
@ -153,9 +154,9 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.blendball")
public BrushSettings blendBallBrush(Player player, LocalSession session,
@Arg(desc = "The radius to sample for blending", def = "5")
Expression radiusOpt, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
return set(session, context, new BlendBall()).setSize(radiusOpt);
Expression radius, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
return set(session, context, new BlendBall()).setSize(radius);
}
@Command(
@ -165,9 +166,9 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.erode")
public BrushSettings erodeBrush(Player player, LocalSession session,
@Arg(desc = "The radius for eroding", def = "5")
Expression radiusOpt, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
return set(session, context, new ErodeBrush()).setSize(radiusOpt);
Expression radius, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
return set(session, context, new ErodeBrush()).setSize(radius);
}
@Command(
@ -177,9 +178,9 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.pull")
public BrushSettings pullBrush(Player player, LocalSession session,
@Arg(desc = "The radius to sample for blending", def = "5")
Expression radiusOpt, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
return set(session, context, new RaiseBrush()).setSize(radiusOpt);
Expression radius, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
return set(session, context, new RaiseBrush()).setSize(radius);
}
@Command(
@ -189,9 +190,9 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.sphere")
public BrushSettings circleBrush(Player player, EditSession editSession, LocalSession session, Pattern fill,
@Arg(desc = "The radius to sample for blending", def = "5")
Expression radiusOpt, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
return set(session, context, new CircleBrush(player)).setSize(radiusOpt).setFill(fill);
Expression radius, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
return set(session, context, new CircleBrush(player)).setSize(radius).setFill(fill);
}
@Command(
@ -204,14 +205,14 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.recursive")
public BrushSettings recursiveBrush(Player player, LocalSession session, EditSession editSession, Pattern fill,
@Arg(desc = "The radius to sample for blending", def = "5")
Expression radiusOpt,
Expression radius,
@Switch(name = 'd', desc = "Apply in depth first order")
boolean depthFirst,
InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
return set(session, context,
new RecurseBrush(depthFirst))
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill)
.setMask(new IdMask(editSession));
}
@ -224,17 +225,17 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.line")
public BrushSettings lineBrush(Player player, LocalSession session, Pattern fill,
@Arg(desc = "The radius to sample for blending", def = "0")
Expression radiusOpt,
Expression radius,
@Switch(name = 'h', desc = "Create only a shell")
boolean shell,
@Switch(name = 's', desc = "Selects the clicked point after drawing")
boolean select,
@Switch(name = 'f', desc = "Create a flat line")
boolean flat, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
return set(session, context,
new LineBrush(shell, select, flat))
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill);
}
@ -251,12 +252,12 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.spline")
public BrushSettings splineBrush(Player player, EditSession editSession, LocalSession session, Pattern fill,
@Arg(desc = "The radius to sample for blending", def = "25")
Expression radiusOpt, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
player.print(BBC.BRUSH_SPLINE.format(radiusOpt));
Expression radius, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
player.print(BBC.BRUSH_SPLINE.format(radius));
return set(session, context,
new SplineBrush(player, session))
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill);
}
@ -282,7 +283,7 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.spline")
public BrushSettings catenaryBrush(LocalSession session, Pattern fill, @Arg(def = "1.2", desc = "Length of wire compared to distance between points") @Range(min = 1) double lengthFactor,
@Arg(desc = "The radius to sample for blending", def = "0")
Expression radiusOpt,
Expression radius,
@Switch(name = 'h', desc = "Create only a shell")
boolean shell,
@Switch(name = 's', desc = "Select the clicked point after drawing")
@ -290,11 +291,11 @@ public class BrushCommands {
@Switch(name = 'd', desc = "sags the catenary toward the facing direction")
boolean facingDirection,
InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
Brush brush = new CatenaryBrush(shell, select, facingDirection, lengthFactor);
return set(session, context,
new CatenaryBrush(shell, select, facingDirection, lengthFactor))
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill);
}
@ -308,12 +309,12 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.surfacespline") // 0, 0, 0, 10, 0,
public BrushSettings surfaceSpline(Player player, LocalSession session, Pattern fill,
@Arg(desc = "The radius to sample for blending", def = "0")
Expression radiusOpt, @Arg(name = "tension", desc = "double", def = "0") double tension, @Arg(name = "bias", desc = "double", def = "0") double bias, @Arg(name = "continuity", desc = "double", def = "0") double continuity, @Arg(name = "quality", desc = "double", def = "10") double quality, InjectedValueAccess context) throws WorldEditException {
player.print(BBC.BRUSH_SPLINE.format(radiusOpt));
worldEdit.checkMaxBrushRadius(radiusOpt);
Expression radius, @Arg(name = "tension", desc = "double", def = "0") double tension, @Arg(name = "bias", desc = "double", def = "0") double bias, @Arg(name = "continuity", desc = "double", def = "0") double continuity, @Arg(name = "quality", desc = "double", def = "10") double quality, InjectedValueAccess context) throws WorldEditException {
player.print(BBC.BRUSH_SPLINE.format(radius));
worldEdit.checkMaxBrushRadius(radius);
return set(session, context,
new SurfaceSpline(tension, bias, continuity, quality))
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill);
}
@ -343,12 +344,12 @@ public class BrushCommands {
@Arg(desc = "The pattern of blocks to set")
Pattern pattern,
@Arg(desc = "The radius of the sphere", def = "2")
Expression radiusOpt,
Expression radius,
@Switch(name = 'h', desc = "Create hollow spheres instead")
boolean hollow,
@Switch(name = 'f', desc = "Create falling spheres instead")
boolean falling, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
Brush brush;
if (hollow) {
brush = new HollowSphereBrush();
@ -371,7 +372,7 @@ public class BrushCommands {
}
return set(session, context,
brush)
.setSize(radiusOpt)
.setSize(radius)
.setFill(pattern);
}
@ -385,12 +386,12 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.shatter")
public BrushSettings shatterBrush(Player player, EditSession editSession, LocalSession session, Pattern fill,
@Arg(desc = "The radius to sample for blending", def = "10")
Expression radiusOpt,
Expression radius,
@Arg(desc = "Lines", def = "10") int count, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
return set(session, context,
new ShatterBrush(count))
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill)
.setMask(new ExistingBlockMask(editSession));
}
@ -402,14 +403,14 @@ public class BrushCommands {
)
@CommandPermissions("worldedit.brush.stencil")
public BrushSettings stencilBrush(Player player, LocalSession session, Pattern fill,
@Arg(name = "radius", desc = "Expression", def = "5") Expression radiusOpt,
@Arg(name = "radius", desc = "Expression", def = "5") Expression radius,
@Arg(name = "image", desc = "String", def = "") String image,
@Arg(def = "0", desc = "rotation") @Range(min = 0, max = 360) int rotation,
@Arg(name = "yscale", desc = "double", def = "1") double yscale,
@Switch(name = 'w', desc = "Apply at maximum saturation") boolean onlyWhite,
@Switch(name = 'r', desc = "Apply random rotation") boolean randomRotate,
InjectedValueAccess context) throws WorldEditException, FileNotFoundException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
InputStream stream = getHeightmapStream(image);
HeightBrush brush;
try {
@ -422,7 +423,7 @@ public class BrushCommands {
}
return set(session, context,
brush)
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill);
}
@ -432,14 +433,14 @@ public class BrushCommands {
desc = "Use a height map to paint a surface",
descFooter = "Use a height map to paint any surface.\n")
@CommandPermissions("worldedit.brush.stencil")
public BrushSettings imageBrush(LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radiusOpt,
public BrushSettings imageBrush(LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius,
ProvideBindings.ImageUri imageUri,
@Arg(def = "1", desc = "scale height") @Range(min = Double.MIN_NORMAL) double yscale,
@Switch(name = 'a', desc = "Use image Alpha") boolean alpha,
@Switch(name = 'f', desc = "Blend the image with existing terrain") boolean fadeOut,
InjectedValueAccess context) throws WorldEditException, IOException {
BufferedImage image = imageUri.load();
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
if (yscale != 1) {
ImageUtil.scaleAlpha(image, yscale);
alpha = true;
@ -451,7 +452,7 @@ public class BrushCommands {
ImageBrush brush = new ImageBrush(image, session, alpha);
return set(session, context,
brush)
.setSize(radiusOpt);
.setSize(radius);
}
@Command(
@ -465,10 +466,10 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.surface")
public BrushSettings surfaceBrush(LocalSession session, Pattern fill,
@Arg(name = "radius", desc = "Expression", def = "5")
Expression radiusOpt,
Expression radius,
InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
return set(session, context, new SurfaceSphereBrush()).setFill(fill).setSize(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
return set(session, context, new SurfaceSphereBrush()).setFill(fill).setSize(radius);
}
@Command(
@ -478,8 +479,8 @@ public class BrushCommands {
"Video: https://youtu.be/RPZIaTbqoZw?t=34s"
)
@CommandPermissions("worldedit.brush.scatter")
public BrushSettings scatterBrush(LocalSession session, Pattern fill, @Arg(name = "radius", desc = "Expression", def = "5") Expression radiusOpt, @Arg(name = "points", desc = "double", def = "5") double pointsOpt, @Arg(name = "distance", desc = "double", def = "1") double distanceOpt, @Switch(name = 'o', desc = "Overlay the block") boolean overlay, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
public BrushSettings scatterBrush(LocalSession session, Pattern fill, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "points", desc = "double", def = "5") double pointsOpt, @Arg(name = "distance", desc = "double", def = "1") double distanceOpt, @Switch(name = 'o', desc = "Overlay the block") boolean overlay, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
Brush brush;
if (overlay) {
brush = new ScatterOverlayBrush((int) pointsOpt, (int) distanceOpt);
@ -488,7 +489,7 @@ public class BrushCommands {
}
return set(session, context,
brush)
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill);
}
@ -498,8 +499,8 @@ public class BrushCommands {
desc = "Scatter a schematic on a surface"
)
@CommandPermissions("worldedit.brush.populateschematic")
public BrushSettings scatterSchemBrush(Player player, LocalSession session, Mask mask, @Arg(name = "clipboard", desc = "Clipboard uri") String clipboardStr, @Arg(name = "radius", desc = "Expression", def = "30") Expression radiusOpt, @Arg(name = "density", desc = "double", def = "50") double density, @Switch(name = 'r', desc = "Apply random rotation") boolean rotate, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
public BrushSettings scatterSchemBrush(Player player, LocalSession session, Mask mask, @Arg(name = "clipboard", desc = "Clipboard uri") String clipboardStr, @Arg(name = "radius", desc = "Expression", def = "30") Expression radius, @Arg(name = "density", desc = "double", def = "50") double density, @Switch(name = 'r', desc = "Apply random rotation") boolean rotate, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
try {
MultiClipboardHolder clipboards = ClipboardFormats.loadAllFromInput(player, clipboardStr, null, true);
if (clipboards == null) {
@ -513,7 +514,7 @@ public class BrushCommands {
}
return set(session, context,
new PopulateSchem(mask, holders, (int) density, rotate)).setSize(radiusOpt);
new PopulateSchem(mask, holders, (int) density, rotate)).setSize(radius);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -541,9 +542,9 @@ public class BrushCommands {
"Note: The seeds define how many splotches there are, recursion defines how large, solid defines whether the pattern is applied per seed, else per block."
)
@CommandPermissions("worldedit.brush.splatter")
public BrushSettings splatterBrush(LocalSession session, Pattern fill, @Arg(name = "radius", desc = "Expression", def = "5") Expression radiusOpt, @Arg(name = "points", desc = "double", def = "1") double pointsOpt, @Arg(name = "recursion", desc = "double", def = "5") double recursion, @Arg(name = "solid", desc = "boolean", def = "true") boolean solid, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
return set(session, context, new SplatterBrush((int) pointsOpt, (int) recursion, solid)).setSize(radiusOpt).setFill(fill);
public BrushSettings splatterBrush(LocalSession session, Pattern fill, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "points", desc = "double", def = "1") double pointsOpt, @Arg(name = "recursion", desc = "double", def = "5") double recursion, @Arg(name = "solid", desc = "boolean", def = "true") boolean solid, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
return set(session, context, new SplatterBrush((int) pointsOpt, (int) recursion, solid)).setSize(radius).setFill(fill);
}
@Command(
@ -574,13 +575,13 @@ public class BrushCommands {
@Arg(desc = "The pattern of blocks to set")
Pattern pattern,
@Arg(desc = "The radius of the cylinder", def = "2")
Expression radiusOpt,
Expression radius,
@Arg(desc = "The height of the cylinder", def = "1")
int height,
@Switch(name = 'h', desc = "Create hollow cylinders instead")
boolean hollow,
InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
worldEdit.checkMaxBrushRadius(height);
BrushSettings settings;
@ -589,7 +590,7 @@ public class BrushCommands {
} else {
settings = set(session, context, new CylinderBrush(height));
}
settings.setSize(radiusOpt)
settings.setSize(radius)
.setFill(pattern);
return settings;
}
@ -604,7 +605,7 @@ public class BrushCommands {
"stood relative to the copied area when you copied it."
)
@CommandPermissions("worldedit.brush.clipboard")
public BrushSettings clipboardBrush(LocalSession session,
public BrushSettings clipboardBrush(Player player,LocalSession session,
@Switch(name = 'a', desc = "Don't paste air from the clipboard")
boolean ignoreAir,
@Switch(name = 'o', desc = "Paste starting at the target location, instead of centering on it")
@ -614,17 +615,21 @@ public class BrushCommands {
@Switch(name = 'b', desc = "Paste biomes if available")
boolean pasteBiomes,
@ArgFlag(name = 'm', desc = "Skip blocks matching this mask in the clipboard", def = "")
@ClipboardMask
Mask sourceMask,
InjectedValueAccess context) throws WorldEditException {
ClipboardHolder holder = session.getClipboard();
Clipboard clipboard = holder.getClipboard();
ClipboardHolder newHolder = new ClipboardHolder(clipboard);
newHolder.setTransform(holder.getTransform());
BlockVector3 size = clipboard.getDimensions();
worldEdit.checkMaxBrushRadius(size.getBlockX());
worldEdit.checkMaxBrushRadius(size.getBlockY());
worldEdit.checkMaxBrushRadius(size.getBlockZ());
return set(session, context, new ClipboardBrush(holder, ignoreAir, usingOrigin, !skipEntities, pasteBiomes, sourceMask));
worldEdit.checkMaxBrushRadius(size.getBlockX() / 2D - 1);
worldEdit.checkMaxBrushRadius(size.getBlockY() / 2D - 1);
worldEdit.checkMaxBrushRadius(size.getBlockZ() / 2D - 1);
return set(session, context, new ClipboardBrush(newHolder, ignoreAir, usingOrigin, !skipEntities, pasteBiomes, sourceMask));
}
@Command(
@ -635,12 +640,12 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.smooth")
public BrushSettings smoothBrush(Player player, LocalSession session, EditSession editSession,
@Arg(desc = "The radius to sample for softening", def = "2")
Expression radiusOpt,
Expression radius,
@Arg(desc = "The number of iterations to perform", def = "4")
int iterations,
@Arg(desc = "The mask of blocks to use for the heightmap", def = "")
Mask maskOpt, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
FawePlayer fp = FawePlayer.wrap(player);
FaweLimit limit = Settings.IMP.getLimit(fp);
@ -648,7 +653,7 @@ public class BrushCommands {
return set(session, context,
new SmoothBrush(iterations, maskOpt))
.setSize(radiusOpt);
.setSize(radius);
}
@Command(
@ -659,14 +664,14 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.ex")
public BrushSettings extinguishBrush(Player player, LocalSession session, EditSession editSession,
@Arg(desc = "The radius to extinguish", def = "5")
Expression radiusOpt,
Expression radius,
InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
Pattern fill = BlockTypes.AIR.getDefaultState();
return set(session, context,
new SphereBrush())
.setSize(radiusOpt)
.setSize(radius)
.setFill(fill)
.setMask(new SingleBlockTypeMask(editSession, BlockTypes.FIRE));
}
@ -679,15 +684,15 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.gravity")
public BrushSettings gravityBrush(Player player, LocalSession session,
@Arg(desc = "The radius to apply gravity in", def = "5")
Expression radiusOpt,
Expression radius,
@Switch(name = 'h', desc = "Affect blocks starting at max Y, rather than the target location Y + radius")
boolean fromMaxY,
InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
return set(session, context,
new GravityBrush(fromMaxY))
.setSize(radiusOpt);
.setSize(radius);
}
@Command(
@ -702,8 +707,8 @@ public class BrushCommands {
"Snow Pic: https://i.imgur.com/Hrzn0I4.png"
)
@CommandPermissions("worldedit.brush.height")
public BrushSettings heightBrush(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radiusOpt, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Range(min = 0, max = 360) int rotation, @Arg(name = "yscale", desc = "double", def = "1") double yscale, @Switch(name = 'r', desc = "TODO") boolean randomRotate, @Switch(name = 'l', desc = "TODO") boolean layers, @Switch(name = 's', desc = "TODO") boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException {
return terrainBrush(player, session, radiusOpt, image, rotation, yscale, false, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context);
public BrushSettings heightBrush(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Range(min = 0, max = 360) int rotation, @Arg(name = "yscale", desc = "double", def = "1") double yscale, @Switch(name = 'r', desc = "TODO") boolean randomRotate, @Switch(name = 'l', desc = "TODO") boolean layers, @Switch(name = 's', desc = "TODO") boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException {
return terrainBrush(player, session, radius, image, rotation, yscale, false, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context);
}
@Command(
@ -715,7 +720,7 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.height")
public BrushSettings cliffBrush(Player player, LocalSession session,
@Arg(name = "radius", desc = "Expression", def = "5")
Expression radiusOpt,
Expression radius,
@Arg(name = "image", desc = "String", def = "")
String image,
@Arg(def = "0", desc = "rotation") @Step(90) @Range(min = 0, max = 360)
@ -728,7 +733,7 @@ public class BrushCommands {
boolean layers,
@Switch(name = 's', desc = "Disables smoothing")
boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException {
return terrainBrush(player, session, radiusOpt, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CYLINDER, context);
return terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CYLINDER, context);
}
@Command(
@ -737,14 +742,14 @@ public class BrushCommands {
desc = "This brush raises or lowers land towards the clicked point"
)
@CommandPermissions("worldedit.brush.height")
public BrushSettings flattenBrush(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radiusOpt, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Step(90) @Range(min = 0, max = 360) int rotation, @Arg(name = "yscale", desc = "double", def = "1") double yscale,
public BrushSettings flattenBrush(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Arg(name = "image", desc = "String", def = "") String image, @Arg(def = "0", desc = "rotation") @Step(90) @Range(min = 0, max = 360) int rotation, @Arg(name = "yscale", desc = "double", def = "1") double yscale,
@Switch(name = 'r', desc = "Enables random off-axis rotation")
boolean randomRotate,
@Switch(name = 'l', desc = "Will work on snow layers")
boolean layers,
@Switch(name = 's', desc = "Disables smoothing")
boolean dontSmooth, InjectedValueAccess context) throws WorldEditException, FileNotFoundException {
return terrainBrush(player, session, radiusOpt, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context);
return terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers, !dontSmooth, ScalableHeightMap.Shape.CONE, context);
}
private BrushSettings terrainBrush(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression") Expression radius, String image, int rotation, double yscale, boolean flat, boolean randomRotate, boolean layers, boolean smooth, ScalableHeightMap.Shape shape, InjectedValueAccess context) throws WorldEditException, FileNotFoundException {
@ -792,13 +797,13 @@ public class BrushCommands {
"Video: https://www.youtube.com/watch?v=RPZIaTbqoZw"
)
@CommandPermissions("worldedit.brush.copy")
public BrushSettings copy(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radiusOpt, @Switch(name = 'r', desc = "Apply random rotation on paste") boolean randomRotate, @Switch(name = 'a', desc = "Apply auto view based rotation on paste") boolean autoRotate, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
player.print(BBC.BRUSH_COPY.format(radiusOpt));
public BrushSettings copy(Player player, LocalSession session, @Arg(name = "radius", desc = "Expression", def = "5") Expression radius, @Switch(name = 'r', desc = "Apply random rotation on paste") boolean randomRotate, @Switch(name = 'a', desc = "Apply auto view based rotation on paste") boolean autoRotate, InjectedValueAccess context) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
player.print(BBC.BRUSH_COPY.format(radius));
return set(session, context,
new CopyPastaBrush(player, session, randomRotate, autoRotate))
.setSize(radiusOpt);
.setSize(radius);
}
@Command(
@ -826,7 +831,7 @@ public class BrushCommands {
@CommandPermissions("worldedit.brush.butcher")
public BrushSettings butcherBrush(Player player, LocalSession session, InjectedValueAccess context,
@Arg(desc = "Radius to kill mobs in", def = "5")
Expression radiusOpt,
Expression radius,
@Switch(name = 'p', desc = "Also kill pets")
boolean killPets,
@Switch(name = 'n', desc = "Also kill NPCs")
@ -843,7 +848,7 @@ public class BrushCommands {
boolean killFriendly,
@Switch(name = 'r', desc = "Also destroy armor stands")
boolean killArmorStands) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radiusOpt);
worldEdit.checkMaxBrushRadius(radius);
CreatureButcher flags = new CreatureButcher(player);
flags.or(CreatureButcher.Flags.FRIENDLY , killFriendly); // No permission check here. Flags will instead be filtered by the subsequent calls.
@ -857,7 +862,7 @@ public class BrushCommands {
return set(session, context,
new ButcherBrush(flags))
.setSize(radiusOpt);
.setSize(radius);
}
public BrushSettings process(CommandLocals locals, BrushSettings settings) throws WorldEditException {
@ -899,12 +904,12 @@ public class BrushCommands {
@Arg(desc = "The shape of the region")
RegionFactory shape,
@Arg(desc = "The size of the brush", def = "5")
Expression radiusOpt,
Expression radius,
@Arg(desc = "The density of the brush", def = "20")
double density,
@Arg(desc = "The type of tree to use")
TreeGenerator.TreeType type) throws WorldEditException, EvaluationException {
setOperationBasedBrush(player, localSession, radiusOpt,
setOperationBasedBrush(player, localSession, radius,
new Paint(new TreeGeneratorFactory(type), density / 100), shape, "worldedit.brush.forest");
}
@ -917,8 +922,8 @@ public class BrushCommands {
@Arg(desc = "The shape of the region")
RegionFactory shape,
@Arg(desc = "The size of the brush", def = "5")
Expression radiusOpt) throws WorldEditException, EvaluationException {
setOperationBasedBrush(player, localSession, radiusOpt,
Expression radius) throws WorldEditException, EvaluationException {
setOperationBasedBrush(player, localSession, radius,
new Deform("y-=1"), shape, "worldedit.brush.raise");
}
@ -931,8 +936,8 @@ public class BrushCommands {
@Arg(desc = "The shape of the region")
RegionFactory shape,
@Arg(desc = "The size of the brush", def = "5")
Expression radiusOpt) throws WorldEditException, EvaluationException {
setOperationBasedBrush(player, localSession, radiusOpt,
Expression radius) throws WorldEditException, EvaluationException {
setOperationBasedBrush(player, localSession, radius,
new Deform("y+=1"), shape, "worldedit.brush.lower");
}

View File

@ -62,6 +62,7 @@ import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.annotation.ClipboardMask;
import com.sk89q.worldedit.internal.annotation.Direction;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.math.BlockVector3;
@ -441,6 +442,7 @@ public class ClipboardCommands {
@Switch(name = 'b', desc = "Paste biomes if available")
boolean pasteBiomes,
@ArgFlag(name = 'm', desc = "Only paste blocks matching this mask", def = "")
@ClipboardMask
Mask sourceMask) throws WorldEditException {
ClipboardHolder holder = session.getClipboard();

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.command.argument;
import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseItem;
@ -27,9 +28,14 @@ import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.annotation.ClipboardMask;
import com.sk89q.worldedit.internal.registry.AbstractFactory;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.session.request.RequestExtent;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.world.World;
@ -41,30 +47,53 @@ import org.enginehub.piston.converter.SuccessfulConversion;
import org.enginehub.piston.inject.InjectedValueAccess;
import org.enginehub.piston.inject.Key;
import javax.annotation.Nullable;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
public class FactoryConverter<T> implements ArgumentConverter<T> {
public static void register(WorldEdit worldEdit, CommandManager commandManager) {
commandManager.registerConverter(Key.of(Pattern.class),
new FactoryConverter<>(worldEdit, WorldEdit::getPatternFactory, "pattern"));
new FactoryConverter<>(worldEdit, WorldEdit::getPatternFactory, "pattern", null));
commandManager.registerConverter(Key.of(Mask.class),
new FactoryConverter<>(worldEdit, WorldEdit::getMaskFactory, "mask"));
new FactoryConverter<>(worldEdit, WorldEdit::getMaskFactory, "mask", null));
commandManager.registerConverter(Key.of(BaseItem.class),
new FactoryConverter<>(worldEdit, WorldEdit::getItemFactory, "item"));
new FactoryConverter<>(worldEdit, WorldEdit::getItemFactory, "item", null));
commandManager.registerConverter(Key.of(Mask.class, ClipboardMask.class),
new FactoryConverter<>(worldEdit, WorldEdit::getMaskFactory, "mask",
context -> {
try {
ClipboardHolder holder = context.getSession().getClipboard();
Transform transform = holder.getTransform();
Extent target;
if (transform.isIdentity()) {
target = holder.getClipboard();
} else {
target = new BlockTransformExtent(holder.getClipboard(), transform);
}
context.setExtent(target);
} catch (EmptyClipboardException e) {
throw new IllegalStateException(e);
}
}));
}
private final WorldEdit worldEdit;
private final Function<WorldEdit, AbstractFactory<T>> factoryExtractor;
private final String description;
@Nullable private final Consumer<ParserContext> contextTweaker;
private FactoryConverter(WorldEdit worldEdit,
Function<WorldEdit, AbstractFactory<T>> factoryExtractor,
String description) {
String description,
@Nullable Consumer<ParserContext> contextTweaker) {
this.worldEdit = worldEdit;
this.factoryExtractor = factoryExtractor;
this.description = description;
this.contextTweaker = contextTweaker;
}
@Override
@ -80,10 +109,15 @@ public class FactoryConverter<T> implements ArgumentConverter<T> {
if (extent instanceof World) {
parserContext.setWorld((World) extent);
}
parserContext.setExtent(new RequestExtent());
}
parserContext.setSession(session);
parserContext.setRestricted(true);
if (contextTweaker != null) {
contextTweaker.accept(parserContext);
}
try {
return SuccessfulConversion.fromSingle(
factoryExtractor.apply(worldEdit).parseFromInput(argument, parserContext)

View File

@ -78,6 +78,6 @@ public class BiomeMaskParser extends InputParser<Mask> {
biomes.add(biome);
}
return Masks.asMask(new BiomeMask2D(new RequestExtent(), biomes));
return Masks.asMask(new BiomeMask2D(context.getExtent(), biomes));
}
}

View File

@ -54,7 +54,7 @@ public class BlockCategoryMaskParser extends InputParser<Mask> {
if (category == null) {
throw new InputParseException("Unrecognised tag '" + input.substring(2) + '\'');
} else {
return new BlockCategoryMask(new RequestExtent(), category);
return new BlockCategoryMask(context.getExtent(), category);
}
}
}

View File

@ -53,7 +53,7 @@ public class BlockStateMaskParser extends InputParser<Mask> {
boolean strict = input.charAt(1) == '=';
String states = input.substring(2 + (strict ? 1 : 0), input.length() - 1);
try {
return new BlockStateMask(new RequestExtent(),
return new BlockStateMask(context.getExtent(),
Splitter.on(',').omitEmptyStrings().trimResults().withKeyValueSeparator('=').split(states),
strict);
} catch (Exception e) {

View File

@ -56,7 +56,7 @@ public class BlocksMaskParser extends InputParser<Mask> {
if (holders.isEmpty()) {
return null;
}
return new BlockMask(new RequestExtent(), holders);
return new BlockMask(context.getExtent(), holders);
} catch (NoMatchException e) {
return null;
}

View File

@ -44,6 +44,6 @@ public class ExistingMaskParser extends SimpleInputParser<Mask> {
@Override
public Mask parseFromSimpleInput(String input, ParserContext context) {
return new ExistingBlockMask(new RequestExtent());
return new ExistingBlockMask(context.getExtent());
}
}

View File

@ -58,7 +58,7 @@ public class ExpressionMaskParser extends InputParser<Mask> {
try {
Expression exp = Expression.compile(input.substring(1), "x", "y", "z");
WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(
new RequestExtent(), Vector3.ONE, Vector3.ZERO);
context.getExtent(), Vector3.ONE, Vector3.ZERO);
exp.setEnvironment(env);
if (context.getActor() != null) {
SessionOwner owner = context.getActor();

View File

@ -62,7 +62,7 @@ public class OffsetMaskParser extends InputParser<Mask> {
if (input.length() > 1) {
submask = worldEdit.getMaskFactory().parseFromInput(input.substring(1), context);
} else {
submask = new ExistingBlockMask(new RequestExtent());
submask = new ExistingBlockMask(context.getExtent());
}
OffsetMask offsetMask = new OffsetMask(submask, BlockVector3.at(0, firstChar == '>' ? -1 : 1, 0));
return new MaskIntersection(offsetMask, Masks.negate(submask));

View File

@ -44,6 +44,6 @@ public class SolidMaskParser extends SimpleInputParser<Mask> {
@Override
public Mask parseFromSimpleInput(String input, ParserContext context) {
return new SolidBlockMask(new RequestExtent());
return new SolidBlockMask(context.getExtent());
}
}

View File

@ -19,8 +19,7 @@
package com.sk89q.worldedit.extent.reorder;
import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable;
import com.google.common.collect.ImmutableSortedSet;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent;
@ -28,15 +27,14 @@ import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.RegionOptimizedComparator;
import com.sk89q.worldedit.util.collection.BlockMap;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* A special extent that batches changes into Minecraft chunks. This helps
@ -46,17 +44,7 @@ import java.util.Set;
*/
public class ChunkBatchingExtent extends AbstractBufferingExtent {
/**
* Comparator optimized for sorting chunks by the region file they reside
* in. This allows for file caches to be used while loading the chunk.
*/
private static final Comparator<BlockVector2> REGION_OPTIMIZED_SORT =
Comparator.comparing((BlockVector2 vec) -> vec.shr(5), BlockVector2.COMPARING_GRID_ARRANGEMENT)
.thenComparing(BlockVector2.COMPARING_GRID_ARRANGEMENT);
private final Table<BlockVector2, BlockVector3, BaseBlock> batches =
TreeBasedTable.create(REGION_OPTIMIZED_SORT, BlockVector3.sortByCoordsYzx());
private final Set<BlockVector3> containedBlocks = new HashSet<>();
private final BlockMap blockMap = BlockMap.create();
private boolean enabled;
public ChunkBatchingExtent(Extent extent) {
@ -80,32 +68,20 @@ public class ChunkBatchingExtent extends AbstractBufferingExtent {
return enabled;
}
private BlockVector2 getChunkPos(BlockVector3 location) {
return location.shr(4).toBlockVector2();
}
private BlockVector3 getInChunkPos(BlockVector3 location) {
return BlockVector3.at(location.getX() & 15, location.getY(), location.getZ() & 15);
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
if (!enabled) {
return setDelegateBlock(location, block);
}
BlockVector2 chunkPos = getChunkPos(location);
BlockVector3 inChunkPos = getInChunkPos(location);
batches.put(chunkPos, inChunkPos, block.toBaseBlock());
containedBlocks.add(location);
blockMap.put(location, block.toBaseBlock());
return true;
}
@Override
protected Optional<BaseBlock> getBufferedBlock(BlockVector3 position) {
if (!containedBlocks.contains(position)) {
return Optional.empty();
}
return Optional.of(batches.get(getChunkPos(position), getInChunkPos(position)));
return Optional.ofNullable(blockMap.get(position));
}
@Override
protected Operation commitBefore() {
if (!commitRequired()) {
@ -114,24 +90,21 @@ public class ChunkBatchingExtent extends AbstractBufferingExtent {
return new Operation() {
// we get modified between create/resume -- only create this on resume to prevent CME
private Iterator<Map.Entry<BlockVector2, Map<BlockVector3, BaseBlock>>> batchIterator;
private Iterator<BlockVector3> iterator;
@Override
public Operation resume(RunContext run) throws WorldEditException {
if (batchIterator == null) {
batchIterator = batches.rowMap().entrySet().iterator();
if (iterator == null) {
iterator = ImmutableSortedSet.copyOf(RegionOptimizedComparator.INSTANCE,
blockMap.keySet()).iterator();
}
if (!batchIterator.hasNext()) {
return null;
while (iterator.hasNext()) {
BlockVector3 position = iterator.next();
BaseBlock block = blockMap.get(position);
getExtent().setBlock(position, block);
}
Map.Entry<BlockVector2, Map<BlockVector3, BaseBlock>> next = batchIterator.next();
BlockVector3 chunkOffset = next.getKey().toBlockVector3().shl(4);
for (Map.Entry<BlockVector3, BaseBlock> block : next.getValue().entrySet()) {
getExtent().setBlock(block.getKey().add(chunkOffset), block.getValue());
containedBlocks.remove(block.getKey());
}
batchIterator.remove();
return this;
blockMap.clear();
return null;
}
@Override

View File

@ -24,23 +24,23 @@ import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.OperationQueue;
import com.sk89q.worldedit.function.operation.SetLocatedBlocks;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.function.operation.SetBlockMap;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.collection.LocatedBlockList;
import com.sk89q.worldedit.util.collection.BlockMap;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockCategories;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
/**
* Re-orders blocks into several stages.
@ -142,8 +142,7 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde
priorityMap.put(BlockTypes.MOVING_PISTON, PlacementPriority.FINAL);
}
private final Set<BlockVector3> containedBlocks = new HashSet<>();
private Map<PlacementPriority, LocatedBlockList> stages = new HashMap<>();
private Map<PlacementPriority, BlockMap> stages = new HashMap<>();
private boolean enabled;
@ -177,7 +176,7 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde
this.enabled = enabled;
for (PlacementPriority priority : PlacementPriority.values()) {
stages.put(priority, new LocatedBlockList());
stages.put(priority, BlockMap.create());
}
}
@ -219,7 +218,7 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde
return setDelegateBlock(location, block);
}
BlockState existing = getBlock(location);
BlockState existing = getExtent().getBlock(location);
PlacementPriority priority = getPlacementPriority(block);
PlacementPriority srcPriority = getPlacementPriority(existing);
@ -228,13 +227,13 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde
switch (srcPriority) {
case FINAL:
stages.get(PlacementPriority.CLEAR_FINAL).add(location, replacement);
stages.get(PlacementPriority.CLEAR_FINAL).put(location, replacement);
break;
case LATE:
stages.get(PlacementPriority.CLEAR_LATE).add(location, replacement);
stages.get(PlacementPriority.CLEAR_LATE).put(location, replacement);
break;
case LAST:
stages.get(PlacementPriority.CLEAR_LAST).add(location, replacement);
stages.get(PlacementPriority.CLEAR_LAST).put(location, replacement);
break;
}
@ -243,16 +242,12 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde
}
}
stages.get(priority).add(location, block);
containedBlocks.add(location);
stages.get(priority).put(location, block.toBaseBlock());
return !existing.equalsFuzzy(block);
}
@Override
protected Optional<BaseBlock> getBufferedBlock(BlockVector3 position) {
if (!containedBlocks.contains(position)) {
return Optional.empty();
}
return stages.values().stream()
.map(blocks -> blocks.get(position))
.filter(Objects::nonNull)
@ -266,7 +261,17 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde
}
List<Operation> operations = new ArrayList<>();
for (PlacementPriority priority : PlacementPriority.values()) {
operations.add(new SetLocatedBlocks(getExtent(), stages.get(priority)));
BlockMap blocks = stages.get(priority);
operations.add(new SetBlockMap(getExtent(), blocks) {
@Override
public Operation resume(RunContext run) throws WorldEditException {
Operation operation = super.resume(run);
if (operation == null) {
blocks.clear();
}
return operation;
}
});
}
return new OperationQueue(operations);

View File

@ -0,0 +1,60 @@
/*
* 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.function.operation;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.LocatedBlock;
import com.sk89q.worldedit.util.collection.BlockMap;
import com.sk89q.worldedit.world.block.BaseBlock;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
public class SetBlockMap implements Operation {
private final Extent extent;
private final BlockMap blocks;
public SetBlockMap(Extent extent, BlockMap blocks) {
this.extent = checkNotNull(extent);
this.blocks = checkNotNull(blocks);
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
for (Map.Entry<BlockVector3, BaseBlock> entry : blocks.entrySet()) {
extent.setBlock(entry.getKey(), entry.getValue());
}
return null;
}
@Override
public void cancel() {
}
@Override
public void addStatusMessages(List<String> messages) {
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.internal.annotation;
import com.sk89q.worldedit.function.mask.Mask;
import org.enginehub.piston.inject.InjectAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a {@link Mask} parameter to use the clipboard as the extent instead of target World/EditSession.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@InjectAnnotation
public @interface ClipboardMask {
}

View File

@ -0,0 +1,77 @@
/*
* 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.internal.block;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.registry.BlockRegistry;
import javax.annotation.Nullable;
import java.util.BitSet;
import java.util.OptionalInt;
import static com.google.common.base.Preconditions.checkState;
public final class BlockStateIdAccess {
private static final BiMap<BlockState, Integer> ASSIGNED_IDS = HashBiMap.create(2 << 13);
public static OptionalInt getBlockStateId(BlockState holder) {
Integer value = ASSIGNED_IDS.get(holder);
return value == null ? OptionalInt.empty() : OptionalInt.of(value);
}
public static @Nullable BlockState getBlockStateById(int id) {
return ASSIGNED_IDS.inverse().get(id);
}
/**
* For platforms that don't have an internal ID system,
* {@link BlockRegistry#getInternalBlockStateId(BlockState)} will return
* {@link OptionalInt#empty()}. In those cases, we will use our own ID system,
* since it's useful for other entries as well.
* @return an unused ID in WorldEdit's ID tracker
*/
private static int provideUnusedWorldEditId() {
return usedIds.nextClearBit(0);
}
private static final BitSet usedIds = new BitSet();
public static void register(BlockState blockState, OptionalInt id) {
int i = id.orElseGet(BlockStateIdAccess::provideUnusedWorldEditId);
BlockState existing = ASSIGNED_IDS.inverse().get(i);
checkState(existing == null || existing == blockState,
"BlockState %s is using the same block ID (%s) as BlockState %s",
blockState, i, existing);
ASSIGNED_IDS.put(blockState, i);
usedIds.set(i);
}
public static void clear() {
ASSIGNED_IDS.clear();
usedIds.clear();
}
private BlockStateIdAccess() {
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.math;
public final class BitMath {
public static int mask(int bits) {
return ~(~0 << bits);
}
public static int unpackX(long packed) {
return extractSigned(packed, 0, 26);
}
public static int unpackZ(long packed) {
return extractSigned(packed, 26, 26);
}
public static int unpackY(long packed) {
return extractSigned(packed, 26 + 26, 12);
}
public static int extractSigned(long i, int shift, int bits) {
return fixSign((int) (i >> shift) & mask(bits), bits);
}
public static int fixSign(int i, int bits) {
// Using https://stackoverflow.com/a/29266331/436524
return i << (32 - bits) >> (32 - bits);
}
private BitMath() {
}
}

View File

@ -20,6 +20,10 @@
package com.sk89q.worldedit.math;
import static com.google.common.base.Preconditions.checkArgument;
import static com.sk89q.worldedit.math.BitMath.mask;
import static com.sk89q.worldedit.math.BitMath.unpackX;
import static com.sk89q.worldedit.math.BitMath.unpackY;
import static com.sk89q.worldedit.math.BitMath.unpackZ;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.extent.Extent;
@ -67,6 +71,31 @@ public abstract class BlockVector3 {
return new BlockVector3Imp(x, y, z);
}
private static final int WORLD_XZ_MINMAX = 30_000_000;
private static final int WORLD_Y_MAX = 4095;
private static boolean isHorizontallyInBounds(int h) {
return -WORLD_XZ_MINMAX <= h && h <= WORLD_XZ_MINMAX;
}
public static boolean isLongPackable(BlockVector3 location) {
return isHorizontallyInBounds(location.getX()) &&
isHorizontallyInBounds(location.getZ()) &&
0 <= location.getY() && location.getY() <= WORLD_Y_MAX;
}
public static void checkLongPackable(BlockVector3 location) {
checkArgument(isLongPackable(location),
"Location exceeds long packing limits: %s", location);
}
private static final long BITS_26 = mask(26);
private static final long BITS_12 = mask(12);
public static BlockVector3 fromLongPackedForm(long packed) {
return at(unpackX(packed), unpackY(packed), unpackZ(packed));
}
// thread-safe initialization idiom
private static final class YzxOrderComparator {
@ -154,6 +183,11 @@ public abstract class BlockVector3 {
// return orDefault.setComponents(getX(), getY() - 1, getZ());
// }
public long toLongPackedForm() {
checkLongPackable(this);
return (getX() & BITS_26) | ((getZ() & BITS_26) << 26) | (((getY() & (long) BITS_12) << (26 + 26)));
}
/**
* Get the X coordinate.
*

View File

@ -0,0 +1,41 @@
/*
* 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.math;
import java.util.Comparator;
import static com.sk89q.worldedit.math.BlockVector2.COMPARING_GRID_ARRANGEMENT;
/**
* Sort block positions by region, then chunk.
*/
public class RegionOptimizedChunkComparator {
private static final Comparator<BlockVector2> CHUNK_COMPARATOR =
Comparator.comparing((BlockVector2 chunkPos) -> chunkPos.shr(5), COMPARING_GRID_ARRANGEMENT)
.thenComparing(COMPARING_GRID_ARRANGEMENT);
public static final Comparator<BlockVector3> INSTANCE
= Comparator.comparing(blockPos -> blockPos.toBlockVector2().shr(4), CHUNK_COMPARATOR);
private RegionOptimizedChunkComparator() {
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.math;
import java.util.Comparator;
/**
* Sort block positions by region, chunk, and finally Y-Z-X.
*/
public class RegionOptimizedComparator {
public static final Comparator<BlockVector3> INSTANCE
= RegionOptimizedChunkComparator.INSTANCE
.thenComparing(BlockVector3.sortByCoordsYzx());
private RegionOptimizedComparator() {
}
}

View File

@ -121,6 +121,7 @@ public class PropertiesConfiguration extends LocalConfiguration {
butcherMaxRadius = getInt("butcher-max-radius", butcherMaxRadius);
allowSymlinks = getBool("allow-symbolic-links", allowSymlinks);
serverSideCUI = getBool("server-side-cui", serverSideCUI);
extendedYLimit = getBool("extended-y-limit", extendedYLimit);
LocalSession.MAX_HISTORY_SIZE = Math.max(15, getInt("history-size", 15));

View File

@ -125,6 +125,7 @@ public class YAMLConfiguration extends LocalConfiguration {
String type = config.getString("shell-save-type", "").trim();
shellSaveType = type.isEmpty() ? null : type;
extendedYLimit = config.getBoolean("compat.extended-y-limit", false);
}
public void unload() {

View File

@ -0,0 +1,427 @@
/*
* 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.util.collection;
import com.google.common.collect.AbstractIterator;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import static com.sk89q.worldedit.math.BitMath.fixSign;
import static com.sk89q.worldedit.math.BitMath.mask;
/**
* A space-efficient map implementation for block locations.
*/
public class BlockMap extends AbstractMap<BlockVector3, BaseBlock> {
/* =========================
IF YOU MAKE CHANGES TO THIS CLASS
Re-run BlockMapTest with the blockmap.fulltesting=true system property.
Or just temporarily remove the annotation disabling the related tests.
========================= */
public static BlockMap create() {
return new BlockMap();
}
public static BlockMap copyOf(Map<? extends BlockVector3, ? extends BaseBlock> source) {
return new BlockMap(source);
}
/*
* Stores blocks by sub-dividing them into smaller groups.
* A block location is 26 bits long for x + z, and usually
* 8 bits for y, although mods such as cubic chunks may
* expand this to infinite. We support up to 32 bits of y.
*
* Grouping key stores 20 bits x + z, 24 bits y.
* Inner key stores 6 bits x + z, 8 bits y.
* Order (lowest to highest) is x-z-y.
*/
private static final long BITS_24 = mask(24);
private static final long BITS_20 = mask(20);
private static final int BITS_8 = mask(8);
private static final int BITS_6 = mask(6);
private static long toGroupKey(BlockVector3 location) {
return ((location.getX() >>> 6) & BITS_20)
| (((location.getZ() >>> 6) & BITS_20) << 20)
| (((location.getY() >>> 8) & BITS_24) << (20 + 20));
}
private static int toInnerKey(BlockVector3 location) {
return (location.getX() & BITS_6)
| ((location.getZ() & BITS_6) << 6)
| ((location.getY() & BITS_8) << (6 + 6));
}
private static final long GROUP_X = BITS_20;
private static final long GROUP_Z = BITS_20 << 20;
private static final long GROUP_Y = BITS_24 << (20 + 20);
private static final int INNER_X = BITS_6;
private static final int INNER_Z = BITS_6 << 6;
private static final int INNER_Y = BITS_8 << (6 + 6);
private static BlockVector3 reconstructLocation(long group, int inner) {
int groupX = (int) ((group & GROUP_X) << 6);
int x = fixSign(groupX | (inner & INNER_X), 26);
int groupZ = (int) ((group & GROUP_Z) >>> (20 - 6));
int z = fixSign(groupZ | ((inner & INNER_Z) >>> 6), 26);
int groupY = (int) ((group & GROUP_Y) >>> (20 + 20 - 8));
int y = groupY | ((inner & INNER_Y) >>> (6 + 6));
return BlockVector3.at(x, y, z);
}
private final Long2ObjectMap<SubBlockMap> maps = new Long2ObjectOpenHashMap<>(4, 1f);
private Set<Entry<BlockVector3, BaseBlock>> entrySet;
private Collection<BaseBlock> values;
private BlockMap() {
}
private BlockMap(Map<? extends BlockVector3, ? extends BaseBlock> source) {
putAll(source);
}
private SubBlockMap getOrCreateMap(long groupKey) {
return maps.computeIfAbsent(groupKey, k -> new SubBlockMap());
}
private SubBlockMap getOrEmptyMap(long groupKey) {
return maps.getOrDefault(groupKey, SubBlockMap.EMPTY);
}
/**
* Apply the function the the map at {@code groupKey}, and if the function empties the map,
* delete it from {@code maps}.
*/
private <R> R cleanlyModifyMap(long groupKey, Function<Int2ObjectMap<BaseBlock>, R> func) {
SubBlockMap map = maps.get(groupKey);
if (map != null) {
R result = func.apply(map);
if (map.isEmpty()) {
maps.remove(groupKey);
}
return result;
}
map = new SubBlockMap();
R result = func.apply(map);
if (!map.isEmpty()) {
maps.put(groupKey, map);
}
return result;
}
@Override
public BaseBlock put(BlockVector3 key, BaseBlock value) {
return getOrCreateMap(toGroupKey(key)).put(toInnerKey(key), value);
}
@Override
public BaseBlock getOrDefault(Object key, BaseBlock defaultValue) {
BlockVector3 vec = (BlockVector3) key;
return getOrEmptyMap(toGroupKey(vec))
.getOrDefault(toInnerKey(vec), defaultValue);
}
@Override
public void forEach(BiConsumer<? super BlockVector3, ? super BaseBlock> action) {
maps.forEach((groupKey, m) ->
m.forEach((innerKey, block) ->
action.accept(reconstructLocation(groupKey, innerKey), block)
)
);
}
@Override
public void replaceAll(BiFunction<? super BlockVector3, ? super BaseBlock, ? extends BaseBlock> function) {
maps.forEach((groupKey, m) ->
m.replaceAll((innerKey, block) ->
function.apply(reconstructLocation(groupKey, innerKey), block)
)
);
}
@Override
public BaseBlock putIfAbsent(BlockVector3 key, BaseBlock value) {
return getOrCreateMap(toGroupKey(key)).putIfAbsent(toInnerKey(key), value);
}
@Override
public boolean remove(Object key, Object value) {
BlockVector3 vec = (BlockVector3) key;
return cleanlyModifyMap(toGroupKey(vec),
map -> map.remove(toInnerKey(vec), value));
}
@Override
public boolean replace(BlockVector3 key, BaseBlock oldValue, BaseBlock newValue) {
return cleanlyModifyMap(toGroupKey(key),
map -> map.replace(toInnerKey(key), oldValue, newValue));
}
@Override
public BaseBlock replace(BlockVector3 key, BaseBlock value) {
return getOrCreateMap(toGroupKey(key)).replace(toInnerKey(key), value);
}
@Override
public BaseBlock computeIfAbsent(BlockVector3 key, Function<? super BlockVector3, ? extends BaseBlock> mappingFunction) {
return cleanlyModifyMap(toGroupKey(key),
map -> map.computeIfAbsent(toInnerKey(key), ik -> mappingFunction.apply(key)));
}
@Override
public BaseBlock computeIfPresent(BlockVector3 key, BiFunction<? super BlockVector3, ? super BaseBlock, ? extends BaseBlock> remappingFunction) {
return cleanlyModifyMap(toGroupKey(key),
map -> map.computeIfPresent(toInnerKey(key), (ik, block) -> remappingFunction.apply(key, block)));
}
@Override
public BaseBlock compute(BlockVector3 key, BiFunction<? super BlockVector3, ? super BaseBlock, ? extends BaseBlock> remappingFunction) {
return cleanlyModifyMap(toGroupKey(key),
map -> map.compute(toInnerKey(key), (ik, block) -> remappingFunction.apply(key, block)));
}
@Override
public BaseBlock merge(BlockVector3 key, BaseBlock value, BiFunction<? super BaseBlock, ? super BaseBlock, ? extends BaseBlock> remappingFunction) {
return cleanlyModifyMap(toGroupKey(key),
map -> map.merge(toInnerKey(key), value, remappingFunction));
}
@Override
public Set<Entry<BlockVector3, BaseBlock>> entrySet() {
Set<Entry<BlockVector3, BaseBlock>> es = entrySet;
if (es == null) {
entrySet = es = new AbstractSet<Entry<BlockVector3, BaseBlock>>() {
@Override
public Iterator<Entry<BlockVector3, BaseBlock>> iterator() {
return new AbstractIterator<Entry<BlockVector3, BaseBlock>>() {
private final ObjectIterator<Long2ObjectMap.Entry<SubBlockMap>> primaryIterator
= Long2ObjectMaps.fastIterator(maps);
private long currentGroupKey;
private ObjectIterator<Int2ObjectMap.Entry<BaseBlock>> secondaryIterator;
@Override
protected Entry<BlockVector3, BaseBlock> computeNext() {
if (secondaryIterator == null || !secondaryIterator.hasNext()) {
if (!primaryIterator.hasNext()) {
return endOfData();
}
Long2ObjectMap.Entry<SubBlockMap> next = primaryIterator.next();
currentGroupKey = next.getLongKey();
secondaryIterator = Int2ObjectMaps.fastIterator(next.getValue());
}
Int2ObjectMap.Entry<BaseBlock> next = secondaryIterator.next();
return new LazyEntry(currentGroupKey, next.getIntKey(), next.getValue());
}
};
}
@Override
public int size() {
return BlockMap.this.size();
}
};
}
return es;
}
private final class LazyEntry implements Map.Entry<BlockVector3, BaseBlock> {
private final long groupKey;
private final int innerKey;
private BlockVector3 lazyKey;
private BaseBlock value;
private LazyEntry(long groupKey, int innerKey, BaseBlock value) {
this.groupKey = groupKey;
this.innerKey = innerKey;
this.value = value;
}
@Override
public BlockVector3 getKey() {
BlockVector3 result = lazyKey;
if (result == null) {
lazyKey = result = reconstructLocation(groupKey, innerKey);
}
return result;
}
@Override
public BaseBlock getValue() {
return value;
}
@Override
public BaseBlock setValue(BaseBlock value) {
this.value = value;
return getOrCreateMap(groupKey).put(innerKey, value);
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
if (o instanceof LazyEntry) {
LazyEntry otherE = (LazyEntry) o;
return otherE.groupKey == groupKey
&& otherE.innerKey == innerKey
&& Objects.equals(value, e.getValue());
}
return Objects.equals(getKey(), e.getKey()) && Objects.equals(value, e.getValue());
}
@Override
public int hashCode() {
return Objects.hashCode(getKey()) ^ Objects.hashCode(value);
}
@Override
public String toString() {
return getKey() + "=" + getValue();
}
}
@Override
public boolean containsValue(Object value) {
return maps.values().stream().anyMatch(m -> m.containsValue(value));
}
@Override
public boolean containsKey(Object key) {
BlockVector3 vec = (BlockVector3) key;
Map<Integer, BaseBlock> activeMap = maps.get(toGroupKey(vec));
if (activeMap == null) {
return false;
}
return activeMap.containsKey(toInnerKey(vec));
}
@Override
public BaseBlock get(Object key) {
BlockVector3 vec = (BlockVector3) key;
Map<Integer, BaseBlock> activeMap = maps.get(toGroupKey(vec));
if (activeMap == null) {
return null;
}
return activeMap.get(toInnerKey(vec));
}
@Override
public BaseBlock remove(Object key) {
BlockVector3 vec = (BlockVector3) key;
Map<Integer, BaseBlock> activeMap = maps.get(toGroupKey(vec));
if (activeMap == null) {
return null;
}
BaseBlock removed = activeMap.remove(toInnerKey(vec));
if (activeMap.isEmpty()) {
maps.remove(toGroupKey(vec));
}
return removed;
}
@Override
public void putAll(Map<? extends BlockVector3, ? extends BaseBlock> m) {
if (m instanceof BlockMap) {
// optimize insertions:
((BlockMap) m).maps.forEach((groupKey, map) ->
getOrCreateMap(groupKey).putAll(map)
);
} else {
super.putAll(m);
}
}
@Override
public void clear() {
maps.clear();
}
@Override
public int size() {
return maps.values().stream().mapToInt(Map::size).sum();
}
// no keySet override, since we can't really optimize it.
// we can optimize values access though, by skipping BV construction.
@Override
public Collection<BaseBlock> values() {
Collection<BaseBlock> vs = values;
if (vs == null) {
values = vs = new AbstractCollection<BaseBlock>() {
@Override
public Iterator<BaseBlock> iterator() {
return maps.values().stream()
.flatMap(m -> m.values().stream())
.iterator();
}
@Override
public int size() {
return BlockMap.this.size();
}
};
}
return vs;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof BlockMap) {
// optimize by skipping entry translations:
return maps.equals(((BlockMap) o).maps);
}
return super.equals(o);
}
// satisfy checkstyle
@Override
public int hashCode() {
return super.hashCode();
}
}

View File

@ -19,71 +19,76 @@
package com.sk89q.worldedit.util.collection;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Iterators;
import com.sk89q.worldedit.WorldEdit;
import com.google.common.collect.Iterators;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.LocatedBlock;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Iterator;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Wrapper around a list of blocks located in the world.
*/
public class LocatedBlockList implements Iterable<LocatedBlock> {
private final Map<BlockVector3, LocatedBlock> map = new LinkedHashMap<>();
private final BlockMap blocks = BlockMap.create();
private final PositionList order = PositionList.create(
WorldEdit.getInstance().getConfiguration().extendedYLimit
);
public LocatedBlockList() {
}
public LocatedBlockList(Collection<? extends LocatedBlock> collection) {
for (LocatedBlock locatedBlock : collection) {
map.put(locatedBlock.getLocation(), locatedBlock);
add(locatedBlock.getLocation(), locatedBlock.getBlock());
}
}
public void add(LocatedBlock setBlockCall) {
checkNotNull(setBlockCall);
map.put(setBlockCall.getLocation(), setBlockCall);
add(setBlockCall.getLocation(), setBlockCall.getBlock());
}
public <B extends BlockStateHolder<B>> void add(BlockVector3 location, B block) {
add(new LocatedBlock(location, block.toBaseBlock()));
blocks.put(location, block.toBaseBlock());
order.add(location);
}
public boolean containsLocation(BlockVector3 location) {
return map.containsKey(location);
return blocks.containsKey(location);
}
public @Nullable BaseBlock get(BlockVector3 location) {
return map.get(location).getBlock();
return blocks.get(location);
}
public int size() {
return map.size();
return order.size();
}
public void clear() {
map.clear();
blocks.clear();
order.clear();
}
@Override
public Iterator<LocatedBlock> iterator() {
return map.values().iterator();
return Iterators.transform(order.iterator(), position ->
new LocatedBlock(position, blocks.get(position)));
}
public Iterator<LocatedBlock> reverseIterator() {
List<LocatedBlock> data = new ArrayList<>(map.values());
Collections.reverse(data);
return data.iterator();
return Iterators.transform(order.reverseIterator(), position ->
new LocatedBlock(position, blocks.get(position)));
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.util.collection;
import com.google.common.collect.AbstractIterator;
import com.sk89q.worldedit.math.BlockVector3;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
import it.unimi.dsi.fastutil.longs.LongListIterator;
import java.util.Iterator;
import java.util.function.Predicate;
import java.util.function.ToLongFunction;
class LongPositionList implements PositionList {
private final LongList delegate = new LongArrayList();
@Override
public BlockVector3 get(int index) {
return BlockVector3.fromLongPackedForm(delegate.getLong(index));
}
@Override
public void add(BlockVector3 vector) {
delegate.add(vector.toLongPackedForm());
}
@Override
public int size() {
return delegate.size();
}
@Override
public void clear() {
delegate.clear();
}
@Override
public Iterator<BlockVector3> iterator() {
return new PositionIterator(delegate.iterator(),
LongListIterator::hasNext,
LongListIterator::nextLong);
}
@Override
public Iterator<BlockVector3> reverseIterator() {
return new PositionIterator(delegate.listIterator(size()),
LongListIterator::hasPrevious,
LongListIterator::previousLong);
}
private static final class PositionIterator extends AbstractIterator<BlockVector3> {
private final LongListIterator iterator;
private final Predicate<LongListIterator> hasNext;
private final ToLongFunction<LongListIterator> next;
private PositionIterator(LongListIterator iterator,
Predicate<LongListIterator> hasNext,
ToLongFunction<LongListIterator> next) {
this.iterator = iterator;
this.hasNext = hasNext;
this.next = next;
}
@Override
protected BlockVector3 computeNext() {
return hasNext.test(iterator)
? BlockVector3.fromLongPackedForm(next.applyAsLong(iterator))
: endOfData();
}
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.util.collection;
import com.sk89q.worldedit.math.BlockVector3;
import java.util.Iterator;
interface PositionList {
static PositionList create(boolean extendedYLimit) {
if (extendedYLimit) {
return new VectorPositionList();
}
return new LongPositionList();
}
BlockVector3 get(int index);
void add(BlockVector3 vector);
int size();
void clear();
Iterator<BlockVector3> iterator();
Iterator<BlockVector3> reverseIterator();
}

View File

@ -0,0 +1,194 @@
/*
* 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.util.collection;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntMaps;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.AbstractObjectSet;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import java.util.NoSuchElementException;
import java.util.function.BiFunction;
/**
* Int-to-BaseBlock map, but with optimizations for common cases.
*/
class SubBlockMap extends AbstractInt2ObjectMap<BaseBlock> {
private static boolean hasInt(BlockState b) {
return BlockStateIdAccess.getBlockStateId(b).isPresent();
}
private static boolean isUncommon(BaseBlock block) {
return block.hasNbtData() || !hasInt(block.toImmutableState());
}
private static int assumeAsInt(BlockState b) {
return BlockStateIdAccess.getBlockStateId(b)
.orElseThrow(() -> new IllegalStateException("Block state " + b + " did not have an ID"));
}
private static BaseBlock assumeAsBlock(int id) {
if (id == Integer.MIN_VALUE) {
return null;
}
BlockState state = BlockStateIdAccess.getBlockStateById(id);
if (state == null) {
throw new IllegalStateException("No state for ID " + id);
}
return state.toBaseBlock();
}
static final SubBlockMap EMPTY = new SubBlockMap();
private final Int2IntMap commonMap = new Int2IntOpenHashMap(64, 1f);
private final Int2ObjectMap<BaseBlock> uncommonMap = new Int2ObjectOpenHashMap<>(1, 1f);
{
commonMap.defaultReturnValue(Integer.MIN_VALUE);
}
@Override
public int size() {
return commonMap.size() + uncommonMap.size();
}
@Override
public ObjectSet<Entry<BaseBlock>> int2ObjectEntrySet() {
return new AbstractObjectSet<Entry<BaseBlock>>() {
@Override
public ObjectIterator<Entry<BaseBlock>> iterator() {
return new ObjectIterator<Entry<BaseBlock>>() {
private final ObjectIterator<Int2IntMap.Entry> commonIter
= Int2IntMaps.fastIterator(commonMap);
private final ObjectIterator<Int2ObjectMap.Entry<BaseBlock>> uncommonIter
= Int2ObjectMaps.fastIterator(uncommonMap);
@Override
public boolean hasNext() {
return commonIter.hasNext() || uncommonIter.hasNext();
}
@Override
public Entry<BaseBlock> next() {
if (commonIter.hasNext()) {
Int2IntMap.Entry e = commonIter.next();
return new BasicEntry<>(
e.getIntKey(), assumeAsBlock(e.getIntValue())
);
}
if (uncommonIter.hasNext()) {
return uncommonIter.next();
}
throw new NoSuchElementException();
}
};
}
@Override
public int size() {
return SubBlockMap.this.size();
}
};
}
@Override
public BaseBlock get(int key) {
int oldId = commonMap.get(key);
if (oldId == Integer.MIN_VALUE) {
return uncommonMap.get(key);
}
return assumeAsBlock(oldId);
}
@Override
public boolean containsKey(int k) {
return commonMap.containsKey(k) || uncommonMap.containsKey(k);
}
@Override
public boolean containsValue(Object v) {
BaseBlock block = (BaseBlock) v;
if (isUncommon(block)) {
return uncommonMap.containsValue(block);
}
return commonMap.containsValue(assumeAsInt(block.toImmutableState()));
}
@Override
public BaseBlock put(int key, BaseBlock value) {
if (isUncommon(value)) {
BaseBlock old = uncommonMap.put(key, value);
if (old == null) {
// ensure common doesn't have the entry too
int oldId = commonMap.remove(key);
return assumeAsBlock(oldId);
}
return old;
}
int oldId = commonMap.put(key, assumeAsInt(value.toImmutableState()));
return assumeAsBlock(oldId);
}
@Override
public BaseBlock remove(int key) {
int removed = commonMap.remove(key);
if (removed == Integer.MIN_VALUE) {
return uncommonMap.remove(key);
}
return assumeAsBlock(removed);
}
@Override
public void replaceAll(BiFunction<? super Integer, ? super BaseBlock, ? extends BaseBlock> function) {
for (ObjectIterator<Int2IntMap.Entry> iter = Int2IntMaps.fastIterator(commonMap);
iter.hasNext(); ) {
Int2IntMap.Entry next = iter.next();
BaseBlock value = function.apply(next.getIntKey(), assumeAsBlock(next.getIntValue()));
if (isUncommon(value)) {
uncommonMap.put(next.getIntKey(), value);
iter.remove();
} else {
next.setValue(assumeAsInt(value.toImmutableState()));
}
}
for (ObjectIterator<Int2ObjectMap.Entry<BaseBlock>> iter = Int2ObjectMaps.fastIterator(uncommonMap);
iter.hasNext(); ) {
Int2ObjectMap.Entry<BaseBlock> next = iter.next();
BaseBlock value = function.apply(next.getIntKey(), next.getValue());
if (isUncommon(value)) {
next.setValue(value);
} else {
commonMap.put(next.getIntKey(), assumeAsInt(value.toImmutableState()));
iter.remove();
}
}
}
}

View File

@ -0,0 +1,98 @@
/*
* 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.util.collection;
import com.google.common.collect.AbstractIterator;
import com.sk89q.worldedit.math.BlockVector3;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntListIterator;
import java.util.Iterator;
class VectorPositionList implements PositionList {
private final IntList delegate = new IntArrayList();
@Override
public BlockVector3 get(int index) {
int ri = index * 3;
return BlockVector3.at(
delegate.getInt(ri),
delegate.getInt(ri + 1),
delegate.getInt(ri + 2));
}
@Override
public void add(BlockVector3 vector) {
delegate.add(vector.getX());
delegate.add(vector.getY());
delegate.add(vector.getZ());
}
@Override
public int size() {
return delegate.size();
}
@Override
public void clear() {
delegate.clear();
}
@Override
public Iterator<BlockVector3> iterator() {
return new AbstractIterator<BlockVector3>() {
private final IntIterator iterator = delegate.iterator();
@Override
protected BlockVector3 computeNext() {
if (!iterator.hasNext()) {
return endOfData();
}
return BlockVector3.at(
iterator.nextInt(),
iterator.nextInt(),
iterator.nextInt());
}
};
}
@Override
public Iterator<BlockVector3> reverseIterator() {
return new AbstractIterator<BlockVector3>() {
private final IntListIterator iterator = delegate.listIterator(delegate.size());
@Override
protected BlockVector3 computeNext() {
if (!iterator.hasPrevious()) {
return endOfData();
}
return BlockVector3.at(
iterator.previousInt(),
iterator.previousInt(),
iterator.previousInt());
}
};
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.util.concurrency;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
/**
* Thread-safe lazy reference.
*/
public class LazyReference<T> {
public static <T> LazyReference<T> from(Supplier<T> valueComputation) {
return new LazyReference<>(valueComputation);
}
// Memory saving technique: hold the computation info in the same reference field that we'll
// put the value into, so the memory possibly retained by those parts is GC'able as soon as
// it's no longer needed.
private static final class RefInfo<T> {
private final Lock lock = new ReentrantLock();
private final Supplier<T> valueComputation;
private RefInfo(Supplier<T> valueComputation) {
this.valueComputation = valueComputation;
}
}
private Object value;
private LazyReference(Supplier<T> valueComputation) {
this.value = new RefInfo<>(valueComputation);
}
// casts are safe, value is either RefInfo or T
@SuppressWarnings("unchecked")
public T getValue() {
Object v = value;
if (!(v instanceof RefInfo)) {
return (T) v;
}
RefInfo<T> refInfo = (RefInfo<T>) v;
refInfo.lock.lock();
try {
v = value;
if (!(v instanceof RefInfo)) {
return (T) v;
}
value = v = refInfo.valueComputation.get();
return (T) v;
} finally {
refInfo.lock.unlock();
}
}
}

View File

@ -104,7 +104,7 @@ public final class LegacyMapper {
if (type.hasProperty(PropertyKey.WATERLOGGED)) {
blockState = blockState.with(PropertyKey.WATERLOGGED, false);
}
int combinedId = getCombinedId(blockEntry.getKey());
Integer combinedId = getCombinedId(blockEntry.getKey());
blockArr[combinedId] = blockState.getInternalId();
blockStateToLegacyId4Data.put(blockState.getInternalId(), (Integer) combinedId);
@ -221,7 +221,7 @@ public final class LegacyMapper {
}
public void register(int id, int data, BlockStateHolder state) {
int combinedId = ((id << 4) + data);
Integer combinedId = ((id << 4) + data);
extraId4DataToStateId.put(combinedId, (Integer) state.getInternalId());
blockStateToLegacyId4Data.putIfAbsent(state.getInternalId(), combinedId);
}

View File

@ -43,11 +43,7 @@ public enum UnsafeUtils {
INT_ARRAY_SCALE = UNSAFE.arrayIndexScale(int[].class);
SHORT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(short[].class);
SHORT_ARRAY_SCALE = UNSAFE.arrayIndexScale(short[].class);
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError("Cannot access Unsafe");
} catch (NoSuchFieldException e) {
throw new ExceptionInInitializerError("Cannot access Unsafe");
} catch (SecurityException e) {
} catch (IllegalAccessException | NoSuchFieldException | SecurityException e) {
throw new ExceptionInInitializerError("Cannot access Unsafe");
}
}

View File

@ -19,16 +19,6 @@
package com.sk89q.worldedit.internal.expression;
import static java.lang.Math.atan2;
import static java.lang.Math.sin;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Platform;
@ -36,17 +26,27 @@ import com.sk89q.worldedit.internal.expression.lexer.LexerException;
import com.sk89q.worldedit.internal.expression.parser.ParserException;
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
import com.sk89q.worldedit.internal.expression.runtime.ExpressionEnvironment;
import com.sk89q.worldedit.internal.expression.runtime.ExpressionTimeoutException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static java.lang.Math.atan2;
import static java.lang.Math.sin;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ExpressionTest {
@Before
private Platform mockPlat = mock(Platform.class);
@BeforeEach
public void setup() {
Platform mockPlat = Mockito.mock(Platform.class);
Mockito.when(mockPlat.getConfiguration()).thenReturn(new LocalConfiguration() {
when(mockPlat.getConfiguration()).thenReturn(new LocalConfiguration() {
@Override
public void load() {
}
@ -54,9 +54,14 @@ public class ExpressionTest {
WorldEdit.getInstance().getPlatformManager().register(mockPlat);
}
@AfterEach
public void tearDown() {
WorldEdit.getInstance().getPlatformManager().unregister(mockPlat);
}
@Test
public void testEvaluate() throws ExpressionException {
// check
// check
assertEquals(1 - 2 + 3, simpleEval("1 - 2 + 3"), 0);
// check unary ops
@ -73,56 +78,46 @@ public class ExpressionTest {
}
@Test
public void testErrors() throws ExpressionException {
// test lexer errors
try {
compile("#");
fail("Error expected");
} catch (LexerException e) {
assertEquals("Error position", 0, e.getPosition());
}
// test parser errors
try {
compile("x");
fail("Error expected");
} catch (ParserException e) {
assertEquals("Error position", 0, e.getPosition());
}
try {
compile("x()");
fail("Error expected");
} catch (ParserException e) {
assertEquals("Error position", 0, e.getPosition());
}
try {
compile("(");
fail("Error expected");
} catch (ParserException ignored) {}
try {
compile("x(");
fail("Error expected");
} catch (ParserException ignored) {}
// test overloader errors
try {
compile("atan2(1)");
fail("Error expected");
} catch (ParserException e) {
assertEquals("Error position", 0, e.getPosition());
}
try {
compile("atan2(1, 2, 3)");
fail("Error expected");
} catch (ParserException e) {
assertEquals("Error position", 0, e.getPosition());
}
try {
compile("rotate(1, 2, 3)");
fail("Error expected");
} catch (ParserException e) {
assertEquals("Error position", 0, e.getPosition());
}
public void testErrors() {
assertAll(
// test lexer errors
() -> {
LexerException e = assertThrows(LexerException.class,
() -> compile("#"));
assertEquals(0, e.getPosition(), "Error position");
},
// test parser errors
() -> {
ParserException e = assertThrows(ParserException.class,
() -> compile("x"));
assertEquals(0, e.getPosition(), "Error position");
},
() -> {
ParserException e = assertThrows(ParserException.class,
() -> compile("x()"));
assertEquals(0, e.getPosition(), "Error position");
},
() -> assertThrows(ParserException.class,
() -> compile("(")),
() -> assertThrows(ParserException.class,
() -> compile("x(")),
// test overloader errors
() -> {
ParserException e = assertThrows(ParserException.class,
() -> compile("atan2(1)"));
assertEquals(0, e.getPosition(), "Error position");
},
() -> {
ParserException e = assertThrows(ParserException.class,
() -> compile("atan2(1, 2, 3)"));
assertEquals(0, e.getPosition(), "Error position");
},
() -> {
ParserException e = assertThrows(ParserException.class,
() -> compile("rotate(1, 2, 3)"));
assertEquals(0, e.getPosition(), "Error position");
}
);
}
@Test
@ -186,13 +181,11 @@ public class ExpressionTest {
}
@Test
public void testTimeout() throws Exception {
try {
simpleEval("for(i=0;i<256;i++){for(j=0;j<256;j++){for(k=0;k<256;k++){for(l=0;l<256;l++){ln(pi)}}}}");
fail("Loop was not stopped.");
} catch (EvaluationException e) {
assertTrue(e.getMessage().contains("Calculations exceeded time limit"));
}
public void testTimeout() {
ExpressionTimeoutException e = assertThrows(ExpressionTimeoutException.class,
() -> simpleEval("for(i=0;i<256;i++){for(j=0;j<256;j++){for(k=0;k<256;k++){for(l=0;l<256;l++){ln(pi)}}}}"),
"Loop was not stopped.");
assertTrue(e.getMessage().contains("Calculations exceeded time limit"));
}
private double simpleEval(String expressionString) throws ExpressionException {