mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-10-07 02:57:28 +02:00
Rewrite state calculation in ApplicableRegionSet to be easier to read.
This commit is contained in:
parent
d3f3489c7e
commit
1d24be8c34
@ -41,6 +41,7 @@
|
|||||||
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.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the effective set of flags, owners, and members for a given
|
* Represents the effective set of flags, owners, and members for a given
|
||||||
@ -92,7 +93,7 @@ public ApplicableRegionSet(SortedSet<ProtectedRegion> applicable, @Nullable Prot
|
|||||||
*/
|
*/
|
||||||
public boolean canBuild(LocalPlayer player) {
|
public boolean canBuild(LocalPlayer player) {
|
||||||
checkNotNull(player);
|
checkNotNull(player);
|
||||||
return internalGetState(DefaultFlag.BUILD, player, null);
|
return test(calculateState(DefaultFlag.BUILD, player, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,7 +122,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 internalGetState(flag, null, null);
|
return test(calculateState(flag, null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,7 +139,7 @@ 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 internalGetState(flag, null, player);
|
return test(calculateState(flag, null, player));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,21 +179,126 @@ public boolean isMemberOfAll(LocalPlayer player) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether a flag tests 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 flag the flag to check
|
||||||
* @param player the player, or null to not check owners and members
|
* @param player the player, or null to not check owners and members
|
||||||
* @param groupPlayer a player to use for the group flag check
|
* @param groupPlayer a player to use for the group flag check
|
||||||
* @return the allow/deny state for the flag
|
* @return the allow/deny state for the flag
|
||||||
*/
|
*/
|
||||||
private boolean internalGetState(StateFlag flag, @Nullable LocalPlayer player, @Nullable LocalPlayer groupPlayer) {
|
private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Nullable LocalPlayer groupPlayer) {
|
||||||
checkNotNull(flag);
|
checkNotNull(flag);
|
||||||
|
|
||||||
boolean found = false;
|
int minimumPriority = Integer.MIN_VALUE;
|
||||||
boolean hasFlagDefined = false;
|
boolean regionsThatCountExistHere = false; // We can't do a application.isEmpty() because
|
||||||
boolean allowed = false; // Used for ALLOW override
|
// PASSTHROUGH regions have to be skipped
|
||||||
boolean def = flag.getDefault();
|
// (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:
|
||||||
|
// PARENT and its parents are added to needsClear.
|
||||||
|
// When the loop reaches CHILD, all parents of CHILD (including
|
||||||
|
// PARENT) are removed from needsClear. (Any parents not in
|
||||||
|
// needsClear are added to hasCleared for the 2nd scenario.)
|
||||||
|
//
|
||||||
|
// 2) CHILD first, PARENT later:
|
||||||
|
// CHILD's parents are added to hasCleared.
|
||||||
|
// When the loop reaches PARENT, since PARENT is already in
|
||||||
|
// hasCleared, it doe not add PARENT to needsClear.
|
||||||
|
//
|
||||||
|
// If there are any regions left over in needsClear, that means that
|
||||||
|
// there was at least one region that the player is not a member of
|
||||||
|
// (any of its children) and thus we can deny building.
|
||||||
|
|
||||||
|
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
|
||||||
|
// 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 (player != 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 (player != null) {
|
||||||
|
minimumPriority = region.getPriority();
|
||||||
|
|
||||||
|
if (!hasCleared.contains(region)) {
|
||||||
|
if (!region.isMember(player)) {
|
||||||
|
needsClear.add(region);
|
||||||
|
} else {
|
||||||
|
// Need to clear all parents
|
||||||
|
clearParents(needsClear, hasCleared, region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regionsThatCountExistHere) {
|
||||||
|
if (player != null) {
|
||||||
|
State membership = allowOrNone(needsClear.isEmpty());
|
||||||
|
return combine(state, membership);
|
||||||
|
} else {
|
||||||
|
return combine(state, getDefault(flag, null));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return combine(getDefault(flag, player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private State getDefault(StateFlag flag, @Nullable LocalPlayer player) {
|
||||||
|
boolean allowed = flag.getDefault();
|
||||||
|
|
||||||
// Handle defaults
|
// Handle defaults
|
||||||
if (globalRegion != null) {
|
if (globalRegion != null) {
|
||||||
State globalState = globalRegion.getFlag(flag);
|
State globalState = globalRegion.getFlag(flag);
|
||||||
@ -201,110 +307,19 @@ private boolean internalGetState(StateFlag flag, @Nullable LocalPlayer player, @
|
|||||||
if (globalState != null) {
|
if (globalState != null) {
|
||||||
// Build flag is very special
|
// Build flag is very special
|
||||||
if (player != null && globalRegion.hasMembersOrOwners()) {
|
if (player != null && globalRegion.hasMembersOrOwners()) {
|
||||||
def = globalRegion.isMember(player) && (globalState == State.ALLOW);
|
allowed = globalRegion.isMember(player) && (globalState == State.ALLOW);
|
||||||
} else {
|
} else {
|
||||||
def = (globalState == State.ALLOW);
|
allowed = (globalState == State.ALLOW);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Build flag is very special
|
// Build flag is very special
|
||||||
if (player != null && globalRegion.hasMembersOrOwners()) {
|
if (player != null && globalRegion.hasMembersOrOwners()) {
|
||||||
def = globalRegion.isMember(player);
|
allowed = globalRegion.isMember(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The player argument is used if and only if the flag is the build
|
|
||||||
// flag -- in which case, if there are any regions in this area, we
|
|
||||||
// default to FALSE, otherwise true if there are no defined regions.
|
|
||||||
// However, other flags are different -- if there are regions defined,
|
|
||||||
// we default to the global region value.
|
|
||||||
if (player == null) {
|
|
||||||
allowed = def;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lastPriority = Integer.MIN_VALUE;
|
|
||||||
|
|
||||||
// The algorithm is as follows:
|
return allowed ? State.ALLOW : null;
|
||||||
// While iterating through the list of regions, if an entry disallows
|
|
||||||
// the flag, then put it into the needsClear set. If an entry allows
|
|
||||||
// the flag and it has a parent, then its parent is put into hasCleared.
|
|
||||||
// In the situation that the child is reached before the parent, upon
|
|
||||||
// the parent being reached, even if the parent disallows, because the
|
|
||||||
// parent will be in hasCleared, permission will be allowed. In the
|
|
||||||
// other case, where the parent is reached first, if it does not allow
|
|
||||||
// permissions, it will be placed into needsClear. If a child of
|
|
||||||
// the parent is reached later, the parent will be removed from
|
|
||||||
// needsClear. At the end, if needsClear is not empty, that means that
|
|
||||||
// permission should not be given. If a parent has multiple children
|
|
||||||
// and one child does not allow permissions, then it will be placed into
|
|
||||||
// needsClear just like as if was a parent.
|
|
||||||
|
|
||||||
Set<ProtectedRegion> needsClear = new HashSet<ProtectedRegion>();
|
|
||||||
Set<ProtectedRegion> hasCleared = new HashSet<ProtectedRegion>();
|
|
||||||
|
|
||||||
for (ProtectedRegion region : applicable) {
|
|
||||||
// Ignore lower priority regions
|
|
||||||
if (hasFlagDefined && region.getPriority() < lastPriority) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastPriority = region.getPriority();
|
|
||||||
|
|
||||||
// Ignore non-build regions
|
|
||||||
if (player != null && getStateFlagIncludingParents(region, DefaultFlag.PASSTHROUGH) == State.ALLOW) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
State v = getStateFlagIncludingParents(region, flag);
|
|
||||||
|
|
||||||
// Allow DENY to override everything
|
|
||||||
if (v == State.DENY) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forget about regions that allow it, although make sure the
|
|
||||||
// default state is now to allow
|
|
||||||
if (v == State.ALLOW) {
|
|
||||||
allowed = true;
|
|
||||||
found = true;
|
|
||||||
hasFlagDefined = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For the build flag, the flags are conditional and are based
|
|
||||||
// on membership, so we have to check for parent-child
|
|
||||||
// relationships
|
|
||||||
if (player != null) {
|
|
||||||
hasFlagDefined = true;
|
|
||||||
|
|
||||||
//noinspection StatementWithEmptyBody
|
|
||||||
if (hasCleared.contains(region)) {
|
|
||||||
// Already cleared, so do nothing
|
|
||||||
} else {
|
|
||||||
if (!region.isMember(player)) {
|
|
||||||
needsClear.add(region);
|
|
||||||
} else {
|
|
||||||
// Need to clear all parents
|
|
||||||
clearParents(needsClear, hasCleared, region);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !found ? def : (allowed || (player != null && needsClear.isEmpty()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author sk89q
|
* @author sk89q
|
||||||
@ -89,4 +91,59 @@ public Object marshal(State o) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether at least one of the given states is {@code ALLOW}
|
||||||
|
* but none are set to {@code DENY}.
|
||||||
|
*
|
||||||
|
* @param states zero or more states
|
||||||
|
* @return true if the condition is matched
|
||||||
|
*/
|
||||||
|
public static boolean test(State... states) {
|
||||||
|
boolean allowed = false;
|
||||||
|
|
||||||
|
for (State state : states) {
|
||||||
|
if (state == State.DENY) {
|
||||||
|
return false;
|
||||||
|
} else if (state == State.ALLOW) {
|
||||||
|
allowed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combine states, letting {@code DENY} override {@code ALLOW} and
|
||||||
|
* {@code ALLOW} override {@code NONE} (or null).
|
||||||
|
*
|
||||||
|
* @param states zero or more states
|
||||||
|
* @return the new state
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static State combine(State... states) {
|
||||||
|
boolean allowed = false;
|
||||||
|
|
||||||
|
for (State state : states) {
|
||||||
|
if (state == State.DENY) {
|
||||||
|
return State.DENY;
|
||||||
|
} else if (state == State.ALLOW) {
|
||||||
|
allowed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowed ? State.ALLOW : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn a boolean into either {@code NONE} (null) or {@code ALLOW} if
|
||||||
|
* the boolean is false or true, respectively.
|
||||||
|
*
|
||||||
|
* @param flag a boolean value
|
||||||
|
* @return a state
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static State allowOrNone(boolean flag) {
|
||||||
|
return flag ? State.ALLOW : null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user