UI work - trade is now a snapshot of the players' inventories at the time the trade was initiated and everything that happens on the trade menu is interacting with references to that snapshot. Inventory is validated at the end to make sure it still has the items that were in there when the trade started, and then the trade completes.

This commit is contained in:
trunksbomb
2026-03-24 21:27:32 -04:00
parent ce3e83b928
commit 31fd6c33ff
4 changed files with 294 additions and 112 deletions

View File

@@ -36,6 +36,7 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
private static final int BANNER_Y = 6;
private static final int CENTER_COLUMN_X = 115;
private static final int STATUS_LABEL_Y = 54;
private static final int CONFIRM_LABEL_Y = 34;
private static final int ACCEPT_BUTTON_Y = 74;
private static final int CANCEL_BUTTON_Y = 98;
private static final int ACTION_BUTTON_WIDTH = 48;
@@ -99,6 +100,9 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
onClose();
return true;
}
if (hoveredSlot != null) {
return true;
}
return super.keyPressed(keyCode, scanCode, modifiers);
}
@@ -120,6 +124,9 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
}
if (menu.view().stage() == TradeStage.CONFIRMING) {
if (hoveredSlot != null) {
return true;
}
return super.mouseClicked(mouseX, mouseY, button);
}
@@ -136,15 +143,19 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
return true;
}
if (hoveredSlot != null && hoveredSlot.container == menu.playerInventory()) {
if (hoveredSlot instanceof GhostInventorySlot ghostInventorySlot) {
if (button == 1) {
openContextMenu(mouseX, mouseY, TradeAction.ADD_ITEM, hoveredSlot.getSlotIndex(), hoveredSlot.getItem(), "Trade");
openContextMenu(mouseX, mouseY, TradeAction.ADD_ITEM, ghostInventorySlot.inventoryIndex(), hoveredSlot.getItem(), "Trade");
} else {
sendAction(TradeAction.ADD_ITEM, hoveredSlot.getSlotIndex(), 1);
sendAction(TradeAction.ADD_ITEM, ghostInventorySlot.inventoryIndex(), 1);
}
return true;
}
if (hoveredSlot != null) {
return true;
}
return super.mouseClicked(mouseX, mouseY, button);
}
@@ -158,10 +169,11 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
guiGraphics.drawString(font, "Trading with " + menu.view().otherName(), titleLabelX, titleLabelY, 0x404040, false);
guiGraphics.drawString(font, playerInventoryTitle, inventoryLabelX, inventoryLabelY, 0x404040, false);
if (menu.view().stage() == TradeStage.CONFIRMING) {
drawScaledCenteredColumnText(guiGraphics, "Are you", STATUS_LABEL_Y, 0xB02020, 0.7F);
drawScaledCenteredColumnText(guiGraphics, "sure you want", STATUS_LABEL_Y + 7, 0xB02020, 0.7F);
drawScaledCenteredColumnText(guiGraphics, "to trade?", STATUS_LABEL_Y + 14, 0xB02020, 0.7F);
} else if (menu.view().otherAccepted()) {
drawScaledCenteredColumnText(guiGraphics, "Are you", CONFIRM_LABEL_Y, 0xB02020, 0.7F);
drawScaledCenteredColumnText(guiGraphics, "sure you want", CONFIRM_LABEL_Y + 7, 0xB02020, 0.7F);
drawScaledCenteredColumnText(guiGraphics, "to trade?", CONFIRM_LABEL_Y + 14, 0xB02020, 0.7F);
}
if (menu.view().otherAccepted()) {
drawScaledCenteredColumnText(guiGraphics, "Other player", STATUS_LABEL_Y, 0x2E7D32, 0.7F);
drawScaledCenteredColumnText(guiGraphics, "has accepted", STATUS_LABEL_Y + 7, 0x2E7D32, 0.7F);
}
@@ -317,15 +329,14 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
private static final int OFFER_COLUMNS = 6;
private static final int OFFER_ROWS = 6;
private TradeView view;
private final Inventory playerInventory;
private final SimpleContainer inventoryDisplay = new SimpleContainer(TradeView.INVENTORY_SLOT_COUNT);
private final SimpleContainer selfOffer = new SimpleContainer(TradeView.OFFER_SLOT_COUNT);
private final SimpleContainer otherOffer = new SimpleContainer(TradeView.OFFER_SLOT_COUNT);
public TradeMenu(int containerId, Inventory inventory, TradeView view) {
super(MenuType.GENERIC_9x6, containerId);
this.playerInventory = inventory;
this.view = view;
syncOfferContainers();
syncContainers();
for (int row = 0; row < OFFER_ROWS; row++) {
for (int col = 0; col < OFFER_COLUMNS; col++) {
@@ -344,12 +355,12 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 9; col++) {
int slot = col + row * 9 + 9;
addSlot(new Slot(inventory, slot, INVENTORY_START_X + col * 18, INVENTORY_START_Y + row * 18));
addSlot(new GhostInventorySlot(inventoryDisplay, slot, INVENTORY_START_X + col * 18, INVENTORY_START_Y + row * 18));
}
}
for (int col = 0; col < 9; col++) {
addSlot(new Slot(inventory, col, INVENTORY_START_X + col * 18, INVENTORY_START_Y + 58));
addSlot(new GhostInventorySlot(inventoryDisplay, col, INVENTORY_START_X + col * 18, INVENTORY_START_Y + 58));
}
}
@@ -357,16 +368,15 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
return view;
}
public Inventory playerInventory() {
return playerInventory;
}
public void updateView(TradeView view) {
this.view = view;
syncOfferContainers();
syncContainers();
}
private void syncOfferContainers() {
private void syncContainers() {
for (int i = 0; i < TradeView.INVENTORY_SLOT_COUNT; i++) {
inventoryDisplay.setItem(i, view.inventory().get(i).copy());
}
for (int i = 0; i < TradeView.OFFER_SLOT_COUNT; i++) {
selfOffer.setItem(i, view.selfOffer().get(i).copy());
otherOffer.setItem(i, view.otherOffer().get(i).copy());
@@ -419,4 +429,24 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
return false;
}
}
private static class GhostInventorySlot extends Slot {
public GhostInventorySlot(Container container, int slot, int x, int y) {
super(container, slot, x, y);
}
public int inventoryIndex() {
return getSlotIndex();
}
@Override
public boolean mayPickup(Player player) {
return false;
}
@Override
public boolean mayPlace(ItemStack stack) {
return false;
}
}
}

View File

@@ -23,6 +23,7 @@ public class TradeMod {
modEventBus.addListener(this::registerPayloads);
NeoForge.EVENT_BUS.addListener(this::registerCommands);
NeoForge.EVENT_BUS.addListener(this::onPlayerLogout);
NeoForge.EVENT_BUS.addListener(this::onItemPickup);
}
private void commonSetup(FMLCommonSetupEvent event) {
@@ -43,4 +44,11 @@ public class TradeMod {
TradeManager.get(player.server).handleDisconnect(player);
}
}
public void onItemPickup(net.neoforged.neoforge.event.entity.player.ItemEntityPickupEvent.Pre event) {
if (event.getPlayer() instanceof net.minecraft.server.level.ServerPlayer player
&& TradeManager.get(player.server).isTrading(player)) {
event.setCanPickup(net.neoforged.neoforge.common.util.TriState.FALSE);
}
}
}

View File

@@ -22,7 +22,8 @@ public class DebugTradeSession {
private final UUID id = UUID.randomUUID();
private final ServerPlayer player;
private final List<ItemStack> selfOffer = blankOffer();
private final List<ItemStack> inventorySnapshot;
private final List<TradeEntry> selfOffer = blankOffer();
private final List<ItemStack> otherOffer = blankStacks();
private boolean selfAccepted;
private boolean otherAccepted;
@@ -30,6 +31,7 @@ public class DebugTradeSession {
public DebugTradeSession(ServerPlayer player) {
this.player = player;
this.inventorySnapshot = inventorySnapshot(player);
}
public UUID id() {
@@ -53,23 +55,34 @@ public class DebugTradeSession {
return false;
}
Inventory inventory = player.getInventory();
ItemStack sourceStack = inventory.getItem(inventorySlot);
ItemStack sourceStack = inventorySnapshot.get(inventorySlot);
if (sourceStack.isEmpty()) {
return false;
}
int moveAmount = Math.max(1, Math.min(amount, sourceStack.getCount()));
ItemStack moving = sourceStack.copyWithCount(moveAmount);
if (!insertStack(selfOffer, moving)) {
int available = sourceStack.getCount() - reservedCount(inventorySlot);
if (available <= 0) {
return false;
}
sourceStack.shrink(moveAmount);
if (sourceStack.isEmpty()) {
inventory.setItem(inventorySlot, ItemStack.EMPTY);
int moveAmount = Math.max(1, Math.min(amount, available));
for (int i = 0; i < selfOffer.size(); i++) {
TradeEntry entry = selfOffer.get(i);
if (entry != null && entry.sourceSlot() == inventorySlot && ItemStack.isSameItemSameComponents(entry.stack(), sourceStack)) {
ItemStack merged = entry.stack().copy();
merged.grow(moveAmount);
selfOffer.set(i, new TradeEntry(inventorySlot, merged));
clearAccepts();
return true;
}
}
inventory.setChanged();
int freeSlot = firstFreeOfferSlot(selfOffer);
if (freeSlot == -1) {
return false;
}
selfOffer.set(freeSlot, new TradeEntry(inventorySlot, sourceStack.copyWithCount(moveAmount)));
clearAccepts();
return true;
}
@@ -79,30 +92,18 @@ public class DebugTradeSession {
return false;
}
ItemStack offered = selfOffer.get(offerSlot);
if (offered.isEmpty()) {
TradeEntry entry = selfOffer.get(offerSlot);
if (entry == null) {
return false;
}
List<ItemStack> workingInventory = inventorySnapshot();
int moveAmount = Math.max(1, Math.min(amount, offered.getCount()));
ItemStack returned = offered.copyWithCount(moveAmount);
if (!insertStack(workingInventory, returned)) {
return false;
}
Inventory inventory = player.getInventory();
for (int i = 0; i < workingInventory.size(); i++) {
inventory.setItem(i, workingInventory.get(i));
}
inventory.setChanged();
if (moveAmount < offered.getCount()) {
ItemStack updated = offered.copy();
int moveAmount = Math.max(1, Math.min(amount, entry.stack().getCount()));
if (moveAmount < entry.stack().getCount()) {
ItemStack updated = entry.stack().copy();
updated.shrink(moveAmount);
selfOffer.set(offerSlot, updated);
selfOffer.set(offerSlot, new TradeEntry(entry.sourceSlot(), updated));
} else {
selfOffer.set(offerSlot, ItemStack.EMPTY);
selfOffer.set(offerSlot, null);
}
clearAccepts();
@@ -208,7 +209,11 @@ public class DebugTradeSession {
}
public boolean completeTrade() {
List<ItemStack> result = inventorySnapshot();
List<ItemStack> result = inventorySnapshot(player);
if (!removeOutgoing(result, selfOffer)) {
return false;
}
for (ItemStack stack : otherOffer) {
if (!stack.isEmpty() && !insertStack(result, stack.copy())) {
return false;
@@ -232,7 +237,7 @@ public class DebugTradeSession {
stage,
selfAccepted,
otherAccepted,
inventorySnapshot(),
inventoryDisplay(),
emptyReservedSnapshot(),
selfOfferSnapshot(),
otherOfferSnapshot());
@@ -312,6 +317,25 @@ public class DebugTradeSession {
}
}
private static boolean removeOutgoing(List<ItemStack> working, List<TradeEntry> outgoing) {
for (TradeEntry entry : outgoing) {
if (entry == null) {
continue;
}
ItemStack current = working.get(entry.sourceSlot());
if (!ItemStack.isSameItemSameComponents(current, entry.stack()) || current.getCount() < entry.stack().getCount()) {
return false;
}
ItemStack updated = current.copy();
updated.shrink(entry.stack().getCount());
working.set(entry.sourceSlot(), updated);
}
return true;
}
private static boolean insertStack(List<ItemStack> working, ItemStack incoming) {
if (incoming.isEmpty()) {
return true;
@@ -350,6 +374,49 @@ public class DebugTradeSession {
return false;
}
private int reservedCount(int inventorySlot) {
int count = 0;
for (TradeEntry entry : selfOffer) {
if (entry != null && entry.sourceSlot() == inventorySlot) {
count += entry.stack().getCount();
}
}
return count;
}
private void clearAccepts() {
selfAccepted = false;
otherAccepted = false;
}
private List<ItemStack> inventorySnapshotCopy() {
List<ItemStack> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
for (ItemStack stack : inventorySnapshot) {
result.add(stack.copy());
}
return result;
}
private List<ItemStack> inventoryDisplay() {
List<ItemStack> result = inventorySnapshotCopy();
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
int reserved = reservedCount(i);
if (reserved <= 0) {
continue;
}
ItemStack stack = result.get(i);
if (stack.isEmpty()) {
continue;
}
ItemStack updated = stack.copy();
updated.shrink(reserved);
result.set(i, updated);
}
return result;
}
private static List<Integer> emptyReservedSnapshot() {
List<Integer> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
@@ -358,12 +425,7 @@ public class DebugTradeSession {
return result;
}
private void clearAccepts() {
selfAccepted = false;
otherAccepted = false;
}
private List<ItemStack> inventorySnapshot() {
private static List<ItemStack> inventorySnapshot(ServerPlayer player) {
List<ItemStack> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
Inventory inventory = player.getInventory();
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
@@ -374,8 +436,8 @@ public class DebugTradeSession {
private List<ItemStack> selfOfferSnapshot() {
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
for (ItemStack stack : selfOffer) {
result.add(stack.copy());
for (TradeEntry entry : selfOffer) {
result.add(entry == null ? ItemStack.EMPTY : entry.stack().copy());
}
return result;
}
@@ -388,10 +450,10 @@ public class DebugTradeSession {
return result;
}
private static List<ItemStack> blankOffer() {
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
private static List<TradeEntry> blankOffer() {
List<TradeEntry> result = new ArrayList<>(OFFER_SLOT_COUNT);
for (int i = 0; i < OFFER_SLOT_COUNT; i++) {
result.add(ItemStack.EMPTY);
result.add(null);
}
return result;
}
@@ -404,4 +466,12 @@ public class DebugTradeSession {
return result;
}
private static int firstFreeOfferSlot(List<TradeEntry> offer) {
for (int i = 0; i < offer.size(); i++) {
if (offer.get(i) == null) {
return i;
}
}
return -1;
}
}

View File

@@ -18,8 +18,10 @@ public class TradeSession {
private final UUID id = UUID.randomUUID();
private final ServerPlayer first;
private final ServerPlayer second;
private final List<ItemStack> firstOffer = blankOffer();
private final List<ItemStack> secondOffer = blankOffer();
private final List<ItemStack> firstInventory;
private final List<ItemStack> secondInventory;
private final List<TradeEntry> firstOffer = blankOffer();
private final List<TradeEntry> secondOffer = blankOffer();
private boolean firstAccepted;
private boolean secondAccepted;
private TradeStage stage = TradeStage.OFFERING;
@@ -27,6 +29,8 @@ public class TradeSession {
public TradeSession(ServerPlayer first, ServerPlayer second) {
this.first = first;
this.second = second;
this.firstInventory = inventorySnapshot(first);
this.secondInventory = inventorySnapshot(second);
}
public UUID id() {
@@ -64,24 +68,35 @@ public class TradeSession {
return false;
}
List<ItemStack> offer = offerFor(player);
Inventory inventory = player.getInventory();
ItemStack sourceStack = inventory.getItem(inventorySlot);
ItemStack sourceStack = inventoryFor(player).get(inventorySlot);
if (sourceStack.isEmpty()) {
return false;
}
int moveAmount = Math.max(1, Math.min(amount, sourceStack.getCount()));
ItemStack moving = sourceStack.copyWithCount(moveAmount);
if (!insertStack(offer, moving)) {
int available = sourceStack.getCount() - reservedCount(player, inventorySlot);
if (available <= 0) {
return false;
}
sourceStack.shrink(moveAmount);
if (sourceStack.isEmpty()) {
inventory.setItem(inventorySlot, ItemStack.EMPTY);
int moveAmount = Math.max(1, Math.min(amount, available));
List<TradeEntry> offer = offerFor(player);
for (int i = 0; i < offer.size(); i++) {
TradeEntry entry = offer.get(i);
if (entry != null && entry.sourceSlot() == inventorySlot && ItemStack.isSameItemSameComponents(entry.stack(), sourceStack)) {
ItemStack merged = entry.stack().copy();
merged.grow(moveAmount);
offer.set(i, new TradeEntry(inventorySlot, merged));
clearAccepts();
return true;
}
}
inventory.setChanged();
int freeSlot = firstFreeOfferSlot(offer);
if (freeSlot == -1) {
return false;
}
offer.set(freeSlot, new TradeEntry(inventorySlot, sourceStack.copyWithCount(moveAmount)));
clearAccepts();
return true;
}
@@ -91,28 +106,21 @@ public class TradeSession {
return false;
}
List<ItemStack> offer = offerFor(player);
ItemStack offered = offer.get(offerSlot);
if (offered.isEmpty()) {
List<TradeEntry> offer = offerFor(player);
TradeEntry entry = offer.get(offerSlot);
if (entry == null) {
return false;
}
Inventory inventory = player.getInventory();
List<ItemStack> workingInventory = inventorySnapshot(player);
int moveAmount = Math.max(1, Math.min(amount, offered.getCount()));
ItemStack returned = offered.copyWithCount(moveAmount);
if (!insertStack(workingInventory, returned)) {
return false;
}
applyInventory(player, workingInventory);
if (moveAmount < offered.getCount()) {
ItemStack updated = offered.copy();
int moveAmount = Math.max(1, Math.min(amount, entry.stack().getCount()));
if (moveAmount < entry.stack().getCount()) {
ItemStack updated = entry.stack().copy();
updated.shrink(moveAmount);
offer.set(offerSlot, updated);
offer.set(offerSlot, new TradeEntry(entry.sourceSlot(), updated));
} else {
offer.set(offerSlot, ItemStack.EMPTY);
offer.set(offerSlot, null);
}
clearAccepts();
return true;
}
@@ -142,16 +150,14 @@ public class TradeSession {
}
public boolean completeTrade() {
List<ItemStack> firstResult = simulateResult(first, secondOffer);
List<ItemStack> secondResult = simulateResult(second, firstOffer);
List<ItemStack> firstResult = simulateResult(first, firstOffer, secondOffer);
List<ItemStack> secondResult = simulateResult(second, secondOffer, firstOffer);
if (firstResult == null || secondResult == null) {
return false;
}
applyInventory(first, firstResult);
applyInventory(second, secondResult);
clearOffer(firstOffer);
clearOffer(secondOffer);
return true;
}
@@ -166,7 +172,7 @@ public class TradeSession {
stage,
isFirst ? firstAccepted : secondAccepted,
isFirst ? secondAccepted : firstAccepted,
inventorySnapshot(player),
inventoryDisplayFor(player),
emptyReservedSnapshot(),
offerSnapshot(isFirst ? firstOffer : secondOffer),
offerSnapshot(isFirst ? secondOffer : firstOffer));
@@ -176,10 +182,14 @@ public class TradeSession {
PacketDistributor.sendToPlayer(player, new TradeStatePayload(viewFor(player)));
}
private List<ItemStack> simulateResult(ServerPlayer player, List<ItemStack> incoming) {
private List<ItemStack> simulateResult(ServerPlayer player, List<TradeEntry> outgoing, List<TradeEntry> incoming) {
List<ItemStack> working = inventorySnapshot(player);
for (ItemStack stack : incoming) {
if (!stack.isEmpty() && !insertStack(working, stack.copy())) {
if (!removeOutgoing(working, outgoing)) {
return null;
}
for (TradeEntry entry : incoming) {
if (entry != null && !insertStack(working, entry.stack().copy())) {
return null;
}
}
@@ -187,6 +197,25 @@ public class TradeSession {
return working;
}
private static boolean removeOutgoing(List<ItemStack> working, List<TradeEntry> outgoing) {
for (TradeEntry entry : outgoing) {
if (entry == null) {
continue;
}
ItemStack current = working.get(entry.sourceSlot());
if (!ItemStack.isSameItemSameComponents(current, entry.stack()) || current.getCount() < entry.stack().getCount()) {
return false;
}
ItemStack updated = current.copy();
updated.shrink(entry.stack().getCount());
working.set(entry.sourceSlot(), updated);
}
return true;
}
private static boolean insertStack(List<ItemStack> working, ItemStack incoming) {
if (incoming.isEmpty()) {
return true;
@@ -225,12 +254,6 @@ public class TradeSession {
return false;
}
private static void clearOffer(List<ItemStack> offer) {
for (int i = 0; i < offer.size(); i++) {
offer.set(i, ItemStack.EMPTY);
}
}
private static void applyInventory(ServerPlayer player, List<ItemStack> result) {
Inventory inventory = player.getInventory();
for (int i = 0; i < result.size(); i++) {
@@ -239,19 +262,74 @@ public class TradeSession {
inventory.setChanged();
}
private static List<ItemStack> blankOffer() {
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
private static List<TradeEntry> blankOffer() {
List<TradeEntry> result = new ArrayList<>(OFFER_SLOT_COUNT);
for (int i = 0; i < OFFER_SLOT_COUNT; i++) {
result.add(ItemStack.EMPTY);
result.add(null);
}
return result;
}
private static int firstFreeOfferSlot(List<TradeEntry> offer) {
for (int i = 0; i < offer.size(); i++) {
if (offer.get(i) == null) {
return i;
}
}
return -1;
}
private void clearAccepts() {
firstAccepted = false;
secondAccepted = false;
}
private int reservedCount(ServerPlayer player, int inventorySlot) {
int count = 0;
for (TradeEntry entry : offerFor(player)) {
if (entry != null && entry.sourceSlot() == inventorySlot) {
count += entry.stack().getCount();
}
}
return count;
}
private List<TradeEntry> offerFor(ServerPlayer player) {
return player == first ? firstOffer : secondOffer;
}
private List<ItemStack> inventoryFor(ServerPlayer player) {
return player == first ? firstInventory : secondInventory;
}
private List<ItemStack> inventorySnapshotFor(ServerPlayer player) {
List<ItemStack> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
for (ItemStack stack : inventoryFor(player)) {
result.add(stack.copy());
}
return result;
}
private List<ItemStack> inventoryDisplayFor(ServerPlayer player) {
List<ItemStack> result = inventorySnapshotFor(player);
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
int reserved = reservedCount(player, i);
if (reserved <= 0) {
continue;
}
ItemStack stack = result.get(i);
if (stack.isEmpty()) {
continue;
}
ItemStack updated = stack.copy();
updated.shrink(reserved);
result.set(i, updated);
}
return result;
}
private static List<Integer> emptyReservedSnapshot() {
List<Integer> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
@@ -260,10 +338,6 @@ public class TradeSession {
return result;
}
private List<ItemStack> offerFor(ServerPlayer player) {
return player == first ? firstOffer : secondOffer;
}
private static List<ItemStack> inventorySnapshot(ServerPlayer player) {
List<ItemStack> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
Inventory inventory = player.getInventory();
@@ -273,10 +347,10 @@ public class TradeSession {
return result;
}
private static List<ItemStack> offerSnapshot(List<ItemStack> offer) {
private static List<ItemStack> offerSnapshot(List<TradeEntry> offer) {
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
for (ItemStack stack : offer) {
result.add(stack.copy());
for (TradeEntry entry : offer) {
result.add(entry == null ? ItemStack.EMPTY : entry.stack().copy());
}
return result;
}