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_env.h"
#include "lua_api/l_internal.h"
#include "lua_api/l_nodemeta.h"
#include "lua_api/l_nodetimer.h"
#include "lua_api/l_noise.h"
#include "lua_api/l_vmanip.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "scripting_server.h"
#include "environment.h"
#include "server.h"
#include "nodedef.h"
#include "daynightratio.h"
#include "util/pointedthing.h"
#include "content_sao.h"
#include "treegen.h"
#include "emerge.h"
#include "pathfinder.h"
#include "face_position_cache.h"

struct EnumString ModApiEnvMod::es_ClearObjectsMode[] =
{
	{CLEAR_OBJECTS_MODE_FULL,  "full"},
	{CLEAR_OBJECTS_MODE_QUICK, "quick"},
	{0, NULL},
};

///////////////////////////////////////////////////////////////////////////////

void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
		u32 active_object_count, u32 active_object_count_wider)
{
	ServerScripting *scriptIface = env->getScriptIface();
	scriptIface->realityCheck();

	lua_State *L = scriptIface->getStack();
	sanity_check(lua_checkstack(L, 20));
	StackUnroller stack_unroller(L);

	int error_handler = PUSH_ERROR_HANDLER(L);

	// Get registered_abms
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_abms");
	luaL_checktype(L, -1, LUA_TTABLE);
	lua_remove(L, -2); // Remove core

	// Get registered_abms[m_id]
	lua_pushnumber(L, m_id);
	lua_gettable(L, -2);
	if(lua_isnil(L, -1))
		FATAL_ERROR("");
	lua_remove(L, -2); // Remove registered_abms

	scriptIface->setOriginFromTable(-1);

	// Call action
	luaL_checktype(L, -1, LUA_TTABLE);
	lua_getfield(L, -1, "action");
	luaL_checktype(L, -1, LUA_TFUNCTION);
	lua_remove(L, -2); // Remove registered_abms[m_id]
	push_v3s16(L, p);
	pushnode(L, n, env->getGameDef()->ndef());
	lua_pushnumber(L, active_object_count);
	lua_pushnumber(L, active_object_count_wider);

	int result = lua_pcall(L, 4, 0, error_handler);
	if (result)
		scriptIface->scriptError(result, "LuaABM::trigger");

	lua_pop(L, 1); // Pop error handler
}

void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n)
{
	ServerScripting *scriptIface = env->getScriptIface();
	scriptIface->realityCheck();

	lua_State *L = scriptIface->getStack();
	sanity_check(lua_checkstack(L, 20));
	StackUnroller stack_unroller(L);

	int error_handler = PUSH_ERROR_HANDLER(L);

	// Get registered_lbms
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_lbms");
	luaL_checktype(L, -1, LUA_TTABLE);
	lua_remove(L, -2); // Remove core

	// Get registered_lbms[m_id]
	lua_pushnumber(L, m_id);
	lua_gettable(L, -2);
	FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table");
	lua_remove(L, -2); // Remove registered_lbms

	scriptIface->setOriginFromTable(-1);

	// Call action
	luaL_checktype(L, -1, LUA_TTABLE);
	lua_getfield(L, -1, "action");
	luaL_checktype(L, -1, LUA_TFUNCTION);
	lua_remove(L, -2); // Remove registered_lbms[m_id]
	push_v3s16(L, p);
	pushnode(L, n, env->getGameDef()->ndef());

	int result = lua_pcall(L, 2, 0, error_handler);
	if (result)
		scriptIface->scriptError(result, "LuaLBM::trigger");

	lua_pop(L, 1); // Pop error handler
}

void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
{
	ScriptCallbackState *state = (ScriptCallbackState *)param;
	assert(state != NULL);
	assert(state->script != NULL);
	assert(state->refcount > 0);

	// state must be protected by envlock
	Server *server = state->script->getServer();
	MutexAutoLock envlock(server->m_env_mutex);

	state->refcount--;

	state->script->on_emerge_area_completion(blockpos, action, state);

	if (state->refcount == 0)
		delete state;
}

// Exported functions

// set_node(pos, node)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_set_node(lua_State *L)
{
	GET_ENV_PTR;

	INodeDefManager *ndef = env->getGameDef()->ndef();
	// parameters
	v3s16 pos = read_v3s16(L, 1);
	MapNode n = readnode(L, 2, ndef);
	// Do it
	bool succeeded = env->setNode(pos, n);
	lua_pushboolean(L, succeeded);
	return 1;
}

int ModApiEnvMod::l_add_node(lua_State *L)
{
	return l_set_node(L);
}

// remove_node(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_remove_node(lua_State *L)
{
	GET_ENV_PTR;

	// parameters
	v3s16 pos = read_v3s16(L, 1);
	// Do it
	bool succeeded = env->removeNode(pos);
	lua_pushboolean(L, succeeded);
	return 1;
}

// swap_node(pos, node)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_swap_node(lua_State *L)
{
	GET_ENV_PTR;

	INodeDefManager *ndef = env->getGameDef()->ndef();
	// parameters
	v3s16 pos = read_v3s16(L, 1);
	MapNode n = readnode(L, 2, ndef);
	// Do it
	bool succeeded = env->swapNode(pos, n);
	lua_pushboolean(L, succeeded);
	return 1;
}

// get_node(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_node(lua_State *L)
{
	GET_ENV_PTR;

	// pos
	v3s16 pos = read_v3s16(L, 1);
	// Do it
	MapNode n = env->getMap().getNodeNoEx(pos);
	// Return node
	pushnode(L, n, env->getGameDef()->ndef());
	return 1;
}

// get_node_or_nil(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_node_or_nil(lua_State *L)
{
	GET_ENV_PTR;

	// pos
	v3s16 pos = read_v3s16(L, 1);
	// Do it
	bool pos_ok;
	MapNode n = env->getMap().getNodeNoEx(pos, &pos_ok);
	if (pos_ok) {
		// Return node
		pushnode(L, n, env->getGameDef()->ndef());
	} else {
		lua_pushnil(L);
	}
	return 1;
}

// get_node_light(pos, timeofday)
// pos = {x=num, y=num, z=num}
// timeofday: nil = current time, 0 = night, 0.5 = day
int ModApiEnvMod::l_get_node_light(lua_State *L)
{
	GET_ENV_PTR;

	// Do it
	v3s16 pos = read_v3s16(L, 1);
	u32 time_of_day = env->getTimeOfDay();
	if(lua_isnumber(L, 2))
		time_of_day = 24000.0 * lua_tonumber(L, 2);
	time_of_day %= 24000;
	u32 dnr = time_to_daynight_ratio(time_of_day, true);

	bool is_position_ok;
	MapNode n = env->getMap().getNodeNoEx(pos, &is_position_ok);
	if (is_position_ok) {
		INodeDefManager *ndef = env->getGameDef()->ndef();
		lua_pushinteger(L, n.getLightBlend(dnr, ndef));
	} else {
		lua_pushnil(L);
	}
	return 1;
}

// place_node(pos, node)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_place_node(lua_State *L)
{
	GET_ENV_PTR;

	ScriptApiItem *scriptIfaceItem = getScriptApi<ScriptApiItem>(L);
	Server *server = getServer(L);
	INodeDefManager *ndef = server->ndef();
	IItemDefManager *idef = server->idef();

	v3s16 pos = read_v3s16(L, 1);
	MapNode n = readnode(L, 2, ndef);

	// Don't attempt to load non-loaded area as of now
	MapNode n_old = env->getMap().getNodeNoEx(pos);
	if(n_old.getContent() == CONTENT_IGNORE){
		lua_pushboolean(L, false);
		return 1;
	}
	// Create item to place
	ItemStack item(ndef->get(n).name, 1, 0, idef);
	// Make pointed position
	PointedThing pointed;
	pointed.type = POINTEDTHING_NODE;
	pointed.node_abovesurface = pos;
	pointed.node_undersurface = pos + v3s16(0,-1,0);
	// Place it with a NULL placer (appears in Lua as nil)
	bool success = scriptIfaceItem->item_OnPlace(item, nullptr, pointed);
	lua_pushboolean(L, success);
	return 1;
}

// dig_node(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_dig_node(lua_State *L)
{
	GET_ENV_PTR;

	ScriptApiNode *scriptIfaceNode = getScriptApi<ScriptApiNode>(L);

	v3s16 pos = read_v3s16(L, 1);

	// Don't attempt to load non-loaded area as of now
	MapNode n = env->getMap().getNodeNoEx(pos);
	if(n.getContent() == CONTENT_IGNORE){
		lua_pushboolean(L, false);
		return 1;
	}
	// Dig it out with a NULL digger (appears in Lua as a
	// non-functional ObjectRef)
	bool success = scriptIfaceNode->node_on_dig(pos, n, NULL);
	lua_pushboolean(L, success);
	return 1;
}

// punch_node(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_punch_node(lua_State *L)
{
	GET_ENV_PTR;

	ScriptApiNode *scriptIfaceNode = getScriptApi<ScriptApiNode>(L);

	v3s16 pos = read_v3s16(L, 1);

	// Don't attempt to load non-loaded area as of now
	MapNode n = env->getMap().getNodeNoEx(pos);
	if(n.getContent() == CONTENT_IGNORE){
		lua_pushboolean(L, false);
		return 1;
	}
	// Punch it with a NULL puncher (appears in Lua as a non-functional
	// ObjectRef)
	bool success = scriptIfaceNode->node_on_punch(pos, n, NULL, PointedThing());
	lua_pushboolean(L, success);
	return 1;
}

// get_node_max_level(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_node_max_level(lua_State *L)
{
	Environment *env = getEnv(L);
	if (!env) {
		return 0;
	}

	v3s16 pos = read_v3s16(L, 1);
	MapNode n = env->getMap().getNodeNoEx(pos);
	lua_pushnumber(L, n.getMaxLevel(env->getGameDef()->ndef()));
	return 1;
}

// get_node_level(pos)
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_get_node_level(lua_State *L)
{
	Environment *env = getEnv(L);
	if (!env) {
		return 0;
	}

	v3s16 pos = read_v3s16(L, 1);
	MapNode n = env->getMap().getNodeNoEx(pos);
	lua_pushnumber(L, n.getLevel(env->getGameDef()->ndef()));
	return 1;
}

// set_node_level(pos, level)
// pos = {x=num, y=num, z=num}
// level: 0..63
int ModApiEnvMod::l_set_node_level(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 pos = read_v3s16(L, 1);
	u8 level = 1;
	if(lua_isnumber(L, 2))
		level = lua_tonumber(L, 2);
	MapNode n = env->getMap().getNodeNoEx(pos);
	lua_pushnumber(L, n.setLevel(env->getGameDef()->ndef(), level));
	env->setNode(pos, n);
	return 1;
}

// add_node_level(pos, level)
// pos = {x=num, y=num, z=num}
// level: 0..63
int ModApiEnvMod::l_add_node_level(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 pos = read_v3s16(L, 1);
	u8 level = 1;
	if(lua_isnumber(L, 2))
		level = lua_tonumber(L, 2);
	MapNode n = env->getMap().getNodeNoEx(pos);
	lua_pushnumber(L, n.addLevel(env->getGameDef()->ndef(), level));
	env->setNode(pos, n);
	return 1;
}

