void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_from = mgr->getInventory(from_inv); if(!inv_from){ infostream<<"IDropAction::apply(): FAIL: source inventory not found: " <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl; return; } InventoryList *list_from = inv_from->getList(from_list); /* If a list doesn't exist or the source item doesn't exist */ if(!list_from){ infostream<<"IDropAction::apply(): FAIL: source list not found: " <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl; return; } if(list_from->getItem(from_i).empty()) { infostream<<"IDropAction::apply(): FAIL: source item not found: " <<"from_inv=\""<<from_inv.dump()<<"\"" <<", from_list=\""<<from_list<<"\"" <<" from_i="<<from_i<<std::endl; return; } /* Do not handle rollback if inventory is player's */ bool ignore_src_rollback = (from_inv.type == InventoryLocation::PLAYER); /* Collect information of endpoints */ int take_count = list_from->getItem(from_i).count; if(count != 0 && count < take_count) take_count = count; int src_can_take_count = take_count; // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { ItemStack src_item = list_from->getItem(from_i); src_item.count = take_count; src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( from_inv.name, from_list, from_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { ItemStack src_item = list_from->getItem(from_i); src_item.count = take_count; src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( from_inv.p, from_list, from_i, src_item, player); } if(src_can_take_count != -1 && src_can_take_count < take_count) take_count = src_can_take_count; int actually_dropped_count = 0; ItemStack src_item = list_from->getItem(from_i); // Drop the item ItemStack item1 = list_from->getItem(from_i); item1.count = take_count; if(PLAYER_TO_SA(player)->item_OnDrop(item1, player, player->getBasePosition() + v3f(0,1,0))) { actually_dropped_count = take_count - item1.count; if(actually_dropped_count == 0){ infostream<<"Actually dropped no items"<<std::endl; return; } // If source isn't infinite if(src_can_take_count != -1){ // Take item from source list ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count); if(item2.count != actually_dropped_count) errorstream<<"Could not take dropped count of items"<<std::endl; mgr->setInventoryModified(from_inv, false); } } infostream<<"IDropAction::apply(): dropped " <<" from inv=\""<<from_inv.dump()<<"\"" <<" list=\""<<from_list<<"\"" <<" i="<<from_i <<std::endl; src_item.count = actually_dropped_count; /* Report drop to endpoints */ // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { PLAYER_TO_SA(player)->detached_inventory_OnTake( from_inv.name, from_list, from_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( from_inv.p, from_list, from_i, src_item, player); } /* Record rollback information */ if(!ignore_src_rollback && gamedef->rollback()) { IRollbackManager *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); rollback->reportAction(action); } } }
void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { 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: " << "from_inv=\""<<from_inv.dump() << "\"" << ", to_inv=\"" << to_inv.dump() << "\"" << std::endl; return; } if (!inv_to) { infostream << "IMoveAction::apply(): FAIL: destination inventory not found: " << "from_inv=\"" << from_inv.dump() << "\"" << ", to_inv=\"" << to_inv.dump() << "\"" << std::endl; return; } InventoryList *list_from = inv_from->getList(from_list); InventoryList *list_to = inv_to->getList(to_list); /* If a list doesn't exist or the source item doesn't exist */ if (!list_from) { infostream << "IMoveAction::apply(): FAIL: source list not found: " << "from_inv=\"" << from_inv.dump() << "\"" << ", from_list=\"" << from_list << "\"" << std::endl; return; } if (!list_to) { infostream << "IMoveAction::apply(): FAIL: destination list not found: " << "to_inv=\""<<to_inv.dump() << "\"" << ", to_list=\"" << to_list << "\"" << std::endl; return; } if (move_somewhere) { s16 old_to_i = to_i; u16 old_count = count; caused_by_move_somewhere = true; move_somewhere = false; infostream << "IMoveAction::apply(): moving item somewhere" << " msom=" << move_somewhere << " count=" << count << " from inv=\"" << from_inv.dump() << "\"" << " list=\"" << from_list << "\"" << " i=" << from_i << " to inv=\"" << to_inv.dump() << "\"" << " list=\"" << to_list << "\"" << std::endl; // Try to add the item to destination list s16 dest_size = list_to->getSize(); // First try all the non-empty slots for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) { if (!list_to->getItem(dest_i).empty()) { to_i = dest_i; apply(mgr, player, gamedef); count -= move_count; } } // Then try all the empty ones for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) { if (list_to->getItem(dest_i).empty()) { to_i = dest_i; apply(mgr, player, gamedef); count -= move_count; } } to_i = old_to_i; count = old_count; caused_by_move_somewhere = false; move_somewhere = true; return; } if ((u16)to_i > list_to->getSize()) { infostream << "IMoveAction::apply(): FAIL: destination index out of bounds: " << "to_i=" << to_i << ", size=" << list_to->getSize() << std::endl; return; } /* Do not handle rollback if both inventories are that of the same player */ bool ignore_rollback = ( from_inv.type == InventoryLocation::PLAYER && to_inv.type == InventoryLocation::PLAYER && from_inv.name == to_inv.name); /* Collect information of endpoints */ int try_take_count = count; if(try_take_count == 0) try_take_count = list_from->getItem(from_i).count; int src_can_take_count = 0xffff; int dst_can_put_count = 0xffff; /* Query detached inventories */ // Move occurs in the same detached inventory if(from_inv.type == InventoryLocation::DETACHED && to_inv.type == InventoryLocation::DETACHED && from_inv.name == to_inv.name) { src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove( from_inv.name, from_list, from_i, to_list, to_i, try_take_count, player); dst_can_put_count = src_can_take_count; } else { // Destination is detached if(to_inv.type == InventoryLocation::DETACHED) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut( to_inv.name, to_list, to_i, src_item, player); } // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( from_inv.name, from_list, from_i, src_item, player); } } /* Query node metadata inventories */ // Both endpoints are nodemeta // Move occurs in the same nodemeta inventory if(from_inv.type == InventoryLocation::NODEMETA && to_inv.type == InventoryLocation::NODEMETA && from_inv.p == to_inv.p) { src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove( from_inv.p, from_list, from_i, to_list, to_i, try_take_count, player); dst_can_put_count = src_can_take_count; } else { // Destination is nodemeta if(to_inv.type == InventoryLocation::NODEMETA) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut( to_inv.p, to_list, to_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( from_inv.p, from_list, from_i, src_item, player); } } int old_count = count; /* Modify count according to collected data */ count = try_take_count; if(src_can_take_count != -1 && count > 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="<<old_count <<" from inv=\""<<from_inv.dump()<<"\"" <<" list=\""<<from_list<<"\"" <<" i="<<from_i <<" to inv=\""<<to_inv.dump()<<"\"" <<" list=\""<<to_list<<"\"" <<" i="<<to_i <<std::endl; return; } ItemStack src_item = list_from->getItem(from_i); src_item.count = count; ItemStack from_stack_was = list_from->getItem(from_i); ItemStack to_stack_was = list_to->getItem(to_i); /* Perform actual move If something is wrong (source item is empty, destination is the same as source), nothing happens */ move_count = list_from->moveItem(from_i, list_to, to_i, count, !caused_by_move_somewhere); // If source is infinite, reset it's stack if(src_can_take_count == -1){ // If destination stack is of different type and there are leftover // items, attempt to put the leftover items to a different place in the // destination inventory. // The client-side GUI will try to guess if this happens. if(from_stack_was.name != to_stack_was.name){ for(u32 i=0; i<list_to->getSize(); i++){ if(list_to->getItem(i).empty()){ list_to->changeItem(i, to_stack_was); break; } } } list_from->deleteItem(from_i); list_from->addItem(from_i, from_stack_was); } // If destination is infinite, reset it's stack and take count from source if(dst_can_put_count == -1){ list_to->deleteItem(to_i); list_to->addItem(to_i, to_stack_was); list_from->deleteItem(from_i); list_from->addItem(from_i, from_stack_was); list_from->takeItem(from_i, count); } infostream << "IMoveAction::apply(): moved" << " msom=" << move_somewhere << " caused=" << caused_by_move_somewhere << " count=" << count << " from inv=\"" << from_inv.dump() << "\"" << " list=\"" << from_list << "\"" << " i=" << from_i << " to inv=\"" << to_inv.dump() << "\"" << " list=\"" << to_list << "\"" << " i=" << to_i << std::endl; /* Record rollback information */ if(!ignore_rollback && gamedef->rollback()) { IRollbackManager *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); rollback->reportAction(action); } // If destination is not infinite, record item put if(dst_can_put_count != -1){ RollbackAction action; std::string loc; { std::ostringstream os(std::ios::binary); to_inv.serialize(os); loc = os.str(); } action.setModifyInventoryStack(loc, to_list, to_i, true, src_item); rollback->reportAction(action); } } /* Report move to endpoints */ /* Detached inventories */ // Both endpoints are same detached if(from_inv.type == InventoryLocation::DETACHED && to_inv.type == InventoryLocation::DETACHED && from_inv.name == to_inv.name) { PLAYER_TO_SA(player)->detached_inventory_OnMove( from_inv.name, from_list, from_i, to_list, to_i, count, player); } else { // Destination is detached if(to_inv.type == InventoryLocation::DETACHED) { PLAYER_TO_SA(player)->detached_inventory_OnPut( to_inv.name, to_list, to_i, src_item, player); } // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { PLAYER_TO_SA(player)->detached_inventory_OnTake( from_inv.name, from_list, from_i, src_item, player); } } /* Node metadata inventories */ // Both endpoints are same nodemeta if(from_inv.type == InventoryLocation::NODEMETA && to_inv.type == InventoryLocation::NODEMETA && from_inv.p == to_inv.p) { PLAYER_TO_SA(player)->nodemeta_inventory_OnMove( from_inv.p, from_list, from_i, to_list, to_i, count, player); } else{ // Destination is nodemeta if(to_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnPut( to_inv.p, to_list, to_i, src_item, player); } // Source is nodemeta else if(from_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( from_inv.p, from_list, from_i, src_item, player); } } mgr->setInventoryModified(from_inv, false); if(inv_from != inv_to) mgr->setInventoryModified(to_inv, false); }