Attempting to use wait to wait for a particular line and failing

Posted by Lilija on Tue 27 Jul 2010 07:33 AM — 23 posts, 88,606 views.

#0
Hello all,

I'm currently writing a script and am trying to capture some output from the game, then perform some math with it. Obviously the script keeps executing before the mud returns the line I'm waiting for. I read the FAQ about waiting for a trigger line, but apparently I'm doing something wrong. Here's my code for the wait, along with what string I'm trying to match and a few lines after the wait function.


	   EnableTrigger("temper_base_capture", true)
	   function cr ()
	    repeat
		 Send ("wp " .. TemperWeapon)
		 line, wildcards = 
		  wait.regexp ("^\s*Damage:\s+(\d+)\s+Precision:\s+(\d+)\s+Speed:\s+(\d+)\s*$")
		  
		  until string.find (line, "Speed")
		  wait.time (5)
		  Note ("Hopefully variables are set")
		  end
		  
		wait.make (cr)
	   EnableTrigger("temper_base_capture", false)
	   TemperDamage = GetVariable("temper_damage_base")
	   TemperPrecision = GetVariable("temper_precision_base")
	   TemperSpeed = GetVariable("temper_speed_base")
	   TemperWeapon = "axe"
	   TemperSrc = GetVariable("temper_source")
	   local FinalDamage
	   local FinalPrecision
	   local FinalSpeed
	   FinalDamage = GetVariable("temper_damage")
	   FinalPrecision = GetVariable("temper_precision")
	   FinalSpeed = GetVariable("temper_speed")
	   local DamageDifference
	   local PrecisionDifference
	   local SpeedDifference
	   
	   DamageDifference = FinalDamage - TemperDamage   
    <trigger
     expand_variables="y"
	 enabled="y"
     group="Lil_Forger_Suite"
     ignore_case="y"
     keep_evaluating="y"
     match="^\s*Damage:\s+(\d+)\s+Precision:\s+(\d+)\s+Speed:\s+(\d+)\s*$"
     name="temper_base_capture"
     regexp="y"
     send_to="12"
     sequence="100"
    > 
     <send>
	  SetVariable("temper_damage_base", %1)
      SetVariable("temper_precision_base", %2)
      SetVariable("temper_speed_base", %3)
     </send>	  
    </trigger>



The trigger captures the variables fine (I check after the error happens, and the variables are set), unfortunately, the script errors because the trigger doesn't set them fast enough. It appears that the wait isn't working to me. The error I get is that TemperDamage is a nil value. There is, literally, no pause whatsoever. Anyone have any tips for making this work? Just need the script to sit and loop/pause long enough to allow the trigger to set the variables.
Australia Forum Administrator #1
I'm not clear on where the script part you posted is. Where is that? I don't get the part about the trigger not setting the variable fast enough.
#2
Sorry about that, that's what I get for drinking and posting. What I'm attempting to do is pause the script in my alias until it receives a line from the game similar to this


Damage: 230  Precision: 187  Speed: 280


This trigger captures the values for the above stats and sets a variable for each.


 <trigger
     expand_variables="y"
	 enabled="y"
     group="Lil_Forger_Suite"
     ignore_case="y"
     keep_evaluating="y"
     match="^\s*Damage:\s+(\d+)\s+Precision:\s+(\d+)\s+Speed:\s+(\d+)\s*$"
     name="temper_base_capture"
     regexp="y"
     send_to="12"
     sequence="100"
    > 
     <send>
	  SetVariable("temper_damage_base", %1)
      SetVariable("temper_precision_base", %2)
      SetVariable("temper_speed_base", %3)
     </send>	  
    </trigger>


After having read the post about pausing a script until a certain line arrives from the mud, I believe I want something similar to this to keep the script from advancing until the regexp is matched and it sees the word Speed with string.find.


           function cr ()
	    repeat
		 Send ("wp " .. TemperWeapon)
		 line, wildcards = 
		  wait.regexp ("^\s*Damage:\s+(\d+)\s+Precision:\s+(\d+)\s+Speed:\s+(\d+)\s*$")
		  
		  until string.find (line, "Speed")
		  wait.time (5)
		  Note ("Hopefully variables are set")
		  end
		  
		wait.make (cr)