// find_nodes_with_meta(pos1, pos2)
int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L)
{
	GET_ENV_PTR;

	std::vector<v3s16> positions = env->getMap().findNodesWithMetadata(
		check_v3s16(L, 1), check_v3s16(L, 2));

	lua_newtable(L);
	for (size_t i = 0; i != positions.size(); i++) {
		push_v3s16(L, positions[i]);
		lua_rawseti(L, -2, i + 1);
	}

	return 1;
}

// get_meta(pos)
int ModApiEnvMod::l_get_meta(lua_State *L)
{
	GET_ENV_PTR;

	// Do it
	v3s16 p = read_v3s16(L, 1);
	NodeMetaRef::create(L, p, env);
	return 1;
}

// get_node_timer(pos)
int ModApiEnvMod::l_get_node_timer(lua_State *L)
{
	GET_ENV_PTR;

	// Do it
	v3s16 p = read_v3s16(L, 1);
	NodeTimerRef::create(L, p, env);
	return 1;
}

// add_entity(pos, entityname, [staticdata]) -> ObjectRef or nil
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_add_entity(lua_State *L)
{
	GET_ENV_PTR;

	// pos
	v3f pos = checkFloatPos(L, 1);
	// content
	const char *name = luaL_checkstring(L, 2);
	// staticdata
	const char *staticdata = luaL_optstring(L, 3, "");
	// Do it
	ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, staticdata);
	int objectid = env->addActiveObject(obj);
	// If failed to add, return nothing (reads as nil)
	if(objectid == 0)
		return 0;
	// Return ObjectRef
	getScriptApiBase(L)->objectrefGetOrCreate(L, obj);
	return 1;
}

// add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
// pos = {x=num, y=num, z=num}
int ModApiEnvMod::l_add_item(lua_State *L)
{
	GET_ENV_PTR;

	// pos
	//v3f pos = checkFloatPos(L, 1);
	// item
	ItemStack item = read_item(L, 2,getServer(L)->idef());
	if(item.empty() || !item.isKnown(getServer(L)->idef()))
		return 0;

	int error_handler = PUSH_ERROR_HANDLER(L);

	// Use spawn_item to spawn a __builtin:item
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "spawn_item");
	lua_remove(L, -2); // Remove core
	if(lua_isnil(L, -1))
		return 0;
	lua_pushvalue(L, 1);
	lua_pushstring(L, item.getItemString().c_str());

	PCALL_RESL(L, lua_pcall(L, 2, 1, error_handler));

	lua_remove(L, error_handler);
	return 1;
}

// get_player_by_name(name)
int ModApiEnvMod::l_get_player_by_name(lua_State *L)
{
	GET_ENV_PTR;

	// Do it
	const char *name = luaL_checkstring(L, 1);
	RemotePlayer *player = dynamic_cast<RemotePlayer *>(env->getPlayer(name));
	if (player == NULL){
		lua_pushnil(L);
		return 1;
	}
	PlayerSAO *sao = player->getPlayerSAO();
	if(sao == NULL){
		lua_pushnil(L);
		return 1;
	}
	// Put player on stack
	getScriptApiBase(L)->objectrefGetOrCreate(L, sao);
	return 1;
}

// get_objects_inside_radius(pos, radius)
int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L)
{
	GET_ENV_PTR;

	// Do it
	v3f pos = checkFloatPos(L, 1);
	float radius = luaL_checknumber(L, 2) * BS;
	std::vector<u16> ids;
	env->getObjectsInsideRadius(ids, pos, radius);
	ScriptApiBase *script = getScriptApiBase(L);
	lua_createtable(L, ids.size(), 0);
	std::vector<u16>::const_iterator iter = ids.begin();
	for(u32 i = 0; iter != ids.end(); ++iter) {
		ServerActiveObject *obj = env->getActiveObject(*iter);
		if (!obj->isGone()) {
			// Insert object reference into table
			script->objectrefGetOrCreate(L, obj);
			lua_rawseti(L, -2, ++i);
		}
	}
	return 1;
}

// set_timeofday(val)
// val = 0...1
int ModApiEnvMod::l_set_timeofday(lua_State *L)
{
	GET_ENV_PTR;

	// Do it
	float timeofday_f = luaL_checknumber(L, 1);
	sanity_check(timeofday_f >= 0.0 && timeofday_f <= 1.0);
	int timeofday_mh = (int)(timeofday_f * 24000.0);
	// This should be set directly in the environment but currently
	// such changes aren't immediately sent to the clients, so call
	// the server instead.
	//env->setTimeOfDay(timeofday_mh);
	getServer(L)->setTimeOfDay(timeofday_mh);
	return 0;
}

// get_timeofday() -> 0...1
int ModApiEnvMod::l_get_timeofday(lua_State *L)
{
	Environment *env = getEnv(L);
	if (!env) {
		return 0;
	}

	// Do it
	int timeofday_mh = env->getTimeOfDay();
	float timeofday_f = (float)timeofday_mh / 24000.0f;
	lua_pushnumber(L, timeofday_f);
	return 1;
}

// get_day_count() -> int
int ModApiEnvMod::l_get_day_count(lua_State *L)
{
	Environment *env = getEnv(L);
	if (!env) {
		return 0;
	}

	lua_pushnumber(L, env->getDayCount());
	return 1;
}

// get_gametime()
int ModApiEnvMod::l_get_gametime(lua_State *L)
{
	GET_ENV_PTR;

	int game_time = env->getGameTime();
	lua_pushnumber(L, game_time);
	return 1;
}

// find_node_near(pos, radius, nodenames, search_center) -> pos or nil
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
int ModApiEnvMod::l_find_node_near(lua_State *L)
{
	Environment *env = getEnv(L);
	if (!env) {
		return 0;
	}

	INodeDefManager *ndef = getGameDef(L)->ndef();
	v3s16 pos = read_v3s16(L, 1);
	int radius = luaL_checkinteger(L, 2);
	std::set<content_t> filter;
	if (lua_istable(L, 3)) {
		lua_pushnil(L);
		while (lua_next(L, 3) != 0) {
			// key at index -2 and value at index -1
			luaL_checktype(L, -1, LUA_TSTRING);
			ndef->getIds(lua_tostring(L, -1), filter);
			// removes value, keeps key for next iteration
			lua_pop(L, 1);
		}
	} else if (lua_isstring(L, 3)) {
		ndef->getIds(lua_tostring(L, 3), filter);
	}

	int start_radius = (lua_toboolean(L, 4)) ? 0 : 1;
	for (int d = start_radius; d <= radius; d++) {
		std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
		for (std::vector<v3s16>::iterator i = list.begin();
				i != list.end(); ++i) {
			v3s16 p = pos + (*i);
			content_t c = env->getMap().getNodeNoEx(p).getContent();
			if (filter.count(c) != 0) {
				push_v3s16(L, p);
				return 1;
			}
		}
	}
	return 0;
}

// find_nodes_in_area(minp, maxp, nodenames) -> list of positions
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
{
	GET_ENV_PTR;

	INodeDefManager *ndef = getServer(L)->ndef();
	v3s16 minp = read_v3s16(L, 1);
	v3s16 maxp = read_v3s16(L, 2);
	sortBoxVerticies(minp, maxp);

	v3s16 cube = maxp - minp + 1;

	/* Limit for too large areas, assume default values
	 * and give tolerances of 1 node on each side
	 * (chunksize * MAP_BLOCKSIZE + 2)^3 = 551368
	*/
	if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 551368) {
		luaL_error(L, "find_nodes_in_area(): area volume"
				" exceeds allowed value of 551368");
		return 0;
	}

	std::set<content_t> filter;
	if (lua_istable(L, 3)) {
		lua_pushnil(L);
		while (lua_next(L, 3) != 0) {
			// key at index -2 and value at index -1
			luaL_checktype(L, -1, LUA_TSTRING);
			ndef->getIds(lua_tostring(L, -1), filter);
			// removes value, keeps key for next iteration
			lua_pop(L, 1);
		}
	} else if (lua_isstring(L, 3)) {
		ndef->getIds(lua_tostring(L, 3), filter);
	}

	std::unordered_map<content_t, u32> individual_count;

	lua_newtable(L);
	u64 i = 0;
	for (s16 x = minp.X; x <= maxp.X; x++)
	for (s16 y = minp.Y; y <= maxp.Y; y++)
	for (s16 z = minp.Z; z <= maxp.Z; z++) {
		v3s16 p(x, y, z);
		content_t c = env->getMap().getNodeNoEx(p).getContent();
		if (filter.count(c) != 0) {
			push_v3s16(L, p);
			lua_rawseti(L, -2, ++i);
			individual_count[c]++;
		}
	}
	lua_newtable(L);
	for (std::set<content_t>::const_iterator it = filter.begin();
			it != filter.end(); ++it) {
		lua_pushnumber(L, individual_count[*it]);
		lua_setfield(L, -2, ndef->get(*it).name.c_str());
	}
	return 2;
}

// find_nodes_in_area_under_air(minp, maxp, nodenames) -> list of positions
// nodenames: e.g. {"ignore", "group:tree"} or "default:dirt"
int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
{
	/* Note: A similar but generalized (and therefore slower) version of this
	 * function could be created -- e.g. find_nodes_in_area_under -- which
	 * would accept a node name (or ID?) or list of names that the "above node"
	 * should be.
	 * TODO
	 */

	GET_ENV_PTR;

	INodeDefManager *ndef = getServer(L)->ndef();
	v3s16 minp = read_v3s16(L, 1);
	v3s16 maxp = read_v3s16(L, 2);
	sortBoxVerticies(minp, maxp);

	v3s16 cube = maxp - minp + 1;

	/* Limit for too large areas, assume default values
	 * and give tolerances of 1 node on each side
	 * (chunksize * MAP_BLOCKSIZE + 2)^3 = 551368
	*/
	if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 551368) {
		luaL_error(L, "find_nodes_in_area_under_air(): area volume"
				" exceeds allowed value of 551368");
		return 0;
	}

	std::set<content_t> filter;

	if (lua_istable(L, 3)) {
		lua_pushnil(L);
		while (lua_next(L, 3) != 0) {
			// key at index -2 and value at index -1
			luaL_checktype(L, -1, LUA_TSTRING);
			ndef->getIds(lua_tostring(L, -1), filter);
			// removes value, keeps key for next iteration
			lua_pop(L, 1);
		}
	} else if (lua_isstring(L, 3)) {
		ndef->getIds(lua_tostring(L, 3), filter);
	}

	lua_newtable(L);
	u64 i = 0;
	for (s16 x = minp.X; x <= maxp.X; x++)
	for (s16 z = minp.Z; z <= maxp.Z; z++) {
		s16 y = minp.Y;
		v3s16 p(x, y, z);
		content_t c = env->getMap().getNodeNoEx(p).getContent();
		for (; y <= maxp.Y; y++) {
			v3s16 psurf(x, y + 1, z);
			content_t csurf = env->getMap().getNodeNoEx(psurf).getContent();
			if (c != CONTENT_AIR && csurf == CONTENT_AIR &&
					filter.count(c) != 0) {
				push_v3s16(L, v3s16(x, y, z));
				lua_rawseti(L, -2, ++i);
			}
			c = csurf;
		}
	}
	return 1;
}

// get_perlin(seeddiff, octaves, persistence, scale)
// returns world-specific PerlinNoise
int ModApiEnvMod::l_get_perlin(lua_State *L)
{
	GET_ENV_PTR_NO_MAP_LOCK;

	NoiseParams params;

	if (lua_istable(L, 1)) {
		read_noiseparams(L, 1, &params);
	} else {
		params.seed    = luaL_checkint(L, 1);
		params.octaves = luaL_checkint(L, 2);
		params.persist = luaL_checknumber(L, 3);
		params.spread  = v3f(1, 1, 1) * luaL_checknumber(L, 4);
	}

	params.seed += (int)env->getServerMap().getSeed();

	LuaPerlinNoise *n = new LuaPerlinNoise(&params);
	*(void **)(lua_newuserdata(L, sizeof(void *))) = n;
	luaL_getmetatable(L, "PerlinNoise");
	lua_setmetatable(L, -2);
	return 1;
}

// get_perlin_map(noiseparams, size)
// returns world-specific PerlinNoiseMap
int ModApiEnvMod::l_get_perlin_map(lua_State *L)
{
	GET_ENV_PTR_NO_MAP_LOCK;

	NoiseParams np;
	if (!read_noiseparams(L, 1, &np))
		return 0;
	v3s16 size = read_v3s16(L, 2);

	s32 seed = (s32)(env->getServerMap().getSeed());
	LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(&np, seed, size);
	*(void **)(lua_newuserdata(L, sizeof(void *))) = n;
	luaL_getmetatable(L, "PerlinNoiseMap");
	lua_setmetatable(L, -2);
	return 1;
}

// get_voxel_manip()
// returns voxel manipulator
int ModApiEnvMod::l_get_voxel_manip(lua_State *L)
{
	GET_ENV_PTR;

	Map *map = &(env->getMap());
	LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ?
		new LuaVoxelManip(map, read_v3s16(L, 1), read_v3s16(L, 2)) :
		new LuaVoxelManip(map);

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

// clear_objects([options])
// clear all objects in the environment
// where options = {mode = "full" or "quick"}
int ModApiEnvMod::l_clear_objects(lua_State *L)
{
	GET_ENV_PTR;

	ClearObjectsMode mode = CLEAR_OBJECTS_MODE_FULL;
	if (lua_istable(L, 1)) {
		mode = (ClearObjectsMode)getenumfield(L, 1, "mode",
			ModApiEnvMod::es_ClearObjectsMode, mode);
	}

	env->clearObjects(mode);
	return 0;
}

// line_of_sight(pos1, pos2, stepsize) -> true/false, pos
int ModApiEnvMod::l_line_of_sight(lua_State *L)
{
	float stepsize = 1.0;

	GET_ENV_PTR;

	// read position 1 from lua
	v3f pos1 = checkFloatPos(L, 1);
	// read position 2 from lua
	v3f pos2 = checkFloatPos(L, 2);
	//read step size from lua
	if (lua_isnumber(L, 3)) {
		stepsize = lua_tonumber(L, 3);
	}

	v3s16 p;
	bool success = env->line_of_sight(pos1, pos2, stepsize, &p);
	lua_pushboolean(L, success);
	if (!success) {
		push_v3s16(L, p);
		return 2;
	}
	return 1;
}

// fix_light(p1, p2)
int ModApiEnvMod::l_fix_light(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 blockpos1 = getContainerPos(read_v3s16(L, 1), MAP_BLOCKSIZE);
	v3s16 blockpos2 = getContainerPos(read_v3s16(L, 2), MAP_BLOCKSIZE);
	ServerMap &map = env->getServerMap();
	std::map<v3s16, MapBlock *> modified_blocks;
	bool success = true;
	v3s16 blockpos;
	for (blockpos.X = blockpos1.X; blockpos.X <= blockpos2.X; blockpos.X++)
	for (blockpos.Y = blockpos1.Y; blockpos.Y <= blockpos2.Y; blockpos.Y++)
	for (blockpos.Z = blockpos1.Z; blockpos.Z <= blockpos2.Z; blockpos.Z++) {
		success = success & map.repairBlockLight(blockpos, &modified_blocks);
	}
	if (modified_blocks.size() > 0) {
		MapEditEvent event;
		event.type = MEET_OTHER;
		for (std::map<v3s16, MapBlock *>::iterator it = modified_blocks.begin();
				it != modified_blocks.end(); ++it)
			event.modified_blocks.insert(it->first);

		map.dispatchEvent(&event);
	}
	lua_pushboolean(L, success);

	return 1;
}

// emerge_area(p1, p2, [callback, context])
// emerge mapblocks in area p1..p2, calls callback with context upon completion
int ModApiEnvMod::l_emerge_area(lua_State *L)
{
	GET_ENV_PTR;

	EmergeCompletionCallback callback = NULL;
	ScriptCallbackState *state = NULL;

	EmergeManager *emerge = getServer(L)->getEmergeManager();

	v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1));
	v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2));
	sortBoxVerticies(bpmin, bpmax);

	size_t num_blocks = VoxelArea(bpmin, bpmax).getVolume();
	assert(num_blocks != 0);

	if (lua_isfunction(L, 3)) {
		callback = LuaEmergeAreaCallback;

		lua_pushvalue(L, 3);
		int callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);

		lua_pushvalue(L, 4);
		int args_ref = luaL_ref(L, LUA_REGISTRYINDEX);

		state = new ScriptCallbackState;
		state->script       = getServer(L)->getScriptIface();
		state->callback_ref = callback_ref;
		state->args_ref     = args_ref;
		state->refcount     = num_blocks;
		state->origin       = getScriptApiBase(L)->getOrigin();
	}

	for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
	for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
	for (s16 x = bpmin.X; x <= bpmax.X; x++) {
		emerge->enqueueBlockEmergeEx(v3s16(x, y, z), PEER_ID_INEXISTENT,
			BLOCK_EMERGE_ALLOW_GEN | BLOCK_EMERGE_FORCE_QUEUE, callback, state);
	}

	return 0;
}

// delete_area(p1, p2)
// delete mapblocks in area p1..p2
int ModApiEnvMod::l_delete_area(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1));
	v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2));
	sortBoxVerticies(bpmin, bpmax);

	ServerMap &map = env->getServerMap();

	MapEditEvent event;
	event.type = MEET_OTHER;

	bool success = true;
	for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
	for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
	for (s16 x = bpmin.X; x <= bpmax.X; x++) {
		v3s16 bp(x, y, z);
		if (map.deleteBlock(bp)) {
			env->setStaticForActiveObjectsInBlock(bp, false);
			event.modified_blocks.insert(bp);
		} else {
			success = false;
		}
	}

	map.dispatchEvent(&event);
	lua_pushboolean(L, success);
	return 1;
}

// find_path(pos1, pos2, searchdistance,
//     max_jump, max_drop, algorithm) -> table containing path
int ModApiEnvMod::l_find_path(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 pos1                  = read_v3s16(L, 1);
	v3s16 pos2                  = read_v3s16(L, 2);
	unsigned int searchdistance = luaL_checkint(L, 3);
	unsigned int max_jump       = luaL_checkint(L, 4);
	unsigned int max_drop       = luaL_checkint(L, 5);
	PathAlgorithm algo          = PA_PLAIN_NP;
	if (!lua_isnil(L, 6)) {
		std::string algorithm = luaL_checkstring(L,6);

		if (algorithm == "A*")
			algo = PA_PLAIN;

		if (algorithm == "Dijkstra")
			algo = PA_DIJKSTRA;
	}

	std::vector<v3s16> path = get_path(env, pos1, pos2,
		searchdistance, max_jump, max_drop, algo);

	if (path.size() > 0)
	{
		lua_newtable(L);
		int top = lua_gettop(L);
		unsigned int index = 1;
		for (std::vector<v3s16>::iterator i = path.begin(); i != path.end(); ++i) {
			lua_pushnumber(L,index);
			push_v3s16(L, *i);
			lua_settable(L, top);
			index++;
		}
		return 1;
	}

	return 0;
}

// spawn_tree(pos, treedef)
int ModApiEnvMod::l_spawn_tree(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 p0 = read_v3s16(L, 1);

	treegen::TreeDef tree_def;
	std::string trunk,leaves,fruit;
	INodeDefManager *ndef = env->getGameDef()->ndef();

	if(lua_istable(L, 2))
	{
		getstringfield(L, 2, "axiom", tree_def.initial_axiom);
		getstringfield(L, 2, "rules_a", tree_def.rules_a);
		getstringfield(L, 2, "rules_b", tree_def.rules_b);
		getstringfield(L, 2, "rules_c", tree_def.rules_c);
		getstringfield(L, 2, "rules_d", tree_def.rules_d);
		getstringfield(L, 2, "trunk", trunk);
		tree_def.trunknode=ndef->getId(trunk);
		getstringfield(L, 2, "leaves", leaves);
		tree_def.leavesnode=ndef->getId(leaves);
		tree_def.leaves2_chance=0;
		getstringfield(L, 2, "leaves2", leaves);
		if (leaves !="")
		{
			tree_def.leaves2node=ndef->getId(leaves);
			getintfield(L, 2, "leaves2_chance", tree_def.leaves2_chance);
		}
		getintfield(L, 2, "angle", tree_def.angle);
		getintfield(L, 2, "iterations", tree_def.iterations);
		if (!getintfield(L, 2, "random_level", tree_def.iterations_random_level))
			tree_def.iterations_random_level = 0;
		getstringfield(L, 2, "trunk_type", tree_def.trunk_type);
		getboolfield(L, 2, "thin_branches", tree_def.thin_branches);
		tree_def.fruit_chance=0;
		getstringfield(L, 2, "fruit", fruit);
		if (fruit != "")
		{
			tree_def.fruitnode=ndef->getId(fruit);
			getintfield(L, 2, "fruit_chance",tree_def.fruit_chance);
		}
		tree_def.explicit_seed = getintfield(L, 2, "seed", tree_def.seed);
	}
	else
		return 0;

	treegen::error e;
	if ((e = treegen::spawn_ltree (env, p0, ndef, tree_def)) != treegen::SUCCESS) {
		if (e == treegen::UNBALANCED_BRACKETS) {
			luaL_error(L, "spawn_tree(): closing ']' has no matching opening bracket");
		} else {
			luaL_error(L, "spawn_tree(): unknown error");
		}
	}

	return 1;
}

// transforming_liquid_add(pos)
int ModApiEnvMod::l_transforming_liquid_add(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 p0 = read_v3s16(L, 1);
	env->getMap().transforming_liquid_add(p0);
	return 1;
}

// forceload_block(blockpos)
// blockpos = {x=num, y=num, z=num}
int ModApiEnvMod::l_forceload_block(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 blockpos = read_v3s16(L, 1);
	env->getForceloadedBlocks()->insert(blockpos);
	return 0;
}

// forceload_free_block(blockpos)
// blockpos = {x=num, y=num, z=num}
int ModApiEnvMod::l_forceload_free_block(lua_State *L)
{
	GET_ENV_PTR;

	v3s16 blockpos = read_v3s16(L, 1);
	env->getForceloadedBlocks()->erase(blockpos);
	return 0;
}

void ModApiEnvMod::Initialize(lua_State *L, int top)
{
	API_FCT(set_node);
	API_FCT(add_node);
	API_FCT(swap_node);
	API_FCT(add_item);
	API_FCT(remove_node);
	API_FCT(get_node);
	API_FCT(get_node_or_nil);
	API_FCT(get_node_light);
	API_FCT(place_node);
	API_FCT(dig_node);
	API_FCT(punch_node);
	API_FCT(get_node_max_level);
	API_FCT(get_node_level);
	API_FCT(set_node_level);
	API_FCT(add_node_level);
	API_FCT(add_entity);
	API_FCT(find_nodes_with_meta);
	API_FCT(get_meta);
	API_FCT(get_node_timer);
	API_FCT(get_player_by_name);
	API_FCT(get_objects_inside_radius);
	API_FCT(set_timeofday);
	API_FCT(get_timeofday);
	API_FCT(get_gametime);
	API_FCT(get_day_count);
	API_FCT(find_node_near);
	API_FCT(find_nodes_in_area);
	API_FCT(find_nodes_in_area_under_air);
	API_FCT(fix_light);
	API_FCT(emerge_area);
	API_FCT(delete_area);
	API_FCT(get_perlin);
	API_FCT(get_perlin_map);
	API_FCT(get_voxel_manip);
	API_FCT(clear_objects);
	API_FCT(spawn_tree);
	API_FCT(find_path);
	API_FCT(line_of_sight);
	API_FCT(transforming_liquid_add);
	API_FCT(forceload_block);
	API_FCT(forceload_free_block);
}

void ModApiEnvMod::InitializeClient(lua_State *L, int top)
{
	API_FCT(get_timeofday);
	API_FCT(get_day_count);
	API_FCT(get_node_max_level);
	API_FCT(get_node_level);
	API_FCT(find_node_near);
}


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_ENV_H_
#define L_ENV_H_

#include "lua_api/l_base.h"
#include "serverenvironment.h"

class ModApiEnvMod : public ModApiBase {
private:
	// set_node(pos, node)
	// pos = {x=num, y=num, z=num}
	static int l_set_node(lua_State *L);

	static int l_add_node(lua_State *L);

	// remove_node(pos)
	// pos = {x=num, y=num, z=num}
	static int l_remove_node(lua_State *L);

	// swap_node(pos, node)
	// pos = {x=num, y=num, z=num}
	static int l_swap_node(lua_State *L);

	// get_node(pos)
	// pos = {x=num, y=num, z=num}
	static int l_get_node(lua_State *L);

	// get_node_or_nil(pos)
	// pos = {x=num, y=num, z=num}
	static int l_get_node_or_nil(lua_State *L);

	// get_node_light(pos, timeofday)
	// pos = {x=num, y=num, z=num}
	// timeofday: nil = current time, 0 = night, 0.5 = day
	static int l_get_node_light(lua_State *L);

	// place_node(pos, node)
	// pos = {x=num, y=num, z=num}
	static int l_place_node(lua_State *L);

	// dig_node(pos)
	// pos = {x=num, y=num, z=num}
	static int l_dig_node(lua_State *L);

	// punch_node(pos)
	// pos = {x=num, y=num, z=num}
	static int l_punch_node(lua_State *L);

	// get_node_max_level(pos)
	// pos = {x=num, y=num, z=num}
	static int l_get_node_max_level(lua_State *L);

	// get_node_level(pos)
	// pos = {x=num, y=num, z=num}
	static int l_get_node_level(lua_State *L);

	// set_node_level(pos)
	// pos = {x=num, y=num, z=num}
	static int l_set_node_level(lua_State *L);

	// add_node_level(pos)
	// pos = {x=num, y=num, z=num}
	static int l_add_node_level(lua_State *L);

	// find_nodes_with_meta(pos1, pos2)
	static int l_find_nodes_with_meta(lua_State *L);

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

	// get_node_timer(pos)
	static int l_get_node_timer(lua_State *L);

	// add_entity(pos, entityname) -> ObjectRef or nil
	// pos = {x=num, y=num, z=num}
	static int l_add_entity(lua_State *L);

	// add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
	// pos = {x=num, y=num, z=num}
	static int l_add_item(lua_State *L);

	// get_player_by_name(name)
	static int l_get_player_by_name(lua_State *L);

	// get_objects_inside_radius(pos, radius)
	static int l_get_objects_inside_radius(lua_State *L);

	// set_timeofday(val)
	// val = 0...1
	static int l_set_timeofday(lua_State *L);

	// get_timeofday() -> 0...1
	static int l_get_timeofday(lua_State *L);

	// get_gametime()
	static int l_get_gametime(lua_State *L);

	// get_day_count() -> int
	static int l_get_day_count(lua_State *L);

	// find_node_near(pos, radius, nodenames, search_center) -> pos or nil
	// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
	static int l_find_node_near(lua_State *L);

	// find_nodes_in_area(minp, maxp, nodenames) -> list of positions
	// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
	static int l_find_nodes_in_area(lua_State *L);

	// find_surface_nodes_in_area(minp, maxp, nodenames) -> list of positions
	// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
	static int l_find_nodes_in_area_under_air(lua_State *L);

	// fix_light(p1, p2) -> true/false
	static int l_fix_light(lua_State *L);

	// emerge_area(p1, p2)
	static int l_emerge_area(lua_State *L);

	// delete_area(p1, p2) -> true/false
	static int l_delete_area(lua_State *L);

	// get_perlin(seeddiff, octaves, persistence, scale)
	// returns world-specific PerlinNoise
	static int l_get_perlin(lua_State *L);

	// get_perlin_map(noiseparams, size)
	// returns world-specific PerlinNoiseMap
	static int l_get_perlin_map(lua_State *L);

	// get_voxel_manip()
	// returns world-specific voxel manipulator
	static int l_get_voxel_manip(lua_State *L);

	// clear_objects()
	// clear all objects in the environment
	static int l_clear_objects(lua_State *L);

	// spawn_tree(pos, treedef)
	static int l_spawn_tree(lua_State *L);

	// line_of_sight(pos1, pos2, stepsize) -> true/false
	static int l_line_of_sight(lua_State *L);

	// find_path(pos1, pos2, searchdistance,
	//     max_jump, max_drop, algorithm) -> table containing path
	static int l_find_path(lua_State *L);

	// transforming_liquid_add(pos)
	static int l_transforming_liquid_add(lua_State *L);

	// forceload_block(blockpos)
	// forceloads a block
	static int l_forceload_block(lua_State *L);

	// forceload_free_block(blockpos)
	// stops forceloading a position
	static int l_forceload_free_block(lua_State *L);

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

	static struct EnumString es_ClearObjectsMode[];
};

class LuaABM : public ActiveBlockModifier {
private:
	int m_id;

	std::set<std::string> m_trigger_contents;
	std::set<std::string> m_required_neighbors;
	float m_trigger_interval;
	u32 m_trigger_chance;
	bool m_simple_catch_up;
public:
	LuaABM(lua_State *L, int id,
			const std::set<std::string> &trigger_contents,
			const std::set<std::string> &required_neighbors,
			float trigger_interval, u32 trigger_chance, bool simple_catch_up):
		m_id(id),
		m_trigger_contents(trigger_contents),
		m_required_neighbors(required_neighbors),
		m_trigger_interval(trigger_interval),
		m_trigger_chance(trigger_chance),
		m_simple_catch_up(simple_catch_up)
	{
	}
	virtual const std::set<std::string> &getTriggerContents() const
	{
		return m_trigger_contents;
	}
	virtual const std::set<std::string> &getRequiredNeighbors() const
	{
		return m_required_neighbors;
	}
	virtual float getTriggerInterval()
	{
		return m_trigger_interval;
	}
	virtual u32 getTriggerChance()
	{
		return m_trigger_chance;
	}
	virtual bool getSimpleCatchUp()
	{
		return m_simple_catch_up;
	}
	virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
			u32 active_object_count, u32 active_object_count_wider);
};

class LuaLBM : public LoadingBlockModifierDef
{
private:
	int m_id;
public:
	LuaLBM(lua_State *L, int id,
			const std::set<std::string> &trigger_contents,
			const std::string &name,
			bool run_at_every_load):
		m_id(id)
	{
		this->run_at_every_load = run_at_every_load;
		this->trigger_contents = trigger_contents;
		this->name = name;
	}
	virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n);
};

struct ScriptCallbackState {
	ServerScripting *script;
	int callback_ref;
	int args_ref;
	unsigned int refcount;
	std::string origin;
};

#endif /* L_ENV_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 "lua_api/l_internal.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "lua_api/l_http.h"
#include "httpfetch.h"
#include "settings.h"
#include "debug.h"
#include "log.h"

#include <algorithm>
#include <iomanip>
#include <cctype>

#define HTTP_API(name) \
	lua_pushstring(L, #name); \
	lua_pushcfunction(L, l_http_##name); \
	lua_settable(L, -3);

#if USE_CURL
void ModApiHttp::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req)
{
	luaL_checktype(L, 1, LUA_TTABLE);

	req.caller = httpfetch_caller_alloc_secure();
	getstringfield(L, 1, "url", req.url);
	lua_getfield(L, 1, "user_agent");
	if (lua_isstring(L, -1))
		req.useragent = getstringfield_default(L, 1, "user_agent", "");
	lua_pop(L, 1);
	req.multipart = getboolfield_default(L, 1, "multipart", false);
	req.timeout = getintfield_default(L, 1, "timeout", 3) * 1000;

	// post_data: if table, post form data, otherwise raw data
	lua_getfield(L, 1, "post_data");
	if (lua_istable(L, 2)) {
		lua_pushnil(L);
		while (lua_next(L, 2) != 0)
		{
			req.post_fields[luaL_checkstring(L, -2)] = luaL_checkstring(L, -1);
			lua_pop(L, 1);
		}
	} else if (lua_isstring(L, 2)) {
		req.post_data = lua_tostring(L, 2);
	}
	lua_pop(L, 1);

	lua_getfield(L, 1, "extra_headers");
	if (lua_istable(L, 2)) {
		lua_pushnil(L);
		while (lua_next(L, 2) != 0)
		{
			const char *header = luaL_checkstring(L, -1);
			req.extra_headers.push_back(header);
			lua_pop(L, 1);
		}
	}
	lua_pop(L, 1);
}

void ModApiHttp::push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed)
{
	lua_newtable(L);
	setboolfield(L, -1, "succeeded", res.succeeded);
	setboolfield(L, -1, "timeout", res.timeout);
	setboolfield(L, -1, "completed", completed);
	setintfield(L, -1, "code", res.response_code);
	setstringfield(L, -1, "data", res.data.c_str());
}

// http_api.fetch_async(HTTPRequest definition)
int ModApiHttp::l_http_fetch_async(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	HTTPFetchRequest req;
	read_http_fetch_request(L, req);

	actionstream << "Mod performs HTTP request with URL " << req.url << std::endl;
	httpfetch_async(req);

	// Convert handle to hex string since lua can't handle 64-bit integers
	std::stringstream handle_conversion_stream;
	handle_conversion_stream << std::hex << req.caller;
	std::string caller_handle(handle_conversion_stream.str());

	lua_pushstring(L, caller_handle.c_str());
	return 1;
}

// http_api.fetch_async_get(handle)
int ModApiHttp::l_http_fetch_async_get(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	std::string handle_str = luaL_checkstring(L, 1);

	// Convert hex string back to 64-bit handle
	u64 handle;
	std::stringstream handle_conversion_stream;
	handle_conversion_stream << std::hex << handle_str;
	handle_conversion_stream >> handle;

	HTTPFetchResult res;
	bool completed = httpfetch_async_get(handle, res);

	push_http_fetch_result(L, res, completed);

	return 1;
}

