]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Add User::ForEachNeighbor()
authorAttila Molnar <attilamolnar@hush.com>
Sat, 24 Jan 2015 13:49:10 +0000 (14:49 +0100)
committerAttila Molnar <attilamolnar@hush.com>
Sat, 24 Jan 2015 13:49:10 +0000 (14:49 +0100)
include/users.h
src/users.cpp

index ceee4396bece7ef01ffe2479dfc139a5a73ebe06..6f319018fd135a3e16cdf3dfe9831b1f52bff461 100644 (file)
@@ -248,6 +248,19 @@ class CoreExport User : public Extensible
        std::bitset<ModeParser::MODEID_MAX> modes;
 
  public:
+       /** To execute a function for each local neighbor of a user, inherit from this class and
+        * pass an instance of it to User::ForEachNeighbor().
+        */
+       class ForEachNeighborHandler
+       {
+        public:
+               /** Method to execute for each local neighbor of a user.
+                * Derived classes must implement this.
+                * @param user Current neighbor
+                */
+               virtual void Execute(LocalUser* user) = 0;
+       };
+
        /** List of Memberships for this user
         */
        typedef insp::intrusive_list<Membership> ChanList;
@@ -542,6 +555,16 @@ class CoreExport User : public Extensible
         */
        void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text);
 
+       /** Execute a function once for each local neighbor of this user. By default, the neighbors of a user are the users
+        * who have at least one common channel with the user. Modules are allowed to alter the set of neighbors freely.
+        * This function is used for example to send something conditionally to neighbors, or to send different messages
+        * to different users depending on their oper status.
+        * @param handler Function object to call, inherited from ForEachNeighborHandler.
+        * @param include_self True to include this user in the set of neighbors, false otherwise.
+        * Modules may override this. Has no effect if this user is not local.
+        */
+       void ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self = true);
+
        /** Dump text to a user target, splitting it appropriately to fit
         * @param linePrefix text to prefix each complete line with
         * @param textStream the text to send to the user
index 34986a1833073f4629fccff7b383871acc55185f..cb1bc901ee89a4c038b6d4b8e08c5a0335658daa 100644 (file)
@@ -936,6 +936,59 @@ void User::WriteCommonQuit(const std::string &normal_text, const std::string &op
        }
 }
 
+void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
+{
+       // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user
+       // and visit all users on those channels. Because two users may share more than one common channel,
+       // we must skip users that we have already visited.
+       // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser.
+       // The global counter is incremented every time we do something for each neighbor of a user. Then,
+       // before visiting a member we examine user->already_sent. If it's equal to the current counter, we
+       // skip the member. Otherwise, we set it to the current counter and visit the member.
+
+       // Ask modules to build a list of exceptions.
+       // Mods may also exclude entire channels by erasing them from include_chans.
+       IncludeChanList include_chans(chans.begin(), chans.end());
+       std::map<User*, bool> exceptions;
+       exceptions[this] = include_self;
+       FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions));
+
+       // Get next id, guaranteed to differ from the already_sent field of all users
+       const already_sent_t newid = ++LocalUser::already_sent_id;
+
+       // Handle exceptions first
+       for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
+       {
+               LocalUser* curr = IS_LOCAL(i->first);
+               if (curr)
+               {
+                       // Mark as visited to ensure we won't visit again if there is a common channel
+                       curr->already_sent = newid;
+                       // Always treat quitting users as excluded
+                       if ((i->second) && (!curr->quitting))
+                               handler.Execute(curr);
+               }
+       }
+
+       // Now consider the real neighbors
+       for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
+       {
+               Channel* chan = (*i)->chan;
+               const Channel::MemberMap& userlist = chan->GetUsers();
+               for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j)
+               {
+                       LocalUser* curr = IS_LOCAL(j->first);
+                       // User not yet visited?
+                       if ((curr) && (curr->already_sent != newid))
+                       {
+                               // Mark as visited and execute function
+                               curr->already_sent = newid;
+                               handler.Execute(curr);
+                       }
+               }
+       }
+}
+
 void LocalUser::SendText(const std::string& line)
 {
        Write(line);