1 /*
2 script/cpp_api/s_base.cpp
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 */
5
6 /*
7 This file is part of Freeminer.
8
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Freeminer is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "cpp_api/s_base.h"
24 #include "cpp_api/s_internal.h"
25 #include "lua_api/l_object.h"
26 #include "serverobject.h"
27 #include "debug.h"
28 #include "filesys.h"
29 #include "log.h"
30 #include "mods.h"
31 #include "porting.h"
32 #include "util/string.h"
33
34
35 extern "C" {
36 #include "lualib.h"
37 #if USE_LUAJIT
38 #include "luajit.h"
39 #endif
40 }
41
42 #include <stdio.h>
43 #include <cstdarg>
44
45
46 class ModNameStorer
47 {
48 private:
49 lua_State *L;
50 public:
ModNameStorer(lua_State * L_,const std::string & modname)51 ModNameStorer(lua_State *L_, const std::string &modname):
52 L(L_)
53 {
54 // Store current modname in registry
55 lua_pushstring(L, modname.c_str());
56 lua_setfield(L, LUA_REGISTRYINDEX, "current_modname");
57 }
~ModNameStorer()58 ~ModNameStorer()
59 {
60 // Clear current modname in registry
61 lua_pushnil(L);
62 lua_setfield(L, LUA_REGISTRYINDEX, "current_modname");
63 }
64 };
65
66
67 /*
68 ScriptApiBase
69 */
70
ScriptApiBase()71 ScriptApiBase::ScriptApiBase()
72 {
73 m_luastack = luaL_newstate();
74 assert(m_luastack);
75
76 luaL_openlibs(m_luastack);
77
78 // Add and save an error handler
79 lua_pushcfunction(m_luastack, script_error_handler);
80 m_errorhandler = lua_gettop(m_luastack);
81
82 // Make the ScriptApiBase* accessible to ModApiBase
83 lua_pushlightuserdata(m_luastack, this);
84 lua_setfield(m_luastack, LUA_REGISTRYINDEX, "scriptapi");
85
86 // If we are using LuaJIT add a C++ wrapper function to catch
87 // exceptions thrown in Lua -> C++ calls
88 #if USE_LUAJIT
89 lua_pushlightuserdata(m_luastack, (void*) script_exception_wrapper);
90 luaJIT_setmode(m_luastack, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
91 lua_pop(m_luastack, 1);
92 #endif
93
94 // Add basic globals
95 lua_newtable(m_luastack);
96 lua_setglobal(m_luastack, "core");
97
98 lua_pushstring(m_luastack, DIR_DELIM);
99 lua_setglobal(m_luastack, "DIR_DELIM");
100
101 lua_pushstring(m_luastack, porting::getPlatformName());
102 lua_setglobal(m_luastack, "PLATFORM");
103
104 m_server = NULL;
105 m_environment = NULL;
106 m_guiengine = NULL;
107 }
108
~ScriptApiBase()109 ScriptApiBase::~ScriptApiBase()
110 {
111 lua_close(m_luastack);
112 }
113
loadMod(const std::string & scriptpath,const std::string & modname)114 bool ScriptApiBase::loadMod(const std::string &scriptpath,
115 const std::string &modname)
116 {
117 ModNameStorer modnamestorer(getStack(), modname);
118
119 if (!string_allowed(modname, MODNAME_ALLOWED_CHARS)) {
120 errorstream<<"Error loading mod \""<<modname
121 <<"\": modname does not follow naming conventions: "
122 <<"Only chararacters [a-z0-9_-] are allowed."<<std::endl;
123 return false;
124 }
125
126 return loadScript(scriptpath);
127 }
128
loadScript(const std::string & scriptpath)129 bool ScriptApiBase::loadScript(const std::string &scriptpath)
130 {
131 verbosestream<<"Loading and running script from "<<scriptpath<<std::endl;
132
133 lua_State *L = getStack();
134
135 int ret = luaL_loadfile(L, scriptpath.c_str()) || lua_pcall(L, 0, 0, m_errorhandler);
136 if (ret) {
137 errorstream << "========== ERROR FROM LUA ===========" << std::endl;
138 errorstream << "Failed to load and run script from " << std::endl;
139 errorstream << scriptpath << ":" << std::endl;
140 errorstream << std::endl;
141 errorstream << lua_tostring(L, -1) << std::endl;
142 errorstream << std::endl;
143 errorstream << "======= END OF ERROR FROM LUA ========" << std::endl;
144 lua_pop(L, 1); // Pop error message from stack
145 return false;
146 }
147 return true;
148 }
149
realityCheck()150 void ScriptApiBase::realityCheck()
151 {
152 int top = lua_gettop(m_luastack);
153 if(top >= 30){
154 dstream<<"Stack is over 30:"<<std::endl;
155 stackDump(dstream);
156 std::string traceback = script_get_backtrace(m_luastack);
157 throw LuaError("Stack is over 30 (reality check)\n" + traceback);
158 }
159 }
160
scriptError()161 void ScriptApiBase::scriptError()
162 {
163 throw LuaError(lua_tostring(m_luastack, -1));
164 }
165
stackDump(std::ostream & o)166 void ScriptApiBase::stackDump(std::ostream &o)
167 {
168 int i;
169 int top = lua_gettop(m_luastack);
170 for (i = 1; i <= top; i++) { /* repeat for each level */
171 int t = lua_type(m_luastack, i);
172 switch (t) {
173
174 case LUA_TSTRING: /* strings */
175 o<<"\""<<lua_tostring(m_luastack, i)<<"\"";
176 break;
177
178 case LUA_TBOOLEAN: /* booleans */
179 o<<(lua_toboolean(m_luastack, i) ? "true" : "false");
180 break;
181
182 case LUA_TNUMBER: /* numbers */ {
183 char buf[10];
184 snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i));
185 o<<buf;
186 break; }
187
188 default: /* other values */
189 o<<lua_typename(m_luastack, t);
190 break;
191
192 }
193 o<<" ";
194 }
195 o<<std::endl;
196 }
197
addObjectReference(ServerActiveObject * cobj)198 void ScriptApiBase::addObjectReference(ServerActiveObject *cobj)
199 {
200 SCRIPTAPI_PRECHECKHEADER
201 //infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;
202
203 // Create object on stack
204 ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
205 int object = lua_gettop(L);
206
207 // Get core.object_refs table
208 lua_getglobal(L, "core");
209 lua_getfield(L, -1, "object_refs");
210 luaL_checktype(L, -1, LUA_TTABLE);
211 int objectstable = lua_gettop(L);
212
213 // object_refs[id] = object
214 lua_pushnumber(L, cobj->getId()); // Push id
215 lua_pushvalue(L, object); // Copy object to top of stack
216 lua_settable(L, objectstable);
217 }
218
removeObjectReference(ServerActiveObject * cobj)219 void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj)
220 {
221 SCRIPTAPI_PRECHECKHEADER
222 //infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;
223
224 // Get core.object_refs table
225 lua_getglobal(L, "core");
226 lua_getfield(L, -1, "object_refs");
227 luaL_checktype(L, -1, LUA_TTABLE);
228 int objectstable = lua_gettop(L);
229
230 // Get object_refs[id]
231 lua_pushnumber(L, cobj->getId()); // Push id
232 lua_gettable(L, objectstable);
233 // Set object reference to NULL
234 ObjectRef::set_null(L);
235 lua_pop(L, 1); // pop object
236
237 // Set object_refs[id] = nil
238 lua_pushnumber(L, cobj->getId()); // Push id
239 lua_pushnil(L);
240 lua_settable(L, objectstable);
241 }
242
243 // Creates a new anonymous reference if cobj=NULL or id=0
objectrefGetOrCreate(lua_State * L,ServerActiveObject * cobj)244 void ScriptApiBase::objectrefGetOrCreate(lua_State *L,
245 ServerActiveObject *cobj)
246 {
247 if(cobj == NULL || cobj->getId() == 0){
248 ObjectRef::create(L, cobj);
249 } else {
250 objectrefGet(L, cobj->getId());
251 }
252 }
253
objectrefGet(lua_State * L,u16 id)254 void ScriptApiBase::objectrefGet(lua_State *L, u16 id)
255 {
256 // Get core.object_refs[i]
257 lua_getglobal(L, "core");
258 lua_getfield(L, -1, "object_refs");
259 luaL_checktype(L, -1, LUA_TTABLE);
260 lua_pushnumber(L, id);
261 lua_gettable(L, -2);
262 lua_remove(L, -2); // object_refs
263 lua_remove(L, -2); // core
264 }
265
266