]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/script/cpp_api/s_base.cpp
Add LuaEntity on_death callback (#6177)
[dragonfireclient.git] / src / script / cpp_api / s_base.cpp
index bfe03ec46f6cb189d25ca90a69e2eee1f8acb073..6bea8230b28c8161f15df1144722c682b4a149b1 100644 (file)
@@ -23,12 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "lua_api/l_object.h"
 #include "common/c_converter.h"
 #include "serverobject.h"
-#include "debug.h"
 #include "filesys.h"
-#include "log.h"
 #include "mods.h"
 #include "porting.h"
 #include "util/string.h"
+#include "server.h"
+#ifndef SERVER
+#include "client.h"
+#endif
 
 
 extern "C" {
@@ -40,6 +42,8 @@ extern "C" {
 
 #include <stdio.h>
 #include <cstdarg>
+#include "script/common/c_content.h"
+#include <sstream>
 
 
 class ModNameStorer
@@ -70,22 +74,26 @@ class ModNameStorer
 ScriptApiBase::ScriptApiBase()
 {
 #ifdef SCRIPTAPI_LOCK_DEBUG
-       m_locked = false;
+       m_lock_recursion_count = 0;
 #endif
 
        m_luastack = luaL_newstate();
        FATAL_ERROR_IF(!m_luastack, "luaL_newstate() failed");
 
-       luaL_openlibs(m_luastack);
+       lua_atpanic(m_luastack, &luaPanic);
 
-       // Add and save an error handler
-       lua_pushcfunction(m_luastack, script_error_handler);
-       m_errorhandler = lua_gettop(m_luastack);
+       luaL_openlibs(m_luastack);
 
        // Make the ScriptApiBase* accessible to ModApiBase
        lua_pushlightuserdata(m_luastack, this);
        lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
 
+       // Add and save an error handler
+       lua_getglobal(m_luastack, "debug");
+       lua_getfield(m_luastack, -1, "traceback");
+       lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE);
+       lua_pop(m_luastack, 1); // pop debug
+
        // If we are using LuaJIT add a C++ wrapper function to catch
        // exceptions thrown in Lua -> C++ calls
 #if USE_LUAJIT
@@ -103,15 +111,6 @@ ScriptApiBase::ScriptApiBase()
 
        lua_pushstring(m_luastack, porting::getPlatformName());
        lua_setglobal(m_luastack, "PLATFORM");
-
-       // m_secure gets set to true inside
-       // ScriptApiSecurity::initializeSecurity(), if neccessary.
-       // Default to false otherwise
-       m_secure = false;
-
-       m_server = NULL;
-       m_environment = NULL;
-       m_guiengine = NULL;
 }
 
 ScriptApiBase::~ScriptApiBase()
@@ -119,77 +118,117 @@ ScriptApiBase::~ScriptApiBase()
        lua_close(m_luastack);
 }
 
-bool ScriptApiBase::loadMod(const std::string &script_path,
-               const std::string &mod_name, std::string *error)
+int ScriptApiBase::luaPanic(lua_State *L)
+{
+       std::ostringstream oss;
+       oss << "LUA PANIC: unprotected error in call to Lua API ("
+               << lua_tostring(L, -1) << ")";
+       FATAL_ERROR(oss.str().c_str());
+       // NOTREACHED
+       return 0;
+}
+
+void ScriptApiBase::loadMod(const std::string &script_path,
+               const std::string &mod_name)
 {
        ModNameStorer mod_name_storer(getStack(), mod_name);
 
-       return loadScript(script_path, error);
+       loadScript(script_path);
 }
 
-bool ScriptApiBase::loadScript(const std::string &script_path, std::string *error)
+void ScriptApiBase::loadScript(const std::string &script_path)
 {
        verbosestream << "Loading and running script from " << script_path << std::endl;
 
        lua_State *L = getStack();
 
+       int error_handler = PUSH_ERROR_HANDLER(L);
+
        bool ok;
        if (m_secure) {
                ok = ScriptApiSecurity::safeLoadFile(L, script_path.c_str());
        } else {
                ok = !luaL_loadfile(L, script_path.c_str());
        }
-       ok = ok && !lua_pcall(L, 0, 0, m_errorhandler);
+       ok = ok && !lua_pcall(L, 0, 0, error_handler);
        if (!ok) {
                std::string error_msg = lua_tostring(L, -1);
-               if (error)
-                       *error = error_msg;
-               errorstream << "========== ERROR FROM LUA ===========" << std::endl
-                       << "Failed to load and run script from " << std::endl
-                       << script_path << ":" << std::endl << std::endl
-                       << error_msg << std::endl << std::endl
-                       << "======= END OF ERROR FROM LUA ========" << std::endl;
-               lua_pop(L, 1); // Pop error message from stack
-               return false;
+               lua_pop(L, 2); // Pop error message and error handler
+               throw ModError("Failed to load and run script from " +
+                               script_path + ":\n" + error_msg);
        }
-       return true;
+       lua_pop(L, 1); // Pop error handler
 }
 
+#ifndef SERVER
+void ScriptApiBase::loadModFromMemory(const std::string &mod_name)
+{
+       ModNameStorer mod_name_storer(getStack(), mod_name);
+
+       const std::string *init_filename = getClient()->getModFile(mod_name + ":init.lua");
+       const std::string display_filename = mod_name + ":init.lua";
+       if(init_filename == NULL)
+               throw ModError("Mod:\"" + mod_name + "\" lacks init.lua");
+
+       verbosestream << "Loading and running script " << display_filename << std::endl;
+
+       lua_State *L = getStack();
+
+       int error_handler = PUSH_ERROR_HANDLER(L);
+
+       bool ok = ScriptApiSecurity::safeLoadFile(L, init_filename->c_str(), display_filename.c_str());
+       if (ok)
+               ok = !lua_pcall(L, 0, 0, error_handler);
+       if (!ok) {
+               std::string error_msg = luaL_checkstring(L, -1);
+               lua_pop(L, 2); // Pop error message and error handler
+               throw ModError("Failed to load and run mod \"" +
+                               mod_name + "\":\n" + error_msg);
+       }
+       lua_pop(L, 1); // Pop error handler
+}
+#endif
+
 // Push the list of callbacks (a lua table).
 // Then push nargs arguments.
 // Then call this function, which
 // - runs the callbacks
 // - replaces the table and arguments with the return value,
 //     computed depending on mode
+// This function must only be called with scriptlock held (i.e. inside of a
+// code block with SCRIPTAPI_PRECHECKHEADER declared)
 void ScriptApiBase::runCallbacksRaw(int nargs,
                RunCallbacksMode mode, const char *fxn)
 {
+#ifdef SCRIPTAPI_LOCK_DEBUG
+       assert(m_lock_recursion_count > 0);
+#endif
        lua_State *L = getStack();
        FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
 
        // Insert error handler
-       lua_pushcfunction(L, script_error_handler);
-       int errorhandler = lua_gettop(L) - nargs - 1;
-       lua_insert(L, errorhandler);
+       PUSH_ERROR_HANDLER(L);
+       int error_handler = lua_gettop(L) - nargs - 1;
+       lua_insert(L, error_handler);
 
        // Insert run_callbacks between error handler and table
        lua_getglobal(L, "core");
        lua_getfield(L, -1, "run_callbacks");
        lua_remove(L, -2);
-       lua_insert(L, errorhandler + 1);
+       lua_insert(L, error_handler + 1);
 
        // Insert mode after table
        lua_pushnumber(L, (int)mode);
-       lua_insert(L, errorhandler + 3);
+       lua_insert(L, error_handler + 3);
 
        // Stack now looks like this:
        // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
 
-       int result = lua_pcall(L, nargs + 2, 1, errorhandler);
+       int result = lua_pcall(L, nargs + 2, 1, error_handler);
        if (result != 0)
                scriptError(result, fxn);
 
-       lua_remove(L, -2); // Remove error handler
+       lua_remove(L, error_handler);
 }
 
 void ScriptApiBase::realityCheck()
@@ -222,7 +261,7 @@ void ScriptApiBase::stackDump(std::ostream &o)
                                break;
                        case LUA_TNUMBER:  /* numbers */ {
                                char buf[10];
-                               snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i));
+                               snprintf(buf, 10, "%lf", lua_tonumber(m_luastack, i));
                                o << buf;
                                break;
                        }
@@ -303,18 +342,17 @@ void ScriptApiBase::objectrefGetOrCreate(lua_State *L,
        if (cobj == NULL || cobj->getId() == 0) {
                ObjectRef::create(L, cobj);
        } else {
-               objectrefGet(L, cobj->getId());
+               push_objectRef(L, cobj->getId());
        }
 }
 
-void ScriptApiBase::objectrefGet(lua_State *L, u16 id)
+Server* ScriptApiBase::getServer()
 {
-       // Get core.object_refs[i]
-       lua_getglobal(L, "core");
-       lua_getfield(L, -1, "object_refs");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       lua_pushnumber(L, id);
-       lua_gettable(L, -2);
-       lua_remove(L, -2); // object_refs
-       lua_remove(L, -2); // core
+       return dynamic_cast<Server *>(m_gamedef);
+}
+#ifndef SERVER
+Client* ScriptApiBase::getClient()
+{
+       return dynamic_cast<Client *>(m_gamedef);
 }
+#endif