// 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);
- // Stack now looks like this:
- // ... <return value = nil> <error handler> <table> <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);
- }
+ // 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);
- // 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;
+ // Insert mode after table
+ lua_pushnumber(L, (int) mode);
+ lua_insert(L, errorhandler + 3);
- // value removed, keep key for next iteration
- }
-
- // Remove stuff from stack, leaving only the return value
- lua_settop(L, rv);
+ // Stack now looks like this:
+ // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
- // 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);
- }
+ if (lua_pcall(L, nargs + 2, 1, errorhandler)) {
+ script_error(L);
}
+
+ lua_remove(L, -2); // Remove error handler
}