Work around vanilla capturing ItemStack references (#1700)

This commit is contained in:
Technici4n 2021-09-09 19:47:06 +02:00 committed by GitHub
parent ffb6d41e97
commit 86bae2c0e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 1 deletions

View file

@ -115,6 +115,7 @@ public abstract class SingleStackStorage extends SnapshotParticipant<ItemStack>
if (insertedAmount > 0) { if (insertedAmount > 0) {
updateSnapshots(transaction); updateSnapshots(transaction);
currentStack = getStack();
if (currentStack.isEmpty()) { if (currentStack.isEmpty()) {
currentStack = insertedVariant.toStack(insertedAmount); currentStack = insertedVariant.toStack(insertedAmount);
@ -142,6 +143,7 @@ public abstract class SingleStackStorage extends SnapshotParticipant<ItemStack>
if (extracted > 0) { if (extracted > 0) {
this.updateSnapshots(transaction); this.updateSnapshots(transaction);
currentStack = getStack();
currentStack.decrement(extracted); currentStack.decrement(extracted);
setStack(currentStack); setStack(currentStack);
} }
@ -154,7 +156,9 @@ public abstract class SingleStackStorage extends SnapshotParticipant<ItemStack>
@Override @Override
protected final ItemStack createSnapshot() { protected final ItemStack createSnapshot() {
return getStack().copy(); ItemStack original = getStack();
setStack(original.copy());
return original;
} }
@Override @Override

View file

@ -34,6 +34,7 @@ class InventorySlotWrapper extends SingleStackStorage {
*/ */
private final InventoryStorageImpl storage; private final InventoryStorageImpl storage;
final int slot; final int slot;
private ItemStack lastReleasedSnapshot = null;
InventorySlotWrapper(InventoryStorageImpl storage, int slot) { InventorySlotWrapper(InventoryStorageImpl storage, int slot) {
this.storage = storage; this.storage = storage;
@ -66,4 +67,25 @@ class InventorySlotWrapper extends SingleStackStorage {
storage.markDirtyParticipant.updateSnapshots(transaction); storage.markDirtyParticipant.updateSnapshots(transaction);
super.updateSnapshots(transaction); super.updateSnapshots(transaction);
} }
@Override
protected void releaseSnapshot(ItemStack snapshot) {
lastReleasedSnapshot = snapshot;
}
@Override
protected void onFinalCommit() {
// Try to apply the change to the original stack
ItemStack original = lastReleasedSnapshot;
ItemStack currentStack = getStack();
if (!original.isEmpty() && !currentStack.isEmpty() && ItemStack.canCombine(original, currentStack)) {
// None is empty and the contents match: just update the amount and reuse the original stack.
original.setCount(currentStack.getCount());
setStack(original);
} else {
// Otherwise assume everything was taken from original so empty it.
original.setCount(0);
}
}
} }

View file

@ -40,11 +40,36 @@ import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
*/ */
class ItemTests { class ItemTests {
public static void run() { public static void run() {
testStackReference();
testInventoryWrappers(); testInventoryWrappers();
testLimitedStackCountInventory(); testLimitedStackCountInventory();
testLimitedStackCountItem(); testLimitedStackCountItem();
} }
private static void testStackReference() {
// Ensure that Inventory wrappers will try to mutate the backing stack as much as possible.
// In many cases, MC code captures a reference to the ItemStack so we want to edit that stack directly
// and not a copy whenever we can. Obviously this can't be perfect, but we try to cover as many cases as possible.
SimpleInventory inv = new SimpleInventory(new ItemStack(Items.DIAMOND, 2));
InventoryStorage invWrapper = InventoryStorage.of(inv, null);
ItemStack stack = inv.getStack(0);
// Simulate should correctly reset the stack.
try (Transaction tx = Transaction.openOuter()) {
invWrapper.extract(ItemVariant.of(Items.DIAMOND), 2, tx);
}
if (stack != inv.getStack(0)) throw new AssertionError("Stack should have stayed the same.");
// Commit should try to edit the original stack when it is feasible to do so.
try (Transaction tx = Transaction.openOuter()) {
invWrapper.extract(ItemVariant.of(Items.DIAMOND), 1, tx);
tx.commit();
}
if (stack != inv.getStack(0)) throw new AssertionError("Stack should have stayed the same.");
}
private static void testInventoryWrappers() { private static void testInventoryWrappers() {
ItemVariant emptyBucket = ItemVariant.of(Items.BUCKET); ItemVariant emptyBucket = ItemVariant.of(Items.BUCKET);
TestSidedInventory testInventory = new TestSidedInventory(); TestSidedInventory testInventory = new TestSidedInventory();