diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index 8a7a9dc2..67b10200 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -23,6 +23,8 @@ import com.sk89q.worldguard.protection.association.RegionAssociable; import com.sk89q.worldguard.protection.flags.Flags; import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.MapFlag; +import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.flags.StateFlag.State; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -119,6 +121,28 @@ public interface ApplicableRegionSet extends Iterable { @Nullable V queryValue(@Nullable RegionAssociable subject, Flag flag); + /** + * Get the effective value for a key in a {@link MapFlag}. 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 be undefined. + * + *

A subject 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 + * subject is not a member, then the region would be skipped when + * querying that flag. If {@code null} is provided for the subject, then + * only flags that use {@link RegionGroup#ALL}, + * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

+ * + * @param subject an optional subject, which would be used to determine the region group to apply + * @param flag the flag of type {@link MapFlag} + * @param key the key for the map flag + * @return a value, which could be {@code null} + */ + @Nullable + V queryMapValue(@Nullable RegionAssociable subject, MapFlag flag, K key); + /** * Get the effective values for a flag, returning a collection of all * values. It is up to the caller to determine which value, if any, diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/FailedLoadRegionSet.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/FailedLoadRegionSet.java index 452c516a..cef3f409 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/FailedLoadRegionSet.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/FailedLoadRegionSet.java @@ -24,6 +24,7 @@ import com.sk89q.worldguard.protection.association.RegionAssociable; import com.sk89q.worldguard.protection.flags.Flags; import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.MapFlag; import com.sk89q.worldguard.protection.flags.StateFlag.State; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -31,6 +32,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.Map; import java.util.Set; /** @@ -65,6 +67,13 @@ public V queryValue(@Nullable RegionAssociable subject, Flag flag) { return flag.getDefault(); } + @Nullable + @Override + public V queryMapValue(@Nullable RegionAssociable subject, MapFlag flag, K key) { + Map defaultVal = flag.getDefault(); + return defaultVal != null ? defaultVal.get(key) : null; + } + @SuppressWarnings("unchecked") @Override public Collection queryAllValues(@Nullable RegionAssociable subject, Flag flag) { diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java index 48c41316..caabb79e 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java @@ -26,6 +26,7 @@ import com.sk89q.worldguard.protection.association.RegionAssociable; import com.sk89q.worldguard.protection.flags.Flags; import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.MapFlag; import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.flags.StateFlag.State; @@ -224,6 +225,93 @@ public V queryValue(@Nullable RegionAssociable subject, Flag flag) { return flag.chooseValue(values); } + /** + * Get the effective value for a key in a {@link MapFlag}. 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 be undefined. + * + *

A subject 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 + * subject is not a member, then the region would be skipped when + * querying that flag. If {@code null} is provided for the subject, then + * only flags that use {@link RegionGroup#ALL}, + * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

+ * + * @param subject an optional subject, which would be used to determine the region group to apply + * @param flag the flag of type {@link MapFlag} + * @param key the key for the map flag + * @return a value, which could be {@code null} + */ + @Nullable + public V queryMapValue(@Nullable RegionAssociable subject, MapFlag flag, T key) { + checkNotNull(flag); + checkNotNull(key); + + V value = null; + int minimumPriority = Integer.MIN_VALUE; + Set ignoredParents = new HashSet<>(); + + for(ProtectedRegion region : getApplicable()) { + if (getPriority(region) < minimumPriority) { + break; + } + + if (ignoredParents.contains(region)) { + continue; + } + + V effectiveValue = getEffectiveMapValue(region, flag, key, subject); + + if (effectiveValue != null) { + minimumPriority = getPriority(region); + value = effectiveValue; + } + + addParents(ignoredParents, region); + } + return value; + } + + @Nullable + private V getEffectiveMapValue(ProtectedRegion region, MapFlag mapFlag, T key, RegionAssociable subject) { + List seen = new ArrayList<>(); + ProtectedRegion current = region; + + while (current != null) { + seen.add(current); + + Map mapValue = current.getFlag(mapFlag); + + if (mapValue != null && mapValue.containsKey(key)) { + boolean use = true; + + if (mapFlag.getRegionGroupFlag() != null) { + RegionGroup group = current.getFlag(mapFlag.getRegionGroupFlag()); + if (group == null) { + group = mapFlag.getRegionGroupFlag().getDefault(); + } + + if (group == null) { + use = false; + } else if (subject == null) { + use = group.contains(Association.NON_MEMBER); + } else if (!group.contains(subject.getAssociation(seen))) { + use = false; + } + } + + if (use) { + return mapValue.get(key); + } + } + + current = current.getParent(); + } + return null; + } + /** * Get the effective values for a flag, returning a collection of all * values. It is up to the caller to determine which value, if any, diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/PermissiveRegionSet.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/PermissiveRegionSet.java index 91dccc05..0ce29571 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/PermissiveRegionSet.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/PermissiveRegionSet.java @@ -24,6 +24,7 @@ import com.sk89q.worldguard.protection.association.RegionAssociable; import com.sk89q.worldguard.protection.flags.Flags; import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.MapFlag; import com.sk89q.worldguard.protection.flags.StateFlag.State; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -31,6 +32,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.Map; import java.util.Set; /** @@ -59,6 +61,13 @@ public V queryValue(@Nullable RegionAssociable subject, Flag flag) { return flag.getDefault(); } + @Nullable + @Override + public V queryMapValue(@Nullable RegionAssociable subject, MapFlag flag, K key) { + Map defaultVal = flag.getDefault(); + return defaultVal != null ? defaultVal.get(key) : null; + } + @SuppressWarnings("unchecked") @Override public Collection queryAllValues(@Nullable RegionAssociable subject, Flag flag) { diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/RegionResultSet.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/RegionResultSet.java index ae868295..967d82cb 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/RegionResultSet.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/RegionResultSet.java @@ -22,6 +22,7 @@ import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.protection.association.RegionAssociable; import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.MapFlag; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.flags.StateFlag.State; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -108,6 +109,12 @@ public Collection queryAllValues(@Nullable RegionAssociable subject, Flag return flagValueCalculator.queryAllValues(subject, flag); } + @Override + @Nullable + public V queryMapValue(@Nullable RegionAssociable subject, MapFlag flag, K key) { + return flagValueCalculator.queryMapValue(subject, flag, key); + } + @Override public boolean isOwnerOfAll(LocalPlayer player) { checkNotNull(player); diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/regions/RegionQuery.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/regions/RegionQuery.java index 59adb0c8..a3f24492 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/regions/RegionQuery.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/regions/RegionQuery.java @@ -34,6 +34,8 @@ import com.sk89q.worldguard.protection.association.RegionAssociable; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.Flags; +import com.sk89q.worldguard.protection.flags.MapFlag; +import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.flags.StateFlag.State; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -319,6 +321,30 @@ public V queryValue(Location location, @Nullable RegionAssociable associable return getApplicableRegions(location).queryValue(associable, flag); } + /** + * Get the effective value for a key in a {@link MapFlag}. 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 be undefined. + * + *

A subject 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 + * subject is not a member, then the region would be skipped when + * querying that flag. If {@code null} is provided for the subject, then + * only flags that use {@link RegionGroup#ALL}, + * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

+ * + * @param subject an optional subject, which would be used to determine the region group to apply + * @param flag the flag of type {@link MapFlag} + * @param key the key for the map flag + * @return a value, which could be {@code null} + */ + @Nullable + public V queryMapValue(Location location, @Nullable RegionAssociable subject, MapFlag flag, K key) { + return getApplicableRegions(location).queryMapValue(subject, flag, key); + } + /** * Get the effective values for a flag, returning a collection of all * values. It is up to the caller to determine which value, if any,