Page fault problem..

Posted by Shadowfyr on Thu 07 Feb 2002 11:48 PM — 11 posts, 28,160 views.

USA #0
I am having a problem here. The scripts I include here are
supposed to 1) create a trigger 'grabit' that grabs
everything that follows and sends it to the script Mixcount,
then 2) send an 'inventory' command. 3) The Mixcount reads
line input and sorts it into herb types, storing the number
of herbs found until it reads 'Your total load is', then 4)
terminates the trigger and 5) calls the CalcPotions script.
The first time through it works perfectly, however the
second time I use the command to trigger these scripts it
does everything correctly up until the return from the
CalcPotions script, then it page faults and crashes
Mushclient.

The problem did not happen when I was testing 'only'
the Mixcount without the call. However, it still happens
if I copy the code from CalcPotions into Mixcount, instead
of using a call to the sub. I can't figure out what could
be causing it. :p

The following is a sample of the output from the 'inventory' command:


You have the following items in your inventory:
-----------------------------------------------
[keep] A "legendary defender of loriah" badge [worn]
       A bundle of 81 hops herbs
       A bundle of 17 eyebright herbs
       A bundle of 2 tansy herbs
       A bundle of 8 nightshade herbs
       A bundle of 8 gentian herbs
[keep] A druid belt
       A bundle of 46 echinacea herbs
       A single masterwort herb
       A bundle of 4 hazel herbs
       A bundle of 6 linden herbs
       A bundle of 14 narcissus herbs
[keep] A holy circle
       A bundle of 11 clubmoss herbs
       A bundle of 6 peony herbs
       A bundle of 19 cinchona herbs
       A bundle of 4 dragonblood herbs
       A single carnation herb
[keep] Kagehi's slave title
       A bundle of 28 feverfew herbs
       A single eucalyptus herb
       A bundle of 4 goldenseal herbs
       A bundle of 2 rue herbs
       A bundle of 38 agrimony herbs
       A bundle of 63 lotus herbs
       A single mullein herb
       A bundle of 20 borage herbs
       A bundle of 4 hemlock herbs
-----------------------------------------------
You are carrying 1000 coins.
Your total load is: 40.4 kg, out of 72.2 kg.



My Alias>
  match on: potions
  Label: PotionCount
  Script: Mixit

The Sctipts>

sub Mixit (Aliasname, Output, Wildcards)
  dim sTarget
  int count
  sTarget = Trim (Wildcards (1))
  for count = 0 to 23
    HerbCount(count) = 0
  next
  world.addtrigger "Grabit","*","",1,-1,0,"","Mixcount"
  world.send "inventory"
end sub

sub Mixcount (Trigname, Output, Wildcards)
  dim sTest
  dim sLoad
  dim sBundle
  dim sSingle
  dim sValue
  int i
  int d
  int count
  int found
  int fat
  int Best

  dim Herbs
  dim PotionCount(5)
  Herbs = array ("goldenseal","rue","carnation","eucalyptus","masterwort","p
eony","nightshade","hazel","narcissus","linden","tansy","gen
tian","dragonblood","clubmoss","mullein","cinchona","hemlock
","lotus","hops","echinacea","eyebright","borage","agrimony"
,"feverfew")

  'Note the above should not have line breaks, but won't
  'wrap correctly on this forum.

  found = 0
  sLoad = "Your total load is"
  sBundle = "A bundle of"
  sSingle = "A single"
  sTest = Wildcards(1)
  'world.note "Testing> " & sTest
  i = InStr(1, sTest, sLoad, 1)
  if i > 0 then
    d = world.DeleteTrigger ("Grabit")
    call CalcPotions
  end if
  i = 0
  i = InStrRev(sTest, sBundle, -1, 1)
  if i > 0 then
    found = 1
    fat = i
    'world.note "Found! " & i    
  end if
  i = 0
  i = InStrRev(sTest, sSingle, -1, 1)
  if i > 0 then
    fat = i
    found = 2
    'world.note "Found2! " & i
  end if
  if found then
    for count = 0 to 23
      i = InStr(1, sTest, Herbs(count), 1)
      if i > 0 and found = 1 then
        sValue = mid(sTest, fat +11, i-(fat+11))
        HerbCount(count) = Cint(sValue)
        'world.note "" & i & " " & sValue &; " index " & count
      end if
      if i > 0 and found = 2 then
        HerbCount(count) = 1
        'world.note "" & i & " index " & count
      end if
    next
  end if
end sub

sub CalcPotions
  dim PotionCount(5)
