aboutsummaryrefslogtreecommitdiff
path: root/src/script/lua_api/l_mainmenu.cpp
Commit message (Expand)AuthorAge
...
* Reduce gettext wide/narrow and string/char* conversionsShadowNinja2015-02-05
* Fix all warnings and remove -Wno-unused-but-set cflagkwolekr2015-01-18
* Reorganize supported video driver query mechanismskwolekr2015-01-18
* Add core.get_mapgen_names() to Main Menu API (and use it)kwolekr2014-12-29
* Add video driver selection to settings menu (based uppon idea from webdesigne...sapier2014-08-23
* Fix the *CDP displaySmallJoker2014-08-15
* Remove a lot of superfluous ifndef USE_CURL checkssapier2014-06-19
* Fix regression dirt texture not beeing default in non cloud menusapier2014-06-14
* Organize builtin into subdirectoriesShadowNinja2014-05-07
* Fix code style of async APIShadowNinja2014-04-27
* Remove dependency on marshal and many other async changesShadowNinja2014-04-27
* Add support for dpi based HUD scalingsapier2014-04-27
* Minor fixes for file/modlist download in mainmenusapier2014-04-09
* Fix invalid check for fread error on extracting zipsapier2014-02-07
* Add formspec tableKahrl2014-01-13
* Fix absence of images when compiled with RUN_IN_PLACE=0.Ilya Zhuravlev2014-01-05
* Implement search tab and version pickersapier2013-12-11
* Fix modstore/favourites hang by adding asynchronous lua job supportsapier2013-11-29
* Fix invalid usage of temporary object in mainmenu json conversionsapier2013-11-11
* Show git hash in version string at top left corner of windowKahrl2013-09-28
* Fix umlauts/special character issue in lua gettextBlockMen2013-08-19
* Allow SIGINT to kill mainmenu againKahrl2013-08-19
* Add translation for main menusapier2013-08-17
* Omnicleanup: header cleanup, add ModApiUtil shared between game and mainmenuKahrl2013-08-14
> 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "cpp_api/s_base.h"
#include "cpp_api/s_internal.h"
#include "cpp_api/s_security.h"
#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"


extern "C" {
#include "lualib.h"
#if USE_LUAJIT
	#include "luajit.h"
#endif
}

#include <stdio.h>
#include <cstdarg>


class ModNameStorer
{
private:
	lua_State *L;
public:
	ModNameStorer(lua_State *L_, const std::string &mod_name):
		L(L_)
	{
		// Store current mod name in registry
		lua_pushstring(L, mod_name.c_str());
		lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
	}
	~ModNameStorer()
	{
		// Clear current mod name from registry
		lua_pushnil(L);
		lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
	}
};


/*
	ScriptApiBase
*/

ScriptApiBase::ScriptApiBase() :
	m_luastackmutex(true)
{
#ifdef SCRIPTAPI_LOCK_DEBUG
	m_lock_recursion_count = 0;
#endif

	m_luastack = luaL_newstate();
	FATAL_ERROR_IF(!m_luastack, "luaL_newstate() failed");

	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_pushcfunction(m_luastack, script_error_handler);
	lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_ERROR_HANDLER);

	// If we are using LuaJIT add a C++ wrapper function to catch
	// exceptions thrown in Lua -> C++ calls
#if USE_LUAJIT
	lua_pushlightuserdata(m_luastack, (void*) script_exception_wrapper);
	luaJIT_setmode(m_luastack, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
	lua_pop(m_luastack, 1);
#endif

	// Add basic globals
	lua_newtable(m_luastack);
	lua_setglobal(m_luastack, "core");

	lua_pushstring(m_luastack, DIR_DELIM);
	lua_setglobal(m_luastack, "DIR_DELIM");

	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()
{
	lua_close(m_luastack);
}

void ScriptApiBase::loadMod(const std::string &script_path,
		const std::string &mod_name)
{
	ModNameStorer mod_name_storer(getStack(), mod_name);

	loadScript(script_path);
}

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, error_handler);
	if (!ok) {
		std::string error_msg = lua_tostring(L, -1);
		lua_pop(L, 2); // Pop error message and error handler
		throw ModError("Failed to load and run script from " +
				script_path + ":\n" + error_msg);
	}
	lua_pop(L, 1); // Pop error handler
}

