Skip to content

Commit

Permalink
make schematic save/load/create/rotate waitable async
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmonkey4eva committed Oct 13, 2019
1 parent 75a3ed8 commit 2e6c12a
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 74 deletions.
Expand Up @@ -48,9 +48,12 @@ public class SchematicCommand extends AbstractCommand implements Holdable, Liste
//
// Denizen offers a number of tools to manipulate and work with schematics.
// Schematics can be rotated, flipped, pasted with no air, or pasted with a delay.
// The "noair" option skips air blocks in the pasted schematics- this means those air blocks will not replace
// any blocks in the target location.
// The "delayed" option delays how many blocks can be pasted at once. This is recommended for large schematics.
//
// The "noair" option skips air blocks in the pasted schematics- this means those air blocks will not replace any blocks in the target location.
//
// The "delayed" option makes the command non-instant. This is recommended for large schematics.
// For 'save', 'load', and 'rotate', this processes async to prevent server lockup.
// For 'paste' and 'create', this delays how many blocks can be processed at once, spread over many ticks.
//
// @Tags
// <schematic[<name>].height>
Expand Down Expand Up @@ -190,13 +193,9 @@ public void execute(final ScriptEntry scriptEntry) {

CuboidBlockSet set;
Type ttype = Type.valueOf(type.asString());
if (scriptEntry.shouldWaitFor() && ttype != Type.PASTE) {
Debug.echoError("Tried to wait for a non-paste schematic command.");
scriptEntry.setFinished(true);
}
String fname = filename != null ? filename.asString() : name.asString();
switch (ttype) {
case CREATE:
case CREATE: {
if (schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is already loaded.");
return;
Expand All @@ -210,52 +209,79 @@ public void execute(final ScriptEntry scriptEntry) {
return;
}
try {
// TODO: Make this waitable!
set = new CuboidBlockSet(cuboid, location);
schematics.put(name.asString().toUpperCase(), set);
if (delayed != null && delayed.asBoolean()) {
set = new CuboidBlockSet();
set.buildDelayed(cuboid, location, () -> {
schematics.put(name.asString().toUpperCase(), set);
scriptEntry.setFinished(true);
});
}
else {
scriptEntry.setFinished(true);
set = new CuboidBlockSet(cuboid, location);
schematics.put(name.asString().toUpperCase(), set);
}
}
catch (Exception ex) {
Debug.echoError(scriptEntry.getResidingQueue(), "Error creating schematic object " + name.asString() + ".");
Debug.echoError(scriptEntry.getResidingQueue(), ex);
return;
}
scriptEntry.setFinished(true);
break;
case LOAD:
}
case LOAD: {
if (schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is already loaded.");
return;
}
try {
String directory = URLDecoder.decode(System.getProperty("user.dir"));
File f = new File(directory + "/plugins/Denizen/schematics/" + fname + ".schematic");
if (!Utilities.canReadFile(f)) {
Debug.echoError("Server config denies reading files in that location.");
return;
String directory = URLDecoder.decode(System.getProperty("user.dir"));
File f = new File(directory + "/plugins/Denizen/schematics/" + fname + ".schematic");
if (!Utilities.canReadFile(f)) {
Debug.echoError("Server config denies reading files in that location.");
return;
}
if (!f.exists()) {
Debug.echoError("Schematic file " + fname + " does not exist. Are you sure it's in " + directory + "/plugins/Denizen/schematics/?");
return;
}
Runnable loadRunnable = () -> {
try {
InputStream fs = new FileInputStream(f);
CuboidBlockSet newSet = MCEditSchematicHelper.fromMCEditStream(fs);
fs.close();
Bukkit.getScheduler().runTask(DenizenAPI.getCurrentInstance(), () -> {
schematics.put(name.asString().toUpperCase(), newSet);
scriptEntry.setFinished(true);
});
}
if (!f.exists()) {
Debug.echoError("Schematic file " + fname + " does not exist. Are you sure it's in " + directory + "/plugins/Denizen/schematics/?");
catch (Exception ex) {
Bukkit.getScheduler().runTask(DenizenAPI.getCurrentInstance(), () -> {
Debug.echoError(scriptEntry.getResidingQueue(), "Error loading schematic file " + name.asString() + ".");
Debug.echoError(scriptEntry.getResidingQueue(), ex);
});
return;
}
InputStream fs = new FileInputStream(f);
// TODO: Make this waitable!
set = MCEditSchematicHelper.fromMCEditStream(fs);
fs.close();
schematics.put(name.asString().toUpperCase(), set);
};
if (delayed != null && delayed.asBoolean()) {
Bukkit.getScheduler().runTaskAsynchronously(DenizenAPI.getCurrentInstance(), loadRunnable);
}
catch (Exception ex) {
Debug.echoError(scriptEntry.getResidingQueue(), "Error loading schematic file " + name.asString() + ".");
Debug.echoError(scriptEntry.getResidingQueue(), ex);
return;
else {
scriptEntry.setFinished(true);
loadRunnable.run();
}
break;
case UNLOAD:
}
case UNLOAD: {
if (!schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is not loaded.");
return;
}
schematics.remove(name.asString().toUpperCase());
scriptEntry.setFinished(true);
break;
case ROTATE:
}
case ROTATE: {
if (!schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is not loaded.");
return;
Expand All @@ -264,41 +290,57 @@ public void execute(final ScriptEntry scriptEntry) {
Debug.echoError(scriptEntry.getResidingQueue(), "Missing angle argument!");
return;
}
// TODO: Make this waitable!
int ang = angle.asInt();
while (ang < 0) {
ang = 360 + ang;
}
while (ang > 360) {
ang -= 360;
Runnable rotateRunnable = () -> {
int ang = angle.asInt();
while (ang < 0) {
ang = 360 + ang;
}
while (ang > 360) {
ang -= 360;
}
while (ang > 0) {
ang -= 90;
schematics.get(name.asString().toUpperCase()).rotateOne();
}
Bukkit.getScheduler().runTask(DenizenAPI.getCurrentInstance(), () -> scriptEntry.setFinished(true));
};
if (delayed != null && delayed.asBoolean()) {
Bukkit.getScheduler().runTaskAsynchronously(DenizenAPI.getCurrentInstance(), rotateRunnable);
}
while (ang > 0) {
ang -= 90;
schematics.get(name.asString().toUpperCase()).rotateOne();
else {
scriptEntry.setFinished(true);
rotateRunnable.run();
}
break;
case FLIP_X:
}
case FLIP_X: {
if (!schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is not loaded.");
return;
}
schematics.get(name.asString().toUpperCase()).flipX();
scriptEntry.setFinished(true);
break;
case FLIP_Y:
}
case FLIP_Y: {
if (!schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is not loaded.");
return;
}
schematics.get(name.asString().toUpperCase()).flipY();
scriptEntry.setFinished(true);
break;
case FLIP_Z:
}
case FLIP_Z: {
if (!schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is not loaded.");
return;
}
schematics.get(name.asString().toUpperCase()).flipZ();
scriptEntry.setFinished(true);
break;
case PASTE:
}
case PASTE: {
if (!schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is not loaded.");
return;
Expand Down Expand Up @@ -327,32 +369,45 @@ public void run() {
return;
}
break;
case SAVE:
}
case SAVE: {
if (!schematics.containsKey(name.asString().toUpperCase())) {
Debug.echoError(scriptEntry.getResidingQueue(), "Schematic file " + name.asString() + " is not loaded.");
return;
}
try {
set = schematics.get(name.asString().toUpperCase());
String directory = URLDecoder.decode(System.getProperty("user.dir"));
File f = new File(directory + "/plugins/Denizen/schematics/" + fname + ".schematic");
if (!Utilities.canWriteToFile(f)) {
Debug.echoError(scriptEntry.getResidingQueue(), "Cannot edit that file!");
set = schematics.get(name.asString().toUpperCase());
String directory = URLDecoder.decode(System.getProperty("user.dir"));
File f = new File(directory + "/plugins/Denizen/schematics/" + fname + ".schematic");
if (!Utilities.canWriteToFile(f)) {
Debug.echoError(scriptEntry.getResidingQueue(), "Cannot edit that file!");
return;
}
Runnable saveRunnable = () -> {
try {
f.getParentFile().mkdirs();
FileOutputStream fs = new FileOutputStream(f);
MCEditSchematicHelper.saveMCEditFormatToStream(set, fs);
fs.flush();
fs.close();
Bukkit.getScheduler().runTask(DenizenAPI.getCurrentInstance(), () -> scriptEntry.setFinished(true));
}
catch (Exception ex) {
Bukkit.getScheduler().runTask(DenizenAPI.getCurrentInstance(), () -> {
Debug.echoError(scriptEntry.getResidingQueue(), "Error saving schematic file " + fname + ".");
Debug.echoError(scriptEntry.getResidingQueue(), ex);
});
return;
}
f.getParentFile().mkdirs();
// TODO: Make this waitable!
FileOutputStream fs = new FileOutputStream(f);
MCEditSchematicHelper.saveMCEditFormatToStream(set, fs);
fs.flush();
fs.close();
};
if (delayed != null && delayed.asBoolean()) {
Bukkit.getScheduler().runTaskAsynchronously(DenizenAPI.getCurrentInstance(), saveRunnable);
}
catch (Exception ex) {
Debug.echoError(scriptEntry.getResidingQueue(), "Error saving schematic file " + fname + ".");
Debug.echoError(scriptEntry.getResidingQueue(), ex);
return;
else {
scriptEntry.setFinished(true);
saveRunnable.run();
}
break;
}
}
}

Expand Down
Expand Up @@ -35,6 +35,41 @@ public CuboidBlockSet(CuboidTag cuboid, Location center) {
}
}

public void buildDelayed(CuboidTag cuboid, Location center, Runnable runme) {
Location low = cuboid.pairs.get(0).low;
Location high = cuboid.pairs.get(0).high;
x_width = (high.getX() - low.getX()) + 1;
y_length = (high.getY() - low.getY()) + 1;
z_height = (high.getZ() - low.getZ()) + 1;
center_x = center.getX() - low.getX();
center_y = center.getY() - low.getY();
center_z = center.getZ() - low.getZ();
final long goal = (long) (x_width * y_length * z_height);
new BukkitRunnable() {
int index;
@Override
public void run() {
long start = System.currentTimeMillis();
while (index < goal) {
long z = index % ((long) (z_height));
long y = ((index - z) % ((long) (y_length * z_height))) / ((long) z_height);
long x = (index - y - z) / ((long) (y_length * z_height));
blocks.add(NMSHandler.getBlockHelper().getBlockData(low.clone().add(x, y, z).getBlock()));
index++;
if (System.currentTimeMillis() - start > 50) {
SchematicCommand.noPhys = false;
return;
}
}
if (runme != null) {
runme.run();
}
cancel();

}
}.runTaskTimer(DenizenAPI.getCurrentInstance(), 1, 1);
}

public List<BlockData> blocks = new ArrayList<>();

public double x_width;
Expand All @@ -60,27 +95,23 @@ public CuboidTag getCuboid(Location loc) {
return new CuboidTag(low, high);
}

public class IntHolder {
public long theInt = 0;
}

@Override
public void setBlocksDelayed(final Location loc, final Runnable runme, final boolean noAir) {
final IntHolder index = new IntHolder();
final long goal = (long) (x_width * y_length * z_height);
new BukkitRunnable() {
int index = 0;
@Override
public void run() {
SchematicCommand.noPhys = true;
long start = System.currentTimeMillis();
while (index.theInt < goal) {
long z = index.theInt % ((long) (z_height));
long y = ((index.theInt - z) % ((long) (y_length * z_height))) / ((long) z_height);
long x = (index.theInt - y - z) / ((long) (y_length * z_height));
if (!noAir || blocks.get((int) index.theInt).getMaterial() != Material.AIR) {
blocks.get((int) index.theInt).setBlock(loc.clone().add(x - center_x, y - center_y, z - center_z).getBlock(), false);
while (index < goal) {
long z = index % ((long) (z_height));
long y = ((index - z) % ((long) (y_length * z_height))) / ((long) z_height);
long x = (index - y - z) / ((long) (y_length * z_height));
if (!noAir || blocks.get(index).getMaterial() != Material.AIR) {
blocks.get(index).setBlock(loc.clone().add(x - center_x, y - center_y, z - center_z).getBlock(), false);
}
index.theInt++;
index++;
if (System.currentTimeMillis() - start > 50) {
SchematicCommand.noPhys = false;
return;
Expand Down

0 comments on commit 2e6c12a

Please sign in to comment.