From bd6d4666abe20247437ede28f73955d413fe5e22 Mon Sep 17 00:00:00 2001
From: Novatux <nathanael.courant@laposte.net>
Date: Sat, 26 Oct 2013 11:25:28 +0200
Subject: [PATCH] Add a callback: minetest.register_on_craft(itemstack, player,
 old_craft_grid, craft_inv) and minetest.register_craft_predict(itemstack,
 player, old_craft_grid, craft_inv)

---
 builtin/misc_register.lua     | 16 +++++++++++
 doc/lua_api.txt               |  9 +++++++
 src/inventorymanager.cpp      |  8 ++++++
 src/script/cpp_api/s_item.cpp | 51 +++++++++++++++++++++++++++++++++++
 src/script/cpp_api/s_item.h   |  6 +++++
 src/server.cpp                |  3 +++
 6 files changed, 93 insertions(+)

diff --git a/builtin/misc_register.lua b/builtin/misc_register.lua
index f1d816124..aa8399f16 100644
--- a/builtin/misc_register.lua
+++ b/builtin/misc_register.lua
@@ -230,6 +230,20 @@ function minetest.register_biome(biome)
 	register_biome_raw(biome)
 end
 
+function minetest.on_craft(itemstack, player, old_craft_list, craft_inv)
+	for _, func in ipairs(minetest.registered_on_crafts) do
+		itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack
+	end
+	return itemstack
+end
+
+function minetest.craft_predict(itemstack, player, old_craft_list, craft_inv)
+	for _, func in ipairs(minetest.registered_craft_predicts) do
+		itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack
+	end
+	return itemstack
+end
+
 -- Alias the forbidden item names to "" so they can't be
 -- created via itemstrings (e.g. /give)
 local name
@@ -327,4 +341,6 @@ minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_regis
 minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration()
 minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse()
 minetest.registered_on_cheats, minetest.register_on_cheat = make_registration()
+minetest.registered_on_crafts, minetest.register_on_craft = make_registration()
+minetest.registered_craft_predicts, minetest.register_craft_predict = make_registration()
 
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 5a0d5817f..e94fd30a9 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -1145,6 +1145,15 @@ minetest.register_on_player_receive_fields(func(player, formname, fields))
 minetest.register_on_mapgen_init(func(MapgenParams))
 ^ Called just before the map generator is initialized but before the environment is initialized
 ^ MapgenParams consists of a table with the fields mgname, seed, water_level, and flags
+minetest.register_on_craft(func(itemstack, player, old_craft_grid, craft_inv))
+^ Called when player crafts something
+^ itemstack is the output
+^ old_craft_grid contains the recipe (Note: the one in the inventory is cleared)
+^ craft_inv is the inventory with the crafting grid
+^ Return either an ItemStack, to replace the output, or nil, to not modify it
+minetest.register_craft_predict(func(itemstack, player, old_craft_grid, craft_inv))
+^ The same as before, except that it is called before the player crafts, to make
+^ craft prediction, and it should not change anything.
 
 Other registration functions:
 minetest.register_chatcommand(cmd, chatcommand definition)
diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp
index c81f7a19e..1c511e9cb 100644
--- a/src/inventorymanager.cpp
+++ b/src/inventorymanager.cpp
@@ -724,13 +724,19 @@ void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGam
 	}
 
 	ItemStack crafted;
+	ItemStack craftresultitem;
 	int count_remaining = count;
 	bool found = getCraftingResult(inv_craft, crafted, false, gamedef);
+	PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
+	found = !crafted.empty();
 
 	while(found && list_craftresult->itemFits(0, crafted))
 	{
+		InventoryList saved_craft_list = *list_craft;
+		
 		// Decrement input and add crafting output
 		getCraftingResult(inv_craft, crafted, true, gamedef);
+		PLAYER_TO_SA(player)->item_OnCraft(crafted, player, &saved_craft_list, craft_inv);
 		list_craftresult->addItem(0, crafted);
 		mgr->setInventoryModified(craft_inv);
 
@@ -747,6 +753,8 @@ void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGam
 
 		// Get next crafting result
 		found = getCraftingResult(inv_craft, crafted, false, gamedef);
+		PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
+		found = !crafted.empty();
 	}
 
 	infostream<<"ICraftAction::apply(): crafted "
diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp
index b4536ac63..1d5f218cf 100644
--- a/src/script/cpp_api/s_item.cpp
+++ b/src/script/cpp_api/s_item.cpp
@@ -22,9 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "common/c_converter.h"
 #include "common/c_content.h"
 #include "lua_api/l_item.h"
+#include "lua_api/l_inventory.h"
 #include "server.h"
 #include "log.h"
 #include "util/pointedthing.h"
+#include "inventory.h"
+#include "inventorymanager.h"
 
 bool ScriptApiItem::item_OnDrop(ItemStack &item,
 		ServerActiveObject *dropper, v3f pos)
@@ -86,6 +89,54 @@ bool ScriptApiItem::item_OnUse(ItemStack &item,
 	return true;
 }
 
+bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
+		const InventoryList *old_craft_grid, const InventoryLocation &craft_inv)
+{
+	SCRIPTAPI_PRECHECKHEADER
+
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "on_craft");
+	LuaItemStack::create(L, item);
+	objectrefGetOrCreate(user);
+	
+	//Push inventory list
+	std::vector<ItemStack> items;
+	for(u32 i=0; i<old_craft_grid->getSize(); i++)
+		items.push_back(old_craft_grid->getItem(i));
+	push_items(L, items);
+
+	InvRef::create(L, craft_inv);
+	if(lua_pcall(L, 4, 1, 0))
+		scriptError("error: %s", lua_tostring(L, -1));
+	if(!lua_isnil(L, -1))
+		item = read_item(L,-1, getServer());
+	return true;
+}
+
+bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
+		const InventoryList *old_craft_grid, const InventoryLocation &craft_inv)
+{
+	SCRIPTAPI_PRECHECKHEADER
+
+	lua_getglobal(L, "minetest");
+	lua_getfield(L, -1, "craft_predict");
+	LuaItemStack::create(L, item);
+	objectrefGetOrCreate(user);
+	
+	//Push inventory list
+	std::vector<ItemStack> items;
+	for(u32 i=0; i<old_craft_grid->getSize(); i++)
+		items.push_back(old_craft_grid->getItem(i));
+	push_items(L, items);
+
+	InvRef::create(L, craft_inv);
+	if(lua_pcall(L, 4, 1, 0))
+		scriptError("error: %s", lua_tostring(L, -1));
+	if(!lua_isnil(L, -1))
+		item = read_item(L,-1, getServer());
+	return true;
+}
+
 // Retrieves minetest.registered_items[name][callbackname]
 // If that is nil or on error, return false and stack is unchanged
 // If that is a function, returns true and pushes the
diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h
index 0f2b16042..4964dd5b4 100644
--- a/src/script/cpp_api/s_item.h
+++ b/src/script/cpp_api/s_item.h
@@ -29,6 +29,8 @@ class ServerActiveObject;
 struct ItemDefinition;
 class LuaItemStack;
 class ModApiItemMod;
+class InventoryList;
+class InventoryLocation;
 
 class ScriptApiItem
 : virtual public ScriptApiBase
@@ -40,6 +42,10 @@ class ScriptApiItem
 			ServerActiveObject *placer, const PointedThing &pointed);
 	bool item_OnUse(ItemStack &item,
 			ServerActiveObject *user, const PointedThing &pointed);
+	bool item_OnCraft(ItemStack &item, ServerActiveObject *user,
+			const InventoryList *old_craft_grid, const InventoryLocation &craft_inv);
+	bool item_CraftPredict(ItemStack &item, ServerActiveObject *user,
+			const InventoryList *old_craft_grid, const InventoryLocation &craft_inv);
 
 protected:
 	friend class LuaItemStack;
diff --git a/src/server.cpp b/src/server.cpp
index 8350b7013..c29ec3d83 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -4775,7 +4775,10 @@ void Server::UpdateCrafting(u16 peer_id)
 
 	// Get a preview for crafting
 	ItemStack preview;
+	InventoryLocation loc;
+	loc.setPlayer(player->getName());
 	getCraftingResult(&player->inventory, preview, false, this);
+	m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
 
 	// Put the new preview in
 	InventoryList *plist = player->inventory.getList("craftpreview");
-- 
GitLab