// 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
	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, error_handler + 1);

	// Insert mode after table
	lua_pushnumber(L, (int)mode);
	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, error_handler);
	if (result != 0)
		scriptError(result, fxn);

	lua_remove(L, error_handler);
}

void ScriptApiBase::realityCheck()
{
	int top = lua_gettop(m_luastack);
	if (top >= 30) {
		dstream << "Stack is over 30:" << std::endl;
		stackDump(dstream);
		std::string traceback = script_get_backtrace(m_luastack);
		throw LuaError("Stack is over 30 (reality check)\n" + traceback);
	}
}

void ScriptApiBase::scriptError(int result, const char *fxn)
{
	script_error(getStack(), result, m_last_run_mod.c_str(), fxn);
}

void ScriptApiBase::stackDump(std::ostream &o)
{
	int top = lua_gettop(m_luastack);
	for (int i = 1; i <= top; i++) {  /* repeat for each level */
		int t = lua_type(m_luastack, i);
		switch (t) {
			case LUA_TSTRING:  /* strings */
				o << "\"" << lua_tostring(m_luastack, i) << "\"";
				break;
			case LUA_TBOOLEAN:  /* booleans */
				o << (lua_toboolean(m_luastack, i) ? "true" : "false");
				break;
			case LUA_TNUMBER:  /* numbers */ {
				char buf[10];
				snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i));
				o << buf;
				break;
			}
			default:  /* other values */
				o << lua_typename(m_luastack, t);
				break;
		}
		o << " ";
	}
	o << std::endl;
}

void ScriptApiBase::setOriginDirect(const char *origin)
{
	m_last_run_mod = origin ? origin : "??";
}

void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn)
{
#ifdef SCRIPTAPI_DEBUG
	lua_State *L = getStack();

	m_last_run_mod = lua_istable(L, index) ?
		getstringfield_default(L, index, "mod_origin", "") : "";
	//printf(">>>> running %s for mod: %s\n", fxn, m_last_run_mod.c_str());
#endif
}

void ScriptApiBase::addObjectReference(ServerActiveObject *cobj)
{
	SCRIPTAPI_PRECHECKHEADER
	//infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;

	// Create object on stack
	ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
	int object = lua_gettop(L);

	// Get core.object_refs table
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "object_refs");
	luaL_checktype(L, -1, LUA_TTABLE);
	int objectstable = lua_gettop(L);

	// object_refs[id] = object
	lua_pushnumber(L, cobj->getId()); // Push id
	lua_pushvalue(L, object); // Copy object to top of stack
	lua_settable(L, objectstable);
}

void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj)
{
	SCRIPTAPI_PRECHECKHEADER
	//infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;

	// Get core.object_refs table
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "object_refs");
	luaL_checktype(L, -1, LUA_TTABLE);
	int objectstable = lua_gettop(L);

	// Get object_refs[id]
	lua_pushnumber(L, cobj->getId()); // Push id
	lua_gettable(L, objectstable);
	// Set object reference to NULL
	ObjectRef::set_null(L);
	lua_pop(L, 1); // pop object

	// Set object_refs[id] = nil
	lua_pushnumber(L, cobj->getId()); // Push id
	lua_pushnil(L);
	lua_settable(L, objectstable);
}

// Creates a new anonymous reference if cobj=NULL or id=0
void ScriptApiBase::objectrefGetOrCreate(lua_State *L,
		ServerActiveObject *cobj)
{
	if (cobj == NULL || cobj->getId() == 0) {
		ObjectRef::create(L, cobj);
	} else {
		objectrefGet(L, cobj->getId());
	}
}

void ScriptApiBase::objectrefGet(lua_State *L, u16 id)
{
	// 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
}