[Home] [Downloads] [Search] [Help/forum]

Gammon Software Solutions forum

See www.mushclient.com/spam for dealing with forum spam. Please read the MUSHclient FAQ!

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Lua
. . -> [Subject]  Pausing a script until input arrives from the MUD
Home  |  Users  |  Search  |  FAQ
Username:
Register forum user name
Password:
Forgotten password?

Pausing a script until input arrives from the MUD

[Reply to this subject]  Reply to this subject   [New subject]  Start a new subject   [Refresh] Refresh page


Pages: 1 2  

Posted by Nick Gammon   Australia  (19,435 posts)  [Biography] bio   Forum Administrator
Date Thu 02 Dec 2004 (UTC)  quote  ]

Amended on Thu 02 Dec 2004 12:11 AM (UTC) by Nick Gammon

Message
Following on from the ideas presented in:


http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=4956&page=1


- which are to do with building in pauses into the middle of a script, let's now look at waiting for text to arrive from the MUD.

Again, this isn't quite as intuitive as you might think, a single script function cannot just do something like:


Send ("prepare heal")

-- wait for something to arrive

Send ("cast heal")


This is because until the script stops executing, MUSHclient will not be processing input from the MUD.

However using a similar idea to the timers one, we can achieve that. Again, the idea is to yield execution until the wanted input arrives.

To to this, we need three things, like in the other (forum) thread:


  • A table of outstanding threads, keyed by trigger name (we can share this table with timers)

  • A routine called when the trigger fires, which resumes the thread (and deletes the trigger, as it isn't needed any more)

  • A "waitfor" script which is called when we want to pause and wait for a certain type of line. We will make two similar scripts, waitfor (for normal triggers) and waitforregexp (for regular expressions).

    The "waitforregexp" script:


    • Generates a unique trigger name to be used in our table of threads

    • Adds a trigger with this unique trigger name to match the specified line

    • Adds the trigger name and the thread address to the table of threads

    • Yields execution (pauses)





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

-- 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 for a trigger with a regexp
function waitforregexp (thread, regexp)
  id = "wait_trigger_" .. GetUniqueNumber ()
  status = AddTrigger (id, regexp, "",
            trigger_flag.Enabled + trigger_flag.RegularExpression + 
            trigger_flag.Temporary + trigger_flag.Replace, 
            custom_colour.NoChange, 
            0, "",  -- wildcard number, sound file name
            "wait_trigger_resume")
  assert (status == error_code.eOK, error_desc [status])
  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)
  return waitforregexp (thread, MakeRegularExpression (match))
end -- function waitfor 



The function wait_trigger_resume is a bit fancier than the corresponding one for timers (wait_timer_resume) because we want to know what line arrived to trigger the response.

Let's assume that we are trying to cast a spell, and the casting might succeed or fail. To do this, we need to wait for one response OR another, like this:


waitforregexp (t, "^(You heal (.+)|The spell fizzles out)$")


Now the question is, which did we receive? Either:


  • You heal Gandalf

  • The spell fizzles out


If the first one, we are done (or maybe we can do something else), if the second one, we try again.

Fortunately, we can find out. In the function wait_trigger_resume we also pass back the matching line and wildcards table (from the trigger that causes the script to resume). So, we detect those on the "waitfor" line, like this:


  line, wildcards = waitforregexp (t, 
        "^(You heal (.+)|The spell fizzles out)$")


Now we can test either the whole line, or look at individual wildcards, to see how to proceed.

Now we can put it all together. To make an particular alias (like a heal alias) we'll do this:


function my_alias_thread (t, name, line, wildcards)

  -- if no-one, set to empty string
  who = wildcards.who or ""

  repeat
    Send "prepare heal"
    line, wildcards = waitforregexp (t, 
          "^(You are ready|You lose your concentration)$")

  until line == "You are ready"

  repeat

    -- wait a second for luck
    wait (t, 1) 

    Send ("cast heal " .. who)
    line, wildcards = waitforregexp (t, 
          "^(You heal (.+)|The spell fizzles out)$")

  until string.sub (line, 1, 8) == "You heal"
  
  Note "Spell done!"

end -- function my_alias_thread 


