From f6eff57f7cf504c715f9b58147667f2ae77732c0 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 31 Mar 2018 12:30:43 +0200 Subject: [PATCH] Add player inventory callbacks --- builtin/game/register.lua | 2 + doc/lua_api.txt | 16 ++++ src/inventorymanager.cpp | 51 ++++++++++- src/script/cpp_api/s_player.cpp | 146 ++++++++++++++++++++++++++++++++ src/script/cpp_api/s_player.h | 45 ++++++++++ 5 files changed, 259 insertions(+), 1 deletion(-) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 7b2b149e3..1e478bc35 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -593,6 +593,8 @@ core.registered_on_priv_revoke, core.register_on_priv_revoke = make_registration core.registered_can_bypass_userlimit, core.register_can_bypass_userlimit = make_registration() core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration() core.registered_on_auth_fail, core.register_on_auth_fail = make_registration() +core.registered_on_player_inventory_actions, core.register_on_player_inventory_action = make_registration() +core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration() -- -- Compatibility for on_mapgen_init() diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 00f5e3f71..0c995b54e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2880,6 +2880,22 @@ Call these functions only at load time! * `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. +* `minetest.register_allow_player_inventory_action(func(player, inventory, action, inventory_info))` + * Determinates how much of a stack may be taken, put or moved to a + player inventory. + * `player` (type `ObjectRef`) is the player who modified the inventory + `inventory` (type `InvRef`). + * List of possible `action` (string) values and their + `inventory_info` (table) contents: + * `move`: `{from_list=string, to_list=string, from_index=number, to_index=number, count=number}` + * `put`: `{listname=string, index=number, stack=ItemStack}` + * `take`: Same as `put` + * Return a numeric value to limit the amount of items to be taken, put or + moved. A value of `-1` for `take` will make the source stack infinite. +* `minetest.register_on_player_inventory_action(func(player, inventory, action, inventory_info))` + * Called after a take, put or move event from/to/in a player inventory + * Function arguments: see `minetest.register_allow_player_inventory_action` + * Does not accept or handle any return value. * `minetest.register_on_protection_violation(func(pos, name))` * Called by `builtin` and mods when a player violates protection at a position (eg, digs a node or punches a protected entity). diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index ad567f44c..0243bd3c0 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -314,6 +314,33 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame } } + // Query player inventories + + // Move occurs in the same player inventory + if (from_inv.type == InventoryLocation::PLAYER && + to_inv.type == InventoryLocation::PLAYER && + from_inv.name == to_inv.name) { + src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowMove( + from_inv, from_list, from_i, + to_list, to_i, try_take_count, player); + dst_can_put_count = src_can_take_count; + } else { + // Destination is a player + if (to_inv.type == InventoryLocation::PLAYER) { + ItemStack src_item = list_from->getItem(from_i); + src_item.count = try_take_count; + dst_can_put_count = PLAYER_TO_SA(player)->player_inventory_AllowPut( + to_inv, to_list, to_i, src_item, player); + } + // Source is a player + if (from_inv.type == InventoryLocation::PLAYER) { + ItemStack src_item = list_from->getItem(from_i); + src_item.count = try_take_count; + src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowTake( + from_inv, from_list, from_i, src_item, player); + } + } + int old_count = count; /* Modify count according to collected data */ @@ -482,12 +509,34 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame to_inv.p, to_list, to_i, src_item, player); } // Source is nodemeta - else if (from_inv.type == InventoryLocation::NODEMETA) { + if (from_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( from_inv.p, from_list, from_i, src_item, player); } } + // Player inventories + + // Both endpoints are same player inventory + if (from_inv.type == InventoryLocation::PLAYER && + to_inv.type == InventoryLocation::PLAYER && + from_inv.name == to_inv.name) { + PLAYER_TO_SA(player)->player_inventory_OnMove( + from_inv, from_list, from_i, + to_list, to_i, count, player); + } else { + // Destination is player inventory + if (to_inv.type == InventoryLocation::PLAYER) { + PLAYER_TO_SA(player)->player_inventory_OnPut( + to_inv, to_list, to_i, src_item, player); + } + // Source is player inventory + if (from_inv.type == InventoryLocation::PLAYER) { + PLAYER_TO_SA(player)->player_inventory_OnTake( + from_inv, from_list, from_i, src_item, player); + } + } + mgr->setInventoryModified(from_inv, false); if (inv_from != inv_to) mgr->setInventoryModified(to_inv, false); diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp index cc2b96d5e..8c01f9b1f 100644 --- a/src/script/cpp_api/s_player.cpp +++ b/src/script/cpp_api/s_player.cpp @@ -22,6 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "debug.h" +#include "inventorymanager.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" #include "util/string.h" void ScriptApiPlayer::on_newplayer(ServerActiveObject *player) @@ -225,3 +228,146 @@ void ScriptApiPlayer::on_auth_failure(const std::string &name, const std::string lua_pushstring(L, ip.c_str()); runCallbacks(2, RUN_CALLBACKS_MODE_FIRST); } + +void ScriptApiPlayer::pushMoveArguments( + const InventoryLocation &loc, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + lua_State *L = getStack(); + objectrefGetOrCreate(L, player); // player + lua_pushstring(L, "move"); // action + InvRef::create(L, loc); // inventory + lua_newtable(L); + { + // Table containing the action information + lua_pushstring(L, from_list.c_str()); + lua_setfield(L, -2, "from_list"); + lua_pushstring(L, to_list.c_str()); + lua_setfield(L, -2, "to_list"); + + lua_pushinteger(L, from_index + 1); + lua_setfield(L, -2, "from_index"); + lua_pushinteger(L, to_index + 1); + lua_setfield(L, -2, "to_index"); + + lua_pushinteger(L, count); + lua_setfield(L, -2, "count"); + } +} + +void ScriptApiPlayer::pushPutTakeArguments( + const char *method, const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player) +{ + lua_State *L = getStack(); + objectrefGetOrCreate(L, player); // player + lua_pushstring(L, method); // action + InvRef::create(L, loc); // inventory + lua_newtable(L); + { + // Table containing the action information + lua_pushstring(L, listname.c_str()); + lua_setfield(L, -2, "listname"); + + lua_pushinteger(L, index + 1); + lua_setfield(L, -2, "index"); + + LuaItemStack::create(L, stack); + lua_setfield(L, -2, "stack"); + } +} + +// Return number of accepted items to be moved +int ScriptApiPlayer::player_inventory_AllowMove( + const InventoryLocation &loc, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_allow_player_inventory_actions"); + pushMoveArguments(loc, from_list, from_index, to_list, to_index, count, player); + runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC); + + return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : count; +} + +// Return number of accepted items to be put +int ScriptApiPlayer::player_inventory_AllowPut( + const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_allow_player_inventory_actions"); + pushPutTakeArguments("put", loc, listname, index, stack, player); + runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC); + + return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : stack.count; +} + +// Return number of accepted items to be taken +int ScriptApiPlayer::player_inventory_AllowTake( + const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_allow_player_inventory_actions"); + pushPutTakeArguments("take", loc, listname, index, stack, player); + runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC); + + return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : stack.count; +} + +// Report moved items +void ScriptApiPlayer::player_inventory_OnMove( + const InventoryLocation &loc, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_player_inventory_actions"); + pushMoveArguments(loc, from_list, from_index, to_list, to_index, count, player); + runCallbacks(4, RUN_CALLBACKS_MODE_FIRST); +} + +// Report put items +void ScriptApiPlayer::player_inventory_OnPut( + const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_player_inventory_actions"); + pushPutTakeArguments("put", loc, listname, index, stack, player); + runCallbacks(4, RUN_CALLBACKS_MODE_FIRST); +} + +// Report taken items +void ScriptApiPlayer::player_inventory_OnTake( + const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_player_inventory_actions"); + pushPutTakeArguments("take", loc, listname, index, stack, player); + runCallbacks(4, RUN_CALLBACKS_MODE_FIRST); +} diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h index 59e1505ae..d60dfcaf4 100644 --- a/src/script/cpp_api/s_player.h +++ b/src/script/cpp_api/s_player.h @@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include "util/string.h" +struct InventoryLocation; +struct ItemStack; struct ToolCapabilities; struct PlayerHPChangeReason; @@ -48,4 +50,47 @@ public: void on_playerReceiveFields(ServerActiveObject *player, const std::string &formname, const StringMap &fields); void on_auth_failure(const std::string &name, const std::string &ip); + + // Player inventory callbacks + // Return number of accepted items to be moved + int player_inventory_AllowMove( + const InventoryLocation &loc, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); + // Return number of accepted items to be put + int player_inventory_AllowPut( + const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player); + // Return number of accepted items to be taken + int player_inventory_AllowTake( + const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player); + // Report moved items + void player_inventory_OnMove( + const InventoryLocation &loc, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); + // Report put items + void player_inventory_OnPut( + const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player); + // Report taken items + void player_inventory_OnTake( + const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player); +private: + void pushPutTakeArguments( + const char *method, const InventoryLocation &loc, + const std::string &listname, int index, const ItemStack &stack, + ServerActiveObject *player); + void pushMoveArguments(const InventoryLocation &loc, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); };