Просмотр исходного кода

Merge branch '49-member-caching'

* 49-member-caching:
  Delete expired cached members after wait_split (900 seconds) (#49)
  Remove from cached members if killing a member without caching (#49)
  Update member from cache if possible (#49)
  Cache members when removing them (#49)
  DRY member destruction (#49)

Conflicts:
	doc/UPDATES
Bryan Drewery 14 лет назад
Родитель
Сommit
1b063c0461
6 измененных файлов с 105 добавлено и 12 удалено
  1. 1 0
      doc/UPDATES
  2. 16 0
      src/chan.h
  3. 20 3
      src/mod/channels.mod/chanmisc.c
  4. 9 1
      src/mod/irc.mod/chan.c
  5. 56 7
      src/mod/irc.mod/irc.c
  6. 3 1
      src/mod/irc.mod/irc.h

+ 1 - 0
doc/UPDATES

@@ -51,6 +51,7 @@
   * Add chanset 'voice-moderate' which will auto set +m in '+voice' channel and devoice flooding clients instead of kick. (#48)
   * Bots now will enforce devoices made by ops (user +o) and allow any op (user +o) to revoice to disable that enforcement.
     This used to use +m to enforce/allowed revoicing.
+  * Bots will now remember a client's flood/devoice settings if they cycle (#49)
 
 1.3.4 - http://wraith.botpack.net/milestone/1.3.4
   * Fix various compile warnings with newer GCC

+ 16 - 0
src/chan.h

@@ -31,6 +31,19 @@ typedef struct memstruct {
   bd::HashTable<flood_t, int>         *floodnum; // floodnum[FLOOD_PRIVMSG] = 1;
 } memberlist;
 
+#include <bdlib/src/bdlib.h>
+BDLIB_NS_BEGIN
+template<typename T>
+  struct Hash;
+
+template<>
+  struct Hash<memberlist*>
+  {
+    // Use memory address
+    inline size_t operator()(memberlist* m) const { return reinterpret_cast<size_t>(m); }
+  };
+BDLIB_NS_END
+
 #define CHAN_FLAG_OP	1
 #define CHAN_FLAG_VOICE 2
 #define CHAN_FLAG_USER 3
@@ -128,6 +141,9 @@ struct chan_t {
   // Shared non-member counts (JOIN/PART)
   bd::HashTable<bd::String, bd::HashTable<flood_t, time_t> >     *floodtime; // floodtime[uhost][FLOOD_PRIVMSG] = now;
   bd::HashTable<bd::String, bd::HashTable<flood_t, int> >         *floodnum; //  floodnum[uhost][FLOOD_PRIVMSG] = 1;
+
+  // Member caching to cache cyclers
+  bd::HashTable<bd::String, memberlist*> *cached_members;
 };
 
 #define CHANINV    BIT0		/* +i					*/

+ 20 - 3
src/mod/channels.mod/chanmisc.c

@@ -970,6 +970,7 @@ static void init_channel(struct chanset_t *chan, bool reset)
   chan->channel.topic = NULL;
   chan->channel.floodtime = new bd::HashTable<bd::String, bd::HashTable<flood_t, time_t> >;
   chan->channel.floodnum  = new bd::HashTable<bd::String, bd::HashTable<flood_t, int> >;
+  chan->channel.cached_members = new bd::HashTable<bd::String, memberlist*>;
 }
 
 static void clear_masklist(masklist *m)
@@ -996,9 +997,7 @@ void clear_channel(struct chanset_t *chan, bool reset)
     free(chan->channel.topic);
   for (m = chan->channel.member; m; m = m1) {
     m1 = m->next;
-    delete m->floodtime;
-    delete m->floodnum;
-    free(m);
+    delete_member(m);
   }
 
   clear_masklist(chan->channel.ban);
@@ -1018,6 +1017,24 @@ void clear_channel(struct chanset_t *chan, bool reset)
   delete chan->channel.floodnum;
   chan->channel.floodnum = NULL;
 
+  if (chan->channel.cached_members) {
+    if (chan->channel.cached_members->size()) {
+      bd::Array<bd::String> member_uhosts(chan->channel.cached_members->keys());
+      for (size_t i = 0; i < member_uhosts.length(); ++i) {
+        const bd::String uhost(member_uhosts[i]);
+
+        // Delete the cached member
+        m = (*chan->channel.cached_members)[uhost];
+        delete_member(m);
+
+        // Remove the cached member (not technically needed as it is deleted below, but for completeness.)
+        chan->channel.cached_members->remove(uhost);
+      }
+    }
+    delete chan->channel.cached_members;
+    chan->channel.cached_members = NULL;
+  }
+
   if (reset)
     init_channel(chan, 1);
   for (size_t i = 0; i < MODES_PER_LINE_MAX; ++i) {

+ 9 - 1
src/mod/irc.mod/chan.c

@@ -285,6 +285,12 @@ static memberlist *newmember(struct chanset_t *chan, char *nick)
   return n;
 }
 
+void delete_member(memberlist* m) {
+  delete m->floodtime;
+  delete m->floodnum;
+  free(m);
+}
+
 static bool member_getuser(memberlist* m, bool act_on_lookup) {
   if (!m) return 0;
   if (!m->user && !m->tried_getuser) {
@@ -1947,6 +1953,7 @@ static int got352or4(struct chanset_t *chan, char *user, char *host, char *nick,
     /* Store the userhost */
     simple_snprintf(m->userhost, sizeof(m->userhost), "%s@%s", user, host);
     simple_snprintf(m->from, sizeof(m->from), "%s!%s", m->nick, m->userhost);
+    member_update_from_cache(chan, m);
 
     if (!m->userip[0]) {
       if (ip)
@@ -2732,6 +2739,7 @@ static int gotjoin(char *from, char *chname)
 	m = newmember(chan, nick);
 	m->joined = m->last = now;
 	strlcpy(m->userhost, uhost, sizeof(m->userhost));
+        member_update_from_cache(chan, m);
         simple_snprintf(m->from, sizeof(m->from), "%s!%s", m->nick, m->userhost);
         if (is_dotted_ip(host)) {
           strlcpy(m->userip, uhost, sizeof(m->userip));
@@ -3053,7 +3061,7 @@ static int gotnick(char *from, char *msg)
       if (rfc_casecmp(nick, msg)) {
         /* Someone on channel with old nick?! */
 	if ((mm = ismember(chan, msg)))
-	  killmember(chan, mm->nick);
+	  killmember(chan, mm->nick, false);
       }
 
       strlcpy(m->nick, msg, sizeof(m->nick));

+ 56 - 7
src/mod/irc.mod/irc.c

@@ -1146,7 +1146,7 @@ new_mask(masklist *m, char *s, char *who)
 /* Removes a nick from the channel member list (returns 1 if successful)
  */
 static bool
-killmember(struct chanset_t *chan, char *nick)
+killmember(struct chanset_t *chan, char *nick, bool cacheMember)
 {
   memberlist *x = NULL, *old = NULL;
 
@@ -1162,10 +1162,17 @@ killmember(struct chanset_t *chan, char *nick)
     old->next = x->next;
   else
     chan->channel.member = x->next;
-  delete x->floodtime;
-  delete x->floodnum;
-  free(x);
-  chan->channel.members--;
+
+  if (cacheMember) {
+    x->last = now;
+    // Don't delete here, will delete when it expires from the cache.
+    (*chan->channel.cached_members)[x->userhost] = x;
+  } else {
+    chan->channel.cached_members->remove(x->userhost);
+    delete_member(x);
+  }
+
+  --chan->channel.members;
 
   /* The following two errors should NEVER happen. We will try to correct
    * them though, to keep the bot from crashing.
@@ -1188,6 +1195,32 @@ killmember(struct chanset_t *chan, char *nick)
   return 1;
 }
 
+/**
+ * Update the member with cached information from a parted/quitted member
+ */
+static void member_update_from_cache(struct chanset_t* chan, memberlist *m) {
+  using std::swap;
+
+  // Are they in the cache?
+  const bd::String userhost(m->userhost);
+  if (chan->channel.cached_members->contains(userhost)) {
+    memberlist *cached_member = (*chan->channel.cached_members)[userhost];
+
+    // Update important flood tracking information
+    swap(m->floodtime, cached_member->floodtime);
+    swap(m->floodnum, cached_member->floodnum);
+
+    // Update EVOICE flag
+    if (cached_member->flags & EVOICE) {
+      m->flags |= EVOICE;
+    }
+
+    // No longer need the cached member
+    delete_member(cached_member);
+    chan->channel.cached_members->remove(userhost);
+  }
+}
+
 /* Check whether I'm voice. Returns boolean 1 or 0.
  */
 bool
@@ -1464,10 +1497,11 @@ void check_shouldjoin(struct chanset_t* chan)
 static void
 check_expired_chanstuff(struct chanset_t *chan)
 {
+  memberlist *m = NULL;
   check_shouldjoin(chan);
   if (channel_active(chan) && shouldjoin(chan)) {
     masklist *b = NULL, *e = NULL;
-    memberlist *m = NULL, *n = NULL;
+    memberlist *n = NULL;
     struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0 };
 
     if (me_op(chan)) {
@@ -1529,7 +1563,7 @@ check_expired_chanstuff(struct chanset_t *chan)
         if (now - m->split > wait_split) {
           putlog(LOG_JOIN, chan->dname, "%s (%s) got lost in the net-split.", m->nick, m->userhost);
           --(chan->channel.splitmembers);
-          killmember(chan, m->nick);
+          killmember(chan, m->nick, false);
           continue;
         }
       }
@@ -1586,6 +1620,21 @@ check_expired_chanstuff(struct chanset_t *chan)
       recheck_channel_modes(chan);
     }
   }
+  // Clear out expired cached members
+  if (chan->channel.cached_members && chan->channel.cached_members->size()) {
+    bd::Array<bd::String> member_uhosts(chan->channel.cached_members->keys());
+    for (size_t i = 0; i < member_uhosts.length(); ++i) {
+      const bd::String uhost(member_uhosts[i]);
+
+      m = (*chan->channel.cached_members)[uhost];
+
+      // Delete the expired member
+      if (now - m->last > wait_split) {
+        delete_member(m);
+        chan->channel.cached_members->remove(uhost);
+      }
+    }
+  }
 }
 
 void

+ 3 - 1
src/mod/irc.mod/irc.h

@@ -92,7 +92,8 @@ static bool detect_chan_flood(memberlist *m, const char* from, struct chanset_t
 static bool new_mask(masklist *, char *, char *);
 static void do_closed_kick(struct chanset_t *, memberlist *);
 static char *quickban(struct chanset_t *, const char *);
-static bool killmember(struct chanset_t *chan, char *nick);
+static bool killmember(struct chanset_t *chan, char *nick, bool cacheMember = true);
+static void member_update_from_cache(struct chanset_t* chan, memberlist *m);
 static void check_lonely_channel(struct chanset_t *chan);
 static int gotmode(char *, char *);
 void unset_im(struct chanset_t* chan);
@@ -157,6 +158,7 @@ void flush_modes();
 void reset_chan_info(struct chanset_t *);
 char *getnick(const char *, struct chanset_t *);
 void check_shouldjoin(struct chanset_t* chan);
+void delete_member(memberlist* m);
 
 extern int		max_bans, max_exempts, max_invites, max_modes;
 extern bool		use_354, include_lk;