Disallowing Unregistered Items in Craft Recipes

For people working on the C++ code.
Post Reply
User avatar
AntumDeluge
Member
Posts: 213
Joined: Sun Aug 07, 2016 05:42
GitHub: AntumDeluge
IRC: AntumDeluge
Contact:

Disallowing Unregistered Items in Craft Recipes

by AntumDeluge » Post

I may be going about this all wrong, but, I have already created some code that disallows craft recipes using ingredients that are not registered items. Unfortunately this breaks compatibility with A LOT of mods because 'register_craft' is declared before 'register_craftitem', 'register_node', & 'register_alias' quite often.

The code checks each ingredient of a recipe against registered craftitems, nodes, & aliases:

src/craftdef.cpp:

Code: Select all

std::vector<std::string> CraftDefinitionShaped::getRecipe() const
{
	return recipe;
}

...
...

std::vector<std::string> CraftDefinitionShapeless::getRecipe() const
{
	return recipe;
}

...
...

std::vector<std::string> CraftDefinitionToolRepair::getRecipe() const
{
	// FIXME: Does not use a recipe
	std::vector<std::string> recipe_container;
	
	return recipe_container;
}

...
... // Continued for CraftDefinitionCooking & CraftDefinitionFuel
src/script/lua_api/l_craft.cpp:

Code: Select all

std::pair<bool, std::string> checkRecipeItems(lua_State *L, CraftDefinition *def)
{
	std::vector<std::string> recipe = def->getRecipe();
	std::string ingredient;
	
	std::pair<bool, std::string> checked_item;
	
	for(unsigned int recipe_it = 0; recipe_it < recipe.size(); recipe_it++)
	{
		ingredient = recipe.at(recipe_it);
		
		// FIXME: How to do this for groups?
		if (ingredient.find("group:") != std::string::npos)
		{
			checked_item = std::make_pair(true, ingredient);
			return checked_item;
		}
		
		lua_getglobal(L, "core");
		lua_getfield(L, -1, "registered_items");
		luaL_checktype(L, -1, LUA_TTABLE);
		lua_getfield(L, -1, ingredient.c_str());
		if(!lua_isnil(L, -1))
		{
			checked_item = std::make_pair(true, ingredient);
			return checked_item;
		}
		
		lua_getglobal(L, "core"); // FIXME: Does 'lua_getglobal' need to be called for every field?
		lua_getfield(L, -1, "registered_nodes");
		luaL_checktype(L, -1, LUA_TTABLE);
		lua_getfield(L, -1, ingredient.c_str());
		if(!lua_isnil(L, -1))
		{
			checked_item = std::make_pair(true, ingredient);
			return checked_item;
		}
		
		lua_getglobal(L, "core");
		lua_getfield(L, -1, "registered_aliases");
		luaL_checktype(L, -1, LUA_TTABLE);
		lua_getfield(L, -1, ingredient.c_str());
		if(!lua_isnil(L, -1))
		{
			checked_item = std::make_pair(true, ingredient);
			return checked_item;
		}
		
	}
	
	checked_item = std::make_pair(false, ingredient);
	
	return checked_item;
}

// 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");
	
	// Used to check recipe ingredients
	std::pair<bool, std::string> recipe_check;

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

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

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

		CraftDefinition *def = new CraftDefinitionShaped(
				output, width, recipe, replacements);
		recipe_check = checkRecipeItems(L, def);
		if(!recipe_check.first)
		{
			throw LuaError("Invalid ingredient \"" + recipe_check.second +
					"\" for \"" + output + "\" craft recipe");
		}
		craftdef->registerCraft(def, getServer(L));
	}
...
... // And so on for other craft types
This code seems to work fine. But, because of the incompatibility with so many mods, I decided that it would be best to have the option to disable it. Currently I'm trying to get it to work through a command line argument:

src/server.cpp:

Code: Select all

Server::Server(
		const std::string &path_world,
		const SubgameSpec &gamespec,
		bool simple_singleplayer_mode,
		bool ipv6,
		ChatInterface *iface
	):
...
...
	
	// Craft recipes with unregistered items are not allowed by default
	craft_allowed_unregistered = false;

...
...

void Server::setCraftAllowedUnregistered(const bool allowed)
{
	craft_allowed_unregistered = allowed;
}

