diff --git a/core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java b/core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java index 7a7ffb5b..94cc5220 100644 --- a/core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java +++ b/core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java @@ -13,6 +13,7 @@ public class AngleMask extends SolidBlockMask implements ResettableMask { public static double ADJACENT_MOD = 0.5; public static double DIAGONAL_MOD = 1 / Math.sqrt(8); + private final CachedMask mask; private final double max; private final double min; private final boolean overlay; @@ -22,6 +23,7 @@ public class AngleMask extends SolidBlockMask implements ResettableMask { public AngleMask(Extent extent, double min, double max, boolean overlay) { super(extent); + this.mask = new CachedMask(new SolidBlockMask(extent)); this.min = min; this.max = max; this.maxY = extent.getMaximumPoint().getBlockY(); @@ -53,33 +55,34 @@ public class AngleMask extends SolidBlockMask implements ResettableMask { private transient boolean lastValue; public int getHeight(int x, int y, int z) { - try { - int rx = x - cacheBotX + 16; - int rz = z - cacheBotZ + 16; - int index; - if (((rx & 0xFF) != rx || (rz & 0xFF) != rz)) { - cacheBotX = x - 16; - cacheBotZ = z - 16; - rx = x - cacheBotX + 16; - rz = z - cacheBotZ + 16; - index = rx + (rz << 8); - if (cacheHeights == null) { - cacheHeights = new byte[65536]; - } else { - Arrays.fill(cacheHeights, (byte) 0); - } - } else { - index = rx + (rz << 8); - } - int result = cacheHeights[index] & 0xFF; - if (result == 0) { - cacheHeights[index] = (byte) (result = lastY = getExtent().getNearestSurfaceTerrainBlock(x, z, lastY, 0, maxY)); - } - return result; - } catch (Throwable e) { - e.printStackTrace(); - throw e; - } + return lastY = getExtent().getNearestSurfaceTerrainBlock(x, z, y, 0, maxY); +// try { +// int rx = x - cacheBotX + 16; +// int rz = z - cacheBotZ + 16; +// int index; +// if (((rx & 0xFF) != rx || (rz & 0xFF) != rz)) { +// cacheBotX = x - 16; +// cacheBotZ = z - 16; +// rx = x - cacheBotX + 16; +// rz = z - cacheBotZ + 16; +// index = rx + (rz << 8); +// if (cacheHeights == null) { +// cacheHeights = new byte[65536]; +// } else { +// Arrays.fill(cacheHeights, (byte) 0); +// } +// } else { +// index = rx + (rz << 8); +// } +// int result = cacheHeights[index] & 0xFF; +// if (result == 0) { +// cacheHeights[index] = (byte) (result = lastY = getExtent().getNearestSurfaceTerrainBlock(x, z, lastY, 0, maxY)); +// } +// return result; +// } catch (Throwable e) { +// e.printStackTrace(); +// throw e; +// } } private boolean testSlope(int x, int y, int z) { @@ -98,6 +101,31 @@ public class AngleMask extends SolidBlockMask implements ResettableMask { return lastValue = (slope >= min && slope <= max); } + public boolean adjacentAir(Vector v) { + int x = v.getBlockX(); + int y = v.getBlockY(); + int z = v.getBlockZ(); + if (mask.test(x + 1, y, z)) { + return true; + } + if (mask.test(x - 1, y, z)) { + return true; + } + if (mask.test(x, y, z + 1)) { + return true; + } + if (mask.test(x, y, z - 1)) { + return true; + } + if (y < 256 && mask.test(x, y + 1, z)) { + return true; + } + if (y > 0 && mask.test(x, y - 1, z)) { + return true; + } + return false; + } + @Override public boolean test(Vector vector) { int x = vector.getBlockX(); @@ -110,6 +138,8 @@ public class AngleMask extends SolidBlockMask implements ResettableMask { if (overlay) { block = getExtent().getLazyBlock(x, y + 1, z); if (test(block.getId(), block.getData())) return lastValue = false; + } else if (!adjacentAir(vector)) { + return false; } return testSlope(x, y, z); } diff --git a/core/src/main/java/com/boydti/fawe/object/pattern/Linear2DBlockPattern.java b/core/src/main/java/com/boydti/fawe/object/pattern/Linear2DBlockPattern.java index be0bbf5b..614210da 100644 --- a/core/src/main/java/com/boydti/fawe/object/pattern/Linear2DBlockPattern.java +++ b/core/src/main/java/com/boydti/fawe/object/pattern/Linear2DBlockPattern.java @@ -32,4 +32,4 @@ public class Linear2DBlockPattern extends AbstractPattern { } return patternsArray[index].apply(extent, set, get); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/util/metrics/BStats.java b/core/src/main/java/com/boydti/fawe/util/metrics/BStats.java index fbcb0520..e82d8075 100644 --- a/core/src/main/java/com/boydti/fawe/util/metrics/BStats.java +++ b/core/src/main/java/com/boydti/fawe/util/metrics/BStats.java @@ -2,7 +2,9 @@ package com.boydti.fawe.util.metrics; import com.boydti.fawe.Fawe; import com.boydti.fawe.configuration.file.YamlConfiguration; +import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.io.PGZIPOutputStream; +import com.boydti.fawe.util.TaskManager; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; @@ -21,9 +23,10 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Path; +import java.util.Timer; +import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; import javax.net.ssl.HttpsURLConnection; /** @@ -45,7 +48,7 @@ public class BStats implements Closeable { private final boolean online; private final String serverVersion; private final String pluginVersion; - private Thread task; + private Timer timer; private Gson gson = new Gson(); // Is bStats enabled on this server? @@ -145,7 +148,7 @@ public class BStats implements Closeable { * @param metrics An object of the metrics class to link. */ public static void linkMetrics(Object metrics) { - knownMetricsInstances.add(metrics); + if (!knownMetricsInstances.contains(metrics)) knownMetricsInstances.add(metrics); } /** @@ -167,22 +170,18 @@ public class BStats implements Closeable { } private void startSubmitting() { - // No delay, as this class is only instantiated after the server is loaded - this.task = new Thread(new Runnable() { + this.timer = new Timer(true); + timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { - while (enabled) { - submitData(); - try { - if (enabled) Thread.sleep(TimeUnit.MINUTES.toMillis(30)); - } catch (InterruptedException e) { - break; - } + if (!enabled) { + timer.cancel(); + return; } submitData(); } - }); - this.task.start(); + // No 2m delay, as this is only started after the server is loaded + }, 0, 1000*60*30); } @Override @@ -194,13 +193,8 @@ public class BStats implements Closeable { @Override public void close() { enabled = false; - if (task != null) { - task.interrupt(); - try { - task.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } + if (timer != null) { + timer.cancel(); } } @@ -249,14 +243,21 @@ public class BStats implements Closeable { final JsonArray pluginData = new JsonArray(); // Search for all other bStats Metrics classes to get their plugin data for (Object metrics : knownMetricsInstances) { - try { - Object plugin = metrics.getClass().getMethod("getPluginData").invoke(metrics); + Object plugin = TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value) { + try { + this.value = metrics.getClass().getMethod("getPluginData").invoke(metrics); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NullPointerException | JsonSyntaxException ignored) {} + } + }); + if (plugin != null) { if (plugin instanceof JsonObject) { pluginData.add((JsonObject) plugin); } else { pluginData.add(gson.fromJson(plugin.toString(), JsonObject.class)); } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NullPointerException | JsonSyntaxException ignored) {} + } } data.add("plugins", pluginData); diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 4bce5a70..f2fbc942 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2214,6 +2214,14 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int makeCylinder(Vector pos, final Pattern block, double radiusX, double radiusZ, int height, final boolean filled) { + return makeCylinder(pos, block, radiusX, radiusZ, height, 0, filled); + } + + public int makeHollowCylinder(Vector pos, final Pattern block, double radiusX, double radiusZ, int height, int thickness) { + return makeCylinder(pos, block, radiusX, radiusZ, height, thickness, false); + } + + private int makeCylinder(Vector pos, final Pattern block, double radiusX, double radiusZ, int height, int thickness, final boolean filled) { radiusX += 0.5; radiusZ += 0.5; @@ -2230,8 +2238,8 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting height = (maxY - pos.getBlockY()) + 1; } - final double invRadiusX = 1 / radiusX; - final double invRadiusZ = 1 / radiusZ; + final double invRadiusX = 1 / (radiusX); + final double invRadiusZ = 1 / (radiusZ); int px = pos.getBlockX(); int py = pos.getBlockY(); @@ -2242,36 +2250,79 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting final int ceilRadiusZ = (int) Math.ceil(radiusZ); double dx, dxz, dz; double nextXn = 0; - forX: - for (int x = 0; x <= ceilRadiusX; ++x) { - final double xn = nextXn; - nextXn = (x + 1) * invRadiusX; - double nextZn = 0; - dx = xn * xn; - forZ: - for (int z = 0; z <= ceilRadiusZ; ++z) { - final double zn = nextZn; - nextZn = (z + 1) * invRadiusZ; - dz = zn * zn; - dxz = dx + dz; - if (dxz > 1) { - if (z == 0) { - break forX; - } - break forZ; - } - if (!filled) { - if ((dz + nextXn * nextXn <= 1) && (nextZn * nextZn + dx <= 1)) { + if (thickness != 0) { + double nextMinXn = 0; + final double minInvRadiusX = 1 / (radiusX - thickness); + final double minInvRadiusZ = 1 / (radiusZ - thickness); + forX: + for (int x = 0; x <= ceilRadiusX; ++x) { + final double xn = nextXn; + double dx2 = nextMinXn * nextMinXn; + nextXn = (x + 1) * invRadiusX; + nextMinXn = (x + 1) * minInvRadiusX; + double nextZn = 0; + double nextMinZn = 0; + dx = xn * xn; + forZ: + for (int z = 0; z <= ceilRadiusZ; ++z) { + final double zn = nextZn; + double dz2 = nextMinZn * nextMinZn; + nextZn = (z + 1) * invRadiusZ; + nextMinZn = (z + 1) * minInvRadiusZ; + dz = zn * zn; + dxz = dx + dz; + if (dxz > 1) { + if (z == 0) { + break forX; + } + break forZ; + } + + if ((dz2 + nextMinXn * nextMinXn <= 1) && (nextMinZn * nextMinZn + dx2 <= 1)) { continue; } - } - for (int y = 0; y < height; ++y) { - this.setBlock(mutable.setComponents(px + x, py + y, pz + z), block); - this.setBlock(mutable.setComponents(px - x, py + y, pz + z), block); - this.setBlock(mutable.setComponents(px + x, py + y, pz - z), block); - this.setBlock(mutable.setComponents(px - x, py + y, pz - z), block); + for (int y = 0; y < height; ++y) { + this.setBlock(mutable.setComponents(px + x, py + y, pz + z), block); + this.setBlock(mutable.setComponents(px - x, py + y, pz + z), block); + this.setBlock(mutable.setComponents(px + x, py + y, pz - z), block); + this.setBlock(mutable.setComponents(px - x, py + y, pz - z), block); + } + } + } + } else { + forX: + for (int x = 0; x <= ceilRadiusX; ++x) { + final double xn = nextXn; + nextXn = (x + 1) * invRadiusX; + double nextZn = 0; + dx = xn * xn; + forZ: + for (int z = 0; z <= ceilRadiusZ; ++z) { + final double zn = nextZn; + nextZn = (z + 1) * invRadiusZ; + dz = zn * zn; + dxz = dx + dz; + if (dxz > 1) { + if (z == 0) { + break forX; + } + break forZ; + } + + if (!filled) { + if ((dz + nextXn * nextXn <= 1) && (nextZn * nextZn + dx <= 1)) { + continue; + } + } + + for (int y = 0; y < height; ++y) { + this.setBlock(mutable.setComponents(px + x, py + y, pz + z), block); + this.setBlock(mutable.setComponents(px - x, py + y, pz + z), block); + this.setBlock(mutable.setComponents(px + x, py + y, pz - z), block); + this.setBlock(mutable.setComponents(px - x, py + y, pz - z), block); + } } } } diff --git a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 40d918cc..ce2c842e 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -546,7 +546,7 @@ public class BrushCommands extends MethodCommands { } @Command( - aliases = {"cylinder", "cyl", "c"}, + aliases = {"cylinder", "cyl", "c", "disk", "disc"}, usage = " [radius=2] [height=1]", flags = "h", desc = "Creates a cylinder", diff --git a/core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 5d08aa46..45468e39 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -20,7 +20,6 @@ package com.sk89q.worldedit.command; import com.boydti.fawe.Fawe; -import com.boydti.fawe.command.FawePrimitiveBinding; import com.boydti.fawe.config.BBC; import com.boydti.fawe.jnbt.anvil.generator.CavesGen; import com.boydti.fawe.object.FawePlayer; @@ -35,6 +34,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.MutableBlockVector; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; @@ -180,12 +180,18 @@ public class GenerationCommands extends MethodCommands { "you can generate elliptical cylinders.\n" + "The 1st radius is north/south, the 2nd radius is east/west.", min = 2, - max = 3 + max = 4 ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) - public void hcyl(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, String radiusString, @Optional("1") int height, CommandContext context) throws WorldEditException, ParameterException { - cyl(fp, player, session, editSession, pattern, radiusString, height, true, context); + public void hcyl(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, Vector2D radius, @Optional("1") int height, @Optional("1") int thickness, CommandContext context) throws WorldEditException, ParameterException { + double max = MathMan.max(radius.getBlockX(), radius.getBlockZ()); + worldEdit.checkMaxBrushRadius(max); + fp.checkConfirmationRadius(getArguments(context), (int) max); + height = Math.min(256, height); + Vector pos = session.getPlacementPosition(player); + int affected = editSession.makeHollowCylinder(pos, pattern, radius.getBlockX(), radius.getBlockZ(), height, thickness); + BBC.VISITOR_BLOCK.send(fp, affected); } @Command( @@ -203,33 +209,13 @@ public class GenerationCommands extends MethodCommands { ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) - public void cyl(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, String radiusString, @Optional("1") int height, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException, ParameterException { - String[] radii = radiusString.split(","); - final double radiusX, radiusZ; - switch (radii.length) { - case 1: - radiusX = radiusZ = Math.max(1, FawePrimitiveBinding.parseNumericInput(radii[0])); - break; - - case 2: - radiusX = Math.max(1, FawePrimitiveBinding.parseNumericInput(radii[0])); - radiusZ = Math.max(1, FawePrimitiveBinding.parseNumericInput(radii[1])); - break; - - default: - fp.sendMessage(BBC.getPrefix() + "You must either specify 1 or 2 radius values."); - return; - } - height = Math.min(256, height); - worldEdit.checkMaxRadius(radiusX); - worldEdit.checkMaxRadius(radiusZ); - worldEdit.checkMaxRadius(height); - - double max = MathMan.max(radiusX, radiusZ, height); + public void cyl(FawePlayer fp, Player player, LocalSession session, EditSession editSession, Pattern pattern, Vector2D radius, @Optional("1") int height, @Switch('h') boolean hollow, CommandContext context) throws WorldEditException, ParameterException { + double max = MathMan.max(radius.getBlockX(), radius.getBlockZ()); + worldEdit.checkMaxBrushRadius(max); fp.checkConfirmationRadius(getArguments(context), (int) max); - + height = Math.min(256, height); Vector pos = session.getPlacementPosition(player); - int affected = editSession.makeCylinder(pos, pattern, radiusX, radiusZ, height, !hollow); + int affected = editSession.makeCylinder(pos, pattern, radius.getBlockX(), radius.getBlockZ(), height, !hollow); BBC.VISITOR_BLOCK.send(fp, affected); } diff --git a/forge112/src/main/java/com/boydti/fawe/forge/v112/ForgeQueue_All.java b/forge112/src/main/java/com/boydti/fawe/forge/v112/ForgeQueue_All.java index 0b247bef..9532ccf3 100644 --- a/forge112/src/main/java/com/boydti/fawe/forge/v112/ForgeQueue_All.java +++ b/forge112/src/main/java/com/boydti/fawe/forge/v112/ForgeQueue_All.java @@ -221,6 +221,128 @@ public class ForgeQueue_All extends NMSMappedFaweQueue() { +// @Override +// public void run(Boolean value) { +// long start = System.currentTimeMillis(); +// long last = start; +// synchronized (RegionFileCache.class) { +// WorldServer world = (WorldServer) getWorld(); +// ChunkProviderServer provider = nmsWorld.getChunkProvider(); +// +// boolean mustSave = false; +// boolean[][] chunksUnloaded = null; +// { // Unload chunks +// Iterator iter = provider.getLoadedChunks().iterator(); +// while (iter.hasNext()) { +// Chunk chunk = iter.next(); +// if (chunk.x >> 5 == mcaX && chunk.z >> 5 == mcaZ) { +// boolean isIn = allowed.isInChunk(chunk.x, chunk.x); +// if (isIn) { +// if (!load) { +// if (chunk.needsSaving(false)) { +// mustSave = true; +// try { +// provider.chunkLoader.saveChunk(nmsWorld, chunk); +// provider.chunkLoader.saveExtraChunkData(nmsWorld, chunk); +// } catch (IOException | MinecraftException e) { +// e.printStackTrace(); +// } +// } +// continue; +// } +// iter.remove(); +// boolean save = chunk.needsSaving(false); +// mustSave |= save; +// if (save) { +// provider.queueUnload(chunk); +// } else { +// chunk.onUnload(); +// } +// if (chunksUnloaded == null) { +// chunksUnloaded = new boolean[32][]; +// } +// int relX = chunk.x & 31; +// boolean[] arr = chunksUnloaded[relX]; +// if (arr == null) { +// arr = chunksUnloaded[relX] = new boolean[32]; +// } +// arr[chunk.z & 31] = true; +// } +// } +// } +// } +// if (mustSave) provider.flushToDisk(); // TODO only the necessary chunks +// +// File unloadedRegion = null; +// if (load && !RegionFileCache.a.isEmpty()) { +// Map map = RegionFileCache.a; +// Iterator> iter = map.entrySet().iterator(); +// String requiredPath = world.getName() + File.separator + "region"; +// while (iter.hasNext()) { +// Map.Entry entry = iter.next(); +// File file = entry.getKey(); +// int[] regPos = MainUtil.regionNameToCoords(file.getPath()); +// if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) { +// if (file.exists()) { +// unloadedRegion = file; +// RegionFile regionFile = entry.getValue(); +// iter.remove(); +// try { +// regionFile.c(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// break; +// } +// } +// } +// +// long now = System.currentTimeMillis(); +// if (whileLocked != null) whileLocked.run(); +// if (!load) return; +// +// { // Load the region again +// if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) { +// final boolean[][] finalChunksUnloaded = chunksUnloaded; +// TaskManager.IMP.async(() -> { +// int bx = mcaX << 5; +// int bz = mcaZ << 5; +// for (int x = 0; x < finalChunksUnloaded.length; x++) { +// boolean[] arr = finalChunksUnloaded[x]; +// if (arr != null) { +// for (int z = 0; z < arr.length; z++) { +// if (arr[z]) { +// int cx = bx + x; +// int cz = bz + z; +// TaskManager.IMP.sync(new RunnableVal() { +// @Override +// public void run(Object value1) { +// Chunk chunk = provider.getChunkAt(cx, cz, null, false); +// if (chunk != null) { +// PlayerChunk pc = getPlayerChunk(nmsWorld, cx, cz); +// if (pc != null) { +// sendChunk(pc, chunk, 0); +// } +// } +// } +// }); +// } +// } +// } +// } +// }); +// } +// } +// } +// } +// }); +// return true; +// } + @Override public void setHeightMap(FaweChunk chunk, byte[] heightMap) { Chunk forgeChunk = (Chunk) chunk.getChunk();