Infinite Loop?

Posted by tobiassjosten on Fri 29 Apr 2005 08:48 AM — 25 posts, 99,079 views.

Sweden #0
I think I'm getting stuck in a infinite loop or something, as MUSHclient keeps crashing on me when I get into a certain mode.. It happens whenever I try to cure an affliction (the curePrompt-function below). All seems to be running as it should, but MC still crashes.. Tried to include some failsafes, to make the cure only being sent once. Here's the functions involved in the crash, somewhat stripped to keep the thread sane.
function curePrompt (name, line, wildcards)
    curing = 0
    attacked = 0
    if (canEat () > 0 and haha ~= 1) then
        if aff['paralysis'] ~= nil then
            haha = 1
            cureHerb ("maidenhair", "emh")
        elseif aff['clumsiness'] ~= nil then
            haha = 1
            cureHerb ("kelp", "eke")
        end
    else
        debugNote ("Cannot eat!")
    end
end

function cureHerb (name, line, wildcards)
    if line == "eww" then doHerb = "wormwood"
    elseif line == "emh" then doHerb = "maidenhair"
    end
    Send ("outr " .. doHerb)
    Send ("eat " .. doHerb)
end

function cureBLostHerb (name, line, wildcards)
    if doHerb == wildcards['herb'] then
        bHerb = 0
        --doHerb = "none"
        curing = 1
        --EnableTimer("cureBLostHerb", true)
        debugNote ("Lost herb balance")
    else
        debugNote ("Illusion catched, doHerb was " .. doHerb .. "!")
    end
end

function cureBGotHerb (name, line, wildcards)
    --EnableTimer("cureBLostHerb", false)
    if bHerb > 0 then
        debugNote ("Illusion catched, allready had herb balance!")
    else
        debugNote ("Got herb balance")
    end
end

function affAdd (name, toxin)
    if toxin ~= nil then
        if attacked ~= nil then
	    aff[name] = "yes"
	    debugNote ("Added affliction: " .. name .. " (was attacked)")
	else
	    debugNote ("Illusion catched, was not attacked")
	end
    else
        aff[name] = "yes"
        debugNote ("Added affliction: " .. name)
    end
end

function affDel (name)
    if curing ~= nil then
        aff[name] = nil
        debugNote ("Removed affliction: " .. name)
    else
        debugNote ("Illusion catched, no cured detected")
    end
end

function cureAttacked (weapon)
    -- Check for rebound and shield
    attacked = 1;
    debugNote ("Attack noted, toxins possible")
end

function canEat ()
  if bHerb == 1 then
    if doHerb == "none" then
      if aff['anorexia'] ~= 2 then
        return 1
      end
    end
  end
  return 0
end


And here's the triggers that calls those functions:

<triggers>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^H\:\d+ M\:\d+ E\:\d+ W\:\d+ \<eb\>"
   regexp="y"
   script="curePrompt"
   sequence="100"
  >
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^You quickly eat (an?|some)( piece of)? (?P&lt;herb&gt;\w+)( (root|flower|seed|leaf|stem|berry))?\.$"
   regexp="y"
   script="cureBLostHerb"
   sequence="100"
  >
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^You may eat another herb or plant\.$"
   regexp="y"
   script="cureBGotHerb"
   sequence="100"
  >
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^You concentrate on purging your body of foreign toxins\.$"
   regexp="y"
   script="cureBLostPurge"
   sequence="100"
  >
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^You have regained the ability to purge your body\.$"
   regexp="y"
   script="cureBGotPurge"
   sequence="100"
  >
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^Terror takes hold\, as a numb sensation overcomes your body\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>affAdd("paralysis", true)</send>
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^You stumble and it becomes very difficult to concentrate on your coordination\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>affAdd("clumsiness", true)</send>
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^Your paralysis fades and you can move once more\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>affDel("paralysis")</send>
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^You feel coordinated once more\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>affDel("clumsiness")</send>
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^(\w+) pricks you twice in rapid succession with (his|her) dirk\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>cureAttacked(true)</send>
  </trigger>
  <trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^With a lightning-quick motion\, (\w+) slashes you with .+\.$"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>cureAttacked(true)</send>
  </trigger>
</triggers>


What happens is that when I'm afflicted by clumsiness, for example, I send out the "outr wormwood;eat wormwood" and "(DEBUG) Cannot eat!" is outputted. I cannot see what's causing MC to crash! Someone else?
Amended on Fri 29 Apr 2005 09:02 AM by tobiassjosten
USA #1
I took a quick look over this, and I can't immediatly see the problem. I did notice a couple things though.