The following is the alias I'm attempting to trigger off the string "temper go" (which it does). It requires wait, sets a few local variables from Mushclient variables that are set, enables the trigger that should strip out the numbers from the line, does the wait function (during which I imagine the trigger ought to capture the values I want from the line), then it should disable said trigger after the wait function. After that, it should just do some math with said variables.


	<alias
	 name="lil_forger_temper_go"
	 match="^\s*temper\s+go\s*$"
	 enabled="y"
	 group="Lil_Forger_Suite"
	 regexp="y"
	 send_to="12"
	 ignore_case="y"
	 sequence="100"
    >
	 <send>
	 
	  require "wait"
	  
	   local TemperDamage
	   local TemperWeapon
	   TemperWeapon = GetVariable("weapon_number")
	   EnableTrigger("temper_base_capture", true)
	   function cr ()
	    repeat
		 Send ("wp " .. TemperWeapon)
		 line, wildcards = 
		  wait.regexp ("^\s*Damage:\s+(\d+)\s+Precision:\s+(\d+)\s+Speed:\s+(\d+)\s*$")
		  
		  until string.find (line, "Speed")
		  wait.time (5)
		  Note ("Hopefully variables are set")
		  end
		wait.make (cr)
	   EnableTrigger("temper_base_capture", false)
	   TemperDamage = GetVariable("temper_damage_base")
	   local FinalDamage
	   FinalDamage = GetVariable("temper_damage")
	   local DamageDifference
	   
	   DamageDifference = FinalDamage - TemperDamage
     </send>
	</alias>


This is the error message that I receive when I attempt to run the alias the first time.


Run-time error
World: forger test world
Immediate execution
[string "Alias: lil_forger_temper_go"]:43: attempt to perform arithmetic on local 'TemperDamage' (a nil value)


However, subsequently, if I run the alias my desired variables have been set, and it runs fine. I'd just like for them to be set the first time, hence trying to wait for the Damage/Precision/Speed line to be output from the mud before continuing to process. I've stripped down the alias some to just the parts that should be needed for troubleshooting.

Does that help? And thank you very much for the quick reply Nick. You rule!
Netherlands #3
Well, the situation is exactly as the error message says. At the time you are trying to deduct TemperDamage from FinalDamage to get the difference, TemperDamage cannot be converted since it is 'a nil value'.

In other words, you never stored a value in that MUSHclient variable, since the GetVariable() call returned nil.

Thus, are you sure you are setting the variable properly beforehand? I am going to do a bit of conjecture now, but I believe you are misunderstanding the wait.regexp() function in how it works and how it should be used. (Disclaimer: I have never used the wait library, as it was written way after I started coding and I never really got the hang of switching to using/learning it.)

Basically, the wait.regexp() will MAKE a new trigger behind the scenes, and from that, call back to your code so it can be run once more. That trigger will quite likely prevent your OTHER trigger from being run. To boot, you are doing your own matching in a loop to see if the word Speed is in the trigger somewhere, where it should not be needed anymore. And eventually, due to the wait the wait library works, you think you have done the entire waiting gig, but end up doing the calculations immediately.

So, once your wait.regexp() matches, you get your values deposited in the line and wildcards variables. Thus, you could simply fill the appropriate variables at that point. So, I'd arrive at something like this for you to put in your Send box...

require "wait"

wait.make (function ()
      
      -- Send the command.
      local TemperWeapon = GetVariable("weapon_number")
      Send ("wp " .. TemperWeapon)
      
      -- Wait for the line. (Your old trigger is not needed.)
      local line, wildcards = 
      wait.regexp ("^\\s*Damage:\\s+(\\d+)\\s+Precision:\\s+(\\d+)\\s+Speed:\\s+(\\d+)\\s*$")
      
      -- Assign the values according to the captures.
      -- These are local to this 'scope' only, so you can't use them elsewhere
      -- in your script. If you do want to use them elsewhere, remove the
      -- 'local' bits, and they will become available to your entire script,
      -- no matter which trigger, alias or timer it would be.
      local TemperDamage = wildcards[1]
      local TemperPrecision = wildcards[2]
      local TemperSpeed = wildcards[3]
      
      -- Do our logic stuff. (I took some liberties here since you had so many
      -- variables, some of which you never assigned, so it likely doesn't do
      -- exactly what you want. Still, hopefully you can understand how this
      -- works now.
      local FinalDamage = TemperDamage
      local DamageDifference = FinalDamage - TemperDamage
      
end)


Also, on another note... your wait.regexp() call is called with a string containing the regular expression to trigger on, but you should keep in mind that Lua will do some magic with backslashes (the \ character) itself. (Think of when you want to put a literal newline into a string, one would use a \n, thus the actual text '\n' would need to be written as '\\n' in your code. If you want to send a literal \ to the regexp function, you'll need to double it (and yes, you want that here). Thus, that would become...

wait.regexp ("^\\s*Damage:\\s+(\\d+)\\s+Precision:\\s+(\\d+)\\s+Speed:\\s+(\\d+)\\s*$")


(I already put that 'fix' in the above example.)

Edit: Nick, why the hell is the forum eating my backslashes outside the edit screen once forum codes are activated? Gonna double them up to insane levels now, I suppose. Bear with me Lilija while I fix the post.

Edit: Post should be fixed. Nick, if you fix the forum (I assume it is a bug as no other forum I know does it), please fix this post back? On the other hand, that could explain why Lilija's wait.regexp() missed half its backslashes, so I might be able to remove that part of my post. xD
Amended on Tue 27 Jul 2010 10:15 AM by Worstje
Australia Forum Administrator #4
Worstje said:


Edit: Nick, why the hell is the forum eating my backslashes outside the edit screen once forum codes are activated? Gonna double them up to insane levels now, I suppose. Bear with me Lilija while I fix the post.



Once forum codes are activated, since they use backslashes to escape square brackets, you must double backslashes themselves.

What I (almost) always do, when posting code examples, is to use the utility built into MUSHclient itself: Edit Menu -> Convert Clipboard Forum Codes (Shift+Ctrl+Alt+Q). So I:

  • Copy an example:
  • In an open MUSHclient world or notepad window: Shift+Ctrl+Alt+Q
  • Post into forum message between: [code] ... [/code]


Your problem is that in Send-to-script, MUSHclient also processes backslashes, so each backslash that you actually want there has to be doubled again. And then inside a Lua string if you want backslashes you need to double them yet again.

So, if you want a single backslash in a regexp, inside "send to script", you need:

  • For Lua strings: "\\d+"
  • Because it is send-to-script: "\\\\d+"
  • To post on the forum with forum codes: "\\\\\\\\d+"


You can reduce the backslashes by using a script file, not send-to-script, especially if you are planning to use backslashes a lot, like in PCRE regexps.

Note that Lua regexps use % instead, which simplifies things, however %1 has a meaning inside send-to-script as well, so the % needs to be doubled also. Sigh.
Netherlands #5
Yeah, I am quite familiar with the phenomenon and the reasons, and am not saying it is unreasonable for it to happen in strings, in send to script and so forth. I am only argueing it is strongly counter-intuitive to have another layer in the forums. An example of the trouble it can cause is someone pasting a trigger using send-to-script and the appropriate code tags.

To be fair, you did already document the way to do it at http://www.gammon.com.au/forum/?id=4776, but I doubt anyone will read the entire thing - at most they'll want to use it to figure out where the 'Copy Trigger' button is.

Every kid on the internet has posted on a forum and has experience with forum codes. Expecting them to think they don't know how to post a snippet in a code tag... well, that doesn't happen.

But I guess I'm derailing this topic even after going out of my way to create a seperate topic elsewhere. Sorry about that, Lilija.
#6
No worries! Going to crack away at this now, so thank you VERY much for the help!
Australia Forum Administrator #7
Lilija said:

I'm currently writing a script and am trying to capture some output from the game, then perform some math with it. Obviously the script keeps


You may be trying something more complex than it has to be.

To react to output from the game, simply make a trigger that detects that output, and then do arithmetic when it happens. You don't need all this wait stuff for something simple like that.
#8
I've looked around a few places on the forums as well as the site, and haven't been successful in locating the information I'm looking for. I'd like to modularize my script into a few different files. When I try to import them in (via require, as in: require "myfile") it's unable to find them. Is there something specific I need to set to do this? Or do I need to locate the files in a specific folder?

I feel like an idiot not being able to figure it out. So thanks for bearing with me.
Australia Forum Administrator #9
Can you post the message? Require normally lists a whole heap of places it looks if it fails. Is your file in one of those?
#10
I'm a moron. Realized what I was doing wrong, and I can just make the user stick it in the lua folder I suppose. Any way to tell it to look inside a folder inside the lua folder? Or will it do that automatically?
Amended on Wed 28 Jul 2010 06:35 AM by Lilija
USA #11
require("subfolder.yourscript")


That will look for lua/subfolder/yourscript.lua (among other paths).
Australia Forum Administrator #12
See:

http://www.gammon.com.au/scripts/doc.php?lua=require

In particular:

http://www.gammon.com.au/scripts/doc.php?lua=package.path
#13
And, since I still can't seem to grasp what seem like they should be basic things.

Is there some sort of tut on the forums that discusses what belongs in what file type? Ie I imagine most code/logic belongs in .lua files, whereas aliases/triggers/variables go in .xml files. If this is the case, if I define a function in a .lua file, am I then able to use it within an alias from a .xml file, and what's a general syntax to do so? I played with this for half an hour, and can't seem to figure it out.

My current file that I was using before I decided to make it a bit more modular was a .lua file that I loaded in using File, Import. I'd like to split the functionality of what I'm doing across multiple files to keep my code cleaner.

I apologize if this makes little since, since it's late where I am. If you need a better explanation, I'll provide one tomorrow.
Netherlands #14
Opinions tend to differ, and different approaches have been used over the years by various people. All in all, I'd say you do what is best for you given your situation.

While you are still learning the basics, I'd recommend not to stick to a single plugin, and doing your scripting in its script tags as opposed to Sending to Script. That way, you won't run into extra difficulty of loading other files, and be able to keep it quite contained, while at the same time getting more insight into how MUSHclient works on the inside.

For example, you can use the Plugin Wizard to create a really basic plugin if you want, and look at the file it generates, and from then on edit that file rather than work your way through the crappy MUSHclient interface for editing stuff. It takes a bit of getting used to, but once you get past the hurdle you won't want to do it any differently. :)

Additionally, you can look at the plugins supplied with MUSHclient, or ask other people on these forums for example plugins they have written.
#15
Hate to necro this thread, but having an issue that did't exist a few saves ago with my script. Saving as a .lua file, and importing into Mushclient. Attempting to create a function with the following code.



     function TemperStats (MaximumStat, MinimumStat, TemperStatValue, TemperStat, TemperWeaponType)
	  if (TemperStatValue \> MaximumStat or TemperStatValue \< MinimumStat) then
       ColourNote ("white", "red", TemperWeaponType .. "s have a minimum " .. TemperStat .. " of " .. MinimumStat .. " and a maximum " .. TemperStat .. " of " .. MaximumStat .. "!")
      else
       ColourNote ("white", "green", "Tempering value for " .. TemperStat .. " is " .. TemperStatValue .. ".")
	   SetVariable ("temper_" .. TemperStat, TemperStatValue)
      end -- if
     end -- function


For some reason, the less than symbol seems to be messing the whole thing up. I previously had used \< with no issue importing, but now I'm given an error that says "Attribute name must start with letter or underscore, but starts with ")"". Suppose I could solve this issue if I were to use &lt; instead of the less than symbol, but that seems like a lot of work. Is there something completely obvious that I'm missing? I feel like that's the case.
Australia Forum Administrator #16
The syntax:


if (TemperStatValue \> MaximumStat or TemperStatValue \< MinimumStat) then


isn't valid Lua. Do you mean:


if (TemperStatValue > MaximumStat or TemperStatValue < MinimumStat) then

#17
Perhaps I do mean what you posted Nick. Suppose it'd be helpful if I include all of what I'm doing.



   --This allows the user to set what stats they want to temper to.  If they go beyond maximums and minimums, it will return an error.
   <alias
    name="lil_forger_temperstats"
	match="^\s*temper\s+(damage|precision|speed)\s+(\d+)\s*$" 
	enabled="y"
	group="Lil_Forger_Suite"
	regexp="y"
	send_to="12"
	ignore_case="y"
	sequence="100"
   >
    <send>
	
     --Setting a local variable for the weapon type taken from the Lil_Forger_tempertype alias
	 
	 local WeaponType
	 WeaponType = GetVariable("weapon_type")
	 
	 --This function is used to evaluate the user's input.  It takes five arguments, the maximum, minimum and user given numbers for the particular stat, said stat, and the weapon type.  It then evaluates the relevant numbers, and if they meet the conditions sets an appropriate variable.  If they don't, it returns an error message that tells them what the minimum and maximum values for the particular stat is.

     function TemperStats (MaximumStat, MinimumStat, TemperStatValue, TemperStat, TemperWeaponType)
	  if (TemperStatValue \> MaximumStat or TemperStatValue \< MinimumStat) then
       ColourNote ("white", "red", TemperWeaponType .. "s have a minimum " .. TemperStat .. " of " .. MinimumStat .. " and a maximum " .. TemperStat .. " of " .. MaximumStat .. "!")
      else
       ColourNote ("white", "green", "Tempering value for " .. TemperStat .. " is " .. TemperStatValue .. ".")
	   SetVariable ("temper_" .. TemperStat, TemperStatValue)
      end -- if
     end -- function
	 
--Start of the alias's logic.
	 
	 if string.lower("%1") == "damage" then
	  if (WeaponType == "rapier" or WeaponType == "hammer") then
	   TemperStats (180, 35, %2, "damage", WeaponType)
	  else
	   ColourNote ("white", "red", "You have to set the type of weapon you want to temper first.  Use the command [temper type X] where X is one of the types of weapons (rapier, klangaxe, katana, etc.)")
	  end -- if
	 elseif string.lower("%1") == "precision" then
	  if (WeaponType == "rapier" or WeaponType == "hammer") then
	   TemperStats (235, 85, %2, "precision", WeaponType)
	  else
	   ColourNote ("white", "red", "You have to set the type of weapon you want to temper first.  Use the command [temper type X] where X is one of the types of weapons (rapier, klangaxe, katana, etc.)")
	  end -- if
	 elseif string.lower("%1") == "speed" then
	  if (WeaponType == "rapier" or WeaponType == "hammer") then
	   TemperStats (280, 139, %2, "speed", WeaponType)
	  else
	   ColourNote ("white", "red", "You have to set the type of weapon you want to temper first.  Use the command [temper type X] without the brackets where X is one of the types of weapons (rapier, klangaxe, katana, etc.)")
	  end -- if
	 end -- if
	</send>
   </alias>


My understanding is that if it's inside an alias/would be inside Mushclient's command box for making a new alias, the <, >, ", and other similar characters need to be escaped. What really kills me about this is that it worked just fine about a week ago, and now suddenly doesn't want to. Also, removing the \s still gives me the same error. I'm pretty sure it's just a dumb mistake on my part somewhere, so thanks in advance for being patient and showing dumb ol' me the light.
Amended on Wed 04 Aug 2010 04:15 AM by Lilija
USA #18
Yeah, just replace the <'s with &lt; (and & with &amp; if any). The problem is that the exported format is XML, and those two characters are considered special. The XML file itself doesn't care what the content it holds is, so it can't, say, give Lua special syntactic treatment.
Australia Forum Administrator #19
I don't get this line still:


if (TemperStatValue \> MaximumStat or TemperStatValue \< MinimumStat) then


If it went in "as is" then Lua will object to the \> part - that doesn't make sense.

However the XML import is likely to complain anyway about the < not being a valid element.

Is this from an alias that already exists? I don't quite understand where the stuff you have posted comes from. If you used MUSHclient's "alias copy" then it would have converted the < into &lt;, so I am guessing you didn't do that.

Template:copying
For advice on how to copy aliases, timers or triggers from within MUSHclient, and paste them into a forum message, please see Copying XML.
#20
It's just a file I'm writing in Notepad++. What's funny is that the ONLY thing that causes that line to error is the < . If I remove that and the values it's comparing, the line works perfectly fine, \> and all. I'll just use &lt; , suppose my question was just more along the lines of why does escaping > seem to work and < doesn't.
Australia Forum Administrator #21
Ah OK, I start to see...

In this line:


if (TemperStatValue \> MaximumStat or TemperStatValue \< MinimumStat) then


In the Send box (which you are using) escape sequences are converted (eg. \n becomes a newline, \t becomes a tab etc.).

However an unrecognized sequence simply becomes itself, so for example \< becomes < and \> becomes >.

Thus, as far as Lua is concerned all you have done is this:


if (TemperStatValue > MaximumStat or TemperStatValue < MinimumStat) then


So the backslashes effectively do nothing (except confuse me). <grin>

Having got past the backslashes, when importing XML, the characters '&' and '<' in particular are important, because they "start" something. For example:


<alias>
&lt;


The '>' symbol isn't quite as important - it finishes a sequence like <alias> but on its own doesn't do a huge amount of harm.

So, the < definitely has to be turned into &lt; if you are going to import things this way.

But if you are going to edit in Notepad, you can copy and paste *directly from the Send box* and you don't need to muck around converting the < and > symbols.
USA #22
Something else you may want to try is wrapping the content in a CDATA section. A CDATA section specifically tells the parser to treat the contents as non-XML data.

<alias
  enabled="y"
  match="^\s*foo\s*"
  regexp="y"
  >
  <send><![CDATA[
    if 1 < 2 then
      Note("I can use < and & just fine here!")
    end
  ]]></send>
  </alias>


You just have to be careful not to use "]]>" inside the CDATA section, because that's what ends it.