77import java .util .UUID ;
88import java .util .stream .Collectors ;
99
10- import org .bukkit .Keyed ;
10+ import org .bukkit .Bukkit ;
11+ import org .bukkit .Material ;
1112import org .bukkit .NamespacedKey ;
1213import org .bukkit .Registry ;
1314import org .bukkit .World ;
14- import org .bukkit .Material ;
1515import org .bukkit .block .Block ;
1616import org .bukkit .block .BlockState ;
1717import org .bukkit .block .CreatureSpawner ;
1818import org .bukkit .entity .EntityType ;
1919import org .bukkit .inventory .ItemStack ;
2020import org .bukkit .inventory .meta .BlockStateMeta ;
2121import org .bukkit .inventory .meta .ItemMeta ;
22- import org .eclipse .jdt .annotation .Nullable ;
23- import org .bukkit .Bukkit ;
2422import org .bukkit .persistence .PersistentDataContainer ;
2523import org .bukkit .persistence .PersistentDataType ;
24+ import org .eclipse .jdt .annotation .Nullable ;
2625
2726import world .bentobox .bentobox .BentoBox ;
2827import world .bentobox .bentobox .api .addons .GameModeAddon ;
3534import world .bentobox .level .objects .TopTenData ;
3635
3736/**
38- * Handles Level placeholders
39- *
40- * @author tastybento
37+ * Handles registration and resolution of Level placeholders for the Level addon.
38+ *
39+ * The class implements:
40+ * - registering placeholders via the BentoBox PlaceholdersManager
41+ * - resolving top-ten and per-island level values
42+ * - mapping blocks/items/spawners to the identifier used by IslandLevels
4143 *
4244 */
4345public class PlaceholderManager {
@@ -50,6 +52,23 @@ public PlaceholderManager(Level addon) {
5052 this .plugin = addon .getPlugin ();
5153 }
5254
55+ /**
56+ * Register placeholders for a given GameModeAddon.
57+ *
58+ * This method registers a number of placeholders with BentoBox's PlaceholdersManager:
59+ * - island level placeholders (formatted, raw, owner-only)
60+ * - points / points-to-next-level placeholders
61+ * - top-ten placeholders (name, island name, members, level) for ranks 1..10
62+ * - visited island placeholder
63+ * - mainhand & looking placeholders (value and count)
64+ * - dynamic placeholders for each configured block key from the BlockConfig
65+ *
66+ * The registered placeholders call into the Level manager and IslandLevels to fetch
67+ * values. Safety checks are performed so that missing players, islands or data return "0"
68+ * or empty strings rather than throwing exceptions.
69+ *
70+ * @param gm the GameModeAddon for which placeholders are being registered
71+ */
5372 protected void registerPlaceholders (GameModeAddon gm ) {
5473 if (plugin .getPlaceholdersManager () == null )
5574 return ;
@@ -170,15 +189,17 @@ protected void registerPlaceholders(GameModeAddon gm) {
170189 // Format the key for the placeholder name (e.g., minecraft_stone, pig_spawner)
171190 String placeholderSuffix = configKey .replace (':' , '_' ).replace ('.' , '_' ).toLowerCase ();
172191
173- // Register value placeholder
174- bpm .registerPlaceholder (addon , gm .getDescription ().getName ().toLowerCase () + "_island_value_" + placeholderSuffix ,
192+ // Register value placeholders
193+ String placeholder = gm .getDescription ().getName ().toLowerCase () + "_island_value_" + placeholderSuffix ;
194+ bpm .registerPlaceholder (addon , placeholder ,
175195 user -> String .valueOf (Objects .requireNonNullElse (
176196 // Use the configKey directly, getValue handles String keys
177197 addon .getBlockConfig ().getValue (gm .getOverWorld (), configKey ), 0 ))
178198 );
179199
180- // Register count placeholder
181- bpm .registerPlaceholder (addon , gm .getDescription ().getName ().toLowerCase () + "_island_count_" + placeholderSuffix ,
200+ // Register count placeholders
201+ placeholder = gm .getDescription ().getName ().toLowerCase () + "_island_count_" + placeholderSuffix ;
202+ bpm .registerPlaceholder (addon , placeholder ,
182203 user -> {
183204 // Convert the String configKey back to the expected Object type (EntityType, Material, String)
184205 // for IslandLevels lookup.
@@ -189,15 +210,27 @@ protected void registerPlaceholders(GameModeAddon gm) {
189210 );
190211 });
191212 }
213+ // Register limit placeholders
214+ addon .getBlockConfig ().getBlockLimits ().forEach ((configKey , configValue ) -> {
215+ // Format the key for the placeholder name (e.g., minecraft_stone, pig_spawner)
216+ String placeholderSuffix = configKey .replace (':' , '_' ).replace ('.' , '_' ).toLowerCase ();
217+ String placeholder = gm .getDescription ().getName ().toLowerCase () + "_island_limit_" + placeholderSuffix ;
218+ bpm .registerPlaceholder (addon , placeholder , user -> String .valueOf (configValue ));
219+ });
192220 }
193221
194222 /**
195223 * Get the name of the owner of the island who holds the rank in this world.
196- *
197- * @param world world
198- * @param rank rank 1 to 10
199- * @param weighted if true, then the weighted rank name is returned
200- * @return rank name
224+ *
225+ * Behavior / notes:
226+ * - rank is clamped between 1 and Level.TEN
227+ * - when weighted == true, the weighted top-ten is used; otherwise the plain top-ten is used
228+ * - returns an empty string if a rank is not available or owner is null
229+ *
230+ * @param world world to look up the ranking in
231+ * @param rank 1-based rank (will be clamped)
232+ * @param weighted whether to use the weighted top-ten
233+ * @return owner name or empty string
201234 */
202235 String getRankName (World world , int rank , boolean weighted ) {
203236 // Ensure rank is within bounds
@@ -216,12 +249,14 @@ String getRankName(World world, int rank, boolean weighted) {
216249 }
217250
218251 /**
219- * Get the island name for this rank
220- *
221- * @param world world
222- * @param rank rank 1 to 10
223- * @param weighted if true, then the weighted rank name is returned
224- * @return name of island or nothing if there isn't one
252+ * Get the island name for this rank.
253+ *
254+ * Similar behavior to getRankName, but returns the island's name (or empty string).
255+ *
256+ * @param world world to look up the island in
257+ * @param rank 1-based rank (clamped)
258+ * @param weighted whether to use the weighted list
259+ * @return name of island or empty string
225260 */
226261 String getRankIslandName (World world , int rank , boolean weighted ) {
227262 // Ensure rank is within bounds
@@ -237,12 +272,16 @@ String getRankIslandName(World world, int rank, boolean weighted) {
237272 }
238273
239274 /**
240- * Gets a comma separated string of island member names
241- *
242- * @param world world
243- * @param rank rank to request
244- * @param weighted if true, then the weighted rank name is returned
245- * @return comma separated string of island member names
275+ * Gets a comma separated string of island member names for a given ranked island.
276+ *
277+ * - Members are filtered to those at or above RanksManager.MEMBER_RANK.
278+ * - Members are sorted by rank descending for consistent ordering.
279+ * - If the island is missing or has no members, returns an empty string.
280+ *
281+ * @param world world to look up
282+ * @param rank rank in the top-ten (1..10)
283+ * @param weighted whether to use weighted top-ten
284+ * @return comma-separated member names, or empty string
246285 */
247286 String getRankMembers (World world , int rank , boolean weighted ) {
248287 // Ensure rank is within bounds
@@ -269,12 +308,15 @@ String getRankMembers(World world, int rank, boolean weighted) {
269308 }
270309
271310 /**
272- * Get the level for the rank requested
273- *
274- * @param world world
275- * @param rank rank wanted
276- * @param weighted true if weighted (level/number of team members)
277- * @return level for the rank requested
311+ * Get the level for the rank requested.
312+ *
313+ * - Returns a formatted level string using the manager's formatLevel helper.
314+ * - If a value is missing, manager.formatLevel receives null which should handle the fallback.
315+ *
316+ * @param world world to query
317+ * @param rank rank 1..10 (clamped)
318+ * @param weighted whether to fetch weighted level
319+ * @return string representation of the level for the rank
278320 */
279321 String getRankLevel (World world , int rank , boolean weighted ) {
280322 // Ensure rank is within bounds
@@ -288,11 +330,11 @@ String getRankLevel(World world, int rank, boolean weighted) {
288330 }
289331
290332 /**
291- * Return the rank of the player in a world
292- *
333+ * Return the rank of the player in a world.
334+ *
293335 * @param world world
294336 * @param user player
295- * @return rank where 1 is the top rank.
337+ * @return rank where 1 is the top rank as a String; returns empty string for null user
296338 */
297339 private String getRankValue (World world , User user ) {
298340 if (user == null ) {
@@ -304,6 +346,13 @@ private String getRankValue(World world, User user) {
304346 .values ().stream ().filter (l -> l > level ).count () + 1 );
305347 }
306348
349+ /**
350+ * Return the level for the island the user is currently visiting (if any).
351+ *
352+ * @param gm the GameModeAddon (used to map to the overworld)
353+ * @param user the user to check
354+ * @return island level string for the visited island, or empty/ "0" when not applicable
355+ */
307356 String getVisitedIslandLevel (GameModeAddon gm , User user ) {
308357 if (user == null || !gm .inWorld (user .getWorld ()))
309358 return "" ;
@@ -314,10 +363,16 @@ String getVisitedIslandLevel(GameModeAddon gm, User user) {
314363
315364 /**
316365 * Gets the most specific identifier object for a block.
317- * NOTE: Does not currently support getting custom block IDs (e.g., ItemsAdder)
318- * directly from the Block object due to hook limitations.
319- * @param block The block
320- * @return EntityType, Material, or null if air/invalid.
366+ *
367+ * The identifier is one of:
368+ * - EntityType for mob spawners (when the spawner block contains a specific spawned type)
369+ * - Material for regular blocks
370+ * - null for air or unknown/invalid blocks
371+ *
372+ * This is used to map the block to the same identifier the BlockConfig and IslandLevels use.
373+ *
374+ * @param block The block to inspect, null-safe
375+ * @return an EntityType or Material, or null for air/unknown
321376 */
322377 @ Nullable
323378 private Object getBlockIdentifier (@ Nullable Block block ) {
@@ -345,13 +400,22 @@ private Object getBlockIdentifier(@Nullable Block block) {
345400
346401 /**
347402 * Gets the most specific identifier object for an ItemStack.
348- * Prioritizes standard Bukkit methods for spawners.
349- * Adds support for reading "spawnermeta:type" NBT tag via PDC.
350- * Returns null for spawners if the specific type cannot be determined.
351- * Supports ItemsAdder items.
352- * @param itemStack The ItemStack
353- * @return EntityType, Material (for standard blocks), String (for custom items),
354- * or null (if air, invalid, or unidentified spawner).
403+ *
404+ * This method attempts to:
405+ * 1) Resolve a specific EntityType for spawner items via BlockStateMeta or a PersistentDataContainer key.
406+ * If the exact spawned mob cannot be determined, it returns null for spawner items so counts
407+ * are not incorrectly attributed.
408+ * 2) If ItemsAdder is present, check for custom item Namespaced ID and return it (String).
409+ * 3) Fallback to returning the Material for block-like items, otherwise null for non-blocks.
410+ *
411+ * The return type is one of:
412+ * - EntityType (specific spawner type)
413+ * - Material (normal block-type items)
414+ * - String (custom items IDs like ItemsAdder)
415+ * - null (air, invalid item, or unidentified spawner item)
416+ *
417+ * @param itemStack the item to inspect (may be null)
418+ * @return EntityType, Material, String, or null
355419 */
356420 @ Nullable
357421 private Object getItemIdentifier (@ Nullable ItemStack itemStack ) {
@@ -422,8 +486,14 @@ private Object getItemIdentifier(@Nullable ItemStack itemStack) {
422486 }
423487
424488 /**
425- * Helper method to convert a String key from the config (e.g., "pig_spawner", "minecraft:stone")
426- * back into the corresponding Object (EntityType, Material, String) used by IslandLevels.
489+ * Convert a configuration key string (from the block config) into the identifier object
490+ * used by IslandLevels.
491+ *
492+ * - Handles "pig_spawner" style keys and resolves them to EntityType where possible.
493+ * - Resolves namespaced Material keys using Bukkit's Registry.
494+ * - Returns the original string for custom items (ItemsAdder) when present in registry.
495+ * - Returns Material.SPAWNER for generic "spawner" key, otherwise null if unresolvable.
496+ *
427497 * @param configKey The key string from block config.
428498 * @return EntityType, Material, String identifier, or null if not resolvable.
429499 */
@@ -482,10 +552,12 @@ private Object getObjectFromConfigKey(String configKey) {
482552
483553 /**
484554 * Gets the block count for a specific identifier object in a user's island.
555+ * This is a thin wrapper that validates inputs and returns "0" when missing.
556+ *
485557 * @param gm GameModeAddon
486558 * @param user User requesting the count
487559 * @param identifier The identifier object (EntityType, Material, String)
488- * @return String representation of the count.
560+ * @return String representation of the count (zero when not available)
489561 */
490562 private String getBlockCount (GameModeAddon gm , User user , @ Nullable Object identifier ) {
491563 if (user == null || identifier == null ) {
@@ -496,7 +568,12 @@ private String getBlockCount(GameModeAddon gm, User user, @Nullable Object ident
496568
497569 /**
498570 * Gets the block count for a specific identifier object from IslandLevels.
499- * This now correctly uses EntityType or Material as keys based on `DetailsPanel`'s logic.
571+ *
572+ * - Fetches the Island for the user and then the IslandLevels data.
573+ * - IslandLevels stores counts in two maps (mdCount and uwCount) depending on how values
574+ * are classified; we add both to provide the complete count.
575+ * - Returns "0" if island or data is unavailable.
576+ *
500577 * @param gm GameModeAddon
501578 * @param user User to get count for
502579 * @param identifier The identifier object (EntityType, Material, String)
@@ -520,5 +597,4 @@ private String getBlockCountForUser(GameModeAddon gm, User user, Object identifi
520597
521598 return String .valueOf (count );
522599 }
523-
524600}
0 commit comments