LCOV - code coverage report
Current view: top level - src - inventorymanager.cpp (source / functions) Hit Total Coverage
Test: report Lines: 1 495 0.2 %
Date: 2015-07-11 18:23:49 Functions: 2 17 11.8 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : #include "inventorymanager.h"
      21             : #include "log.h"
      22             : #include "environment.h"
      23             : #include "scripting_game.h"
      24             : #include "serverobject.h"
      25             : #include "settings.h"
      26             : #include "craftdef.h"
      27             : #include "rollback_interface.h"
      28             : #include "strfnd.h"
      29             : 
      30             : #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
      31             : 
      32             : #define PLAYER_TO_SA(p)   p->getEnv()->getScriptIface()
      33             : 
      34             : /*
      35             :         InventoryLocation
      36             : */
      37             : 
      38           0 : std::string InventoryLocation::dump() const
      39             : {
      40           0 :         std::ostringstream os(std::ios::binary);
      41           0 :         serialize(os);
      42           0 :         return os.str();
      43             : }
      44             : 
      45           0 : void InventoryLocation::serialize(std::ostream &os) const
      46             : {
      47           0 :         switch(type){
      48             :         case InventoryLocation::UNDEFINED:
      49           0 :                 os<<"undefined";
      50           0 :                 break;
      51             :         case InventoryLocation::CURRENT_PLAYER:
      52           0 :                 os<<"current_player";
      53           0 :                 break;
      54             :         case InventoryLocation::PLAYER:
      55           0 :                 os<<"player:"<<name;
      56           0 :                 break;
      57             :         case InventoryLocation::NODEMETA:
      58           0 :                 os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
      59           0 :                 break;
      60             :         case InventoryLocation::DETACHED:
      61           0 :                 os<<"detached:"<<name;
      62           0 :                 break;
      63             :         default:
      64           0 :                 FATAL_ERROR("Unhandled inventory location type");
      65             :         }
      66           0 : }
      67             : 
      68           0 : void InventoryLocation::deSerialize(std::istream &is)
      69             : {
      70           0 :         std::string tname;
      71           0 :         std::getline(is, tname, ':');
      72           0 :         if(tname == "undefined")
      73             :         {
      74           0 :                 type = InventoryLocation::UNDEFINED;
      75             :         }
      76           0 :         else if(tname == "current_player")
      77             :         {
      78           0 :                 type = InventoryLocation::CURRENT_PLAYER;
      79             :         }
      80           0 :         else if(tname == "player")
      81             :         {
      82           0 :                 type = InventoryLocation::PLAYER;
      83           0 :                 std::getline(is, name, '\n');
      84             :         }
      85           0 :         else if(tname == "nodemeta")
      86             :         {
      87           0 :                 type = InventoryLocation::NODEMETA;
      88           0 :                 std::string pos;
      89           0 :                 std::getline(is, pos, '\n');
      90           0 :                 Strfnd fn(pos);
      91           0 :                 p.X = stoi(fn.next(","));
      92           0 :                 p.Y = stoi(fn.next(","));
      93           0 :                 p.Z = stoi(fn.next(","));
      94             :         }
      95           0 :         else if(tname == "detached")
      96             :         {
      97           0 :                 type = InventoryLocation::DETACHED;
      98           0 :                 std::getline(is, name, '\n');
      99             :         }
     100             :         else
     101             :         {
     102           0 :                 infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;
     103           0 :                 throw SerializationError("Unknown InventoryLocation type");
     104             :         }
     105           0 : }
     106             : 
     107           0 : void InventoryLocation::deSerialize(std::string s)
     108             : {
     109           0 :         std::istringstream is(s, std::ios::binary);
     110           0 :         deSerialize(is);
     111           0 : }
     112             : 
     113             : /*
     114             :         InventoryAction
     115             : */
     116             : 
     117           0 : InventoryAction * InventoryAction::deSerialize(std::istream &is)
     118             : {
     119           0 :         std::string type;
     120           0 :         std::getline(is, type, ' ');
     121             : 
     122           0 :         InventoryAction *a = NULL;
     123             : 
     124           0 :         if (type == "Move") {
     125           0 :                 a = new IMoveAction(is, false);
     126           0 :         } else if (type == "MoveSomewhere") {
     127           0 :                 a = new IMoveAction(is, true);
     128           0 :         } else if (type == "Drop") {
     129           0 :                 a = new IDropAction(is);
     130           0 :         } else if(type == "Craft") {
     131           0 :                 a = new ICraftAction(is);
     132             :         }
     133             : 
     134           0 :         return a;
     135             : }
     136             : 
     137             : /*
     138             :         IMoveAction
     139             : */
     140             : 
     141           0 : IMoveAction::IMoveAction(std::istream &is, bool somewhere)
     142             : {
     143           0 :         std::string ts;
     144           0 :         move_somewhere = somewhere;
     145           0 :         caused_by_move_somewhere = false;
     146           0 :         move_count = 0;
     147             : 
     148           0 :         std::getline(is, ts, ' ');
     149           0 :         count = stoi(ts);
     150             : 
     151           0 :         std::getline(is, ts, ' ');
     152           0 :         from_inv.deSerialize(ts);
     153             : 
     154           0 :         std::getline(is, from_list, ' ');
     155             : 
     156           0 :         std::getline(is, ts, ' ');
     157           0 :         from_i = stoi(ts);
     158             : 
     159           0 :         std::getline(is, ts, ' ');
     160           0 :         to_inv.deSerialize(ts);
     161             : 
     162           0 :         std::getline(is, to_list, ' ');
     163             : 
     164           0 :         if (!somewhere) {
     165           0 :                 std::getline(is, ts, ' ');
     166           0 :                 to_i = stoi(ts);
     167             :         }
     168           0 : }
     169             : 
     170           0 : void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
     171             : {
     172           0 :         Inventory *inv_from = mgr->getInventory(from_inv);
     173           0 :         Inventory *inv_to = mgr->getInventory(to_inv);
     174             :         
     175           0 :         if(!inv_from){
     176           0 :                 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
     177           0 :                                 <<"from_inv=\""<<from_inv.dump()<<"\""
     178           0 :                                 <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
     179           0 :                 return;
     180             :         }
     181           0 :         if(!inv_to){
     182           0 :                 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
     183           0 :                                 <<"from_inv=\""<<from_inv.dump()<<"\""
     184           0 :                                 <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
     185           0 :                 return;
     186             :         }
     187             : 
     188           0 :         InventoryList *list_from = inv_from->getList(from_list);
     189           0 :         InventoryList *list_to = inv_to->getList(to_list);
     190             : 
     191             :         /*
     192             :                 If a list doesn't exist or the source item doesn't exist
     193             :         */
     194           0 :         if(!list_from){
     195           0 :                 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
     196           0 :                                 <<"from_inv=\""<<from_inv.dump()<<"\""
     197           0 :                                 <<", from_list=\""<<from_list<<"\""<<std::endl;
     198           0 :                 return;
     199             :         }
     200           0 :         if(!list_to){
     201           0 :                 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
     202           0 :                                 <<"to_inv=\""<<to_inv.dump()<<"\""
     203           0 :                                 <<", to_list=\""<<to_list<<"\""<<std::endl;
     204           0 :                 return;
     205             :         }
     206             : 
     207           0 :         if (move_somewhere) {
     208           0 :                 s16 old_to_i = to_i;
     209           0 :                 u16 old_count = count;
     210           0 :                 caused_by_move_somewhere = true;
     211           0 :                 move_somewhere = false;
     212             : 
     213           0 :                 infostream << "IMoveAction::apply(): moving item somewhere"
     214           0 :                         << " msom=" << move_somewhere
     215           0 :                         << " count=" << count
     216           0 :                         << " from inv=\"" << from_inv.dump() << "\""
     217           0 :                         << " list=\"" << from_list << "\""
     218           0 :                         << " i=" << from_i
     219           0 :                         << " to inv=\"" << to_inv.dump() << "\""
     220           0 :                         << " list=\"" << to_list << "\""
     221           0 :                         << std::endl;
     222             : 
     223             :                 // Try to add the item to destination list
     224           0 :                 s16 dest_size = list_to->getSize();
     225             :                 // First try all the non-empty slots
     226           0 :                 for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) {
     227           0 :                         if (!list_to->getItem(dest_i).empty()) {
     228           0 :                                 to_i = dest_i;
     229           0 :                                 apply(mgr, player, gamedef);
     230           0 :                                 count -= move_count;
     231             :                         }
     232             :                 }
     233             : 
     234             :                 // Then try all the empty ones
     235           0 :                 for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) {
     236           0 :                         if (list_to->getItem(dest_i).empty()) {
     237           0 :                                 to_i = dest_i;
     238           0 :                                 apply(mgr, player, gamedef);
     239           0 :                                 count -= move_count;
     240             :                         }
     241             :                 }
     242             : 
     243           0 :                 to_i = old_to_i;
     244           0 :                 count = old_count;
     245           0 :                 caused_by_move_somewhere = false;
     246           0 :                 move_somewhere = true;
     247           0 :                 return;
     248             :         }
     249             :         /*
     250             :                 Do not handle rollback if both inventories are that of the same player
     251             :         */
     252             :         bool ignore_rollback = (
     253           0 :                 from_inv.type == InventoryLocation::PLAYER &&
     254           0 :                 to_inv.type == InventoryLocation::PLAYER &&
     255           0 :                 from_inv.name == to_inv.name);
     256             : 
     257             :         /*
     258             :                 Collect information of endpoints
     259             :         */
     260             : 
     261           0 :         int try_take_count = count;
     262           0 :         if(try_take_count == 0)
     263           0 :                 try_take_count = list_from->getItem(from_i).count;
     264             : 
     265           0 :         int src_can_take_count = 0xffff;
     266           0 :         int dst_can_put_count = 0xffff;
     267             :         
     268             :         /* Query detached inventories */
     269             : 
     270             :         // Move occurs in the same detached inventory
     271           0 :         if(from_inv.type == InventoryLocation::DETACHED &&
     272           0 :                         to_inv.type == InventoryLocation::DETACHED &&
     273           0 :                         from_inv.name == to_inv.name)
     274             :         {
     275           0 :                 src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove(
     276           0 :                                 from_inv.name, from_list, from_i,
     277           0 :                                 to_list, to_i, try_take_count, player);
     278           0 :                 dst_can_put_count = src_can_take_count;
     279             :         }
     280             :         else
     281             :         {
     282             :                 // Destination is detached
     283           0 :                 if(to_inv.type == InventoryLocation::DETACHED)
     284             :                 {
     285           0 :                         ItemStack src_item = list_from->getItem(from_i);
     286           0 :                         src_item.count = try_take_count;
     287           0 :                         dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut(
     288           0 :                                         to_inv.name, to_list, to_i, src_item, player);
     289             :                 }
     290             :                 // Source is detached
     291           0 :                 if(from_inv.type == InventoryLocation::DETACHED)
     292             :                 {
     293           0 :                         ItemStack src_item = list_from->getItem(from_i);
     294           0 :                         src_item.count = try_take_count;
     295           0 :                         src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake(
     296           0 :                                         from_inv.name, from_list, from_i, src_item, player);
     297             :                 }
     298             :         }
     299             : 
     300             :         /* Query node metadata inventories */
     301             : 
     302             :         // Both endpoints are nodemeta
     303             :         // Move occurs in the same nodemeta inventory
     304           0 :         if(from_inv.type == InventoryLocation::NODEMETA &&
     305           0 :                         to_inv.type == InventoryLocation::NODEMETA &&
     306           0 :                         from_inv.p == to_inv.p)
     307             :         {
     308           0 :                 src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove(
     309           0 :                                 from_inv.p, from_list, from_i,
     310           0 :                                 to_list, to_i, try_take_count, player);
     311           0 :                 dst_can_put_count = src_can_take_count;
     312             :         }
     313             :         else
     314             :         {
     315             :                 // Destination is nodemeta
     316           0 :                 if(to_inv.type == InventoryLocation::NODEMETA)
     317             :                 {
     318           0 :                         ItemStack src_item = list_from->getItem(from_i);
     319           0 :                         src_item.count = try_take_count;
     320           0 :                         dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut(
     321           0 :                                         to_inv.p, to_list, to_i, src_item, player);
     322             :                 }
     323             :                 // Source is nodemeta
     324           0 :                 if(from_inv.type == InventoryLocation::NODEMETA)
     325             :                 {
     326           0 :                         ItemStack src_item = list_from->getItem(from_i);
     327           0 :                         src_item.count = try_take_count;
     328           0 :                         src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake(
     329           0 :                                         from_inv.p, from_list, from_i, src_item, player);
     330             :                 }
     331             :         }
     332             : 
     333           0 :         int old_count = count;
     334             :         
     335             :         /* Modify count according to collected data */
     336           0 :         count = try_take_count;
     337           0 :         if(src_can_take_count != -1 && count > src_can_take_count)
     338           0 :                 count = src_can_take_count;
     339           0 :         if(dst_can_put_count != -1 && count > dst_can_put_count)
     340           0 :                 count = dst_can_put_count;
     341             :         /* Limit according to source item count */
     342           0 :         if(count > list_from->getItem(from_i).count)
     343           0 :                 count = list_from->getItem(from_i).count;
     344             :         
     345             :         /* If no items will be moved, don't go further */
     346           0 :         if(count == 0)
     347             :         {
     348           0 :                 infostream<<"IMoveAction::apply(): move was completely disallowed:"
     349           0 :                                 <<" count="<<old_count
     350           0 :                                 <<" from inv=\""<<from_inv.dump()<<"\""
     351           0 :                                 <<" list=\""<<from_list<<"\""
     352           0 :                                 <<" i="<<from_i
     353           0 :                                 <<" to inv=\""<<to_inv.dump()<<"\""
     354           0 :                                 <<" list=\""<<to_list<<"\""
     355           0 :                                 <<" i="<<to_i
     356           0 :                                 <<std::endl;
     357           0 :                 return;
     358             :         }
     359             : 
     360           0 :         ItemStack src_item = list_from->getItem(from_i);
     361           0 :         src_item.count = count;
     362           0 :         ItemStack from_stack_was = list_from->getItem(from_i);
     363           0 :         ItemStack to_stack_was = list_to->getItem(to_i);
     364             : 
     365             :         /*
     366             :                 Perform actual move
     367             : 
     368             :                 If something is wrong (source item is empty, destination is the
     369             :                 same as source), nothing happens
     370             :         */
     371           0 :         move_count = list_from->moveItem(from_i,
     372           0 :                 list_to, to_i, count, !caused_by_move_somewhere);
     373             : 
     374             :         // If source is infinite, reset it's stack
     375           0 :         if(src_can_take_count == -1){
     376             :                 // If destination stack is of different type and there are leftover
     377             :                 // items, attempt to put the leftover items to a different place in the
     378             :                 // destination inventory.
     379             :                 // The client-side GUI will try to guess if this happens.
     380           0 :                 if(from_stack_was.name != to_stack_was.name){
     381           0 :                         for(u32 i=0; i<list_to->getSize(); i++){
     382           0 :                                 if(list_to->getItem(i).empty()){
     383           0 :                                         list_to->changeItem(i, to_stack_was);
     384           0 :                                         break;
     385             :                                 }
     386             :                         }
     387             :                 }
     388           0 :                 list_from->deleteItem(from_i);
     389           0 :                 list_from->addItem(from_i, from_stack_was);
     390             :         }
     391             :         // If destination is infinite, reset it's stack and take count from source
     392           0 :         if(dst_can_put_count == -1){
     393           0 :                 list_to->deleteItem(to_i);
     394           0 :                 list_to->addItem(to_i, to_stack_was);
     395           0 :                 list_from->deleteItem(from_i);
     396           0 :                 list_from->addItem(from_i, from_stack_was);
     397           0 :                 list_from->takeItem(from_i, count);
     398             :         }
     399             : 
     400           0 :         infostream << "IMoveAction::apply(): moved"
     401           0 :                         << " msom=" << move_somewhere
     402           0 :                         << " caused=" << caused_by_move_somewhere
     403           0 :                         << " count=" << count
     404           0 :                         << " from inv=\"" << from_inv.dump() << "\""
     405           0 :                         << " list=\"" << from_list << "\""
     406           0 :                         << " i=" << from_i
     407           0 :                         << " to inv=\"" << to_inv.dump() << "\""
     408           0 :                         << " list=\"" << to_list << "\""
     409           0 :                         << " i=" << to_i
     410           0 :                         << std::endl;
     411             : 
     412             :         /*
     413             :                 Record rollback information
     414             :         */
     415           0 :         if(!ignore_rollback && gamedef->rollback())
     416             :         {
     417           0 :                 IRollbackManager *rollback = gamedef->rollback();
     418             : 
     419             :                 // If source is not infinite, record item take
     420           0 :                 if(src_can_take_count != -1){
     421           0 :                         RollbackAction action;
     422           0 :                         std::string loc;
     423             :                         {
     424           0 :                                 std::ostringstream os(std::ios::binary);
     425           0 :                                 from_inv.serialize(os);
     426           0 :                                 loc = os.str();
     427             :                         }
     428           0 :                         action.setModifyInventoryStack(loc, from_list, from_i, false,
     429           0 :                                         src_item);
     430           0 :                         rollback->reportAction(action);
     431             :                 }
     432             :                 // If destination is not infinite, record item put
     433           0 :                 if(dst_can_put_count != -1){
     434           0 :                         RollbackAction action;
     435           0 :                         std::string loc;
     436             :                         {
     437           0 :                                 std::ostringstream os(std::ios::binary);
     438           0 :                                 to_inv.serialize(os);
     439           0 :                                 loc = os.str();
     440             :                         }
     441           0 :                         action.setModifyInventoryStack(loc, to_list, to_i, true,
     442           0 :                                         src_item);
     443           0 :                         rollback->reportAction(action);
     444             :                 }
     445             :         }
     446             : 
     447             :         /*
     448             :                 Report move to endpoints
     449             :         */
     450             :         
     451             :         /* Detached inventories */
     452             : 
     453             :         // Both endpoints are same detached
     454           0 :         if(from_inv.type == InventoryLocation::DETACHED &&
     455           0 :                         to_inv.type == InventoryLocation::DETACHED &&
     456           0 :                         from_inv.name == to_inv.name)
     457             :         {
     458           0 :                 PLAYER_TO_SA(player)->detached_inventory_OnMove(
     459           0 :                                 from_inv.name, from_list, from_i,
     460           0 :                                 to_list, to_i, count, player);
     461             :         }
     462             :         else
     463             :         {
     464             :                 // Destination is detached
     465           0 :                 if(to_inv.type == InventoryLocation::DETACHED)
     466             :                 {
     467           0 :                         PLAYER_TO_SA(player)->detached_inventory_OnPut(
     468           0 :                                         to_inv.name, to_list, to_i, src_item, player);
     469             :                 }
     470             :                 // Source is detached
     471           0 :                 if(from_inv.type == InventoryLocation::DETACHED)
     472             :                 {
     473           0 :                         PLAYER_TO_SA(player)->detached_inventory_OnTake(
     474           0 :                                         from_inv.name, from_list, from_i, src_item, player);
     475             :                 }
     476             :         }
     477             : 
     478             :         /* Node metadata inventories */
     479             : 
     480             :         // Both endpoints are same nodemeta
     481           0 :         if(from_inv.type == InventoryLocation::NODEMETA &&
     482           0 :                         to_inv.type == InventoryLocation::NODEMETA &&
     483           0 :                         from_inv.p == to_inv.p)
     484             :         {
     485           0 :                 PLAYER_TO_SA(player)->nodemeta_inventory_OnMove(
     486           0 :                                 from_inv.p, from_list, from_i,
     487           0 :                                 to_list, to_i, count, player);
     488             :         }
     489             :         else{
     490             :                 // Destination is nodemeta
     491           0 :                 if(to_inv.type == InventoryLocation::NODEMETA)
     492             :                 {
     493           0 :                         PLAYER_TO_SA(player)->nodemeta_inventory_OnPut(
     494           0 :                                         to_inv.p, to_list, to_i, src_item, player);
     495             :                 }
     496             :                 // Source is nodemeta
     497           0 :                 else if(from_inv.type == InventoryLocation::NODEMETA)
     498             :                 {
     499           0 :                         PLAYER_TO_SA(player)->nodemeta_inventory_OnTake(
     500           0 :                                         from_inv.p, from_list, from_i, src_item, player);
     501             :                 }
     502             :         }
     503             :         
     504           0 :         mgr->setInventoryModified(from_inv, false);
     505           0 :         if(inv_from != inv_to)
     506           0 :                 mgr->setInventoryModified(to_inv, false);
     507             : }
     508             : 
     509           0 : void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
     510             : {
     511             :         // Optional InventoryAction operation that is run on the client
     512             :         // to make lag less apparent.
     513             : 
     514           0 :         Inventory *inv_from = mgr->getInventory(from_inv);
     515           0 :         Inventory *inv_to = mgr->getInventory(to_inv);
     516           0 :         if(!inv_from || !inv_to)
     517           0 :                 return;
     518             : 
     519           0 :         InventoryLocation current_player;
     520           0 :         current_player.setCurrentPlayer();
     521           0 :         Inventory *inv_player = mgr->getInventory(current_player);
     522           0 :         if(inv_from != inv_player || inv_to != inv_player)
     523           0 :                 return;
     524             : 
     525           0 :         InventoryList *list_from = inv_from->getList(from_list);
     526           0 :         InventoryList *list_to = inv_to->getList(to_list);
     527           0 :         if(!list_from || !list_to)
     528           0 :                 return;
     529             : 
     530           0 :         if (!move_somewhere)
     531           0 :                 list_from->moveItem(from_i, list_to, to_i, count);
     532             :         else
     533           0 :                 list_from->moveItemSomewhere(from_i, list_to, count);
     534             : 
     535           0 :         mgr->setInventoryModified(from_inv);
     536           0 :         if(inv_from != inv_to)
     537           0 :                 mgr->setInventoryModified(to_inv);
     538             : }
     539             : 
     540             : /*
     541             :         IDropAction
     542             : */
     543             : 
     544           0 : IDropAction::IDropAction(std::istream &is)
     545             : {
     546           0 :         std::string ts;
     547             : 
     548           0 :         std::getline(is, ts, ' ');
     549           0 :         count = stoi(ts);
     550             : 
     551           0 :         std::getline(is, ts, ' ');
     552           0 :         from_inv.deSerialize(ts);
     553             : 
     554           0 :         std::getline(is, from_list, ' ');
     555             : 
     556           0 :         std::getline(is, ts, ' ');
     557           0 :         from_i = stoi(ts);
     558           0 : }
     559             : 
     560           0 : void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
     561             : {
     562           0 :         Inventory *inv_from = mgr->getInventory(from_inv);
     563             :         
     564           0 :         if(!inv_from){
     565           0 :                 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
     566           0 :                                 <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
     567           0 :                 return;
     568             :         }
     569             : 
     570           0 :         InventoryList *list_from = inv_from->getList(from_list);
     571             : 
     572             :         /*
     573             :                 If a list doesn't exist or the source item doesn't exist
     574             :         */
     575           0 :         if(!list_from){
     576           0 :                 infostream<<"IDropAction::apply(): FAIL: source list not found: "
     577           0 :                                 <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
     578           0 :                 return;
     579             :         }
     580           0 :         if(list_from->getItem(from_i).empty())
     581             :         {
     582           0 :                 infostream<<"IDropAction::apply(): FAIL: source item not found: "
     583           0 :                                 <<"from_inv=\""<<from_inv.dump()<<"\""
     584           0 :                                 <<", from_list=\""<<from_list<<"\""
     585           0 :                                 <<" from_i="<<from_i<<std::endl;
     586           0 :                 return;
     587             :         }
     588             : 
     589             :         /*
     590             :                 Do not handle rollback if inventory is player's
     591             :         */
     592           0 :         bool ignore_src_rollback = (from_inv.type == InventoryLocation::PLAYER);
     593             : 
     594             :         /*
     595             :                 Collect information of endpoints
     596             :         */
     597             : 
     598           0 :         int take_count = list_from->getItem(from_i).count;
     599           0 :         if(count != 0 && count < take_count)
     600           0 :                 take_count = count;
     601           0 :         int src_can_take_count = take_count;
     602             : 
     603             :         // Source is detached
     604           0 :         if(from_inv.type == InventoryLocation::DETACHED)
     605             :         {
     606           0 :                 ItemStack src_item = list_from->getItem(from_i);
     607           0 :                 src_item.count = take_count;
     608           0 :                 src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake(
     609           0 :                                 from_inv.name, from_list, from_i, src_item, player);
     610             :         }
     611             : 
     612             :         // Source is nodemeta
     613           0 :         if(from_inv.type == InventoryLocation::NODEMETA)
     614             :         {
     615           0 :                 ItemStack src_item = list_from->getItem(from_i);
     616           0 :                 src_item.count = take_count;
     617           0 :                 src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake(
     618           0 :                                 from_inv.p, from_list, from_i, src_item, player);
     619             :         }
     620             : 
     621           0 :         if(src_can_take_count != -1 && src_can_take_count < take_count)
     622           0 :                 take_count = src_can_take_count;
     623             :         
     624           0 :         int actually_dropped_count = 0;
     625             : 
     626           0 :         ItemStack src_item = list_from->getItem(from_i);
     627             : 
     628             :         // Drop the item
     629           0 :         ItemStack item1 = list_from->getItem(from_i);
     630           0 :         item1.count = take_count;
     631           0 :         if(PLAYER_TO_SA(player)->item_OnDrop(item1, player,
     632           0 :                                 player->getBasePosition() + v3f(0,1,0)))
     633             :         {
     634           0 :                 actually_dropped_count = take_count - item1.count;
     635             : 
     636           0 :                 if(actually_dropped_count == 0){
     637           0 :                         infostream<<"Actually dropped no items"<<std::endl;
     638           0 :                         return;
     639             :                 }
     640             :                 
     641             :                 // If source isn't infinite
     642           0 :                 if(src_can_take_count != -1){
     643             :                         // Take item from source list
     644           0 :                         ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count);
     645             : 
     646           0 :                         if(item2.count != actually_dropped_count)
     647           0 :                                 errorstream<<"Could not take dropped count of items"<<std::endl;
     648             : 
     649           0 :                         mgr->setInventoryModified(from_inv, false);
     650             :                 }
     651             :         }
     652             : 
     653           0 :         infostream<<"IDropAction::apply(): dropped "
     654           0 :                         <<" from inv=\""<<from_inv.dump()<<"\""
     655           0 :                         <<" list=\""<<from_list<<"\""
     656           0 :                         <<" i="<<from_i
     657           0 :                         <<std::endl;
     658             :         
     659           0 :         src_item.count = actually_dropped_count;
     660             : 
     661             :         /*
     662             :                 Report drop to endpoints
     663             :         */
     664             :         
     665             :         // Source is detached
     666           0 :         if(from_inv.type == InventoryLocation::DETACHED)
     667             :         {
     668           0 :                 PLAYER_TO_SA(player)->detached_inventory_OnTake(
     669           0 :                                 from_inv.name, from_list, from_i, src_item, player);
     670             :         }
     671             : 
     672             :         // Source is nodemeta
     673           0 :         if(from_inv.type == InventoryLocation::NODEMETA)
     674             :         {
     675           0 :                 PLAYER_TO_SA(player)->nodemeta_inventory_OnTake(
     676           0 :                                 from_inv.p, from_list, from_i, src_item, player);
     677             :         }
     678             : 
     679             :         /*
     680             :                 Record rollback information
     681             :         */
     682           0 :         if(!ignore_src_rollback && gamedef->rollback())
     683             :         {
     684           0 :                 IRollbackManager *rollback = gamedef->rollback();
     685             : 
     686             :                 // If source is not infinite, record item take
     687           0 :                 if(src_can_take_count != -1){
     688           0 :                         RollbackAction action;
     689           0 :                         std::string loc;
     690             :                         {
     691           0 :                                 std::ostringstream os(std::ios::binary);
     692           0 :                                 from_inv.serialize(os);
     693           0 :                                 loc = os.str();
     694             :                         }
     695           0 :                         action.setModifyInventoryStack(loc, from_list, from_i,
     696           0 :                                         false, src_item);
     697           0 :                         rollback->reportAction(action);
     698             :                 }
     699             :         }
     700             : }
     701             : 
     702           0 : void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
     703             : {
     704             :         // Optional InventoryAction operation that is run on the client
     705             :         // to make lag less apparent.
     706             : 
     707           0 :         Inventory *inv_from = mgr->getInventory(from_inv);
     708           0 :         if(!inv_from)
     709           0 :                 return;
     710             : 
     711           0 :         InventoryLocation current_player;
     712           0 :         current_player.setCurrentPlayer();
     713           0 :         Inventory *inv_player = mgr->getInventory(current_player);
     714           0 :         if(inv_from != inv_player)
     715           0 :                 return;
     716             : 
     717           0 :         InventoryList *list_from = inv_from->getList(from_list);
     718           0 :         if(!list_from)
     719           0 :                 return;
     720             : 
     721           0 :         if(count == 0)
     722           0 :                 list_from->changeItem(from_i, ItemStack());
     723             :         else
     724           0 :                 list_from->takeItem(from_i, count);
     725             : 
     726           0 :         mgr->setInventoryModified(from_inv);
     727             : }
     728             : 
     729             : /*
     730             :         ICraftAction
     731             : */
     732             : 
     733           0 : ICraftAction::ICraftAction(std::istream &is)
     734             : {
     735           0 :         std::string ts;
     736             : 
     737           0 :         std::getline(is, ts, ' ');
     738           0 :         count = stoi(ts);
     739             : 
     740           0 :         std::getline(is, ts, ' ');
     741           0 :         craft_inv.deSerialize(ts);
     742           0 : }
     743             : 
     744           0 : void ICraftAction::apply(InventoryManager *mgr,
     745             :         ServerActiveObject *player, IGameDef *gamedef)
     746             : {
     747           0 :         Inventory *inv_craft = mgr->getInventory(craft_inv);
     748             :         
     749           0 :         if (!inv_craft) {
     750           0 :                 infostream << "ICraftAction::apply(): FAIL: inventory not found: "
     751           0 :                                 << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
     752           0 :                 return;
     753             :         }
     754             : 
     755           0 :         InventoryList *list_craft = inv_craft->getList("craft");
     756           0 :         InventoryList *list_craftresult = inv_craft->getList("craftresult");
     757           0 :         InventoryList *list_main = inv_craft->getList("main");
     758             : 
     759             :         /*
     760             :                 If a list doesn't exist or the source item doesn't exist
     761             :         */
     762           0 :         if (!list_craft) {
     763           0 :                 infostream << "ICraftAction::apply(): FAIL: craft list not found: "
     764           0 :                                 << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
     765           0 :                 return;
     766             :         }
     767           0 :         if (!list_craftresult) {
     768           0 :                 infostream << "ICraftAction::apply(): FAIL: craftresult list not found: "
     769           0 :                                 << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
     770           0 :                 return;
     771             :         }
     772           0 :         if (list_craftresult->getSize() < 1) {
     773           0 :                 infostream << "ICraftAction::apply(): FAIL: craftresult list too short: "
     774           0 :                                 << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
     775           0 :                 return;
     776             :         }
     777             : 
     778           0 :         ItemStack crafted;
     779           0 :         ItemStack craftresultitem;
     780           0 :         int count_remaining = count;
     781           0 :         std::vector<ItemStack> output_replacements;
     782           0 :         getCraftingResult(inv_craft, crafted, output_replacements, false, gamedef);
     783           0 :         PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
     784           0 :         bool found = !crafted.empty();
     785             : 
     786           0 :         while (found && list_craftresult->itemFits(0, crafted)) {
     787           0 :                 InventoryList saved_craft_list = *list_craft;
     788             : 
     789           0 :                 std::vector<ItemStack> temp;
     790             :                 // Decrement input and add crafting output
     791           0 :                 getCraftingResult(inv_craft, crafted, temp, true, gamedef);
     792           0 :                 PLAYER_TO_SA(player)->item_OnCraft(crafted, player, &saved_craft_list, craft_inv);
     793           0 :                 list_craftresult->addItem(0, crafted);
     794           0 :                 mgr->setInventoryModified(craft_inv);
     795             : 
     796             :                 // Add the new replacements to the list
     797           0 :                 IItemDefManager *itemdef = gamedef->getItemDefManager();
     798           0 :                 for (std::vector<ItemStack>::iterator it = temp.begin();
     799           0 :                                 it != temp.end(); it++) {
     800           0 :                         for (std::vector<ItemStack>::iterator jt = output_replacements.begin();
     801           0 :                                         jt != output_replacements.end(); jt++) {
     802           0 :                                 if (it->name == jt->name) {
     803           0 :                                         *it = jt->addItem(*it, itemdef);
     804           0 :                                         if (it->empty())
     805           0 :                                                 continue;
     806             :                                 }
     807             :                         }
     808           0 :                         output_replacements.push_back(*it);
     809             :                 }
     810             : 
     811           0 :                 actionstream << player->getDescription()
     812           0 :                                 << " crafts "
     813           0 :                                 << crafted.getItemString()
     814           0 :                                 << std::endl;
     815             : 
     816             :                 // Decrement counter
     817           0 :                 if (count_remaining == 1)
     818           0 :                         break;
     819           0 :                 else if (count_remaining > 1)
     820           0 :                         count_remaining--;
     821             : 
     822             :                 // Get next crafting result
     823           0 :                 found = getCraftingResult(inv_craft, crafted, temp, false, gamedef);
     824           0 :                 PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
     825           0 :                 found = !crafted.empty();
     826             :         }
     827             : 
     828             :         // Put the replacements in the inventory or drop them on the floor, if
     829             :         // the invenotry is full
     830           0 :         for (std::vector<ItemStack>::iterator it = output_replacements.begin();
     831           0 :                         it != output_replacements.end(); it++) {
     832           0 :                 if (list_main)
     833           0 :                         *it = list_main->addItem(*it);
     834           0 :                 if (it->empty())
     835           0 :                         continue;
     836           0 :                 u16 count = it->count;
     837           0 :                 do {
     838           0 :                         PLAYER_TO_SA(player)->item_OnDrop(*it, player,
     839           0 :                                 player->getBasePosition() + v3f(0,1,0));
     840           0 :                         if (count >= it->count) {
     841           0 :                                 errorstream << "Couldn't drop replacement stack " <<
     842           0 :                                         it->getItemString() << " because drop loop didn't "
     843           0 :                                         "decrease count." << std::endl;
     844             : 
     845           0 :                                 break;
     846             :                         }
     847           0 :                 } while (!it->empty());
     848             :         }
     849             : 
     850           0 :         infostream<<"ICraftAction::apply(): crafted "
     851           0 :                         <<" craft_inv=\""<<craft_inv.dump()<<"\""
     852           0 :                         <<std::endl;
     853             : }
     854             : 
     855           0 : void ICraftAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
     856             : {
     857             :         // Optional InventoryAction operation that is run on the client
     858             :         // to make lag less apparent.
     859           0 : }
     860             : 
     861             : 
     862             : // Crafting helper
     863           0 : bool getCraftingResult(Inventory *inv, ItemStack& result,
     864             :                 std::vector<ItemStack> &output_replacements,
     865             :                 bool decrementInput, IGameDef *gamedef)
     866             : {
     867           0 :         DSTACK(__FUNCTION_NAME);
     868             :         
     869           0 :         result.clear();
     870             : 
     871             :         // Get the InventoryList in which we will operate
     872           0 :         InventoryList *clist = inv->getList("craft");
     873           0 :         if(!clist)
     874           0 :                 return false;
     875             : 
     876             :         // Mangle crafting grid to an another format
     877           0 :         CraftInput ci;
     878           0 :         ci.method = CRAFT_METHOD_NORMAL;
     879           0 :         ci.width = clist->getWidth() ? clist->getWidth() : 3;
     880           0 :         for(u16 i=0; i<clist->getSize(); i++)
     881           0 :                 ci.items.push_back(clist->getItem(i));
     882             : 
     883             :         // Find out what is crafted and add it to result item slot
     884           0 :         CraftOutput co;
     885           0 :         bool found = gamedef->getCraftDefManager()->getCraftResult(
     886           0 :                         ci, co, output_replacements, decrementInput, gamedef);
     887           0 :         if(found)
     888           0 :                 result.deSerialize(co.item, gamedef->getItemDefManager());
     889             : 
     890           0 :         if(found && decrementInput)
     891             :         {
     892             :                 // CraftInput has been changed, apply changes in clist
     893           0 :                 for(u16 i=0; i<clist->getSize(); i++)
     894             :                 {
     895           0 :                         clist->changeItem(i, ci.items[i]);
     896             :                 }
     897             :         }
     898             : 
     899           0 :         return found;
     900           3 : }
     901             : 

Generated by: LCOV version 1.11