changeset 633:c07819d1d306

Irccd: add onDisconnect event, closes #767 @1h As an example of use, delete games in plugin tictactoe that belong to a specific server.
author David Demelier <markand@malikania.fr>
date Wed, 14 Mar 2018 13:12:36 +0100
parents e5d0f4289e04
children 06f5486bfdcb
files doc/html/CMakeLists.txt doc/html/template.html doc/src/api/event/onDisconnect.md libirccd-js/irccd/js/js_plugin.cpp libirccd-js/irccd/js/js_plugin.hpp libirccd-test/irccd/test/journal_server.hpp libirccd/irccd/daemon/dynlib_plugin.cpp libirccd/irccd/daemon/dynlib_plugin.hpp libirccd/irccd/daemon/plugin.hpp libirccd/irccd/daemon/server.cpp libirccd/irccd/daemon/server.hpp libirccd/irccd/daemon/service/server_service.cpp libirccd/irccd/daemon/service/server_service.hpp plugins/tictactoe/tictactoe.js tests/src/libirccd/command-server-disconnect/main.cpp tests/src/plugins/tictactoe/main.cpp
diffstat 16 files changed, 209 insertions(+), 137 deletions(-) [+]
line wrap: on
line diff
--- a/doc/html/CMakeLists.txt	Tue Mar 13 13:51:17 2018 +0100
+++ b/doc/html/CMakeLists.txt	Wed Mar 14 13:12:36 2018 +0100
@@ -20,114 +20,115 @@
 
 set(
     HTML_API_SOURCES
+    api/event/onCommand.md
+    api/event/onConnect.md
+    api/event/onDisconnect.md
+    api/event/onInvite.md
+    api/event/onJoin.md
+    api/event/onKick.md
+    api/event/onLoad.md
+    api/event/onMe.md
+    api/event/onMessage.md
+    api/event/onMode.md
+    api/event/onNames.md
+    api/event/onNick.md
+    api/event/onNotice.md
+    api/event/onPart.md
+    api/event/onReload.md
+    api/event/onTopic.md
+    api/event/onUnload.md
+    api/event/onWhois.md
+    api/index.md
     api/module/Irccd.Directory/index.md
-    api/module/Irccd.Directory/Irccd.Directory.remove.md
-    api/module/Irccd.Directory/Irccd.Directory.mkdir.md
     api/module/Irccd.Directory/Irccd.Directory.find.md
+    api/module/Irccd.Directory/Irccd.Directory.mkdir.md
     api/module/Irccd.Directory/Irccd.Directory.prototype.constructor.md
+    api/module/Irccd.Directory/Irccd.Directory.prototype.find.md
     api/module/Irccd.Directory/Irccd.Directory.prototype.remove.md
-    api/module/Irccd.Directory/Irccd.Directory.prototype.find.md
+    api/module/Irccd.Directory/Irccd.Directory.remove.md
     api/module/Irccd.ElapsedTimer/index.md
-    api/module/Irccd.ElapsedTimer/Irccd.ElapsedTimer.prototype.pause.md
+    api/module/Irccd.ElapsedTimer/Irccd.ElapsedTimer.prototype.constructor.md
     api/module/Irccd.ElapsedTimer/Irccd.ElapsedTimer.prototype.elapsed.md
-    api/module/Irccd.ElapsedTimer/Irccd.ElapsedTimer.prototype.constructor.md
+    api/module/Irccd.ElapsedTimer/Irccd.ElapsedTimer.prototype.pause.md
+    api/module/Irccd.ElapsedTimer/Irccd.ElapsedTimer.prototype.reset.md
     api/module/Irccd.ElapsedTimer/Irccd.ElapsedTimer.prototype.restart.md
-    api/module/Irccd.ElapsedTimer/Irccd.ElapsedTimer.prototype.reset.md
     api/module/Irccd.File/index.md
-    api/module/Irccd.File/Irccd.File.stat.md
+    api/module/Irccd.File/Irccd.File.basename.md
     api/module/Irccd.File/Irccd.File.dirname.md
     api/module/Irccd.File/Irccd.File.exists.md
-    api/module/Irccd.File/Irccd.File.remove.md
-    api/module/Irccd.File/Irccd.File.basename.md
+    api/module/Irccd.File/Irccd.File.prototype.basename.md
+    api/module/Irccd.File/Irccd.File.prototype.close.md
+    api/module/Irccd.File/Irccd.File.prototype.constructor.md
+    api/module/Irccd.File/Irccd.File.prototype.dirname.md
+    api/module/Irccd.File/Irccd.File.prototype.lines.md
+    api/module/Irccd.File/Irccd.File.prototype.readline.md
     api/module/Irccd.File/Irccd.File.prototype.read.md
+    api/module/Irccd.File/Irccd.File.prototype.remove.md
     api/module/Irccd.File/Irccd.File.prototype.seek.md
     api/module/Irccd.File/Irccd.File.prototype.stat.md
-    api/module/Irccd.File/Irccd.File.prototype.constructor.md
     api/module/Irccd.File/Irccd.File.prototype.tell.md
-    api/module/Irccd.File/Irccd.File.prototype.dirname.md
-    api/module/Irccd.File/Irccd.File.prototype.readline.md
     api/module/Irccd.File/Irccd.File.prototype.write.md
-    api/module/Irccd.File/Irccd.File.prototype.lines.md
-    api/module/Irccd.File/Irccd.File.prototype.close.md
-    api/module/Irccd.File/Irccd.File.prototype.remove.md
-    api/module/Irccd.File/Irccd.File.prototype.basename.md
+    api/module/Irccd.File/Irccd.File.remove.md
+    api/module/Irccd.File/Irccd.File.stat.md
+    api/module/Irccd/index.md
     api/module/Irccd.Logger/index.md
-    api/module/Irccd.Logger/Irccd.Logger.warning.md
+    api/module/Irccd.Logger/Irccd.Logger.debug.md
     api/module/Irccd.Logger/Irccd.Logger.info.md
-    api/module/Irccd.Logger/Irccd.Logger.debug.md
+    api/module/Irccd.Logger/Irccd.Logger.warning.md
     api/module/Irccd.Plugin/index.md
-    api/module/Irccd.Plugin/Irccd.Plugin.unload.md
+    api/module/Irccd.Plugin/Irccd.Plugin.info.md
+    api/module/Irccd.Plugin/Irccd.Plugin.list.md
     api/module/Irccd.Plugin/Irccd.Plugin.load.md
     api/module/Irccd.Plugin/Irccd.Plugin.reload.md
-    api/module/Irccd.Plugin/Irccd.Plugin.info.md
-    api/module/Irccd.Plugin/Irccd.Plugin.list.md
+    api/module/Irccd.Plugin/Irccd.Plugin.unload.md
     api/module/Irccd.Server/index.md
-    api/module/Irccd.Server/Irccd.Server.remove.md
-    api/module/Irccd.Server/Irccd.Server.list.md
+    api/module/Irccd.Server/Irccd.Server.add.md
     api/module/Irccd.Server/Irccd.Server.find.md
-    api/module/Irccd.Server/Irccd.Server.add.md
+    api/module/Irccd.Server/Irccd.Server.list.md
+    api/module/Irccd.Server/Irccd.Server.prototype.constructor.md
+    api/module/Irccd.Server/Irccd.Server.prototype.info.md
+    api/module/Irccd.Server/Irccd.Server.prototype.invite.md
+    api/module/Irccd.Server/Irccd.Server.prototype.isSelf.md
+    api/module/Irccd.Server/Irccd.Server.prototype.join.md
+    api/module/Irccd.Server/Irccd.Server.prototype.kick.md
     api/module/Irccd.Server/Irccd.Server.prototype.me.md
+    api/module/Irccd.Server/Irccd.Server.prototype.message.md
     api/module/Irccd.Server/Irccd.Server.prototype.mode.md
-    api/module/Irccd.Server/Irccd.Server.prototype.part.md
-    api/module/Irccd.Server/Irccd.Server.prototype.message.md
-    api/module/Irccd.Server/Irccd.Server.prototype.topic.md
-    api/module/Irccd.Server/Irccd.Server.prototype.whois.md
+    api/module/Irccd.Server/Irccd.Server.prototype.names.md
     api/module/Irccd.Server/Irccd.Server.prototype.nick.md
-    api/module/Irccd.Server/Irccd.Server.prototype.constructor.md
-    api/module/Irccd.Server/Irccd.Server.prototype.join.md
-    api/module/Irccd.Server/Irccd.Server.prototype.invite.md
-    api/module/Irccd.Server/Irccd.Server.prototype.info.md
-    api/module/Irccd.Server/Irccd.Server.prototype.isSelf.md
     api/module/Irccd.Server/Irccd.Server.prototype.notice.md
-    api/module/Irccd.Server/Irccd.Server.prototype.kick.md
+    api/module/Irccd.Server/Irccd.Server.prototype.part.md
+    api/module/Irccd.Server/Irccd.Server.prototype.topic.md
     api/module/Irccd.Server/Irccd.Server.prototype.toString.md
-    api/module/Irccd.Server/Irccd.Server.prototype.names.md
+    api/module/Irccd.Server/Irccd.Server.prototype.whois.md
+    api/module/Irccd.Server/Irccd.Server.remove.md
     api/module/Irccd.System/index.md
+    api/module/Irccd.System/Irccd.System.env.md
     api/module/Irccd.System/Irccd.System.exec.md
-    api/module/Irccd.System/Irccd.System.sleep.md
+    api/module/Irccd.System/Irccd.System.home.md
+    api/module/Irccd.System/Irccd.System.name.md
     api/module/Irccd.System/Irccd.System.popen.md
-    api/module/Irccd.System/Irccd.System.env.md
-    api/module/Irccd.System/Irccd.System.home.md
+    api/module/Irccd.System/Irccd.System.sleep.md
+    api/module/Irccd.System/Irccd.System.ticks.md
     api/module/Irccd.System/Irccd.System.uptime.md
+    api/module/Irccd.System/Irccd.System.usleep.md
     api/module/Irccd.System/Irccd.System.version.md
-    api/module/Irccd.System/Irccd.System.usleep.md
-    api/module/Irccd.System/Irccd.System.name.md
-    api/module/Irccd.System/Irccd.System.ticks.md
     api/module/Irccd.Timer/index.md
+    api/module/Irccd.Timer/Irccd.Timer.prototype.constructor.md
     api/module/Irccd.Timer/Irccd.Timer.prototype.start.md
-    api/module/Irccd.Timer/Irccd.Timer.prototype.constructor.md
     api/module/Irccd.Timer/Irccd.Timer.prototype.stop.md
-    api/module/Irccd/index.md
     api/module/Irccd.Unicode/index.md
+    api/module/Irccd.Unicode/Irccd.Unicode.isDigit.md
+    api/module/Irccd.Unicode/Irccd.Unicode.isLetter.md
+    api/module/Irccd.Unicode/Irccd.Unicode.isLower.md
     api/module/Irccd.Unicode/Irccd.Unicode.isSpace.md
     api/module/Irccd.Unicode/Irccd.Unicode.isTitle.md
     api/module/Irccd.Unicode/Irccd.Unicode.isUpper.md
-    api/module/Irccd.Unicode/Irccd.Unicode.isLetter.md
-    api/module/Irccd.Unicode/Irccd.Unicode.isDigit.md
-    api/module/Irccd.Unicode/Irccd.Unicode.isLower.md
     api/module/Irccd.Util/index.md
     api/module/Irccd.Util/Irccd.Util.cut.md
     api/module/Irccd.Util/Irccd.Util.format.md
     api/module/Irccd.Util/Irccd.Util.splithost.md
     api/module/Irccd.Util/Irccd.Util.splituser.md
-    api/index.md
-    api/event/onWhois.md
-    api/event/onMessage.md
-    api/event/onPart.md
-    api/event/onMode.md
-    api/event/onNotice.md
-    api/event/onLoad.md
-    api/event/onInvite.md
-    api/event/onCommand.md
-    api/event/onKick.md
-    api/event/onReload.md
-    api/event/onTopic.md
-    api/event/onConnect.md
-    api/event/onJoin.md
-    api/event/onMe.md
-    api/event/onNick.md
-    api/event/onNames.md
-    api/event/onUnload.md
 )
 
 set(
--- a/doc/html/template.html	Tue Mar 13 13:51:17 2018 +0100
+++ b/doc/html/template.html	Wed Mar 14 13:12:36 2018 +0100
@@ -38,6 +38,7 @@
       <ul class="nav nav-sidebar sidebar-list">
         <li><a href="$baseurl$api/event/onCommand.html">onCommand</a></li>
         <li><a href="$baseurl$api/event/onConnect.html">onConnect</a></li>
+        <li><a href="$baseurl$api/event/onDisconnect.html">onDisconnect</a></li>
         <li><a href="$baseurl$api/event/onInvite.html">onInvite</a></li>
         <li><a href="$baseurl$api/event/onJoin.html">onJoin</a></li>
         <li><a href="$baseurl$api/event/onKick.html">onKick</a></li>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/src/api/event/onDisconnect.md	Wed Mar 14 13:12:36 2018 +0100
@@ -0,0 +1,14 @@
+# Event onDisconnect
+
+This callback is called when a server has been disconnected by any way.
+
+# Synopsis
+
+```javascript
+function onDisonnect(server)
+```
+
+# Arguments
+
+  - **server**: the current server.
+
--- a/libirccd-js/irccd/js/js_plugin.cpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd-js/irccd/js/js_plugin.cpp	Wed Mar 14 13:12:36 2018 +0100
@@ -182,6 +182,11 @@
     call("onConnect", event.server);
 }
 
+void js_plugin::on_disconnect(irccd&, const disconnect_event& event)
+{
+    call("onDisconnect", event.server);
+}
+
 void js_plugin::on_invite(irccd&, const invite_event& event)
 {
     call("onInvite", event.server, event.origin, event.channel);
--- a/libirccd-js/irccd/js/js_plugin.hpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd-js/irccd/js/js_plugin.hpp	Wed Mar 14 13:12:36 2018 +0100
@@ -99,7 +99,7 @@
     void open();
 
     /**
-     * \copydoc Plugin::config
+     * \copydoc plugin::config
      */
     plugin_config config() override
     {
@@ -107,7 +107,7 @@
     }
 
     /**
-     * \copydoc Plugin::setConfig
+     * \copydoc plugin::setConfig
      */
     void set_config(plugin_config config) override
     {
@@ -115,7 +115,7 @@
     }
 
     /**
-     * \copydoc Plugin::formats
+     * \copydoc plugin::formats
      */
     plugin_formats formats() override
     {
@@ -123,7 +123,7 @@
     }
 
     /**
-     * \copydoc Plugin::setFormats
+     * \copydoc plugin::setFormats
      */
     void set_formats(plugin_formats formats) override
     {
@@ -131,7 +131,7 @@
     }
 
     /**
-     * \copydoc Plugin::paths
+     * \copydoc plugin::paths
      */
     plugin_paths paths() override
     {
@@ -139,7 +139,7 @@
     }
 
     /**
-     * \copydoc Plugin::set_paths
+     * \copydoc plugin::set_paths
      */
     void set_paths(plugin_paths paths) override
     {
@@ -147,87 +147,92 @@
     }
 
     /**
-     * \copydoc Plugin::on_command
+     * \copydoc plugin::on_command
      */
     void on_command(irccd& irccd, const message_event& event) override;
 
     /**
-     * \copydoc Plugin::on_connect
+     * \copydoc plugin::on_connect
      */
     void on_connect(irccd& irccd, const connect_event& event) override;
 
     /**
-     * \copydoc Plugin::on_invite
+     * \copydoc plugin::on_disconnect
+     */
+    void on_disconnect(irccd& irccd, const disconnect_event& event) override;
+
+    /**
+     * \copydoc plugin::on_invite
      */
     void on_invite(irccd& irccd, const invite_event& event) override;
 
     /**
-     * \copydoc Plugin::on_join
+     * \copydoc plugin::on_join
      */
     void on_join(irccd& irccd, const join_event& event) override;
 
     /**
-     * \copydoc Plugin::on_kick
+     * \copydoc plugin::on_kick
      */
     void on_kick(irccd& irccd, const kick_event& event) override;
 
     /**
-     * \copydoc Plugin::on_load
+     * \copydoc plugin::on_load
      */
     void on_load(irccd& irccd) override;
 
     /**
-     * \copydoc Plugin::on_message
+     * \copydoc plugin::on_message
      */
     void on_message(irccd& irccd, const message_event& event) override;
 
     /**
-     * \copydoc Plugin::on_me
+     * \copydoc plugin::on_me
      */
     void on_me(irccd& irccd, const me_event& event) override;
 
     /**
-     * \copydoc Plugin::on_mode
+     * \copydoc plugin::on_mode
      */
     void on_mode(irccd& irccd, const mode_event& event) override;
 
     /**
-     * \copydoc Plugin::on_names
+     * \copydoc plugin::on_names
      */
     void on_names(irccd& irccd, const names_event& event) override;
 
     /**
-     * \copydoc Plugin::on_nick
+     * \copydoc plugin::on_nick
      */
     void on_nick(irccd& irccd, const nick_event& event) override;
 
     /**
-     * \copydoc Plugin::on_notice
+     * \copydoc plugin::on_notice
      */
     void on_notice(irccd& irccd, const notice_event& event) override;
 
     /**
-     * \copydoc Plugin::on_part
+     * \copydoc plugin::on_part
      */
     void on_part(irccd& irccd, const part_event& event) override;
 
     /**
-     * \copydoc Plugin::on_reload
+     * \copydoc plugin::on_reload
      */
     void on_reload(irccd& irccd) override;
 
     /**
-     * \copydoc Plugin::on_topic
+     * \copydoc plugin::on_topic
      */
     void on_topic(irccd& irccd, const topic_event& event) override;
 
     /**
-     * \copydoc Plugin::on_unload
+     * \copydoc plugin::on_unload
      */
     void on_unload(irccd& irccd) override;
 
     /**
-     * \copydoc Plugin::on_whois
+     * \copydoc plugin::on_whois
      */
     void on_whois(irccd& irccd, const whois_event& event) override;
 };
--- a/libirccd-test/irccd/test/journal_server.hpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd-test/irccd/test/journal_server.hpp	Wed Mar 14 13:12:36 2018 +0100
@@ -93,14 +93,6 @@
     }
 
     /**
-     * \copydoc server::connect
-     */
-    void disconnect() noexcept override
-    {
-        // avoid disconnecting.
-    }
-
-    /**
      * \copydoc server::reconnect
      */
     void reconnect() noexcept override;
--- a/libirccd/irccd/daemon/dynlib_plugin.cpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd/irccd/daemon/dynlib_plugin.cpp	Wed Mar 14 13:12:36 2018 +0100
@@ -76,6 +76,11 @@
     plugin_->on_connect(irccd, ev);
 }
 
+void dynlib_plugin::on_disconnect(irccd& irccd, const disconnect_event& ev)
+{
+    plugin_->on_disconnect(irccd, ev);
+}
+
 void dynlib_plugin::on_invite(irccd& irccd, const invite_event& ev)
 {
     plugin_->on_invite(irccd, ev);
--- a/libirccd/irccd/daemon/dynlib_plugin.hpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd/irccd/daemon/dynlib_plugin.hpp	Wed Mar 14 13:12:36 2018 +0100
@@ -60,6 +60,11 @@
     void on_connect(irccd& irccd, const connect_event& event) override;
 
     /**
+     * \copydoc plugin::on_disconnect
+     */
+    void on_disconnect(irccd& irccd, const disconnect_event& event) override;
+
+    /**
      * \copydoc plugin::on_invite
      */
     void on_invite(irccd& irccd, const invite_event& event) override;
--- a/libirccd/irccd/daemon/plugin.hpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd/irccd/daemon/plugin.hpp	Wed Mar 14 13:12:36 2018 +0100
@@ -283,6 +283,18 @@
     }
 
     /**
+     * On disconnection.
+     *
+     * \param irccd the irccd instance
+     * \param event the event
+     */
+    virtual void on_disconnect(irccd& irccd, const disconnect_event& event)
+    {
+        (void)irccd;
+        (void)event;
+    }
+
+    /**
      * On invitation.
      *
      * \param irccd the irccd instance
--- a/libirccd/irccd/daemon/server.cpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd/irccd/daemon/server.cpp	Wed Mar 14 13:12:36 2018 +0100
@@ -199,25 +199,6 @@
     for (unsigned int i = 0; i < msg.args().size(); ++i) {
         if (msg.arg(i).compare(0, 6, "PREFIX") == 0) {
             modes_ = isupport_extract_prefixes(msg.arg(i));
-
-#if 0
-#if !defined(NDEBUG)
-            auto show = [this] (auto mode, auto title) {
-                auto it = modes_.find(mode);
-
-                if (it != modes_.end())
-                    log::debug(string_util::sprintf("  %-12s: %c", title, it->second));
-            };
-
-            log::debug(string_util::sprintf("server %s: isupport modes:", name_));
-            show(channel_mode::creator, "creator");
-            show(channel_mode::half_op, "half_op");
-            show(channel_mode::op, "op");
-            show(channel_mode::protection, "protection");
-            show(channel_mode::voiced, "voiced");
-#endif // !NDEBUG
-#endif
-
             break;
         }
     }
@@ -465,16 +446,12 @@
 {
     if (code) {
         conn_ = nullptr;
-#if 0
-        log::warning(string_util::sprintf("server %s: error while connecting", name_));
-        log::warning(string_util::sprintf("server %s: %s", name_, code.message()));
-#endif
 
         // Wait before reconnecting.
         if (recotries_ != 0) {
             if (recotries_ > 0 && recocur_ >= recotries_) {
                 state_ = state_t::disconnected;
-                on_die();
+                on_die({shared_from_this()});
             } else {
                 state_ = state_t::waiting;
                 wait();
@@ -493,7 +470,8 @@
 
 server::~server()
 {
-    disconnect();
+    conn_ = nullptr;
+    state_ = state_t::disconnected;
 }
 
 void server::set_nickname(std::string nickname)
@@ -544,7 +522,7 @@
 {
     conn_ = nullptr;
     state_ = state_t::disconnected;
-    on_die();
+    on_die({shared_from_this()});
 }
 
 void server::reconnect() noexcept
--- a/libirccd/irccd/daemon/server.hpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd/irccd/daemon/server.hpp	Wed Mar 14 13:12:36 2018 +0100
@@ -84,6 +84,14 @@
 };
 
 /**
+ * \brief Connection success event.
+ */
+class disconnect_event {
+public:
+    std::shared_ptr<class server> server;   //!< The server.
+};
+
+/**
  * \brief Invite event.
  */
 class invite_event {
@@ -261,7 +269,7 @@
         connecting,         //!< network connection in progress,
         identifying,        //!< sending nick, user and password commands,
         waiting,            //!< waiting for reconnection,
-        connected           //!< ready for use
+        connected           //!< ready for use.
     };
 
     /**
@@ -278,7 +286,7 @@
      *
      * The server is dead.
      */
-    boost::signals2::signal<void ()> on_die;
+    boost::signals2::signal<void (disconnect_event)> on_die;
 
     /**
      * Signal: on_invite
--- a/libirccd/irccd/daemon/service/server_service.cpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd/irccd/daemon/service/server_service.cpp	Wed Mar 14 13:12:36 2018 +0100
@@ -261,6 +261,27 @@
     );
 }
 
+void server_service::handle_die(const disconnect_event& ev)
+{
+    // First, remove the server in case of exceptions.
+    servers_.erase(std::find(servers_.begin(), servers_.end(), ev.server));
+
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onDisconnect" << std::endl;
+    irccd_.transports().broadcast(nlohmann::json::object({
+        { "event",      "onDisconnect"      },
+        { "server",     ev.server->name()   }
+    }));
+
+    dispatch(irccd_, ev.server->name(), /* origin */ "", /* channel */ "",
+        [=] (plugin&) -> std::string {
+            return "onDisconnect";
+        },
+        [=] (plugin& plugin) {
+            plugin.on_disconnect(irccd_, ev);
+        }
+    );
+}
+
 void server_service::handle_invite(const invite_event& ev)
 {
     irccd_.log().debug() << "server " << ev.server->name() << ": event onInvite:\n";
@@ -635,9 +656,8 @@
 {
     assert(!has(server->name()));
 
-    std::weak_ptr<class server> ptr(server);
-
     server->on_connect.connect(boost::bind(&server_service::handle_connect, this, _1));
+    server->on_die.connect(boost::bind(&server_service::handle_die, this, _1));
     server->on_invite.connect(boost::bind(&server_service::handle_invite, this, _1));
     server->on_join.connect(boost::bind(&server_service::handle_join, this, _1));
     server->on_kick.connect(boost::bind(&server_service::handle_kick, this, _1));
@@ -650,15 +670,6 @@
     server->on_part.connect(boost::bind(&server_service::handle_part, this, _1));
     server->on_topic.connect(boost::bind(&server_service::handle_topic, this, _1));
     server->on_whois.connect(boost::bind(&server_service::handle_whois, this, _1));
-    server->on_die.connect([this, ptr] () {
-        auto server = ptr.lock();
-
-        if (server) {
-            irccd_.log().info(string_util::sprintf("server %s: removed", server->name()));
-            servers_.erase(std::find(servers_.begin(), servers_.end(), server));
-        }
-    });
-
     server->connect();
     servers_.push_back(std::move(server));
 }
@@ -704,7 +715,13 @@
 
 void server_service::clear() noexcept
 {
-    for (auto &server : servers_)
+    /*
+     * Copy the array, because disconnect() may trigger on_die signal which
+     * erase the server from itself.
+     */
+    const auto save = servers_;
+
+    for (const auto& server : save)
         server->disconnect();
 
     servers_.clear();
--- a/libirccd/irccd/daemon/service/server_service.hpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/libirccd/irccd/daemon/service/server_service.hpp	Wed Mar 14 13:12:36 2018 +0100
@@ -44,6 +44,7 @@
     std::vector<std::shared_ptr<server>> servers_;
 
     void handle_connect(const connect_event&);
+    void handle_die(const disconnect_event&);
     void handle_invite(const invite_event&);
     void handle_join(const join_event&);
     void handle_kick(const kick_event&);
--- a/plugins/tictactoe/tictactoe.js	Tue Mar 13 13:51:17 2018 +0100
+++ b/plugins/tictactoe/tictactoe.js	Wed Mar 14 13:12:36 2018 +0100
@@ -341,6 +341,13 @@
         Game.remove(server, channel);
 }
 
+function onDisconnect(server)
+{
+    for (var key in Game.map)
+        if (key.endsWith(server.toString()))
+            delete Game.map[key];
+}
+
 function onKick(server, origin, channel, target)
 {
     Game.clear(server, target, channel);
--- a/tests/src/libirccd/command-server-disconnect/main.cpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/tests/src/libirccd/command-server-disconnect/main.cpp	Wed Mar 14 13:12:36 2018 +0100
@@ -29,12 +29,22 @@
 
 namespace {
 
+class no_disconnect_server : public journal_server {
+public:
+    using journal_server::journal_server;
+
+    void disconnect() noexcept override
+    {
+        // do nothing.
+    }
+};
+
 class server_disconnect_test : public command_test<server_disconnect_command> {
 protected:
     server_disconnect_test()
     {
-        daemon_->servers().add(std::make_unique<journal_server>(service_, "s1"));
-        daemon_->servers().add(std::make_unique<journal_server>(service_, "s2"));
+        daemon_->servers().add(std::make_unique<no_disconnect_server>(service_, "s1"));
+        daemon_->servers().add(std::make_unique<no_disconnect_server>(service_, "s2"));
     }
 };
 
--- a/tests/src/plugins/tictactoe/main.cpp	Tue Mar 13 13:51:17 2018 +0100
+++ b/tests/src/plugins/tictactoe/main.cpp	Wed Mar 14 13:12:36 2018 +0100
@@ -230,6 +230,17 @@
     }
 }
 
+BOOST_AUTO_TEST_CASE(disconnect)
+{
+    auto players = start();
+
+    server_->disconnect();
+    server_->cqueue().clear();
+    plugin_->on_message(irccd_, {server_, players.first, "#tictactoe", "a 1"});
+
+    BOOST_TEST(server_->cqueue().empty());
+}
+
 BOOST_AUTO_TEST_CASE(kick)
 {
     auto players = start();