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);
internal protected abstract void Unhook(BasePlugin plugin);
// 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) { Tools.NotifyAllPlayers("<Server> Reloading plugin " + Name + ", you may experience lag...", Color.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)) { // Tools.WriteLine ("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 { Tools.NotifyAllPlayers("<Server> Done.", Color.White, true); // clean up remaining hooks if (noreturn) { ProgramLog.Debug.Log("Disposing of old plugin instance..."); Dispose(); } } } return(result); }