Move script_run_callbacks to Lua

This commit is contained in:
ShadowNinja 2013-12-07 22:03:07 -05:00
parent 96f753a108
commit a0dce51af6
2 changed files with 57 additions and 84 deletions

@ -314,6 +314,45 @@ minetest.register_item(":", {
groups = {not_in_creative_inventory=1},
})
function minetest.run_callbacks(callbacks, mode, ...)
assert(type(callbacks) == "table")
local cb_len = #callbacks
if cb_len == 0 then
if mode == 2 or mode == 3 then
return true
elseif mode == 4 or mode == 5 then
return false
end
end
local ret = nil
for i = 1, cb_len do
local cb_ret = callbacks[i](...)
if mode == 0 and i == 1 then
ret = cb_ret
elseif mode == 1 and i == cb_len then
ret = cb_ret
elseif mode == 2 then
if not cb_ret or i == 1 then
ret = cb_ret
end
elseif mode == 3 then
if cb_ret then
return cb_ret
end
ret = cb_ret
elseif mode == 4 then
if (cb_ret and not ret) or i == 1 then
ret = cb_ret
end
elseif mode == 5 and cb_ret then
return cb_ret
end
end
return ret
end
--
-- Callback registration
--

@ -66,101 +66,35 @@ void script_error(lua_State *L)
// Then push nargs arguments.
// Then call this function, which
// - runs the callbacks
// - removes the table and arguments from the lua stack
// - pushes the return value, computed depending on mode
// - replaces the table and arguments with the return value,
// computed depending on mode
void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode)
{
// Insert the return value into the lua stack, below the table
assert(lua_gettop(L) >= nargs + 1);
lua_pushnil(L);
int rv = lua_gettop(L) - nargs - 1;
lua_insert(L, rv);
// Insert error handler after return value
// Insert error handler
lua_pushcfunction(L, script_error_handler);
int errorhandler = rv + 1;
int errorhandler = lua_gettop(L) - nargs - 1;
lua_insert(L, errorhandler);
// Insert minetest.run_callbacks between error handler and table
lua_getglobal(L, "minetest");
lua_getfield(L, -1, "run_callbacks");
lua_remove(L, -2);
lua_insert(L, errorhandler + 1);
// Insert mode after table
lua_pushnumber(L, (int) mode);
lua_insert(L, errorhandler + 3);
// Stack now looks like this:
// ... <return value = nil> <error handler> <table> <arg#1> <arg#2> ... <arg#n>
// ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
int table = errorhandler + 1;
int arg = table + 1;
luaL_checktype(L, table, LUA_TTABLE);
// Foreach
lua_pushnil(L);
bool first_loop = true;
while(lua_next(L, table) != 0){
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TFUNCTION);
// Call function
for(int i = 0; i < nargs; i++)
lua_pushvalue(L, arg+i);
if(lua_pcall(L, nargs, 1, errorhandler))
script_error(L);
// Move return value to designated space in stack
// Or pop it
if(first_loop){
// Result of first callback is always moved
lua_replace(L, rv);
first_loop = false;
} else {
// Otherwise, what happens depends on the mode
if(mode == RUN_CALLBACKS_MODE_FIRST)
lua_pop(L, 1);
else if(mode == RUN_CALLBACKS_MODE_LAST)
lua_replace(L, rv);
else if(mode == RUN_CALLBACKS_MODE_AND ||
mode == RUN_CALLBACKS_MODE_AND_SC){
if((bool)lua_toboolean(L, rv) == true &&
(bool)lua_toboolean(L, -1) == false)
lua_replace(L, rv);
else
lua_pop(L, 1);
}
else if(mode == RUN_CALLBACKS_MODE_OR ||
mode == RUN_CALLBACKS_MODE_OR_SC){
if((bool)lua_toboolean(L, rv) == false &&
(bool)lua_toboolean(L, -1) == true)
lua_replace(L, rv);
else
lua_pop(L, 1);
}
else
assert(0);
}
// Handle short circuit modes
if(mode == RUN_CALLBACKS_MODE_AND_SC &&
(bool)lua_toboolean(L, rv) == false)
break;
else if(mode == RUN_CALLBACKS_MODE_OR_SC &&
(bool)lua_toboolean(L, rv) == true)
break;
// value removed, keep key for next iteration
if (lua_pcall(L, nargs + 2, 1, errorhandler)) {
script_error(L);
}
// Remove stuff from stack, leaving only the return value
lua_settop(L, rv);
// Fix return value in case no callbacks were called
if(first_loop){
if(mode == RUN_CALLBACKS_MODE_AND ||
mode == RUN_CALLBACKS_MODE_AND_SC){
lua_pop(L, 1);
lua_pushboolean(L, true);
}
else if(mode == RUN_CALLBACKS_MODE_OR ||
mode == RUN_CALLBACKS_MODE_OR_SC){
lua_pop(L, 1);
lua_pushboolean(L, false);
}
}
lua_remove(L, -2); // Remove error handler
}