const bool Server::getCraftAllowedUnregistered()
{
	return craft_allowed_unregistered;
}
src/script/lua_api/l_craft.cpp:

Code: Select all

// register_craft({output=item, recipe={{item00,item10},{item01,item11}})
int ModApiCraft::l_register_craft(lua_State *L)
{
...
...
	
	// Used to check recipe ingredients
	std::pair<bool, std::string> recipe_check;
	const bool unregistered_allowed = getServer(L)->getCraftAllowedUnregistered();

...
...

		if (!unregistered_allowed)
		{
			recipe_check = checkRecipeItems(L, def);
			if(!recipe_check.first)
			{
				throw LuaError("Invalid ingredient \"" + recipe_check.second +
						"\" for \"" + output + "\" craft recipe");
			}
		}
		craftdef->registerCraft(def, getServer(L));
	}

...
...
src/main.cpp:

Code: Select all

static void set_allowed_options(OptionList *allowed_options)
{
	allowed_options->clear();

	allowed_options->insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG,
			_("Show allowed options"))));

...
...

	allowed_options->insert(std::make_pair("craft-allow-unregistered", ValueSpec(VALUETYPE_FLAG,
			_("Don't throw an error when craft recipe includes unregistered items"))));

...
...

static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args)
{
	DSTACK("Dedicated server branch");

...
...

		try {
			// Create server
			Server server(game_params.world_path,
				game_params.game_spec, false, bind_addr.isIPv6(), &iface);
			if (cmd_args.exists("craft-allow-unregistered"))
			{
				server.setCraftAllowedUnregistered(true);
			}

			g_term_console.setup(&iface, &kill, admin_nick);

			g_term_console.start();

			server.start(bind_addr);
			// Run server
			dedicated_server_loop(server, kill);

...
...
Somehow, after adding the command line argument, unregistered craft ingredients are always allowed, whether or not the '--craft-allow-unregistered' argument is declared. The Server construction should be setting 'craft-allowed-unregistered' to 'false' by default.

I'm not an experienced coder by any means, so it won't surprise me if I have missed something obvious, or if there are many errors in my code.

I hope that this is clear & any help is appreciated.

--- EDIT ---

The reason why I wanted to add this was because mods that craft_guide & craftguide list recipes with 'unknown items'. This can be fixed at the mod level, but I think it would be better to do it from the engine & would encourage mods to be in a more uniform layout.

--- EDIT ---

These are the patches that should add my code above.
minetest-craft-disallow-unregistered.patch.zip
(3.56 KiB) Downloaded 63 times
Last edited by AntumDeluge on Tue Sep 06, 2016 00:44, edited 1 time in total.

User avatar
rubenwardy
Moderator
Posts: 6978
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

Re: Disallowing Unregistered Items in Craft Recipes

by rubenwardy » Post

Also see: viewtopic.php?id=5831

A better fix for craft guides would be to not show craft recipes with undefined items
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
AntumDeluge
Member
Posts: 213
Joined: Sun Aug 07, 2016 05:42
GitHub: AntumDeluge
IRC: AntumDeluge
Contact:

Re: Disallowing Unregistered Items in Craft Recipes

by AntumDeluge » Post

rubenwardy wrote:A better fix for craft guides would be to not show craft recipes with undefined items
This is what I was trying to say when I said it can be fixed at the mod level. But still I think it would be good to have an option for the main engine to handle it.

I will check out the topic you posted.

User avatar
AntumDeluge
Member
Posts: 213
Joined: Sun Aug 07, 2016 05:42
GitHub: AntumDeluge
IRC: AntumDeluge
Contact:

Re: Disallowing Unregistered Items in Craft Recipes

by AntumDeluge » Post

rubenwardy wrote:Also see: viewtopic.php?id=5831
Why hasn't this been added to the main branch?

--- Edit ---

I created a branch with your 'mod_debug' script. AntumDeluge/minetest/tree/mod_debug

User avatar
AntumDeluge
Member
Posts: 213
Joined: Sun Aug 07, 2016 05:42
GitHub: AntumDeluge
IRC: AntumDeluge
Contact:

Re: Disallowing Unregistered Items in Craft Recipes

by AntumDeluge » Post

Actually, rubenwardy, looking at what you have done with that, I may be satisfied.

--- Edit ---

I modified the script to ouput to the debug log.

Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests