diff --git a/.gitignore b/.gitignore index 0c1a7e02..3dcdaadb 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ spigot-1.10 wiki_permissions.md /textures *.iml +/obj \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/command/CFICommands.java b/core/src/main/java/com/boydti/fawe/command/CFICommands.java index f94c7e21..94b42ee5 100644 --- a/core/src/main/java/com/boydti/fawe/command/CFICommands.java +++ b/core/src/main/java/com/boydti/fawe/command/CFICommands.java @@ -582,15 +582,28 @@ public class CFICommands extends MethodCommands { } @Command( - aliases = {"thickness", "width", "floorthickness"}, + aliases = {"thickness", "width", "worldthickness"}, usage = "", - desc = "Set the thickness of the generated world from the floor\n" + + desc = "Set the thickness of the generated world\n" + + " - A value of 0 is the default and will not modify the height" + ) + @CommandPermissions("worldedit.anvil.cfi") + public void worldthickness(FawePlayer fp, int height) throws ParameterException, WorldEditException { + assertSettings(fp).getGenerator().setWorldThickness(height); + msg("Set world thickness!").send(fp); + component(fp); + } + + @Command( + aliases = {"floorthickness", "floorheight", "floorwidth"}, + usage = "", + desc = "Set the thickness of the top layer\n" + " - A value of 0 is the default and will only set the top block" ) @CommandPermissions("worldedit.anvil.cfi") public void floorthickness(FawePlayer fp, int height) throws ParameterException, WorldEditException { assertSettings(fp).getGenerator().setFloorThickness(height); - msg("Set world thickness!").send(fp); + msg("Set floor thickness!").send(fp); component(fp); } @@ -944,6 +957,8 @@ public class CFICommands extends MethodCommands { .newline() .text("&7[&aFloorThickness&7]").suggestTip(alias() + " " + alias("floorthickness") + " 60").text(" - Floor thickness of entire map") .newline() + .text("&7[&aWorldThickness&7]").suggestTip(alias() + " " + alias("worldthickness") + " 60").text(" - World thickness of entire map") + .newline() .text("&7[&aSnow&7]").suggestTip(alias() + " " + alias("snow") + maskArgs).text(" - Set snow in the masked areas") .newline(); diff --git a/core/src/main/java/com/boydti/fawe/command/FaweParser.java b/core/src/main/java/com/boydti/fawe/command/FaweParser.java index 87cd7317..9351e07f 100644 --- a/core/src/main/java/com/boydti/fawe/command/FaweParser.java +++ b/core/src/main/java/com/boydti/fawe/command/FaweParser.java @@ -5,6 +5,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.internal.registry.InputParser; +import com.sk89q.worldedit.util.command.Dispatcher; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; @@ -25,6 +26,8 @@ public abstract class FaweParser extends InputParser { } } + public abstract Dispatcher getDispatcher(); + public List suggestRemaining(String input, String... expected) throws InputParseException { List remainder = StringMan.split(input, ':'); int len = remainder.size(); diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java index 787494da..2ffefced 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -43,7 +43,6 @@ public enum BBC { WORLDEDIT_ITERATIONS("&7You cannot iterate %current% times. The maximum number of iterations allowed is %max%.", "Info"), WORLDEDIT_UNSAFE("&7Access to that command has been blocked", "Info"), WORLDEDIT_DANGEROUS_WORLDEDIT("&cProcessed unsafe edit at %s0 by %s1", "Info"), - WORLDEDIT_BYPASS("&7&oTo bypass your restrictions use &c/wea", "Info"), WORLDEDIT_EXTEND("&cYour edit may have extended outside your allowed region.", "Error"), WORLDEDIT_TOGGLE_TIPS_ON("&7Disabled FAWE tips.", "Info"), WORLDEDIT_TOGGLE_TIPS_OFF("&7Enabled FAWE tips.", "Info"), diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java index e3a240a8..c495dc47 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/HeightMapMCAGenerator.java @@ -91,6 +91,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw public final class CFIPrimtives implements Cloneable { protected int waterHeight = 0; protected int floorThickness = 0; + protected int worldThickness = 0; protected boolean randomVariation = true; protected int biomePriority = 0; protected byte waterId = BlockID.STATIONARY_WATER; @@ -366,6 +367,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw this.primtives.floorThickness = floorThickness; } + public void setWorldThickness(int height) { + this.primtives.worldThickness = height; + } + public void setWaterHeight(int waterHeight) { this.primtives.waterHeight = waterHeight; } @@ -546,13 +551,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw } public void addCaves() throws WorldEditException { - CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth(), 255, getLength())); + CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth() -1, 255, getLength() -1)); addCaves(region); } @Deprecated public void addSchems(Mask mask, WorldData worldData, List clipboards, int rarity, boolean rotate) throws WorldEditException { - CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth(), 255, getLength())); + CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth() -1, 255, getLength() -1)); addSchems(region, mask, worldData, clipboards, rarity, rotate); } @@ -657,12 +662,12 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw } public void addOre(Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException { - CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth(), 255, getLength())); + CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth() -1, 255, getLength() -1)); addOre(region, mask, material, size, frequency, rarity, minY, maxY); } public void addDefaultOres(Mask mask) throws WorldEditException { - addOres(new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth(), 255, getLength())), mask); + addOres(new CuboidRegion(new Vector(0, 0, 0), new Vector(getWidth() -1, 255, getLength() -1)), mask); } @Override @@ -921,7 +926,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw public int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException { int index = z * getWidth() + x; if (y < 0) return 0; - if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); + if (index < 0 || index >= getArea() || x < 0 || x >= getWidth()) return 0; int height = heights.getByte(index) & 0xFF; if (y > height) { if (y == height + 1) { @@ -1824,7 +1829,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw } } - if (primtives.floorThickness != 0) { + if (primtives.floorThickness != 0 || primtives.worldThickness != 0) { // Use biomes array as temporary buffer byte[] minArr = chunk.biomes; for (int z = csz; z <= cez; z++) { @@ -1842,33 +1847,74 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw } int minLayer = Math.max(0, (minY - primtives.floorThickness) >> 4); - for (int layer = 0; layer < minLayer; layer++) { - chunk.ids[layer] = null; - chunk.data[layer] = null; + if (primtives.worldThickness != 0) { + for (int layer = 0; layer < minLayer; layer++) { + chunk.ids[layer] = null; + chunk.data[layer] = null; + } } - for (int layer = minLayer; layer <= maxLayer; layer++) { - byte[] layerIds = chunk.ids[layer]; - byte[] layerDatas = chunk.data[layer]; - int startY = layer << 4; - int endY = startY + 15; - for (int z = csz; z <= cez; z++) { - index = (z & 15) << 4; - for (int x = csx; x <= cex; x++, index++) { - globalIndex = indexes[index]; - int height = heightMap[index]; + if (primtives.floorThickness != 0) { + for (int layer = minLayer; layer <= maxLayer; layer++) { + byte[] layerIds = chunk.ids[layer]; + byte[] layerDatas = chunk.data[layer]; + int startY = layer << 4; + int endY = startY + 15; + for (int z = csz; z <= cez; z++) { + index = (z & 15) << 4; + for (int x = csx; x <= cex; x++, index++) { + globalIndex = indexes[index]; + int height = heightMap[index]; - int min = (minArr[index] & 0xFF) - primtives.floorThickness; - int localMin = min - startY; - if (localMin > 0) { - char floorCombined = floor[globalIndex]; - final byte id = (byte) FaweCache.getId(floorCombined); - final int data = FaweCache.getData(floorCombined); + int min = (minArr[index] & 0xFF) - primtives.floorThickness; + int localMin = min - startY; - for (int y = 0; y < localMin; y++) { - int floorIndex = index + ((y & 15) << 8); - layerIds[floorIndex] = 0; - if (data != 0) { - chunk.setNibble(floorIndex, layerDatas, 0); + int max = height + 1; + if (min < startY) min = startY; + if (max > endY) max = endY + 1; + + + if (min < max) { + char floorCombined = floor[globalIndex]; + final byte id = (byte) FaweCache.getId(floorCombined); + final int data = FaweCache.getData(floorCombined); + for (int y = min; y < max; y++) { + int floorIndex = index + ((y & 15) << 8); + layerIds[floorIndex] = id; + if (data != 0) { + chunk.setNibble(floorIndex, layerDatas, data); + } + } + } + + } + } + } + } + if (primtives.worldThickness != 0) { + for (int layer = minLayer; layer <= maxLayer; layer++) { + byte[] layerIds = chunk.ids[layer]; + byte[] layerDatas = chunk.data[layer]; + int startY = layer << 4; + int endY = startY + 15; + for (int z = csz; z <= cez; z++) { + index = (z & 15) << 4; + for (int x = csx; x <= cex; x++, index++) { + globalIndex = indexes[index]; + int height = heightMap[index]; + + int min = (minArr[index] & 0xFF) - primtives.floorThickness; + int localMin = min - startY; + if (localMin > 0) { + char floorCombined = floor[globalIndex]; + final byte id = (byte) FaweCache.getId(floorCombined); + final int data = FaweCache.getData(floorCombined); + + for (int y = 0; y < localMin; y++) { + int floorIndex = index + ((y & 15) << 8); + layerIds[floorIndex] = 0; + if (data != 0) { + chunk.setNibble(floorIndex, layerDatas, 0); + } } } } diff --git a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java index 4b1eef91..4b5ddbaa 100644 --- a/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java +++ b/core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAFile.java @@ -123,6 +123,9 @@ public class MCAFile { return queue; } + /** + * Loads the location header from disk + */ public void init() { try { if (raf == null) { @@ -403,7 +406,9 @@ public class MCAFile { if (raf.length() - offset < len) { raf.setLength(((offset + len + 4095) / 4096) * 4096); } + // Length of remaining data raf.writeInt(data.length + 1); + // Compression type raf.write(2); raf.write(data); } @@ -458,24 +463,38 @@ public class MCAFile { return false; } + /** + * Write the chunk to the file + * @param pool + */ public void flush(ForkJoinPool pool) { synchronized (raf) { + // If the file is marked as deleted, nothing is written if (isDeleted()) { clear(); file.delete(); return; } - boolean wait; + + boolean wait; // If the flush method needs to wait for the pool if (pool == null) { wait = true; pool = new ForkJoinPool(); } else wait = false; + + // Chunks that need to be relocated Int2ObjectOpenHashMap relocate = new Int2ObjectOpenHashMap<>(); + // The position of each chunk final Int2ObjectOpenHashMap offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> + // The data of each modified chunk final Int2ObjectOpenHashMap compressedMap = new Int2ObjectOpenHashMap<>(); + // The data of each chunk that needs to be moved final Int2ObjectOpenHashMap append = new Int2ObjectOpenHashMap<>(); boolean modified = false; + // Get the current time for the chunk timestamp long now = System.currentTimeMillis(); + + // Load the chunks into the append or compressed map for (MCAChunk chunk : getCachedChunks()) { if (chunk.isModified() || chunk.isDeleted()) { modified = true; @@ -504,8 +523,12 @@ public class MCAFile { } } } + + // If any changes were detected if (modified) { file.setLastModified(now); + + // Load the offset data into the offset map forEachChunk(new RunnableVal4() { @Override public void run(Integer cx, Integer cz, Integer offset, Integer size) { @@ -514,29 +537,41 @@ public class MCAFile { offsetMap.put((int) offset, (Integer) MathMan.pair(pair1, pair2)); } }); + // Wait for previous tasks pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + + int start = 8192; int written = start; int end = 8192; int nextOffset = 8192; try { for (int count = 0; count < offsetMap.size(); count++) { + // Get the previous position of the next chunk Integer loc = offsetMap.get(nextOffset); while (loc == null) { nextOffset += 4096; loc = offsetMap.get(nextOffset); } int offset = nextOffset; + + // Get the x/z from the paired location short cxz = MathMan.unpairX(loc); int cx = MathMan.unpairShortX(cxz); int cz = MathMan.unpairShortY(cxz); + + // Get the size from the pair int size = MathMan.unpairY(loc) << 12; + nextOffset += size; end = Math.min(start + size, end); int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31)); byte[] newBytes = relocate.get(pair); + + // newBytes is null if the chunk isn't modified or marked for moving if (newBytes == null) { MCAChunk cached = getCachedChunk(cx, cz); + // If the previous offset marks the current write position (start) then we only write the header if (offset == start) { if (cached == null || !cached.isModified()) { writeHeader(raf, cx, cz, start >> 12, size >> 12, true); @@ -547,6 +582,7 @@ public class MCAFile { newBytes = compressedMap.get(pair); } } else { + // The chunk needs to be moved, fetch the data if necessary newBytes = compressedMap.get(pair); if (newBytes == null) { if (cached == null || !cached.isDeleted()) { @@ -555,14 +591,19 @@ public class MCAFile { } } } + if (newBytes == null) { writeHeader(raf, cx, cz, 0, 0, false); continue; } + + // The length to be written (compressed data + 5 byte chunk header) int len = newBytes.length + 5; int oldSize = (size + 4095) >> 12; int newSize = (len + 4095) >> 12; int nextOffset2 = end; + + // If the current write position (start) + length of data to write (len) are longer than the position of the next chunk, we need to move the next chunks while (start + len > end) { Integer nextLoc = offsetMap.get(nextOffset2); if (nextLoc != null) { @@ -582,11 +623,16 @@ public class MCAFile { nextOffset2 += 4096; } } + // Write the chunk + chunk header writeSafe(raf, start, newBytes); + // Write the location data (beginning of file) writeHeader(raf, cx, cz, start >> 12, newSize, true); + written = start + newBytes.length + 5; start += newSize << 12; } + + // Write all the chunks which need to be appended if (!append.isEmpty()) { for (Int2ObjectMap.Entry entry : append.int2ObjectEntrySet()) { int pair = entry.getIntKey(); @@ -601,6 +647,7 @@ public class MCAFile { start += newSize << 12; } } + // Round the file length, since the vanilla server doesn't like it for some reason raf.setLength(4096 * ((written + 4095) / 4096)); if (raf instanceof BufferedRandomAccessFile) { ((BufferedRandomAccessFile) raf).flush(); diff --git a/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java b/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java index f80211f2..c5cc53af 100644 --- a/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/DelegateTextureUtil.java @@ -123,11 +123,6 @@ public class DelegateTextureUtil extends TextureUtil { return TextureUtil.hueDistance(red1, green1, blue1, red2, green2, blue2); } - @Override - public int getColor(BufferedImage image) { - return parent.getColor(image); - } - @Override public long getDistance(BufferedImage image, int c1) { return parent.getDistance(image, c1); diff --git a/core/src/main/java/com/boydti/fawe/util/TextureUtil.java b/core/src/main/java/com/boydti/fawe/util/TextureUtil.java index a1039997..3a61d486 100644 --- a/core/src/main/java/com/boydti/fawe/util/TextureUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/TextureUtil.java @@ -3,6 +3,7 @@ package com.boydti.fawe.util; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.util.image.ImageUtil; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; @@ -59,7 +60,6 @@ public class TextureUtil { protected int[] validMixBiomeColors; protected long[] validMixBiomeIds; - /** * https://github.com/erich666/Mineways/blob/master/Win/biomes.cpp */ @@ -638,7 +638,7 @@ public class TextureUtil { ZipEntry textureEntry = zipFile.getEntry(path); try (InputStream is = zipFile.getInputStream(textureEntry)) { BufferedImage image = ImageIO.read(is); - int color = getColor(image); + int color = ImageUtil.getColor(image); long distance = getDistance(image, color); distanceMap.put((int) combined, (Long) distance); colorMap.put((int) combined, (Integer) color); @@ -1002,30 +1002,6 @@ public class TextureUtil { return (int) ((r * r + g * g + b * b) >> 25); } - protected int getColor(BufferedImage image) { - int width = image.getWidth(); - int height = image.getHeight(); - long totalRed = 0; - long totalGreen = 0; - long totalBlue = 0; - long totalAlpha = 0; - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - int color = image.getRGB(x, y); - totalRed += (color >> 16) & 0xFF; - totalGreen += (color >> 8) & 0xFF; - totalBlue += (color >> 0) & 0xFF; - totalAlpha += (color >> 24) & 0xFF; - } - } - int a = width * height; - int red = (int) (totalRed / a); - int green = (int) (totalGreen / a); - int blue = (int) (totalBlue / a); - int alpha = (int) (totalAlpha / a); - return (alpha << 24) + (red << 16) + (green << 8) + (blue << 0); - } - public long getDistance(BufferedImage image, int c1) { long totalDistSqr = 0; int width = image.getWidth(); diff --git a/core/src/main/java/com/boydti/fawe/util/image/ImageUtil.java b/core/src/main/java/com/boydti/fawe/util/image/ImageUtil.java index 34a83a43..3e09bbda 100644 --- a/core/src/main/java/com/boydti/fawe/util/image/ImageUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/image/ImageUtil.java @@ -68,6 +68,30 @@ public class ImageUtil { return ret; } + public static int getColor(BufferedImage image) { + int width = image.getWidth(); + int height = image.getHeight(); + long totalRed = 0; + long totalGreen = 0; + long totalBlue = 0; + long totalAlpha = 0; + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + int color = image.getRGB(x, y); + totalRed += (color >> 16) & 0xFF; + totalGreen += (color >> 8) & 0xFF; + totalBlue += (color >> 0) & 0xFF; + totalAlpha += (color >> 24) & 0xFF; + } + } + int a = width * height; + int red = (int) (totalRed / a); + int green = (int) (totalGreen / a); + int blue = (int) (totalBlue / a); + int alpha = (int) (totalAlpha / a); + return (alpha << 24) + (red << 16) + (green << 8) + (blue << 0); + } + public static BufferedImage getImage(String arg) throws ParameterException { try { if (arg.startsWith("http")) { diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index d829f1b3..718b64f6 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2665,6 +2665,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, if (dx2 + dz2 > radiusSq) { continue; } + outer: for (int y = maxY; y >= 1; --y) { final int id = FaweCache.getId(queue.getCombinedId4Data(x, y, z)); if (id == BlockID.AIR) { @@ -2678,7 +2679,13 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, // Snow should not cover these blocks if (BlockType.isTranslucent(id)) { - break; + switch (id) { + case BlockID.LEAVES: + case BlockID.LEAVES2: + break; + default: + break outer; + } } // Too high? diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java index e9150a51..fdeb0997 100644 --- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -449,6 +449,7 @@ public class LocalSession { if (changeSet.isEmpty()) { return; } + FawePlayer fp = editSession.getPlayer(); if (fp != null) { loadSessionHistoryFromDisk(fp.getUUID(), editSession.getWorld()); diff --git a/core/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java b/core/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java index 7f89fbed..e993482f 100644 --- a/core/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java +++ b/core/src/main/java/com/sk89q/worldedit/blocks/BaseBlock.java @@ -32,6 +32,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.world.registry.BundledBlockData; import com.sk89q.worldedit.world.registry.WorldData; import java.io.DataInputStream; import java.io.DataOutput; diff --git a/core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 940b77a2..318e235e 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -26,6 +26,7 @@ import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.TextureUtil; +import com.boydti.fawe.util.image.ImageUtil; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -56,6 +57,7 @@ import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.util.command.parametric.ParameterException; import com.sk89q.worldedit.world.biome.BaseBiome; +import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.IOException; import java.net.URL; @@ -121,7 +123,7 @@ public class GenerationCommands extends MethodCommands { ) @CommandPermissions("worldedit.generation.image") @Logging(PLACEMENT) - public void image(Player player, LocalSession session, EditSession editSession, String arg, @Optional("true") boolean randomize, @Optional("100") int threshold) throws WorldEditException, ParameterException, IOException { + public void image(Player player, LocalSession session, EditSession editSession, String arg, @Optional("true") boolean randomize, @Optional("100") int threshold, @Optional Vector2D dimensions) throws WorldEditException, ParameterException, IOException { TextureUtil tu = Fawe.get().getCachedTextureUtil(randomize, 0, threshold); URL url = new URL(arg); if (!url.getHost().equalsIgnoreCase("i.imgur.com") && !url.getHost().equalsIgnoreCase("empcraft.com")) { @@ -129,17 +131,22 @@ public class GenerationCommands extends MethodCommands { } FawePlayer fp = FawePlayer.wrap(player); BufferedImage image = MainUtil.readImage(url); + if (dimensions != null) { + image = ImageUtil.getScaledInstance(image, dimensions.getBlockX(), dimensions.getBlockZ(), RenderingHints.VALUE_INTERPOLATION_BILINEAR, false); + } + MutableBlockVector pos1 = new MutableBlockVector(player.getPosition()); MutableBlockVector pos2 = new MutableBlockVector(pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1)); CuboidRegion region = new CuboidRegion(pos1, pos2); int[] count = new int[1]; + final BufferedImage finalImage = image; RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { @Override public boolean apply(Vector pos) throws WorldEditException { try { int x = pos.getBlockX() - pos1.getBlockX(); int z = pos.getBlockZ() - pos1.getBlockZ(); - int color = image.getRGB(x, z); + int color = finalImage.getRGB(x, z); BaseBlock block = tu.getNearestBlock(color); count[0]++; return editSession.setBlockFast(pos, block); diff --git a/core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index d26ef0df..614e8ae5 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweAPI; +import com.boydti.fawe.command.FaweParser; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Commands; import com.boydti.fawe.object.FaweLimit; @@ -48,6 +49,8 @@ import com.sk89q.worldedit.command.util.CreatureButcher; import com.sk89q.worldedit.command.util.EntityRemover; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.factory.DefaultMaskParser; +import com.sk89q.worldedit.extension.factory.DefaultTransformParser; import com.sk89q.worldedit.extension.factory.HashTagPatternParser; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; @@ -122,7 +125,7 @@ public class UtilityCommands extends MethodCommands { "More Info: https://git.io/vSPmA" ) public void patterns(Player player, LocalSession session, CommandContext args) throws WorldEditException { - displayModifierHelp(player, args); + displayModifierHelp(player, HashTagPatternParser.class, args); } @Command( @@ -137,7 +140,7 @@ public class UtilityCommands extends MethodCommands { "More Info: https://git.io/v9r4K" ) public void masks(Player player, LocalSession session, CommandContext args) throws WorldEditException { - displayModifierHelp(player, args); + displayModifierHelp(player, DefaultMaskParser.class, args); } @Command( @@ -151,17 +154,17 @@ public class UtilityCommands extends MethodCommands { "More Info: https://git.io/v9KHO" ) public void transforms(Player player, LocalSession session, CommandContext args) throws WorldEditException { - displayModifierHelp(player, args); + displayModifierHelp(player, DefaultTransformParser.class, args); } - private void displayModifierHelp(Player player, CommandContext args) { + private void displayModifierHelp(Player player, Class clazz, CommandContext args) { + FaweParser parser = FaweAPI.getParser(clazz); if (args.argsLength() == 0) { String base = getCommand().aliases()[0]; UsageMessage msg = new UsageMessage(getCallable(), (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + base, args.getLocals()); msg.newline().paginate(base, 0, 1).send(player); return; } - HashTagPatternParser parser = FaweAPI.getParser(HashTagPatternParser.class); if (parser != null) { CommandMapping mapping = parser.getDispatcher().get(args.getString(0)); if (mapping != null) { diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java index 72ee9178..8f7d5890 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java @@ -38,6 +38,7 @@ public class DefaultMaskParser extends FaweParser { this.register(new MaskCommands(worldEdit)); } + @Override public Dispatcher getDispatcher() { return dispatcher; } diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultTransformParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultTransformParser.java index 8b947257..7604f79c 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultTransformParser.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultTransformParser.java @@ -34,6 +34,7 @@ public class DefaultTransformParser extends FaweParser { this.register(new TransformCommands(worldEdit)); } + @Override public Dispatcher getDispatcher() { return dispatcher; } diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java index d8a4866c..8260f665 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java @@ -33,6 +33,7 @@ public class HashTagPatternParser extends FaweParser { this.register(new PatternCommands(worldEdit)); } + @Override public Dispatcher getDispatcher() { return dispatcher; } diff --git a/core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/core/src/main/java/com/sk89q/worldedit/extent/Extent.java index 8e217e7d..15eb366b 100644 --- a/core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -122,6 +122,7 @@ public interface Extent extends InputExtent, OutputExtent { } public default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { + y = Math.max(minY, Math.min(maxY, y)); int clearanceAbove = maxY - y; int clearanceBelow = y - minY; int clearance = Math.min(clearanceAbove, clearanceBelow); diff --git a/core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java b/core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java index 843f1bec..cc8d1c5b 100644 --- a/core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java +++ b/core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java @@ -179,6 +179,7 @@ public class HeightMap { int zr = z + originZ; for (int x = 0; x < width; ++x) { int curHeight = this.data[index]; + if (curHeight == -1) continue; int newHeight = Math.min(maxY4, data[index++]); int curBlock = (curHeight) >> 3; int newBlock = (newHeight + 7) >> 3; @@ -255,6 +256,7 @@ public class HeightMap { int zr = z + originZ; for (int x = 0; x < width; ++x) { int curHeight = this.data[index]; + if (curHeight == -1) continue; int newHeight = Math.min(maxY, data[index++]); int xr = x + originX; diff --git a/core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java b/core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java index 44908015..637dabad 100644 --- a/core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java +++ b/core/src/main/java/com/sk89q/worldedit/world/registry/BundledBlockData.java @@ -363,6 +363,32 @@ public class BundledBlockData { return legacyMap[id]; } + public String getName(BaseBlock block) { + BlockEntry entry = legacyMap[block.getId()]; + if (entry != null) { + String name = entry.id; + if (block.getData() == 0) return name; + StringBuilder append = new StringBuilder(); + Map states = entry.states; + FaweState variants = states.get("variant"); + if (variants == null) variants = states.get("color"); + if (variants == null && !states.isEmpty()) variants = states.entrySet().iterator().next().getValue(); + if (variants != null) { + for (Map.Entry variant : variants.valueMap().entrySet()) { + FaweStateValue state = variant.getValue(); + if (state.isSet(block)) { + append.append(append.length() == 0 ? ":" : "|"); + append.append(variant.getKey()); + } + } + } + if (append.length() == 0) return name + ":" + block.getData(); + return name + append; + } + if (block.getData() == 0) return Integer.toString(block.getId()); + return block.getId() + ":" + block.getData(); + } + /** * Convert the given string ID to a legacy numeric ID. * diff --git a/sponge/build.gradle b/sponge/build.gradle index 6f90098b..5976a60a 100644 --- a/sponge/build.gradle +++ b/sponge/build.gradle @@ -42,7 +42,7 @@ repositories { dependencies { compile project(':core') - compile 'org.spongepowered:spongeapi:7.0.0-SNAPSHOT' + compile 'org.spongepowered:spongeapi:7.1.0-SNAPSHOT' compile name: 'worldedit-sponge-6.1.9-SNAPSHOT-dist' compile name: 'worldedit-core-6.1.9-SNAPSHOT-dist' }