Fix server replaying the close window packet

This commit is contained in:
GreatWyrm 2024-01-12 22:00:15 -08:00 committed by mworzala
parent f5f8e21427
commit 4ba779d3f1
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
3 changed files with 78 additions and 11 deletions

View File

@ -1758,6 +1758,39 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
}
}
/**
* Updates the player's inventory state, given a window id to compare against.
* Note: This should only be used when receiving a {@link net.minestom.server.network.packet.client.play.ClientCloseWindowPacket}.
* Use {@link #closeInventory()} instead for more general use.
* @param windowId The window id to compare against.
*/
public void updateCloseInventoryState(byte windowId) {
if (windowId == 0) {
// Put cursor item back in inventory if possible
ItemStack stack = getInventory().getCursorItem();
boolean success = getInventory().addItemStack(stack);
// Drop if inventory is full
if (!success) {
dropItem(stack);
}
getInventory().setCursorItem(ItemStack.AIR);
} else {
if (getOpenInventory() != null && windowId == getOpenInventory().getWindowId()) {
ItemStack stack = getOpenInventory().getCursorItem(this);
// Try to put item into our inventory
boolean success = getInventory().addItemStack(stack);
// Drop if inventory is full
if (!success) {
dropItem(stack);
}
getOpenInventory().setCursorItem(this, ItemStack.AIR);
} else {
// Something has gone wrong
logger.warn("Tried to update inventory state with invalid window id!");
}
}
}
/**
* Used internally to prevent an inventory click to be processed
* when the inventory listeners closed the inventory.

View File

@ -27,8 +27,6 @@ public class WindowListener {
final byte button = packet.button();
final ClientClickWindowPacket.ClickType clickType = packet.clickType();
//System.out.println("Window id: " + windowId + " | slot: " + slot + " | button: " + button + " | clickType: " + clickType);
boolean successful = false;
// prevent click in a non-interactive slot (why does it exist?)
@ -90,7 +88,7 @@ public class WindowListener {
InventoryCloseEvent inventoryCloseEvent = new InventoryCloseEvent(player.getOpenInventory(), player);
EventDispatcher.call(inventoryCloseEvent);
player.closeInventory();
player.updateCloseInventoryState(packet.windowId());
Inventory newInventory = inventoryCloseEvent.getNewInventory();
if (newInventory != null)
@ -103,10 +101,10 @@ public class WindowListener {
*/
private static void refreshCursorItem(Player player, AbstractInventory inventory) {
ItemStack cursorItem;
if (inventory instanceof PlayerInventory) {
cursorItem = ((PlayerInventory) inventory).getCursorItem();
} else if (inventory instanceof Inventory) {
cursorItem = ((Inventory) inventory).getCursorItem(player);
if (inventory instanceof PlayerInventory playerInventory) {
cursorItem = playerInventory.getCursorItem();
} else if (inventory instanceof Inventory standardInventory) {
cursorItem = standardInventory.getCursorItem(player);
} else {
throw new RuntimeException("Invalid inventory: " + inventory.getClass());
}
@ -115,10 +113,10 @@ public class WindowListener {
}
private static void setCursor(Player player, AbstractInventory inventory, ItemStack itemStack) {
if (inventory instanceof PlayerInventory) {
((PlayerInventory) inventory).setCursorItem(itemStack);
} else if (inventory instanceof Inventory) {
((Inventory) inventory).setCursorItem(player, itemStack);
if (inventory instanceof PlayerInventory playerInventory) {
playerInventory.setCursorItem(itemStack);
} else if (inventory instanceof Inventory standardInventory) {
standardInventory.setCursorItem(player, itemStack);
} else {
throw new RuntimeException("Invalid inventory: " + inventory.getClass());
}

View File

@ -0,0 +1,36 @@
package net.minestom.server.inventory;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.packet.client.play.ClientCloseWindowPacket;
import net.minestom.server.network.packet.server.play.CloseWindowPacket;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@EnvTest
public class InventoryCloseStateTest {
@Test
public void doNotReceiveClosePacketFromServerWhenSendingClientCloseWindowPacket(Env env) {
var instance = env.createFlatInstance();
var connection = env.createConnection();
var player = connection.connect(instance, new Pos(0, 42, 0)).join();
assertEquals(instance, player.getInstance());
var packetTracker = connection.trackIncoming(CloseWindowPacket.class);
var inventory = new Inventory(InventoryType.CHEST_2_ROW, Component.text("Test"));
player.openInventory(inventory);
player.closeInventory(); // Closes the inventory server-side, should send a CloseWindowPacket
player.openInventory(inventory);
// Send the close window packet
player.addPacketToQueue(ConnectionState.PLAY, new ClientCloseWindowPacket(inventory.getWindowId()));
player.interpretPacketQueue();
packetTracker.assertSingle(closeWindowPacket -> assertEquals(inventory.getWindowId(), closeWindowPacket.windowId()));
packetTracker.assertCount(1); // Assert we only get 1 close window packet from the closeInventory(); call
}
}