Various minor

Add hcyl thickness Closes #467
Fix angle mask
This commit is contained in:
Jesse Boyd 2017-08-23 18:06:16 +10:00
parent 6db1910710
commit ce19c2026b
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
7 changed files with 300 additions and 110 deletions

View File

@ -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);
}

View File

@ -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) {
Object plugin = TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
try {
Object plugin = metrics.getClass().getMethod("getPluginData").invoke(metrics);
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);

View File

@ -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,6 +2250,48 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
final int ceilRadiusZ = (int) Math.ceil(radiusZ);
double dx, dxz, dz;
double nextXn = 0;
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);
}
}
}
} else {
forX:
for (int x = 0; x <= ceilRadiusX; ++x) {
final double xn = nextXn;
@ -2275,6 +2325,7 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
}
}
}
}
return this.changes;
}

View File

@ -546,7 +546,7 @@ public class BrushCommands extends MethodCommands {
}
@Command(
aliases = {"cylinder", "cyl", "c"},
aliases = {"cylinder", "cyl", "c", "disk", "disc"},
usage = "<pattern> [radius=2] [height=1]",
flags = "h",
desc = "Creates a cylinder",

View File

@ -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);
}

View File

@ -221,6 +221,128 @@ public class ForgeQueue_All extends NMSMappedFaweQueue<World, Chunk, ExtendedBlo
}
}
// @Override
// public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean load) {
// TaskManager.IMP.sync(new RunnableVal<Boolean>() {
// @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<Chunk> 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<File, RegionFile> map = RegionFileCache.a;
// Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
// String requiredPath = world.getName() + File.separator + "region";
// while (iter.hasNext()) {
// Map.Entry<File, RegionFile> 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<Object>() {
// @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();