Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #122 by simulating inventory transactions before execution. #127

Open
wants to merge 2 commits into
base: mc-1.19.x/stable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -302,39 +302,30 @@ else if( object instanceof Container container )
private static int moveItem( ItemStorage from, int fromSlot, ItemStorage to, int toSlot, final int limit )
{
// Moving nothing is easy
if( limit == 0 )
{
return 0;
}
if( limit == 0 ) return 0;

// Get stack to move
ItemStack stack = InventoryUtil.takeItems( limit, from, fromSlot, 1, fromSlot );
if( stack.isEmpty() )
{
return 0;
}
int stackCount = stack.getCount();
// Simulate transaction
ItemStack simulatedStack = InventoryUtil.takeItems( limit, from, fromSlot, 1, fromSlot, true );
ItemStack simulatedRemainder = toSlot < 0
? InventoryUtil.storeItems( simulatedStack, to, true )
: InventoryUtil.storeItems( simulatedStack, to, toSlot, 1, toSlot, true );

// Move items in
ItemStack remainder;
if( toSlot < 0 )
{
remainder = InventoryUtil.storeItems( stack, to );
}
else
{
remainder = InventoryUtil.storeItems( stack, to, toSlot, 1, toSlot );
}
// Count how many items were successfully transferred
int transferCount = simulatedStack.getCount() - simulatedRemainder.getCount();

// Calculate items moved
int count = stackCount - remainder.getCount();
// Execute the transaction
ItemStack stack = InventoryUtil.takeItems( transferCount, from, fromSlot, 1, fromSlot, false );
ItemStack remainder = toSlot < 0
? InventoryUtil.storeItems( stack, to, false )
: InventoryUtil.storeItems( stack, to, toSlot, 1, toSlot, false );

if( !remainder.isEmpty() )
{
// Put the remainder back
InventoryUtil.storeItems( remainder, from, fromSlot, 1, fromSlot );
ComputerCraft.log.error( "Items were lost during a generic inventory transfer!" );
ComputerCraft.log.error( String.format( "from=%s fromSlot=%d to=%s toSlot=%d limit=%d", from, fromSlot, to, toSlot, limit ) );
ComputerCraft.log.error( "Please report this at https://github.com/cc-tweaked/cc-restitched/issues" );
}

return count;
return transferCount;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public final Object[] equipBack()
ItemStack stack = previousUpgrade.getCraftingItem();
if( !stack.isEmpty() )
{
stack = InventoryUtil.storeItems( stack, ItemStorage.wrap( inventory ), inventory.selected );
stack = InventoryUtil.storeItems( stack, ItemStorage.wrap( inventory ), inventory.selected, false );
if( !stack.isEmpty() )
{
WorldUtil.dropItemStack( stack, player.getCommandSenderWorld(), player.position() );
Expand Down Expand Up @@ -118,7 +118,7 @@ public final Object[] unequipBack()
ItemStack stack = previousUpgrade.getCraftingItem();
if( !stack.isEmpty() )
{
stack = InventoryUtil.storeItems( stack, ItemStorage.wrap( inventory ), inventory.selected );
stack = InventoryUtil.storeItems( stack, ItemStorage.wrap( inventory ), inventory.selected, false );
if( stack.isEmpty() )
{
WorldUtil.dropItemStack( stack, player.getCommandSenderWorld(), player.position() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public int refuel( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack currentStac
Item replacementStack = stack.getItem().getCraftingRemainingItem();
if( replacementStack != null )
{
ItemStack remainder = InventoryUtil.storeItems( new ItemStack( replacementStack ), turtle.getItemHandler(), turtle.getSelectedSlot() );
ItemStack remainder = InventoryUtil.storeItems( new ItemStack( replacementStack ), turtle.getItemHandler(), turtle.getSelectedSlot(), false );
if( !remainder.isEmpty() )
{
WorldUtil.dropItemStack( remainder, turtle.getLevel(), turtle.getPosition(), turtle.getDirection().getOpposite() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
// Store or drop any remainders
for( ItemStack stack : results )
{
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot(), false );
if( !remainder.isEmpty() )
{
WorldUtil.dropItemStack( remainder, turtle.getLevel(), turtle.getPosition(), turtle.getDirection() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
Direction direction = this.direction.toWorldDir( turtle );

// Get things to drop
ItemStack stack = InventoryUtil.takeItems( quantity, turtle.getItemHandler(), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot() );
ItemStack stack = InventoryUtil.takeItems( quantity, turtle.getItemHandler(), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot(), false );
if( stack.isEmpty() )
{
return TurtleCommandResult.failure( "No items to drop" );
Expand All @@ -63,11 +63,11 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
if( inventory != null )
{
// Drop the item into the inventory
ItemStack remainder = InventoryUtil.storeItems( stack, ItemStorage.wrap( inventory, side ) );
ItemStack remainder = InventoryUtil.storeItems( stack, ItemStorage.wrap( inventory, side ), false );
if( !remainder.isEmpty() )
{
// Put the remainder back in the turtle
InventoryUtil.storeItems( remainder, turtle.getItemHandler(), turtle.getSelectedSlot() );
InventoryUtil.storeItems( remainder, turtle.getItemHandler(), turtle.getSelectedSlot(), false );
}

// Return true if we stored anything
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
if( newUpgradeStack != null )
{
// Consume new upgrades item
InventoryUtil.takeItems( 1, ItemStorage.wrap( inventory ), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot() );
InventoryUtil.takeItems( 1, ItemStorage.wrap( inventory ), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot(), false );
}
if( oldUpgradeStack != null )
{
// Store old upgrades item
ItemStack remainder = InventoryUtil.storeItems( oldUpgradeStack, ItemStorage.wrap( inventory ), turtle.getSelectedSlot() );
ItemStack remainder = InventoryUtil.storeItems( oldUpgradeStack, ItemStorage.wrap( inventory ), turtle.getSelectedSlot(), false );
if( !remainder.isEmpty() )
{
// If there's no room for the items, drop them
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ private static boolean deployOnEntity( @Nonnull ItemStack stack, final ITurtleAc
Vec3 hitPos = hit.getValue();

ItemStorage itemHandler = ItemStorage.wrap( turtlePlayer.getInventory() );
DropConsumer.set( hitEntity, drop -> InventoryUtil.storeItems( drop, itemHandler, 1 ) );
DropConsumer.set( hitEntity, drop -> InventoryUtil.storeItems( drop, itemHandler, 1, false ) );

boolean placed = doDeployOnEntity( stack, turtlePlayer, hitEntity, hitPos );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ public void unloadInventory( ITurtleAccess turtle )
int totalSize = getInventory().getContainerSize();
for( int i = slots; i < totalSize; i++ )
{
ItemStack remainder = InventoryUtil.storeItems( getInventory().getItem( i ), turtle.getItemHandler(), turtle.getSelectedSlot() );
ItemStack remainder = InventoryUtil.storeItems( getInventory().getItem( i ), turtle.getItemHandler(), turtle.getSelectedSlot(), false );
if( !remainder.isEmpty() )
{
WorldUtil.dropItemStack( remainder, turtle.getLevel(), dropPosition, dropDirection );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package dan200.computercraft.shared.turtle.core;

import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleAnimation;
Expand Down Expand Up @@ -60,28 +61,30 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
{
ItemStorage inventory = ItemStorage.wrap( inventoryContainer );

// Take from inventory of thing in front
ItemStack stack = InventoryUtil.takeItems( quantity, inventory );
if( stack.isEmpty() ) return TurtleCommandResult.failure( "No items to take" );
// Simulate extracting from inventory of thing in front
ItemStack simulatedExtraction = InventoryUtil.takeItems( quantity, inventory, true );
if( simulatedExtraction.isEmpty() ) return TurtleCommandResult.failure( "No items to take" );

// Simulate inserting result into turtle's inventory
ItemStack simulatedRemainder = InventoryUtil.storeItems( simulatedExtraction, turtle.getItemHandler(), turtle.getSelectedSlot(), true );
if( simulatedRemainder == simulatedExtraction ) return TurtleCommandResult.failure( "No space for items" );

// Calculate how many items successfully transferred
int transferCount = simulatedExtraction.getCount() - simulatedRemainder.getCount();

// Execute the transaction
ItemStack stack = InventoryUtil.takeItems( transferCount, inventory, false );
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot(), false );

// Try to place into the turtle
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
if( !remainder.isEmpty() )
{
// Put the remainder back in the inventory
InventoryUtil.storeItems( remainder, inventory );
ComputerCraft.log.error( "Items were lost during a TurtleSuckCommand!" );
ComputerCraft.log.error( String.format( "from=%s quantity=%d, direction=%s", inventory, quantity, direction ) );
ComputerCraft.log.error( "Please report this at https://github.com/cc-tweaked/cc-restitched/issues" );
}

// Return true if we consumed anything
if( remainder != stack )
{
turtle.playAnimation( TurtleAnimation.WAIT );
return TurtleCommandResult.success();
}
else
{
return TurtleCommandResult.failure( "No space for items" );
}
turtle.playAnimation( TurtleAnimation.WAIT );
return TurtleCommandResult.success();
}
else
{
Expand Down Expand Up @@ -111,7 +114,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
leaveStack = ItemStack.EMPTY;
}

ItemStack remainder = InventoryUtil.storeItems( storeStack, turtle.getItemHandler(), turtle.getSelectedSlot() );
ItemStack remainder = InventoryUtil.storeItems( storeStack, turtle.getItemHandler(), turtle.getSelectedSlot(), false );

if( remainder != storeStack )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ public TurtleTransferToCommand( int slot, int limit )
public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
{
// Take stack
ItemStack stack = InventoryUtil.takeItems( quantity, turtle.getItemHandler(), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot() );
ItemStack stack = InventoryUtil.takeItems( quantity, turtle.getItemHandler(), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot(), false );
if( stack.isEmpty() )
{
turtle.playAnimation( TurtleAnimation.WAIT );
return TurtleCommandResult.success();
}

// Store stack
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), slot, 1, slot );
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), slot, 1, slot, false );
if( !remainder.isEmpty() )
{
// Put the remainder back
InventoryUtil.storeItems( remainder, turtle.getItemHandler(), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot() );
InventoryUtil.storeItems( remainder, turtle.getItemHandler(), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot(), false );
}

// Return true if we moved anything
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ private TurtleCommandResult dig( ITurtleAccess turtle, Direction direction )

private static Function<ItemStack, ItemStack> turtleDropConsumer( BlockEntity tile, ITurtleAccess turtle )
{
return drop -> tile.isRemoved() ? drop : InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
return drop -> tile.isRemoved() ? drop : InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot(), false );
}

private static void stopConsuming( BlockEntity tile, ITurtleAccess turtle )
Expand Down
24 changes: 12 additions & 12 deletions src/main/java/dan200/computercraft/shared/util/InventoryUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,19 @@ public static Container getInventory( BlockEntity tileEntity )
// Methods for placing into inventories:

@Nonnull
public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory, int begin )
public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory, int begin, boolean simulate )
{
return storeItems( itemstack, inventory, 0, inventory.size(), begin );
return storeItems( itemstack, inventory, 0, inventory.size(), begin, simulate );
}

@Nonnull
public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory )
public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory, boolean simulate )
{
return storeItems( itemstack, inventory, 0, inventory.size(), 0 );
return storeItems( itemstack, inventory, 0, inventory.size(), 0, simulate );
}

@Nonnull
public static ItemStack storeItems( @Nonnull ItemStack stack, ItemStorage inventory, int start, int range, int begin )
public static ItemStack storeItems( @Nonnull ItemStack stack, ItemStorage inventory, int start, int range, int begin, boolean simulate )
{
if( stack.isEmpty() ) return ItemStack.EMPTY;

Expand All @@ -125,27 +125,27 @@ public static ItemStack storeItems( @Nonnull ItemStack stack, ItemStorage invent
{
int slot = start + (i + begin - start) % range;
if( remainder.isEmpty() ) break;
remainder = inventory.store( slot, remainder, false );
remainder = inventory.store( slot, remainder, simulate );
}
return areItemsEqual( stack, remainder ) ? stack : remainder;
}

// Methods for taking out of inventories

@Nonnull
public static ItemStack takeItems( int count, ItemStorage inventory, int begin )
public static ItemStack takeItems( int count, ItemStorage inventory, int begin, boolean simulate )
{
return takeItems( count, inventory, 0, inventory.size(), begin );
return takeItems( count, inventory, 0, inventory.size(), begin, simulate );
}

@Nonnull
public static ItemStack takeItems( int count, ItemStorage inventory )
public static ItemStack takeItems( int count, ItemStorage inventory, boolean simulate )
{
return takeItems( count, inventory, 0, inventory.size(), 0 );
return takeItems( count, inventory, 0, inventory.size(), 0, simulate );
}

@Nonnull
public static ItemStack takeItems( int count, ItemStorage inventory, int start, int range, int begin )
public static ItemStack takeItems( int count, ItemStorage inventory, int start, int range, int begin, boolean simulate )
{
// Combine multiple stacks from inventory into one if necessary
ItemStack partialStack = ItemStack.EMPTY;
Expand All @@ -157,7 +157,7 @@ public static ItemStack takeItems( int count, ItemStorage inventory, int start,
if( count <= 0 ) break;

// If this doesn't slot, abort.
ItemStack extracted = inventory.take( slot, count, partialStack, false );
ItemStack extracted = inventory.take( slot, count, partialStack, simulate );
if( extracted.isEmpty() )
{
continue;
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/dan200/computercraft/shared/util/ItemStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ else if( areStackable( stack, existing ) )
return stack;
}
}

@Override
public String toString()
{
return String.format( "%s{type=%s}", getClass().getSimpleName(), inventory.getClass().getSimpleName() );
}
}

class SidedInventoryWrapper extends InventoryWrapper
Expand Down Expand Up @@ -248,6 +254,12 @@ public ItemStack store( int slot, @Nonnull ItemStack stack, boolean simulate )
}
return super.store( mappedSlot, stack, simulate );
}

@Override
public String toString()
{
return String.format( "%s{type=%s, facing=%s}", getClass().getSimpleName(), inventory.getClass().getSimpleName(), facing );
}
}

class View implements ItemStorage
Expand Down Expand Up @@ -317,5 +329,11 @@ public ItemStorage view( int start, int size )
{
return new View( parent, this.start + start, size );
}

@Override
public String toString()
{
return String.format( "%s{parent=%s, start=%d, size=%d}", getClass().getSimpleName(), parent, start, size );
}
}
}