Implement region blacklisting

This commit is contained in:
dordsor21 2021-09-23 01:19:24 +01:00
parent f0880a27a0
commit 7d894228d0
No known key found for this signature in database
GPG Key ID: 1E53E88969FFCF0B
25 changed files with 657 additions and 491 deletions

View File

@ -1,42 +0,0 @@
package com.fastasyncworldedit.bukkit.filter;
import com.fastasyncworldedit.core.regions.filter.CuboidRegionFilter;
import com.fastasyncworldedit.core.util.TaskManager;
import com.flowpowered.math.vector.Vector3i;
import com.griefdefender.api.GriefDefender;
import com.griefdefender.api.claim.Claim;
import com.sk89q.worldedit.math.BlockVector2;
import org.bukkit.World;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull;
public class GriefDefenderFilter extends CuboidRegionFilter {
private final Collection<Claim> claims;
private final World world;
public GriefDefenderFilter(World world) {
checkNotNull(world);
this.claims = TaskManager.IMP.sync(
(Supplier<Collection<Claim>>) () -> new ArrayDeque<>(GriefDefender.getCore().getAllClaims()));
this.world = world;
}
@Override
public void calculateRegions() {
for (Claim claim : claims) {
Vector3i bot = claim.getGreaterBoundaryCorner();
if (world.getUID().equals(claim.getWorldUniqueId())) {
Vector3i top = claim.getGreaterBoundaryCorner();
BlockVector2 pos1 = BlockVector2.at(bot.getX(), bot.getZ());
BlockVector2 pos2 = BlockVector2.at(top.getX(), top.getZ());
add(pos1, pos2);
}
}
}
}

View File

@ -1,41 +0,0 @@
package com.fastasyncworldedit.bukkit.filter;
import com.fastasyncworldedit.core.regions.filter.CuboidRegionFilter;
import com.fastasyncworldedit.core.util.TaskManager;
import com.sk89q.worldedit.math.BlockVector2;
import me.ryanhamshire.GriefPrevention.Claim;
import me.ryanhamshire.GriefPrevention.GriefPrevention;
import org.bukkit.World;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull;
public class GriefPreventionFilter extends CuboidRegionFilter {
private final Collection<Claim> claims;
private final World world;
public GriefPreventionFilter(World world) {
checkNotNull(world);
this.claims = TaskManager.IMP.sync(
(Supplier<Collection<Claim>>) () -> new ArrayDeque<>(GriefPrevention.instance.dataStore.getClaims()));
this.world = world;
}
@Override
public void calculateRegions() {
for (Claim claim : claims) {
org.bukkit.Location bot = claim.getGreaterBoundaryCorner();
if (world.equals(bot.getWorld())) {
org.bukkit.Location top = claim.getGreaterBoundaryCorner();
BlockVector2 pos1 = BlockVector2.at(bot.getBlockX(), bot.getBlockZ());
BlockVector2 pos2 = BlockVector2.at(top.getBlockX(), top.getBlockZ());
add(pos1, pos2);
}
}
}
}

View File

@ -1,73 +0,0 @@
package com.fastasyncworldedit.bukkit.filter;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.regions.filter.CuboidRegionFilter;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import org.apache.logging.log4j.Logger;
import org.bukkit.World;
import static com.google.common.base.Preconditions.checkNotNull;
public class WorldGuardFilter extends CuboidRegionFilter {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private final World world;
private boolean large;
private RegionManager manager;
public WorldGuardFilter(World world) {
checkNotNull(world);
this.world = world;
}
@Override
public void calculateRegions() {
Fawe.get().getQueueHandler().sync(() -> {
WorldGuardFilter.this.manager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(
BukkitAdapter.adapt(world));
for (ProtectedRegion region : manager.getRegions().values()) {
BlockVector3 min = region.getMinimumPoint();
BlockVector3 max = region.getMaximumPoint();
if (max.getBlockX() - min.getBlockX() > 1024 || max.getBlockZ() - min.getBlockZ() > 1024) {
LOGGER.info("Large or complex region shapes cannot be optimized. Filtering will be slower");
large = true;
break;
}
add(min.toBlockVector2(), max.toBlockVector2());
}
});
}
@Override
public boolean containsChunk(int chunkX, int chunkZ) {
if (!large) {
return super.containsChunk(chunkX, chunkZ);
}
BlockVector3 pos1 = BlockVector3.at(chunkX << 4, 0, chunkZ << 4);
BlockVector3 pos2 = BlockVector3.at(pos1.getBlockX() + 15, 255, pos1.getBlockZ() + 15);
ProtectedCuboidRegion chunkRegion = new ProtectedCuboidRegion("unimportant", pos1, pos2);
ApplicableRegionSet set = manager.getApplicableRegions(chunkRegion);
return set.size() > 0 && !set.getRegions().iterator().next().getId().equals("__global__");
}
@Override
public boolean containsRegion(int mcaX, int mcaZ) {
if (!large) {
return super.containsRegion(mcaX, mcaZ);
}
BlockVector3 pos1 = BlockVector3.at(mcaX << 9, 0, mcaZ << 9);
BlockVector3 pos2 = BlockVector3.at(pos1.getBlockX() + 511, 255, pos1.getBlockZ() + 511);
ProtectedCuboidRegion regionRegion = new ProtectedCuboidRegion("unimportant", pos1, pos2);
ApplicableRegionSet set = manager.getApplicableRegions(regionRegion);
return set.size() > 0 && !set.getRegions().iterator().next().getId().equals("__global__");
}
}

View File

@ -1,8 +1,6 @@
package com.fastasyncworldedit.bukkit.regions; package com.fastasyncworldedit.bukkit.regions;
import com.fastasyncworldedit.bukkit.filter.GriefDefenderFilter;
import com.fastasyncworldedit.core.regions.FaweMask; import com.fastasyncworldedit.core.regions.FaweMask;
import com.fastasyncworldedit.core.regions.filter.RegionFilter;
import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector3i;
import com.griefdefender.api.GriefDefender; import com.griefdefender.api.GriefDefender;
import com.griefdefender.api.claim.Claim; import com.griefdefender.api.claim.Claim;
@ -12,7 +10,6 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CuboidRegion;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -65,9 +62,4 @@ public class GriefDefenderFeature extends BukkitMaskManager implements Listener
return null; return null;
} }
@Override
public RegionFilter getFilter(String world) {
return new GriefDefenderFilter(Bukkit.getWorld(world));
}
} }

View File

@ -1,8 +1,6 @@
package com.fastasyncworldedit.bukkit.regions; package com.fastasyncworldedit.bukkit.regions;
import com.fastasyncworldedit.bukkit.filter.GriefPreventionFilter;
import com.fastasyncworldedit.core.regions.FaweMask; import com.fastasyncworldedit.core.regions.FaweMask;
import com.fastasyncworldedit.core.regions.filter.RegionFilter;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
@ -10,7 +8,6 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import me.ryanhamshire.GriefPrevention.Claim; import me.ryanhamshire.GriefPrevention.Claim;
import me.ryanhamshire.GriefPrevention.GriefPrevention; import me.ryanhamshire.GriefPrevention.GriefPrevention;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -61,9 +58,4 @@ public class GriefPreventionFeature extends BukkitMaskManager implements Listene
return null; return null;
} }
@Override
public RegionFilter getFilter(String world) {
return new GriefPreventionFilter(Bukkit.getWorld(world));
}
} }

View File

@ -1,9 +1,8 @@
package com.fastasyncworldedit.bukkit.regions; package com.fastasyncworldedit.bukkit.regions;
import com.fastasyncworldedit.bukkit.filter.WorldGuardFilter;
import com.fastasyncworldedit.core.regions.FaweMask; import com.fastasyncworldedit.core.regions.FaweMask;
import com.fastasyncworldedit.core.regions.RegionWrapper; import com.fastasyncworldedit.core.regions.RegionWrapper;
import com.fastasyncworldedit.core.regions.filter.RegionFilter; import com.google.common.collect.ImmutableSet;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
@ -11,6 +10,7 @@ import com.sk89q.worldedit.regions.AbstractRegion;
import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Polygonal2DRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionIntersection;
import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
@ -29,12 +29,38 @@ import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import java.util.Set;
public class WorldGuardFeature extends BukkitMaskManager implements Listener { public class WorldGuardFeature extends BukkitMaskManager implements Listener {
private final WorldGuardPlugin worldguard;
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private final WorldGuardPlugin worldguard;
public WorldGuardFeature(Plugin plugin) {
super(plugin.getName());
this.worldguard = this.getWorldGuard();
LOGGER.info("Plugin 'WorldGuard' found. Using it now.");
}
private static Region adapt(ProtectedRegion region) {
if (region instanceof ProtectedCuboidRegion) {
return new CuboidRegion(region.getMinimumPoint(), region.getMaximumPoint());
}
if (region instanceof GlobalProtectedRegion) {
return RegionWrapper.GLOBAL();
}
if (region instanceof ProtectedPolygonalRegion) {
ProtectedPolygonalRegion casted = (ProtectedPolygonalRegion) region;
BlockVector3 max = region.getMaximumPoint();
BlockVector3 min = region.getMinimumPoint();
return new Polygonal2DRegion(null, casted.getPoints(), min.getBlockY(), max.getBlockY());
}
return new AdaptedRegion(region);
}
private WorldGuardPlugin getWorldGuard() { private WorldGuardPlugin getWorldGuard() {
final Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldGuard"); final Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldGuard");
@ -47,27 +73,21 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener {
return (WorldGuardPlugin) plugin; return (WorldGuardPlugin) plugin;
} }
public WorldGuardFeature(Plugin plugin) { public Set<ProtectedRegion> getRegions(LocalPlayer player, Location location, boolean isWhitelist) {
super(plugin.getName());
this.worldguard = this.getWorldGuard();
LOGGER.info("Plugin 'WorldGuard' found. Using it now.");
}
public ProtectedRegion getRegion(LocalPlayer player, Location location) {
RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer(); RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
if (container == null) { if (container == null) {
LOGGER.info("Region capability is not enabled for WorldGuard."); LOGGER.info("Region capability is not enabled for WorldGuard.");
return null; return Collections.emptySet();
} }
RegionManager manager = container.get(BukkitAdapter.adapt(location.getWorld())); RegionManager manager = container.get(BukkitAdapter.adapt(location.getWorld()));
if (manager == null) { if (manager == null) {
LOGGER.info("Region capability is not enabled for that world."); LOGGER.info("Region capability is not enabled for that world.");
return null; return Collections.emptySet();
} }
final ProtectedRegion global = manager.getRegion("__global__"); final ProtectedRegion global = manager.getRegion("__global__");
if (global != null && isAllowed(player, global)) { // If they're allowed and it's a whitelist return, else if they're not allowed and it's a blacklist, return
return global; if (global != null && isAllowed(player, global) == isWhitelist) {
return Collections.singleton(global);
} }
final ApplicableRegionSet regions = manager.getApplicableRegions(BlockVector3.at( final ApplicableRegionSet regions = manager.getApplicableRegions(BlockVector3.at(
location.getX(), location.getX(),
@ -75,20 +95,39 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener {
location.getZ() location.getZ()
)); ));
//Merge WorldGuardFlag //Merge WorldGuardFlag
if (isWhitelist) {
if (player.hasPermission("fawe.worldguardflag") && !regions.testState( if (player.hasPermission("fawe.worldguardflag") && !regions.testState(
player, player,
Flags.BUILD, Flags.BUILD,
Flags.BLOCK_PLACE, Flags.BLOCK_PLACE,
Flags.BLOCK_BREAK Flags.BLOCK_BREAK
)) { )) {
return null; return Collections.emptySet();
} }
Set<ProtectedRegion> protectedRegions = new HashSet<>();
for (ProtectedRegion region : regions) { for (ProtectedRegion region : regions) {
if (isAllowed(player, region)) { if (isAllowed(player, region)) {
return region; protectedRegions.add(region);
} }
} }
return null; return Collections.unmodifiableSet(protectedRegions);
} else {
if (player.hasPermission("fawe.worldguardflag") && !regions.testState(
player,
Flags.BUILD,
Flags.BLOCK_PLACE,
Flags.BLOCK_BREAK
)) {
return ImmutableSet.copyOf(regions.getRegions());
}
Set<ProtectedRegion> protectedRegions = new HashSet<>();
for (ProtectedRegion region : regions) {
if (!isAllowed(player, region)) {
protectedRegions.add(region);
}
}
return Collections.unmodifiableSet(protectedRegions);
}
} }
public boolean isAllowed(LocalPlayer localplayer, ProtectedRegion region) { public boolean isAllowed(LocalPlayer localplayer, ProtectedRegion region) {
@ -115,45 +154,45 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener {
} }
@Override @Override
public FaweMask getMask(com.sk89q.worldedit.entity.Player wePlayer, MaskType type) { public FaweMask getMask(com.sk89q.worldedit.entity.Player wePlayer, MaskType type, boolean isWhitelist) {
final Player player = BukkitAdapter.adapt(wePlayer); final Player player = BukkitAdapter.adapt(wePlayer);
final LocalPlayer localplayer = this.worldguard.wrapPlayer(player); final LocalPlayer localplayer = this.worldguard.wrapPlayer(player);
final Location location = player.getLocation(); final Location location = player.getLocation();
final ProtectedRegion myregion = this.getRegion(localplayer, location); final Set<ProtectedRegion> regions = this.getRegions(localplayer, location, isWhitelist);
if (myregion != null) { if (!regions.isEmpty()) {
final BlockVector3 pos1; Set<Region> result = new HashSet<>();
final BlockVector3 pos2; for (ProtectedRegion myregion : regions) {
if (myregion.getId().equals("__global__")) { if (myregion.getId().equals("__global__")) {
pos1 = BlockVector3.at(Integer.MIN_VALUE, wePlayer.getWorld().getMinY(), Integer.MIN_VALUE); return new FaweMask(RegionWrapper.GLOBAL()) {
pos2 = BlockVector3.at(Integer.MAX_VALUE, wePlayer.getWorld().getMaxY(), Integer.MAX_VALUE);
} else {
if (myregion instanceof ProtectedCuboidRegion) {
pos1 = myregion.getMinimumPoint();
pos2 = myregion.getMaximumPoint();
} else {
return new FaweMask(adapt(myregion)) {
@Override @Override
public boolean isValid(com.sk89q.worldedit.entity.Player player, MaskType type) { public boolean isValid(com.sk89q.worldedit.entity.Player player, MaskType type) {
return isAllowed(worldguard.wrapPlayer(BukkitAdapter.adapt(player)), myregion); return isAllowed(worldguard.wrapPlayer(BukkitAdapter.adapt(player)), myregion);
} }
}; };
} else {
if (myregion instanceof ProtectedCuboidRegion) {
result.add(new CuboidRegion(myregion.getMaximumPoint(), myregion.getMaximumPoint()));
} else {
result.add(adapt(myregion));
} }
} }
return new FaweMask(new CuboidRegion(pos1, pos2)) { }
return new FaweMask(new RegionIntersection(wePlayer.getWorld(), result)) {
@Override @Override
public boolean isValid(com.sk89q.worldedit.entity.Player player, MaskType type) { public boolean isValid(com.sk89q.worldedit.entity.Player player, MaskType type) {
return isAllowed(worldguard.wrapPlayer(BukkitAdapter.adapt(player)), myregion); final LocalPlayer localplayer = worldguard.wrapPlayer(BukkitAdapter.adapt(player));
for (ProtectedRegion myregion : regions) {
if (!isAllowed(localplayer, myregion)) {
return false;
}
}
return true;
} }
}; };
} }
return null; return null;
} }
@Override
public RegionFilter getFilter(String world) {
return new WorldGuardFilter(Bukkit.getWorld(world));
}
private static class AdaptedRegion extends AbstractRegion { private static class AdaptedRegion extends AbstractRegion {
private final ProtectedRegion region; private final ProtectedRegion region;
@ -190,20 +229,4 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener {
} }
private static Region adapt(ProtectedRegion region) {
if (region instanceof ProtectedCuboidRegion) {
return new CuboidRegion(region.getMinimumPoint(), region.getMaximumPoint());
}
if (region instanceof GlobalProtectedRegion) {
return RegionWrapper.GLOBAL();
}
if (region instanceof ProtectedPolygonalRegion) {
ProtectedPolygonalRegion casted = (ProtectedPolygonalRegion) region;
BlockVector3 max = region.getMaximumPoint();
BlockVector3 min = region.getMinimumPoint();
return new Polygonal2DRegion(null, casted.getPoints(), min.getBlockY(), max.getBlockY());
}
return new AdaptedRegion(region);
}
} }

View File

@ -1,32 +0,0 @@
package com.fastasyncworldedit.bukkit.regions.plotsquared;
import com.fastasyncworldedit.core.regions.filter.CuboidRegionFilter;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.sk89q.worldedit.math.BlockVector2;
import java.util.ArrayList;
import static com.google.common.base.Preconditions.checkNotNull;
public class PlotRegionFilter extends CuboidRegionFilter {
private final PlotArea area;
public PlotRegionFilter(PlotArea area) {
checkNotNull(area);
this.area = area;
}
@Override
public void calculateRegions() {
ArrayList<Plot> plots = new ArrayList<>(area.getPlots());
for (Plot plot : plots) {
Location bottom = plot.getCorners()[0];
Location top = plot.getCorners()[1];
add(BlockVector2.at(bottom.getX(), bottom.getZ()), BlockVector2.at(top.getX(), top.getZ()));
}
}
}

View File

@ -184,13 +184,4 @@ public class PlotSquaredFeature extends FaweMaskManager {
}; };
} }
@Override
public RegionFilter getFilter(String world) {
PlotArea area = PlotSquared.get().getPlotAreaManager().getPlotArea(world, null);
if (area != null) {
return new PlotRegionFilter(area);
}
return null;
}
} }

