diff --git a/server/session/handler_inventory_transaction.go b/server/session/handler_inventory_transaction.go index 2d1cecb03..965c3ae9e 100644 --- a/server/session/handler_inventory_transaction.go +++ b/server/session/handler_inventory_transaction.go @@ -15,9 +15,34 @@ import ( type InventoryTransactionHandler struct{} // Handle ... -func (h *InventoryTransactionHandler) Handle(p packet.Packet, s *Session, tx *world.Tx, c Controllable) error { +func (h *InventoryTransactionHandler) Handle(p packet.Packet, s *Session, tx *world.Tx, c Controllable) (err error) { pk := p.(*packet.InventoryTransaction) + if len(pk.LegacySetItemSlots) > 2 { + return fmt.Errorf("too many slot sync requests in inventory transaction") + } + + defer func() { + // The client has requested the server to resend the specified slots even if they haven't changed server-side. + // Handling these requests is necessary to ensure the client's inventory remains in sync with the server. + for _, slot := range pk.LegacySetItemSlots { + if len(slot.Slots) > 2 { + err = fmt.Errorf("too many slots in slot sync request") + return + } + switch slot.ContainerID { + case protocol.ContainerOffhand: + s.sendInv(s.offHand, protocol.WindowIDOffHand) + case protocol.ContainerInventory: + for _, slot := range slot.Slots { + if i, err := s.inv.Item(int(slot)); err == nil { + s.sendItem(i, int(slot), protocol.WindowIDInventory) + } + } + } + } + }() + switch data := pk.TransactionData.(type) { case *protocol.NormalTransactionData: h.resendInventories(s) @@ -25,26 +50,25 @@ func (h *InventoryTransactionHandler) Handle(p packet.Packet, s *Session, tx *wo // transactions, so we're best off making sure the client and server stay in sync. if err := h.handleNormalTransaction(pk, s, c); err != nil { s.conf.Log.Debug("process packet: InventoryTransaction: verify Normal transaction actions: " + err.Error()) - return nil } - return nil + return case *protocol.MismatchTransactionData: // Just resend the inventory and don't do anything. h.resendInventories(s) - return nil + return case *protocol.UseItemOnEntityTransactionData: - if err := s.VerifyAndSetHeldSlot(int(data.HotBarSlot), stackToItem(data.HeldItem.Stack), c); err != nil { - return err + if err = s.VerifyAndSetHeldSlot(int(data.HotBarSlot), stackToItem(data.HeldItem.Stack), c); err != nil { + return } return h.handleUseItemOnEntityTransaction(data, s, tx, c) case *protocol.UseItemTransactionData: - if err := s.VerifyAndSetHeldSlot(int(data.HotBarSlot), stackToItem(data.HeldItem.Stack), c); err != nil { - return err + if err = s.VerifyAndSetHeldSlot(int(data.HotBarSlot), stackToItem(data.HeldItem.Stack), c); err != nil { + return } return h.handleUseItemTransaction(data, s, c) case *protocol.ReleaseItemTransactionData: - if err := s.VerifyAndSetHeldSlot(int(data.HotBarSlot), stackToItem(data.HeldItem.Stack), c); err != nil { - return err + if err = s.VerifyAndSetHeldSlot(int(data.HotBarSlot), stackToItem(data.HeldItem.Stack), c); err != nil { + return } return h.handleReleaseItemTransaction(c) }