Skip to content

Commit 722092d

Browse files
authored
feat: add more placeholders (#463)
1 parent 164c6c7 commit 722092d

File tree

1 file changed

+126
-150
lines changed

1 file changed

+126
-150
lines changed

bukkit/src/main/java/net/william278/husktowns/hook/PlaceholderAPIHook.java

+126-150
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
import org.jetbrains.annotations.NotNull;
3838
import org.jetbrains.annotations.Nullable;
3939

40+
import java.math.BigDecimal;
41+
import java.math.RoundingMode;
42+
import java.text.DecimalFormat;
4043
import java.util.*;
4144
import java.util.stream.Collectors;
4245

@@ -54,6 +57,17 @@ public static class HuskTownsExpansion extends PlaceholderExpansion {
5457

5558
private static final String NOT_IN_TOWN_COLOR = "#aaaaaa";
5659
private static final String WILDERNESS_COLOR = "#2e2e2e";
60+
private static final BigDecimal THOUSAND = BigDecimal.valueOf(1000);
61+
private static final String DECIMAL_FORMAT = "#.#";
62+
private static final TreeMap<BigDecimal, String> NUMBER_FORMAT = new TreeMap<>();
63+
64+
static {
65+
NUMBER_FORMAT.put(THOUSAND, "k");
66+
NUMBER_FORMAT.put(THOUSAND.pow(2), "M");
67+
NUMBER_FORMAT.put(THOUSAND.pow(3), "G");
68+
NUMBER_FORMAT.put(THOUSAND.pow(4), "T");
69+
NUMBER_FORMAT.put(THOUSAND.pow(5), "Q");
70+
}
5771

5872
@NotNull
5973
private final HuskTowns plugin;
@@ -68,6 +82,13 @@ public String onRequest(@Nullable OfflinePlayer offlinePlayer, @NotNull String p
6882
return plugin.getLocales().getNotApplicable();
6983
}
7084

85+
if (params.startsWith("town_leaderboard_")) {
86+
if (params.length() == 17) {
87+
return null;
88+
}
89+
return getTownLeaderboard(params.substring(17));
90+
}
91+
7192
// Ensure the player is online
7293
if (offlinePlayer == null || !offlinePlayer.isOnline() || offlinePlayer.getPlayer() == null) {
7394
return plugin.getLocales().getRawLocale("placeholder_player_offline")
@@ -76,135 +97,82 @@ public String onRequest(@Nullable OfflinePlayer offlinePlayer, @NotNull String p
7697

7798
// Return the requested placeholder
7899
final OnlineUser player = BukkitUser.adapt(offlinePlayer.getPlayer(), plugin);
79-
return switch (params) {
80-
case "town_name" -> plugin.getUserTown(player)
100+
101+
if (params.startsWith("town_")) {
102+
if (params.length() == 5) {
103+
return null;
104+
}
105+
return getTown(player, params.substring(5));
106+
}
107+
108+
if (params.startsWith("current_location_")) {
109+
if (params.length() == 17) {
110+
return null;
111+
}
112+
return getCurrentLocation(player, params.substring(17));
113+
}
114+
115+
return null;
116+
}
117+
118+
@Nullable
119+
private String getTown(@NotNull OnlineUser player, @NotNull String identifier) {
120+
return switch (identifier) {
121+
case "name" -> plugin.getUserTown(player)
81122
.map(Member::town)
82123
.map(Town::getName)
83124
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
84125
.orElse("Not in town"));
85126

86-
case "town_role" -> plugin.getUserTown(player)
127+
case "role" -> plugin.getUserTown(player)
87128
.map(Member::role)
88129
.map(Role::getName)
89130
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
90131
.orElse("Not in town"));
91132

92-
case "town_mayor" -> plugin.getUserTown(player)
93-
.map(Member::town)
94-
.map(town -> resolveTownMemberName(town, town.getMayor()).orElse("?"))
95-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
96-
.orElse("Not in town"));
97-
98-
case "town_color" -> plugin.getUserTown(player)
133+
case "color" -> plugin.getUserTown(player)
99134
.map(Member::town)
100135
.map(Town::getColorRgb)
101136
.orElse(NOT_IN_TOWN_COLOR);
102137

103-
case "town_members" -> plugin.getUserTown(player)
104-
.map(Member::town)
105-
.map(town -> town.getMembers().keySet().stream()
106-
.map(uuid -> resolveTownMemberName(town, uuid).orElse("?"))
107-
.collect(Collectors.joining(", ")))
108-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
109-
.orElse("Not in town"));
110-
111-
case "town_member_count" -> plugin.getUserTown(player)
112-
.map(Member::town)
113-
.map(Town::getMembers)
114-
.map(members -> String.valueOf(members.size()))
115-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
116-
.orElse("Not in town"));
117-
118-
case "town_claim_count" -> plugin.getUserTown(player)
138+
default -> plugin.getUserTown(player)
119139
.map(Member::town)
120-
.map(Town::getClaimCount)
121-
.map(String::valueOf)
122-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
123-
.orElse("Not in town"));
124-
125-
case "town_max_claims" -> plugin.getUserTown(player)
126-
.map(Member::town)
127-
.map(town -> town.getMaxClaims(plugin))
128-
.map(String::valueOf)
129-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
130-
.orElse("Not in town"));
131-
132-
case "town_max_members" -> plugin.getUserTown(player)
133-
.map(Member::town)
134-
.map(town -> town.getMaxMembers(plugin))
135-
.map(String::valueOf)
136-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
137-
.orElse("Not in town"));
138-
139-
case "town_crop_growth_rate" -> plugin.getUserTown(player)
140-
.map(Member::town)
141-
.map(town -> town.getCropGrowthRate(plugin))
142-
.map(String::valueOf)
143-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
144-
.orElse("Not in town"));
145-
146-
case "town_mob_spawner_rate" -> plugin.getUserTown(player)
147-
.map(Member::town)
148-
.map(town -> town.getMobSpawnerRate(plugin))
149-
.map(String::valueOf)
150-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
151-
.orElse("Not in town"));
152-
153-
case "town_money" -> plugin.getUserTown(player)
154-
.map(Member::town)
155-
.map(Town::getMoney)
156-
.map(plugin::formatMoney)
157-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
158-
.orElse("Not in town"));
159-
160-
case "town_level_up_cost" -> plugin.getUserTown(player)
161-
.map(Member::town)
162-
.map(town -> town.getLevel() >= plugin.getLevels().getMaxLevel()
163-
? plugin.getLocales().getNotApplicable()
164-
: plugin.formatMoney(plugin.getLevels().getLevelUpCost(town.getLevel())))
165-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
166-
.orElse("Not in town"));
167-
168-
case "town_level" -> plugin.getUserTown(player)
169-
.map(Member::town)
170-
.map(Town::getLevel)
171-
.map(String::valueOf)
172-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
173-
.orElse("Not in town"));
174-
175-
case "town_max_level" -> plugin.getUserTown(player)
176-
.map(Member::town)
177-
.map(town -> plugin.getLevels().getMaxLevel())
140+
.map(town -> resolveTownData(town, identifier))
178141
.map(String::valueOf)
179142
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
180143
.orElse("Not in town"));
144+
};
145+
}
181146

