Implement region blacklisting

This commit is contained in:
dordsor21
2021-09-23 01:19:24 +01:00
parent f0880a27a0
commit 7d894228d0
25 changed files with 657 additions and 491 deletions

View File

@ -152,12 +152,24 @@ public class FaweAPI {
}
/**
* Get a player's allowed WorldEdit region.
* Get a player's allowed WorldEdit region(s).
*/
public static Region[] getRegions(Player player) {
return WEManager.IMP.getMask(player);
}
/**
* Get a player's allowed WorldEdit region(s).
*
* @param player Player to get mask of
* @param type Mask type; whether to check if the player is an owner of a member of the regions
* @param isWhiteList If searching for whitelist or blacklist regions. True if whitelist
* @return array of allowed regions if whitelist, else of disallowed regions.
*/
public static Region[] getRegions(Player player, FaweMaskManager.MaskType type, boolean isWhiteList) {
return WEManager.IMP.getMask(player, type, isWhiteList);
}
/**
* Cancel the edit with the following extent.
*

View File

@ -125,6 +125,12 @@ public class Settings extends Config {
" - OWNER = Players who own the region"
})
public String MODE = "MEMBER";
@Comment({
"Allow region blacklists.",
" - Currently only implemented for WorldGuard ",
" - see region-restrictions-options.worldguard-region-blacklist"
})
public boolean ALLOW_BLACKLISTS = false;
@Comment({
"List of plugin mask managers that should be exclusive. Exclusive managers are not ",
"checked for edit restrictions if another manager already allowed an edit, and further ",
@ -133,6 +139,16 @@ public class Settings extends Config {
" - Some custom-implementations in other plugins may override this setting"
})
public List<String> EXCLUSIVE_MANAGERS = new ArrayList<>(Collections.singleton(("ExamplePlugin")));
@Comment({
"If a worldguard-protected world should be considered as a region blacklist.",
" - This will create a blacklist of regions where an edit cannot operate.",
" - Useful for a \"freebuild\" worlds with few protected areas.",
" - May cause performance loss with large numbers of protected areas.",
" - Requires region-restrictions-options.allow-blacklists be true.",
" - Will still search for current allowed regions to limit the edit to.",
" - Any blacklist regions are likely to override any internal allowed regions."
})
public boolean WORLDGUARD_REGION_BLACKLIST = false;
}

View File

@ -84,7 +84,7 @@ public class ProvideBindings extends Bindings {
}
public Region[] regions(Player player, FaweMaskManager.MaskType type) {
Region[] regions = player.getCurrentRegions(type);
Region[] regions = player.getAllowedRegions(type);
if (regions == null) {
throw new IllegalArgumentException(Caption.toString(Caption.of("fawe.error.no.region")));
}

View File

@ -50,7 +50,7 @@ public class HeightBoundExtent extends FaweRegionExtent {
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
if (trimY(set, min, max) | trimNBT(set, this::contains)) {
if (trimY(set, min, max, true) | trimNBT(set, this::contains)) {
return set;
}
return null;

View File

@ -4,88 +4,177 @@ import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.regions.RegionWrapper;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionIntersection;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Future;
public class MultiRegionExtent extends FaweRegionExtent {
@Nullable
private final RegionIntersection intersection;
private final Region[] regions;
@Nullable
private final RegionIntersection disallowedIntersection;
@Nullable
private final Region[] allowed;
@Nullable
private final Region[] disallowed;
@Nullable
private Region region;
private int index;
/**
* Create a new instance.
* Create a new instance. Has both allowed and disallowed regions. Assumes that disallowed regions are encompassed by
* allowed regions.
*
* @param extent the extent
* @param extent the extent
* @param limit the limit to be used
* @param allowed the allowed regions or null for global editing
* @param disallowed the disallowed regions or null for no disallowed regions
*/
public MultiRegionExtent(Extent extent, FaweLimit limit, Region[] regions) {
public MultiRegionExtent(Extent extent, FaweLimit limit, @Nullable Region[] allowed, @Nullable Region[] disallowed) {
super(extent, limit);
this.index = 0;
this.region = regions[0];
this.regions = regions;
this.intersection = new RegionIntersection(Arrays.asList(regions));
if (allowed != null && !allowed[0].isGlobal()) {
this.region = allowed[0];
this.allowed = allowed;
this.intersection = new RegionIntersection(Arrays.asList(allowed));
} else {
this.region = null;
this.allowed = null;
this.intersection = null;
}
if (disallowed != null && disallowed.length > 0) {
this.disallowed = disallowed;
this.disallowedIntersection = new RegionIntersection(Arrays.asList(disallowed));
} else {
this.disallowed = null;
this.disallowedIntersection = null;
}
}
@Override
public boolean contains(int x, int y, int z) {
if (region.contains(x, y, z)) {
if (region != null && region.contains(x, y, z)) {
if (disallowed != null) {
for (final Region disallow : disallowed) {
if (disallow.contains(x, y, z)) {
return false;
}
}
}
return true;
}
for (int i = 0; i < regions.length; i++) {
if (i != index) {
Region current = regions[i];
if (current.contains(x, y, z)) {
region = current;
index = i;
return true;
boolean result = allowed == null;
if (!result) {
for (int i = 0; i < allowed.length; i++) {
if (i != index) {
Region current = allowed[i];
if (current.contains(x, y, z)) {
region = current;
index = i;
result = true;
break;
}
}
}
}
return false;
}
@Override
public boolean processGet(int chunkX, int chunkZ) {
for (Region region : regions) {
if (region.containsChunk(chunkX, chunkZ)) {
return true;
if (!result || disallowed == null) {
return result;
}
for (final Region disallow : disallowed) {
if (disallow.contains(x, y, z)) {
return false;
}
}
return false;
return true;
}
@Override
public boolean contains(int x, int z) {
if (region.contains(x, z)) {
if (region != null && region.contains(x, z)) {
if (disallowed != null) {
for (final Region disallow : disallowed) {
if (disallow.contains(x, z)) {
return false;
}
}
}
return true;
}
for (int i = 0; i < regions.length; i++) {
if (i != index) {
Region current = regions[i];
if (current.contains(x, z)) {
region = current;
index = i;
return true;
boolean result = allowed == null;
if (!result) {
for (int i = 0; i < allowed.length; i++) {
if (i != index) {
Region current = allowed[i];
if (current.contains(x, z)) {
region = current;
index = i;
result = true;
break;
}
}
}
}
return false;
if (!result || disallowed == null) {
return result;
}
for (final Region disallow : disallowed) {
if (disallow.contains(x, z)) {
return false;
}
}
return true;
}
/**
* Get all allowed regions
*/
@Override
public Collection<Region> getRegions() {
if (allowed == null) {
return List.of(RegionWrapper.GLOBAL());
}
return Arrays.asList(allowed);
}
@Override
public Collection<Region> getRegions() {
return Arrays.asList(regions);
public boolean processGet(int chunkX, int chunkZ) {
boolean result = allowed == null;
if (!result) {
for (Region region : allowed) {
if (region.containsChunk(chunkX, chunkZ)) {
result = true;
break;
}
}
}
if (!result || disallowed == null) {
return result;
}
for (Region region : disallowed) {
if (region.containsChunk(chunkX, chunkZ)) {
return false;
}
}
return true;
}
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return intersection.processSet(chunk, get, set);
if (intersection != null) {
set = intersection.processSet(chunk, get, set);
}
if (disallowedIntersection != null) {
intersection.processSet(chunk, get, set);
}
return set;
}
@Override

View File

@ -34,61 +34,99 @@ public interface IBatchProcessor {
Extent construct(Extent child);
/**
* Utility method to trim a chunk based on min and max Y.
* Utility method to trim a chunk based on min and max Y (inclusive).
*
* @param keepInsideRange if all blocks inside the range (inclusive) should be kept (default)
* @return false if chunk is empty of blocks
*/
default boolean trimY(IChunkSet set, int minY, int maxY) {
default boolean trimY(IChunkSet set, int minY, int maxY, final boolean keepInsideRange) {
int minLayer = (minY - 1) >> 4;
for (int layer = set.getMinSectionPosition(); layer <= minLayer; layer++) {
if (set.hasSection(layer)) {
if (layer == minLayer) {
char[] arr = set.loadIfPresent(layer);
if (arr != null) {
int index = (minY & 15) << 8;
for (int i = 0; i < index; i++) {
arr[i] = 0;
}
} else {
arr = new char[4096];
}
set.setBlocks(layer, arr);
} else {
set.setBlocks(layer, null);
}
}
}
int maxLayer = (maxY + 1) >> 4;
for (int layer = maxLayer; layer < set.getMaxSectionPosition(); layer++) {
if (set.hasSection(layer)) {
if (layer == minLayer) {
char[] arr = set.loadIfPresent(layer);
if (arr != null) {
int index = ((maxY + 1) & 15) << 8;
for (int i = index; i < arr.length; i++) {
arr[i] = 0;
}
} else {
arr = new char[4096];
}
set.setBlocks(layer, arr);
} else {
set.setBlocks(layer, null);
}
}
}
try {
int layer = (minY - 15) >> 4;
while (layer < (maxY + 15) >> 4) {
if (keepInsideRange) {
for (int layer = set.getMinSectionPosition(); layer <= minLayer; layer++) {
if (set.hasSection(layer)) {
return true;
if (layer == minLayer) {
char[] arr = set.loadIfPresent(layer);
if (arr != null) {
int index = (minY & 15) << 8;
for (int i = 0; i < index; i++) {
arr[i] = 0;
}
} else {
arr = new char[4096];
}
set.setBlocks(layer, arr);
} else {
set.setBlocks(layer, null);
}
}
layer++;
}
} catch (ArrayIndexOutOfBoundsException exception) {
WorldEdit.logger.error("minY = {} , layer = {}", minY, ((minY - 15) >> 4), exception);
for (int layer = maxLayer; layer < set.getMaxSectionPosition(); layer++) {
if (set.hasSection(layer)) {
if (layer == minLayer) {
char[] arr = set.loadIfPresent(layer);
if (arr != null) {
int index = ((maxY + 1) & 15) << 8;
for (int i = index; i < arr.length; i++) {
arr[i] = 0;
}
} else {
arr = new char[4096];
}
set.setBlocks(layer, arr);
} else {
set.setBlocks(layer, null);
}
}
}
try {
int layer = (minY - 15) >> 4;
while (layer < (maxY + 15) >> 4) {
if (set.hasSection(layer)) {
return true;
}
layer++;
}
} catch (ArrayIndexOutOfBoundsException exception) {
WorldEdit.logger.error("IBatchProcessor: minY = {} , layer = {}", minY, ((minY - 15) >> 4), exception);
}
return false;
}
return false;
int chunkMaxY = (set.getMaxSectionPosition() << 4) + 15;
int chunkMinY = set.getMinSectionPosition() << 4;
if (maxY >= chunkMaxY && minY <= chunkMinY) {
set.reset();
return false;
}
boolean hasBlocks = false;
for (int layer = set.getMinSectionPosition(); layer <= set.getMaxSectionPosition(); layer++) {
if (layer < minLayer || layer > maxLayer) {
hasBlocks |= set.hasSection(layer);
continue;
}
if (layer == minLayer) {
char[] arr = set.loadIfPresent(layer);
if (arr != null) {
int index = (minY & 15) << 8;
for (int i = index; i < 4096; i++) {
arr[i] = 0;
}
}
set.setBlocks(layer, arr);
} else if (layer == maxLayer) {
char[] arr = set.loadIfPresent(layer);
if (arr != null) {
int index = ((maxY + 1) & 15) << 8;
for (int i = 0; i < index; i++) {
arr[i] = 0;
}
}
set.setBlocks(layer, arr);
} else {
set.setBlocks(layer, null);
}
}
return hasBlocks;
}
/**

View File

@ -23,22 +23,39 @@ public abstract class FaweMaskManager {
return this.key;
}
/**
* Get a {@link FaweMask} for the given player and {@link MaskType}
*
* @deprecated Use {@link #getMask(Player, MaskType, boolean)}
*/
@Deprecated(forRemoval = true)
public FaweMask getMask(final Player player, MaskType type) {
return null;
}
/**
* Get a {@link FaweMask} for the given player and {@link MaskType}. If isWhitelist is false, will return a "blacklist" mask.
*/
public FaweMask getMask(final Player player, MaskType type, boolean isWhitelist) {
return null;
}
/**
* @deprecated Not used internally
*/
@Deprecated(forRemoval = true)
public boolean isValid(FaweMask mask) {
return true;
}
/**
* @deprecated Not used internally
*/
@Deprecated(forRemoval = true)
public RegionFilter getFilter(String world) {
return null;
}
private boolean hasMemberPermission(Player player) {
return player.hasPermission("fawe." + getKey() + ".member");
}
public boolean isExclusive() {
return Settings.IMP.REGION_RESTRICTIONS_OPTIONS.EXCLUSIVE_MANAGERS.contains(this.key);
}

View File

@ -391,7 +391,7 @@ public class EditSessionBuilder {
}
if (allowedRegions == null) {
if (player != null && !player.hasPermission("fawe.bypass") && !player.hasPermission("fawe.bypass.regions")) {
allowedRegions = player.getCurrentRegions();
allowedRegions = player.getAllowedRegions();
}
}
FaweRegionExtent regionExtent = null;
@ -402,7 +402,7 @@ public class EditSessionBuilder {
if (allowedRegions.length == 1) {
regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]);
} else {
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions);
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, null);
}
}
} else {

View File

@ -61,19 +61,26 @@ public class WEManager {
cancelEditSafe(parent, reason);
}
@Deprecated
public Region[] getMask(Player player) {
return getMask(player, FaweMaskManager.MaskType.getDefaultMaskType());
}
public boolean isIn(int x, int y, int z, Region region) {
return region.contains(x, y, z);
}
/**
* Get a player's mask.
* Get a player's allowed WorldEdit region(s).
*/
public Region[] getMask(Player player, FaweMaskManager.MaskType type) {
public Region[] getMask(Player player) {
return getMask(player, FaweMaskManager.MaskType.getDefaultMaskType(), true);
}
/**
* Get a player's mask.
*
* @param player Player to get mask of
* @param type Mask type; whether to check if the player is an owner of a member of the regions
* @param isWhitelist If searching for whitelist or blacklist regions. True if whitelist
* @return array of allowed regions if whitelist, else of disallowed regions.
*/
public Region[] getMask(Player player, FaweMaskManager.MaskType type, final boolean isWhitelist) {
if (!Settings.IMP.REGION_RESTRICTIONS || player.hasPermission("fawe.bypass") || player.hasPermission("fawe.bypass.regions")) {
return new Region[]{RegionWrapper.GLOBAL()};
}
@ -89,7 +96,7 @@ public class WEManager {
Set<Region> regions = new HashSet<>();
if (masks == null) {
if (masks == null || !isWhitelist) {
masks = new HashSet<>();
} else {
synchronized (masks) {
@ -125,7 +132,7 @@ public class WEManager {
if (manager.isExclusive() && !masks.isEmpty()) {
continue;
}
final FaweMask mask = manager.getMask(player, FaweMaskManager.MaskType.getDefaultMaskType());
final FaweMask mask = manager.getMask(player, FaweMaskManager.MaskType.getDefaultMaskType(), isWhitelist);
if (mask != null) {
regions.add(mask.getRegion());
masks.add(mask);
@ -140,9 +147,10 @@ public class WEManager {
player.printError(TextComponent.of("Missing permission " + "fawe." + manager.getKey()));
}
}
if (!masks.isEmpty()) {
regions.addAll(backupRegions);
if (!masks.isEmpty() && isWhitelist) {
player.setMeta("lastMask", masks);
} else {
} else if (isWhitelist) {
player.deleteMeta("lastMask");
}
return regions.toArray(new Region[0]);

View File

@ -86,6 +86,7 @@ public final class EditSessionBuilder {
private FaweLimit limit;
private AbstractChangeSet changeSet;
private Region[] allowedRegions;
private Region[] disallowedRegions;
private Boolean fastMode;
private Boolean checkMemory;
private Boolean combineStages;
@ -341,6 +342,34 @@ public final class EditSessionBuilder {
return setDirty();
}
/**
* Set the regions the edit is allowed to operate in. Set to null for the regions to be calculated based on the actor if
* present
*/
public EditSessionBuilder disallowedRegions(@Nullable Region[] disallowedRegions) {
this.disallowedRegions = disallowedRegions;
return setDirty();
}
/**
* Set the regions the edit is allowed to operate in. Set to null for the regions to be calculated based on the actor if
* present
*/
@Deprecated
public EditSessionBuilder disallowedRegions(@Nullable RegionWrapper[] disallowedRegions) {
this.disallowedRegions = disallowedRegions;
return setDirty();
}
/**
* Set the region the edit is allowed to operate in. Set to null for the regions to be calculated based on the actor if
* present
*/
public EditSessionBuilder disallowedRegions(@Nullable RegionWrapper disallowedRegion) {
this.disallowedRegions = disallowedRegion == null ? null : disallowedRegion.toArray();
return setDirty();
}
/**
* Set the edit to be allowed to edit everywhere
*/
@ -511,23 +540,33 @@ public final class EditSessionBuilder {
if (actor != null && !actor.hasPermission("fawe.bypass") && !actor.hasPermission("fawe.bypass.regions")) {
if (actor instanceof Player) {
Player player = (Player) actor;
allowedRegions = player.getCurrentRegions();
allowedRegions = player.getAllowedRegions();
}
}
}
if (disallowedRegions == null && Settings.IMP.REGION_RESTRICTIONS && Settings.IMP.REGION_RESTRICTIONS_OPTIONS.ALLOW_BLACKLISTS) {
if (actor != null && !actor.hasPermission("fawe.bypass") && !actor.hasPermission("fawe.bypass.regions")) {
if (actor instanceof Player) {
Player player = (Player) actor;
disallowedRegions = player.getDisallowedRegions();
}
}
}
FaweRegionExtent regionExtent = null;
if (allowedRegions != null) {
if (disallowedRegions != null) { // Always use MultiRegionExtent if we have blacklist regions
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, disallowedRegions);
} else if (allowedRegions == null) {
allowedRegions = new Region[]{RegionWrapper.GLOBAL()};
} else {
if (allowedRegions.length == 0) {
regionExtent = new NullExtent(this.extent, FaweCache.NO_REGION);
} else {
if (allowedRegions.length == 1) {
regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]);
} else {
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions);
regionExtent = new MultiRegionExtent(this.extent, this.limit, allowedRegions, disallowedRegions);
}
}
} else {
allowedRegions = new Region[]{RegionWrapper.GLOBAL()};
}
// There's no need to do lighting (and it'll also just be a pain to implement) if we're not placing chunks
if (placeChunks) {

View File

@ -348,12 +348,46 @@ public interface Player extends Entity, Actor {
<B extends BlockStateHolder<B>> void sendFakeBlock(BlockVector3 pos, @Nullable B block);
//FAWE start
Region[] getCurrentRegions();
/**
* Get the player's current allowed WorldEdit regions.
*
* @return an array of allowed regions
*/
Region[] getAllowedRegions();
Region[] getCurrentRegions(FaweMaskManager.MaskType type);
/**
* Get the player's current allowed WorldEdit regions.
*
* @param type Mask type; whether to check if the player is an owner of a member of the regions
* @return an array of allowed regions
*/
Region[] getAllowedRegions(FaweMaskManager.MaskType type);
/**
* Get the player's current disallowed WorldEdit regions. Effectively a blacklist.
*
* @return an array of disallowed regions
*/
Region[] getDisallowedRegions();
/**
* Get the player's current disallowed WorldEdit regions. Effectively a blacklist.
*
* @param type Mask type; whether to check if the player is an owner of a member of the regions
* @return an array of disallowed regions
*/
Region[] getDisallowedRegions(FaweMaskManager.MaskType type);
/**
* Get the largest region in the player's allowed WorldEdit region.
*/
Region getLargestRegion();
/**
* Set a players selection and selector type to the given region
*/
void setSelection(Region region);
/**
* Get the player's selection region. If the selection is defined in
* a different world, the {@code IncompleteRegionException}
@ -366,8 +400,6 @@ public interface Player extends Entity, Actor {
return getSession().getSelection(getWorld());
}
void setSelection(Region region);
/**
* Set the player's WorldEdit selection.
*

View File

@ -483,27 +483,31 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
}
//FAWE start
/**
* Get the player's current allowed WorldEdit regions.
*
* @return an array of allowed regions
*/
public Region[] getCurrentRegions() {
return getCurrentRegions(FaweMaskManager.MaskType.MEMBER);
@Override
public Region[] getAllowedRegions() {
return getAllowedRegions(FaweMaskManager.MaskType.getDefaultMaskType());
}
public Region[] getCurrentRegions(FaweMaskManager.MaskType type) {
return WEManager.IMP.getMask(this, type);
@Override
public Region[] getAllowedRegions(FaweMaskManager.MaskType type) {
return WEManager.IMP.getMask(this, type, true);
}
/**
* Get the largest region in the player's allowed WorldEdit region.
*/
@Override
public Region[] getDisallowedRegions() {
return getDisallowedRegions(FaweMaskManager.MaskType.getDefaultMaskType());
}
@Override
public Region[] getDisallowedRegions(FaweMaskManager.MaskType type) {
return WEManager.IMP.getMask(this, type, false);
}
@Override
public Region getLargestRegion() {
long area = 0;
Region max = null;
for (Region region : this.getCurrentRegions()) {
for (Region region : this.getAllowedRegions()) {
final long tmp = region.getVolume();
if (tmp > area) {
area = tmp;
@ -513,6 +517,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
return max;
}
@Override
public void setSelection(Region region) {
RegionSelector selector;
if (region instanceof ConvexPolyhedralRegion) {

View File

@ -748,13 +748,13 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
if (minY <= set.getMinSectionPosition() << 4 && maxY >= (set.getMaxSectionPosition() << 4) + 15) {
return set;
}
trimY(set, minY, maxY);
trimY(set, minY, maxY, true);
trimNBT(set, this::contains);
return set;
}
if (tx >= minX && bx <= maxX && tz >= minZ && bz <= maxZ) {
if (minY > set.getMinSectionPosition() << 4 || maxY < (set.getMaxSectionPosition() << 4) + 15) {
trimY(set, minY, maxY);
trimY(set, minY, maxY, true);
}
final int lowerX = Math.max(0, minX - bx);
final int upperX = Math.min(15, 15 + maxX - tx);
@ -769,51 +769,128 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
boolean trimZ = lowerZ != 0 || upperZ != 15;
for (int layer = get.getMinSectionPosition(); layer < get.getMaxSectionPosition(); layer++) {
if (set.hasSection(layer)) {
char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true
if (trimX || trimZ) {
int indexY = 0;
for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section
int index;
if (trimZ) {
index = indexY;
for (int z = 0; z < lowerZ; z++) {
// null the z values
for (int x = 0; x < 16; x++, index++) {
arr[index] = 0;
}
}
index = indexY + upperZi;
for (int z = upperZ + 1; z < 16; z++) {
// null the z values
for (int x = 0; x < 16; x++, index++) {
arr[index] = 0;
}
}
}
if (trimX) {
index = indexY + lowerZi;
for (int z = lowerZ; z <= upperZ; z++, index += 16) {
for (int x = 0; x < lowerX; x++) {
// null the x values
arr[index + x] = 0;
}
for (int x = upperX + 1; x < 16; x++) {
// null the x values
arr[index + x] = 0;
}
}
if (!set.hasSection(layer)) {
continue;
}
char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true
if (!(trimX || trimZ)) {
continue;
}
int indexY = 0;
for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section
int index;
if (trimZ) {
index = indexY;
for (int z = 0; z < lowerZ; z++) {
// null the z values
for (int x = 0; x < 16; x++, index++) {
arr[index] = 0;
}
}
index = indexY + upperZi;
for (int z = upperZ + 1; z < 16; z++) {
// null the z values
for (int x = 0; x < 16; x++, index++) {
arr[index] = 0;
}
}
}
if (trimX) {
index = indexY + lowerZi; // Skip blocks already removed by trimZ
for (int z = lowerZ; z <= upperZ; z++, index += 16) {
for (int x = 0; x < lowerX; x++) {
// null the x values
arr[index + x] = 0;
}
for (int x = upperX + 1; x < 16; x++) {
// null the x values
arr[index + x] = 0;
}
}
set.setBlocks(layer, arr);
}
}
set.setBlocks(layer, arr);
}
trimNBT(set, this::contains);
return set;
}
return null;
}
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean asBlacklist) {
if (!asBlacklist) {
return processSet(chunk, get, set);
}
int bx = chunk.getX() << 4;
int bz = chunk.getZ() << 4;
int tx = bx + 15;
int tz = bz + 15;
if (bx >= minX && tx <= maxX && bz >= minZ && tz <= maxZ) {
// contains all X/Z
int sMaxY = (set.getMaxSectionPosition() << 4) + 15;
int sMinY = set.getMinSectionPosition() << 4;
if (minY <= sMinY && maxY >= sMaxY) {
return null;
}
trimY(set, minY, maxY, false);
trimNBT(set, this::contains);
return set;
}
if (tx >= minX && bx <= maxX && tz >= minZ && bz <= maxZ) {
if (minY > set.getMinSectionPosition() << 4 || maxY < (set.getMaxSectionPosition() << 4) + 15) {
trimY(set, minY, maxY, false);
}
final int lowerX = Math.max(0, minX - bx);
final int upperX = Math.min(15, 15 + maxX - tx);
final int lowerZ = Math.max(0, minZ - bz);
final int upperZ = Math.min(15, 15 + maxZ - tz);
final int lowerZi = (lowerZ << 4);
boolean trimX = lowerX != 0 || upperX != 15;
boolean trimZ = lowerZ != 0 || upperZ != 15;
for (int layer = get.getMinSectionPosition(); layer < get.getMaxSectionPosition(); layer++) {
if (!set.hasSection(layer)) {
continue;
}
char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true
if (!(trimX || trimZ)) {
continue;
}
int indexY = 0;
for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section
int index;
if (trimZ) {
index = indexY;
for (int z = lowerZ; z <= upperZ; z++) {
// null the z values
for (int x = 0; x < 16; x++, index++) {
arr[index] = 0;
}
}
}
if (trimX) {
index = indexY + lowerZi; // Skip blocks already removed by trimZ
for (int z = lowerZ; z <= upperZ; z++, index += 16) {
for (int x = lowerX; x <= upperX; x++) {
// null the x values
arr[index + x] = 0;
}
}
}
}
set.setBlocks(layer, arr);
}
trimNBT(set, bv3 -> !this.contains(bv3));
return set;
}
return set;
}
//FAWE end
}

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.regions;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.extent.SingleRegionExtent;
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
@ -366,6 +367,7 @@ public interface Region extends Iterable<BlockVector3>, Cloneable, IBatchProcess
return tx >= min.getX() && bx <= max.getX() && tz >= min.getZ() && bz <= max.getZ();
}
@Override
default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
int bx = chunk.getX() << 4;
int bz = chunk.getZ() << 4;
@ -377,12 +379,10 @@ public interface Region extends Iterable<BlockVector3>, Cloneable, IBatchProcess
if (tx >= min.getX() && bx <= max.getX() && tz >= min.getZ() && bz <= max.getZ()) {
// contains some
boolean processExtra = false;
for (int layer = 0; layer < 16; layer++) {
for (int layer = getMinimumY() >> 4; layer <= getMaximumY() >> 4; layer++) {
int by = layer << 4;
int ty = by + 15;
if (containsEntireCuboid(bx, tx, by, ty, bz, tz)) {
continue;
} else {
if (!containsEntireCuboid(bx, tx, by, ty, bz, tz)) {
processExtra = true;
char[] arr = set.load(layer);
for (int y = 0, index = 0; y < 16; y++) {
@ -406,6 +406,58 @@ public interface Region extends Iterable<BlockVector3>, Cloneable, IBatchProcess
}
}
/**
* Process the chunk, with the option to process as if the region is a blacklisted region, and thus any contained blocks
* should be removed, rather than uncontained blocks being removed.
*
* @param asBlacklist If any blocks contained by the region should be removed
*/
default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean asBlacklist) {
if (!asBlacklist) {
return processSet(chunk, get, set);
}
int bx = chunk.getX() << 4;
int bz = chunk.getZ() << 4;
int tx = bx + 15;
int tz = bz + 15;
BlockVector3 min = getMinimumPoint();
BlockVector3 max = getMaximumPoint();
if (tx >= min.getX() && bx <= max.getX() && tz >= min.getZ() && bz <= max.getZ()) {
// contains some
boolean processExtra = false;
for (int layer = getMinimumY() >> 4; layer <= getMaximumY() >> 4; layer++) {
int by = layer << 4;
int ty = by + 15;
if (containsEntireCuboid(bx, tx, by, ty, bz, tz)) {
set.setBlocks(layer, FaweCache.IMP.EMPTY_CHAR_4096);
processExtra = true;
continue;
}
char[] arr = set.load(layer);
for (int y = 0, index = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++, index++) {
if (arr[index] != 0 && contains(x, y, z)) {
arr[index] = 0;
processExtra = true;
}
}
}
}
if (processExtra) {
set.setBlocks(layer, arr);
}
}
if (processExtra) {
trimNBT(set, bv3 -> !this.contains(bv3));
}
return set;
} else {
return null;
}
}
@Override
default Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
// Doesn't need to do anything

View File

@ -30,12 +30,11 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.World;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
@ -58,7 +57,7 @@ public class RegionIntersection extends AbstractRegion {
*
* @param regions a list of regions, which is copied
*/
public RegionIntersection(List<Region> regions) {
public RegionIntersection(Collection<Region> regions) {
this(null, regions);
}
@ -77,7 +76,7 @@ public class RegionIntersection extends AbstractRegion {
* @param world the world
* @param regions a list of regions, which is copied
*/
public RegionIntersection(World world, List<Region> regions) {
public RegionIntersection(World world, Collection<Region> regions) {
super(world);
checkNotNull(regions);
checkArgument(!regions.isEmpty(), "empty region list is not supported");
@ -174,9 +173,22 @@ public class RegionIntersection extends AbstractRegion {
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
// Doesn't need to do anything
return CompletableFuture.completedFuture(set);
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean asBlacklist) {
if (!asBlacklist) {
return processSet(chunk, get, set);
}
int bx = chunk.getX() << 4;
int bz = chunk.getZ() << 4;
int tx = bx + 15;
int tz = bz + 15;
for (Region region : regions) {
BlockVector3 regMin = region.getMinimumPoint();
BlockVector3 regMax = region.getMaximumPoint();
if (tx >= regMin.getX() && bx <= regMax.getX() && tz >= regMin.getZ() && bz <= regMax.getZ()) {
return region.processSet(chunk, get, set, true);
}
}
return set; // default return set as no "blacklist" regions contained the chunk
}
public List<Region> getRegions() {