boy0001 9c635810b0 Multiple changes
Working on async schematic saving
Default plot clearing is now properly async (and somewhat slower)
Offline mode servers now default to lowercase (there has been ample time
to update)
Fixed some issues with plot expiry
Fixed some issues with UUID caching
Optimized UUID fetching from cache (marginal)
2015-07-19 23:12:48 +10:00

568 lines
21 KiB
Java

package com.intellectualcrafters.plot.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.bukkit.Bukkit;
import com.intellectualcrafters.jnbt.ByteArrayTag;
import com.intellectualcrafters.jnbt.CompoundTag;
import com.intellectualcrafters.jnbt.IntTag;
import com.intellectualcrafters.jnbt.ListTag;
import com.intellectualcrafters.jnbt.NBTInputStream;
import com.intellectualcrafters.jnbt.NBTOutputStream;
import com.intellectualcrafters.jnbt.ShortTag;
import com.intellectualcrafters.jnbt.StringTag;
import com.intellectualcrafters.jnbt.Tag;
import com.intellectualcrafters.plot.PS;
import com.intellectualcrafters.plot.config.Settings;
import com.intellectualcrafters.plot.object.Location;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotId;
import com.intellectualcrafters.plot.object.RunnableVal;
import com.intellectualcrafters.plot.object.schematic.PlotItem;
import com.intellectualcrafters.plot.object.schematic.StateWrapper;
import com.intellectualcrafters.plot.util.bukkit.BukkitUtil;
import com.intellectualcrafters.plot.util.bukkit.UUIDHandler;
public abstract class SchematicHandler {
public static SchematicHandler manager = new BukkitSchematicHandler();
private boolean exportAll = false;
public boolean exportAll(final Collection<Plot> collection, final File outputDir, final String namingScheme, final Runnable ifSuccess) {
if (exportAll) {
return false;
}
if (collection.size() == 0) {
return false;
}
exportAll = true;
final ArrayList<Plot> plots = new ArrayList<Plot>(collection);
TaskManager.runTask(new Runnable() {
@Override
public void run() {
if (plots.size() == 0) {
exportAll = false;
TaskManager.runTask(ifSuccess);
return;
}
Iterator<Plot> i = plots.iterator();
final Plot plot = i.next();
i.remove();
String o = UUIDHandler.getName(plot.owner);
if (o == null) {
o = "unknown";
}
final String name;
if (namingScheme == null) {
name = plot.id.x + ";" + plot.id.y + "," + plot.world + "," + o;
}
else {
name = namingScheme.replaceAll("%owner%", o).replaceAll("%id%", plot.id.toString()).replaceAll("%idx%", plot.id.x + "").replaceAll("%idy%", plot.id.y + "").replaceAll("%world%", plot.world);
}
final String directory;
if (outputDir == null) {
directory = Settings.SCHEMATIC_SAVE_PATH;
}
else {
directory = outputDir.getPath();
}
if (PS.get().worldEdit != null) {
new WorldEditSchematic().saveSchematic(directory + File.separator + name + ".schematic", plot.world, plot.id);
}
else {
final Runnable THIS = this;
SchematicHandler.manager.getCompoundTag(plot.world, plot.id, new RunnableVal<CompoundTag>() {
@Override
public void run() {
if (value == null) {
MainUtil.sendMessage(null, "&7 - Skipped plot &c" + plot.id);
}
else {
TaskManager.runTaskAsync(new Runnable() {
@Override
public void run() {
MainUtil.sendMessage(null, "&6ID: " + plot.id);
final boolean result = SchematicHandler.manager.save(value, directory + File.separator + name + ".schematic");
if (!result) {
MainUtil.sendMessage(null, "&7 - Failed to save &c" + plot.id);
} else {
MainUtil.sendMessage(null, "&7 - &a success: " + plot.id);
}
TaskManager.runTask(new Runnable() {
@Override
public void run() {
THIS.run();
}
});
}
});
}
}
});
}
}
});
return true;
}
/**
* Paste a schematic
*
* @param schematic the schematic object to paste
* @param plot plot to paste in
* @param x_offset offset x to paste it from plot origin
* @param z_offset offset z to paste it from plot origin
*
* @return boolean true if succeeded
*/
public boolean paste(final Schematic schematic, final Plot plot, final int x_offset, final int z_offset) {
if (schematic == null) {
PS.log("Schematic == null :|");
return false;
}
try {
final Dimension demensions = schematic.getSchematicDimension();
final int WIDTH = demensions.getX();
final int LENGTH = demensions.getZ();
final int HEIGHT = demensions.getY();
final DataCollection[] blocks = schematic.getBlockCollection();
Location l1 = MainUtil.getPlotBottomLoc(plot.world, plot.getId());
final int sy = BukkitUtil.getHeighestBlock(plot.world, l1.getX() + 1, l1.getZ() + 1);
if (!(HEIGHT == BukkitUtil.getMaxHeight(plot.world))) {
l1 = l1.add(1, sy - 1, 1);
}
else {
l1 = l1.add(1, 0, 1);
}
int X = l1.getX();
int Y = l1.getY();
int Z = l1.getZ();
final int[] xl = new int[blocks.length];
final int[] yl = new int[blocks.length];
final int[] zl = new int[blocks.length];
final int[] ids = new int[blocks.length];
final byte[] data = new byte[blocks.length];
for (int x = 0; x < WIDTH; x++) {
for (int z = 0; z < LENGTH; z++) {
for (int y = 0; y < HEIGHT; y++) {
final int index = (y * WIDTH * LENGTH) + (z * WIDTH) + x;
final DataCollection block = blocks[index];
xl[index] = x + X;
yl[index] = y + Y;
zl[index] = z + Z;
ids[index] = block.block;
data[index] = block.data;
}
}
}
BlockManager.setBlocks(plot.world, xl, yl, zl, ids, data);
pasteStates(schematic, plot, x_offset, z_offset);
} catch (final Exception e) {
return false;
}
return true;
}
public boolean pasteStates(final Schematic schematic, final Plot plot, final int x_offset, final int z_offset) {
if (schematic == null) {
PS.log("Schematic == null :|");
return false;
}
HashSet<PlotItem> items = schematic.getItems();
if (items == null) {
return false;
}
Location l1 = MainUtil.getPlotBottomLoc(plot.world, plot.getId());
final int sy = BukkitUtil.getHeighestBlock(plot.world, l1.getX() + 1, l1.getZ() + 1);
final Dimension demensions = schematic.getSchematicDimension();
final int HEIGHT = demensions.getY();
if (!(HEIGHT == BukkitUtil.getMaxHeight(plot.world))) {
l1 = l1.add(1, sy - 1, 1);
} else {
l1 = l1.add(1, 0, 1);
}
int X = l1.getX() + x_offset;
int Y = l1.getY();
int Z = l1.getZ() + z_offset;
for (PlotItem item : items) {
item.x += X;
item.y += Y;
item.z += Z;
BlockManager.manager.addItems(plot.world, item);
}
return true;
}
public Schematic getSchematic(final CompoundTag tag, final File file) {
final Map<String, Tag> tagMap = tag.getValue();
byte[] addId = new byte[0];
if (tagMap.containsKey("AddBlocks")) {
addId = ByteArrayTag.class.cast(tagMap.get("AddBlocks")).getValue();
}
final short width = ShortTag.class.cast(tagMap.get("Width")).getValue();
final short length = ShortTag.class.cast(tagMap.get("Length")).getValue();
final short height = ShortTag.class.cast(tagMap.get("Height")).getValue();
final byte[] b = ByteArrayTag.class.cast(tagMap.get("Blocks")).getValue();
final byte[] d = ByteArrayTag.class.cast(tagMap.get("Data")).getValue();
final short[] blocks = new short[b.length];
final Dimension dimension = new Dimension(width, height, length);
for (int index = 0; index < b.length; index++) {
if ((index >> 1) >= addId.length) {
blocks[index] = (short) (b[index] & 0xFF);
} else {
if ((index & 1) == 0) {
blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (b[index] & 0xFF));
} else {
blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (b[index] & 0xFF));
}
}
}
final DataCollection[] collection = new DataCollection[b.length];
for (int x = 0; x < b.length; x++) {
collection[x] = new DataCollection(blocks[x], d[x]);
}
Schematic schem = new Schematic(collection, dimension, file);
try {
List<Tag> blockStates = ListTag.class.cast(tagMap.get("TileEntities")).getValue();
for (Tag stateTag : blockStates) {
CompoundTag ct = ((CompoundTag) stateTag);
Map<String, Tag> state = ct.getValue();
short x = IntTag.class.cast(state.get("x")).getValue().shortValue();
short y = IntTag.class.cast(state.get("y")).getValue().shortValue();
short z = IntTag.class.cast(state.get("z")).getValue().shortValue();
new StateWrapper(ct).restoreTag(x, y, z, schem);
}
}
catch (Exception e) {
e.printStackTrace();
}
return schem;
}
/**
* Get a schematic
*
* @param name to check
*
* @return schematic if found, else null
*/
public Schematic getSchematic(final String name) {
{
final File parent = new File(PS.get().IMP.getDirectory() + File.separator + "schematics");
if (!parent.exists()) {
if (!parent.mkdir()) {
throw new RuntimeException("Could not create schematic parent directory");
}
}
}
final File file = new File(PS.get().IMP.getDirectory() + File.separator + "schematics" + File.separator + name + ".schematic");
return getSchematic(file);
}
/**
* Get a schematic
*
* @param name to check
*
* @return schematic if found, else null
*/
public Schematic getSchematic(File file) {
if (!file.exists()) {
PS.log(file.toString() + " doesn't exist");
return null;
}
try {
final InputStream iStream = new FileInputStream(file);
final NBTInputStream stream = new NBTInputStream(new GZIPInputStream(iStream));
final CompoundTag tag = (CompoundTag) stream.readTag();
stream.close();
return getSchematic(tag, file);
} catch (final Exception e) {
PS.log(file.toString() + " is not in GZIP format");
return null;
}
}
public URL upload(final CompoundTag tag) {
if (tag == null) {
PS.log("&cCannot save empty tag");
return null;
}
try {
UUID uuid = UUID.randomUUID();
String website = Settings.WEB_URL + "upload.php?" + uuid;
String charset = "UTF-8";
String param = "value";
String boundary = Long.toHexString(System.currentTimeMillis());
String CRLF = "\r\n";
URLConnection con = new URL(website).openConnection();
con.setDoOutput(true);
con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
try (
OutputStream output = con.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
) {
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
writer.append(CRLF).append(param).append(CRLF).flush();
writer.append("--" + boundary).append(CRLF);
writer.append("Content-Disposition: form-data; name=\"schematicFile\"; filename=\"" + "plot.schematic" + "\"").append(CRLF);
writer.append("Content-Type: " + URLConnection.guessContentTypeFromName("plot.schematic")).append(CRLF);
writer.append("Content-Transfer-Encoding: binary").append(CRLF);
writer.append(CRLF).flush();
GZIPOutputStream gzip = new GZIPOutputStream(output);
NBTOutputStream nos = new NBTOutputStream(gzip);
nos.writeTag(tag);
gzip.finish();
nos.flush();
output.flush();
writer.append(CRLF).flush();
writer.append("--" + boundary + "--").append(CRLF).flush();
nos.close();
output.close();
}
int responseCode = ((HttpURLConnection) con).getResponseCode();
if (responseCode != 200) {
return null;
}
return new URL(Settings.WEB_URL + "?key=" + uuid + "&ip=" + Settings.WEB_IP);
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Saves a schematic to a file path
*
* @param tag to save
* @param path to save in
*
* @return true if succeeded
*/
public boolean save(final CompoundTag tag, final String path) {
if (tag == null) {
PS.log("&cCannot save empty tag");
return false;
}
try {
final File tmp = new File(path);
tmp.getParentFile().mkdirs();
final OutputStream stream = new FileOutputStream(path);
final NBTOutputStream output = new NBTOutputStream(new GZIPOutputStream(stream));
output.writeTag(tag);
output.close();
stream.close();
} catch (final IOException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* Create a compound tag from blocks
* - Untested
* @param blocks
* @param blockdata
* @param d
* @return
*/
public CompoundTag createTag(byte[] blocks, byte[] blockdata, Dimension d) {
final HashMap<String, Tag> schematic = new HashMap<>();
schematic.put("Width", new ShortTag("Width", (short) d.getX()));
schematic.put("Length", new ShortTag("Length", (short) d.getZ()));
schematic.put("Height", new ShortTag("Height", (short) d.getY()));
schematic.put("Materials", new StringTag("Materials", "Alpha"));
schematic.put("WEOriginX", new IntTag("WEOriginX", 0));
schematic.put("WEOriginY", new IntTag("WEOriginY", 0));
schematic.put("WEOriginZ", new IntTag("WEOriginZ", 0));
schematic.put("WEOffsetX", new IntTag("WEOffsetX", 0));
schematic.put("WEOffsetY", new IntTag("WEOffsetY", 0));
schematic.put("WEOffsetZ", new IntTag("WEOffsetZ", 0));
schematic.put("Blocks", new ByteArrayTag("Blocks", blocks));
schematic.put("Data", new ByteArrayTag("Data", blockdata));
schematic.put("Entities", new ListTag("Entities", CompoundTag.class, new ArrayList<Tag>()));
schematic.put("TileEntities", new ListTag("TileEntities", CompoundTag.class, new ArrayList<Tag>()));
return new CompoundTag("Schematic", schematic);
}
/**
* Gets the schematic of a plot
*
* @param world to check
* @param id plot
*
* @return tag
*/
public void getCompoundTag(final String world, final PlotId id, RunnableVal<CompoundTag> whenDone) {
if (!PS.get().getPlots(world).containsKey(id)) {
whenDone.run();
}
final Location pos1 = MainUtil.getPlotBottomLoc(world, id).add(1, 0, 1);
final Location pos2 = MainUtil.getPlotTopLoc(world, id);
getCompoundTag(world, pos1, pos2, whenDone);
}
public abstract void getCompoundTag(final String world, final Location pos1, final Location pos2, RunnableVal<CompoundTag> whenDone);
public boolean pastePart(final String world, final DataCollection[] blocks, final Location l1, final int x_offset, final int z_offset, final int i1, final int i2, final int WIDTH, final int LENGTH) {
int length = 0;
for (int i = i1; i <= i2; i++) {
if (blocks[i].block == 0) {
length++;
}
}
length = i2 - i1 - length + 1;
int X = l1.getX();
int Y = l1.getY();
int Z = l1.getZ();
final int[] xl = new int[length];
final int[] yl = new int[length];
final int[] zl = new int[length];
final int[] ids = new int[length];
final byte[] data = new byte[length];
int count = 0;
for (int i = i1; i <= i2; i++) {
final short id = blocks[i].block;
if (id == 0) {
continue; //
}
final int area = WIDTH * LENGTH;
final int r = i % (area);
final int x = r % WIDTH;
final int y = i / area;
final int z = r / WIDTH;
xl[count] = x + X;
yl[count] = y + Y;
zl[count] = z + Z;
ids[count] = id;
data[count] = blocks[i].data;
count++;
if (y > 256) {
break;
}
}
BlockManager.setBlocks(world, xl, yl, zl, ids, data);
return true;
}
/**
* Schematic Class
*
* @author Citymonstret
*/
public class Schematic {
private final DataCollection[] blockCollection;
private final Dimension schematicDimension;
private final File file;
private HashSet<PlotItem> items;
public Schematic(final DataCollection[] blockCollection, final Dimension schematicDimension, final File file) {
this.blockCollection = blockCollection;
this.schematicDimension = schematicDimension;
this.file = file;
}
public void addItem(PlotItem item) {
if (this.items == null) {
this.items = new HashSet<>();
}
items.add(item);
}
public HashSet<PlotItem> getItems() {
return this.items;
}
public File getFile() {
return this.file;
}
public Dimension getSchematicDimension() {
return this.schematicDimension;
}
public DataCollection[] getBlockCollection() {
return this.blockCollection;
}
}
/**
* Schematic Dimensions
*
* @author Citymonstret
*/
public static class Dimension {
private final int x;
private final int y;
private final int z;
public Dimension(final int x, final int y, final int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public int getZ() {
return this.z;
}
}
/**
* Schematic Data Collection
*
* @author Citymonstret
*/
public class DataCollection {
private final short block;
private final byte data;
// public CompoundTag tag;
public DataCollection(final short block, final byte data) {
this.block = block;
this.data = data;
}
public short getBlock() {
return this.block;
}
public byte getData() {
return this.data;
}
}
}