This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Possible eggdrop bug when user disconnects from dcc chat?

Help for those learning Tcl or writing their own scripts.
Post Reply
A
Anansi
Voice
Posts: 16
Joined: Mon Jul 02, 2007 8:03 am

Possible eggdrop bug when user disconnects from dcc chat?

Post by Anansi »

I wrote and maintain a TCL with roughly 2000 lines that implements a game of Mafia to be played on IRC with multiple players (it runs on irchighway). The game is played in a telnet session hosted by the bot. Note that I will only paste here code snippets, often taken from different locations in the script.

Code: Select all

listen $mafia_port script mafia_incoming pub

proc mafia_incoming {idx} {
  global mafia_players
  set dl [dcclist socket]
  set myhost unknown
  for {set i 0} {$i < [llength $dl]} {incr i} {
    if {[lindex [lindex $dl $i] 0] == $idx} {set myhost [lindex [split [lindex [lindex $dl $i] 2] "@"] 1]}
  }
  set dl [dcclist script]
  set clones 0
  for {set i 0} {$i < [llength $dl]} {incr i} {
    if {[lindex [lindex $dl $i] 0] != $idx} {
      if {[string tolower [lindex [split [lindex [lindex $dl $i] 2] "@"] 1]] == [string tolower $myhost]} {set clones 1}
    }
  }
  if {!$clones} {
    control $idx mafia_line
  } else {
    killdcc $idx
  }
}
This is the code that handles incoming connections. It works perfectly. As you can see, mafia_line is the procedure that handles the messages received from the user. According to the eggdrop documentation, this procedure can also handle user disconnections, as a blank message is sent to it when the user disconnects - and blank messages are never passed to the procedure otherwise. So I set things up like this:

Code: Select all

proc mafia_line {idx text} {
  set me [mafia_getplayerbyidx $idx]
  set mynick [mafia_geti $me 1]

  (...)

  if {$text == ""} {
    mafia_killplayer [mafia_geti $me 1]
    mafia_echo "common" "0,1*** $mynick has disconnected from Mafia."
    mafia_killcon [mafia_geti $me 0]
  }

  (...)

}

proc mafia_killcon {idx} {
  global mafia_players
  set me [mafia_getplayerbyidx $idx]
  if {$me != {}} {
    set mafia_players [lreplace $mafia_players $me $me]
  }
  if {[valididx $idx]} {
    killdcc $idx
  }
}
This also works perfectly, with one single exception: mafia_echo is supposed to relay text to everyone who is connected to the game. This function, in this situation, does not work. It is used in a bazillion of other places in the script, as well as mafia_geti and mafia_getplayerbyidx, so they can't POSSIBLY be wrong. It's just that, in a procedure triggered by a player's disconnection, eggdrop does not seem to allow putdcc/putidx to be called. To confirm this, I have tried to manually use:

.tcl mafia_line "SomePlayersNickname" ""

The player was successfully disconnected AND the remaining players received the message, $mynick has disconnected from Mafia. So the only occasion in which players can't receive the message is when mafia_line is triggered by a user's disconnection.

Now, the Eggdrop documentation emphasizes that the programmer should be careful not to send messages to the IDX of the user who has just disconnected. While I was careful not to do that, I was wondering if Eggdrop doesn't somehow block ALL calls to putidx/putdcc during disconnect calls to the message handling procedure in order to stop the user from making this mistake, thus accidentally blocking calls to putidx/putdcc with legitimate (not yet disconnected) IDXs in these circumstances.

Any ideas?

EDIT/PS: Note that putidx/putdcc do not work in the situation described, but they do not output any errors either, and they do not halt the evaluation of the procedure.
User avatar
Sir_Fz
Revered One
Posts: 3794
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

Are you sure that $text is "" when a user is disconnected? Try adding some putlogs to debug the problem.
A
Anansi
Voice
Posts: 16
Joined: Mon Jul 02, 2007 8:03 am

Post by Anansi »

Like I wrote, all the other commands in the IF are executed properly when a user is disconnected - both before and after the mafia_echo . The ONLY thing that doesn't work as expected is the putidx having no effect. It doesn't even halt the script execution, it's just ignored as if the statement wasn't there. But, like I previously wrote, if I manually call the procedure putidx will work as expected.
User avatar
Sir_Fz
Revered One
Posts: 3794
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

That's why I asked, if it's acting as if the expression does not exists that means the expression is not being satisfied.
A
Anansi
Voice
Posts: 16
Joined: Mon Jul 02, 2007 8:03 am

Post by Anansi »

Either you don't understand or you aren't explaining yourself clearly. Just in
case it's the former, here it goes:

In bold are the statements the bot evaluates and executes. Imagine the player had the IDX 25 .

SITUATION 1) .tcl mafia_line 25 ""

