Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ MUSHclient ➜ Perlscript ➜ non-blocking wait

non-blocking wait

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by KP   (24 posts)  Bio
Date Wed 13 Sep 2006 12:49 AM (UTC)

Amended on Wed 13 Sep 2006 12:50 AM (UTC) by KP

Message
first of all: Nick, you are THE MAN(tm)! thanks for this really great client! i was looking for quite some time for a good one that would support scripting (especially perl) and stumbled upon MC a few days ago. the more i looked into it, the more i loved your implementation of the whole scripting engine. truly amazing solution!

well, now to my problem. i'd like to react on a variable change at the time it happens. simple example: i have to cast a few spells after each other but have to wait until i regain equilibrium after each cast. so i have to loop until a trigger sets the according variable back to '1'. yes, i could go for hardcoded timers, but that would be a pain. the problem is that any kind of perl-sided loop instantly blocks the whole client. has anyone tried the same in perl? or is there any kind of built-in function i can use?
just please, dont suggest threads or even ithreads unless you have a -working- solution under 32bit winXP.

thank you for your time!

KP
Top

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #1 on Wed 13 Sep 2006 01:07 AM (UTC)
Message
Quote:

... thanks for this really great client!


Thanks for the compliments *blushes*.

Quote:

... i'd like to react on a variable change at the time it happens ...



That problem sounds familiar. Try searching this forum for 'equilibrium'.

I have a feeling there are quite a few posts about that sort of thing. I think in your case you need a table of outstanding things you want to cast, and when you get the message "you regain equilibrium" or whatever it is, pull out another item from the table.

When you make a trigger you can "send to script" so the trigger can do more than just set a variable.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #2 on Wed 13 Sep 2006 01:08 AM (UTC)
Message
The other approach is to use Lua scripting and coroutines. See this post:

http://www.gammon.com.au/forum/?id=4957

This is not theoretical stuff, it actually works, and lets you 'pause' a script in the middle and wait for further messages, or time to elapse, or both.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by KP   (24 posts)  Bio
Date Reply #3 on Wed 13 Sep 2006 03:01 PM (UTC)

Amended on Wed 13 Sep 2006 03:58 PM (UTC) by KP

Message
thanks alot, Nick! i have really no clue about Lua as i never used it before, but i took a look at the basic structure of that script and got a few ideas myself. in fact i was already thinking of a waiting queue at some point, but was affraid that it would get too 'messy'. i just finished a working perl prototype of a quite flexible queueing solution. i thought i'd post it here, as there seem to be very few people doing such things in perl. i really wonder why.

well, this baby works like a charm, but might be hard to read. i inserted comments to make it more understandable, even for perl beginners (err.. i hope).

what it does: if one of the required states is not set (in this case i need both equilibrium and balance to cast a spell), the action is queued. an action can also be submitted with the 'supersede' flag set, in which case the queue is deleted and only this action is enqueued (imagine yourself in the middle of a fight with 4 spells in queue and you need a healing spell RIGHT NOW). the actions and flags are submitted to the script, allowing you to set up different aliases for the same spell if you feel like it (one to launch it with queueing, one to make it an 'instant'). it can be perfectly used for whatever else you might need. healing, timed attacks..

what do i need: you will need at least one variable with the same name as the requirement that is checked (in this case 'eq' and 'bal'). you will also need triggers to change its state. most importantly, the regain trigger has to be named [var]_regain (bal_regain, eq_regain) and contain the regex to catch the change. this regex will be used within the script for the new triggers. set up some aliases to play with. they have to send
 cast_spell("cast this on that",1) 
last '1' is the optional supersede flag. with it you will erase the queue and put the action on the first position.

well, i hope thats enough information and that you will find this script useful. just remember that although it perfectly works right now, it still can use some tweaking. there are alot more possibilities ranging from basic (empty queue on walk - hard fight, no time to cast, need to run!) and necessary (a new sub to display the contents of the queue) to exotic (one queue to handle everything you do with priorities). have fun!
Top

Posted by KP   (24 posts)  Bio
Date Reply #4 on Wed 13 Sep 2006 03:02 PM (UTC)

Amended on Wed 13 Sep 2006 04:00 PM (UTC) by KP

Message

# NOTE1 - to make this thing work you will need triggers to set the state of your variables.
#         i used my previously written routines, which captured the state from prompt and are not included here.
#         the easiest way would be to set equilibrium to 0 after the spell is cast in 'cast_now'
#         and set it to 1 when the according trigger fires. this is far from foolproof, but the fastest way to get
#         you started.
#
# NOTE2 - if you have no idea what happens here with the variables and their types, refer to perldoc.
#         i used alot of references and complex data structures just to compress the code and make the whole thing
#         faster and easier
#
# NOTE3 - the whole solution is based on as much flexibility as i could think of at this point. you can use it for healing,
#         herbs, attacks, whatever you want. all you have to do is rewrite 'cast_spell' so it checks the propper requirements,
#         the variables to check and a new @waiting_queue
#
# NOTE4 - a few things can be removed here. the creation of new triggers is back from my idea/attempt to delete them after
#         fireing, which is unnecessary and even time consuming. a better way is to deactivate them as it actually happens,
#         or even keep them up and use them for loss/regain control too. there are many possibilities
#
# NOTE5 - as you see, the readiness triggers are set up as temporary and with moderate sequence number. to make the checks
#         work properly your -real- regain triggers will need lower seq and 'keep evaluating'
#
#

#####
my @waiting_queue = ();  # this line has to be either in global script scope or in some routine that is run at the start of the world
#####

sub cast_now {  # this one just sends the supplied string to the world. you can rename it and use it as the main output sub
  $world->Send(shift);
}  # see NOTE 1


sub cast_spell {   # this is what all your spell aliases should call
  my ($spell,$supersede) = @_;   # we get the full spell string as it will be sent to the world and optional 'supersede' flag
  my $failed = &check_readiness('eq','bal');  # first we check if variables for all required states give us a 'go' - NOTE2 & 3
  if ($failed) {   # nope, some of them was not ready, we have to queue
    @waiting_queue = () if ($supersede == 1);   # we empty the queue if this is a high priority spell
    push @waiting_queue, {$spell => ['eq','bal']};   # we queue the spell with its requirements - NOTE3
    &set_readiness_triggers(@$failed);   # and set the according triggers for those states that failed the test
  } else {   # ah! looks like we can cast!
    @waiting_queue = ();   # empty the queue 'just in case'. it should be emty, but you never know
    &cast_now($spell);   # and we cast the spell
  }
}


sub check_readiness {   # this sub checks the variables and returns those that are not '1'
  my $failed = undef;
  foreach my $name (@_) {
    push @$failed, $name if ($world->GetVariable("$name")) ne '1';  # if the variable is not '1', we add it to the 'failed' array
  }  # see also NOTE 1, 2 & 3
  return $failed;  # return the failed 
}


sub set_readiness_triggers {   # this is used to set the triggers that will be watching for changes - NOTE4
  foreach my $name (@_) {
    if ($world->GetTriggerInfo("waitfor_$name", 1) eq '') { # sets a trigger if its not set already - NOTE5
      $world->AddTriggerEx("waitfor_$name", ($world->GetTriggerInfo($name."_regain", 1)), "%1", 16384+32+8+1, -1, 0, "", "readiness_trigger", 12, 50)
    } else {  # there is a trigger already, lets activate it
      $world->EnableTrigger("waitfor_$name", 1);
    }
  }
}  


sub readiness_trigger {    # this one will be used by our created triggers
  my $name = shift;
  if ($#waiting_queue >= 0) {  # if there is at least 1 item in queue we can go ahead
    my ($spell,$requirements) = each %{$waiting_queue[0]};  # get the first spell and its requirements from the queue - NOTE 2
    my $failed = &check_readiness(@$requirements);  # check if we meet the requirements
    if ($failed) {   # nope, we dont
      &set_readiness_triggers(@$failed)    #set new triggers for what we are missing
    } else {   # yup, we do
      shift @waiting_queue;   # remove the first spell from the queue
      &cast_now($spell);   # and cast it
    }
  } else {   # the queue is empty
    $world->EnableTrigger($name, 0);   # disable the trigger that fired
  }
}
Top

Posted by Nick Gammon   Australia  (23,102 posts)  Bio   Forum Administrator
Date Reply #5 on Wed 13 Sep 2006 10:14 PM (UTC)
Message
Thanks for sharing your script with us. I hope it is useful to other players who script in Perl.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
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.


24,098 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.