1. Your prompt trigger ends with <eb>, which will not always match, if for example, you don't have equilibrium.

2. In order to use your script functions through triggers you are also sending to the world sometimes by putting it through the command parsers, so I assume you have the relevant aliases. Wouldn't it be simpler just to directly call the script functions as you do in some of your other triggers.

3. I have a similar plugin, and I find it unnecessary to recheck if you can cure every time the prompt is captured. Usually simply when herb balance is regained is often enough.

4. I am also unsure as to why your eatherb function is passed both the name of the herb and the alias to eat it? It then finds the name of the herb based on the alias name rather than on the herb name which is apparently also passed to it? Isn't that a bit redundant?

I'll take another look at this later and see if I can find anything. Good luck!
USA #2
ok, one thing just occured to me. I think you have a check against this, but you might want to make sure.

As I said in my previous post, you are recheck your herbs every time you get a prompt. Since you get a prompt every line, this is somewhat spammy, computation wise. When you attempt to heal any affliction, this causes you to eat a herb, which sets off another prompt line, ad infinitum.

We'll see if that's it.
USA #3
That wouldn't cause an infinite loop, or rather, if it did, you'd notice it (since it'd be spammy with the server) and provided you get echo from the server regarding being healed or whatever, it's liable to stop anyway (you'll just spam a few times, until the server responds with something about you being healed).

NOTHING of this sort should make Mushclient crash. Have you done any tweaking of the Lua DLLs? Try reinstalling (overtop of your old installation should be fine) to get new fresh files. Anything wrong with the script (especially stuff like this) should just make an error that gets caught by mushclient, definately not crash.

On another note, you said that you get your outr/eat message AND your debug note? At the same time (well, the same time through the subroutine)? Since that shouldn't happen (since there's an if in between them, it should do one, or the other).
Sweden #4
Tsunami,
1) It's just to test these routines, and to learn LUA. It'll change when can get this to work and move on to the next step. For now, I sit down when I get afflicted (a "p" is added to the prompt) and stand up when I want to cure it. ;)
2) I do that to avoid having triggers named "affClumsiness1", "affClumsiness2" etc. And to pass onto the trigger the toxin-parameter (to indicate this message came from a toxin, and therefor an attack-message must have been seen)
3) That'll change, just found it easiest to do it this way for testing. However, you might want to use a prompt-based tracker, to add better support for Lifevision (detects some illusions)
4) Good point, I'll probably change that. Did it this way to not only call the eatHerb function from other functions, but also aliases (like: eww, emh). But it could be worked around, for prettier code. ;)
5) Yeah, I got bHerb for the actualy balance and doHerb for when the system has sent out an eat command. If either bHerb is nil or doHerb isn't nil, I don't send out new commands.

Flannel,
No tweaking, no nothing. The only thing that differs from other plugins I've seen so far is that I keep my LUA-code in a seperate file which is requested.
And yes, just before MC crashed I see that it tries to send the "outr herb", "eat herb". Then I get the prompt, and "(DEBUG) Cannot eat!" is printed. That's all I have time to see before Windows shuts it down..

I'll heed you advice and try to reinstall it. But don't hesitate to gimme pointers on this code if you feel a need. ;) It's driving me insane, as I really think it should work, but for some wierd reason it doesn't.

Edit: Reinstalled it, but it didn't work. I checked out the error message I got from Windows this time though, and it complained about the LUA DLL. The whole error message wasn't saved, but I could recreate it in case you want to check it out Nick?
Amended on Fri 29 Apr 2005 10:58 PM by tobiassjosten
Australia Forum Administrator #5
Ok, I haven't tried to reproduce the crash, which I suspect is some sort of stack overflow in the script engine, however I am puzzled by this:


           cureHerb ("maidenhair", "emh")
...
            cureHerb ("kelp", "eke")




function cureHerb (name, line, wildcards)
    if line == "eww" then doHerb = "wormwood"
    elseif line == "emh" then doHerb = "maidenhair"
    end


Now you only call cureHerb in 2 places, with 2 arguments, however you are accepting 3 arguments. Not that this is a huge problem, but it seems to indicate you aren't totally sure what you are passing from one function to another.

The second argument is either "emh" or "eke" but you are testing it to be "emh" or "eww", so it will fail both those tests.




