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