mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-11-30 14:33:47 +01:00
Updated FlagValueCalculator to treat global regions a lowest priority region.
This commit is contained in:
parent
4d43ef5305
commit
7481acba8c
@ -26,23 +26,18 @@
|
|||||||
import com.sk89q.worldguard.protection.flags.RegionGroup;
|
import com.sk89q.worldguard.protection.flags.RegionGroup;
|
||||||
import com.sk89q.worldguard.protection.flags.RegionGroupFlag;
|
import com.sk89q.worldguard.protection.flags.RegionGroupFlag;
|
||||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||||
import com.sk89q.worldguard.protection.flags.StateFlag.*;
|
|
||||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.sk89q.worldguard.protection.flags.StateFlag.*;
|
import static com.sk89q.worldguard.protection.flags.StateFlag.test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the effective set of flags, owners, and members for a given
|
* Represents the effective set of flags, owners, and members for a given
|
||||||
@ -61,6 +56,7 @@ public class ApplicableRegionSet implements Iterable<ProtectedRegion> {
|
|||||||
private final SortedSet<ProtectedRegion> applicable;
|
private final SortedSet<ProtectedRegion> applicable;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final ProtectedRegion globalRegion;
|
private final ProtectedRegion globalRegion;
|
||||||
|
private final FlagValueCalculator flagValueCalculator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the object.
|
* Construct the object.
|
||||||
@ -84,6 +80,7 @@ public ApplicableRegionSet(SortedSet<ProtectedRegion> applicable, @Nullable Prot
|
|||||||
checkNotNull(applicable);
|
checkNotNull(applicable);
|
||||||
this.applicable = applicable;
|
this.applicable = applicable;
|
||||||
this.globalRegion = globalRegion;
|
this.globalRegion = globalRegion;
|
||||||
|
this.flagValueCalculator = new FlagValueCalculator(applicable, globalRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,7 +91,7 @@ public ApplicableRegionSet(SortedSet<ProtectedRegion> applicable, @Nullable Prot
|
|||||||
*/
|
*/
|
||||||
public boolean canBuild(LocalPlayer player) {
|
public boolean canBuild(LocalPlayer player) {
|
||||||
checkNotNull(player);
|
checkNotNull(player);
|
||||||
return test(calculateState(DefaultFlag.BUILD, new RegionMemberTest(player), null));
|
return test(flagValueCalculator.testPermission(player, DefaultFlag.BUILD));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,7 +120,7 @@ public boolean allows(StateFlag flag) {
|
|||||||
throw new IllegalArgumentException("Can't use build flag with allows()");
|
throw new IllegalArgumentException("Can't use build flag with allows()");
|
||||||
}
|
}
|
||||||
|
|
||||||
return test(calculateState(flag, null, null));
|
return test(flagValueCalculator.queryState(null, flag));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,7 +137,8 @@ public boolean allows(StateFlag flag, @Nullable LocalPlayer player) {
|
|||||||
if (flag == DefaultFlag.BUILD) {
|
if (flag == DefaultFlag.BUILD) {
|
||||||
throw new IllegalArgumentException("Can't use build flag with allows()");
|
throw new IllegalArgumentException("Can't use build flag with allows()");
|
||||||
}
|
}
|
||||||
return test(calculateState(flag, null, player));
|
|
||||||
|
return test(flagValueCalculator.queryState(player, flag));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,206 +177,6 @@ public boolean isMemberOfAll(LocalPlayer player) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the effective value of a flag based on the regions
|
|
||||||
* in this set, membership, the global region (if set), and the default
|
|
||||||
* value of a flag {@link StateFlag#getDefault()}.
|
|
||||||
*
|
|
||||||
* @param flag the flag to check
|
|
||||||
* @param membershipTest null to perform a "wilderness check" or a predicate
|
|
||||||
* returns true if a the subject is a member of the
|
|
||||||
* region passed
|
|
||||||
* @param groupPlayer a player to use for the group flag check
|
|
||||||
* @return the allow/deny state for the flag
|
|
||||||
*/
|
|
||||||
private State calculateState(StateFlag flag, @Nullable Predicate<ProtectedRegion> membershipTest, @Nullable LocalPlayer groupPlayer) {
|
|
||||||
checkNotNull(flag);
|
|
||||||
|
|
||||||
// This method works in two modes:
|
|
||||||
//
|
|
||||||
// 1) Membership mode (if membershipTest != null):
|
|
||||||
// a) Regions in this set -> Check membership + Check region flags
|
|
||||||
// a) No regions -> Use global region + default value
|
|
||||||
// 1) Flag mode:
|
|
||||||
// a) Regions in this set -> Use global region + default value
|
|
||||||
// a) No regions -> Use global region + default value
|
|
||||||
|
|
||||||
int minimumPriority = Integer.MIN_VALUE;
|
|
||||||
boolean regionsThatCountExistHere = false; // We can't do a application.isEmpty() because
|
|
||||||
// PASSTHROUGH regions have to be skipped
|
|
||||||
// (in some cases)
|
|
||||||
State state = null; // Start with NONE
|
|
||||||
|
|
||||||
// Say there are two regions in one location: CHILD and PARENT (CHILD
|
|
||||||
// is a child of PARENT). If there are two overlapping regions in WG, a
|
|
||||||
// player has to be a member of /both/ (or flags permit) in order to
|
|
||||||
// build in that location. However, inheritance is supposed
|
|
||||||
// to allow building if the player is a member of just CHILD. That
|
|
||||||
// presents a problem.
|
|
||||||
//
|
|
||||||
// To rectify this, we keep two sets. When we iterate over the list of
|
|
||||||
// regions, there are two scenarios that we may encounter:
|
|
||||||
//
|
|
||||||
// 1) PARENT first, CHILD later:
|
|
||||||
// a) When the loop reaches PARENT, PARENT is added to needsClear.
|
|
||||||
// b) When the loop reaches CHILD, parents of CHILD (which includes
|
|
||||||
// PARENT) are removed from needsClear.
|
|
||||||
// c) needsClear is empty again.
|
|
||||||
//
|
|
||||||
// 2) CHILD first, PARENT later:
|
|
||||||
// a) When the loop reaches CHILD, CHILD's parents (i.e. PARENT) are
|
|
||||||
// added to hasCleared.
|
|
||||||
// b) When the loop reaches PARENT, since PARENT is already in
|
|
||||||
// hasCleared, it does not add PARENT to needsClear.
|
|
||||||
// c) needsClear stays empty.
|
|
||||||
//
|
|
||||||
// As long as the process ends with needsClear being empty, then
|
|
||||||
// we have satisfied all membership requirements.
|
|
||||||
|
|
||||||
Set<ProtectedRegion> needsClear = new HashSet<ProtectedRegion>();
|
|
||||||
Set<ProtectedRegion> hasCleared = new HashSet<ProtectedRegion>();
|
|
||||||
|
|
||||||
for (ProtectedRegion region : applicable) {
|
|
||||||
// Don't consider lower priorities below minimumPriority
|
|
||||||
// (which starts at Integer.MIN_VALUE). A region that "counts"
|
|
||||||
// (has the flag set OR has members) will raise minimumPriority
|
|
||||||
// to its own priority.
|
|
||||||
if (region.getPriority() < minimumPriority) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If PASSTHROUGH is set and we are checking to see if a player
|
|
||||||
// is a member, then skip this region
|
|
||||||
if (membershipTest != null && getStateFlagIncludingParents(region, DefaultFlag.PASSTHROUGH) == State.ALLOW) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the flag has a group set on to it, skip this region if
|
|
||||||
// the group does not match our (group) player
|
|
||||||
if (groupPlayer != null && flag.getRegionGroupFlag() != null) {
|
|
||||||
RegionGroup group = region.getFlag(flag.getRegionGroupFlag());
|
|
||||||
if (group == null) {
|
|
||||||
group = flag.getRegionGroupFlag().getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!RegionGroupFlag.isMember(region, group, groupPlayer)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
regionsThatCountExistHere = true;
|
|
||||||
|
|
||||||
State v = getStateFlagIncludingParents(region, flag);
|
|
||||||
|
|
||||||
// DENY overrides everything
|
|
||||||
if (v == State.DENY) {
|
|
||||||
state = State.DENY;
|
|
||||||
break; // No need to process any more regions
|
|
||||||
|
|
||||||
// ALLOW means we don't care about membership
|
|
||||||
} else if (v == State.ALLOW) {
|
|
||||||
state = State.ALLOW;
|
|
||||||
minimumPriority = region.getPriority();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (membershipTest != null) {
|
|
||||||
minimumPriority = region.getPriority();
|
|
||||||
|
|
||||||
if (!hasCleared.contains(region)) {
|
|
||||||
if (!membershipTest.apply(region)) {
|
|
||||||
needsClear.add(region);
|
|
||||||
} else {
|
|
||||||
// Need to clear all parents
|
|
||||||
clearParents(needsClear, hasCleared, region);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (membershipTest != null) {
|
|
||||||
State fallback;
|
|
||||||
|
|
||||||
if (regionsThatCountExistHere) {
|
|
||||||
fallback = allowOrNone(needsClear.isEmpty());
|
|
||||||
} else {
|
|
||||||
fallback = getDefault(flag, membershipTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
return combine(state, fallback);
|
|
||||||
} else {
|
|
||||||
return combine(state, getDefault(flag, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private State getDefault(StateFlag flag, @Nullable Predicate<ProtectedRegion> membershipTest) {
|
|
||||||
boolean allowed = flag.getDefault() == State.ALLOW;
|
|
||||||
|
|
||||||
// Handle defaults
|
|
||||||
if (globalRegion != null) {
|
|
||||||
State globalState = globalRegion.getFlag(flag);
|
|
||||||
|
|
||||||
// The global region has this flag set
|
|
||||||
if (globalState != null) {
|
|
||||||
// Build flag is very special
|
|
||||||
if (membershipTest != null && globalRegion.hasMembersOrOwners()) {
|
|
||||||
allowed = membershipTest.apply(globalRegion) && (globalState == State.ALLOW);
|
|
||||||
} else {
|
|
||||||
allowed = (globalState == State.ALLOW);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Build flag is very special
|
|
||||||
if (membershipTest != null && globalRegion.hasMembersOrOwners()) {
|
|
||||||
allowed = membershipTest.apply(globalRegion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allowed ? State.ALLOW : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear a region's parents for isFlagAllowed().
|
|
||||||
*
|
|
||||||
* @param needsClear the regions that should be cleared
|
|
||||||
* @param hasCleared the regions already cleared
|
|
||||||
* @param region the region to start from
|
|
||||||
*/
|
|
||||||
private void clearParents(Set<ProtectedRegion> needsClear, Set<ProtectedRegion> hasCleared, ProtectedRegion region) {
|
|
||||||
ProtectedRegion parent = region.getParent();
|
|
||||||
|
|
||||||
while (parent != null) {
|
|
||||||
if (!needsClear.remove(parent)) {
|
|
||||||
hasCleared.add(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
parent = parent.getParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a region's state flag, checking parent regions until a value for the
|
|
||||||
* flag can be found (if one even exists).
|
|
||||||
*
|
|
||||||
* @param region the region
|
|
||||||
* @param flag the flag
|
|
||||||
* @return the value
|
|
||||||
*/
|
|
||||||
private static State getStateFlagIncludingParents(ProtectedRegion region, StateFlag flag) {
|
|
||||||
while (region != null) {
|
|
||||||
State value = region.getFlag(flag);
|
|
||||||
|
|
||||||
if (value != null) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
region = region.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of a flag. Do not use this for state flags
|
* Gets the value of a flag. Do not use this for state flags
|
||||||
* (use {@link #allows(StateFlag, LocalPlayer)} for that).
|
* (use {@link #allows(StateFlag, LocalPlayer)} for that).
|
||||||
@ -402,82 +200,7 @@ public <T extends Flag<V>, V> V getFlag(T flag) {
|
|||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T extends Flag<V>, V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer) {
|
public <T extends Flag<V>, V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer) {
|
||||||
checkNotNull(flag);
|
return flagValueCalculator.queryValue(groupPlayer, flag);
|
||||||
|
|
||||||
/*
|
|
||||||
if (flag instanceof StateFlag) {
|
|
||||||
throw new IllegalArgumentException("Cannot use StateFlag with getFlag()");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
int lastPriority = 0;
|
|
||||||
boolean found = false;
|
|
||||||
|
|
||||||
Map<ProtectedRegion, V> needsClear = new HashMap<ProtectedRegion, V>();
|
|
||||||
Set<ProtectedRegion> hasCleared = new HashSet<ProtectedRegion>();
|
|
||||||
|
|
||||||
for (ProtectedRegion region : applicable) {
|
|
||||||
// Ignore lower priority regions
|
|
||||||
if (found && region.getPriority() < lastPriority) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check group permissions
|
|
||||||
if (groupPlayer != null && flag.getRegionGroupFlag() != null) {
|
|
||||||
RegionGroup group = region.getFlag(flag.getRegionGroupFlag());
|
|
||||||
|
|
||||||
if (group == null) {
|
|
||||||
group = flag.getRegionGroupFlag().getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!RegionGroupFlag.isMember(region, group, groupPlayer)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection StatementWithEmptyBody
|
|
||||||
if (hasCleared.contains(region)) {
|
|
||||||
// Already cleared, so do nothing
|
|
||||||
} else if (region.getFlag(flag) != null) {
|
|
||||||
clearParents(needsClear, hasCleared, region);
|
|
||||||
|
|
||||||
needsClear.put(region, region.getFlag(flag));
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastPriority = region.getPriority();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!needsClear.isEmpty()) {
|
|
||||||
return needsClear.values().iterator().next();
|
|
||||||
} else {
|
|
||||||
if (globalRegion != null) {
|
|
||||||
V gFlag = globalRegion.getFlag(flag);
|
|
||||||
if (gFlag != null) return gFlag;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear a region's parents for getFlag().
|
|
||||||
*
|
|
||||||
* @param needsClear The regions that should be cleared
|
|
||||||
* @param hasCleared The regions already cleared
|
|
||||||
* @param region The region to start from
|
|
||||||
*/
|
|
||||||
private void clearParents(Map<ProtectedRegion, ?> needsClear,
|
|
||||||
Set<ProtectedRegion> hasCleared, ProtectedRegion region) {
|
|
||||||
ProtectedRegion parent = region.getParent();
|
|
||||||
|
|
||||||
while (parent != null) {
|
|
||||||
if (needsClear.remove(parent) == null) {
|
|
||||||
hasCleared.add(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
parent = parent.getParent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package com.sk89q.worldguard.protection;
|
package com.sk89q.worldguard.protection;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.sk89q.worldguard.LocalPlayer;
|
import com.sk89q.worldguard.LocalPlayer;
|
||||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||||
import com.sk89q.worldguard.protection.flags.Flag;
|
import com.sk89q.worldguard.protection.flags.Flag;
|
||||||
@ -50,23 +52,37 @@
|
|||||||
*/
|
*/
|
||||||
public class FlagValueCalculator {
|
public class FlagValueCalculator {
|
||||||
|
|
||||||
private final SortedSet<ProtectedRegion> applicable;
|
private final SortedSet<ProtectedRegion> regions;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final ProtectedRegion globalRegion;
|
private final ProtectedRegion globalRegion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance.
|
* Create a new instance.
|
||||||
*
|
*
|
||||||
* @param applicable a list of applicable regions
|
* @param regions a list of applicable regions
|
||||||
* @param globalRegion an optional global region (null to not use one)
|
* @param globalRegion an optional global region (null to not use one)
|
||||||
*/
|
*/
|
||||||
public FlagValueCalculator(SortedSet<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion) {
|
public FlagValueCalculator(SortedSet<ProtectedRegion> regions, @Nullable ProtectedRegion globalRegion) {
|
||||||
checkNotNull(applicable);
|
checkNotNull(regions);
|
||||||
|
|
||||||
this.applicable = applicable;
|
this.regions = regions;
|
||||||
this.globalRegion = globalRegion;
|
this.globalRegion = globalRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterable of regions sorted by priority (descending), with
|
||||||
|
* the global region tacked on at the end if one exists.
|
||||||
|
*
|
||||||
|
* @return an iterable
|
||||||
|
*/
|
||||||
|
private Iterable<ProtectedRegion> getApplicable() {
|
||||||
|
if (globalRegion != null) {
|
||||||
|
return Iterables.concat(regions, ImmutableList.of(globalRegion));
|
||||||
|
} else {
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the membership status of the given player, indicating
|
* Return the membership status of the given player, indicating
|
||||||
* whether there are no (counted) regions in the list of regions,
|
* whether there are no (counted) regions in the list of regions,
|
||||||
@ -119,12 +135,12 @@ public Result getMembership(LocalPlayer player) {
|
|||||||
Set<ProtectedRegion> needsClear = new HashSet<ProtectedRegion>();
|
Set<ProtectedRegion> needsClear = new HashSet<ProtectedRegion>();
|
||||||
Set<ProtectedRegion> hasCleared = new HashSet<ProtectedRegion>();
|
Set<ProtectedRegion> hasCleared = new HashSet<ProtectedRegion>();
|
||||||
|
|
||||||
for (ProtectedRegion region : applicable) {
|
for (ProtectedRegion region : getApplicable()) {
|
||||||
// Don't consider lower priorities below minimumPriority
|
// Don't consider lower priorities below minimumPriority
|
||||||
// (which starts at Integer.MIN_VALUE). A region that "counts"
|
// (which starts at Integer.MIN_VALUE). A region that "counts"
|
||||||
// (has the flag set OR has members) will raise minimumPriority
|
// (has the flag set OR has members) will raise minimumPriority
|
||||||
// to its own priority.
|
// to its own priority.
|
||||||
if (region.getPriority() < minimumPriority) {
|
if (getPriority(region) < minimumPriority) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +149,7 @@ public Result getMembership(LocalPlayer player) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
minimumPriority = region.getPriority();
|
minimumPriority = getPriority(region);
|
||||||
foundApplicableRegion = true;
|
foundApplicableRegion = true;
|
||||||
|
|
||||||
if (!hasCleared.contains(region)) {
|
if (!hasCleared.contains(region)) {
|
||||||
@ -194,76 +210,27 @@ public State testPermission(LocalPlayer player, StateFlag... flags) {
|
|||||||
|
|
||||||
switch (getMembership(player)) {
|
switch (getMembership(player)) {
|
||||||
case SUCCESS:
|
case SUCCESS:
|
||||||
return StateFlag.combine(getState(player, flags), State.ALLOW);
|
return StateFlag.combine(queryState(player, flags), State.ALLOW);
|
||||||
case FAIL:
|
case FAIL:
|
||||||
return getState(player, flags);
|
return queryState(player, flags);
|
||||||
case NO_REGIONS:
|
case NO_REGIONS:
|
||||||
if (globalRegion != null && globalRegion.hasMembersOrOwners()) {
|
|
||||||
if (globalRegion.isMember(player)) {
|
|
||||||
return StateFlag.combine(getState(player, flags), State.ALLOW);
|
|
||||||
} else {
|
|
||||||
State value = null;
|
|
||||||
|
|
||||||
for (StateFlag flag : flags) {
|
|
||||||
value = StateFlag.combine(value,globalRegion.getFlag(flag));
|
|
||||||
if (value == State.DENY) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return getStateWithFallback(player, flags);
|
State fallback = null;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the effective value for a list of state flags. The rules of
|
|
||||||
* states is observed here; that is, {@code DENY} overrides {@code ALLOW},
|
|
||||||
* and {@code ALLOW} overrides {@code NONE}. This method will check
|
|
||||||
* the global region and {@link Flag#getDefault()} (in that order) if
|
|
||||||
* a value for the flag is not set in any region.
|
|
||||||
*
|
|
||||||
* <p>This method does <strong>not</strong> properly process build
|
|
||||||
* permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)}
|
|
||||||
* for that purpose. This method is ideal for testing non-build related
|
|
||||||
* state flags (although a rarity), an example of which would be whether
|
|
||||||
* to play a song to players that enter an area.</p>
|
|
||||||
*
|
|
||||||
* <p>A player can be provided that is used to determine whether the value
|
|
||||||
* of a flag on a particular region should be used. For example, if a
|
|
||||||
* flag's region group is set to {@link RegionGroup#MEMBERS} and the given
|
|
||||||
* player is not a member, then the region would be skipped when
|
|
||||||
* querying that flag. If {@code null} is provided for the player, then
|
|
||||||
* only flags that use {@link RegionGroup#ALL},
|
|
||||||
* {@link RegionGroup#NON_MEMBERS}, etc. will apply.</p>
|
|
||||||
*
|
|
||||||
* @param player an optional player, which would be used to determine the region group to apply
|
|
||||||
* @param flags a list of flags to check
|
|
||||||
* @return a state
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public State getStateWithFallback(@Nullable LocalPlayer player, StateFlag... flags) {
|
|
||||||
State value = null;
|
|
||||||
|
|
||||||
for (StateFlag flag : flags) {
|
for (StateFlag flag : flags) {
|
||||||
value = StateFlag.combine(value, getSingleValueWithFallback(player, flag));
|
if (flag.getDefault()) {
|
||||||
if (value == State.DENY) {
|
fallback = State.ALLOW;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return StateFlag.combine(queryState(player, flags), fallback);
|
||||||
return value;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the effective value for a list of state flags. The rules of
|
* Get the effective value for a list of state flags. The rules of
|
||||||
* states is observed here; that is, {@code DENY} overrides {@code ALLOW},
|
* states is observed here; that is, {@code DENY} overrides {@code ALLOW},
|
||||||
* and {@code ALLOW} overrides {@code NONE}. This method does not check
|
* and {@code ALLOW} overrides {@code NONE}.
|
||||||
* the global region and ignores a flag's default value.
|
|
||||||
*
|
*
|
||||||
* <p>This method does <strong>not</strong> properly process build
|
* <p>This method does <strong>not</strong> properly process build
|
||||||
* permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)}
|
* permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)}
|
||||||
@ -284,11 +251,11 @@ public State getStateWithFallback(@Nullable LocalPlayer player, StateFlag... fla
|
|||||||
* @return a state
|
* @return a state
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public State getState(@Nullable LocalPlayer player, StateFlag... flags) {
|
public State queryState(@Nullable LocalPlayer player, StateFlag... flags) {
|
||||||
State value = null;
|
State value = null;
|
||||||
|
|
||||||
for (StateFlag flag : flags) {
|
for (StateFlag flag : flags) {
|
||||||
value = StateFlag.combine(value, getSingleValue(player, flag));
|
value = StateFlag.combine(value, queryValue(player, flag));
|
||||||
if (value == State.DENY) {
|
if (value == State.DENY) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -302,8 +269,7 @@ public State getState(@Nullable LocalPlayer player, StateFlag... flags) {
|
|||||||
* (for example, if there are multiple regions with the same priority
|
* (for example, if there are multiple regions with the same priority
|
||||||
* but with different farewell messages set, there would be multiple
|
* but with different farewell messages set, there would be multiple
|
||||||
* completing values), then the selected (or "winning") value will depend
|
* completing values), then the selected (or "winning") value will depend
|
||||||
* on the flag type. This method will check the global region
|
* on the flag type.
|
||||||
* for a value as well as the flag's default value.
|
|
||||||
*
|
*
|
||||||
* <p>Only some flag types actually have a strategy for picking the
|
* <p>Only some flag types actually have a strategy for picking the
|
||||||
* "best value." For most types, the actual value that is chosen to be
|
* "best value." For most types, the actual value that is chosen to be
|
||||||
@ -326,65 +292,10 @@ public State getState(@Nullable LocalPlayer player, StateFlag... flags) {
|
|||||||
* @param player an optional player, which would be used to determine the region group to apply
|
* @param player an optional player, which would be used to determine the region group to apply
|
||||||
* @param flag the flag
|
* @param flag the flag
|
||||||
* @return a value, which could be {@code null}
|
* @return a value, which could be {@code null}
|
||||||
* @see #getSingleValue(LocalPlayer, Flag) does not check global region, defaults
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public <V> V getSingleValueWithFallback(@Nullable LocalPlayer player, Flag<V> flag) {
|
public <V> V queryValue(@Nullable LocalPlayer player, Flag<V> flag) {
|
||||||
checkNotNull(flag);
|
Collection<V> values = queryAllValues(player, flag);
|
||||||
|
|
||||||
V value = getSingleValue(player, flag);
|
|
||||||
|
|
||||||
if (value != null) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the value from the global region
|
|
||||||
if (globalRegion != null) {
|
|
||||||
value = globalRegion.getFlag(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Still no value? Check the default value for the flag
|
|
||||||
if (value == null) {
|
|
||||||
value = flag.getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
return flag.validateDefaultValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the effective value for a flag. If there are multiple values
|
|
||||||
* (for example, if there are multiple regions with the same priority
|
|
||||||
* but with different farewell messages set, there would be multiple
|
|
||||||
* completing values), then the selected (or "winning") value will depend
|
|
||||||
* on the flag type. This method never checks the global region or
|
|
||||||
* the flag's default value.
|
|
||||||
*
|
|
||||||
* <p>Only some flag types actually have a strategy for picking the
|
|
||||||
* "best value." For most types, the actual value that is chosen to be
|
|
||||||
* returned is undefined (it could be any value). As of writing, the only
|
|
||||||
* type of flag that can consistently return the same 'best' value is
|
|
||||||
* {@link StateFlag}.</p>
|
|
||||||
*
|
|
||||||
* <p>This method does <strong>not</strong> properly process build
|
|
||||||
* permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)}
|
|
||||||
* for that purpose.</p>
|
|
||||||
*
|
|
||||||
* <p>A player can be provided that is used to determine whether the value
|
|
||||||
* of a flag on a particular region should be used. For example, if a
|
|
||||||
* flag's region group is set to {@link RegionGroup#MEMBERS} and the given
|
|
||||||
* player is not a member, then the region would be skipped when
|
|
||||||
* querying that flag. If {@code null} is provided for the player, then
|
|
||||||
* only flags that use {@link RegionGroup#ALL},
|
|
||||||
* {@link RegionGroup#NON_MEMBERS}, etc. will apply.</p>
|
|
||||||
*
|
|
||||||
* @param player an optional player, which would be used to determine the region group to apply
|
|
||||||
* @param flag the flag
|
|
||||||
* @return a value, which could be {@code null}
|
|
||||||
* @see #getSingleValueWithFallback(LocalPlayer, Flag) checks global regions, defaults
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public <V> V getSingleValue(@Nullable LocalPlayer player, Flag<V> flag) {
|
|
||||||
Collection<V> values = getValues(player, flag);
|
|
||||||
return flag.chooseValue(values);
|
return flag.chooseValue(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +316,7 @@ public <V> V getSingleValue(@Nullable LocalPlayer player, Flag<V> flag) {
|
|||||||
* only flags that use {@link RegionGroup#ALL},
|
* only flags that use {@link RegionGroup#ALL},
|
||||||
* {@link RegionGroup#NON_MEMBERS}, etc. will apply.</p>
|
* {@link RegionGroup#NON_MEMBERS}, etc. will apply.</p>
|
||||||
*/
|
*/
|
||||||
public <V> Collection<V> getValues(@Nullable LocalPlayer player, Flag<V> flag) {
|
public <V> Collection<V> queryAllValues(@Nullable LocalPlayer player, Flag<V> flag) {
|
||||||
checkNotNull(flag);
|
checkNotNull(flag);
|
||||||
|
|
||||||
int minimumPriority = Integer.MIN_VALUE;
|
int minimumPriority = Integer.MIN_VALUE;
|
||||||
@ -441,12 +352,12 @@ public <V> Collection<V> getValues(@Nullable LocalPlayer player, Flag<V> flag) {
|
|||||||
Map<ProtectedRegion, V> consideredValues = new HashMap<ProtectedRegion, V>();
|
Map<ProtectedRegion, V> consideredValues = new HashMap<ProtectedRegion, V>();
|
||||||
Set<ProtectedRegion> ignoredRegions = new HashSet<ProtectedRegion>();
|
Set<ProtectedRegion> ignoredRegions = new HashSet<ProtectedRegion>();
|
||||||
|
|
||||||
for (ProtectedRegion region : applicable) {
|
for (ProtectedRegion region : getApplicable()) {
|
||||||
// Don't consider lower priorities below minimumPriority
|
// Don't consider lower priorities below minimumPriority
|
||||||
// (which starts at Integer.MIN_VALUE). A region that "counts"
|
// (which starts at Integer.MIN_VALUE). A region that "counts"
|
||||||
// (has the flag set) will raise minimumPriority to its own
|
// (has the flag set) will raise minimumPriority to its own
|
||||||
// priority.
|
// priority.
|
||||||
if (region.getPriority() < minimumPriority) {
|
if (getPriority(region) < minimumPriority) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +365,7 @@ public <V> Collection<V> getValues(@Nullable LocalPlayer player, Flag<V> flag) {
|
|||||||
|
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (!ignoredRegions.contains(region)) {
|
if (!ignoredRegions.contains(region)) {
|
||||||
minimumPriority = region.getPriority();
|
minimumPriority = getPriority(region);
|
||||||
|
|
||||||
ignoreValuesOfParents(consideredValues, ignoredRegions, region);
|
ignoreValuesOfParents(consideredValues, ignoredRegions, region);
|
||||||
consideredValues.put(region, value);
|
consideredValues.put(region, value);
|
||||||
@ -471,6 +382,21 @@ public <V> Collection<V> getValues(@Nullable LocalPlayer player, Flag<V> flag) {
|
|||||||
return consideredValues.values();
|
return consideredValues.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the effective priority of a region, overriding a region's priority
|
||||||
|
* when appropriate (i.e. with the global region).
|
||||||
|
*
|
||||||
|
* @param region the region
|
||||||
|
* @return the priority
|
||||||
|
*/
|
||||||
|
public int getPriority(final ProtectedRegion region) {
|
||||||
|
if (region == globalRegion) {
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
} else {
|
||||||
|
return region.getPriority();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a region's state flag, checking parent regions until a value for the
|
* Get a region's state flag, checking parent regions until a value for the
|
||||||
* flag can be found (if one even exists).
|
* flag can be found (if one even exists).
|
||||||
@ -479,7 +405,19 @@ public <V> Collection<V> getValues(@Nullable LocalPlayer player, Flag<V> flag) {
|
|||||||
* @param flag the flag
|
* @param flag the flag
|
||||||
* @return the value
|
* @return the value
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public <V> V getEffectiveFlag(final ProtectedRegion region, Flag<V> flag, @Nullable LocalPlayer player) {
|
public <V> V getEffectiveFlag(final ProtectedRegion region, Flag<V> flag, @Nullable LocalPlayer player) {
|
||||||
|
// The global region normally does not prevent building so
|
||||||
|
// PASSTHROUGH has to be ALLOW, except when people use the global
|
||||||
|
// region as a whitelist
|
||||||
|
if (region == globalRegion && flag == DefaultFlag.PASSTHROUGH) {
|
||||||
|
if (region.hasMembersOrOwners()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return (V) State.ALLOW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ProtectedRegion current = region;
|
ProtectedRegion current = region;
|
||||||
|
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
|
@ -51,24 +51,6 @@ public String getName() {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Suppress the value of the flag that came from the global region, reducing
|
|
||||||
* its severity (i.e. DENY -> NONE).
|
|
||||||
*
|
|
||||||
* <p>This is really only used for the {@link StateFlag}.</p>
|
|
||||||
*
|
|
||||||
* @param current the value to suppress
|
|
||||||
* @return a new value
|
|
||||||
*/
|
|
||||||
public T validateDefaultValue(T current) {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public T getDefault() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public T chooseValue(Collection<T> values) {
|
public T chooseValue(Collection<T> values) {
|
||||||
if (!values.isEmpty()) {
|
if (!values.isEmpty()) {
|
||||||
|
@ -38,7 +38,6 @@ public RegionGroupFlag(String name, RegionGroup def) {
|
|||||||
this.def = def;
|
this.def = def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public RegionGroup getDefault() {
|
public RegionGroup getDefault() {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
@ -48,14 +48,8 @@ public StateFlag(String name, boolean def) {
|
|||||||
this.def = def;
|
this.def = def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean getDefault() {
|
||||||
public State getDefault() {
|
return def;
|
||||||
return def ? State.ALLOW : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public State validateDefaultValue(State current) {
|
|
||||||
return denyToNone(current);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user