Refactor + Fixes #300

This commit is contained in:
Jesse Boyd 2016-09-20 18:46:49 +10:00
parent 326dcab1f1
commit 07f4f61cc2
33 changed files with 849 additions and 254 deletions

View File

@ -46,15 +46,6 @@ subprojects {
sourceCompatibility = 1.7 sourceCompatibility = 1.7
targetCompatibility = 1.7 targetCompatibility = 1.7
dependencies {
compile(group: 'com.sk89q.worldedit', name: 'worldedit-core', version:'6.1.3-SNAPSHOT') {
exclude(module: 'bukkit-classloader-check')
}
compile 'com.sk89q:worldguard:6.0.0-SNAPSHOT'
compile 'com.plotsquared:PlotSquared:3.4.1-SNAPSHOT'
compile 'org.primesoft:BlocksHub:2.0'
}
repositories { repositories {
mavenCentral() mavenCentral()
maven {url "http://ci.regularbox.com/plugin/repository/everything/"} maven {url "http://ci.regularbox.com/plugin/repository/everything/"}

View File

@ -1,5 +1,6 @@
dependencies { dependencies {
compile project(':core') compile project(':core')
compile 'org.bukkit.craftbukkitv1_10:craftbukkitv1_10:1.10'
compile 'net.milkbowl.vault:VaultAPI:1.5' compile 'net.milkbowl.vault:VaultAPI:1.5'
compile 'com.massivecraft:factions:2.8.0' compile 'com.massivecraft:factions:2.8.0'
compile 'com.drtshock:factions:1.6.9.5' compile 'com.drtshock:factions:1.6.9.5'
@ -12,6 +13,7 @@ dependencies {
compile 'com.palmergames.bukkit:towny:0.84.0.9' compile 'com.palmergames.bukkit:towny:0.84.0.9'
compile 'com.worldcretornica:plotme_core:0.16.3' compile 'com.worldcretornica:plotme_core:0.16.3'
compile 'junit:junit:4.11' compile 'junit:junit:4.11'
compile 'com.sk89q.worldedit:worldedit-bukkit:6.1.1-SNAPSHOT' compile 'com.sk89q.worldedit:worldedit-bukkit:6.1.4-SNAPSHOT'
compile 'com.sk89q.worldedit:worldedit-core:6.1.4-SNAPSHOT'
compile 'com.thevoxelbox.voxelsniper:voxelsniper:5.171.0' compile 'com.thevoxelbox.voxelsniper:voxelsniper:5.171.0'
} }

View File

@ -22,6 +22,7 @@ import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.bukkit.EditSessionBlockChangeDelegate;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import java.io.File; import java.io.File;
@ -78,6 +79,8 @@ public class FaweBukkit implements IFawe, Listener {
new ChunkListener(); new ChunkListener();
} }
}); });
// Inject
EditSessionBlockChangeDelegate.inject();
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import org.bukkit.Difficulty;
import org.bukkit.Effect; import org.bukkit.Effect;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.TreeType; import org.bukkit.TreeType;
import org.bukkit.World; import org.bukkit.World;
@ -33,7 +34,6 @@ import org.bukkit.WorldType;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.entity.Arrow; import org.bukkit.entity.Arrow;
import org.bukkit.entity.CreatureType;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.FallingBlock; import org.bukkit.entity.FallingBlock;
@ -63,6 +63,11 @@ public class AsyncWorld implements World {
private FaweQueue queue; private FaweQueue queue;
private BukkitImplAdapter adapter; private BukkitImplAdapter adapter;
@Override
public <T> void spawnParticle(Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6, T t) {
parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5, v6, t);
}
/** /**
* @deprecated use {@link #wrap(org.bukkit.World)} instead * @deprecated use {@link #wrap(org.bukkit.World)} instead
* @param parent Parent world * @param parent Parent world
@ -158,6 +163,61 @@ public class AsyncWorld implements World {
}); });
} }
@Override
public void spawnParticle(Particle particle, Location location, int i) {
parent.spawnParticle(particle, location, i);
}
@Override
public void spawnParticle(Particle particle, double v, double v1, double v2, int i) {
parent.spawnParticle(particle, v, v1, v2, i);
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int i, T t) {
parent.spawnParticle(particle, location, i, t);
}
@Override
public <T> void spawnParticle(Particle particle, double v, double v1, double v2, int i, T t) {
parent.spawnParticle(particle, v, v1, v2, i, t);
}
@Override
public void spawnParticle(Particle particle, Location location, int i, double v, double v1, double v2) {
parent.spawnParticle(particle, location, i, v, v1, v2);
}
@Override
public void spawnParticle(Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5) {
parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5);
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int i, double v, double v1, double v2, T t) {
parent.spawnParticle(particle, location, i, v, v1, v2, t);
}
@Override
public <T> void spawnParticle(Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, T t) {
parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5, t);
}
@Override
public void spawnParticle(Particle particle, Location location, int i, double v, double v1, double v2, double v3) {
parent.spawnParticle(particle, location, i, v, v1, v2, v3);
}
@Override
public void spawnParticle(Particle particle, double v, double v1, double v2, int i, double v3, double v4, double v5, double v6) {
parent.spawnParticle(particle, v, v1, v2, i, v3, v4, v5, v6);
}
@Override
public <T> void spawnParticle(Particle particle, Location location, int i, double v, double v1, double v2, double v3, T t) {
parent.spawnParticle(particle, location, i, v, v1, v2, v3, t);
}
@Override @Override
public Block getBlockAt(final int x, final int y, final int z) { public Block getBlockAt(final int x, final int y, final int z) {
return new AsyncBlock(this, queue, x, y, z); return new AsyncBlock(this, queue, x, y, z);
@ -315,6 +375,7 @@ public class AsyncWorld implements World {
return unloadChunk(x, z, save, false); return unloadChunk(x, z, save, false);
} }
@Deprecated
@Override @Override
public boolean unloadChunk(final int x, final int z, final boolean save, final boolean safe) { public boolean unloadChunk(final int x, final int z, final boolean save, final boolean safe) {
if (isChunkLoaded(x, z)) { if (isChunkLoaded(x, z)) {
@ -385,6 +446,11 @@ public class AsyncWorld implements World {
}); });
} }
@Override
public <T extends Arrow> T spawnArrow(Location location, Vector vector, float v, float v1, Class<T> aClass) {
return parent.spawnArrow(location, vector, v, v1, aClass);
}
@Override @Override
public boolean generateTree(final Location location, final TreeType type) { public boolean generateTree(final Location location, final TreeType type) {
return TaskManager.IMP.sync(new RunnableVal<Boolean>() { return TaskManager.IMP.sync(new RunnableVal<Boolean>() {
@ -410,18 +476,6 @@ public class AsyncWorld implements World {
return spawn(loc, type.getEntityClass()); return spawn(loc, type.getEntityClass());
} }
@Override
@Deprecated
public LivingEntity spawnCreature(Location loc, EntityType type) {
return (LivingEntity)this.spawnEntity(loc, type);
}
@Override
@Deprecated
public LivingEntity spawnCreature(Location loc, CreatureType type) {
return this.spawnCreature(loc, type.toEntityType());
}
@Override @Override
public LightningStrike strikeLightning(final Location loc) { public LightningStrike strikeLightning(final Location loc) {
return TaskManager.IMP.sync(new RunnableVal<LightningStrike>() { return TaskManager.IMP.sync(new RunnableVal<LightningStrike>() {
@ -896,6 +950,16 @@ public class AsyncWorld implements World {
}); });
} }
@Override
public void playSound(final Location location, final String sound, final float volume, final float pitch) {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
parent.playSound(location, sound, volume, pitch);
}
});
}
@Override @Override
public String[] getGameRules() { public String[] getGameRules() {
return parent.getGameRules(); return parent.getGameRules();
@ -917,8 +981,18 @@ public class AsyncWorld implements World {
} }
@Override @Override
public void setMetadata(String key, MetadataValue value) { public Spigot spigot() {
parent.setMetadata(key, value); return parent.spigot();
}
@Override
public void setMetadata(final String key, final MetadataValue meta) {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
parent.setMetadata(key, meta);
}
});
} }
@Override @Override
@ -932,8 +1006,13 @@ public class AsyncWorld implements World {
} }
@Override @Override
public void removeMetadata(String key, Plugin plugin) { public void removeMetadata(final String key, final Plugin plugin) {
parent.removeMetadata(key, plugin); TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
parent.removeMetadata(key, plugin);
}
});
} }
@Override @Override

View File

@ -0,0 +1,77 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit;
import com.boydti.fawe.FaweCache;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.BlockID;
import org.bukkit.BlockChangeDelegate;
/**
* Proxy class to catch calls to set blocks.
*/
public class EditSessionBlockChangeDelegate implements BlockChangeDelegate {
private EditSession editSession;
public EditSessionBlockChangeDelegate(EditSession editSession) {
this.editSession = editSession;
}
@Override
public boolean setRawTypeId(int x, int y, int z, int typeId) {
return editSession.setBlock(x, y, z, FaweCache.getBlock(typeId, 0));
}
@Override
public boolean setRawTypeIdAndData(int x, int y, int z, int typeId, int data) {
return editSession.setBlock(x, y, z, FaweCache.getBlock(typeId, data));
}
@Override
public boolean setTypeId(int x, int y, int z, int typeId) {
return setRawTypeId(x, y, z, typeId);
}
@Override
public boolean setTypeIdAndData(int x, int y, int z, int typeId, int data) {
return setRawTypeIdAndData(x, y, z, typeId, data);
}
@Override
public int getTypeId(int x, int y, int z) {
return editSession.getBlock(x, y, z).getId();
}
@Override
public int getHeight() {
return editSession.getMaxY() + 1;
}
@Override
public boolean isEmpty(int x, int y, int z) {
return editSession.getBlock(x, y, z).getId() == BlockID.AIR;
}
public static Class<?> inject() {
return EditSessionBlockChangeDelegate.class;
}
}