if {$text == ""} {
mafia_killplayer [mafia_geti $me 1]
mafia_echo "common" "0,1*** $mynick has disconnected from Mafia."
mafia_killcon [mafia_geti $me 0]
}


SITUATION 2) Player 25 disconnects

if {$text == ""} {
mafia_killplayer [mafia_geti $me 1]
mafia_echo "common" "0,1*** $mynick has disconnected from Mafia."
mafia_killcon [mafia_geti $me 0]
}


Exactly what do you mean by an expression not being satisfied?
User avatar
Sir_Fz
Revered One
Posts: 3794
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

You can never know if the bolded set commands are being executed unless you add some putlog line to see if the code is actually being processed.

I was talking about the if-expression. Just add something like

Code: Select all

if {$text == ""} {
 mafia_killplayer [mafia_geti $me 1]
 putlog "The player disconnected!"
 mafia_echo "common" "0,1*** $mynick has disconnected from Mafia."
 mafia_killcon [mafia_geti $me 0]
}
Just to see if this putlog is executing when a player disconnects, if it doesn't then it means that the if-expression was not satisfied (meaning $text != "").
A
Anansi
Voice
Posts: 16
Joined: Mon Jul 02, 2007 8:03 am

Post by Anansi »

I know that they are being executed because mafia_killplayer and mafia_killcon both perform vital changes to variables and those changes are being performed. I repeat, ONLY the putidx commands in mafia_echo are ignored. I tested this exhaustively.
A
Anansi
Voice
Posts: 16
Joined: Mon Jul 02, 2007 8:03 am

Post by Anansi »

As you have suggested, I added some putlogs here and there to try and narrow down the problematic code. mafia_echo has a check that uses the valididx function to confirm that an IDX currently exists before using putdcc on it. I added a putlog there, and as soon as someone disconnected, the partyline was flooded with invalid idx messages - apparently when mafia_line is called with "" due to a disconnection, valididx considers ALL IDXs are invalid, for some reason...
User avatar
Sir_Fz
Revered One
Posts: 3794
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

That's weird; a quick solution would be delaying the send to all idxs in case of a disconnection.
A
Anansi
Voice
Posts: 16
Joined: Mon Jul 02, 2007 8:03 am

Post by Anansi »

I tried going around the valididx check in case of a disconnection but it doesn't work either - putidx returns invalid idx for all IDXs

I think I understand what you mean. So should I make a queue, add messages to that queue in case of a disconnection and have a timer calling a procedure to output messages from that queue? That might work.
User avatar
Sir_Fz
Revered One
Posts: 3794
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Post by Sir_Fz »

Anansi wrote:I think I understand what you mean. So should I make a queue, add messages to that queue in case of a disconnection and have a timer calling a procedure to output messages from that queue? That might work.
Yes, a simple [utimer] which calls the putidx for everybody after a few seconds should do the trick.
A
Anansi
Voice
Posts: 16
Joined: Mon Jul 02, 2007 8:03 am

Post by Anansi »

But it's still only a workaround for the bug. mafia_killplayer also uses mafia_echo and calls other procedures that also use mafia_echo, so such a workaround will be pretty hard to implement - I'll have to mess up my otherwise clean code by adding tests and globals all over the place. Still, thanks for the help.
h
heidel
Voice
Posts: 1
Joined: Thu Jul 12, 2007 3:14 pm

Post by heidel »

Indeed. I doubt there's a cure, unless you want to modify the eggdrop source. Btw, I appended some code that I used to reproduce this odd problem. And if you want another hack solution: use the undocumented function putdccraw. It works but might not be as safe. ;-)

Code: Select all

proc putraw {idx text} {
    append text "\n"
    putdccraw $idx [string length $text] $text
}

proc test_listen:control {idx text} {
    if {$text != ""} {
        putlog "test_listen:control($idx) RECV: $text"
    } else {
        putlog "test_listen:control($idx) CLOSED"
        putlog "Notifying all other script connections:"
        foreach {dcc} [dcclist script] {
            foreach {dccidx handle hostname type other timestamp} $dcc {
                if {$dccidx != $idx} {
                    putlog "    $dccidx ($hostname)"

                    # putdcc doesn't work: invalid idx
                    #putdcc $dccidx "Disconnected sock $idx..."

                    # hack with undocumented function putdccraw (wrapped in putraw)
                    putraw $dccidx "Disconnected sock $idx..."
                }
            }
        }
    }
}

proc test_listen:grab {newidx} {
    putlog "test_listen:grab CONNECT"
    control $newidx test_listen:control
}
listen 31234 script test_listen:grab
A
Anansi
Voice
Posts: 16
Joined: Mon Jul 02, 2007 8:03 am

Post by Anansi »

Thanks :) Sorry it took so long to reply, but I had to study for an exam these past few days, so I had no time for Tcl.
Post Reply