will document in next commit
This commit is contained in:
Jesse Boyd 2017-03-27 05:14:28 +11:00
parent 06f61208f3
commit 4cab0b223d
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
91 changed files with 4471 additions and 688 deletions

2
.gitignore vendored
View File

@ -13,6 +13,8 @@
*.log
gradle.log
/lib
/core/lib
/bukkit/lib
/core/build
/forge189/build
/forge1710/build

View File

@ -1,3 +1,6 @@
repositories {
flatDir {dirs 'lib'}
}
dependencies {
compile project(':core')
compile 'org.bukkit.craftbukkitv1_10:craftbukkitv1_10:1.10'
@ -37,8 +40,8 @@ apply plugin: 'com.github.johnrengelman.shadow'
shadowJar {
dependencies {
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('it.unimi.dsi:fastutil:6.5.1'))
include(dependency(':core'))
}
archiveName = "${parent.name}-${project.name}-${parent.version}.jar"

View File

@ -5,7 +5,6 @@ import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.bukkit.v0.BukkitQueue_All;
import com.boydti.fawe.bukkit.v1_10.BukkitQueue_1_10;
import com.boydti.fawe.bukkit.v1_11.BukkitQueue_1_11;
import com.boydti.fawe.bukkit.v1_11.compression.CompressionOptimizer;
import com.boydti.fawe.bukkit.v1_7.BukkitQueue17;
import com.boydti.fawe.bukkit.v1_8.BukkitQueue18R3;
import com.boydti.fawe.bukkit.v1_9.BukkitQueue_1_9_R1;
@ -69,15 +68,6 @@ public class BukkitMain extends JavaPlugin {
break;
} catch (IllegalStateException e) {}
}
switch (version) {
case v1_11_R1:
try {
CompressionOptimizer optimizer = new CompressionOptimizer();
// optimizer.optimize();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
private enum Version {

View File

@ -195,7 +195,8 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
@Override
public boolean hasSky() {
return getWorld().getEnvironment() == World.Environment.NORMAL;
World world = getWorld();
return world == null || world.getEnvironment() == World.Environment.NORMAL;
}
private volatile boolean timingsEnabled;

View File

@ -1,118 +0,0 @@
package com.boydti.fawe.bukkit.v1_11.compression;
import com.boydti.fawe.util.MainUtil;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.LoaderClassPath;
import net.minecraft.server.v1_11_R1.RegionFile;
public class CompressionOptimizer {
private final ClassPool pool;
public CompressionOptimizer() {
this.pool = ClassPool.getDefault();
}
public void loadSafe(String name) throws Throwable {
try {
pool.get(name).toClass();
} catch (Throwable e) {
while (e.getCause() != null) {
e = e.getCause();
}
if (e instanceof ClassNotFoundException) {
loadSafe(e.getMessage());
}
}
}
public void loadPackage(String... packages) throws IOException {
JarInputStream jarFile = new JarInputStream(new FileInputStream(MainUtil.getJarFile()));
JarEntry jarEntry;
while(true) {
jarEntry = jarFile.getNextJarEntry();
if(jarEntry == null){
break;
}
if (jarEntry.getName ().endsWith (".class")) {
for (String p : packages) {
if (jarEntry.getName ().startsWith(p)) {
String name = jarEntry.getName().substring(0, jarEntry.getName().length() - 6).replaceAll("/", "\\.");
try {
loadSafe(name);
} catch (Throwable ignore) {}
break;
}
}
}
}
}
public void optimize() throws Throwable {
// pool.insertClassPath(new ClassClassPath(PGZIPOutputStream.class));
// pool.insertClassPath(new ClassClassPath(PGZIPBlock.class));
// pool.insertClassPath(new ClassClassPath(PGZIPState.class));
// pool.insertClassPath(new ClassClassPath(PGZIPThreadLocal.class));
// pool.insertClassPath(new ClassClassPath(ClassPath.class));
// pool.insertClassPath(new ClassClassPath(CompressionOptimizer.class));
pool.insertClassPath(new LoaderClassPath(this.getClass().getClassLoader()));
pool.get("com.boydti.fawe.object.io.PGZIPOutputStream").toClass();
pool.get("com.boydti.fawe.object.io.PGZIPBlock").toClass();
pool.get("com.boydti.fawe.object.io.PGZIPState").toClass();
pool.get("com.boydti.fawe.object.io.PGZIPThreadLocal").toClass();
pool.get("com.boydti.fawe.bukkit.v1_11.compression.CompressionOptimizer").toClass();
pool.get("javassist.ClassPath").toClass();
pool.importPackage("net.minecraft.server.v1_11_R1");
pool.importPackage("java.lang");
pool.importPackage("java.lang.reflect");
pool.importPackage("java.io");
pool.importPackage("com.boydti.fawe.bukkit.v1_11.compression");
// RegionFile.class.getDeclaredClasses()[0];
{ // Optimize NBTCompressedStreamTools
CtClass clazz = pool.get("net.minecraft.server.v1_11_R1.NBTCompressedStreamTools");
CtMethod methodA_getStream = clazz.getDeclaredMethod("a", new CtClass[]{pool.get("net.minecraft.server.v1_11_R1.NBTTagCompound"), pool.get("java.io.OutputStream")});
methodA_getStream.setBody("{" +
"java.io.DataOutputStream dataoutputstream = new java.io.DataOutputStream(new java.io.BufferedOutputStream(new com.boydti.fawe.object.io.PGZIPOutputStream($2)));" +
"try {" +
" a($1, (java.io.DataOutput) dataoutputstream);" +
"} finally {" +
" dataoutputstream.close();" +
"}" +
"}");
clazz.toClass();
}
{ // Optimize RegionFile
CtClass clazz = pool.get("net.minecraft.server.v1_11_R1.RegionFile");
CtMethod methodB_getStream = clazz.getDeclaredMethod("b", new CtClass[]{CtClass.intType, CtClass.intType});
methodB_getStream.setBody("{" +
"Constructor constructor = $0.getClass().getDeclaredClasses()[0].getConstructors()[0];" +
" constructor.setAccessible(true);" +
" return $0.d($1, $2) ? null : new java.io.DataOutputStream(new java.io.BufferedOutputStream(new com.boydti.fawe.object.io.PGZIPOutputStream((OutputStream) CompressionOptimizer.newInstance(constructor, $0, $1, $2))));" +
"}");
clazz.toClass();
// RegionFile $0 = null;
// int $1 = 0;
// int $2 = 0;
//
// Constructor<?> constructor = $0.getClass().getDeclaredClasses()[0].getConstructors()[0];
// constructor.setAccessible(true);
// return $0.d($1, $2) ? null : new java.io.DataOutputStream(new java.io.BufferedOutputStream(new com.boydti.fawe.object.io.PGZIPOutputStream((OutputStream) constructor.newInstance($1, $2))));
}
}
public static Object newInstance(Constructor constructor, RegionFile file, int a, int b) throws IllegalAccessException, InvocationTargetException, InstantiationException {
return constructor.newInstance(file, a, b);
}
}

View File

@ -1,3 +1,6 @@
repositories {
flatDir {dirs 'lib'}
}
dependencies {
testCompile 'junit:junit:4.12'
compile 'org.yaml:snakeyaml:1.16'
@ -7,8 +10,8 @@ dependencies {
compile group: "com.plotsquared", name: "plotsquared-api", version: "latest", changing: true
compile 'org.primesoft:BlocksHub:2.0'
compile 'com.github.luben:zstd-jni:1.1.1'
compile 'org.javassist:javassist:3.22.0-CR1'
compile 'co.aikar:fastutil-lite:1.0'
// compile 'org.javassist:javassist:3.22.0-CR1'
compile 'it.unimi.dsi:fastutil:6.5.1'
compile(group: 'com.sk89q.worldedit', name: 'worldedit-core', version:'6.1.3-SNAPSHOT') {
exclude(module: 'bukkit-classloader-check')
}

View File

@ -533,11 +533,7 @@ public class FaweAPI {
final int i = i2 + x;
final int xx = x_offset + x;
final short id = (short) (ids[i] & 0xFF);
if (FaweCache.hasData(id)) {
queue.setBlock(xx, yy, zz, id, datas[i]);
} else {
queue.setBlock(xx, yy, zz, id, (byte) 0);
}
queue.setBlock(xx, yy, zz, id, datas[i]);
}
}
}

View File

@ -5,8 +5,11 @@ import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFilter;
import com.boydti.fawe.jnbt.anvil.MCAQueue;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.mask.FaweBlockMatcher;
import com.boydti.fawe.object.number.LongAdder;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.worldedit.EditSession;
@ -16,9 +19,12 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.util.command.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@ -60,61 +66,107 @@ public class AnvilCommands {
final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
File root = new File(folder + File.separator + "region");
MCAQueue queue = new MCAQueue(folder, root, true);
final LongAdder count = new LongAdder();
queue.filterWorld(new MCAFilter() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block) {
if (matchFrom.apply(block) && matchTo.apply(block)) {
count.add(1);
}
if (matchFrom.apply(block)) matchTo.apply(block);
}
});
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(count.longValue()));
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(-1));
}
@Command(
aliases = {"/replaceallpattern", "/reap", "/repallpat"},
usage = "<folder> [from-block] <to-pattern>",
desc = "Replace all blocks in the selection with another",
flags = "d",
flags = "dm",
min = 2,
max = 4
)
@CommandPermissions("worldedit.anvil.replaceall")
public void replaceAllPattern(Player player, EditSession editSession, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData) throws WorldEditException {
final FaweBlockMatcher matchFrom;
if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR;
} else {
if (from.contains(":")) {
useData = true; //override d flag, if they specified data they want it
}
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
}
File root = new File(folder + File.separator + "region");
MCAQueue queue = new MCAQueue(folder, root, true);
final LongAdder count = new LongAdder();
queue.filterWorld(new MCAFilter() {
private final MutableBlockVector mutable = new MutableBlockVector(0, 0, 0);
@Override
public void applyBlock(int x, int y, int z, BaseBlock block) {
if (matchFrom.apply(block)) {
mutable.mutX(x);
mutable.mutY(y);
mutable.mutZ(z);
BaseBlock newBlock = to.apply(mutable);
int currentId = block.getId();
if (FaweCache.hasNBT(currentId)) {
block.setNbtData(null);
public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException {
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky());
if (useMap) {
List<String> split = StringMan.split(from, ',');
if (to instanceof RandomPattern) {
Pattern[] patterns = ((RandomPattern) to).getPatterns().toArray(new Pattern[0]);
if (patterns.length == split.size()) {
Pattern[] map = new Pattern[Character.MAX_VALUE + 1];
for (int i = 0; i < split.size(); i++) {
Pattern pattern = patterns[i];
String arg = split.get(i);
ArrayList<BaseBlock> blocks = new ArrayList<BaseBlock>();
for (String arg2 : arg.split(",")) {
BaseBlock block = worldEdit.getBlock(player, arg, true);
if (!useData && !arg2.contains(":")) {
block = new BaseBlock(block.getId(), -1);
}
blocks.add(block);
}
for (BaseBlock block : blocks) {
if (block.getData() != -1) {
int combined = FaweCache.getCombined(block);
map[combined] = pattern;
} else {
for (int data = 0; data < 16; data++) {
int combined = FaweCache.getCombined(block.getId(), data);
map[combined] = pattern;
}
}
}
}
block.setId(newBlock.getId());
block.setData(newBlock.getData());
count.add(1);
queue.filterWorld(new MCAFilter() {
private final MutableBlockVector mutable = new MutableBlockVector(0, 0, 0);
@Override
public void applyBlock(int x, int y, int z, BaseBlock block) {
int id = block.getId();
int data = FaweCache.hasData(id) ? block.getData() : 0;
int combined = FaweCache.getCombined(id, data);
Pattern p = map[combined];
if (p != null) {
BaseBlock newBlock = p.apply(x, y, z);
int currentId = block.getId();
if (FaweCache.hasNBT(currentId)) {
block.setNbtData(null);
}
block.setId(newBlock.getId());
block.setData(newBlock.getData());
}
}
});
} else {
player.print(BBC.getPrefix() + "Mask:Pattern must be a 1:1 match");
}
} else {
player.print(BBC.getPrefix() + "Must be a pattern list!");
}
});
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(count.longValue()));
} else {
final FaweBlockMatcher matchFrom;
if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR;
} else {
if (from.contains(":")) {
useData = true; //override d flag, if they specified data they want it
}
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
}
queue.filterWorld(new MCAFilter() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block) {
if (matchFrom.apply(block)) {
BaseBlock newBlock = to.apply(x, y, z);
int currentId = block.getId();
if (FaweCache.hasNBT(currentId)) {
block.setNbtData(null);
}
block.setId(newBlock.getId());
block.setData(newBlock.getData());
}
}
});
}
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(-1));
}
@Command(

View File

@ -123,6 +123,8 @@ public enum BBC {
BRUSH_SPHERE("Sphere brush shape equipped (%s0).", "WorldEdit.Brush"),
BRUSH_SCATTER("Scatter brush shape equipped (%s0, %s1).", "WorldEdit.Brush"),
BRUSH_SHATTER("Shatter brush shape equipped (%s0, %s1).", "WorldEdit.Brush"),
BRUSH_POPULATE("Populate brush shape equipped (%s0, %s1).", "WorldEdit.Brush"),
BRUSH_LAYER("Layer brush shape equipped (%s0, %s1).", "WorldEdit.Brush"),
BRUSH_STENCIL("Stencil brush equipped (%s0).", "WorldEdit.Brush"),
BRUSH_LINE("Line brush shape equipped (%s0).", "WorldEdit.Brush"),
BRUSH_SPLINE("Spline brush shape equipped (%s0). Right click an end to add a shape", "WorldEdit.Brush"),

View File

@ -9,6 +9,7 @@ import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Commands {
@ -36,6 +37,15 @@ public class Commands {
return new TranslatedCommand(command);
}
public static String getAlias(String command) {
if (cmdConfig == null) {
return command;
}
ConfigurationSection commands = cmdConfig.getConfigurationSection(command);
List<String> aliases = commands.getStringList("aliases");
return (aliases == null || aliases.isEmpty()) ? command : aliases.get(0);
}
public static class TranslatedCommand implements Command {
private final String[] aliases;
private final String usage;

View File

@ -206,7 +206,7 @@ public class NMSRelighter implements Relighter{
currentMap.put((int) MathMan.tripleBlockCoord(x, y, z), present);
}
public synchronized void fixLightingSafe(boolean sky) {
public void fixLightingSafe(boolean sky) {
try {
if (sky) {
fixSkyLighting();

View File

@ -14,7 +14,7 @@ import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.regions.CuboidRegion;
import java.io.BufferedInputStream;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -49,7 +49,7 @@ public class CorruptSchematicStreamer {
try {
stream.reset();
stream.mark(Integer.MAX_VALUE);
DataInputStream dataInput = new DataInputStream(new BufferedInputStream(new GZIPInputStream(stream)));
DataInputStream dataInput = new DataInputStream(new FastBufferedInputStream(new GZIPInputStream(stream)));
byte[] match = matchTag.getBytes();
int[] matchValue = new int[match.length];
int matchIndex = 0;

View File

@ -27,7 +27,6 @@ public class SchematicStreamer extends NBTStreamer {
}
public void addBlockReaders() {
final long start = System.currentTimeMillis();
NBTStreamReader initializer = new NBTStreamReader<Integer, Integer>() {
@Override
public void run(Integer length, Integer type) {

View File

@ -0,0 +1,940 @@
package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.object.io.BufferedRandomAccessFile;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.registry.WorldData;
import it.unimi.dsi.fastutil.chars.Char2CharMap;
import it.unimi.dsi.fastutil.chars.Char2CharOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.zip.Deflater;
public class HeightMapMCAGenerator implements Extent {
private final MutableBlockVector mutable = new MutableBlockVector();
private final ForkJoinPool pool = new ForkJoinPool();
final Int2ObjectOpenHashMap<Char2CharOpenHashMap> blocks = new Int2ObjectOpenHashMap<>();
private final byte[] heights;
private final byte[] biomes;
private final char[] floor;
private final char[] main;
private char[] overlay;
private final File folder;
private final int length;
private final int width;
private final int area;
private boolean modifiedMain = false;
public HeightMapMCAGenerator(BufferedImage img, File regionFolder) {
if (!regionFolder.exists()) {
regionFolder.mkdirs();
}
this.folder = regionFolder;
this.width = img.getWidth();
this.length = img.getHeight();
this.area = width * length;
heights = new byte[area];
biomes = new byte[area];
floor = new char[area];
main = new char[area];
char stone = (char) FaweCache.getCombined(1, 0);
char grass = (char) FaweCache.getCombined(2, 0);
Arrays.fill(main, stone);
Arrays.fill(floor, grass);
int index = 0;
for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++, index++){
heights[index] = (byte) img.getRGB(x, z);
}
}
}
public void addCaves() throws WorldEditException {
CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(width, 255, length));
addCaves(region);
}
public void addSchems(Mask mask, WorldData worldData, ClipboardHolder[] clipboards, int rarity, boolean rotate) throws WorldEditException{
CuboidRegion region = new CuboidRegion(new Vector(0, 0, 0), new Vector(width, 255, length));
addSchems(region, mask, worldData, clipboards, rarity, rotate);
}
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(width, 255, length));
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(width, 255, length)), mask);
}
@Override
public Vector getMinimumPoint() {
return new Vector(0, 0, 0);
}
@Override
public Vector getMaximumPoint() {
return new Vector(width - 1, 255, length - 1);
}
@Override
public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException {
return setBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), block);
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
int index = position.getBlockZ() * width + position.getBlockX();
if (index < 0 || index >= heights.length) return false;
biomes[index] = (byte) biome.getId();
return true;
}
public int count;
@Override
public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException {
count++;
int index = z * width + x;
if (index < 0 || index >= heights.length) return false;
int height = heights[index] & 0xFF;
char combined = (char) FaweCache.getCombined(block);
if (y > height) {
if (y == height + 1) {
if (overlay == null) {
overlay = new char[area];
}
overlay[index] = combined;
return true;
}
} else if (y == height) {
floor[index] = combined;
return true;
}
short chunkX = (short) (x >> 4);
short chunkZ = (short) (z >> 4);
int pair = MathMan.pair(chunkX, chunkZ);
Char2CharOpenHashMap map = blocks.get(pair);
if (map == null) {
map = new Char2CharOpenHashMap();
blocks.put(pair, map);
}
char blockPair = (char) (((y >> 4) << 12) + (x & 15) + ((z & 15) << 4) + ((y & 15) << 8));
map.put(blockPair, combined != 0 ? combined : 1);
return true;
}
@Override
public BaseBiome getBiome(Vector2D position) {
int index = position.getBlockZ() * width + position.getBlockX();
if (index < 0 || index >= heights.length) return EditSession.nullBiome;
return FaweCache.CACHE_BIOME[biomes[index] & 0xFF];
}
@Override
public BaseBlock getBlock(Vector position) {
return getLazyBlock(position);
}
@Override
public BaseBlock getLazyBlock(Vector position) {
return getLazyBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ());
}
@Override
public BaseBlock getLazyBlock(int x, int y, int z) {
int index = z * width + x;
if (index < 0 || index >= heights.length) return EditSession.nullBlock;
int height = heights[index] & 0xFF;
if (y > height) {
if (y == height + 1) {
return FaweCache.CACHE_BLOCK[overlay != null ? overlay[index] : 0];
}
if (!blocks.isEmpty()) {
short chunkX = (short) (x >> 4);
short chunkZ = (short) (z >> 4);
int pair = MathMan.pair(chunkX, chunkZ);
Char2CharOpenHashMap map = blocks.get(pair);
if (map != null) {
char blockPair = (char)(((y >> 4) << 12) + (x & 15) + ((z & 15) << 4) + ((y & 15) << 8));
char combined = map.get(blockPair);
if (combined != 0) {
return FaweCache.CACHE_BLOCK[combined];
}
}
}
return FaweCache.CACHE_BLOCK[0];
} else if (y == height) {
return FaweCache.CACHE_BLOCK[floor[index]];
} else {
if (!blocks.isEmpty()) {
short chunkX = (short) (x >> 4);
short chunkZ = (short) (z >> 4);
int pair = MathMan.pair(chunkX, chunkZ);
Char2CharOpenHashMap map = blocks.get(pair);
if (map != null) {
char blockPair = (char)(((y >> 4) << 12) + (x & 15) + ((z & 15) << 4) + ((y & 15) << 8));
char combined = map.get(blockPair);
if (combined != 0) {
return FaweCache.CACHE_BLOCK[combined];
}
}
}
return FaweCache.CACHE_BLOCK[main[index]];
}
}
@Override
public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
int index = z * width + x;
if (index < 0 || index >= heights.length) return y;
return ((heights[index] & 0xFF) << 3) + (floor[index] & 0xFF) + 1;
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
int index = z * width + x;
if (index < 0 || index >= heights.length) return y;
return heights[index] & 0xFF;
}
public void setBiome(BufferedImage img, byte biome, boolean white) {
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
int index = 0;
for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
biomes[index] = biome;
}
}
}
}
private void setOverlay(BufferedImage img, char combined, boolean white) {
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
if (overlay == null) overlay = new char[area];
int index = 0;
for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
overlay[index] = combined;
}
}
}
}
private void setMain(BufferedImage img, char combined, boolean white) {
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
modifiedMain = true;
int index = 0;
for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
main[index] = combined;
}
}
}
}
private void setFloor(BufferedImage img, char combined, boolean white) {
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
int index = 0;
for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
floor[index] = combined;
}
}
}
}
private void setColumn(BufferedImage img, char combined, boolean white) {
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
modifiedMain = true;
int index = 0;
for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
main[index] = combined;
floor[index] = combined;
}
}
}
}
public void setBiome(Mask mask, byte biome) {
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
biomes[index] = biome;
}
}
}
}
private void setOverlay(Mask mask, char combined) {
int index = 0;
if (overlay == null) overlay = new char[area];
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
overlay[index] = combined;
}
}
}
}
private void setFloor(Mask mask, char combined) {
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
floor[index] = combined;
}
}
}
}
private void setMain(Mask mask, char combined) {
modifiedMain = true;
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
main[index] = combined;
}
}
}
}
private void setColumn(Mask mask, char combined) {
modifiedMain = true;
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
floor[index] = combined;
main[index] = combined;
}
}
}
}
public void setOverlay(BufferedImage img, Pattern pattern, boolean white) {
if (pattern instanceof BlockPattern) {
setOverlay(img, (char) ((BlockPattern) pattern).getBlock().getCombined(), white);
return;
}
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
if (overlay == null) overlay = new char[area];
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
mutable.mutX(x);
mutable.mutY(height);
overlay[index] = (char) pattern.apply(mutable).getCombined();
}
}
}
}
public void setMain(BufferedImage img, Pattern pattern, boolean white) {
if (pattern instanceof BlockPattern) {
setMain(img, (char) ((BlockPattern) pattern).getBlock().getCombined(), white);
return;
}
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
modifiedMain = true;
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
mutable.mutX(x);
mutable.mutY(height);
main[index] = (char) pattern.apply(mutable).getCombined();
}
}
}
}
public void setFloor(BufferedImage img, Pattern pattern, boolean white) {
if (pattern instanceof BlockPattern) {
setFloor(img, (char) ((BlockPattern) pattern).getBlock().getCombined(), white);
return;
}
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
mutable.mutX(x);
mutable.mutY(height);
floor[index] = (char) pattern.apply(mutable).getCombined();
}
}
}
}
public void setColumn(BufferedImage img, Pattern pattern, boolean white) {
if (pattern instanceof BlockPattern) {
setColumn(img, (char) ((BlockPattern) pattern).getBlock().getCombined(), white);
return;
}
if (img.getWidth() != width || img.getHeight() != length) throw new IllegalArgumentException("Input image dimensions do not match the current height map!");
modifiedMain = true;
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int height = img.getRGB(x, z) & 0xFF;
if (height == 255 || height > 0 && white && PseudoRandom.random.nextInt(256) <= height) {
mutable.mutX(x);
mutable.mutY(height);
char combined = (char) pattern.apply(mutable).getCombined();
main[index] = combined;
floor[index] = combined;
}
}
}
}
public void setOverlay(Mask mask, Pattern pattern) {
if (pattern instanceof BlockPattern) {
setOverlay(mask, (char) ((BlockPattern) pattern).getBlock().getCombined());
return;
}
int index = 0;
if (overlay == null) overlay = new char[area];
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
overlay[index] = (char) pattern.apply(mutable).getCombined();
}
}
}
}
public void setFloor(Mask mask, Pattern pattern) {
if (pattern instanceof BlockPattern) {
setFloor(mask, (char) ((BlockPattern) pattern).getBlock().getCombined());
return;
}
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
floor[index] = (char) pattern.apply(mutable).getCombined();
}
}
}
}
public void setMain(Mask mask, Pattern pattern) {
if (pattern instanceof BlockPattern) {
setMain(mask, (char) ((BlockPattern) pattern).getBlock().getCombined());
return;
}
modifiedMain = true;
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
main[index] = (char) pattern.apply(mutable).getCombined();
}
}
}
}
public void setColumn(Mask mask, Pattern pattern) {
if (pattern instanceof BlockPattern) {
setColumn(mask, (char) ((BlockPattern) pattern).getBlock().getCombined());
return;
}
modifiedMain = true;
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++){
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
if (mask.test(mutable)) {
char combined = (char) pattern.apply(mutable).getCombined();
floor[index] = combined;
main[index] = combined;
}
}
}
}
public void setBiome(int biome) {
Arrays.fill(biomes, (byte) biome);
}
private void setFloor(int value) {
Arrays.fill(floor, (char) value);
}
private void setColumn(int value) {
setFloor(value);
setMain(value);
}
private void setMain(int value) {
modifiedMain = true;
Arrays.fill(main, (char) value);
}
private void setOverlay(int value) {
if (overlay == null) overlay = new char[area];
Arrays.fill(overlay, (char) value);
}
public void setFloor(Pattern value) {
if (value instanceof BlockPattern) {
setFloor(((BlockPattern) value).getBlock().getCombined());
return;
}
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++) {
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
floor[index] = (char) value.apply(mutable).getCombined();
}
}
}
public void setColumn(Pattern value) {
if (value instanceof BlockPattern) {
setColumn(((BlockPattern) value).getBlock().getCombined());
return;
}
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++) {
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
char combined = (char) value.apply(mutable).getCombined();
main[index] = combined;
floor[index] = combined;
}
}
}
public void setMain(Pattern value) {
if (value instanceof BlockPattern) {
setMain(((BlockPattern) value).getBlock().getCombined());
return;
}
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++) {
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
main[index] = (char) value.apply(mutable).getCombined();
}
}
}
public void setOverlay(Pattern value) {
if (overlay == null) overlay = new char[area];
if (value instanceof BlockPattern) {
setOverlay(((BlockPattern) value).getBlock().getCombined());
return;
}
int index = 0;
for (int z = 0; z < length; z++) {
mutable.mutZ(z);
for (int x = 0; x < width; x++, index++) {
int y = heights[index] & 0xFF;
mutable.mutX(x);
mutable.mutY(y);
overlay[index] = (char) value.apply(mutable).getCombined();
}
}
}
public void setHeights(int value) {
Arrays.fill(heights, (byte) value);
}
public void generate() throws IOException {
int bcx = 0;
int bcz = 0;
int tcx = (width - 1) >> 4;
int tcz = (length - 1) >> 4;
final ThreadLocal<MCAChunk> chunkStore = new ThreadLocal<MCAChunk>() {
@Override
protected MCAChunk initialValue() {
MCAChunk chunk = new MCAChunk(null, 0, 0);
chunk.biomes = new byte[256];
return chunk;
}
};
final ThreadLocal<byte[]> byteStore1 = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[500000];
}
};
final ThreadLocal<byte[]> byteStore2 = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[500000];
}
};
final ThreadLocal<Deflater> deflateStore = new ThreadLocal<Deflater>() {
@Override
protected Deflater initialValue() {
Deflater deflater = new Deflater(1, false);
return deflater;
}
};
final ThreadLocal<int[]> indexStore = new ThreadLocal<int[]>() {
@Override
protected int[] initialValue() {
return new int[256];
}
};
boolean hasOverlay = this.overlay != null;
byte[] fileBuf = new byte[1 << 16];
for (int mcaZ = 0; mcaZ <= (length >> 9); mcaZ++) {
for (int mcaX = 0; mcaX <= (width >> 9); mcaX++) {
final int fmcaX = mcaX;
final int fmcaZ = mcaZ;
File file = new File(folder, "r." + mcaX + "." + mcaZ + ".mca");
if (!file.exists()) {
file.createNewFile();
}
final BufferedRandomAccessFile raf = new BufferedRandomAccessFile(file, "rw", fileBuf);
final byte[] header = new byte[4096];
final byte[][] compressed = new byte[1024][];
int bx = mcaX << 9;
int bz = mcaZ << 9;
int scx = bx >> 4;
int ecx = Math.min(scx + 31, tcx);
int scz = bz >> 4;
int ecz = Math.min(scz + 31, tcz);
short pair = MathMan.pairByte(mcaX, mcaZ);
for (int cz = scz; cz <= ecz; cz++) {
final int csz = cz << 4;
final int cez = Math.min(csz + 15, length - 1);
for (int cx = scx; cx <= ecx; cx++) {
final int csx = cx << 4;
final int cex = Math.min(csx + 15, width - 1);
final int fcx = cx;
final int fcz = cz;
int chunkPair = MathMan.pair((short) cx, (short) cz);
final Char2CharOpenHashMap localBlocks = blocks.get(chunkPair);
pool.submit(new Runnable() {
@Override
public void run() {
try {
MCAChunk chunk = chunkStore.get();
int[] indexes = indexStore.get();
for (byte[] array : chunk.ids) {
if (array != null) {
Arrays.fill(array, (byte) 0);
}
}
int index = 0;
int maxY = 0;
int minY = Integer.MAX_VALUE;
int[] heightMap = chunk.getHeightMapArray();
int globalIndex;
for (int z = csz; z <= cez; z++) {
globalIndex = z * width + csx;
for (int x = csx; x <= cex; x++, index++, globalIndex++) {
indexes[index] = globalIndex;
int height = heights[globalIndex] & 0xFF;
heightMap[index] = height;
maxY = Math.max(maxY, height);
minY = Math.min(minY, height);
}
}
if (hasOverlay) {
maxY++;
}
int maxLayer = maxY >> 4;
int fillLayers = Math.max(0, (minY - 1)) >> 4;
for (int layer = 0; layer <= maxLayer; layer++) {
if (chunk.ids[layer] == null) {
chunk.ids[layer] = new byte[4096];
chunk.data[layer] = new byte[2048];
chunk.skyLight[layer] = new byte[2048];
chunk.blockLight[layer] = new byte[2048];
}
}
if (modifiedMain) { // If the main block is modified, we can't short circuit this
for (int layer = 0; layer < fillLayers; layer++) {
index = 0;
byte[] layerIds = chunk.ids[layer];
byte[] layerDatas = chunk.data[layer];
for (int z = csz; z <= cez; z++) {
for (int x = csx; x <= cex; x++, index++) {
globalIndex = indexes[index];
char mainCombined = main[globalIndex];
byte id = (byte) FaweCache.getId(mainCombined);
int data = FaweCache.getData(mainCombined);
if (data != 0) {
for (int y = 0; y < 16; y++) {
int mainIndex = index + (y << 8);
chunk.setNibble(mainIndex, layerDatas, data);
}
}
for (int y = 0; y < 16; y++) {
layerIds[index + (y << 8)] = id;
}
}
}
}
} else {
for (int layer = 0; layer < fillLayers; layer++) {
Arrays.fill(chunk.ids[layer], (byte) 1);
}
}
for (int layer = fillLayers; layer <= maxLayer; layer++) {
Arrays.fill(chunk.skyLight[layer], (byte) 255);
byte[] layerIds = chunk.ids[layer];
byte[] layerDatas = chunk.data[layer];
index = 0;
int startY = layer << 4;
int endY = startY + 15;
for (int z = csz; z <= cez; z++) {
for (int x = csx; x <= cex; x++, index++) {
globalIndex = indexes[index];
int height = heightMap[index];
int diff;
if (height > endY) {
diff = 16;
} else if (height >= startY) {
diff = height - startY;
char floorCombined = floor[globalIndex];
int id = FaweCache.getId(floorCombined);
int floorIndex = index + ((height & 15) << 8);
layerIds[floorIndex] = (byte) id;
int data = FaweCache.getData(floorCombined);
if (data != 0) {
chunk.setNibble(floorIndex, layerDatas, data);
}
if (hasOverlay && height >= startY - 1 && height < endY) {
char overlayCombined = overlay[globalIndex];
id = FaweCache.getId(overlayCombined);
int overlayIndex = index + (((height + 1) & 15) << 8);
layerIds[overlayIndex] = (byte) id;
data = FaweCache.getData(overlayCombined);
if (data != 0) {
chunk.setNibble(overlayIndex, layerDatas, data);
}
}
} else if (hasOverlay && height == startY - 1) {
char overlayCombined = overlay[globalIndex];
int id = FaweCache.getId(overlayCombined);
int overlayIndex = index + (((height + 1) & 15) << 8);
layerIds[overlayIndex] = (byte) id;
int data = FaweCache.getData(overlayCombined);
if (data != 0) {
chunk.setNibble(overlayIndex, layerDatas, data);
}
continue;
} else {
continue;
}
char mainCombined = main[globalIndex];
byte id = (byte) FaweCache.getId(mainCombined);
int data = FaweCache.getData(mainCombined);
if (data != 0) {
for (int y = 0; y < diff; y++) {
int mainIndex = index + (y << 8);
chunk.setNibble(mainIndex, layerDatas, data);
}
}
for (int y = 0; y < diff; y++) {
layerIds[index + (y << 8)] = id;
}
}
}
}
int maxYMod = 15 + (maxLayer << 4);
for (int layer = (maxY >> 4) + 1; layer < 16; layer++) {
chunk.ids[layer] = null;
}
index = 0;
{ // Bedrock
byte[] layerIds = chunk.ids[0];
for (int z = csz; z <= cez; z++) {
for (int x = csx; x <= cex; x++) {
layerIds[index++] = (byte) 7;
}
}
}
if (localBlocks != null && !localBlocks.isEmpty()) {
for (Char2CharMap.Entry entry : localBlocks.char2CharEntrySet()) {
char key = entry.getCharKey();
char combined = entry.getCharValue();
byte id = (byte) FaweCache.getId(combined);
int and = key & 4095;
int y = ((key >> 12) << 4) + (and >> 8);
int x = and & 0xF;
int z = (and >> 4) & 0xF;
int layer = key >> 12;
int localIndex = key & 4095;
if (!FaweCache.hasData(id)) {
if (chunk.ids[layer] == null) {
chunk.ids[layer] = new byte[4096];
chunk.data[layer] = new byte[2048];
chunk.skyLight[layer] = new byte[2048];
chunk.blockLight[layer] = new byte[2048];
}
chunk.setIdUnsafe(layer, localIndex, id);
} else {
int data = FaweCache.getData(combined);
chunk.setBlockUnsafe(layer, localIndex, id, data);
}
}
}
chunk.setLoc(null, fcx, fcz);
byte[] bytes = chunk.toBytes(byteStore1.get());
byte[] compressedBytes = MainUtil.compress(bytes, byteStore2.get(), deflateStore.get());
int blocks = (compressed.length + 4095) >> 12;
compressed[((fcx & 31)) + ((fcz & 31) << 5)] = compressedBytes.clone();
} catch (Throwable e) {
e.printStackTrace();
}
}
});
}
}
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
pool.submit(new Runnable() {
@Override
public void run() {
try {
int totalLength = 8192;
for (int i = 0; i < compressed.length; i++) {
byte[] compressedBytes = compressed[i];
if (compressedBytes != null) {
int blocks = ((4095 + compressedBytes.length + 5) / 4096) * 4096;
totalLength += blocks;
}
}
raf.setLength(totalLength);
int offset = 8192;
for (int i = 0; i < compressed.length; i++) {
byte[] compressedBytes = compressed[i];
if (compressedBytes != null) {
// Set header
int index = i << 2;
int offsetMedium = offset >> 12;
int blocks = ((4095 + compressedBytes.length + 5) / 4096);
header[index] = (byte) (offsetMedium >> 16);
header[index + 1] = (byte) ((offsetMedium >> 8));
header[index + 2] = (byte) ((offsetMedium >> 0));
header[index + 3] = (byte) (blocks);
// Write bytes
int cx = (fmcaX << 5) + (i & 31);
int cz = (fmcaZ << 5) + (i >> 5);
raf.seek(offset);
raf.writeInt(compressedBytes.length);
raf.write(2);
raf.write(compressedBytes);
offset += blocks * 4096;
}
}
raf.seek(0);
raf.write(header);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
}
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
}

View File

@ -5,11 +5,15 @@ import com.boydti.fawe.jnbt.NBTStreamer;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -19,6 +23,9 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class MCAChunk extends FaweChunk<Void> {
// ids: byte[16][4096]
@ -47,6 +54,65 @@ public class MCAChunk extends FaweChunk<Void> {
private boolean modified;
private boolean deleted;
public byte[] toBytes(byte[] buffer) throws IOException {
checkNotNull(buffer);
if (buffer == null) {
buffer = new byte[8192];
}
FastByteArrayOutputStream buffered = new FastByteArrayOutputStream(buffer);
DataOutputStream dataOut = new DataOutputStream(buffered);
NBTOutputStream nbtOut = new NBTOutputStream(dataOut);
nbtOut.writeNamedTagName("", NBTConstants.TYPE_COMPOUND);
nbtOut.writeLazyCompoundTag("Level", new NBTOutputStream.LazyWrite() {
@Override
public void write(NBTOutputStream out) throws IOException {
out.writeNamedTag("V", (byte) 1);
out.writeNamedTag("xPos", getX());
out.writeNamedTag("zPos", getZ());
out.writeNamedTag("LightPopulated", (byte) 0);
out.writeNamedTag("TerrainPopulated", (byte) 1);
if (entities.isEmpty()) {
out.writeNamedEmptyList("Entities");
} else {
out.writeNamedTag("Entities", new ListTag(CompoundTag.class, new ArrayList<CompoundTag>(entities.values())));
}
if (tiles.isEmpty()) {
out.writeNamedEmptyList("TileEntities");
} else {
out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class, new ArrayList<CompoundTag>(tiles.values())));
}
out.writeNamedTag("InhabitedTime", inhabitedTime);
out.writeNamedTag("LastUpdate", lastUpdate);
if (biomes != null) {
out.writeNamedTag("Biomes", biomes);
}
out.writeNamedTag("HeightMap", heightMap);
out.writeNamedTagName("Sections", NBTConstants.TYPE_LIST);
dataOut.writeByte(NBTConstants.TYPE_COMPOUND);
int len = 0;
for (int layer = 0; layer < ids.length; layer++) {
if (ids[layer] != null) len++;
}
dataOut.writeInt(len);
for (int layer = 0; layer < ids.length; layer++) {
byte[] idLayer = ids[layer];
if (idLayer == null) {
continue;
}
out.writeNamedTag("Y", (byte) layer);
out.writeNamedTag("BlockLight", blockLight[layer]);
out.writeNamedTag("SkyLight", skyLight[layer]);
out.writeNamedTag("Blocks", idLayer);
out.writeNamedTag("Data", data[layer]);
out.writeEndTag();
}
}
});
nbtOut.writeEndTag();
nbtOut.close();
return buffered.toByteArray();
}
public CompoundTag toTag() {
if (deleted) {
return null;
@ -87,6 +153,20 @@ public class MCAChunk extends FaweChunk<Void> {
return FaweCache.asTag(root);
}
public MCAChunk(FaweQueue queue, int x, int z) {
super(queue, x, z);
this.ids = new byte[16][];
this.data = new byte[16][];
this.skyLight = new byte[16][];
this.blockLight = new byte[16][];
this.biomes = new byte[256];
this.tiles = new HashMap<>();
this.entities = new HashMap<>();
this.lastUpdate = System.currentTimeMillis();
this.heightMap = new int[256];
this.modified = true;
}
public MCAChunk(MCAChunk parent, boolean shallow) {
super(parent.getParent(), parent.getX(), parent.getZ());
if (shallow) {
@ -127,7 +207,6 @@ public class MCAChunk extends FaweChunk<Void> {
skyLight = new byte[16][];
blockLight = new byte[16][];
this.compressedSize = compressedSize;
// NamedTag tag = nis.readNamedTag();
NBTStreamer streamer = new NBTStreamer(nis);
streamer.addReader(".Level.InhabitedTime", new RunnableVal2<Integer, Long>() {
@Override
@ -151,7 +230,7 @@ public class MCAChunk extends FaweChunk<Void> {
blockLight[layer] = tag.getByteArray("BlockLight");
}
});
streamer.addReader(".Level.Entities.#", new RunnableVal2<Integer, CompoundTag>() {
streamer.addReader(".Level.TileEntities.#", new RunnableVal2<Integer, CompoundTag>() {
@Override
public void run(Integer index, CompoundTag tile) {
int x = tile.getInt("x") & 15;
@ -161,12 +240,13 @@ public class MCAChunk extends FaweChunk<Void> {
tiles.put(pair, tile);
}
});
streamer.addReader(".Level.TileEntities.#", new RunnableVal2<Integer, CompoundTag>() {
streamer.addReader(".Level.Entities.#", new RunnableVal2<Integer, CompoundTag>() {
@Override
public void run(Integer index, CompoundTag entityTag) {
if (entities == null) {
entities = new HashMap<UUID, CompoundTag>();
}
long least = entityTag.getLong("UUIDLeast");
long most = entityTag.getLong("UUIDMost");
entities.put(new UUID(most, least), entityTag);
@ -205,7 +285,7 @@ public class MCAChunk extends FaweChunk<Void> {
}
@Deprecated
public void setModified() {
public final void setModified() {
this.modified = true;
}
@ -375,11 +455,34 @@ public class MCAChunk extends FaweChunk<Void> {
public void setNibble(int index, byte[] array, int value) {
int indexShift = index >> 1;
if((index & 1) == 0) {
array[indexShift] = (byte)(array[indexShift] & 240 | value & 15);
} else {
array[indexShift] = (byte)(array[indexShift] & 15 | (value & 15) << 4);
byte existing = array[indexShift];
int valueShift = value << 4;
if (existing == value + valueShift) {
return;
}
if((index & 1) == 0) {
array[indexShift] = (byte)(existing & 240 | value);
} else {
array[indexShift] = (byte)(existing & 15 | valueShift);
}
}
public void setIdUnsafe(int layer, int index, byte id) {
byte[] idsLayer = ids[layer];
idsLayer[index] = id;
}
public void setBlockUnsafe(int layer, int index, byte id, int data) {
byte[] idsLayer = ids[layer];
if (idsLayer == null) {
idsLayer = this.ids[layer] = new byte[4096];
this.data[layer] = new byte[2048];
this.skyLight[layer] = new byte[2048];
this.blockLight[layer] = new byte[2048];
}
idsLayer[index] = id;
byte[] dataLayer = this.data[layer];
setNibble(index, dataLayer, data);
}
@Override
@ -399,6 +502,11 @@ public class MCAChunk extends FaweChunk<Void> {
setNibble(j, dataLayer, data);
}
@Override
public void setBiome(byte biome) {
Arrays.fill(biomes, biome);
}
@Override
public void removeEntity(UUID uuid) {
modified = true;

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.jnbt.NBTStreamer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RunnableVal;
@ -8,21 +9,22 @@ import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.io.BufferedRandomAccessFile;
import com.boydti.fawe.object.io.FastByteArrayInputStream;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import java.io.BufferedInputStream;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.zip.Deflater;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
@ -33,24 +35,55 @@ import java.util.zip.InflaterInputStream;
*/
public class MCAFile {
private static Field fieldBuf2;
private static Field fieldBuf3;
private static Field fieldBuf4;
private static Field fieldBuf5;
private static Field fieldBuf6;
static {
try {
fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf");
fieldBuf2.setAccessible(true);
fieldBuf3 = NBTInputStream.class.getDeclaredField("buf");
fieldBuf3.setAccessible(true);
fieldBuf4 = FastByteArrayOutputStream.class.getDeclaredField("buffer");
fieldBuf4.setAccessible(true);
fieldBuf5 = DeflaterOutputStream.class.getDeclaredField("buf");
fieldBuf5.setAccessible(true);
fieldBuf6 = BufferedOutputStream.class.getDeclaredField("buf");
fieldBuf6.setAccessible(true);
} catch (Throwable e) {
e.printStackTrace();
}
}
private FaweQueue queue;
private File file;
private RandomAccessFile raf;
private byte[] locations;
private FaweQueue queue;
private Field fieldBuf1;
private Field fieldBuf2;
private Field fieldBuf3;
private Field fieldBuf4;
private Field fieldBuf5;
private Field fieldBuf6;
private byte[] buffer1 = new byte[4096];
private byte[] buffer2 = new byte[4096];
private byte[] buffer3 = new byte[720];
final ThreadLocal<byte[]> byteStore1 = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[4096];
}
};
final ThreadLocal<byte[]> byteStore2 = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[4096];
}
};
final ThreadLocal<byte[]> byteStore3 = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {
return new byte[1024];
}
};
private final int X, Z;
private Map<Integer, MCAChunk> chunks = new HashMap<>();
private Int2ObjectOpenHashMap<MCAChunk> chunks = new Int2ObjectOpenHashMap<>();
public MCAFile(FaweQueue parent, File file) {
this.queue = parent;
@ -71,20 +104,10 @@ public class MCAFile {
try {
if (raf == null) {
this.locations = new byte[4096];
this.raf = new BufferedRandomAccessFile(file, "rw", (int) file.length());
this.raf = new RandomAccessFile(file, "rw");
raf.seek(0);
raf.readFully(locations);
fieldBuf1 = BufferedInputStream.class.getDeclaredField("buf");
fieldBuf1.setAccessible(true);
fieldBuf2 = InflaterInputStream.class.getDeclaredField("buf");
fieldBuf2.setAccessible(true);
fieldBuf3 = NBTInputStream.class.getDeclaredField("buf");
fieldBuf3.setAccessible(true);
fieldBuf4 = FastByteArrayOutputStream.class.getDeclaredField("buffer");
fieldBuf4.setAccessible(true);
fieldBuf5 = DeflaterOutputStream.class.getDeclaredField("buf");
fieldBuf5.setAccessible(true);
fieldBuf6 = BufferedOutputStream.class.getDeclaredField("buf");
fieldBuf6.setAccessible(true);
}
} catch (Throwable e) {
e.printStackTrace();
@ -109,7 +132,9 @@ public class MCAFile {
public MCAChunk getCachedChunk(int cx, int cz) {
int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31));
return chunks.get(pair);
synchronized (chunks) {
return chunks.get(pair);
}
}
public MCAChunk getChunk(int cx, int cz) throws IOException {
@ -132,10 +157,42 @@ public class MCAFile {
MCAChunk chunk = new MCAChunk(nis, queue, cx, cz, size);
nis.close();
int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31));
chunks.put(pair, chunk);
synchronized (chunks) {
chunks.put(pair, chunk);
}
return chunk;
}
public void forEachSortedChunk(RunnableVal4<Integer, Integer, Integer, Integer> onEach) throws IOException {
char[] offsets = new char[(int) (raf.length() / 4096) - 2];
Arrays.fill(offsets, Character.MAX_VALUE);
char i = 0;
for (int z = 0; z < 32; z++) {
for (int x = 0; x < 32; x++, i += 4) {
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i+ 2] & 0xFF))) - 2;
int size = locations[i + 3] & 0xFF;
if (size != 0) {
if (offset < offsets.length) {
offsets[offset] = i;
} else {
Fawe.debug("Ignoring invalid offset " + offset);
}
}
}
}
for (i = 0; i < offsets.length; i++) {
int index = offsets[i];
if (index != Character.MAX_VALUE) {
int offset = i + 2;
int size = locations[index + 3] & 0xFF;
int index2 = index >> 2;
int x = (index2) & 31;
int z = (index2) >> 5;
onEach.run(x, z, offset << 12, size << 12);
}
}
}
/**
* @param onEach cx, cz, offset, size
*/
@ -188,23 +245,17 @@ public class MCAFile {
}
private byte[] getChunkCompressedBytes(int offset) throws IOException{
raf.seek(offset);
int size = raf.readInt();
int compression = raf.read();
byte[] data = new byte[size];
raf.readFully(data);
return data;
}
private void writeSafe(int offset, byte[] data) throws IOException {
int len = data.length + 5;
raf.seek(offset);
if (raf.length() - offset < len) {
raf.setLength(offset + len);
if (offset == 0) {
return null;
}
synchronized (raf) {
raf.seek(offset);
int size = raf.readInt();
int compression = raf.read();
byte[] data = new byte[size];
raf.readFully(data);
return data;
}
raf.writeInt(data.length);
raf.write(2);
raf.write(data);
}
private NBTInputStream getChunkIS(int offset) throws IOException {
@ -212,11 +263,10 @@ public class MCAFile {
byte[] data = getChunkCompressedBytes(offset);
FastByteArrayInputStream bais = new FastByteArrayInputStream(data);
InflaterInputStream iis = new InflaterInputStream(bais, new Inflater(), 1);
fieldBuf2.set(iis, buffer2);
BufferedInputStream bis = new BufferedInputStream(iis, 1);
fieldBuf1.set(bis, buffer1);
fieldBuf2.set(iis, byteStore2.get());
FastBufferedInputStream bis = new FastBufferedInputStream(iis, byteStore1.get());
NBTInputStream nis = new NBTInputStream(bis);
fieldBuf3.set(nis, buffer3);
fieldBuf3.set(nis, byteStore3.get());
return nis;
} catch (IllegalAccessException unlikely) {
unlikely.printStackTrace();
@ -243,45 +293,33 @@ public class MCAFile {
* @param onEach chunk
*/
public void forEachCachedChunk(RunnableVal<MCAChunk> onEach) {
for (Map.Entry<Integer, MCAChunk> entry : chunks.entrySet()) {
onEach.run(entry.getValue());
synchronized (chunks) {
for (Map.Entry<Integer, MCAChunk> entry : chunks.entrySet()) {
onEach.run(entry.getValue());
}
}
}
public List<MCAChunk> getCachedChunks() {
return new ArrayList<>(chunks.values());
synchronized (chunks) {
return new ArrayList<>(chunks.values());
}
}
public void uncache(int cx, int cz) {
int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31));
chunks.remove(pair);
synchronized (chunks) {
chunks.remove(pair);
}
}
private byte[] toBytes(MCAChunk chunk) throws Exception {
CompoundTag tag = chunk.toTag();
if (tag == null || chunk.isDeleted()) {
if (chunk.isDeleted()) {
return null;
}
FastByteArrayOutputStream baos = new FastByteArrayOutputStream(buffer3);
// PGZIPOutputStream deflater = new PGZIPOutputStream(baos);
// deflater.setStrategy(Deflater.FILTERED);
Deflater deflate = new Deflater(1);
deflate.setStrategy(Deflater.FILTERED);
DeflaterOutputStream deflater = new DeflaterOutputStream(baos, deflate, 1, true);
fieldBuf5.set(deflater, buffer2);
BufferedOutputStream bos = new BufferedOutputStream(deflater, 1);
fieldBuf6.set(bos, buffer1);
NBTOutputStream nos = new NBTOutputStream(bos);
nos.writeNamedTag("", tag);
bos.flush();
bos.close();
byte[] result = baos.toByteArray();
baos.close();
deflater.close();
bos.close();
nos.close();
return result;
byte[] uncompressed = chunk.toBytes(byteStore3.get());
byte[] compressed = MainUtil.compress(uncompressed, byteStore2.get(), null);
return compressed;
}
private byte[] getChunkBytes(int cx, int cz) throws Exception{
@ -296,7 +334,19 @@ public class MCAFile {
return toBytes(mca);
}
private void writeHeader(int cx, int cz, int offsetMedium, int sizeByte) throws IOException {
private void writeSafe(RandomAccessFile raf, int offset, byte[] data) throws IOException {
int len = data.length + 5;
raf.seek(offset);
if (raf.length() - offset < len) {
raf.setLength(((offset + len + 4095) / 4096) * 4096);
}
raf.writeInt(data.length);
raf.write(2);
raf.write(data);
}
private void writeHeader(RandomAccessFile raf, int cx, int cz, int offsetMedium, int sizeByte, boolean writeTime) throws IOException {
int i = ((cx & 31) << 2) + ((cz & 31) << 7);
raf.seek(i);
raf.write((offsetMedium >> 16));
@ -309,134 +359,150 @@ public class MCAFile {
} else {
raf.writeInt((int) (System.currentTimeMillis() / 1000L));
}
int offset = (((locations[i] & 0xFF) << 16) + ((locations[i + 1] & 0xFF) << 8) + ((locations[i+ 2] & 0xFF))) << 12;
int size = (locations[i + 3] & 0xFF) << 12;
}
public void close() {
flush();
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
file = null;
raf = null;
locations = null;
queue = null;
fieldBuf1 = null;
fieldBuf2 = null;
fieldBuf3 = null;
fieldBuf4 = null;
fieldBuf5 = null;
fieldBuf6 = null;
buffer1 = null;
buffer2 = null;
buffer3 = null;
chunks = null;
}
}
public void flush() {
boolean modified = false;
for (MCAChunk chunk : getCachedChunks()) {
if (chunk.isModified()) {
modified = true;
break;
}
}
if (!modified) {
return;
}
final HashMap<Integer, Integer> offsetMap = new HashMap<>(); // Offset -> <byte cx, byte cz, short size>
forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(Integer cx, Integer cz, Integer offset, Integer size) {
short pair1 = MathMan.pairByte((byte) (cx & 31), (byte) (cz & 31));
short pair2 = (short) (size >> 12);
offsetMap.put(offset, MathMan.pair(pair1, pair2));
}
});
HashMap<Integer, byte[]> relocate = new HashMap<Integer, byte[]>();
int start = 8192;
int written = start;
int end = 8192;
int nextOffset = 8192;
try {
for (int count = 0; count < offsetMap.size(); count++) {
Integer loc = offsetMap.get(nextOffset);
while (loc == null) {
nextOffset += 4096;
loc = offsetMap.get(nextOffset);
public void close(ForkJoinPool pool) {
synchronized (raf) {
if (raf != null) {
flush(pool);
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
int offset = nextOffset;
short cxz = MathMan.unpairX(loc);
int cx = MathMan.unpairShortX(cxz);
int cz = MathMan.unpairShortY(cxz);
int size = MathMan.unpairY(loc) << 12;
nextOffset += size;
end += size;
int pair = MathMan.pair((short) (cx & 31), (short) (cz & 31));
byte[] newBytes = relocate.get(pair);
if (newBytes == null) {
if (offset == start) {
MCAChunk cached = getCachedChunk(cx, cz);
if (cached == null || !cached.isModified()) {
start += size;
written = start + size;
file = null;
raf = null;
locations = null;
queue = null;
chunks = null;
}
}
}
public void flush(ForkJoinPool pool) {
synchronized (raf) {
boolean wait;
if (pool == null) {
wait = true;
pool = new ForkJoinPool();
} else wait = false;
Int2ObjectOpenHashMap<byte[]> relocate = new Int2ObjectOpenHashMap<>();
final Int2ObjectOpenHashMap<Integer> offsetMap = new Int2ObjectOpenHashMap<>(); // Offset -> <byte cx, byte cz, short size>
final Int2ObjectOpenHashMap<byte[]> compressedMap = new Int2ObjectOpenHashMap<>();
boolean modified = false;
for (MCAChunk chunk : getCachedChunks()) {
if (chunk.isModified()) {
modified = true;
if (!chunk.isDeleted()) {
pool.submit(new Runnable() {
@Override
public void run() {
try {
byte[] compressed = toBytes(chunk);
int pair = MathMan.pair((short) (chunk.getX() & 31), (short) (chunk.getZ() & 31));
synchronized (compressedMap) {
compressedMap.put(pair, compressed);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
}
}
}
if (modified) {
forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(Integer cx, Integer cz, Integer offset, Integer size) {
short pair1 = MathMan.pairByte((byte) (cx & 31), (byte) (cz & 31));
short pair2 = (short) (size >> 12);
offsetMap.put((int) offset, (Integer) MathMan.pair(pair1, pair2));
}
});
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++) {
Integer loc = offsetMap.get(nextOffset);
while (loc == null) {
nextOffset += 4096;
loc = offsetMap.get(nextOffset);
}
int offset = nextOffset;
short cxz = MathMan.unpairX(loc);
int cx = MathMan.unpairShortX(cxz);
int cz = MathMan.unpairShortY(cxz);
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);
if (newBytes == null) {
if (offset == start) {
MCAChunk cached = getCachedChunk(cx, cz);
if (cached == null || !cached.isModified()) {
writeHeader(raf, cx, cz, start >> 12, size >> 12, true);
start += size;
written = start + size;
continue;
} else {
newBytes = compressedMap.get(pair);
}
} else {
newBytes = compressedMap.get(pair);
if (newBytes == null) {
newBytes = getChunkCompressedBytes(getOffset(cx, cz));
}
}
}
if (newBytes == null) {
writeHeader(raf, cx, cz, 0, 0, false);
continue;
} else {
newBytes = toBytes(cached);
}
} else {
newBytes = getChunkBytes(cx, cz);
}
}
if (newBytes == null) {
writeHeader(cx, cz, 0, 0);
continue;
}
int len = newBytes.length + 5;
int oldSize = (size + 4095) >> 12;
int newSize = (len + 4095) >> 12;
int nextOffset2 = nextOffset;
while (start + len > end) {
Integer nextLoc = offsetMap.get(nextOffset2);
if (nextLoc != null) {
short nextCXZ = MathMan.unpairX(nextLoc);
int nextCX = MathMan.unpairShortX(nextCXZ);
int nextCZ = MathMan.unpairShortY(nextCXZ);
if (getCachedChunk(nextCX, nextCZ) == null) {
byte[] nextBytes = getChunkCompressedBytes(nextOffset2);
relocate.put(pair, nextBytes);
int len = newBytes.length + 5;
int oldSize = (size + 4095) >> 12;
int newSize = (len + 4095) >> 12;
int nextOffset2 = end;
while (start + len > end) {
Integer nextLoc = offsetMap.get(nextOffset2);
if (nextLoc != null) {
short nextCXZ = MathMan.unpairX(nextLoc);
int nextCX = MathMan.unpairShortX(nextCXZ);
int nextCZ = MathMan.unpairShortY(nextCXZ);
if (getCachedChunk(nextCX, nextCZ) == null) {
byte[] nextBytes = getChunkCompressedBytes(nextOffset2);
relocate.put(MathMan.pair((short) (nextCX & 31), (short) (nextCZ & 31)), nextBytes);
}
int nextSize = MathMan.unpairY(nextLoc) << 12;
end += nextSize;
nextOffset2 += nextSize;
} else {
end += 4096;
nextOffset2 += 4096;
}
}
// System.out.println("Relocating " + nextCX + "," + nextCZ);
int nextSize = MathMan.unpairY(nextLoc) << 12;
end += nextSize;
nextOffset2 += nextSize;
} else {
end = start + len;
break;
writeSafe(raf, start, newBytes);
writeHeader(raf, cx, cz, start >> 12, newSize, true);
written = start + newBytes.length + 5;
start += newSize << 12;
}
raf.setLength(4096 * ((written + 4095) / 4096));
if (raf instanceof BufferedRandomAccessFile) {
((BufferedRandomAccessFile) raf).flush();
}
raf.close();
} catch (Throwable e) {
e.printStackTrace();
}
// System.out.println("Writing: " + cx + "," + cz);
writeSafe(start, newBytes);
if (offset != start || end != start + size || oldSize != newSize || true) {
// System.out.println("Header: " + cx + "," + cz + " | " + offset + "," + start + " | " + end + "," + (start + size) + " | " + size + " | " + start);
writeHeader(cx, cz, start >> 12, newSize);
if (wait) {
pool.shutdown();
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
written = start + newBytes.length + 5;
start += newSize << 12;
}
raf.setLength(written);
if (raf instanceof BufferedRandomAccessFile) {
((BufferedRandomAccessFile) raf).flush();
}
raf.close();
} catch (Throwable e) {
e.printStackTrace();
}
}
}

View File

@ -70,6 +70,12 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
public void filterWorld(final MCAFilter filter) {
File folder = getSaveFolder();
final ForkJoinPool pool = new ForkJoinPool();
final ThreadLocal<MutableMCABackedBaseBlock> blockStore = new ThreadLocal<MutableMCABackedBaseBlock>() {
@Override
protected MutableMCABackedBaseBlock initialValue() {
return new MutableMCABackedBaseBlock();
}
};
for (final File file : folder.listFiles()) {
try {
String name = file.getName();
@ -82,18 +88,16 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
final MCAFile finalFile = filter.applyFile(mcaFile);
if (finalFile != null) {
finalFile.init();
Runnable run = new Runnable() {
// May not do anything, but seems to lead to smaller lag spikes
final int cbx = mcaX << 5;
final int cbz = mcaZ << 5;
finalFile.forEachSortedChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run() {
// May not do anything, but seems to lead to smaller lag spikes
System.gc();
System.gc();
final MutableMCABackedBaseBlock mutableBlock = new MutableMCABackedBaseBlock();
final int cbx = mcaX << 5;
final int cbz = mcaZ << 5;
finalFile.forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
public void run(final Integer rcx, final Integer rcz, Integer offset, Integer size) {
pool.submit(new Runnable() {
@Override
public void run(final Integer rcx, final Integer rcz, Integer offset, Integer size) {
public void run() {
int cx = cbx + rcx;
int cz = cbz + rcz;
if (filter.appliesChunk(cx, cz)) {
@ -102,6 +106,7 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
try {
chunk = filter.applyChunk(chunk);
if (chunk != null) {
final MutableMCABackedBaseBlock mutableBlock = blockStore.get();
mutableBlock.setChunk(chunk);
int bx = cx << 4;
int bz = cz << 4;
@ -111,9 +116,12 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
int yStart = layer << 4;
int index = 0;
for (int y = yStart; y < yStart + 16; y++) {
mutableBlock.setY(y);
for (int z = bz; z < bz + 16; z++) {
mutableBlock.setZ(z);
for (int x = bx; x < bx + 16; x++,index++) {
mutableBlock.setIndex(x & 15, y, z & 15, index);
mutableBlock.setX(x);
mutableBlock.setIndex(index);
filter.applyBlock(x, y, z, mutableBlock);
}
}
@ -131,26 +139,26 @@ public class MCAQueue extends NMSMappedFaweQueue<FaweQueue, FaweChunk, FaweChunk
}
}
});
original.close();
finalFile.close();
System.gc();
System.gc();
}
};
pool.submit(run);
});
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
original.close(pool);
if (original != finalFile) finalFile.close(pool);
} else {
try {
original.close();
original.close(pool);
file.delete();
} catch (Throwable ignore) {}
}
}
} catch (Throwable ignore) {}
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
pool.shutdown();
try {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
} catch (Throwable e) {
e.printStackTrace();
}
}

View File

@ -162,8 +162,7 @@ public class MCAQueueMap implements IFaweQueueMap {
if (result = iter.hasNext()) {
MCAFile file = iter.next().getValue();
iter.remove();
file.flush();
file.close();
file.close(null);
} else {
break;
}

View File

@ -31,28 +31,37 @@ public class MutableMCABackedBaseBlock extends BaseBlock {
data = chunk.data[layer];
}
public void setIndex(int x, int y, int z, int index) {
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setZ(int z) {
this.z = z;
}
public void setIndex(int index) {
this.index = index;
}
@Override
public int getId() {
return ids[index] & 0xFF;
return Byte.toUnsignedInt(ids[index]);
}
@Override
public int getData() {
if (!FaweCache.hasData(ids[index])) {
if (!FaweCache.hasData(ids[index] & 0xFF)) {
return 0;
} else {
int indexShift = index >> 1;
if ((index & 1) == 0) {
return data[index] & 15;
return data[indexShift] & 15;
} else {
return data[index] >> 4 & 15;
return (data[indexShift] >> 4) & 15;
}
}
}

View File

@ -0,0 +1,256 @@
package com.boydti.fawe.jnbt.anvil.generator;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.extent.Extent;
public class CavesGen extends GenBase {
private boolean evenCaveDistribution = false;
private int caveFrequency = 40;
private int caveRarity = 7;
private int caveMinAltitude = 8;
private int caveMaxAltitude = 127;
private int caveSystemFrequency = 1;
private int individualCaveRarity = 25;
private int caveSystemPocketChance = 0;
private int caveSystemPocketMinSize = 0;
private int caveSystemPocketMaxSize = 3;
public CavesGen(int caveSize) {
super(caveSize);
}
public CavesGen(int caveSize, int caveFrequency, int caveRarity, int caveMinAltitude, int caveMaxAltitude, int caveSystemFrequency, int individualCaveRarity, int caveSystemPocketChance, int caveSystemPocketMinSize, int caveSystemPocketMaxSize) {
super(caveSize);
this.caveFrequency = caveFrequency;
this.caveRarity = caveRarity;
this.caveMinAltitude = caveMinAltitude;
this.caveMaxAltitude = caveMaxAltitude;
this.caveSystemFrequency = caveSystemFrequency;
this.individualCaveRarity = individualCaveRarity;
this.caveSystemPocketChance = caveSystemPocketChance;
this.caveSystemPocketMinSize = caveSystemPocketMinSize;
this.caveSystemPocketMaxSize = caveSystemPocketMaxSize;
}
protected void generateLargeCaveNode(long seed, Vector2D pos, Extent chunk, double x, double y, double z) throws WorldEditException {
generateCaveNode(seed, pos, chunk, x, y, z, 1.0F + PseudoRandom.random.nextDouble() * 6.0F, 0.0F, 0.0F, -1, -1, 0.5D);
}
protected void generateCaveNode(long seed, Vector2D chunkPos, Extent chunk, double x, double y, double z, double paramdouble1, double paramdouble2, double paramdouble3, int angle, int maxAngle, double paramDouble4) throws WorldEditException {
int bx = (chunkPos.getBlockX() << 4);
int bz = (chunkPos.getBlockZ() << 4);
double real_x = bx + 7;
double real_z = bz + 7;
double f1 = 0.0F;
double f2 = 0.0F;
PseudoRandom localRandom = new PseudoRandom(seed);
if (maxAngle <= 0) {
int checkAreaSize = this.getCheckAreaSize() * 16 - 16;
maxAngle = checkAreaSize - localRandom.nextInt(checkAreaSize / 4);
}
boolean isLargeCave = false;
if (angle == -1) {
angle = maxAngle / 2;
isLargeCave = true;
}
int j = localRandom.nextInt(maxAngle / 2) + maxAngle / 4;
int k = localRandom.nextInt(6) == 0 ? 1 : 0;
for (; angle < maxAngle; angle++) {
double d3 = 1.5D + MathMan.sinInexact(angle * 3.141593F / maxAngle) * paramdouble1 * 1.0F;
double d4 = d3 * paramDouble4;
double f3 = MathMan.cosInexact(paramdouble3);
double f4 = MathMan.sinInexact(paramdouble3);
x += MathMan.cosInexact(paramdouble2) * f3;
y += f4;
z += MathMan.sinInexact(paramdouble2) * f3;
if (k != 0)
paramdouble3 *= 0.92F;
else {
paramdouble3 *= 0.7F;
}
paramdouble3 += f2 * 0.1F;
paramdouble2 += f1 * 0.1F;
f2 *= 0.9F;
f1 *= 0.75F;
f2 += (localRandom.nextDouble() - localRandom.nextDouble()) * localRandom.nextDouble() * 2.0F;
f1 += (localRandom.nextDouble() - localRandom.nextDouble()) * localRandom.nextDouble() * 4.0F;
if ((!isLargeCave) && (angle == j) && (paramdouble1 > 1.0F) && (maxAngle > 0)) {
generateCaveNode(localRandom.nextLong(), chunkPos, chunk, x, y, z, localRandom.nextDouble() * 0.5F + 0.5F, paramdouble2 - 1.570796F, paramdouble3 / 3.0F, angle, maxAngle, 1.0D);
generateCaveNode(localRandom.nextLong(), chunkPos, chunk, x, y, z, localRandom.nextDouble() * 0.5F + 0.5F, paramdouble2 + 1.570796F, paramdouble3 / 3.0F, angle, maxAngle, 1.0D);
return;
}
if ((!isLargeCave) && (localRandom.nextInt(4) == 0)) {
continue;
}
// Check if distance to working point (x and z) too larger than working radius (maybe ??)
double d5 = x - real_x;
double d6 = z - real_z;
double d7 = maxAngle - angle;
double d8 = paramdouble1 + 2.0F + 16.0F;
if (d5 * d5 + d6 * d6 - d7 * d7 > d8 * d8) {
return;
}
//Boundaries check.
if ((x < real_x - 16.0D - d3 * 2.0D) || (z < real_z - 16.0D - d3 * 2.0D) || (x > real_x + 16.0D + d3 * 2.0D) || (z > real_z + 16.0D + d3 * 2.0D))
continue;
int m = (int) (x - d3) - bx - 1;
int n = (int) (x + d3) - bx + 1;
int i1 = (int) (y - d4) - 1;
int i2 = (int) (y + d4) + 1;
int i3 = (int) (z - d3) - bz - 1;
int i4 = (int) (z + d3) - bz + 1;
if (m < 0)
m = 0;
if (n > 16)
n = 16;
if (i1 < 1)
i1 = 1;
if (i2 > 256 - 8) {
i2 = 256 - 8;
}
if (i3 < 0)
i3 = 0;
if (i4 > 16)
i4 = 16;
// Search for water
boolean waterFound = false;
for (int local_x = m; (!waterFound) && (local_x < n); local_x++) {
for (int local_z = i3; (!waterFound) && (local_z < i4); local_z++) {
for (int local_y = i2 + 1; (!waterFound) && (local_y >= i1 - 1); local_y--) {
if (local_y >= 0 && local_y < 255) {
BaseBlock material = chunk.getLazyBlock(bx + local_x, local_y, bz + local_z);
if (material.getId() == 8 || material.getId() == 9) {
waterFound = true;
}
if ((local_y != i1 - 1) && (local_x != m) && (local_x != n - 1) && (local_z != i3) && (local_z != i4 - 1))
local_y = i1;
}
}
}
}
if (waterFound) {
continue;
}
// Generate cave
for (int local_x = m; local_x < n; local_x++) {
double d9 = (local_x + bx + 0.5D - x) / d3;
for (int local_z = i3; local_z < i4; local_z++) {
double d10 = (local_z + bz + 0.5D - z) / d3;
boolean grassFound = false;
if (d9 * d9 + d10 * d10 < 1.0D) {
for (int local_y = i2; local_y > i1; local_y--) {
double d11 = ((local_y - 1) + 0.5D - y) / d4;
if ((d11 > -0.7D) && (d9 * d9 + d11 * d11 + d10 * d10 < 1.0D)) {
BaseBlock material = chunk.getLazyBlock(bx + local_x, local_y, bz + local_z);
BaseBlock materialAbove = chunk.getLazyBlock(bx + local_x, local_y + 1, bz + local_z);
if (material.getId() == BlockID.GRASS || material.getId() == BlockID.MYCELIUM) {
grassFound = true;
}
if (this.isSuitableBlock(material, materialAbove)) {
if (local_y - 1 < 10) {
chunk.setBlock(bx + local_x, local_y, bz + local_z, FaweCache.getBlock(BlockID.LAVA, 0));
} else {
chunk.setBlock(bx + local_x, local_y, bz + local_z, FaweCache.getBlock(0, 0));
// If grass was just deleted, try to
// move it down
if (grassFound) {
BaseBlock block = chunk.getLazyBlock(bx + local_x, local_y - 1, bz + local_z);
if (block.getId() == BlockID.DIRT) {
chunk.setBlock(bx + local_x, local_y - 1, bz + local_z, FaweCache.getBlock(BlockID.STONE, 0));
}
}
}
}
}
}
}
}
}
if (isLargeCave)
break;
}
}
protected boolean isSuitableBlock(BaseBlock material, BaseBlock materialAbove) {
switch (material.getId()) {
case 0:
case 8:
case 9:
case 10:
case 11:
case 7:
return false;
default:
return true;
}
}
@Override
public void generateChunk(Vector2D adjacentChunk, Vector2D originChunk, Extent chunk) throws WorldEditException {
PseudoRandom random = getRandom();
int i = random.nextInt(random.nextInt(random.nextInt(this.caveFrequency) + 1) + 1);
if (this.evenCaveDistribution)
i = this.caveFrequency;
if (random.nextInt(100) >= this.caveRarity)
i = 0;
for (int j = 0; j < i; j++) {
double x = (adjacentChunk.getBlockX() << 4) + random.nextInt(16);
double y;
if (this.evenCaveDistribution)
y = random.nextInt(this.caveMinAltitude, this.caveMaxAltitude);
else
y = random.nextInt(random.nextInt(this.caveMaxAltitude - this.caveMinAltitude + 1) + 1) + this.caveMinAltitude;
double z = (adjacentChunk.getBlockZ() << 4) + random.nextInt(16);
int count = this.caveSystemFrequency;
boolean largeCaveSpawned = false;
if (random.nextInt(100) <= this.individualCaveRarity) {
generateLargeCaveNode(random.nextLong(), originChunk, chunk, x, y, z);
largeCaveSpawned = true;
}
if ((largeCaveSpawned) || (random.nextInt(100) <= this.caveSystemPocketChance - 1)) {
count += random.nextInt(this.caveSystemPocketMinSize, this.caveSystemPocketMaxSize);
}
while (count > 0) {
count--;
double f1 = random.nextDouble() * 3.141593F * 2.0F;
double f2 = (random.nextDouble() - 0.5F) * 2.0F / 8.0F;
double f3 = random.nextDouble() * 2.0F + random.nextDouble();
generateCaveNode(random.nextLong(), originChunk, chunk, x, y, z, f3, f1, f2, 0, 0, 1.0D);
}
}
}
}

View File

@ -0,0 +1,49 @@
package com.boydti.fawe.jnbt.anvil.generator;
import com.boydti.fawe.object.PseudoRandom;
import com.sk89q.worldedit.MutableBlockVector2D;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
public abstract class GenBase {
private final int checkAreaSize;
private final PseudoRandom random;
private final long seed;
private final long worldSeed1, worldSeed2;
private MutableBlockVector2D mutable = new MutableBlockVector2D();
public GenBase(int area) {
this.random = new PseudoRandom();
this.checkAreaSize = area;
this.seed = PseudoRandom.random.nextLong();
this.worldSeed1 = PseudoRandom.random.nextLong();
this.worldSeed2 = PseudoRandom.random.nextLong();
}
public int getCheckAreaSize() {
return checkAreaSize;
}
public PseudoRandom getRandom() {
return random;
}
public void generate(Vector2D chunkPos, Extent chunk) throws WorldEditException {
int i = this.checkAreaSize;
int chunkX = chunkPos.getBlockX();
int chunkZ = chunkPos.getBlockZ();
for (int x = chunkX - i; x <= chunkX + i; x++) {
mutable.mutX(x);
for (int z = chunkZ - i; z <= chunkZ + i; z++) {
mutable.mutZ(z);
this.random.setSeed(worldSeed1 * x ^ worldSeed2 * z ^ seed);
generateChunk(mutable, chunkPos, chunk);
}
}
}
public abstract void generateChunk(Vector2D adjacentChunk, Vector2D originChunk, Extent chunk) throws WorldEditException;
}

View File

@ -0,0 +1,118 @@
package com.boydti.fawe.jnbt.anvil.generator;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.MutableBlockVector;
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;
public class OreGen extends Resource {
private final int maxSize;
private final double maxSizeO8;
private final double maxSizeO16;
private final double sizeInverse;
private final int minY;
private final int maxY;
private final Pattern pattern;
private final Extent extent;
private final Mask mask;
private MutableBlockVector mutable = new MutableBlockVector();
private double ONE_2 = 1 / 2F;
private double ONE_8 = 1 / 8F;
private double ONE_16 = 1 / 16F;
public int laced =0;
public OreGen(Extent extent, Mask mask, Pattern pattern, int size, int minY, int maxY) {
this.maxSize = size;
this.maxSizeO8 = size * ONE_8;
this.maxSizeO16 = size * ONE_16;
this.sizeInverse = 1.0 / size;
this.minY = minY;
this.maxY = maxY;
this.mask = mask;
this.pattern = pattern;
this.extent = extent;
}
@Override
public boolean spawn(PseudoRandom rand, int x, int z) throws WorldEditException {
int y = rand.nextInt(minY, maxY);
if (!mask.test(mutable.setComponents(x, y, z))) {
return false;
}
double f = rand.nextDouble() * Math.PI;
int x8 = x + 8;
int z8 = z + 8;
double so8 = maxSizeO8;
double so16 = maxSizeO16;
double sf = MathMan.sinInexact(f) * so8;
double cf = MathMan.cosInexact(f) * so8;
double d1 = x8 + sf;
double d2 = x8 - sf;
double d3 = z8 + cf;
double d4 = z8 - cf;
double d5 = y + rand.nextInt(3) - 2;
double d6 = y + rand.nextInt(3) - 2;
double xd = (d2 - d1);
double yd = (d6 - d5);
double zd = (d4 - d3);
double iFactor = 0;
for (int i = 0; i < maxSize; i++, iFactor += sizeInverse) {
double d7 = d1 + xd * iFactor;
double d8 = d5 + yd * iFactor;
double d9 = d3 + zd * iFactor;
double d10 = rand.nextDouble() * so16;
double sif = MathMan.sinInexact(Math.PI * iFactor);
double d11 = (sif + 1.0) * d10 + 1.0;
double d12 = (sif + 1.0) * d10 + 1.0;
double d11o2 = d11 * ONE_2;
double d12o2 = d12 * ONE_2;
int minX = MathMan.floorZero(d7 - d11o2);
int minY = Math.max(1, MathMan.floorZero(d8 - d12o2));
int minZ = MathMan.floorZero(d9 - d11o2);
int maxX = MathMan.floorZero(d7 + d11o2);
int maxY = Math.min(255, MathMan.floorZero(d8 + d12o2));
int maxZ = MathMan.floorZero(d9 + d11o2);
double id11o2 = 1.0 / (d11o2);
double id12o2 = 1.0 / (d12o2);
for (int xx = minX; xx <= maxX; xx++) {
double dx = (xx + 0.5D - d7) * id11o2;
double dx2 = dx * dx;
if (dx2 < 1) {
mutable.mutX(xx);
for (int yy = minY; yy <= maxY; yy++) {
double dy = (yy + 0.5D - d8) * id12o2;
double dxy2 = dx2 + dy * dy;
if (dxy2 < 1) {
mutable.mutY(yy);
for (int zz = minZ; zz <= maxZ; zz++) {
mutable.mutZ(zz);
double dz = (zz + 0.5D - d9) * id11o2;
double dxyz2 = dxy2 + dz * dz;
if ((dxyz2 < 1)) {
if (mask.test(mutable))
extent.setBlock(xx, yy, zz, pattern.apply(mutable));
}
}
}
}
}
}
}
return true;
}
}

View File

@ -0,0 +1,11 @@
package com.boydti.fawe.jnbt.anvil.generator;
import com.boydti.fawe.object.PseudoRandom;
import com.sk89q.worldedit.WorldEditException;
public abstract class Resource {
public Resource() {
}
public abstract boolean spawn(PseudoRandom random, int x, int z) throws WorldEditException;
}

View File

@ -0,0 +1,56 @@
package com.boydti.fawe.jnbt.anvil.generator;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.object.schematic.Schematic;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
public class SchemGen extends Resource {
private final Extent extent;
private final WorldData worldData;
private final ClipboardHolder[] clipboards;
private final boolean randomRotate;
private final Mask mask;
private MutableBlockVector mutable = new MutableBlockVector();
public SchemGen(Mask mask, Extent extent, WorldData worldData, ClipboardHolder[] clipboards, boolean randomRotate) {
this.mask = mask;
this.extent = extent;
this.worldData = worldData;
this.clipboards = clipboards;
this.randomRotate = randomRotate;
}
@Override
public boolean spawn(PseudoRandom random, int x, int z) throws WorldEditException {
mutable.mutX(x);
mutable.mutZ(z);
int y = extent.getNearestSurfaceTerrainBlock(x, z, mutable.getBlockY(), 0, 255);
mutable.mutY(y);
if (!mask.test(mutable)) {
return false;
}
mutable.mutY(y + 1);
ClipboardHolder holder = clipboards[PseudoRandom.random.random(clipboards.length)];
if (randomRotate) {
holder.setTransform(new AffineTransform().rotateY(PseudoRandom.random.random(4) * 90));
}
Clipboard clipboard = holder.getClipboard();
Schematic schematic = new Schematic(clipboard);
if (holder.getTransform().isIdentity()) {
schematic.paste(extent, mutable, false);
} else {
schematic.paste(extent, worldData, mutable, false, holder.getTransform());
}
mutable.mutY(y);
return true;
}
}

View File

@ -295,6 +295,14 @@ public abstract class FaweChunk<T> implements Callable<FaweChunk> {
public abstract void setBiome(final int x, final int z, final byte biome);
public void setBiome(final byte biome) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
setBiome(x, z, biome);
}
}
}
/**
* Spend time now so that the chunk can be more efficiently dispatched later<br>
* - Modifications after this call will be ignored

View File

@ -146,7 +146,7 @@ public class HistoryExtent extends AbstractDelegateExtent {
}
}
private class TrackedEntity implements Entity {
public class TrackedEntity implements Entity {
private final Entity entity;
private TrackedEntity(final Entity entity) {

View File

@ -1,7 +1,5 @@
package com.boydti.fawe.object;
import java.util.Random;
public class PseudoRandom {
public static PseudoRandom random = new PseudoRandom();
@ -10,13 +8,16 @@ public class PseudoRandom {
public PseudoRandom() {
this.state = System.nanoTime();
new Random().nextDouble();
}
public PseudoRandom(final long state) {
this.state = state;
}
public void setSeed(long state) {
this.state = state;
}
public long nextLong() {
final long a = this.state;
this.state = this.xorShift64(a);
@ -45,4 +46,8 @@ public class PseudoRandom {
public int nextInt(int i) {
return random(i);
}
public int nextInt(int start, int end) {
return nextInt(end - start + 1) + start;
}
}

View File

@ -5,6 +5,8 @@ import com.sk89q.worldedit.Vector;
public class RegionWrapper {
public int minX;
public int maxX;
public int minY;
public int maxY;
public int minZ;
public int maxZ;
@ -13,10 +15,16 @@ public class RegionWrapper {
}
public RegionWrapper(final int minX, final int maxX, final int minZ, final int maxZ) {
this(minX, maxX, 0, 255, minZ, maxZ);
}
public RegionWrapper(final int minX, final int maxX, final int minY, final int maxY, final int minZ, final int maxZ) {
this.maxX = maxX;
this.minX = minX;
this.maxZ = maxZ;
this.minZ = minZ;
this.minY = minY;
this.maxY = Math.min(255, maxY);
}
public RegionWrapper(final Vector pos1, final Vector pos2) {
@ -24,12 +32,35 @@ public class RegionWrapper {
this.minZ = Math.min(pos1.getBlockZ(), pos2.getBlockZ());
this.maxX = Math.max(pos1.getBlockX(), pos2.getBlockX());
this.maxZ = Math.max(pos1.getBlockZ(), pos2.getBlockZ());
this.minY = Math.min(pos1.getBlockY(), pos2.getBlockY());
this.maxY = Math.max(pos1.getBlockY(), pos2.getBlockY());
}
public RegionWrapper[] toArray() {
return new RegionWrapper[]{this};
}
private int ly = Integer.MIN_VALUE;
private int lz = Integer.MIN_VALUE;
private boolean lr, lry, lrz;
public boolean isIn(int x, int y, int z) {
if (z != lz) {
lz = z;
lrz = z >= this.minZ && z <= this.maxZ;
if (y != ly) {
ly = y;
lry = y >= this.minY && y <= this.maxY;
}
lr = lrz && lry;
} else if (y != ly) {
ly = y;
lry = y >= this.minY && y <= this.maxY;
lr = lrz && lry;
}
return lr && (x >= this.minX && x <= this.maxX);
}
public boolean isIn(final int x, final int z) {
return ((x >= this.minX) && (x <= this.maxX) && (z >= this.minZ) && (z <= this.maxZ));
}
@ -91,7 +122,7 @@ public class RegionWrapper {
}
public boolean isGlobal() {
return minX == Integer.MIN_VALUE && minZ == Integer.MIN_VALUE && maxX == Integer.MAX_VALUE && maxZ == Integer.MAX_VALUE;
return minX == Integer.MIN_VALUE && minZ == Integer.MIN_VALUE && maxX == Integer.MAX_VALUE && maxZ == Integer.MAX_VALUE && minY <= 0 && maxY >= 255;
}
public boolean contains(RegionWrapper current) {

View File

@ -0,0 +1,85 @@
package com.boydti.fawe.object.brush;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.collection.BlockVectorSet;
import com.boydti.fawe.object.mask.AdjacentAnyMask;
import com.boydti.fawe.object.mask.RadiusMask;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.command.tool.brush.Brush;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.SolidBlockMask;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
import java.util.Arrays;
public class LayerBrush implements Brush {
private final BaseBlock[] layers;
private RecursiveVisitor visitor;
private MutableBlockVector mutable = new MutableBlockVector();
public LayerBrush(BaseBlock[] layers) {
this.layers = layers;
}
@Override
public void build(EditSession editSession, Vector position, Pattern ignore, double size) throws MaxChangedBlocksException {
final FaweQueue queue = editSession.getQueue();
final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, Arrays.asList(new BaseBlock(0)));
final SolidBlockMask solid = new SolidBlockMask(editSession);
final RadiusMask radius = new RadiusMask(0, (int) size);
visitor = new RecursiveVisitor(vector -> solid.test(vector) && radius.test(vector) && adjacent.test(vector), function -> true);
visitor.visit(position);
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
Operations.completeBlindly(visitor);
BlockVectorSet visited = visitor.getVisited();
BaseBlock firstPattern = layers[0];
visitor = new RecursiveVisitor(new Mask() {
@Override
public boolean test(Vector pos) {
int depth = visitor.getDepth() + 1;
if (depth > 1) {
boolean found = false;
int previous = layers[depth - 1].getCombined();
int previous2 = layers[depth - 2].getCombined();
for (Vector dir : BreadthFirstSearch.DEFAULT_DIRECTIONS) {
mutable.setComponents(pos.getBlockX() + dir.getBlockX(), pos.getBlockY() + dir.getBlockY(), pos.getBlockZ() + dir.getBlockZ());
if (visitor.isVisited(mutable) && queue.getCachedCombinedId4Data(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous) {
mutable.setComponents(pos.getBlockX() + dir.getBlockX() * 2, pos.getBlockY() + dir.getBlockY() * 2, pos.getBlockZ() + dir.getBlockZ() * 2);
if (visitor.isVisited(mutable) && queue.getCachedCombinedId4Data(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous2) {
found = true;
break;
} else {
return false;
}
}
}
if (!found) {
return false;
}
}
return !adjacent.test(pos);
}
}, new RegionFunction() {
@Override
public boolean apply(Vector pos) throws WorldEditException {
int depth = visitor.getDepth();
BaseBlock currentPattern = layers[depth];
return editSession.setBlock(pos, currentPattern);
}
}, layers.length - 1, editSession);
for (Vector pos : visited) {
visitor.visit(pos);
}
Operations.completeBlindly(visitor);
visitor = null;
}
}

View File

@ -0,0 +1,39 @@
package com.boydti.fawe.object.brush;
import com.boydti.fawe.jnbt.anvil.generator.SchemGen;
import com.boydti.fawe.util.MaskTraverser;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.command.tool.brush.Brush;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.session.ClipboardHolder;
public class PopulateSchem implements Brush {
private final Mask mask;
private final boolean randomRotate;
private final ClipboardHolder[] clipboards;
private final int rarity;
public PopulateSchem(Mask mask, ClipboardHolder[] clipboards, int rarity, boolean randomRotate) {
this.mask = mask;
this.clipboards = clipboards;
this.rarity = rarity;
this.randomRotate = randomRotate;
}
@Override
public void build(EditSession editSession, Vector position, Pattern pattern, double size) throws MaxChangedBlocksException {
new MaskTraverser(mask).reset(editSession);
SchemGen gen = new SchemGen(mask, editSession, editSession.getWorldData(), clipboards, randomRotate);
CuboidRegion cuboid = new CuboidRegion(position.subtract(size, size, size), position.add(size, size, size));
try {
editSession.addSchems(cuboid, mask, editSession.getWorldData(), clipboards, rarity, randomRotate);
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -50,7 +50,7 @@ public class SplatterBrush extends ScatterBrush {
@Override
public boolean test(Vector vector) {
double dist = vector.distanceSq(position);
if (!placed.contains(vector) && (PseudoRandom.random.random(5) < 2) && solid.test(vector) && adjacent.test(vector)) {
if (dist < size2 && !placed.contains(vector) && (PseudoRandom.random.random(5) < 2) && solid.test(vector) && adjacent.test(vector)) {
placed.add(vector);
return true;
}

View File

@ -7,7 +7,6 @@ import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
@ -40,9 +39,8 @@ public class StencilBrush extends HeightBrush {
final HeightMap map = getHeightMap();
map.setSize(size);
int cutoff = onlyWhite ? maxY : 0;
final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, Arrays.asList(new BaseBlock(0)));
final SolidBlockMask solid = new SolidBlockMask(editSession);
final AdjacentAnyMask adjacent = new AdjacentAnyMask(editSession, solid.getInverseBlocks());
RegionMask region = new RegionMask(new CuboidRegion(position.subtract(size, size, size), position.add(size, size, size)));
RecursiveVisitor visitor = new RecursiveVisitor(new Mask() {
@Override

View File

@ -312,6 +312,8 @@ public class LocalBlockVectorSet implements Set<Vector> {
@Override
public void clear() {
offsetZ = Integer.MAX_VALUE;
offsetX = Integer.MAX_VALUE;
set.clear();
}
}

View File

@ -10,7 +10,6 @@ import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
@ -105,9 +104,6 @@ public class FastWorldEditExtent extends AbstractDelegateExtent implements HasFa
@Override
public BaseBlock getLazyBlock(int x, int y, int z) {
if (y > maxY || y < 0) {
return EditSession.nullBlock;
}
int combinedId4Data = queue.getCombinedId4Data(x, y, z, 0);
int id = FaweCache.getId(combinedId4Data);
if (!FaweCache.hasNBT(id)) {
@ -149,9 +145,6 @@ public class FastWorldEditExtent extends AbstractDelegateExtent implements HasFa
@Override
public boolean setBlock(int x, int y, int z, final BaseBlock block) throws WorldEditException {
if (y > maxY || y < 0) {
return false;
}
final short id = (short) block.getId();
switch (id) {
case 63:

View File

@ -1,21 +1,200 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.util.WEManager;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.Collection;
import javax.annotation.Nullable;
public abstract class FaweRegionExtent extends AbstractDelegateExtent {
private final FaweLimit limit;
/**
* Create a new instance.
*
* @param extent the extent
*/
public FaweRegionExtent(Extent extent) {
public FaweRegionExtent(Extent extent, FaweLimit limit) {
super(extent);
this.limit = limit;
}
public abstract boolean contains(int x, int y, int z);
public abstract boolean contains(int x, int z);
public abstract Collection<RegionWrapper> getRegions();
public boolean isGlobal() {
for (RegionWrapper region : getRegions()) {
if (region.isGlobal()) {
return true;
}
}
return false;
}
public final boolean contains(Vector p) {
return contains(p.getBlockX(), p.getBlockY(), p.getBlockZ());
}
public final boolean contains(Vector2D p) {
return contains(p.getBlockX(), p.getBlockZ());
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
if (!contains(location)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return false;
}
return super.setBlock(location, block);
}
@Override
public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException {
if (!contains(x, y, z)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return false;
}
return super.setBlock(x, y, z, block);
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
if (!contains(position)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return false;
}
return super.setBiome(position, biome);
}
@Override
public BaseBiome getBiome(Vector2D position) {
if (!contains(position)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return EditSession.nullBiome;
}
return super.getBiome(position);
}
@Override
public BaseBlock getBlock(Vector position) {
if (!contains(position)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return EditSession.nullBlock;
}
return super.getBlock(position);
}
@Override
public BaseBlock getLazyBlock(Vector position) {
if (!contains(position)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return EditSession.nullBlock;
}
return super.getLazyBlock(position);
}
@Override
public BaseBlock getLazyBlock(int x, int y, int z) {
if (!contains(x, y, z)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return EditSession.nullBlock;
}
return super.getLazyBlock(x, y, z);
}
@Override
public int getBlockLight(int x, int y, int z) {
if (!contains(x, y, z)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return 0;
}
return super.getBlockLight(x, y, z);
}
@Override
public int getBrightness(int x, int y, int z) {
if (!contains(x, y, z)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return 0;
}
return super.getBrightness(x, y, z);
}
@Override
public int getLight(int x, int y, int z) {
if (!contains(x, y, z)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return 0;
}
return super.getLight(x, y, z);
}
@Override
public int getOpacity(int x, int y, int z) {
if (!contains(x, y, z)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return 0;
}
return super.getOpacity(x, y, z);
}
@Override
public int getSkyLight(int x, int y, int z) {
if (!contains(x, y, z)) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return 0;
}
return super.getSkyLight(x, y, z);
}
@Nullable
@Override
public Entity createEntity(Location location, BaseEntity entity) {
if (!contains(location.getBlockX(), location.getBlockY(), location.getBlockZ())) {
if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
}
return null;
}
return super.createEntity(location, entity);
}
}

View File

@ -0,0 +1,39 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RegionWrapper;
import com.sk89q.worldedit.extent.Extent;
import java.util.Arrays;
import java.util.Collection;
public class HeightBoundExtent extends FaweRegionExtent {
private final int min, max;
private int lastY;
private boolean lastResult;
public HeightBoundExtent(Extent extent, FaweLimit limit, int min, int max) {
super(extent, limit);
this.min = min;
this.max = max;
}
@Override
public boolean contains(int x, int z) {
return true;
}
@Override
public boolean contains(int x, int y, int z) {
if (y == lastY) {
return lastResult;
}
lastY = y;
return lastResult = (y >= min && y <= max);
}
@Override
public Collection<RegionWrapper> getRegions() {
return Arrays.asList(new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, min, max, Integer.MIN_VALUE, Integer.MAX_VALUE));
}
}

View File

@ -0,0 +1,67 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RegionWrapper;
import com.sk89q.worldedit.extent.Extent;
import java.util.Arrays;
import java.util.Collection;
public class MultiRegionExtent extends FaweRegionExtent {
private RegionWrapper region;
private final RegionWrapper[] regions;
private int index;
/**
* Create a new instance.
*
* @param extent the extent
*/
public MultiRegionExtent(Extent extent, FaweLimit limit, RegionWrapper[]regions) {
super(extent, limit);
this.index = 0;
this.region = regions[0];
this.regions = regions;
}
@Override
public boolean contains(int x, int y, int z) {
if (region.isIn(x, y, z)) {
return true;
}
for (int i = 0; i < regions.length; i++) {
if (i != index) {
RegionWrapper current = regions[i];
if (current.isIn(x, y, z)) {
region = current;
index = i;
return true;
}
}
}
return false;
}
@Override
public boolean contains(int x, int z) {
if (region.isIn(x, z)) {
return true;
}
for (int i = 0; i < regions.length; i++) {
if (i != index) {
RegionWrapper current = regions[i];
if (current.isIn(x, z)) {
region = current;
index = i;
return true;
}
}
}
return false;
}
@Override
public Collection<RegionWrapper> getRegions() {
return Arrays.asList(regions);
}
}

View File

@ -1,6 +1,7 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.worldedit.Vector;
@ -29,7 +30,7 @@ public class NullExtent extends FaweRegionExtent {
* @param extent the extent
*/
public NullExtent(Extent extent, BBC failReason) {
super(extent);
super(extent, FaweLimit.MAX);
this.reason = failReason;
}
@ -94,7 +95,14 @@ public class NullExtent extends FaweRegionExtent {
}
@Override
public boolean contains(int x, int y, int z) { return false; }
public boolean contains(int x, int z) {
throw new FaweException(reason);
}
@Override
public boolean contains(int x, int y, int z) {
throw new FaweException(reason);
}
@Override
public Collection<RegionWrapper> getRegions() {

View File

@ -1,10 +1,9 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.util.WEManager;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
@ -17,27 +16,18 @@ import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class ProcessedWEExtent extends FaweRegionExtent {
public class ProcessedWEExtent extends AbstractDelegateExtent {
private final FaweLimit limit;
private final RegionWrapper[] mask;
private final AbstractDelegateExtent extent;
public ProcessedWEExtent(final Extent parent, final RegionWrapper[] mask, FaweLimit limit) {
public ProcessedWEExtent(final Extent parent, FaweLimit limit) {
super(parent);
this.mask = mask;
this.limit = limit;
this.extent = (AbstractDelegateExtent) parent;
}
@Override
public Collection<RegionWrapper> getRegions() {
return Arrays.asList(mask);
}
public void setLimit(FaweLimit other) {
this.limit.set(other);
}
@ -47,16 +37,11 @@ public class ProcessedWEExtent extends FaweRegionExtent {
if (entity == null) {
return null;
}
if (WEManager.IMP.maskContains(this.mask, location.getBlockX(), location.getBlockZ())) {
if (!limit.MAX_ENTITIES()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_ENTITIES);
return null;
}
return super.createEntity(location, entity);
} else if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
if (!limit.MAX_ENTITIES()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_ENTITIES);
return null;
}
return null;
return super.createEntity(location, entity);
}
@Override
@ -74,23 +59,13 @@ public class ProcessedWEExtent extends FaweRegionExtent {
return super.getEntities(region);
}
int count = 0;
@Override
public BaseBlock getLazyBlock(int x, int y, int z) {
count++;
if (WEManager.IMP.maskContains(this.mask, x, z)) {
if (!limit.MAX_CHECKS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
return EditSession.nullBlock;
} else {
return extent.getLazyBlock(x, y, z);
}
} else if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
if (!limit.MAX_CHECKS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
return EditSession.nullBlock;
} else {
return EditSession.nullBlock;
return extent.getLazyBlock(x, y, z);
}
}
@ -106,56 +81,33 @@ public class ProcessedWEExtent extends FaweRegionExtent {
@Override
public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException {
if (block.hasNbtData() && FaweCache.hasNBT(block.getType())) {
CompoundTag nbt = block.getNbtData();
if (nbt != null) {
if (!limit.MAX_BLOCKSTATES()) {
WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES);
return false;
} else if (WEManager.IMP.maskContains(this.mask, x, z)) {
} else {
if (!limit.MAX_CHANGES()) {
WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
return false;
}
return extent.setBlock(x, y, z, block);
} else if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
return false;
} else {
return false;
}
}
if (WEManager.IMP.maskContains(this.mask, x, z)) {
if (!limit.MAX_CHANGES()) {
WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
return false;
} else {
return extent.setBlock(x, y, z, block);
}
} else if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
if (!limit.MAX_CHANGES()) {
WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
return false;
} else {
return false;
return extent.setBlock(x, y, z, block);
}
}
@Override
public boolean setBiome(final Vector2D position, final BaseBiome biome) {
if (WEManager.IMP.maskContains(this.mask, position.getBlockX(), position.getBlockZ())) {
if (!limit.MAX_CHANGES()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
return false;
}
return super.setBiome(position, biome);
} else if (!limit.MAX_FAILS()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
return false;
} else {
if (!limit.MAX_CHANGES()) {
WEManager.IMP.cancelEditSafe(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
return false;
}
}
@Override
public boolean contains(int x, int y, int z) {
return WEManager.IMP.maskContains(this.mask, x, z);
return super.setBiome(position, biome);
}
}

View File

@ -0,0 +1,37 @@
package com.boydti.fawe.object.extent;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RegionWrapper;
import com.sk89q.worldedit.extent.Extent;
import java.util.Arrays;
import java.util.Collection;
public class SingleRegionExtent extends FaweRegionExtent{
private final RegionWrapper region;
/**
* Create a new instance.
*
* @param extent the extent
*/
public SingleRegionExtent(Extent extent, FaweLimit limit, RegionWrapper region) {
super(extent, limit);
this.region = region;
}
@Override
public boolean contains(int x, int y, int z) {
return region.isIn(x, y, z);
}
@Override
public boolean contains(int x, int z) {
return region.isIn(x, z);
}
@Override
public Collection<RegionWrapper> getRegions() {
return Arrays.asList(region);
}
}

View File

@ -141,6 +141,17 @@ public class BufferedRandomAccessFile extends RandomAccessFile
this.init(size);
}
public BufferedRandomAccessFile(File file, String mode, byte[] buf) throws FileNotFoundException
{
super(file, mode);
this.dirty_ = this.closed_ = false;
this.lo_ = this.curr_ = this.hi_ = 0;
this.buff_ = buf;
this.maxHi_ = (long) BuffSz_;
this.hitEOF_ = false;
this.diskPos_ = 0L;
}
private void init(int size)
{
this.dirty_ = this.closed_ = false;

View File

@ -1,9 +1,9 @@
package com.boydti.fawe.object.mask;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.mask.SolidBlockMask;
import javax.annotation.Nullable;
@ -15,16 +15,16 @@ public class AngleMask extends SolidBlockMask {
private final double max;
private final double min;
private final EditSession extent;
private final Extent extent;
private MutableBlockVector mutable = new MutableBlockVector();
private int maxY;
public AngleMask(EditSession editSession, double min, double max) {
super(editSession);
this.extent = editSession;
public AngleMask(Extent extent, double min, double max) {
super(extent);
this.extent = extent;
this.min = min;
this.max = max;
this.maxY = extent.getMaxY();
this.maxY = extent.getMaximumPoint().getBlockY();
}
@Override
@ -32,11 +32,11 @@ public class AngleMask extends SolidBlockMask {
int x = vector.getBlockX();
int y = vector.getBlockY();
int z = vector.getBlockZ();
BaseBlock block = extent.getBlock(x, y, z);
BaseBlock block = extent.getLazyBlock(x, y, z);
if (!test(block.getId(), block.getData())) {
return false;
}
block = extent.getBlock(x, y + 1, z);
block = extent.getLazyBlock(x, y + 1, z);
if (test(block.getId(), block.getData())) {
return false;
}

View File

@ -43,7 +43,7 @@ public class BiomePattern extends ExistingPattern {
}
@Override
public synchronized Throwable fillInStackTrace() {
public Throwable fillInStackTrace() {
return this;
}
}

View File

@ -1,4 +0,0 @@
package com.boydti.fawe.object.player;
public class DefaultFawePlayer {
}

View File

@ -0,0 +1,337 @@
/*
* 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.boydti.fawe.object.regions;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.regions.AbstractRegion;
import com.sk89q.worldedit.regions.RegionOperationException;
import com.sk89q.worldedit.regions.polyhedron.Edge;
import com.sk89q.worldedit.world.World;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
public class PolyhedralRegion extends AbstractRegion {
/**
* Vertices that are contained in the convex hull.
*/
private final Set<Vector> vertices = new LinkedHashSet<Vector>();
/**
* Triangles that form the convex hull.
*/
private final List<Triangle> triangles = new ArrayList<Triangle>();
/**
* Vertices that are coplanar to the first 3 vertices.
*/
private final Set<Vector> vertexBacklog = new LinkedHashSet<Vector>();
/**
* Minimum point of the axis-aligned bounding box.
*/
private Vector minimumPoint;
/**
* Maximum point of the axis-aligned bounding box.
*/
private Vector maximumPoint;
/**
* Accumulator for the barycenter of the polyhedron. Divide by vertices.size() to get the actual center.
*/
private Vector centerAccum = Vector.ZERO;
/**
* The last triangle that caused a {@link #contains(Vector)} to classify a point as "outside". Used for optimization.
*/
private Triangle lastTriangle;
/**
* Constructs an empty mesh, containing no vertices or triangles.
*
* @param world the world
*/
public PolyhedralRegion(@Nullable World world) {
super(world);
}
/**
* @deprecated cast {@code world} to {@link World}
*/
@Deprecated
public PolyhedralRegion(LocalWorld world) {
super(world);
}
/**
* Constructs an independent copy of the given region.
*
* @param region the region to copy
*/
public PolyhedralRegion(PolyhedralRegion region) {
this(region.world);
vertices.addAll(region.vertices);
triangles.addAll(region.triangles);
vertexBacklog.addAll(region.vertexBacklog);
minimumPoint = region.minimumPoint;
maximumPoint = region.maximumPoint;
centerAccum = region.centerAccum;
lastTriangle = region.lastTriangle;
}
/**
* Clears the region, removing all vertices and triangles.
*/
public void clear() {
vertices.clear();
triangles.clear();
vertexBacklog.clear();
minimumPoint = null;
maximumPoint = null;
centerAccum = Vector.ZERO;
lastTriangle = null;
}
/**
* Add a vertex to the region.
*
* @param vertex the vertex
* @return true, if something changed.
*/
public boolean addVertex(Vector vertex) {
checkNotNull(vertex);
lastTriangle = null; // Probably not necessary
if (vertices.contains(vertex)) {
return false;
}
if (vertices.size() == 3) {
if (vertexBacklog.contains(vertex)) {
return false;
}
if (containsRaw(vertex)) {
return vertexBacklog.add(vertex);
}
}
vertices.add(vertex);
centerAccum = centerAccum.add(vertex);
if (minimumPoint == null) {
minimumPoint = maximumPoint = vertex;
} else {
minimumPoint = new MutableBlockVector(Vector.getMinimum(minimumPoint, vertex));
maximumPoint = new MutableBlockVector(Vector.getMaximum(maximumPoint, vertex));
}
int size = vertices.size();
switch (size) {
case 0:
case 1:
case 2:
// Incomplete, can't make a mesh yet
return true;
case 3:
// Generate minimal mesh to start from
final Vector[] v = vertices.toArray(new Vector[vertices.size()]);
triangles.add((new Triangle(v[0], v[size - 2], v[size - 1])));
triangles.add((new Triangle(v[0], v[size - 1], v[size - 2])));
return true;
}
final Set<Edge> borderEdges = new LinkedHashSet<Edge>();
for (Iterator<Triangle> it = triangles.iterator(); it.hasNext(); ) {
final Triangle triangle = it.next();
// If the triangle can't be seen, it's not relevant
if (!triangle.above(vertex)) {
continue;
}
// Remove the triangle from the mesh
it.remove();
// ...and remember its edges
for (int i = 0; i < 3; ++i) {
final Edge edge = triangle.getEdge(i);
if (borderEdges.remove(edge)) {
continue;
}
borderEdges.add(edge);
}
}
// Add triangles between the remembered edges and the new vertex.
for (Edge edge : borderEdges) {
com.sk89q.worldedit.regions.polyhedron.Triangle triangle = edge.createTriangle(vertex);
Triangle fTria = new Triangle(triangle.getVertex(0), triangle.getVertex(1), triangle.getVertex(2));
triangles.add(fTria);
}
if (!vertexBacklog.isEmpty()) {
// Remove the new vertex
vertices.remove(vertex);
// Clone, clear and work through the backlog
final List<Vector> vertexBacklog2 = new ArrayList<Vector>(vertexBacklog);
vertexBacklog.clear();
for (Vector vertex2 : vertexBacklog2) {
addVertex(vertex2);
}
// Re-add the new vertex after the backlog.
vertices.add(vertex);
}
return true;
}
public boolean isDefined() {
return !triangles.isEmpty();
}
@Override
public Vector getMinimumPoint() {
return minimumPoint;
}
@Override
public Vector getMaximumPoint() {
return maximumPoint;
}
@Override
public Vector getCenter() {
return centerAccum.divide(vertices.size());
}
@Override
public void expand(Vector... changes) throws RegionOperationException {
}
@Override
public void contract(Vector... changes) throws RegionOperationException {
}
@Override
public void shift(Vector change) throws RegionOperationException {
shiftCollection(vertices, change);
shiftCollection(vertexBacklog, change);
for (int i = 0; i < triangles.size(); ++i) {
final Triangle triangle = triangles.get(i);
final Vector v0 = change.add(triangle.getVertex(0));
final Vector v1 = change.add(triangle.getVertex(1));
final Vector v2 = change.add(triangle.getVertex(2));
triangles.set(i, new Triangle(v0, v1, v2));
}
minimumPoint = change.add(minimumPoint);
maximumPoint = change.add(maximumPoint);
centerAccum = change.multiply(vertices.size()).add(centerAccum);
lastTriangle = null;
}
private static void shiftCollection(Collection<Vector> collection, Vector change) {
final List<Vector> tmp = new ArrayList<Vector>(collection);
collection.clear();
for (Vector vertex : tmp) {
collection.add(change.add(vertex));
}
}
@Override
public boolean contains(Vector position) {
if (!isDefined()) {
return false;
}
final int x = position.getBlockX();
final int y = position.getBlockY();
final int z = position.getBlockZ();
final Vector min = getMinimumPoint();
final Vector max = getMaximumPoint();
if (x < min.getBlockX()) return false;
if (x > max.getBlockX()) return false;
if (z < min.getBlockZ()) return false;
if (z > max.getBlockZ()) return false;
if (y < min.getBlockY()) return false;
if (y > max.getBlockY()) return false;
return containsRaw(position);
}
private boolean containsRaw(Vector pt) {
if (lastTriangle != null && lastTriangle.contains(pt)) {
return true;
}
for (Triangle triangle : triangles) {
if (lastTriangle == triangle) {
continue;
}
if (triangle.contains(pt)) {
lastTriangle = triangle;
return true;
}
}
return false;
}
public Collection<Vector> getVertices() {
if (vertexBacklog.isEmpty()) {
return vertices;
}
final List<Vector> ret = new ArrayList<Vector>(vertices);
ret.addAll(vertexBacklog);
return ret;
}
public Collection<Triangle> getTriangles() {
return triangles;
}
@Override
public AbstractRegion clone() {
return new PolyhedralRegion(this);
}
}

View File

@ -0,0 +1,246 @@
package com.boydti.fawe.object.regions;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.StringMan;
import com.google.common.base.Preconditions;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.regions.polyhedron.Edge;
public class Triangle {
public static double RADIUS = 0.5;
private final double[][] verts = new double[3][3];
private final double[] center = new double[3];
private final double[] radius = new double[3];
private final double[] v0 = new double[3];
private final double[] v1 = new double[3];
private final double[] v2 = new double[3];
private final double[] normal = new double[3];
private final double[] e0 = new double[3];
private final double[] e1 = new double[3];
private final double[] e2 = new double[3];
private final double[] vmin = new double[3];
private final double[] vmax = new double[3];
private final Vector normalVec;
private final double b;
public Triangle(Vector pos1, Vector pos2, Vector pos3) {
verts[0] = new double[]{pos1.getBlockX(), pos1.getBlockY(), pos1.getBlockZ()};
verts[1] = new double[]{pos2.getBlockX(), pos2.getBlockY(), pos2.getBlockZ()};
verts[2] = new double[]{pos3.getBlockX(), pos3.getBlockY(), pos3.getBlockZ()};
radius[0] = RADIUS;
radius[1] = RADIUS;
radius[2] = RADIUS;
this.normalVec = pos2.subtract(pos1).cross(pos3.subtract(pos1)).normalize();
this.b = Math.max(Math.max(this.normalVec.dot(pos1), this.normalVec.dot(pos2)), this.normalVec.dot(pos3));
}
public boolean above(Vector pt) {
Preconditions.checkNotNull(pt);
return this.normalVec.dot(pt) > this.b;
}
public Edge getEdge(int index) {
if (index == this.verts.length - 1) {
return new Edge(new Vector(this.verts[index]), new Vector(this.verts[0]));
} else {
return new Edge(new Vector(this.verts[index]), new Vector(this.verts[index + 1]));
}
}
@Override
public String toString() {
return StringMan.getString(verts);
}
public Vector getVertex(int index) {
return new Vector(verts[index]);
}
public boolean contains(Vector pos) {
center[0] = pos.getBlockX() + RADIUS;
center[1] = pos.getBlockY() + RADIUS;
center[2] = pos.getBlockZ() + RADIUS;
return overlaps(center, radius, verts);
}
private void sub(double[] dest, double[] v1, double[] v2) {
dest[0] = v1[0] - v2[0];
dest[1] = v1[1] - v2[1];
dest[2] = v1[2] - v2[2];
}
private void cross(double[] dest, double[] v1, double[] v2) {
dest[0] = v1[1] * v2[2] - v1[2] * v2[1];
dest[1] = v1[2] * v2[0] - v1[0] * v2[2];
dest[2] = v1[0] * v2[1] - v1[1] * v2[0];
}
private double dot(double[] v1, double[] v2) {
return (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]);
}
private boolean axisTestX01(double a, double b, double fa, double fb) {
double p0 = a * v0[1] - b * v0[2];
double p2 = a * v2[1] - b * v2[2];
double min, max;
if (p0 < p2) {
min = p0;
max = p2;
} else {
min = p2;
max = p0;
}
double rad = fa * radius[1] + fb * radius[2];
return !(min > rad || max < -rad);
}
private boolean axisTestX2(double a, double b, double fa, double fb) {
double p0 = a * v0[1] - b * v0[2];
double p1 = a * v1[1] - b * v1[2];
double min, max;
if (p0 < p1) {
min = p0;
max = p1;
} else {
min = p1;
max = p0;
}
double rad = fa * radius[1] + fb * radius[2];
return !(min > rad || max < -rad);
}
private boolean axisTestY02(double a, double b, double fa, double fb) {
double p0 = -a * v0[0] + b * v0[2];
double p2 = -a * v2[0] + b * v2[2];
double min, max;
if (p0 < p2) {
min = p0;
max = p2;
} else {
min = p2;
max = p0;
}
double rad = fa * radius[0] + fb * radius[2];
return !(min > rad || max < -rad);
}
private boolean axisTestY1(double a, double b, double fa, double fb) {
double p0 = -a * v0[0] + b * v0[2];
double p1 = -a * v1[0] + b * v1[2];
double min, max;
if (p0 < p1) {
min = p0;
max = p1;
} else {
min = p1;
max = p0;
}
double rad = fa * radius[0] + fb * radius[2];
return !(min > rad || max < -rad);
}
private boolean axisTestZ12(double a, double b, double fa, double fb) {
double p1 = a * v1[0] - b * v1[1];
double p2 = a * v2[0] - b * v2[1];
double min, max;
if (p2 < p1) {
min = p2;
max = p1;
} else {
min = p1;
max = p2;
}
double rad = fa * radius[0] + fb * radius[1];
return !(min > rad || max < -rad);
}
private boolean axisTestZ0(double a, double b, double fa, double fb) {
double p0 = a * v0[0] - b * v0[1];
double p1 = a * v1[0] - b * v1[1];
double min, max;
if (p0 < p1) {
min = p0;
max = p1;
} else {
min = p1;
max = p0;
}
double rad = fa * radius[0] + fb * radius[1];
return !(min > rad || max < -rad);
}
private boolean overlaps(double boxcenter[], double boxhalfsize[], double triverts[][]) {
double min, max, p0, p1, p2, rad, fex, fey, fez;
sub(v0, triverts[0], boxcenter);
sub(v1, triverts[1], boxcenter);
sub(v2, triverts[2], boxcenter);
sub(e0, v1, v0); /* tri edge 0 */
sub(e1, v2, v1); /* tri edge 1 */
sub(e2, v0, v2); /* tri edge 2 */
fex = Math.abs(e0[0]);
fey = Math.abs(e0[1]);
fez = Math.abs(e0[2]);
if (!axisTestX01(e0[2], e0[1], fez, fey)) return false;
if (!axisTestY02(e0[2], e0[0], fez, fex)) return false;
if (!axisTestZ12(e0[1], e0[0], fey, fex)) return false;
fex = Math.abs(e1[0]);
fey = Math.abs(e1[1]);
fez = Math.abs(e1[2]);
if (!axisTestX01(e1[2], e1[1], fez, fey)) return false;
if (!axisTestY02(e1[2], e1[0], fez, fex)) return false;
if (!axisTestZ0(e1[1], e1[0], fey, fex)) return false;
fex = Math.abs(e2[0]);
fey = Math.abs(e2[1]);
fez = Math.abs(e2[2]);
if (!axisTestX2(e2[2], e2[1], fez, fey)) return false;
if (!axisTestY1(e2[2], e2[0], fez, fex)) return false;
if (!axisTestZ12(e2[1], e2[0], fey, fex)) return false;
max = MathMan.max(v0[0], v1[0], v2[0]);
min = MathMan.min(v0[0], v1[0], v2[0]);
if (min > boxhalfsize[0] || max < -boxhalfsize[0]) return false;
max = MathMan.max(v0[1], v1[1], v2[1]);
min = MathMan.min(v0[1], v1[1], v2[1]);
if (min > boxhalfsize[1] || max < -boxhalfsize[1]) return false;
max = MathMan.max(v0[2], v1[2], v2[2]);
min = MathMan.min(v0[2], v1[2], v2[2]);
if (min > boxhalfsize[2] || max < -boxhalfsize[2]) return false;
cross(normal, e0, e1);
return (planeBoxOverlap(normal, v0, boxhalfsize));
}
private boolean planeBoxOverlap(double normal[], double vert[], double maxbox[]) {
for (int q = 0; q <= 2; q++) {
double v = vert[q];
if (normal[q] > 0.0f) {
vmin[q] = -maxbox[q] - v;
vmax[q] = maxbox[q] - v;
} else {
vmin[q] = maxbox[q] - v;
vmax[q] = -maxbox[q] - v;
}
}
if (dot(normal, vmin) > 0.0f) return false;
if (dot(normal, vmax) >= 0.0f) return true;
return false;
}
}

View File

@ -0,0 +1,237 @@
/*
* 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.boydti.fawe.object.regions.selector;
import com.boydti.fawe.object.regions.PolyhedralRegion;
import com.boydti.fawe.object.regions.Triangle;
import com.google.common.base.Optional;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.cui.CUIRegion;
import com.sk89q.worldedit.internal.cui.SelectionPointEvent;
import com.sk89q.worldedit.internal.cui.SelectionPolygonEvent;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
import com.sk89q.worldedit.world.World;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Creates a {@code PolyhedralRegion} from a user's selections.
*/
public class PolyhedralRegionSelector implements RegionSelector, CUIRegion {
private final transient PolyhedralRegion region;
private transient BlockVector pos1;
/**
* Create a new selector with a {@code null} world.
*/
public PolyhedralRegionSelector() {
this((World) null);
}
/**
* Create a new selector.
*
* @param world the world, which may be {@code null}
*/
public PolyhedralRegionSelector(@Nullable World world) {
region = new PolyhedralRegion(world);
}
@Nullable
@Override
public World getWorld() {
return region.getWorld();
}
@Override
public void setWorld(@Nullable World world) {
region.setWorld(world);
}
@Override
public boolean selectPrimary(Vector position, SelectorLimits limits) {
checkNotNull(position);
clear();
pos1 = position.toBlockVector();
return region.addVertex(position);
}
@Override
public boolean selectSecondary(Vector position, SelectorLimits limits) {
checkNotNull(position);
Optional<Integer> vertexLimit = limits.getPolyhedronVertexLimit();
if (vertexLimit.isPresent() && region.getVertices().size() > vertexLimit.get()) {
return false;
}
return region.addVertex(position);
}
@Override
public BlockVector getPrimaryPosition() throws IncompleteRegionException {
return pos1;
}
@Override
public Region getRegion() throws IncompleteRegionException {
if (!region.isDefined()) {
throw new IncompleteRegionException();
}
return region;
}
@Override
public Region getIncompleteRegion() {
return region;
}
@Override
public boolean isDefined() {
return region.isDefined();
}
@Override
public int getArea() {
return region.getArea();
}
@Override
public void learnChanges() {
pos1 = region.getVertices().iterator().next().toBlockVector();
}
@Override
public void clear() {
region.clear();
}
@Override
public String getTypeName() {
return "Polyhedron";
}
@Override
public List<String> getInformationLines() {
List<String> ret = new ArrayList<String>();
ret.add("Vertices: "+region.getVertices().size());
ret.add("Triangles: "+region.getTriangles().size());
return ret;
}
@Override
public void explainPrimarySelection(Actor player, LocalSession session, Vector pos) {
checkNotNull(player);
checkNotNull(session);
checkNotNull(pos);
session.describeCUI(player);
player.print("Started new selection with vertex "+pos+".");
}
@Override
public void explainSecondarySelection(Actor player, LocalSession session, Vector pos) {
checkNotNull(player);
checkNotNull(session);
checkNotNull(pos);
session.describeCUI(player);
player.print("Added vertex " + pos + " to the selection.");
}
@Override
public void explainRegionAdjust(Actor player, LocalSession session) {
checkNotNull(player);
checkNotNull(session);
session.describeCUI(player);
}
@Override
public int getProtocolVersion() {
return 3;
}
@Override
public String getTypeID() {
return "polyhedron";
}
@Override
public void describeCUI(LocalSession session, Actor player) {
checkNotNull(player);
checkNotNull(session);
Collection<Vector> vertices = region.getVertices();
Collection<Triangle> triangles = region.getTriangles();
Map<Vector, Integer> vertexIds = new HashMap<Vector, Integer>(vertices.size());
int lastVertexId = -1;
for (Vector vertex : vertices) {
vertexIds.put(vertex, ++lastVertexId);
session.dispatchCUIEvent(player, new SelectionPointEvent(lastVertexId, vertex, getArea()));
}
for (Triangle triangle : triangles) {
final int[] v = new int[3];
for (int i = 0; i < 3; ++i) {
v[i] = vertexIds.get(triangle.getVertex(i));
}
session.dispatchCUIEvent(player, new SelectionPolygonEvent(v));
}
}
@Override
public String getLegacyTypeID() {
return "cuboid";
}
@Override
public void describeLegacyCUI(LocalSession session, Actor player) {
checkNotNull(player);
checkNotNull(session);
if (isDefined()) {
session.dispatchCUIEvent(player, new SelectionPointEvent(0, region.getMinimumPoint(), getArea()));
session.dispatchCUIEvent(player, new SelectionPointEvent(1, region.getMaximumPoint(), getArea()));
}
}
}

View File

@ -30,7 +30,7 @@ public class FaweMask {
public HashSet<RegionWrapper> getRegions() {
final BlockVector lower = this.getLowerBound();
final BlockVector upper = this.getUpperBound();
return new HashSet<>(Arrays.asList(new RegionWrapper(lower.getBlockX(), upper.getBlockX(), lower.getBlockZ(), upper.getBlockZ())));
return new HashSet<>(Arrays.asList(new RegionWrapper(lower.getBlockX(), upper.getBlockX(), lower.getBlockY(), upper.getBlockY(), lower.getBlockZ(), upper.getBlockZ())));
}
public String getName() {

View File

@ -0,0 +1,362 @@
package com.boydti.fawe.regions.general.plot;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.anvil.HeightMapMCAGenerator;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager;
import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.commands.Auto;
import com.intellectualcrafters.plot.commands.CommandCategory;
import com.intellectualcrafters.plot.commands.MainCommand;
import com.intellectualcrafters.plot.commands.RequiredType;
import com.intellectualcrafters.plot.config.C;
import com.intellectualcrafters.plot.config.Settings;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotId;
import com.intellectualcrafters.plot.object.PlotPlayer;
import com.intellectualcrafters.plot.object.RunnableVal2;
import com.intellectualcrafters.plot.object.RunnableVal3;
import com.intellectualcrafters.plot.object.worlds.PlotAreaManager;
import com.intellectualcrafters.plot.object.worlds.SinglePlotArea;
import com.intellectualcrafters.plot.object.worlds.SinglePlotAreaManager;
import com.intellectualcrafters.plot.util.MainUtil;
import com.plotsquared.general.commands.Command;
import com.plotsquared.general.commands.CommandDeclaration;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.biome.Biomes;
import com.sk89q.worldedit.world.registry.BiomeRegistry;
import com.sk89q.worldedit.world.registry.WorldData;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import javax.imageio.ImageIO;
@CommandDeclaration(
command = "cfi",
permission = "plots.createfromimage",
aliases = {"createfromheightmap", "createfromimage", "cfhm"},
category = CommandCategory.APPEARANCE,
requiredType = RequiredType.NONE,
description = "Generate a world from an image",
usage = "/plots cfi [url]"
)
public class CreateFromImage extends Command {
private final WorldEdit we;
public CreateFromImage() {
super(MainCommand.getInstance(), true);
this.we = WorldEdit.getInstance();
}
@Override
public void execute(final PlotPlayer player, String[] args, RunnableVal3<Command, Runnable, Runnable> confirm, RunnableVal2<Command, CommandResult> whenDone) throws CommandException {
List<String> argList = StringMan.split(StringMan.join(args, " "), ' ');
checkTrue(argList.size() >= 1, C.COMMAND_SYNTAX, getUsage());
PlotAreaManager manager = PS.get().getPlotAreaManager();
if (manager instanceof SinglePlotAreaManager) {
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
FawePlayer<Object> fp = FawePlayer.wrap(player.getName());
if (argList.get(0).toLowerCase().startsWith("http")) {
final BufferedImage image;
try {
URL url = new URL(argList.get(0));
if (!url.getHost().equals("i.imgur.com")) {
player.sendMessage("Images can only be loaded from i.imgur.com");
return;
}
player.sendMessage(BBC.getPrefix() + "Loading image... (1)");
image = ImageIO.read(url);
} catch (IOException e) {
player.sendMessage(e.getMessage());
return;
}
fp.runAction(new Runnable() {
@Override
public void run() {
SinglePlotAreaManager sManager = (SinglePlotAreaManager) manager;
SinglePlotArea area = sManager.getArea();
Plot plot = TaskManager.IMP.sync(new com.boydti.fawe.object.RunnableVal<Plot>() {
@Override
public void run(Plot o) {
int currentPlots = Settings.Limit.GLOBAL ? player.getPlotCount() : player.getPlotCount(area.worldname);
int diff = player.getAllowedPlots() - currentPlots;
if (diff < 1) {
MainUtil.sendMessage(player, C.CANT_CLAIM_MORE_PLOTS_NUM, -diff + "");
return;
}
if (area.getMeta("lastPlot") == null) {
area.setMeta("lastPlot", new PlotId(0, 0));
}
PlotId lastId = (PlotId) area.getMeta("lastPlot");
while (true) {
lastId = Auto.getNextPlotId(lastId, 1);
if (area.canClaim(player, lastId, lastId)) {
break;
}
}
area.setMeta("lastPlot", lastId);
this.value = area.getPlot(lastId);
this.value.setOwner(player.getUUID());
}
});
fp.sendMessage(BBC.getPrefix() + "Initializing components... (2)");
fp.sendMessage(BBC.getPrefix() + "/2 cfi setbiome");
fp.sendMessage(BBC.getPrefix() + "/2 cfi setoverlay");
fp.sendMessage(BBC.getPrefix() + "/2 cfi setmain");
fp.sendMessage(BBC.getPrefix() + "/2 cfi setfloor");
fp.sendMessage(BBC.getPrefix() + "/2 cfi setcolumn");
fp.sendMessage(BBC.getPrefix() + "/2 cfi addcaves");
fp.sendMessage(BBC.getPrefix() + "/2 cfi addore[s]");
fp.sendMessage(BBC.getPrefix() + "/2 cfi addschems");
fp.sendMessage(BBC.getPrefix() + "/2 cfi done");
fp.sendMessage(BBC.getPrefix() + "/2 cfi cancel");
File folder = new File(PS.imp().getWorldContainer(), plot.getWorldName() + File.separator + "region");
HeightMapMCAGenerator generator = new HeightMapMCAGenerator(image, folder);
player.setMeta("HMGenerator", generator);
player.setMeta("HMGeneratorPlot", plot);
}
}, true, false);
return;
}
fp.runAction(new Runnable() {
@Override
public void run() {
HeightMapMCAGenerator generator = player.getMeta("HMGenerator");
Plot plot = player.getMeta("HMGeneratorPlot");
if (generator == null) {
C.COMMAND_SYNTAX.send(player, getUsage());
return;
}
if (argList.size() == 1) {
if (StringMan.isEqualIgnoreCaseToAny(argList.get(0), "setbiome", "setoverlay", "setmain", "setfloor", "setcolumn")) {
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <image or mask> <value> [white-only]");
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <value>");
return;
} else if (!StringMan.isEqualIgnoreCaseToAny(argList.get(0), "done", "cancel", "addcaves", "addore", "addores", "addschems")) {
C.COMMAND_SYNTAX.send(player, "/2 cfi <setbiome|setoverlay|setmain|setfloor|setcolumn|done|cancel|addcaves|addore[s]|addschems>");
return;
}
}
ParserContext context = new ParserContext();
context.setActor(fp.getPlayer());
context.setWorld(fp.getWorld());
context.setSession(fp.getSession());
context.setExtent(generator);
Request.request().setExtent(generator);
try {
switch (argList.get(0).toLowerCase()) {
case "addschems": {
if (argList.size() != 5) {
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <mask> <file|folder|url> <rarity> <rotate>");
return;
}
World world = fp.getWorld();
WorldData wd = world.getWorldData();
Mask mask = we.getMaskFactory().parseFromInput(argList.get(1), context);
ClipboardHolder[] clipboards = ClipboardFormat.SCHEMATIC.loadAllFromInput(fp.getPlayer(), wd, argList.get(2), true);
if (clipboards == null) {
return;
}
int rarity = Integer.parseInt(argList.get(3));
boolean rotate = Boolean.parseBoolean(argList.get(4));
generator.addSchems(mask, wd, clipboards, rarity, rotate);
player.sendMessage(BBC.getPrefix() + "Added schems, what's next?");
return;
}
case "addores":
if (argList.size() != 2) {
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <mask>");
return;
}
generator.addDefaultOres(we.getMaskFactory().parseFromInput(argList.get(1), context));
player.sendMessage(BBC.getPrefix() + "Added ores, what's next?");
return;
case "addore": {
if (argList.size() != 8) {
C.COMMAND_SYNTAX.send(player, "/2 cfi " + argList.get(0) + " <mask> <pattern> <size> <frequency> <rarity> <min-Y> <max-Y>");
return;
}
// mask pattern size freq rarity miny maxy
Mask mask = we.getMaskFactory().parseFromInput(argList.get(1), context);
Pattern pattern = we.getPatternFactory().parseFromInput(argList.get(2), context);
int size = Integer.parseInt(argList.get(3));
int frequency = Integer.parseInt(argList.get(4));
int rarity = Integer.parseInt(argList.get(5));
int min = Integer.parseInt(argList.get(6));
int max = Integer.parseInt(argList.get(7));
generator.addOre(mask, pattern, size, frequency, rarity, min, max);
player.sendMessage(BBC.getPrefix() + "Added ore, what's next?");
return;
}
case "addcaves": {
generator.addCaves();
player.sendMessage(BBC.getPrefix() + "Added caves, what's next?");
return;
}
case "setbiome": {
int id;
if (argList.size() == 2) {
id = getBiome(argList.get(1), fp).getId();
generator.setBiome(id);
} else {
id = getBiome(argList.get(2), fp).getId();
BufferedImage img = getImgurImage(argList.get(1), fp);
if (img != null) {
boolean whiteOnly = argList.size() == 4 && Boolean.parseBoolean(argList.get(3));
generator.setBiome(img, (byte) id, whiteOnly);
} else {
generator.setBiome(we.getMaskFactory().parseFromInput(argList.get(1), context), (byte) id);
}
}
player.sendMessage(BBC.getPrefix() + "Set biome, what's next?");
return;
}
case "setoverlay": {
Pattern id;
if (argList.size() == 2) {
id = we.getPatternFactory().parseFromInput(argList.get(1), context);
generator.setOverlay(id);
} else {
id = we.getPatternFactory().parseFromInput(argList.get(2), context);
BufferedImage img = getImgurImage(argList.get(1), fp);
if (img != null) {
boolean whiteOnly = argList.size() == 4 && Boolean.parseBoolean(argList.get(3));
generator.setOverlay(img, id, whiteOnly);
} else {
generator.setOverlay(we.getMaskFactory().parseFromInput(argList.get(1), context), id);
}
}
player.sendMessage(BBC.getPrefix() + "Set overlay, what's next?");
return;
}
case "setmain": {
Pattern id;
if (argList.size() == 2) {
id = we.getPatternFactory().parseFromInput(argList.get(1), context);
generator.setMain(id);
} else {
id = we.getPatternFactory().parseFromInput(argList.get(2), context);
BufferedImage img = getImgurImage(argList.get(1), fp);
if (img != null) {
boolean whiteOnly = argList.size() == 4 && Boolean.parseBoolean(argList.get(3));
generator.setMain(img, id, whiteOnly);
} else {
generator.setMain(we.getMaskFactory().parseFromInput(argList.get(1), context), id);
}
}
player.sendMessage(BBC.getPrefix() + "Set main, what's next?");
return;
}
case "setfloor": {
Pattern id;
if (argList.size() == 2) {
id = we.getPatternFactory().parseFromInput(argList.get(1), context);
generator.setFloor(id);
} else {
id = we.getPatternFactory().parseFromInput(argList.get(2), context);
BufferedImage img = getImgurImage(argList.get(1), fp);
if (img != null) {
boolean whiteOnly = argList.size() == 4 && Boolean.parseBoolean(argList.get(3));
generator.setFloor(img, id, whiteOnly);
} else {
generator.setFloor(we.getMaskFactory().parseFromInput(argList.get(1), context), id);
}
}
player.sendMessage(BBC.getPrefix() + "Set floor, what's next?");
return;
}
case "setcolumn": {
Pattern id;
if (argList.size() == 2) {
id = we.getPatternFactory().parseFromInput(argList.get(1), context);
generator.setColumn(id);
} else {
id = we.getPatternFactory().parseFromInput(argList.get(2), context);
BufferedImage img = getImgurImage(argList.get(1), fp);
if (img != null) {
boolean whiteOnly = argList.size() == 4 && Boolean.parseBoolean(argList.get(3));
generator.setColumn(img, id, whiteOnly);
} else {
generator.setColumn(we.getMaskFactory().parseFromInput(argList.get(1), context), id);
}
}
player.sendMessage(BBC.getPrefix() + "Set columns, what's next?");
return;
}
case "done":
player.deleteMeta("HMGenerator");
player.deleteMeta("HMGeneratorPlot");
player.sendMessage("Generating... (4)");
try {
generator.generate();
} catch (IOException e) {
e.printStackTrace();
player.sendMessage(e.getMessage() + " (see console)");
return;
}
player.sendMessage("Done!");
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
plot.teleportPlayer(player);
}
});
return;
case "cancel":
player.deleteMeta("HMGenerator");
player.deleteMeta("HMGeneratorPlot");
player.sendMessage(BBC.getPrefix() + "Cancelled!");
return;
default:
C.COMMAND_SYNTAX.send(player, getUsage());
}
} catch (IOException e) {
player.sendMessage("Invalid url: " + e.getMessage());
} catch (InputParseException e) {
player.sendMessage("Invalid mask " + e.getMessage());
} catch (Throwable e) {
player.sendMessage("Error " + e.getMessage());
}
}
}, true, false);
}
});
} else {
player.sendMessage("Must have the `worlds` component enabled in the PlotSquared config.yml");
}
}
private BaseBiome getBiome(String arg, FawePlayer fp) {
World world = fp.getWorld();
BiomeRegistry biomeRegistry = world.getWorldData().getBiomeRegistry();
List<BaseBiome> knownBiomes = biomeRegistry.getBiomes();
return Biomes.findBiomeByName(knownBiomes, arg, biomeRegistry);
}
private BufferedImage getImgurImage(String arg, FawePlayer fp) throws IOException {
if (arg.startsWith("http")) {
URL url = new URL(arg);
if (!url.getHost().equalsIgnoreCase("i.imgur.com")) {
throw new IOException("Only i.imgur.com links are allowed!");
}
fp.sendMessage(BBC.getPrefix() + "Downloading image... (3)");
return ImageIO.read(url);
}
return null;
}
}

View File

@ -33,6 +33,10 @@ public class PlotSquaredFeature extends FaweMaskManager {
}
if (MainCommand.getInstance().getCommand("generatebiome") == null) {
new PlotSetBiome();
new CreateFromImage();
}
if (Settings.Enabled_Components.WORLDS) {
new ReplaceAll();
}
}

View File

@ -0,0 +1,81 @@
package com.boydti.fawe.regions.general.plot;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.wrappers.FakePlayer;
import com.intellectualcrafters.plot.commands.CommandCategory;
import com.intellectualcrafters.plot.commands.MainCommand;
import com.intellectualcrafters.plot.commands.RequiredType;
import com.intellectualcrafters.plot.config.C;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotArea;
import com.intellectualcrafters.plot.object.PlotPlayer;
import com.intellectualcrafters.plot.object.RunnableVal2;
import com.intellectualcrafters.plot.object.RunnableVal3;
import com.intellectualcrafters.plot.object.worlds.SinglePlotArea;
import com.plotsquared.bukkit.util.BukkitSetupUtils;
import com.plotsquared.general.commands.Command;
import com.plotsquared.general.commands.CommandDeclaration;
import com.sk89q.worldedit.event.platform.CommandEvent;
import com.sk89q.worldedit.extension.platform.CommandManager;
@CommandDeclaration(
command = "replaceall",
permission = "plots.replaceall",
category = CommandCategory.APPEARANCE,
requiredType = RequiredType.NONE,
description = "Replace all block in the plot",
usage = "/plots replaceall <from> <to>"
)
public class ReplaceAll extends Command {
public ReplaceAll() {
super(MainCommand.getInstance(), true);
}
@Override
public void execute(final PlotPlayer player, String[] args, RunnableVal3<Command, Runnable, Runnable> confirm, RunnableVal2<Command, CommandResult> whenDone) throws CommandException {
checkTrue(args.length >= 1, C.COMMAND_SYNTAX, getUsage());
final Plot plot = check(player.getCurrentPlot(), C.NOT_IN_PLOT);
checkTrue(plot.isOwner(player.getUUID()), C.NOW_OWNER);
checkTrue(plot.getRunning() == 0, C.WAIT_FOR_TIMER);
final PlotArea area = plot.getArea();
if (area instanceof SinglePlotArea) {
plot.addRunning();
FawePlayer<Object> fp = FawePlayer.wrap(player.getName());
C.TASK_START.send(player);
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
fp.runAction(new Runnable() {
@Override
public void run() {
String worldName = plot.getWorldName();
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
BukkitSetupUtils.manager.unload(worldName, true);
}
});
FakePlayer actor = FakePlayer.getConsole();
String cmd = "/replaceallpattern " + worldName + " " + StringMan.join(args, " ");
CommandEvent event = new CommandEvent(actor, cmd);
CommandManager.getInstance().handleCommandOnCurrentThread(event);
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
plot.teleportPlayer(player);
}
});
plot.removeRunning();
}
}, true, false);
}
});
} else {
player.sendMessage("Must have the `worlds` component enabled in the PlotSquared config.yml");
return;
}
}
}

View File

@ -3,7 +3,7 @@ package com.boydti.fawe.util;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.io.BufferedInputStream;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@ -24,7 +24,7 @@ public class ImgurUtility {
}
public static URL uploadImage(InputStream is) throws IOException {
is = new BufferedInputStream(is);
is = new FastBufferedInputStream(is);
FastByteArrayOutputStream baos = new FastByteArrayOutputStream(Short.MAX_VALUE);
int d;
while ((d = is.read()) != -1) {

View File

@ -24,8 +24,9 @@ import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.util.Location;
import java.io.BufferedInputStream;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@ -58,7 +59,10 @@ import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import net.jpountz.lz4.LZ4Compressor;
@ -244,6 +248,43 @@ public class MainUtil {
return LZ4Utils.maxCompressedLength(size);
}
public static byte[] compress(byte[] bytes, byte[] buffer, Deflater deflate) throws IOException {
if (buffer == null) {
buffer = new byte[8192];
}
if (deflate == null) {
deflate = new Deflater(1, false);
} else {
deflate.reset();
}
deflate.setInput(bytes);
deflate.finish();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (!deflate.finished()) {
int n = deflate.deflate(buffer);
if (n != 0) baos.write(buffer, 0, n);
}
return baos.toByteArray();
}
public static byte[] decompress(byte[] bytes, byte[] buffer, Inflater inflater) throws IOException, DataFormatException {
if (buffer == null) {
buffer = new byte[8192];
}
if (inflater == null) {
inflater = new Inflater(false);
} else {
inflater.reset();
}
inflater.setInput(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (!inflater.finished()) {
int n = inflater.inflate(buffer);
if (n != 0) baos.write(buffer, 0, n);
}
return baos.toByteArray();
}
public static byte[] compress(byte[] bytes, byte[] buffer, int level) {
if (level == 0) {
return bytes;
@ -302,14 +343,14 @@ public class MainUtil {
public static FaweInputStream getCompressedIS(InputStream is, int buffer) throws IOException {
int amount = (byte) is.read();
is = new BufferedInputStream(is, buffer);
is = new FastBufferedInputStream(is, buffer);
if (amount == 0) {
return new FaweInputStream(is);
}
int amountAbs = Math.abs(amount);
if (amountAbs > 6) {
if (amount > 0) {
is = new BufferedInputStream(new GZIPInputStream(is, buffer));
is = new FastBufferedInputStream(new GZIPInputStream(is, buffer));
} else {
is = new ZstdInputStream(is);
}
@ -836,8 +877,7 @@ public class MainUtil {
if (file.isDirectory()) {
deleteDirectory(files[i]);
} else {
Fawe.debug("Deleting file: " + file);
file.delete();
Fawe.debug("Deleting file: " + file + " | " + file.delete());
}
}
}

View File

@ -21,6 +21,76 @@ public class MathMan {
}
}
private static float[] ANGLES = new float[65536];
public static float sinInexact(double paramFloat) {
return ANGLES[(int)(paramFloat * 10430.378F) & 0xFFFF];
}
public static float cosInexact(double paramFloat) {
return ANGLES[(int)(paramFloat * 10430.378F + 16384.0F) & 0xFFFF];
}
public static int floorZero(double d0) {
int i = (int)d0;
return d0 < (double) i ? i - 1 : i;
}
public static double max(double... values) {
double max = Double.MIN_VALUE;
for (double d : values) {
if (d > max) {
max = d;
}
}
return max;
}
public static int max(int... values) {
int max = Integer.MIN_VALUE;
for (int d : values) {
if (d > max) {
max = d;
}
}
return max;
}
public static int min(int... values) {
int min = Integer.MAX_VALUE;
for (int d : values) {
if (d < min) {
min = d;
}
}
return min;
}
public static double min(double... values) {
double min = Double.MAX_VALUE;
for (double d : values) {
if (d < min) {
min = d;
}
}
return min;
}
public static int ceilZero(float floatNumber) {
int floor = (int)floatNumber;
return floatNumber > (float) floor ? floor + 1 : floor;
}
public static int clamp(int check, int min, int max) {
return check > max?max:(check < min?min:check);
}
static {
for(int i = 0; i < 65536; ++i) {
ANGLES[i] = (float)Math.sin((double)i * 3.141592653589793D * 2.0D / 65536.0D);
}
}
private final static int[] table = {
0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57,
59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83,

View File

@ -76,7 +76,7 @@ public class WEManager {
*/
public RegionWrapper[] getMask(final FawePlayer<?> player, FaweMaskManager.MaskType type) {
if (player.hasPermission("fawe.bypass") || !Settings.IMP.REGION_RESTRICTIONS) {
return new RegionWrapper[] {new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE)};
return new RegionWrapper[] {RegionWrapper.GLOBAL()};
}
HashSet<RegionWrapper> mask = new HashSet<>();
String world = player.getLocation().world;

View File

@ -346,7 +346,7 @@ public class TaskBuilder extends Metadatable {
public static final class TaskAbortException extends RuntimeException {
@Override
public synchronized Throwable fillInStackTrace() {
public Throwable fillInStackTrace() {
return this;
}
}

View File

@ -157,12 +157,12 @@ public final class NBTInputStream implements Closeable {
int i = 0;
if (is instanceof InputStream) {
DataInputStream dis = (DataInputStream) is;
if (length > 720) {
if (length > 1024) {
if (buf == null) {
buf = new byte[720];
buf = new byte[1024];
}
int left = length;
for (; left > 720; left -= 720) {
for (; left > 1024; left -= 1024) {
dis.readFully(buf);
for (byte b : buf) {
byteReader.run(i++, b & 0xFF);
@ -173,12 +173,12 @@ public final class NBTInputStream implements Closeable {
byteReader.run(i, dis.read());
}
} else {
if (length > 720) {
if (length > 1024) {
if (buf == null) {
buf = new byte[720];
buf = new byte[1024];
}
int left = length;
for (; left > 720; left -= 720) {
for (; left > 1024; left -= 1024) {
is.readFully(buf);
for (byte b : buf) {
byteReader.run(i++, b & 0xFF);
@ -338,8 +338,17 @@ public final class NBTInputStream implements Closeable {
case NBTConstants.TYPE_INT_ARRAY:
length = is.readInt();
int[] data = new int[length];
for (int i = 0; i < length; i++) {
data[i] = is.readInt();
if (buf == null) {
buf = new byte[1024];
}
int index = 0;
while (length > 0) {
int toRead = Math.min(length << 2, buf.length);
is.readFully(buf, 0, toRead);
for (int i = 0; i < toRead; i += 4, index++) {
data[index] = ((buf[i] << 24) + (buf[i + 1]<< 16) + (buf[i + 2] << 8) + (buf[i + 3] << 0));
}
length -= toRead;
}
return (data);
default:

View File

@ -80,6 +80,72 @@ public final class NBTOutputStream implements Closeable {
writeTagPayload(tag);
}
public void writeNamedTag(String name, String value) throws IOException {
checkNotNull(name);
checkNotNull(value);
int type = NBTConstants.TYPE_STRING;
writeNamedTagName(name, type);
byte[] bytes = value.getBytes(NBTConstants.CHARSET);
os.writeShort(bytes.length);
os.write(bytes);
}
public void writeNamedTag(String name, int value) throws IOException {
checkNotNull(name);
int type = NBTConstants.TYPE_INT;
writeNamedTagName(name, type);
os.writeInt(value);
}
public void writeNamedTag(String name, byte value) throws IOException {
checkNotNull(name);
int type = NBTConstants.TYPE_BYTE;
writeNamedTagName(name, type);
os.writeByte(value);
}
public void writeNamedTag(String name, short value) throws IOException {
checkNotNull(name);
int type = NBTConstants.TYPE_SHORT;
writeNamedTagName(name, type);
os.writeShort(value);
}
public void writeNamedTag(String name, long value) throws IOException {
checkNotNull(name);
int type = NBTConstants.TYPE_LONG;
writeNamedTagName(name, type);
os.writeLong(value);
}
public void writeNamedTag(String name, byte[] bytes) throws IOException {
checkNotNull(name);
int type = NBTConstants.TYPE_BYTE_ARRAY;
writeNamedTagName(name, type);
os.writeInt(bytes.length);
os.write(bytes);
}
public void writeNamedTag(String name, int[] data) throws IOException {
checkNotNull(name);
int type = NBTConstants.TYPE_INT_ARRAY;
writeNamedTagName(name, type);
os.writeInt(data.length);
for (int aData : data) {
os.writeInt(aData);
}
}
public void writeNamedEmptyList(String name) throws IOException {
writeNamedEmptyList(name, NBTConstants.TYPE_COMPOUND);
}
public void writeNamedEmptyList(String name, int type) throws IOException {
writeNamedTagName(name, NBTConstants.TYPE_LIST);
os.writeByte(type);
os.writeInt(0);
}
public void writeNamedTagName(String name, int type) throws IOException {
byte[] nameBytes = name.getBytes(NBTConstants.CHARSET);
os.writeByte(type);
@ -213,6 +279,9 @@ public final class NBTOutputStream implements Closeable {
*/
private void writeListTagPayload(ListTag tag) throws IOException {
Class<? extends Tag> clazz = tag.getType();
if (clazz == null) {
clazz = CompoundTag.class;
}
List<Tag> tags = tag.getValue();
int size = tags.size();
if (!tags.isEmpty()) {

View File

@ -46,10 +46,13 @@ import com.boydti.fawe.object.collection.LocalBlockVectorSet;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.object.extent.FaweRegionExtent;
import com.boydti.fawe.object.extent.HeightBoundExtent;
import com.boydti.fawe.object.extent.LightingExtent;
import com.boydti.fawe.object.extent.MultiRegionExtent;
import com.boydti.fawe.object.extent.NullExtent;
import com.boydti.fawe.object.extent.ProcessedWEExtent;
import com.boydti.fawe.object.extent.ResettableExtent;
import com.boydti.fawe.object.extent.SingleRegionExtent;
import com.boydti.fawe.object.extent.SlowExtent;
import com.boydti.fawe.object.extent.SourceMaskExtent;
import com.boydti.fawe.object.mask.ResettableMask;
@ -319,15 +322,22 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
}
}
}
this.maxY = getWorld() == null ? 255 : world.getMaxY();
if (allowedRegions != null) {
if (allowedRegions.length == 0) {
this.extent = new NullExtent(this.extent, BBC.WORLDEDIT_CANCEL_REASON_NO_REGION);
} else {
this.extent = new ProcessedWEExtent(this.extent, allowedRegions, this.limit);
this.extent = new ProcessedWEExtent(this.extent, this.limit);
if (allowedRegions.length == 1) {
this.extent = new SingleRegionExtent(this.extent, limit, allowedRegions[0]);
} else {
this.extent = new MultiRegionExtent(this.extent, limit, allowedRegions);
}
}
} else {
this.extent = new HeightBoundExtent(this.extent, limit, 0, maxY);
}
this.extent = wrapExtent(this.extent, bus, event, Stage.BEFORE_HISTORY);
this.maxY = getWorld() == null ? 255 : world.getMaxY();
}
/**
@ -981,84 +991,6 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
return this.getHighestTerrainBlock(x, z, minY, maxY, false);
}
public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
int clearanceAbove = maxY - y;
int clearanceBelow = y - minY;
int clearance = Math.min(clearanceAbove, clearanceBelow);
BaseBlock block = getBlock(x, y, z);
boolean state = FaweCache.isLiquidOrGas(block.getId());
int data1 = block.getData();
int data2 = block.getData();
int offset = state ? 0 : 1;
for (int d = 0; d <= clearance; d++) {
int y1 = y + d;
block = getLazyBlock(x, y1, z);
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
return ((y1 - offset) << 3) - (7 - (state ? block.getData() : data1));
}
data1 = block.getData();
int y2 = y - d;
block = getLazyBlock(x, y2, z);
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
return ((y2 + offset) << 3) - (7 - (state ? block.getData() : data2));
}
data2 = block.getData();
}
if (clearanceAbove != clearanceBelow) {
if (clearanceAbove < clearanceBelow) {
for (int layer = y - clearance - 1; layer >= minY; layer--) {
block = getLazyBlock(x, layer, z);
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
return ((layer + offset) << 3) - (7 - (state ? block.getData() : data1));
}
data1 = block.getData();
}
} else {
for (int layer = y + clearance + 1; layer <= maxY; layer++) {
block = getLazyBlock(x, layer, z);
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
return ((layer - offset) << 3) - (7 - (state ? block.getData() : data2));
}
data2 = block.getData();
}
}
}
return maxY << 4;
}
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
int clearanceAbove = maxY - y;
int clearanceBelow = y - minY;
int clearance = Math.min(clearanceAbove, clearanceBelow);
BaseBlock block = getBlock(x, y, z);
boolean state = FaweCache.canPassThrough(block.getId(), block.getData());
int offset = state ? 0 : 1;
for (int d = 0; d <= clearance; d++) {
int y1 = y + d;
block = getLazyBlock(x, y1, z);
if (FaweCache.canPassThrough(block.getId(), block.getData()) != state) return y1 - offset;
int y2 = y - d;
block = getLazyBlock(x, y2, z);
if (FaweCache.canPassThrough(block.getId(), block.getData()) != state) return y2 + offset;
}
if (clearanceAbove != clearanceBelow) {
if (clearanceAbove < clearanceBelow) {
for (int layer = y - clearance - 1; layer >= minY; layer--) {
block = getLazyBlock(x, layer, z);
if (FaweCache.canPassThrough(block.getId(), block.getData()) != state) return layer + offset;
}
} else {
for (int layer = y + clearance + 1; layer <= maxY; layer++) {
block = getLazyBlock(x, layer, z);
if (FaweCache.canPassThrough(block.getId(), block.getData()) != state) return layer - offset;
}
}
}
return maxY;
}
/**
* Returns the highest solid 'terrain' block which can occur naturally.
*

View File

@ -88,6 +88,12 @@ public class Vector implements Comparable<Vector> {
this.mutZ(other.getZ());
}
public Vector(double[] arr) {
this.mutX(arr[0]);
this.mutY(arr[1]);
this.mutZ(arr[2]);
}
/**
* Construct a new instance with X, Y, and Z coordinates set to 0.
*

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.blocks;
import com.boydti.fawe.FaweCache;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
@ -125,6 +126,10 @@ public class BaseBlock extends Block implements TileEntityBlock {
this(other.getId(), other.getData(), other.getNbtData());
}
public final int getCombined() {
return FaweCache.getCombined(this);
}
/**
* Get the ID of the block.
*

View File

@ -31,7 +31,9 @@ import com.boydti.fawe.object.brush.CopyPastaBrush;
import com.boydti.fawe.object.brush.ErodeBrush;
import com.boydti.fawe.object.brush.FlattenBrush;
import com.boydti.fawe.object.brush.HeightBrush;
import com.boydti.fawe.object.brush.LayerBrush;
import com.boydti.fawe.object.brush.LineBrush;
import com.boydti.fawe.object.brush.PopulateSchem;
import com.boydti.fawe.object.brush.RaiseBrush;
import com.boydti.fawe.object.brush.RecurseBrush;
import com.boydti.fawe.object.brush.ScatterBrush;
@ -98,6 +100,8 @@ import java.io.InputStream;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
@ -525,6 +529,7 @@ public class BrushCommands {
help =
"Chooses the scatter brush.\n" +
" The -o flag will overlay the block",
flags = "o",
min = 1,
max = 4
)
@ -544,6 +549,62 @@ public class BrushCommands {
player.print(BBC.getPrefix() + BBC.BRUSH_SCATTER.f(radius, points));
}
@Command(
aliases = { "populateschematic", "populateschem", "popschem", "pschem", "ps" },
usage = "<mask> <file|folder|url> [radius=30] [points=5]",
desc = "Scatter a schematic on a surface",
help =
"Chooses the scatter schematic brush.\n" +
"The -r flag will apply random rotation",
flags = "r",
min = 2,
max = 4
)
@CommandPermissions("worldedit.brush.populateschematic")
public void scatterSchemBrush(Player player, EditSession editSession, LocalSession session, Mask mask, String clipboard, @Optional("30") double radius, @Optional("50") double density, @Switch('r') boolean rotate) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
BrushTool tool = session.getBrushTool(player);
tool.setSize(radius);
try {
ClipboardHolder[] clipboards = ClipboardFormat.SCHEMATIC.loadAllFromInput(player, player.getWorld().getWorldData(), clipboard, true);
if (clipboards == null) {
return;
}
Brush brush = new PopulateSchem(mask, clipboards, (int) density, rotate);
tool.setBrush(brush, "worldedit.brush.populateschematic", player);
player.print(BBC.getPrefix() + BBC.BRUSH_POPULATE.f(radius, density));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Command(
aliases = { "layer" },
usage = "<radius> <pattern1> <patern2> <pattern3>...",
desc = "Scatter a schematic on a surface",
help = "Chooses the layer brush.",
min = 3,
max = 999
)
@CommandPermissions("worldedit.brush.layer")
public void surfaceLayer(Player player, EditSession editSession, LocalSession session, double radius, CommandContext args) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
BrushTool tool = session.getBrushTool(player);
tool.setSize(radius);
ParserContext parserContext = new ParserContext();
parserContext.setActor(player);
parserContext.setWorld(player.getWorld());
parserContext.setSession(session);
parserContext.setExtent(editSession);
List<BaseBlock> blocks = new ArrayList<>();
for (int i = 1; i < args.argsLength(); i++) {
String arg = args.getString(i);
blocks.add(worldEdit.getBlockFactory().parseFromInput(arg, parserContext));
}
tool.setBrush(new LayerBrush(blocks.toArray(new BaseBlock[blocks.size()])), "worldedit.brush.layer", player);
player.print(BBC.getPrefix() + BBC.BRUSH_LAYER.f(radius, args.getJoinedStrings(1)));
}
@Command(
aliases = { "splatter", "splat" },
usage = "<pattern> [radius=5] [seeds=1] [recursion=5] [solid=true]",
@ -852,6 +913,7 @@ public class BrushCommands {
BrushTool tool = session.getBrushTool(player);
String cmd = args.getJoinedStrings(1);
tool.setBrush(new CommandBrush(cmd, radius), "worldedit.brush.copy", player);
tool.setSize(radius);
player.print(BBC.getPrefix() + BBC.BRUSH_COMMAND.f(cmd));
}

View File

@ -44,7 +44,6 @@ public class GeneralCommands {
aliases = { "/tips", "tips" },
desc = "Toggle WorldEdit tips"
)
@CommandPermissions("fawe.use")
public void tips(Player player, LocalSession session) throws WorldEditException {
FawePlayer<Object> fp = FawePlayer.wrap(player);
if (fp.toggle("fawe.tips")) {

View File

@ -21,6 +21,7 @@ package com.sk89q.worldedit.command;
import com.boydti.fawe.command.FawePrimitiveBinding;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.anvil.generator.CavesGen;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.minecraft.util.commands.Logging;
@ -30,6 +31,7 @@ import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.internal.expression.ExpressionException;
@ -66,6 +68,49 @@ public class GenerationCommands {
this.worldEdit = worldEdit;
}
@Command(
aliases = { "/caves [size=8] [freq=40] [rarity=7] [minY=8] [maxY=127] [sysFreq=1] [sysRarity=25] [pocketRarity=0] [pocketMin=0] [pocketMax=3]" },
desc = "Generates caves",
help = "Generates a cave network"
)
@CommandPermissions("worldedit.generation.cylinder")
@Logging(PLACEMENT)
public void caves(Player player, LocalSession session, EditSession editSession, @Selection Region region, @Optional("8") int size, @Optional("40") int frequency, @Optional("7") int rarity, @Optional("8") int minY, @Optional("127") int maxY, @Optional("1") int systemFrequency, @Optional("25") int individualRarity, @Optional("0") int pocketChance, @Optional("0") int pocketMin, @Optional("3") int pocketMax) throws WorldEditException, ParameterException {
CavesGen gen = new CavesGen(size, frequency, rarity, minY, maxY, systemFrequency, individualRarity, pocketChance, pocketMin, pocketMax);
editSession.generate(region, gen);
BBC.VISITOR_BLOCK.send(player, editSession.getBlockChangeCount());
}
// public void addOre(Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException {
@Command(
aliases = { "/ores" },
desc = "Generates ores",
help = "Generates ores",
min = 1,
max = 1
)
@CommandPermissions("worldedit.generation.cylinder")
@Logging(PLACEMENT)
public void ores(Player player, LocalSession session, EditSession editSession, @Selection Region region, Mask mask) throws WorldEditException, ParameterException {
editSession.addOres(region, mask);
BBC.VISITOR_BLOCK.send(player, editSession.getBlockChangeCount());
}
@Command(
aliases = { "/ore <mask> <pattern> <size> <freq> <rarity> <minY> <maxY>" },
desc = "Generates ores",
help = "Generates ores",
min = 7,
max = 7
)
@CommandPermissions("worldedit.generation.cylinder")
@Logging(PLACEMENT)
public void ore(Player player, LocalSession session, EditSession editSession, @Selection Region region, Mask mask, Pattern material, int size, int freq, int rarity, int minY, int maxY) throws WorldEditException, ParameterException {
editSession.addOre(region, mask, material, size, freq, rarity, minY, maxY);
BBC.VISITOR_BLOCK.send(player, editSession.getBlockChangeCount());
}
@Command(
aliases = { "/hcyl" },
usage = "<pattern> <radius>[,<radius>] [height]",

View File

@ -313,24 +313,6 @@ public class RegionCommands {
if (!FawePlayer.wrap(player).hasPermission("fawe.tips")) BBC.TIP_REPLACE_ID.or(BBC.TIP_REPLACE_LIGHT, BBC.TIP_REPLACE_MARKER, BBC.TIP_TAB_COMPLETE).send(player);
}
// @Command(
// aliases = { "/mapreplace", "/mr", "/maprep" },
// usage = "<from-block-1,from-block-2> <to-block-1,to-block-2>",
// desc = "Replace all blocks in a selection 1:1 with the ",
// flags = "f",
// min = 1,
// max = 2
// )
// @CommandPermissions("worldedit.region.mapreplace")
// @Logging(REGION)
// public void mapreplace(Player player, EditSession editSession, @Selection Region region, @Optional Mask from, Pattern to) throws WorldEditException {
// if (from == null) {
// from = new ExistingBlockMask(editSession);
// }
// int affected = editSession.replaceBlocks(region, from, to);
// BBC.VISITOR_BLOCK.send(player, affected);
// }
@Command(
aliases = { "/set" },
usage = "[pattern]",

View File

@ -293,11 +293,13 @@ public class SchematicCommands {
}
}
// schem list all|mine|global page
@Command(
aliases = {"list", "all", "ls"},
desc = "List saved schematics",
min = 0,
max = 1,
max = 2,
flags = "dnp",
help = "List all schematics in the schematics directory\n" +
" -d sorts by date, oldest first\n" +
@ -306,8 +308,12 @@ public class SchematicCommands {
)
@CommandPermissions("worldedit.schematic.list")
public void list(Actor actor, CommandContext args, @Switch('p') @Optional("1") int page) throws WorldEditException {
if (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS) {
}
File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().saveDir);
List<File> fileList = allFiles(dir);
List<File> fileList = allFiles(dir, true);
if (fileList == null || fileList.isEmpty()) {
BBC.SCHEMATIC_NONE.send(actor);
@ -367,13 +373,13 @@ public class SchematicCommands {
}
private List<File> allFiles(File root) {
private List<File> allFiles(File root, boolean recursive) {
File[] files = root.listFiles();
if (files == null) return null;
List<File> fileList = new ArrayList<File>();
for (File f : files) {
if (f.isDirectory()) {
List<File> subFiles = allFiles(f);
if (recursive && f.isDirectory()) {
List<File> subFiles = allFiles(f, recursive);
if (subFiles == null) continue; // empty subdir
fileList.addAll(subFiles);
} else {

View File

@ -23,6 +23,7 @@ import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.mask.IdMask;
import com.boydti.fawe.object.regions.selector.FuzzyRegionSelector;
import com.boydti.fawe.object.regions.selector.PolyhedralRegionSelector;
import com.google.common.base.Optional;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
@ -736,7 +737,7 @@ public class SelectionCommands {
} else if (typeName.equalsIgnoreCase("cyl")) {
selector = new CylinderRegionSelector(oldSelector);
player.print(BBC.getPrefix() + BBC.SEL_CYLINDRICAL.s());
} else if (typeName.equalsIgnoreCase("convex") || typeName.equalsIgnoreCase("hull") || typeName.equalsIgnoreCase("polyhedron")) {
} else if (typeName.equalsIgnoreCase("convex") || typeName.equalsIgnoreCase("hull")) {
selector = new ConvexPolyhedralRegionSelector(oldSelector);
player.print(BBC.getPrefix() + BBC.SEL_CONVEX_POLYHEDRAL.s());
Optional<Integer> limit = ActorSelectorLimits.forActor(player).getPolyhedronVertexLimit();
@ -744,6 +745,14 @@ public class SelectionCommands {
player.print(BBC.getPrefix() + BBC.SEL_MAX.f(limit.get()));
}
player.print(BBC.getPrefix() + BBC.SEL_LIST.s());
} else if (typeName.equalsIgnoreCase("polyhedral") || typeName.equalsIgnoreCase("polyhedron")) {
selector = new PolyhedralRegionSelector(player.getWorld());
player.print(BBC.getPrefix() + BBC.SEL_CONVEX_POLYHEDRAL.s());
Optional<Integer> limit = ActorSelectorLimits.forActor(player).getPolyhedronVertexLimit();
if (limit.isPresent()) {
player.print(BBC.getPrefix() + BBC.SEL_MAX.f(limit.get()));
}
player.print(BBC.getPrefix() + BBC.SEL_LIST.s());
} else if (typeName.startsWith("fuzzy") || typeName.startsWith("magic")) {
Mask mask;
if (typeName.length() > 6) {
@ -772,6 +781,7 @@ public class SelectionCommands {
box.appendCommand("//sel sphere", "Select a sphere");
box.appendCommand("//sel cyl", "Select a cylinder");
box.appendCommand("//sel convex", "Select a convex polyhedral");
box.appendCommand("//sel polyhedral", "Select a hollow polyhedral");
box.appendCommand("//sel fuzzy[=<mask>]", "Select all connected blocks");
player.printRaw(ColorCodeBuilder.asColorCodes(box));

View File

@ -100,7 +100,7 @@ public class SelectionCommand extends SimpleCommand<Operation> {
FawePlayer fp = FawePlayer.wrap(player);
FaweRegionExtent regionExtent = editSession.getRegionExtent();
if (function instanceof BlockReplace && regionExtent == null) {
if (function instanceof BlockReplace && regionExtent == null || regionExtent.isGlobal()) {
try {
BlockReplace replace = ((BlockReplace) function);
Field field = replace.getClass().getDeclaredField("pattern");
@ -148,7 +148,6 @@ public class SelectionCommand extends SimpleCommand<Operation> {
}
});
queue.enqueue();
long start = System.currentTimeMillis();
BBC.OPERATION.send(actor, BBC.VISITOR_BLOCK.format(cuboid.getArea()));
queue.flush();
return null;

View File

@ -164,6 +164,7 @@ public class DefaultMaskParser extends FaweParser<Mask> {
private Mask getBlockMaskComponent(List<Mask> masks, String input, ParserContext context) throws InputParseException {
Extent extent = Request.request().getExtent();
if (extent == null) extent = context.getExtent();
final char firstChar = input.charAt(0);
switch (firstChar) {
case '#':
@ -303,7 +304,7 @@ public class DefaultMaskParser extends FaweParser<Mask> {
y1 = (Expression.compile(split[0]).evaluate());
y2 = (Expression.compile(split[1]).evaluate());
}
return new AngleMask(Request.request().getEditSession(), y1, y2);
return new AngleMask(extent, y1, y2);
} catch (NumberFormatException | ExpressionException e) {
throw new SuggestInputParseException(input, "/<min-angle>:<max-angle>");
}
@ -385,7 +386,7 @@ public class DefaultMaskParser extends FaweParser<Mask> {
try {
Expression exp = Expression.compile(input.substring(1), "x", "y", "z");
WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(
Request.request().getEditSession(), Vector.ONE, Vector.ZERO);
Request.request().getEditSession (), Vector.ONE, Vector.ZERO);
exp.setEnvironment(env);
return new ExpressionMask(exp);
} catch (ExpressionException e) {

View File

@ -22,6 +22,7 @@ import com.boydti.fawe.object.pattern.RelativePattern;
import com.boydti.fawe.object.pattern.SolidRandomOffsetPattern;
import com.boydti.fawe.object.pattern.SurfaceRandomOffsetPattern;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.StringMan;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.EmptyClipboardException;
@ -122,7 +123,7 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
switch (split2[0].toLowerCase()) {
case "#*":
case "#existing": {
return new ExistingPattern(Request.request().getEditSession());
return new ExistingPattern(Request.request().getExtent());
}
case "#clipboard":
case "#copy": {
@ -151,7 +152,7 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
throw new InputParseException("#fullcopy:<source>");
}
boolean random = split2.length == 3 && split2[2].equalsIgnoreCase("true");
return new RandomFullClipboardPattern(Request.request().getEditSession(), context.requireWorld().getWorldData(), clipboards, random);
return new RandomFullClipboardPattern(Request.request().getExtent(), context.requireWorld().getWorldData(), clipboards, random);
} catch (IOException e) {
throw new RuntimeException(e);
@ -159,7 +160,7 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
}
ClipboardHolder holder = session.getClipboard();
Clipboard clipboard = holder.getClipboard();
return new FullClipboardPattern(Request.request().getEditSession(), clipboard);
return new FullClipboardPattern(Request.request().getExtent(), clipboard);
} catch (EmptyClipboardException e) {
throw new InputParseException("To use #fullcopy, please first copy something to your clipboard");
}
@ -168,17 +169,20 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
}
}
case "#id": {
return new IdPattern(Request.request().getEditSession(), catchSuggestion(input, rest, context));
return new IdPattern(Request.request().getExtent(), catchSuggestion(input, rest, context));
}
case "#data": {
return new DataPattern(Request.request().getEditSession(), catchSuggestion(input, rest, context));
return new DataPattern(Request.request().getExtent(), catchSuggestion(input, rest, context));
}
case "#biome": {
if (MathMan.isInteger(rest)) {
return new BiomePattern(Request.request().getExtent(), new BaseBiome(Integer.parseInt(rest)));
}
World world = context.requireWorld();
BiomeRegistry biomeRegistry = world.getWorldData().getBiomeRegistry();
List<BaseBiome> knownBiomes = biomeRegistry.getBiomes();
BaseBiome biome = Biomes.findBiomeByName(knownBiomes, rest, biomeRegistry);
return new BiomePattern(Request.request().getEditSession(), biome);
return new BiomePattern(Request.request().getExtent(), biome);
}
case "#~":
case "#r":
@ -272,6 +276,9 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
}
case "#l":
case "#linear": {
if (rest.startsWith("\"") && rest.endsWith("\"")) {
rest = rest.substring(1, rest.length() - 1);
}
ArrayList<Pattern> patterns = new ArrayList<>();
for (String token : StringMan.split(rest, ',')) {
patterns.add(catchSuggestion(input, token, context));
@ -283,6 +290,9 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
}
case "#l3d":
case "#linear3d": {
if (rest.startsWith("\"") && rest.endsWith("\"")) {
rest = rest.substring(1, rest.length() - 1);
}
ArrayList<Pattern> patterns = new ArrayList<>();
for (String token : StringMan.split(rest, ',')) {
patterns.add(catchSuggestion(input, token, context));

View File

@ -117,6 +117,7 @@ public abstract class AbstractDelegateExtent implements LightingExtent {
private MutableBlockVector mutable = new MutableBlockVector(0,0,0);
@Override
public BaseBlock getLazyBlock(int x, int y, int z) {
mutable.mutX(x);
mutable.mutY(y);
@ -129,6 +130,7 @@ public abstract class AbstractDelegateExtent implements LightingExtent {
return extent.getLazyBlock(position);
}
@Override
public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException {
mutable.mutX(x);
mutable.mutY(y);

View File

@ -0,0 +1,191 @@
package com.sk89q.worldedit.extent;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.jnbt.anvil.generator.CavesGen;
import com.boydti.fawe.jnbt.anvil.generator.GenBase;
import com.boydti.fawe.jnbt.anvil.generator.OreGen;
import com.boydti.fawe.jnbt.anvil.generator.Resource;
import com.boydti.fawe.jnbt.anvil.generator.SchemGen;
import com.boydti.fawe.object.PseudoRandom;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.registry.WorldData;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
public interface Extent extends InputExtent, OutputExtent {
Vector getMinimumPoint();
Vector getMaximumPoint();
default List<? extends Entity> getEntities(Region region) {
return new ArrayList<>();
}
default List<? extends Entity> getEntities() {
return new ArrayList<>();
}
default @Nullable Entity createEntity(Location location, BaseEntity entity) {
throw new UnsupportedOperationException(getClass() + " does not support entity creation!");
}
@Override
default BaseBlock getLazyBlock(Vector position) {
return getBlock(position);
}
default public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException {
return setBlock(MutableBlockVector.get(x, y, z), block);
}
@Nullable
@Override
default Operation commit() {
return null;
}
default BaseBlock getLazyBlock(int x, int y, int z) {
return getLazyBlock(MutableBlockVector.get(x, y, z));
}
default public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
int clearanceAbove = maxY - y;
int clearanceBelow = y - minY;
int clearance = Math.min(clearanceAbove, clearanceBelow);
BaseBlock block = getLazyBlock(x, y, z);
boolean state = FaweCache.isLiquidOrGas(block.getId());
int data1 = block.getData();
int data2 = block.getData();
int offset = state ? 0 : 1;
for (int d = 0; d <= clearance; d++) {
int y1 = y + d;
block = getLazyBlock(x, y1, z);
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
return ((y1 - offset) << 3) - (7 - (state ? block.getData() : data1));
}
data1 = block.getData();
int y2 = y - d;
block = getLazyBlock(x, y2, z);
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
return ((y2 + offset) << 3) - (7 - (state ? block.getData() : data2));
}
data2 = block.getData();
}
if (clearanceAbove != clearanceBelow) {
if (clearanceAbove < clearanceBelow) {
for (int layer = y - clearance - 1; layer >= minY; layer--) {
block = getLazyBlock(x, layer, z);
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
return ((layer + offset) << 3) - (7 - (state ? block.getData() : data1));
}
data1 = block.getData();
}
} else {
for (int layer = y + clearance + 1; layer <= maxY; layer++) {
block = getLazyBlock(x, layer, z);
if (FaweCache.isLiquidOrGas(block.getId()) != state) {
return ((layer - offset) << 3) - (7 - (state ? block.getData() : data2));
}
data2 = block.getData();
}
}
}
return maxY << 4;
}
public default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
int clearanceAbove = maxY - y;
int clearanceBelow = y - minY;
int clearance = Math.min(clearanceAbove, clearanceBelow);
BaseBlock block = getLazyBlock(x, y, z);
boolean state = FaweCache.canPassThrough(block.getId(), block.getData());
int offset = state ? 0 : 1;
for (int d = 0; d <= clearance; d++) {
int y1 = y + d;
block = getLazyBlock(x, y1, z);
if (FaweCache.canPassThrough(block.getId(), block.getData()) != state) return y1 - offset;
int y2 = y - d;
block = getLazyBlock(x, y2, z);
if (FaweCache.canPassThrough(block.getId(), block.getData()) != state) return y2 + offset;
}
if (clearanceAbove != clearanceBelow) {
if (clearanceAbove < clearanceBelow) {
for (int layer = y - clearance - 1; layer >= minY; layer--) {
block = getLazyBlock(x, layer, z);
if (FaweCache.canPassThrough(block.getId(), block.getData()) != state) return layer + offset;
}
} else {
for (int layer = y + clearance + 1; layer <= maxY; layer++) {
block = getLazyBlock(x, layer, z);
if (FaweCache.canPassThrough(block.getId(), block.getData()) != state) return layer - offset;
}
}
}
return maxY;
}
default public void addCaves(Region region) throws WorldEditException {
generate(region, new CavesGen(8));
}
public default void generate(Region region, GenBase gen) throws WorldEditException {
for (Vector2D chunkPos : region.getChunks()) {
gen.generate(chunkPos, this);
}
}
default public void addSchems(Region region, Mask mask, WorldData worldData, ClipboardHolder[] clipboards, int rarity, boolean rotate) throws WorldEditException{
spawnResource(region, new SchemGen(mask, this, worldData, clipboards, rotate), rarity, 1);
}
default public void spawnResource(Region region, Resource gen, int rarity, int frequency) throws WorldEditException {
PseudoRandom random = new PseudoRandom();
for (Vector2D chunkPos : region.getChunks()) {
for (int i = 0; i < frequency; i++) {
if (random.nextInt(101) > rarity) {
continue;
}
int x = (chunkPos.getBlockX() << 4) + PseudoRandom.random.nextInt(16);
int z = (chunkPos.getBlockZ() << 4) + PseudoRandom.random.nextInt(16);
gen.spawn(random, x, z);
}
}
}
default public void addOre(Region region, Mask mask, Pattern material, int size, int frequency, int rarity, int minY, int maxY) throws WorldEditException {
spawnResource(region, new OreGen(this, mask, material, size, minY, maxY), rarity, frequency);
}
default public void addOres(Region region, Mask mask) throws WorldEditException {
addOre(region, mask, new BlockPattern(BlockID.DIRT), 20, 2, 100, 0, 255);
addOre(region, mask, new BlockPattern(BlockID.GRAVEL), 20, 1, 100, 0, 255);
addOre(region, mask, new BlockPattern(BlockID.STONE, 1), 20, 2, 100, 0, 79);
addOre(region, mask, new BlockPattern(BlockID.STONE, 3), 20, 2, 100, 0, 79);
addOre(region, mask, new BlockPattern(BlockID.STONE, 5), 20, 2, 100, 0, 79);
addOre(region, mask, new BlockPattern(BlockID.COAL_ORE), 17, 20, 100, 0, 127);
addOre(region, mask, new BlockPattern(BlockID.IRON_ORE), 9, 20, 100, 0, 63);
addOre(region, mask, new BlockPattern(BlockID.GOLD_ORE), 9, 2, 100, 0, 31);
addOre(region, mask, new BlockPattern(BlockID.REDSTONE_ORE), 8, 8, 100, 0, 15);
addOre(region, mask, new BlockPattern(BlockID.DIAMOND_ORE), 8, 1, 100, 0, 15);
addOre(region, mask, new BlockPattern(BlockID.LAPIS_LAZULI_ORE), 7, 1, 100, 0, 15);
addOre(region, mask, new BlockPattern(BlockID.EMERALD_ORE), 5, 1, 100, 4, 31);
}
}

View File

@ -52,7 +52,7 @@ import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
import java.io.BufferedInputStream;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
@ -94,8 +94,8 @@ public enum ClipboardFormat {
if (inputStream instanceof FileInputStream) {
inputStream = new ResettableFileInputStream((FileInputStream) inputStream);
}
BufferedInputStream buffered = new BufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(buffered)));
FastBufferedInputStream buffered = new FastBufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new FastBufferedInputStream(new GZIPInputStream(buffered)));
SchematicReader input = new SchematicReader(nbtStream);
input.setUnderlyingStream(inputStream);
return input;
@ -151,8 +151,8 @@ public enum ClipboardFormat {
STRUCTURE(new AbstractClipboardFormat("STRUCTURE", "structure", "nbt") {
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
inputStream = new BufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(inputStream)));
inputStream = new FastBufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new FastBufferedInputStream(new GZIPInputStream(inputStream)));
return new StructureFormat(nbtStream);
}
@ -340,10 +340,14 @@ public enum ClipboardFormat {
dir = new File(worldEdit.getWorkingDirectoryFile(config.saveDir), input);
}
}
if (!dir.exists() || !dir.isDirectory()) {
if (!dir.exists()) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);
return null;
}
if (!dir.isDirectory()) {
ByteSource source = Files.asByteSource(dir);
return new ClipboardHolder[] {new LazyClipboardHolder(source, this, worldData, null)};
}
ClipboardHolder[] clipboards = loadAllFromDirectory(dir, worldData);
if (clipboards.length < 1) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);

View File

@ -146,16 +146,16 @@ public class SchematicWriter implements ClipboardWriter {
public void write(NBTOutputStream out) throws IOException {
int volume = width * height * length;
out.writeNamedTag("Width", new ShortTag((short) width));
out.writeNamedTag("Length", new ShortTag((short) length));
out.writeNamedTag("Height", new ShortTag((short) height));
out.writeNamedTag("Materials", new StringTag("Alpha"));
out.writeNamedTag("WEOriginX", new IntTag(min.getBlockX()));
out.writeNamedTag("WEOriginY", new IntTag(min.getBlockY()));
out.writeNamedTag("WEOriginZ", new IntTag(min.getBlockZ()));
out.writeNamedTag("WEOffsetX", new IntTag(offset.getBlockX()));
out.writeNamedTag("WEOffsetY", new IntTag(offset.getBlockY()));
out.writeNamedTag("WEOffsetZ", new IntTag(offset.getBlockZ()));
out.writeNamedTag("Width", ((short) width));
out.writeNamedTag("Length", ((short) length));
out.writeNamedTag("Height", ((short) height));
out.writeNamedTag("Materials", ("Alpha"));
out.writeNamedTag("WEOriginX", (min.getBlockX()));
out.writeNamedTag("WEOriginY", (min.getBlockY()));
out.writeNamedTag("WEOriginZ", (min.getBlockZ()));
out.writeNamedTag("WEOffsetX", (offset.getBlockX()));
out.writeNamedTag("WEOffsetY", (offset.getBlockY()));
out.writeNamedTag("WEOffsetZ", (offset.getBlockZ()));
out.writeNamedTagName("Blocks", NBTConstants.TYPE_BYTE_ARRAY);
out.getOutputStream().writeInt(volume);
@ -217,7 +217,7 @@ public class SchematicWriter implements ClipboardWriter {
final List<CompoundTag> tileEntities = clipboard.IMP.getTileEntities();
out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class, tileEntities));
} else {
out.writeNamedTag("TileEntities", new ListTag(CompoundTag.class, new ArrayList<Tag>()));
out.writeNamedEmptyList("TileEntities");
}
List<Tag> entities = new ArrayList<Tag>();
@ -242,7 +242,11 @@ public class SchematicWriter implements ClipboardWriter {
entities.add(entityTag);
}
}
out.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities));
if (entities.isEmpty()) {
out.writeNamedEmptyList("Entities");
} else {
out.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities));
}
}
});
outputStream.flush();

View File

@ -115,6 +115,28 @@ public class BlockMask extends AbstractExtentMask {
return computedLegacyList;
}
public Collection<BaseBlock> getInverseBlocks() {
if (computedLegacyList != null) {
return computedLegacyList;
}
computedLegacyList = new LinkedHashSet<>();
for (int id = 0; id < FaweCache.getId(blocks.length); id++) {
boolean all = true;
ArrayList<BaseBlock> tmp = new ArrayList<BaseBlock>(16);
for (int data = 0; data < 16; data++) {
if (!blocks[FaweCache.getCombined(id, data)]) {
tmp.add(FaweCache.getBlock(id, data));
}
}
if (tmp.size() == 16) {
computedLegacyList.add(new BaseBlock(id, -1));
} else {
computedLegacyList.addAll(tmp);
}
}
return computedLegacyList;
}
@Override
public String toString() {
return StringMan.getString(getBlocks());

View File

@ -1,5 +1,6 @@
package com.sk89q.worldedit.function.pattern;
import com.boydti.fawe.FaweCache;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
@ -14,6 +15,14 @@ public class BlockPattern implements Pattern {
this.block = block;
}
public BlockPattern(int id) {
this.block = FaweCache.getBlock(id, 0);
}
public BlockPattern(int id, int data) {
this.block = FaweCache.getBlock(id, data);
}
@Override
public BaseBlock apply(Vector position) {
return block;

View File

@ -6,6 +6,7 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.Extent;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@ -19,7 +20,7 @@ public class RandomPattern extends AbstractPattern {
private Map<Pattern, Double> weights = new HashMap<>();
private RandomCollection<Pattern> collection;
private Set<Pattern> patterns;
private LinkedHashSet<Pattern> patterns = new LinkedHashSet<>();
/**
* Add a pattern to the weight list of patterns.
@ -34,7 +35,11 @@ public class RandomPattern extends AbstractPattern {
checkNotNull(pattern);
weights.put(pattern, chance);
collection = RandomCollection.of(weights);
this.patterns = weights.keySet();
this.patterns.add(pattern);
}
public Set<Pattern> getPatterns() {
return patterns;
}
@Override

View File

@ -58,6 +58,7 @@ public abstract class BreadthFirstSearch implements Operation {
private BlockVectorSet visited;
private final MappedFaweQueue mFaweQueue;
private BlockVectorSet queue;
private int currentDepth = 0;
private final int maxDepth;
private int affected = 0;
private int maxBranch = Integer.MAX_VALUE;
@ -107,6 +108,12 @@ public abstract class BreadthFirstSearch implements Operation {
}
}
public void resetVisited() {
queue.clear();
visited.clear();
affected = 0;
}
public void setVisited(BlockVectorSet set) {
this.visited = set;
}
@ -131,7 +138,7 @@ public abstract class BreadthFirstSearch implements Operation {
IntegerTrio[] dirs = getIntDirections();
BlockVectorSet tempQueue = new BlockVectorSet();
BlockVectorSet chunkLoadSet = new BlockVectorSet();
for (int layer = 0; !queue.isEmpty() && layer <= maxDepth; layer++) {
for (currentDepth = 0; !queue.isEmpty() && currentDepth <= maxDepth; currentDepth++) {
if (mFaweQueue != null && Settings.IMP.QUEUE.PRELOAD_CHUNKS > 1) {
int cx = Integer.MIN_VALUE;
int cz = Integer.MIN_VALUE;
@ -176,7 +183,7 @@ public abstract class BreadthFirstSearch implements Operation {
}
}
}
if (layer == maxDepth) {
if (currentDepth == maxDepth) {
break;
}
int size = queue.size();
@ -190,6 +197,10 @@ public abstract class BreadthFirstSearch implements Operation {
return null;
}
public int getDepth() {
return currentDepth;
}
@Override
public void addStatusMessages(List<String> messages) {
messages.add(BBC.VISITOR_BLOCK.format(getAffected()));

View File

@ -474,7 +474,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
if (!hasNext) {
throw new NoSuchElementException("End of iterator") {
@Override
public synchronized Throwable fillInStackTrace() {
public Throwable fillInStackTrace() {
return this;
}
};
@ -539,7 +539,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
if (!hasNext) {
throw new NoSuchElementException("End of iterator") {
@Override
public synchronized Throwable fillInStackTrace() {
public Throwable fillInStackTrace() {
return this;
}
};
@ -561,7 +561,6 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
};
}
@Override
public Iterable<Vector2D> asFlatRegion() {
return new Iterable<Vector2D>() {
@ -576,7 +575,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
@Override
public boolean hasNext() {
return (nextZ != Integer.MIN_VALUE);
return (nextZ != Integer.MAX_VALUE);
}
@Override
@ -586,7 +585,16 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
if (++nextX > max.getBlockX()) {
nextX = min.getBlockX();
if (++nextZ > max.getBlockZ()) {
nextZ = Integer.MIN_VALUE;
if (nextZ == Integer.MIN_VALUE) {
throw new NoSuchElementException("End of iterator") {
@Override
public Throwable fillInStackTrace() {
return this;
}
};
}
nextZ = Integer.MAX_VALUE;
nextX = Integer.MAX_VALUE;
}
}
return answer;
@ -623,6 +631,12 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
return new CuboidRegion(region.getMinimumPoint(), region.getMaximumPoint());
}
public static boolean contains(CuboidRegion region) {
Vector min = region.getMinimumPoint();
Vector max = region.getMaximumPoint();
return region.contains(min.getBlockX(), min.getBlockY(), min.getBlockZ()) && region.contains(max.getBlockX(), max.getBlockY(), max.getBlockZ());
}
/**
* Make a cuboid from the center.
*

View File

@ -143,9 +143,8 @@ public class BundledBlockData {
}
idMap.put(entry.id, entry);
String id = (entry.id.contains(":") ? entry.id.split(":")[1] : entry.id).toLowerCase().replace(" ", "_");
if (!idMap.containsKey(id)) {
idMap.put(id, entry);
}
localIdMap.putIfAbsent(id, entry);
idMap.putIfAbsent(id, entry);
legacyMap[entry.legacyId] = entry;
if (entry.states == null) {
return true;

View File

@ -58,8 +58,8 @@ shadowJar {
relocate 'org.yaml.snakeyaml', 'com.boydti.fawe.yaml'
dependencies {
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('it.unimi.dsi:fastutil:6.5.1'))
include(dependency(':core'))
include(dependency('org.yaml:snakeyaml:1.16'))
}

View File

@ -58,8 +58,8 @@ shadowJar {
relocate 'org.yaml.snakeyaml', 'com.boydti.fawe.yaml'
dependencies {
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('it.unimi.dsi:fastutil:6.5.1'))
include(dependency(':core'))
include(dependency('org.yaml:snakeyaml:1.16'))
}

View File

@ -49,8 +49,8 @@ shadowJar {
relocate 'org.yaml.snakeyaml', 'com.boydti.fawe.yaml'
dependencies {
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('it.unimi.dsi:fastutil:6.5.1'))
include(dependency(':core'))
include(dependency('org.yaml:snakeyaml:1.16'))
}

View File

@ -58,8 +58,8 @@ shadowJar {
relocate 'org.yaml.snakeyaml', 'com.boydti.fawe.yaml'
dependencies {
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('it.unimi.dsi:fastutil:6.5.1'))
include(dependency(':core'))
include(dependency('org.yaml:snakeyaml:1.16'))
}

View File

@ -57,8 +57,8 @@ shadowJar {
relocate 'org.yaml.snakeyaml', 'com.boydti.fawe.yaml'
dependencies {
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('it.unimi.dsi:fastutil:6.5.1'))
include(dependency(':core'))
include(dependency('org.yaml:snakeyaml:1.16'))
}

BIN
heightmap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -23,8 +23,8 @@ jar.enabled = false
shadowJar {
dependencies {
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('it.unimi.dsi:fastutil:6.5.1'))
include(dependency(name: 'worldedit-core-6.1.4-SNAPSHOT-dist'))
include(dependency('com.google.code.gson:gson:2.2.4'))
include(dependency('org.yaml:snakeyaml:1.16'))

BIN
region/r.0.-1.mca Normal file

Binary file not shown.

View File

@ -1,3 +1,3 @@
rootProject.name = 'FastAsyncWorldEdit'
include 'core', 'bukkit', 'favs', 'nukkit', 'sponge', 'forge1710', 'forge189', 'forge194', 'forge110', 'forge111'
include 'core', 'bukkit'//, 'favs', 'nukkit', 'sponge', 'forge1710', 'forge189', 'forge194', 'forge110', 'forge111'

View File

@ -76,8 +76,8 @@ shadowJar {
dependencies {
include(dependency(':core'))
include(dependency('com.github.luben:zstd-jni:1.1.1'))
include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('co.aikar:fastutil-lite:1.0'))
// include(dependency('org.javassist:javassist:3.22.0-CR1'))
include(dependency('it.unimi.dsi:fastutil:6.5.1'))
include(dependency(name: 'worldedit-core-6.1.7-SNAPSHOT-dist'))
include(dependency('com.sk89q.worldedit:worldedit-sponge:6.1.7-SNAPSHOT'))
include(dependency('org.yaml:snakeyaml:1.16'))