// newPlugin should not have been initialized at this point! internal bool ReplaceWith(BasePlugin newPlugin, bool saveState = true) { var result = false; var noreturn = false; var paused = new LinkedList <HookPoint> (); object savedState = null; lock (HookPoint.editLock) { Server.notifyAll("<Server> Reloading plugin " + Name + ", you may experience lag...", Misc.ChatColor.White, true); var signal = new ManualResetEvent(false); lock (HookPoint.editLock) try { using (this.Pause()) { // initialize new instance with saved state if (saveState) { savedState = Suspend(); } ProgramLog.Debug.Log("Initializing new plugin instance..."); if (!newPlugin.Initialize(savedState)) { if (saveState) { Resume(savedState); } return(false); } // point of no return, if the new plugin fails now, // blame the author // because it's time to dispose the old plugin noreturn = true; // use command objects from the old plugin, because command invocations // may be paused inside them, this way when they unpause // they run the new plugin's methods lock (commands) { ProgramLog.Debug.Log("Replacing commands..."); var prefixes = newPlugin.commands.Keys.ToArray(); var done = new HashSet <CommandInfo> (); foreach (var prefix in prefixes) { CommandInfo oldCmd; if (commands.TryGetValue(prefix, out oldCmd)) { // ProgramLog.Debug.Log ("Replacing command {0}.", prefix); var newCmd = newPlugin.commands[prefix]; newPlugin.commands[prefix] = oldCmd; commands.Remove(prefix); if (done.Contains(oldCmd)) { continue; } oldCmd.InitFrom(newCmd); done.Add(oldCmd); oldCmd.AfterEvent += newPlugin.NotifyAfterCommand; oldCmd.BeforeEvent += newPlugin.NotifyBeforeCommand; // garbage newCmd.ClearCallbacks(); newCmd.ClearEvents(); } } foreach (var kv in commands) { var cmd = kv.Value; ProgramLog.Debug.Log("Clearing command {0}.", kv.Key); cmd.ClearCallbacks(); } commands.Clear(); } // replace hook subscriptions from the old plugin with new ones // in the exact same spots in the invocation chains lock (newPlugin.desiredHooks) { ProgramLog.Debug.Log("Replacing hooks..."); foreach (var h in newPlugin.desiredHooks) { if (hooks.Contains(h.hookPoint)) { h.hookPoint.Replace(this, newPlugin, h.callback, h.order); hooks.Remove(h.hookPoint); newPlugin.hooks.Add(h.hookPoint); } else { // this adds the hook to newPlugin.hooks h.hookPoint.HookBase(newPlugin, h.callback, h.order); } } } ProgramLog.Debug.Log("Disabling old plugin instance..."); Disable(); ProgramLog.Debug.Log("Enabling new plugin instance..."); if (newPlugin.Enable()) { result = true; } } } finally { Server.notifyAll("<Server> Done.", Misc.ChatColor.White, true); // clean up remaining hooks if (noreturn) { ProgramLog.Debug.Log("Disposing of old plugin instance..."); Dispose(); } } } return(result); }
public static void PluginCommand(ISender sender, ArgumentList args) { /* * Commands: * list - shows all plugins * info - shows a plugin's author & description etc * disable - disables a plugin * enable - enables a plugin * reload * unload * status * load */ if (args.Count == 0) { throw new CommandError("Subcommand expected."); } string command = args[0]; args.RemoveAt(0); //Allow the commands to use any additional arguments without also getting the command lock (plugins) switch (command) { case "-l": case "ls": case "list": { if (PluginCount == 0) { sender.Message(255, "No plugins loaded."); return; } var msg = new StringBuilder(); msg.Append("Plugins: "); int i = 0; foreach (var plugin in EnumeratePlugins) { if (i > 0) { msg.Append(", "); } msg.Append(plugin.Name); if (!plugin.IsEnabled) { msg.Append("[OFF]"); } i++; } msg.Append("."); sender.Message(255, ChatColor.DodgerBlue, msg.ToString()); break; } case "-s": case "stat": case "status": { if (PluginCount == 0) { sender.Message(255, "No plugins loaded."); return; } var msg = new StringBuilder(); foreach (var plugin in EnumeratePlugins) { msg.Clear(); msg.Append(plugin.IsDisposed ? "[DISPOSED] " : (plugin.IsEnabled ? "[ON] " : "[OFF] ")); msg.Append(plugin.Name); msg.Append(" "); msg.Append(plugin.Version); if (plugin.Status != null && plugin.Status.Length > 0) { msg.Append(" : "); msg.Append(plugin.Status); } sender.Message(255, ChatColor.DodgerBlue, msg.ToString()); } break; } case "-i": case "info": { string name; args.ParseOne(out name); var fplugin = GetPlugin(name); if (fplugin != null) { var path = Path.GetFileName(fplugin.Path); sender.Message(255, ChatColor.DodgerBlue, fplugin.Name); sender.Message(255, ChatColor.DodgerBlue, "Filename: " + path); sender.Message(255, ChatColor.DodgerBlue, "Version: " + fplugin.Version); sender.Message(255, ChatColor.DodgerBlue, "Author: " + fplugin.Author); if (fplugin.Description != null && fplugin.Description.Length > 0) { sender.Message(255, ChatColor.DodgerBlue, fplugin.Description); } sender.Message(255, ChatColor.DodgerBlue, "Status: " + (fplugin.IsEnabled ? "[ON] " : "[OFF] ") + fplugin.Status); } else { sender.sendMessage("The plugin \"" + args[1] + "\" was not found."); } break; } case "-d": case "disable": { string name; args.ParseOne(out name); var fplugin = GetPlugin(name); if (fplugin != null) { if (fplugin.Disable()) { sender.Message(255, ChatColor.DodgerBlue, fplugin.Name + " was disabled."); } else { sender.Message(255, ChatColor.DodgerBlue, fplugin.Name + " was disabled, errors occured during the process."); } } else { sender.Message(255, "The plugin \"" + name + "\" could not be found."); } break; } case "-e": case "enable": { string name; args.ParseOne(out name); var fplugin = GetPlugin(name); if (fplugin != null) { if (fplugin.Enable()) { sender.Message(255, ChatColor.DodgerBlue, fplugin.Name + " was enabled."); } else { sender.Message(255, ChatColor.DodgerBlue, fplugin.Name + " was enabled, errors occured during the process."); } } else { sender.Message(255, "The plugin \"" + name + "\" could not be found."); } break; } case "-u": case "-ua": case "unload": { string name; if (command == "-ua" || command == "-uca") { name = "all"; } else { args.ParseOne(out name); } BasePlugin[] plugs; if (name == "all" || name == "-a") { plugs = plugins.Values.ToArray(); } else { var splugin = PluginManager.GetPlugin(name); if (splugin == null) { sender.Message(255, "The plugin \"" + name + "\" could not be found."); return; } plugs = new BasePlugin [] { splugin }; } foreach (var fplugin in plugs) { if (UnloadPlugin(fplugin)) { sender.Message(255, ChatColor.DodgerBlue, fplugin.Name + " was unloaded."); } else { sender.Message(255, ChatColor.DodgerBlue, fplugin.Name + " was unloaded, errors occured during the process."); } } break; } case "-r": case "-rc": case "-ra": case "-rca": case "reload": { bool save = true; if (command == "-rc" || command == "-rca" || args.TryPop("-c") || args.TryPop("-clean")) { save = false; } string name; if (command == "-ra" || command == "-rca") { name = "all"; } else { args.ParseOne(out name); } BasePlugin[] plugs; if (name == "all" || name == "-a") { plugs = plugins.Values.ToArray(); } else { var splugin = PluginManager.GetPlugin(name); if (splugin == null) { sender.Message(255, "The plugin \"" + name + "\" could not be found."); return; } plugs = new BasePlugin [] { splugin }; } foreach (var fplugin in plugs) { var nplugin = PluginManager.ReloadPlugin(fplugin, save); if (nplugin == fplugin) { sender.Message(255, ChatColor.DodgerBlue, "Errors occured while reloading plugin " + fplugin.Name + ", old instance kept."); } else if (nplugin == null) { sender.Message(255, ChatColor.DodgerBlue, "Errors occured while reloading plugin " + fplugin.Name + ", it has been unloaded."); } } break; } case "-L": case "-LR": case "load": { bool replace = command == "-LR" || args.TryPop("-R") || args.TryPop("-replace"); bool save = command != "-LRc" && !args.TryPop("-c") && !args.TryPop("-clean"); var fname = string.Join(" ", args); string path; if (fname == "") { throw new CommandError("File name expected"); } if (Path.IsPathRooted(fname)) { path = Path.GetFullPath(fname); } else { path = Path.Combine(pluginPath, fname); } var fi = new FileInfo(path); if (!fi.Exists) { sender.Message(255, "Specified file doesn't exist."); return; } var newPlugin = LoadPluginFromPath(path); if (newPlugin == null) { sender.Message(255, "Unable to load plugin."); return; } var oldPlugin = GetPlugin(newPlugin.Name); if (oldPlugin != null) { if (!replace) { sender.Message(255, "A plugin named {0} is already loaded, use -replace to replace it.", oldPlugin.Name); return; } if (ReplacePlugin(oldPlugin, newPlugin, save)) { sender.Message(255, ChatColor.DodgerBlue, "Plugin {0} has been replaced.", oldPlugin.Name); } else if (oldPlugin.IsDisposed) { sender.Message(255, ChatColor.DodgerBlue, "Replacement of plugin {0} failed, it has been unloaded.", oldPlugin.Name); } else { sender.Message(255, ChatColor.DodgerBlue, "Replacement of plugin {0} failed, old instance kept.", oldPlugin.Name); } return; } if (!newPlugin.InitializeAndHookUp()) { sender.Message(255, ChatColor.DodgerBlue, "Failed to initialize new plugin instance."); } plugins.Add(newPlugin.Name.ToLower().Trim(), newPlugin); if (!newPlugin.Enable()) { sender.Message(255, ChatColor.DodgerBlue, "Failed to enable new plugin instance."); } break; } default: { throw new CommandError("Subcommand not recognized."); } } }
internal protected abstract void Unhook(BasePlugin plugin);
internal abstract void Replace(BasePlugin oldPlugin, BasePlugin newPlugin, Delegate callback, HookOrder order);
internal protected abstract void HookBase(BasePlugin plugin, Delegate callback, HookOrder order = HookOrder.NORMAL);