Various minor

Fix transforms and mask help pages
Closes #852
Add world and floor thickness to CFI
Fix snow layers not affecting leaves
This commit is contained in:
Jesse Boyd 2018-01-10 14:51:58 +11:00
parent e6ea640d21
commit 4609b472fe
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
21 changed files with 232 additions and 75 deletions

1
.gitignore vendored
View File

@ -30,3 +30,4 @@ spigot-1.10
wiki_permissions.md wiki_permissions.md
/textures /textures
*.iml *.iml
/obj

View File

@ -582,15 +582,28 @@ public class CFICommands extends MethodCommands {
} }
@Command( @Command(
aliases = {"thickness", "width", "floorthickness"}, aliases = {"thickness", "width", "worldthickness"},
usage = "<height>", usage = "<height>",
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 = "<height>",
desc = "Set the thickness of the top layer\n" +
" - A value of 0 is the default and will only set the top block" " - A value of 0 is the default and will only set the top block"
) )
@CommandPermissions("worldedit.anvil.cfi") @CommandPermissions("worldedit.anvil.cfi")
public void floorthickness(FawePlayer fp, int height) throws ParameterException, WorldEditException { public void floorthickness(FawePlayer fp, int height) throws ParameterException, WorldEditException {
assertSettings(fp).getGenerator().setFloorThickness(height); assertSettings(fp).getGenerator().setFloorThickness(height);
msg("Set world thickness!").send(fp); msg("Set floor thickness!").send(fp);
component(fp); component(fp);
} }
@ -944,6 +957,8 @@ public class CFICommands extends MethodCommands {
.newline() .newline()
.text("&7[&aFloorThickness&7]").suggestTip(alias() + " " + alias("floorthickness") + " 60").text(" - Floor thickness of entire map") .text("&7[&aFloorThickness&7]").suggestTip(alias() + " " + alias("floorthickness") + " 60").text(" - Floor thickness of entire map")
.newline() .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") .text("&7[&aSnow&7]").suggestTip(alias() + " " + alias("snow") + maskArgs).text(" - Set snow in the masked areas")
.newline(); .newline();

View File

@ -5,6 +5,7 @@ import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.util.command.Dispatcher;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -25,6 +26,8 @@ public abstract class FaweParser<T> extends InputParser<T> {
} }
} }
public abstract Dispatcher getDispatcher();
public List<String> suggestRemaining(String input, String... expected) throws InputParseException { public List<String> suggestRemaining(String input, String... expected) throws InputParseException {
List<String> remainder = StringMan.split(input, ':'); List<String> remainder = StringMan.split(input, ':');
int len = remainder.size(); int len = remainder.size();

View File

@ -43,7 +43,6 @@ public enum BBC {
WORLDEDIT_ITERATIONS("&7You cannot iterate %current% times. The maximum number of iterations allowed is %max%.", "Info"), 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_UNSAFE("&7Access to that command has been blocked", "Info"),
WORLDEDIT_DANGEROUS_WORLDEDIT("&cProcessed unsafe edit at %s0 by %s1", "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_EXTEND("&cYour edit may have extended outside your allowed region.", "Error"),
WORLDEDIT_TOGGLE_TIPS_ON("&7Disabled FAWE tips.", "Info"), WORLDEDIT_TOGGLE_TIPS_ON("&7Disabled FAWE tips.", "Info"),
WORLDEDIT_TOGGLE_TIPS_OFF("&7Enabled FAWE tips.", "Info"), WORLDEDIT_TOGGLE_TIPS_OFF("&7Enabled FAWE tips.", "Info"),

View File

@ -91,6 +91,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
public final class CFIPrimtives implements Cloneable { public final class CFIPrimtives implements Cloneable {
protected int waterHeight = 0; protected int waterHeight = 0;
protected int floorThickness = 0; protected int floorThickness = 0;
protected int worldThickness = 0;
protected boolean randomVariation = true; protected boolean randomVariation = true;
protected int biomePriority = 0; protected int biomePriority = 0;
protected byte waterId = BlockID.STATIONARY_WATER; protected byte waterId = BlockID.STATIONARY_WATER;
@ -366,6 +367,10 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
this.primtives.floorThickness = floorThickness; this.primtives.floorThickness = floorThickness;
} }
public void setWorldThickness(int height) {
this.primtives.worldThickness = height;
}
public void setWaterHeight(int waterHeight) { public void setWaterHeight(int waterHeight) {
this.primtives.waterHeight = waterHeight; this.primtives.waterHeight = waterHeight;
} }
@ -546,13 +551,13 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
} }
public void addCaves() throws WorldEditException { 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); addCaves(region);
} }
@Deprecated @Deprecated
public void addSchems(Mask mask, WorldData worldData, List<ClipboardHolder> clipboards, int rarity, boolean rotate) throws WorldEditException { public void addSchems(Mask mask, WorldData worldData, List<ClipboardHolder> 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); 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 { 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); addOre(region, mask, material, size, frequency, rarity, minY, maxY);
} }
public void addDefaultOres(Mask mask) throws WorldEditException { 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 @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 { public int getCombinedId4Data(int x, int y, int z) throws FaweException.FaweChunkLoadException {
int index = z * getWidth() + x; int index = z * getWidth() + x;
if (y < 0) return 0; 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; int height = heights.getByte(index) & 0xFF;
if (y > height) { if (y > height) {
if (y == height + 1) { 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 // Use biomes array as temporary buffer
byte[] minArr = chunk.biomes; byte[] minArr = chunk.biomes;
for (int z = csz; z <= cez; z++) { for (int z = csz; z <= cez; z++) {
@ -1842,10 +1847,50 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
} }
int minLayer = Math.max(0, (minY - primtives.floorThickness) >> 4); int minLayer = Math.max(0, (minY - primtives.floorThickness) >> 4);
if (primtives.worldThickness != 0) {
for (int layer = 0; layer < minLayer; layer++) { for (int layer = 0; layer < minLayer; layer++) {
chunk.ids[layer] = null; chunk.ids[layer] = null;
chunk.data[layer] = null; chunk.data[layer] = null;
} }
}
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;
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++) { for (int layer = minLayer; layer <= maxLayer; layer++) {
byte[] layerIds = chunk.ids[layer]; byte[] layerIds = chunk.ids[layer];
byte[] layerDatas = chunk.data[layer]; byte[] layerDatas = chunk.data[layer];
@ -1875,6 +1920,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
} }
} }
} }
}
for (int layer = fillLayers; layer <= maxLayer; layer++) { for (int layer = fillLayers; layer <= maxLayer; layer++) {
Arrays.fill(chunk.skyLight[layer], (byte) 255); Arrays.fill(chunk.skyLight[layer], (byte) 255);

View File

@ -123,6 +123,9 @@ public class MCAFile {
return queue; return queue;
} }
/**
* Loads the location header from disk
*/
public void init() { public void init() {
try { try {
if (raf == null) { if (raf == null) {
@ -403,7 +406,9 @@ public class MCAFile {
if (raf.length() - offset < len) { if (raf.length() - offset < len) {
raf.setLength(((offset + len + 4095) / 4096) * 4096); raf.setLength(((offset + len + 4095) / 4096) * 4096);
} }
// Length of remaining data
raf.writeInt(data.length + 1); raf.writeInt(data.length + 1);
// Compression type
raf.write(2); raf.write(2);
raf.write(data); raf.write(data);
} }
@ -458,24 +463,38 @@ public class MCAFile {
return false; return false;
} }
/**
* Write the chunk to the file
* @param pool
*/
public void flush(ForkJoinPool pool) { public void flush(ForkJoinPool pool) {
synchronized (raf) { synchronized (raf) {
// If the file is marked as deleted, nothing is written
if (isDeleted()) { if (isDeleted()) {
clear(); clear();
file.delete(); file.delete();
return; return;
} }
boolean wait;
boolean wait; // If the flush method needs to wait for the pool
if (pool == null) { if (pool == null) {
wait = true; wait = true;
pool = new ForkJoinPool(); pool = new ForkJoinPool();
} else wait = false; } else wait = false;
// Chunks that need to be relocated
Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>();
// The position of each chunk
final Int2ObjectOpenHashMap<Integer> offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> <byte cx, byte cz, short size> final Int2ObjectOpenHashMap<Integer> offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> <byte cx, byte cz, short size>
// The data of each modified chunk
final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>(); final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>();
// The data of each chunk that needs to be moved
final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>(); final Int2ObjectOpenHashMap<byte[]> append = new Int2ObjectOpenHashMap<>();
boolean modified = false; boolean modified = false;
// Get the current time for the chunk timestamp
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
// Load the chunks into the append or compressed map
for (MCAChunk chunk : getCachedChunks()) { for (MCAChunk chunk : getCachedChunks()) {
if (chunk.isModified() || chunk.isDeleted()) { if (chunk.isModified() || chunk.isDeleted()) {
modified = true; modified = true;
@ -504,8 +523,12 @@ public class MCAFile {
} }
} }
} }
// If any changes were detected
if (modified) { if (modified) {
file.setLastModified(now); file.setLastModified(now);
// Load the offset data into the offset map
forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() { forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override @Override
public void run(Integer cx, Integer cz, Integer offset, Integer size) { 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)); offsetMap.put((int) offset, (Integer) MathMan.pair(pair1, pair2));
} }
}); });
// Wait for previous tasks
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
int start = 8192; int start = 8192;
int written = start; int written = start;
int end = 8192; int end = 8192;
int nextOffset = 8192; int nextOffset = 8192;
try { try {
for (int count = 0; count < offsetMap.size(); count++) { for (int count = 0; count < offsetMap.size(); count++) {
// Get the previous position of the next chunk
Integer loc = offsetMap.get(nextOffset); Integer loc = offsetMap.get(nextOffset);
while (loc == null) { while (loc == null) {
nextOffset += 4096; nextOffset += 4096;
loc = offsetMap.get(nextOffset); loc = offsetMap.get(nextOffset);
} }
int offset = nextOffset; int offset = nextOffset;
// Get the x/z from the paired location
short cxz = MathMan.unpairX(loc); short cxz = MathMan.unpairX(loc);
int cx = MathMan.unpairShortX(cxz); int cx = MathMan.unpairShortX(cxz);
int cz = MathMan.unpairShortY(cxz); int cz = MathMan.unpairShortY(cxz);
// Get the size from the pair
int size = MathMan.unpairY(loc) << 12; int size = MathMan.unpairY(loc) << 12;
nextOffset += size; nextOffset += size;
end = Math.min(start + size, end); end = Math.min(start + size, end);
int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31)); int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31));
byte[] newBytes = relocate.get(pair); byte[] newBytes = relocate.get(pair);
// newBytes is null if the chunk isn't modified or marked for moving
if (newBytes == null) { if (newBytes == null) {
MCAChunk cached = getCachedChunk(cx, cz); 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 (offset == start) {
if (cached == null || !cached.isModified()) { if (cached == null || !cached.isModified()) {
writeHeader(raf, cx, cz, start >> 12, size >> 12, true); writeHeader(raf, cx, cz, start >> 12, size >> 12, true);
@ -547,6 +582,7 @@ public class MCAFile {
newBytes = compressedMap.get(pair); newBytes = compressedMap.get(pair);
} }
} else { } else {
// The chunk needs to be moved, fetch the data if necessary
newBytes = compressedMap.get(pair); newBytes = compressedMap.get(pair);
if (newBytes == null) { if (newBytes == null) {
if (cached == null || !cached.isDeleted()) { if (cached == null || !cached.isDeleted()) {
@ -555,14 +591,19 @@ public class MCAFile {
} }
} }
} }
if (newBytes == null) { if (newBytes == null) {
writeHeader(raf, cx, cz, 0, 0, false); writeHeader(raf, cx, cz, 0, 0, false);
continue; continue;
} }
// The length to be written (compressed data + 5 byte chunk header)
int len = newBytes.length + 5; int len = newBytes.length + 5;
int oldSize = (size + 4095) >> 12; int oldSize = (size + 4095) >> 12;
int newSize = (len + 4095) >> 12; int newSize = (len + 4095) >> 12;
int nextOffset2 = end; 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) { while (start + len > end) {
Integer nextLoc = offsetMap.get(nextOffset2); Integer nextLoc = offsetMap.get(nextOffset2);
if (nextLoc != null) { if (nextLoc != null) {
@ -582,11 +623,16 @@ public class MCAFile {
nextOffset2 += 4096; nextOffset2 += 4096;
} }
} }
// Write the chunk + chunk header
writeSafe(raf, start, newBytes); writeSafe(raf, start, newBytes);
// Write the location data (beginning of file)
writeHeader(raf, cx, cz, start >> 12, newSize, true); writeHeader(raf, cx, cz, start >> 12, newSize, true);
written = start + newBytes.length + 5; written = start + newBytes.length + 5;
start += newSize << 12; start += newSize << 12;
} }
// Write all the chunks which need to be appended
if (!append.isEmpty()) { if (!append.isEmpty()) {
for (Int2ObjectMap.Entry<byte[]> entry : append.int2ObjectEntrySet()) { for (Int2ObjectMap.Entry<byte[]> entry : append.int2ObjectEntrySet()) {
int pair = entry.getIntKey(); int pair = entry.getIntKey();
@ -601,6 +647,7 @@ public class MCAFile {
start += newSize << 12; start += newSize << 12;
} }
} }
// Round the file length, since the vanilla server doesn't like it for some reason
raf.setLength(4096 * ((written + 4095) / 4096)); raf.setLength(4096 * ((written + 4095) / 4096));
if (raf instanceof BufferedRandomAccessFile) { if (raf instanceof BufferedRandomAccessFile) {
((BufferedRandomAccessFile) raf).flush(); ((BufferedRandomAccessFile) raf).flush();

View File

@ -123,11 +123,6 @@ public class DelegateTextureUtil extends TextureUtil {
return TextureUtil.hueDistance(red1, green1, blue1, red2, green2, blue2); return TextureUtil.hueDistance(red1, green1, blue1, red2, green2, blue2);
} }
@Override
public int getColor(BufferedImage image) {
return parent.getColor(image);
}
@Override @Override
public long getDistance(BufferedImage image, int c1) { public long getDistance(BufferedImage image, int c1) {
return parent.getDistance(image, c1); return parent.getDistance(image, c1);

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.util;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.image.ImageUtil;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
@ -59,7 +60,6 @@ public class TextureUtil {
protected int[] validMixBiomeColors; protected int[] validMixBiomeColors;
protected long[] validMixBiomeIds; protected long[] validMixBiomeIds;
/** /**
* https://github.com/erich666/Mineways/blob/master/Win/biomes.cpp * https://github.com/erich666/Mineways/blob/master/Win/biomes.cpp
*/ */
@ -638,7 +638,7 @@ public class TextureUtil {
ZipEntry textureEntry = zipFile.getEntry(path); ZipEntry textureEntry = zipFile.getEntry(path);
try (InputStream is = zipFile.getInputStream(textureEntry)) { try (InputStream is = zipFile.getInputStream(textureEntry)) {
BufferedImage image = ImageIO.read(is); BufferedImage image = ImageIO.read(is);
int color = getColor(image); int color = ImageUtil.getColor(image);
long distance = getDistance(image, color); long distance = getDistance(image, color);
distanceMap.put((int) combined, (Long) distance); distanceMap.put((int) combined, (Long) distance);
colorMap.put((int) combined, (Integer) color); colorMap.put((int) combined, (Integer) color);
@ -1002,30 +1002,6 @@ public class TextureUtil {
return (int) ((r * r + g * g + b * b) >> 25); 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) { public long getDistance(BufferedImage image, int c1) {
long totalDistSqr = 0; long totalDistSqr = 0;
int width = image.getWidth(); int width = image.getWidth();

View File

@ -68,6 +68,30 @@ public class ImageUtil {
return ret; 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 { public static BufferedImage getImage(String arg) throws ParameterException {
try { try {
if (arg.startsWith("http")) { if (arg.startsWith("http")) {

View File

@ -2665,6 +2665,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue,
if (dx2 + dz2 > radiusSq) { if (dx2 + dz2 > radiusSq) {
continue; continue;
} }
outer:
for (int y = maxY; y >= 1; --y) { for (int y = maxY; y >= 1; --y) {
final int id = FaweCache.getId(queue.getCombinedId4Data(x, y, z)); final int id = FaweCache.getId(queue.getCombinedId4Data(x, y, z));
if (id == BlockID.AIR) { if (id == BlockID.AIR) {
@ -2678,7 +2679,13 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue,
// Snow should not cover these blocks // Snow should not cover these blocks
if (BlockType.isTranslucent(id)) { if (BlockType.isTranslucent(id)) {
switch (id) {
case BlockID.LEAVES:
case BlockID.LEAVES2:
break; break;
default:
break outer;
}
} }
// Too high? // Too high?

View File

@ -449,6 +449,7 @@ public class LocalSession {
if (changeSet.isEmpty()) { if (changeSet.isEmpty()) {
return; return;
} }
FawePlayer fp = editSession.getPlayer(); FawePlayer fp = editSession.getPlayer();
if (fp != null) { if (fp != null) {
loadSessionHistoryFromDisk(fp.getUUID(), editSession.getWorld()); loadSessionHistoryFromDisk(fp.getUUID(), editSession.getWorld());

View File

@ -32,6 +32,7 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.world.registry.BundledBlockData;
import com.sk89q.worldedit.world.registry.WorldData; import com.sk89q.worldedit.world.registry.WorldData;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutput; import java.io.DataOutput;

View File

@ -26,6 +26,7 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.TextureUtil; 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.Command;
import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.CommandPermissions;
@ -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.Optional;
import com.sk89q.worldedit.util.command.parametric.ParameterException; import com.sk89q.worldedit.util.command.parametric.ParameterException;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
@ -121,7 +123,7 @@ public class GenerationCommands extends MethodCommands {
) )
@CommandPermissions("worldedit.generation.image") @CommandPermissions("worldedit.generation.image")
@Logging(PLACEMENT) @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); TextureUtil tu = Fawe.get().getCachedTextureUtil(randomize, 0, threshold);
URL url = new URL(arg); URL url = new URL(arg);
if (!url.getHost().equalsIgnoreCase("i.imgur.com") && !url.getHost().equalsIgnoreCase("empcraft.com")) { if (!url.getHost().equalsIgnoreCase("i.imgur.com") && !url.getHost().equalsIgnoreCase("empcraft.com")) {
@ -129,17 +131,22 @@ public class GenerationCommands extends MethodCommands {
} }
FawePlayer<Object> fp = FawePlayer.wrap(player); FawePlayer<Object> fp = FawePlayer.wrap(player);
BufferedImage image = MainUtil.readImage(url); 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 pos1 = new MutableBlockVector(player.getPosition());
MutableBlockVector pos2 = new MutableBlockVector(pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1)); MutableBlockVector pos2 = new MutableBlockVector(pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1));
CuboidRegion region = new CuboidRegion(pos1, pos2); CuboidRegion region = new CuboidRegion(pos1, pos2);
int[] count = new int[1]; int[] count = new int[1];
final BufferedImage finalImage = image;
RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() { RegionVisitor visitor = new RegionVisitor(region, new RegionFunction() {
@Override @Override
public boolean apply(Vector pos) throws WorldEditException { public boolean apply(Vector pos) throws WorldEditException {
try { try {
int x = pos.getBlockX() - pos1.getBlockX(); int x = pos.getBlockX() - pos1.getBlockX();
int z = pos.getBlockZ() - pos1.getBlockZ(); int z = pos.getBlockZ() - pos1.getBlockZ();
int color = image.getRGB(x, z); int color = finalImage.getRGB(x, z);
BaseBlock block = tu.getNearestBlock(color); BaseBlock block = tu.getNearestBlock(color);
count[0]++; count[0]++;
return editSession.setBlockFast(pos, block); return editSession.setBlockFast(pos, block);

View File

@ -21,6 +21,7 @@ package com.sk89q.worldedit.command;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.command.FaweParser;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Commands; import com.boydti.fawe.config.Commands;
import com.boydti.fawe.object.FaweLimit; 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.command.util.EntityRemover;
import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.Player; 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.factory.HashTagPatternParser;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Capability;
@ -122,7 +125,7 @@ public class UtilityCommands extends MethodCommands {
"More Info: https://git.io/vSPmA" "More Info: https://git.io/vSPmA"
) )
public void patterns(Player player, LocalSession session, CommandContext args) throws WorldEditException { public void patterns(Player player, LocalSession session, CommandContext args) throws WorldEditException {
displayModifierHelp(player, args); displayModifierHelp(player, HashTagPatternParser.class, args);
} }
@Command( @Command(
@ -137,7 +140,7 @@ public class UtilityCommands extends MethodCommands {
"More Info: https://git.io/v9r4K" "More Info: https://git.io/v9r4K"
) )
public void masks(Player player, LocalSession session, CommandContext args) throws WorldEditException { public void masks(Player player, LocalSession session, CommandContext args) throws WorldEditException {
displayModifierHelp(player, args); displayModifierHelp(player, DefaultMaskParser.class, args);
} }
@Command( @Command(
@ -151,17 +154,17 @@ public class UtilityCommands extends MethodCommands {
"More Info: https://git.io/v9KHO" "More Info: https://git.io/v9KHO"
) )
public void transforms(Player player, LocalSession session, CommandContext args) throws WorldEditException { 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<? extends FaweParser> clazz, CommandContext args) {
FaweParser parser = FaweAPI.getParser(clazz);
if (args.argsLength() == 0) { if (args.argsLength() == 0) {
String base = getCommand().aliases()[0]; String base = getCommand().aliases()[0];
UsageMessage msg = new UsageMessage(getCallable(), (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + base, args.getLocals()); UsageMessage msg = new UsageMessage(getCallable(), (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + base, args.getLocals());
msg.newline().paginate(base, 0, 1).send(player); msg.newline().paginate(base, 0, 1).send(player);
return; return;
} }
HashTagPatternParser parser = FaweAPI.getParser(HashTagPatternParser.class);
if (parser != null) { if (parser != null) {
CommandMapping mapping = parser.getDispatcher().get(args.getString(0)); CommandMapping mapping = parser.getDispatcher().get(args.getString(0));
if (mapping != null) { if (mapping != null) {

View File

@ -38,6 +38,7 @@ public class DefaultMaskParser extends FaweParser<Mask> {
this.register(new MaskCommands(worldEdit)); this.register(new MaskCommands(worldEdit));
} }
@Override
public Dispatcher getDispatcher() { public Dispatcher getDispatcher() {
return dispatcher; return dispatcher;
} }

View File

@ -34,6 +34,7 @@ public class DefaultTransformParser extends FaweParser<ResettableExtent> {
this.register(new TransformCommands(worldEdit)); this.register(new TransformCommands(worldEdit));
} }
@Override
public Dispatcher getDispatcher() { public Dispatcher getDispatcher() {
return dispatcher; return dispatcher;
} }

View File

@ -33,6 +33,7 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
this.register(new PatternCommands(worldEdit)); this.register(new PatternCommands(worldEdit));
} }
@Override
public Dispatcher getDispatcher() { public Dispatcher getDispatcher() {
return dispatcher; return dispatcher;
} }

View File

@ -122,6 +122,7 @@ public interface Extent extends InputExtent, OutputExtent {
} }
public default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) { 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 clearanceAbove = maxY - y;
int clearanceBelow = y - minY; int clearanceBelow = y - minY;
int clearance = Math.min(clearanceAbove, clearanceBelow); int clearance = Math.min(clearanceAbove, clearanceBelow);

View File

@ -179,6 +179,7 @@ public class HeightMap {
int zr = z + originZ; int zr = z + originZ;
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {
int curHeight = this.data[index]; int curHeight = this.data[index];
if (curHeight == -1) continue;
int newHeight = Math.min(maxY4, data[index++]); int newHeight = Math.min(maxY4, data[index++]);
int curBlock = (curHeight) >> 3; int curBlock = (curHeight) >> 3;
int newBlock = (newHeight + 7) >> 3; int newBlock = (newHeight + 7) >> 3;
@ -255,6 +256,7 @@ public class HeightMap {
int zr = z + originZ; int zr = z + originZ;
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {
int curHeight = this.data[index]; int curHeight = this.data[index];
if (curHeight == -1) continue;
int newHeight = Math.min(maxY, data[index++]); int newHeight = Math.min(maxY, data[index++]);
int xr = x + originX; int xr = x + originX;

View File

@ -363,6 +363,32 @@ public class BundledBlockData {
return legacyMap[id]; 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<String, FaweState> 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<String, FaweStateValue> 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. * Convert the given string ID to a legacy numeric ID.
* *

View File

@ -42,7 +42,7 @@ repositories {
dependencies { dependencies {
compile project(':core') 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-sponge-6.1.9-SNAPSHOT-dist'
compile name: 'worldedit-core-6.1.9-SNAPSHOT-dist' compile name: 'worldedit-core-6.1.9-SNAPSHOT-dist'
} }