code:
/*
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_player.h"
#include "cpp_api/s_internal.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "util/string.h"

void ScriptApiPlayer::on_newplayer(ServerActiveObject *player)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_newplayers
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_newplayers");
	// Call callbacks
	objectrefGetOrCreate(L, player);
	runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}

void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_dieplayers
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_dieplayers");
	// Call callbacks
	objectrefGetOrCreate(L, player);
	runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}

bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player,
		ServerActiveObject *hitter,
		float time_from_last_punch,
		const ToolCapabilities *toolcap,
		v3f dir,
		s16 damage)
{
	SCRIPTAPI_PRECHECKHEADER
	// Get core.registered_on_punchplayers
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_punchplayers");
	// Call callbacks
	objectrefGetOrCreate(L, player);
	objectrefGetOrCreate(L, hitter);
	lua_pushnumber(L, time_from_last_punch);
	push_tool_capabilities(L, *toolcap);
	push_v3f(L, dir);
	lua_pushnumber(L, damage);
	runCallbacks(6, RUN_CALLBACKS_MODE_OR);
	return lua_toboolean(L, -1);
}

s16 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player,
	s16 hp_change)
{
	SCRIPTAPI_PRECHECKHEADER

	int error_handler = PUSH_ERROR_HANDLER(L);

	// Get core.registered_on_player_hpchange
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_player_hpchange");
	lua_remove(L, -2);

	objectrefGetOrCreate(L, player);
	lua_pushnumber(L, hp_change);
	PCALL_RES(lua_pcall(L, 2, 1, error_handler));
	hp_change = lua_tointeger(L, -1);
	lua_pop(L, 2); // Pop result and error handler
	return hp_change;
}

bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_respawnplayers
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_respawnplayers");
	// Call callbacks
	objectrefGetOrCreate(L, player);
	runCallbacks(1, RUN_CALLBACKS_MODE_OR);
	bool positioning_handled_by_some = lua_toboolean(L, -1);
	return positioning_handled_by_some;
}

bool ScriptApiPlayer::on_prejoinplayer(
	const std::string &name,
	const std::string &ip,
	std::string *reason)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_prejoinplayers
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_prejoinplayers");
	lua_pushstring(L, name.c_str());
	lua_pushstring(L, ip.c_str());
	runCallbacks(2, RUN_CALLBACKS_MODE_OR);
	if (lua_isstring(L, -1)) {
		reason->assign(lua_tostring(L, -1));
		return true;
	}
	return false;
}

void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_joinplayers
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_joinplayers");
	// Call callbacks
	objectrefGetOrCreate(L, player);
	runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}

void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player,
		bool timeout)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_leaveplayers
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_leaveplayers");
	// Call callbacks
	objectrefGetOrCreate(L, player);
	lua_pushboolean(L, timeout);
	runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}

void ScriptApiPlayer::on_cheat(ServerActiveObject *player,
		const std::string &cheat_type)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_cheats
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_cheats");
	// Call callbacks
	objectrefGetOrCreate(L, player);
	lua_newtable(L);
	lua_pushlstring(L, cheat_type.c_str(), cheat_type.size());
	lua_setfield(L, -2, "type");
	runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}

void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player,
		const std::string &formname,
		const StringMap &fields)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_chat_messages
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_player_receive_fields");
	// Call callbacks
	// param 1
	objectrefGetOrCreate(L, player);
	// param 2
	lua_pushstring(L, formname.c_str());
	// param 3
	lua_newtable(L);
	StringMap::const_iterator it;
	for (it = fields.begin(); it != fields.end(); ++it) {
		const std::string &name = it->first;
		const std::string &value = it->second;
		lua_pushstring(L, name.c_str());
		lua_pushlstring(L, value.c_str(), value.size());
		lua_settable(L, -3);
	}
	runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC);
}

ScriptApiPlayer::~ScriptApiPlayer()
{
}


code:
/*
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.
*/

#ifndef S_PLAYER_H_
#define S_PLAYER_H_

#include "cpp_api/s_base.h"
#include "irr_v3d.h"
#include "util/string.h"

struct ToolCapabilities;

class ScriptApiPlayer : virtual public ScriptApiBase
{
public:
	virtual ~ScriptApiPlayer();

	void on_newplayer(ServerActiveObject *player);
	void on_dieplayer(ServerActiveObject *player);
	bool on_respawnplayer(ServerActiveObject *player);
	bool on_prejoinplayer(const std::string &name, const std::string &ip,
			std::string *reason);
	void on_joinplayer(ServerActiveObject *player);
	void on_leaveplayer(ServerActiveObject *player, bool timeout);
	void on_cheat(ServerActiveObject *player, const std::string &cheat_type);
	bool on_punchplayer(ServerActiveObject *player, ServerActiveObject *hitter,
			float time_from_last_punch, const ToolCapabilities *toolcap,
			v3f dir, s16 damage);
	s16 on_player_hpchange(ServerActiveObject *player, s16 hp_change);
	void on_playerReceiveFields(ServerActiveObject *player,
			const std::string &formname, const StringMap &fields);
};

#endif /* S_PLAYER_H_ */

repo_name: pgimeno/minetest

license: unknown


code:
/*
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_security.h"

#include "filesys.h"
#include "porting.h"
#include "server.h"
#include "settings.h"

#include <cerrno>
#include <string>
#include <iostream>

#define SECURE_API(lib, name) \
	lua_pushcfunction(L, sl_##lib##_##name); \
	lua_setfield(L, -2, #name);

static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int from=-2, int to=-1)
{
	if (from < 0) from = lua_gettop(L) + from + 1;
	if (to   < 0) to   = lua_gettop(L) + to   + 1;
	for (unsigned i = 0; i < (len / sizeof(list[0])); i++) {
		lua_getfield(L, from, list[i]);
		lua_setfield(L, to,   list[i]);
	}
}

// Pushes the original version of a library function on the stack, from the old version
static inline void push_original(lua_State *L, const char *lib, const char *func)
{
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
	lua_getfield(L, -1, lib);
	lua_remove(L, -2);  // Remove globals_backup
	lua_getfield(L, -1, func);
	lua_remove(L, -2);  // Remove lib
}

void ScriptApiSecurity::initializeSecurity()
{
	static const char *whitelist[] = {
		"assert",
		"core",
		"collectgarbage",
		"DIR_DELIM",
		"error",
		"getfenv",
		"getmetatable",
		"ipairs",
		"next",
		"pairs",
		"pcall",
		"print",
		"rawequal",
		"rawget",
		"rawset",
		"select",
		"setfenv",
		"setmetatable",
		"tonumber",
		"tostring",
		"type",
		"unpack",
		"_VERSION",
		"xpcall",
		// Completely safe libraries
		"coroutine",
		"string",
		"table",
		"math",
	};
	static const char *io_whitelist[] = {
		"close",
		"flush",
		"read",
		"type",
		"write",
	};
	static const char *os_whitelist[] = {
		"clock",
		"date",
		"difftime",
		"getenv",
		"setlocale",
		"time",
		"tmpname",
	};
	static const char *debug_whitelist[] = {
		"gethook",
		"traceback",
		"getinfo",
		"getmetatable",
		"setupvalue",
		"setmetatable",
		"upvalueid",
		"upvaluejoin",
		"sethook",
		"debug",
		"setlocal",
	};
	static const char *package_whitelist[] = {
		"config",
		"cpath",
		"path",
		"searchpath",
	};
#if USE_LUAJIT
	static const char *jit_whitelist[] = {
		"arch",
		"flush",
		"off",
		"on",
		"opt",
		"os",
		"status",
		"version",
		"version_num",
	};
#endif
	m_secure = true;

	lua_State *L = getStack();

	int old_globals = backupGlobals(L);

	// Copy safe base functions
	lua_getglobal(L, "_G");
	copy_safe(L, whitelist, sizeof(whitelist));

	// And replace unsafe ones
	SECURE_API(g, dofile);
	SECURE_API(g, load);
	SECURE_API(g, loadfile);
	SECURE_API(g, loadstring);
	SECURE_API(g, require);
	lua_pop(L, 1);

	// Copy safe IO functions
	lua_getfield(L, old_globals, "io");
	lua_newtable(L);
	copy_safe(L, io_whitelist, sizeof(io_whitelist));

	// And replace unsafe ones
	SECURE_API(io, open);
	SECURE_API(io, input);
	SECURE_API(io, output);
	SECURE_API(io, lines);

	lua_setglobal(L, "io");
	lua_pop(L, 1);  // Pop old IO

	// Copy safe OS functions
	lua_getfield(L, old_globals, "os");
	lua_newtable(L);
	copy_safe(L, os_whitelist, sizeof(os_whitelist));

	// And replace unsafe ones
	SECURE_API(os, remove);
	SECURE_API(os, rename);

	lua_setglobal(L, "os");
	lua_pop(L, 1);  // Pop old OS

	// Copy safe debug functions
	lua_getfield(L, old_globals, "debug");
	lua_newtable(L);
	copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
	lua_setglobal(L, "debug");
	lua_pop(L, 1);  // Pop old debug

	// Copy safe package fields
	lua_getfield(L, old_globals, "package");
	lua_newtable(L);
	copy_safe(L, package_whitelist, sizeof(package_whitelist));
	lua_setglobal(L, "package");
	lua_pop(L, 1);  // Pop old package

#if USE_LUAJIT
	// Copy safe jit functions, if they exist
	lua_getfield(L, -1, "jit");
	if (!lua_isnil(L, -1)) {
		lua_newtable(L);
		copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
		lua_setglobal(L, "jit");
	}
	lua_pop(L, 1);  // Pop old jit
#endif

	lua_pop(L, 1); // Pop globals_backup
}

void ScriptApiSecurity::initializeSecurityClient()
{
	static const char *whitelist[] = {
		"assert",
		"core",
		"collectgarbage",
		"DIR_DELIM",
		"error",
		"getfenv",
		"ipairs",
		"next",
		"pairs",
		"pcall",
		"print",
		"rawequal",
		"rawget",
		"rawset",
		"select",
		"setfenv",
		"setmetatable",
		"tonumber",
		"tostring",
		"type",
		"unpack",
		"_VERSION",
		"xpcall",
		// Completely safe libraries
		"coroutine",
		"string",
		"table",
		"math",
	};
	static const char *os_whitelist[] = {
		"clock",
		"date",
		"difftime",
		"time",
		"setlocale",
	};
	static const char *debug_whitelist[] = {
		"getinfo",
	};

#if USE_LUAJIT
	static const char *jit_whitelist[] = {
		"arch",
		"flush",
		"off",
		"on",
		"opt",
		"os",
		"status",
		"version",
		"version_num",
	};
#endif

	m_secure = true;

	lua_State *L = getStack();

	int old_globals = backupGlobals(L);

	// Copy safe base functions
	lua_getglobal(L, "_G");
	copy_safe(L, whitelist, sizeof(whitelist));

	// And replace unsafe ones
	SECURE_API(g, dofile);
	SECURE_API(g, loadstring);
	SECURE_API(g, require);
	lua_pop(L, 1);

	// Copy safe OS functions
	lua_getfield(L, old_globals, "os");
	lua_newtable(L);
	copy_safe(L, os_whitelist, sizeof(os_whitelist));
	lua_setglobal(L, "os");
	lua_pop(L, 1);  // Pop old OS

	// Copy safe debug functions
	lua_getfield(L, old_globals, "debug");
	lua_newtable(L);
	copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
	lua_setglobal(L, "debug");
	lua_pop(L, 1);  // Pop old debug

#if USE_LUAJIT
	// Copy safe jit functions, if they exist
	lua_getfield(L, -1, "jit");
	if (!lua_isnil(L, -1)) {
		lua_newtable(L);
		copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
		lua_setglobal(L, "jit");
	}
	lua_pop(L, 1);  // Pop old jit
#endif

	lua_pop(L, 1); // Pop globals_backup
}

int ScriptApiSecurity::backupGlobals(lua_State *L)
{
	// Backup globals to the registry
	lua_getglobal(L, "_G");
	lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);

	// Replace the global environment with an empty one
#if LUA_VERSION_NUM <= 501
	int is_main = lua_pushthread(L);  // Push the main thread
	FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
		"isn't the main Lua thread!");
#endif
	lua_newtable(L);  // Create new environment
	lua_pushvalue(L, -1);
	lua_setfield(L, -2, "_G");  // Set _G of new environment
#if LUA_VERSION_NUM >= 502  // Lua >= 5.2
	// Set the global environment
	lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
#else  // Lua <= 5.1
	// Set the environment of the main thread
	FATAL_ERROR_IF(!lua_setfenv(L, -2), "Security: Unable to set "
		"environment of the main Lua thread!");
	lua_pop(L, 1);  // Pop thread
#endif

	// Get old globals
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
	return lua_gettop(L);
}

bool ScriptApiSecurity::isSecure(lua_State *L)
{
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
	bool secure = !lua_isnil(L, -1);
	lua_pop(L, 1);
	return secure;
}

#define CHECK_FILE_ERR(ret, fp) \
	if (ret) { \
		lua_pushfstring(L, "%s: %s", path, strerror(errno)); \
		if (fp) std::fclose(fp); \
		return false; \
	}

bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path)
{
	FILE *fp;
	char *chunk_name;
	if (path == NULL) {
		fp = stdin;
		chunk_name = const_cast<char *>("=stdin");
	} else {
		fp = fopen(path, "rb");
		if (!fp) {
			lua_pushfstring(L, "%s: %s", path, strerror(errno));
			return false;
		}
		chunk_name = new char[strlen(path) + 2];
		chunk_name[0] = '@';
		chunk_name[1] = '\0';
		strcat(chunk_name, path);
	}

	size_t start = 0;
	int c = std::getc(fp);
	if (c == '#') {
		// Skip the first line
		while ((c = std::getc(fp)) != EOF && c != '\n');
		if (c == '\n') c = std::getc(fp);
		start = std::ftell(fp);
	}

	if (c == LUA_SIGNATURE[0]) {
		lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
		std::fclose(fp);
		if (path) {
			delete [] chunk_name;
		}
		return false;
	}

	// Read the file
	int ret = std::fseek(fp, 0, SEEK_END);
	if (ret) {
		lua_pushfstring(L, "%s: %s", path, strerror(errno));
		std::fclose(fp);
		if (path) {
			delete [] chunk_name;
		}
		return false;
	}

	size_t size = std::ftell(fp) - start;
	char *code = new char[size];
	ret = std::fseek(fp, start, SEEK_SET);
	if (ret) {
		lua_pushfstring(L, "%s: %s", path, strerror(errno));
		std::fclose(fp);
		delete [] code;
		if (path) {
			delete [] chunk_name;
		}
		return false;
	}

	size_t num_read = std::fread(code, 1, size, fp);
	if (path) {
		std::fclose(fp);
	}
	if (num_read != size) {
		lua_pushliteral(L, "Error reading file to load.");
		delete [] code;
		if (path) {
			delete [] chunk_name;
		}
		return false;
	}

	if (luaL_loadbuffer(L, code, size, chunk_name)) {
		delete [] code;
		return false;
	}

	delete [] code;

	if (path) {
		delete [] chunk_name;
	}
	return true;
}

bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
		bool write_required, bool *write_allowed)
{
	if (write_allowed)
		*write_allowed = false;

	std::string str;  // Transient

	std::string abs_path = fs::AbsolutePath(path);

	if (!abs_path.empty()) {
		// Don't allow accessing the settings file
		str = fs::AbsolutePath(g_settings_path);
		if (str == abs_path) return false;
	}

	// If we couldn't find the absolute path (path doesn't exist) then
	// try removing the last components until it works (to allow
	// non-existent files/folders for mkdir).
	std::string cur_path = path;
	std::string removed;
	while (abs_path.empty() && !cur_path.empty()) {
		std::string component;
		cur_path = fs::RemoveLastPathComponent(cur_path, &component);
		if (component == "..") {
			// Parent components can't be allowed or we could allow something like
			// /home/user/minetest/worlds/foo/noexist/../../../../../../etc/passwd.
			// If we have previous non-relative elements in the path we might be
			// able to remove them so that things like worlds/foo/noexist/../auth.txt
			// could be allowed, but those paths will be interpreted as nonexistent
			// by the operating system anyways.
			return false;
		}
		removed = component + (removed.empty() ? "" : DIR_DELIM + removed);
		abs_path = fs::AbsolutePath(cur_path);
	}
	if (abs_path.empty())
		return false;
	// Add the removed parts back so that you can't, eg, create a
	// directory in worldmods if worldmods doesn't exist.
	if (!removed.empty())
		abs_path += DIR_DELIM + removed;

	// Get server from registry
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
	ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
	lua_pop(L, 1);
	const IGameDef *gamedef = script->getGameDef();
	if (!gamedef)
		return false;

	// Get mod name
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
	if (lua_isstring(L, -1)) {
		std::string mod_name = lua_tostring(L, -1);

		// Builtin can access anything
		if (mod_name == BUILTIN_MOD_NAME) {
			if (write_allowed) *write_allowed = true;
			return true;
		}

		// Allow paths in mod path
		// Don't bother if write access isn't important, since it will be handled later
		if (write_required || write_allowed != NULL) {
			const ModSpec *mod = gamedef->getModSpec(mod_name);
			if (mod) {
				str = fs::AbsolutePath(mod->path);
				if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
					if (write_allowed) *write_allowed = true;
					return true;
				}
			}
		}
	}
	lua_pop(L, 1);  // Pop mod name

	// Allow read-only access to all mod directories
	if (!write_required) {
		const std::vector<ModSpec> mods = gamedef->getMods();
		for (size_t i = 0; i < mods.size(); ++i) {
			str = fs::AbsolutePath(mods[i].path);
			if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
				return true;
			}
		}
	}

	str = fs::AbsolutePath(gamedef->getWorldPath());
	if (!str.empty()) {
		// Don't allow access to other paths in the world mod/game path.
		// These have to be blocked so you can't override a trusted mod
		// by creating a mod with the same name in a world mod directory.
		// We add to the absolute path of the world instead of getting
		// the absolute paths directly because that won't work if they
		// don't exist.
		if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") ||
				fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) {
			return false;
		}
		// Allow all other paths in world path
		if (fs::PathStartsWith(abs_path, str)) {
			if (write_allowed) *write_allowed = true;
			return true;
		}
	}

	// Default to disallowing
	return false;
}

int ScriptApiSecurity::sl_g_dofile(lua_State *L)
{
	int nret = sl_g_loadfile(L);
	if (nret != 1) {
		lua_error(L);
		// code after this function isn't executed
	}
	int top_precall = lua_gettop(L);
	lua_call(L, 0, LUA_MULTRET);
	// Return number of arguments returned by the function,
	// adjusting for the function being poped.
	return lua_gettop(L) - (top_precall - 1);
}

int ScriptApiSecurity::sl_g_load(lua_State *L)
{
	size_t len;
	const char *buf;
	std::string code;
	const char *chunk_name = "=(load)";

	luaL_checktype(L, 1, LUA_TFUNCTION);
	if (!lua_isnone(L, 2)) {
		luaL_checktype(L, 2, LUA_TSTRING);
		chunk_name = lua_tostring(L, 2);
	}

	while (true) {
		lua_pushvalue(L, 1);
		lua_call(L, 0, 1);
		int t = lua_type(L, -1);
		if (t == LUA_TNIL) {
			break;
		} else if (t != LUA_TSTRING) {
			lua_pushnil(L);
			lua_pushliteral(L, "Loader didn't return a string");
			return 2;
		}
		buf = lua_tolstring(L, -1, &len);
		code += std::string(buf, len);
		lua_pop(L, 1); // Pop return value
	}
	if (code[0] == LUA_SIGNATURE[0]) {
		lua_pushnil(L);
		lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
		return 2;
	}
	if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) {
		lua_pushnil(L);
		lua_insert(L, lua_gettop(L) - 1);
		return 2;
	}
	return 1;
}

int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
{
	const char *path = NULL;

	if (lua_isstring(L, 1)) {
		path = lua_tostring(L, 1);
		CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
	}

	if (!safeLoadFile(L, path)) {
		lua_pushnil(L);
		lua_insert(L, -2);
		return 2;
	}

	return 1;
}

int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
{
	const char *chunk_name = "=(load)";

	luaL_checktype(L, 1, LUA_TSTRING);
	if (!lua_isnone(L, 2)) {
		luaL_checktype(L, 2, LUA_TSTRING);
		chunk_name = lua_tostring(L, 2);
	}

	size_t size;
	const char *code = lua_tolstring(L, 1, &size);

	if (size > 0 && code[0] == LUA_SIGNATURE[0]) {
		lua_pushnil(L);
		lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
		return 2;
	}
	if (luaL_loadbuffer(L, code, size, chunk_name)) {
		lua_pushnil(L);
		lua_insert(L, lua_gettop(L) - 1);
		return 2;
	}
	return 1;
}

int ScriptApiSecurity::sl_g_require(lua_State *L)
{
	lua_pushliteral(L, "require() is disabled when mod security is on.");
	return lua_error(L);
}

int ScriptApiSecurity::sl_io_open(lua_State *L)
{
	bool with_mode = lua_gettop(L) > 1;

	luaL_checktype(L, 1, LUA_TSTRING);
	const char *path = lua_tostring(L, 1);

	bool write_requested = false;
	if (with_mode) {
		luaL_checktype(L, 2, LUA_TSTRING);
		const char *mode = lua_tostring(L, 2);
		write_requested = strchr(mode, 'w') != NULL ||
			strchr(mode, '+') != NULL ||
			strchr(mode, 'a') != NULL;
	}
	CHECK_SECURE_PATH_INTERNAL(L, path, write_requested, NULL);

	push_original(L, "io", "open");
	lua_pushvalue(L, 1);
	if (with_mode) {
		lua_pushvalue(L, 2);
	}

	lua_call(L, with_mode ? 2 : 1, 2);
	return 2;
}

int ScriptApiSecurity::sl_io_input(lua_State *L)
{
	if (lua_isstring(L, 1)) {
		const char *path = lua_tostring(L, 1);
		CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
	}

	push_original(L, "io", "input");
	lua_pushvalue(L, 1);
	lua_call(L, 1, 1);
	return 1;
}

int ScriptApiSecurity::sl_io_output(lua_State *L)
{
	if (lua_isstring(L, 1)) {
		const char *path = lua_tostring(L, 1);
		CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
	}

	push_original(L, "io", "output");
	lua_pushvalue(L, 1);
	lua_call(L, 1, 1);
	return 1;
}

int ScriptApiSecurity::sl_io_lines(lua_State *L)
{
	if (lua_isstring(L, 1)) {
		const char *path = lua_tostring(L, 1);
		CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
	}

	int top_precall = lua_gettop(L);
	push_original(L, "io", "lines");
	lua_pushvalue(L, 1);
	lua_call(L, 1, LUA_MULTRET);
	// Return number of arguments returned by the function,
	// adjusting for the function being poped.
	return lua_gettop(L) - top_precall;
}

int ScriptApiSecurity::sl_os_rename(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TSTRING);
	const char *path1 = lua_tostring(L, 1);
	CHECK_SECURE_PATH_INTERNAL(L, path1, true, NULL);

	luaL_checktype(L, 2, LUA_TSTRING);
	const char *path2 = lua_tostring(L, 2);
	CHECK_SECURE_PATH_INTERNAL(L, path2, true, NULL);

	push_original(L, "os", "rename");
	lua_pushvalue(L, 1);
	lua_pushvalue(L, 2);
	lua_call(L, 2, 2);
	return 2;
}

int ScriptApiSecurity::sl_os_remove(lua_State *L)
{
	luaL_checktype(L, 1, LUA_TSTRING);
	const char *path = lua_tostring(L, 1);
	CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);

	push_original(L, "os", "remove");
	lua_pushvalue(L, 1);
	lua_call(L, 1, 2);
	return 2;
}


code:
/*
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.
*/

