aboutsummaryrefslogtreecommitdiff
Commit message (Expand)AuthorAge
...
* Switch to check_v3s16 in l_mapgen.cpp and l_vmanip.cpp for type safetykwolekr2015-04-17
* Schematics: Remove referenced schematics from Decorations on clearkwolekr2015-04-17
* Refactor around translatePasswordest312015-04-17
* Biomes: Remove referenced biomes from Decorations on clearkwolekr2015-04-16
* Fix path detection with --std= parameterest312015-04-17
* Fix build for < C++11 since last commitest312015-04-17
* Schematics: Refactor NodeResolver and add NodeResolveMethodkwolekr2015-04-16
* Use std::vector instead of std::set for Environment::getObjectsInsideRadiusLoic Blot2015-04-16
* Mgv5: Fix above/below ground spawn when water level is alteredparamat2015-04-16
* Mgv5/v7: Add check for water for deciding biome node stabilityparamat2015-04-16
* Defaultsettings: Add dungeons and jungles flags. Update conf.exampleparamat2015-04-14
* Schematics: Reorganize (de)serialization and add Lua serialization APIkwolekr2015-04-13
* Conf.example: Update, fix and improve mapgen flags docsparamat2015-04-13
* Add Spanish comment translation to minetest.desktopngosang2015-04-12
* Mgv6: Fix taiga, allow pine tree spawning on snowblocksparamat2015-04-12
* Mgv6: Add optional snow biomesparamat2015-04-12
* Fix a rare crash case un SendPlayerHPNer'zhul2015-04-11
* Document for api functions that rely on current_modname, that it doesn't work...est312015-04-08
* Link against shlwapi.lib and unify linker input sources for Windowskwolekr2015-04-08
* Fix freetype detectionDmitry Marakasov2015-04-08
* Schematics: Prepend mod path to relative schematic filepathskwolekr2015-04-08
* TOCLIENT_ACTIVE_OBJECT_MESSAGES: channel must be 1 for unreliable messageLoic Blot2015-04-07
* Fix missing SQLite3 statement pointer initializationShadowNinja2015-04-07
* Cavegen: Remove now unnecessary checks for water, lava, iceparamat2015-04-07
* Make comments consistent with TOSERVER_INIT -> TOSERVER_INIT_LEGACY renameest312015-04-07
* Change screenshot filename to one likely to work on all modern filesystemsCraig Robbins2015-04-06
* TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD can be unreliable, catch PacketError except...Loic Blot2015-04-05
* craftdef.cpp: Return 0 after assert to make Clang happyLoic Blot2015-04-05
* Fix uninitialized variabled in ConnectionEventLoic Blot2015-04-05
* Crafting speedupest312015-04-05
* Add reason to kicked log message and use present tenseest312015-04-05
* Remove errorstream logging on password changeest312015-04-03
* Fix players spawned at (0,0,0) in some rare cases instead of static_spawnpoin...Loic Blot2015-04-03
* Create PacketError exception and use it with ACTIVEOBJECT_REMOVE_ADD handler ...Loic Blot2015-04-03
* Fix crash caused by null texture in GUI formspec/HUD.Aaron Suen2015-04-02
* Fix MSVC error caused by ed10005SmallJoker2015-04-02
* Treegen: Add pine tree. Force place trunksparamat2015-04-01
* Move globals from main.cpp to more sane locationsCraig Robbins2015-04-01
* NetworkPacket: reading outside packet is now clearer.Loic Blot2015-04-01
* Fix some minor details from 6d61375kwolekr2015-04-01
* Clean scaling pre-filter for formspec/HUD.Aaron Suen2015-04-01
* Fix usage of destroyed mutexkwolekr2015-03-31
* Fix set_bitskwolekr2015-03-31
* ObjDefManager, Mapgen SAPI: Huge refactoringkwolekr2015-03-31
* Fix use of unitialized variable in gettext.cppCraig Robbins2015-03-31
* Connection::Receive(): receive Network Packet instead of SharedBuffer<u8>.Loic Blot2015-03-31
* RunCommandQueues: little optimizationLoic Blot2015-03-31
* Typo in getOutgoingSequenceNumber => successfull to successfulLoic Blot2015-03-31
* Move texture_min_size even further down the pipe. Now, textures are JIT-upsca...Aaron Suen2015-03-31
* Change format of screenshot namesCraig Robbins2015-03-31
href='#n418'>418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

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

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

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


#include "lua_api/l_craft.h"
#include "lua_api/l_internal.h"
#include "lua_api/l_item.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "server.h"
#include "craftdef.h"

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

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

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

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

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

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

	lua_pushnil(L);
	while(lua_next(L, index) != 0){
		// key at index -2 and value at index -1
		if(!lua_isstring(L, -1))
			return false;
		recipe.emplace_back(readParam<std::string>(L, -1));
		// removes value, keeps key for next iteration
		lua_pop(L, 1);
	}
	return true;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	std::string output = getstringfield_default(L, table, "output", "");
	std::string type = getstringfield_default(L, table, "type", "shaped");
	CraftOutput c_output(output, 0);
	if (!output.empty()) {
		if (craftdef->clearCraftsByOutput(c_output, getServer(L))) {
			lua_pushboolean(L, true);
			return 1;
		}

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

	std::vector<ItemStack> items;
	items.reserve(recipe.size());
	for (const auto &item : recipe)
		items.emplace_back(item, 1, 0, getServer(L)->idef());
	CraftInput input(method, width, items);

	if (!craftdef->clearCraftsByInput(input, getServer(L))) {
		warningstream << "No craft recipe matches input" << std::endl;
		lua_pushboolean(L, false);
		return 1;
	}

	lua_pushboolean(L, true);
	return 1;
}

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

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

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


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

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

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

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

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

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

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

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


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

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

	lua_createtable(L, 1, 0);

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

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

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

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

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