diff --git a/server/item/arrow.go b/server/item/arrow.go index 078d25688..2243a69a3 100644 --- a/server/item/arrow.go +++ b/server/item/arrow.go @@ -16,3 +16,8 @@ func (a Arrow) EncodeItem() (name string, meta int16) { } return "minecraft:arrow", 0 } + +// OffHand ... +func (Arrow) OffHand() bool { + return true +} diff --git a/server/item/firework.go b/server/item/firework.go index 0cd5f55db..531bf9241 100644 --- a/server/item/firework.go +++ b/server/item/firework.go @@ -82,6 +82,11 @@ func (f Firework) RandomisedDuration() time.Duration { return f.Duration + time.Duration(rand.IntN(int(time.Millisecond*600))) } +// OffHand ... +func (Firework) OffHand() bool { + return true +} + // EncodeItem ... func (Firework) EncodeItem() (name string, meta int16) { return "minecraft:firework_rocket", 0 diff --git a/server/item/inventory/armour.go b/server/item/inventory/armour.go index dad331c27..86871eb0d 100644 --- a/server/item/inventory/armour.go +++ b/server/item/inventory/armour.go @@ -21,7 +21,7 @@ type Armour struct { // The function passed is called when a slot is changed. It may be nil to not call anything. func NewArmour(f func(slot int, before, after item.Stack)) *Armour { inv := New(4, f) - inv.canAdd = canAddArmour + inv.validator = canAddArmour return &Armour{inv: inv} } diff --git a/server/item/inventory/inventory.go b/server/item/inventory/inventory.go index 572855e2e..134ff8471 100644 --- a/server/item/inventory/inventory.go +++ b/server/item/inventory/inventory.go @@ -20,13 +20,16 @@ type Inventory struct { h Handler slots []item.Stack - f SlotFunc - canAdd func(s item.Stack, slot int) bool + f SlotFunc + validator SlotValidator } // SlotFunc is a function called for each item changed in an Inventory. type SlotFunc func(slot int, before, after item.Stack) +// SlotValidator is a function that limits changes in the Inventory slot. +type SlotValidator func(s item.Stack, slot int) bool + // ErrSlotOutOfRange is returned by any methods on inventory when a slot is passed which is not within the // range of valid values for the inventory. var ErrSlotOutOfRange = errors.New("slot is out of range: must be in range 0 <= slot < inventory.Size()") @@ -42,7 +45,7 @@ func New(size int, f SlotFunc) *Inventory { if f == nil { f = func(slot int, before, after item.Stack) {} } - return &Inventory{h: NopHandler{}, slots: make([]item.Stack, size), f: f, canAdd: func(s item.Stack, slot int) bool { return true }} + return &Inventory{h: NopHandler{}, slots: make([]item.Stack, size), f: f, validator: func(s item.Stack, slot int) bool { return true }} } // Clone copies an Inventory and returns it, calling the SlotFunc passed for any @@ -51,7 +54,7 @@ func (inv *Inventory) Clone(f SlotFunc) *Inventory { if f == nil { f = func(slot int, before, after item.Stack) {} } - return &Inventory{h: NopHandler{}, slots: inv.Slots(), f: f, canAdd: func(s item.Stack, slot int) bool { return true }} + return &Inventory{h: NopHandler{}, slots: inv.Slots(), f: f, validator: func(s item.Stack, slot int) bool { return true }} } // SlotFunc changes the function called when a slot in the inventory is changed. @@ -61,6 +64,13 @@ func (inv *Inventory) SlotFunc(f SlotFunc) { inv.f = f } +// SlotValidator changes the function that limits item placement in the inventory slot. +func (inv *Inventory) SlotValidator(f SlotValidator) { + inv.mu.Lock() + defer inv.mu.Unlock() + inv.validator = f +} + // Item attempts to obtain an item from a specific slot in the inventory. If an item was present in that slot, // the item is returned and the error is nil. If no item was present in the slot, a Stack with air as its item // and a count of 0 is returned. Stack.Empty() may be called to check if this is the case. @@ -356,7 +366,7 @@ func (inv *Inventory) Handler() Handler { // setItem sets an item to a specific slot and overwrites the existing item. It calls the function which is // called for every item change and does so without locking the inventory. func (inv *Inventory) setItem(slot int, it item.Stack) func() { - if !inv.canAdd(it, slot) { + if !inv.validator(it, slot) { return func() {} } if it.Count() > it.MaxCount() { diff --git a/server/item/nautilus_shell.go b/server/item/nautilus_shell.go index 67f48f5d8..a065686fe 100644 --- a/server/item/nautilus_shell.go +++ b/server/item/nautilus_shell.go @@ -7,3 +7,8 @@ type NautilusShell struct{} func (NautilusShell) EncodeItem() (name string, meta int16) { return "minecraft:nautilus_shell", 0 } + +// OffHand ... +func (NautilusShell) OffHand() bool { + return true +} diff --git a/server/item/totem.go b/server/item/totem.go index 8d08f752e..1696d2f82 100644 --- a/server/item/totem.go +++ b/server/item/totem.go @@ -12,3 +12,8 @@ func (Totem) MaxCount() int { func (Totem) EncodeItem() (name string, meta int16) { return "minecraft:totem_of_undying", 0 } + +// OffHand ... +func (Totem) OffHand() bool { + return true +} diff --git a/server/player/type.go b/server/player/type.go index 37ca90204..ca3ded8d0 100644 --- a/server/player/type.go +++ b/server/player/type.go @@ -20,6 +20,14 @@ func (t ptype) Open(tx *world.Tx, handle *world.EntityHandle, data *world.Entity playerData: pd, } + pd.offHand.SlotValidator(func(s item.Stack, _ int) bool { + if s.Empty() { + return true + } + _, allowedInOffhand := s.Item().(item.OffHand) + return allowedInOffhand + }) + if pd.s != nil { pd.s.HandleInventories(tx, p, pd.inv, pd.offHand, pd.enderChest, pd.ui, pd.armour, pd.heldSlot) } else {