Commit e74603bf authored by Adam Gausmann's avatar Adam Gausmann

Merge branch '1.2-release' into 'master'

1.2 release

See merge request !19
parents 708a698d dcc257fe
Pipeline #7639407 passed with stages
in 38 seconds
......@@ -5,7 +5,7 @@
<parent>
<artifactId>samurai</artifactId>
<groupId>ninja.nonemu</groupId>
<version>1.2-SNAPSHOT</version>
<version>1.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -14,6 +14,11 @@ public interface Plugin {
*/
void init() throws Exception;
/**
* Called periodically by the plugin manager, once for every iteration of the bot's main loop.
*/
void run() throws Exception;
/**
* Called when the bot is ready to remove the plugin. A good time to free resources!
*/
......
package ninja.nonemu.samurai.plugin;
import java.util.Map;
/**
* Responsible for loading and managing plugins for the bot.
*/
......@@ -11,7 +13,7 @@ public interface PluginManager {
* @param plugin The plugin to register.
* @param name The name of the plugin.
*/
void registerPlugin(Plugin plugin, String name);
void registerPlugin(Plugin plugin, String name) throws Exception;
/**
* Registers a plugin extended from the plugin base using its given name.
......@@ -19,7 +21,28 @@ public interface PluginManager {
* <br>Equivalent to {@code registerPlugin(plugin, plugin.getId())}.
* @param plugin The plugin to register.
*/
default void registerPlugin(PluginBase plugin) {
default void registerPlugin(PluginBase plugin) throws Exception {
registerPlugin(plugin, plugin.getName());
}
/**
* Calls the named plugin's {@link Plugin#cleanup()} method and removes it from the list.
* @param name The name of the plugin; case-insensitive
*/
void unregisterPlugin(String name) throws Exception;
/**
* Retrieves a registered plugin, given its name.
* @param name The name of the plugin; case-insensitive
* @return The plugin with the given name, or {@code null} if no plugin with that name is registered.
*/
Plugin getPlugin(String name);
/**
* Generates a map of all plugins that are registered at the time of this method's execution.
* <br>The key is a case-insensitive string that denotes the plugin's name, and each corresponding value is that
* plugin's instance.
* @return A map containing all registered Plugin instances.
*/
Map<String, Plugin> getPlugins();
}
......@@ -6,7 +6,7 @@
<groupId>ninja.nonemu</groupId>
<artifactId>samurai</artifactId>
<version>1.2-SNAPSHOT</version>
<version>1.2</version>
<modules>
<module>api</module>
<module>runtime</module>
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>samurai</artifactId>
<groupId>ninja.nonemu</groupId>
<version>1.2-SNAPSHOT</version>
<version>1.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
package ninja.nonemu.samurai.command;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import ninja.nonemu.samurai.BotImpl;
import ninja.nonemu.samurai.connection.Connection;
import ninja.nonemu.samurai.connection.User;
import ninja.nonemu.samurai.connection.UserMask;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class CommandExecutorImpl {
private final BotImpl bot;
......@@ -75,6 +77,12 @@ public class CommandExecutorImpl {
"msg <target> <message>",
true
), this::handleMsg);
commandSystem.registerCommand(new CommandInfo(
"plugins",
"Returns a list of all registered plugins.",
"plugins",
false
), this::handlePlugins);
}
private boolean handleVersion(CommandSender sender, CommandInfo info, String label, String[] args) {
......@@ -263,6 +271,15 @@ public class CommandExecutorImpl {
return false;
}
private boolean handlePlugins(CommandSender sender, CommandInfo info, String label, String[] args) {
Set<String> pluginSet = bot.getPluginManager().getPlugins().keySet();
String[] pluginNames = new String[pluginSet.size()];
pluginSet.toArray(pluginNames);
sender.sendChat(String.format("Plugins (%d): %s", pluginNames.length, String.join(",", pluginNames)));
return true;
}
private Connection getConnection(String name) {
return bot.getConnectionManager().getConnection(name);
}
......
package ninja.nonemu.samurai.plugin;
import ninja.nonemu.samurai.BotImpl;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
......@@ -8,8 +11,6 @@ import java.net.URLClassLoader;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.JarFile;
import ninja.nonemu.samurai.BotImpl;
import org.apache.logging.log4j.Logger;
public class PluginManagerImpl implements PluginManager {
......@@ -34,6 +35,7 @@ public class PluginManagerImpl implements PluginManager {
if (files != null) {
for (File file : files) {
PluginBase plugin = null;
try {
JarFile jarFile = new JarFile(file);
String className = jarFile.getManifest().getMainAttributes().getValue("Plugin-Class");
......@@ -47,8 +49,7 @@ public class PluginManagerImpl implements PluginManager {
}
PluginBase.setDefaultBot(bot);
PluginBase plugin = (PluginBase) pluginClass.getConstructor().newInstance();
registerPlugin(plugin);
plugin = (PluginBase) pluginClass.getConstructor().newInstance();
} catch (IOException e) {
logger.error("I/O error while loading plugin. Check to see that your manifest file exists.", e);
......@@ -63,10 +64,18 @@ public class PluginManagerImpl implements PluginManager {
} catch (IllegalAccessException e) {
logger.error("Cannot load plugin. Make sure the bot can access the plugin class.", e);
}
if (plugin != null) {
try {
registerPlugin(plugin);
} catch (Exception e) {
logger.error("Plugin threw an exception while initializing", e);
}
}
}
}
} else if (pluginDir.exists()) {
logger.error("Plugin directory is not a directory. Please move that file and restart the bot.");
logger.error("Plugin directory `./plugins' is not a directory. Please move that file and restart the bot.");
System.exit(1);
} else {
pluginDir.mkdir();
......@@ -74,30 +83,75 @@ public class PluginManagerImpl implements PluginManager {
}
public void run() {
plugins.values().forEach((plugin) -> {
try {
plugin.run();
} catch (Exception e) {
logger.error("Plugin threw an exception while running", e);
}
});
}
public void cleanup() {
logger.trace("Cleaning up plugin manager");
plugins.keySet().iterator().forEachRemaining((name) -> {
try {
unregisterPlugin(name);
} catch (Exception e) {
logger.error("Plugin threw an exception while cleaning up", e);
}
});
}
@Override
public void registerPlugin(Plugin plugin, String name) {
public void registerPlugin(Plugin plugin, String name) throws Exception {
logger.info("Registering plugin {}", name);
if (plugins.containsKey(name)) {
throw new IllegalStateException("A plugin is already registered with that name");
logger.warn("A plugin is already registered as {}; aborting", name);
return;
}
if (plugins.containsValue(plugin)) {
logger.warn("That instance of Plugin ({}) is already registered; aborting", plugin);
return;
}
logger.info("Registering plugin {}", plugin);
plugins.put(name, plugin);
try {
plugin.init();
} catch (Exception e) {
logger.error("Unable to initialize plugin", e);
plugins.remove(name);
throw e;
}
}
@Override
public void unregisterPlugin(String name) throws Exception {
logger.info("Unregistering plugin {}", name);
if (!plugins.containsKey(name)) {
logger.warn("No Plugin is registered as {}; aborting", name);
}
Plugin plugin = plugins.get(name);
try {
plugin.cleanup();
} finally {
plugins.remove(name);
}
}
@Override
public Plugin getPlugin(String name) {
return plugins.get(name);
}
@Override
public Map<String, Plugin> getPlugins() {
Map<String, Plugin> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
result.putAll(plugins);
return result;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment