+ 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
+ */
+ bool did_swap = false;
+ move_count = list_from->moveItem(from_i,
+ list_to, to_i, count, allow_swap, &did_swap);
+ if (caused_by_move_somewhere)
+ count = old_count;
+ assert(allow_swap == did_swap);
+
+ // If source is infinite, reset it's stack
+ if (src_can_take_count == -1) {
+ // For the caused_by_move_somewhere == true case we didn't force-put the item,
+ // which guarantees there is no leftover, and code below would duplicate the
+ // (not replaced) to_stack_was item.
+ if (!caused_by_move_somewhere) {
+ // 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;
+ }
+ }
+ }
+ }
+ if (move_count > 0 || did_swap) {
+ 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;
+
+ // If we are inside the move somewhere loop, we don't need to report
+ // anything if nothing happened
+ if (caused_by_move_somewhere && move_count == 0)
+ return;
+
+ /*
+ 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
+ */
+
+ // Source = destination => move
+ if (from_inv == to_inv) {
+ onMove(count, player);
+ if (did_swap) {
+ // Item is now placed in source list
+ src_item = list_from->getItem(from_i);
+ swapDirections();
+ onMove(src_item.count, player);
+ swapDirections();
+ }
+ mgr->setInventoryModified(from_inv);
+ } else {
+ int src_item_count = src_item.count;
+ if (caused_by_move_somewhere)
+ // When moving somewhere: temporarily use the actual movable stack
+ // size to ensure correct callback execution.
+ src_item.count = move_count;
+ onPutAndOnTake(src_item, player);
+ if (caused_by_move_somewhere)
+ // Reset source item count
+ src_item.count = src_item_count;
+ if (did_swap) {
+ // Item is now placed in source list
+ src_item = list_from->getItem(from_i);
+ swapDirections();
+ onPutAndOnTake(src_item, player);
+ swapDirections();
+ }