Waiting for text to arrive from the MUD, with a timeout

Posted by Nick Gammon on Sat 15 Oct 2005 06:13 AM — 1 posts, 7,195 views.

Australia Forum Administrator #0
After reading the posts about speedwalks which wait for you to move from room to room, I thought it might be handy to have a way of building into a script the ability to wait for a line to arrive (eg. Exits: north west) OR a time limit to elapse (eg. 10 seconds).

That way, a script could terminate itself if the desired line did not arrive in a reasonable time.

To do this, the relevant part of the exampscript.lua file has been modified, as below. Lines which have changed are in bold.

Now you can supply an optional timeout argument to the calls to waitforregexp or waitfor. This is the number of seconds to wait.

The functions do this by adding a trigger and a timer. If the trigger fires, it has "send to script" set to delete the corresponding timer. If the timer fires, it deletes the trigger.

In both cases, the thread is resumed. However the return from the resume will indicate whether it timed out or not.



-- ----------------------------------------------------------
-- table of outstanding threads that are waiting
-- ----------------------------------------------------------
wait_table = {}

-- ----------------------------------------------------------
-- called by a timer to resume a thread
-- ----------------------------------------------------------
function wait_timer_resume (name)
  thread = wait_table [name]
  if thread then
    assert (coroutine.resume (thread))
  end -- if
end -- function wait_timer_resume 

-- ----------------------------------------------------------
-- called by a trigger to resume a thread
-- ----------------------------------------------------------
function wait_trigger_resume (name, line, wildcards)
  EnableTrigger (name, false)  -- don't want it to fire again
  DoAfterSpecial (1, "DeleteTrigger ('" .. name .. "')", 12) -- delete it
  thread = wait_table [name]
  if thread then
    assert (coroutine.resume (thread, line, wildcards))
  end -- if
end -- function wait_trigger_resume 


-- ----------------------------------------------------------
-- we call this to wait in a script
-- ----------------------------------------------------------
function wait (thread, seconds, id)
  id = id or "wait_" .. GetUniqueNumber ()
  hours = math.floor (seconds / 3600)
  seconds = seconds - (hours * 3600)
  minutes = math.floor (seconds / 60)
  seconds = seconds - (minutes * 60)
  status = AddTimer (id, hours, minutes, seconds, 
            "DeleteTrigger ('" .. id .. "')",
            timer_flag.Enabled + timer_flag.OneShot + 
            timer_flag.Temporary + timer_flag.Replace, 
            "wait_timer_resume")
  assert (status == error_code.eOK, error_desc [status])
  SetTimerOption (id, "send_to", "12")  -- send to script
  wait_table [id] = thread
  return coroutine.yield ()
end -- function wait

-- ----------------------------------------------------------
-- we call this to wait for a trigger with a regexp
-- ----------------------------------------------------------
function waitforregexp (thread, regexp, timeout)
  local id = "wait_" .. GetUniqueNumber ()
  status = AddTriggerEx (id, regexp, 
            "DeleteTimer ('" .. id .. "')",  -- script to delete the timeout timer
            trigger_flag.Enabled + trigger_flag.RegularExpression + 
            trigger_flag.Temporary + trigger_flag.Replace, 
            custom_colour.NoChange, 
            0, "",  -- wildcard number, sound file name
            "wait_trigger_resume", 
            12, 100)  -- send to scripting
  assert (status == error_code.eOK, error_desc [status])
  if timeout and timeout > 0 then
    return wait (thread, timeout, id)
  end -- if having a timeout
  wait_table [id] = thread
  return coroutine.yield ()  -- return line, wildcards
end -- function waitforregexp 

-- ----------------------------------------------------------
-- we call this to wait for a trigger (not a regexp)
-- ----------------------------------------------------------
function waitfor (thread, match, timeout)
  return waitforregexp (thread, MakeRegularExpression (match), timeout)
end -- function waitfor 




An example of using it is in this alias:


<aliases>
  <alias
   match="test"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>  
do local t = coroutine.create (function (t)

    print ("about to wait for 'east' or 5 seconds")

    line, wildcards = waitforregexp (t, "east", 5) 

    if line then
      print ("line = " .. line)
    else
      print ("timeout!")
    end -- if

end) assert (coroutine.resume (t, t)) end
</send>
  </alias>
</aliases>



This simple example waits for the word "east" to match the regular expression, or 5 seconds. You can tell from the return codes, whether it timed out or not. If the returned line is nil, then the timer fired, and therefore it timed out. If it is not nil, then the matching line is returned.