diff --git a/.travis.yml b/.travis.yml index 7b43b6822..9321cf073 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,19 @@ language: java notifications: email: false before_install: chmod +x gradlew -install: ./gradlew setupCIWorkspace -S -matrix: - include: - - jdk: oraclejdk7 - script: ./gradlew build -S \ No newline at end of file +install: ./gradlew setupCIWorkspace -s +script: ./gradlew build -s +jdk: + - oraclejdk8 + - oraclejdk7 + - openjdk6 +# Caching for Gradle files, prevents hitting Maven too much. +before_cache: + - find $HOME/.gradle/ -name '*.lock' -print -exec rm -f {} \; +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + +# Faster builds without sudo. +sudo: false diff --git a/build.gradle b/build.gradle index 6d316e9b6..de82d17eb 100644 --- a/build.gradle +++ b/build.gradle @@ -26,9 +26,9 @@ buildscript { } dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.0' + classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3' classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:3.0.1' - classpath 'org.ajoberstar:gradle-git:0.12.0' + classpath 'org.ajoberstar:gradle-git:1.4.2' } } @@ -36,14 +36,19 @@ if (!project.hasProperty("artifactory_contextUrl")) ext.artifactory_contextUrl = if (!project.hasProperty("artifactory_user")) ext.artifactory_user = "guest" if (!project.hasProperty("artifactory_password")) ext.artifactory_password = "" -if (!project.hasProperty("gitCommitHash")) { +if (!project.hasProperty("gitCommitHash") && !JavaVersion.current().isJava6()) { try { - def repo = org.ajoberstar.grgit.Grgit.open(project.file('.')) + def Grgit = Class.forName("org.ajoberstar.grgit.Grgit"); + def Grgit_open = Grgit.getDeclaredMethod("open", File.class) + def repo = Grgit_open.invoke(null, project.file('.')) ext.gitCommitHash = repo.head().abbreviatedId } catch (Exception e) { - ext.gitCommitHash = "no_git_id" + println "Error getting commit hash: " + e.getMessage() } } +if (!project.hasProperty("gitCommitHash")) { + ext.gitCommitHash = "no_git_id" +} subprojects { apply plugin: 'java' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372aef5..5ccda13e9 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7f72d79da..b2c6e7a02 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jan 12 02:06:32 PST 2016 +#Mon Feb 22 17:40:44 PST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-bin.zip diff --git a/gradlew.bat b/gradlew.bat index aec99730b..72d362daf 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -46,7 +46,7 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args diff --git a/worldedit-bukkit/build.gradle b/worldedit-bukkit/build.gradle index ed882a1c0..cc23519df 100644 --- a/worldedit-bukkit/build.gradle +++ b/worldedit-bukkit/build.gradle @@ -1,11 +1,14 @@ apply plugin: 'eclipse' apply plugin: 'idea' +repositories { + maven { url "https://hub.spigotmc.org/nexus/content/groups/public" } +} + dependencies { compile project(':worldedit-core') compile 'com.sk89q:dummypermscompat:1.8' - compile 'org.sk89q.bukkit:bukkit-classloader-check:1.7.9-R0.2' - compile 'org.bukkit:bukkit:1.7.9-R0.2' + compile 'org.bukkit:bukkit:1.8.8-R0.1-SNAPSHOT' // zzz testCompile 'org.mockito:mockito-core:1.9.0-rc1' } diff --git a/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_9_R1.class b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_9_R1.class new file mode 100644 index 000000000..77edca329 Binary files /dev/null and b/worldedit-bukkit/src/main/resources/com/sk89q/worldedit/bukkit/adapter/impl/Spigot_v1_9_R1.class differ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 817be6368..9439f8184 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -24,8 +24,6 @@ import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.util.io.file.FilenameException; -import com.sk89q.worldedit.util.io.file.FilenameResolutionException; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; @@ -40,21 +38,25 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.util.io.Closer; +import com.sk89q.worldedit.util.command.binding.Switch; import com.sk89q.worldedit.util.command.parametric.Optional; +import com.sk89q.worldedit.util.io.Closer; +import com.sk89q.worldedit.util.io.file.FilenameException; import com.sk89q.worldedit.world.registry.WorldData; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; import static com.google.common.base.Preconditions.checkNotNull; @@ -63,6 +65,10 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class SchematicCommands { + /** + * 9 schematics per page fits in the MC chat window. + */ + private static final int SCHEMATICS_PER_PAGE = 9; private static final Logger log = Logger.getLogger(SchematicCommands.class.getCanonicalName()); private final WorldEdit worldEdit; @@ -244,26 +250,35 @@ public class SchematicCommands { @Command( aliases = {"list", "all", "ls"}, desc = "List saved schematics", - max = 0, - flags = "dn", + min = 0, + max = 1, + flags = "dnp", help = "List all schematics in the schematics directory\n" + " -d sorts by date, oldest first\n" + - " -n sorts by date, newest first\n" + " -n sorts by date, newest first\n" + + " -p prints the requested page\n" ) @CommandPermissions("worldedit.schematic.list") - public void list(Actor actor, CommandContext args) throws WorldEditException { + public void list(Actor actor, CommandContext args, @Switch('p') @Optional("1") int page) throws WorldEditException { File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().saveDir); - File[] files = dir.listFiles(new FileFilter(){ - @Override - public boolean accept(File file) { - // sort out directories from the schematic list - // if WE supports sub-directories in the future, - // this will have to be changed - return file.isFile(); - } - }); - if (files == null) { - throw new FilenameResolutionException(dir.getPath(), "Schematics directory invalid or not found."); + List fileList = allFiles(dir); + + if (fileList.isEmpty()) { + actor.printError("No schematics found."); + return; + } + + File[] files = new File[fileList.size()]; + fileList.toArray(files); + + int pageCount = files.length / SCHEMATICS_PER_PAGE + 1; + if (page < 1) { + actor.printError("Page must be at least 1"); + return; + } + if (page > pageCount) { + actor.printError("Page must be less than " + (pageCount + 1)); + return; } final int sortType = args.hasFlag('d') ? -1 : args.hasFlag('n') ? 1 : 0; @@ -271,38 +286,68 @@ public class SchematicCommands { Arrays.sort(files, new Comparator(){ @Override public int compare(File f1, File f2) { - // this should no longer happen, as directory-ness is checked before - // however, if a directory slips through, this will break the contract - // of comparator transitivity - if (!f1.isFile() || !f2.isFile()) return -1; // http://stackoverflow.com/questions/203030/best-way-to-list-files-in-java-sorted-by-date-modified - int result = sortType == 0 ? f1.getName().compareToIgnoreCase(f2.getName()) : // use name by default - Long.valueOf(f1.lastModified()).compareTo(f2.lastModified()); // use date if there is a flag - if (sortType == 1) result = -result; // flip date for newest first instead of oldest first - return result; + int res; + if (sortType == 0) { // use name by default + int p = f1.getParent().compareTo(f2.getParent()); + if (p == 0) { // same parent, compare names + res = f1.getName().compareTo(f2.getName()); + } else { // different parent, sort by that + res = p; + } + } else { + res = Long.valueOf(f1.lastModified()).compareTo(f2.lastModified()); // use date if there is a flag + if (sortType == 1) res = -res; // flip date for newest first instead of oldest first + } + return res; } }); - actor.print("Available schematics (Filename (Format)):"); - actor.print(listFiles("", files)); + List schematics = listFiles(worldEdit.getConfiguration().saveDir, files); + int offset = (page - 1) * SCHEMATICS_PER_PAGE; + + actor.print("Available schematics (Filename: Format) [" + page + "/" + pageCount + "]:"); + StringBuilder build = new StringBuilder(); + int limit = Math.min(offset + SCHEMATICS_PER_PAGE, schematics.size()); + for (int i = offset; i < limit;) { + build.append(schematics.get(i)); + if (++i != limit) { + build.append("\n"); + } + } + + actor.print(build.toString()); } - private String listFiles(String prefix, File[] files) { - StringBuilder build = new StringBuilder(); - for (File file : files) { - if (file.isDirectory()) { - build.append(listFiles(prefix + file.getName() + "/", file.listFiles())); - continue; + private List allFiles(File root) { + File[] files = root.listFiles(); + if (files == null) return null; + List fileList = new ArrayList(); + for (File f : files) { + if (f.isDirectory()) { + List subFiles = allFiles(f); + if (subFiles == null) continue; // empty subdir + fileList.addAll(subFiles); + } else { + fileList.add(f); } - - if (!file.isFile()) { - continue; - } - - build.append("\n\u00a79"); - ClipboardFormat format = ClipboardFormat.findByFile(file); - build.append(prefix).append(file.getName()).append(": ").append(format == null ? "Unknown" : format.name()); } - return build.toString(); + return fileList; + } + + private List listFiles(String prefix, File[] files) { + if (prefix == null) prefix = ""; + List result = new ArrayList(); + for (File file : files) { + StringBuilder build = new StringBuilder(); + + build.append("\u00a72"); + ClipboardFormat format = ClipboardFormat.findByFile(file); + boolean inRoot = file.getParentFile().getName().equals(prefix); + build.append(inRoot ? file.getName() : file.getPath().split(Pattern.quote(prefix + File.separator))[1]) + .append(": ").append(format == null ? "Unknown" : format.name()); + result.add(build.toString()); + } + return result; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java index fa2cdf163..88d4b53ac 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java @@ -37,9 +37,11 @@ import com.sk89q.worldedit.function.mask.NoiseFilter; import com.sk89q.worldedit.function.mask.OffsetMask; import com.sk89q.worldedit.function.mask.RegionMask; import com.sk89q.worldedit.function.mask.SolidBlockMask; +import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.noise.RandomNoise; +import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.session.request.RequestSelection; import com.sk89q.worldedit.world.biome.BaseBiome; @@ -144,7 +146,11 @@ class DefaultMaskParser extends InputParser { case '=': try { - return new ExpressionMask(component.substring(1)); + Expression exp = Expression.compile(component.substring(1), "x", "y", "z"); + WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment( + Request.request().getEditSession(), Vector.ONE, Vector.ZERO); + exp.setEnvironment(env); + return new ExpressionMask(exp); } catch (ExpressionException e) { throw new InputParseException("Invalid expression: " + e.getMessage()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java index 1a5981855..a9cf3b684 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/runtime/Functions.java @@ -442,7 +442,9 @@ public final class Functions { private static double queryInternal(RValue type, RValue data, double typeId, double dataValue) throws EvaluationException { // Compare to input values and determine return value - final double ret = (typeId == type.getValue() && dataValue == data.getValue()) ? 1.0 : 0.0; + // -1 is a wildcard, always true + final double ret = ((type.getValue() == -1 || typeId == type.getValue()) + && (data.getValue() == -1 || dataValue == data.getValue())) ? 1.0 : 0.0; if (type instanceof LValue) { ((LValue) type).assign(typeId); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index 71143b3d3..258be2c6f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -300,10 +300,9 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { Vector min = getMinimumPoint(); Vector max = getMaximumPoint(); - for (int x = min.getBlockX(); x <= max.getBlockX(); ++x) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); ++z) { - chunks.add(new BlockVector2D(x >> ChunkStore.CHUNK_SHIFTS, - z >> ChunkStore.CHUNK_SHIFTS)); + for (int x = min.getBlockX() >> ChunkStore.CHUNK_SHIFTS; x <= max.getBlockX() >> ChunkStore.CHUNK_SHIFTS; ++x) { + for (int z = min.getBlockZ() >> ChunkStore.CHUNK_SHIFTS; z <= max.getBlockZ() >> ChunkStore.CHUNK_SHIFTS; ++z) { + chunks.add(new BlockVector2D(x, z)); } } @@ -317,11 +316,10 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { Vector min = getMinimumPoint(); Vector max = getMaximumPoint(); - for (int x = min.getBlockX(); x <= max.getBlockX(); ++x) { - for (int y = min.getBlockY(); y <= max.getBlockY(); ++y) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); ++z) { - chunks.add(new BlockVector(x >> ChunkStore.CHUNK_SHIFTS, - y >> ChunkStore.CHUNK_SHIFTS, z >> ChunkStore.CHUNK_SHIFTS)); + for (int x = min.getBlockX() >> ChunkStore.CHUNK_SHIFTS; x <= max.getBlockX() >> ChunkStore.CHUNK_SHIFTS; ++x) { + for (int z = min.getBlockZ() >> ChunkStore.CHUNK_SHIFTS; z <= max.getBlockZ() >> ChunkStore.CHUNK_SHIFTS; ++z) { + for (int y = min.getBlockY() >> ChunkStore.CHUNK_SHIFTS; y <= max.getBlockY() >> ChunkStore.CHUNK_SHIFTS; ++y) { + chunks.add(new BlockVector(x, y, z)); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/io/Closer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/io/Closer.java index 2e733c6ab..6b5c37efe 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/io/Closer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/io/Closer.java @@ -29,6 +29,7 @@ import java.util.ArrayDeque; import java.util.Deque; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.zip.ZipFile; import static com.google.common.base.Preconditions.checkNotNull; @@ -55,6 +56,7 @@ public final class Closer implements Closeable { // only need space for 2 elements in most cases, so try to use the smallest array possible private final Deque stack = new ArrayDeque(4); + private final Deque zipStack = new ArrayDeque(4); private Throwable thrown; @VisibleForTesting Closer(Suppressor suppressor) { @@ -73,6 +75,17 @@ public final class Closer implements Closeable { return closeable; } + /** + * Registers the given {@code zipFile} to be closed when this {@code Closer} is + * {@linkplain #close closed}. + * + * @return the given {@code closeable} + */ + public Z register(Z zipFile) { + zipStack.push(zipFile); + return zipFile; + } + /** * Stores the given throwable and rethrows it. It will be rethrown as is if it is an * {@code IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown @@ -161,6 +174,18 @@ public final class Closer implements Closeable { } } } + while (!zipStack.isEmpty()) { + ZipFile zipFile = zipStack.pop(); + try { + zipFile.close(); + } catch (Throwable e) { + if (throwable == null) { + throwable = e; + } else { + suppressor.suppress(zipFile, throwable, e); + } + } + } if (thrown == null && throwable != null) { Throwables.propagateIfPossible(throwable, IOException.class); @@ -177,7 +202,7 @@ public final class Closer implements Closeable { * the given closeable. {@code thrown} is the exception that is actually being thrown from the * method. Implementations of this method should not throw under any circumstances. */ - void suppress(Closeable closeable, Throwable thrown, Throwable suppressed); + void suppress(Object closeable, Throwable thrown, Throwable suppressed); } /** @@ -188,7 +213,7 @@ public final class Closer implements Closeable { static final LoggingSuppressor INSTANCE = new LoggingSuppressor(); @Override - public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + public void suppress(Object closeable, Throwable thrown, Throwable suppressed) { // log to the same place as Closeables logger.log(Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed); } @@ -217,7 +242,7 @@ public final class Closer implements Closeable { } @Override - public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + public void suppress(Object closeable, Throwable thrown, Throwable suppressed) { // ensure no exceptions from addSuppressed if (thrown == suppressed) { return; diff --git a/worldedit-forge/build.gradle b/worldedit-forge/build.gradle index 2873f5608..41e0f4126 100644 --- a/worldedit-forge/build.gradle +++ b/worldedit-forge/build.gradle @@ -16,7 +16,7 @@ apply plugin: 'net.minecraftforge.gradle.forge' dependencies { compile project(':worldedit-core') compile 'org.spongepowered:spongeapi:3.1.0-SNAPSHOT' - testCompile group: 'org.mockito', name: 'mockito-core', version:'1.9.0-rc1' + testCompile group: 'org.mockito', name: 'mockito-core', version: '1.9.0-rc1' } repositories { @@ -27,7 +27,7 @@ repositories { } version = "6.1.1" -ext.forgeVersion = "11.15.0.1695" +ext.forgeVersion = "11.15.1.1760" ext.internalVersion = version + ";" + gitCommitHash minecraft { diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 2ba05bf28..5ae29054e 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -59,7 +59,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * The Forge implementation of WorldEdit. */ -@Mod(modid = ForgeWorldEdit.MOD_ID, name = "WorldEdit", version = "%VERSION%", acceptableRemoteVersions = "*", dependencies = "after:sponge") +@Mod(modid = ForgeWorldEdit.MOD_ID, name = "WorldEdit", version = "%VERSION%", acceptableRemoteVersions = "*") public class ForgeWorldEdit { public static Logger logger; diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/TileEntityUtils.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/TileEntityUtils.java index 4bc57b2b8..328420b27 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/TileEntityUtils.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/TileEntityUtils.java @@ -142,5 +142,4 @@ final class TileEntityUtils { return genericTE; } - } diff --git a/worldedit-forge/src/main/resources/mcmod.info b/worldedit-forge/src/main/resources/mcmod.info index b21a45185..599e2f5e1 100644 --- a/worldedit-forge/src/main/resources/mcmod.info +++ b/worldedit-forge/src/main/resources/mcmod.info @@ -1,5 +1,5 @@ [{ - "modid": "WorldEdit", + "modid": "worldedit", "name": "WorldEdit", "description": "WorldEdit is an easy-to-use in-game world editor for Minecraft, supporting both single player and multiplayer.", "version": "${internalVersion}", @@ -14,7 +14,8 @@ "Forge@[${forgeVersion},)" ], "dependencies": [ - "Forge@[${forgeVersion},)" + "Forge@[${forgeVersion},)", + "sponge" ], "dependants": [] }]