mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-11-14 22:56:18 +01:00
Fix member inheritance for non-player associables (#1804)
* Add options to query region sets unsorted and without parents * Fix member inheritance for non-player associables * Add member inheritance for non-player associables * Rename Option to QueryOption, remove functional definitions, bit of cleanup. Co-authored-by: wizjany <wizjany@gmail.com>
This commit is contained in:
parent
c81f5892eb
commit
4644268214
@ -19,6 +19,7 @@
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.association.RegionAssociable;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
@ -29,7 +30,11 @@
|
||||
import com.sk89q.worldguard.protection.util.NormativeOrders;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@ -64,7 +69,7 @@ public RegionResultSet(List<ProtectedRegion> applicable, @Nullable ProtectedRegi
|
||||
*/
|
||||
public RegionResultSet(Set<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion) {
|
||||
this(NormativeOrders.fromSet(applicable), globalRegion, true);
|
||||
this.regionSet = applicable;
|
||||
this.regionSet = ImmutableSet.copyOf(applicable);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,7 +88,7 @@ public RegionResultSet(List<ProtectedRegion> applicable, @Nullable ProtectedRegi
|
||||
if (!sorted) {
|
||||
NormativeOrders.sort(applicable);
|
||||
}
|
||||
this.applicable = applicable;
|
||||
this.applicable = Collections.unmodifiableList(applicable);
|
||||
this.flagValueCalculator = new FlagValueCalculator(applicable, globalRegion);
|
||||
}
|
||||
|
||||
@ -157,7 +162,7 @@ public Set<ProtectedRegion> getRegions() {
|
||||
if (regionSet != null) {
|
||||
return regionSet;
|
||||
}
|
||||
regionSet = Collections.unmodifiableSet(new HashSet<>(applicable));
|
||||
regionSet = ImmutableSet.copyOf(applicable);
|
||||
return regionSet;
|
||||
}
|
||||
|
||||
|
@ -87,31 +87,35 @@ private boolean checkNonplayerProtectionDomains(Iterable<? extends ProtectedRegi
|
||||
public Association getAssociation(List<ProtectedRegion> regions) {
|
||||
checkNotNull(source);
|
||||
for (ProtectedRegion region : regions) {
|
||||
if ((region.getId().equals(ProtectedRegion.GLOBAL_REGION) && source.isEmpty())) {
|
||||
return Association.OWNER;
|
||||
}
|
||||
|
||||
if (source.contains(region)) {
|
||||
if (useMaxPriorityAssociation) {
|
||||
int priority = region.getPriority();
|
||||
if (priority == maxPriority) {
|
||||
return Association.OWNER;
|
||||
}
|
||||
} else {
|
||||
while (region != null) {
|
||||
if ((region.getId().equals(ProtectedRegion.GLOBAL_REGION) && source.isEmpty())) {
|
||||
return Association.OWNER;
|
||||
}
|
||||
}
|
||||
|
||||
Set<ProtectedRegion> source;
|
||||
if (source.contains(region)) {
|
||||
if (useMaxPriorityAssociation) {
|
||||
int priority = region.getPriority();
|
||||
if (priority == maxPriority) {
|
||||
return Association.OWNER;
|
||||
}
|
||||
} else {
|
||||
return Association.OWNER;
|
||||
}
|
||||
}
|
||||
|
||||
if (useMaxPriorityAssociation) {
|
||||
source = maxPriorityRegions;
|
||||
} else {
|
||||
source = this.source;
|
||||
}
|
||||
Set<ProtectedRegion> source;
|
||||
|
||||
if (checkNonplayerProtectionDomains(source, region.getFlag(Flags.NONPLAYER_PROTECTION_DOMAINS))) {
|
||||
return Association.OWNER;
|
||||
if (useMaxPriorityAssociation) {
|
||||
source = maxPriorityRegions;
|
||||
} else {
|
||||
source = this.source;
|
||||
}
|
||||
|
||||
if (checkNonplayerProtectionDomains(source, region.getFlag(Flags.NONPLAYER_PROTECTION_DOMAINS))) {
|
||||
return Association.OWNER;
|
||||
}
|
||||
|
||||
region = region.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.RegionQuery;
|
||||
import com.sk89q.worldguard.protection.regions.RegionQuery.QueryOption;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -67,7 +68,7 @@ public DelayedRegionOverlapAssociation(RegionQuery query, Location location, boo
|
||||
@Override
|
||||
public Association getAssociation(List<ProtectedRegion> regions) {
|
||||
if (source == null) {
|
||||
ApplicableRegionSet result = query.getApplicableRegions(location);
|
||||
ApplicableRegionSet result = query.getApplicableRegions(location, QueryOption.NONE);
|
||||
source = result.getRegions();
|
||||
calcMaxPriority();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.util.RegionCollectionConsumer;
|
||||
import com.sk89q.worldguard.protection.regions.RegionQuery.QueryOption;
|
||||
import com.sk89q.worldguard.util.Normal;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -293,32 +293,60 @@ public Set<ProtectedRegion> removeRegion(String id, RemovalStrategy strategy) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for effective flags and owners for the given positive.
|
||||
* Query for effective flags and members for the given position.
|
||||
*
|
||||
* <p>{@link QueryOption#COMPUTE_PARENTS} is used.</p>
|
||||
*
|
||||
* @param position the position
|
||||
* @return the query object
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(BlockVector3 position) {
|
||||
checkNotNull(position);
|
||||
|
||||
Set<ProtectedRegion> regions = Sets.newHashSet();
|
||||
index.applyContaining(position, new RegionCollectionConsumer(regions, true));
|
||||
return new RegionResultSet(regions, index.get("__global__"));
|
||||
return getApplicableRegions(position, QueryOption.COMPUTE_PARENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for effective flags and owners for the area represented
|
||||
* Return a region set for the given position.
|
||||
*
|
||||
* @param position the position
|
||||
* @param option the option
|
||||
* @return a region set
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(BlockVector3 position, QueryOption option) {
|
||||
checkNotNull(position);
|
||||
checkNotNull(option);
|
||||
|
||||
Set<ProtectedRegion> regions = Sets.newHashSet();
|
||||
index.applyContaining(position, option.createIndexConsumer(regions));
|
||||
return new RegionResultSet(option.constructResult(regions), index.get("__global__"), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for effective flags and members for the area represented
|
||||
* by the given region.
|
||||
*
|
||||
* <p>{@link QueryOption#COMPUTE_PARENTS} is used.</p>
|
||||
*
|
||||
* @param region the region
|
||||
* @return the query object
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(ProtectedRegion region) {
|
||||
return getApplicableRegions(region, QueryOption.COMPUTE_PARENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a region set for the area represented by the given region.
|
||||
*
|
||||
* @param region the region
|
||||
* @param option the option
|
||||
* @return a region set
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(ProtectedRegion region, QueryOption option) {
|
||||
checkNotNull(region);
|
||||
checkNotNull(option);
|
||||
|
||||
Set<ProtectedRegion> regions = Sets.newHashSet();
|
||||
index.applyIntersecting(region, new RegionCollectionConsumer(regions, true));
|
||||
return new RegionResultSet(regions, index.get("__global__"));
|
||||
index.applyIntersecting(region, option.createIndexConsumer(regions));
|
||||
return new RegionResultSet(option.constructResult(regions), index.get("__global__"), true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,7 +24,9 @@
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.RegionResultSet;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.RegionQuery.QueryOption;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@ -38,7 +40,7 @@
|
||||
*/
|
||||
public class QueryCache {
|
||||
|
||||
private final ConcurrentMap<CacheKey, ApplicableRegionSet> cache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
private final ConcurrentMap<CacheKey, Map<QueryOption, ApplicableRegionSet>> cache = new ConcurrentHashMap<>(16, 0.75f, 2);
|
||||
|
||||
/**
|
||||
* Get from the cache a {@code ApplicableRegionSet} if an entry exists;
|
||||
@ -46,20 +48,16 @@ public class QueryCache {
|
||||
*
|
||||
* @param manager the region manager
|
||||
* @param location the location
|
||||
* @param option the option
|
||||
* @return a result
|
||||
*/
|
||||
public ApplicableRegionSet queryContains(RegionManager manager, Location location) {
|
||||
public ApplicableRegionSet queryContains(RegionManager manager, Location location, QueryOption option) {
|
||||
checkNotNull(manager);
|
||||
checkNotNull(location);
|
||||
checkNotNull(option);
|
||||
|
||||
CacheKey key = new CacheKey(location);
|
||||
ApplicableRegionSet result = cache.get(key);
|
||||
if (result == null) {
|
||||
result = manager.getApplicableRegions(location.toVector().toBlockPoint());
|
||||
cache.put(key, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
return cache.compute(key, (k, v) -> option.createCache(manager, location, v)).get(option);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
@ -39,8 +40,16 @@
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag.State;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.RegionIndex;
|
||||
import com.sk89q.worldguard.protection.util.NormativeOrders;
|
||||
import com.sk89q.worldguard.protection.util.RegionCollectionConsumer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ -74,17 +83,37 @@ public RegionQuery(QueryCache cache) {
|
||||
/**
|
||||
* Query for regions containing the given location.
|
||||
*
|
||||
* <p>An instance of {@link RegionResultSet} will always be returned,
|
||||
* <p>{@link QueryOption#COMPUTE_PARENTS} is used.</p>
|
||||
*
|
||||
* <p>An instance of {@link ApplicableRegionSet} will always be returned,
|
||||
* even if regions are disabled or region data failed to load. An
|
||||
* appropriate "virtual" set will be returned in such a case
|
||||
* (for example, if regions are disabled, the returned set
|
||||
* would permit all activities).</p>
|
||||
* appropriate "virtual" set will be returned in such a case (for example,
|
||||
* if regions are disabled, the returned set would permit all
|
||||
* activities).</p>
|
||||
*
|
||||
* @param location the location
|
||||
* @return a region set
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(Location location) {
|
||||
return getApplicableRegions(location, QueryOption.COMPUTE_PARENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for regions containing the given location.
|
||||
*
|
||||
* <p>An instance of {@link ApplicableRegionSet} will always be returned,
|
||||
* even if regions are disabled or region data failed to load. An
|
||||
* appropriate "virtual" set will be returned in such a case (for example,
|
||||
* if regions are disabled, the returned set would permit all
|
||||
* activities).</p>
|
||||
*
|
||||
* @param location the location
|
||||
* @param option the option
|
||||
* @return a region set
|
||||
*/
|
||||
public ApplicableRegionSet getApplicableRegions(Location location, QueryOption option) {
|
||||
checkNotNull(location);
|
||||
checkNotNull(option);
|
||||
|
||||
World world = (World) location.getExtent();
|
||||
WorldConfiguration worldConfig = config.get(world);
|
||||
@ -95,7 +124,7 @@ public ApplicableRegionSet getApplicableRegions(Location location) {
|
||||
|
||||
RegionManager manager = WorldGuard.getInstance().getPlatform().getRegionContainer().get((World) location.getExtent());
|
||||
if (manager != null) {
|
||||
return cache.queryContains(manager, location);
|
||||
return cache.queryContains(manager, location, option);
|
||||
} else {
|
||||
return FailedLoadRegionSet.getInstance();
|
||||
}
|
||||
@ -465,4 +494,150 @@ public <V> Collection<V> queryAllValues(Location location, @Nullable RegionAssoc
|
||||
return getApplicableRegions(location).queryAllValues(associable, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for constructing a region set via
|
||||
* {@link #getApplicableRegions(Location, QueryOption)} for example.
|
||||
*/
|
||||
public enum QueryOption {
|
||||
/**
|
||||
* Constructs a region set that does not include parent regions and
|
||||
* may be left unsorted (but a cached, sorted set of the same regions
|
||||
* may be returned).
|
||||
*/
|
||||
NONE(false) {
|
||||
@Override
|
||||
public List<ProtectedRegion> constructResult(Set<ProtectedRegion> applicable) {
|
||||
return ImmutableList.copyOf(applicable);
|
||||
}
|
||||
|
||||
@Override
|
||||
Map<QueryOption, ApplicableRegionSet> createCache(RegionManager manager, Location location, Map<QueryOption, ApplicableRegionSet> cache) {
|
||||
if (cache == null) {
|
||||
cache = new EnumMap<>(QueryOption.class);
|
||||
cache.put(QueryOption.NONE, manager.getApplicableRegions(location.toVector().toBlockPoint(), QueryOption.NONE));
|
||||
}
|
||||
|
||||
// If c != null, we can assume that Option.NONE is present.
|
||||
return cache;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructs a region set that does not include parent regions and is
|
||||
* sorted by {@link NormativeOrders}.
|
||||
*/
|
||||
SORT(false) {
|
||||
@Override
|
||||
Map<QueryOption, ApplicableRegionSet> createCache(RegionManager manager, Location location, Map<QueryOption, ApplicableRegionSet> cache) {
|
||||
if (cache == null) {
|
||||
Map<QueryOption, ApplicableRegionSet> newCache = new EnumMap<>(QueryOption.class);
|
||||
ApplicableRegionSet result = manager.getApplicableRegions(location.toVector().toBlockPoint(), QueryOption.SORT);
|
||||
newCache.put(QueryOption.NONE, result);
|
||||
newCache.put(QueryOption.SORT, result);
|
||||
return newCache;
|
||||
} else {
|
||||
// If c != null, we can assume that Option.NONE is present.
|
||||
cache.computeIfAbsent(QueryOption.SORT, k -> new RegionResultSet(cache.get(QueryOption.NONE).getRegions(), manager.getRegion("__global__")));
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructs a region set that includes parent regions and is sorted by
|
||||
* {@link NormativeOrders}.
|
||||
*/
|
||||
COMPUTE_PARENTS(true) {
|
||||
@Override
|
||||
Map<QueryOption, ApplicableRegionSet> createCache(RegionManager manager, Location location, Map<QueryOption, ApplicableRegionSet> cache) {
|
||||
if (cache == null) {
|
||||
Map<QueryOption, ApplicableRegionSet> newCache = new EnumMap<>(QueryOption.class);
|
||||
ApplicableRegionSet noParResult = manager.getApplicableRegions(location.toVector().toBlockPoint(), QueryOption.NONE);
|
||||
Set<ProtectedRegion> noParRegions = noParResult.getRegions();
|
||||
Set<ProtectedRegion> regions = new HashSet<>();
|
||||
noParRegions.forEach(new RegionCollectionConsumer(regions, true)::apply);
|
||||
ApplicableRegionSet result = new RegionResultSet(regions, manager.getRegion("__global__"));
|
||||
|
||||
if (regions.size() == noParRegions.size()) {
|
||||
newCache.put(QueryOption.NONE, result);
|
||||
newCache.put(QueryOption.SORT, result);
|
||||
} else {
|
||||
newCache.put(QueryOption.NONE, noParResult);
|
||||
}
|
||||
|
||||
newCache.put(QueryOption.COMPUTE_PARENTS, result);
|
||||
return newCache;
|
||||
}
|
||||
|
||||
cache.computeIfAbsent(QueryOption.COMPUTE_PARENTS, k -> {
|
||||
Set<ProtectedRegion> regions = new HashSet<>();
|
||||
ApplicableRegionSet result = cache.get(QueryOption.SORT);
|
||||
boolean sorted = true;
|
||||
|
||||
if (result == null) {
|
||||
// If c != null, we can assume that Option.NONE is present.
|
||||
result = cache.get(QueryOption.NONE);
|
||||
sorted = false;
|
||||
}
|
||||
|
||||
Set<ProtectedRegion> noParRegions = result.getRegions();
|
||||
noParRegions.forEach(new RegionCollectionConsumer(regions, true)::apply);
|
||||
|
||||
if (sorted && regions.size() == noParRegions.size()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = new RegionResultSet(regions, manager.getRegion("__global__"));
|
||||
|
||||
if (regions.size() == noParRegions.size()) {
|
||||
cache.put(QueryOption.SORT, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
return cache;
|
||||
}
|
||||
};
|
||||
|
||||
private final boolean collectParents;
|
||||
|
||||
QueryOption(boolean collectParents) {
|
||||
this.collectParents = collectParents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link RegionCollectionConsumer} with the given collection
|
||||
* used for the {@link RegionIndex}. Internal API.
|
||||
*
|
||||
* @param collection the collection
|
||||
* @return a region collection consumer
|
||||
*/
|
||||
public RegionCollectionConsumer createIndexConsumer(Collection<? super ProtectedRegion> collection) {
|
||||
return new RegionCollectionConsumer(collection, collectParents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the set of regions to a list. Sort and add parents if
|
||||
* necessary. Internal API.
|
||||
*
|
||||
* @param applicable the set of regions
|
||||
* @return a list of regions
|
||||
*/
|
||||
public List<ProtectedRegion> constructResult(Set<ProtectedRegion> applicable) {
|
||||
return NormativeOrders.fromSet(applicable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create (if null) or update the given cache map with at least an entry
|
||||
* for this option if necessary and return it.
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param location the location
|
||||
* @param cache the cache map
|
||||
* @return a cache map
|
||||
*/
|
||||
abstract Map<QueryOption, ApplicableRegionSet> createCache(RegionManager manager, Location location, Map<QueryOption, ApplicableRegionSet> cache);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
*/
|
||||
public class RegionCollectionConsumer implements Predicate<ProtectedRegion> {
|
||||
|
||||
private final Collection<ProtectedRegion> collection;
|
||||
private final Collection<? super ProtectedRegion> collection;
|
||||
private final boolean addParents;
|
||||
|
||||
/**
|
||||
@ -44,7 +44,7 @@ public class RegionCollectionConsumer implements Predicate<ProtectedRegion> {
|
||||
* @param collection the collection to add regions to
|
||||
* @param addParents true to also add the parents to the collection
|
||||
*/
|
||||
public RegionCollectionConsumer(Collection<ProtectedRegion> collection, boolean addParents) {
|
||||
public RegionCollectionConsumer(Collection<? super ProtectedRegion> collection, boolean addParents) {
|
||||
checkNotNull(collection);
|
||||
|
||||
this.collection = collection;
|
||||
|
@ -212,7 +212,7 @@ public void testNonPlayerBuildAccessInOneRegion(boolean useMaxPriorityAssociatio
|
||||
assertTrue(appl.testState(assoc, Flags.BUILD));
|
||||
// Inside fountain
|
||||
appl = manager.getApplicableRegions(inFountain);
|
||||
assertFalse(appl.testState(assoc, Flags.BUILD));
|
||||
assertTrue(appl.testState(assoc, Flags.BUILD));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "useMaxPriorityAssociation={0}")
|
||||
|
Loading…
Reference in New Issue
Block a user