package com.asteria.game.plugin;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.asteria.game.character.combat.Combat;
import com.asteria.game.character.combat.CombatStrategy;
import com.asteria.game.character.player.Player;
import com.asteria.game.character.player.minigame.Minigame;
import com.asteria.game.character.player.minigame.MinigameHandler;
import com.asteria.game.character.player.skill.action.SkillAction;
import com.asteria.utility.LoggerUtils;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
/**
* The plugin handler that contains data structures to hold and functions to
* manage the held plugin listeners. Plugin listeners can be initialized,
* submitted, and executed from this class.
* <p>
* <p>
* The data structures that hold the plugin listeners are not thread safe which
* means that plugin listeners should only be executed on the main game thread.
*
* @author lare96 <http://github.com/lare96>
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public final class PluginHandler {
/**
* The map of {@link PluginContext}s mapped to their collection of
* {@link PluginListener}s.
*/
private final Multimap<Class<? extends PluginContext>, PluginListener> plugins = ArrayListMultimap.create();
/**
* The logger that will print important information.
*/
private final Logger logger = LoggerUtils.getLogger(PluginHandler.class);
/**
* Initializes the bootstrap, effectively loading all plugin listeners in
* the {@code ./plugins/} directory.
*/
public void init() {
try {
Class<?> c = Class.forName("plugin.PluginBootstrap");
Constructor<?> bootstrap = c.getConstructor(Logger.class);
bootstrap.newInstance(logger);
} catch (Exception e) {
logger.log(Level.SEVERE, "An error has occured while initializing the Bootstrap!", e);
}
}
/**
* Executes all of the {@link PluginListener}s for {@code type} in
* {@code context}. This function will throw a {@link NullPointerException}
* if no plugin listeners are available for the specified plugin context
* type.
*
* @param player
* the player that these plugin listeners will be executed for.
* @param type
* the context type to grab the plugins listeners for.
* @param context
* the plugin context that will supply data to the plugin
* listeners.
*/
public void execute(Player player, Class<? extends PluginContext> type, PluginContext context) {
Collection<PluginListener> collection = plugins.get(type);
if (collection == null)
throw new NullPointerException("No plugin listeners exist for this plugin signature!");
collection.forEach(c -> c.execute(player, context));
}
/**
* Submits a new plugin listener into the multimap of plugins. This function
* will throw a {@link PluginSignatureException} if there is no plugin
* signature for {@code clazz}.
*
* @param clazz
* the class of the plugin listener that will be submitted.
*/
public void submit(Class<?> clazz) {
try {
PluginSignature type = clazz.getAnnotation(PluginSignature.class);
if (type == null)
throw new PluginSignatureException(clazz);
for (Class<? extends PluginContext> c : type.value()) {
if (c == Minigame.class) {
MinigameHandler.MINIGAMES.add((Minigame) clazz.newInstance());
continue;
} else if (c == SkillAction.class) {
continue; // We don't need to cache skills.
} else if (c == CombatStrategy.class) {
CombatStrategy combat = (CombatStrategy) clazz.newInstance();
for (int npc : combat.getNpcs())
Combat.STRATEGIES.put(npc, combat);
continue;
}
plugins.put(c, (PluginListener<?>) clazz.newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Clears the backing map of all its plugin entries. When this method
* returns there will be no active plugin listeners.
*/
public void clear() {
plugins.clear();
}
}