#ifndef S_SECURITY_H
#define S_SECURITY_H

#include "cpp_api/s_base.h"

#define CHECK_SECURE_PATH_INTERNAL(L, path, write_required, ptr) \
	if (!ScriptApiSecurity::checkPath(L, path, write_required, ptr)) { \
		throw LuaError(std::string("Mod security: Blocked attempted ") + \
				(write_required ? "write to " : "read from ") + path); \
	}
#define CHECK_SECURE_PATH(L, path, write_required) \
	if (ScriptApiSecurity::isSecure(L)) { \
		CHECK_SECURE_PATH_INTERNAL(L, path, write_required, NULL); \
	}
#define CHECK_SECURE_PATH_POSSIBLE_WRITE(L, path, ptr) \
	if (ScriptApiSecurity::isSecure(L)) { \
		CHECK_SECURE_PATH_INTERNAL(L, path, false, ptr); \
	}

class ScriptApiSecurity : virtual public ScriptApiBase
{
public:
	int backupGlobals(lua_State *L);
	// Sets up security on the ScriptApi's Lua state
	void initializeSecurity();
	void initializeSecurityClient();
	// Checks if the Lua state has been secured
	static bool isSecure(lua_State *L);
	// Loads a file as Lua code safely (doesn't allow bytecode).
	static bool safeLoadFile(lua_State *L, const char *path);
	// Checks if mods are allowed to read (and optionally write) to the path
	static bool checkPath(lua_State *L, const char *path, bool write_required,
			bool *write_allowed=NULL);

private:
	// Syntax: "sl_" <Library name or 'g' (global)> '_' <Function name>
	// (sl stands for Secure Lua)

	static int sl_g_dofile(lua_State *L);
	static int sl_g_load(lua_State *L);
	static int sl_g_loadfile(lua_State *L);
	static int sl_g_loadstring(lua_State *L);
	static int sl_g_require(lua_State *L);

	static int sl_io_open(lua_State *L);
	static int sl_io_input(lua_State *L);
	static int sl_io_output(lua_State *L);
	static int sl_io_lines(lua_State *L);

	static int sl_os_rename(lua_State *L);
	static int sl_os_remove(lua_State *L);
};

#endif


code:
/*
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_server.h"
#include "cpp_api/s_internal.h"
#include "common/c_converter.h"

bool ScriptApiServer::getAuth(const std::string &playername,
		std::string *dst_password,
		std::set<std::string> *dst_privs)
{
	SCRIPTAPI_PRECHECKHEADER

	int error_handler = PUSH_ERROR_HANDLER(L);
	getAuthHandler();
	lua_getfield(L, -1, "get_auth");
	if (lua_type(L, -1) != LUA_TFUNCTION)
		throw LuaError("Authentication handler missing get_auth");
	lua_pushstring(L, playername.c_str());
	PCALL_RES(lua_pcall(L, 1, 1, error_handler));
	lua_remove(L, -2); // Remove auth handler
	lua_remove(L, error_handler);

	// nil = login not allowed
	if (lua_isnil(L, -1))
		return false;
	luaL_checktype(L, -1, LUA_TTABLE);

	std::string password;
	bool found = getstringfield(L, -1, "password", password);
	if (!found)
		throw LuaError("Authentication handler didn't return password");
	if (dst_password)
		*dst_password = password;

	lua_getfield(L, -1, "privileges");
	if (!lua_istable(L, -1))
		throw LuaError("Authentication handler didn't return privilege table");
	if (dst_privs)
		readPrivileges(-1, *dst_privs);
	lua_pop(L, 1);

	return true;
}

void ScriptApiServer::getAuthHandler()
{
	lua_State *L = getStack();

	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_auth_handler");
	if (lua_isnil(L, -1)){
		lua_pop(L, 1);
		lua_getfield(L, -1, "builtin_auth_handler");
	}

	setOriginFromTable(-1);

	lua_remove(L, -2); // Remove core
	if (lua_type(L, -1) != LUA_TTABLE)
		throw LuaError("Authentication handler table not valid");
}

void ScriptApiServer::readPrivileges(int index, std::set<std::string> &result)
{
	lua_State *L = getStack();

	result.clear();
	lua_pushnil(L);
	if (index < 0)
		index -= 1;
	while (lua_next(L, index) != 0) {
		// key at index -2 and value at index -1
		std::string key = luaL_checkstring(L, -2);
		bool value = lua_toboolean(L, -1);
		if (value)
			result.insert(key);
		// removes value, keeps key for next iteration
		lua_pop(L, 1);
	}
}

void ScriptApiServer::createAuth(const std::string &playername,
		const std::string &password)
{
	SCRIPTAPI_PRECHECKHEADER

	int error_handler = PUSH_ERROR_HANDLER(L);
	getAuthHandler();
	lua_getfield(L, -1, "create_auth");
	lua_remove(L, -2); // Remove auth handler
	if (lua_type(L, -1) != LUA_TFUNCTION)
		throw LuaError("Authentication handler missing create_auth");
	lua_pushstring(L, playername.c_str());
	lua_pushstring(L, password.c_str());
	PCALL_RES(lua_pcall(L, 2, 0, error_handler));
	lua_pop(L, 1); // Pop error handler
}

bool ScriptApiServer::setPassword(const std::string &playername,
		const std::string &password)
{
	SCRIPTAPI_PRECHECKHEADER

	int error_handler = PUSH_ERROR_HANDLER(L);
	getAuthHandler();
	lua_getfield(L, -1, "set_password");
	lua_remove(L, -2); // Remove auth handler
	if (lua_type(L, -1) != LUA_TFUNCTION)
		throw LuaError("Authentication handler missing set_password");
	lua_pushstring(L, playername.c_str());
	lua_pushstring(L, password.c_str());
	PCALL_RES(lua_pcall(L, 2, 1, error_handler));
	lua_remove(L, error_handler);
	return lua_toboolean(L, -1);
}

bool ScriptApiServer::on_chat_message(const std::string &name,
		const std::string &message)
{
	SCRIPTAPI_PRECHECKHEADER

	// Get core.registered_on_chat_messages
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_chat_messages");
	// Call callbacks
	lua_pushstring(L, name.c_str());
	lua_pushstring(L, message.c_str());
	runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC);
	bool ate = lua_toboolean(L, -1);
	return ate;
}

void ScriptApiServer::on_shutdown()
{
	SCRIPTAPI_PRECHECKHEADER

	// Get registered shutdown hooks
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_on_shutdown");
	// Call callbacks
	runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}


code:
/*
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.
*/

#ifndef S_SERVER_H_
#define S_SERVER_H_

#include "cpp_api/s_base.h"
#include <set>

class ScriptApiServer
		: virtual public ScriptApiBase
{
public:
	// Calls on_chat_message handlers
	// Returns true if script handled message
	bool on_chat_message(const std::string &name, const std::string &message);

	// Calls on_shutdown handlers
	void on_shutdown();

	/* auth */
	bool getAuth(const std::string &playername,
			std::string *dst_password,
			std::set<std::string> *dst_privs);
	void createAuth(const std::string &playername,
			const std::string &password);
	bool setPassword(const std::string &playername,
			const std::string &password);
private:
	void getAuthHandler();
	void readPrivileges(int index, std::set<std::string> &result);
};

#endif /* S_SERVER_H_ */


code:
set(common_SCRIPT_LUA_API_SRCS
	${CMAKE_CURRENT_SOURCE_DIR}/l_areastore.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_base.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_craft.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_itemstackmeta.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_mapgen.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_metadata.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_nodemeta.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_nodetimer.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_noise.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_object.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_particles.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_rollback.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_server.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_util.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_vmanip.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_http.cpp
	PARENT_SCOPE)

set(client_SCRIPT_LUA_API_SRCS
	${CMAKE_CURRENT_SOURCE_DIR}/l_client.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_minimap.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_sound.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_localplayer.cpp
	${CMAKE_CURRENT_SOURCE_DIR}/l_camera.cpp
	PARENT_SCOPE)


code:
/*
Minetest
Copyright (C) 2015 est31 <mtest31@outlook.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 "lua_api/l_areastore.h"
#include "lua_api/l_internal.h"
#include "common/c_converter.h"
#include "cpp_api/s_security.h"
#include "irr_v3d.h"
#include "util/areastore.h"
#include "filesys.h"
#include <fstream>

static inline void get_data_and_border_flags(lua_State *L, u8 start_i,
		bool *borders, bool *data)
{
	if (!lua_isboolean(L, start_i))
		return;
	*borders = lua_toboolean(L, start_i);
	if (!lua_isboolean(L, start_i + 1))
		return;
	*data = lua_toboolean(L, start_i + 1);
}

static void push_area(lua_State *L, const Area *a,
		bool include_borders, bool include_data)
{
	if (!include_borders && !include_data) {
		lua_pushboolean(L, true);
		return;
	}
	lua_newtable(L);
	if (include_borders) {
		push_v3s16(L, a->minedge);
		lua_setfield(L, -2, "min");
		push_v3s16(L, a->maxedge);
		lua_setfield(L, -2, "max");
	}
	if (include_data) {
		lua_pushlstring(L, a->data.c_str(), a->data.size());
		lua_setfield(L, -2, "data");
	}
}

static inline void push_areas(lua_State *L, const std::vector<Area *> &areas,
		bool borders, bool data)
{
	lua_newtable(L);
	size_t cnt = areas.size();
	for (size_t i = 0; i < cnt; i++) {
		lua_pushnumber(L, areas[i]->id);
		push_area(L, areas[i], borders, data);
		lua_settable(L, -3);
	}
}

// Deserializes value and handles errors
static int deserialization_helper(lua_State *L, AreaStore *as,
		std::istream &is)
{
	try {
		as->deserialize(is);
	} catch (const SerializationError &e) {
		lua_pushboolean(L, false);
		lua_pushstring(L, e.what());
		return 2;
	}

	lua_pushboolean(L, true);
	return 1;
}

// garbage collector
int LuaAreaStore::gc_object(lua_State *L)
{
	LuaAreaStore *o = *(LuaAreaStore **)(lua_touserdata(L, 1));
	delete o;
	return 0;
}

// get_area(id, include_borders, include_data)
int LuaAreaStore::l_get_area(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);
	AreaStore *ast = o->as;

	u32 id = luaL_checknumber(L, 2);

	bool include_borders = true;
	bool include_data = false;
	get_data_and_border_flags(L, 3, &include_borders, &include_data);

	const Area *res;

	res = ast->getArea(id);
	if (!res)
		return 0;

	push_area(L, res, include_borders, include_data);

	return 1;
}

// get_areas_for_pos(pos, include_borders, include_data)
int LuaAreaStore::l_get_areas_for_pos(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);
	AreaStore *ast = o->as;

	v3s16 pos = check_v3s16(L, 2);

	bool include_borders = true;
	bool include_data = false;
	get_data_and_border_flags(L, 3, &include_borders, &include_data);

	std::vector<Area *> res;

	ast->getAreasForPos(&res, pos);
	push_areas(L, res, include_borders, include_data);

	return 1;
}

// get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data)
int LuaAreaStore::l_get_areas_in_area(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);
	AreaStore *ast = o->as;

	v3s16 minedge = check_v3s16(L, 2);
	v3s16 maxedge = check_v3s16(L, 3);

	bool include_borders = true;
	bool include_data = false;
	bool accept_overlap = false;
	if (lua_isboolean(L, 4)) {
		accept_overlap = lua_toboolean(L, 4);
		get_data_and_border_flags(L, 5, &include_borders, &include_data);
	}
	std::vector<Area *> res;

	ast->getAreasInArea(&res, minedge, maxedge, accept_overlap);
	push_areas(L, res, include_borders, include_data);

	return 1;
}

// insert_area(edge1, edge2, data, id)
int LuaAreaStore::l_insert_area(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);
	AreaStore *ast = o->as;

	Area a(check_v3s16(L, 2), check_v3s16(L, 3));

	size_t d_len;
	const char *data = luaL_checklstring(L, 4, &d_len);

	a.data = std::string(data, d_len);

	if (lua_isnumber(L, 5))
		a.id = lua_tonumber(L, 5);

	if (!ast->insertArea(&a))
		return 0;

	lua_pushnumber(L, a.id);
	return 1;
}

// reserve(count)
int LuaAreaStore::l_reserve(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);
	AreaStore *ast = o->as;

	size_t count = luaL_checknumber(L, 2);
	ast->reserve(count);
	return 0;
}

// remove_area(id)
int LuaAreaStore::l_remove_area(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);
	AreaStore *ast = o->as;

	u32 id = luaL_checknumber(L, 2);
	bool success = ast->removeArea(id);

	lua_pushboolean(L, success);
	return 1;
}

// set_cache_params(params)
int LuaAreaStore::l_set_cache_params(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);
	AreaStore *ast = o->as;

	luaL_checktype(L, 2, LUA_TTABLE);

	bool enabled = getboolfield_default(L, 2, "enabled", true);
	u8 block_radius = getintfield_default(L, 2, "block_radius", 64);
	size_t limit = getintfield_default(L, 2, "block_radius", 1000);

	ast->setCacheParams(enabled, block_radius, limit);

	return 0;
}

// to_string()
int LuaAreaStore::l_to_string(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);

	std::ostringstream os(std::ios_base::binary);
	o->as->serialize(os);
	std::string str = os.str();

	lua_pushlstring(L, str.c_str(), str.length());
	return 1;
}

// to_file(filename)
int LuaAreaStore::l_to_file(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);
	AreaStore *ast = o->as;

	const char *filename = luaL_checkstring(L, 2);
	CHECK_SECURE_PATH(L, filename, true);

	std::ostringstream os(std::ios_base::binary);
	ast->serialize(os);

	lua_pushboolean(L, fs::safeWriteToFile(filename, os.str()));
	return 1;
}

// from_string(str)
int LuaAreaStore::l_from_string(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);

	size_t len;
	const char *str = luaL_checklstring(L, 2, &len);

	std::istringstream is(std::string(str, len), std::ios::binary);
	return deserialization_helper(L, o->as, is);
}

// from_file(filename)
int LuaAreaStore::l_from_file(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = checkobject(L, 1);

	const char *filename = luaL_checkstring(L, 2);
	CHECK_SECURE_PATH(L, filename, false);

	std::ifstream is(filename, std::ios::binary);
	return deserialization_helper(L, o->as, is);
}

LuaAreaStore::LuaAreaStore()
{
	this->as = AreaStore::getOptimalImplementation();
}

LuaAreaStore::LuaAreaStore(const std::string &type)
{
#if USE_SPATIAL
	if (type == "LibSpatial") {
		this->as = new SpatialAreaStore();
	} else
#endif
	{
		this->as = new VectorAreaStore();
	}
}

LuaAreaStore::~LuaAreaStore()
{
	delete as;
}

// LuaAreaStore()
// Creates an LuaAreaStore and leaves it on top of stack
int LuaAreaStore::create_object(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	LuaAreaStore *o = (lua_isstring(L, 1)) ?
		new LuaAreaStore(lua_tostring(L, 1)) :
		new LuaAreaStore();

	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
	luaL_getmetatable(L, className);
	lua_setmetatable(L, -2);
	return 1;
}

LuaAreaStore *LuaAreaStore::checkobject(lua_State *L, int narg)
{
	NO_MAP_LOCK_REQUIRED;

	luaL_checktype(L, narg, LUA_TUSERDATA);

	void *ud = luaL_checkudata(L, narg, className);
	if (!ud)
		luaL_typerror(L, narg, className);

	return *(LuaAreaStore **)ud;  // unbox pointer
}

void LuaAreaStore::Register(lua_State *L)
{
	lua_newtable(L);
	int methodtable = lua_gettop(L);
	luaL_newmetatable(L, className);
	int metatable = lua_gettop(L);

	lua_pushliteral(L, "__metatable");
	lua_pushvalue(L, methodtable);
	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()

	lua_pushliteral(L, "__index");
	lua_pushvalue(L, methodtable);
	lua_settable(L, metatable);

	lua_pushliteral(L, "__gc");
	lua_pushcfunction(L, gc_object);
	lua_settable(L, metatable);

	lua_pop(L, 1);  // drop metatable

	luaL_openlib(L, 0, methods, 0);  // fill methodtable
	lua_pop(L, 1);  // drop methodtable

	// Can be created from Lua (AreaStore())
	lua_register(L, className, create_object);
}

const char LuaAreaStore::className[] = "AreaStore";
const luaL_Reg LuaAreaStore::methods[] = {
	luamethod(LuaAreaStore, get_area),
	luamethod(LuaAreaStore, get_areas_for_pos),
	luamethod(LuaAreaStore, get_areas_in_area),
	luamethod(LuaAreaStore, insert_area),
	luamethod(LuaAreaStore, reserve),
	luamethod(LuaAreaStore, remove_area),
	luamethod(LuaAreaStore, set_cache_params),
	luamethod(LuaAreaStore, to_string),
	luamethod(LuaAreaStore, to_file),
	luamethod(LuaAreaStore, from_string),
	luamethod(LuaAreaStore, from_file),
	{0,0}
};


code:
/*
Minetest
Copyright (C) 2015 est31 <mtest31@outlook.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.
*/

#ifndef L_AREA_STORE_H_
#define L_AREA_STORE_H_

#include "lua_api/l_base.h"

class AreaStore;

class LuaAreaStore : public ModApiBase
{
private:
	static const char className[];
	static const luaL_Reg methods[];

	static int gc_object(lua_State *L);

	static int l_get_area(lua_State *L);

	static int l_get_areas_for_pos(lua_State *L);
	static int l_get_areas_in_area(lua_State *L);
	static int l_insert_area(lua_State *L);
	static int l_reserve(lua_State *L);
	static int l_remove_area(lua_State *L);

	static int l_set_cache_params(lua_State *L);

	static int l_to_string(lua_State *L);
	static int l_to_file(lua_State *L);

	static int l_from_string(lua_State *L);
	static int l_from_file(lua_State *L);

public:
	AreaStore *as;

	LuaAreaStore();
	LuaAreaStore(const std::string &type);
	~LuaAreaStore();

	// AreaStore()
	// Creates a AreaStore and leaves it on top of stack
	static int create_object(lua_State *L);

	static LuaAreaStore *checkobject(lua_State *L, int narg);

	static void Register(lua_State *L);
};

#endif // L_AREA_STORE_H_


code:
/*
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 "lua_api/l_base.h"
#include "lua_api/l_internal.h"
#include "cpp_api/s_base.h"
#include <mods.h>
#include <server.h>

ScriptApiBase *ModApiBase::getScriptApiBase(lua_State *L)
{
	// Get server from registry
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
	ScriptApiBase *sapi_ptr = (ScriptApiBase*) lua_touserdata(L, -1);
	lua_pop(L, 1);
	return sapi_ptr;
}

Server *ModApiBase::getServer(lua_State *L)
{
	return getScriptApiBase(L)->getServer();
}

#ifndef SERVER
Client *ModApiBase::getClient(lua_State *L)
{
	return getScriptApiBase(L)->getClient();
}
#endif

IGameDef *ModApiBase::getGameDef(lua_State *L)
{
	return getScriptApiBase(L)->getGameDef();
}

Environment *ModApiBase::getEnv(lua_State *L)
{
	return getScriptApiBase(L)->getEnv();
}

GUIEngine *ModApiBase::getGuiEngine(lua_State *L)
{
	return getScriptApiBase(L)->getGuiEngine();
}

std::string ModApiBase::getCurrentModPath(lua_State *L)
{
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
	const char *current_mod_name = lua_tostring(L, -1);
	if (!current_mod_name)
		return ".";

	const ModSpec *mod = getServer(L)->getModSpec(current_mod_name);
	if (!mod)
		return ".";

	return mod->path;
}

bool ModApiBase::registerFunction(lua_State *L, const char *name,
		lua_CFunction func, int top)
{
	// TODO: Check presence first!

	lua_pushcfunction(L, func);
	lua_setfield(L, top, name);

	return true;
}


code:
/*
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.
*/

#ifndef L_BASE_H_
#define L_BASE_H_

#include "common/c_types.h"
#include "common/c_internal.h"
#include "gamedef.h"

extern "C" {
#include <lua.h>
#include <lauxlib.h>
}

#ifndef SERVER
#include "client.h"
#endif

class ScriptApiBase;
class Server;
class Environment;
class GUIEngine;

class ModApiBase {

public:
	static ScriptApiBase*   getScriptApiBase(lua_State *L);
	static Server*          getServer(lua_State *L);
	#ifndef SERVER
	static Client*          getClient(lua_State *L);
	#endif // !SERVER

	static IGameDef*        getGameDef(lua_State *L);

	static Environment*     getEnv(lua_State *L);
	static GUIEngine*       getGuiEngine(lua_State *L);
	// When we are not loading the mod, this function returns "."
	static std::string      getCurrentModPath(lua_State *L);

	// Get an arbitrary subclass of ScriptApiBase
	// by using dynamic_cast<> on getScriptApiBase()
	template<typename T>
	static T* getScriptApi(lua_State *L) {
		ScriptApiBase *scriptIface = getScriptApiBase(L);
		T *scriptIfaceDowncast = dynamic_cast<T*>(scriptIface);
		if (!scriptIfaceDowncast) {
			throw LuaError("Requested unavailable ScriptApi - core engine bug!");
		}
		return scriptIfaceDowncast;
	}

	static bool registerFunction(lua_State *L,
			const char* name,
			lua_CFunction func,
			int top);
};

#endif /* L_BASE_H_ */


code:
#include "script/common/c_converter.h"
#include "l_camera.h"
#include "l_internal.h"
#include "content_cao.h"
#include "camera.h"

LuaCamera::LuaCamera(Camera *m)
{
	m_camera = m;
}

void LuaCamera::create(lua_State *L, Camera *m)
{
	LuaCamera *o = new LuaCamera(m);
	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
	luaL_getmetatable(L, className);
	lua_setmetatable(L, -2);

	int camera_object = lua_gettop(L);

	lua_getglobal(L, "core");
	luaL_checktype(L, -1, LUA_TTABLE);
	int coretable = lua_gettop(L);

	lua_pushvalue(L, camera_object);
	lua_setfield(L, coretable, "camera");
}

int LuaCamera::l_set_camera_mode(lua_State *L)
{
	Camera *camera = getobject(L, 1);
	GenericCAO *playercao = getClient(L)->getEnv().getLocalPlayer()->getCAO();
	if (!camera)
		return 0;
	sanity_check(playercao);
	if (!lua_isnumber(L, 2))
		return 0;

	camera->setCameraMode((CameraMode)((int)lua_tonumber(L, 2)));
	playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
	playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
	return 0;
}

int LuaCamera::l_get_camera_mode(lua_State *L)
{
	Camera *camera = getobject(L, 1);
	if (!camera)
		return 0;

	lua_pushnumber(L, (int)camera->getCameraMode());

	return 1;
}

int LuaCamera::l_get_fov(lua_State *L)
{
	Camera *camera = getobject(L, 1);
	if (!camera)
		return 0;

	lua_newtable(L);
	lua_pushnumber(L, camera->getFovX() * core::DEGTORAD);
	lua_setfield(L, -2, "x");
	lua_pushnumber(L, camera->getFovY() * core::DEGTORAD);
	lua_setfield(L, -2, "y");
	lua_pushnumber(L, camera->getCameraNode()->getFOV() * core::RADTODEG);
	lua_setfield(L, -2, "actual");
	lua_pushnumber(L, camera->getFovMax() * core::RADTODEG);
	lua_setfield(L, -2, "max");
	return 1;
}

int LuaCamera::l_get_pos(lua_State *L)
{
	Camera *camera = getobject(L, 1);
	if (!camera)
		return 0;

	push_v3f(L, camera->getPosition());
	return 1;
}

int LuaCamera::l_get_offset(lua_State *L)
{
	Camera *camera = getobject(L, 1);
	if (!camera)
		return 0;

	push_v3s16(L, camera->getOffset());
	return 1;
}

int LuaCamera::l_get_look_dir(lua_State *L)
{
	LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer();
	sanity_check(player);

	float pitch = -1.0 * player->getPitch() * core::DEGTORAD;
	float yaw = (player->getYaw() + 90.) * core::DEGTORAD;
	v3f v(cos(pitch) * cos(yaw), sin(pitch), cos(pitch) * sin(yaw));

	push_v3f(L, v);
	return 1;
}

int LuaCamera::l_get_look_horizontal(lua_State *L)
{
	LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer();
	sanity_check(player);

	lua_pushnumber(L, (player->getYaw() + 90.) * core::DEGTORAD);
	return 1;
}

int LuaCamera::l_get_look_vertical(lua_State *L)
{
	LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer();
	sanity_check(player);

	lua_pushnumber(L, -1.0 * player->getPitch() * core::DEGTORAD);
	return 1;
}

int LuaCamera::l_get_aspect_ratio(lua_State *L)
{
	Camera *camera = getobject(L, 1);
	if (!camera)
		return 0;

	lua_pushnumber(L, camera->getCameraNode()->getAspectRatio());
	return 1;
}

LuaCamera *LuaCamera::checkobject(lua_State *L, int narg)
{
	luaL_checktype(L, narg, LUA_TUSERDATA);

	void *ud = luaL_checkudata(L, narg, className);
	if (!ud)
		luaL_typerror(L, narg, className);

	return *(LuaCamera **)ud;
}

Camera *LuaCamera::getobject(LuaCamera *ref)
{
	return ref->m_camera;
}

Camera *LuaCamera::getobject(lua_State *L, int narg)
{
	LuaCamera *ref = checkobject(L, narg);
	assert(ref);
	Camera *camera = getobject(ref);
	if (!camera)
		return NULL;
	return camera;
}

int LuaCamera::gc_object(lua_State *L)
{
	LuaCamera *o = *(LuaCamera **)(lua_touserdata(L, 1));
	delete o;
	return 0;
}

void LuaCamera::Register(lua_State *L)
{
	lua_newtable(L);
	int methodtable = lua_gettop(L);
	luaL_newmetatable(L, className);
	int metatable = lua_gettop(L);

	lua_pushliteral(L, "__metatable");
	lua_pushvalue(L, methodtable);
	lua_settable(L, metatable);

	lua_pushliteral(L, "__index");
	lua_pushvalue(L, methodtable);
	lua_settable(L, metatable);

	lua_pushliteral(L, "__gc");
	lua_pushcfunction(L, gc_object);
	lua_settable(L, metatable);

	lua_pop(L, 1);

	luaL_openlib(L, 0, methods, 0);
	lua_pop(L, 1);
}

const char LuaCamera::className[] = "Camera";
const luaL_Reg LuaCamera::methods[] = {luamethod(LuaCamera, set_camera_mode),
		luamethod(LuaCamera, get_camera_mode), luamethod(LuaCamera, get_fov),
		luamethod(LuaCamera, get_pos), luamethod(LuaCamera, get_offset),
		luamethod(LuaCamera, get_look_dir),
		luamethod(LuaCamera, get_look_vertical),
		luamethod(LuaCamera, get_look_horizontal),
		luamethod(LuaCamera, get_aspect_ratio),

		{0, 0}};


code:
#ifndef L_CAMERA_H
#define L_CAMERA_H

#include "l_base.h"

class Camera;

class LuaCamera : public ModApiBase
{
private:
	static const char className[];
	static const luaL_Reg methods[];

	// garbage collector
	static int gc_object(lua_State *L);

	static int l_set_camera_mode(lua_State *L);
	static int l_get_camera_mode(lua_State *L);

	static int l_get_fov(lua_State *L);

	static int l_get_pos(lua_State *L);
	static int l_get_offset(lua_State *L);
	static int l_get_look_dir(lua_State *L);
	static int l_get_look_vertical(lua_State *L);
	static int l_get_look_horizontal(lua_State *L);
	static int l_get_aspect_ratio(lua_State *L);

	Camera *m_camera;

public:
	LuaCamera(Camera *m);
	~LuaCamera() {}

	static void create(lua_State *L, Camera *m);

	static LuaCamera *checkobject(lua_State *L, int narg);
	static Camera *getobject(LuaCamera *ref);
	static Camera *getobject(lua_State *L, int narg);

	static void Register(lua_State *L);
};

#endif // L_CAMERA_H


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>

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 "l_client.h"
#include "clientenvironment.h"
#include "common/c_content.h"
#include "common/c_converter.h"
#include "cpp_api/s_base.h"
#include "gettext.h"
#include "l_internal.h"
#include "lua_api/l_item.h"
#include "lua_api/l_nodemeta.h"
#include "mainmenumanager.h"
#include "map.h"
#include "util/string.h"
#include "nodedef.h"

extern MainGameCallback *g_gamecallback;

int ModApiClient::l_get_current_modname(lua_State *L)
{
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
	return 1;
}

// get_last_run_mod()
int ModApiClient::l_get_last_run_mod(lua_State *L)
{
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
	const char *current_mod = lua_tostring(L, -1);
	if (current_mod == NULL || current_mod[0] == '\0') {
		lua_pop(L, 1);
		lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str());
	}
	return 1;
}

// set_last_run_mod(modname)
int ModApiClient::l_set_last_run_mod(lua_State *L)
{
	if (!lua_isstring(L, 1))
		return 0;

	const char *mod = lua_tostring(L, 1);
	getScriptApiBase(L)->setOriginDirect(mod);
	lua_pushboolean(L, true);
	return 1;
}

// print(text)
int ModApiClient::l_print(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::string text = luaL_checkstring(L, 1);
	rawstream << text << std::endl;
	return 0;
}

// display_chat_message(message)
int ModApiClient::l_display_chat_message(lua_State *L)
{
	if (!lua_isstring(L, 1))
		return 0;

	std::string message = luaL_checkstring(L, 1);
	getClient(L)->pushToChatQueue(utf8_to_wide(message));
	lua_pushboolean(L, true);
	return 1;
}

// send_chat_message(message)
int ModApiClient::l_send_chat_message(lua_State *L)
{
	if (!lua_isstring(L, 1))
		return 0;
	std::string message = luaL_checkstring(L, 1);
	getClient(L)->sendChatMessage(utf8_to_wide(message));
	return 0;
}

// clear_out_chat_queue()
int ModApiClient::l_clear_out_chat_queue(lua_State *L)
{
	getClient(L)->clearOutChatQueue();
	return 0;
}

// get_player_names()
int ModApiClient::l_get_player_names(lua_State *L)
{
	const std::list<std::string> &plist = getClient(L)->getConnectedPlayerNames();
	lua_createtable(L, plist.size(), 0);
	int newTable = lua_gettop(L);
	int index = 1;
	std::list<std::string>::const_iterator iter;
	for (iter = plist.begin(); iter != plist.end(); ++iter) {
		lua_pushstring(L, (*iter).c_str());
		lua_rawseti(L, newTable, index);
		index++;
	}
	return 1;
}

// show_formspec(formspec)
int ModApiClient::l_show_formspec(lua_State *L)
{
	if (!lua_isstring(L, 1) || !lua_isstring(L, 2))
		return 0;

	ClientEvent event;
	event.type = CE_SHOW_LOCAL_FORMSPEC;
	event.show_formspec.formname = new std::string(luaL_checkstring(L, 1));
	event.show_formspec.formspec = new std::string(luaL_checkstring(L, 2));
	getClient(L)->pushToEventQueue(event);
	lua_pushboolean(L, true);
	return 1;
}

// send_respawn()
int ModApiClient::l_send_respawn(lua_State *L)
{
	getClient(L)->sendRespawn();
	return 0;
}

// disconnect()
int ModApiClient::l_disconnect(lua_State *L)
{
	// Stops badly written Lua code form causing boot loops
	if (getClient(L)->isShutdown()) {
		lua_pushboolean(L, false);
		return 1;
	}

	g_gamecallback->disconnect();
	lua_pushboolean(L, true);
	return 1;
}

// gettext(text)
int ModApiClient::l_gettext(lua_State *L)
{
	std::string text = strgettext(std::string(luaL_checkstring(L, 1)));
	lua_pushstring(L, text.c_str());

	return 1;
}

// get_node(pos)
// pos = {x=num, y=num, z=num}
int ModApiClient::l_get_node(lua_State *L)
{
	// pos
	v3s16 pos = read_v3s16(L, 1);
	// Do it
	bool pos_ok;
	MapNode n = getClient(L)->getNode(pos, &pos_ok);
	// Return node
	pushnode(L, n, getClient(L)->ndef());
	return 1;
}

// get_node_or_nil(pos)
// pos = {x=num, y=num, z=num}
int ModApiClient::l_get_node_or_nil(lua_State *L)
{
	// pos
	v3s16 pos = read_v3s16(L, 1);
	// Do it
	bool pos_ok;
	MapNode n = getClient(L)->getNode(pos, &pos_ok);
	if (pos_ok) {
		// Return node
		pushnode(L, n, getClient(L)->ndef());
	} else {
		lua_pushnil(L);
	}
	return 1;
}

int ModApiClient::l_get_wielded_item(lua_State *L)
{
	Client *client = getClient(L);

	Inventory local_inventory(client->idef());
	client->getLocalInventory(local_inventory);

	InventoryList *mlist = local_inventory.getList("main");

	if (mlist && client->getPlayerItem() < mlist->getSize()) {
		LuaItemStack::create(L, mlist->getItem(client->getPlayerItem()));
	} else {
		LuaItemStack::create(L, ItemStack());
	}
	return 1;
}

// get_meta(pos)
int ModApiClient::l_get_meta(lua_State *L)
{
	v3s16 p = read_v3s16(L, 1);
	NodeMetadata *meta = getClient(L)->getEnv().getMap().getNodeMetadata(p);
	NodeMetaRef::createClient(L, meta);
	return 1;
}

int ModApiClient::l_sound_play(lua_State *L)
{
	ISoundManager *sound = getClient(L)->getSoundManager();

	SimpleSoundSpec spec;
	read_soundspec(L, 1, spec);
	float gain = 1.0;
	bool looped = false;
	s32 handle;

	if (lua_istable(L, 2)) {
		getfloatfield(L, 2, "gain", gain);
		getboolfield(L, 2, "loop", looped);

		lua_getfield(L, 2, "pos");
		if (!lua_isnil(L, -1)) {
			v3f pos = read_v3f(L, -1) * BS;
			lua_pop(L, 1);
			handle = sound->playSoundAt(
					spec.name, looped, gain * spec.gain, pos);
			lua_pushinteger(L, handle);
			return 1;
		}
	}

	handle = sound->playSound(spec.name, looped, gain * spec.gain);
	lua_pushinteger(L, handle);

	return 1;
}

int ModApiClient::l_sound_stop(lua_State *L)
{
	u32 handle = luaL_checkinteger(L, 1);

	getClient(L)->getSoundManager()->stopSound(handle);

	return 0;
}

// get_server_info()
int ModApiClient::l_get_server_info(lua_State *L)
{
	Client *client = getClient(L);
	Address serverAddress = client->getServerAddress();
	lua_newtable(L);
	lua_pushstring(L, client->getAddressName().c_str());
	lua_setfield(L, -2, "address");
	lua_pushstring(L, serverAddress.serializeString().c_str());
	lua_setfield(L, -2, "ip");
	lua_pushinteger(L, serverAddress.getPort());
	lua_setfield(L, -2, "port");
	lua_pushinteger(L, client->getProtoVersion());
	lua_setfield(L, -2, "protocol_version");
	return 1;
}

