Flag Calculation¶
To check flags, an instance of ApplicableRegionSet
(which contains a list of regions) must have been retrieved. That is documented in Spatial Queries.
Querying Flags¶
One issue with query flags given a list of regions is that there may be several regions with the same flag assigned (and with different values). If priorities and child inheritance are properly configured, then there might only be one “effective” value, but otherwise there will be several.
Getting All Values¶
queryAllValues(RegionAssociable, Flag)
can be used to get all the values that have been set for a flag. Flags can be obtained from Flags
.
Example: Getting the greeting message flag, given a player
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
Collection<String> greetings = set.queryAllValues(localPlayer, Flags.GREET_MESSAGE);
Getting One Value¶
queryValue(RegionAssociable, Flag)
can be used to get one value. Depending on the flag type, this may be the first value found, or it may be a “best” value. As of writing, only StateFlags
will actually pick a “best” value.
Example: Getting the greeting message flag, given a player
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
String greeting = set.queryValue(localPlayer, Flags.GREET_MESSAGE);
The returned value may be null
if the flag is not set on any regions.
Getting StateFlag Values¶
If you are trying to query a StateFlag
(a flag with allow/deny), there are additional methods that you can use. They allow you to specify multiple state flags for automatic combination (remember, “deny” overrides “allow”).
queryState(RegionAssociable, StateFlag...)
takes one or more flags and returns an allow, deny, or nulltestState(RegionAssociable, StateFlag...)
does the same thing, but returns true if it’s allow
You can still use queryValue
, but you can only specify one flag at a time.
Example: Testing the build flag
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
if (!set.testState(localPlayer, Flags.BUILD)) {
event.setCancelled(true);
}
Flags With No Player¶
If you are trying to lookup a flag that doesn’t use a player (for example, the creeper-explosion
flag), then you can use null
for the RegionAssociable
.
Example: Testing the creeper explosion flag
if (!set.testState(null, Flags.CREEPER_EXPLOSION)) {
event.setCancelled(true);
}
Hint
You can also pass in null
for flags that do use a player, but then flag region groups will not work correctly.
Also via RegionQuery¶
The methods described on this page are also conveniently available directly on instances of RegionQuery
.
Example: Using a RegionQuery
to directly query flags
LocalPlayer localPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
Location loc = new Location(world, 10, 64, 100);
RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
RegionQuery query = container.createQuery();
// No need to bother:
// ApplicableRegionSet set = query.getApplicableRegions(loc);
// Just directly test the flag
query.testState(loc, localPlayer, Flags.BUILD);
In addition, you can use testBuild
and so on as a shortcut to testState(..., Flags.BUILD, your flags)
.
Non-Player Actors¶
Instead of passing in a player, you can instead pass in a (non-LocalPlayer
) RegionAssociable
. This object is used to determine whether to use rules for owners, members, or non-members should be used.
However, let’s first consider what happens with players. Given a player part of the build team, who has been made an owner of both spawn’s region and the “builder’s club,” the association returned should be OWNER
, as illustrated below:
List<ProtectedRegion> regions = Arrays.asList(spawnRegion, buildersClub);
builderPlayer.getAssociation(regions) == Association.OWNER;
As you may be aware, you cannot add entities or blocks as members to a region, so it can’t work the same way. To do that, a special RegionAssociable
is used for blocks and entities: it takes a list of source regions to determine whether the source regions should be considered a “member” of the target location. This is illustrated below.
Set deepInside = newHashSet(spawn, mall);
Set inside = newHashSet(spawn);
Set outside = newHashSet(); // Empty set
// outside -> inside = considered as "non-member"
new RegionOverlapAssociation(outside).getAssociation(inside) == NON_MEMBER
// inside -> inside = considered as "member"
new RegionOverlapAssociation(inside).getAssociation(inside) == OWNER
// inside -> deepInside = considered as "member"
// Note that by default building is blocked in this case.
// The association must be at least "member" for all regions individually.
new RegionOverlapAssociation(inside).getAssociation(deepInside) == OWNER
// inside -> outside = considered as "non-member"
new RegionOverlapAssociation(inside).getAssociation(outside) == NON_MEMBER
// deepInside -> inside = considered as "member"
new RegionOverlapAssociation(deepInside).getAssociation(inside) == OWNER
Note that the nonplayer-protection-domains
flag and region inheritance can override this behavior. The various test...
and query...
methods will handle this for you.
To summarize:
- Player (
LocalPlayer
) objects already implementRegionAssociable
- For entities and blocks, WorldGuard uses the regions where the block or entity is (
RegionOverlapAssociation
)
There is also:
ConstantAssociation
uses a pre-set type of association (new ConstantAssociation(Association.MEMBER)
orAssociables.constant(Association.MEMBER)
)DelayedRegionOverlapAssociation
which works likeRegionOverlapAssociation
, but doesn’t do the spatial query for regions at the source until it is needed
Example: Examining how WorldGuard handles region protection
First, the correct RegionAssociation
must be created for the event. createRegionAssociable()
described below takes an object and returns a RegionAssociable
.
private RegionAssociable createRegionAssociable(Object cause) {
if (cause instanceof Player) {
return WorldGuardPlugin.inst().wrapPlayer((Player) cause);
} else if (cause instanceof Entity entity) {
RegionQuery query = WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery();
WorldConfiguration config = WorldGuard.getInstance().getPlatform().getGlobalStateManager().get(BukkitAdapter.adapt(entity.getWorld()));
Location loc = entity.getLocation(); // getOrigin() can be used on Paper if present
return new DelayedRegionOverlapAssociation(query, BukkitAdapter.adapt(loc), config.useMaxPriorityAssociation);
} else if (cause instanceof Block block) {
RegionQuery query = WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery();
WorldConfiguration config = WorldGuard.getInstance().getPlatform().getGlobalStateManager().get(BukkitAdapter.adapt(block.getWorld()));
Location loc = block.getLocation();
return new DelayedRegionOverlapAssociation(query, BukkitAdapter.adapt(loc), config.useMaxPriorityAssociation);
} else {
return Associables.constant(Association.NON_MEMBER);
}
}
Let’s see where it could be used:
@EventHandler
public void onPlayerBucketFill(PlayerBucketFillEvent event) {
Player player = event.getPlayer();
RegionAssociable associable = createRegionAssociable(player);
if (!set.testState(associable, /* flags here */)) {
event.setCancelled(true);
}
}