/*
* IDK Game Server by Steve Winfield
* https://github.com/WinfieldSteve
*/
package org.stevewinfield.suja.idk.game.plugins;
import org.apache.log4j.Logger;
import org.stevewinfield.suja.idk.Bootloader;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
import java.io.FileReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class PluginManager {
private static final Logger logger = Logger.getLogger(PluginManager.class);
public GamePlugin getPlugin(final String name) {
return this.plugins.containsKey(name) ? this.plugins.get(name) : null;
}
public PluginManager() {
}
public boolean addPlugin(final String filename, final Object content, final boolean showLog, final boolean loadedExternally) {
String extension = "";
int i = filename.lastIndexOf('.');
if (i > 0) {
extension = filename.substring(i + 1);
}
String name = filename.substring(0, filename.length() - extension.length() - 1);
if (extension.isEmpty()) {
extension = "js";
name = filename;
}
if (showLog) {
logger.info("Loading plugin \"" + name + "\"");
}
final ScriptEngine engine = factory.getEngineByExtension(extension);
if (engine == null) {
logger.error("No script engine found for plugin " + name);
return false;
}
try {
if (engine.getFactory().getLanguageName().equals("ECMAScript")) {
// needed to make the imports work
if (engine.getFactory().getEngineName().equals("Oracle Nashorn")) {
engine.eval("load(\"nashorn:mozilla_compat.js\");");
}
engine.eval(
"importClass(org.stevewinfield.suja.idk.Bootloader);" +
"importPackage(org.stevewinfield.suja.idk.game.miscellaneous);" +
"importPackage(org.stevewinfield.suja.idk.game.players);" +
"importPackage(org.stevewinfield.suja.idk.game.rooms);" +
"importClass(org.stevewinfield.suja.idk.game.plugins.PluginManager);" +
"importClass(org.stevewinfield.suja.idk.game.plugins.PluginHelper);"
);
}
GamePlugin plugin = new GamePlugin(name, engine, loadedExternally);
engine.put("IDK", plugin);
engine.put("gamePlugin", plugin);
engine.put("logger", Logger.getLogger(name));
if (content instanceof FileReader) {
engine.eval((FileReader) content);
} else {
engine.eval((String) content);
}
PluginInterfaces.ScriptPlugin scriptPlugin = ((Invocable) engine).getInterface(PluginInterfaces.ScriptPlugin.class);
if (scriptPlugin == null) {
logger.warn("No method initializePlugin() found for plugin " + name);
} else {
try {
scriptPlugin.initializePlugin();
} catch (final Throwable t) {
logger.error("Plugin Initialization Error", t);
}
}
} catch (final ScriptException e) {
logger.error("ScriptException for plugin " + name, e);
}
if (plugins.containsKey(name) && showLog) {
logger.warn("A plugin by the name " + name + " already exists. Replacing it by " + filename);
}
this.plugins.put(name, (GamePlugin) engine.get("IDK"));
if (showLog) {
logger.info("Loaded the plugin \"" + name + "\".");
}
return true;
}
public void loadLocalPlugins(final File pluginDir) {
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(Bootloader.getCustomClassLoader());
final File[] jars = pluginDir.listFiles();
if (jars != null) {
for (final File f : jars) {
final String name = f.getName();
final FileReader reader = new FileReader(f);
this.addPlugin(name, reader, true, false);
reader.close();
}
}
} catch (final Exception e) {
logger.error("Loading plugins failed.", e);
} finally {
Thread.currentThread().setContextClassLoader(oldLoader);
}
logger.info(plugins.size() + " plugin(s) local loaded from /" + pluginDir.getPath());
}
public void load(final File pluginDir) {
this.plugins = new ConcurrentHashMap<>();
this.factory = new ScriptEngineManager();
this.loadLocalPlugins(pluginDir);
logger.info(plugins.size() + " plugin(s) local loaded from /" + pluginDir.getPath());
}
public static void log(final String txt) {
logger.info("PLUGIN: " + txt);
}
private static URL[] buildClassPath(String... directories) {
try {
final List<URL> classPath = new ArrayList<>();
for (String directory : directories) {
File directoryFile = new File(directory);
if (!directoryFile.exists()) {
directoryFile.mkdirs();
}
if (!directoryFile.isDirectory()) {
continue;
}
for (File pathname : directoryFile.listFiles()) {
if (pathname.isFile() && pathname.toString().toLowerCase().endsWith(".jar")) {
URL url = pathname.toURI().toURL();
classPath.add(url);
}
}
}
return classPath.toArray(new URL[classPath.size()]);
} catch (MalformedURLException e) {
throw new IllegalStateException(e);
}
}
private ConcurrentHashMap<String, GamePlugin> plugins;
private ScriptEngineManager factory;
}