bentobox/src/main/java/world/bentobox/bentobox/managers/PlaceholdersManager.java

337 lines
15 KiB
Java

package world.bentobox.bentobox.managers;
import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.placeholders.PlaceholderReplacer;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.hooks.placeholders.PlaceholderAPIHook;
import world.bentobox.bentobox.lists.GameModePlaceholder;
/**
* Manages placeholder integration.
*
* @author Poslovitch
*/
public class PlaceholdersManager {
private static final int MAX_TEAM_MEMBER_PLACEHOLDERS = 50;
private final BentoBox plugin;
public PlaceholdersManager(BentoBox plugin) {
this.plugin = plugin;
}
/**
* Registers this placeholder on the behalf of BentoBox.
* @param placeholder the placeholder to register, not null.
* It will be appended with {@code "bentobox_"} by the placeholder plugin.
* @param replacer the expression that will return a {@code String} when executed, which will be this placeholder's replacement.
*/
public void registerPlaceholder(@NonNull String placeholder, @NonNull PlaceholderReplacer replacer) {
// Register it in PlaceholderAPI
getPlaceholderAPIHook().ifPresent(hook -> hook.registerPlaceholder(placeholder, replacer));
}
/**
* Registers this placeholder on the behalf of the specified addon.
* @param addon the addon to register this placeholder on its behalf.
* If null, the placeholder will be registered using {@link #registerPlaceholder(String, PlaceholderReplacer)}.
* @param placeholder the placeholder to register, not null.
* It will be appended with the addon's name by the placeholder plugin.
* @param replacer the expression that will return a {@code String} when executed, which will replace the placeholder.
*/
public void registerPlaceholder(@Nullable Addon addon, @NonNull String placeholder, @NonNull PlaceholderReplacer replacer) {
if (addon == null) {
registerPlaceholder(placeholder, replacer);
return;
}
// Register it in PlaceholderAPI
getPlaceholderAPIHook().ifPresent(hook -> hook.registerPlaceholder(addon, placeholder, replacer));
}
/**
* Registers default placeholders for this gamemode addon.
* @param addon the gamemode addon to register the default placeholders too.
* @since 1.5.0
*/
public void registerDefaultPlaceholders(@NonNull GameModeAddon addon) {
Arrays.stream(GameModePlaceholder.values())
.filter(placeholder -> !isPlaceholder(addon, placeholder.getPlaceholder()))
.forEach(placeholder -> registerPlaceholder(addon, placeholder.getPlaceholder(), new DefaultPlaceholder(addon, placeholder)));
// Register team member placeholders
registerTeamMemberPlaceholders(addon);
// Register potential island names and member info
registerOwnedIslandPlaceholders(addon);
}
private void registerOwnedIslandPlaceholders(@NonNull GameModeAddon addon) {
int maxIslands = plugin.getIWM().getWorldSettings(addon.getOverWorld()).getConcurrentIslands();
IntStream.range(0, maxIslands).forEach(i -> registerPlaceholder(addon, "island_name_" + (i + 1), user -> {
if (user == null)
return "";
AtomicInteger generatedCount = new AtomicInteger(1); // To increment within lambda
return plugin.getIslands().getIslands(addon.getOverWorld(), user).stream().map(island -> {
IslandName islandName = getIslandName(island, user, generatedCount.get());
if (islandName.generatated()) {
generatedCount.getAndIncrement(); // Increment if the name was generated
}
return islandName.name;
}).skip(i) // Skip to the island at index 'i'
.findFirst() // Take the first island after skipping, effectively the (i+1)th
.orElse(""); // Default to empty string if no island is found
}));
// Island_memberlist
IntStream.range(0, maxIslands)
.forEach(i -> registerPlaceholder(addon, "island_memberlist_" + (i + 1), user -> user == null ? ""
: plugin.getIslands().getIslands(addon.getOverWorld(), user).stream().skip(i).findFirst()
.map(island -> island.getMemberSet().stream()
.map(addon.getPlayers()::getName).collect(Collectors.joining(",")))
.orElse("")));
}
private record IslandName(String name, boolean generatated) {
}
private IslandName getIslandName(Island island, User user, int index) {
if (island.getName() != null && !island.getName().isBlank()) {
// Name has been set
return new IslandName(island.getName(), false);
} else {
// Name has not been set
return new IslandName(user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()) + " " + index, true);
}
}
private void registerTeamMemberPlaceholders(@NonNull GameModeAddon addon) {
for (int i = 1; i <= MAX_TEAM_MEMBER_PLACEHOLDERS; i++) {
final int count = i;
// Names
registerPlaceholder(addon, "island_member_name_" + i, user -> {
if (user != null) {
Island island = plugin.getIslands().getIsland(addon.getOverWorld(), user);
int j = 1;
for (UUID uuid : island.getMemberSet(RanksManager.MEMBER_RANK)) {
if (j++ == count) {
return plugin.getPlayers().getName(uuid);
}
}
}
return "";
});
// Register ranks
registerPlaceholder(addon, "island_member_rank_" + i, user -> {
if (user != null) {
Island island = plugin.getIslands().getIsland(addon.getOverWorld(), user);
int j = 1;
for (UUID uuid : island.getMemberSet(RanksManager.MEMBER_RANK)) {
if (j++ == count) {
return user.getTranslationOrNothing(RanksManager.getInstance().getRank(island.getRank(uuid)));
}
}
}
return "";
});
// Banned
registerPlaceholder(addon, "island_banned_name_" + i, user -> {
if (user != null) {
Island island = plugin.getIslands().getIsland(addon.getOverWorld(), user);
int j = 1;
for (UUID uuid : island.getBanned()) {
if (j++ == count) {
return plugin.getPlayers().getName(uuid);
}
}
}
return "";
});
// Visited Island
registerPlaceholder(addon, "visited_island_member_name_" + i, user -> {
if (user != null) {
return plugin.getIslands().getIslandAt(user.getLocation())
.filter(island -> addon.inWorld(island.getCenter()))
.map(island -> {
int j = 1;
for (UUID uuid : island.getMemberSet(RanksManager.MEMBER_RANK)) {
if (j++ == count) {
return plugin.getPlayers().getName(uuid);
}
}
return "";
}).orElse("");
}
return "";
});
registerPlaceholder(addon, "visited_island_member_rank_" + i, user -> {
if (user != null) {
return plugin.getIslands().getIslandAt(user.getLocation())
.filter(island -> addon.inWorld(island.getCenter()))
.map(island -> {
int j = 1;
for (UUID uuid : island.getMemberSet(RanksManager.MEMBER_RANK)) {
if (j++ == count) {
return user.getTranslationOrNothing(RanksManager.getInstance().getRank(island.getRank(uuid)));
}
}
return "";
}).orElse("");
}
return "";
});
registerPlaceholder(addon, "visited_island_banned_name_" + i, user -> {
if (user != null) {
return plugin.getIslands().getIslandAt(user.getLocation())
.filter(island -> addon.inWorld(island.getCenter()))
.map(island -> {
int j = 1;
for (UUID uuid : island.getBanned()) {
if (j++ == count) {
return plugin.getPlayers().getName(uuid);
}
}
return "";
}).orElse("");
}
return "";
});
}
// Counts
// Number of online members
// {@since 2.1.0}
registerPlaceholder(addon, "island_online_members_count", user -> {
if (user == null)
return "";
Island island = plugin.getIslands().getIsland(addon.getOverWorld(), user);
return island != null
? String.valueOf(island.getMemberSet(RanksManager.MEMBER_RANK).stream()
.map(Bukkit::getOfflinePlayer).filter(OfflinePlayer::isOnline).count())
: "";
});
// Number of online members of visited island
registerPlaceholder(addon, "visited_island_online_members_count", user -> {
if (user == null)
return "";
return plugin.getIslands().getIslandAt(user.getLocation())
.map(island -> String.valueOf(island.getMemberSet(RanksManager.MEMBER_RANK).stream()
.map(Bukkit::getOfflinePlayer).filter(OfflinePlayer::isOnline).count()))
.orElse("");
});
}
/**
* Unregisters this placeholder on the behalf of BentoBox.
* Note that if the placeholder you are trying to unregister has been registered by an addon, you should use {@link #unregisterPlaceholder(Addon, String)} instead.
* @param placeholder the placeholder to unregister, not null.
* @since 1.4.0
*/
public void unregisterPlaceholder(@NonNull String placeholder) {
// Unregister it from PlaceholderAPI
getPlaceholderAPIHook().ifPresent(hook -> hook.unregisterPlaceholder(placeholder));
}
/**
* Unregisters this placeholder on the behalf of the specified addon.
* @param addon the addon that originally registered this placeholder.
* If null, this placeholder will be unregistered using {@link #unregisterPlaceholder(String)}.
* @param placeholder the placeholder to unregister, not null.
* @since 1.4.0
*/
public void unregisterPlaceholder(@Nullable Addon addon, @NonNull String placeholder) {
if (addon == null) {
unregisterPlaceholder(placeholder);
return;
}
// Unregister it from PlaceholderAPI
getPlaceholderAPIHook().ifPresent(hook -> hook.unregisterPlaceholder(addon, placeholder));
}
/**
* Returns an Optional containing the PlaceholderAPIHook instance, or an empty Optional otherwise.
* @return Optional containing the PlaceholderAPIHook instance or an empty Optional otherwise.
* @since 1.4.0
*/
@NonNull
private Optional<PlaceholderAPIHook> getPlaceholderAPIHook() {
return plugin.getHooks().getHook("PlaceholderAPI").map(PlaceholderAPIHook.class::cast);
}
/**
* Checks if a placeholder with this name is already registered
* @param addon the addon, not null
* @param placeholder - name of placeholder
* @return {@code true} if a placeholder with this name is already registered
* @since 1.4.0
*/
public boolean isPlaceholder(@NonNull Addon addon, @NonNull String placeholder) {
return getPlaceholderAPIHook().map(h -> h.isPlaceholder(addon, placeholder)).orElse(false);
}
/**
* Replaces the placeholders in this String and returns it.
* @param player the Player to get the placeholders for or null for non-player-specific placeholders
* @param string the String to replace the placeholders in.
* @return the String with placeholders replaced, or the identical String if no placeholders were available.
* @since 1.5.0
*/
public String replacePlaceholders(@Nullable Player player, @NonNull String string) {
return getPlaceholderAPIHook().map(papi -> papi.replacePlaceholders(player, string)).orElse(string);
}
/**
* Unregisters all the placeholders.
* @since 1.15.0
*/
public void unregisterAll() {
getPlaceholderAPIHook().ifPresent(PlaceholderAPIHook::unregisterAll);
}
/**
* Default placeholder
*
*/
class DefaultPlaceholder implements PlaceholderReplacer {
private final GameModeAddon addon;
private final GameModePlaceholder type;
public DefaultPlaceholder(GameModeAddon addon, GameModePlaceholder type) {
this.addon = addon;
this.type = type;
}
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.placeholders.PlaceholderReplacer#onReplace(world.bentobox.bentobox.api.user.User)
*/
@NonNull
@Override
public String onReplace(@Nullable User user) {
if (user == null) {
return "";
}
Island island = addon.getIslands().getIsland(addon.getOverWorld(), user);
return type.getReplacer().onReplace(addon, user, island);
}
}
}