Add MiniPlaceholder support via UtilitiesOG

This commit is contained in:
NotAlexNoyle committed 2026-05-05 23:56:07 -04:00
1 parent 3b152d6578
commit f1cceaca0c
14 files changed
+844 -358

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
[submodule "libs/Utilities-OG"]
path = libs/Utilities-OG
url = https://github.com/true-og/Utilities-OG
+2 -4
View File
@@ -10,8 +10,6 @@ Full featured union system for PVP Minecraft servers!
[![Issues](https://img.shields.io/github/issues/RoinujNosde/SimpleClans.svg)](https://github.com/RoinujNosde/SimpleClans/issues)
[![Crowdin](https://badges.crowdin.net/simpleclans/localized.svg)](https://crowdin.com/project/simpleclans)
[![Discord](https://img.shields.io/discord/719557355917934613?label=discord&logo=discord)](https://discord.gg/CkNwgdE)
[![bStats Servers](https://img.shields.io/bstats/servers/7131)](https://bstats.org/plugin/bukkit/SimpleClans/7131)
[![bStats Players](https://img.shields.io/bstats/players/7131)](https://bstats.org/plugin/bukkit/SimpleClans/7131)
#### Download Link
@@ -35,7 +33,7 @@ Full featured union system for PVP Minecraft servers!
* [Union name on Tablist](https://simpleclans.gitbook.io/simpleclans/how-to-setup/clan-on-tablist)
* Plugins & Development
* [Unions-OG API Example](https://simpleclans.gitbook.io/simpleclans/other/simpleclans-api)
* [PlaceholderAPI Placeholders](https://simpleclans.gitbook.io/simpleclans/other/placeholderapi-support)
* [Placeholder and MiniPlaceholder Reference](placeholder-info.txt)
* [Land Protection Plugins](https://simpleclans.gitbook.io/simpleclans/other/land-claims)
#### Support & Suggestions
@@ -87,7 +85,7 @@ dependencies {
Feel free to learn our [Javadoc](https://ci.roinujnosde.me/job/SimpleClans/Javadoc/).
Compatibility note: the published Maven coordinates, package names, and permissions still use `simpleclans`.
PlaceholderAPI supports both `simpleclans` and `simpleunions`, and union aliases such as `in_union`, `union_name`, and `topunions_#_union_name` resolve under either identifier.
PlaceholderAPI and MiniPlaceholders support both `simpleclans` and `simpleunions`; see [placeholder-info.txt](placeholder-info.txt) for the full placeholder reference.
##### Latest version:
[![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Frepo.roinujnosde.me%2Freleases%2Fnet%2Fsacredlabyrinth%2Fphaed%2Fsimpleclans%2FSimpleClans%2Fmaven-metadata.xml)](https://repo.roinujnosde.me/#/releases/net/sacredlabyrinth/phaed/simpleclans/SimpleClans)
##### API Examples
+1 -2
View File
@@ -76,7 +76,7 @@ dependencies {
compileOnly("com.github.MilkBowl:VaultAPI:1.7") {
exclude(group = "com.googlecode.json-simple", module = "json-simple")
}
implementation("org.bstats:bstats-bukkit:3.0.2")
compileOnlyApi(project(":libs:Utilities-OG")) // Import TrueOG Network Utilities-OG Java API (from source).
compileOnly("me.clip:placeholderapi:2.11.3")
compileOnly("org.jetbrains:annotations:24.0.1")
implementation("io.papermc:paperlib:1.0.8-SNAPSHOT")
@@ -106,7 +106,6 @@ tasks.withType<AbstractArchiveTask>().configureEach { // Ensure reproducible .ja
/* ----------------------------- Shadow -------------------------------- */
tasks.shadowJar {
relocate("org.bstats", "net.sacredlabyrinth.phaed.simpleclans.metrics")
relocate("io.papermc.lib", "net.sacredlabyrinth.phaed.simpleclans.paperlib")
relocate("co.aikar.commands", "net.sacredlabyrinth.phaed.simpleclans.acf")
relocate("co.aikar.locales", "net.sacredlabyrinth.phaed.simpleclans.locales")
+1
Submodule libs/Utilities-OG added at cfb722c394.
+200
View File
@@ -0,0 +1,200 @@
Unions-OG Placeholder Reference
===============================
Unions-OG registers legacy PlaceholderAPI placeholders when PlaceholderAPI is installed.
It also registers MiniPlaceholders through the Utilities-OG API when Utilities-OG is installed.
Identifiers
-----------
All placeholders use both identifiers:
- simpleclans
- simpleunions
In the syntax patterns below, replace {id} with either identifier.
Legacy PlaceholderAPI syntax:
- Player placeholder: %{id}_{player-placeholder}%
- Union placeholder: %{id}_clan_{union-placeholder}%
- Union compatibility alias: %{id}_union_{union-placeholder}%
- Relational color placeholder: %rel_{id}_color%
MiniPlaceholders syntax:
- Player placeholder: <{id}_{player-placeholder}>
- Union placeholder: <{id}_clan_{union-placeholder}>
- Union compatibility alias: <{id}_union_{union-placeholder}>
- Relational color placeholder: <{id}_rel_color>
MiniPlaceholders are audience placeholders unless marked relational. They require a player audience.
Relational MiniPlaceholders require a relational MiniPlaceholders context.
Boolean output follows PlaceholderAPI's boolean.true and boolean.false config values when PlaceholderAPI is loaded.
Without PlaceholderAPI, boolean placeholders return yes or no.
Player Placeholders
-------------------
These resolve against the requested player:
- name
- clean_name
- is_leader
- lastseendays
- total_kills
- rival_kills
- civilian_kills
- neutral_kills
- ally_kills
- is_friendlyfire_on
- deaths
- weighted_kills
- kdr
- topplayers_position
- join_date
- lastseen
- inactive_days
- in_clan
- tag
- tag_label
- is_trusted
- is_member
- clanchat_player_color
- allychat_player_color
- is_bb_enabled
- is_invite_enabled
- is_tag_enabled
- rank_displayname
- has_rank
- rank
- is_muted
- is_mutedally
Player compatibility aliases:
- in_union maps to in_clan
- unionchat_player_color maps to clanchat_player_color
Examples:
- %simpleclans_name%
- %simpleunions_in_union%
- <simpleclans_name>
- <simpleunions_in_union>
Union Placeholders
------------------
These resolve against the requested player's union:
- name
- balance
- balance_formatted
- tag
- color
- inactivedays
- friendly_fire
- allies_count
- founded
- color_tag
- size
- rivals_count
- is_verified
- is_permanent
- onlinemembers_count
- leader_size
- total_kdr
- topclans_position
- total_deaths
- average_wk
- total_kills
- total_rival
- total_neutral
- total_civilian
- total_ally
- is_anyonline
- is_unrivable
- allow_withdraw
- allow_deposit
Union compatibility aliases:
- Use union_ instead of clan_ for any union placeholder.
- topunions_position maps to topclans_position.
Examples:
- %simpleclans_clan_name%
- %simpleclans_union_name%
- %simpleunions_clan_color_tag%
- %simpleunions_union_topunions_position%
- <simpleclans_clan_name>
- <simpleclans_union_name>
- <simpleunions_clan_color_tag>
- <simpleunions_union_topunions_position>
Top Player Placeholders
-----------------------
Legacy PlaceholderAPI syntax:
- %{id}_topplayers_{position}_{player-placeholder}%
MiniPlaceholders syntax:
- <{id}_topplayers_{player-placeholder}:{position}>
The {position} value is 1-based. The {player-placeholder} value can be any player placeholder or player compatibility alias listed above.
Examples:
- %simpleclans_topplayers_1_name%
- %simpleunions_topplayers_3_kdr%
- <simpleclans_topplayers_name:1>
- <simpleunions_topplayers_kdr:3>
Top Union Placeholders
----------------------
Legacy PlaceholderAPI syntax:
- %{id}_topclans_{position}_clan_{union-placeholder}%
- %{id}_topunions_{position}_union_{union-placeholder}%
MiniPlaceholders syntax:
- <{id}_topclans_clan_{union-placeholder}:{position}>
- <{id}_topunions_union_{union-placeholder}:{position}>
The {position} value is 1-based. The {union-placeholder} value can be any union placeholder or union compatibility alias listed above.
Examples:
- %simpleclans_topclans_1_clan_name%
- %simpleclans_topunions_1_union_name%
- %simpleunions_topclans_5_clan_total_kdr%
- <simpleclans_topclans_clan_name:1>
- <simpleclans_topunions_union_name:1>
- <simpleunions_topclans_clan_total_kdr:5>
Relational Color Placeholder
----------------------------
The relational color placeholder returns a color code based on the relationship between two players:
- same union: &a
- ally: &b
- rival: &c
- unrelated: empty string
Legacy PlaceholderAPI syntax:
- %rel_simpleclans_color%
- %rel_simpleunions_color%
MiniPlaceholders syntax:
- <simpleclans_rel_color>
- <simpleunions_rel_color>
@@ -3,6 +3,7 @@ package net.sacredlabyrinth.phaed.simpleclans;
import co.aikar.commands.BukkitCommandIssuer;
import net.sacredlabyrinth.phaed.simpleclans.commands.SCCommandManager;
import net.sacredlabyrinth.phaed.simpleclans.hooks.papi.SimpleClansExpansion;
import net.sacredlabyrinth.phaed.simpleclans.hooks.papi.SimpleClansMiniPlaceholders;
import net.sacredlabyrinth.phaed.simpleclans.language.LanguageResource;
import net.sacredlabyrinth.phaed.simpleclans.listeners.*;
import net.sacredlabyrinth.phaed.simpleclans.loggers.BankLogger;
@@ -19,9 +20,6 @@ import net.sacredlabyrinth.phaed.simpleclans.utils.ChatUtils;
import net.sacredlabyrinth.phaed.simpleclans.utils.TagValidator;
import net.sacredlabyrinth.phaed.simpleclans.utils.UpdateChecker;
import net.sacredlabyrinth.phaed.simpleclans.uuid.UUIDMigration;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bstats.charts.SingleLineChart;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
@@ -140,8 +138,8 @@ public class SimpleClans extends JavaPlugin {
logStatus();
startTasks();
startMetrics();
hookIntoPAPI();
hookIntoMiniPlaceholders();
new UpdateChecker(this).check();
}
@@ -180,32 +178,14 @@ public class SimpleClans extends JavaPlugin {
}
private void startMetrics() {
private void hookIntoMiniPlaceholders() {
Metrics metrics = new Metrics(this, 7131);
SettingsManager sm = getSettingsManager();
ClanManager cm = getClanManager();
String on = "enabled";
String off = "disabled";
metrics.addCustomChart(new SingleLineChart("clans", () -> cm.getClans().size()));
metrics.addCustomChart(new SingleLineChart("clan_players", () -> cm.getAllClanPlayers().size()));
metrics.addCustomChart(new SimplePie("language", () -> sm.getLanguage().toString()));
metrics.addCustomChart(new SimplePie("machine_language", () -> Locale.getDefault().toString()));
metrics.addCustomChart(new SimplePie("language_chooser", () -> sm.is(LANGUAGE_SELECTOR) ? on : off));
metrics.addCustomChart(new SimplePie("database", () -> sm.is(MYSQL_ENABLE) ? "MySQL" : "SQLite"));
metrics.addCustomChart(
new SimplePie("save_periodically", () -> sm.is(PERFORMANCE_SAVE_PERIODICALLY) ? on : off));
metrics.addCustomChart(new SimplePie("save_interval", () -> sm.getString(PERFORMANCE_SAVE_INTERVAL)));
metrics.addCustomChart(new SimplePie("upkeep", () -> sm.is(ECONOMY_UPKEEP_ENABLED) ? on : off));
metrics.addCustomChart(new SimplePie("member_fee", () -> sm.is(ECONOMY_MEMBER_FEE_ENABLED) ? on : off));
metrics.addCustomChart(new SimplePie("rejoin_cooldown", () -> sm.is(ENABLE_REJOIN_COOLDOWN) ? on : off));
metrics.addCustomChart(new SimplePie("clan_verification", () -> sm.is(REQUIRE_VERIFICATION) ? on : off));
metrics.addCustomChart(new SimplePie("money_per_kill", () -> sm.is(ECONOMY_MONEY_PER_KILL) ? on : off));
metrics.addCustomChart(new SimplePie("threads", () -> sm.is(PERFORMANCE_USE_THREADS) ? on : off));
metrics.addCustomChart(new SimplePie("bungeecord", () -> sm.is(PERFORMANCE_USE_BUNGEECORD) ? on : off));
metrics.addCustomChart(new SimplePie("discord_chat", () -> sm.is(DISCORDCHAT_ENABLE) ? on : off));
metrics.addCustomChart(
new SimplePie("default_rank", () -> sm.getString(CLAN_DEFAULT_RANK).isEmpty() ? off : on));
if (getPluginManager().getPlugin("Utilities-OG") != null) {
getLogger().info("Utilities-OG found. Registering MiniPlaceholders...");
new SimpleClansMiniPlaceholders(this).register();
}
}
@@ -0,0 +1,41 @@
package net.sacredlabyrinth.phaed.simpleclans.hooks.papi;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
public final class PlaceholderBooleanFormatter {
private static final String PLACEHOLDER_API = "PlaceholderAPI";
private PlaceholderBooleanFormatter() {
}
public static @NotNull String trueValue() {
return getConfiguredValue("boolean.true", "yes");
}
public static @NotNull String falseValue() {
return getConfiguredValue("boolean.false", "no");
}
private static @NotNull String getConfiguredValue(@NotNull String path, @NotNull String fallback) {
Plugin placeholderApi = Bukkit.getPluginManager().getPlugin(PLACEHOLDER_API);
if (placeholderApi instanceof JavaPlugin) {
return ((JavaPlugin) placeholderApi).getConfig().getString(path, fallback);
}
return fallback;
}
}
@@ -3,35 +3,21 @@ package net.sacredlabyrinth.phaed.simpleclans.hooks.papi;
import me.clip.placeholderapi.expansion.Configurable;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.Relational;
import net.sacredlabyrinth.phaed.simpleclans.Clan;
import net.sacredlabyrinth.phaed.simpleclans.ClanPlayer;
import net.sacredlabyrinth.phaed.simpleclans.Helper;
import net.sacredlabyrinth.phaed.simpleclans.SimpleClans;
import net.sacredlabyrinth.phaed.simpleclans.managers.ClanManager;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SimpleClansExpansion extends PlaceholderExpansion implements Relational, Configurable {
private static final Pattern TOP_CLANS_PATTERN = Pattern.compile("(?<strip>^topclans_(?<position>\\d+)_)clan_");
private static final Pattern TOP_PLAYERS_PATTERN = Pattern.compile("(?<strip>^topplayers_(?<position>\\d+)_)");
private static final String CLAN_COLOR_TAG_PLACEHOLDER = "clan_color_tag";
private static final String NO_CLAN_COLOR_TAG = "&8None";
private static final Map<String, PlaceholderResolver> RESOLVERS = new HashMap<>();
private List<String> placeholders;
private final SimpleClans plugin;
private final ClanManager clanManager;
private final String identifier;
private final SimpleClansPlaceholderEngine placeholders;
public SimpleClansExpansion(SimpleClans plugin) {
@@ -43,8 +29,7 @@ public class SimpleClansExpansion extends PlaceholderExpansion implements Relati
this.plugin = plugin;
this.identifier = identifier;
clanManager = plugin.getClanManager();
registerResolvers();
placeholders = new SimpleClansPlaceholderEngine(plugin);
}
@@ -86,18 +71,7 @@ public class SimpleClansExpansion extends PlaceholderExpansion implements Relati
@Override
public @NotNull List<String> getPlaceholders() {
if (placeholders == null) {
LinkedHashSet<String> allPlaceholders = new LinkedHashSet<>();
addPlaceholders(identifier + "_", ClanPlayer.class, allPlaceholders);
addAliasedPlaceholders(identifier + "_", ClanPlayer.class, allPlaceholders);
addPlaceholders(identifier + "_clan_", Clan.class, allPlaceholders);
addAliasedPlaceholders(identifier + "_union_", Clan.class, allPlaceholders);
this.placeholders = new ArrayList<>(allPlaceholders);
}
return placeholders;
return placeholders.getLegacyPlaceholders(identifier);
}
@@ -125,289 +99,15 @@ public class SimpleClansExpansion extends PlaceholderExpansion implements Relati
@Nullable
public String onPlaceholderRequest(Player player1, Player player2, String params) {
if (player1 == null || player2 == null) {
return null;
}
if (params.equalsIgnoreCase("color")) {
ClanPlayer cp1 = clanManager.getClanPlayer(player1);
if (cp1 == null) {
return "";
}
// noinspection ConstantConditions -- getClanPlayer != null == getClan() != null
if (cp1.getClan().isMember(player2)) {
return getString("color.same_clan", null);
}
if (cp1.isRival(player2)) {
return getString("color.rival", null);
}
if (cp1.isAlly(player2)) {
return getString("color.ally", null);
}
return "";
}
return null;
return placeholders.resolveRelational(player1, player2, params, getString("color.same_clan", null),
getString("color.rival", null), getString("color.ally", null));
}
@Override
public String onRequest(@Nullable OfflinePlayer player, @NotNull String params) {
params = normalizePlaceholder(params);
ClanPlayer cp = null;
if (player != null) {
cp = clanManager.getAnyClanPlayer(player.getUniqueId());
}
if (cp == null) {
return getNoClanValue(params);
}
Clan clan = cp.getClan();
if (clan == null && CLAN_COLOR_TAG_PLACEHOLDER.equals(params)) {
return NO_CLAN_COLOR_TAG;
}
Matcher matcher = TOP_CLANS_PATTERN.matcher(params);
if (matcher.find()) {
int position = Integer.parseInt(matcher.group("position"));
clan = getFromPosition(clanManager.getClans(), position, clanManager::sortClansByKDR);
params = params.replace(matcher.group("strip"), "");
}
matcher = TOP_PLAYERS_PATTERN.matcher(params);
if (matcher.find()) {
int position = Integer.parseInt(matcher.group("position"));
cp = getFromPosition(clanManager.getAllClanPlayers(), position, clanManager::sortClanPlayersByKDR);
params = params.replace(matcher.group("strip"), "");
}
return getValue(player, cp, clan, params);
}
@NotNull
private String getNoClanValue(@NotNull String placeholder) {
return CLAN_COLOR_TAG_PLACEHOLDER.equals(placeholder) ? NO_CLAN_COLOR_TAG : "";
}
@Nullable
private <T> T getFromPosition(List<T> list, int position, Consumer<List<T>> sort) {
if (isPositionValid(list, position)) {
sort.accept(list);
return list.get(position - 1);
}
return null;
}
private boolean isPositionValid(@NotNull Collection<?> collection, int position) {
return position >= 1 && position <= collection.size();
}
@NotNull
private String getValue(@Nullable OfflinePlayer player, @Nullable ClanPlayer cp, @Nullable Clan clan,
@NotNull String placeholder)
{
if (placeholder.startsWith("clan_")) {
placeholder = placeholder.replace("clan_", "");
return getValue(player, clan, placeholder);
}
return getValue(player, cp, placeholder);
}
@NotNull
private String getValue(@Nullable OfflinePlayer player, @Nullable Object object, @NotNull String placeholder) {
if (object != null) {
for (Method declaredMethod : object.getClass().getDeclaredMethods()) {
Placeholder[] annotations = declaredMethod.getAnnotationsByType(Placeholder.class);
for (Placeholder p : annotations) {
if (p.value().equals(placeholder)) {
return resolve(player, object, declaredMethod, p.resolver(), placeholder, p.config());
}
}
}
plugin.getLogger().warning(String.format("Placeholder %s not found", placeholder));
}
return "";
}
private String resolve(@Nullable OfflinePlayer player, @NotNull Object object, @NotNull Method method,
@NotNull String resolverId, @NotNull String placeholder, @NotNull String config)
{
PlaceholderResolver resolver = RESOLVERS.get(resolverId);
if (resolver != null) {
return resolver.resolve(player, object, method, placeholder, getConfigMap(config));
}
plugin.getLogger().warning(String.format("Resolver %s for %s not found", resolverId, placeholder));
return "";
}
@NotNull
private Map<String, String> getConfigMap(@NotNull String config) {
HashMap<String, String> map = new HashMap<>();
String[] elements = config.split(",");
for (String element : elements) {
String[] keyAndValue = element.split(":");
map.put(keyAndValue[0], keyAndValue.length > 1 ? keyAndValue[1] : null);
}
return map;
}
private void registerResolvers() {
Set<Class<? extends PlaceholderResolver>> resolvers = Helper
.getSubTypesOf("net.sacredlabyrinth.phaed.simpleclans.hooks.papi.resolvers", PlaceholderResolver.class);
plugin.getLogger().info(String.format("Registering %d placeholder resolvers...", resolvers.size()));
for (Class<? extends PlaceholderResolver> r : resolvers) {
try {
PlaceholderResolver resolver = r.getConstructor(SimpleClans.class).newInstance(plugin);
RESOLVERS.put(resolver.getId(), resolver);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException
| NoSuchMethodException e)
{
plugin.getLogger().log(Level.SEVERE, "Error registering placeholder resolver", e);
}
}
}
private void addPlaceholders(String prefix, Class<?> clazz, Collection<String> placeholders) {
for (Method method : clazz.getDeclaredMethods()) {
Placeholder[] annotations = method.getAnnotationsByType(Placeholder.class);
for (Placeholder annotation : annotations) {
placeholders.add("%" + prefix + annotation.value() + "%");
// Commented because the list would be very long
// placeholders.add("%simpleclans_topplayers_<number>_" + annotation.value() +
// "%");
}
}
}
private void addAliasedPlaceholders(String prefix, Class<?> clazz, Collection<String> placeholders) {
for (Method method : clazz.getDeclaredMethods()) {
Placeholder[] annotations = method.getAnnotationsByType(Placeholder.class);
for (Placeholder annotation : annotations) {
String alias = toUnionAlias(annotation.value());
if (!alias.equals(annotation.value())) {
placeholders.add("%" + prefix + alias + "%");
}
}
}
}
@NotNull
private String normalizePlaceholder(@NotNull String placeholder) {
String normalized = placeholder;
if (normalized.startsWith("topunions_")) {
normalized = "topclans_" + normalized.substring("topunions_".length());
}
if (normalized.startsWith("union_")) {
normalized = "clan_" + normalized.substring("union_".length());
}
normalized = normalized.replace("_union_", "_clan_");
normalized = normalized.replace("in_union", "in_clan");
normalized = normalized.replace("unionchat_player_color", "clanchat_player_color");
normalized = normalized.replace("topunions_position", "topclans_position");
return normalized;
}
@NotNull
private String toUnionAlias(@NotNull String placeholder) {
return placeholder.replace("topclans", "topunions").replace("in_clan", "in_union")
.replace("clanchat_player_color", "unionchat_player_color");
return placeholders.resolve(player, params);
}
@@ -0,0 +1,155 @@
package net.sacredlabyrinth.phaed.simpleclans.hooks.papi;
import net.sacredlabyrinth.phaed.simpleclans.SimpleClans;
import net.trueog.utilitiesog.UtilitiesOG;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
public class SimpleClansMiniPlaceholders {
private static final List<String> IDENTIFIERS = Arrays.asList("simpleclans", "simpleunions");
private static final String SAME_CLAN_COLOR = "&a";
private static final String RIVAL_COLOR = "&c";
private static final String ALLY_COLOR = "&b";
private final SimpleClansPlaceholderEngine placeholders;
public SimpleClansMiniPlaceholders(@NotNull SimpleClans plugin) {
placeholders = new SimpleClansPlaceholderEngine(plugin);
}
public void register() {
for (String identifier : IDENTIFIERS) {
registerRelationalColor(identifier);
registerPlayerPlaceholders(identifier);
registerClanPlaceholders(identifier);
registerTopPlayerPlaceholders(identifier);
registerTopClanPlaceholders(identifier);
}
}
private void registerRelationalColor(@NotNull String identifier) {
UtilitiesOG.registerRelationalPlaceholder(identifier + "_color", (player, target) -> nullToEmpty(
placeholders.resolveRelational(player, target, "color", SAME_CLAN_COLOR, RIVAL_COLOR, ALLY_COLOR)));
}
private void registerPlayerPlaceholders(@NotNull String identifier) {
for (String placeholder : placeholders.getPlayerPlaceholdersAndAliases()) {
registerAudiencePlaceholder(identifier + "_" + placeholder, placeholder);
}
}
private void registerClanPlaceholders(@NotNull String identifier) {
for (String placeholder : placeholders.getClanPlaceholders()) {
registerAudiencePlaceholder(identifier + "_clan_" + placeholder, "clan_" + placeholder);
}
for (String placeholder : placeholders.getUnionPlaceholders()) {
registerAudiencePlaceholder(identifier + "_union_" + placeholder, "union_" + placeholder);
}
}
private void registerTopPlayerPlaceholders(@NotNull String identifier) {
for (String placeholder : placeholders.getPlayerPlaceholdersAndAliases()) {
registerPositionedAudiencePlaceholder(identifier + "_topplayers_" + placeholder,
(player, position) -> placeholders.resolve(player, "topplayers_" + position + "_" + placeholder));
}
}
private void registerTopClanPlaceholders(@NotNull String identifier) {
for (String placeholder : placeholders.getClanPlaceholders()) {
registerPositionedAudiencePlaceholder(identifier + "_topclans_clan_" + placeholder, (player,
position) -> placeholders.resolve(player, "topclans_" + position + "_clan_" + placeholder));
}
for (String placeholder : placeholders.getUnionPlaceholders()) {
registerPositionedAudiencePlaceholder(identifier + "_topunions_union_" + placeholder, (player,
position) -> placeholders.resolve(player, "topunions_" + position + "_union_" + placeholder));
}
}
private void registerAudiencePlaceholder(@NotNull String name, @NotNull String params) {
UtilitiesOG.registerAudiencePlaceholder(name, (Player player) -> placeholders.resolve(player, params));
}
private void registerPositionedAudiencePlaceholder(@NotNull String name,
@NotNull PositionedPlaceholder placeholder)
{
UtilitiesOG.registerAudiencePlaceholder(name, (player, args) -> {
if (args.isEmpty() || !isPositiveInteger(args.get(0))) {
return "";
}
return placeholder.resolve(player, args.get(0));
});
}
private boolean isPositiveInteger(@NotNull String value) {
try {
return Integer.parseInt(value) > 0;
} catch (NumberFormatException e) {
return false;
}
}
@NotNull
private String nullToEmpty(String value) {
return value != null ? value : "";
}
@FunctionalInterface
private interface PositionedPlaceholder {
@NotNull
String resolve(@NotNull Player player, @NotNull String position);
}
}
@@ -0,0 +1,409 @@
package net.sacredlabyrinth.phaed.simpleclans.hooks.papi;
import net.sacredlabyrinth.phaed.simpleclans.Clan;
import net.sacredlabyrinth.phaed.simpleclans.ClanPlayer;
import net.sacredlabyrinth.phaed.simpleclans.Helper;
import net.sacredlabyrinth.phaed.simpleclans.SimpleClans;
import net.sacredlabyrinth.phaed.simpleclans.managers.ClanManager;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SimpleClansPlaceholderEngine {
private static final Pattern TOP_CLANS_PATTERN = Pattern.compile("(?<strip>^topclans_(?<position>\\d+)_)clan_");
private static final Pattern TOP_PLAYERS_PATTERN = Pattern.compile("(?<strip>^topplayers_(?<position>\\d+)_)");
private static final String CLAN_COLOR_TAG_PLACEHOLDER = "clan_color_tag";
private static final String NO_CLAN_COLOR_TAG = "&8None";
private final SimpleClans plugin;
private final ClanManager clanManager;
private final Map<String, PlaceholderResolver> resolvers = new HashMap<>();
public SimpleClansPlaceholderEngine(@NotNull SimpleClans plugin) {
this.plugin = plugin;
clanManager = plugin.getClanManager();
registerResolvers();
}
@NotNull
public List<String> getLegacyPlaceholders(@NotNull String identifier) {
LinkedHashSet<String> allPlaceholders = new LinkedHashSet<>();
addLegacyPlaceholders(identifier + "_", getPlayerPlaceholders(), allPlaceholders);
addLegacyPlaceholders(identifier + "_", getPlayerAliases(), allPlaceholders);
addLegacyPlaceholders(identifier + "_clan_", getClanPlaceholders(), allPlaceholders);
addLegacyPlaceholders(identifier + "_union_", getUnionPlaceholders(), allPlaceholders);
return new ArrayList<>(allPlaceholders);
}
@NotNull
public List<String> getPlayerPlaceholders() {
return getPlaceholderNames(ClanPlayer.class);
}
@NotNull
public List<String> getPlayerAliases() {
return getAliases(ClanPlayer.class);
}
@NotNull
public List<String> getPlayerPlaceholdersAndAliases() {
return combine(getPlayerPlaceholders(), getPlayerAliases());
}
@NotNull
public List<String> getClanPlaceholders() {
return getPlaceholderNames(Clan.class);
}
@NotNull
public List<String> getUnionPlaceholders() {
return combine(getClanPlaceholders(), getAliases(Clan.class));
}
@Nullable
public String resolveRelational(@Nullable Player player1, @Nullable Player player2, @NotNull String params,
@NotNull String sameClanColor, @NotNull String rivalColor, @NotNull String allyColor)
{
if (player1 == null || player2 == null) {
return null;
}
if (params.equalsIgnoreCase("color")) {
ClanPlayer cp1 = clanManager.getClanPlayer(player1);
if (cp1 == null) {
return "";
}
// noinspection ConstantConditions -- getClanPlayer != null == getClan() != null
if (cp1.getClan().isMember(player2)) {
return sameClanColor;
}
if (cp1.isRival(player2)) {
return rivalColor;
}
if (cp1.isAlly(player2)) {
return allyColor;
}
return "";
}
return null;
}
@NotNull
public String resolve(@Nullable OfflinePlayer player, @NotNull String params) {
params = normalizePlaceholder(params);
ClanPlayer cp = null;
if (player != null) {
cp = clanManager.getAnyClanPlayer(player.getUniqueId());
}
Clan clan = cp != null ? cp.getClan() : null;
Matcher matcher = TOP_CLANS_PATTERN.matcher(params);
if (matcher.find()) {
int position = Integer.parseInt(matcher.group("position"));
clan = getFromPosition(clanManager.getClans(), position, clanManager::sortClansByKDR);
params = params.replace(matcher.group("strip"), "");
return getValue(player, null, clan, params);
}
matcher = TOP_PLAYERS_PATTERN.matcher(params);
if (matcher.find()) {
int position = Integer.parseInt(matcher.group("position"));
cp = getFromPosition(clanManager.getAllClanPlayers(), position, clanManager::sortClanPlayersByKDR);
clan = cp != null ? cp.getClan() : null;
params = params.replace(matcher.group("strip"), "");
return getValue(player, cp, clan, params);
}
if (cp == null) {
return getNoClanValue(params);
}
if (clan == null && CLAN_COLOR_TAG_PLACEHOLDER.equals(params)) {
return NO_CLAN_COLOR_TAG;
}
return getValue(player, cp, clan, params);
}
@NotNull
private String getNoClanValue(@NotNull String placeholder) {
return CLAN_COLOR_TAG_PLACEHOLDER.equals(placeholder) ? NO_CLAN_COLOR_TAG : "";
}
@Nullable
private <T> T getFromPosition(List<T> list, int position, Consumer<List<T>> sort) {
if (isPositionValid(list, position)) {
sort.accept(list);
return list.get(position - 1);
}
return null;
}
private boolean isPositionValid(@NotNull Collection<?> collection, int position) {
return position >= 1 && position <= collection.size();
}
@NotNull
private String getValue(@Nullable OfflinePlayer player, @Nullable ClanPlayer cp, @Nullable Clan clan,
@NotNull String placeholder)
{
if (placeholder.startsWith("clan_")) {
return getValue(player, clan, placeholder.substring("clan_".length()));
}
return getValue(player, cp, placeholder);
}
@NotNull
private String getValue(@Nullable OfflinePlayer player, @Nullable Object object, @NotNull String placeholder) {
if (object != null) {
for (Method declaredMethod : object.getClass().getDeclaredMethods()) {
Placeholder[] annotations = declaredMethod.getAnnotationsByType(Placeholder.class);
for (Placeholder p : annotations) {
if (p.value().equals(placeholder)) {
return resolve(player, object, declaredMethod, p.resolver(), placeholder, p.config());
}
}
}
plugin.getLogger().warning(String.format("Placeholder %s not found", placeholder));
}
return "";
}
private String resolve(@Nullable OfflinePlayer player, @NotNull Object object, @NotNull Method method,
@NotNull String resolverId, @NotNull String placeholder, @NotNull String config)
{
PlaceholderResolver resolver = resolvers.get(resolverId);
if (resolver != null) {
return resolver.resolve(player, object, method, placeholder, getConfigMap(config));
}
plugin.getLogger().warning(String.format("Resolver %s for %s not found", resolverId, placeholder));
return "";
}
@NotNull
private Map<String, String> getConfigMap(@NotNull String config) {
if (config.isEmpty()) {
return Collections.emptyMap();
}
HashMap<String, String> map = new HashMap<>();
String[] elements = config.split(",");
for (String element : elements) {
String[] keyAndValue = element.split(":");
map.put(keyAndValue[0], keyAndValue.length > 1 ? keyAndValue[1] : null);
}
return map;
}
private void registerResolvers() {
Set<Class<? extends PlaceholderResolver>> placeholderResolvers = Helper
.getSubTypesOf("net.sacredlabyrinth.phaed.simpleclans.hooks.papi.resolvers", PlaceholderResolver.class);
plugin.getLogger().info(String.format("Registering %d placeholder resolvers...", placeholderResolvers.size()));
for (Class<? extends PlaceholderResolver> resolverClass : placeholderResolvers) {
try {
PlaceholderResolver resolver = resolverClass.getConstructor(SimpleClans.class).newInstance(plugin);
resolvers.put(resolver.getId(), resolver);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException
| NoSuchMethodException e)
{
plugin.getLogger().log(Level.SEVERE, "Error registering placeholder resolver", e);
}
}
}
private void addLegacyPlaceholders(String prefix, Collection<String> placeholderNames,
Collection<String> placeholders)
{
for (String placeholderName : placeholderNames) {
placeholders.add("%" + prefix + placeholderName + "%");
}
}
@NotNull
private List<String> getPlaceholderNames(Class<?> clazz) {
LinkedHashSet<String> placeholders = new LinkedHashSet<>();
for (Method method : clazz.getDeclaredMethods()) {
Placeholder[] annotations = method.getAnnotationsByType(Placeholder.class);
for (Placeholder annotation : annotations) {
placeholders.add(annotation.value());
}
}
return new ArrayList<>(placeholders);
}
@NotNull
private List<String> getAliases(Class<?> clazz) {
LinkedHashSet<String> aliases = new LinkedHashSet<>();
for (String placeholder : getPlaceholderNames(clazz)) {
String alias = toUnionAlias(placeholder);
if (!alias.equals(placeholder)) {
aliases.add(alias);
}
}
return new ArrayList<>(aliases);
}
@NotNull
private List<String> combine(Collection<String> first, Collection<String> second) {
LinkedHashSet<String> combined = new LinkedHashSet<>(first);
combined.addAll(second);
return new ArrayList<>(combined);
}
@NotNull
private String normalizePlaceholder(@NotNull String placeholder) {
String normalized = placeholder;
if (normalized.startsWith("topunions_")) {
normalized = "topclans_" + normalized.substring("topunions_".length());
}
if (normalized.startsWith("union_")) {
normalized = "clan_" + normalized.substring("union_".length());
}
normalized = normalized.replace("_union_", "_clan_");
normalized = normalized.replace("in_union", "in_clan");
normalized = normalized.replace("unionchat_player_color", "clanchat_player_color");
normalized = normalized.replace("topunions_position", "topclans_position");
return normalized;
}
@NotNull
private String toUnionAlias(@NotNull String placeholder) {
return placeholder.replace("topclans", "topunions").replace("in_clan", "in_union")
.replace("clanchat_player_color", "unionchat_player_color");
}
}
@@ -2,6 +2,7 @@ package net.sacredlabyrinth.phaed.simpleclans.hooks.papi.resolvers;
import net.sacredlabyrinth.phaed.simpleclans.ClanPlayer;
import net.sacredlabyrinth.phaed.simpleclans.SimpleClans;
import net.sacredlabyrinth.phaed.simpleclans.hooks.papi.PlaceholderBooleanFormatter;
import net.sacredlabyrinth.phaed.simpleclans.hooks.papi.PlaceholderResolver;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
@@ -10,9 +11,6 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
import java.util.Map;
import static me.clip.placeholderapi.PlaceholderAPIPlugin.booleanFalse;
import static me.clip.placeholderapi.PlaceholderAPIPlugin.booleanTrue;
@SuppressWarnings("unused")
public class MemberStatusResolver extends PlaceholderResolver {
@@ -52,7 +50,7 @@ public class MemberStatusResolver extends PlaceholderResolver {
}
return result ? booleanTrue() : booleanFalse();
return result ? PlaceholderBooleanFormatter.trueValue() : PlaceholderBooleanFormatter.falseValue();
}
@@ -1,7 +1,7 @@
package net.sacredlabyrinth.phaed.simpleclans.hooks.papi.resolvers;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import net.sacredlabyrinth.phaed.simpleclans.SimpleClans;
import net.sacredlabyrinth.phaed.simpleclans.hooks.papi.PlaceholderBooleanFormatter;
import net.sacredlabyrinth.phaed.simpleclans.hooks.papi.PlaceholderResolver;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
@@ -40,7 +40,8 @@ public class MethodReturnResolver extends PlaceholderResolver {
if (result instanceof Boolean) {
return ((Boolean) result) ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse();
return ((Boolean) result) ? PlaceholderBooleanFormatter.trueValue()
: PlaceholderBooleanFormatter.falseValue();
}
@@ -1,6 +1,7 @@
package net.sacredlabyrinth.phaed.simpleclans.hooks.papi.resolvers;
import net.sacredlabyrinth.phaed.simpleclans.SimpleClans;
import net.sacredlabyrinth.phaed.simpleclans.hooks.papi.PlaceholderBooleanFormatter;
import net.sacredlabyrinth.phaed.simpleclans.hooks.papi.PlaceholderResolver;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
@@ -9,9 +10,6 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
import java.util.Map;
import static me.clip.placeholderapi.PlaceholderAPIPlugin.booleanFalse;
import static me.clip.placeholderapi.PlaceholderAPIPlugin.booleanTrue;
@SuppressWarnings("unused")
public class NotNullReturnResolver extends PlaceholderResolver {
@@ -33,7 +31,8 @@ public class NotNullReturnResolver extends PlaceholderResolver {
@NotNull String placeholder, @NotNull Map<String, String> config)
{
return invoke(object, method, placeholder) != null ? booleanTrue() : booleanFalse();
return invoke(object, method, placeholder) != null ? PlaceholderBooleanFormatter.trueValue()
: PlaceholderBooleanFormatter.falseValue();
}
+6 -4
View File
@@ -1,10 +1,12 @@
name: Unions-OG
main: net.sacredlabyrinth.phaed.simpleclans.SimpleClans
version: '${version}'
softdepend:
- Vault
- PlaceholderAPI
- LuckPerms
softdepend:
- Vault
- PlaceholderAPI
- Utilities-OG
- MiniPlaceholders
- LuckPerms
- WorldGuard
- DiscordSRV
loadbefore: