|
7 | 7 | import java.util.UUID;
|
8 | 8 | import java.util.stream.Collectors;
|
9 | 9 |
|
| 10 | +import org.bukkit.Keyed; |
| 11 | +import org.bukkit.NamespacedKey; |
| 12 | +import org.bukkit.Registry; |
10 | 13 | import org.bukkit.World;
|
11 | 14 | import org.bukkit.Material;
|
| 15 | +import org.bukkit.block.Block; |
| 16 | +import org.bukkit.block.BlockState; |
| 17 | +import org.bukkit.block.CreatureSpawner; |
| 18 | +import org.bukkit.entity.EntityType; |
| 19 | +import org.bukkit.inventory.ItemStack; |
| 20 | +import org.bukkit.inventory.meta.BlockStateMeta; |
| 21 | +import org.bukkit.inventory.meta.ItemMeta; |
12 | 22 | import org.eclipse.jdt.annotation.Nullable;
|
13 | 23 | import org.bukkit.Bukkit;
|
| 24 | +import org.bukkit.persistence.PersistentDataContainer; |
| 25 | +import org.bukkit.persistence.PersistentDataType; |
14 | 26 |
|
15 | 27 | import world.bentobox.bentobox.BentoBox;
|
16 | 28 | import world.bentobox.bentobox.api.addons.GameModeAddon;
|
17 | 29 | import world.bentobox.bentobox.api.user.User;
|
18 | 30 | import world.bentobox.bentobox.database.objects.Island;
|
| 31 | +import world.bentobox.bentobox.hooks.ItemsAdderHook; |
19 | 32 | import world.bentobox.bentobox.managers.PlaceholdersManager;
|
20 | 33 | import world.bentobox.bentobox.managers.RanksManager;
|
21 | 34 | import world.bentobox.level.objects.IslandLevels;
|
@@ -101,81 +114,77 @@ protected void registerPlaceholders(GameModeAddon gm) {
|
101 | 114 | u -> getRankValue(gm.getOverWorld(), u));
|
102 | 115 |
|
103 | 116 | // Register mainhand placeholders
|
104 |
| - bpm.registerPlaceholder(addon, |
105 |
| - gm.getDescription().getName().toLowerCase() + "_island_value_mainhand", |
| 117 | + bpm.registerPlaceholder(addon,gm.getDescription().getName().toLowerCase() + "_island_value_mainhand", |
106 | 118 | user -> {
|
107 |
| - if (user.getPlayer() == null || !user.getPlayer().getInventory().getItemInMainHand().getType().isBlock()) { |
108 |
| - return "0"; |
109 |
| - } |
110 |
| - String blockName = user.getPlayer().getInventory().getItemInMainHand().getType().getKey().getKey(); |
| 119 | + if (user.getPlayer() == null) return "0"; |
| 120 | + ItemStack itemInHand = user.getPlayer().getInventory().getItemInMainHand(); |
| 121 | + Object identifier = getItemIdentifier(itemInHand); // Get EntityType, Material, String, or null |
| 122 | + if (identifier == null) return "0"; |
| 123 | + // BlockConfig.getValue handles EntityType, Material, String correctly |
111 | 124 | return String.valueOf(Objects.requireNonNullElse(
|
112 |
| - addon.getBlockConfig().getValue(gm.getOverWorld(), blockName), |
113 |
| - 0 |
114 |
| - )); |
| 125 | + addon.getBlockConfig().getValue(gm.getOverWorld(), identifier), 0)); |
115 | 126 | }
|
116 | 127 | );
|
117 | 128 |
|
118 |
| - bpm.registerPlaceholder(addon, |
119 |
| - gm.getDescription().getName().toLowerCase() + "_island_count_mainhand", |
| 129 | + bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_count_mainhand", |
120 | 130 | user -> {
|
121 |
| - if (user.getPlayer() == null || !user.getPlayer().getInventory().getItemInMainHand().getType().isBlock()) { |
122 |
| - return "0"; |
123 |
| - } |
124 |
| - Material material = user.getPlayer().getInventory().getItemInMainHand().getType(); |
125 |
| - return getBlockCount(gm, user, material); |
| 131 | + if (user.getPlayer() == null) return "0"; |
| 132 | + ItemStack itemInHand = user.getPlayer().getInventory().getItemInMainHand(); |
| 133 | + Object identifier = getItemIdentifier(itemInHand); // Get EntityType, Material, String, or null |
| 134 | + if (identifier == null) return "0"; |
| 135 | + // Pass the actual object identifier to getBlockCount |
| 136 | + return getBlockCount(gm, user, identifier); |
126 | 137 | }
|
127 | 138 | );
|
128 | 139 |
|
129 | 140 | // Register looking at block placeholders
|
130 |
| - bpm.registerPlaceholder(addon, |
131 |
| - gm.getDescription().getName().toLowerCase() + "_island_value_looking", |
| 141 | + bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_value_looking", |
132 | 142 | user -> {
|
133 | 143 | if (user.getPlayer() == null) return "0";
|
134 |
| - var targetBlock = user.getPlayer().getTargetBlock(null, 5); |
135 |
| - if (targetBlock != null && !targetBlock.getType().isAir()) { |
136 |
| - String blockName = targetBlock.getType().getKey().getKey(); |
| 144 | + Block targetBlock = user.getPlayer().getTargetBlockExact(5); |
| 145 | + Object identifier = getBlockIdentifier(targetBlock); // Get EntityType, Material, String, or null |
| 146 | + if (identifier == null) return "0"; |
| 147 | + // BlockConfig.getValue handles EntityType, Material, String correctly |
137 | 148 | return String.valueOf(Objects.requireNonNullElse(
|
138 |
| - addon.getBlockConfig().getValue(gm.getOverWorld(), blockName), |
139 |
| - 0 |
140 |
| - )); |
141 |
| - } |
142 |
| - return "0"; |
| 149 | + addon.getBlockConfig().getValue(gm.getOverWorld(), identifier), 0)); |
143 | 150 | }
|
144 | 151 | );
|
145 | 152 |
|
146 |
| - bpm.registerPlaceholder(addon, |
147 |
| - gm.getDescription().getName().toLowerCase() + "_island_count_looking", |
| 153 | + bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_count_looking", |
148 | 154 | user -> {
|
149 | 155 | if (user.getPlayer() == null) return "0";
|
150 |
| - var targetBlock = user.getPlayer().getTargetBlock(null, 5); |
151 |
| - if (targetBlock != null && !targetBlock.getType().isAir()) { |
152 |
| - return getBlockCount(gm, user, targetBlock.getType()); |
153 |
| - } |
154 |
| - return "0"; |
| 156 | + Block targetBlock = user.getPlayer().getTargetBlockExact(5); |
| 157 | + Object identifier = getBlockIdentifier(targetBlock); // Get EntityType, Material, String, or null |
| 158 | + if (identifier == null) return "0"; |
| 159 | + // Pass the actual object identifier to getBlockCount |
| 160 | + return getBlockCount(gm, user, identifier); |
155 | 161 | }
|
156 | 162 | );
|
157 | 163 |
|
158 |
| - // Register placeholders for all block materials |
| 164 | + // Register placeholders for all block materials/types from config |
159 | 165 | if (Bukkit.getServer() != null) {
|
160 |
| - // Get all materials from the block config |
161 |
| - addon.getBlockConfig().getBlockValues().keySet().forEach(blockName -> { |
162 |
| - String formattedName = blockName.replace(':', '_').toLowerCase(); |
| 166 | + // Iterate over the String keys defined in the block config's baseline values |
| 167 | + addon.getBlockConfig().getBlockValues().keySet().forEach(configKey -> { |
| 168 | + // configKey is a String like "minecraft:stone", "pig_spawner", "itemsadder:my_custom_block" |
| 169 | + |
| 170 | + // Format the key for the placeholder name (e.g., minecraft_stone, pig_spawner) |
| 171 | + String placeholderSuffix = configKey.replace(':', '_').replace('.', '_').toLowerCase(); |
163 | 172 |
|
164 | 173 | // Register value placeholder
|
165 |
| - bpm.registerPlaceholder(addon, |
166 |
| - gm.getDescription().getName().toLowerCase() + "_island_value_" + formattedName, |
| 174 | + bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_value_" + placeholderSuffix, |
167 | 175 | user -> String.valueOf(Objects.requireNonNullElse(
|
168 |
| - addon.getBlockConfig().getValue(gm.getOverWorld(), blockName), |
169 |
| - 0 |
170 |
| - )) |
| 176 | + // Use the configKey directly, getValue handles String keys |
| 177 | + addon.getBlockConfig().getValue(gm.getOverWorld(), configKey), 0)) |
171 | 178 | );
|
172 | 179 |
|
173 | 180 | // Register count placeholder
|
174 |
| - bpm.registerPlaceholder(addon, |
175 |
| - gm.getDescription().getName().toLowerCase() + "_island_count_" + formattedName, |
| 181 | + bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_count_" + placeholderSuffix, |
176 | 182 | user -> {
|
177 |
| - Material material = Material.valueOf(blockName.toUpperCase()); |
178 |
| - return getBlockCount(gm, user, material); |
| 183 | + // Convert the String configKey back to the expected Object type (EntityType, Material, String) |
| 184 | + // for IslandLevels lookup. |
| 185 | + Object identifier = getObjectFromConfigKey(configKey); |
| 186 | + if (identifier == null) return "0"; |
| 187 | + return getBlockCount(gm, user, identifier); |
179 | 188 | }
|
180 | 189 | );
|
181 | 190 | });
|
@@ -304,41 +313,211 @@ String getVisitedIslandLevel(GameModeAddon gm, User user) {
|
304 | 313 | }
|
305 | 314 |
|
306 | 315 | /**
|
307 |
| - * Gets the block count for a specific material in a user's island |
| 316 | + * 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. |
| 321 | + */ |
| 322 | + @Nullable |
| 323 | + private Object getBlockIdentifier(@Nullable Block block) { |
| 324 | + if (block == null || block.getType().isAir()) return null; |
| 325 | + |
| 326 | + Material type = block.getType(); |
| 327 | + |
| 328 | + // Handle Spawners |
| 329 | + if (type == Material.SPAWNER) { |
| 330 | + BlockState state = block.getState(); |
| 331 | + if (state instanceof CreatureSpawner) { |
| 332 | + CreatureSpawner spawner = (CreatureSpawner) state; |
| 333 | + EntityType spawnedType = spawner.getSpawnedType(); |
| 334 | + if (spawnedType != null) { |
| 335 | + return spawnedType; // Return EntityType |
| 336 | + } |
| 337 | + return Material.SPAWNER; // Return generic spawner material if type unknown |
| 338 | + } |
| 339 | + return Material.SPAWNER; // Return generic spawner material if state invalid |
| 340 | + } |
| 341 | + |
| 342 | + // Fallback to the Material for regular blocks |
| 343 | + return type; |
| 344 | + } |
| 345 | + |
| 346 | + /** |
| 347 | + * 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). |
| 355 | + */ |
| 356 | + @Nullable |
| 357 | + private Object getItemIdentifier(@Nullable ItemStack itemStack) { |
| 358 | + if (itemStack == null || itemStack.getType().isAir()) { |
| 359 | + return null; // Invalid item |
| 360 | + } |
| 361 | + |
| 362 | + Material type = itemStack.getType(); |
| 363 | + |
| 364 | + // 1. Handle Spawners |
| 365 | + if (type == Material.SPAWNER) { |
| 366 | + if (itemStack.hasItemMeta()) { |
| 367 | + ItemMeta meta = itemStack.getItemMeta(); |
| 368 | + EntityType specificType = null; // Variable to store the result |
| 369 | + |
| 370 | + // 1a. Try standard BlockStateMeta method FIRST |
| 371 | + if (meta instanceof BlockStateMeta) { |
| 372 | + BlockStateMeta blockStateMeta = (BlockStateMeta) meta; |
| 373 | + if (blockStateMeta.hasBlockState()) { |
| 374 | + BlockState blockState = blockStateMeta.getBlockState(); |
| 375 | + if (blockState instanceof CreatureSpawner) { |
| 376 | + CreatureSpawner spawner = (CreatureSpawner) blockState; |
| 377 | + // Get type if standard method works |
| 378 | + specificType = spawner.getSpawnedType(); |
| 379 | + } |
| 380 | + } |
| 381 | + } |
| 382 | + |
| 383 | + // 1b. If standard method failed (specificType is still null), try reading PDC tag |
| 384 | + if (specificType == null && meta != null) { // Check meta != null again just in case |
| 385 | + PersistentDataContainer pdc = meta.getPersistentDataContainer(); |
| 386 | + // Define the key used by SpawnerMeta (and potentially others) |
| 387 | + NamespacedKey spawnerMetaTypeKey = new NamespacedKey("spawnermeta", "type"); |
| 388 | + |
| 389 | + if (pdc.has(spawnerMetaTypeKey, PersistentDataType.STRING)) { |
| 390 | + String entityName = pdc.get(spawnerMetaTypeKey, PersistentDataType.STRING); |
| 391 | + if (entityName != null && !entityName.isEmpty()) { |
| 392 | + try { |
| 393 | + // Convert the name (e.g., "ZOMBIE") to EntityType |
| 394 | + // Use uppercase as EntityType names are uppercase enums |
| 395 | + specificType = EntityType.valueOf(entityName.toUpperCase()); |
| 396 | + } catch (IllegalArgumentException e) { |
| 397 | + // Keep specificType as null |
| 398 | + } |
| 399 | + } |
| 400 | + } |
| 401 | + } |
| 402 | + |
| 403 | + // Return the identified type (from standard or PDC), or null if neither worked |
| 404 | + return specificType; |
| 405 | + |
| 406 | + } else { |
| 407 | + // No ItemMeta at all for the spawner item |
| 408 | + return null; |
| 409 | + } |
| 410 | + } // End of Spawner handling |
| 411 | + |
| 412 | + // 2. Handle potential custom items (e.g., ItemsAdder) |
| 413 | + if (addon.isItemsAdder()) { |
| 414 | + Optional<String> customId = ItemsAdderHook.getNamespacedId(itemStack); |
| 415 | + if (customId.isPresent()) { |
| 416 | + return customId.get(); // Return the String ID from ItemsAdder |
| 417 | + } |
| 418 | + } |
| 419 | + |
| 420 | + // 3. Fallback to Material for regular items that represent blocks |
| 421 | + return type.isBlock() ? type : null; |
| 422 | + } |
| 423 | + |
| 424 | + /** |
| 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. |
| 427 | + * @param configKey The key string from block config. |
| 428 | + * @return EntityType, Material, String identifier, or null if not resolvable. |
| 429 | + */ |
| 430 | + @Nullable |
| 431 | + private Object getObjectFromConfigKey(String configKey) { |
| 432 | + if (configKey == null || configKey.isBlank()) { |
| 433 | + return null; |
| 434 | + } |
| 435 | + |
| 436 | + String lowerCaseKey = configKey.toLowerCase(); // Normalize for checks |
| 437 | + |
| 438 | + // Check if it's a spawner key (e.g., "pig_spawner") |
| 439 | + // Ensure it's not the generic "minecraft:spawner" or just "spawner" |
| 440 | + if (lowerCaseKey.endsWith("_spawner") && !lowerCaseKey.equals(Material.SPAWNER.getKey().toString()) && !lowerCaseKey.equals("spawner")) { |
| 441 | + String entityTypeName = lowerCaseKey.substring(0, lowerCaseKey.length() - "_spawner".length()); |
| 442 | + // Entity types require namespace in modern MC. Assume minecraft if none provided. |
| 443 | + // This might need adjustment if config uses non-namespaced keys for entities. |
| 444 | + NamespacedKey entityKey = NamespacedKey.fromString(entityTypeName); // Allow full key like "minecraft:pig" |
| 445 | + if (entityKey == null && !entityTypeName.contains(":")) { // If no namespace, assume minecraft |
| 446 | + entityKey = NamespacedKey.minecraft(entityTypeName); |
| 447 | + } |
| 448 | + |
| 449 | + if (entityKey != null) { |
| 450 | + EntityType entityType = Registry.ENTITY_TYPE.get(entityKey); |
| 451 | + if (entityType != null) { |
| 452 | + return entityType; |
| 453 | + } |
| 454 | + } |
| 455 | + return null; // Cannot resolve |
| 456 | + } |
| 457 | + |
| 458 | + // Check if it's a standard Material key (namespaced) |
| 459 | + NamespacedKey matKey = NamespacedKey.fromString(lowerCaseKey); |
| 460 | + Material material = null; |
| 461 | + if (matKey != null) { |
| 462 | + material = Registry.MATERIAL.get(matKey); |
| 463 | + } |
| 464 | + // Add check for non-namespaced legacy material names? Might conflict with custom keys. Risky. |
| 465 | + // Example: Material legacyMat = Material.matchMaterial(configKey); |
| 466 | + |
| 467 | + if (material != null) { |
| 468 | + return material; |
| 469 | + } |
| 470 | + |
| 471 | + // Assume it's a custom String key (e.g., ItemsAdder) if not resolved yet |
| 472 | + if (addon.isItemsAdder() && ItemsAdderHook.isInRegistry(configKey)) { // Use original case key for lookup? |
| 473 | + return configKey; |
| 474 | + } |
| 475 | + |
| 476 | + // Final check: maybe it's the generic "spawner" key from config? |
| 477 | + if(lowerCaseKey.equals("spawner")) { |
| 478 | + return Material.SPAWNER; |
| 479 | + } |
| 480 | + return null; |
| 481 | + } |
| 482 | + |
| 483 | + /** |
| 484 | + * Gets the block count for a specific identifier object in a user's island. |
308 | 485 | * @param gm GameModeAddon
|
309 | 486 | * @param user User requesting the count
|
310 |
| - * @param material Material to count |
311 |
| - * @return String representation of the count |
| 487 | + * @param identifier The identifier object (EntityType, Material, String) |
| 488 | + * @return String representation of the count. |
312 | 489 | */
|
313 |
| - private String getBlockCount(GameModeAddon gm, User user, Object material) { |
314 |
| - if (user == null) { |
| 490 | + private String getBlockCount(GameModeAddon gm, User user, @Nullable Object identifier) { |
| 491 | + if (user == null || identifier == null) { |
315 | 492 | return "0";
|
316 | 493 | }
|
317 |
| - return getBlockCountForUser(gm, user, material); |
| 494 | + return getBlockCountForUser(gm, user, identifier); |
318 | 495 | }
|
319 | 496 |
|
320 | 497 | /**
|
321 |
| - * Gets the block count for a specific material in a user's island |
| 498 | + * 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. |
322 | 500 | * @param gm GameModeAddon
|
323 | 501 | * @param user User to get count for
|
324 |
| - * @param material Material to count |
325 |
| - * @return String representation of the count |
| 502 | + * @param identifier The identifier object (EntityType, Material, String) |
| 503 | + * @return String representation of the count. |
326 | 504 | */
|
327 |
| - private String getBlockCountForUser(GameModeAddon gm, User user, Object material) { |
328 |
| - // Get the island for the user |
| 505 | + private String getBlockCountForUser(GameModeAddon gm, User user, Object identifier) { |
329 | 506 | Island island = addon.getIslands().getIsland(gm.getOverWorld(), user);
|
330 | 507 | if (island == null) {
|
331 | 508 | return "0";
|
332 | 509 | }
|
333 | 510 |
|
334 |
| - // Get the level data for the island |
335 | 511 | IslandLevels data = addon.getManager().getLevelsData(island);
|
336 | 512 | if (data == null) {
|
337 | 513 | return "0";
|
338 | 514 | }
|
339 | 515 |
|
340 |
| - // Get the total count from both above sea level and underwater |
341 |
| - int count = data.getMdCount().getOrDefault(material, 0) + data.getUwCount().getOrDefault(material, 0); |
| 516 | + // Get the count based on the type of the identifier |
| 517 | + // Assumes IslandLevels uses EntityType for specific spawners, Material for blocks, |
| 518 | + // and potentially String for custom items, based on DetailsPanel and BlockConfig analysis. |
| 519 | + int count = data.getMdCount().getOrDefault(identifier, 0) + data.getUwCount().getOrDefault(identifier, 0); |
| 520 | + |
342 | 521 | return String.valueOf(count);
|
343 | 522 | }
|
344 | 523 |
|
|
0 commit comments