182-
case "current_location_town" -> plugin.getClaimAt(player.getPosition())
147+
@Nullable
148+
public String getCurrentLocation(@NotNull OnlineUser player, @NotNull String identifier) {
149+
return switch (identifier) {
150+
case "town" -> plugin.getClaimAt(player.getPosition())
183151
.map(TownClaim::town)
184152
.map(Town::getName)
185153
.orElse(plugin.getLocales().getRawLocale("placeholder_wilderness")
186154
.orElse("Wilderness"));
187155

188-
case "current_location_can_build" -> getBooleanValue(!plugin
156+
case "can_build" -> getBooleanValue(!plugin
189157
.cancelOperation(Operation.of(player, OperationType.BLOCK_PLACE, player.getPosition(), true)));
190158

191-
case "current_location_can_interact" -> getBooleanValue(!plugin.cancelOperation(
159+
case "can_interact" -> getBooleanValue(!plugin.cancelOperation(
192160
Operation.of(player, OperationType.BLOCK_INTERACT, player.getPosition(), true)
193161
));
194162

195-
case "current_location_can_open_containers" -> getBooleanValue(!plugin.cancelOperation(
163+
case "can_open_containers" -> getBooleanValue(!plugin.cancelOperation(
196164
Operation.of(player, OperationType.CONTAINER_OPEN, player.getPosition(), true)
197165
));
198166

199-
case "current_location_claim_type" -> plugin.getClaimAt(player.getPosition())
167+
case "claim_type" -> plugin.getClaimAt(player.getPosition())
200168
.map(TownClaim::claim)
201169
.map(Claim::getType)
202170
.map(Claim.Type::name)
203171
.map(String::toLowerCase)
204172
.orElse(plugin.getLocales().getRawLocale("placeholder_wilderness")
205173
.orElse("Wilderness"));
206174

207-
case "current_location_plot_members" -> plugin.getClaimAt(player.getPosition())
175+
case "plot_members" -> plugin.getClaimAt(player.getPosition())
208176
.map(townClaim -> {
209177
final Claim claim = townClaim.claim();
210178
if (claim.getType() != Claim.Type.PLOT) {
@@ -219,7 +187,7 @@ public String onRequest(@Nullable OfflinePlayer offlinePlayer, @NotNull String p
219187
.orElse(plugin.getLocales().getRawLocale("placeholder_not_claimed")
220188
.orElse("Not claimed"));
221189

222-
case "current_location_plot_managers" -> plugin.getClaimAt(player.getPosition())
190+
case "plot_managers" -> plugin.getClaimAt(player.getPosition())
223191
.map(townClaim -> {
224192
final Claim claim = townClaim.claim();
225193
if (claim.getType() != Claim.Type.PLOT) {
@@ -235,87 +203,46 @@ public String onRequest(@Nullable OfflinePlayer offlinePlayer, @NotNull String p
235203
.orElse(plugin.getLocales().getRawLocale("placeholder_not_claimed")
236204
.orElse("Not claimed"));
237205

238-
case "current_location_town_color" -> plugin.getClaimAt(player.getPosition())
206+
case "town_color" -> plugin.getClaimAt(player.getPosition())
239207
.map(TownClaim::town)
240208
.map(Town::getColorRgb)
241209
.orElse(WILDERNESS_COLOR);
242210

243-
case "current_location_town_money" -> plugin.getClaimAt(player.getPosition())
244-
.map(TownClaim::town)
245-
.map(Town::getMoney)
246-
.map(plugin::formatMoney)
247-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_claimed")
248-
.orElse("Not claimed"));
249-
250-
case "current_location_town_level" -> plugin.getClaimAt(player.getPosition())
251-
.map(TownClaim::town)
252-
.map(Town::getLevel)
253-
.map(String::valueOf)
254-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_claimed")
255-
.orElse("Not claimed"));
256-
257-
case "current_location_town_level_up_cost" -> plugin.getClaimAt(player.getPosition())
258-
.map(TownClaim::town)
259-
.map(town -> town.getLevel() >= plugin.getLevels().getMaxLevel()
260-
? plugin.getLocales().getNotApplicable()
261-
: plugin.formatMoney(plugin.getLevels().getLevelUpCost(town.getLevel())))
262-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_in_town")
263-
.orElse("Not in town"));
264-
265-
case "current_location_town_max_claims" -> plugin.getClaimAt(player.getPosition())
266-
.map(TownClaim::town)
267-
.map(town -> town.getMaxClaims(plugin))
268-
.map(String::valueOf)
269-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_claimed")
270-
.orElse("Not claimed"));
271-
272-
case "current_location_town_max_members" -> plugin.getClaimAt(player.getPosition())
273-
.map(TownClaim::town)
274-
.map(town -> town.getMaxMembers(plugin))
275-
.map(String::valueOf)
276-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_claimed")
277-
.orElse("Not claimed"));
278-
279-
case "current_location_town_crop_growth_rate" -> plugin.getClaimAt(player.getPosition())
280-
.map(TownClaim::town)
281-
.map(town -> town.getCropGrowthRate(plugin))
282-
.map(String::valueOf)
283-
.orElse(plugin.getLocales().getRawLocale("placeholder_not_claimed")
284-
.orElse("Not claimed"));
285-
286-
case "current_location_town_mob_spawner_rate" -> plugin.getClaimAt(player.getPosition())
211+
default -> identifier.startsWith("town_") ? plugin.getClaimAt(player.getPosition())
287212
.map(TownClaim::town)
288-
.map(town -> town.getMobSpawnerRate(plugin))
213+
.map(town -> resolveTownData(town, identifier.substring(5)))
289214
.map(String::valueOf)
290215
.orElse(plugin.getLocales().getRawLocale("placeholder_not_claimed")
291-
.orElse("Not claimed"));
292-
293-
default -> params.startsWith("town_leaderboard_") ? getTownLeaderboard(params.substring(17)) : null;
216+
.orElse("Not claimed")) : null;
294217
};
295218
}
296219

297220
// Get a town leaderboard
298221
@Nullable
299222
private String getTownLeaderboard(@NotNull String identifier) {
300-
// Get the identifier up to the last underscore
301-
final int lastUnderscore = identifier.lastIndexOf('_');
302-
if (lastUnderscore == -1) {
223+
final String[] split = identifier.split("_", 3);
224+
if (split.length < 2) {
303225
return null;
304226
}
305227

306228
// Get the leaderboard index and return the sorted list element at the index
307229
try {
308-
final int leaderboardIndex = Math.max(1, Integer.parseInt(
309-
identifier.substring(lastUnderscore + 1)
310-
));
311-
final List<Town> towns = getSortedTownList(identifier.substring(0, lastUnderscore));
230+
final int leaderboardIndex = Math.max(1, Integer.parseInt(split[1]));
231+
final List<Town> towns = getSortedTownList(split[0]);
312232
if (towns == null) {
313233
return null;
314234
}
315235

316-
return towns.size() >= leaderboardIndex
317-
? towns.get(leaderboardIndex - 1).getName()
318-
: plugin.getLocales().getNotApplicable();
236+
if (towns.size() >= leaderboardIndex) {
237+
final Town town = towns.get(leaderboardIndex - 1);
238+
if (split.length > 2) {
239+
return String.valueOf(resolveTownData(town, split[2]));
240+
} else {
241+
return town.getName();
242+
}
243+
} else {
244+
return plugin.getLocales().getNotApplicable();
245+
}
319246
} catch (NumberFormatException e) {
320247
return null;
321248
}
@@ -340,6 +267,16 @@ private List<Town> getSortedTownList(@NotNull String sortingKey) {
340267
};
341268
}
342269

270+
@NotNull
271+
public static String formatNumber(@NotNull BigDecimal number) {
272+
final Map.Entry<BigDecimal, String> format = NUMBER_FORMAT.floorEntry(number);
273+
if (format != null) {
274+
return new DecimalFormat(DECIMAL_FORMAT).format(number.divide(format.getKey().divide(THOUSAND, RoundingMode.DOWN), RoundingMode.DOWN).doubleValue() / 1000.0) + format.getValue();
275+
} else {
276+
return number.toString();
277+
}
278+
}
279+
343280
// Resolve a cached town member name from a UUID
344281
private Optional<String> resolveTownMemberName(@NotNull Town town, @NotNull UUID uuid) {
345282
return town.getLog().getActions().values().stream()
@@ -349,6 +286,45 @@ private Optional<String> resolveTownMemberName(@NotNull Town town, @NotNull UUID
349286
.findFirst();
350287
}
351288

289+
@Nullable
290+
public Object resolveTownData(@NotNull Town town, @NotNull String identifier) {
291+
return switch (identifier) {
292+
case "name" -> town.getName();
293+
294+
case "mayor" -> resolveTownMemberName(town, town.getMayor()).orElse("?");
295+
296+
case "members" -> town.getMembers().keySet().stream()
297+
.map(uuid -> resolveTownMemberName(town, uuid).orElse("?"))
298+
.collect(Collectors.joining(", "));
299+
300+
case "member_count" -> town.getMembers().size();
301+
302+
case "claim_count" -> town.getClaimCount();
303+
304+
case "max_claims" -> town.getMaxClaims(plugin);
305+
306+
case "max_members" -> town.getMaxMembers(plugin);
307+
308+
case "crop_growth_rate" -> town.getCropGrowthRate(plugin);
309+
310+
case "mob_spawner_rate" -> town.getMobSpawnerRate(plugin);
311+
312+
case "money" -> plugin.formatMoney(town.getMoney());
313+
314+
case "money_formatted" -> formatNumber(town.getMoney());
315+
316+
case "level_up_cost" -> town.getLevel() >= plugin.getLevels().getMaxLevel()
317+
? plugin.getLocales().getNotApplicable()
318+
: plugin.formatMoney(plugin.getLevels().getLevelUpCost(town.getLevel()));
319+
320+
case "level" -> town.getLevel();
321+
322+
case "max_level" -> plugin.getLevels().getMaxLevel();
323+
324+
default -> null;
325+
};
326+
}
327+
352328
@Override
353329
public boolean persist() {
354330
return true;

0 commit comments

Comments
 (0)