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:
@@ -36,6 +36,7 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
|
|||||||
private static final int BANNER_Y = 6;
|
private static final int BANNER_Y = 6;
|
||||||
private static final int CENTER_COLUMN_X = 115;
|
private static final int CENTER_COLUMN_X = 115;
|
||||||
private static final int STATUS_LABEL_Y = 54;
|
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 ACCEPT_BUTTON_Y = 74;
|
||||||
private static final int CANCEL_BUTTON_Y = 98;
|
private static final int CANCEL_BUTTON_Y = 98;
|
||||||
private static final int ACTION_BUTTON_WIDTH = 48;
|
private static final int ACTION_BUTTON_WIDTH = 48;
|
||||||
@@ -99,6 +100,9 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
|
|||||||
onClose();
|
onClose();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (hoveredSlot != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +124,9 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (menu.view().stage() == TradeStage.CONFIRMING) {
|
if (menu.view().stage() == TradeStage.CONFIRMING) {
|
||||||
|
if (hoveredSlot != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return super.mouseClicked(mouseX, mouseY, button);
|
return super.mouseClicked(mouseX, mouseY, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,15 +143,19 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hoveredSlot != null && hoveredSlot.container == menu.playerInventory()) {
|
if (hoveredSlot instanceof GhostInventorySlot ghostInventorySlot) {
|
||||||
if (button == 1) {
|
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 {
|
} else {
|
||||||
sendAction(TradeAction.ADD_ITEM, hoveredSlot.getSlotIndex(), 1);
|
sendAction(TradeAction.ADD_ITEM, ghostInventorySlot.inventoryIndex(), 1);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hoveredSlot != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return super.mouseClicked(mouseX, mouseY, button);
|
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, "Trading with " + menu.view().otherName(), titleLabelX, titleLabelY, 0x404040, false);
|
||||||
guiGraphics.drawString(font, playerInventoryTitle, inventoryLabelX, inventoryLabelY, 0x404040, false);
|
guiGraphics.drawString(font, playerInventoryTitle, inventoryLabelX, inventoryLabelY, 0x404040, false);
|
||||||
if (menu.view().stage() == TradeStage.CONFIRMING) {
|
if (menu.view().stage() == TradeStage.CONFIRMING) {
|
||||||
drawScaledCenteredColumnText(guiGraphics, "Are you", STATUS_LABEL_Y, 0xB02020, 0.7F);
|
drawScaledCenteredColumnText(guiGraphics, "Are you", CONFIRM_LABEL_Y, 0xB02020, 0.7F);
|
||||||
drawScaledCenteredColumnText(guiGraphics, "sure you want", STATUS_LABEL_Y + 7, 0xB02020, 0.7F);
|
drawScaledCenteredColumnText(guiGraphics, "sure you want", CONFIRM_LABEL_Y + 7, 0xB02020, 0.7F);
|
||||||
drawScaledCenteredColumnText(guiGraphics, "to trade?", STATUS_LABEL_Y + 14, 0xB02020, 0.7F);
|
drawScaledCenteredColumnText(guiGraphics, "to trade?", CONFIRM_LABEL_Y + 14, 0xB02020, 0.7F);
|
||||||
} else if (menu.view().otherAccepted()) {
|
}
|
||||||
|
if (menu.view().otherAccepted()) {
|
||||||
drawScaledCenteredColumnText(guiGraphics, "Other player", STATUS_LABEL_Y, 0x2E7D32, 0.7F);
|
drawScaledCenteredColumnText(guiGraphics, "Other player", STATUS_LABEL_Y, 0x2E7D32, 0.7F);
|
||||||
drawScaledCenteredColumnText(guiGraphics, "has accepted", STATUS_LABEL_Y + 7, 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_COLUMNS = 6;
|
||||||
private static final int OFFER_ROWS = 6;
|
private static final int OFFER_ROWS = 6;
|
||||||
private TradeView view;
|
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 selfOffer = new SimpleContainer(TradeView.OFFER_SLOT_COUNT);
|
||||||
private final SimpleContainer otherOffer = new SimpleContainer(TradeView.OFFER_SLOT_COUNT);
|
private final SimpleContainer otherOffer = new SimpleContainer(TradeView.OFFER_SLOT_COUNT);
|
||||||
|
|
||||||
public TradeMenu(int containerId, Inventory inventory, TradeView view) {
|
public TradeMenu(int containerId, Inventory inventory, TradeView view) {
|
||||||
super(MenuType.GENERIC_9x6, containerId);
|
super(MenuType.GENERIC_9x6, containerId);
|
||||||
this.playerInventory = inventory;
|
|
||||||
this.view = view;
|
this.view = view;
|
||||||
syncOfferContainers();
|
syncContainers();
|
||||||
|
|
||||||
for (int row = 0; row < OFFER_ROWS; row++) {
|
for (int row = 0; row < OFFER_ROWS; row++) {
|
||||||
for (int col = 0; col < OFFER_COLUMNS; col++) {
|
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 row = 0; row < 3; row++) {
|
||||||
for (int col = 0; col < 9; col++) {
|
for (int col = 0; col < 9; col++) {
|
||||||
int slot = col + row * 9 + 9;
|
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++) {
|
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;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Inventory playerInventory() {
|
|
||||||
return playerInventory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateView(TradeView view) {
|
public void updateView(TradeView view) {
|
||||||
this.view = 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++) {
|
for (int i = 0; i < TradeView.OFFER_SLOT_COUNT; i++) {
|
||||||
selfOffer.setItem(i, view.selfOffer().get(i).copy());
|
selfOffer.setItem(i, view.selfOffer().get(i).copy());
|
||||||
otherOffer.setItem(i, view.otherOffer().get(i).copy());
|
otherOffer.setItem(i, view.otherOffer().get(i).copy());
|
||||||
@@ -419,4 +429,24 @@ public class TradeScreen extends AbstractContainerScreen<TradeScreen.TradeMenu>
|
|||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class TradeMod {
|
|||||||
modEventBus.addListener(this::registerPayloads);
|
modEventBus.addListener(this::registerPayloads);
|
||||||
NeoForge.EVENT_BUS.addListener(this::registerCommands);
|
NeoForge.EVENT_BUS.addListener(this::registerCommands);
|
||||||
NeoForge.EVENT_BUS.addListener(this::onPlayerLogout);
|
NeoForge.EVENT_BUS.addListener(this::onPlayerLogout);
|
||||||
|
NeoForge.EVENT_BUS.addListener(this::onItemPickup);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void commonSetup(FMLCommonSetupEvent event) {
|
private void commonSetup(FMLCommonSetupEvent event) {
|
||||||
@@ -43,4 +44,11 @@ public class TradeMod {
|
|||||||
TradeManager.get(player.server).handleDisconnect(player);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ public class DebugTradeSession {
|
|||||||
|
|
||||||
private final UUID id = UUID.randomUUID();
|
private final UUID id = UUID.randomUUID();
|
||||||
private final ServerPlayer player;
|
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 final List<ItemStack> otherOffer = blankStacks();
|
||||||
private boolean selfAccepted;
|
private boolean selfAccepted;
|
||||||
private boolean otherAccepted;
|
private boolean otherAccepted;
|
||||||
@@ -30,6 +31,7 @@ public class DebugTradeSession {
|
|||||||
|
|
||||||
public DebugTradeSession(ServerPlayer player) {
|
public DebugTradeSession(ServerPlayer player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
|
this.inventorySnapshot = inventorySnapshot(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID id() {
|
public UUID id() {
|
||||||
@@ -53,23 +55,34 @@ public class DebugTradeSession {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Inventory inventory = player.getInventory();
|
ItemStack sourceStack = inventorySnapshot.get(inventorySlot);
|
||||||
ItemStack sourceStack = inventory.getItem(inventorySlot);
|
|
||||||
if (sourceStack.isEmpty()) {
|
if (sourceStack.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int moveAmount = Math.max(1, Math.min(amount, sourceStack.getCount()));
|
int available = sourceStack.getCount() - reservedCount(inventorySlot);
|
||||||
ItemStack moving = sourceStack.copyWithCount(moveAmount);
|
if (available <= 0) {
|
||||||
if (!insertStack(selfOffer, moving)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceStack.shrink(moveAmount);
|
int moveAmount = Math.max(1, Math.min(amount, available));
|
||||||
if (sourceStack.isEmpty()) {
|
for (int i = 0; i < selfOffer.size(); i++) {
|
||||||
inventory.setItem(inventorySlot, ItemStack.EMPTY);
|
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();
|
clearAccepts();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -79,30 +92,18 @@ public class DebugTradeSession {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemStack offered = selfOffer.get(offerSlot);
|
TradeEntry entry = selfOffer.get(offerSlot);
|
||||||
if (offered.isEmpty()) {
|
if (entry == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ItemStack> workingInventory = inventorySnapshot();
|
int moveAmount = Math.max(1, Math.min(amount, entry.stack().getCount()));
|
||||||
int moveAmount = Math.max(1, Math.min(amount, offered.getCount()));
|
if (moveAmount < entry.stack().getCount()) {
|
||||||
ItemStack returned = offered.copyWithCount(moveAmount);
|
ItemStack updated = entry.stack().copy();
|
||||||
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();
|
|
||||||
updated.shrink(moveAmount);
|
updated.shrink(moveAmount);
|
||||||
selfOffer.set(offerSlot, updated);
|
selfOffer.set(offerSlot, new TradeEntry(entry.sourceSlot(), updated));
|
||||||
} else {
|
} else {
|
||||||
selfOffer.set(offerSlot, ItemStack.EMPTY);
|
selfOffer.set(offerSlot, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAccepts();
|
clearAccepts();
|
||||||
@@ -208,7 +209,11 @@ public class DebugTradeSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean completeTrade() {
|
public boolean completeTrade() {
|
||||||
List<ItemStack> result = inventorySnapshot();
|
List<ItemStack> result = inventorySnapshot(player);
|
||||||
|
if (!removeOutgoing(result, selfOffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (ItemStack stack : otherOffer) {
|
for (ItemStack stack : otherOffer) {
|
||||||
if (!stack.isEmpty() && !insertStack(result, stack.copy())) {
|
if (!stack.isEmpty() && !insertStack(result, stack.copy())) {
|
||||||
return false;
|
return false;
|
||||||
@@ -232,7 +237,7 @@ public class DebugTradeSession {
|
|||||||
stage,
|
stage,
|
||||||
selfAccepted,
|
selfAccepted,
|
||||||
otherAccepted,
|
otherAccepted,
|
||||||
inventorySnapshot(),
|
inventoryDisplay(),
|
||||||
emptyReservedSnapshot(),
|
emptyReservedSnapshot(),
|
||||||
selfOfferSnapshot(),
|
selfOfferSnapshot(),
|
||||||
otherOfferSnapshot());
|
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) {
|
private static boolean insertStack(List<ItemStack> working, ItemStack incoming) {
|
||||||
if (incoming.isEmpty()) {
|
if (incoming.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
@@ -350,6 +374,49 @@ public class DebugTradeSession {
|
|||||||
return false;
|
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() {
|
private static List<Integer> emptyReservedSnapshot() {
|
||||||
List<Integer> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
|
List<Integer> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
|
||||||
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
|
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
|
||||||
@@ -358,12 +425,7 @@ public class DebugTradeSession {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearAccepts() {
|
private static List<ItemStack> inventorySnapshot(ServerPlayer player) {
|
||||||
selfAccepted = false;
|
|
||||||
otherAccepted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ItemStack> inventorySnapshot() {
|
|
||||||
List<ItemStack> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
|
List<ItemStack> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
|
||||||
Inventory inventory = player.getInventory();
|
Inventory inventory = player.getInventory();
|
||||||
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
|
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
|
||||||
@@ -374,8 +436,8 @@ public class DebugTradeSession {
|
|||||||
|
|
||||||
private List<ItemStack> selfOfferSnapshot() {
|
private List<ItemStack> selfOfferSnapshot() {
|
||||||
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
|
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
|
||||||
for (ItemStack stack : selfOffer) {
|
for (TradeEntry entry : selfOffer) {
|
||||||
result.add(stack.copy());
|
result.add(entry == null ? ItemStack.EMPTY : entry.stack().copy());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -388,10 +450,10 @@ public class DebugTradeSession {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ItemStack> blankOffer() {
|
private static List<TradeEntry> blankOffer() {
|
||||||
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
|
List<TradeEntry> result = new ArrayList<>(OFFER_SLOT_COUNT);
|
||||||
for (int i = 0; i < OFFER_SLOT_COUNT; i++) {
|
for (int i = 0; i < OFFER_SLOT_COUNT; i++) {
|
||||||
result.add(ItemStack.EMPTY);
|
result.add(null);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -404,4 +466,12 @@ public class DebugTradeSession {
|
|||||||
return result;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ public class TradeSession {
|
|||||||
private final UUID id = UUID.randomUUID();
|
private final UUID id = UUID.randomUUID();
|
||||||
private final ServerPlayer first;
|
private final ServerPlayer first;
|
||||||
private final ServerPlayer second;
|
private final ServerPlayer second;
|
||||||
private final List<ItemStack> firstOffer = blankOffer();
|
private final List<ItemStack> firstInventory;
|
||||||
private final List<ItemStack> secondOffer = blankOffer();
|
private final List<ItemStack> secondInventory;
|
||||||
|
private final List<TradeEntry> firstOffer = blankOffer();
|
||||||
|
private final List<TradeEntry> secondOffer = blankOffer();
|
||||||
private boolean firstAccepted;
|
private boolean firstAccepted;
|
||||||
private boolean secondAccepted;
|
private boolean secondAccepted;
|
||||||
private TradeStage stage = TradeStage.OFFERING;
|
private TradeStage stage = TradeStage.OFFERING;
|
||||||
@@ -27,6 +29,8 @@ public class TradeSession {
|
|||||||
public TradeSession(ServerPlayer first, ServerPlayer second) {
|
public TradeSession(ServerPlayer first, ServerPlayer second) {
|
||||||
this.first = first;
|
this.first = first;
|
||||||
this.second = second;
|
this.second = second;
|
||||||
|
this.firstInventory = inventorySnapshot(first);
|
||||||
|
this.secondInventory = inventorySnapshot(second);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID id() {
|
public UUID id() {
|
||||||
@@ -64,24 +68,35 @@ public class TradeSession {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ItemStack> offer = offerFor(player);
|
ItemStack sourceStack = inventoryFor(player).get(inventorySlot);
|
||||||
Inventory inventory = player.getInventory();
|
|
||||||
ItemStack sourceStack = inventory.getItem(inventorySlot);
|
|
||||||
if (sourceStack.isEmpty()) {
|
if (sourceStack.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int moveAmount = Math.max(1, Math.min(amount, sourceStack.getCount()));
|
int available = sourceStack.getCount() - reservedCount(player, inventorySlot);
|
||||||
ItemStack moving = sourceStack.copyWithCount(moveAmount);
|
if (available <= 0) {
|
||||||
if (!insertStack(offer, moving)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceStack.shrink(moveAmount);
|
int moveAmount = Math.max(1, Math.min(amount, available));
|
||||||
if (sourceStack.isEmpty()) {
|
List<TradeEntry> offer = offerFor(player);
|
||||||
inventory.setItem(inventorySlot, ItemStack.EMPTY);
|
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();
|
clearAccepts();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -91,28 +106,21 @@ public class TradeSession {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ItemStack> offer = offerFor(player);
|
List<TradeEntry> offer = offerFor(player);
|
||||||
ItemStack offered = offer.get(offerSlot);
|
TradeEntry entry = offer.get(offerSlot);
|
||||||
if (offered.isEmpty()) {
|
if (entry == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Inventory inventory = player.getInventory();
|
int moveAmount = Math.max(1, Math.min(amount, entry.stack().getCount()));
|
||||||
List<ItemStack> workingInventory = inventorySnapshot(player);
|
if (moveAmount < entry.stack().getCount()) {
|
||||||
int moveAmount = Math.max(1, Math.min(amount, offered.getCount()));
|
ItemStack updated = entry.stack().copy();
|
||||||
ItemStack returned = offered.copyWithCount(moveAmount);
|
|
||||||
if (!insertStack(workingInventory, returned)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyInventory(player, workingInventory);
|
|
||||||
if (moveAmount < offered.getCount()) {
|
|
||||||
ItemStack updated = offered.copy();
|
|
||||||
updated.shrink(moveAmount);
|
updated.shrink(moveAmount);
|
||||||
offer.set(offerSlot, updated);
|
offer.set(offerSlot, new TradeEntry(entry.sourceSlot(), updated));
|
||||||
} else {
|
} else {
|
||||||
offer.set(offerSlot, ItemStack.EMPTY);
|
offer.set(offerSlot, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAccepts();
|
clearAccepts();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -142,16 +150,14 @@ public class TradeSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean completeTrade() {
|
public boolean completeTrade() {
|
||||||
List<ItemStack> firstResult = simulateResult(first, secondOffer);
|
List<ItemStack> firstResult = simulateResult(first, firstOffer, secondOffer);
|
||||||
List<ItemStack> secondResult = simulateResult(second, firstOffer);
|
List<ItemStack> secondResult = simulateResult(second, secondOffer, firstOffer);
|
||||||
if (firstResult == null || secondResult == null) {
|
if (firstResult == null || secondResult == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyInventory(first, firstResult);
|
applyInventory(first, firstResult);
|
||||||
applyInventory(second, secondResult);
|
applyInventory(second, secondResult);
|
||||||
clearOffer(firstOffer);
|
|
||||||
clearOffer(secondOffer);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +172,7 @@ public class TradeSession {
|
|||||||
stage,
|
stage,
|
||||||
isFirst ? firstAccepted : secondAccepted,
|
isFirst ? firstAccepted : secondAccepted,
|
||||||
isFirst ? secondAccepted : firstAccepted,
|
isFirst ? secondAccepted : firstAccepted,
|
||||||
inventorySnapshot(player),
|
inventoryDisplayFor(player),
|
||||||
emptyReservedSnapshot(),
|
emptyReservedSnapshot(),
|
||||||
offerSnapshot(isFirst ? firstOffer : secondOffer),
|
offerSnapshot(isFirst ? firstOffer : secondOffer),
|
||||||
offerSnapshot(isFirst ? secondOffer : firstOffer));
|
offerSnapshot(isFirst ? secondOffer : firstOffer));
|
||||||
@@ -176,10 +182,14 @@ public class TradeSession {
|
|||||||
PacketDistributor.sendToPlayer(player, new TradeStatePayload(viewFor(player)));
|
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);
|
List<ItemStack> working = inventorySnapshot(player);
|
||||||
for (ItemStack stack : incoming) {
|
if (!removeOutgoing(working, outgoing)) {
|
||||||
if (!stack.isEmpty() && !insertStack(working, stack.copy())) {
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TradeEntry entry : incoming) {
|
||||||
|
if (entry != null && !insertStack(working, entry.stack().copy())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,6 +197,25 @@ public class TradeSession {
|
|||||||
return working;
|
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) {
|
private static boolean insertStack(List<ItemStack> working, ItemStack incoming) {
|
||||||
if (incoming.isEmpty()) {
|
if (incoming.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
@@ -225,12 +254,6 @@ public class TradeSession {
|
|||||||
return false;
|
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) {
|
private static void applyInventory(ServerPlayer player, List<ItemStack> result) {
|
||||||
Inventory inventory = player.getInventory();
|
Inventory inventory = player.getInventory();
|
||||||
for (int i = 0; i < result.size(); i++) {
|
for (int i = 0; i < result.size(); i++) {
|
||||||
@@ -239,19 +262,74 @@ public class TradeSession {
|
|||||||
inventory.setChanged();
|
inventory.setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ItemStack> blankOffer() {
|
private static List<TradeEntry> blankOffer() {
|
||||||
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
|
List<TradeEntry> result = new ArrayList<>(OFFER_SLOT_COUNT);
|
||||||
for (int i = 0; i < OFFER_SLOT_COUNT; i++) {
|
for (int i = 0; i < OFFER_SLOT_COUNT; i++) {
|
||||||
result.add(ItemStack.EMPTY);
|
result.add(null);
|
||||||
}
|
}
|
||||||
return result;
|
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() {
|
private void clearAccepts() {
|
||||||
firstAccepted = false;
|
firstAccepted = false;
|
||||||
secondAccepted = 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() {
|
private static List<Integer> emptyReservedSnapshot() {
|
||||||
List<Integer> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
|
List<Integer> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
|
||||||
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
|
for (int i = 0; i < INVENTORY_SLOT_COUNT; i++) {
|
||||||
@@ -260,10 +338,6 @@ public class TradeSession {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ItemStack> offerFor(ServerPlayer player) {
|
|
||||||
return player == first ? firstOffer : secondOffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<ItemStack> inventorySnapshot(ServerPlayer player) {
|
private static List<ItemStack> inventorySnapshot(ServerPlayer player) {
|
||||||
List<ItemStack> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
|
List<ItemStack> result = new ArrayList<>(INVENTORY_SLOT_COUNT);
|
||||||
Inventory inventory = player.getInventory();
|
Inventory inventory = player.getInventory();
|
||||||
@@ -273,10 +347,10 @@ public class TradeSession {
|
|||||||
return result;
|
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);
|
List<ItemStack> result = new ArrayList<>(OFFER_SLOT_COUNT);
|
||||||
for (ItemStack stack : offer) {
|
for (TradeEntry entry : offer) {
|
||||||
result.add(stack.copy());
|
result.add(entry == null ? ItemStack.EMPTY : entry.stack().copy());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user