<freeStyleBuild _class='hudson.model.FreeStyleBuild'><action _class='hudson.model.CauseAction'><cause _class='hudson.triggers.SCMTrigger$SCMTriggerCause'><shortDescription>Started by an SCM change</shortDescription></cause></action><action></action><action _class='hudson.plugins.git.util.BuildData'><buildsByBranchName><refsremotesoriginmaster _class='hudson.plugins.git.util.Build'><buildNumber>359</buildNumber><marked><SHA1>f77233db9983cf4af29b495a68dfad6333344210</SHA1><branch><SHA1>f77233db9983cf4af29b495a68dfad6333344210</SHA1><name>refs/remotes/origin/master</name></branch></marked><revision><SHA1>f77233db9983cf4af29b495a68dfad6333344210</SHA1><branch><SHA1>f77233db9983cf4af29b495a68dfad6333344210</SHA1><name>refs/remotes/origin/master</name></branch></revision></refsremotesoriginmaster></buildsByBranchName><lastBuiltRevision><SHA1>f77233db9983cf4af29b495a68dfad6333344210</SHA1><branch><SHA1>f77233db9983cf4af29b495a68dfad6333344210</SHA1><name>refs/remotes/origin/master</name></branch></lastBuiltRevision><remoteUrl>https://github.com/mcMMO-Dev/mcMMO.git</remoteUrl><scmName></scmName></action><action></action><action></action><action></action><action _class='org.jenkinsci.plugins.displayurlapi.actions.RunDisplayAction'></action><artifact><displayPath>mcMMO.jar</displayPath><fileName>mcMMO.jar</fileName><relativePath>target/mcMMO.jar</relativePath></artifact><artifact><displayPath>original-mcMMO.jar</displayPath><fileName>original-mcMMO.jar</fileName><relativePath>target/original-mcMMO.jar</relativePath></artifact><building>false</building><displayName>#359</displayName><duration>76054</duration><estimatedDuration>66807</estimatedDuration><fullDisplayName>mcMMO #359</fullDisplayName><id>359</id><inProgress>false</inProgress><keepLog>false</keepLog><number>359</number><queueId>305</queueId><result>SUCCESS</result><timestamp>1790038152724</timestamp><url>https://popicraft.net/jenkins/job/mcMMO/359/</url><builtOn>Builder</builtOn><changeSet _class='hudson.plugins.git.GitChangeSetList'><item _class='hudson.plugins.git.GitChangeSet'><affectedPath>src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java</affectedPath><affectedPath>src/test/java/com/gmail/nossr50/skills/crossbows/CrossbowsManagerTest.java</affectedPath><commitId>9168683b6d9aeea3b16365592e6c7365f0f19a5f</commitId><timestamp>1789621745000</timestamp><author><absoluteUrl>https://popicraft.net/jenkins/user/127477265+cyrne1-7208</absoluteUrl><fullName>127477265+cyrne1-7208</fullName></author><authorEmail>127477265+cyrne1-7208@users.noreply.github.com</authorEmail><comment>Avoid in-place Vector mutation in Crossbow ricochet calculations

In CrossbowsManager.handleRicochet(), Bukkit Vector arithmetic methods
(subtract, multiply) mutate the receiver in place. The reflection formula
previously mutated both arrowInBlockVector and normal, and then reused the
mutated normal to compute inverseNormal.

While the existing production path produces new Vector instances for
projectile hits, mutating arguments passed into a public method is unsafe
for future callers and external consumers.

This change:
- Clones vectors before in-place mutations (subtract, multiply) so that
  supplied arguments and intermediate vectors remain unmodified.
- Eliminates inverseNormal by directly comparing reflectedDirection.angle(normal),
  matching the comment description and specular reflection geometry.
- Avoids Vector.normalize() on the surface normal, avoiding potential
  NaN values if a non-cardinal zero vector is supplied.
- Adds a unit test ensuring that neither the arrow velocity nor the
  surface normal vector is mutated during handleRicochet().

Signed-off-by: Cyrne1_7208 &lt;127477265+cyrne1-7208@users.noreply.github.com&gt;
</comment><date>2026-09-17 05:09:05 +0000</date><id>9168683b6d9aeea3b16365592e6c7365f0f19a5f</id><msg>Avoid in-place Vector mutation in Crossbow ricochet calculations</msg><path><editType>edit</editType><file>src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java</file></path><path><editType>edit</editType><file>src/test/java/com/gmail/nossr50/skills/crossbows/CrossbowsManagerTest.java</file></path></item><item _class='hudson.plugins.git.GitChangeSet'><affectedPath>pom.xml</affectedPath><commitId>6166d098296fbd8ae0ed8711f64dca21129b2b92</commitId><timestamp>1790022472000</timestamp><author><absoluteUrl>https://popicraft.net/jenkins/user/nossr50</absoluteUrl><fullName>nossr50</fullName></author><authorEmail>nossr50@gmail.com</authorEmail><comment>2.3.002 snapshots
</comment><date>2026-09-21 13:27:52 -0700</date><id>6166d098296fbd8ae0ed8711f64dca21129b2b92</id><msg>2.3.002 snapshots</msg><path><editType>edit</editType><file>pom.xml</file></path></item><item _class='hudson.plugins.git.GitChangeSet'><affectedPath>src/test/java/com/gmail/nossr50/util/RecipeUtilsTest.java</affectedPath><affectedPath>src/test/java/com/gmail/nossr50/util/skills/SkillUtilsTest.java</affectedPath><affectedPath>src/main/java/com/gmail/nossr50/util/ItemUtils.java</affectedPath><affectedPath>src/main/java/com/gmail/nossr50/util/RecipeUtils.java</affectedPath><affectedPath>src/test/java/com/gmail/nossr50/util/ThrowingRecipeIterator.java</affectedPath><affectedPath>src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java</affectedPath><affectedPath>src/test/java/com/gmail/nossr50/util/ItemUtilsTest.java</affectedPath><affectedPath>Changelog.txt</affectedPath><commitId>6b055d2a370131d04abe3836d41f3f100189f471</commitId><timestamp>1790037588000</timestamp><author><absoluteUrl>https://popicraft.net/jenkins/user/nossr50</absoluteUrl><fullName>nossr50</fullName></author><authorEmail>nossr50@gmail.com</authorEmail><comment>Fix mcMMO failing to enable on Spigot 26.3

Minecraft 26.3 made brewing recipes data-driven, so they now sit in the
server's recipe manager. Spigot 26.3 has no Bukkit counterpart for them:
BrewingRecipe does not implement toBukkitRecipe, so Server.recipeIterator()
throws AbstractMethodError from next() for every brewing recipe.

SalvageConfig counts ingredients through
SkillUtils.getRepairAndSalvageQuantities while loading, which walks that
iterator, so onEnable died and mcMMO disabled itself. Spigot's
Server.getRecipesFor walks the same iterator, so ItemUtils.isSmelted would
have thrown at runtime on furnace extraction as well.

RecipeUtils.safeRecipeIterator wraps the server iterator and skips recipes
the server cannot convert. CraftBukkit advances its underlying iterator
before converting, so a failed entry is already consumed and the walk
continues with the next one. Both call sites use it, and
hasOreSmeltingRecipe filters by result type itself instead of calling
getRecipesFor.

Servers that convert every recipe (Paper 26.3, and Paper and Spigot
1.20.6) see the same recipes in the same order as before.

Fixes #5346
</comment><date>2026-09-21 17:39:48 -0700</date><id>6b055d2a370131d04abe3836d41f3f100189f471</id><msg>Fix mcMMO failing to enable on Spigot 26.3</msg><path><editType>edit</editType><file>src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java</file></path><path><editType>add</editType><file>src/test/java/com/gmail/nossr50/util/ThrowingRecipeIterator.java</file></path><path><editType>edit</editType><file>src/test/java/com/gmail/nossr50/util/ItemUtilsTest.java</file></path><path><editType>edit</editType><file>src/test/java/com/gmail/nossr50/util/skills/SkillUtilsTest.java</file></path><path><editType>add</editType><file>src/main/java/com/gmail/nossr50/util/RecipeUtils.java</file></path><path><editType>add</editType><file>src/test/java/com/gmail/nossr50/util/RecipeUtilsTest.java</file></path><path><editType>edit</editType><file>Changelog.txt</file></path><path><editType>edit</editType><file>src/main/java/com/gmail/nossr50/util/ItemUtils.java</file></path></item><item _class='hudson.plugins.git.GitChangeSet'><affectedPath>src/main/java/com/gmail/nossr50/skills/repair/repairables/SimpleRepairable.java</affectedPath><affectedPath>Changelog.txt</affectedPath><affectedPath>src/test/java/com/gmail/nossr50/skills/repair/repairables/SimpleRepairableTest.java</affectedPath><commitId>5f2e9f86009a561f8f5b1b479be9328d139f1b8c</commitId><timestamp>1790037588000</timestamp><author><absoluteUrl>https://popicraft.net/jenkins/user/nossr50</absoluteUrl><fullName>nossr50</fullName></author><authorEmail>nossr50@gmail.com</authorEmail><comment>Count repair ingredients once per repairable instead of on every repair

SimpleRepairable.getMinimumQuantity fell through to
SkillUtils.getRepairAndSalvageQuantities whenever MinimumQuantity was not
configured, and RepairManager reaches it through getBaseRepairDurability
on every anvil repair. That method converts every recipe on the server to
its Bukkit form, around two thousand of them, and on Spigot 26.3 each walk
also throws and catches an AbstractMethodError per brewing recipe.