int ModApiHttp::l_request_http_api(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	// We have to make sure that this function is being called directly by
	// a mod, otherwise a malicious mod could override this function and
	// steal its return value.
	lua_Debug info;

	// Make sure there's only one item below this function on the stack...
	if (lua_getstack(L, 2, &info)) {
		return 0;
	}
	FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
	FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");

	// ...and that that item is the main file scope.
	if (strcmp(info.what, "main") != 0) {
		return 0;
	}

	// Mod must be listed in secure.http_mods or secure.trusted_mods
	lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
	if (!lua_isstring(L, -1)) {
		return 0;
	}

	const char *mod_name = lua_tostring(L, -1);
	std::string http_mods = g_settings->get("secure.http_mods");
	http_mods.erase(std::remove(http_mods.begin(), http_mods.end(), ' '), http_mods.end());
	std::vector<std::string> mod_list_http = str_split(http_mods, ',');

	std::string trusted_mods = g_settings->get("secure.trusted_mods");
	trusted_mods.erase(std::remove(trusted_mods.begin(), trusted_mods.end(), ' '), trusted_mods.end());
	std::vector<std::string> mod_list_trusted = str_split(trusted_mods, ',');

	mod_list_http.insert(mod_list_http.end(), mod_list_trusted.begin(), mod_list_trusted.end());
	if (std::find(mod_list_http.begin(), mod_list_http.end(), mod_name) == mod_list_http.end()) {
		lua_pushnil(L);
		return 1;
	}

	lua_getglobal(L, "core");
	lua_getfield(L, -1, "http_add_fetch");

	lua_newtable(L);
	HTTP_API(fetch_async);
	HTTP_API(fetch_async_get);

	// Stack now looks like this:
	// <core.http_add_fetch> <table with fetch_async, fetch_async_get>
	// Now call core.http_add_fetch to append .fetch(request, callback) to table
	lua_call(L, 1, 1);

	return 1;
}
#endif

void ModApiHttp::Initialize(lua_State *L, int top)
{
#if USE_CURL
	API_FCT(request_http_api);
#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.
*/

#ifndef L_HTTP_H_
#define L_HTTP_H_

#include "lua_api/l_base.h"
#include "config.h"

struct HTTPFetchRequest;
struct HTTPFetchResult;

class ModApiHttp : public ModApiBase {
private:
#if USE_CURL
	// Helpers for HTTP fetch functions
	static void read_http_fetch_request(lua_State *L, HTTPFetchRequest &req);
	static void push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed = true);

	// http_fetch_async({url=, timeout=, post_data=})
	static int l_http_fetch_async(lua_State *L);

	// http_fetch_async_get(handle)
	static int l_http_fetch_async_get(lua_State *L);

	// request_http_api()
	static int l_request_http_api(lua_State *L);
#endif

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

#endif /* L_HTTP_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.
*/

/******************************************************************************/
/******************************************************************************/
/* WARNING!!!! do NOT add this header in any include file or any code file    */
/*             not being a modapi file!!!!!!!!                                */
/******************************************************************************/
/******************************************************************************/

#ifndef L_INTERNAL_H_
#define L_INTERNAL_H_

#include "common/c_internal.h"

#define luamethod(class, name) {#name, class::l_##name}
#define luamethod_aliased(class, name, alias) {#name, class::l_##name}, {#alias, class::l_##name}
#define API_FCT(name) registerFunction(L, #name, l_##name, top)

#define MAP_LOCK_REQUIRED
#define NO_MAP_LOCK_REQUIRED

#define GET_ENV_PTR_NO_MAP_LOCK                              \
	ServerEnvironment *env = (ServerEnvironment *)getEnv(L); \
	if (env == NULL)                                         \
		return 0

#define GET_ENV_PTR         \
	MAP_LOCK_REQUIRED;      \
	GET_ENV_PTR_NO_MAP_LOCK

#endif /* L_INTERNAL_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_inventory.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 "player.h"

/*
	InvRef
*/
InvRef* InvRef::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 *(InvRef**)ud;  // unbox pointer
}

Inventory* InvRef::getinv(lua_State *L, InvRef *ref)
{
	return getServer(L)->getInventory(ref->m_loc);
}

InventoryList* InvRef::getlist(lua_State *L, InvRef *ref,
		const char *listname)
{
	NO_MAP_LOCK_REQUIRED;
	Inventory *inv = getinv(L, ref);
	if(!inv)
		return NULL;
	return inv->getList(listname);
}

void InvRef::reportInventoryChange(lua_State *L, InvRef *ref)
{
	// Inform other things that the inventory has changed
	getServer(L)->setInventoryModified(ref->m_loc);
}

// Exported functions

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

// is_empty(self, listname) -> true/false
int InvRef::l_is_empty(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	InventoryList *list = getlist(L, ref, listname);
	if(list && list->getUsedSlots() > 0){
		lua_pushboolean(L, false);
	} else {
		lua_pushboolean(L, true);
	}
	return 1;
}

// get_size(self, listname)
int InvRef::l_get_size(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	InventoryList *list = getlist(L, ref, listname);
	if(list){
		lua_pushinteger(L, list->getSize());
	} else {
		lua_pushinteger(L, 0);
	}
	return 1;
}

// get_width(self, listname)
int InvRef::l_get_width(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	InventoryList *list = getlist(L, ref, listname);
	if(list){
		lua_pushinteger(L, list->getWidth());
	} else {
		lua_pushinteger(L, 0);
	}
	return 1;
}

// set_size(self, listname, size)
int InvRef::l_set_size(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);

	int newsize = luaL_checknumber(L, 3);
	if (newsize < 0) {
		lua_pushboolean(L, false);
		return 1;
	}

	Inventory *inv = getinv(L, ref);
	if(inv == NULL){
		lua_pushboolean(L, false);
		return 1;
	}
	if(newsize == 0){
		inv->deleteList(listname);
		reportInventoryChange(L, ref);
		lua_pushboolean(L, true);
		return 1;
	}
	InventoryList *list = inv->getList(listname);
	if(list){
		list->setSize(newsize);
	} else {
		list = inv->addList(listname, newsize);
		if (!list)
		{
			lua_pushboolean(L, false);
			return 1;
		}
	}
	reportInventoryChange(L, ref);
	lua_pushboolean(L, true);
	return 1;
}

// set_width(self, listname, size)
int InvRef::l_set_width(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	int newwidth = luaL_checknumber(L, 3);
	Inventory *inv = getinv(L, ref);
	if(inv == NULL){
		return 0;
	}
	InventoryList *list = inv->getList(listname);
	if(list){
		list->setWidth(newwidth);
	} else {
		return 0;
	}
	reportInventoryChange(L, ref);
	return 0;
}

// get_stack(self, listname, i) -> itemstack
int InvRef::l_get_stack(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	int i = luaL_checknumber(L, 3) - 1;
	InventoryList *list = getlist(L, ref, listname);
	ItemStack item;
	if(list != NULL && i >= 0 && i < (int) list->getSize())
		item = list->getItem(i);
	LuaItemStack::create(L, item);
	return 1;
}

// set_stack(self, listname, i, stack) -> true/false
int InvRef::l_set_stack(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	int i = luaL_checknumber(L, 3) - 1;
	ItemStack newitem = read_item(L, 4, getServer(L)->idef());
	InventoryList *list = getlist(L, ref, listname);
	if(list != NULL && i >= 0 && i < (int) list->getSize()){
		list->changeItem(i, newitem);
		reportInventoryChange(L, ref);
		lua_pushboolean(L, true);
	} else {
		lua_pushboolean(L, false);
	}
	return 1;
}

// get_list(self, listname) -> list or nil
int InvRef::l_get_list(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	Inventory *inv = getinv(L, ref);
	if(inv){
		push_inventory_list(L, inv, listname);
	} else {
		lua_pushnil(L);
	}
	return 1;
}

// set_list(self, listname, list)
int InvRef::l_set_list(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	Inventory *inv = getinv(L, ref);
	if(inv == NULL){
		return 0;
	}
	InventoryList *list = inv->getList(listname);
	if(list)
		read_inventory_list(L, 3, inv, listname,
				getServer(L), list->getSize());
	else
		read_inventory_list(L, 3, inv, listname, getServer(L));
	reportInventoryChange(L, ref);
	return 0;
}

// get_lists(self) -> list of InventoryLists
int InvRef::l_get_lists(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	Inventory *inv = getinv(L, ref);
	if (!inv) {
		return 0;
	}
	std::vector<const InventoryList*> lists = inv->getLists();
	std::vector<const InventoryList*>::iterator iter = lists.begin();
	lua_createtable(L, 0, lists.size());
	for (; iter != lists.end(); iter++) {
		const char* name = (*iter)->getName().c_str();
		lua_pushstring(L, name);
		push_inventory_list(L, inv, name);
		lua_rawset(L, -3);
	}
	return 1;
}

// set_lists(self, lists)
int InvRef::l_set_lists(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	Inventory *inv = getinv(L, ref);
	if (!inv) {
		return 0;
	}

	// Make a temporary inventory in case reading fails
	Inventory *tempInv(inv);
	tempInv->clear();

	Server *server = getServer(L);

	lua_pushnil(L);
	while (lua_next(L, 2)) {
		const char *listname = lua_tostring(L, -2);
		read_inventory_list(L, -1, tempInv, listname, server);
		lua_pop(L, 1);
	}
	inv = tempInv;
	return 0;
}

// add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
// Returns the leftover stack
int InvRef::l_add_item(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	ItemStack item = read_item(L, 3, getServer(L)->idef());
	InventoryList *list = getlist(L, ref, listname);
	if(list){
		ItemStack leftover = list->addItem(item);
		if(leftover.count != item.count)
			reportInventoryChange(L, ref);
		LuaItemStack::create(L, leftover);
	} else {
		LuaItemStack::create(L, item);
	}
	return 1;
}

// room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
// Returns true if the item completely fits into the list
int InvRef::l_room_for_item(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	ItemStack item = read_item(L, 3, getServer(L)->idef());
	InventoryList *list = getlist(L, ref, listname);
	if(list){
		lua_pushboolean(L, list->roomForItem(item));
	} else {
		lua_pushboolean(L, false);
	}
	return 1;
}

// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
// Returns true if the list contains the given count of the given item name
int InvRef::l_contains_item(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	ItemStack item = read_item(L, 3, getServer(L)->idef());
	InventoryList *list = getlist(L, ref, listname);
	if(list){
		lua_pushboolean(L, list->containsItem(item));
	} else {
		lua_pushboolean(L, false);
	}
	return 1;
}

// remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
// Returns the items that were actually removed
int InvRef::l_remove_item(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const char *listname = luaL_checkstring(L, 2);
	ItemStack item = read_item(L, 3, getServer(L)->idef());
	InventoryList *list = getlist(L, ref, listname);
	if(list){
		ItemStack removed = list->removeItem(item);
		if(!removed.empty())
			reportInventoryChange(L, ref);
		LuaItemStack::create(L, removed);
	} else {
		LuaItemStack::create(L, ItemStack());
	}
	return 1;
}