View File

@ -1,6 +1,5 @@
dependencies { dependencies {
compile project(':bukkit0') compile project(':bukkit0')
compile 'org.bukkit.craftbukkitv1_10:craftbukkitv1_10:1.10'
} }
processResources { processResources {

View File

@ -3,6 +3,12 @@ dependencies {
compile 'org.yaml:snakeyaml:1.16' compile 'org.yaml:snakeyaml:1.16'
compile 'com.google.code.gson:gson:2.2.4' compile 'com.google.code.gson:gson:2.2.4'
compile 'net.fabiozumbi12:redprotect:1.9.6' compile 'net.fabiozumbi12:redprotect:1.9.6'
compile 'com.sk89q:worldguard:6.0.0-SNAPSHOT'
compile 'com.plotsquared:PlotSquared:3.4.1-SNAPSHOT'
compile 'org.primesoft:BlocksHub:2.0'
compile(group: 'com.sk89q.worldedit', name: 'worldedit-core', version:'6.1.3-SNAPSHOT') {
exclude(module: 'bukkit-classloader-check')
}
} }
sourceCompatibility = 1.7 sourceCompatibility = 1.7

View File

@ -46,8 +46,13 @@ import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
import com.sk89q.worldedit.extent.clipboard.io.SchematicWriter; import com.sk89q.worldedit.extent.clipboard.io.SchematicWriter;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent; import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.entity.ExtentEntityCopy; import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
import com.sk89q.worldedit.function.mask.BlockMask;
import com.sk89q.worldedit.function.mask.FuzzyBlockMask;
import com.sk89q.worldedit.function.mask.SolidBlockMask;
import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.pattern.ClipboardPattern;
import com.sk89q.worldedit.function.pattern.Patterns; import com.sk89q.worldedit.function.pattern.Patterns;
import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
import com.sk89q.worldedit.function.visitor.DownwardVisitor; import com.sk89q.worldedit.function.visitor.DownwardVisitor;
import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.function.visitor.EntityVisitor;
@ -368,6 +373,12 @@ public class Fawe {
Vector.inject(); // Optimizations Vector.inject(); // Optimizations
// Pattern // Pattern
Patterns.inject(); // Optimizations (reduce object creation) Patterns.inject(); // Optimizations (reduce object creation)
RandomPattern.inject(); // Optimizations
ClipboardPattern.inject(); // Optimizations
// Mask
BlockMask.inject(); // Optimizations
SolidBlockMask.inject(); // Optimizations
FuzzyBlockMask.inject(); // Optimizations
// Operations // Operations
Operations.inject(); // Optimizations Operations.inject(); // Optimizations
// BlockData // BlockData

View File

@ -407,7 +407,7 @@ public class FaweAPI {
NMSRelighter relighter = new NMSRelighter(nmsQueue); NMSRelighter relighter = new NMSRelighter(nmsQueue);
for (int x = minX; x <= maxX; x++) { for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z ++) { for (int z = minZ; z <= maxZ; z ++) {
relighter.addChunk(x, z, null); relighter.addChunk(x, z, null, 65536);
count++; count++;
} }
} }

View File

@ -79,7 +79,7 @@ public class FaweCache {
* @param data * @param data
* @return * @return
*/ */
public static BaseBlock getBlock(int id, int data) { public static final BaseBlock getBlock(int id, int data) {
return CACHE_BLOCK[(id << 4) + data]; return CACHE_BLOCK[(id << 4) + data];
} }
@ -89,15 +89,15 @@ public class FaweCache {
* @param data * @param data
* @return * @return
*/ */
public static int getCombined(int id, int data) { public static final int getCombined(int id, int data) {
return (id << 4) + data; return (id << 4) + data;
} }
public static int getId(int combined) { public static final int getId(int combined) {
return combined >> 4; return combined >> 4;
} }
public static int getData(int combined) { public static final int getData(int combined) {
return combined & 15; return combined & 15;
} }
@ -106,11 +106,11 @@ public class FaweCache {
* @param block * @param block
* @return * @return
*/ */
public static int getCombined(BaseBlock block) { public static final int getCombined(BaseBlock block) {
return getCombined(block.getId(), block.getData()); return getCombined(block.getId(), block.getData());
} }
public static Color getColor(int id, int data) { public static final Color getColor(int id, int data) {
Color exact = CACHE_COLOR[getCombined(id, data)]; Color exact = CACHE_COLOR[getCombined(id, data)];
if (exact != null) { if (exact != null) {
return exact; return exact;

View File

@ -62,7 +62,7 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
relighter = new NMSRelighter(this); relighter = new NMSRelighter(this);
} }
if (Settings.LIGHTING.MODE == 2) { if (Settings.LIGHTING.MODE == 2) {
relighter.addChunk(chunk.getX(), chunk.getZ(), null); relighter.addChunk(chunk.getX(), chunk.getZ(), null, chunk.getBitMask());
return; return;
} }
CharFaweChunk cfc = (CharFaweChunk) chunk; CharFaweChunk cfc = (CharFaweChunk) chunk;
@ -76,7 +76,7 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
} }
} }
if (relight) { if (relight) {
relighter.addChunk(chunk.getX(), chunk.getZ(), fix); relighter.addChunk(chunk.getX(), chunk.getZ(), fix, chunk.getBitMask());
} else { } else {
refreshChunk(chunk); refreshChunk(chunk);
} }

View File

@ -28,12 +28,12 @@ public class NMSRelighter {
this.maxY = queue.getWEWorld().getMaxY(); this.maxY = queue.getWEWorld().getMaxY();
} }
public boolean addChunk(int cx, int cz, boolean[] fix) { public boolean addChunk(int cx, int cz, boolean[] fix, int bitmask) {
long pair = MathMan.pairInt(cx, cz); long pair = MathMan.pairInt(cx, cz);
if (skyToRelight.containsKey(pair)) { if (skyToRelight.containsKey(pair)) {
return false; return false;
} }
skyToRelight.put(pair, new RelightSkyEntry(cx, cz, fix)); skyToRelight.put(pair, new RelightSkyEntry(cx, cz, fix, bitmask));
return true; return true;
} }
@ -126,16 +126,9 @@ public class NMSRelighter {
for (Map.Entry<Long, RelightSkyEntry> entry : skyToRelight.entrySet()) { for (Map.Entry<Long, RelightSkyEntry> entry : skyToRelight.entrySet()) {
RelightSkyEntry chunk = entry.getValue(); RelightSkyEntry chunk = entry.getValue();
CharFaweChunk fc = (CharFaweChunk) queue.getFaweChunk(chunk.x, chunk.z); CharFaweChunk fc = (CharFaweChunk) queue.getFaweChunk(chunk.x, chunk.z);
int mask = 0; fc.setBitMask(chunk.bitmask);
for (int y = 0; y < chunk.fix.length; y++) {
if (chunk.fix[y]) {
mask += 1 << y;
}
}
fc.setBitMask(mask);
queue.sendChunk(fc); queue.sendChunk(fc);
} }
} }
private boolean isTransparent(int x, int y, int z) { private boolean isTransparent(int x, int y, int z) {
@ -338,14 +331,16 @@ public class NMSRelighter {
public final int z; public final int z;
public final byte[] mask; public final byte[] mask;
public final boolean[] fix; public final boolean[] fix;
public final int bitmask;
public boolean smooth; public boolean smooth;
public RelightSkyEntry(int x, int z, boolean[] fix) { public RelightSkyEntry(int x, int z, boolean[] fix, int bitmask) {
this.x = x; this.x = x;
this.z = z; this.z = z;
byte[] array = new byte[maxY + 1]; byte[] array = new byte[maxY + 1];
Arrays.fill(array, (byte) 15); Arrays.fill(array, (byte) 15);
this.mask = array; this.mask = array;
this.bitmask = bitmask;
if (fix == null) { if (fix == null) {
this.fix = new boolean[(maxY + 1) >> 4]; this.fix = new boolean[(maxY + 1) >> 4];
Arrays.fill(this.fix, true); Arrays.fill(this.fix, true);

View File

@ -5,6 +5,7 @@ import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
@ -116,7 +117,12 @@ public abstract class FawePlayer<T> {
try { try {
run.run(); run.run();
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); FaweException fe = FaweException.get(e);
if (fe != null) {
sendMessage(fe.getMessage());
} else {
e.printStackTrace();
}
} }
runningCount.decrementAndGet(); runningCount.decrementAndGet();
Runnable next = getActions().poll(); Runnable next = getActions().poll();
@ -157,7 +163,16 @@ public abstract class FawePlayer<T> {
public boolean runAction(final Runnable ifFree, boolean checkFree, boolean async) { public boolean runAction(final Runnable ifFree, boolean checkFree, boolean async) {
if (checkFree) { if (checkFree) {
queueAction(ifFree); if (async) {
TaskManager.IMP.taskNow(new Runnable() {
@Override
public void run() {
queueAction(ifFree);
}
}, async);
} else {
queueAction(ifFree);
}
return true; return true;
} else { } else {
TaskManager.IMP.taskNow(ifFree, async); TaskManager.IMP.taskNow(ifFree, async);

View File

@ -20,6 +20,11 @@ public class IntegerTrio {
return hash; return hash;
} }
@Override
public String toString() {
return x + "," + y + "," + z;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) { if (obj == null) {

View File

@ -0,0 +1,45 @@
package com.boydti.fawe.object.collection;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.util.MathMan;
import java.util.ArrayList;
import java.util.Map;
public class FastRandomCollection<T> extends RandomCollection<T> {
private PseudoRandom random = new PseudoRandom();
private T[] values;
public FastRandomCollection(Map<T, Double> weights) {
super(weights);
int max = 0;
int[] counts = new int[weights.size()];
Double[] weightDoubles = weights.values().toArray(new Double[weights.size()]);
for (int i = 0; i < weightDoubles.length; i++) {
int weight = (int) (weightDoubles[i] * 100);
counts[i] = weight;
if (weight != (weightDoubles[i] * 100)) {
throw new IllegalArgumentException("Too small");
}
if (weight > max) {
max = weight;
}
}
int gcd = MathMan.gcd(counts);
if (max / gcd > 100000) {
throw new IllegalArgumentException("Too large");
}
ArrayList<T> parsed = new ArrayList<>();
for (Map.Entry<T, Double> entry : weights.entrySet()) {
int num = (int) (100 * entry.getValue());
for (int j = 0; j < num / gcd; j++) {
parsed.add(entry.getKey());
}
}
this.values = (T[]) parsed.toArray();
}
@Override
public T next() {
return values[random.nextInt(values.length)];
}
}

View File

@ -0,0 +1,17 @@
package com.boydti.fawe.object.collection;
import java.util.Map;
public abstract class RandomCollection<T> {
public RandomCollection(Map<T, Double> weights) {}
public static <T> RandomCollection<T> of(Map<T, Double> weights) {
try {
return new FastRandomCollection<>(weights);
} catch (IllegalArgumentException ignore) {
return new SimpleRandomCollection<>(weights);
}
}
public abstract T next();
}

View File

@ -0,0 +1,33 @@
package com.boydti.fawe.object.collection;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Random;
import java.util.TreeMap;
public class SimpleRandomCollection<E> extends RandomCollection<E> {
private final NavigableMap<Double, E> map = new TreeMap<Double, E>();
private final Random random;
private double total = 0;
public SimpleRandomCollection(Map<E, Double> weights) {
super(weights);
this.random = new Random();
for (Map.Entry<E, Double> entry : weights.entrySet()) {
add(entry.getValue(), entry.getKey());
}
}
public void add(double weight, E result) {
if (weight <= 0) return;
total += weight;
map.put(total, result);
}
public E next() {
double value = random.nextDouble() * total;
return map.ceilingEntry(value).getValue();
}
}

View File

@ -44,7 +44,7 @@ public class AboveVisitor extends RecursiveVisitor {
} }
@Override @Override
protected boolean isVisitable(Vector from, Vector to) { public boolean isVisitable(Vector from, Vector to) {
return (from.getBlockY() >= baseY) && super.isVisitable(from, to); return (from.getBlockY() >= baseY) && super.isVisitable(from, to);
} }
} }

View File

@ -43,56 +43,71 @@ public class MathMan {
253, 254, 254, 255 253, 254, 254, 255
}; };
public static long inverseRound(double val) { public static final long inverseRound(double val) {
long round = Math.round(val); long round = Math.round(val);
return (long) (round + Math.signum(val - round)); return (long) (round + Math.signum(val - round));
} }
public static short pairByte(int x, int y) { public static final short pairByte(int x, int y) {
return (short) ((x << 8) | (y & 0xFF)); return (short) ((x << 8) | (y & 0xFF));
} }
public static byte unpairShortX(short pair) { public static final byte unpairShortX(short pair) {
return (byte) (pair >> 8); return (byte) (pair >> 8);
} }
public static byte unpairShortY(short pair) { public static final byte unpairShortY(short pair) {
return (byte) pair; return (byte) pair;
} }
public static long pairInt(int x, int y) { public static final long pairInt(int x, int y) {
return (((long)x) << 32) | (y & 0xffffffffL); return (((long)x) << 32) | (y & 0xffffffffL);
} }
public static long chunkXZ2Int(int x, int z) { public static final long chunkXZ2Int(int x, int z) {
return (long)x & 4294967295L | ((long)z & 4294967295L) << 32; return (long)x & 4294967295L | ((long)z & 4294967295L) << 32;
} }
public static int unpairIntX(long pair) { public static final int unpairIntX(long pair) {
return (int)(pair >> 32); return (int)(pair >> 32);
} }
public static int unpairIntY(long pair) { public static final int unpairIntY(long pair) {
return (int)pair; return (int)pair;
} }
public static byte pair16(int x, int y) { public static final byte pair16(int x, int y) {
return (byte) (x + (y << 4)); return (byte) (x + (y << 4));
} }
public static byte unpair16x(byte value) { public static final byte unpair16x(byte value) {
return (byte) (value & 0xF); return (byte) (value & 0xF);
} }
public static byte unpair16y(byte value) { public static final byte unpair16y(byte value) {
return (byte) ((value >> 4) & 0xF); return (byte) ((value >> 4) & 0xF);
} }
public static int lossyFastDivide(int a, int b) { public static final int lossyFastDivide(int a, int b) {
return (a*((1<<16)/b))>>16; return (a*((1<<16)/b))>>16;
} }
public static int sqrt(int x) { public static final int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
public static final int gcd(int[] a) {
int result = a[0];
for (int i = 1; i < a.length; i++) {
result = gcd(result, a[i]);
}
return result;
}
public static final int sqrt(int x) {
int xn; int xn;
if (x >= 0x10000) { if (x >= 0x10000) {
@ -160,7 +175,7 @@ public class MathMan {
} }
public static double getMean(int[] array) { public static final double getMean(int[] array) {
double count = 0; double count = 0;
for (int i : array) { for (int i : array) {
count += i; count += i;
@ -168,7 +183,7 @@ public class MathMan {
return count / array.length; return count / array.length;
} }
public static double getMean(double[] array) { public static final double getMean(double[] array) {
double count = 0; double count = 0;
for (double i : array) { for (double i : array) {
count += i; count += i;
@ -176,15 +191,15 @@ public class MathMan {
return count / array.length; return count / array.length;
} }
public static int pair(short x, short y) { public static final int pair(short x, short y) {
return (x << 16) | (y & 0xFFFF); return (x << 16) | (y & 0xFFFF);
} }
public static short unpairX(int hash) { public static final short unpairX(int hash) {
return (short) (hash >> 16); return (short) (hash >> 16);
} }
public static short unpairY(int hash) { public static final short unpairY(int hash) {
return (short) (hash & 0xFFFF); return (short) (hash & 0xFFFF);
} }
@ -194,12 +209,12 @@ public class MathMan {
* @param pitch * @param pitch
* @return * @return
*/ */
public static float[] getDirection(float yaw, float pitch) { public static final float[] getDirection(float yaw, float pitch) {
double pitch_sin = Math.sin(pitch); double pitch_sin = Math.sin(pitch);
return new float[]{(float) (pitch_sin * Math.cos(yaw)), (float) (pitch_sin * Math.sin(yaw)), (float) Math.cos(pitch)}; return new float[]{(float) (pitch_sin * Math.cos(yaw)), (float) (pitch_sin * Math.sin(yaw)), (float) Math.cos(pitch)};
} }
public static int roundInt(double value) { public static final int roundInt(double value) {
return (int) (value < 0 ? (value == (int) value) ? value : value - 1 : value); return (int) (value < 0 ? (value == (int) value) ? value : value - 1 : value);
} }
@ -210,7 +225,7 @@ public class MathMan {
* @param z * @param z
* @return * @return
*/ */
public static float[] getPitchAndYaw(float x, float y, float z) { public static final float[] getPitchAndYaw(float x, float y, float z) {
float distance = sqrtApprox((z * z) + (x * x)); float distance = sqrtApprox((z * z) + (x * x));
return new float[]{atan2(y, distance), atan2(x, z)}; return new float[]{atan2(y, distance), atan2(x, z)};
} }
@ -249,15 +264,15 @@ public class MathMan {
return (atan2[(yi * ATAN2_DIM) + xi] + add) * mul; return (atan2[(yi * ATAN2_DIM) + xi] + add) * mul;
} }
public static float sqrtApprox(float f) { public static final float sqrtApprox(float f) {
return f * Float.intBitsToFloat(0x5f375a86 - (Float.floatToIntBits(f) >> 1)); return f * Float.intBitsToFloat(0x5f375a86 - (Float.floatToIntBits(f) >> 1));
} }
public static double sqrtApprox(double d) { public static final double sqrtApprox(double d) {
return Double.longBitsToDouble(((Double.doubleToLongBits(d) - (1l << 52)) >> 1) + (1l << 61)); return Double.longBitsToDouble(((Double.doubleToLongBits(d) - (1l << 52)) >> 1) + (1l << 61));
} }
public static float invSqrt(float x) { public static final float invSqrt(float x) {
float xhalf = 0.5f * x; float xhalf = 0.5f * x;
int i = Float.floatToIntBits(x); int i = Float.floatToIntBits(x);
i = 0x5f3759df - (i >> 1); i = 0x5f3759df - (i >> 1);
@ -266,14 +281,14 @@ public class MathMan {
return x; return x;
} }
public static int getPositiveId(int i) { public static final int getPositiveId(int i) {
if (i < 0) { if (i < 0) {
return (-i * 2) - 1; return (-i * 2) - 1;
} }
return i * 2; return i * 2;
} }
public static boolean isInteger(String str) { public static final boolean isInteger(String str) {
if (str == null) { if (str == null) {
return false; return false;
} }
@ -297,7 +312,7 @@ public class MathMan {
return true; return true;
} }
public static double getSD(double[] array, double av) { public static final double getSD(double[] array, double av) {
double sd = 0; double sd = 0;
for (double element : array) { for (double element : array) {
sd += Math.pow(Math.abs(element - av), 2); sd += Math.pow(Math.abs(element - av), 2);
@ -305,7 +320,7 @@ public class MathMan {
return Math.sqrt(sd / array.length); return Math.sqrt(sd / array.length);
} }
public static double getSD(int[] array, double av) { public static final double getSD(int[] array, double av) {
double sd = 0; double sd = 0;
for (int element : array) { for (int element : array) {
sd += Math.pow(Math.abs(element - av), 2); sd += Math.pow(Math.abs(element - av), 2);
@ -313,21 +328,21 @@ public class MathMan {
return Math.sqrt(sd / array.length); return Math.sqrt(sd / array.length);
} }
public static int mod(int x, int y) { public static final int mod(int x, int y) {
if (isPowerOfTwo(y)) { if (isPowerOfTwo(y)) {
return x & (y - 1); return x & (y - 1);
} }
return x % y; return x % y;
} }
public static int unsignedmod(int x, int y) { public static final int unsignedmod(int x, int y) {
if (isPowerOfTwo(y)) { if (isPowerOfTwo(y)) {
return x & (y - 1); return x & (y - 1);
} }
return x % y; return x % y;
} }
public static boolean isPowerOfTwo(int x) { public static final boolean isPowerOfTwo(int x) {
return (x & (x - 1)) == 0; return (x & (x - 1)) == 0;
} }
} }

View File

@ -191,6 +191,8 @@ public class SetQueue {
pool.awaitQuiescence(Settings.QUEUE.DISCARD_AFTER_MS, TimeUnit.MILLISECONDS); pool.awaitQuiescence(Settings.QUEUE.DISCARD_AFTER_MS, TimeUnit.MILLISECONDS);
completer = new ExecutorCompletionService(pool); completer = new ExecutorCompletionService(pool);
MainUtil.handleError(e); MainUtil.handleError(e);
} finally {
queue.runTasks();
} }
} }

View File

@ -57,9 +57,9 @@ public class Vector implements Comparable<Vector> {
* @param z the Z coordinate * @param z the Z coordinate
*/ */
public Vector(int x, int y, int z) { public Vector(int x, int y, int z) {
this.x = (double) x; this.x = x;
this.y = (double) y; this.y = y;
this.z = (double) z; this.z = z;
} }
/** /**
@ -70,9 +70,9 @@ public class Vector implements Comparable<Vector> {
* @param z the Z coordinate * @param z the Z coordinate
*/ */
public Vector(float x, float y, float z) { public Vector(float x, float y, float z) {
this.x = (double) x; this.x = x;
this.y = (double) y; this.y = y;
this.z = (double) z; this.z = z;
} }
/** /**
@ -112,7 +112,7 @@ public class Vector implements Comparable<Vector> {
* @return the x coordinate * @return the x coordinate
*/ */
public int getBlockX() { public int getBlockX() {
return (int) Math.round(x); return (int) x;
} }
/** /**
@ -150,7 +150,7 @@ public class Vector implements Comparable<Vector> {
* @return the y coordinate * @return the y coordinate
*/ */
public int getBlockY() { public int getBlockY() {
return (int) Math.round(y); return (int) (y);
} }
/** /**
@ -188,7 +188,7 @@ public class Vector implements Comparable<Vector> {
* @return the z coordinate * @return the z coordinate
*/ */
public int getBlockZ() { public int getBlockZ() {
return (int) Math.round(z); return (int) (z);
} }
/** /**
@ -773,7 +773,7 @@ public class Vector implements Comparable<Vector> {
} }
Vector other = (Vector) obj; Vector other = (Vector) obj;
return other.x == this.x && other.y == this.y && other.z == this.z; return other.x == this.x && other.z == this.z && other.y == this.y;
} }
@Override @Override
@ -789,12 +789,7 @@ public class Vector implements Comparable<Vector> {
@Override @Override
public int hashCode() { public int hashCode() {
int hash = 7; return (int) x ^ ((int) z << 16) ^ ((byte) y << 31);
hash = 79 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
hash = 79 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
hash = 79 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
return hash;
} }
@Override @Override

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.blocks; package com.sk89q.worldedit.blocks;
import com.boydti.fawe.util.StringMan;
import com.sk89q.worldedit.CuboidClipboard.FlipDirection; import com.sk89q.worldedit.CuboidClipboard.FlipDirection;
/** /**

View File

@ -35,7 +35,7 @@ import com.sk89q.worldedit.command.tool.BlockDataCyler;
import com.sk89q.worldedit.command.tool.BlockReplacer; import com.sk89q.worldedit.command.tool.BlockReplacer;
import com.sk89q.worldedit.command.tool.DistanceWand; import com.sk89q.worldedit.command.tool.DistanceWand;
import com.sk89q.worldedit.command.tool.FloatingTreeRemover; import com.sk89q.worldedit.command.tool.FloatingTreeRemover;
import com.sk89q.worldedit.command.tool.FloodFillTool; import com.sk89q.worldedit.command.tool.FloodFillTool;
import com.sk89q.worldedit.command.tool.LongRangeBuildTool; import com.sk89q.worldedit.command.tool.LongRangeBuildTool;
import com.sk89q.worldedit.command.tool.QueryTool; import com.sk89q.worldedit.command.tool.QueryTool;
import com.sk89q.worldedit.command.tool.TreePlanter; import com.sk89q.worldedit.command.tool.TreePlanter;

View File

@ -0,0 +1,136 @@
package com.sk89q.worldedit.function.mask;
import com.boydti.fawe.FaweCache;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.Extent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A mask that checks whether blocks at the given positions are matched by
* a block in a list.
*
* <p>This mask checks for both an exact block ID and data value match, as well
* for a block with the same ID but a data value of -1.</p>
*/
public class BlockMask extends AbstractExtentMask {
public final boolean[] blocks = new boolean[Character.MAX_VALUE + 1];
public final boolean[] blockIds = new boolean[4096];
public Collection<BaseBlock> computedLegacyList;
/**
* Create a new block mask.
*
* @param extent the extent
* @param blocks a list of blocks to match
*/
public BlockMask(Extent extent, Collection<BaseBlock> blocks) {
super(extent);
checkNotNull(blocks);
add(blocks);
}
/**
* Create a new block mask.
*
* @param extent the extent
* @param block an array of blocks to match
*/
public BlockMask(Extent extent, BaseBlock... block) {
this(extent, Arrays.asList(checkNotNull(block)));
}
/**
* Add the given blocks to the list of criteria.
*
* @param blocks a list of blocks
*/
public void add(Collection<BaseBlock> blocks) {
checkNotNull(blocks);
for (BaseBlock block : blocks) {
add(block);
}
}
/**
* Add the given blocks to the list of criteria.
*
* @param blocks an array of blocks
*/
public void add(BaseBlock... blocks) {
for (BaseBlock block : blocks) {
add(block);
}
}
public void add(BaseBlock block) {
blockIds[block.getId()] = true;
if (block.getData() == -1) {
for (int data = 0; data < 16; data++) {
blocks[FaweCache.getCombined(block.getId(), data)] = true;
}
} else {
blocks[FaweCache.getCombined(block)] = true;
}
computedLegacyList = null;
}
/**
* Get the list of blocks that are tested with.
*
* @return a list of blocks
*/
public Collection<BaseBlock> getBlocks() {
if (computedLegacyList != null) {
return computedLegacyList;
}
computedLegacyList = new LinkedHashSet<>();
for (int id = 0; id < FaweCache.getId(blocks.length); id++) {
boolean all = true;
ArrayList<BaseBlock> tmp = new ArrayList<BaseBlock>(16);
for (int data = 0; data < 16; data++) {
if (blocks[FaweCache.getCombined(id, data)]) {
tmp.add(FaweCache.getBlock(id, data));
}
}
if (tmp.size() == 16) {
computedLegacyList.add(new BaseBlock(id, -1));
} else {
computedLegacyList.addAll(tmp);
}
}
return computedLegacyList;
}
public boolean test(int blockId) {
return blockIds[blockId];
}
public boolean test(int blockId, int data) {
return blocks[FaweCache.getCombined(blockId, data)];
}
@Override
public boolean test(Vector vector) {
BaseBlock block = getExtent().getBlock(vector);
return blocks[FaweCache.getCombined(block)];
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
public static Class<?> inject() {
return BlockMask.class;
}
}

View File

@ -0,0 +1,33 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.Extent;
import java.util.Collection;
public class FuzzyBlockMask extends BlockMask {
public FuzzyBlockMask(Extent extent, Collection<BaseBlock> blocks) {
super(extent, blocks);
}
public FuzzyBlockMask(Extent extent, BaseBlock... block) {
super(extent, block);
}
@Override
public boolean test(Vector vector) {
Extent extent = getExtent();
Collection<BaseBlock> blocks = getBlocks();
BaseBlock lazyBlock = extent.getBlock(vector);
if (lazyBlock.getData() == -1) {
return test(lazyBlock.getId());
} else {
return test(lazyBlock.getId(), lazyBlock.getData());
}
}
public static Class<?> inject() {
return FuzzyBlockMask.class;
}
}

View File

@ -0,0 +1,30 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.extent.Extent;
import javax.annotation.Nullable;
public class SolidBlockMask extends BlockMask {
public SolidBlockMask(Extent extent) {
super(extent);
for (int id = 0; id < 4096; id++) {
for (int data = 0; data < 16; data++) {
if (!BlockType.canPassThrough(id, data)) {
add(new BaseBlock(id, data));
}
}
}
}
@Nullable
@Override
public Mask2D toMask2D() {
return null; // 9751418
}
public static Class<?> inject() {
return SolidBlockMask.class;
}
}

View File

@ -0,0 +1,46 @@
package com.sk89q.worldedit.function.pattern;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A pattern that reads from {@link Clipboard}.
*/
public class ClipboardPattern extends AbstractPattern {
private final Clipboard clipboard;
private final Vector size;
private final Vector min;
/**
* Create a new clipboard pattern.
*
* @param clipboard the clipboard
*/
public ClipboardPattern(Clipboard clipboard) {
checkNotNull(clipboard);
this.clipboard = clipboard;
this.size = clipboard.getMaximumPoint().subtract(clipboard.getMinimumPoint()).add(1, 1, 1);
this.min = clipboard.getMinimumPoint();
}
private Vector mutable = new Vector();
@Override
public BaseBlock apply(Vector position) {
int xp = Math.abs(position.getBlockX()) % size.getBlockX();
int yp = Math.abs(position.getBlockY()) % size.getBlockY();
int zp = Math.abs(position.getBlockZ()) % size.getBlockZ();
mutable.x = min.x + xp;
mutable.y = min.y + yp;
mutable.z = min.z + zp;
return clipboard.getBlock(mutable);
}
public static Class<?> inject() {
return ClipboardPattern.class;
}
}

View File

@ -0,0 +1,62 @@
package com.sk89q.worldedit.function.pattern;
import com.boydti.fawe.object.collection.RandomCollection;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import java.util.HashMap;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Uses a random pattern of a weighted list of patterns.
*/
public class RandomPattern extends AbstractPattern {
private Map<Pattern, Double> weights = new HashMap<>();
private RandomCollection<Pattern> collection;
/**
* Add a pattern to the weight list of patterns.
*
* <p>The probability for the pattern added is chance / max where max is
* the sum of the probabilities of all added patterns.</p>
*
* @param pattern the pattern
* @param chance the chance, which can be any positive number
*/
public void add(Pattern pattern, double chance) {
checkNotNull(pattern);
weights.put(pattern, chance);
collection = RandomCollection.of(weights);
}
@Override
public BaseBlock apply(Vector position) {
return collection.next().apply(position);
}
private static class Chance {
private Pattern pattern;
private double chance;
private Chance(Pattern pattern, double chance) {
this.pattern = pattern;
this.chance = chance;
}
public Pattern getPattern() {
return pattern;
}
public double getChance() {
return chance;
}
}
public static Class<?> inject() {
return RandomPattern.class;
}
}

View File

@ -1,93 +1,32 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.visitor; package com.sk89q.worldedit.function.visitor;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.sk89q.worldedit.BlockVector; import com.boydti.fawe.object.IntegerTrio;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.operation.RunContext; import com.sk89q.worldedit.function.operation.RunContext;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Performs a breadth-first search starting from points added with
* {@link #visit(com.sk89q.worldedit.Vector)}. The search continues
* to a certain adjacent point provided that the method
* {@link #isVisitable(com.sk89q.worldedit.Vector, com.sk89q.worldedit.Vector)}
* returns true for that point.
*
* <p>As an abstract implementation, this class can be used to implement
* functionality that starts at certain points and extends outward from
* those points.</p>
*/
public abstract class BreadthFirstSearch implements Operation { public abstract class BreadthFirstSearch implements Operation {
private final RegionFunction function; private final RegionFunction function;
private final Queue<BlockVector> queue = new ArrayDeque<BlockVector>(); private final List<Vector> directions = new ArrayList<>();
private final Set<BlockVector> visited = new HashSet<BlockVector>(); private final Map<Node, Integer> visited;
private final List<Vector> directions = new ArrayList<Vector>(); private final ArrayDeque<Node> queue;
private int affected = 0; private int affected = 0;
/** public BreadthFirstSearch(final RegionFunction function) {
* Create a new instance. this.queue = new ArrayDeque<>();
* this.visited = new LinkedHashMap<>();
* @param function the function to apply to visited blocks
*/
protected BreadthFirstSearch(final RegionFunction function) {
checkNotNull(function);
this.function = function; this.function = function;
this.addAxes();
}
/**
* Get the list of directions will be visited.
*
* <p>Directions are {@link com.sk89q.worldedit.Vector}s that determine
* what adjacent points area available. Vectors should not be
* unit vectors. An example of a valid direction is
* {@code new Vector(1, 0, 1)}.</p>
*
* <p>The list of directions can be cleared.</p>
*
* @return the list of directions
*/
protected Collection<Vector> getDirections() {
return this.directions;
}
/**
* Add the directions along the axes as directions to visit.
*/
protected void addAxes() {
this.directions.add(new Vector(0, -1, 0)); this.directions.add(new Vector(0, -1, 0));
this.directions.add(new Vector(0, 1, 0)); this.directions.add(new Vector(0, 1, 0));
this.directions.add(new Vector(-1, 0, 0)); this.directions.add(new Vector(-1, 0, 0));
@ -96,97 +35,150 @@ public abstract class BreadthFirstSearch implements Operation {
this.directions.add(new Vector(0, 0, 1)); this.directions.add(new Vector(0, 0, 1));
} }
/** public abstract boolean isVisitable(Vector from, Vector to);
* Add the diagonal directions as directions to visit.
*/ public Collection<Vector> getDirections() {
protected void addDiagonal() { return this.directions;
this.directions.add(new Vector(1, 0, 1));
this.directions.add(new Vector(-1, 0, -1));
this.directions.add(new Vector(1, 0, -1));
this.directions.add(new Vector(-1, 0, 1));
} }
/** private IntegerTrio[] getIntDirections() {
* Add the given location to the list of locations to visit, provided IntegerTrio[] array = new IntegerTrio[directions.size()];
* that it has not been visited. The position passed to this method for (int i = 0; i < array.length; i++) {
* will still be visited even if it fails Vector dir = directions.get(i);
* {@link #isVisitable(com.sk89q.worldedit.Vector, com.sk89q.worldedit.Vector)}. array[i] = new IntegerTrio(dir.getBlockX(), dir.getBlockY(), dir.getBlockZ());
*
* <p>This method should be used before the search begins, because if
* the position <em>does</em> fail the test, and the search has already
* visited it (because it is connected to another root point),
* the search will mark the position as "visited" and a call to this
* method will do nothing.</p>
*
* @param position the position
*/
public void visit(final Vector position) {
final BlockVector blockVector = new BlockVector(position);
if (!this.visited.contains(blockVector)) {
this.queue.add(blockVector);
this.visited.add(blockVector);
} }
return array;
} }
/** public void visit(final Vector pos) {
* Try to visit the given 'to' location. Node node = new Node((int) pos.x, (int) pos.y, (int) pos.z);
* if (!this.visited.containsKey(node)) {
* @param from the origin block visited.put(node, 0);
* @param to the block under question queue.add(node);
*/
private void visit(final Vector from, final Vector to) {
final BlockVector blockVector = to.toBlockVector();
if (!this.visited.contains(blockVector)) {
this.visited.add(blockVector);
if (this.isVisitable(from, to)) {
this.queue.add(blockVector);
}
} }
} }
/**
* Return whether the given 'to' block should be visited, starting from the
* 'from' block.
*
* @param from the origin block
* @param to the block under question
* @return true if the 'to' block should be visited
*/
protected abstract boolean isVisitable(final Vector from, final Vector to);
/**
* Get the number of affected objects.
*
* @return the number of affected
*/
public int getAffected() {
return this.affected;
}
@Override @Override
public Operation resume(final RunContext run) throws WorldEditException { public Operation resume(RunContext run) throws WorldEditException {
Vector position; Runtime runtime = Runtime.getRuntime();
while ((position = this.queue.poll()) != null) { Node from;
if (this.function.apply(position)) { Node adjacent;
this.affected++; Vector mutable = new Vector();
Vector mutable2 = new Vector();
boolean shouldTrim = false;
IntegerTrio[] dirs = getIntDirections();
for (int layer = 0; !queue.isEmpty(); layer++) {
int size = queue.size();
for (int i = 0; i < size; i++) {
from = queue.poll();
mutable.x = from.getX();
mutable.y = from.getY();
mutable.z = from.getZ();
function.apply(mutable);
affected++;
for (IntegerTrio direction : dirs) {
mutable2.x = from.getX() + direction.x;
mutable2.y = from.getY() + direction.y;
mutable2.z = from.getZ() + direction.z;
if (isVisitable(mutable, mutable2)) {
adjacent = new Node(mutable2.getBlockX(), mutable2.getBlockY(), mutable2.getBlockZ());
if (!visited.containsKey(adjacent)) {
visited.put(adjacent, layer);
queue.add(adjacent);
}
}
}
} }
int lastLayer = layer - 1;
for (final Vector dir : this.directions) { size = visited.size();
this.visit(position, position.add(dir)); if (shouldTrim || (shouldTrim = size > 16384)) {
Iterator<Map.Entry<Node, Integer>> iter = visited.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Node, Integer> entry = iter.next();
Integer val = entry.getValue();
if (val < lastLayer) {
iter.remove();
} else {
break;
}
}
} }
} }
return null; return null;
} }
@Override @Override
public void cancel() {} public void addStatusMessages(List<String> messages) {
messages.add(BBC.VISITOR_BLOCK.format(getAffected()));
}
public static Class<?> inject() { public int getAffected() {
return Operations.class; return this.affected;
} }
@Override @Override
public void addStatusMessages(final List<String> messages) { public void cancel() {
messages.add(BBC.VISITOR_BLOCK.format(getAffected())); }
public static final class Node {
private int x,y,z;
public Node(int x, int y, int z) {
this.x = x;
this.y = (short) y;
this.z = z;
}
public Node(Node node) {
this.x = node.x;
this.y = node.y;
this.z = node.z;
}
public Node() {}
private final void set(int x, int y, int z) {
this.x = x;
this.y = (short) y;
this.z = z;
}
private final void set(Node node) {
this.x = node.x;
this.y = node.y;
this.z = node.z;
}
@Override
public final int hashCode() {
return (x ^ (z << 12)) ^ (y << 24);
}
private final int getX() {
return x;
}
private final int getY() {
return y;
}
private final int getZ() {
return z;
}
@Override
public String toString() {
return x + "," + y + "," + z;
}
@Override
public boolean equals(Object obj) {
Node other = (Node) obj;
return other.x == x && other.z == z && other.y == y;
}
}
public static Class<?> inject() {
return BreadthFirstSearch.class;
} }
} }

View File

@ -62,12 +62,12 @@ public class DownwardVisitor extends RecursiveVisitor {
} }
@Override @Override
protected boolean isVisitable(final Vector from, final Vector to) { public boolean isVisitable(final Vector from, final Vector to) {
final int fromY = from.getBlockY(); final int fromY = from.getBlockY();
return ((fromY == this.baseY) || (to.subtract(from).getBlockY() < 0)) && super.isVisitable(from, to); return ((fromY == this.baseY) || (to.subtract(from).getBlockY() < 0)) && super.isVisitable(from, to);
} }
public static Class<?> inject() { public static Class<?> inject() {
return Operations.class; return DownwardVisitor.class;
} }
} }

View File

@ -48,7 +48,7 @@ public class NonRisingVisitor extends RecursiveVisitor {
} }
public static Class<?> inject() { public static Class<?> inject() {
return Operations.class; return NonRisingVisitor.class;
} }
} }

View File

@ -48,11 +48,11 @@ public class RecursiveVisitor extends BreadthFirstSearch {
} }
@Override @Override
protected boolean isVisitable(final Vector from, final Vector to) { public boolean isVisitable(final Vector from, final Vector to) {
return this.mask.test(to); return this.mask.test(to);
} }
public static Class<?> inject() { public static Class<?> inject() {
return Operations.class; return RecursiveVisitor.class;
} }
} }

View File

@ -42,6 +42,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
private Vector pos1; private Vector pos1;
private Vector pos2; private Vector pos2;
private int minX,minY,minZ,maxX,maxY,maxZ;
/** /**
* Construct a new instance of this cuboid using two corners of the cuboid. * Construct a new instance of this cuboid using two corners of the cuboid.
@ -93,6 +94,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
*/ */
public void setPos1(Vector pos1) { public void setPos1(Vector pos1) {
this.pos1 = pos1; this.pos1 = pos1;
recalculate();
} }
/** /**
@ -111,6 +113,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
*/ */
public void setPos2(Vector pos2) { public void setPos2(Vector pos2) {
this.pos2 = pos2; this.pos2 = pos2;
recalculate();
} }
/** /**
@ -119,6 +122,14 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
private void recalculate() { private void recalculate() {
pos1 = pos1.clampY(0, world == null ? 255 : world.getMaxY()); pos1 = pos1.clampY(0, world == null ? 255 : world.getMaxY());
pos2 = pos2.clampY(0, world == null ? 255 : world.getMaxY()); pos2 = pos2.clampY(0, world == null ? 255 : world.getMaxY());
Vector min = getMinimumPoint();
Vector max = getMaximumPoint();
minX = min.getBlockX();
minY = min.getBlockY();
minZ = min.getBlockZ();
maxX = max.getBlockX();
maxY = max.getBlockY();
maxZ = max.getBlockZ();
} }
/** /**
@ -337,13 +348,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
int x = position.getBlockX(); int x = position.getBlockX();
int y = position.getBlockY(); int y = position.getBlockY();
int z = position.getBlockZ(); int z = position.getBlockZ();
return x >= this.minX && x <= this.maxX && z >= this.minZ && z <= this.maxZ && y >= this.minY && y <= this.maxY;
Vector min = getMinimumPoint();
Vector max = getMaximumPoint();
return x >= min.getBlockX() && x <= max.getBlockX()
&& y >= min.getBlockY() && y <= max.getBlockY()
&& z >= min.getBlockZ() && z <= max.getBlockZ();
} }
@Override @Override