View File

@ -1,32 +0,0 @@
package com.fastasyncworldedit.bukkit.regions.plotsquaredv4;
import com.fastasyncworldedit.core.regions.filter.CuboidRegionFilter;
import com.github.intellectualsites.plotsquared.plot.object.Location;
import com.github.intellectualsites.plotsquared.plot.object.Plot;
import com.github.intellectualsites.plotsquared.plot.object.PlotArea;
import com.sk89q.worldedit.math.BlockVector2;
import java.util.ArrayList;
import static com.google.common.base.Preconditions.checkNotNull;
public class PlotRegionFilter extends CuboidRegionFilter {
private final PlotArea area;
public PlotRegionFilter(PlotArea area) {
checkNotNull(area);
this.area = area;
}
@Override
public void calculateRegions() {
ArrayList<Plot> plots = new ArrayList<>(area.getPlots());
for (Plot plot : plots) {
Location bottom = plot.getCorners()[0];
Location top = plot.getCorners()[1];
add(BlockVector2.at(bottom.getX(), bottom.getZ()), BlockVector2.at(top.getX(), top.getZ()));
}
}
}

View File

@ -224,13 +224,4 @@ public class PlotSquaredFeature extends FaweMaskManager {
}; };
} }
@Override
public RegionFilter getFilter(String world) {
PlotArea area = PlotSquared.get().getPlotArea(world, null);
if (area != null) {
return new PlotRegionFilter(area);
}
return null;
}
} }

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) { public static Region[] getRegions(Player player) {
return WEManager.IMP.getMask(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. * Cancel the edit with the following extent.
* *

View File

@ -125,6 +125,12 @@ public class Settings extends Config {
" - OWNER = Players who own the region" " - OWNER = Players who own the region"
}) })
public String MODE = "MEMBER"; 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({ @Comment({
"List of plugin mask managers that should be exclusive. Exclusive managers are not ", "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 ", "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" " - Some custom-implementations in other plugins may override this setting"
}) })
public List<String> EXCLUSIVE_MANAGERS = new ArrayList<>(Collections.singleton(("ExamplePlugin"))); 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) { public Region[] regions(Player player, FaweMaskManager.MaskType type) {
Region[] regions = player.getCurrentRegions(type); Region[] regions = player.getAllowedRegions(type);
if (regions == null) { if (regions == null) {
throw new IllegalArgumentException(Caption.toString(Caption.of("fawe.error.no.region"))); throw new IllegalArgumentException(Caption.toString(Caption.of("fawe.error.no.region")));
} }

View File

@ -50,7 +50,7 @@ public class HeightBoundExtent extends FaweRegionExtent {
@Override @Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { 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 set;
} }
return null; 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.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.regions.RegionWrapper;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionIntersection; import com.sk89q.worldedit.regions.RegionIntersection;
import javax.annotation.Nullable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.concurrent.Future; import java.util.concurrent.Future;
public class MultiRegionExtent extends FaweRegionExtent { public class MultiRegionExtent extends FaweRegionExtent {
@Nullable
private final RegionIntersection intersection; 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 Region region;
private int index; 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); super(extent, limit);
this.index = 0; this.index = 0;
this.region = regions[0]; if (allowed != null && !allowed[0].isGlobal()) {
this.regions = regions; this.region = allowed[0];
this.intersection = new RegionIntersection(Arrays.asList(regions)); 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 @Override
public boolean contains(int x, int y, int z) { 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; return true;
} }
for (int i = 0; i < regions.length; i++) { boolean result = allowed == null;
if (!result) {
for (int i = 0; i < allowed.length; i++) {
if (i != index) { if (i != index) {
Region current = regions[i]; Region current = allowed[i];
if (current.contains(x, y, z)) { if (current.contains(x, y, z)) {
region = current; region = current;
index = i; index = i;
return true; result = true;
break;
} }
} }
} }
}
if (!result || disallowed == null) {
return result;
}
for (final Region disallow : disallowed) {
if (disallow.contains(x, y, z)) {
return false; return false;
} }
}
@Override
public boolean processGet(int chunkX, int chunkZ) {
for (Region region : regions) {
if (region.containsChunk(chunkX, chunkZ)) {
return true; return true;
} }
}
return false;
}
@Override @Override
public boolean contains(int x, int z) { 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; return true;
} }
for (int i = 0; i < regions.length; i++) { boolean result = allowed == null;
if (!result) {
for (int i = 0; i < allowed.length; i++) {
if (i != index) { if (i != index) {
Region current = regions[i]; Region current = allowed[i];
if (current.contains(x, z)) { if (current.contains(x, z)) {
region = current; region = current;
index = i; index = i;
result = true;
break;
}
}
}
}
if (!result || disallowed == null) {
return result;
}
for (final Region disallow : disallowed) {
if (disallow.contains(x, z)) {
return false;
}
}
return true; return true;
} }
/**
* Get all allowed regions
*/
@Override
public Collection<Region> getRegions() {
if (allowed == null) {
return List.of(RegionWrapper.GLOBAL());
} }
} return Arrays.asList(allowed);
return false;
} }
@Override @Override
public Collection<Region> getRegions() { public boolean processGet(int chunkX, int chunkZ) {
return Arrays.asList(regions); 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 @Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { 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 @Override

View File

@ -34,12 +34,15 @@ public interface IBatchProcessor {
Extent construct(Extent child); 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 * @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; int minLayer = (minY - 1) >> 4;
int maxLayer = (maxY + 1) >> 4;
if (keepInsideRange) {
for (int layer = set.getMinSectionPosition(); layer <= minLayer; layer++) { for (int layer = set.getMinSectionPosition(); layer <= minLayer; layer++) {
if (set.hasSection(layer)) { if (set.hasSection(layer)) {
if (layer == minLayer) { if (layer == minLayer) {
@ -58,7 +61,6 @@ public interface IBatchProcessor {
} }
} }
} }
int maxLayer = (maxY + 1) >> 4;
for (int layer = maxLayer; layer < set.getMaxSectionPosition(); layer++) { for (int layer = maxLayer; layer < set.getMaxSectionPosition(); layer++) {
if (set.hasSection(layer)) { if (set.hasSection(layer)) {
if (layer == minLayer) { if (layer == minLayer) {
@ -86,10 +88,46 @@ public interface IBatchProcessor {
layer++; layer++;
} }
} catch (ArrayIndexOutOfBoundsException exception) { } catch (ArrayIndexOutOfBoundsException exception) {
WorldEdit.logger.error("minY = {} , layer = {}", minY, ((minY - 15) >> 4), 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;
}
/** /**
* Utility method to trim entity and blocks with a provided contains function. * Utility method to trim entity and blocks with a provided contains function.

View File

@ -23,22 +23,39 @@ public abstract class FaweMaskManager {
return this.key; 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) { public FaweMask getMask(final Player player, MaskType type) {
return null; 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) { public boolean isValid(FaweMask mask) {
return true; return true;
} }
/**
* @deprecated Not used internally
*/
@Deprecated(forRemoval = true)
public RegionFilter getFilter(String world) { public RegionFilter getFilter(String world) {
return null; return null;
} }
private boolean hasMemberPermission(Player player) {
return player.hasPermission("fawe." + getKey() + ".member");
}
public boolean isExclusive() { public boolean isExclusive() {
return Settings.IMP.REGION_RESTRICTIONS_OPTIONS.EXCLUSIVE_MANAGERS.contains(this.key); return Settings.IMP.REGION_RESTRICTIONS_OPTIONS.EXCLUSIVE_MANAGERS.contains(this.key);
} }

View File

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

View File

@ -61,19 +61,26 @@ public class WEManager {
cancelEditSafe(parent, reason); 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) { public boolean isIn(int x, int y, int z, Region region) {
return region.contains(x, y, z); 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")) { if (!Settings.IMP.REGION_RESTRICTIONS || player.hasPermission("fawe.bypass") || player.hasPermission("fawe.bypass.regions")) {
return new Region[]{RegionWrapper.GLOBAL()}; return new Region[]{RegionWrapper.GLOBAL()};
} }
@ -89,7 +96,7 @@ public class WEManager {
Set<Region> regions = new HashSet<>(); Set<Region> regions = new HashSet<>();
if (masks == null) { if (masks == null || !isWhitelist) {
masks = new HashSet<>(); masks = new HashSet<>();
} else { } else {
synchronized (masks) { synchronized (masks) {
@ -125,7 +132,7 @@ public class WEManager {
if (manager.isExclusive() && !masks.isEmpty()) { if (manager.isExclusive() && !masks.isEmpty()) {
continue; continue;
} }
final FaweMask mask = manager.getMask(player, FaweMaskManager.MaskType.getDefaultMaskType()); final FaweMask mask = manager.getMask(player, FaweMaskManager.MaskType.getDefaultMaskType(), isWhitelist);
if (mask != null) { if (mask != null) {
regions.add(mask.getRegion()); regions.add(mask.getRegion());
masks.add(mask); masks.add(mask);
@ -140,9 +147,10 @@ public class WEManager {
player.printError(TextComponent.of("Missing permission " + "fawe." + manager.getKey())); player.printError(TextComponent.of("Missing permission " + "fawe." + manager.getKey()));
} }
} }
if (!masks.isEmpty()) { regions.addAll(backupRegions);
if (!masks.isEmpty() && isWhitelist) {
player.setMeta("lastMask", masks); player.setMeta("lastMask", masks);
} else { } else if (isWhitelist) {
player.deleteMeta("lastMask"); player.deleteMeta("lastMask");
} }
return regions.toArray(new Region[0]); return regions.toArray(new Region[0]);

View File

@ -86,6 +86,7 @@ public final class EditSessionBuilder {
private FaweLimit limit; private FaweLimit limit;
private AbstractChangeSet changeSet; private AbstractChangeSet changeSet;
private Region[] allowedRegions; private Region[] allowedRegions;
private Region[] disallowedRegions;
private Boolean fastMode; private Boolean fastMode;
private Boolean checkMemory; private Boolean checkMemory;
private Boolean combineStages; private Boolean combineStages;
@ -341,6 +342,34 @@ public final class EditSessionBuilder {
return setDirty(); 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 * 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 != null && !actor.hasPermission("fawe.bypass") && !actor.hasPermission("fawe.bypass.regions")) {
if (actor instanceof Player) { if (actor instanceof Player) {
Player player = (Player) actor; 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; 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) { if (allowedRegions.length == 0) {
regionExtent = new NullExtent(this.extent, FaweCache.NO_REGION); regionExtent = new NullExtent(this.extent, FaweCache.NO_REGION);
} else { } else {
if (allowedRegions.length == 1) { if (allowedRegions.length == 1) {
regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]); regionExtent = new SingleRegionExtent(this.extent, this.limit, allowedRegions[0]);
} else { } 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 // 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) { 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); <B extends BlockStateHolder<B>> void sendFakeBlock(BlockVector3 pos, @Nullable B block);
//FAWE start //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(); 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 * Get the player's selection region. If the selection is defined in
* a different world, the {@code IncompleteRegionException} * a different world, the {@code IncompleteRegionException}
@ -366,8 +400,6 @@ public interface Player extends Entity, Actor {
return getSession().getSelection(getWorld()); return getSession().getSelection(getWorld());
} }
void setSelection(Region region);
/** /**
* Set the player's WorldEdit selection. * Set the player's WorldEdit selection.
* *

View File

@ -483,27 +483,31 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
} }
//FAWE start //FAWE start
@Override
/** public Region[] getAllowedRegions() {
* Get the player's current allowed WorldEdit regions. return getAllowedRegions(FaweMaskManager.MaskType.getDefaultMaskType());
*
* @return an array of allowed regions
*/
public Region[] getCurrentRegions() {
return getCurrentRegions(FaweMaskManager.MaskType.MEMBER);
} }
public Region[] getCurrentRegions(FaweMaskManager.MaskType type) { @Override
return WEManager.IMP.getMask(this, type); public Region[] getAllowedRegions(FaweMaskManager.MaskType type) {
return WEManager.IMP.getMask(this, type, true);
} }
/** @Override
* Get the largest region in the player's allowed WorldEdit region. 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() { public Region getLargestRegion() {
long area = 0; long area = 0;
Region max = null; Region max = null;
for (Region region : this.getCurrentRegions()) { for (Region region : this.getAllowedRegions()) {
final long tmp = region.getVolume(); final long tmp = region.getVolume();
if (tmp > area) { if (tmp > area) {
area = tmp; area = tmp;
@ -513,6 +517,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
return max; return max;
} }
@Override
public void setSelection(Region region) { public void setSelection(Region region) {
RegionSelector selector; RegionSelector selector;
if (region instanceof ConvexPolyhedralRegion) { 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) { if (minY <= set.getMinSectionPosition() << 4 && maxY >= (set.getMaxSectionPosition() << 4) + 15) {
return set; return set;
} }
trimY(set, minY, maxY); trimY(set, minY, maxY, true);
trimNBT(set, this::contains); trimNBT(set, this::contains);
return set; return set;
} }
if (tx >= minX && bx <= maxX && tz >= minZ && bz <= maxZ) { if (tx >= minX && bx <= maxX && tz >= minZ && bz <= maxZ) {
if (minY > set.getMinSectionPosition() << 4 || maxY < (set.getMaxSectionPosition() << 4) + 15) { 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 lowerX = Math.max(0, minX - bx);
final int upperX = Math.min(15, 15 + maxX - tx); final int upperX = Math.min(15, 15 + maxX - tx);
@ -769,9 +769,13 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
boolean trimZ = lowerZ != 0 || upperZ != 15; boolean trimZ = lowerZ != 0 || upperZ != 15;
for (int layer = get.getMinSectionPosition(); layer < get.getMaxSectionPosition(); layer++) { for (int layer = get.getMinSectionPosition(); layer < get.getMaxSectionPosition(); layer++) {
if (set.hasSection(layer)) { if (!set.hasSection(layer)) {
continue;
}
char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true
if (trimX || trimZ) { if (!(trimX || trimZ)) {
continue;
}
int indexY = 0; int indexY = 0;
for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section
int index; int index;
@ -792,7 +796,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
} }
} }
if (trimX) { if (trimX) {
index = indexY + lowerZi; index = indexY + lowerZi; // Skip blocks already removed by trimZ
for (int z = lowerZ; z <= upperZ; z++, index += 16) { for (int z = lowerZ; z <= upperZ; z++, index += 16) {
for (int x = 0; x < lowerX; x++) { for (int x = 0; x < lowerX; x++) {
// null the x values // null the x values
@ -807,13 +811,86 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
} }
set.setBlocks(layer, arr); set.setBlocks(layer, arr);
} }
}
}
trimNBT(set, this::contains); trimNBT(set, this::contains);
return set; return set;
} }
return null; 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 //FAWE end
} }

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.regions; package com.sk89q.worldedit.regions;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.extent.SingleRegionExtent; import com.fastasyncworldedit.core.extent.SingleRegionExtent;
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope; 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(); return tx >= min.getX() && bx <= max.getX() && tz >= min.getZ() && bz <= max.getZ();
} }
@Override
default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { default IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
int bx = chunk.getX() << 4; int bx = chunk.getX() << 4;
int bz = chunk.getZ() << 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()) { if (tx >= min.getX() && bx <= max.getX() && tz >= min.getZ() && bz <= max.getZ()) {
// contains some // contains some
boolean processExtra = false; boolean processExtra = false;
for (int layer = 0; layer < 16; layer++) { for (int layer = getMinimumY() >> 4; layer <= getMaximumY() >> 4; layer++) {
int by = layer << 4; int by = layer << 4;
int ty = by + 15; int ty = by + 15;
if (containsEntireCuboid(bx, tx, by, ty, bz, tz)) { if (!containsEntireCuboid(bx, tx, by, ty, bz, tz)) {
continue;
} else {
processExtra = true; processExtra = true;
char[] arr = set.load(layer); char[] arr = set.load(layer);
for (int y = 0, index = 0; y < 16; y++) { 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 @Override
default Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { default Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
// Doesn't need to do anything // 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 com.sk89q.worldedit.world.World;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; 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 * @param regions a list of regions, which is copied
*/ */
public RegionIntersection(List<Region> regions) { public RegionIntersection(Collection<Region> regions) {
this(null, regions); this(null, regions);
} }
@ -77,7 +76,7 @@ public class RegionIntersection extends AbstractRegion {
* @param world the world * @param world the world
* @param regions a list of regions, which is copied * @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); super(world);
checkNotNull(regions); checkNotNull(regions);
checkArgument(!regions.isEmpty(), "empty region list is not supported"); checkArgument(!regions.isEmpty(), "empty region list is not supported");
@ -174,9 +173,22 @@ public class RegionIntersection extends AbstractRegion {
} }
@Override @Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set, boolean asBlacklist) {
// Doesn't need to do anything if (!asBlacklist) {
return CompletableFuture.completedFuture(set); 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() { public List<Region> getRegions() {