ILua scripting

by Unknown

Back to Project L.

Unknown2007-05-26 19:12:07
Cool. I've got a little different way of handling the parameters (basically, a strsplit function), but this looks good, too. The add_timer creates a sub-second, one-shot timer. The del_timer is just for removing one before it fires. (I think the name goes in the optional third parameter, if I'm not mistaken.)

CODE
echo("Timer test (3 seconds)")
add_timer(3.0, function () echo("   Ding!") end)
Xinael2007-05-26 19:20:34
Thanks for the explanation smile.gif

Still not having luck actually running any code, though =/
Xinael2007-05-27 01:16:35
This is very frustrating. I downloaded vanilla LMTS from the Sourceforge page, added
CODE
module "test"
load "test.lua"
to the config file. test.lua contains only
CODE
function mb.client_aliases (input)
    echo(input)
    return true
end
and it's not working. It's as if it just didn't exist. I also tried copying and pasting the examples from the start of the thread. `il shows that test is loaded, but the alias isn't doing anything.
Unknown2007-05-27 02:19:03
Here's a snippet (more complete) from my aliases.lua file I use:

CODE
function alias_handler(cmd)
  cmd = string.gsub(cmd, "^%s*(.-)%s*$", "%1")

  if cmd == "_timer" then
    echo("Timer test (3 seconds)")
    add_timer(3.0, function () echo("   Ding!") end)
    return true
  end

  return false
end

mb.client_aliases = alias_handler


