]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_base.cpp
Omnicleanup: header cleanup, add ModApiUtil shared between game and mainmenu
[minetest.git] / src / script / cpp_api / s_base.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 "cpp_api/s_base.h"
21 #include "cpp_api/s_internal.h"
22 #include "lua_api/l_object.h"
23 #include "serverobject.h"
24 #include "debug.h"
25 #include "log.h"
26 #include "mods.h"
27 #include "util/string.h"
28
29
30 extern "C" {
31 #include "lualib.h"
32 }
33
34 #include <stdio.h>
35 #include <cstdarg>
36
37
38 class ModNameStorer
39 {
40 private:
41         lua_State *L;
42 public:
43         ModNameStorer(lua_State *L_, const std::string modname):
44                 L(L_)
45         {
46                 // Store current modname in registry
47                 lua_pushstring(L, modname.c_str());
48                 lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
49         }
50         ~ModNameStorer()
51         {
52                 // Clear current modname in registry
53                 lua_pushnil(L);
54                 lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
55         }
56 };
57
58 static int loadScript_ErrorHandler(lua_State *L) {
59         lua_getfield(L, LUA_GLOBALSINDEX, "debug");
60         if (!lua_istable(L, -1)) {
61                 lua_pop(L, 1);
62                 return 1;
63         }
64         lua_getfield(L, -1, "traceback");
65         if (!lua_isfunction(L, -1)) {
66                 lua_pop(L, 2);
67                 return 1;
68         }
69         lua_pushvalue(L, 1);
70         lua_pushinteger(L, 2);
71         lua_call(L, 2, 1);
72         return 1;
73 }
74
75
76 /*
77         ScriptApiBase
78 */
79
80 ScriptApiBase::ScriptApiBase()
81 {
82         m_luastackmutex.Init();
83
84         #ifdef SCRIPTAPI_LOCK_DEBUG
85         m_locked = false;
86         #endif
87
88         m_luastack = luaL_newstate();
89         assert(m_luastack);
90
91         // Make the ScriptApiBase* accessible to ModApiBase
92         lua_pushlightuserdata(m_luastack, this);
93         lua_setfield(m_luastack, LUA_REGISTRYINDEX, "scriptapi");
94
95         m_server = 0;
96         m_environment = 0;
97         m_guiengine = 0;
98 }
99
100 ScriptApiBase::~ScriptApiBase()
101 {
102         lua_close(m_luastack);
103 }
104
105 bool ScriptApiBase::loadMod(const std::string &scriptpath,
106                 const std::string &modname)
107 {
108         ModNameStorer modnamestorer(getStack(), modname);
109
110         if(!string_allowed(modname, MODNAME_ALLOWED_CHARS)){
111                 errorstream<<"Error loading mod \""<<modname
112                                 <<"\": modname does not follow naming conventions: "
113                                 <<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
114                 return false;
115         }
116
117         bool success = false;
118
119         try{
120                 success = loadScript(scriptpath);
121         }
122         catch(LuaError &e){
123                 errorstream<<"Error loading mod \""<<modname
124                                 <<"\": "<<e.what()<<std::endl;
125         }
126
127         return success;
128 }
129
130 bool ScriptApiBase::loadScript(const std::string &scriptpath)
131 {
132         verbosestream<<"Loading and running script from "<<scriptpath<<std::endl;
133
134         lua_State *L = getStack();
135
136         lua_pushcfunction(L, loadScript_ErrorHandler);
137         int errorhandler = lua_gettop(L);
138
139         int ret = luaL_loadfile(L, scriptpath.c_str()) || lua_pcall(L, 0, 0, errorhandler);
140         if(ret){
141                 errorstream<<"========== ERROR FROM LUA ==========="<<std::endl;
142                 errorstream<<"Failed to load and run script from "<<std::endl;
143                 errorstream<<scriptpath<<":"<<std::endl;
144                 errorstream<<std::endl;
145                 errorstream<<lua_tostring(L, -1)<<std::endl;
146                 errorstream<<std::endl;
147                 errorstream<<"=======END OF ERROR FROM LUA ========"<<std::endl;
148                 lua_pop(L, 1); // Pop error message from stack
149                 lua_pop(L, 1); // Pop the error handler from stack
150                 return false;
151         }
152         lua_pop(L, 1); // Pop the error handler from stack
153         return true;
154 }
155
156 void ScriptApiBase::realityCheck()
157 {
158         int top = lua_gettop(m_luastack);
159         if(top >= 30){
160                 dstream<<"Stack is over 30:"<<std::endl;
161                 stackDump(dstream);
162                 scriptError("Stack is over 30 (reality check)");
163         }
164 }
165
166 void ScriptApiBase::scriptError(const char *fmt, ...)
167 {
168         va_list argp;
169         va_start(argp, fmt);
170         char buf[10000];
171         vsnprintf(buf, 10000, fmt, argp);
172         va_end(argp);
173         //errorstream<<"SCRIPT ERROR: "<<buf;
174         throw LuaError(m_luastack, buf);
175 }
176
177 void ScriptApiBase::stackDump(std::ostream &o)
178 {
179         int i;
180         int top = lua_gettop(m_luastack);
181         for (i = 1; i <= top; i++) {  /* repeat for each level */
182                 int t = lua_type(m_luastack, i);
183                 switch (t) {
184
185                         case LUA_TSTRING:  /* strings */
186                                 o<<"\""<<lua_tostring(m_luastack, i)<<"\"";
187                                 break;
188
189                         case LUA_TBOOLEAN:  /* booleans */
190                                 o<<(lua_toboolean(m_luastack, i) ? "true" : "false");
191                                 break;
192
193                         case LUA_TNUMBER:  /* numbers */ {
194                                 char buf[10];
195                                 snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i));
196                                 o<<buf;
197                                 break; }
198
199                         default:  /* other values */
200                                 o<<lua_typename(m_luastack, t);
201                                 break;
202
203                 }
204                 o<<" ";
205         }
206         o<<std::endl;
207 }
208
209 void ScriptApiBase::addObjectReference(ServerActiveObject *cobj)
210 {
211         SCRIPTAPI_PRECHECKHEADER
212         //infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;
213
214         // Create object on stack
215         ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
216         int object = lua_gettop(L);
217
218         // Get minetest.object_refs table
219         lua_getglobal(L, "minetest");
220         lua_getfield(L, -1, "object_refs");
221         luaL_checktype(L, -1, LUA_TTABLE);
222         int objectstable = lua_gettop(L);
223
224         // object_refs[id] = object
225         lua_pushnumber(L, cobj->getId()); // Push id
226         lua_pushvalue(L, object); // Copy object to top of stack
227         lua_settable(L, objectstable);
228 }
229
230 void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj)
231 {
232         SCRIPTAPI_PRECHECKHEADER
233         //infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;
234
235         // Get minetest.object_refs table
236         lua_getglobal(L, "minetest");
237         lua_getfield(L, -1, "object_refs");
238         luaL_checktype(L, -1, LUA_TTABLE);
239         int objectstable = lua_gettop(L);
240
241         // Get object_refs[id]
242         lua_pushnumber(L, cobj->getId()); // Push id
243         lua_gettable(L, objectstable);
244         // Set object reference to NULL
245         ObjectRef::set_null(L);
246         lua_pop(L, 1); // pop object
247
248         // Set object_refs[id] = nil
249         lua_pushnumber(L, cobj->getId()); // Push id
250         lua_pushnil(L);
251         lua_settable(L, objectstable);
252 }
253
254 // Creates a new anonymous reference if cobj=NULL or id=0
255 void ScriptApiBase::objectrefGetOrCreate(
256                 ServerActiveObject *cobj)
257 {
258         lua_State *L = getStack();
259
260         if(cobj == NULL || cobj->getId() == 0){
261                 ObjectRef::create(L, cobj);
262         } else {
263                 objectrefGet(cobj->getId());
264         }
265 }
266
267 void ScriptApiBase::objectrefGet(u16 id)
268 {
269         lua_State *L = getStack();
270
271         // Get minetest.object_refs[i]
272         lua_getglobal(L, "minetest");
273         lua_getfield(L, -1, "object_refs");
274         luaL_checktype(L, -1, LUA_TTABLE);
275         lua_pushnumber(L, id);
276         lua_gettable(L, -2);
277         lua_remove(L, -2); // object_refs
278         lua_remove(L, -2); // minetest
279 }