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.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ MUSHclient ➜ Lua ➜ Can you redefine the functions used by triggers etc.?

Can you redefine the functions used by triggers etc.?

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


Posted by Tsunami   USA  (204 posts)  Bio
Date Wed 07 Jun 2006 01:14 AM (UTC)
Message
With lua it is now possible to define functions on the fly. Before I waste a lot of time, can these function be called by a trigger? Ie. something along the lines of:

<trigger
 ...
 script="blahblah"
 ...
>
</trigger>

...

function dostuff()
 doeslotsofstuff...
end

blahblah = dostuff


This is just a very basic example, but I'm looking to potentially define 100's of functions along these lines. Because they are all very similar, it would be one hell of a pain to write them all out, when I can just make a loop to create them all. They need to be able to be called by triggers however. Any ideas?

Thanks
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #1 on Wed 07 Jun 2006 01:16 AM (UTC)
Message
If MUSHclient just looks up the function given by the name in the 'script' field and calls it, then there's no reason why this shouldn't work. Functions in Lua are first-class data types which means that you can assign them to variables, etc.

The easiest to confirm this is to just try it out for a very simple example like you have here.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Tsunami   USA  (204 posts)  Bio
Date Reply #2 on Wed 07 Jun 2006 01:20 AM (UTC)
Message
Well, having decided to throw away my laziness, I tested it, and it apparently worked. Thanks GAWD, cause I was not looking forward to copy and pasting those 100's of functions...
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #3 on Wed 07 Jun 2006 01:34 AM (UTC)

Amended on Wed 07 Jun 2006 01:44 AM (UTC) by Nick Gammon

Message
The only restriction on this is that MUSHclient checks that the function exists when the trigger is loaded. In this case, function blahblah needs to exist.

However there is nothing stopping you making dummy functions that are subequently replaced.

eg.


function blahblah ()  -- dummy function so trigger loads
end -- function 


-- and later on ...

blahblah = function (name, line, wildcards)
  -- do something different now
end -- function


What MUSHclient does is simply remember whether or not the function exists (not its address) so that later on it can call it. In fact, you can confuse it by deleting a function after it has done its checks.

For example, if a trigger did this:


function blahblah () 
  blahblah = nil   -- function no longer bound to variable blahblah
end -- function 


Then next time the trigger fires you see an error dialog:


attempt to call a nil value
stack traceback:


- Nick Gammon

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

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #4 on Wed 07 Jun 2006 01:40 AM (UTC)
Message
Another approach would be to have every trigger call the same function, and inside that function, call the "real" function based on the trigger name.

For example:



function trigger1 (name, line, wildcards)
  print "in trigger1"
end -- trigger1

function trigger2 (name, line, wildcards)
  print "in trigger2"
end -- trigger1


triggerfuncs = {
  foo = trigger1, 
  bar = trigger2,
  }
  
function alltriggers (name, line, wildcards)
  local f = triggerfuncs [name]
  if f then
    f (name, line, wildcards)
  end -- function found
end -- alltriggers


Now the trigger whose name (label) is "foo" goes to script trigger1, and trigger whose name is "bar" goes to script trigger2.

- Nick Gammon

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

Posted by Tsunami   USA  (204 posts)  Bio
Date Reply #5 on Wed 07 Jun 2006 05:40 AM (UTC)
Message
I was thinking along those lines, but I figured it would be just as much as a pain to create triggers with all those different names and then test for them...

Anyways, here's the real kicker. Suppose I do something like this?

functionname = 'function1'

_G[functionname] = function() ... end


Now, I want, within the body of the created function to call another function with 'function1' as the argument. So the basic problem is finding a way to load the functionname variable into the function scope. Assuming that the functionname variable will no longer be in scope, or will have changed values when the created function is called, is there anyway to do this? As far as I can see, no, but I leave it up to your collective wisdom.
Top

Posted by Ked   Russia  (524 posts)  Bio
Date Reply #6 on Wed 07 Jun 2006 08:32 AM (UTC)
Message
You mean something like:


function test(...) test2(test) end


That shouldn't be a problem, since "test" function is in the global scope, so it is its own upvalue.

Works just the same with function objects explicitely bound to names in the _G table, since it's the same machinery, just under a different disguise.
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #7 on Wed 07 Jun 2006 11:01 AM (UTC)
Message
I think he means the function name, not a reference to the function itself. I was going to (try to) do an example using upvalues, and will do so tomorrow. :)

- Nick Gammon

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

Posted by Ked   Russia  (524 posts)  Bio
Date Reply #8 on Wed 07 Jun 2006 12:13 PM (UTC)
Message
I think I get it now. Still not sure though :)

But how about this:



functionname = 'function1'
_G[functionname] = function (tocall) 
  local functionname = functionname
  local name = function()
    tocall(functionname)
  end
  
  return name
end

function testfunc(name) print(name) end

function1 = function1(testfunc)
function1()                       --> "function1"
functionname = "function2"
function1()                       --> "function1"


Top

Posted by Tsunami   USA  (204 posts)  Bio
Date Reply #9 on Wed 07 Jun 2006 05:11 PM (UTC)
Message
This looks promising, I'll be testing it. Nick was right, I wanted the string value, not function value. I'll post again once I've tested.
Top

Posted by Tsunami   USA  (204 posts)  Bio
Date Reply #10 on Wed 07 Jun 2006 05:32 PM (UTC)
Message
I've done some testing, and I think I finally get how it works. Took me a bit to bend my brain around it, but I think it should help a great deal. Thanks!
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #11 on Wed 07 Jun 2006 09:17 PM (UTC)

Amended on Wed 07 Jun 2006 09:24 PM (UTC) by Nick Gammon

Message
Yes I was thinking along those lines, but you don't need to use local variables inside the function because arguments are automatically upvalues. Try this:


function makefunc (name, f)
  _G [name] = 
    function (...) 
      f (name, unpack (arg)) 
    end -- local function
end -- makefunc


This function does everything in one hit, adds the function "f" into the global namespace with name "name", and remembers the name as an upvalue, so when "f" is called it is supplied an initial argument, which is the name it was called. Examples:


function test (name, a, b, c)
  print ("in test:", name, a, b, c)
end -- test

function test2 (name, ...)
  print ("in test2:", name, unpack (arg))
end -- test2

makefunc ("Nick", test)
makefunc ("Tsunami", test)
makefunc ("Ked", test2)

Nick ("foo", "bar", "look") --> in test: Nick foo bar look
Tsunami ("x", "y", "z") --> in test: Tsunami x y z
Ked ("ready", "set", "go", "let's", "move", 42) --> in test2: Ked ready set go  let's move  42


You can see from this that the newly created functions, Nick, Tsunami and Ked, all remembered both their own names, and the functions they should call.

The called functions can also be passed arguments. By using "..." and "unpack (arg)" we can pass any number of arguments to the final function.

We can check they are upvalues by using the "showupvalues" function I wrote for another thread:


function showupvalues (f)
  assert (type (f) == "function")
  
  local i = 1
  local name, val
  
  repeat
    name, val = debug.getupvalue (f, i)
    if name then
      print ("index", i, name, "=", val)
      i = i + 1
    end -- if
  until not name

end -- function showupvalues

print (test) --> function: 00325590
showupvalues (Nick)

--> index 1 f = function: 00325590
--> index 2 name  = Nick

- Nick Gammon

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

Posted by Tsunami   USA  (204 posts)  Bio
Date Reply #12 on Thu 08 Jun 2006 09:18 PM (UTC)
Message
Yup, thats how I ended up doing it after playing around with ways to simplify the code to what I needed. Thanks!
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.


29,852 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.