]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/common/c_internal.cpp
Implement client node dig prediction
[dragonfireclient.git] / src / script / common / c_internal.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "common/c_internal.h"
21 #include "debug.h"
22 #include "log.h"
23 #include "settings.h"
24
25 std::string script_get_backtrace(lua_State *L)
26 {
27         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE);
28         lua_call(L, 0, 1);
29         return luaL_checkstring(L, -1);
30 }
31
32 int script_exception_wrapper(lua_State *L, lua_CFunction f)
33 {
34         try {
35                 return f(L);  // Call wrapped function and return result.
36         } catch (const char *s) {  // Catch and convert exceptions.
37                 lua_pushstring(L, s);
38         } catch (std::exception &e) {
39                 lua_pushstring(L, e.what());
40         }
41         return lua_error(L);  // Rethrow as a Lua error.
42 }
43
44 /*
45  * Note that we can't get tracebacks for LUA_ERRMEM or LUA_ERRERR (without
46  * hacking Lua internals).  For LUA_ERRMEM, this is because memory errors will
47  * not execute the the error handler, and by the time lua_pcall returns the
48  * execution stack will have already been unwound.  For LUA_ERRERR, there was
49  * another error while trying to generate a backtrace from a LUA_ERRRUN.  It is
50  * presumed there is an error with the internal Lua state and thus not possible
51  * to gather a coherent backtrace.  Realistically, the best we can do here is
52  * print which C function performed the failing pcall.
53  */
54 void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn)
55 {
56         if (pcall_result == 0)
57                 return;
58
59         const char *err_type;
60         switch (pcall_result) {
61         case LUA_ERRRUN:
62                 err_type = "Runtime";
63                 break;
64         case LUA_ERRMEM:
65                 err_type = "OOM";
66                 break;
67         case LUA_ERRERR:
68                 err_type = "Double fault";
69                 break;
70         default:
71                 err_type = "Unknown";
72         }
73
74         if (!mod)
75                 mod = "??";
76
77         if (!fxn)
78                 fxn = "??";
79
80         const char *err_descr = lua_tostring(L, -1);
81         if (!err_descr)
82                 err_descr = "<no description>";
83
84         char buf[256];
85         snprintf(buf, sizeof(buf), "%s error from mod '%s' in callback %s(): ",
86                 err_type, mod, fxn);
87
88         std::string err_msg(buf);
89         err_msg += err_descr;
90
91         if (pcall_result == LUA_ERRMEM) {
92                 err_msg += "\nCurrent Lua memory usage: "
93                         + itos(lua_gc(L, LUA_GCCOUNT, 0) >> 10) + " MB";
94         }
95
96         throw LuaError(err_msg);
97 }
98
99 // Push the list of callbacks (a lua table).
100 // Then push nargs arguments.
101 // Then call this function, which
102 // - runs the callbacks
103 // - replaces the table and arguments with the return value,
104 //     computed depending on mode
105 void script_run_callbacks_f(lua_State *L, int nargs,
106         RunCallbacksMode mode, const char *fxn)
107 {
108         FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
109
110         // Insert error handler
111         PUSH_ERROR_HANDLER(L);
112         int error_handler = lua_gettop(L) - nargs - 1;
113         lua_insert(L, error_handler);
114
115         // Insert run_callbacks between error handler and table
116         lua_getglobal(L, "core");
117         lua_getfield(L, -1, "run_callbacks");
118         lua_remove(L, -2);
119         lua_insert(L, error_handler + 1);
120
121         // Insert mode after table
122         lua_pushnumber(L, (int) mode);
123         lua_insert(L, error_handler + 3);
124
125         // Stack now looks like this:
126         // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
127
128         int result = lua_pcall(L, nargs + 2, 1, error_handler);
129         if (result != 0)
130                 script_error(L, result, NULL, fxn);
131
132         lua_remove(L, error_handler);
133 }
134
135 void log_deprecated(lua_State *L, const std::string &message)
136 {
137         static bool configured = false;
138         static bool do_log     = false;
139         static bool do_error   = false;
140
141         // Only read settings on first call
142         if (!configured) {
143                 std::string value = g_settings->get("deprecated_lua_api_handling");
144                 if (value == "log") {
145                         do_log = true;
146                 } else if (value == "error") {
147                         do_log   = true;
148                         do_error = true;
149                 }
150         }
151
152         if (do_log) {
153                 warningstream << message << std::endl;
154                 // L can be NULL if we get called by log_deprecated(const std::string &msg)
155                 // from scripting_game.cpp.
156                 if (L) {
157                         if (do_error)
158                                 script_error(L, LUA_ERRRUN, NULL, NULL);
159                         else
160                                 infostream << script_get_backtrace(L) << std::endl;
161                 }
162         }
163 }
164