Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -41,13 +40,11 @@ public int execute(CommandContext<CommandSourceStack> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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> textDisplay = new AtomicReference<>(null);

// HologramLib hologram (used when HologramLib is available)
private Object hologramLibHologram;

private final Location spawnerLocation;
private int stackSize;
private EntityType entityType;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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();
}

Expand All @@ -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
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
6 changes: 6 additions & 0 deletions core/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/resources/paper-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading