From 8b7cffbe4914c354b25b8108d048f887d2a9397c Mon Sep 17 00:00:00 2001
From: Warrior <50800980+Warriorrrr@users.noreply.github.com>
Date: Sat, 17 Jan 2026 21:38:28 +0100
Subject: [PATCH 1/5] Bump mockito version to fix compiling on java 25 (#5247)
---
pom.xml | 35 ++++++++++++++++++++++++++++-------
1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/pom.xml b/pom.xml
index 4ee43d00a8..8d0240bc1e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,6 +89,17 @@
UTF-8
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+
+ properties
+
+
+
+
maven-surefire-plugin
3.2.5
@@ -97,6 +108,8 @@
org.junit.jupiter:junit-jupiter
false
skip
+
+ -javaagent:${org.mockito:mockito-core:jar}
@@ -290,12 +303,26 @@
assertj-core
3.25.3
test
+
+
+
+ net.bytebuddy
+ byte-buddy
+
+
net.dmulloy2
ProtocolLib
5.3.0
compile
+
+
+
+ net.bytebuddy
+ byte-buddy
+
+
com.h2database
@@ -506,13 +533,7 @@
org.mockito
mockito-core
- 5.12.0
- test
-
-
- org.mockito
- mockito-inline
- 5.2.0
+ 5.21.0
test
From 4a890a28a16a72cb7cc3e2c1f0a5f6187c5d1c7f Mon Sep 17 00:00:00 2001
From: Dominykas <37642364+dnocturne@users.noreply.github.com>
Date: Sat, 17 Jan 2026 22:51:39 +0200
Subject: [PATCH 2/5] Add support for sweet berry bush harvesting and
replanting (#5238)
* Add support for sweet berry bush harvesting and replanting
* Accidentally left my test code for torchflower replanting.
---
.../skills/herbalism/HerbalismManager.java | 16 ++++++++++++++++
src/main/resources/config.yml | 1 +
2 files changed, 17 insertions(+)
diff --git a/src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java b/src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java
index f415fafcd0..40082391ae 100644
--- a/src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java
+++ b/src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java
@@ -535,6 +535,10 @@ public void awardXPForPlantBlocks(HashSet brokenPlants) {
}
public boolean isAgeableMature(Ageable ageable) {
+ // Sweet berry bush is harvestable at age 2 and 3 (max is 3)
+ if (ageable.getMaterial() == Material.SWEET_BERRY_BUSH) {
+ return ageable.getAge() >= 2;
+ }
return ageable.getAge() == ageable.getMaximumAge()
&& ageable.getAge() != 0;
}
@@ -868,6 +872,7 @@ private boolean processGreenThumbPlants(@NotNull BlockState blockState,
case "beetroots" -> replantMaterial = Material.matchMaterial("BEETROOT_SEEDS");
case "cocoa" -> replantMaterial = Material.matchMaterial("COCOA_BEANS");
case "torchflower" -> replantMaterial = Material.matchMaterial("TORCHFLOWER_SEEDS");
+ case "sweet_berry_bush" -> replantMaterial = Material.matchMaterial("SWEET_BERRIES");
default -> {
return false;
}
@@ -956,6 +961,17 @@ private boolean processGrowingPlants(BlockState blockState, Ageable ageable,
}
break;
+ case "sweet_berry_bush":
+
+ // Sweet berry bush has ages 0-3, where 2+ has berries
+ // Cap at age 1 to prevent instant re-harvest exploit with enough herbalism levels
+ if (greenTerra || greenThumbStage >= 2) {
+ finalAge = 1;
+ } else {
+ finalAge = 0;
+ }
+ break;
+
default:
return false;
}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index c3706f43a8..ff53f5109f 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -484,6 +484,7 @@ Green_Thumb_Replanting_Crops:
Potatoes: true
Beetroots: true
Cocoa: true
+ Sweet_Berry_Bush: true
#
# Settings for Double Drops
###
From 5a9390f88f738d9b3fabac2e549638661de8807c Mon Sep 17 00:00:00 2001
From: nossr50
Date: Sat, 17 Jan 2026 13:27:14 -0800
Subject: [PATCH 3/5] allow combat abilities to activate with spear in off-hand
---
Changelog.txt | 6 ++
.../nossr50/datatypes/player/McMMOPlayer.java | 10 ++++
.../nossr50/listeners/PlayerListener.java | 22 ++++++-
.../nossr50/util/skills/CombatUtils.java | 58 ++++++++++---------
4 files changed, 68 insertions(+), 28 deletions(-)
diff --git a/Changelog.txt b/Changelog.txt
index 5360ea9c12..90cb04df47 100644
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,3 +1,9 @@
+Version 2.2.049
+ Combat abilities work with spear in off-hand again (see notes)
+
+ NOTES:
+ As a semi-permanent work around, mcMMO keeps track of when players swing their weapon, if they have swung it recently enough combat skills will work even if you have a spear in your off-hand.
+ Prior to this patch, mcMMO disabled combat abilities when spear was in off-hand to prevent the off-hand spear charge from applying combat abilities from other skills, as mcMMO was unable to determine which item (mainhand vs offhand) caused the damage from the event information alone.
Version 2.2.048
Fixed error when loading Spears skill manager on older Minecraft versions
Fixed error when using /spears command on an older Minecraft version
diff --git a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java
index be6ed57acc..595f478111 100644
--- a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java
+++ b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java
@@ -81,6 +81,7 @@
import org.jetbrains.annotations.VisibleForTesting;
public class McMMOPlayer implements Identified {
+ private static final long NO_SWING = 0L;
private final @NotNull Identity identity;
//Hacky fix for now, redesign later
@@ -97,6 +98,7 @@ public class McMMOPlayer implements Identified {
private Party invite;
private Party allianceInvite;
private int itemShareModifier;
+ private long lastSwingTimestamp = NO_SWING;
private PartyTeleportRecord ptpRecord;
@@ -1278,4 +1280,12 @@ public void cleanup() {
public void setChatMode(@NotNull ChatChannel chatChannel) {
this.chatChannel = chatChannel;
}
+
+ public long getLastSwingTimestamp() {
+ return lastSwingTimestamp;
+ }
+
+ public void setLastSwingTimestamp(long lastSwingTimestamp) {
+ this.lastSwingTimestamp = lastSwingTimestamp;
+ }
}
diff --git a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java
index bf24749f44..dd67906d5c 100644
--- a/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java
+++ b/src/main/java/com/gmail/nossr50/listeners/PlayerListener.java
@@ -63,6 +63,8 @@
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
+import org.bukkit.event.player.PlayerAnimationEvent;
+import org.bukkit.event.player.PlayerAnimationType;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
@@ -909,7 +911,6 @@ public void onPlayerInteractMonitor(PlayerInteractEvent event) {
HerbalismManager herbalismManager = mmoPlayer.getHerbalismManager();
- // FakePlayerAnimationEvent fakeSwing = new FakePlayerAnimationEvent(event.getPlayer(), PlayerAnimationType.ARM_SWING); //PlayerAnimationEvent compat
if (!event.isCancelled() || event.useInteractedBlock() != Event.Result.DENY) {
//TODO: Is this code to set false from bone meal even needed? I'll have to double check later.
if (heldItem.getType() == Material.BONE_MEAL) {
@@ -1122,4 +1123,23 @@ public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) {
SkillUtils.removeAbilityBuff(event.getMainHandItem());
SkillUtils.removeAbilityBuff(event.getOffHandItem());
}
+
+ @EventHandler(ignoreCancelled = false, priority = EventPriority.MONITOR)
+ public void onPlayerAnimation(PlayerAnimationEvent event) {
+ if (event.getAnimationType() != PlayerAnimationType.ARM_SWING) {
+ return;
+ }
+
+ final Player player = event.getPlayer();
+
+ if (!UserManager.hasPlayerDataKey(player)) {
+ return;
+ }
+
+ final McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
+
+ if (mmoPlayer != null) {
+ mmoPlayer.setLastSwingTimestamp(System.currentTimeMillis());
+ }
+ }
}
diff --git a/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java b/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
index cd05099f39..895378a8db 100644
--- a/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
+++ b/src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java
@@ -125,11 +125,6 @@ private static void processSwordCombat(@NotNull LivingEntity target, @NotNull Pl
return;
}
- // TODO: Temporary hack to avoid unintended spear interactions
- if (isSpear(player.getInventory().getItemInOffHand())) {
- return;
- }
-
final McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
//Make sure the profiles been loaded
@@ -137,6 +132,11 @@ private static void processSwordCombat(@NotNull LivingEntity target, @NotNull Pl
return;
}
+ // Hack to avoid other combat abilities applying to off-hand spear attacks
+ if (isSpear(player.getInventory().getItemInOffHand()) && isNotSwinging(mmoPlayer)) {
+ return;
+ }
+
SwordsManager swordsManager = mmoPlayer.getSwordsManager();
double boostedDamage = event.getDamage();
@@ -194,11 +194,6 @@ private static void processTridentCombatMelee(@NotNull LivingEntity target,
return;
}
- // TODO: Temporary hack to avoid unintended spear interactions
- if (isSpear(player.getInventory().getItemInOffHand())) {
- return;
- }
-
double boostedDamage = event.getDamage();
final McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
@@ -208,6 +203,11 @@ private static void processTridentCombatMelee(@NotNull LivingEntity target,
return;
}
+ // Hack to avoid other combat abilities applying to off-hand spear attacks
+ if (isSpear(player.getInventory().getItemInOffHand()) && isNotSwinging(mmoPlayer)) {
+ return;
+ }
+
final TridentsManager tridentsManager = mmoPlayer.getTridentsManager();
// if (tridentsManager.canActivateAbility()) {
@@ -312,11 +312,6 @@ private static void processMacesCombat(@NotNull LivingEntity target,
return;
}
- // TODO: Temporary hack to avoid unintended spear interactions
- if (isSpear(player.getInventory().getItemInOffHand())) {
- return;
- }
-
double boostedDamage = event.getDamage();
final McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
@@ -326,6 +321,11 @@ private static void processMacesCombat(@NotNull LivingEntity target,
return;
}
+ // Hack to avoid other combat abilities applying to off-hand spear attacks
+ if (isSpear(player.getInventory().getItemInOffHand()) && isNotSwinging(mmoPlayer)) {
+ return;
+ }
+
final MacesManager macesManager = mmoPlayer.getMacesManager();
// Apply Limit Break DMG
@@ -395,11 +395,6 @@ private static void processAxeCombat(@NotNull LivingEntity target, @NotNull Play
return;
}
- // TODO: Temporary hack to avoid unintended spear interactions
- if (isSpear(player.getInventory().getItemInOffHand())) {
- return;
- }
-
double boostedDamage = event.getDamage();
final McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
@@ -409,6 +404,11 @@ private static void processAxeCombat(@NotNull LivingEntity target, @NotNull Play
return;
}
+ // Hack to avoid other combat abilities applying to off-hand spear attacks
+ if (isSpear(player.getInventory().getItemInOffHand()) && isNotSwinging(mmoPlayer)) {
+ return;
+ }
+
final AxesManager axesManager = mmoPlayer.getAxesManager();
if (axesManager.canActivateAbility()) {
@@ -454,11 +454,6 @@ private static void processUnarmedCombat(@NotNull LivingEntity target, @NotNull
double boostedDamage = event.getDamage();
- // TODO: Temporary hack to avoid unintended spear interactions
- if (isSpear(player.getInventory().getItemInOffHand())) {
- return;
- }
-
final McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
//Make sure the profiles been loaded
@@ -466,6 +461,11 @@ private static void processUnarmedCombat(@NotNull LivingEntity target, @NotNull
return;
}
+ // Hack to avoid other combat abilities applying to off-hand spear attacks
+ if (isSpear(player.getInventory().getItemInOffHand()) && isNotSwinging(mmoPlayer)) {
+ return;
+ }
+
final UnarmedManager unarmedManager = mmoPlayer.getUnarmedManager();
if (unarmedManager.canActivateAbility()) {
@@ -949,8 +949,6 @@ public static void applyAbilityAoE(@NotNull Player attacker, @NotNull LivingEnti
continue;
}
- //EventUtils.callFakeArmSwingEvent(attacker);
-
switch (type) {
case SWORDS:
if (entity instanceof Player) {
@@ -1227,4 +1225,10 @@ public static void delayArrowMetaCleanup(@NotNull AbstractArrow arrow) {
mcMMO.p.getFoliaLib().getScheduler()
.runLater(() -> ProjectileUtils.cleanupProjectileMetadata(arrow), 20 * 120);
}
+
+ public static boolean isNotSwinging(McMMOPlayer mmoPlayer) {
+ // If player has swung in the last second, it's extremely unlikely the damage originates
+ // from an off-hand spear charge attack
+ return mmoPlayer.getLastSwingTimestamp() + 500L < System.currentTimeMillis();
+ }
}
From c14b82fff179af4a4a8feb33afdbbe54f7ac8533 Mon Sep 17 00:00:00 2001
From: nossr50
Date: Sat, 17 Jan 2026 13:31:05 -0800
Subject: [PATCH 4/5] updated notes
---
Changelog.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Changelog.txt b/Changelog.txt
index 90cb04df47..eb04894ea3 100644
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,5 +1,8 @@
Version 2.2.049
Combat abilities work with spear in off-hand again (see notes)
+ Sweet berry bushes now work with Herbalism (thanks dnocturne)
+ (Codebase) Fixed unit tests for Java 25 (thanks Warriorrrr)
+ Fixed copper items not giving XP for Repair (thanks Remski01)
NOTES:
As a semi-permanent work around, mcMMO keeps track of when players swing their weapon, if they have swung it recently enough combat skills will work even if you have a spear in your off-hand.
From 9c9e5313ca9daf5f50fc55c13b43cc852542c794 Mon Sep 17 00:00:00 2001
From: nossr50
Date: Sat, 17 Jan 2026 13:38:29 -0800
Subject: [PATCH 5/5] prevent item stack size from going outside normal bounds
---
Changelog.txt | 1 +
.../com/gmail/nossr50/listeners/BlockListener.java | 12 +++++++-----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/Changelog.txt b/Changelog.txt
index eb04894ea3..6379e41053 100644
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -3,6 +3,7 @@ Version 2.2.049
Sweet berry bushes now work with Herbalism (thanks dnocturne)
(Codebase) Fixed unit tests for Java 25 (thanks Warriorrrr)
Fixed copper items not giving XP for Repair (thanks Remski01)
+ Fixed edge case where mcMMO could drop items with stack size set outside normal bounds
NOTES:
As a semi-permanent work around, mcMMO keeps track of when players swing their weapon, if they have swung it recently enough combat skills will work even if you have a spear in your off-hand.
diff --git a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java
index ca83de6cb2..e42bc2e762 100644
--- a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java
+++ b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java
@@ -147,12 +147,14 @@ public void onBlockDropItemEvent(BlockDropItemEvent event) {
}
int amountToAddFromBonus = bonusDropMeta.asInt();
- final McMMOModifyBlockDropItemEvent modifyBlockDropItemEvent
+ final McMMOModifyBlockDropItemEvent modifyDropEvent
= new McMMOModifyBlockDropItemEvent(event, item, amountToAddFromBonus);
- plugin.getServer().getPluginManager().callEvent(modifyBlockDropItemEvent);
- if (!modifyBlockDropItemEvent.isCancelled()
- && modifyBlockDropItemEvent.getModifiedItemStackQuantity() > originalAmount) {
- eventItemStack.setAmount(modifyBlockDropItemEvent.getModifiedItemStackQuantity());
+ plugin.getServer().getPluginManager().callEvent(modifyDropEvent);
+ if (!modifyDropEvent.isCancelled()
+ && modifyDropEvent.getModifiedItemStackQuantity() > originalAmount) {
+ eventItemStack.setAmount(
+ Math.min(modifyDropEvent.getModifiedItemStackQuantity(),
+ item.getItemStack().getMaxStackSize()));
}
}
}