The recipe count is now kept in a field after the first repair of that
item type. The field is a plain int: a count is always at least one, zero
means not counted yet, and an int write is atomic, so a racing region
thread either counts for itself or reads a usable value. Recipes changed
after the first repair are no longer picked up, which matches Salvage,
where the count has always been taken once at startup.

A configured quantity of zero or less now falls back to the recipe count
as well. It is the divisor of the base repair amount, and only a
configured zero was mapped to the fallback before, so a negative value in
repair.vanilla.yml or a zero passed to RepairableFactory by another plugin
produced a negative repair or a division by zero.
</comment><date>2026-09-21 17:39:48 -0700</date><id>5f2e9f86009a561f8f5b1b479be9328d139f1b8c</id><msg>Count repair ingredients once per repairable instead of on every repair</msg><path><editType>edit</editType><file>src/main/java/com/gmail/nossr50/skills/repair/repairables/SimpleRepairable.java</file></path><path><editType>edit</editType><file>Changelog.txt</file></path><path><editType>add</editType><file>src/test/java/com/gmail/nossr50/skills/repair/repairables/SimpleRepairableTest.java</file></path></item><item _class='hudson.plugins.git.GitChangeSet'><affectedPath>src/test/java/com/gmail/nossr50/util/RecipeUtilsTest.java</affectedPath><affectedPath>src/main/java/com/gmail/nossr50/util/ItemUtils.java</affectedPath><affectedPath>src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java</affectedPath><affectedPath>src/main/java/com/gmail/nossr50/util/RecipeUtils.java</affectedPath><commitId>13ea4c34c9da40ace04a64456f51e27961eb8a82</commitId><timestamp>1790037589000</timestamp><author><absoluteUrl>https://popicraft.net/jenkins/user/nossr50</absoluteUrl><fullName>nossr50</fullName></author><authorEmail>nossr50@gmail.com</authorEmail><comment>Only tolerate AbstractMethodError in the recipe walk and log what was skipped

safeRecipeIterator also swallowed UnsupportedOperationException, which no
server is known to throw from its recipe iterator. Swallowing it hid
unrelated failures, and because a skipped entry is assumed to be consumed,
an iterator that keeps throwing without advancing turned the walk into an
endless loop. Only the AbstractMethodError that Spigot 26.3 produces is
skipped now; anything else reaches the caller.

A walk that completes after skipping recipes writes one debug line with
the count and the first failure, shown when General.Verbose_Logging is
on. A server that failed to convert a crafting recipe would otherwise
lower Repair and Salvage quantities with nothing in the log to explain
it. Walks that stop early, like the ore smelting lookup, stay quiet since
their count would be partial. The iterator takes the logger as a
parameter for this.
</comment><date>2026-09-21 17:39:49 -0700</date><id>13ea4c34c9da40ace04a64456f51e27961eb8a82</id><msg>Only tolerate AbstractMethodError in the recipe walk and log what was skipped</msg><path><editType>edit</editType><file>src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java</file></path><path><editType>edit</editType><file>src/main/java/com/gmail/nossr50/util/ItemUtils.java</file></path><path><editType>edit</editType><file>src/test/java/com/gmail/nossr50/util/RecipeUtilsTest.java</file></path><path><editType>edit</editType><file>src/main/java/com/gmail/nossr50/util/RecipeUtils.java</file></path></item><item _class='hudson.plugins.git.GitChangeSet'><affectedPath>Changelog.txt</affectedPath><commitId>4626785d5f175acdf90aa763d2ffe18bfd61c89d</commitId><timestamp>1790037589000</timestamp><author><absoluteUrl>https://popicraft.net/jenkins/user/nossr50</absoluteUrl><fullName>nossr50</fullName></author><authorEmail>nossr50@gmail.com</authorEmail><comment>Note that the Spigot 26.3 startup failure is a Spigot bug mcMMO works around
</comment><date>2026-09-21 17:39:49 -0700</date><id>4626785d5f175acdf90aa763d2ffe18bfd61c89d</id><msg>Note that the Spigot 26.3 startup failure is a Spigot bug mcMMO works around</msg><path><editType>edit</editType><file>Changelog.txt</file></path></item><kind>git</kind></changeSet><culprit><absoluteUrl>https://popicraft.net/jenkins/user/nossr50</absoluteUrl><fullName>nossr50</fullName></culprit><culprit><absoluteUrl>https://popicraft.net/jenkins/user/127477265+cyrne1-7208</absoluteUrl><fullName>127477265+cyrne1-7208</fullName></culprit></freeStyleBuild>