diff --git a/build.gradle b/build.gradle index e50bff35..c281054b 100644 --- a/build.gradle +++ b/build.gradle @@ -76,6 +76,7 @@ allprojects { maven { url 'https://api.modrinth.com/maven/' } maven { url 'https://repo.extendedclip.com/content/repositories/placeholderapi/' } maven { url 'https://jitpack.io' } + maven { url "https://maven.enginehub.org/repo/" } } dependencies { diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 94398484..de0accfb 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -16,12 +16,13 @@ dependencies { compileOnly 'org.jetbrains:annotations:24.1.0' compileOnly 'net.william278:DesertWell:2.0.4' compileOnly 'org.projectlombok:lombok:1.18.32' + compileOnly 'it.unimi.dsi:fastutil:8.5.13' compileOnly 'net.william278.huskhomes:huskhomes-bukkit:4.6.1' compileOnly 'net.william278.husktowns:husktowns-bukkit:3.0.4' compileOnly 'com.github.MilkBowl:VaultAPI:1.7.1' compileOnly 'me.clip:placeholderapi:2.11.6' - compileOnly 'it.unimi.dsi:fastutil:8.5.13' + compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT' annotationProcessor 'org.projectlombok:lombok:1.18.32' } diff --git a/bukkit/src/main/java/net/william278/huskclaims/hook/BukkitHookProvider.java b/bukkit/src/main/java/net/william278/huskclaims/hook/BukkitHookProvider.java index cdd2601c..ca1b087e 100644 --- a/bukkit/src/main/java/net/william278/huskclaims/hook/BukkitHookProvider.java +++ b/bukkit/src/main/java/net/william278/huskclaims/hook/BukkitHookProvider.java @@ -46,6 +46,9 @@ default List getAvailableHooks() { if (isDependencyAvailable("PlaceholderAPI") && settings.getPlaceholders().isEnabled()) { hooks.add(new BukkitPlaceholderAPIHook(getPlugin())); } + if (isDependencyAvailable("WorldGuard") && settings.getWorldGuard().isEnabled()) { + hooks.add(new BukkitWorldGuardHook(getPlugin())); + } // Add bukkit importers hooks.add(new BukkitGriefPreventionImporter(getPlugin())); @@ -55,7 +58,7 @@ default List getAvailableHooks() { @Override default boolean isDependencyAvailable(@NotNull String name) { - return getPlugin().getServer().getPluginManager().isPluginEnabled(name); + return getPlugin().getServer().getPluginManager().getPlugin(name) != null; } @NotNull diff --git a/bukkit/src/main/java/net/william278/huskclaims/hook/BukkitWorldGuardHook.java b/bukkit/src/main/java/net/william278/huskclaims/hook/BukkitWorldGuardHook.java new file mode 100644 index 00000000..96c6f31e --- /dev/null +++ b/bukkit/src/main/java/net/william278/huskclaims/hook/BukkitWorldGuardHook.java @@ -0,0 +1,79 @@ +/* + * This file is part of HuskClaims, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.william278.huskclaims.hook; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import net.william278.huskclaims.BukkitHuskClaims; +import net.william278.huskclaims.HuskClaims; +import net.william278.huskclaims.claim.Region; +import net.william278.huskclaims.position.World; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +@PluginHook( + name = "WorldGuard", + register = PluginHook.Register.ON_LOAD +) +public class BukkitWorldGuardHook extends WorldGuardHook { + + public BukkitWorldGuardHook(@NotNull HuskClaims plugin) { + super(plugin); + } + + public boolean isRestricted(@NotNull Region region, @NotNull World world) { + final org.bukkit.World bukkitWorld = BukkitHuskClaims.Adapter.adapt(world); + final Optional regionManager = getRegionManager(bukkitWorld); + if (regionManager.isEmpty()) { + return false; + } + final ApplicableRegionSet set = getOverlappingRegions(region, regionManager.get(), bukkitWorld); + for (ProtectedRegion protectedRegion : set.getRegions()) { + return protectedRegion.getFlag(CLAIMING) == StateFlag.State.DENY; + } + return false; + } + + @NotNull + private static ApplicableRegionSet getOverlappingRegions(@NotNull Region region, @NotNull RegionManager manager, + @NotNull org.bukkit.World bukkitWorld) { + final Region.Point minCorner = region.getNearCorner(); + final Region.Point maxCorner = region.getFarCorner(); + return manager.getApplicableRegions(new ProtectedCuboidRegion( + "dummy", + BlockVector3.at(minCorner.getBlockX(), bukkitWorld.getMinHeight(), minCorner.getBlockZ()), + BlockVector3.at(maxCorner.getBlockX(), bukkitWorld.getMaxHeight(), maxCorner.getBlockZ()) + )); + } + + private Optional getRegionManager(@NotNull org.bukkit.World world) { + return Optional.ofNullable( + WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(world)) + ); + } + +} diff --git a/bukkit/src/main/java/net/william278/huskclaims/user/BukkitUser.java b/bukkit/src/main/java/net/william278/huskclaims/user/BukkitUser.java index 263c8146..90e89e74 100644 --- a/bukkit/src/main/java/net/william278/huskclaims/user/BukkitUser.java +++ b/bukkit/src/main/java/net/william278/huskclaims/user/BukkitUser.java @@ -32,9 +32,9 @@ import org.bukkit.permissions.PermissionDefault; import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.List; @Getter public class BukkitUser extends OnlineUser { diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index ef76f6ae..992afbd5 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -17,6 +17,7 @@ softdepend: - 'Vault' - 'Plan' - 'PlaceholderAPI' + - 'WorldGuard' - 'Pl3xMap' - 'BlueMap' - 'dynmap' \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle index 8603a159..7581ac02 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -30,6 +30,7 @@ dependencies { compileOnly 'us.dynmap:DynmapCoreAPI:3.6' compileOnly 'maven.modrinth:pl3xmap:1.20.4-492' compileOnly 'com.github.plan-player-analytics:Plan:5.6.2883' + compileOnly 'com.sk89q.worldguard:worldguard-core:7.1.0-SNAPSHOT' testImplementation 'com.github.plan-player-analytics:Plan:5.6.2883' testImplementation 'com.google.guava:guava:33.2.1-jre' diff --git a/common/src/main/java/net/william278/huskclaims/claim/ClaimEditor.java b/common/src/main/java/net/william278/huskclaims/claim/ClaimEditor.java index f006e2b6..eca82078 100644 --- a/common/src/main/java/net/william278/huskclaims/claim/ClaimEditor.java +++ b/common/src/main/java/net/william278/huskclaims/claim/ClaimEditor.java @@ -129,6 +129,7 @@ default void userResizeClaim(@NotNull OnlineUser user, @NotNull ClaimWorld world default void userResizeClaim(@NotNull OnlineUser user, @NotNull ClaimWorld world, @NotNull Claim claim, @NotNull Region resized) { + // Check the area doesn't overlap with another claim, excluding the original claim final List overlapsWith = world.getParentClaimsOverlapping(resized, claim.getRegion()); if (!overlapsWith.isEmpty()) { getPlugin().getLocales().getLocale("land_selection_overlaps", Integer.toString(overlapsWith.size())) @@ -137,6 +138,13 @@ default void userResizeClaim(@NotNull OnlineUser user, @NotNull ClaimWorld world return; } + // Check the new area isn't restricted by another plugin + if (getPlugin().getRestrictedRegionHooks().stream().anyMatch(h -> h.isRestricted(resized, user.getWorld()))) { + getPlugin().getLocales().getLocale("land_selection_restricted") + .ifPresent(user::sendMessage); + return; + } + // Check all existing child claims still fit within the resized claim if (!claim.getChildren().stream().map(Claim::getRegion).allMatch(resized::fullyEncloses)) { getPlugin().getLocales().getLocale("selection_resize_not_enclosing_children") @@ -289,6 +297,7 @@ default void userDeleteClaim(@NotNull OnlineUser executor, @NotNull ClaimWorld w } private boolean doesClaimOverlap(@NotNull OnlineUser user, @NotNull ClaimWorld world, @NotNull Region region) { + // Check the area doesn't overlap with another claim final List overlapsWith = world.getParentClaimsOverlapping(region); if (!overlapsWith.isEmpty()) { getPlugin().getLocales().getLocale("land_selection_overlaps", Integer.toString(overlapsWith.size())) @@ -296,6 +305,13 @@ private boolean doesClaimOverlap(@NotNull OnlineUser user, @NotNull ClaimWorld w getPlugin().getHighlighter().startHighlighting(user, user.getWorld(), overlapsWith, true); return true; } + + // Check the area isn't restricted by another plugin + if (getPlugin().getRestrictedRegionHooks().stream().anyMatch(h -> h.isRestricted(region, user.getWorld()))) { + getPlugin().getLocales().getLocale("land_selection_restricted") + .ifPresent(user::sendMessage); + return true; + } return false; } diff --git a/common/src/main/java/net/william278/huskclaims/config/Settings.java b/common/src/main/java/net/william278/huskclaims/config/Settings.java index 48c9214d..3c85b8e5 100644 --- a/common/src/main/java/net/william278/huskclaims/config/Settings.java +++ b/common/src/main/java/net/william278/huskclaims/config/Settings.java @@ -599,6 +599,16 @@ public static class PlaceholderSettings { private boolean enabled = true; } + private WorldGuardSettings worldGuard = new WorldGuardSettings(); + + @Getter + @Configuration + @NoArgsConstructor(access = AccessLevel.PRIVATE) + public static class WorldGuardSettings { + @Comment("Whether to hook into WorldGuard to provide a flag to deny claiming in WorldGuard regions") + private boolean enabled = true; + } + private MapHookSettings map = new MapHookSettings(); @Getter diff --git a/common/src/main/java/net/william278/huskclaims/hook/HookProvider.java b/common/src/main/java/net/william278/huskclaims/hook/HookProvider.java index 9bb7ce9b..66c2f83d 100644 --- a/common/src/main/java/net/william278/huskclaims/hook/HookProvider.java +++ b/common/src/main/java/net/william278/huskclaims/hook/HookProvider.java @@ -42,6 +42,14 @@ default Optional getHook(@NotNull Class hookClass) { .findFirst(); } + @NotNull + default List getRestrictedRegionHooks() { + return getHooks().stream() + .filter(hook -> hook instanceof RestrictedRegionHook) + .map(hook -> (RestrictedRegionHook) hook) + .toList(); + } + @NotNull @Unmodifiable default Set getImporters() { diff --git a/common/src/main/java/net/william278/huskclaims/hook/RestrictedRegionHook.java b/common/src/main/java/net/william278/huskclaims/hook/RestrictedRegionHook.java new file mode 100644 index 00000000..4f7ea4aa --- /dev/null +++ b/common/src/main/java/net/william278/huskclaims/hook/RestrictedRegionHook.java @@ -0,0 +1,35 @@ +/* + * This file is part of HuskClaims, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.william278.huskclaims.hook; + +import net.william278.huskclaims.HuskClaims; +import net.william278.huskclaims.claim.Region; +import net.william278.huskclaims.position.World; +import org.jetbrains.annotations.NotNull; + +public abstract class RestrictedRegionHook extends Hook { + + protected RestrictedRegionHook(@NotNull HuskClaims plugin) { + super(plugin); + } + + public abstract boolean isRestricted(@NotNull Region region, @NotNull World world); + +} diff --git a/common/src/main/java/net/william278/huskclaims/hook/WorldGuardHook.java b/common/src/main/java/net/william278/huskclaims/hook/WorldGuardHook.java new file mode 100644 index 00000000..2f764e35 --- /dev/null +++ b/common/src/main/java/net/william278/huskclaims/hook/WorldGuardHook.java @@ -0,0 +1,44 @@ +/* + * This file is part of HuskClaims, licensed under the Apache License 2.0. + * + * Copyright (c) William278 + * Copyright (c) contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.william278.huskclaims.hook; + +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.flags.StateFlag; +import net.william278.huskclaims.HuskClaims; +import org.jetbrains.annotations.NotNull; + +public abstract class WorldGuardHook extends RestrictedRegionHook { + + public final static StateFlag CLAIMING = new StateFlag("huskclaims-claim", false); + + public WorldGuardHook(@NotNull HuskClaims plugin) { + super(plugin); + } + + @Override + public void load() { + WorldGuard.getInstance().getFlagRegistry().register(CLAIMING); + } + + @Override + public void unload() { + } + +} diff --git a/common/src/main/resources/locales/en-gb.yml b/common/src/main/resources/locales/en-gb.yml index 7874b0a0..f3e7b828 100644 --- a/common/src/main/resources/locales/en-gb.yml +++ b/common/src/main/resources/locales/en-gb.yml @@ -47,6 +47,7 @@ locales: land_selection_overlaps: '[This land selection overlaps %1% existing claim(s).](#ff7e5e)' land_selection_overlaps_child: '[This land selection overlaps with %1% other child claim(s).](#ff7e5e)' land_already_child_claim: '[This area of the claim is already designated as a child claim.](#ff7e5e)' + land_selection_restricted: '[This land selection contains a restricted region.](#ff7e5e)' husktowns_claim_overlaps: '[Cannot claim here as it overlaps with a player claim.](#ff7e5e)' claim_tool_required: '[You need to be holding the claiming tool to do that.](#ff7e5e)' selection_resize_not_enclosing_children: '[This selection resize would not enclose all its child claims.](#ff7e5e)' diff --git a/common/src/main/resources/locales/ro-ro.yml b/common/src/main/resources/locales/ro-ro.yml index 2e4c5a0d..6e02206e 100644 --- a/common/src/main/resources/locales/ro-ro.yml +++ b/common/src/main/resources/locales/ro-ro.yml @@ -47,6 +47,7 @@ locales: land_selection_overlaps: '[Această selecție de teren se suprapune cu %1% revendicări existente.](#ff7e5e)' land_selection_overlaps_child: '[Această selecție de teren se suprapune cu %1% alte revendicări fiu.](#ff7e5e)' land_already_child_claim: '[Această zonă a revendicării este deja desemnată ca o revendicare fiu.](#ff7e5e)' + land_selection_restricted: '[This land selection contains a restricted region.](#ff7e5e)' husktowns_claim_overlaps: '[Nu se poate revendica aici deoarece se suprapune cu revendicarea unui jucător.](#ff7e5e)' claim_tool_required: '[Trebuie să ții unealta pentru a putea face asta.](#ff7e5e)' selection_resize_not_enclosing_children: '[Această redimensionare a selecției nu ar cuprinde toate revendicările sale fiu.](#ff7e5e)' diff --git a/common/src/main/resources/locales/ru-ru.yml b/common/src/main/resources/locales/ru-ru.yml index 0b9048c7..16aedc39 100644 --- a/common/src/main/resources/locales/ru-ru.yml +++ b/common/src/main/resources/locales/ru-ru.yml @@ -47,6 +47,7 @@ locales: land_selection_overlaps: '[Выделенная территория пересекается с %1% уже существующим(и) регионом(ами).](#ff7e5e)' land_selection_overlaps_child: '[Выбранная территория пересекается с %1% уже существующими потомственным(и) регионом(ами).](#ff7e5e)' land_already_child_claim: '[Выделенная территория уже является потомственным регионом.](#ff7e5e)' + land_selection_restricted: '[This land selection contains a restricted region.](#ff7e5e)' husktowns_claim_overlaps: '[Cannot claim here as it overlaps with a player claim.](#ff7e5e)' claim_tool_required: '[Для совершении операции необходимо держать в руке соответствующий инструмент.](#ff7e5e)' selection_resize_not_enclosing_children: '[Выбранное масштабирование региона не включает в себя всех его потомков.](#ff7e5e)' diff --git a/common/src/main/resources/locales/zh-cn.yml b/common/src/main/resources/locales/zh-cn.yml index b6a77c6e..2d601b45 100644 --- a/common/src/main/resources/locales/zh-cn.yml +++ b/common/src/main/resources/locales/zh-cn.yml @@ -47,6 +47,7 @@ locales: land_selection_overlaps: '[此领地选择与%1%现有领地重叠](#ff7e5e)' land_selection_overlaps_child: '[此领地选择与%1%其他子领地重叠](#ff7e5e)' land_already_child_claim: '[此领地的此区域已被指定为子领地](#ff7e5e)' + land_selection_restricted: '[This land selection contains a restricted region.](#ff7e5e)' husktowns_claim_overlaps: '[无法在此处声明因为它与玩家领地重叠](#ff7e5e)' claim_tool_required: '[您需要持有领地工具才能执行此操作](#ff7e5e)' selection_resize_not_enclosing_children: '[此选择调整大小不会包含所有子领地](#ff7e5e)' diff --git a/docs/Hooks.md b/docs/Hooks.md index 3dce7e9e..9014a5de 100644 --- a/docs/Hooks.md +++ b/docs/Hooks.md @@ -1,16 +1,17 @@ HuskClaims offers several built-in hooks providing support for other plugins. These hooks can be enabled or disabled in the `hooks` section of the plugin [[config]]. -| Name | Description | Link | -|------------------------------------|-------------------------------------|---------------------------------------------------| -| [Vault](#vault) | Economy support for claim blocks | https://www.spigotmc.org/resources/vault.34315/ | -| [LuckPerms](#luckperms) | Trust tags for LuckPerms groups | https://luckperms.net/ | -| [HuskHomes](#huskhomes) | Restricting home creation in claims | https://william278.net/project/huskhomes/ | -| [HuskTowns](#husktowns) | Prevent claiming over town claims | https://william278.net/project/husktowns/ | -| [Plan](#plan) | Display claim analytics in Plan | https://www.playeranalytics.net/ | -| [PlaceholderAPI](#placeholderapi) | Provides HuskClaims placeholders | https://placeholderapi.com/ | -| [Dynmap](#dynmap-pl3xmap-bluemap) | Add claim markers to your Dynmap | https://www.spigotmc.org/resources/dynmap.274/ | -| [Pl3xMap](#dynmap-pl3xmap-bluemap) | Add claim markers to your Pl3xMap | https://modrinth.com/plugin/pl3xmap/ | -| [BlueMap](#dynmap-pl3xmap-bluemap) | Add claim markers to your BlueMap | https://www.spigotmc.org/resources/bluemap.83557/ | +| Name | Description | Link | +|------------------------------------|-----------------------------------------------|---------------------------------------------------| +| [Vault](#vault) | Economy support for claim blocks | https://www.spigotmc.org/resources/vault.34315/ | +| [LuckPerms](#luckperms) | Trust tags for LuckPerms groups | https://luckperms.net/ | +| [HuskHomes](#huskhomes) | Restricting home creation in claims | https://william278.net/project/huskhomes/ | +| [HuskTowns](#husktowns) | Prevent claiming over town claims | https://william278.net/project/husktowns/ | +| [Plan](#plan) | Display claim analytics in Plan | https://www.playeranalytics.net/ | +| [PlaceholderAPI](#placeholderapi) | Provides HuskClaims placeholders | https://placeholderapi.com/ | +| [WorldGuard](#worldguard) | Prevent claiming over flag-restricted regions | https://enginehub.org/worldguard | +| [Dynmap](#dynmap-pl3xmap-bluemap) | Add claim markers to your Dynmap | https://www.spigotmc.org/resources/dynmap.274/ | +| [Pl3xMap](#dynmap-pl3xmap-bluemap) | Add claim markers to your Pl3xMap | https://modrinth.com/plugin/pl3xmap/ | +| [BlueMap](#dynmap-pl3xmap-bluemap) | Add claim markers to your BlueMap | https://www.spigotmc.org/resources/bluemap.83557/ | ## Vault If Vault (and a compatible economy plugin) is installed, the `/buyclaimblocks` command will be enabled allowing users to [purchase claim blocks for money](claim-blocks#buying-claim-blocks). @@ -43,6 +44,9 @@ If PlaceholderAPI is installed, HuskClaims will register a PlaceholderAPI expans | `%huskclaims_can_open_containers%` | Whether the player can open containers in the claim they are standing in | `true` or `false` | | `%huskclaims_can_interact%` | Whether the player can interact in the claim they are standing in | `true` or `false` | +## WorldGuard +If WorldGuard is installed, HuskClaims will register a third party flag (`huskclaims-claim`), which when set to "Deny" in a WorldGuard region will prevent players from creating or resizing a claim over that region. + ## Dynmap, Pl3xMap, BlueMap If one of the supported mapping plugins is installed, HuskClaims will add region markers for claims on your server map: diff --git a/paper/build.gradle b/paper/build.gradle index 9e3de2d7..e6c354c5 100644 --- a/paper/build.gradle +++ b/paper/build.gradle @@ -51,6 +51,8 @@ tasks { downloadPlugins { url("https://ci.lucko.me/job/LuckPerms/lastBuild/artifact/bukkit/loader/build/libs/LuckPerms-Bukkit-5.4.134.jar") url("https://ci.lucko.me/job/spark/lastBuild/artifact/spark-bukkit/build/libs/spark-1.10.73-bukkit.jar") + url("https://ci.enginehub.org/repository/download/bt11/24401:id/worldguard-bukkit-7.0.11-SNAPSHOT-dist.jar?branch=feat/mc-1.21&guest=1") + url("https://ci.enginehub.org/repository/download/bt10/24386:id/worldedit-bukkit-7.4.0-SNAPSHOT-dist.jar?branch=master&guest=1") hangar("Plan-Player-Analytics", "5.6+build.2883") modrinth("huskhomes", "4.7") diff --git a/paper/src/main/resources/paper-plugin.yml b/paper/src/main/resources/paper-plugin.yml index c88f388f..6c146285 100644 --- a/paper/src/main/resources/paper-plugin.yml +++ b/paper/src/main/resources/paper-plugin.yml @@ -32,6 +32,10 @@ dependencies: load: BEFORE required: false join-classpath: true + WorldGuard: + load: BEFORE + required: false + join-classpath: true Pl3xMap: load: BEFORE required: false