// get_location() -> location (like get_inventory(location))
int InvRef::l_get_location(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *ref = checkobject(L, 1);
	const InventoryLocation &loc = ref->m_loc;
	switch(loc.type){
	case InventoryLocation::PLAYER:
		lua_newtable(L);
		lua_pushstring(L, "player");
		lua_setfield(L, -2, "type");
		lua_pushstring(L, loc.name.c_str());
		lua_setfield(L, -2, "name");
		return 1;
	case InventoryLocation::NODEMETA:
		lua_newtable(L);
		lua_pushstring(L, "node");
		lua_setfield(L, -2, "type");
		push_v3s16(L, loc.p);
		lua_setfield(L, -2, "pos");
		return 1;
	case InventoryLocation::DETACHED:
		lua_newtable(L);
		lua_pushstring(L, "detached");
		lua_setfield(L, -2, "type");
		lua_pushstring(L, loc.name.c_str());
		lua_setfield(L, -2, "name");
		return 1;
	case InventoryLocation::UNDEFINED:
	case InventoryLocation::CURRENT_PLAYER:
		break;
	}
	lua_newtable(L);
	lua_pushstring(L, "undefined");
	lua_setfield(L, -2, "type");
	return 1;
}

InvRef::InvRef(const InventoryLocation &loc):
	m_loc(loc)
{
}

InvRef::~InvRef()
{
}

// Creates an InvRef and leaves it on top of stack
// Not callable from Lua; all references are created on the C side.
void InvRef::create(lua_State *L, const InventoryLocation &loc)
{
	NO_MAP_LOCK_REQUIRED;
	InvRef *o = new InvRef(loc);
	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
	luaL_getmetatable(L, className);
	lua_setmetatable(L, -2);
}
void InvRef::createPlayer(lua_State *L, RemotePlayer *player)
{
	NO_MAP_LOCK_REQUIRED;
	InventoryLocation loc;
	loc.setPlayer(player->getName());
	create(L, loc);
}
void InvRef::createNodeMeta(lua_State *L, v3s16 p)
{
	InventoryLocation loc;
	loc.setNodeMeta(p);
	create(L, loc);
}

void InvRef::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

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

const char InvRef::className[] = "InvRef";
const luaL_Reg InvRef::methods[] = {
	luamethod(InvRef, is_empty),
	luamethod(InvRef, get_size),
	luamethod(InvRef, set_size),
	luamethod(InvRef, get_width),
	luamethod(InvRef, set_width),
	luamethod(InvRef, get_stack),
	luamethod(InvRef, set_stack),
	luamethod(InvRef, get_list),
	luamethod(InvRef, set_list),
	luamethod(InvRef, get_lists),
	luamethod(InvRef, set_lists),
	luamethod(InvRef, add_item),
	luamethod(InvRef, room_for_item),
	luamethod(InvRef, contains_item),
	luamethod(InvRef, remove_item),
	luamethod(InvRef, get_location),
	{0,0}
};

// get_inventory(location)
int ModApiInventory::l_get_inventory(lua_State *L)
{
	InventoryLocation loc;

	std::string type = checkstringfield(L, 1, "type");

	if(type == "node"){
		MAP_LOCK_REQUIRED;
		lua_getfield(L, 1, "pos");
		v3s16 pos = check_v3s16(L, -1);
		loc.setNodeMeta(pos);

		if(getServer(L)->getInventory(loc) != NULL)
			InvRef::create(L, loc);
		else
			lua_pushnil(L);
		return 1;
	} else {
		NO_MAP_LOCK_REQUIRED;
		if(type == "player"){
			std::string name = checkstringfield(L, 1, "name");
			loc.setPlayer(name);
		} else if(type == "detached"){
			std::string name = checkstringfield(L, 1, "name");
			loc.setDetached(name);
		}

		if(getServer(L)->getInventory(loc) != NULL)
			InvRef::create(L, loc);
		else
			lua_pushnil(L);
		return 1;
		// END NO_MAP_LOCK_REQUIRED;
	}
}

// create_detached_inventory_raw(name, [player_name])
int ModApiInventory::l_create_detached_inventory_raw(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	const char *name = luaL_checkstring(L, 1);
	const char *player = lua_isstring(L, 2) ? lua_tostring(L, 2) : "";
	if (getServer(L)->createDetachedInventory(name, player) != NULL) {
		InventoryLocation loc;
		loc.setDetached(name);
		InvRef::create(L, loc);
	} else {
		lua_pushnil(L);
	}
	return 1;
}

void ModApiInventory::Initialize(lua_State *L, int top)
{
	API_FCT(create_detached_inventory_raw);
	API_FCT(get_inventory);
}


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_INVENTORY_H_
#define L_INVENTORY_H_

#include "lua_api/l_base.h"

#include "inventory.h"
#include "inventorymanager.h"

class RemotePlayer;

/*
	InvRef
*/

class InvRef : public ModApiBase {
private:
	InventoryLocation m_loc;

	static const char className[];
	static const luaL_Reg methods[];

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

	static Inventory* getinv(lua_State *L, InvRef *ref);

	static InventoryList* getlist(lua_State *L, InvRef *ref,
			const char *listname);

	static void reportInventoryChange(lua_State *L, InvRef *ref);

	// Exported functions

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

	// is_empty(self, listname) -> true/false
	static int l_is_empty(lua_State *L);

	// get_size(self, listname)
	static int l_get_size(lua_State *L);

	// get_width(self, listname)
	static int l_get_width(lua_State *L);

	// set_size(self, listname, size)
	static int l_set_size(lua_State *L);

	// set_width(self, listname, size)
	static int l_set_width(lua_State *L);

	// get_stack(self, listname, i) -> itemstack
	static int l_get_stack(lua_State *L);

	// set_stack(self, listname, i, stack) -> true/false
	static int l_set_stack(lua_State *L);

	// get_list(self, listname) -> list or nil
	static int l_get_list(lua_State *L);

	// set_list(self, listname, list)
	static int l_set_list(lua_State *L);

	// get_lists(self) -> list of InventoryLists
	static int l_get_lists(lua_State *L);

	// set_lists(self, lists)
	static int l_set_lists(lua_State *L);

	// add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
	// Returns the leftover stack
	static int l_add_item(lua_State *L);

	// room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
	// Returns true if the item completely fits into the list
	static int l_room_for_item(lua_State *L);

	// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
	// Returns true if the list contains the given count of the given item name
	static int l_contains_item(lua_State *L);

	// remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
	// Returns the items that were actually removed
	static int l_remove_item(lua_State *L);

	// get_location() -> location (like get_inventory(location))
	static int l_get_location(lua_State *L);

public:
	InvRef(const InventoryLocation &loc);

	~InvRef();

	// Creates an InvRef and leaves it on top of stack
	// Not callable from Lua; all references are created on the C side.
	static void create(lua_State *L, const InventoryLocation &loc);
	static void createPlayer(lua_State *L, RemotePlayer *player);
	static void createNodeMeta(lua_State *L, v3s16 p);
	static void Register(lua_State *L);
};

class ModApiInventory : public ModApiBase {
private:
	static int l_create_detached_inventory_raw(lua_State *L);

	static int l_get_inventory(lua_State *L);

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

#endif /* L_INVENTORY_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_item.h"
#include "lua_api/l_itemstackmeta.h"
#include "lua_api/l_internal.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "itemdef.h"
#include "nodedef.h"
#include "server.h"
#include "content_sao.h"
#include "inventory.h"
#include "log.h"

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

// is_empty(self) -> true/false
int LuaItemStack::l_is_empty(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	lua_pushboolean(L, item.empty());
	return 1;
}

// get_name(self) -> string
int LuaItemStack::l_get_name(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	lua_pushstring(L, item.name.c_str());
	return 1;
}

// set_name(self, name)
int LuaItemStack::l_set_name(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;

	bool status = true;
	item.name = luaL_checkstring(L, 2);
	if (item.name == "" || item.empty()) {
		item.clear();
		status = false;
	}

	lua_pushboolean(L, status);
	return 1;
}

// get_count(self) -> number
int LuaItemStack::l_get_count(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	lua_pushinteger(L, item.count);
	return 1;
}

// set_count(self, number)
int LuaItemStack::l_set_count(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;

	bool status;
	lua_Integer count = luaL_checkinteger(L, 2);
	if (count > 0 && count <= 65535) {
		item.count = count;
		status = true;
	} else {
		item.clear();
		status = false;
	}

	lua_pushboolean(L, status);
	return 1;
}

// get_wear(self) -> number
int LuaItemStack::l_get_wear(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	lua_pushinteger(L, item.wear);
	return 1;
}

// set_wear(self, number)
int LuaItemStack::l_set_wear(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;

	bool status;
	lua_Integer wear = luaL_checkinteger(L, 2);
	if (wear <= 65535) {
		item.wear = wear;
		status = true;
	} else {
		item.clear();
		status = false;
	}

	lua_pushboolean(L, status);
	return 1;
}

// get_meta(self) -> string
int LuaItemStack::l_get_meta(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStackMetaRef::create(L, &o->m_stack);
	return 1;
}

// DEPRECATED
// get_metadata(self) -> string
int LuaItemStack::l_get_metadata(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	const std::string &value = item.metadata.getString("");
	lua_pushlstring(L, value.c_str(), value.size());
	return 1;
}

// DEPRECATED
// set_metadata(self, string)
int LuaItemStack::l_set_metadata(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;

	size_t len = 0;
	const char *ptr = luaL_checklstring(L, 2, &len);
	item.metadata.setString("", std::string(ptr, len));

	lua_pushboolean(L, true);
	return 1;
}

// clear(self) -> true
int LuaItemStack::l_clear(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	o->m_stack.clear();
	lua_pushboolean(L, true);
	return 1;
}

// replace(self, itemstack or itemstring or table or nil) -> true
int LuaItemStack::l_replace(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	o->m_stack = read_item(L, 2, getGameDef(L)->idef());
	lua_pushboolean(L, true);
	return 1;
}

// to_string(self) -> string
int LuaItemStack::l_to_string(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	std::string itemstring = o->m_stack.getItemString();
	lua_pushstring(L, itemstring.c_str());
	return 1;
}

// to_table(self) -> table or nil
int LuaItemStack::l_to_table(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	const ItemStack &item = o->m_stack;
	if(item.empty())
	{
		lua_pushnil(L);
	}
	else
	{
		lua_newtable(L);
		lua_pushstring(L, item.name.c_str());
		lua_setfield(L, -2, "name");
		lua_pushinteger(L, item.count);
		lua_setfield(L, -2, "count");
		lua_pushinteger(L, item.wear);
		lua_setfield(L, -2, "wear");

		const std::string &metadata_str = item.metadata.getString("");
		lua_pushlstring(L, metadata_str.c_str(), metadata_str.size());
		lua_setfield(L, -2, "metadata");

		lua_newtable(L);
		const StringMap &fields = item.metadata.getStrings();
		for (StringMap::const_iterator it = fields.begin();
				it != fields.end(); ++it) {
			const std::string &name = it->first;
			if (name.empty())
				continue;
			const std::string &value = it->second;
			lua_pushlstring(L, name.c_str(), name.size());
			lua_pushlstring(L, value.c_str(), value.size());
			lua_settable(L, -3);
		}
		lua_setfield(L, -2, "meta");
	}
	return 1;
}

// get_stack_max(self) -> number
int LuaItemStack::l_get_stack_max(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	lua_pushinteger(L, item.getStackMax(getGameDef(L)->idef()));
	return 1;
}

// get_free_space(self) -> number
int LuaItemStack::l_get_free_space(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	lua_pushinteger(L, item.freeSpace(getGameDef(L)->idef()));
	return 1;
}

// is_known(self) -> true/false
// Checks if the item is defined.
int LuaItemStack::l_is_known(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	bool is_known = item.isKnown(getGameDef(L)->idef());
	lua_pushboolean(L, is_known);
	return 1;
}

// get_definition(self) -> table
// Returns the item definition table from registered_items,
// or a fallback one (name="unknown")
int LuaItemStack::l_get_definition(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;

	// Get registered_items[name]
	lua_getglobal(L, "core");
	lua_getfield(L, -1, "registered_items");
	luaL_checktype(L, -1, LUA_TTABLE);
	lua_getfield(L, -1, item.name.c_str());
	if(lua_isnil(L, -1))
	{
		lua_pop(L, 1);
		lua_getfield(L, -1, "unknown");
	}
	return 1;
}

// get_tool_capabilities(self) -> table
// Returns the effective tool digging properties.
// Returns those of the hand ("") if this item has none associated.
int LuaItemStack::l_get_tool_capabilities(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	const ToolCapabilities &prop =
		item.getToolCapabilities(getGameDef(L)->idef());
	push_tool_capabilities(L, prop);
	return 1;
}

// add_wear(self, amount) -> true/false
// The range for "amount" is [0,65535]. Wear is only added if the item
// is a tool. Adding wear might destroy the item.
// Returns true if the item is (or was) a tool.
int LuaItemStack::l_add_wear(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	int amount = lua_tointeger(L, 2);
	bool result = item.addWear(amount, getGameDef(L)->idef());
	lua_pushboolean(L, result);
	return 1;
}

// add_item(self, itemstack or itemstring or table or nil) -> itemstack
// Returns leftover item stack
int LuaItemStack::l_add_item(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	ItemStack newitem = read_item(L, -1, getGameDef(L)->idef());
	ItemStack leftover = item.addItem(newitem, getGameDef(L)->idef());
	create(L, leftover);
	return 1;
}

// item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack
// First return value is true iff the new item fits fully into the stack
// Second return value is the would-be-left-over item stack
int LuaItemStack::l_item_fits(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	ItemStack newitem = read_item(L, 2, getGameDef(L)->idef());
	ItemStack restitem;
	bool fits = item.itemFits(newitem, &restitem, getGameDef(L)->idef());
	lua_pushboolean(L, fits);  // first return value
	create(L, restitem);       // second return value
	return 2;
}

// take_item(self, takecount=1) -> itemstack
int LuaItemStack::l_take_item(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	u32 takecount = 1;
	if(!lua_isnone(L, 2))
		takecount = luaL_checkinteger(L, 2);
	ItemStack taken = item.takeItem(takecount);
	create(L, taken);
	return 1;
}

// peek_item(self, peekcount=1) -> itemstack
int LuaItemStack::l_peek_item(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = checkobject(L, 1);
	ItemStack &item = o->m_stack;
	u32 peekcount = 1;
	if(!lua_isnone(L, 2))
		peekcount = lua_tointeger(L, 2);
	ItemStack peekaboo = item.peekItem(peekcount);
	create(L, peekaboo);
	return 1;
}

LuaItemStack::LuaItemStack(const ItemStack &item):
	m_stack(item)
{
}

LuaItemStack::~LuaItemStack()
{
}

const ItemStack& LuaItemStack::getItem() const
{
	return m_stack;
}
ItemStack& LuaItemStack::getItem()
{
	return m_stack;
}

// LuaItemStack(itemstack or itemstring or table or nil)
// Creates an LuaItemStack and leaves it on top of stack
int LuaItemStack::create_object(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	ItemStack item = read_item(L, 1, getGameDef(L)->idef());
	LuaItemStack *o = new LuaItemStack(item);
	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
	luaL_getmetatable(L, className);
	lua_setmetatable(L, -2);
	return 1;
}
// Not callable from Lua
int LuaItemStack::create(lua_State *L, const ItemStack &item)
{
	NO_MAP_LOCK_REQUIRED;
	LuaItemStack *o = new LuaItemStack(item);
	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
	luaL_getmetatable(L, className);
	lua_setmetatable(L, -2);
	return 1;
}

LuaItemStack* LuaItemStack::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 *(LuaItemStack**)ud;  // unbox pointer
}

void LuaItemStack::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 (LuaItemStack(itemstack or itemstring or table or nil))
	lua_register(L, className, create_object);
}

const char LuaItemStack::className[] = "ItemStack";
const luaL_Reg LuaItemStack::methods[] = {
	luamethod(LuaItemStack, is_empty),
	luamethod(LuaItemStack, get_name),
	luamethod(LuaItemStack, set_name),
	luamethod(LuaItemStack, get_count),
	luamethod(LuaItemStack, set_count),
	luamethod(LuaItemStack, get_wear),
	luamethod(LuaItemStack, set_wear),
	luamethod(LuaItemStack, get_meta),
	luamethod(LuaItemStack, get_metadata),
	luamethod(LuaItemStack, set_metadata),
	luamethod(LuaItemStack, clear),
	luamethod(LuaItemStack, replace),
	luamethod(LuaItemStack, to_string),
	luamethod(LuaItemStack, to_table),
	luamethod(LuaItemStack, get_stack_max),
	luamethod(LuaItemStack, get_free_space),
	luamethod(LuaItemStack, is_known),
	luamethod(LuaItemStack, get_definition),
	luamethod(LuaItemStack, get_tool_capabilities),
	luamethod(LuaItemStack, add_wear),
	luamethod(LuaItemStack, add_item),
	luamethod(LuaItemStack, item_fits),
	luamethod(LuaItemStack, take_item),
	luamethod(LuaItemStack, peek_item),
	{0,0}
};

/*
	ItemDefinition
*/

// register_item_raw({lots of stuff})
int ModApiItemMod::l_register_item_raw(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	luaL_checktype(L, 1, LUA_TTABLE);
	int table = 1;

	// Get the writable item and node definition managers from the server
	IWritableItemDefManager *idef =
			getServer(L)->getWritableItemDefManager();
	IWritableNodeDefManager *ndef =
			getServer(L)->getWritableNodeDefManager();

	// Check if name is defined
	std::string name;
	lua_getfield(L, table, "name");
	if(lua_isstring(L, -1)){
		name = lua_tostring(L, -1);
		verbosestream<<"register_item_raw: "<<name<<std::endl;
	} else {
		throw LuaError("register_item_raw: name is not defined or not a string");
	}

	// Check if on_use is defined

	ItemDefinition def;
	// Set a distinctive default value to check if this is set
	def.node_placement_prediction = "__default";

	// Read the item definition
	read_item_definition(L, table, def, def);

	// Default to having client-side placement prediction for nodes
	// ("" in item definition sets it off)
	if(def.node_placement_prediction == "__default"){
		if(def.type == ITEM_NODE)
			def.node_placement_prediction = name;
		else
			def.node_placement_prediction = "";
	}

	// Register item definition
	idef->registerItem(def);

	// Read the node definition (content features) and register it
	if(def.type == ITEM_NODE){
		ContentFeatures f = read_content_features(L, table);
		content_t id = ndef->set(f.name, f);

		if(id > MAX_REGISTERED_CONTENT){
			throw LuaError("Number of registerable nodes ("
					+ itos(MAX_REGISTERED_CONTENT+1)
					+ ") exceeded (" + name + ")");
		}
	}

	return 0; /* number of results */
}

// unregister_item(name)
int ModApiItemMod::l_unregister_item_raw(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::string name = luaL_checkstring(L, 1);

	IWritableItemDefManager *idef =
			getServer(L)->getWritableItemDefManager();

	// Unregister the node
	if (idef->get(name).type == ITEM_NODE) {
		IWritableNodeDefManager *ndef =
			getServer(L)->getWritableNodeDefManager();
		ndef->removeNode(name);
	}

	idef->unregisterItem(name);

	return 0; /* number of results */
}

// register_alias_raw(name, convert_to_name)
int ModApiItemMod::l_register_alias_raw(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::string name = luaL_checkstring(L, 1);
	std::string convert_to = luaL_checkstring(L, 2);

	// Get the writable item definition manager from the server
	IWritableItemDefManager *idef =
			getServer(L)->getWritableItemDefManager();

	idef->registerAlias(name, convert_to);

	return 0; /* number of results */
}

// get_content_id(name)
int ModApiItemMod::l_get_content_id(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	std::string name = luaL_checkstring(L, 1);

	INodeDefManager *ndef = getGameDef(L)->getNodeDefManager();
	content_t c = ndef->getId(name);

	lua_pushinteger(L, c);
	return 1; /* number of results */
}

// get_name_from_content_id(name)
int ModApiItemMod::l_get_name_from_content_id(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;
	content_t c = luaL_checkint(L, 1);

	INodeDefManager *ndef = getGameDef(L)->getNodeDefManager();
	const char *name = ndef->get(c).name.c_str();

	lua_pushstring(L, name);
	return 1; /* number of results */
}

void ModApiItemMod::Initialize(lua_State *L, int top)
{
	API_FCT(register_item_raw);
	API_FCT(unregister_item_raw);
	API_FCT(register_alias_raw);
	API_FCT(get_content_id);
	API_FCT(get_name_from_content_id);
}


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_ITEM_H_
#define L_ITEM_H_

#include "lua_api/l_base.h"
#include "inventory.h"  // ItemStack

class LuaItemStack : public ModApiBase {
private:
	ItemStack m_stack;

	static const char className[];
	static const luaL_Reg methods[];

	// Exported functions

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

	// is_empty(self) -> true/false
	static int l_is_empty(lua_State *L);

	// get_name(self) -> string
	static int l_get_name(lua_State *L);

	// set_name(self, name)
	static int l_set_name(lua_State *L);

	// get_count(self) -> number
	static int l_get_count(lua_State *L);

	// set_count(self, number)
	static int l_set_count(lua_State *L);

	// get_wear(self) -> number
	static int l_get_wear(lua_State *L);

	// set_wear(self, number)
	static int l_set_wear(lua_State *L);

	// get_meta(self) -> string
	static int l_get_meta(lua_State *L);

	// DEPRECATED
	// get_metadata(self) -> string
	static int l_get_metadata(lua_State *L);

	// DEPRECATED
	// set_metadata(self, string)
	static int l_set_metadata(lua_State *L);

	// clear(self) -> true
	static int l_clear(lua_State *L);

	// replace(self, itemstack or itemstring or table or nil) -> true
	static int l_replace(lua_State *L);

	// to_string(self) -> string
	static int l_to_string(lua_State *L);

	// to_table(self) -> table or nil
	static int l_to_table(lua_State *L);

	// get_stack_max(self) -> number
	static int l_get_stack_max(lua_State *L);

	// get_free_space(self) -> number
	static int l_get_free_space(lua_State *L);

	// is_known(self) -> true/false
	// Checks if the item is defined.
	static int l_is_known(lua_State *L);

	// get_definition(self) -> table
	// Returns the item definition table from core.registered_items,
	// or a fallback one (name="unknown")
	static int l_get_definition(lua_State *L);

	// get_tool_capabilities(self) -> table
	// Returns the effective tool digging properties.
	// Returns those of the hand ("") if this item has none associated.
	static int l_get_tool_capabilities(lua_State *L);

	// add_wear(self, amount) -> true/false
	// The range for "amount" is [0,65535]. Wear is only added if the item
	// is a tool. Adding wear might destroy the item.
	// Returns true if the item is (or was) a tool.
	static int l_add_wear(lua_State *L);

	// add_item(self, itemstack or itemstring or table or nil) -> itemstack
	// Returns leftover item stack
	static int l_add_item(lua_State *L);

	// item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack
	// First return value is true iff the new item fits fully into the stack
	// Second return value is the would-be-left-over item stack
	static int l_item_fits(lua_State *L);

	// take_item(self, takecount=1) -> itemstack
	static int l_take_item(lua_State *L);

	// peek_item(self, peekcount=1) -> itemstack
	static int l_peek_item(lua_State *L);

public:
	LuaItemStack(const ItemStack &item);
	~LuaItemStack();

	const ItemStack& getItem() const;
	ItemStack& getItem();

	// LuaItemStack(itemstack or itemstring or table or nil)
	// Creates an LuaItemStack and leaves it on top of stack
	static int create_object(lua_State *L);
	// Not callable from Lua
	static int create(lua_State *L, const ItemStack &item);
	static LuaItemStack* checkobject(lua_State *L, int narg);
	static void Register(lua_State *L);

};

class ModApiItemMod : public ModApiBase {
private:
	static int l_register_item_raw(lua_State *L);
	static int l_unregister_item_raw(lua_State *L);
	static int l_register_alias_raw(lua_State *L);
	static int l_get_content_id(lua_State *L);
	static int l_get_name_from_content_id(lua_State *L);
public:
	static void Initialize(lua_State *L, int top);
};

#endif /* L_ITEM_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_itemstackmeta.h"
#include "lua_api/l_internal.h"
#include "common/c_content.h"

/*
	NodeMetaRef
*/
ItemStackMetaRef* ItemStackMetaRef::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 *(ItemStackMetaRef**)ud;  // unbox pointer
}

Metadata* ItemStackMetaRef::getmeta(bool auto_create)
{
	return &istack->metadata;
}

void ItemStackMetaRef::clearMeta()
{
	istack->metadata.clear();
}

void ItemStackMetaRef::reportMetadataChange()
{
	// TODO
}

// Exported functions

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

// Creates an NodeMetaRef and leaves it on top of stack
// Not callable from Lua; all references are created on the C side.
void ItemStackMetaRef::create(lua_State *L, ItemStack *istack)
{
	ItemStackMetaRef *o = new ItemStackMetaRef(istack);
	//infostream<<"NodeMetaRef::create: o="<<o<<std::endl;
	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
	luaL_getmetatable(L, className);
	lua_setmetatable(L, -2);
}

void ItemStackMetaRef::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, "metadata_class");
	lua_pushlstring(L, className, strlen(className));
	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_pushliteral(L, "__eq");
	lua_pushcfunction(L, l_equals);
	lua_settable(L, metatable);

	lua_pop(L, 1);  // drop metatable

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

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

const char ItemStackMetaRef::className[] = "ItemStackMetaRef";
const luaL_Reg ItemStackMetaRef::methods[] = {
	luamethod(MetaDataRef, get_string),
	luamethod(MetaDataRef, set_string),
	luamethod(MetaDataRef, get_int),
	luamethod(MetaDataRef, set_int),
	luamethod(MetaDataRef, get_float),
	luamethod(MetaDataRef, set_float),
	luamethod(MetaDataRef, to_table),
	luamethod(MetaDataRef, from_table),
	luamethod(MetaDataRef, equals),
	{0,0}
};


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_ITEMSTACKMETA_H_
#define L_ITEMSTACKMETA_H_

#include "lua_api/l_base.h"
#include "lua_api/l_metadata.h"
#include "irrlichttypes_bloated.h"
#include "inventory.h"

class ItemStackMetaRef : public MetaDataRef
{
private:
	ItemStack *istack;

	static const char className[];
	static const luaL_Reg methods[];

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

	virtual Metadata* getmeta(bool auto_create);

	virtual void clearMeta();

	virtual void reportMetadataChange();

	// Exported functions

	// garbage collector
	static int gc_object(lua_State *L);
public:
	ItemStackMetaRef(ItemStack *istack): istack(istack) {}
	~ItemStackMetaRef() {}

	// Creates an ItemStackMetaRef and leaves it on top of stack
	// Not callable from Lua; all references are created on the C side.
	static void create(lua_State *L, ItemStack *istack);

	static void Register(lua_State *L);
};

#endif


code:
/*
Minetest
Copyright (C) 2017 Dumbeldor, Vincent Glize <vincent.glize@live.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_localplayer.h"
#include "l_internal.h"
#include "script/common/c_converter.h"

LuaLocalPlayer::LuaLocalPlayer(LocalPlayer *m)
{
	m_localplayer = m;
}

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

	// Keep localplayer object stack id
	int localplayer_object = lua_gettop(L);

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

	lua_pushvalue(L, localplayer_object);
	lua_setfield(L, coretable, "localplayer");
}

int LuaLocalPlayer::l_get_velocity(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	push_v3f(L, player->getSpeed() / BS);
	return 1;
}

int LuaLocalPlayer::l_get_hp(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushinteger(L, player->hp);
	return 1;
}

int LuaLocalPlayer::l_get_name(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushstring(L, player->getName());
	return 1;
}

int LuaLocalPlayer::l_is_attached(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushboolean(L, player->isAttached);
	return 1;
}

int LuaLocalPlayer::l_is_touching_ground(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushboolean(L, player->touching_ground);
	return 1;
}

int LuaLocalPlayer::l_is_in_liquid(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushboolean(L, player->in_liquid);
	return 1;
}

int LuaLocalPlayer::l_is_in_liquid_stable(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushboolean(L, player->in_liquid_stable);
	return 1;
}

int LuaLocalPlayer::l_get_liquid_viscosity(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushinteger(L, player->liquid_viscosity);
	return 1;
}

int LuaLocalPlayer::l_is_climbing(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushboolean(L, player->is_climbing);
	return 1;
}

int LuaLocalPlayer::l_swimming_vertical(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushboolean(L, player->swimming_vertical);
	return 1;
}

int LuaLocalPlayer::l_get_physics_override(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_newtable(L);
	lua_pushnumber(L, player->physics_override_speed);
	lua_setfield(L, -2, "speed");

	lua_pushnumber(L, player->physics_override_jump);
	lua_setfield(L, -2, "jump");

	lua_pushnumber(L, player->physics_override_gravity);
	lua_setfield(L, -2, "gravity");

	lua_pushboolean(L, player->physics_override_sneak);
	lua_setfield(L, -2, "sneak");

	lua_pushboolean(L, player->physics_override_sneak_glitch);
	lua_setfield(L, -2, "sneak_glitch");

	return 1;
}

int LuaLocalPlayer::l_get_override_pos(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	push_v3f(L, player->overridePosition);
	return 1;
}

int LuaLocalPlayer::l_get_last_pos(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	push_v3f(L, player->last_position / BS);
	return 1;
}

int LuaLocalPlayer::l_get_last_velocity(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	push_v3f(L, player->last_speed);
	return 1;
}

int LuaLocalPlayer::l_get_last_look_vertical(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

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

int LuaLocalPlayer::l_get_last_look_horizontal(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

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

int LuaLocalPlayer::l_get_key_pressed(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushinteger(L, player->last_keyPressed);
	return 1;
}

int LuaLocalPlayer::l_get_breath(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_pushinteger(L, player->getBreath());
	return 1;
}

int LuaLocalPlayer::l_get_pos(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	push_v3f(L, player->getPosition() / BS);
	return 1;
}

int LuaLocalPlayer::l_get_movement_acceleration(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_newtable(L);
	lua_pushnumber(L, player->movement_acceleration_default);
	lua_setfield(L, -2, "default");

	lua_pushnumber(L, player->movement_acceleration_air);
	lua_setfield(L, -2, "air");

	lua_pushnumber(L, player->movement_acceleration_fast);
	lua_setfield(L, -2, "fast");

	return 1;
}

int LuaLocalPlayer::l_get_movement_speed(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_newtable(L);
	lua_pushnumber(L, player->movement_speed_walk);
	lua_setfield(L, -2, "walk");

	lua_pushnumber(L, player->movement_speed_crouch);
	lua_setfield(L, -2, "crouch");

	lua_pushnumber(L, player->movement_speed_fast);
	lua_setfield(L, -2, "fast");

	lua_pushnumber(L, player->movement_speed_climb);
	lua_setfield(L, -2, "climb");

	lua_pushnumber(L, player->movement_speed_jump);
	lua_setfield(L, -2, "jump");

	return 1;
}

int LuaLocalPlayer::l_get_movement(lua_State *L)
{
	LocalPlayer *player = getobject(L, 1);

	lua_newtable(L);

	lua_pushnumber(L, player->movement_liquid_fluidity);
	lua_setfield(L, -2, "liquid_fluidity");

	lua_pushnumber(L, player->movement_liquid_fluidity_smooth);
	lua_setfield(L, -2, "liquid_fluidity_smooth");

	lua_pushnumber(L, player->movement_liquid_sink);
	lua_setfield(L, -2, "liquid_sink");

	lua_pushnumber(L, player->movement_gravity);
	lua_setfield(L, -2, "gravity");

	return 1;
}

LuaLocalPlayer *LuaLocalPlayer::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 *(LuaLocalPlayer **)ud;
}

LocalPlayer *LuaLocalPlayer::getobject(LuaLocalPlayer *ref)
{
	return ref->m_localplayer;
}

LocalPlayer *LuaLocalPlayer::getobject(lua_State *L, int narg)
{
	LuaLocalPlayer *ref = checkobject(L, narg);
	assert(ref);
	LocalPlayer *player = getobject(ref);
	assert(player);
	return player;
}

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

void LuaLocalPlayer::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
}

const char LuaLocalPlayer::className[] = "LocalPlayer";
const luaL_Reg LuaLocalPlayer::methods[] = {
		luamethod(LuaLocalPlayer, get_velocity),
		luamethod(LuaLocalPlayer, get_hp),
		luamethod(LuaLocalPlayer, get_name),
		luamethod(LuaLocalPlayer, is_attached),
		luamethod(LuaLocalPlayer, is_touching_ground),
		luamethod(LuaLocalPlayer, is_in_liquid),
		luamethod(LuaLocalPlayer, is_in_liquid_stable),
		luamethod(LuaLocalPlayer, get_liquid_viscosity),
		luamethod(LuaLocalPlayer, is_climbing),
		luamethod(LuaLocalPlayer, swimming_vertical),
		luamethod(LuaLocalPlayer, get_physics_override),
		luamethod(LuaLocalPlayer, get_override_pos),
		luamethod(LuaLocalPlayer, get_last_pos),
		luamethod(LuaLocalPlayer, get_last_velocity),
		luamethod(LuaLocalPlayer, get_last_look_horizontal),
		luamethod(LuaLocalPlayer, get_last_look_vertical),
		luamethod(LuaLocalPlayer, get_key_pressed),
		luamethod(LuaLocalPlayer, get_breath),
		luamethod(LuaLocalPlayer, get_pos),
		luamethod(LuaLocalPlayer, get_movement_acceleration),
		luamethod(LuaLocalPlayer, get_movement_speed),
		luamethod(LuaLocalPlayer, get_movement),

		{0, 0}
};