mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Inventory: Fix order of callbacks when swapping items
This commit is contained in:
parent
d5d6e36ae0
commit
4245a7604b
6 changed files with 119 additions and 95 deletions
|
@ -162,7 +162,20 @@ void IMoveAction::swapDirections()
|
|||
std::swap(from_i, to_i);
|
||||
}
|
||||
|
||||
void IMoveAction::onPutAndOnTake(const ItemStack &src_item, ServerActiveObject *player) const
|
||||
void IMoveAction::onTake(const ItemStack &src_item, ServerActiveObject *player) const
|
||||
{
|
||||
ServerScripting *sa = PLAYER_TO_SA(player);
|
||||
if (from_inv.type == InventoryLocation::DETACHED)
|
||||
sa->detached_inventory_OnTake(*this, src_item, player);
|
||||
else if (from_inv.type == InventoryLocation::NODEMETA)
|
||||
sa->nodemeta_inventory_OnTake(*this, src_item, player);
|
||||
else if (from_inv.type == InventoryLocation::PLAYER)
|
||||
sa->player_inventory_OnTake(*this, src_item, player);
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void IMoveAction::onPut(const ItemStack &src_item, ServerActiveObject *player) const
|
||||
{
|
||||
ServerScripting *sa = PLAYER_TO_SA(player);
|
||||
if (to_inv.type == InventoryLocation::DETACHED)
|
||||
|
@ -173,15 +186,6 @@ void IMoveAction::onPutAndOnTake(const ItemStack &src_item, ServerActiveObject *
|
|||
sa->player_inventory_OnPut(*this, src_item, player);
|
||||
else
|
||||
assert(false);
|
||||
|
||||
if (from_inv.type == InventoryLocation::DETACHED)
|
||||
sa->detached_inventory_OnTake(*this, src_item, player);
|
||||
else if (from_inv.type == InventoryLocation::NODEMETA)
|
||||
sa->nodemeta_inventory_OnTake(*this, src_item, player);
|
||||
else if (from_inv.type == InventoryLocation::PLAYER)
|
||||
sa->player_inventory_OnTake(*this, src_item, player);
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void IMoveAction::onMove(int count, ServerActiveObject *player) const
|
||||
|
@ -244,6 +248,8 @@ int IMoveAction::allowMove(int try_take_count, ServerActiveObject *player) const
|
|||
|
||||
void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
|
||||
{
|
||||
/// Necessary for executing Lua callbacks which may manipulate the inventory,
|
||||
/// hence invalidate pointers needed by IMoveAction::apply
|
||||
auto get_borrow_checked_invlist = [mgr](const InventoryLocation &invloc,
|
||||
const std::string &listname) -> InventoryList::ResizeLocked
|
||||
{
|
||||
|
@ -375,6 +381,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
bool allow_swap = !list_to->itemFits(to_i, src_item, &restitem)
|
||||
&& restitem.count == src_item.count
|
||||
&& !caused_by_move_somewhere;
|
||||
// move_count : How many items that were moved at the end
|
||||
// count : Total items "in the queue" of being moved. Do not touch.
|
||||
move_count = src_item.count - restitem.count;
|
||||
|
||||
// Shift-click: Cannot fill this stack, proceed with next slot
|
||||
|
@ -383,10 +391,11 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
}
|
||||
|
||||
if (allow_swap) {
|
||||
// Swap will affect the entire stack if it can performed.
|
||||
// Swap will affect the entire stack (= count) if it can performed.
|
||||
src_item = list_from->getItem(from_i);
|
||||
count = src_item.count;
|
||||
move_count = src_item.count;
|
||||
}
|
||||
src_item.count = move_count; // Temporary movement stack
|
||||
|
||||
if (from_inv == to_inv) {
|
||||
// Move action within the same inventory
|
||||
|
@ -409,16 +418,9 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
src_can_take_count = dst_can_put_count = 0;
|
||||
} else {
|
||||
// Take from one inventory, put into another
|
||||
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;
|
||||
dst_can_put_count = allowPut(src_item, player);
|
||||
src_can_take_count = allowTake(src_item, player);
|
||||
if (caused_by_move_somewhere)
|
||||
// Reset source item count
|
||||
src_item.count = src_item_count;
|
||||
|
||||
bool swap_expected = allow_swap;
|
||||
allow_swap = allow_swap
|
||||
&& (src_can_take_count == -1 || src_can_take_count >= src_item.count)
|
||||
|
@ -440,25 +442,20 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
src_can_take_count = dst_can_put_count = 0;
|
||||
}
|
||||
|
||||
int old_count = count;
|
||||
int old_move_count = move_count;
|
||||
|
||||
/* Modify count according to collected data */
|
||||
count = src_item.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;
|
||||
// Apply limits given by allow_* callbacks
|
||||
if (src_can_take_count != -1)
|
||||
move_count = (u32)std::min<s32>(src_can_take_count, move_count);
|
||||
if (dst_can_put_count != -1)
|
||||
move_count = (u32)std::min<s32>(dst_can_put_count, move_count);
|
||||
|
||||
/* Limit according to source item count */
|
||||
if (count > list_from->getItem(from_i).count)
|
||||
count = list_from->getItem(from_i).count;
|
||||
// allow_* callbacks should not modify the stack - but if they do - handle that.
|
||||
if (move_count > list_from->getItem(from_i).count)
|
||||
move_count = list_from->getItem(from_i).count;
|
||||
|
||||
/* If no items will be moved, don't go further */
|
||||
if (count == 0) {
|
||||
if (caused_by_move_somewhere)
|
||||
// Set move count to zero, as no items have been moved
|
||||
move_count = 0;
|
||||
|
||||
if (move_count == 0) {
|
||||
// Undo client prediction. See 'clientApply'
|
||||
if (from_inv.type == InventoryLocation::PLAYER)
|
||||
list_from->setModified();
|
||||
|
@ -467,7 +464,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
list_to->setModified();
|
||||
|
||||
infostream<<"IMoveAction::apply(): move was completely disallowed:"
|
||||
<<" count="<<old_count
|
||||
<<" move_count="<<old_move_count
|
||||
<<" from inv=\""<<from_inv.dump()<<"\""
|
||||
<<" list=\""<<from_list<<"\""
|
||||
<<" i="<<from_i
|
||||
|
@ -479,8 +476,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
return;
|
||||
}
|
||||
|
||||
src_item = list_from->getItem(from_i);
|
||||
src_item.count = count;
|
||||
// Backups stacks for infinite sources
|
||||
ItemStack from_stack_was = list_from->getItem(from_i);
|
||||
ItemStack to_stack_was = list_to->getItem(to_i);
|
||||
|
||||
|
@ -491,13 +487,13 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
same as source), nothing happens
|
||||
*/
|
||||
bool did_swap = false;
|
||||
move_count = list_from->moveItem(from_i,
|
||||
list_to.get(), to_i, count, allow_swap, &did_swap);
|
||||
if (caused_by_move_somewhere)
|
||||
count = old_count;
|
||||
src_item = list_from->moveItem(from_i,
|
||||
list_to.get(), to_i, move_count, allow_swap, &did_swap);
|
||||
move_count = src_item.count;
|
||||
|
||||
assert(allow_swap == did_swap);
|
||||
|
||||
// If source is infinite, reset it's stack
|
||||
// If source is infinite, reset its 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
|
||||
|
@ -517,23 +513,23 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
}
|
||||
}
|
||||
if (move_count > 0 || did_swap) {
|
||||
list_from->deleteItem(from_i);
|
||||
list_from->addItem(from_i, from_stack_was);
|
||||
list_from->changeItem(from_i, from_stack_was);
|
||||
}
|
||||
}
|
||||
// If destination is infinite, reset it's stack and take count from source
|
||||
// If destination is infinite, reset its 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);
|
||||
list_to->changeItem(to_i, to_stack_was);
|
||||
if (did_swap) {
|
||||
// Undo swap result: set the expected stack + size
|
||||
list_from->changeItem(from_i, from_stack_was);
|
||||
list_from->takeItem(from_i, move_count);
|
||||
}
|
||||
}
|
||||
|
||||
infostream << "IMoveAction::apply(): moved"
|
||||
<< " msom=" << move_somewhere
|
||||
<< " caused=" << caused_by_move_somewhere
|
||||
<< " count=" << count
|
||||
<< " move_count=" << move_count
|
||||
<< " from inv=\"" << from_inv.dump() << "\""
|
||||
<< " list=\"" << from_list << "\""
|
||||
<< " i=" << from_i
|
||||
|
@ -589,9 +585,9 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
|
||||
// Source = destination => move
|
||||
if (from_inv == to_inv) {
|
||||
onMove(count, player);
|
||||
onMove(move_count, player);
|
||||
if (did_swap) {
|
||||
// Item is now placed in source list
|
||||
// Already swapped. The other stack is now placed in "from" list
|
||||
list_from = get_borrow_checked_invlist(from_inv, from_list);
|
||||
if (list_from) {
|
||||
src_item = list_from->getItem(from_i);
|
||||
|
@ -603,26 +599,34 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
|
|||
}
|
||||
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;
|
||||
ItemStack swap_item;
|
||||
if (did_swap) {
|
||||
// Item is now placed in source list
|
||||
// Already swapped. The other stack is now placed in "from" list
|
||||
list_from = get_borrow_checked_invlist(from_inv, from_list);
|
||||
if (list_from) {
|
||||
src_item = list_from->getItem(from_i);
|
||||
swap_item = list_from->getItem(from_i);
|
||||
list_from.reset();
|
||||
swapDirections();
|
||||
onPutAndOnTake(src_item, player);
|
||||
swapDirections();
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Take the ItemStack (visually: freely detached)
|
||||
onTake(src_item, player);
|
||||
if (!swap_item.empty() && get_borrow_checked_invlist(to_inv, to_list)) {
|
||||
swapDirections();
|
||||
onTake(swap_item, player);
|
||||
swapDirections();
|
||||
}
|
||||
|
||||
// 2. Put the ItemStack
|
||||
if (get_borrow_checked_invlist(to_inv, to_list))
|
||||
onPut(src_item, player);
|
||||
|
||||
if (!swap_item.empty() && get_borrow_checked_invlist(to_inv, to_list)) {
|
||||
swapDirections();
|
||||
onPut(swap_item, player);
|
||||
swapDirections();
|
||||
}
|
||||
|
||||
mgr->setInventoryModified(to_inv);
|
||||
mgr->setInventoryModified(from_inv);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue