Fixed slow bukkit api placer (if no NMS placer exists) (it will try to
stay above 18.5 TPS)
Added more messages to translations
Added copy brush
Added resizable clipboard builder (API)
Added image download
This commit is contained in:
Jesse Boyd 2016-06-21 15:53:52 +10:00
parent 3d8a7f84cd
commit 8555276a78
26 changed files with 989 additions and 168 deletions

View File

@ -0,0 +1,201 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.bukkit.BukkitUtil;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.ArrayList;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Block;
public class BukkitChunk_All extends CharFaweChunk<Chunk> {
/**
* A FaweSections object represents a chunk and the blocks that you wish to change in it.
*
* @param parent
* @param x
* @param z
*/
public BukkitChunk_All(FaweQueue parent, int x, int z) {
super(parent, x, z);
}
@Override
public Chunk getNewChunk() {
return Bukkit.getWorld(getParent().getWorldName()).getChunkAt(getX(), getZ());
}
private int layer = -1;
private int index;
private boolean place = true;
/**
*
* @return
*/
public void execute(long start) {
int recommended = 25 + BukkitQueue_All.ALLOCATE;
boolean more = true;
FaweQueue parent = getParent();
final Chunk chunk = getChunk();
chunk.load(true);
final World world = chunk.getWorld();
char[][] sections = getCombinedIdArrays();
if (layer == -1) {
// Biomes
if (layer == 0) {
final int[][] biomes = getBiomeArray();
if (biomes != null) {
final LocalWorld lw = BukkitUtil.getLocalWorld(world);
final int X = getX() << 4;
final int Z = getZ() << 4;
final BaseBiome bb = new BaseBiome(0);
int last = 0;
for (int x = 0; x < 16; x++) {
final int[] array = biomes[x];
if (array == null) {
continue;
}
for (int z = 0; z < 16; z++) {
final int biome = array[z];
if (biome == 0) {
continue;
}
if (last != biome) {
last = biome;
bb.setId(biome);
}
lw.setBiome(new Vector2D(X + x, Z + z), bb);
}
}
}
}
} else if (index != 0) {
if (place) {
layer--;
} else {
layer++;
}
}
mainloop:
do {
if (place) {
if (++layer >= sections.length) {
place = false;
layer = sections.length - 1;
}
} else if (--layer < 0) {
more = false;
break;
}
try {
// Efficiently merge sections
int changes = getCount(layer);
int lighting = getRelight(layer);
if (changes == 0) {
continue;
}
final char[] newArray = sections[layer];
if (newArray == null) {
continue;
}
boolean checkTime = !((getAir(layer) == 4096 || (getCount(layer) == 4096 && getAir(layer) == 0) || (getCount(layer) == getAir(layer))) && getRelight(layer) == 0);
if (!checkTime || Settings.PARALLEL_THREADS > 1) {
ArrayList<Thread> threads = new ArrayList<Thread>();
for (int k = 0; k < 16; k++) {
final int l = k << 8;
final int y = FaweCache.CACHE_Y[layer][l];
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int m = l; m < l + 256; m++) {
char combined = newArray[m];
switch (combined) {
case 0:
continue;
case 1:
if (!place) {
int x = FaweCache.CACHE_X[layer][m];
int z = FaweCache.CACHE_Z[layer][m];
chunk.getBlock(x, y, z).setTypeId(0, false);
}
continue;
default:
if (place) {
int x = FaweCache.CACHE_X[layer][m];
int z = FaweCache.CACHE_Z[layer][m];
int id = combined >> 4;
int data = combined & 0xF;
Block block = chunk.getBlock(x, y, z);
if (data == 0) {
block.setTypeId(id, false);
} else {
block.setTypeIdAndData(id, (byte) data, false);
}
}
continue;
}
}
}
});
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
} else {
for (;index < 4096; index++) {
int j = place ? index : 4095 - index;
char combined = newArray[j];
switch (combined) {
case 0:
break;
case 1:
if (!place) {
int x = FaweCache.CACHE_X[layer][j];
int z = FaweCache.CACHE_Z[layer][j];
int y = FaweCache.CACHE_Y[layer][j];
chunk.getBlock(x, y, z).setTypeId(0, false);
}
break;
default:
if (place) {
int id = combined >> 4;
int data = combined & 0xF;
int x = FaweCache.CACHE_X[layer][j];
int z = FaweCache.CACHE_Z[layer][j];
int y = FaweCache.CACHE_Y[layer][j];
Block block = chunk.getBlock(x, y, z);
if (data == 0) {
block.setTypeId(id, false);
} else {
block.setTypeIdAndData(id, (byte) data, false);
}
}
break;
}
if (checkTime && System.currentTimeMillis() - start > recommended) {
index++;
break mainloop;
}
}
index = 0;
}
} catch (final Throwable e) {
MainUtil.handleError(e);
}
} while (System.currentTimeMillis() - start < recommended);
if (more || place) {
this.addToQueue();
}
}
}

View File

@ -1,21 +1,13 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.example.NMSMappedFaweQueue;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.worldedit.LocalWorld;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.bukkit.BukkitUtil;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
@ -23,7 +15,6 @@ import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Block;
public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMappedFaweQueue<World, CHUNK, CHUNKSECTIONS, SECTION> {
@ -143,115 +134,6 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
}
}
@Override
public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) {
try {
// TODO track stage
// TODO set task
final CharFaweChunk<Chunk> fs = ((CharFaweChunk<Chunk>) fc);
final Chunk chunk = fs.getChunk();
chunk.load(true);
final World world = chunk.getWorld();
char[][] sections = fs.getCombinedIdArrays();
boolean done = false;
boolean more = false;
// Efficiently merge sections
for (int j = 0; j < sections.length; j++) {
final int jf = j;
int changes = fs.getCount(j);
int lighting = fs.getRelight(j);
if (changes == 0) {
continue;
}
final char[] newArray = sections[j];
if (newArray == null) {
continue;
}
if (done) {
more = true;
break;
}
done = true;
sections[j] = null;
ArrayList<Thread> threads = new ArrayList<Thread>();
for (int k = 0; k < 16; k++) {
final int l = k << 8;
final int y = FaweCache.CACHE_Y[j][l];
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int m = l; m < l + 256; m++) {
int combined = newArray[m];
switch (combined) {
case 0:
continue;
case 1:
int x = FaweCache.CACHE_X[jf][m];
int z = FaweCache.CACHE_Z[jf][m];
chunk.getBlock(x, y, z).setTypeId(0, false);
continue;
default:
x = FaweCache.CACHE_X[jf][m];
z = FaweCache.CACHE_Z[jf][m];
int id = combined >> 4;
int data = combined & 0xF;
Block block = chunk.getBlock(x, y, z);
if (data == 0) {
block.setTypeId(id, false);
} else {
block.setTypeIdAndData(id, (byte) data, false);
}
continue;
}
}
}
});
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
}
if (more) {
fc.addToQueue();
return true;
}
// Biomes
final int[][] biomes = fs.getBiomeArray();
if (biomes != null) {
final LocalWorld lw = BukkitUtil.getLocalWorld(world);
final int X = fs.getX() << 4;
final int Z = fs.getZ() << 4;
final BaseBiome bb = new BaseBiome(0);
int last = 0;
for (int x = 0; x < 16; x++) {
final int[] array = biomes[x];
if (array == null) {
continue;
}
for (int z = 0; z < 16; z++) {
final int biome = array[z];
if (biome == 0) {
continue;
}
if (last != biome) {
last = biome;
bb.setId(biome);
}
lw.setBiome(new Vector2D(X + x, Z + z), bb);
}
}
}
return true;
} catch (final Throwable e) {
MainUtil.handleError(e);
}
return false;
}
@Override
public FaweChunk getFaweChunk(int x, int z) {
return new CharFaweChunk<Chunk>(this, x, z) {

View File

@ -1,14 +1,26 @@
package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.RunnableVal;
import com.sk89q.jnbt.CompoundTag;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Block;
public class BukkitQueue_All extends BukkitQueue_0<Chunk, Chunk, Chunk> {
public static int ALLOCATE;
public static double TPS_TARGET = 18.5;
public BukkitQueue_All(String world) {
super(world);
if (Settings.ALLOCATE != Integer.MIN_VALUE) {
ALLOCATE = Settings.ALLOCATE;
Settings.ALLOCATE = Integer.MIN_VALUE;
}
}
public int getCombinedId4Data(Chunk section, int x, int y, int z) {
@ -34,4 +46,36 @@ public class BukkitQueue_All extends BukkitQueue_0<Chunk, Chunk, Chunk> {
public Chunk getChunk(World world, int x, int z) {
return world.getChunkAt(x, z);
}
@Override
public FaweChunk getFaweChunk(int x, int z) {
return new BukkitChunk_All(this, x, z);
}
private int skip;
@Override
public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) {
if (skip > 0) {
skip--;
fc.addToQueue();
return true;
}
long start = System.currentTimeMillis();
((BukkitChunk_All) fc).execute(start);
if (System.currentTimeMillis() - start > 50 || Fawe.get().getTPS() < TPS_TARGET) {
skip = 10;
}
return true;
}
@Override
public void startSet(boolean parallel) {
super.startSet(true);
}
@Override
public void endSet(boolean parallel) {
super.endSet(true);
}
}

View File

@ -10,6 +10,7 @@ public class BukkitMain_110 extends ABukkitMain {
@Override
public BukkitQueue_0 getQueue(String world) {
return new com.boydti.fawe.bukkit.v1_10.BukkitQueue_1_10(world);
// return new BukkitQueue_All(world);
}
@Override

View File

@ -626,4 +626,14 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
int j = FaweCache.CACHE_J[y][x][z];
return array[j] >> 4;
}
@Override
public FaweChunk getFaweChunk(int x, int z) {
return new CharFaweChunk<Chunk>(this, x, z) {
@Override
public Chunk getNewChunk() {
return BukkitQueue18R3.this.getWorld().getChunkAt(getX(), getZ());
}
};
}
}

View File

@ -11,6 +11,7 @@ import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.regions.general.plot.PlotSquaredFeature;
import com.boydti.fawe.util.FaweTimer;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.TaskManager;
@ -111,6 +112,11 @@ public class Fawe {
*/
private static Fawe INSTANCE;
/**
* TPS timer
*/
private final FaweTimer timer;
/**
* Get the implementation specific class
* @return
@ -173,6 +179,7 @@ public class Fawe {
MainUtil.deleteOlder(new File(IMP.getDirectory(), "clipboard"), TimeUnit.DAYS.toMillis(Settings.DELETE_CLIPBOARD_AFTER_DAYS));
TaskManager.IMP = this.IMP.getTaskManager();
TaskManager.IMP.repeat(timer = new FaweTimer(), 50);
if (Settings.METRICS) {
this.IMP.startMetrics();
}
@ -201,6 +208,10 @@ public class Fawe {
this.setupMemoryListener();
}
public double getTPS() {
return timer.getTPS();
}
private void setupEvents() {
WorldEdit.getInstance().getEventBus().register(new WESubscriber());
}

View File

@ -9,6 +9,7 @@ import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.schematic.Schematic;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
@ -139,6 +140,17 @@ public class FaweAPI {
});
}
/**
* Just forwards to ClipboardFormat.SCHEMATIC.load(file)
* @see com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat
* @see com.boydti.fawe.object.schematic.Schematic
* @param file
* @return
*/
public static Schematic load(File file) throws IOException {
return ClipboardFormat.SCHEMATIC.load(file);
}
/**
* Get a list of supported protection plugin masks.
* @return Set of FaweMaskManager

View File

@ -44,6 +44,7 @@ public enum BBC {
DOWNLOAD_LINK("%s", "Web"),
COMMAND_COPY("%s0 blocks were copied", "WorldEdit.Copy"),
COMMAND_CUT("%s0 blocks were cut", "WorldEdit.Cut"),
COMMAND_PASTE("The clipboard has been pasted at %s0", "WorldEdit.Paste"),
@ -81,6 +82,7 @@ public enum BBC {
BRUSH_EXTINGUISHER("Extinguisher equipped (%s0).", "WorldEdit.Brush"),
BRUSH_GRAVITY("Gravity brush equipped (%s0)", "WorldEdit.Brush"),
BRUSH_HEIGHT("Height brush equipped (%s0)", "WorldEdit.Brush"),
BRUSH_COPY("Copy brush equipped (%s0)", "WorldEdit.Brush"),
BRUSH_HEIGHT_INVALID("Invalid height map file (%s0)", "WorldEdit.Brush"),
BRUSH_SMOOTH("Smooth brush equipped (%s0 x %s1 using %s2).", "WorldEdit.Brush"),
BRUSH_SPHERE("Sphere brush shape equipped (%s0).", "WorldEdit.Brush"),
@ -100,10 +102,11 @@ public enum BBC {
SELECTOR_CUBOID_POS1("First position set to %s0 %s1.", "WorldEdit.Selector"),
SELECTOR_CUBOID_POS2("Second position set to %s0 %s1.", "WorldEdit.Selector"),
COMMAND_INVALID_SYNTAX("The command was not used properly (no more help available).", "WorldEdit.Command"),
PROGRESS_MESSAGE("[ Queue: %s0 | Dispatched: %s1 ]", "Progress"),
PROGRESS_DONE ("[ Took: %s0s ]", "Progress"),
COMMAND_SYNTAX("&cUsage: &7%s0", "Error"),
NO_PERM("&cYou are lacking the permission node: %s0", "Error"),
SCHEMATIC_NOT_FOUND("&cSchematic not found: &7%s0", "Error"),
@ -308,6 +311,10 @@ public enum BBC {
}
}
public static String getPrefix() {
return (PREFIX.isEmpty() ? "" : PREFIX.s() + " ");
}
public void send(final FawePlayer<?> player, final Object... args) {
if (isEmpty()) {
return;

View File

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

View File

@ -0,0 +1,71 @@
package com.boydti.fawe.object.brush;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.clipboard.ResizableClipboardBuilder;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.function.NullRegionFunction;
import com.boydti.fawe.object.function.mask.AbstractDelegateMask;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.command.tool.BrushTool;
import com.sk89q.worldedit.command.tool.brush.Brush;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
import com.sk89q.worldedit.session.ClipboardHolder;
public class CopyBrush implements Brush {
private final BrushTool tool;
private final LocalSession session;
private final Player player;
public CopyBrush(Player player, LocalSession session, BrushTool tool) {
this.tool = tool;
this.session = session;
this.player = player;
}
@Override
public void build(final EditSession editSession, Vector position, Pattern pattern, double size) throws MaxChangedBlocksException {
Mask mask = tool.getMask();
if (mask == null) {
mask = Masks.alwaysTrue();
}
final ResizableClipboardBuilder builder = new ResizableClipboardBuilder(editSession.getWorld());
final int size2 = (int) (size * size);
final int minY = position.getBlockY();
mask = new AbstractDelegateMask(mask) {
private int visited = 0;
@Override
public boolean test(Vector vector) {
if (super.test(vector) && vector.getBlockY() >= minY) {
BaseBlock block = editSession.getLazyBlock(vector);
if (block != EditSession.nullBlock) {
if (visited++ > size2) {
throw new FaweException(BBC.WORLDEDIT_CANCEL_REASON_MAX_CHECKS);
}
builder.add(vector, EditSession.nullBlock, block);
return true;
}
}
return false;
}
};
RecursiveVisitor visitor = new RecursiveVisitor(mask, new NullRegionFunction());
visitor.visit(position);
Operations.completeBlindly(visitor);
// To clipboard
Clipboard clipboard = builder.build();
session.setClipboard(new ClipboardHolder(clipboard, editSession.getWorld().getWorldData()));
int blocks = builder.size();
BBC.COMMAND_COPY.send(player, blocks);
}
}

View File

@ -22,7 +22,7 @@ public class HeightBrush implements Brush {
double yscale = 1;
private final BrushTool tool;
public HeightBrush(File file, int rotation, double yscale, BrushTool tool, EditSession session, Clipboard clipboard) {
public HeightBrush(File file, int rotation, double yscale, BrushTool tool, Clipboard clipboard) {
this.tool = tool;
this.rotation = (rotation / 90) % 4;
this.yscale = yscale;

View File

@ -271,48 +271,44 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
if (summary != null) {
return summary;
}
try {
if (bdFile.exists()) {
int ox = getOriginX();
int oz = getOriginZ();
if ((ox != 0 || oz != 0) && !requiredRegion.isIn(ox, oz)) {
return summary = new DiskStorageSummary(ox, oz);
if (bdFile.exists()) {
int ox = getOriginX();
int oz = getOriginZ();
if ((ox != 0 || oz != 0) && !requiredRegion.isIn(ox, oz)) {
return summary = new DiskStorageSummary(ox, oz);
}
try (FileInputStream fis = new FileInputStream(bdFile)) {
FaweInputStream gis = MainUtil.getCompressedIS(fis);
// skip mode
gis.skip(1);
// origin
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
setOrigin(ox, oz);
summary = new DiskStorageSummary(ox, oz);
if (!requiredRegion.isIn(ox, oz)) {
fis.close();
gis.close();
return summary;
}
try (FileInputStream fis = new FileInputStream(bdFile)) {
FaweInputStream gis = MainUtil.getCompressedIS(fis);
// skip mode
gis.skip(1);
// origin
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + (gis.read() << 0));
setOrigin(ox, oz);
summary = new DiskStorageSummary(ox, oz);
if (!requiredRegion.isIn(ox, oz)) {
byte[] buffer = new byte[9];
int i = 0;
int amount = (Settings.BUFFER_SIZE - HEADER_SIZE) / 9;
while (!shallow && ++i < amount) {
if (gis.read(buffer) == -1) {
fis.close();
gis.close();
return summary;
}
byte[] buffer = new byte[9];
int i = 0;
int amount = (Settings.BUFFER_SIZE - HEADER_SIZE) / 9;
while (!shallow && ++i < amount) {
if (gis.read(buffer) == -1) {
fis.close();
gis.close();
return summary;
}
int x = ((byte) buffer[0] & 0xFF) + ((byte) buffer[1] << 8) + ox;
int z = ((byte) buffer[2] & 0xFF) + ((byte) buffer[3] << 8) + oz;
int combined1 = buffer[7] & 0xFF;
int combined2 = buffer[8] & 0xFF;
summary.add(x, z, ((combined2 << 4) + (combined1 >> 4)));
}
} catch (IOException e) {
MainUtil.handleError(e);
int x = ((byte) buffer[0] & 0xFF) + ((byte) buffer[1] << 8) + ox;
int z = ((byte) buffer[2] & 0xFF) + ((byte) buffer[3] << 8) + oz;
int combined1 = buffer[7] & 0xFF;
int combined2 = buffer[8] & 0xFF;
summary.add(x, z, ((combined2 << 4) + (combined1 >> 4)));
}
} catch (IOException e) {
MainUtil.handleError(e);
}
} catch (Exception e) {
e.printStackTrace();
}
return summary;
}

View File

@ -0,0 +1,89 @@
package com.boydti.fawe.object.clipboard;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.change.MutableBlockChange;
import com.boydti.fawe.object.change.MutableTileChange;
import com.boydti.fawe.object.changeset.MemoryOptimizedHistory;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.World;
import java.util.Iterator;
public class ResizableClipboardBuilder extends MemoryOptimizedHistory {
private int minX = Integer.MAX_VALUE;
private int minY = Integer.MAX_VALUE;
private int minZ = Integer.MAX_VALUE;
private int maxX = Integer.MIN_VALUE;
private int maxY = Integer.MIN_VALUE;
private int maxZ = Integer.MIN_VALUE;
public ResizableClipboardBuilder(World world) {
super(world);
}
@Override
public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
super.add(x, y, z, combinedFrom, combinedTo);
if (x < minX) {
if (maxX == Integer.MIN_VALUE) {
maxX = x;
}
minX = x;
}
else if (x > maxX) {
maxX = x;
}
if (y < minY) {
if (maxY == Integer.MIN_VALUE) {
maxY = y;
}
minY = y;
}
else if (y > maxY) {
maxY = y;
}
if (z < minZ) {
if (maxZ == Integer.MIN_VALUE) {
maxZ = z;
}
minZ = z;
}
else if (z > maxZ) {
maxZ = z;
}
}
public Clipboard build() {
Vector pos1 = new Vector(minX, minY, minZ);
Vector pos2 = new Vector(maxX, maxY, maxZ);
CuboidRegion region = new CuboidRegion(pos1, pos2);
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
Iterator<Change> iter = getIterator(true);
try {
while (iter.hasNext()) {
Change change = iter.next();
if (change instanceof MutableBlockChange) {
MutableBlockChange blockChange = (MutableBlockChange) change;
clipboard.setBlock(blockChange.x, blockChange.y, blockChange.z, FaweCache.getBlock(blockChange.id, blockChange.data));
} else if (change instanceof MutableTileChange) {
MutableTileChange tileChange = (MutableTileChange) change;
int x = tileChange.tag.getInt("x");
int y = tileChange.tag.getInt("y");
int z = tileChange.tag.getInt("z");
clipboard.setTile(x, y, z, tileChange.tag);
}
}
} catch (WorldEditException e) {
e.printStackTrace();
}
return clipboard;
}
}

View File

@ -0,0 +1,12 @@
package com.boydti.fawe.object.function;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction;
public class NullRegionFunction implements RegionFunction {
@Override
public boolean apply(Vector position) throws WorldEditException {
return false;
}
}

View File

@ -0,0 +1,26 @@
package com.boydti.fawe.object.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import javax.annotation.Nullable;
public class AbstractDelegateMask implements Mask {
private final Mask parent;
public AbstractDelegateMask(Mask parent) {
this.parent = parent;
}
@Override
public boolean test(Vector vector) {
return parent.test(vector);
}
@Nullable
@Override
public Mask2D toMask2D() {
return parent.toMask2D();
}
}

View File

@ -68,7 +68,7 @@ public class PNGWriter implements ClipboardWriter {
}
for (int j = 0; j < dpxj.length; j++) {
dpxj[j] = cx + j * d;
dpyj[j] = imageSize + d + j * d_2;
dpyj[j] = imageSize / 2 + d + j * d_2;
}
for (int i = 0; i < Math.max(256, dpxi.length); i++) {
dpxi[i] = i * d;

View File

@ -0,0 +1,118 @@
package com.boydti.fawe.object.schematic;
import com.boydti.fawe.object.clipboard.LazyClipboard;
import com.boydti.fawe.util.EditSessionBuilder;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.World;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
public class Schematic {
private final Clipboard clipboard;
public Schematic(Clipboard clipboard) {
checkNotNull(clipboard);
this.clipboard = clipboard;
}
/**
* Get the schematic for a region
* @param region
*/
public Schematic(Region region) {
checkNotNull(region);
checkNotNull(region.getWorld());
EditSession session = new EditSessionBuilder(region.getWorld()).allowedRegionsEverywhere().autoQueue(false).build();
this.clipboard = new BlockArrayClipboard(region, LazyClipboard.of(session, region));
}
/**
* Forwards to paste(world, to, true, true, null)
* @param world
* @param to
* @return
*/
public EditSession paste(World world, Vector to) {
return paste(world, to, true, true, null);
}
public void save(File file, ClipboardFormat format) throws IOException {
checkNotNull(file);
checkNotNull(format);
if (!file.exists()) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
file.createNewFile();
}
save(new FileOutputStream(file), format);
}
/**
* Save this schematic to a stream
* @param stream
* @param format
* @throws IOException
*/
public void save(OutputStream stream, ClipboardFormat format) throws IOException {
checkNotNull(stream);
checkNotNull(format);
format.getWriter(stream).write(clipboard, clipboard.getRegion().getWorld().getWorldData());
}
/**
* Paste this schematic in a world
* @param world
* @param to
* @param allowUndo
* @param pasteAir
* @param transform
* @return
*/
public EditSession paste(World world, Vector to, boolean allowUndo, boolean pasteAir, @Nullable Transform transform) {
checkNotNull(world);
checkNotNull(to);
Region region = clipboard.getRegion();
EditSessionBuilder builder = new EditSessionBuilder(world).autoQueue(true).checkMemory(false).allowedRegionsEverywhere().limitUnlimited();
EditSession editSession;
if (allowUndo) {
editSession = builder.build();
} else {
editSession = builder.changeSetNull().build();
}
Extent extent = clipboard;
if (transform != null) {
extent = new BlockTransformExtent(clipboard, transform, world.getWorldData().getBlockRegistry());
}
ForwardExtentCopy copy = new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(), editSession, to);
copy.setTransform(transform);
if (!pasteAir) {
copy.setSourceMask(new ExistingBlockMask(clipboard));
}
try {
Operations.completeLegacy(copy);
} catch (MaxChangedBlocksException e) {
e.printStackTrace();
}
editSession.flushQueue();
return editSession;
}
}

View File

@ -0,0 +1,50 @@
package com.boydti.fawe.object.visitor;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
import java.util.Collection;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Visits adjacent points on the same X-Z plane as long as the points
* pass the given mask, and then executes the provided region
* function on the entire column.
*
* <p>This is used by {@code //fill}.</p>
*/
public class AboveVisitor extends RecursiveVisitor {
private int baseY;
/**
* Create a new visitor.
*
* @param mask the mask
* @param function the function
* @param baseY the base Y
*/
public AboveVisitor(Mask mask, RegionFunction function, int baseY) {
super(mask, function);
checkNotNull(mask);
this.baseY = baseY;
Collection<Vector> directions = getDirections();
directions.clear();
directions.add(new Vector(1, 0, 0));
directions.add(new Vector(-1, 0, 0));
directions.add(new Vector(0, 0, 1));
directions.add(new Vector(0, 0, -1));
directions.add(new Vector(0, 1, 0));
directions.add(new Vector(0, -1, 0));
}
@Override
protected boolean isVisitable(Vector from, Vector to) {
return (from.getBlockY() >= baseY) && super.isVisitable(from, to);
}
}

View File

@ -0,0 +1,45 @@
package com.boydti.fawe.util;
public class FaweTimer implements Runnable {
private long tick = 0;
private final double[] history = new double[] {20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d,20d};
private int historyIndex = 0;
private long lastPoll = System.nanoTime();
private final long tickInterval = 50;
@Override
public void run() {
final long startTime = System.nanoTime();
final long currentTime = System.currentTimeMillis();
long timeSpent = (startTime - lastPoll) / 1000;
if (timeSpent == 0)
{
timeSpent = 1;
}
tick++;
double tps = tickInterval * 1000000.0 / timeSpent;
history[historyIndex++] = tps;
if (historyIndex >= history.length) {
historyIndex = 0;
}
lastPoll = startTime;
}
private long lastGetTPSTick = 0;
private double lastGetTPSValue = 20d;
public double getTPS() {
if (tick == lastGetTPSTick) {
return lastGetTPSValue;
}
double total = 0;
for (double tps : history) {
total += tps;
}
lastGetTPSValue = total / history.length;
lastGetTPSTick = tick;
return lastGetTPSValue;
}
}

View File

@ -0,0 +1,62 @@
package com.boydti.fawe.util;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HastebinUtility {
public static final String BIN_URL = "http://hastebin.com/documents", USER_AGENT = "Mozilla/5.0";
public static final Pattern PATTERN = Pattern.compile("\\{\"key\":\"([\\S\\s]*)\"\\}");
public static String upload(final String string) throws IOException {
final URL url = new URL(BIN_URL);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("User-Agent", USER_AGENT);
connection.setDoOutput(true);
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(string.getBytes());
outputStream.flush();
}
StringBuilder response;
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
response = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
}
Matcher matcher = PATTERN.matcher(response.toString());
if (matcher.matches()) {
return "http://hastebin.com/" + matcher.group(1);
} else {
throw new RuntimeException("Couldn't read response!");
}
}
public static String upload(final File file) throws IOException {
final StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
int i = 0;
while ((line = reader.readLine()) != null && i++ < 1000) {
content.append(line).append("\n");
}
}
return upload(content.toString());
}
}

View File

@ -0,0 +1,71 @@
package com.boydti.fawe.util;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import javax.xml.bind.DatatypeConverter;
public class ImgurUtility {
public static final String CLIENT_ID = "50e34b65351eb07";
public static URL uploadImage(File file) throws IOException {
return uploadImage(new FileInputStream(file));
}
public static URL uploadImage(InputStream is) throws IOException {
is = new BufferedInputStream(is);
ByteArrayOutputStream baos = new ByteArrayOutputStream(Short.MAX_VALUE);
int d;
while ((d = is.read()) != -1) {
baos.write(d);
}
baos.flush();
return uploadImage(baos.toByteArray());
}
public static URL uploadImage(byte[] image) throws IOException {
String json = getImgurContent(CLIENT_ID, image);
Gson gson = new Gson();
JsonObject obj = gson.fromJson(json, JsonObject.class);
JsonObject data = obj.get("data").getAsJsonObject();
String link = data.get("link").getAsString();
return new URL(link);
}
public static String getImgurContent(String clientID, byte[] image) throws IOException {
String imageString = DatatypeConverter.printBase64Binary(image);
URL url = new URL("https://api.imgur.com/3/image");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
String data = URLEncoder.encode("image", "UTF-8") + "=" + URLEncoder.encode(imageString, "UTF-8");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Authorization", "Client-ID " + clientID);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.connect();
StringBuilder stb = new StringBuilder();
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(data);
wr.flush();
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null) {
stb.append(line).append("\n");
}
wr.close();
rd.close();
return stb.toString();
}
}

View File

@ -1,10 +1,12 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.RunnableVal;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
public abstract class TaskManager {
@ -38,6 +40,64 @@ public abstract class TaskManager {
*/
public abstract void task(final Runnable r);
/**
* Run a bunch of tasks in parallel
* @param runnables The tasks to run
* @param numThreads Number of threads (null = config.yml parallel threads)
*/
public void parallel(Collection<Runnable> runnables, @Nullable Integer numThreads) {
if (runnables == null) {
return;
}
if (numThreads == null) {
numThreads = Settings.PARALLEL_THREADS;
}
if (numThreads <= 1) {
for (Runnable run : runnables) {
if (run != null) {
run.run();
}
}
return;
}
int numRuns = runnables.size();
int amountPerThread = 1 + numRuns / numThreads;
final Runnable[][] split = new Runnable[numThreads][amountPerThread];
Thread[] threads = new Thread[numThreads];
int i = 0;
int j = 0;
for (Runnable run : runnables) {
split[i][j] = run;
if (++i >= numThreads) {
i = 0;
j++;
}
}
for (i = 0; i < threads.length; i++) {
final Runnable[] toRun = split[i];
Thread thread = threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < toRun.length; j++) {
Runnable run = toRun[j];
if (run != null) {
run.run();
}
}
}
});
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Run a task on the current thread or asynchronously
* - If it's already the main thread, it will jst call run()

View File

@ -24,6 +24,7 @@ import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.brush.CopyBrush;
import com.boydti.fawe.object.brush.HeightBrush;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
@ -135,7 +136,7 @@ public class BrushCommands {
}
@Command(
aliases = { "clipboard", "copy" },
aliases = { "clipboard", "paste" },
usage = "",
desc = "Choose the clipboard brush",
help =
@ -248,13 +249,31 @@ public class BrushCommands {
BrushTool tool = session.getBrushTool(player.getItemInHand());
tool.setSize(radius);
try {
tool.setBrush(new HeightBrush(file, rotation, yscale, tool, editSession, session.getClipboard().getClipboard()), "worldedit.brush.height");
tool.setBrush(new HeightBrush(file, rotation, yscale, tool, session.getClipboard().getClipboard()), "worldedit.brush.height");
} catch (EmptyClipboardException ignore) {
tool.setBrush(new HeightBrush(file, rotation, yscale, tool, editSession, null), "worldedit.brush.height");
tool.setBrush(new HeightBrush(file, rotation, yscale, tool, null), "worldedit.brush.height");
}
BBC.BRUSH_HEIGHT.send(player, radius);
}
@Command(
aliases = { "copy" },
usage = "[depth]",
desc = "Copy brush",
help =
"Right click the base of an object to copy.\n",
min = 0,
max = 1
)
@CommandPermissions("worldedit.brush.copy")
public void copy(Player player, LocalSession session, EditSession editSession, @Optional("5") double radius, @Optional("") final String filename, @Optional("0") final int rotation, @Optional("1") final double yscale) throws WorldEditException {
worldEdit.checkMaxBrushRadius(radius);
BrushTool tool = session.getBrushTool(player.getItemInHand());
tool.setSize(radius);
tool.setBrush(new CopyBrush(player, session, tool), "worldedit.brush.copy");
BBC.BRUSH_COPY.send(player, radius);
}
@Command(
aliases = { "butcher", "kill" },
usage = "[radius]",

View File

@ -23,6 +23,7 @@ import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.clipboard.LazyClipboard;
import com.boydti.fawe.util.ImgurUtility;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissions;
@ -41,6 +42,7 @@ import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.function.block.BlockReplace;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
@ -58,6 +60,9 @@ import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.command.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
@ -220,7 +225,31 @@ public class ClipboardCommands {
ClipboardHolder holder = session.getClipboard();
Clipboard clipboard = holder.getClipboard();
BBC.GENERATING_LINK.send(player, formatName);
URL url = FaweAPI.upload(clipboard, format);
URL url;
switch (format) {
case PNG:
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(Short.MAX_VALUE);
ClipboardWriter writer = format.getWriter(baos);
writer.write(clipboard, null);
baos.flush();
FileOutputStream fos = new FileOutputStream("blah.png");
fos.write(baos.toByteArray());
fos.close();
url = null;
url = ImgurUtility.uploadImage(baos.toByteArray());
} catch (IOException e) {
e.printStackTrace();
url = null;
}
break;
case SCHEMATIC:
url = FaweAPI.upload(clipboard, format);
break;
default:
url = null;
break;
}
if (url == null) {
BBC.GENERATING_LINK_FAILED.send(player);
} else {

View File

@ -254,7 +254,7 @@ public final class CommandManager {
try {
dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]);
} catch (CommandPermissionsException e) {
actor.printError("You are not permitted to do that. Are you in the right mode?");
BBC.NO_PERM.send(actor, "worldedit.*");
} catch (InvalidUsageException e) {
if (e.isFullHelpSuggested()) {
actor.printRaw(ColorCodeBuilder.asColorCodes(new CommandUsageBox(e.getCommand(), e.getCommandUsed("/", ""), locals)));
@ -264,8 +264,8 @@ public final class CommandManager {
}
} else {
String message = e.getMessage();
actor.printError(message != null ? message : "The command was not used properly (no more help available).");
actor.printError("Usage: " + e.getSimpleUsageString("/"));
actor.print(BBC.getPrefix() + (message != null ? message : "The command was not used properly (no more help available)."));
BBC.COMMAND_SYNTAX.send(actor, e.getSimpleUsageString("/"));
}
} catch (WrappedCommandException e) {
FaweException faweException = FaweException.get(e);

View File

@ -23,6 +23,7 @@ import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.schematic.FaweFormat;
import com.boydti.fawe.object.schematic.PNGWriter;
import com.boydti.fawe.object.schematic.Schematic;
import com.boydti.fawe.object.schematic.StructureFormat;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.jnbt.NBTConstants;
@ -138,7 +139,7 @@ public enum ClipboardFormat {
}
},
PNG("png") {
PNG("png", "image") {
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
return null;
@ -286,6 +287,14 @@ public enum ClipboardFormat {
*/
public abstract ClipboardWriter getWriter(OutputStream outputStream) throws IOException;
public Schematic load(File file) throws IOException {
return load(new FileInputStream(file));
}
public Schematic load(InputStream stream) throws IOException {
return new Schematic(this.getReader(stream).read(null));
}
/**
* Get the file extension used
* @return file extension string