From deeb9b4a35b614010acebd650a65af603e30aec7 Mon Sep 17 00:00:00 2001 From: HashimTheArab Date: Mon, 17 Aug 2026 20:06:01 +0300 Subject: [PATCH] block/item: Give swords and shears per-block mining speeds Both tools returned one number for every block: 1.5 from shears, and 1.5 from a sword except on cobweb. Bedrock decides their speed from the block instead, and neither has a tier speed of its own. Shears get 15 on leaves and cobweb, 5 on wool and 2 on vines, so shearing wool was over three times too slow and cutting leaves or cobweb ten times too slow. A sword gets 15 on cobweb, 30 on bamboo and 1.5 on leaves, vines, moss carpet, pumpkins, melons and cocoa. Only cobweb reached it before, since the speed is gated on the block naming the tool effective and no other block named a sword. BaseMiningEfficiency now asks the block through SwordMineable and ShearsMineable, and the blocks that answer name the tool in Effective too. Glow lichen, chorus plant, chorus flower and big dripleaf also take 1.5 from a sword and are left out until those blocks exist. --- server/block/bamboo.go | 5 ++++- server/block/bamboo_sapling.go | 5 ++++- server/block/break_info.go | 8 ++++++++ server/block/cobweb.go | 6 ++++++ server/block/cocoa_bean.go | 5 ++++- server/block/leaves.go | 10 +++++++--- server/block/melon.go | 5 ++++- server/block/moss_carpet.go | 5 ++++- server/block/pumpkin.go | 5 ++++- server/block/vine.go | 8 +++++++- server/block/wool.go | 3 +++ server/item/shears.go | 15 ++++++++++++--- server/item/sword.go | 14 ++++++++++---- 13 files changed, 77 insertions(+), 17 deletions(-) diff --git a/server/block/bamboo.go b/server/block/bamboo.go index c3090e88fb..2559ac97b2 100644 --- a/server/block/bamboo.go +++ b/server/block/bamboo.go @@ -53,9 +53,12 @@ func (b Bamboo) FlammabilityInfo() FlammabilityInfo { // BreakInfo ... func (b Bamboo) BreakInfo() BreakInfo { - return newBreakInfo(1, alwaysHarvestable, axeEffective, oneOf(b)) + return newBreakInfo(1, alwaysHarvestable, anyEffective(item.TypeAxe, item.TypeSword), oneOf(b)) } +// SwordMiningSpeed is high enough for a sword to break bamboo in one tick. +func (Bamboo) SwordMiningSpeed() float64 { return 30 } + // EncodeBlock ... func (b Bamboo) EncodeBlock() (string, map[string]any) { thickness := "thin" diff --git a/server/block/bamboo_sapling.go b/server/block/bamboo_sapling.go index 5687206f34..a77bb88d2b 100644 --- a/server/block/bamboo_sapling.go +++ b/server/block/bamboo_sapling.go @@ -53,9 +53,12 @@ func (b BambooSapling) RandomTick(pos cube.Pos, tx *world.Tx, r *rand.Rand) { // BreakInfo ... func (b BambooSapling) BreakInfo() BreakInfo { - return newBreakInfo(0, alwaysHarvestable, axeEffective, oneOf(Bamboo{})).withBlastResistance(1) + return newBreakInfo(0, alwaysHarvestable, anyEffective(item.TypeAxe, item.TypeSword), oneOf(Bamboo{})).withBlastResistance(1) } +// SwordMiningSpeed ... +func (BambooSapling) SwordMiningSpeed() float64 { return 30 } + // HasLiquidDrops ... func (b BambooSapling) HasLiquidDrops() bool { return true diff --git a/server/block/break_info.go b/server/block/break_info.go index a6745d1ba8..bbd31a6db0 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -215,6 +215,14 @@ var nothingEffective = func(item.Tool) bool { return false } +// anyEffective is a convenience function for blocks that are effectively mined with more than one type +// of tool. +func anyEffective(types ...item.ToolType) func(item.Tool) bool { + return func(t item.Tool) bool { + return slices.Contains(types, t.ToolType()) + } +} + // alwaysHarvestable is a convenience function for blocks that are harvestable using any item. var alwaysHarvestable = func(t item.Tool) bool { return true diff --git a/server/block/cobweb.go b/server/block/cobweb.go index 94f8df4a69..da9e4b906b 100644 --- a/server/block/cobweb.go +++ b/server/block/cobweb.go @@ -16,6 +16,12 @@ type Cobweb struct { // Cobweb is implemented because the item package needs to identify this block but cannot implement the block package. func (Cobweb) Cobweb() {} +// SwordMiningSpeed ... +func (Cobweb) SwordMiningSpeed() float64 { return 15 } + +// ShearsMiningSpeed ... +func (Cobweb) ShearsMiningSpeed() float64 { return 15 } + // EntityInside slows the entity's velocity and resets its fall distance while it is inside the cobweb. func (Cobweb) EntityInside(_ cube.Pos, _ *world.Tx, e world.Entity) { if fallEntity, ok := e.(fallDistanceEntity); ok { diff --git a/server/block/cocoa_bean.go b/server/block/cocoa_bean.go index b2ad28e65d..fe1ed6fd34 100644 --- a/server/block/cocoa_bean.go +++ b/server/block/cocoa_bean.go @@ -88,7 +88,7 @@ func (c CocoaBean) RandomTick(pos cube.Pos, tx *world.Tx, r *rand.Rand) { // BreakInfo ... func (c CocoaBean) BreakInfo() BreakInfo { - return newBreakInfo(0.2, alwaysHarvestable, axeEffective, func(item.Tool, []item.Enchantment) []item.Stack { + return newBreakInfo(0.2, alwaysHarvestable, anyEffective(item.TypeAxe, item.TypeSword), func(item.Tool, []item.Enchantment) []item.Stack { if c.Age == 2 { return []item.Stack{item.NewStack(c, rand.IntN(2)+2)} } @@ -96,6 +96,9 @@ func (c CocoaBean) BreakInfo() BreakInfo { }).withBlastResistance(3) } +// SwordMiningSpeed ... +func (CocoaBean) SwordMiningSpeed() float64 { return 1.5 } + // CompostChance ... func (CocoaBean) CompostChance() float64 { return 0.65 diff --git a/server/block/leaves.go b/server/block/leaves.go index ec18374af8..1460f9a7f2 100644 --- a/server/block/leaves.go +++ b/server/block/leaves.go @@ -89,6 +89,12 @@ func (l Leaves) NeighbourUpdateTick(pos, _ cube.Pos, tx *world.Tx) { } } +// SwordMiningSpeed ... +func (Leaves) SwordMiningSpeed() float64 { return 1.5 } + +// ShearsMiningSpeed ... +func (Leaves) ShearsMiningSpeed() float64 { return 15 } + // FlammabilityInfo ... func (l Leaves) FlammabilityInfo() FlammabilityInfo { return newFlammabilityInfo(30, 60, true) @@ -96,9 +102,7 @@ func (l Leaves) FlammabilityInfo() FlammabilityInfo { // BreakInfo ... func (l Leaves) BreakInfo() BreakInfo { - return newBreakInfo(0.2, alwaysHarvestable, func(t item.Tool) bool { - return t.ToolType() == item.TypeShears || t.ToolType() == item.TypeHoe - }, func(t item.Tool, enchantments []item.Enchantment) []item.Stack { + return newBreakInfo(0.2, alwaysHarvestable, anyEffective(item.TypeShears, item.TypeHoe, item.TypeSword), func(t item.Tool, enchantments []item.Enchantment) []item.Stack { if t.ToolType() == item.TypeShears || hasSilkTouch(enchantments) { return []item.Stack{item.NewStack(l, 1)} } diff --git a/server/block/melon.go b/server/block/melon.go index 511b7798c2..dfe4b26f2b 100644 --- a/server/block/melon.go +++ b/server/block/melon.go @@ -11,9 +11,12 @@ type Melon struct { // BreakInfo ... func (m Melon) BreakInfo() BreakInfo { - return newBreakInfo(1, alwaysHarvestable, axeEffective, discreteDrops(item.MelonSlice{}, m, 3, 7, 9)) + return newBreakInfo(1, alwaysHarvestable, anyEffective(item.TypeAxe, item.TypeSword), discreteDrops(item.MelonSlice{}, m, 3, 7, 9)) } +// SwordMiningSpeed ... +func (Melon) SwordMiningSpeed() float64 { return 1.5 } + // CompostChance ... func (Melon) CompostChance() float64 { return 0.65 diff --git a/server/block/moss_carpet.go b/server/block/moss_carpet.go index 3f086cdcd1..5b6d086368 100644 --- a/server/block/moss_carpet.go +++ b/server/block/moss_carpet.go @@ -47,9 +47,12 @@ func (m MossCarpet) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *w // BreakInfo ... func (m MossCarpet) BreakInfo() BreakInfo { - return newBreakInfo(0.1, alwaysHarvestable, nothingEffective, oneOf(m)) + return newBreakInfo(0.1, alwaysHarvestable, swordEffective, oneOf(m)) } +// SwordMiningSpeed ... +func (MossCarpet) SwordMiningSpeed() float64 { return 1.5 } + // CompostChance ... func (MossCarpet) CompostChance() float64 { return 0.3 diff --git a/server/block/pumpkin.go b/server/block/pumpkin.go index 51777ef385..58a7a49e95 100644 --- a/server/block/pumpkin.go +++ b/server/block/pumpkin.go @@ -40,9 +40,12 @@ func (p Pumpkin) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *worl // BreakInfo ... func (p Pumpkin) BreakInfo() BreakInfo { - return newBreakInfo(1, alwaysHarvestable, axeEffective, oneOf(p)) + return newBreakInfo(1, alwaysHarvestable, anyEffective(item.TypeAxe, item.TypeSword), oneOf(p)) } +// SwordMiningSpeed ... +func (Pumpkin) SwordMiningSpeed() float64 { return 1.5 } + // CompostChance ... func (Pumpkin) CompostChance() float64 { return 0.65 diff --git a/server/block/vine.go b/server/block/vine.go index 66e32b4512..1f56dc384d 100644 --- a/server/block/vine.go +++ b/server/block/vine.go @@ -50,9 +50,15 @@ func (Vines) FlammabilityInfo() FlammabilityInfo { func (v Vines) BreakInfo() BreakInfo { return newBreakInfo(0.2, func(t item.Tool) bool { return t.ToolType() == item.TypeShears - }, axeEffective, oneOf(v)) + }, anyEffective(item.TypeAxe, item.TypeSword, item.TypeShears), oneOf(v)) } +// SwordMiningSpeed ... +func (Vines) SwordMiningSpeed() float64 { return 1.5 } + +// ShearsMiningSpeed ... +func (Vines) ShearsMiningSpeed() float64 { return 2 } + // EntityInside ... func (Vines) EntityInside(_ cube.Pos, _ *world.Tx, e world.Entity) { if fallEntity, ok := e.(fallDistanceEntity); ok { diff --git a/server/block/wool.go b/server/block/wool.go index 9d0ada494c..a5c3c695c0 100644 --- a/server/block/wool.go +++ b/server/block/wool.go @@ -29,6 +29,9 @@ func (w Wool) BreakInfo() BreakInfo { return newBreakInfo(0.8, alwaysHarvestable, shearsEffective, oneOf(w)) } +// ShearsMiningSpeed ... +func (Wool) ShearsMiningSpeed() float64 { return 5 } + // EncodeItem ... func (w Wool) EncodeItem() (name string, meta int16) { return "minecraft:" + w.Colour.String() + "_wool", 0 diff --git a/server/item/shears.go b/server/item/shears.go index a3b20d1883..698f5b3044 100644 --- a/server/item/shears.go +++ b/server/item/shears.go @@ -43,9 +43,18 @@ func (s Shears) HarvestLevel() int { return 1 } -// BaseMiningEfficiency ... -func (s Shears) BaseMiningEfficiency(world.Block) float64 { - return 1.5 +// ShearsMineable is implemented by blocks that shears mine faster than a bare hand. +type ShearsMineable interface { + // ShearsMiningSpeed returns the speed shears mine the block at. + ShearsMiningSpeed() float64 +} + +// BaseMiningEfficiency returns the speed the block passed names for shears, or 1 if it names none. +func (s Shears) BaseMiningEfficiency(b world.Block) float64 { + if m, ok := b.(ShearsMineable); ok { + return m.ShearsMiningSpeed() + } + return 1 } // DurabilityInfo ... diff --git a/server/item/sword.go b/server/item/sword.go index 6115fe93c0..eb37a8bee5 100644 --- a/server/item/sword.go +++ b/server/item/sword.go @@ -38,12 +38,18 @@ func (s Sword) EnchantmentValue() int { return s.Tier.EnchantmentValue } -// BaseMiningEfficiency always returns 1.5, unless the block passed is cobweb, in which case 15 is returned. +// SwordMineable is implemented by blocks that a sword mines faster than a bare hand. +type SwordMineable interface { + // SwordMiningSpeed returns the speed a sword mines the block at. + SwordMiningSpeed() float64 +} + +// BaseMiningEfficiency returns the speed the block passed names for a sword, or 1 if it names none. func (s Sword) BaseMiningEfficiency(b world.Block) float64 { - if _, ok := b.(interface{ Cobweb() }); ok { - return 15 + if m, ok := b.(SwordMineable); ok { + return m.SwordMiningSpeed() } - return 1.5 + return 1 } // DurabilityInfo ...