diff --git a/build.gradle b/build.gradle index 49fbbad0..6b27d90d 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,11 @@ repositories { url = uri('https://repo.codemc.org/repository/maven-releases') } + maven { + name = "helpchatRepoReleases" + url = uri("https://repo.helpch.at/releases/") + } + maven { name = 'benwoo1110' url = uri('https://repo.c0ding.party/multiverse-beta') @@ -45,6 +50,9 @@ dependencies { // Caching shadowed("com.github.ben-manes.caffeine:caffeine:3.2.0") + // PlaceholderAPI + externalPlugin 'me.clip:placeholderapi:2.11.6' + // Luckperms for group context compileOnly 'net.luckperms:api:5.4' diff --git a/src/main/java/org/mvplugins/multiverse/inventories/MultiverseInventories.java b/src/main/java/org/mvplugins/multiverse/inventories/MultiverseInventories.java index c0b61752..338d84c0 100644 --- a/src/main/java/org/mvplugins/multiverse/inventories/MultiverseInventories.java +++ b/src/main/java/org/mvplugins/multiverse/inventories/MultiverseInventories.java @@ -111,13 +111,14 @@ public final void onEnable() { this.setUpLocales(); this.registerCommands(); this.registerDestinations(); - this.setupMetrics(); // Hook plugins that can be imported from this.hookImportables(); // Init other extensions this.hookLuckPerms(); + this.loadPlaceholderApiIntegration(); + this.setupMetrics(); this.dupingPatch = InventoriesDupingPatch.enableDupingPatch(this); this.playerNamesMapperProvider.get().loadMap(); @@ -185,6 +186,22 @@ private void hookLuckPerms() { }); } + /** + * Setup placeholder api hook + */ + private void loadPlaceholderApiIntegration() { + if (!inventoriesConfig.get().getRegisterPapiHook()) { + return; + } + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + Try.run(() -> serviceLocator.getService(PlaceholderExpansionHook.class)) + .onFailure(e -> { + Logging.severe("Failed to load PlaceholderAPI integration."); + e.printStackTrace(); + }); + } + } + /** * Setup bstats Metrics. */ diff --git a/src/main/java/org/mvplugins/multiverse/inventories/PlaceholderExpansionHook.java b/src/main/java/org/mvplugins/multiverse/inventories/PlaceholderExpansionHook.java new file mode 100644 index 00000000..7d9b7d48 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/inventories/PlaceholderExpansionHook.java @@ -0,0 +1,152 @@ +package org.mvplugins.multiverse.inventories; + +import com.google.common.collect.Lists; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.utils.REPatterns; +import org.mvplugins.multiverse.core.utils.StringFormatter; +import org.mvplugins.multiverse.external.jakarta.annotation.PostConstruct; +import org.mvplugins.multiverse.external.jakarta.inject.Inject; +import org.mvplugins.multiverse.inventories.profile.group.WorldGroup; +import org.mvplugins.multiverse.inventories.profile.group.WorldGroupManager; + +import java.util.List; +import java.util.Objects; + +@Service +final class PlaceholderExpansionHook extends PlaceholderExpansion { + + private final MultiverseInventories plugin; + private final WorldGroupManager worldGroupManager; + + @Inject + PlaceholderExpansionHook(@NotNull MultiverseInventories plugin, @NotNull WorldGroupManager worldGroupManager) { + this.plugin = plugin; + this.worldGroupManager = worldGroupManager; + } + + @PostConstruct + @Override + public boolean register() { + return super.register(); + } + + @NotNull + @Override + public String getIdentifier() { + return "multiverse-inventories"; + } + + @NotNull + @Override + public String getAuthor() { + return StringFormatter.joinAnd(plugin.getDescription().getAuthors()); + } + + @NotNull + @Override + public String getVersion() { + return plugin.getDescription().getVersion(); + } + + @Override + public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) { + List paramsArray = Lists.newArrayList(REPatterns.UNDERSCORE.split(params)); + if (paramsArray.isEmpty() || paramsArray.size() < 2) { + warning("Placeholder has too little parameters: " + params); + return null; + } + + return switch (paramsArray.get(0)) { + case "world" -> worldPlaceholders(offlinePlayer, paramsArray); + case "group" -> groupPlaceholders(offlinePlayer, paramsArray); + default -> { + warning("Unknown placeholder: " + paramsArray.get(0)); + yield null; + } + }; + } + + private String worldPlaceholders(OfflinePlayer offlinePlayer, List paramsArray) { + String world = null; + if (paramsArray.size() > 2) { + world = paramsArray.get(paramsArray.size() - 1); + } + if (offlinePlayer instanceof Player player) { + world = player.getWorld().getName(); + } + + if (world == null) { + warning("Please specify a world name to use with this placeholder."); + return null; + } + + return switch (paramsArray.get(1)) { + case "groups" -> StringFormatter.join(worldGroupManager.getGroupsForWorld(world).stream() + .map(WorldGroup::getName) + .toList(), ", "); + case "groupcount" -> String.valueOf(worldGroupManager.getGroupsForWorld(world).size()); + default -> { + warning("Unknown world placeholder arg: " + paramsArray.get(1)); + yield null; + } + }; + } + + private String groupPlaceholders(OfflinePlayer offlinePlayer, List paramsArray) { + WorldGroup group = null; + if (paramsArray.size() > 2) { + group = worldGroupManager.getGroup(paramsArray.get(paramsArray.size() - 1)); + if (group == null) { + warning("Group not found: " + paramsArray.get(paramsArray.size() - 1)); + return null; + } + } else if (offlinePlayer instanceof Player player) { + group = worldGroupManager.getGroupsForWorld(player.getWorld().getName()) + .stream() + .findFirst() + .orElse(null); + if (group == null) { + return "ungrouped world"; + } + } + + if (group == null) { + warning("Please specify a group name to use with this placeholder."); + return null; + } + + return getGroupPlaceholderValue(group, paramsArray.get(1)); + } + + private String getGroupPlaceholderValue(WorldGroup worldGroup, String placeholder) { + return switch (placeholder) { + case "name" -> worldGroup.getName(); + case "worlds" -> StringFormatter.join(worldGroup.getWorlds(), ", "); + case "shares" -> StringFormatter.join(worldGroup.getShares().toStringList(), ", "); + case "players" -> StringFormatter.join(worldGroup.getWorlds().stream() + .map(Bukkit::getWorld) + .filter(Objects::nonNull) + .flatMap(world -> world.getPlayers().stream().map(Player::getName)) + .toList(), ", "); + case "playercount" -> worldGroup.getWorlds().stream() + .map(Bukkit::getWorld) + .filter(Objects::nonNull) + .map(World::getPlayers) + .map(List::size) + .reduce(Integer::sum) + .orElse(0) + .toString(); + default -> { + warning("Unknown group placeholder arg: " + placeholder); + yield null; + } + }; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/inventories/config/InventoriesConfig.java b/src/main/java/org/mvplugins/multiverse/inventories/config/InventoriesConfig.java index 7694c636..1cb1f429 100644 --- a/src/main/java/org/mvplugins/multiverse/inventories/config/InventoriesConfig.java +++ b/src/main/java/org/mvplugins/multiverse/inventories/config/InventoriesConfig.java @@ -1,6 +1,7 @@ package org.mvplugins.multiverse.inventories.config; import com.dumptruckman.minecraft.util.Logging; +import org.jetbrains.annotations.ApiStatus; import org.jvnet.hk2.annotations.Service; import org.mvplugins.multiverse.core.config.handle.CommentedConfigurationHandle; import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle; @@ -255,6 +256,31 @@ public Try setGlobalProfileCacheExpiry(int globalProfileCacheExpiry) { return this.configHandle.set(configNodes.globalProfileCacheExpiry, globalProfileCacheExpiry); } + /** + * Get the value of registerPapiHook + * + * @return The value of registerPapiHook + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean getRegisterPapiHook() { + return this.configHandle.get(configNodes.registerPapiHook); + } + + /** + * Sets the value of registerPapiHook + * + * @param registerPapiHook The value of registerPapiHook + * @return The result of the operation + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setRegisterPapiHook(boolean registerPapiHook) { + return this.configHandle.set(configNodes.registerPapiHook, registerPapiHook); + } + /** * Tells whether this is the first time the plugin has run as set by a config flag. * diff --git a/src/main/java/org/mvplugins/multiverse/inventories/config/InventoriesConfigNodes.java b/src/main/java/org/mvplugins/multiverse/inventories/config/InventoriesConfigNodes.java index bbf6af36..a5dad2b0 100644 --- a/src/main/java/org/mvplugins/multiverse/inventories/config/InventoriesConfigNodes.java +++ b/src/main/java/org/mvplugins/multiverse/inventories/config/InventoriesConfigNodes.java @@ -224,6 +224,18 @@ public Object serialize(Shares sharables, Class aClass) { .name("global-profile-cache-expiry") .build()); + private final ConfigHeaderNode miscHeader = node(ConfigHeaderNode.builder("misc") + .comment("") + .comment("") + .build()); + + final ConfigNode registerPapiHook = node(ConfigNode.builder("misc.register-papi-hook", Boolean.class) + .comment("This config option defines whether or not Multiverse should register the PlaceholderAPI hook.") + .comment("This only applies if PlaceholderAPI is installed.") + .defaultValue(true) + .name("register-papi-hook") + .build()); + final ConfigNode firstRun = node(ConfigNode.builder("first-run", Boolean.class) .comment("") .comment("") diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 493d7f9c..a5914abb 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,4 +5,4 @@ api-version: 1.13 authors: ['dumptruckman', 'benwoo1110'] website: 'https://dev.bukkit.org/projects/multiverse-inventories' depend: ['Multiverse-Core'] -softdepend: [LuckPerms, MultiInv, WorldInventories, PerWorldInventory] +softdepend: [LuckPerms, PlaceholderAPI, MultiInv, WorldInventories, PerWorldInventory] diff --git a/src/test/resources/config/fresh_config.yml b/src/test/resources/config/fresh_config.yml index 6335cd6c..ffa264d3 100644 --- a/src/test/resources/config/fresh_config.yml +++ b/src/test/resources/config/fresh_config.yml @@ -25,5 +25,8 @@ performance: global-profile-cache-size: 500 global-profile-cache-expiry: 60 +misc: + register-papi-hook: true + first-run: true version: 5.0 diff --git a/src/test/resources/config/migrated_config.yml b/src/test/resources/config/migrated_config.yml index 20f7f48e..71610a4b 100644 --- a/src/test/resources/config/migrated_config.yml +++ b/src/test/resources/config/migrated_config.yml @@ -26,5 +26,8 @@ performance: global-profile-cache-size: 500 global-profile-cache-expiry: 60 +misc: + register-papi-hook: true + first-run: true version: 5.0