+
+ /*
+ Record rollback information
+ */
+ if(!ignore_rollback && gamedef->rollback())
+ {
+ 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);
+ }
+ // 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.getItemString());
+ 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);
+ if(inv_from != inv_to)
+ mgr->setInventoryModified(to_inv);
+}
+
+void IMoveAction::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);
+ Inventory *inv_to = mgr->getInventory(to_inv);
+ if(!inv_from || !inv_to)
+ return;
+
+ InventoryLocation current_player;
+ current_player.setCurrentPlayer();
+ Inventory *inv_player = mgr->getInventory(current_player);
+ if(inv_from != inv_player || inv_to != inv_player)
+ return;
+
+ InventoryList *list_from = inv_from->getList(from_list);
+ InventoryList *list_to = inv_to->getList(to_list);
+ if(!list_from || !list_to)
+ return;
+
+ list_from->moveItem(from_i, list_to, to_i, count);
+
+ mgr->setInventoryModified(from_inv);
+ if(inv_from != inv_to)
+ mgr->setInventoryModified(to_inv);