function my_alias (name, line, wildcards)

  thread = coroutine.create (my_alias_thread)
  assert (coroutine.resume (thread, thread, name, line, wildcards))

end -- function my_alias



This alias above demonstrates three different waits in a single script:


  1. It sends "prepare heal" and then waits for either:


    • You are ready

    • You lose your concentration


    If it doesn't get "You are ready" it resends. Of course, you could build in extra tests, like only doing it 5 times, or something like that.

  2. It waits one second (see other thread for how that is done)

  3. It sends "cast heal" on the person mentioned in the alias, and then waits for either:


    • You heal (.+)

    • The spell fizzles out


    If it doesn't get "You heal" then it goes back to step 2, waits another second, and tries again.


Finally, it finishes up, displaying "Spell done!".

You can see that the alias looks neat and easy to read, compared with having to make lots of timers and triggers (although that is happening behind the scenes).

Again, we can do the whole thing in "send to script" by bracketing what we are trying to achieve with a couple of extra lines:


<aliases>
  <alias
   match="^heal(?P&lt;who&gt; .+)?$"
   enabled="y"
   echo_alias="y"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>

do local t = coroutine.create (function (t)

  repeat
    Send "prepare heal"
    line, wildcards = waitforregexp (t, 
          "^(You are ready|You lose your concentration)$")

  until line == "You are ready"

  repeat

    -- wait a second for luck
    wait (t, 1) 

    Send ("cast heal %&lt;who&gt;")
    line, wildcards = waitforregexp (t, 
          "^(You heal (.+)|The spell fizzles out)$")

  until string.sub (line, 1, 8) == "You heal"
  
  Note "Spell done!"

end) assert (coroutine.resume (t, t)) end

</send>
  </alias>
</aliases>


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (19,435 posts)  [Biography] bio   Forum Administrator
Date Reply #1 on Thu 02 Dec 2004 12:09 AM (UTC)  quote  ]
Message
To summarise, the support functions needed to make the "wait for trigger" and "wait for timer" to work are as follows. You can put them into your script file (or into the global Lua initialisation area) and then the aliases should work as described.


-- 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 = "wait_timer_" .. 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, "",
            timer_flag.Enabled + timer_flag.OneShot + 
            timer_flag.Temporary + timer_flag.Replace, 
            "wait_timer_resume")
  assert (status == error_code.eOK, error_desc [status])
  wait_table [id] = thread
  coroutine.yield ()
end -- function wait

-- we call this to wait for a trigger with a regexp
function waitforregexp (thread, regexp)
  id = "wait_trigger_" .. GetUniqueNumber ()
  status = AddTrigger (id, regexp, "",
            trigger_flag.Enabled + trigger_flag.RegularExpression + 
            trigger_flag.Temporary + trigger_flag.Replace, 
            custom_colour.NoChange, 
            0, "",  -- wildcard number, sound file name
            "wait_trigger_resume")
  assert (status == error_code.eOK, error_desc [status])
  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)
  return waitforregexp (thread, MakeRegularExpression (match))
end -- function waitfor 

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (19,435 posts)  [Biography] bio   Forum Administrator
Date Reply #2 on Thu 07 Sep 2006 01:00 AM (UTC)  quote  ]

Amended on Thu 07 Sep 2006 09:01 PM (UTC) by Nick Gammon

Message
Now that the release of MUSHclient 3.80 is becoming imminent, with Lua 5.1, I want to present a simplified version of the waiting script. First, the script, which is intended to be saved to disk as the file "wait.lua".


require "check"

module ("wait", package.seeall)

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

-- ----------------------------------------------------------
-- wait.timer_resume: called by a timer to resume a thread
-- ----------------------------------------------------------
function timer_resume (name)
  local thread = threads [name]
  if thread then
    threads [name] = nil
    assert (coroutine.resume (thread))
  end -- if
end -- function timer_resume 

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

-- ----------------------------------------------------------
-- convert x seconds to hours, minutes, seconds (for AddTimer)
-- ----------------------------------------------------------
local function convert_seconds (seconds)
  local hours = math.floor (seconds / 3600)
  seconds = seconds - (hours * 3600)
  local minutes = math.floor (seconds / 60)
  seconds = seconds - (minutes * 60)
  return hours, minutes, seconds
end -- function convert_seconds 

-- ----------------------------------------------------------
-- wait.time: we call this to wait in a script
-- ----------------------------------------------------------
function time (seconds)
  local id = "wait_timer_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")

  local hours, minutes, seconds = convert_seconds (seconds)

  check (AddTimer (id, hours, minutes, seconds, "",
                 timer_flag.Enabled + timer_flag.OneShot + 
                 timer_flag.Temporary + timer_flag.Replace, 
                 "wait.timer_resume"))

  return coroutine.yield ()
end -- function time

-- ----------------------------------------------------------
-- wait.regexp: we call this to wait for a trigger with a regexp
-- ----------------------------------------------------------
function regexp (regexp, timeout)
  local id = "wait_trigger_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")

  check (AddTriggerEx (id, regexp, 
            "-- added by wait.regexp",  
            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 script (in case we have to delete the timer)
 
  -- if timeout specified, also add a timer
  if timeout and timeout > 0 then
    local hours, minutes, seconds = convert_seconds (timeout)

    -- if timer fires, it deletes this trigger
    check (AddTimer (id, hours, minutes, seconds, 
                   "DeleteTrigger ('" .. id .. "')",
                   timer_flag.Enabled + timer_flag.OneShot + 
                   timer_flag.Temporary + timer_flag.Replace, 
                   "wait.timer_resume"))

    check (SetTimerOption (id, "send_to", "12"))  -- send to script

    -- if trigger fires, it should delete the timer we just added
    check (SetTriggerOption (id, "send", "DeleteTimer ('" .. id .. "')"))  

  end -- if having a timeout

  return coroutine.yield ()  -- return line, wildcards
end -- function regexp 

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

-- ----------------------------------------------------------
-- wait.make: makes a coroutine and resumes it
-- ----------------------------------------------------------
function make (f)
  assert (type (f) == "function", "wait.make requires a function")
  assert (not (GetInfo (106) or GetInfo (107)), "Not connected to MUD")
  assert (GetOption ("enable_timers") == 1, "Timers not enabled")
 assert (GetOption ("enable_triggers") == 1, "Triggers not enabled")
  coroutine.wrap (f) () -- make coroutine, resume it
end -- make



An important enhancement in Lua 5.1 is the ability for a coroutine to find its own coroutine address, thus simplifying the script over the previous ones, which had to pass down the coroutine address when needing to wait. For example, in the previous version:


-- wait a second for luck
    wait (t, 1) 


The new version looks like this:


-- wait a second for luck
    wait.time (1) 


A small change, but a helpful one I think. The other important consideration is the fact that it is using the new "module" function, which is in Lua 5.1.

What this effectively does is move all of the waiting functions (wait for time, wait for regexp etc.) into a single table - the "wait" table. Then the individual functions are accessed from inside that table.

Also, its own data (a table of active threads) is now private to the wait table, and cannot be accidentally changed by other scripts.

The exposed functions are:


  • wait.time (n) - wait for 'n' seconds

  • wait.match (text, n) - wait for 'text' to arrive, time out after 'n' seconds.

    If the match is made it returns the matching line. If it times out, it returns nil.

  • wait.regexp (re, n) - wait for regular expression 're' to arrive, time out after 'n' seconds

    If the match is made it returns the matching line. If it times out, it returns nil.

  • wait.make (f) - makes a coroutine thread and starts it up. The above functions must appear inside such a function (see example below).

  • wait.timer_resume - function called by a timer when the requested time is up.

    The wait.time, wait.match and wait.regexp functions automatically creates the appropriate timers.

  • wait.trigger_resume - function called by a trigger when the requested match text arrives from the MUD.

    The wait.match and wait.regexp functions automatically creates the appropriate triggers.



Here is an example of using it (which might go inside an alias):



require "wait"

wait.make (function ()  --- coroutine below here

  repeat
    Send "cast heal"
    line, wildcards = 
       wait.regexp ("^(You heal .*|You lose your concentration)$")

  until string.find (line, "heal")

  -- wait a second for luck
  wait.time (1) 

  Note ("heal done!")

end)  -- end of coroutine



An alternative way of writing it, which might look simpler perhaps, is this:




require "wait"

function cr () 

  repeat
    Send "cast heal"
    line, wildcards = 
       wait.regexp ("^(You heal .*|You lose your concentration)$")

  until string.find (line, "heal")

  -- wait a second for luck
  wait.time (1) 

  Note ("heal done!")

end -- of function cr

wait.make (cr) -- start coroutine up




This makes it clearer that there are really two steps. One is to have a function which runs as a coroutine (cr in this case), and the second step is to start the coroutine up (wait.make).

The nice thing about this is that the specialised code to handle the waiting (wait.lua) is now in its own file, where its purpose is clearly defined.

The "require" function only loads wait.lua from disk if it hasn't already been loaded, so you could put it into every alias that needed it without much overhead. A quick test on my PC shows that you can call:


require "wait"


... 2 million times in a single second, so the overhead is really minimal.

The wait.lua also has a require statement in it, which indicates that it has a dependency on check.lua, which implements the "check" function. The code for that is:


--
-- check.lua
--
-- ----------------------------------------------------------
-- return-code checker for MUSHclient functions that return error codes
-- ----------------------------------------------------------

function check (result)
  if result ~= error_code.eOK then
    error (error_desc [result] or 
           string.format ("Unknown error code: %i", result), 
           2) -- error level - whoever called this function
  end -- if
end -- function check 

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Ked   Russia  (524 posts)  [Biography] bio
Date Reply #3 on Sat 16 Sep 2006 04:31 PM (UTC)  quote  ]

Amended on Sat 16 Sep 2006 04:35 PM (UTC) by Ked

Message
I've played with wait a bit (as supplied with 3.80) and found it to be absolutely great. There's one suggestion though - maybe you could add an option to omit from output to triggers? It's very useful for parsing text that you only need to access programmatically and don't want to spam yourself with on the screen. The needed changes seem simple. Here's a modification I've made for myself:

The trigger_resume function is split into two - one for non-omitting triggers and the other for omitting ones.


-- ----------------------------------------------------------
-- wait.trigger_resume: called by a trigger to resume a thread
-- ----------------------------------------------------------
function trigger_resume1 (name, line, wildcards, styles)
  EnableTrigger (name, false)  -- don't want it to fire again
  DoAfterSpecial (1, "DeleteTrigger ('" .. name .. "')", 12) -- delete it
  local thread = threads [name]
  if thread then
    threads [name] = nil
    assert (coroutine.resume (thread, line, wildcards, styles))
  end -- if
end -- function trigger_resume 


function trigger_resume2 (name, line, wildcards, styles)
  EnableTrigger (name, false)  -- don't want it to fire again
  DoAfterSpecial (1, "DeleteTrigger ('" .. name .. "')", 12) -- delete it
  DeleteTimer(name)
  local thread = threads [name]
  if thread then
    threads [name] = nil
    assert (coroutine.resume (thread, line, wildcards, styles))
  end -- if
end -- function trigger_resume 


The new regexp function (I saved the module as "kwait" so I could keep the original version, thus "wait" appears here as "kwait")


-- ----------------------------------------------------------
-- wait.regexp: we call this to wait for a trigger with a regexp
-- ----------------------------------------------------------
function regexp (regexp, omit, timeout)
  local id = "wait_trigger_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")

  local flags = trigger_flag.Enabled + trigger_flag.RegularExpression + trigger_flag.Temporary + trigger_flag.Replace
  if omit then
    flags = flags + trigger_flag.OmitFromOutput
  end
  
  check (AddTriggerEx (id, regexp, 
            "-- added by wait.regexp",  
            flags, 
            custom_colour.NoChange, 
            0, "",  -- wildcard number, sound file name
            "kwait.trigger_resume1", 
            12, 100))  -- send to script (in case we have to delete the timer)
 
  -- if timeout specified, also add a timer
  if timeout and timeout > 0 then
    local hours, minutes, seconds = convert_seconds (timeout)

    -- if timer fires, it deletes this trigger
    check (AddTimer (id, hours, minutes, seconds, 
                   "DeleteTrigger ('" .. id .. "')",
                   timer_flag.Enabled + timer_flag.OneShot + 
                   timer_flag.Temporary + timer_flag.Replace, 
                   "kwait.timer_resume"))

    check (SetTimerOption (id, "send_to", "12"))  -- send to script

    -- if trigger fires, it should delete the timer we just added
    check (SetTriggerOption (id, "script", "kwait.trigger_resume2"))  

  end -- if having a timeout

  return coroutine.yield ()  -- return line, wildcards
end -- function regexp 



And a small edit of the match function


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


I think that covers all of the changes I had to make.
[Go to top] top

Posted by Ked   Russia  (524 posts)  [Biography] bio
Date Reply #4 on Mon 18 Sep 2006 03:46 AM (UTC)  quote  ]
Message
Hrm, I figured that the timeout argument should probably be moved into the last position, to avoid breaking existing scripts. The only inconvenience is having to pass nil in place of the timeout if you don't want to use it but need to omit from output.

And is there a reason to not allow wait.make to pass arguments to the coroutine it is wrapping? This ability comes in really handy when you can't define a "waiting" function at the same time when defining a function that calls it, and thus can't access the calling function's internals as upvalues.

Also, my love for this little module grows by the minute as I am using it. It's already allowed me to cut down the amount of code I have in some of my bulkier plugins by at least one third, and what's left makes far more sense now :D

[Go to top] top

Posted by Nick Gammon   Australia  (19,435 posts)  [Biography] bio   Forum Administrator
Date Reply #5 on Mon 18 Sep 2006 11:08 PM (UTC)  quote  ]
Message
Quote:

And is there a reason to not allow wait.make to pass arguments to the coroutine it is wrapping?


Not at all. I just didn't think of one at the time, as the functions were effectively "inlined" inside an alias.

I'm glad you find it useful - I think the general concept does simplify complication trigger-matching sequences quite a bit.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Oriol   (31 posts)  [Biography] bio
Date Reply #6 on Wed 25 Mar 2009 08:58 PM (UTC)  quote  ]
Message
Hmm I've been trying to make sense out of this thread but being the relatively newbie scripter that I am, I find it kind of complex. As I explained in another post all I need is way to pause a script, stop it when something happends, and continue it's execution right away, without having to return any arguments. I didn't recognize most of the functions used here, but what I'm trying to do could probably be done in a simpler way..
thanks..
[Go to top] top

Posted by Oriol   (31 posts)  [Biography] bio
Date Reply #7 on Wed 25 Mar 2009 09:02 PM (UTC)  quote  ]
Message
For example here's part of my script. The $ variables are mud vars
function spellbot()
require "wait"
wait.make(function()
wait.time(1)
Send("recall")
wait.time(1)
Send("way 1")
wait.time(1)
Send("cast $spell")
wait.time(1)
Send("s")
wait, cast, wait, move, wait, recast...
Wait is there so the bot detector doesn't catch me botting.
Then a trigger would be for example YOu don't have enough mana.
It'd send coratine.yield()
but thats where the error comes..
I don't knoww.
[Go to top] top

Posted by Nick Gammon   Australia  (19,435 posts)  [Biography] bio   Forum Administrator
Date Reply #8 on Wed 25 Mar 2009 11:42 PM (UTC)  quote  ]
Message
What trigger? Can you post it? See http://mushclient.com/copying

What error?

I would not be trying to mix the coroutine with a separate trigger. See the example further up:


repeat

    -- wait a second for luck
    wait (t, 1) 

    Send ("cast heal <who>")
    line, wildcards = waitforregexp (t, 
          "^(You heal (.+)|The spell fizzles out)$")

until string.sub (line, 1, 8) == "You heal"


That waits for a second, then waits for a message from the MUD.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Oriol   (31 posts)  [Biography] bio
Date Reply #9 on Thu 26 Mar 2009 07:07 AM (UTC)  quote  ]
Message
Alright here's what I have:
In the script file bot.lua I have the functions posted above
Then in the main file, along with all the other triggers:
<triggers>
<trigger
enabled="y"
match="Welcome to Alter Aeon!"
send_to="12"
sequence="100"
>
<send>EnableTimer("checksleep", false) --This is a trigger for checking your regen time
players = {}--tables for showing players on one line
spells = {}-more tables
co=coroutine.create(spellbot)
{}</send>
</trigger>
</triggers>

then another trigger that starts the coroutine which works fine until I want to stop it with the other trigger
<triggers>
<trigger
enabled="y"
match="You stand up."
omit_from_output="y"
send_to="14"
sequence="100"
>
<send>Sound(GetInfo(67).."/sounds/misc/stand.ogg")
pose = "standing"
coroutine.resume(co)</send>
</trigger>
</triggers>
This actually works until I want to stop
<triggers>
<trigger
enabled="y"
match="You don't have enough mana."
omit_from_output="y"
send_to="14"
sequence="100"
>
<send>Coroutine.yield(co)
require "wait"
wait.make(function()
wait.time(20)
Send("stand")</send>
</trigger>
</triggers>
Send("Stand") would make the mud trigger You stand up which would supposedly resume the CoRoutine.
But when it tries to stop it gives the following error
Run-time error
World: alter spell bot
Immediate execution
attempt to yield across metamethod/C-call boundary.
stack traceback:
[C]: in function 'yield'
[string "Alias: "]:1: in main chunk

I don't see what's wrong, it's probably something obvious that I can't see, but when I read the docs it gave me too much info that I didn't need, and I'm relatively new at this, so... I don't know. Maybe this could be done in a completely different way ormaybe I'm just missing soemthing..
Thanks.
[Go to top] top

Posted by Nick Gammon   Australia  (19,435 posts)  [Biography] bio   Forum Administrator
Date Reply #10 on Thu 26 Mar 2009 07:40 PM (UTC)  quote  ]
Message
You are splitting stuff up in a way that isn't likely to work. I suggest you do something like what is in the alias at the top of this thread, not put coroutine calls here and there. Inside the "wait" module is all the coroutine handling.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Viro   (1 post)  [Biography] bio
Date Reply #11 on Wed 19 Aug 2009 02:38 PM (UTC)  quote  ]
Message
Would it be possible to wait for something besides time to elapse or text from the MUD?

The problem is I have to wait for several messages from the MUD before I want to continue running my script, so I have a seperate function that parses the messages and returns a boolean value.

Here is what I would like to do

   wait.make(function ()
     repeat
       bal = balanced()
     until bal == true
     wait.time(1)
     Send("do something cool now that we're balanced")
   end)


How could I go about doing this?
[Go to top] top

Posted by Nick Gammon   Australia  (19,435 posts)  [Biography] bio   Forum Administrator
Date Reply #12 on Wed 19 Aug 2009 08:51 PM (UTC)  quote  ]
Message
Well, as in the earlier example on this page, you can wait for multiple messages, eg:


line, wildcards = waitforregexp (t, 
          "^(You heal (.+)|The spell fizzles out)$")


So each time one of the messages arrives you could check if you are completely balanced or not, if not, loop, if so, exit the loop.

However it might be simpler to not use coroutines in this case but just have triggers for each of the balance conditions. Each trigger could call a common routine that checks if there are outstanding things to wait for.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Victorious   (41 posts)  [Biography] bio
Date Reply #13 on Sun 02 Feb 2014 11:55 AM (UTC)  quote  ]
Message
Tried creating this function to return values based on input from the mud but it isn't returning anything. Anyone has any ideas what I did wrong?

function GetItemInfo(item)
Send("insult "..item.." | prefix EMID ") --sends command that results with info being sent about an item

wait.make(function()
str = wait.regexp ("EMID You insult .* [a-z0-9]+\.", 5)
startpos,endpos,keywords,uid = string.find(str, "^EMID You insult (.-) ([a-z0-9]+)\.$")
return keywords,uid --item info
end) --wait
end --func
[Go to top] top

Posted by Nick Gammon   Australia  (19,435 posts)  [Biography] bio   Forum Administrator
Date Reply #14 on Sun 02 Feb 2014 07:44 PM (UTC)  quote  ]
Message
Can you copy and paste example MUD output so we can see what it is doing?

Is it sending the "insult" command?

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


17,631 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

[Reply to this subject]  Reply to this subject   [New subject]  Start a new subject   [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

[Home]

Written by Nick Gammon - 5K

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( http://www.gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Web site powered by FutureQuest.Net]