I don't know if this function assignment works any better than the direct definition of the function. Let me know if it helps?
Xinael2007-05-27 02:27:21
How bizarre. I changed the config to load aliases.lua. `il mods says it's loaded. But if I type _timer, nothing happens.

And now I changed NOTHING except to add another line loading a non-existent "blah.lua" in the config file and it's working fine. How utterly bizarre. I wonder if I can get other stuff working that way...

Yep, the prompt you provided is now working with zero changes other than adding a reference to that non-existent "blah.lua". Removing the "load "blah.lua"" line breaks them again.

It also doesn't matter whether or not blah.lua actually exists. For some reason, though, having this blah.lua present is the magic word to get the scripts loading.

As if that wasn't enough, I've now got my previous example working. test.lua is still just
CODE
function mb.client_aliases (input)
    echo(input)
    return true
end

but config.ilua.txt now reads
CODE
module "test"
load "test.lua"
load "blah.lua"

and it's working.
Theomar2007-05-27 02:43:06
I think Forren was havin this problem.

Put two lines of space after each module. That solved it for him, I think.
Xinael2007-05-27 02:48:00
There are no extra lines after the blah.lua line and it's loading. If I remove the text on that line and add another newline, it stops working.
Theomar2007-05-27 02:55:20
Interesting...
Unknown2007-05-27 11:43:46
I'll put this on the list of things to fix. Evidently, something in the ILua code must be looking for another line or two of text after the last real line of code. The only changes I made to ILua were to update the ATCP variables, so it's a pre-existing condition. Can you put commented out lines after your last 'load' line? That's what I've got in my config and it works for me.
Xinael2007-05-27 14:18:35
Thanks again - adding a comment after my module declaration fixed it right up.
Theomar2007-05-31 15:15:01
Is there an easier way of making a multi-line trigger?
QUOTE
Hello
World


CODE
if (mb.line == "Hello") then
    test = true
end

if (mb.line == "World") then
    if (test == true) then
        echo("Hello World."
    end
end

This isn't the implementation of my triggers (thanks Xinael), but the way I have it done would be similar to this.

Is there anyway to make it so I could do something similar to:
CODE
if (mb.line == "Hello\\nWorld")

I am pretty sure not, since mb.line saves just one line, but any help in making a faster multi-line trigger is appreciated.
Unknown2007-05-31 16:47:20
You could work out a way that's similar to zMUD's multi-state triggers. Setup tables for your trigger lines and a special field for which one was the last matched. If the first matches, move on to the next. If that matches, move on again, and so on. If nothing matches, reset the count and start looking for the first line again.
Theomar2007-05-31 23:40:03
So essentially what I said, only using sub-tables for the extra lines? I see that that would make it faster. Thanks smile.gif
Unknown2007-06-01 01:06:57
I'll have to play with the idea, together with Xinael's code, and see if I can come up with something easy and fun. Off the top of my head, it might use a table like this to fire the multi-line triggers:

CODE
multi = {
  {
     = { "This is line one.", "This is line two.", "This is line three." },
     = 1,
     = function () echo("First trigger fired.") end
  },
  {
     = { "Another line one.", "Another line two." },
     = 1,
     = function () echo("Second trigger fired.") end
  }
}


In your trigger match function, you check the line indexed by the state number against the text from the game (mb.line). If the pattern matches, you bump up the state. When the last state matches, you execute the function for that trigger. If the current state pattern doesn't match, you reset the state and start over (assuming that you're doing one line after another and nothing in between).

It would be better to generalize the idea into some sort of function or metatable, obviously, and you could get really fancy with things like zMUD does with timing out a state or matching within N lines.
Unknown2007-06-01 09:34:22
Well, another alias script. Basically it uses a tree structured variable aliases, and walks into
the tree until it finds a value. If it is a string, then it will replace the text with that string
if it is a function, then it will call the function with the remaining text.

Examples:
"mark list 123" will execute the command marks:list("123")
"gret name" will send the message "greet name"
"do echo("Show this message)" will execute the text after "do", which will be executed by lua, resulting in showing the message to the client.




-- Just to ease the use of functions, with an object
-- p(A,"b") = function(x) return A:b(x) end
local function p(target, func)
local f = target
return function(...)
return f(target, unpack(arg))
end
end

aliases = {
mark = {
list = p(marks,"list"),
load = p(marks, "load"),
},
= function(str) loadstring(str)() end,
= function(str) loadstring("echo(tostring(" .. str .. "))")() end,
bigmap = p(map, "draw_big"),
room = {
show = room.show
},
gret = "greet"
}

function alias(str)
local al = aliases

local fpat = "(.-)+"
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s and type(al) == "table" do
if s ~= 1 or cap ~= "" then
al = al
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str and type(al) == "table" then
cap = str:sub(last_end)
last_end = #str+1
al = al
end

if type(al) == "string" then
send(al .. str:sub(last_end))
return true
elseif type(al) == "function" then
-- timer.start()
al(str:sub(last_end))
-- timer.stop()
return true
end
return
end

mb.client_aliases = function(x) return alias(x) or status:parse_user_line(x) end
Unknown2007-06-01 10:05:00
And about multi-line stuff.
I would probably try something like this:

CODE
states = {
  scent_state = {
    trigger = "You smell around you.."
    process = function(x) set_state(states.scent_line) end
  },
  scent_line = {
    process = my_object.process_scent_line(x)
  },
  look_state = {
    trigger = function() return mb.raw_line:startswith(C.y) end
    process = parse_title -- Which is a function, that parses titles;-)
  }
}


When a line comes in, the method will check if there is currently a state set. If so
it will use the process of that state. If not, then it will test if one of the trigger variables
fits with the message and will call that process function.

triggers can be a string or a functions that return a boolean. (Or a set of strings and functions...)
process is a function, and it has the capability to set the state to an other state.
I think I like the set_state to be in the process function, since then it is possible
to change the state depending on the line that is processed.

I guess the occurrence of a prompt will always reset the state.
A method add_action_at_prompt would also be useful.
It would allow you to put all the lines into a table, and then you could do calculations when the prompt arrives.

Well so far, for my ramblings.
Theomar2007-06-01 15:52:43
Oo, more interesting and faster ways.

Speed shouldn't matter too much for me in the future: my new laptop will destroy any inefficiencies.

Thanks for all the help and putting up with my questions, Zarquan. And thanks for the advice for the tree structured aliases. I wouldn't have thought of doing aliases that way.
Unknown2007-06-01 17:03:41
The way I do multiline triggers. I add every incoming line to a table, with a limit of 20 entries. Then every trigger has a lines data to it, defaults to 1, which tells my parser of the maximum amount of lines it can need to match. To build the string I use for matching against the pattern, I do something like:

CODE
line = table.concat(lines, "\\n", #lines - (trigger.lines - 1))

This lets me do patterns like:

CODE
^ILua launches a backflipkick into the air\\.\\nIt connects to zMUD's ass!$


The only flaw with this method, so far, is that it -can- fire twice if I do not use anchors such as ^and $. Shouldn't be too hard to code around though and some triggers could very well use this as a feature.
Theomar2007-06-01 18:28:16
Hrm. That's a nifty way and it works using \\n.
Theomar2007-06-07 15:30:37
Looks like I won't be needing multi-line triggers anymore, but thanks for the help. smile.gif