diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/SchematicCommand.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/SchematicCommand.java index c1062a6c28..c34cfebf0d 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/SchematicCommand.java +++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/SchematicCommand.java @@ -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 // ].height> @@ -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; @@ -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; @@ -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; @@ -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; + } } } diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/CuboidBlockSet.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/CuboidBlockSet.java index f562d08a8f..985bcad3b7 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/CuboidBlockSet.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/CuboidBlockSet.java @@ -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 blocks = new ArrayList<>(); public double x_width; @@ -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;