The Lua script engine is supposed to be fairly lightweight and fast, so it possibly does not test for stack overflow (probably a difficult thing to to in a platform-independent way), and thus if the stack does overflow, you may well get a crash, and since the crash is in a DLL called by MUSHclient, then that will crash MUSHclient too.
Sweden #6
It's just a somewhat stripped down version. In the full code I also have two aliases which is used for manual curing.
<aliases>
  <alias
   script="cureHerb"
   match="^eww$"
   enabled="y"
   group="cxCuringAliases"
   regexp="y"
   keep_evaluating="y"
   sequence="100"
  >
  </alias>
  <alias
   script="cureHerb"
   match="^emh$"
   enabled="y"
   group="cxCuringAliases"
   regexp="y"
   keep_evaluating="y"
   sequence="100"
  >
  </alias>
</aliases>

And the cureHerb function is bigger.
function cureHerb (name, line, wildcards)
    if line == "eww" then doHerb = "wormwood"
    elseif line == "emh" then doHerb = "maidenhair"
    elseif line == "ens" then doHerb = "nightshade"
    elseif line == "eke" then doHerb = "kelp"
    elseif line == "ega" then doHerb = "galingale"
    elseif line == "ema" then doHerb = "mandrake"
    elseif line == "eor" then doHerb = "orphine"
    elseif line == "ehs" then doHerb = "hyssop"
    elseif line == "eju" then doHerb = "juniper"
    end
    Send ("outr " .. doHerb)
    Send ("eat " .. doHerb)
end

The way it works is that when the system tries to cure something, it sends cureHerb() with appropriate parameters. cureHerb then sets doHerb to whatever it's eating and sends out the "outr herb" and "eat herb". When the prompt comes up again (comes right after the OUTR command is successful), it checks if it can eat by testing bHerb==1 and doHerb=="none". Since it cant eat when it just fired (doHerb is set to some herb), it prints "Cannot eat!", and then it should just end. But.. It crashes.
Australia Forum Administrator #7
OK, next comment. You can neaten up the cureHerb function by using a table, like this:


herbtable =   {
  eww = "wormwood",
  emh = "maidenhair",
  ens = "nightshade",
  eke = "kelp",
  ega = "galingale",
  ema = "mandrake",
  eor = "orphine",
  ehs = "hyssop",
  eju = "juniper",
  }

function cureHerb (name, line, wildcards)
    doHerb = herbtable [line]
    if doHerb then
      Send ("outr " .. doHerb)
      Send ("eat " .. doHerb)
    end -- if
end


This saves having a heap of ifs and, lets you store the herbs in a variable if you like (see my article about serializing Lua data into MUSHclient variables).




Now, you seem to be making a common mistake C programmers make when using Lua. Try this bit of code:


if 0 then print "hi" end


Possibly to your surprise, "hi" will be printed.

The fact is that only the boolean value "false" and the value "nil" are considered false. Everything else, including the number 0, are true.

Thus this code will always return a true result:


function canEat ()
  if bHerb == 1 then
    if doHerb == "none" then
      if aff['anorexia'] ~= 2 then
        return 1
      end
    end
  end
  return 0
end


I notice you are not actually testing for true or false, so you have used it correctly, but it looks clumsy.

If you change it to:


function canEat ()
  if bHerb == 1 then
    if doHerb == "none" then
      if aff['anorexia'] ~= 2 then
        return true
      end
    end
  end
  return false
end


Then you can simply say "if canEat () then ...".

Similarly you can replace:


if aff['paralysis'] ~= nil then


by ...


if aff['paralysis'] then


I still can't reproduce the crash however. Can you show us what debugNote does?

Australia Forum Administrator #8
Try turning off "keep evaluating" for the cure prompt trigger. The fact that it displays "cannot eat" suggests that maybe that script actually finished, and another one is causing the problem.
Sweden #9
Removed the keep_evaluating, same results.. Even removed everything but these triggers/scripts, uninstalled and reinstalled the client but it still crashes on me.
USA #10
Post ALL (well, all of the ones in the stripped down version you just made that still crashed) the triggers exactly as they appear (and the script file) in your world file and the output from the server that causes the crash (and current state of variables). Since that'll be the only way we track this down.

This is probably going to be too much for one post, so you might want to zip and post a URL (if you have somewhere to host), if not just post it in pieces.
Sweden #11
Thanks alot for trying to help me out, thought you'd given up by now. ;)
Here's the URL: http://www.crox.org/mc_sys.rar
Contains -everything- I use for MUSHclient (3 files). Got some other ideas on how I could work around this, but first I want to know what's causing this..
Australia Forum Administrator #12
Can you please re-post using a more widely-used compression method, such as winzip? Thanks.

Or, don't compress, I presume the files aren't megabytes long.
Amended on Mon 02 May 2005 06:37 AM by Nick Gammon
Australia Forum Administrator #13
Also please post, if it isn't in those files, the exact text that comes from the MUD that causes the problem.
Sweden #14
RAR -> http://www.crox.org/mc_sys.rar
ZIP -> http://www.crox.org/mc_sys.zip

Added a log of what happens when the system crashes. Also, I suspect that the crash isn't caused by the curePrompt function, as I tried to cure i manual (when sitting, and therefor keeping the curePromt from firing) and it still crashes. Perhaps the error is in the addDel function?

It's all in those archives, but I'll paste it here for faster access:
<trigger
   enabled="y"
   group="cxCuring"
   keep_evaluating="y"
   match="^You feel coordinated once more\.$"
   regexp="y"
   send_to="12"
   sequence="100"
>

function affDel (name)
    if curing ~= nil then
        aff[name] = nil
        debugNote ("Removed affliction: " .. name)
    else
        debugNote ("Illusion catched, no cured detected")
    end
end
USA #15
From looking at the log, I see that you get the message:

[DEBUG] Cannot eat!

actually before the log indicates you have eaten the kelp, although you have sent the command. Any significance to that?
Sweden #16
That's the curePrompt function (called from the prompt) telling me that canEat returns 0. It does that because doHerb is set to kelp. The more think about it, the more I think there's something wrong with the eating-message, which calls cureBLostHerb. That function works on it's own, and I can't really see what could be wrong with it. But I'm pretty certain MC crashes around when it's called.
Russia #17
It looks very much like you are right and the crash occurs somewhere in the cureBLostHerb() function. Try using a Note to see how far into the function you can go before the crash occurs. First put the Note on the first line inside the function, if it shows and then crashes - move it to after the if check, and so on, until you pin down the exact line where it crashes.
Sweden #18
Put a Note everywhere in the cureBLostHerb function, but none was echoed out. So I removed the trigger which called it, and now it's running! Now I just have to find what was wrong with that function (or trigger?), because I really need it/them.. Any ideas on this?
Russia #19
You have to love debugging. To figure out what causes the crash you need to take it step by step. First add the trigger back, but make it echo something instead of calling a script. That'll tell you whether the trigger is responsible in some weird way. Then make it call a function, but make that just echo something. Then start bringing the function closer to what it originally was - echo the current state of variables you use in the cureBLostHerb(), then try performing some actions that it used to perform. In short - just keep adding stuff one piece at a time, until you either figure out what crashes, or get it working right.
Australia Forum Administrator #20
I can't reproduce the problem, but what I might suggest is that you change debugNote to AppendToNotepad rather than Note. It is possible that doing a Note is somehow making it crash, although it is hard to say why.
Sweden #21
Now I've been testing this out alot, and come to the conclusion that it -have to- be the following trigger that gets stuck in some loop or something.
<triggers>
  <trigger
   enabled="y"
   group="cxDefense"
   keep_evaluating="y"
   match="^You quickly eat (an?|some)( piece of)? (?P&lt;herb&gt;\w+)( (root|flower|seed|leaf|stem|berry))?\.$"
   regexp="y"
   script="cureBLostHerb"
   sequence="40"
  >
  </trigger>
</triggers>

It's only crashing MC when I eat kelp. So far I've been solving this by eating something else when I login. After that, it seems as if can accept kelp again. These are the messages:
You quickly eat a maidenhair leaf.
You quickly eat a piece of kelp.


Edit: The "cureBLostHerb"-function is never called when it crashes. Tried to put a Note and even an AppendToNotepad in the very beginning of it. Nothing..
Amended on Thu 12 May 2005 05:58 PM by tobiassjosten
Russia #22
Yep, that's it. My guess is that the problem is with how the wildcard table is formed when calling a script. Namely with some wildcards optionally missing, and more exactly - the last wildcard. I am just randomly pointing a finger here though. But changing the match expression to avoid having any missing wildcards seems to solve the problem:


<triggers>
  <trigger
   enabled="y"
   group="cxDefense"
   keep_evaluating="y"
   match="^You quickly eat (a|an|some)( piece of|) (?P&lt;herb&gt;\w+)( root| flower| seed| leaf| stem| berry|)\.$"
   regexp="y"
   script="cureBLostHerb"
   sequence="40"
  >
  </trigger>
</triggers>
#23
I have also found it helpful to use (?:blah) in my patterns to avoid putting in wildcards that I really don't care about or need. That would be another possible solution to this particular problem. Save only the herb name in your list of wildcards and ignore everything else.
Sweden #24
Thanks. I've been looking for that pattern to exclude.