From a92ddd89d8796ca41eebba3889bd8ffc39bdde5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hudson=20Andr=C3=A9s?= <123704241+hudsonandres@users.noreply.github.com> Date: Sat, 29 Nov 2025 16:41:14 -0300 Subject: [PATCH 1/3] Adds leaderboard support to PlaceholderAPI - Adds placeholders to display mcMMO leaderboard information through PlaceholderAPI. - `%mcmmo_mctop_:%` - `%mcmmo_mctop_overall:%` - `%mcmmo_mctop_name_:%` - `%mcmmo_mctop_name_overall:%` - `%mcmmo_checklevel_:%` - Added a new explanatory file `PLACEHOLDERS.md` detailing all the placeholders that can be used. --- PLACEHOLDERS.md | 299 ++++++++++++++++++ .../placeholders/CheckLevelPlaceholder.java | 45 +++ .../McTopPositionPlaceholder.java | 64 ++++ .../placeholders/OverallRankPlaceholder.java | 30 ++ .../nossr50/placeholders/PapiExpansion.java | 35 +- 5 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 PLACEHOLDERS.md create mode 100644 src/main/java/com/gmail/nossr50/placeholders/CheckLevelPlaceholder.java create mode 100644 src/main/java/com/gmail/nossr50/placeholders/McTopPositionPlaceholder.java create mode 100644 src/main/java/com/gmail/nossr50/placeholders/OverallRankPlaceholder.java diff --git a/PLACEHOLDERS.md b/PLACEHOLDERS.md new file mode 100644 index 0000000000..f0616842b2 --- /dev/null +++ b/PLACEHOLDERS.md @@ -0,0 +1,299 @@ +# mcMMO Placeholders + +This document describes all placeholders available in mcMMO, including the new placeholders added to facilitate the creation of menus and more detailed ranking systems. + +## Available Skills + +All mcMMO skills can be used in the placeholders: +- `acrobatics` +- `alchemy` +- `archery` +- `axes` +- `excavation` +- `fishing` +- `herbalism` +- `mining` +- `repair` +- `salvage` +- `smelting` +- `swords` +- `taming` +- `unarmed` +- `woodcutting` +- `crossbows` +- `tridents` +- `maces` + +--- + +## Skill Placeholders (Per Skill) + +The following placeholders are available for **all skills** listed above: + +### Skill Level +**Syntax:** `%mcmmo_level_%` + +Returns the player's current level in the specified skill. + +**Examples:** +- `%mcmmo_level_mining%` - Returns Mining level (e.g., "75") +- `%mcmmo_level_combat%` - Returns Combat level +- `%mcmmo_level_woodcutting%` - Returns Woodcutting level + +### Current Skill XP +**Syntax:** `%mcmmo_xp_%` + +Returns the current amount of XP the player has in the skill. + +**Examples:** +- `%mcmmo_xp_mining%` - Returns current Mining XP (e.g., "1245") +- `%mcmmo_xp_fishing%` - Returns current Fishing XP + +### XP Needed for Next Level +**Syntax:** `%mcmmo_xp_needed_%` + +Returns the total amount of XP needed to reach the next level. + +**Examples:** +- `%mcmmo_xp_needed_mining%` - Returns XP needed (e.g., "2000") +- `%mcmmo_xp_needed_combat%` - Returns XP needed for next level + +### XP Remaining for Next Level +**Syntax:** `%mcmmo_xp_remaining_%` + +Returns how much XP is still needed to level up. + +**Examples:** +- `%mcmmo_xp_remaining_mining%` - Returns remaining XP (e.g., "755") +- `%mcmmo_xp_remaining_fishing%` - XP needed for the next level + +### Skill Rank Position +**Syntax:** `%mcmmo_rank_%` + +Returns the player's position in the leaderboard for that skill. + +**Examples:** +- `%mcmmo_rank_mining%` - Returns "12" if the player is in 12th place +- `%mcmmo_rank_combat%` - Position in Combat ranking + +### Skill XP Rate +**Syntax:** `%mcmmo_xprate_%` + +Returns the XP multiplier the player has for that skill (based on permissions). + +**Examples:** +- `%mcmmo_xprate_mining%` - Returns "2.0" if they have double XP +- `%mcmmo_xprate_fishing%` - Returns "1.5" if they have 1.5x XP + +--- + +## Power Level Placeholders (General) + +### Total Power Level +**Syntax:** `%mcmmo_power_level%` + +Returns the player's total power level (sum of all skill levels). + +**Example:** +- `%mcmmo_power_level%` - Returns "850" + +### Power Level Cap +**Syntax:** `%mcmmo_power_level_cap%` + +Returns the maximum power level cap configured on the server. + +**Example:** +- `%mcmmo_power_level_cap%` - Returns "2000" + +### Global XP Rate +**Syntax:** `%mcmmo_xprate%` + +Returns the global XP multiplier configured on the server. + +**Example:** +- `%mcmmo_xprate%` - Returns "1.5" + +### XP Event Active +**Syntax:** `%mcmmo_is_xp_event_active%` + +Returns whether an XP event is active on the server. + +**Return:** +- `true` - If there is an active XP event +- `false` - If there is no event + +--- + +## Party Placeholders + +### Is in Party +**Syntax:** `%mcmmo_in_party%` + +Checks if the player is in a party. + +**Return:** +- `true` - If in a party +- `false` - If not in a party + +### Party Name +**Syntax:** `%mcmmo_party_name%` + +Returns the player's party name (empty if not in any). + +**Example:** +- `%mcmmo_party_name%` - Returns "Adventurers" + +### Is Party Leader +**Syntax:** `%mcmmo_is_party_leader%` + +Checks if the player is the party leader. + +**Return:** +- `true` - If they are the leader +- `false` - If they are not the leader or not in a party + +### Party Leader Name +**Syntax:** `%mcmmo_party_leader%` + +Returns the name of the party leader. + +**Example:** +- `%mcmmo_party_leader%` - Returns "Steve" + +### Party Size +**Syntax:** `%mcmmo_party_size%` + +Returns the number of members in the party. + +**Example:** +- `%mcmmo_party_size%` - Returns "5" + +--- + +## Leaderboard/McTop Placeholders + +### Get Value/Level at Position X (✨ NEW) + +Returns the value (level or power level) of the player at position X in the ranking. + +**Syntax:** +- `%mcmmo_mctop_:%` - For a specific skill +- `%mcmmo_mctop_overall:%` - For overall ranking (power level) + +**Examples:** +- `%mcmmo_mctop_mining:1%` - Returns the Mining level of the 1st place player +- `%mcmmo_mctop_combat:5%` - Returns the Combat level of the 5th place player +- `%mcmmo_mctop_overall:1%` - Returns the power level of the 1st place player + +### Get Player Name at Position X (✨ NEW) + +Returns the player's name at position X in the ranking. + +**Syntax:** +- `%mcmmo_mctop_name_:%` - For a specific skill +- `%mcmmo_mctop_name_overall:%` - For overall ranking + +**Examples:** +- `%mcmmo_mctop_name_mining:1%` - Returns the name of the 1st place player in Mining +- `%mcmmo_mctop_name_combat:10%` - Returns the name of the 10th place player in Combat +- `%mcmmo_mctop_name_overall:1%` - Returns the name of the 1st place player in overall ranking + +### Get Your Overall Rank Position (✨ NEW) + +Returns the player's position in the overall ranking (power level). + +**Syntax:** +- `%mcmmo_rank_overall%` + +**Example:** +- `%mcmmo_rank_overall%` - Returns "15" if the player is in 15th place + +--- + +## Level Check Placeholder (✨ NEW) + +Checks if the player has reached the required level in a specific skill. + +**Syntax:** +- `%mcmmo_checklevel_:%` + +**Return:** +- `✔` - If the player has the required level or higher +- `✘` - If the player does NOT have the required level + +**Examples:** +- `%mcmmo_checklevel_mining:50%` - Returns ✔ if player has Mining level 50+, otherwise ✘ +- `%mcmmo_checklevel_combat:100%` - Returns ✔ if player has Combat level 100+, otherwise ✘ +- `%mcmmo_checklevel_woodcutting:25%` - Returns ✔ if player has Woodcutting level 25+, otherwise ✘ + +--- + +## Available Skills Reference + +All mcMMO skills that can be used in the placeholders above: +- `acrobatics` - Acrobatics +- `alchemy` - Alchemy +- `archery` - Archery +- `axes` - Axes +- `excavation` - Excavation +- `fishing` - Fishing +- `herbalism` - Herbalism +- `mining` - Mining +- `repair` - Repair +- `salvage` - Salvage +- `smelting` - Smelting +- `swords` - Swords +- `taming` - Taming +- `unarmed` - Unarmed +- `woodcutting` - Woodcutting +- `crossbows` - Crossbows +- `tridents` - Tridents +- `maces` - Maces + +--- + +## Menu Usage Examples + +### Top 10 Mining Menu +```yaml +display: + name: "&6Top 10 - Mining" + lore: + - "&71st - %mcmmo_mctop_name_mining:1% &f- &e%mcmmo_mctop_mining:1%" + - "&72nd - %mcmmo_mctop_name_mining:2% &f- &e%mcmmo_mctop_mining:2%" + - "&73rd - %mcmmo_mctop_name_mining:3% &f- &e%mcmmo_mctop_mining:3%" + - "&74th - %mcmmo_mctop_name_mining:4% &f- &e%mcmmo_mctop_mining:4%" + - "&75th - %mcmmo_mctop_name_mining:5% &f- &e%mcmmo_mctop_mining:5%" +``` + +### Overall Ranking Menu +```yaml +display: + name: "&6Overall Ranking - Power Level" + lore: + - "&7Your position: &e#%mcmmo_rank_overall%" + - "" + - "&6Top 3:" + - "&e1st - %mcmmo_mctop_name_overall:1% &f- &6%mcmmo_mctop_overall:1%" + - "&e2nd - %mcmmo_mctop_name_overall:2% &f- &6%mcmmo_mctop_overall:2%" + - "&e3rd - %mcmmo_mctop_name_overall:3% &f- &6%mcmmo_mctop_overall:3%" +``` + +### Requirement Check Menu +```yaml +display: + name: "&6Unlock Special Ability" + lore: + - "&7Requirements:" + - "&7Mining Level 50: %mcmmo_checklevel_mining:50%" + - "&7Combat Level 75: %mcmmo_checklevel_combat:75%" + - "&7Woodcutting Level 30: %mcmmo_checklevel_woodcutting:30%" +``` + +## Technical Notes + +- Placeholders work with PlaceholderAPI +- Data is obtained directly from the mcMMO database +- PlaceholderAPI cache is used to optimize queries +- Invalid positions (less than 1 or greater than total players) return empty string +- Invalid levels or non-existent skills return ✘ for checklevel diff --git a/src/main/java/com/gmail/nossr50/placeholders/CheckLevelPlaceholder.java b/src/main/java/com/gmail/nossr50/placeholders/CheckLevelPlaceholder.java new file mode 100644 index 0000000000..d4e7594b60 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/placeholders/CheckLevelPlaceholder.java @@ -0,0 +1,45 @@ +package com.gmail.nossr50.placeholders; + +import com.gmail.nossr50.datatypes.skills.PrimarySkillType; +import org.bukkit.entity.Player; + +/** + * Placeholder for checking if a player has reached a specific level in a skill + * Usage: %mcmmo_checklevel_:% + * Returns: ✔ if player has the level, ✘ if not + */ +public class CheckLevelPlaceholder implements Placeholder { + private final PapiExpansion papiExpansion; + private final PrimarySkillType skill; + + public CheckLevelPlaceholder(PapiExpansion papiExpansion, PrimarySkillType skill) { + this.papiExpansion = papiExpansion; + this.skill = skill; + } + + @Override + public String process(Player player, String params) { + if (params == null || params.isEmpty()) { + return "§c✘"; + } + + int requiredLevel; + try { + requiredLevel = Integer.parseInt(params); + } catch (NumberFormatException e) { + return "§c✘"; + } + + Integer currentLevel = papiExpansion.getSkillLevel(skill, player); + if (currentLevel == null) { + return "§c✘"; + } + + return currentLevel >= requiredLevel ? "§a✔" : "§c✘"; + } + + @Override + public String getName() { + return "checklevel_" + skill.toString().toLowerCase(); + } +} diff --git a/src/main/java/com/gmail/nossr50/placeholders/McTopPositionPlaceholder.java b/src/main/java/com/gmail/nossr50/placeholders/McTopPositionPlaceholder.java new file mode 100644 index 0000000000..e78ce3f270 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/placeholders/McTopPositionPlaceholder.java @@ -0,0 +1,64 @@ +package com.gmail.nossr50.placeholders; + +import com.gmail.nossr50.datatypes.database.PlayerStat; +import com.gmail.nossr50.datatypes.skills.PrimarySkillType; +import org.bukkit.entity.Player; + +import java.util.List; + +/** + * Placeholder for getting the player name at a specific position in the leaderboard + * Usage: %mcmmo_mctop_name_:% or %mcmmo_mctop_name_overall:% + */ +public class McTopPositionPlaceholder implements Placeholder { + private final PapiExpansion papiExpansion; + private final PrimarySkillType skill; + private final boolean isName; + + public McTopPositionPlaceholder(PapiExpansion papiExpansion, PrimarySkillType skill, boolean isName) { + this.papiExpansion = papiExpansion; + this.skill = skill; + this.isName = isName; + } + + @Override + public String process(Player player, String params) { + if (params == null || params.isEmpty()) { + return ""; + } + + int position; + try { + position = Integer.parseInt(params); + } catch (NumberFormatException e) { + return ""; + } + + if (position < 1) { + return ""; + } + + List leaderboard = papiExpansion.getLeaderboard(skill, position); + + if (leaderboard == null || leaderboard.isEmpty() || position > leaderboard.size()) { + return ""; + } + + PlayerStat stat = leaderboard.get(position - 1); + + if (isName) { + return stat.playerName(); + } else { + return String.valueOf(stat.value()); + } + } + + @Override + public String getName() { + String prefix = isName ? "mctop_name" : "mctop"; + if (skill == null) { + return prefix + "_overall"; + } + return prefix + "_" + skill.toString().toLowerCase(); + } +} diff --git a/src/main/java/com/gmail/nossr50/placeholders/OverallRankPlaceholder.java b/src/main/java/com/gmail/nossr50/placeholders/OverallRankPlaceholder.java new file mode 100644 index 0000000000..0dc76b6d2f --- /dev/null +++ b/src/main/java/com/gmail/nossr50/placeholders/OverallRankPlaceholder.java @@ -0,0 +1,30 @@ +package com.gmail.nossr50.placeholders; + +import com.gmail.nossr50.api.ExperienceAPI; +import org.bukkit.entity.Player; + +/** + * Placeholder for getting the overall power level rank + * Usage: %mcmmo_rank_overall% + */ +public class OverallRankPlaceholder implements Placeholder { + + public OverallRankPlaceholder(PapiExpansion papiExpansion) { + // Constructor for consistency with other placeholders + } + + @Override + public String process(Player player, String params) { + try { + int rank = ExperienceAPI.getPlayerRankOverall(player.getUniqueId()); + return String.valueOf(rank); + } catch (Exception ex) { + return ""; + } + } + + @Override + public String getName() { + return "rank_overall"; + } +} diff --git a/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java b/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java index 06ad1b25b3..7e543b4e2d 100644 --- a/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java +++ b/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java @@ -2,6 +2,7 @@ import com.gmail.nossr50.api.ExperienceAPI; import com.gmail.nossr50.config.experience.ExperienceConfig; +import com.gmail.nossr50.datatypes.database.PlayerStat; import com.gmail.nossr50.datatypes.party.Party; import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.skills.PrimarySkillType; @@ -9,6 +10,7 @@ import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.player.UserManager; import com.gmail.nossr50.util.text.StringUtils; +import java.util.List; import java.util.Map; import java.util.TreeMap; import me.clip.placeholderapi.PlaceholderAPIPlugin; @@ -196,6 +198,18 @@ public String isExpEventActive(Player player) { : PlaceholderAPIPlugin.booleanFalse(); } + public List getLeaderboard(PrimarySkillType skill, int position) { + try { + // Calculate which page to fetch (10 entries per page by default) + int statsPerPage = 10; + int pageNumber = ((position - 1) / statsPerPage) + 1; + + return mcMMO.getDatabaseManager().readLeaderboard(skill, pageNumber, statsPerPage); + } catch (RuntimeException ex) { + return null; + } + } + public void registerPlaceholder(Placeholder placeholder) { final Placeholder registered = placeholders.get(placeholder.getName()); if (registered != null) { @@ -206,7 +220,7 @@ public void registerPlaceholder(Placeholder placeholder) { placeholders.put(placeholder.getName(), placeholder); } - protected void init() { + private void init() { for (PrimarySkillType skill : PrimarySkillType.values()) { // %mcmmo_level_% @@ -226,6 +240,15 @@ protected void init() { //%mcmmo_xprate_% registerPlaceholder(new SkillXpRatePlaceholder(this, skill)); + + //%mcmmo_mctop__% + registerPlaceholder(new McTopPositionPlaceholder(this, skill, false)); + + //%mcmmo_mctop_name__% + registerPlaceholder(new McTopPositionPlaceholder(this, skill, true)); + + //%mcmmo_checklevel__% + registerPlaceholder(new CheckLevelPlaceholder(this, skill)); } //%mcmmo_power_level% @@ -251,8 +274,18 @@ protected void init() { // %mcmmo_is_xp_event_active% registerPlaceholder(new XpEventActivePlaceholder(this)); + // %mcmmo_xprate% registerPlaceholder(new XpRatePlaceholder(this)); + + // %mcmmo_rank_overall% + registerPlaceholder(new OverallRankPlaceholder(this)); + + // %mcmmo_mctop_overall_% + registerPlaceholder(new McTopPositionPlaceholder(this, null, false)); + + // %mcmmo_mctop_name_overall_% + registerPlaceholder(new McTopPositionPlaceholder(this, null, true)); } } From 59eceff11c5f50b0746d871d096716dc8a431a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hudson=20Andr=C3=A9s?= <123704241+hudsonandres@users.noreply.github.com> Date: Sun, 30 Nov 2025 21:26:55 -0300 Subject: [PATCH 2/3] Updates to the code - Simplifies mctop placeholders - Refactors mctop placeholders to use a single placeholder for both skill level/score and player name. - This removes the need for separate "mctop" and "mctop_name" placeholders, improving usability and reducing code duplication. --- PLACEHOLDERS.md | 14 ++--- .../placeholders/CheckLevelPlaceholder.java | 3 + .../placeholders/McTopNamePlaceholder.java | 56 +++++++++++++++++++ .../McTopPositionPlaceholder.java | 28 ++++------ .../placeholders/OverallRankPlaceholder.java | 4 +- .../nossr50/placeholders/PapiExpansion.java | 26 ++++----- 6 files changed, 94 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/gmail/nossr50/placeholders/McTopNamePlaceholder.java diff --git a/PLACEHOLDERS.md b/PLACEHOLDERS.md index f0616842b2..f25b2fd44d 100644 --- a/PLACEHOLDERS.md +++ b/PLACEHOLDERS.md @@ -37,7 +37,7 @@ Returns the player's current level in the specified skill. **Examples:** - `%mcmmo_level_mining%` - Returns Mining level (e.g., "75") -- `%mcmmo_level_combat%` - Returns Combat level +- `%mcmmo_level_swords%` - Returns Swords level - `%mcmmo_level_woodcutting%` - Returns Woodcutting level ### Current Skill XP @@ -56,7 +56,7 @@ Returns the total amount of XP needed to reach the next level. **Examples:** - `%mcmmo_xp_needed_mining%` - Returns XP needed (e.g., "2000") -- `%mcmmo_xp_needed_combat%` - Returns XP needed for next level +- `%mcmmo_xp_needed_swords%` - Returns XP needed for next level ### XP Remaining for Next Level **Syntax:** `%mcmmo_xp_remaining_%` @@ -74,7 +74,7 @@ Returns the player's position in the leaderboard for that skill. **Examples:** - `%mcmmo_rank_mining%` - Returns "12" if the player is in 12th place -- `%mcmmo_rank_combat%` - Position in Combat ranking +- `%mcmmo_rank_swords%` - Position in Swords ranking ### Skill XP Rate **Syntax:** `%mcmmo_xprate_%` @@ -182,7 +182,7 @@ Returns the value (level or power level) of the player at position X in the rank **Examples:** - `%mcmmo_mctop_mining:1%` - Returns the Mining level of the 1st place player -- `%mcmmo_mctop_combat:5%` - Returns the Combat level of the 5th place player +- `%mcmmo_mctop_swords:5%` - Returns the Swords level of the 5th place player - `%mcmmo_mctop_overall:1%` - Returns the power level of the 1st place player ### Get Player Name at Position X (✨ NEW) @@ -195,7 +195,7 @@ Returns the player's name at position X in the ranking. **Examples:** - `%mcmmo_mctop_name_mining:1%` - Returns the name of the 1st place player in Mining -- `%mcmmo_mctop_name_combat:10%` - Returns the name of the 10th place player in Combat +- `%mcmmo_mctop_name_swords:10%` - Returns the name of the 10th place player in Swords - `%mcmmo_mctop_name_overall:1%` - Returns the name of the 1st place player in overall ranking ### Get Your Overall Rank Position (✨ NEW) @@ -223,7 +223,7 @@ Checks if the player has reached the required level in a specific skill. **Examples:** - `%mcmmo_checklevel_mining:50%` - Returns ✔ if player has Mining level 50+, otherwise ✘ -- `%mcmmo_checklevel_combat:100%` - Returns ✔ if player has Combat level 100+, otherwise ✘ +- `%mcmmo_checklevel_swords:100%` - Returns ✔ if player has Swords level 100+, otherwise ✘ - `%mcmmo_checklevel_woodcutting:25%` - Returns ✔ if player has Woodcutting level 25+, otherwise ✘ --- @@ -286,7 +286,7 @@ display: lore: - "&7Requirements:" - "&7Mining Level 50: %mcmmo_checklevel_mining:50%" - - "&7Combat Level 75: %mcmmo_checklevel_combat:75%" + - "&Swords Level 75: %mcmmo_checklevel_swords:75%" - "&7Woodcutting Level 30: %mcmmo_checklevel_woodcutting:30%" ``` diff --git a/src/main/java/com/gmail/nossr50/placeholders/CheckLevelPlaceholder.java b/src/main/java/com/gmail/nossr50/placeholders/CheckLevelPlaceholder.java index d4e7594b60..b4382d31d8 100644 --- a/src/main/java/com/gmail/nossr50/placeholders/CheckLevelPlaceholder.java +++ b/src/main/java/com/gmail/nossr50/placeholders/CheckLevelPlaceholder.java @@ -15,6 +15,9 @@ public class CheckLevelPlaceholder implements Placeholder { public CheckLevelPlaceholder(PapiExpansion papiExpansion, PrimarySkillType skill) { this.papiExpansion = papiExpansion; this.skill = skill; + // The papiExpansion parameter is intentionally "unused" in this class. + // It's essential for the class to function properly. + // The placeholder needs access to the PapiExpansion instance to query skill levels. } @Override diff --git a/src/main/java/com/gmail/nossr50/placeholders/McTopNamePlaceholder.java b/src/main/java/com/gmail/nossr50/placeholders/McTopNamePlaceholder.java new file mode 100644 index 0000000000..8dad0dbf7b --- /dev/null +++ b/src/main/java/com/gmail/nossr50/placeholders/McTopNamePlaceholder.java @@ -0,0 +1,56 @@ +package com.gmail.nossr50.placeholders; + +import com.gmail.nossr50.datatypes.database.PlayerStat; +import com.gmail.nossr50.datatypes.skills.PrimarySkillType; +import org.bukkit.entity.Player; + +import java.util.List; + +/** + * Placeholder for getting the player name at a specific position in the leaderboard + * Usage: %mcmmo_mctop_name_:% or %mcmmo_mctop_name_overall:% + */ +public class McTopNamePlaceholder implements Placeholder { + private final PapiExpansion papiExpansion; + private final PrimarySkillType skill; + + public McTopNamePlaceholder(PapiExpansion papiExpansion, PrimarySkillType skill) { + this.papiExpansion = papiExpansion; + this.skill = skill; + } + + @Override + public String process(Player player, String params) { + if (params == null || params.isEmpty()) { + return ""; + } + + int position; + try { + position = Integer.parseInt(params); + } catch (NumberFormatException e) { + return ""; + } + + if (position < 1) { + return ""; + } + + List leaderboard = papiExpansion.getLeaderboard(skill, position); + + if (leaderboard == null || leaderboard.isEmpty() || position > leaderboard.size()) { + return ""; + } + + PlayerStat stat = leaderboard.get(position - 1); + return stat.playerName(); + } + + @Override + public String getName() { + if (skill == null) { + return "mctop_name_overall"; + } + return "mctop_name_" + skill.toString().toLowerCase(); + } +} diff --git a/src/main/java/com/gmail/nossr50/placeholders/McTopPositionPlaceholder.java b/src/main/java/com/gmail/nossr50/placeholders/McTopPositionPlaceholder.java index e78ce3f270..6d2ddb2d4a 100644 --- a/src/main/java/com/gmail/nossr50/placeholders/McTopPositionPlaceholder.java +++ b/src/main/java/com/gmail/nossr50/placeholders/McTopPositionPlaceholder.java @@ -7,18 +7,16 @@ import java.util.List; /** - * Placeholder for getting the player name at a specific position in the leaderboard - * Usage: %mcmmo_mctop_name_:% or %mcmmo_mctop_name_overall:% + * Placeholder for getting the skill level/score at a specific position in the leaderboard + * Usage: %mcmmo_mctop_:% or %mcmmo_mctop_overall:% */ public class McTopPositionPlaceholder implements Placeholder { private final PapiExpansion papiExpansion; private final PrimarySkillType skill; - private final boolean isName; - public McTopPositionPlaceholder(PapiExpansion papiExpansion, PrimarySkillType skill, boolean isName) { + public McTopPositionPlaceholder(PapiExpansion papiExpansion, PrimarySkillType skill) { this.papiExpansion = papiExpansion; this.skill = skill; - this.isName = isName; } @Override @@ -38,27 +36,25 @@ public String process(Player player, String params) { return ""; } + final int statsPerPage = 10; // Adjust if your leaderboard uses a different page size List leaderboard = papiExpansion.getLeaderboard(skill, position); - if (leaderboard == null || leaderboard.isEmpty() || position > leaderboard.size()) { + if (leaderboard == null || leaderboard.isEmpty()) { return ""; } - - PlayerStat stat = leaderboard.get(position - 1); - - if (isName) { - return stat.playerName(); - } else { - return String.valueOf(stat.value()); + int pageIndex = (position - 1) % statsPerPage; + if (pageIndex >= leaderboard.size()) { + return ""; } + PlayerStat stat = leaderboard.get(pageIndex); + return String.valueOf(stat.value()); } @Override public String getName() { - String prefix = isName ? "mctop_name" : "mctop"; if (skill == null) { - return prefix + "_overall"; + return "mctop_overall"; } - return prefix + "_" + skill.toString().toLowerCase(); + return "mctop_" + skill.toString().toLowerCase(); } } diff --git a/src/main/java/com/gmail/nossr50/placeholders/OverallRankPlaceholder.java b/src/main/java/com/gmail/nossr50/placeholders/OverallRankPlaceholder.java index 0dc76b6d2f..d6b02b0b40 100644 --- a/src/main/java/com/gmail/nossr50/placeholders/OverallRankPlaceholder.java +++ b/src/main/java/com/gmail/nossr50/placeholders/OverallRankPlaceholder.java @@ -10,7 +10,9 @@ public class OverallRankPlaceholder implements Placeholder { public OverallRankPlaceholder(PapiExpansion papiExpansion) { - // Constructor for consistency with other placeholders + // The papiExpansion parameter is intentionally unused in this class. + // It is kept for consistency with other Placeholder implementations. + // If future functionality requires access to papiExpansion, it can be stored as a field. } @Override diff --git a/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java b/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java index 7e543b4e2d..09d421d311 100644 --- a/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java +++ b/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java @@ -200,12 +200,12 @@ public String isExpEventActive(Player player) { public List getLeaderboard(PrimarySkillType skill, int position) { try { - // Calculate which page to fetch (10 entries per page by default) - int statsPerPage = 10; - int pageNumber = ((position - 1) / statsPerPage) + 1; + // Fetch only the specific entry needed + int statsPerPage = 1; + int pageNumber = position; // Assuming readLeaderboard uses pageNumber as offset for single entry return mcMMO.getDatabaseManager().readLeaderboard(skill, pageNumber, statsPerPage); - } catch (RuntimeException ex) { + } catch (InvalidSkillException ex) { return null; } } @@ -241,13 +241,13 @@ private void init() { //%mcmmo_xprate_% registerPlaceholder(new SkillXpRatePlaceholder(this, skill)); - //%mcmmo_mctop__% - registerPlaceholder(new McTopPositionPlaceholder(this, skill, false)); + //%mcmmo_mctop_:% + registerPlaceholder(new McTopPositionPlaceholder(this, skill)); - //%mcmmo_mctop_name__% - registerPlaceholder(new McTopPositionPlaceholder(this, skill, true)); + //%mcmmo_mctop_name_:% + registerPlaceholder(new McTopNamePlaceholder(this, skill)); - //%mcmmo_checklevel__% + //%mcmmo_checklevel_:% registerPlaceholder(new CheckLevelPlaceholder(this, skill)); } @@ -281,11 +281,11 @@ private void init() { // %mcmmo_rank_overall% registerPlaceholder(new OverallRankPlaceholder(this)); - // %mcmmo_mctop_overall_% - registerPlaceholder(new McTopPositionPlaceholder(this, null, false)); + // %mcmmo_mctop_overall:% + registerPlaceholder(new McTopPositionPlaceholder(this, null)); - // %mcmmo_mctop_name_overall_% - registerPlaceholder(new McTopPositionPlaceholder(this, null, true)); + // %mcmmo_mctop_name_overall:% + registerPlaceholder(new McTopNamePlaceholder(this, null)); } } From dee4744e28d2b148a637db5e0dbc5740ba6ca71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hudson=20Andr=C3=A9s?= <123704241+hudsonandres@users.noreply.github.com> Date: Sun, 30 Nov 2025 21:57:21 -0300 Subject: [PATCH 3/3] Adds the InvalidSkillException import to the PapiExpansion class. - This import is necessary for handling potential exceptions related to invalid skill references within the PlaceholderAPI expansion. --- src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java b/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java index 09d421d311..1ea45f3f40 100644 --- a/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java +++ b/src/main/java/com/gmail/nossr50/placeholders/PapiExpansion.java @@ -1,6 +1,7 @@ package com.gmail.nossr50.placeholders; import com.gmail.nossr50.api.ExperienceAPI; +import com.gmail.nossr50.api.exceptions.InvalidSkillException; import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.datatypes.database.PlayerStat; import com.gmail.nossr50.datatypes.party.Party;