{
  "_class" : "hudson.model.FreeStyleBuild",
  "actions" : [
    {
      "_class" : "hudson.model.CauseAction",
      "causes" : [
        {
          "_class" : "hudson.triggers.SCMTrigger$SCMTriggerCause",
          "shortDescription" : "Started by an SCM change"
        }
      ]
    },
    {
      
    },
    {
      "_class" : "hudson.plugins.git.util.BuildData",
      "buildsByBranchName" : {
        "refs/remotes/origin/master" : {
          "_class" : "hudson.plugins.git.util.Build",
          "buildNumber" : 359,
          "buildResult" : null,
          "marked" : {
            "SHA1" : "f77233db9983cf4af29b495a68dfad6333344210",
            "branch" : [
              {
                "SHA1" : "f77233db9983cf4af29b495a68dfad6333344210",
                "name" : "refs/remotes/origin/master"
              }
            ]
          },
          "revision" : {
            "SHA1" : "f77233db9983cf4af29b495a68dfad6333344210",
            "branch" : [
              {
                "SHA1" : "f77233db9983cf4af29b495a68dfad6333344210",
                "name" : "refs/remotes/origin/master"
              }
            ]
          }
        }
      },
      "lastBuiltRevision" : {
        "SHA1" : "f77233db9983cf4af29b495a68dfad6333344210",
        "branch" : [
          {
            "SHA1" : "f77233db9983cf4af29b495a68dfad6333344210",
            "name" : "refs/remotes/origin/master"
          }
        ]
      },
      "remoteUrls" : [
        "https://github.com/mcMMO-Dev/mcMMO.git"
      ],
      "scmName" : ""
    },
    {
      
    },
    {
      
    },
    {
      
    },
    {
      "_class" : "org.jenkinsci.plugins.displayurlapi.actions.RunDisplayAction"
    }
  ],
  "artifacts" : [
    {
      "displayPath" : "mcMMO.jar",
      "fileName" : "mcMMO.jar",
      "relativePath" : "target/mcMMO.jar"
    },
    {
      "displayPath" : "original-mcMMO.jar",
      "fileName" : "original-mcMMO.jar",
      "relativePath" : "target/original-mcMMO.jar"
    }
  ],
  "building" : false,
  "description" : null,
  "displayName" : "#359",
  "duration" : 76054,
  "estimatedDuration" : 66807,
  "executor" : null,
  "fullDisplayName" : "mcMMO #359",
  "id" : "359",
  "inProgress" : false,
  "keepLog" : false,
  "number" : 359,
  "queueId" : 305,
  "result" : "SUCCESS",
  "timestamp" : 1790038152724,
  "url" : "https://popicraft.net/jenkins/job/mcMMO/359/",
  "builtOn" : "Builder",
  "changeSet" : {
    "_class" : "hudson.plugins.git.GitChangeSetList",
    "items" : [
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java",
          "src/test/java/com/gmail/nossr50/skills/crossbows/CrossbowsManagerTest.java"
        ],
        "commitId" : "9168683b6d9aeea3b16365592e6c7365f0f19a5f",
        "timestamp" : 1789621745000,
        "author" : {
          "absoluteUrl" : "https://popicraft.net/jenkins/user/127477265+cyrne1-7208",
          "fullName" : "127477265+cyrne1-7208"
        },
        "authorEmail" : "127477265+cyrne1-7208@users.noreply.github.com",
        "comment" : "Avoid in-place Vector mutation in Crossbow ricochet calculations\u000a\u000aIn CrossbowsManager.handleRicochet(), Bukkit Vector arithmetic methods\u000a(subtract, multiply) mutate the receiver in place. The reflection formula\u000apreviously mutated both arrowInBlockVector and normal, and then reused the\u000amutated normal to compute inverseNormal.\u000a\u000aWhile the existing production path produces new Vector instances for\u000aprojectile hits, mutating arguments passed into a public method is unsafe\u000afor future callers and external consumers.\u000a\u000aThis change:\u000a- Clones vectors before in-place mutations (subtract, multiply) so that\u000a  supplied arguments and intermediate vectors remain unmodified.\u000a- Eliminates inverseNormal by directly comparing reflectedDirection.angle(normal),\u000a  matching the comment description and specular reflection geometry.\u000a- Avoids Vector.normalize() on the surface normal, avoiding potential\u000a  NaN values if a non-cardinal zero vector is supplied.\u000a- Adds a unit test ensuring that neither the arrow velocity nor the\u000a  surface normal vector is mutated during handleRicochet().\u000a\u000aSigned-off-by: Cyrne1_7208 <127477265+cyrne1-7208@users.noreply.github.com>\u000a",
        "date" : "2026-09-17 05:09:05 +0000",
        "id" : "9168683b6d9aeea3b16365592e6c7365f0f19a5f",
        "msg" : "Avoid in-place Vector mutation in Crossbow ricochet calculations",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "src/main/java/com/gmail/nossr50/skills/crossbows/CrossbowsManager.java"
          },
          {
            "editType" : "edit",
            "file" : "src/test/java/com/gmail/nossr50/skills/crossbows/CrossbowsManagerTest.java"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "pom.xml"
        ],
        "commitId" : "6166d098296fbd8ae0ed8711f64dca21129b2b92",
        "timestamp" : 1790022472000,
        "author" : {
          "absoluteUrl" : "https://popicraft.net/jenkins/user/nossr50",
          "fullName" : "nossr50"
        },
        "authorEmail" : "nossr50@gmail.com",
        "comment" : "2.3.002 snapshots\u000a",
        "date" : "2026-09-21 13:27:52 -0700",
        "id" : "6166d098296fbd8ae0ed8711f64dca21129b2b92",
        "msg" : "2.3.002 snapshots",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "pom.xml"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "src/test/java/com/gmail/nossr50/util/RecipeUtilsTest.java",
          "src/test/java/com/gmail/nossr50/util/skills/SkillUtilsTest.java",
          "src/main/java/com/gmail/nossr50/util/ItemUtils.java",
          "src/main/java/com/gmail/nossr50/util/RecipeUtils.java",
          "src/test/java/com/gmail/nossr50/util/ThrowingRecipeIterator.java",
          "src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java",
          "src/test/java/com/gmail/nossr50/util/ItemUtilsTest.java",
          "Changelog.txt"
        ],
        "commitId" : "6b055d2a370131d04abe3836d41f3f100189f471",
        "timestamp" : 1790037588000,
        "author" : {
          "absoluteUrl" : "https://popicraft.net/jenkins/user/nossr50",
          "fullName" : "nossr50"
        },
        "authorEmail" : "nossr50@gmail.com",
        "comment" : "Fix mcMMO failing to enable on Spigot 26.3\u000a\u000aMinecraft 26.3 made brewing recipes data-driven, so they now sit in the\u000aserver's recipe manager. Spigot 26.3 has no Bukkit counterpart for them:\u000aBrewingRecipe does not implement toBukkitRecipe, so Server.recipeIterator()\u000athrows AbstractMethodError from next() for every brewing recipe.\u000a\u000aSalvageConfig counts ingredients through\u000aSkillUtils.getRepairAndSalvageQuantities while loading, which walks that\u000aiterator, so onEnable died and mcMMO disabled itself. Spigot's\u000aServer.getRecipesFor walks the same iterator, so ItemUtils.isSmelted would\u000ahave thrown at runtime on furnace extraction as well.\u000a\u000aRecipeUtils.safeRecipeIterator wraps the server iterator and skips recipes\u000athe server cannot convert. CraftBukkit advances its underlying iterator\u000abefore converting, so a failed entry is already consumed and the walk\u000acontinues with the next one. Both call sites use it, and\u000ahasOreSmeltingRecipe filters by result type itself instead of calling\u000agetRecipesFor.\u000a\u000aServers that convert every recipe (Paper 26.3, and Paper and Spigot\u000a1.20.6) see the same recipes in the same order as before.\u000a\u000aFixes #5346\u000a",
        "date" : "2026-09-21 17:39:48 -0700",
        "id" : "6b055d2a370131d04abe3836d41f3f100189f471",
        "msg" : "Fix mcMMO failing to enable on Spigot 26.3",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java"
          },
          {
            "editType" : "add",
            "file" : "src/test/java/com/gmail/nossr50/util/ThrowingRecipeIterator.java"
          },
          {
            "editType" : "edit",
            "file" : "src/test/java/com/gmail/nossr50/util/ItemUtilsTest.java"
          },
          {
            "editType" : "edit",
            "file" : "src/test/java/com/gmail/nossr50/util/skills/SkillUtilsTest.java"
          },
          {
            "editType" : "add",
            "file" : "src/main/java/com/gmail/nossr50/util/RecipeUtils.java"
          },
          {
            "editType" : "add",
            "file" : "src/test/java/com/gmail/nossr50/util/RecipeUtilsTest.java"
          },
          {
            "editType" : "edit",
            "file" : "Changelog.txt"
          },
          {
            "editType" : "edit",
            "file" : "src/main/java/com/gmail/nossr50/util/ItemUtils.java"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "src/main/java/com/gmail/nossr50/skills/repair/repairables/SimpleRepairable.java",
          "Changelog.txt",
          "src/test/java/com/gmail/nossr50/skills/repair/repairables/SimpleRepairableTest.java"
        ],
        "commitId" : "5f2e9f86009a561f8f5b1b479be9328d139f1b8c",
        "timestamp" : 1790037588000,
        "author" : {
          "absoluteUrl" : "https://popicraft.net/jenkins/user/nossr50",
          "fullName" : "nossr50"
        },
        "authorEmail" : "nossr50@gmail.com",
        "comment" : "Count repair ingredients once per repairable instead of on every repair\u000a\u000aSimpleRepairable.getMinimumQuantity fell through to\u000aSkillUtils.getRepairAndSalvageQuantities whenever MinimumQuantity was not\u000aconfigured, and RepairManager reaches it through getBaseRepairDurability\u000aon every anvil repair. That method converts every recipe on the server to\u000aits Bukkit form, around two thousand of them, and on Spigot 26.3 each walk\u000aalso throws and catches an AbstractMethodError per brewing recipe.\u000a\u000aThe recipe count is now kept in a field after the first repair of that\u000aitem type. The field is a plain int: a count is always at least one, zero\u000ameans not counted yet, and an int write is atomic, so a racing region\u000athread either counts for itself or reads a usable value. Recipes changed\u000aafter the first repair are no longer picked up, which matches Salvage,\u000awhere the count has always been taken once at startup.\u000a\u000aA configured quantity of zero or less now falls back to the recipe count\u000aas well. It is the divisor of the base repair amount, and only a\u000aconfigured zero was mapped to the fallback before, so a negative value in\u000arepair.vanilla.yml or a zero passed to RepairableFactory by another plugin\u000aproduced a negative repair or a division by zero.\u000a",
        "date" : "2026-09-21 17:39:48 -0700",
        "id" : "5f2e9f86009a561f8f5b1b479be9328d139f1b8c",
        "msg" : "Count repair ingredients once per repairable instead of on every repair",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "src/main/java/com/gmail/nossr50/skills/repair/repairables/SimpleRepairable.java"
          },
          {
            "editType" : "edit",
            "file" : "Changelog.txt"
          },
          {
            "editType" : "add",
            "file" : "src/test/java/com/gmail/nossr50/skills/repair/repairables/SimpleRepairableTest.java"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "src/test/java/com/gmail/nossr50/util/RecipeUtilsTest.java",
          "src/main/java/com/gmail/nossr50/util/ItemUtils.java",
          "src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java",
          "src/main/java/com/gmail/nossr50/util/RecipeUtils.java"
        ],
        "commitId" : "13ea4c34c9da40ace04a64456f51e27961eb8a82",
        "timestamp" : 1790037589000,
        "author" : {
          "absoluteUrl" : "https://popicraft.net/jenkins/user/nossr50",
          "fullName" : "nossr50"
        },
        "authorEmail" : "nossr50@gmail.com",
        "comment" : "Only tolerate AbstractMethodError in the recipe walk and log what was skipped\u000a\u000asafeRecipeIterator also swallowed UnsupportedOperationException, which no\u000aserver is known to throw from its recipe iterator. Swallowing it hid\u000aunrelated failures, and because a skipped entry is assumed to be consumed,\u000aan iterator that keeps throwing without advancing turned the walk into an\u000aendless loop. Only the AbstractMethodError that Spigot 26.3 produces is\u000askipped now; anything else reaches the caller.\u000a\u000aA walk that completes after skipping recipes writes one debug line with\u000athe count and the first failure, shown when General.Verbose_Logging is\u000aon. A server that failed to convert a crafting recipe would otherwise\u000alower Repair and Salvage quantities with nothing in the log to explain\u000ait. Walks that stop early, like the ore smelting lookup, stay quiet since\u000atheir count would be partial. The iterator takes the logger as a\u000aparameter for this.\u000a",
        "date" : "2026-09-21 17:39:49 -0700",
        "id" : "13ea4c34c9da40ace04a64456f51e27961eb8a82",
        "msg" : "Only tolerate AbstractMethodError in the recipe walk and log what was skipped",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "src/main/java/com/gmail/nossr50/util/skills/SkillUtils.java"
          },
          {
            "editType" : "edit",
            "file" : "src/main/java/com/gmail/nossr50/util/ItemUtils.java"
          },
          {
            "editType" : "edit",
            "file" : "src/test/java/com/gmail/nossr50/util/RecipeUtilsTest.java"
          },
          {
            "editType" : "edit",
            "file" : "src/main/java/com/gmail/nossr50/util/RecipeUtils.java"
          }
        ]
      },
      {
        "_class" : "hudson.plugins.git.GitChangeSet",
        "affectedPaths" : [
          "Changelog.txt"
        ],
        "commitId" : "4626785d5f175acdf90aa763d2ffe18bfd61c89d",
        "timestamp" : 1790037589000,
        "author" : {
          "absoluteUrl" : "https://popicraft.net/jenkins/user/nossr50",
          "fullName" : "nossr50"
        },
        "authorEmail" : "nossr50@gmail.com",
        "comment" : "Note that the Spigot 26.3 startup failure is a Spigot bug mcMMO works around\u000a",
        "date" : "2026-09-21 17:39:49 -0700",
        "id" : "4626785d5f175acdf90aa763d2ffe18bfd61c89d",
        "msg" : "Note that the Spigot 26.3 startup failure is a Spigot bug mcMMO works around",
        "paths" : [
          {
            "editType" : "edit",
            "file" : "Changelog.txt"
          }
        ]
      }
    ],
    "kind" : "git"
  },
  "culprits" : [
    {
      "absoluteUrl" : "https://popicraft.net/jenkins/user/nossr50",
      "fullName" : "nossr50"
    },
    {
      "absoluteUrl" : "https://popicraft.net/jenkins/user/127477265+cyrne1-7208",
      "fullName" : "127477265+cyrne1-7208"
    }
  ]
}