Disallowing Unregistered Items in Craft Recipes

AntumDeluge
Member
 
Posts: 17
Joined: Sun Aug 07, 2016 05:42
GitHub: AntumDeluge
IRC: AntumDeluge

Disallowing Unregistered Items in Craft Recipes

by AntumDeluge » Tue Sep 06, 2016 00:16

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:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
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:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
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:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
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:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
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:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
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 162 times
Last edited by AntumDeluge on Tue Sep 06, 2016 00:44, edited 1 time in total.
 

User avatar
rubenwardy
Member
 
Posts: 4500
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy

Re: Disallowing Unregistered Items in Craft Recipes

by rubenwardy » Tue Sep 06, 2016 00:37

Also see: viewtopic.php?id=5831

A better fix for craft guides would be to not show craft recipes with undefined items
 

AntumDeluge
Member
 
Posts: 17
Joined: Sun Aug 07, 2016 05:42
GitHub: AntumDeluge
IRC: AntumDeluge

Re: Disallowing Unregistered Items in Craft Recipes

by AntumDeluge » Tue Sep 06, 2016 00:47

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.
 

AntumDeluge
Member
 
Posts: 17
Joined: Sun Aug 07, 2016 05:42
GitHub: AntumDeluge
IRC: AntumDeluge

Re: Disallowing Unregistered Items in Craft Recipes

by AntumDeluge » Tue Sep 06, 2016 00:52

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
 

AntumDeluge
Member
 
Posts: 17
Joined: Sun Aug 07, 2016 05:42
GitHub: AntumDeluge
IRC: AntumDeluge

Re: Disallowing Unregistered Items in Craft Recipes

by AntumDeluge » Tue Sep 06, 2016 01:51

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.
 


Return to Minetest Engine

Who is online

Users browsing this forum: No registered users and 4 guests

cron