diff --git a/core/build.gradle b/core/build.gradle index 520510a4..02d1a05c 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -3,6 +3,7 @@ dependencies { compileOnly 'io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT' // Hook plugins + compileOnly 'com.github.HologramLib:HologramLib:1.7.6' compileOnly 'org.geysermc.floodgate:api:2.2.5-SNAPSHOT' compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT' compileOnly 'com.sk89q.worldedit:worldedit-bukkit:7.4.0-SNAPSHOT' diff --git a/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramSubCommand.java b/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramSubCommand.java index 0a10164a..81e38917 100644 --- a/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramSubCommand.java +++ b/core/src/main/java/github/nighter/smartspawner/commands/hologram/HologramSubCommand.java @@ -6,7 +6,6 @@ import github.nighter.smartspawner.spawner.properties.SpawnerManager; import io.papermc.paper.command.brigadier.CommandSourceStack; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; import org.jspecify.annotations.NullMarked; @NullMarked @@ -41,13 +40,11 @@ public int execute(CommandContext context) { // Toggle hologram state boolean newValue = !plugin.getConfig().getBoolean("hologram.enabled"); - // Get main config and set new value - FileConfiguration mainConfig = plugin.getConfig(); - mainConfig.set("hologram.enabled", newValue); + // Set new value in config + plugin.getConfig().set("hologram.enabled", newValue); - // Save configs and reload + // Save the config (this only saves the current config, it doesn't create a new one) plugin.saveConfig(); - plugin.reloadConfig(); // Update all holograms spawnerManager.refreshAllHolograms(); diff --git a/core/src/main/java/github/nighter/smartspawner/commands/hologram/SpawnerHologram.java b/core/src/main/java/github/nighter/smartspawner/commands/hologram/SpawnerHologram.java index 812a6557..3cbe9d68 100644 --- a/core/src/main/java/github/nighter/smartspawner/commands/hologram/SpawnerHologram.java +++ b/core/src/main/java/github/nighter/smartspawner/commands/hologram/SpawnerHologram.java @@ -21,7 +21,13 @@ public class SpawnerHologram { private final SmartSpawner plugin; private final LanguageManager languageManager; + + // TextDisplay fallback (used when HologramLib is not available) private final AtomicReference textDisplay = new AtomicReference<>(null); + + // HologramLib hologram (used when HologramLib is available) + private Object hologramLibHologram; + private final Location spawnerLocation; private int stackSize; private EntityType entityType; @@ -57,6 +63,101 @@ public void createHologram() { // Clean up any existing hologram for this spawner first cleanupExistingHologram(); + // Get backend preference from config + String backend = plugin.getConfig().getString("hologram.backend", "auto").toLowerCase(); + + boolean useHologramLib = false; + boolean hologramLibAvailable = plugin.getIntegrationManager().getHologramLibHook() != null && + plugin.getIntegrationManager().getHologramLibHook().isEnabled(); + + switch (backend) { + case "hologramlib": + if (hologramLibAvailable) { + useHologramLib = true; + } else { + plugin.getLogger().warning("HologramLib backend requested but not available, falling back to TextDisplay"); + useHologramLib = false; + } + break; + case "textdisplay": + useHologramLib = false; + break; + case "auto": + default: + useHologramLib = hologramLibAvailable; + break; + } + + if (useHologramLib) { + createHologramLibHologram(); + } else { + createTextDisplayHologram(); + } + } + + private void createHologramLibHologram() { + try { + double offsetX = plugin.getConfig().getDouble("hologram.offset_x", 0.5); + double offsetY = plugin.getConfig().getDouble("hologram.offset_y", 0.5); + double offsetZ = plugin.getConfig().getDouble("hologram.offset_z", 0.5); + + Location holoLoc = spawnerLocation.clone().add(offsetX, offsetY, offsetZ); + + // Get configuration values + String alignmentStr = plugin.getConfig().getString("hologram.alignment", "CENTER"); + boolean shadowed = plugin.getConfig().getBoolean("hologram.shadowed_text", true); + boolean seeThrough = plugin.getConfig().getBoolean("hologram.see_through", false); + + // Use reflection to create HologramLib hologram + Class textHologramClass = Class.forName("me.hsgamer.hologramlib.spigot.hologram.TextHologram"); + Object hologram = textHologramClass.getConstructor(String.class) + .newInstance(uniqueIdentifier); + + // Set text (will be updated later with actual data) + textHologramClass.getMethod("setText", String.class) + .invoke(hologram, "Loading..."); + + // Set see through blocks + textHologramClass.getMethod("setSeeThroughBlocks", boolean.class) + .invoke(hologram, seeThrough); + + // Set billboard + Class billboardClass = Class.forName("org.bukkit.entity.Display$Billboard"); + Object centerBillboard = billboardClass.getField("CENTER").get(null); + textHologramClass.getMethod("setBillboard", billboardClass) + .invoke(hologram, centerBillboard); + + // Set shadow + textHologramClass.getMethod("setShadow", boolean.class) + .invoke(hologram, shadowed); + + // Set alignment + try { + Class alignmentClass = Class.forName("org.bukkit.entity.TextDisplay$TextAlignment"); + Object alignment = alignmentClass.getField(alignmentStr.toUpperCase()).get(null); + textHologramClass.getMethod("setAlignment", alignmentClass) + .invoke(hologram, alignment); + } catch (Exception e) { + // Use default alignment if invalid + plugin.getLogger().warning("Invalid hologram alignment in config: " + alignmentStr + ". Using CENTER as default."); + } + + // Spawn the hologram + Object manager = plugin.getIntegrationManager().getHologramLibHook().getHologramManager(); + manager.getClass().getMethod("spawn", Object.class, Location.class) + .invoke(manager, hologram, holoLoc); + + this.hologramLibHologram = hologram; + updateText(); + } catch (Exception e) { + plugin.getLogger().severe("Error creating HologramLib hologram: " + e.getMessage()); + e.printStackTrace(); + // Fall back to TextDisplay + createTextDisplayHologram(); + } + } + + private void createTextDisplayHologram() { double offsetX = plugin.getConfig().getDouble("hologram.offset_x", 0.5); double offsetY = plugin.getConfig().getDouble("hologram.offset_y", 0.5); double offsetZ = plugin.getConfig().getDouble("hologram.offset_z", 0.5); @@ -98,12 +199,25 @@ public void createHologram() { } public void updateText() { - TextDisplay display = textDisplay.get(); - if (display == null || entityType == null) return; + // Prepare the text content + String hologramText = prepareHologramText(); + if (hologramText == null) return; + + // Apply color codes + final String finalText = ColorUtil.translateHexColorCodes(hologramText); - // Don't check isValid() here as it needs to be on the entity thread + // Update based on which system is being used + if (hologramLibHologram != null) { + updateHologramLibText(finalText); + } else { + updateTextDisplayText(finalText); + } + } - // Prepare the text content outside of the entity thread + private String prepareHologramText() { + if (entityType == null) return null; + + // Prepare the text content String entityTypeName = languageManager.getFormattedMobName(entityType); // Create replacements map @@ -123,22 +237,51 @@ public void updateText() { hologramText = hologramText.replace(entry.getKey(), entry.getValue()); } - // Apply color codes - final String finalText = ColorUtil.translateHexColorCodes(hologramText); + return hologramText; + } + + private void updateHologramLibText(String text) { + try { + hologramLibHologram.getClass().getMethod("setText", String.class) + .invoke(hologramLibHologram, text); + } catch (Exception e) { + plugin.getLogger().severe("Error updating HologramLib text: " + e.getMessage()); + } + } + + private void updateTextDisplayText(String text) { + TextDisplay display = textDisplay.get(); + if (display == null) return; // Schedule the entity update on the entity's thread Scheduler.runEntityTask(display, () -> { if (display.isValid()) { - display.setText(finalText); + display.setText(text); } }); } public void updateData(int stackSize, EntityType entityType, int currentExp, int maxExp, int currentItems, int maxSlots) { // First, ensure we have a valid hologram - TextDisplay display = textDisplay.get(); - if (display == null || !display.isValid()) { - // If hologram doesn't exist or is invalid, recreate it + boolean needsRecreate = false; + + if (hologramLibHologram != null) { + // HologramLib is being used, check if it's still valid + try { + // HologramLib holograms don't have a simple validity check, assume valid if not null + needsRecreate = false; + } catch (Exception e) { + needsRecreate = true; + } + } else { + // TextDisplay is being used + TextDisplay display = textDisplay.get(); + if (display == null || !display.isValid()) { + needsRecreate = true; + } + } + + if (needsRecreate) { createHologram(); } @@ -155,6 +298,19 @@ public void updateData(int stackSize, EntityType entityType, int currentExp, int } public void remove() { + // Remove HologramLib hologram if it exists + if (hologramLibHologram != null) { + try { + Object manager = plugin.getIntegrationManager().getHologramLibHook().getHologramManager(); + manager.getClass().getMethod("remove", Object.class) + .invoke(manager, hologramLibHologram); + hologramLibHologram = null; + } catch (Exception e) { + plugin.getLogger().severe("Error removing HologramLib hologram: " + e.getMessage()); + } + } + + // Remove TextDisplay hologram if it exists TextDisplay display = textDisplay.get(); if (display != null && display.isValid()) { // Run on the entity's thread @@ -166,6 +322,18 @@ public void remove() { public void cleanupExistingHologram() { if (spawnerLocation == null || spawnerLocation.getWorld() == null) return; + // Remove HologramLib hologram if it exists + if (hologramLibHologram != null) { + try { + Object manager = plugin.getIntegrationManager().getHologramLibHook().getHologramManager(); + manager.getClass().getMethod("remove", Object.class) + .invoke(manager, hologramLibHologram); + hologramLibHologram = null; + } catch (Exception e) { + plugin.getLogger().severe("Error cleaning up HologramLib hologram: " + e.getMessage()); + } + } + // First, check if our tracked hologram is still valid TextDisplay display = textDisplay.get(); if (display != null) { diff --git a/core/src/main/java/github/nighter/smartspawner/hooks/IntegrationManager.java b/core/src/main/java/github/nighter/smartspawner/hooks/IntegrationManager.java index 5bdd226f..0b00b1f8 100644 --- a/core/src/main/java/github/nighter/smartspawner/hooks/IntegrationManager.java +++ b/core/src/main/java/github/nighter/smartspawner/hooks/IntegrationManager.java @@ -39,10 +39,12 @@ public class IntegrationManager { // Integration plugin flags private boolean hasAuraSkills = false; private boolean hasFloodgate = false; + private boolean hasHologramLib = false; // Integration instances public AuraSkillsIntegration auraSkillsIntegration; public FloodgateHook floodgateHook; + public github.nighter.smartspawner.hooks.hologram.HologramLibHook hologramLibHook; public IntegrationManager(SmartSpawner plugin) { this.plugin = plugin; @@ -170,6 +172,11 @@ private void checkIntegrationPlugins() { return false; } }, true); + + hasHologramLib = checkPlugin("HologramLib", () -> { + this.hologramLibHook = new github.nighter.smartspawner.hooks.hologram.HologramLibHook(plugin); + return this.hologramLibHook.initialize(); + }, true); } private boolean checkPlugin(String pluginName, PluginCheck checker, boolean logSuccess) { diff --git a/core/src/main/java/github/nighter/smartspawner/hooks/hologram/HologramLibHook.java b/core/src/main/java/github/nighter/smartspawner/hooks/hologram/HologramLibHook.java new file mode 100644 index 00000000..66b38974 --- /dev/null +++ b/core/src/main/java/github/nighter/smartspawner/hooks/hologram/HologramLibHook.java @@ -0,0 +1,65 @@ +package github.nighter.smartspawner.hooks.hologram; + +import github.nighter.smartspawner.SmartSpawner; +import org.bukkit.Bukkit; + +/** + * Integration hook for HologramLib plugin. + * Provides hologram management functionality using HologramLib API. + */ +public class HologramLibHook { + private final SmartSpawner plugin; + private boolean enabled; + private Object hologramManager; + + public HologramLibHook(SmartSpawner plugin) { + this.plugin = plugin; + this.enabled = false; + } + + /** + * Initialize the HologramLib integration + * @return true if successfully initialized, false otherwise + */ + public boolean initialize() { + if (Bukkit.getPluginManager().getPlugin("HologramLib") == null) { + plugin.getLogger().info("HologramLib not found, using fallback hologram system"); + return false; + } + + try { + // Try to get HologramLib manager + Class hologramLibClass = Class.forName("me.hsgamer.hologramlib.spigot.HologramLib"); + Object hologramLibInstance = hologramLibClass.getMethod("getManager").invoke(null); + + if (hologramLibInstance == null) { + plugin.getLogger().warning("Failed to initialize HologramLib manager"); + return false; + } + + this.hologramManager = hologramLibInstance; + this.enabled = true; + plugin.getLogger().info("Successfully hooked into HologramLib!"); + return true; + } catch (Exception e) { + plugin.getLogger().warning("Failed to hook into HologramLib: " + e.getMessage()); + return false; + } + } + + /** + * Check if HologramLib integration is enabled + * @return true if enabled, false otherwise + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Get the HologramLib manager instance + * @return the hologram manager or null if not initialized + */ + public Object getHologramManager() { + return hologramManager; + } +} diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index fc9da3e6..368310f3 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -179,6 +179,12 @@ bedrock_support: hologram: enabled: false # Show floating text above spawners + # Hologram backend (auto, hologramlib, textdisplay) + # - auto: Use HologramLib if available, otherwise use TextDisplay + # - hologramlib: Force use HologramLib (requires HologramLib plugin) + # - textdisplay: Force use Minecraft's native TextDisplay entities + backend: auto + # Hologram Text Display (supports color codes) # Available placeholders: # %entity% - Type of mob diff --git a/core/src/main/resources/paper-plugin.yml b/core/src/main/resources/paper-plugin.yml index f788b3dc..57ad7f53 100644 --- a/core/src/main/resources/paper-plugin.yml +++ b/core/src/main/resources/paper-plugin.yml @@ -127,6 +127,12 @@ dependencies: required: false join-classpath: true + # Hologram Plugins + HologramLib: + load: BEFORE + required: false + join-classpath: true + # Drops Plugins MythicMobs: load: BEFORE diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index f0056c4f..ef311901 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -41,6 +41,9 @@ softdepend: # RPG/Skill Plugins - AuraSkills + # Hologram Plugins + - HologramLib + #Drops Plugins - MythicMobs