Optimizations to schematic loading / pasting -> ~10,000,000 bps

The downside is it completely ignores entities and such, so WorldEdit is
the way to go if you need precision but don't care for large sizes or
speed.
This commit is contained in:
boy0001 2015-07-23 02:33:22 +10:00
parent 5ec4c244d2
commit d09dbee3c7
9 changed files with 296 additions and 174 deletions

View File

@ -27,6 +27,7 @@ import com.intellectualcrafters.plot.object.Location;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotPlayer;
import com.intellectualcrafters.plot.object.PlotWorld;
import com.intellectualcrafters.plot.object.RunnableVal;
import com.intellectualcrafters.plot.util.EconHandler;
import com.intellectualcrafters.plot.util.EventUtil;
import com.intellectualcrafters.plot.util.MainUtil;
@ -72,7 +73,17 @@ public class Claim extends SubCommand {
sch = SchematicHandler.manager.getSchematic(plotworld.SCHEMATIC_FILE);
}
}
SchematicHandler.manager.paste(sch, plot2, 0, 0);
SchematicHandler.manager.paste(sch, plot2, 0, 0, new RunnableVal<Boolean>() {
@Override
public void run() {
if (value) {
MainUtil.sendMessage(player, C.SCHEMATIC_PASTE_SUCCESS);
}
else {
MainUtil.sendMessage(player, C.SCHEMATIC_PASTE_FAILED);
}
}
});
}
PS.get().getPlotManager(world).claimPlot(plotworld, plot);
}

View File

