Routing interpret() through Lua first?

Posted by Darwin on Tue 29 Jan 2008 03:21 AM — 4 posts, 20,664 views.

USA #0
I've seen mention of a few people saying they've routed their interpret function through Lua first to see if there is a Lua command/function to take care of it. I'm curious as to how you go about doing that. Some pointers or an example would be helpful.
USA #1
Here's what I do:

Step 1: get a line from the network

Step 2: send it to Lua line handler
- check for coroutine to resume etc., if none, go to step 3

Step 3: send to C interpret
- before doing normal thing, send to Lua command handler; if that returns true, stop. If it returns false, do the normal interpret thing.

On the Lua side, I have a data structure that stores commands, that are just functions like C commands. If the command isn't in the structure, the Lua command handler returns false. If it is, then it considers the command handled even if the syntax etc. has an error in it.



My command handler looks like this:


function _M.interpret(actor, cmd, ...)

    -- First, check to see that we actually have the command.
    local c = findCommandByPrefix(actor, cmd, CMD_PREFIX_LIMIT)

    if type(c) == "boolean" then
        return c
    end

    -- Check for permission.
    if c.checks then
        for _, check in ipairs(c.checks) do
            local ok, whynot = check(actor)
            if not ok then
                actor:sendText("You can't do that: ", whynot, "\n")
                return true -- we 'handled' the command.
            end
        end
    end

    -- ok, run the command.
    local co = coroutine.create(c.code)
    local result = { coroutine.resume(co, actor, ...) }

    -- first value is true/false; false if errors.
    -- if false, then result[2] is error message.
    if not result[1] then
        actor:sendText("There was an error executing the command.\n")
        actor:sendText("Please be sure to tell an admin about this.\n")
        error(result[2])
    end

    -- Deal with however the command terminated. E.g. we might
    -- need to reschedule the command.
    return handleCommandResult(actor, c, co, result)

end


The data structure I use is a lexical trie, in case that makes a difference.
USA #2
My personal butchery as follows:
interpret.c: Extra check before calling do_fun, between timer checks.

	/* check for command in character then mud lua first */
	if( call_lua(ch, cmd->fun_name, argument, NULL) == LUA_NOT_FOUND )
		(*cmd->do_fun) ( ch, argument );


Lua Code example: the command is of course, echo

-- attempt at dynamic (overwritting internal) function
-- it exists already in C, act_wiz.c
function do_echo(ch, argument)
	mud.msg_game("do_echo lua override: " .. argument, color.immort)
end


There is of course slightly more to the trick than the above, such as func name lookup which requires dynamic lookup (already in SmaugFUSS). My call_lua may be different from the stock one, I really don't remember.
As always, Your Mileage May Vary
USA #3
That reminds me: by the time Lua gets the arguments, they've been divided into a list according to the standard parsing rules. (I don't use an 'argument' string, I use varargs to pass in the arguments one at a time.)


Here's my echo example:


makeCommand {
    name = "luaecho",

    usage = "[stuff to echo]",

    brief = [[Echo a string to stdout.]],

    code = function(actor, ...)
        for i = 1, select('#', ...) do
            actor:sendText(i, ":", select(i, ...),
                " (len:", #select(i,...),")\r\n")
                actor:sendText("\n")
        end
    end,

    --checks = {cmdchks.isPc, cmdchks.isImmortal, },
}


There's a bit more stuff going on because here you see the creation of the whole command structure, not just the command function. The name/usage/brief stuff lets you document commands where the command code is. I'm going to have to experiment with whether or not this is a good idea, and how to override settings set by the code.