mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-11-27 21:15:57 +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.RegionGroupFlag;
|
||||
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.regions.ProtectedRegion;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
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
|
||||
@ -61,6 +56,7 @@ public class ApplicableRegionSet implements Iterable<ProtectedRegion> {
|
||||
private final SortedSet<ProtectedRegion> applicable;
|
||||
@Nullable
|
||||
private final ProtectedRegion globalRegion;
|
||||
private final FlagValueCalculator flagValueCalculator;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
@ -84,6 +80,7 @@ public ApplicableRegionSet(SortedSet<ProtectedRegion> applicable, @Nullable Prot
|
||||
checkNotNull(applicable);
|
||||
this.applicable = applicable;
|
||||
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) {
|
||||
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()");
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* (use {@link #allows(StateFlag, LocalPlayer)} for that).
|
||||
@ -402,82 +200,7 @@ public <T extends Flag<V>, V> V getFlag(T flag) {
|
||||
*/
|
||||
@Nullable
|
||||
public <T extends Flag<V>, V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer) {
|
||||
checkNotNull(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();
|
||||
}
|
||||
return flagValueCalculator.queryValue(groupPlayer, flag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
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.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
@ -50,23 +52,37 @@
|
||||
*/
|
||||
public class FlagValueCalculator {
|
||||
|
||||
private final SortedSet<ProtectedRegion> applicable;
|
||||
private final SortedSet<ProtectedRegion> regions;
|
||||
@Nullable
|
||||
private final ProtectedRegion globalRegion;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public FlagValueCalculator(SortedSet<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion) {
|
||||
checkNotNull(applicable);
|
||||
public FlagValueCalculator(SortedSet<ProtectedRegion> regions, @Nullable ProtectedRegion globalRegion) {
|
||||
checkNotNull(regions);
|
||||
|
||||
this.applicable = applicable;
|
||||
this.regions = regions;
|
||||
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
|
||||
* 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> hasCleared = new HashSet<ProtectedRegion>();
|
||||
|
||||
for (ProtectedRegion region : applicable) {
|
||||
for (ProtectedRegion region : getApplicable()) {
|
||||
// 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) {
|
||||
if (getPriority(region) < minimumPriority) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -133,7 +149,7 @@ public Result getMembership(LocalPlayer player) {
|
||||
continue;
|
||||
}
|
||||
|
||||
minimumPriority = region.getPriority();
|
||||
minimumPriority = getPriority(region);
|
||||
foundApplicableRegion = true;
|
||||
|
||||
if (!hasCleared.contains(region)) {
|
||||
@ -194,76 +210,27 @@ public State testPermission(LocalPlayer player, StateFlag... flags) {
|
||||
|
||||
switch (getMembership(player)) {
|
||||
case SUCCESS:
|
||||
return StateFlag.combine(getState(player, flags), State.ALLOW);
|
||||
return StateFlag.combine(queryState(player, flags), State.ALLOW);
|
||||
case FAIL:
|
||||
return getState(player, flags);
|
||||
return queryState(player, flags);
|
||||
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:
|
||||
return getStateWithFallback(player, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
State fallback = null;
|
||||
for (StateFlag flag : flags) {
|
||||
value = StateFlag.combine(value, getSingleValueWithFallback(player, flag));
|
||||
if (value == State.DENY) {
|
||||
if (flag.getDefault()) {
|
||||
fallback = State.ALLOW;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
return StateFlag.combine(queryState(player, flags), fallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 does not check
|
||||
* the global region and ignores a flag's default value.
|
||||
* and {@code ALLOW} overrides {@code NONE}.
|
||||
*
|
||||
* <p>This method does <strong>not</strong> properly process build
|
||||
* permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)}
|
||||
@ -284,11 +251,11 @@ public State getStateWithFallback(@Nullable LocalPlayer player, StateFlag... fla
|
||||
* @return a state
|
||||
*/
|
||||
@Nullable
|
||||
public State getState(@Nullable LocalPlayer player, StateFlag... flags) {
|
||||
public State queryState(@Nullable LocalPlayer player, StateFlag... flags) {
|
||||
State value = null;
|
||||
|
||||
for (StateFlag flag : flags) {
|
||||
value = StateFlag.combine(value, getSingleValue(player, flag));
|
||||
value = StateFlag.combine(value, queryValue(player, flag));
|
||||
if (value == State.DENY) {
|
||||
break;
|
||||
}
|
||||
@ -302,8 +269,7 @@ public State getState(@Nullable LocalPlayer player, StateFlag... flags) {
|
||||
* (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 will check the global region
|
||||
* for a value as well as the flag's default value.
|
||||
* on the flag type.
|
||||
*
|
||||
* <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
|
||||
@ -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 flag the flag
|
||||
* @return a value, which could be {@code null}
|
||||
* @see #getSingleValue(LocalPlayer, Flag) does not check global region, defaults
|
||||
*/
|
||||
@Nullable
|
||||
public <V> V getSingleValueWithFallback(@Nullable LocalPlayer player, Flag<V> flag) {
|
||||
checkNotNull(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);
|
||||
public <V> V queryValue(@Nullable LocalPlayer player, Flag<V> flag) {
|
||||
Collection<V> values = queryAllValues(player, flag);
|
||||
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},
|
||||
* {@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);
|
||||
|
||||
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>();
|
||||
Set<ProtectedRegion> ignoredRegions = new HashSet<ProtectedRegion>();
|
||||
|
||||
for (ProtectedRegion region : applicable) {
|
||||
for (ProtectedRegion region : getApplicable()) {
|
||||
// Don't consider lower priorities below minimumPriority
|
||||
// (which starts at Integer.MIN_VALUE). A region that "counts"
|
||||
// (has the flag set) will raise minimumPriority to its own
|
||||
// priority.
|
||||
if (region.getPriority() < minimumPriority) {
|
||||
if (getPriority(region) < minimumPriority) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -454,7 +365,7 @@ public <V> Collection<V> getValues(@Nullable LocalPlayer player, Flag<V> flag) {
|
||||
|
||||
if (value != null) {
|
||||
if (!ignoredRegions.contains(region)) {
|
||||
minimumPriority = region.getPriority();
|
||||
minimumPriority = getPriority(region);
|
||||
|
||||
ignoreValuesOfParents(consideredValues, ignoredRegions, region);
|
||||
consideredValues.put(region, value);
|
||||
@ -471,6 +382,21 @@ public <V> Collection<V> getValues(@Nullable LocalPlayer player, Flag<V> flag) {
|
||||
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
|
||||
* 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
|
||||
* @return the value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
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;
|
||||
|
||||
while (current != null) {
|
||||
|
@ -51,24 +51,6 @@ public String getName() {
|
||||
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
|
||||
public T chooseValue(Collection<T> values) {
|
||||
if (!values.isEmpty()) {
|
||||
|
@ -38,7 +38,6 @@ public RegionGroupFlag(String name, RegionGroup def) {
|
||||
this.def = def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegionGroup getDefault() {
|
||||
return def;
|
||||
}
|
||||
|
@ -48,14 +48,8 @@ public StateFlag(String name, boolean def) {
|
||||
this.def = def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getDefault() {
|
||||
return def ? State.ALLOW : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State validateDefaultValue(State current) {
|
||||
return denyToNone(current);
|
||||
public boolean getDefault() {
|
||||
return def;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user