// get_item_def(itemstring)
int ModApiClient::l_get_item_def(lua_State *L)
{
	IGameDef *gdef = getGameDef(L);
	assert(gdef);

	IItemDefManager *idef = gdef->idef();
	assert(idef);

	if (!lua_isstring(L, 1))
		return 0;

	const std::string &name(lua_tostring(L, 1));
	if (!idef->isKnown(name))
		return 0;
	const ItemDefinition &def = idef->get(name);

	push_item_definition_full(L, def);

	return 1;
}

// get_node_def(nodename)
int ModApiClient::l_get_node_def(lua_State *L)
{
	IGameDef *gdef = getGameDef(L);
	assert(gdef);

	INodeDefManager *ndef = gdef->ndef();
	assert(ndef);

	if (!lua_isstring(L, 1))
		return 0;

	const std::string &name = lua_tostring(L, 1);
	const ContentFeatures &cf = ndef->get(ndef->getId(name));
	if (cf.name != name) // Unknown node. | name = <whatever>, cf.name = ignore
		return 0;

	push_content_features(L, cf);

	return 1;
}

int ModApiClient::l_take_screenshot(lua_State *L)
{
	Client *client = getClient(L);
	client->makeScreenshot(client->getDevice());
	return 0;
}

void ModApiClient::Initialize(lua_State *L, int top)
{
	API_FCT(get_current_modname);
	API_FCT(print);
	API_FCT(display_chat_message);
	API_FCT(send_chat_message);
	API_FCT(clear_out_chat_queue);
	API_FCT(get_player_names);
	API_FCT(set_last_run_mod);
	API_FCT(get_last_run_mod);
	API_FCT(show_formspec);
	API_FCT(send_respawn);
	API_FCT(gettext);
	API_FCT(get_node);
	API_FCT(get_node_or_nil);
	API_FCT(get_wielded_item);
	API_FCT(disconnect);
	API_FCT(get_meta);
	API_FCT(sound_play);
	API_FCT(sound_stop);
	API_FCT(get_server_info);
	API_FCT(get_item_def);
	API_FCT(get_node_def);
	API_FCT(take_screenshot);
}


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>

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.
*/

#ifndef L_CLIENT_H_
#define L_CLIENT_H_

#include "lua_api/l_base.h"
#include "itemdef.h"
#include "tool.h"

class ModApiClient : public ModApiBase
{
private:
	// get_current_modname()
	static int l_get_current_modname(lua_State *L);

	// print(text)
	static int l_print(lua_State *L);

	// display_chat_message(message)
	static int l_display_chat_message(lua_State *L);

	// send_chat_message(message)
	static int l_send_chat_message(lua_State *L);

	// clear_out_chat_queue()
	static int l_clear_out_chat_queue(lua_State *L);

	// get_player_names()
	static int l_get_player_names(lua_State *L);

	// show_formspec(name, formspec)
	static int l_show_formspec(lua_State *L);

	// send_respawn()
	static int l_send_respawn(lua_State *L);

	// disconnect()
	static int l_disconnect(lua_State *L);

	// gettext(text)
	static int l_gettext(lua_State *L);

	// get_last_run_mod(n)
	static int l_get_last_run_mod(lua_State *L);

	// set_last_run_mod(modname)
	static int l_set_last_run_mod(lua_State *L);

	// get_node(pos)
	static int l_get_node(lua_State *L);

	// get_node_or_nil(pos)
	static int l_get_node_or_nil(lua_State *L);

	// get_wielded_item()
	static int l_get_wielded_item(lua_State *L);

	// get_meta(pos)
	static int l_get_meta(lua_State *L);

	static int l_sound_play(lua_State *L);

	static int l_sound_stop(lua_State *L);

	// get_server_info()
	static int l_get_server_info(lua_State *L);

	// get_item_def(itemstring)
	static int l_get_item_def(lua_State *L);

	// get_node_def(nodename)
	static int l_get_node_def(lua_State *L);

	static int l_take_screenshot(lua_State *L);

public:
	static void Initialize(lua_State *L, int top);
};

#endif


code:
/*
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 "lua_api/l_craft.h"
#include "lua_api/l_internal.h"
#include "lua_api/l_item.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "server.h"
#include "craftdef.h"

struct EnumString ModApiCraft::es_CraftMethod[] =
{
	{CRAFT_METHOD_NORMAL, "normal"},
	{CRAFT_METHOD_COOKING, "cooking"},
	{CRAFT_METHOD_FUEL, "fuel"},
	{0, NULL},
};

// helper for register_craft
bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
		int &width, std::vector<std::string> &recipe)
{
	if(index < 0)
		index = lua_gettop(L) + 1 + index;

	if(!lua_istable(L, index))
		return false;

	lua_pushnil(L);
	int rowcount = 0;
	while(lua_next(L, index) != 0){
		int colcount = 0;
		// key at index -2 and value at index -1
		if(!lua_istable(L, -1))
			return false;
		int table2 = lua_gettop(L);
		lua_pushnil(L);
		while(lua_next(L, table2) != 0){
			// key at index -2 and value at index -1
			if(!lua_isstring(L, -1))
				return false;
			recipe.push_back(lua_tostring(L, -1));
			// removes value, keeps key for next iteration
			lua_pop(L, 1);
			colcount++;
		}
		if(rowcount == 0){
			width = colcount;
		} else {
			if(colcount != width)
				return false;
		}
		// removes value, keeps key for next iteration
		lua_pop(L, 1);
		rowcount++;
	}
	return width != 0;
}

// helper for register_craft
bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index,
		std::vector<std::string> &recipe)
{
	if(index < 0)
		index = lua_gettop(L) + 1 + index;

	if(!lua_istable(L, index))
		return false;

	lua_pushnil(L);
	while(lua_next(L, index) != 0){
		// key at index -2 and value at index -1
		if(!lua_isstring(L, -1))
			return false;
		recipe.push_back(lua_tostring(L, -1));
		// removes value, keeps key for next iteration
		lua_pop(L, 1);
	}
	return true;
}

// helper for register_craft
bool ModApiCraft::readCraftReplacements(lua_State *L, int index,
		CraftReplacements &replacements)
{
	if(index < 0)
		index = lua_gettop(L) + 1 + index;

	if(!lua_istable(L, index))
		return false;

	lua_pushnil(L);
	while(lua_next(L, index) != 0){
		// key at index -2 and value at index -1
		if(!lua_istable(L, -1))
			return false;
		lua_rawgeti(L, -1, 1);
		if(!lua_isstring(L, -1))
			return false;
		std::string replace_from = lua_tostring(L, -1);
		lua_pop(L, 1);
		lua_rawgeti(L, -1, 2);
		if(!lua_isstring(L, -1))
			return false;
		std::string replace_to = lua_tostring(L, -1);
		lua_pop(L, 1);
		replacements.pairs.push_back(
				std::make_pair(replace_from, replace_to));
		// removes value, keeps key for next iteration
		lua_pop(L, 1);
	}
	return true;
}
// register_craft({output=item, recipe={{item00,item10},{item01,item11}})
int ModApiCraft::l_register_craft(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	//infostream<<"register_craft"<<std::endl;
	luaL_checktype(L, 1, LUA_TTABLE);
	int table = 1;

	// Get the writable craft definition manager from the server
	IWritableCraftDefManager *craftdef =
			getServer(L)->getWritableCraftDefManager();

	std::string type = getstringfield_default(L, table, "type", "shaped");

	/*
		CraftDefinitionShaped
	*/
	if(type == "shaped"){
		std::string output = getstringfield_default(L, table, "output", "");
		if(output == "")
			throw LuaError("Crafting definition is missing an output");

		int width = 0;
		std::vector<std::string> recipe;
		lua_getfield(L, table, "recipe");
		if(lua_isnil(L, -1))
			throw LuaError("Crafting definition is missing a recipe"
					" (output=\"" + output + "\")");
		if(!readCraftRecipeShaped(L, -1, width, recipe))
			throw LuaError("Invalid crafting recipe"
					" (output=\"" + output + "\")");

		CraftReplacements replacements;
		lua_getfield(L, table, "replacements");
		if(!lua_isnil(L, -1))
		{
			if(!readCraftReplacements(L, -1, replacements))
				throw LuaError("Invalid replacements"
						" (output=\"" + output + "\")");
		}

		CraftDefinition *def = new CraftDefinitionShaped(
				output, width, recipe, replacements);
		craftdef->registerCraft(def, getServer(L));
	}
	/*
		CraftDefinitionShapeless
	*/
	else if(type == "shapeless"){
		std::string output = getstringfield_default(L, table, "output", "");
		if(output == "")
			throw LuaError("Crafting definition (shapeless)"
					" is missing an output");

		std::vector<std::string> recipe;
		lua_getfield(L, table, "recipe");
		if(lua_isnil(L, -1))
			throw LuaError("Crafting definition (shapeless)"
					" is missing a recipe"
					" (output=\"" + output + "\")");
		if(!readCraftRecipeShapeless(L, -1, recipe))
			throw LuaError("Invalid crafting recipe"
					" (output=\"" + output + "\")");

		CraftReplacements replacements;
		lua_getfield(L, table, "replacements");
		if(!lua_isnil(L, -1))
		{
			if(!readCraftReplacements(L, -1, replacements))
				throw LuaError("Invalid replacements"
						" (output=\"" + output + "\")");
		}

		CraftDefinition *def = new CraftDefinitionShapeless(
				output, recipe, replacements);
		craftdef->registerCraft(def, getServer(L));
	}
	/*
		CraftDefinitionToolRepair
	*/
	else if(type == "toolrepair"){
		float additional_wear = getfloatfield_default(L, table,
				"additional_wear", 0.0);

		CraftDefinition *def = new CraftDefinitionToolRepair(
				additional_wear);
		craftdef->registerCraft(def, getServer(L));
	}
	/*
		CraftDefinitionCooking
	*/
	else if(type == "cooking"){
		std::string output = getstringfield_default(L, table, "output", "");
		if(output == "")
			throw LuaError("Crafting definition (cooking)"
					" is missing an output");

		std::string recipe = getstringfield_default(L, table, "recipe", "");
		if(recipe == "")
			throw LuaError("Crafting definition (cooking)"
					" is missing a recipe"
					" (output=\"" + output + "\")");

		float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);

		CraftReplacements replacements;
		lua_getfield(L, table, "replacements");
		if(!lua_isnil(L, -1))
		{
			if(!readCraftReplacements(L, -1, replacements))
				throw LuaError("Invalid replacements"
						" (cooking output=\"" + output + "\")");
		}

		CraftDefinition *def = new CraftDefinitionCooking(
				output, recipe, cooktime, replacements);
		craftdef->registerCraft(def, getServer(L));
	}
	/*
		CraftDefinitionFuel
	*/
	else if(type == "fuel"){
		std::string recipe = getstringfield_default(L, table, "recipe", "");
		if(recipe == "")
			throw LuaError("Crafting definition (fuel)"
					" is missing a recipe");

		float burntime = getfloatfield_default(L, table, "burntime", 1.0);

		CraftReplacements replacements;
		lua_getfield(L, table, "replacements");
		if(!lua_isnil(L, -1))
		{
			if(!readCraftReplacements(L, -1, replacements))
				throw LuaError("Invalid replacements"
						" (fuel recipe=\"" + recipe + "\")");
		}

		CraftDefinition *def = new CraftDefinitionFuel(
				recipe, burntime, replacements);
		craftdef->registerCraft(def, getServer(L));
	}
	else
	{
		throw LuaError("Unknown crafting definition type: \"" + type + "\"");
	}

	lua_pop(L, 1);
	return 0; /* number of results */
}

// clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}])
int ModApiCraft::l_clear_craft(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	luaL_checktype(L, 1, LUA_TTABLE);
	int table = 1;

	// Get the writable craft definition manager from the server
	IWritableCraftDefManager *craftdef =
			getServer(L)->getWritableCraftDefManager();

	std::string output = getstringfield_default(L, table, "output", "");
	std::string type = getstringfield_default(L, table, "type", "shaped");
	CraftOutput c_output(output, 0);
	if (output != "") {
		if (craftdef->clearCraftRecipesByOutput(c_output, getServer(L)))
			return 0;
		else
			throw LuaError("No craft recipe known for output"
					" (output=\"" + output + "\")");
	}
	std::vector<std::string> recipe;
	int width = 0;
	CraftMethod method = CRAFT_METHOD_NORMAL;
	/*
		CraftDefinitionShaped
	*/
	if (type == "shaped") {
		lua_getfield(L, table, "recipe");
		if (lua_isnil(L, -1))
			throw LuaError("Either output or recipe has to be defined");
		if (!readCraftRecipeShaped(L, -1, width, recipe))
			throw LuaError("Invalid crafting recipe");
	}
	/*
		CraftDefinitionShapeless
	*/
	else if (type == "shapeless") {
		lua_getfield(L, table, "recipe");
		if (lua_isnil(L, -1))
			throw LuaError("Either output or recipe has to be defined");
		if (!readCraftRecipeShapeless(L, -1, recipe))
			throw LuaError("Invalid crafting recipe");
	}
	/*
		CraftDefinitionCooking
	*/
	else if (type == "cooking") {
		method = CRAFT_METHOD_COOKING;
		std::string rec = getstringfield_default(L, table, "recipe", "");
		if (rec == "")
			throw LuaError("Crafting definition (cooking)"
					" is missing a recipe");
		recipe.push_back(rec);
	}
	/*
		CraftDefinitionFuel
	*/
	else if (type == "fuel") {
		method = CRAFT_METHOD_FUEL;
		std::string rec = getstringfield_default(L, table, "recipe", "");
		if (rec == "")
			throw LuaError("Crafting definition (fuel)"
					" is missing a recipe");
		recipe.push_back(rec);
	} else {
		throw LuaError("Unknown crafting definition type: \"" + type + "\"");
	}
	if (!craftdef->clearCraftRecipesByInput(method, width, recipe, getServer(L)))
		throw LuaError("No crafting specified for input");
	lua_pop(L, 1);
	return 0;
}

// get_craft_result(input)
int ModApiCraft::l_get_craft_result(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	int input_i = 1;
	std::string method_s = getstringfield_default(L, input_i, "method", "normal");
	enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
				es_CraftMethod, CRAFT_METHOD_NORMAL);
	int width = 1;
	lua_getfield(L, input_i, "width");
	if(lua_isnumber(L, -1))
		width = luaL_checkinteger(L, -1);
	lua_pop(L, 1);
	lua_getfield(L, input_i, "items");
	std::vector<ItemStack> items = read_items(L, -1,getServer(L));
	lua_pop(L, 1); // items

	IGameDef *gdef = getServer(L);
	ICraftDefManager *cdef = gdef->cdef();
	CraftInput input(method, width, items);
	CraftOutput output;
	std::vector<ItemStack> output_replacements;
	bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
	lua_newtable(L); // output table
	if (got) {
		ItemStack item;
		item.deSerialize(output.item, gdef->idef());
		LuaItemStack::create(L, item);
		lua_setfield(L, -2, "item");
		setintfield(L, -1, "time", output.time);
		push_items(L, output_replacements);
		lua_setfield(L, -2, "replacements");
	} else {
		LuaItemStack::create(L, ItemStack());
		lua_setfield(L, -2, "item");
		setintfield(L, -1, "time", 0);
		lua_newtable(L);
		lua_setfield(L, -2, "replacements");
	}
	lua_newtable(L); // decremented input table
	lua_pushstring(L, method_s.c_str());
	lua_setfield(L, -2, "method");
	lua_pushinteger(L, width);
	lua_setfield(L, -2, "width");
	push_items(L, input.items);
	lua_setfield(L, -2, "items");
	return 2;
}

static void push_craft_recipe(lua_State *L, IGameDef *gdef,
		const CraftDefinition *recipe,
		const CraftOutput &tmpout)
{
	CraftInput input = recipe->getInput(tmpout, gdef);
	CraftOutput output = recipe->getOutput(input, gdef);

	lua_newtable(L); // items
	std::vector<ItemStack>::const_iterator iter = input.items.begin();
	for (u16 j = 1; iter != input.items.end(); ++iter, j++) {
		if (iter->empty())
			continue;
		lua_pushstring(L, iter->name.c_str());
		lua_rawseti(L, -2, j);
	}
	lua_setfield(L, -2, "items");
	setintfield(L, -1, "width", input.width);

	std::string method_s;
	switch (input.method) {
	case CRAFT_METHOD_NORMAL:
		method_s = "normal";
		break;
	case CRAFT_METHOD_COOKING:
		method_s = "cooking";
		break;
	case CRAFT_METHOD_FUEL:
		method_s = "fuel";
		break;
	default:
		method_s = "unknown";
	}
	lua_pushstring(L, method_s.c_str());
	lua_setfield(L, -2, "method");

	// Deprecated, only for compatibility's sake
	lua_pushstring(L, method_s.c_str());
	lua_setfield(L, -2, "type");

	lua_pushstring(L, output.item.c_str());
	lua_setfield(L, -2, "output");
}

static void push_craft_recipes(lua_State *L, IGameDef *gdef,
		const std::vector<CraftDefinition*> &recipes,
		const CraftOutput &output)
{
	lua_createtable(L, recipes.size(), 0);

	if (recipes.empty()) {
		lua_pushnil(L);
		return;
	}

	std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
	for (unsigned i = 0; it != recipes.end(); ++it) {
		lua_newtable(L);
		push_craft_recipe(L, gdef, *it, output);
		lua_rawseti(L, -2, ++i);
	}
}

// get_craft_recipe(result item)
int ModApiCraft::l_get_craft_recipe(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	std::string item = luaL_checkstring(L, 1);
	Server *server = getServer(L);
	CraftOutput output(item, 0);
	std::vector<CraftDefinition*> recipes = server->cdef()
			->getCraftRecipes(output, server, 1);

	lua_createtable(L, 1, 0);

	if (recipes.empty()) {
		lua_pushnil(L);
		lua_setfield(L, -2, "items");
		setintfield(L, -1, "width", 0);
		return 1;
	}
	push_craft_recipe(L, server, recipes[0], output);
	return 1;
}

// get_all_craft_recipes(result item)
int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	std::string item = luaL_checkstring(L, 1);
	Server *server = getServer(L);
	CraftOutput output(item, 0);
	std::vector<CraftDefinition*> recipes = server->cdef()
			->getCraftRecipes(output, server);

	push_craft_recipes(L, server, recipes, output);
	return 1;
}

void ModApiCraft::Initialize(lua_State *L, int top)
{
	API_FCT(get_all_craft_recipes);
	API_FCT(get_craft_recipe);
	API_FCT(get_craft_result);
	API_FCT(register_craft);
	API_FCT(clear_craft);
}


code:
/*
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.
*/

#ifndef L_CRAFT_H_
#define L_CRAFT_H_

#include <string>
#include <vector>

#include "lua_api/l_base.h"

struct CraftReplacements;

class ModApiCraft : public ModApiBase {
private:
	static int l_register_craft(lua_State *L);
	static int l_get_craft_recipe(lua_State *L);
	static int l_get_all_craft_recipes(lua_State *L);
	static int l_get_craft_result(lua_State *L);
	static int l_clear_craft(lua_State *L);

	static bool readCraftReplacements(lua_State *L, int index,
			CraftReplacements &replacements);
	static bool readCraftRecipeShapeless(lua_State *L, int index,
			std::vector<std::string> &recipe);
	static bool readCraftRecipeShaped(lua_State *L, int index,
			int &width, std::vector<std::string> &recipe);

	static struct EnumString es_CraftMethod[];

public:
	static void Initialize(lua_State *L, int top);
};

#endif /* L_CRAFT_H_ */