@ -30,6 +30,7 @@ import com.intellectualcrafters.plot.object.Location;
import com.intellectualcrafters.plot.object.Plot;
import com.intellectualcrafters.plot.object.PlotId;
import com.intellectualcrafters.plot.object.PlotPlayer;
import com.intellectualcrafters.plot.object.RunnableVal;
import com.intellectualcrafters.plot.util.BlockManager;
import com.intellectualcrafters.plot.util.MainUtil;
import com.intellectualcrafters.plot.util.Permissions;
@ -89,69 +90,24 @@ public class SchematicCmd extends SubCommand {
TaskManager.runTaskAsync(new Runnable() {
@Override
public void run() {
try {
final Schematic schematic = SchematicHandler.manager.getSchematic(file2);
if (schematic == null) {
sendMessage(plr, C.SCHEMATIC_INVALID, "non-existent or not in gzip format");
SchematicCmd.this.running = false;
return;
}
final int x;
final int z;
final Plot plot2 = MainUtil.getPlot(loc);
final Dimension dem = schematic.getSchematicDimension();
final Location bot = MainUtil.getPlotBottomLoc(loc.getWorld(), plot2.id).add(1, 0, 1);
final int length2 = MainUtil.getPlotWidth(loc.getWorld(), plot2.id);
if ((dem.getX() > length2) || (dem.getZ() > length2)) {
sendMessage(plr, C.SCHEMATIC_INVALID, String.format("Wrong size (x: %s, z: %d) vs %d ", dem.getX(), dem.getZ(), length2));
SchematicCmd.this.running = false;
return;
}
if ((dem.getX() != length2) || (dem.getZ() != length2)) {
final Location loc = plr.getLocation();
x = Math.min(length2 - dem.getX(), loc.getX() - bot.getX());
z = Math.min(length2 - dem.getZ(), loc.getZ() - bot.getZ());
} else {
x = 0;
z = 0;
}
final DataCollection[] b = schematic.getBlockCollection();
final int sy = BlockManager.manager.getHeighestBlock(bot);
final int WIDTH = schematic.getSchematicDimension().getX();
final int LENGTH = schematic.getSchematicDimension().getZ();
final Location l1;
if (!(schematic.getSchematicDimension().getY() == BukkitUtil.getMaxHeight(loc.getWorld()))) {
l1 = bot.add(0, sy - 1, 0);
}
else {
l1 = bot;
}
final int blen = b.length - 1;
SchematicCmd.this.task = TaskManager.runTaskRepeat(new Runnable() {
@Override
public void run() {
boolean result = false;
while (!result) {
final int start = SchematicCmd.this.counter * 5000;
if (start > blen) {
SchematicHandler.manager.pasteStates(schematic, plot, 0, 0);
sendMessage(plr, C.SCHEMATIC_PASTE_SUCCESS);
SchematicCmd.this.running = false;
PS.get().TASK.cancelTask(SchematicCmd.this.task);
return;
}
final int end = Math.min(start + 5000, blen);
result = SchematicHandler.manager.pastePart(loc.getWorld(), b, l1, x, z, start, end, WIDTH, LENGTH);
SchematicCmd.this.counter++;
}
}
}, 1);
}
catch (Exception e) {
e.printStackTrace();
final Schematic schematic = SchematicHandler.manager.getSchematic(file2);
if (schematic == null) {
SchematicCmd.this.running = false;
MainUtil.sendMessage(plr, "&cAn error occured, see console for more information");
sendMessage(plr, C.SCHEMATIC_INVALID, "non-existent or not in gzip format");
return;
}
SchematicHandler.manager.paste(schematic, plot, 0, 0, new RunnableVal<Boolean>() {
@Override
public void run() {
SchematicCmd.this.running = false;
if (this.value) {
sendMessage(plr, C.SCHEMATIC_PASTE_SUCCESS);
}
else {
sendMessage(plr, C.SCHEMATIC_PASTE_FAILED);
}
}
});
}
});
break;

View File

@ -162,7 +162,7 @@ public class Settings {
* Days until a plot gets cleared
*/
public static int AUTO_CLEAR_DAYS = 360;
public static int CLEAR_THRESHOLD = -1;
public static int CLEAR_THRESHOLD = 1;
public static int CLEAR_INTERVAL = 120;
/**
* API Location

View File

@ -83,7 +83,7 @@ public class HybridPlotManager extends ClassicPlotManager {
Location bot = getPlotBottomLocAbs(hpw, id2);
Location top = getPlotTopLocAbs(hpw, id);
Location pos1 = new Location(plot.world, top.getX() + 1, 0, bot.getZ());
Location pos2 = new Location(plot.world, bot.getX(), 256, top.getZ() + 1);
Location pos2 = new Location(plot.world, bot.getX(), 255, top.getZ() + 1);
createSchemAbs(hpw, pos1, pos2, hpw.ROAD_HEIGHT, true);
return true;
}
@ -139,7 +139,7 @@ public class HybridPlotManager extends ClassicPlotManager {
Location bot = getPlotBottomLocAbs(hpw, id2);
Location top = getPlotTopLocAbs(hpw, id);
Location pos1 = new Location(plot.world, bot.getX(), 0, top.getZ() + 1);
Location pos2 = new Location(plot.world, top.getX() + 1, 256, bot.getZ());
Location pos2 = new Location(plot.world, top.getX() + 1, 255, bot.getZ());
createSchemAbs(hpw, pos1, pos2, hpw.ROAD_HEIGHT, true);
return true;
}

View File

@ -89,7 +89,8 @@ public class HybridPlotWorld extends ClassicPlotWorld {
}
if (schem3 != null) {
this.PLOT_SCHEMATIC = true;
final DataCollection[] blocks3 = schem3.getBlockCollection();
byte[] ids = schem3.getIds();
byte[] datas = schem3.getDatas();
final Dimension d3 = schem3.getSchematicDimension();
final short w3 = (short) d3.getX();
final short l3 = (short) d3.getZ();
@ -106,8 +107,8 @@ public class HybridPlotWorld extends ClassicPlotWorld {
for (short z = 0; z < l3; z++) {
for (short y = 0; y < h3; y++) {
final int index = (y * w3 * l3) + (z * w3) + x;
final short id = blocks3[index].getBlock();
final byte data = blocks3[index].getData();
final short id = ids[index];
final byte data = ids[index];
if (id != 0) {
addOverlayBlock((short) (x + shift + oddshift + center_shift_x), (y), (short) (z + shift + oddshift + center_shift_z), id, data, false);
}
@ -139,8 +140,13 @@ public class HybridPlotWorld extends ClassicPlotWorld {
this.ROAD_SCHEMATIC_ENABLED = true;
// Do not populate road if using schematic population
this.ROAD_BLOCK = new PlotBlock(this.ROAD_BLOCK.id, (byte) 0);
final DataCollection[] blocks1 = schem1.getBlockCollection();
final DataCollection[] blocks2 = schem2.getBlockCollection();
byte[] ids1 = schem1.getIds();
byte[] datas1 = schem1.getDatas();
byte[] ids2 = schem2.getIds();
byte[] datas2 = schem2.getDatas();
final Dimension d1 = schem1.getSchematicDimension();
final short w1 = (short) d1.getX();
final short l1 = (short) d1.getZ();
@ -154,8 +160,8 @@ public class HybridPlotWorld extends ClassicPlotWorld {
for (short z = 0; z < l1; z++) {
for (short y = 0; y < h1; y++) {
final int index = (y * w1 * l1) + (z * w1) + x;
final short id = blocks1[index].getBlock();
final byte data = blocks1[index].getData();
final short id = ids1[index];
final byte data = datas1[index];
if (id != 0) {
addOverlayBlock((short) (x - (shift)), (y), (short) (z + shift + oddshift), id, data, false);
addOverlayBlock((short) (z + shift + oddshift), (y), (short) (x - shift), id, data, true);
@ -167,8 +173,8 @@ public class HybridPlotWorld extends ClassicPlotWorld {
for (short z = 0; z < l2; z++) {
for (short y = 0; y < h2; y++) {
final int index = (y * w2 * l2) + (z * w2) + x;
final short id = blocks2[index].getBlock();
final byte data = blocks2[index].getData();
final short id = ids2[index];
final byte data = datas2[index];
if (id != 0) {
addOverlayBlock((short) (x - shift), (y), (short) (z - shift), id, data, false);
}

View File

@ -28,7 +28,7 @@ public abstract class SquarePlotManager extends GridPlotManager {
final int pz = plotid.y;
final int x = dpw.ROAD_OFFSET_X + (px * (dpw.ROAD_WIDTH + dpw.PLOT_WIDTH)) - ((int) Math.floor(dpw.ROAD_WIDTH / 2)) - 1;
final int z = dpw.ROAD_OFFSET_Z + (pz * (dpw.ROAD_WIDTH + dpw.PLOT_WIDTH)) - ((int) Math.floor(dpw.ROAD_WIDTH / 2)) - 1;
return new Location(plotworld.worldname, x, 256, z);
return new Location(plotworld.worldname, x, 255, z);
}
@Override

View File

@ -1950,10 +1950,10 @@ public class PlayerEvents extends com.intellectualcrafters.plot.listeners.PlotLi
Block block = event.getBlockPlaced();
sendBlockChange(block.getLocation(), block.getType(), block.getData());
}
int temporary;
if (!player.hasPermission("plots.admin.build.heightlimit") && loc.getY() >= (temporary = PS.get().getPlotWorld(world).MAX_BUILD_HEIGHT)) {
PlotWorld pw = PS.get().getPlotWorld(loc.getWorld());
if (loc.getY() >= pw.MAX_BUILD_HEIGHT && !Permissions.hasPermission(pp, "plots.admin.build.heightlimit")) {
event.setCancelled(true);
MainUtil.sendMessage(pp, C.HEIGHT_LIMIT.s().replace("{limit}", "" + temporary));
MainUtil.sendMessage(pp, C.HEIGHT_LIMIT.s().replace("{limit}", "" + pw.MAX_BUILD_HEIGHT));
return;
}
return;

View File

@ -39,7 +39,7 @@ import com.intellectualcrafters.plot.util.MainUtil;
* @author Empire92
*/
@SuppressWarnings("javadoc")
public class Plot implements Cloneable {
public class Plot {
/**
* plot ID
*/
@ -251,20 +251,6 @@ public class Plot implements Cloneable {
return this.members;
}
/**
* Get a clone of the plot
*
* @return Plot
*/
@Override
public Object clone() throws CloneNotSupportedException {
final Plot p = (Plot) super.clone();
if (!p.equals(this) || (p != this)) {
return new Plot(this.id, this.owner, this.trusted, this.members, this.denied, this.settings.getAlias(), this.settings.getPosition(), this.settings.flags.values(), this.world, this.settings.getMerged());
}
return p;
}
/**
* Deny someone (use DBFunc.addDenied() as well)
*

View File

@ -1,20 +1,16 @@
package com.intellectualcrafters.plot.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -26,8 +22,6 @@ 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;
@ -41,6 +35,7 @@ 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.PlotBlock;
import com.intellectualcrafters.plot.object.PlotId;
import com.intellectualcrafters.plot.object.RunnableVal;
import com.intellectualcrafters.plot.object.schematic.PlotItem;
@ -146,52 +141,167 @@ public abstract class SchematicHandler {
*
* @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;
public void paste(final Schematic schematic, final Plot plot, final int x_offset, final int z_offset, final RunnableVal<Boolean> whenDone) {
System.out.print(1);
TaskManager.runTaskAsync(new Runnable() {
@Override
public void run() {
System.out.print(2);
if (whenDone != null) whenDone.value = false;
if (schematic == null) {
PS.log("Schematic == null :|");
TaskManager.runTask(whenDone);
return;
}
try {
System.out.print(3);
final Dimension demensions = schematic.getSchematicDimension();
final int WIDTH = demensions.getX();
final int LENGTH = demensions.getZ();
final int HEIGHT = demensions.getY();
byte[] ids = schematic.ids;
byte[] datas = schematic.datas;
int y_offset;
if (HEIGHT >= 256) {
y_offset = -1;
}
else {
y_offset = BukkitUtil.getMaxHeight(plot.world) - 1;
}
Location bottom = MainUtil.getPlotBottomLoc(plot.world, plot.id).add(1 + x_offset, y_offset, 1 + z_offset);
int X = bottom.getX();
int Y = bottom.getY();
int Z = bottom.getZ();
int id;
System.out.print(4);
System.out.print("HEIGHT: " + HEIGHT);
for (int y = 0; y < Math.max(256, HEIGHT); y++) {
int i1 = y * WIDTH * LENGTH;
for (int z = 0; z < LENGTH; z++) {
int i2 = z * WIDTH + i1;
for (int x = 0; x < WIDTH; x++) {
int i = i2 + x;
id = ids[i];
switch(id) {
case 0:
case 2:
case 4:
case 13:
case 14:
case 15:
case 20:
case 21:
case 22:
case 24:
case 30:
case 32:
case 37:
case 39:
case 40:
case 41:
case 42:
case 45:
case 46:
case 47:
case 48:
case 49:
case 50:
case 51:
case 55:
case 56:
case 57:
case 58:
case 60:
case 7:
case 8:
case 9:
case 10:
case 11:
case 73:
case 74:
case 75:
case 76:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 85:
case 87:
case 88:
case 101:
case 102:
case 103:
case 110:
case 112:
case 113:
case 121:
case 122:
case 129:
case 133:
case 165:
case 166:
case 169:
case 170:
case 172:
case 173:
case 174:
case 181:
case 182:
case 188:
case 189:
case 190:
case 191:
case 192: {
SetBlockQueue.setBlock(plot.world, X + x, Y + y, Z + z, id);
break;
}
default: {
SetBlockQueue.setBlock(plot.world, X + x, Y + y, Z + z, new PlotBlock((short) id, (byte) datas[i]));
break;
}
}
// set block
}
}
}
System.out.print(5);
SetBlockQueue.addNotify(new Runnable() {
@Override
public void run() {
System.out.print(6);
pasteStates(schematic, plot, x_offset, z_offset);
System.out.print(7);
if (whenDone != null) {
whenDone.value = true;
whenDone.run();
}
}
});
return;
} catch (final Exception e) {
e.printStackTrace();
TaskManager.runTask(whenDone);
return;
}
}
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) {
@ -226,33 +336,42 @@ public abstract class SchematicHandler {
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();
}
// Slow
// byte[] addId = new byte[0];
// if (tagMap.containsKey("AddBlocks")) {
// addId = ByteArrayTag.class.cast(tagMap.get("AddBlocks")).getValue();
// }
// end slow
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);
final byte[] block = ByteArrayTag.class.cast(tagMap.get("Blocks")).getValue();
final byte[] data = ByteArrayTag.class.cast(tagMap.get("Data")).getValue();
// Slow + has code for exceptions (addId) inside the loop rather than outside
// 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));
// }
// }
// }
// Slow as wrapper for each block
// 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);
Dimension dimensions = new Dimension(width, height, length);
Schematic schem = new Schematic(block, data, dimensions , file);
// Slow
try {
List<Tag> blockStates = ListTag.class.cast(tagMap.get("TileEntities")).getValue();
for (Tag stateTag : blockStates) {
@ -505,15 +624,44 @@ public abstract class SchematicHandler {
* @author Citymonstret
*/
public class Schematic {
private final DataCollection[] blockCollection;
// Lossy but fast
private final byte[] ids;
private final byte[] datas;
@Deprecated
private DataCollection[] collection;
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;
/**
* This is deprecated as having a wrapper for each block is really slow.<br>
* - There's also a performance hit by having the cast the DataCollection short / byte
* -
* @param blockCollection
* @param schematicDimension
* @param file
*/
// @Deprecated
// public Schematic(final DataCollection[] blockCollection, final Dimension schematicDimension, final File file) {
// ids = new byte[blockCollection.length];
// datas = new byte[blockCollection.length];
// for (int i = 0; i < blockCollection.length; i++) {
// DataCollection block = blockCollection[i];
// ids[i] = (byte) block.block;
// datas[i] = block.data;
// }
// this.collection = blockCollection;
// this.schematicDimension = schematicDimension;
// this.file = file;
// }
public Schematic(final byte[] i, final byte[] b, final Dimension d, final File f) {
ids = i;
datas = b;
schematicDimension = d;
file = f;
}
public void addItem(PlotItem item) {
@ -534,10 +682,25 @@ public abstract class SchematicHandler {
public Dimension getSchematicDimension() {
return this.schematicDimension;
}
public DataCollection[] getBlockCollection() {
return this.blockCollection;
public byte[] getIds() {
return ids;
}
public byte[] getDatas() {
return datas;
}
// @Deprecated
// public DataCollection[] getBlockCollection() {
// if (this.collection == null) {
// collection = new DataCollection[ids.length];
// for (int i = 0; i < ids.length; i++) {
// collection[i] = new DataCollection(ids[i], datas[i]);
// }
// }
// return this.collection;
// }
}
/**