X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Finventorymanager.cpp;h=1c511e9cbe69f7659e70e8a7a1d5e172285af73b;hb=58e6d25e033c76dc91aaac18fdeda92ac23fe0e1;hp=195438d266013415799ac99205b296b9f252f21e;hpb=103173fc9b4fcb4c0fded2a93d5cbb8f0bea896e;p=oweals%2Fminetest.git diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 195438d26..1c511e9cb 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -1,90 +1,113 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "inventorymanager.h" -#include "serverremoteplayer.h" #include "log.h" -#include "mapblock.h" // getNodeBlockPos +#include "environment.h" +#include "scripting_game.h" +#include "serverobject.h" +#include "main.h" // for g_settings +#include "settings.h" +#include "craftdef.h" +#include "rollback_interface.h" + +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" + +#define PLAYER_TO_SA(p) p->getEnv()->getScriptIface() /* - InventoryManager + InventoryLocation */ -// Wrapper for old code -Inventory* InventoryManager::getInventory(InventoryContext *c, std::string id) +std::string InventoryLocation::dump() const { - if(id == "current_player") - { - assert(c->current_player); - InventoryLocation loc; - loc.setPlayer(c->current_player->getName()); - return getInventory(loc); - } - - Strfnd fn(id); - std::string id0 = fn.next(":"); - - if(id0 == "nodemeta") - { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); + std::ostringstream os(std::ios::binary); + serialize(os); + return os.str(); +} - InventoryLocation loc; - loc.setNodeMeta(p); - return getInventory(loc); +void InventoryLocation::serialize(std::ostream &os) const +{ + switch(type){ + case InventoryLocation::UNDEFINED: + os<<"undefined"; + break; + case InventoryLocation::CURRENT_PLAYER: + os<<"current_player"; + break; + case InventoryLocation::PLAYER: + os<<"player:"<current_player); - InventoryLocation loc; - loc.setPlayer(c->current_player->getName()); - setInventoryModified(loc); - return; + type = InventoryLocation::UNDEFINED; } - - Strfnd fn(id); - std::string id0 = fn.next(":"); - - if(id0 == "nodemeta") + else if(tname == "current_player") + { + type = InventoryLocation::CURRENT_PLAYER; + } + else if(tname == "player") + { + type = InventoryLocation::PLAYER; + std::getline(is, name, '\n'); + } + else if(tname == "nodemeta") { - v3s16 p; + type = InventoryLocation::NODEMETA; + std::string pos; + std::getline(is, pos, '\n'); + Strfnd fn(pos); p.X = stoi(fn.next(",")); p.Y = stoi(fn.next(",")); p.Z = stoi(fn.next(",")); - v3s16 blockpos = getNodeBlockPos(p); - - InventoryLocation loc; - loc.setNodeMeta(p); - setInventoryModified(loc); - return; } + else if(tname == "detached") + { + type = InventoryLocation::DETACHED; + std::getline(is, name, '\n'); + } + else + { + infostream<<"Unknown InventoryLocation type=\""<current_player == NULL) - return "current_player=NULL"; - else - return std::string("current_player=") + c->current_player->getName(); -} +/* + IMoveAction +*/ IMoveAction::IMoveAction(std::istream &is) { @@ -125,14 +148,16 @@ IMoveAction::IMoveAction(std::istream &is) std::getline(is, ts, ' '); count = stoi(ts); - std::getline(is, from_inv, ' '); + std::getline(is, ts, ' '); + from_inv.deSerialize(ts); std::getline(is, from_list, ' '); std::getline(is, ts, ' '); from_i = stoi(ts); - std::getline(is, to_inv, ' '); + std::getline(is, ts, ' '); + to_inv.deSerialize(ts); std::getline(is, to_list, ' '); @@ -140,22 +165,21 @@ IMoveAction::IMoveAction(std::istream &is) to_i = stoi(ts); } -void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr, - ServerEnvironment *env) +void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { - Inventory *inv_from = mgr->getInventory(c, from_inv); - Inventory *inv_to = mgr->getInventory(c, to_inv); + Inventory *inv_from = mgr->getInventory(from_inv); + Inventory *inv_to = mgr->getInventory(to_inv); if(!inv_from){ infostream<<"IMoveAction::apply(): FAIL: source inventory not found: " - <<"context=["<getItem(from_i) == NULL) - { - infostream<<"IMoveAction::apply(): FAIL: source item not found: " - <<"context=["< src_can_take_count) + count = src_can_take_count; + if(dst_can_put_count != -1 && count > dst_can_put_count) + count = dst_can_put_count; + /* Limit according to source item count */ + if(count > list_from->getItem(from_i).count) + count = list_from->getItem(from_i).count; + + /* If no items will be moved, don't go further */ + if(count == 0) + { + infostream<<"IMoveAction::apply(): move was completely disallowed:" + <<" count="<( - static_cast( - c->current_player - )); - - Inventory *inv_from = mgr->getInventory(c, from_inv); + Inventory *inv_from = mgr->getInventory(from_inv); if(!inv_from){ infostream<<"IDropAction::apply(): FAIL: source inventory not found: " - <<"context=["<getItem(from_i); - if(item == NULL) + if(list_from->getItem(from_i).empty()) { infostream<<"IDropAction::apply(): FAIL: source item not found: " - <<"context=["<getMaterial() != num) - return false; - } else { - if(mitem->getNodeName() != name) - return false; + actually_dropped_count = take_count - item1.count; + + if(actually_dropped_count == 0){ + infostream<<"Actually dropped no items"<takeItem(from_i, actually_dropped_count); + + if(item2.count != actually_dropped_count) + errorstream<<"Could not take dropped count of items"<setInventoryModified(from_inv); } } - else if(type == ITEM_CRAFT) + + infostream<<"IDropAction::apply(): dropped " + <<" from inv=\""<getSubName() != name) - return false; + PLAYER_TO_SA(player)->detached_inventory_OnTake( + from_inv.name, from_list, from_i, src_item, player); } - else if(type == ITEM_TOOL) + + // Source is nodemeta + if(from_inv.type == InventoryLocation::NODEMETA) { - // Not supported yet - assert(0); + PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( + from_inv.p, from_list, from_i, src_item, player); } - else if(type == ITEM_MBO) + + /* + Record rollback information + */ + if(!ignore_src_rollback && gamedef->rollback()) { - // Not supported yet - assert(0); + IRollbackReportSink *rollback = gamedef->rollback(); + + // If source is not infinite, record item take + if(src_can_take_count != -1){ + RollbackAction action; + std::string loc; + { + std::ostringstream os(std::ios::binary); + from_inv.serialize(os); + loc = os.str(); + } + action.setModifyInventoryStack(loc, from_list, from_i, + false, src_item.getItemString()); + rollback->reportAction(action); + } } +} + +void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) +{ + // Optional InventoryAction operation that is run on the client + // to make lag less apparent. + + Inventory *inv_from = mgr->getInventory(from_inv); + if(!inv_from) + return; + + InventoryLocation current_player; + current_player.setCurrentPlayer(); + Inventory *inv_player = mgr->getInventory(current_player); + if(inv_from != inv_player) + return; + + InventoryList *list_from = inv_from->getList(from_list); + if(!list_from) + return; + + if(count == 0) + list_from->changeItem(from_i, ItemStack()); else - { - // Not supported yet - assert(0); - } - return true; + list_from->takeItem(from_i, count); + + mgr->setInventoryModified(from_inv); } -bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs) +/* + ICraftAction +*/ + +ICraftAction::ICraftAction(std::istream &is) { - u16 items_min_x = 100; - u16 items_max_x = 100; - u16 items_min_y = 100; - u16 items_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(items[y*3 + x] == NULL) - continue; - if(items_min_x == 100 || x < items_min_x) - items_min_x = x; - if(items_min_y == 100 || y < items_min_y) - items_min_y = y; - if(items_max_x == 100 || x > items_max_x) - items_max_x = x; - if(items_max_y == 100 || y > items_max_y) - items_max_y = y; - } - // No items at all, just return false - if(items_min_x == 100) - return false; + std::string ts; + + std::getline(is, ts, ' '); + count = stoi(ts); + + std::getline(is, ts, ' '); + craft_inv.deSerialize(ts); +} + +void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) +{ + Inventory *inv_craft = mgr->getInventory(craft_inv); - u16 items_w = items_max_x - items_min_x + 1; - u16 items_h = items_max_y - items_min_y + 1; - - u16 specs_min_x = 100; - u16 specs_max_x = 100; - u16 specs_min_y = 100; - u16 specs_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(specs[y*3 + x].type == ITEM_NONE) - continue; - if(specs_min_x == 100 || x < specs_min_x) - specs_min_x = x; - if(specs_min_y == 100 || y < specs_min_y) - specs_min_y = y; - if(specs_max_x == 100 || x > specs_max_x) - specs_max_x = x; - if(specs_max_y == 100 || y > specs_max_y) - specs_max_y = y; - } - // No specs at all, just return false - if(specs_min_x == 100) - return false; + if(!inv_craft){ + infostream<<"ICraftAction::apply(): FAIL: inventory not found: " + <<"craft_inv=\""<getList("craft"); + InventoryList *list_craftresult = inv_craft->getList("craftresult"); - // Different sizes - if(items_w != specs_w || items_h != specs_h) - return false; + /* + If a list doesn't exist or the source item doesn't exist + */ + if(!list_craft){ + infostream<<"ICraftAction::apply(): FAIL: craft list not found: " + <<"craft_inv=\""<getSize() < 1){ + infostream<<"ICraftAction::apply(): FAIL: craftresult list too short: " + <<"craft_inv=\""<item_CraftPredict(crafted, player, list_craft, craft_inv); + found = !crafted.empty(); - if(spec.checkItem(item) == false) - return false; + 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); + + actionstream<getDescription() + <<" crafts " + < 1) + count_remaining--; + + // 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(); } - return true; + infostream<<"ICraftAction::apply(): crafted " + <<" craft_inv=\""< items_max_x) - items_max_x = x; - if(items_max_y == 100 || y > items_max_y) - items_max_y = y; - } - // No items at all, just return false - if(items_min_x == 100) - return false; - - u16 items_w = items_max_x - items_min_x + 1; - u16 items_h = items_max_y - items_min_y + 1; - - u16 specs_min_x = 100; - u16 specs_max_x = 100; - u16 specs_min_y = 100; - u16 specs_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(specs[y*3 + x] == NULL) - continue; - if(specs_min_x == 100 || x < specs_min_x) - specs_min_x = x; - if(specs_min_y == 100 || y < specs_min_y) - specs_min_y = y; - if(specs_max_x == 100 || x > specs_max_x) - specs_max_x = x; - if(specs_max_y == 100 || y > specs_max_y) - specs_max_y = y; - } - // No specs at all, just return false - if(specs_min_x == 100) - return false; + // Optional InventoryAction operation that is run on the client + // to make lag less apparent. +} - u16 specs_w = specs_max_x - specs_min_x + 1; - u16 specs_h = specs_max_y - specs_min_y + 1; - // Different sizes - if(items_w != specs_w || items_h != specs_h) +// Crafting helper +bool getCraftingResult(Inventory *inv, ItemStack& result, + bool decrementInput, IGameDef *gamedef) +{ + DSTACK(__FUNCTION_NAME); + + result.clear(); + + // Get the InventoryList in which we will operate + InventoryList *clist = inv->getList("craft"); + if(!clist) return false; - for(u16 y=0; ygetWidth() ? clist->getWidth() : 3; + for(u16 i=0; igetSize(); i++) + ci.items.push_back(clist->getItem(i)); + + // Find out what is crafted and add it to result item slot + CraftOutput co; + bool found = gamedef->getCraftDefManager()->getCraftResult( + ci, co, decrementInput, gamedef); + if(found) + result.deSerialize(co.item, gamedef->getItemDefManager()); + + if(found && decrementInput) { - u16 items_x = items_min_x + x; - u16 items_y = items_min_y + y; - u16 specs_x = specs_min_x + x; - u16 specs_y = specs_min_y + y; - const InventoryItem *item = items[items_y * 3 + items_x]; - const InventoryItem *spec = specs[specs_y * 3 + specs_x]; - - if(item == NULL && spec == NULL) - continue; - if(item == NULL && spec != NULL) - return false; - if(item != NULL && spec == NULL) - return false; - if(!spec->isSubsetOf(item)) - return false; + // CraftInput has been changed, apply changes in clist + for(u16 i=0; igetSize(); i++) + { + clist->changeItem(i, ci.items[i]); + } } - return true; + return found; } -