Skip to content
Original file line number Diff line number Diff line change
@@ -1,29 +1,120 @@

package com.bencodez.advancedcore.api.item;

import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

import org.bukkit.inventory.ItemStack;

import com.bencodez.advancedcore.AdvancedCorePlugin;

/**
* Reflection-backed Nexo integration kept isolated from the rest of ItemBuilder.
*/
public class NexoItemHandle {
private static final Map<String, ReflectionCache> DEFAULT_CACHES = new HashMap<>();
private static final Map<ClassLoader, Map<String, WeakReference<ReflectionCache>>> CACHES = new WeakHashMap<>();

private final ReflectionCache cache;

public NexoItemHandle() {
this(NexoItemHandle.class.getClassLoader(), "com.nexomc.nexo.api.NexoItems", "com.nexomc.nexo.items.ItemBuilder");
}

protected NexoItemHandle(ClassLoader classLoader, String apiClassName, String builderClassName) {
ClassLoader effectiveClassLoader = classLoader == null ? NexoItemHandle.class.getClassLoader() : classLoader;
cache = sharedCache(effectiveClassLoader, apiClassName, builderClassName);
}

public ItemStack getItem(String item) {
if (item == null || item.isEmpty()) {
return null;
}
try {
Class<?> nexoItemsClass = Class.forName("com.nexomc.nexo.api.NexoItems");
Method itemFromIdMethod = nexoItemsClass.getMethod("itemFromId", String.class);
Object itemBuilder = itemFromIdMethod.invoke(null, item);
if (itemBuilder != null) {
Class<?> itemBuilderClass = Class.forName("com.nexomc.nexo.items.ItemBuilder");
Method buildMethod = itemBuilderClass.getMethod("build");
Object builtItem = buildMethod.invoke(itemBuilder);
ensureMethods();
Object itemBuilder = cache.itemFromIdMethod.invoke(null, item);
if (itemBuilder == null) {
return null;
}
Object builtItem = cache.buildMethod.invoke(itemBuilder);
if (builtItem instanceof ItemStack) {
return (ItemStack) builtItem;
}
} catch (Exception e) {
e.printStackTrace();
logFailureOnce("Nexo build() returned an unexpected type: "
+ (builtItem == null ? "null" : builtItem.getClass().getName()), null);
} catch (ReflectiveOperationException | LinkageError | RuntimeException e) {
logFailureOnce("Unable to use the installed Nexo item API", e);
}
return null;
}

private void ensureMethods() throws ReflectiveOperationException {
if (cache.itemFromIdMethod != null && cache.buildMethod != null) {
return;
}
synchronized (cache) {
if (cache.itemFromIdMethod == null) {
Class<?> nexoItemsClass = Class.forName(cache.apiClassName, true, cache.classLoader);
cache.itemFromIdMethod = nexoItemsClass.getMethod("itemFromId", String.class);
}
if (cache.buildMethod == null) {
Class<?> itemBuilderClass = Class.forName(cache.builderClassName, true, cache.classLoader);
cache.buildMethod = itemBuilderClass.getMethod("build");
}
}
}

private void logFailureOnce(String message, Throwable throwable) {
synchronized (cache) {
if (cache.compatibilityFailureLogged) {
return;
}
cache.compatibilityFailureLogged = true;
}
AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance();
if (plugin != null) {
plugin.getLogger().warning(message);
if (throwable != null) {
plugin.debug(throwable);
}
}
}

private static synchronized ReflectionCache sharedCache(ClassLoader classLoader, String apiClassName,
String builderClassName) {
String key = apiClassName + '\0' + builderClassName;
if (classLoader == NexoItemHandle.class.getClassLoader()) {
return DEFAULT_CACHES.computeIfAbsent(key,
ignored -> new ReflectionCache(classLoader, apiClassName, builderClassName));
}

Map<String, WeakReference<ReflectionCache>> classLoaderCaches = CACHES.computeIfAbsent(classLoader,
ignored -> new HashMap<>());
WeakReference<ReflectionCache> reference = classLoaderCaches.get(key);
ReflectionCache existing = reference == null ? null : reference.get();
if (existing != null) {
return existing;
}

ReflectionCache created = new ReflectionCache(classLoader, apiClassName, builderClassName);
classLoaderCaches.put(key, new WeakReference<>(created));
Comment thread
BenCodez marked this conversation as resolved.
Resolved
return created;
}

private static final class ReflectionCache {
private final ClassLoader classLoader;
private final String apiClassName;
private final String builderClassName;
private volatile Method itemFromIdMethod;
private volatile Method buildMethod;
private boolean compatibilityFailureLogged;

private ReflectionCache(ClassLoader classLoader, String apiClassName, String builderClassName) {
this.classLoader = classLoader;
Comment thread
BenCodez marked this conversation as resolved.
Resolved
this.apiClassName = apiClassName;
this.builderClassName = builderClassName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,61 @@
import java.util.concurrent.TimeUnit;

import org.bukkit.Bukkit;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.entity.Player;

import com.bencodez.advancedcore.AdvancedCorePlugin;
import com.bencodez.simpleapi.messages.MessageAPI;

import lombok.Getter;

// TODO: Auto-generated Javadoc
/**
* The Class BossBar.
* Bukkit boss bar wrapper used by rewards and downstream plugins.
*/
public class BossBar {

/**
* Gets the boss bar.
*
* @return the boss bar
*/
@Getter
private org.bukkit.boss.BossBar bossBar;

/**
* Instantiates a new boss bar.
*
* @param msg the msg
* @param barColor the bar color
* @param barStyle the bar style
* @param progress the progress
*/
public BossBar(String msg, String barColor, String barStyle, double progress) {
bossBar = Bukkit.createBossBar(MessageAPI.colorize(msg), BarColor.valueOf(barColor),
BarStyle.valueOf(barStyle));
bossBar.setProgress(progress);
bossBar = Bukkit.createBossBar(MessageAPI.colorize(msg), EffectCompatibility.parseBarColor(barColor),
EffectCompatibility.parseBarStyle(barStyle));
bossBar.setProgress(EffectCompatibility.clampProgress(progress));
}

/**
* Adds a player to the boss bar.
*
* @param player the player to add
*/
public void addPlayer(Player player) {
bossBar.addPlayer(player);
if (player != null) {
bossBar.addPlayer(player);
}
}

/**
* Adds a player to the boss bar with delay.
*
* @param player the player to add
* @param delay the delay in seconds before removing
*/
public void addPlayer(final Player player, int delay) {
try {
if (player == null) {
return;
}
bossBar.addPlayer(player);

if (delay > 0) {
AdvancedCorePlugin.getInstance().getBukkitScheduler().runTaskLater(AdvancedCorePlugin.getInstance(),
new Runnable() {

@Override
public void run() {
if (bossBar != null && player != null) {
bossBar.removePlayer(player);
}
}
}, delay * 50 + 60, TimeUnit.MILLISECONDS);
AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance();
if (plugin != null) {
plugin.getBukkitScheduler().runTaskLater(plugin, () -> {
if (bossBar != null) {
bossBar.removePlayer(player);
}
}, delay * 50L + 60L, TimeUnit.MILLISECONDS);
}
}
} catch (Exception e) {
AdvancedCorePlugin.getInstance().debug(e);
AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance();
if (plugin != null) {
plugin.debug(e);
}
}
}

/**
* Gets the players viewing the boss bar.
*
* @return the list of players
*/
public List<Player> getPlayers() {
return bossBar.getPlayers();
}

/**
* Hides the boss bar.
*/
public void hide() {
if (bossBar != null) {
bossBar.setVisible(false);
Expand All @@ -100,110 +67,60 @@ public void hide() {
}

private void hideInDelay(int delay) {
AdvancedCorePlugin.getInstance().getBukkitScheduler().runTaskLater(AdvancedCorePlugin.getInstance(),
new Runnable() {

@Override
public void run() {
hide();
}
}, delay * 50, TimeUnit.MILLISECONDS);
AdvancedCorePlugin plugin = AdvancedCorePlugin.getInstance();
if (plugin == null) {
return;
}
plugin.getBukkitScheduler().runTaskLater(plugin, this::hide, delay * 50L, TimeUnit.MILLISECONDS);
}

/**
* Removes a player from the boss bar.
*
* @param player the player to remove
*/
public void removePlayer(Player player) {
bossBar.removePlayer(player);
if (player != null) {
bossBar.removePlayer(player);
}
}

/**
* Sends the boss bar to all players.
*/
public void send() {
bossBar.setVisible(true);
}

/**
* Sends the boss bar with delay.
*
* @param delay the delay in seconds
*/
public void send(int delay) {
bossBar.setVisible(true);

hideInDelay(delay);
}

/**
* Send.
*
* @param player the player
* @param delay the delay
*/
public void send(Player player, int delay) {
if (player == null) {
return;
}
bossBar.addPlayer(player);
bossBar.setVisible(true);
hideInDelay(delay);
}

/**
* Sets the color of the boss bar.
*
* @param barColor the bar color
*/
public void setColor(String barColor) {
if (barColor != null) {
bossBar.setColor(BarColor.valueOf(barColor));
bossBar.setColor(EffectCompatibility.parseBarColor(barColor));
}
}

/**
* Sets the progress of the boss bar.
*
* @param progress the progress (0.0 to 1.0)
*/
public void setProgress(double progress) {
if (progress > 1) {
progress = 1;
}
if (progress < 0) {
progress = 0;
}
bossBar.setProgress(progress);
bossBar.setProgress(EffectCompatibility.clampProgress(progress));
}

/**
* Sets the style of the boss bar.
*
* @param barStyle the bar style
*/
public void setStyle(String barStyle) {
if (barStyle != null) {
bossBar.setStyle(BarStyle.valueOf(barStyle));
bossBar.setStyle(EffectCompatibility.parseBarStyle(barStyle));
}
}

/**
* Sets the title of the boss bar.
*
* @param title the title
*/
public void setTitle(String title) {
if (title != null) {
bossBar.setTitle(MessageAPI.colorize(title));
}

}

/**
* Sets the visibility of the boss bar.
*
* @param visible true to show, false to hide
*/
public void setVisible(boolean visible) {
bossBar.setVisible(visible);
}
}
}
Loading
Loading