Skip to content

Commit f977c7d

Browse files
committed
📝 Add placeholder playerlist
1 parent eb4c1cb commit f977c7d

3 files changed

Lines changed: 117 additions & 0 deletions

File tree

changelog.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@
66

77
# Unreleased
88

9+
- Added player list placeholders for retrieving online player information by index (1-based, sorted alphabetically, excludes vanished players):
10+
- `%zessentials_playerlist_count%` Returns the number of visible online players (excludes vanished)
11+
- `%zessentials_playerlist_<index>_name%` Returns the player's name at the given index
12+
- `%zessentials_playerlist_<index>_uuid%` Returns the player's UUID at the given index
13+
- `%zessentials_playerlist_<index>_ping%` Returns the player's ping in milliseconds
14+
- `%zessentials_playerlist_<index>_colored_ping%` Returns the player's ping with color based on quality
15+
- `%zessentials_playerlist_<index>_level%` Returns the player's experience level
16+
- `%zessentials_playerlist_<index>_health%` Returns the player's current health
17+
- `%zessentials_playerlist_<index>_max_health%` Returns the player's max health
18+
- `%zessentials_playerlist_<index>_food_level%` Returns the player's food level
19+
- `%zessentials_playerlist_<index>_gamemode%` Returns the player's game mode
20+
- `%zessentials_playerlist_<index>_world%` Returns the player's world name
21+
- `%zessentials_playerlist_<index>_x%` Returns the player's X coordinate
22+
- `%zessentials_playerlist_<index>_y%` Returns the player's Y coordinate
23+
- `%zessentials_playerlist_<index>_z%` Returns the player's Z coordinate
24+
- `%zessentials_playerlist_<index>_displayname%` Returns the player's display name
25+
- `%zessentials_playerlist_<index>_is_flying%` Returns true if the player is flying
26+
- `%zessentials_playerlist_<index>_is_op%` Returns true if the player is operator
27+
- `%zessentials_playerlist_<index>_is_sneaking%` Returns true if the player is sneaking
28+
- `%zessentials_playerlist_<index>_is_afk%` Returns true if the player is AFK
29+
930
# 1.0.3.6
1031

1132
- Updated zMenu to version 1.1.1.2

src/main/java/fr/maxlego08/essentials/ZEssentialsPlugin.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
import fr.maxlego08.essentials.user.placeholders.ArmorPlaceholders;
100100
import fr.maxlego08.essentials.user.placeholders.EconomyBaltopPlaceholders;
101101
import fr.maxlego08.essentials.user.placeholders.NearPlaceholders;
102+
import fr.maxlego08.essentials.user.placeholders.PlayerListPlaceholders;
102103
import fr.maxlego08.essentials.user.placeholders.PlayerPlaceholders;
103104
import fr.maxlego08.essentials.user.placeholders.RandomWordPlaceholders;
104105
import fr.maxlego08.essentials.user.placeholders.ReplacePlaceholders;
@@ -268,6 +269,7 @@ public void onEnable() {
268269
this.registerPlaceholder(ArmorPlaceholders.class);
269270
this.registerPlaceholder(NearPlaceholders.class);
270271
this.registerPlaceholder(PlayerPlaceholders.class);
272+
this.registerPlaceholder(PlayerListPlaceholders.class);
271273
this.randomWord = this.registerPlaceholder(RandomWordPlaceholders.class);
272274

273275
new Metrics(this, 21703);
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package fr.maxlego08.essentials.user.placeholders;
2+
3+
import fr.maxlego08.essentials.api.EssentialsPlugin;
4+
import fr.maxlego08.essentials.api.placeholders.Placeholder;
5+
import fr.maxlego08.essentials.api.placeholders.PlaceholderRegister;
6+
import fr.maxlego08.essentials.zutils.utils.ZUtils;
7+
import org.bukkit.entity.Player;
8+
9+
import java.text.DecimalFormat;
10+
import java.util.Comparator;
11+
import java.util.List;
12+
13+
public class PlayerListPlaceholders extends ZUtils implements PlaceholderRegister {
14+
15+
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##");
16+
17+
@Override
18+
public void register(Placeholder placeholder, EssentialsPlugin plugin) {
19+
20+
String empty = plugin.getConfiguration().getPlaceholderEmpty();
21+
22+
// Indexed player placeholders: %zessentials_playerlist_<index>_name%
23+
placeholder.register("playerlist_", (player, args) -> {
24+
25+
if (args.equalsIgnoreCase("count")) {
26+
return String.valueOf(getOnlinePlayers(player).size());
27+
}
28+
try {
29+
String[] parts = args.split("_", 2);
30+
if (parts.length != 2) return empty;
31+
32+
int index = Integer.parseInt(parts[0]);
33+
String property = parts[1];
34+
35+
List<? extends Player> players = getOnlinePlayers(player);
36+
if (index < 0 || index >= players.size()) return empty;
37+
38+
Player target = players.get(index);
39+
return getProperty(target, property, empty);
40+
} catch (NumberFormatException exception) {
41+
return empty;
42+
}
43+
}, "Returns player information at the given index (1-based)", "index", "property (name, uuid, ping, colored_ping, level, health, max_health, food_level, gamemode, world, x, y, z, displayname, is_flying, is_op, is_sneaking, is_afk)");
44+
45+
// Count placeholders
46+
placeholder.register("playerlist_count", player -> String.valueOf(getOnlinePlayers(player).size()), "Returns the number of visible online players (excludes vanished)");
47+
}
48+
49+
private List<? extends Player> getOnlinePlayers(Player viewer) {
50+
return viewer.getServer().getOnlinePlayers().stream()
51+
.filter(p -> !isVanishedFor(p, viewer))
52+
.sorted(Comparator.comparing(Player::getName, String.CASE_INSENSITIVE_ORDER))
53+
.toList();
54+
}
55+
56+
private String getProperty(Player target, String property, String empty) {
57+
return switch (property) {
58+
case "name" -> target.getName();
59+
case "uuid" -> target.getUniqueId().toString();
60+
case "ping" -> String.valueOf(target.getPing());
61+
case "colored_ping" -> {
62+
int ping = target.getPing();
63+
String color;
64+
if (ping < 50) color = "&a";
65+
else if (ping < 100) color = "&2";
66+
else if (ping < 200) color = "&e";
67+
else if (ping < 300) color = "&c";
68+
else color = "&4";
69+
yield color + ping;
70+
}
71+
case "level" -> String.valueOf(target.getLevel());
72+
case "health" -> DECIMAL_FORMAT.format(target.getHealth());
73+
case "max_health" -> {
74+
var attribute = target.getAttribute(org.bukkit.attribute.Attribute.MAX_HEALTH);
75+
yield attribute != null ? DECIMAL_FORMAT.format(attribute.getValue()) : "20";
76+
}
77+
case "food_level" -> String.valueOf(target.getFoodLevel());
78+
case "gamemode" -> target.getGameMode().name();
79+
case "world" -> target.getWorld().getName();
80+
case "x" -> String.valueOf(target.getLocation().getBlockX());
81+
case "y" -> String.valueOf(target.getLocation().getBlockY());
82+
case "z" -> String.valueOf(target.getLocation().getBlockZ());
83+
case "displayname" -> target.getDisplayName();
84+
case "is_flying" -> String.valueOf(target.isFlying());
85+
case "is_op" -> String.valueOf(target.isOp());
86+
case "is_sneaking" -> String.valueOf(target.isSneaking());
87+
case "is_afk" -> {
88+
var metadata = target.getMetadata("afk");
89+
yield metadata.isEmpty() ? "false" : String.valueOf(metadata.getFirst().asBoolean());
90+
}
91+
default -> empty;
92+
};
93+
}
94+
}

0 commit comments

Comments
 (0)