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.