' Find best potion mixes.
  Best = 0 
  if HerbCount(4) > HerbCount (7) then
    Best = HerbCount (4)
  else
    Best = HerbCount (7)
  end if
  if HerbCount(20) < Best then
    Best = HerbCount(20)
  end if
  if HerbCount(21) < Best then
    Best = HerbCount(21)
  end if
  if HerbCount(23) < Best then
    Best = HerbCount(23)
  end if
  if Cint(HerbCount(17)/5) < Best then
    Best = Cint(HebCount(17)/5)
  end if

  'Figure out Individual potions.
  if HerbCount(17) < HerbCount(4) then
    PotionCount(0) = HerbCount(17)
  else
    PotionCount(0) = HerbCount(4)
  end if
  if HerbCount(17) < HerbCount(7) then
    PotionCount(3) = HerbCount(17)
  else
    PotionCount(3) = HerbCount(7)
  end if
  if HerbCount(17) < HerbCount(20) then
    PotionCount(4) = HerbCount(17)
  else
    PotionCount(4) = HerbCount(20)
  end if
  if HerbCount(17) < HerbCount(21) then
    PotionCount(2) = HerbCount(17)
  else
    PotionCount(2) = HerbCount(21)
  end if
  if HerbCount(17) < HerbCount(23) then
    PotionCount(1) = HerbCount(17)
  else
    PotionCount(1) = HerbCount(23)
  end if

  ' Display it.
  world.note " "
  world.note "You can make:"
  world.note " "
  if HerbCount(0) < HerbCount(5) then
    world.note "Focus Potion - " & HerbCount(0)
  else
    world.note "Focus Potion - " & HerbCount(5)
  end if
  world.note "Minor Str - " & PotionCount(0) & " " & Best
  world.note "Minor Dex - " & PotionCount(1) & " " & Best
  world.note "Minor Con - " & PotionCount(2) & " " & Best
  world.note "Minor Wis - " & PotionCount(3) & " " & Best
  world.note "Minor Int - " & PotionCount(4) & " " & Best
end sub
Amended on Thu 07 Feb 2002 11:53 PM by Shadowfyr
Australia Forum Administrator #1
You have a spelling error in the middle:


Best = Cint(HebCount(17)/5)


I can't see where "HerbCount" is defined. If that doesn't help, try commenting out parts of the function until you get a version that works, and then gradually add it back until you see what part causes the crash.

Amended on Fri 08 Feb 2002 08:49 PM by Nick Gammon
USA #2
Oops... Ok. HerbCount is declared globally outside the other script as:

dim HerbCount(24)

Since it has to continue to exist from one execution to the next. I will check that spelling error, though I don't see why that wouldn't return a out of range error, but then this is VBScript not VB. :p

If all else fails I can try to comment stuff out, but other than the world.note lines, everything is fairly standard stuff. And a previous problem that crashed it every time was trying to use "" instead of " " to output a blank line. Even the herb name array declaration is by the book...

The spelling error may be the actual problem, since it would be retrieving a value from a non-existant array. Though as I said... why this would generate a page fault instead of an error I don't get. :p
USA #3
Ok I made the following changes to the Mixcount sub, to make testing easier and to try commenting stuff out, line with * are notes not in the actual script:


sub Mixcount (Trigname, Output, Wildcards)
  dim sTest
  dim sLoad
  dim sBundle
  dim sSingle
  dim sValue
  int i
  int d
  int count
  int found
  int fat
  int Best


  found = 0
  sLoad = "Your total load is"
  sBundle = "A bundle of"
  sSingle = "A single"
  sTest = Wildcards(1)
  'world.note "Testing> " & sTest
  i = InStr(1, sTest, sLoad, 1)
  if i > 0 then
    d = world.DeleteTrigger ("Grabit")
     '*Commented out when shifting code to here.
'    call CalcPotions

    ' Find best potion mixes.
    '*(Commented out to test if causing the crash, but
    '*had no effect on the result.)
'    Best = 0 
'    if HerbCount(4) > HerbCount (7) then
'      Best = HerbCount (4)
'    else
'      Best = HerbCount (7)
'    end if
'    if HerbCount(20) < Best then
'      Best = HerbCount(20)
'    end if
'    if HerbCount(21) < Best then
'      Best = HerbCount(21)
'    end if
'    if HerbCount(23) < Best then
'      Best = HerbCount(23)
'    end if
'    if Cint(HerbCount(17)/5) < Best then
'      Best = Cint(HerbCount(17)/5)
'    end if

    'Figure out Individual potions.
    '*(Commented out to test if causing the crash, but
    '*did not effect result.) :p
'    if HerbCount(17) < HerbCount(4) then
'      PotionCount(0) = HerbCount(17)
'    else
'      PotionCount(0) = HerbCount(4)
'    end if
'    if HerbCount(17) < HerbCount(7) then
'      PotionCount(3) = HerbCount(17)
'    else
'      PotionCount(3) = HerbCount(7)
'    end if
'    if HerbCount(17) < HerbCount(20) then
'      PotionCount(4) = HerbCount(17)
'    else
'      PotionCount(4) = HerbCount(20)
'    end if
'    if HerbCount(17) < HerbCount(21) then
'      PotionCount(2) = HerbCount(17)
'    else
'      PotionCount(2) = HerbCount(21)
'    end if
'    if HerbCount(17) < HerbCount(23) then
'      PotionCount(1) = HerbCount(17)
'    else
'      PotionCount(1) = HerbCount(23)
'    end if

    ' Display it.
    ' *(Left uncommented because it does not
    ' *appear to happen if they are commented out.)
    world.note " "
    world.note "You can make:"
    world.note " "
'    if HerbCount(0) < HerbCount(5) then
'      world.note "Focus Potion - " & HerbCount(0)
'    else
'      world.note "Focus Potion - " & HerbCount(5)
'    end if
'    world.note "Minor Str - " & PotionCount(0) & " " & Best
'    world.note "Minor Dex - " & PotionCount(1) & " " & Best
'    world.note "Minor Con - " & PotionCount(2) & " " & Best
'    world.note "Minor Wis - " & PotionCount(3) & " " & Best
'    world.note "Minor Int - " & PotionCount(4) & " " & Best
  else ' *Added since if we are stopping, there is no need
       ' *to continue processing (possible error source, but
       ' *doesn't fix it.)
    i = 0
    i = InStrRev(sTest, sBundle, -1, 1)
    if i > 0 then
      found = 1
      fat = i
      'world.note "Found! " & i    
    end if
    i = 0
    i = InStrRev(sTest, sSingle, -1, 1)
    if i > 0 then
      fat = i
      found = 2
      'world.note "Found2! " & i
    end if
    if found then
      for count = 0 to 23
        i = InStr(1, sTest, Herbs(count), 1)
        if i > 0 and found = 1 then
          sValue = mid(sTest, fat +11, i-(fat+11))
          HerbCount(count) = Cint(sValue)
          'world.note "" & i & " " & sValue & " index " & count
        end if
        if i > 0 and found = 2 then
          HerbCount(count) = 1
          'world.note "" & i & " index " & count
        end if
      next
    end if
  end if
end sub


I also made the following global, (outside of subs and before all other scripts), in case it was a redeclaration problem.

dim HerbCount(24)
dim Herbs
dim PotionCount(5)
Herbs = array ("goldenseal","rue","carnation","eucalyptus","masterwort","peony","nightshade","hazel","narcissus","linden","tansy","gentian","dragonblood","clubmoss","mullein","cinchona","hemlock","lotus","hops","echinacea","eyebright","borage","agrimony","feverfew")

It still runs ok the first pass, but dies after the second attempt to use the command. This makes no sense, the only commands I am using are world.note commands and a world.deletetrigger. :p Kind of wonder if this is somehow tied into the script problem I had before with calling world.connect from the World Disconnect event script. Unfortunately I can't figure any way to get the results to display automatically without calling them from inside the script that reads the data. At least not without doing something really cheesy. :p Help!!!
Amended on Sat 09 Feb 2002 10:45 AM by Nick Gammon
USA #4
Hmm. Think this has officially become a bug report... Seems that calling the sub to display the results 'prior' to calling world.deletetrigger fixed the glitch. This was in version 3.16 by the way, so not sure if the behaviour is the same in 3.17. The problem is not with any of the pure vbscript code, but rather with using world.note after deleting a trigger. Not sure, but... This may also only happen if you use world.note more than one time following the deletion, since a note I had placed in there to verify that the command executed did not trigger a crash, but as my previous post indicates, 2 or more world.note commands in a row does.

Simple to get around in this case, but until you figure out why it happens... this should be a heads up to anyone trying this sort of thing. ;)
Australia Forum Administrator #5
I haven't followed the code in detail, but I think there is possibly a problem if a trigger deletes itself, directly or indirectly, or if perhaps a queued trigger deletes another one.

Potentially a trigger deleting itself will result in the (script) code for the trigger being deallocated, while the script is still executing. I would avoid that if I could. Perhaps disabling the trigger might be safer.
USA #6
You may be right.. As I said, if the deletion is the last (or nearly the last) command there is not a problem. It doesn't seem to have any effect if you do anything with pure vbscript code, but when you use world.note it blows up. Could be that whatever internal process executes Mushclient commands such as world.note, etc. some how causes this. The thing I find really odd is that is 'only' happens with world.note (and maybe other commands) that 'follow' the deletion of the trigger that called the script and that it only happens when you try to do it twice. The first time always worked without a problem. In any case deleting the trigger as the last action seems to be safe, just not the other way round. ;) It is a very strange behaviour. As for disabling the trigger instead... I could do that, but I have so many of the dang things that it is easier to keep track of them if the temporary ones are just that - temporary. lol
Australia Forum Administrator #7
Quote:

In any case deleting the trigger as the last action seems to be safe, just not the other way round. ;) It is a very strange behaviour.


Well, not really. In C++, for instance, it is possible to create an object on the heap (using 'new') and then when the object knows it is finished it deletes itself, like this:


delete this;


In this case the word 'this' is a reference to the object itself. It is a well-documented fact that once an object is deleted it no longer exists, and thus attempting to execute code after 'delete this' would be likely to fail. Much the same would apply to a trigger that deletes itself, and then attempts to communicate with MUSHclient by doing things like world.note.
USA #8
Yeah I get that... The odd behaviour I was talking about is having it happen the second time and not the first. From a logical standpoint one of two things must be true. A) The script executes as a new object every time it is called, or B) The first time called it creates a master copy and a clone and uses the clone to execute the commands.

In case A there is no object that should still exist from the first attempt to terminate the trigger prior to using other commands, so there is no reason for it it fault the next time you delete that trigger (even the trigger is not technically the same one).

In case B you use a new cloned copy of a clean object every time, so it should not inherit any associated memory or other things that might cause a fault.

The only possiblity I can see, and only now occures to me, is that the trigger is never actually deleted. However, this still does not explain the result.

If it failed 'every' time the delete command was used and followed by world.notes then that would be one thing, but it makes no sense to me that it would do it only if it is the second time you have deleted the trigger. :p I just can't figure out how that could happen short of trying to reuse an object that contains an invalid pointer. And that seems unlikely unless Mushclient keeps a copy of the trigger someplace even after deletion and tries to reuse it, which shouldn't happen anyway, since an object is not reused after deletion, but created fresh from the master copy. I just can't wrap my mind around whatever it is doing that causes a failure only on the second attempt. :p
Australia Forum Administrator #9
Quote:

I just can't wrap my mind around whatever it is doing that causes a failure only on the second attempt. :p


OK - I see what you mean. I have done a test, using a simple couple of triggers. One (A) matches on something, and creates another (B) with world.addtrigger.

B then does a world.note, deletes itself, and then does a couple more world.notes.

I think this reproduces what you are talking about here.

Now I found that the first time I tested it, MUSHclient crashed on the first test (trigger invocation), but when I tried again it crashed on the third test.

I think I can explain that. If you delete memory and then re-use it, the results are unpredicatable, not well-defined. Thus, the time it takes the program to crash may vary, depending on various factors.

For instance, if the piece of memory that was returned to the memory pool after the trigger was deleted is immediately reused, then you might expect an immediate crash. If not, then you might get a delay.

You may well have structured your test so it always crashes on the second time around, however I would guess that if you vary the environment (eg. do some other mud commands) then you might change the time it takes to crash.

I would suggest doing one of the following to fix the problem ...

  1. Don't delete the trigger at all, as I suggested before, but disable it.
  2. If you must delete it, delete it last thing in the script.
  3. Better still, queue up the deletion, so that the trigger is deleted after it has finished executing. For instance, add a (one-shot) timer which fires after 1 second, that timer calls a script, and the script deletes the trigger (make the trigger name and the timer name the same to simplify this process).


Something like this:


sub MyTrigger (Trigname, Output, Wildcards)
' ... whatever the trigger does ...
world.enabletrigger Trigname, FALSE  ' disable it first
World.addtimer Trigname, 0, 0, 1, "", 5, "DeleteTriggerTimer"
end sub

sub DeleteTriggerTimer (strTimerName)
  world.deletetrigger strTimerName
end sub



In this example the trigger adds a one-shot timer that fires after 1 second. Being one-shot the timer will be deleted after it fires. The timer is given the same name as the trigger. The timer deletes the trigger of the corresponding name. We disable the trigger first so it doesn't fire in the one second before it is deleted.

USA #10
I already changed the order so the processing takes place first, then the deletion. I just found it a bit odd the way it was happening. And you are right, since all my testing was on that script I never called one that used world.note or similar commands. Besides this script, the only one I have that does are used to warn me of the wrong command, accidentaly trying to kill my own slave (bugger killed me too. lol), and status changes for a few things. None of those commands are ones I use while foraging, so the issue of if it would crash the first, second, third or x time with different conditions never really came up. ;)

Suppose this falls under the infamous memory leak catagory. Stuff that MS Windows is bad at catching, horrible at dealing with and likes to warn you about only after it is to late to do anything about it. ;) At least it only crashes Mushclient, if using telnet had required linking Mush to IE in some fashion it probably would have destabalized IE and Explorer, as well as taking the desktop, task manager and everything else along with it. :p Always fun when that happens.