diff --git a/.github/dependencies.sha256 b/.github/dependencies.sha256
index 1888689..e878548 100644
--- a/.github/dependencies.sha256
+++ b/.github/dependencies.sha256
@@ -4,3 +4,4 @@ a37f7789fcdcd11c9e5890a2fac742aa4501749588af23182991953e06ee1d17 libs/MMOItems-
225aa7f75d4e6e6ae6b3ad87a7ca678c025c027f00374ab1062275d24f3a73c9 libs/MythicLib-1.7.1-SNAPSHOT.jar
81d511d0830951f988615b6533cc93e82d00a83fb31e8ccbb36441ae3e8157ef libs/MMOCore-1.13.1-SNAPSHOT.jar
5a01b37bd7442fb0f2c809cadac82ba70f75173956e04efac3336595cc070368 libs/ItemsAdder-4.0.18.jar
+c571502e768640c7cff10c49b3eef904ccf3fd95bd81803913abfe43cfc09746 libs/NBTAPI-2.16.1.jar
diff --git a/.github/scripts/install-local-dependencies.sh b/.github/scripts/install-local-dependencies.sh
index 8ee4873..23f32f8 100644
--- a/.github/scripts/install-local-dependencies.sh
+++ b/.github/scripts/install-local-dependencies.sh
@@ -22,3 +22,6 @@ mvn -B --no-transfer-progress org.apache.maven.plugins:maven-install-plugin:3.1.
mvn -B --no-transfer-progress org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file \
-Dfile="libs/ItemsAdder-4.0.18.jar" -DgroupId="local" -DartifactId="ItemsAdder" \
-Dversion="4.0.18-tfmc-5a01b37bd744" -Dpackaging=jar -DgeneratePom=true "$@"
+mvn -B --no-transfer-progress org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file \
+ -Dfile="libs/NBTAPI-2.16.1.jar" -DgroupId="local" -DartifactId="item-nbt-api-plugin" \
+ -Dversion="2.16.1-tfmc-c571502e7686" -Dpackaging=jar -DgeneratePom=true "$@"
diff --git a/.github/scripts/prepare-release.sh b/.github/scripts/prepare-release.sh
index 1dd1106..89d025c 100644
--- a/.github/scripts/prepare-release.sh
+++ b/.github/scripts/prepare-release.sh
@@ -9,4 +9,5 @@ curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer
curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/225aa7f75d4e/MythicLib-1.7.1-SNAPSHOT.jar?ref=$ref" > "libs/MythicLib-1.7.1-SNAPSHOT.jar"
curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/81d511d08309/MMOCore-1.13.1-SNAPSHOT.jar?ref=$ref" > "libs/MMOCore-1.13.1-SNAPSHOT.jar"
curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/5a01b37bd744/ItemsAdder-4.0.18.jar?ref=$ref" > "libs/ItemsAdder-4.0.18.jar"
+curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/c571502e7686/NBTAPI-2.16.1.jar?ref=$ref" > "libs/NBTAPI-2.16.1.jar"
bash .github/scripts/install-local-dependencies.sh "$@"
diff --git a/pom.xml b/pom.xml
index b0143e8..1441572 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,6 +88,13 @@
+
+
+ local
+ item-nbt-api-plugin
+ 2.16.1-tfmc-c571502e7686
+ provided
+
local
MMOItems
diff --git a/src/main/java/net/tfminecraft/magic/gear/GearItemBuilder.java b/src/main/java/net/tfminecraft/magic/gear/GearItemBuilder.java
index 7c23500..5675a4c 100644
--- a/src/main/java/net/tfminecraft/magic/gear/GearItemBuilder.java
+++ b/src/main/java/net/tfminecraft/magic/gear/GearItemBuilder.java
@@ -46,7 +46,7 @@ public static ItemStack rewriteSockets(ItemStack stack, int band) {
List parts = GearProvenance.resolveParts(stack);
List colours = SocketLayout.colours(archetype, parts, band);
ItemStack rewritten = applyMmoData(stack, colours, parts);
- rewritten = GearModelResolver.apply(rewritten, type, parts);
+ rewritten = GearSkin.keep(stack, GearModelResolver.apply(rewritten, type, parts));
copyGearPdc(stack, rewritten);
GearProvenance.lockSockets(rewritten);
WeaponRequirement.fromItem(stack).persist(rewritten);
diff --git a/src/main/java/net/tfminecraft/magic/gear/GearRefresher.java b/src/main/java/net/tfminecraft/magic/gear/GearRefresher.java
index 7ba3765..eaaca55 100644
--- a/src/main/java/net/tfminecraft/magic/gear/GearRefresher.java
+++ b/src/main/java/net/tfminecraft/magic/gear/GearRefresher.java
@@ -134,7 +134,7 @@ private static ItemStack rewrite(ItemStack stack, List colours, List i.getArgument(1));
var lore = mockStatic(WeaponLore.class)) {
models
.when(() -> GearModelResolver.apply(any(), any(), any()))
.thenAnswer(i -> i.getArgument(0));
lore.when(() -> WeaponLore.updateItem(any())).thenAnswer(i -> i.getArgument(0));
assertSame(stack, GearItemBuilder.rewriteSockets(stack, 1));
+ skin.verify(() -> GearSkin.keep(same(stack), same(stack)));
assertTrue(GearProvenance.socketsLocked(stack));
assertEquals("core:1", GearProvenance.partsRaw(stack));
}
diff --git a/src/test/java/net/tfminecraft/magic/GearRefresherCoverageTest.java b/src/test/java/net/tfminecraft/magic/GearRefresherCoverageTest.java
index 314bb9a..f853f7e 100644
--- a/src/test/java/net/tfminecraft/magic/GearRefresherCoverageTest.java
+++ b/src/test/java/net/tfminecraft/magic/GearRefresherCoverageTest.java
@@ -101,6 +101,7 @@ void rebuildPreservesGearPdcAmountsAndExistingGems() {
try (var nbt = mockStatic(NBTItem.class);
var stats = mockStatic(GearStatApplicator.class);
var model = mockStatic(GearModelResolver.class);
+ var skin = mockStatic(GearSkin.class, i -> i.getArgument(1));
var lore = mockStatic(WeaponLore.class);
var items =
mockConstruction(
@@ -130,6 +131,7 @@ void rebuildPreservesGearPdcAmountsAndExistingGems() {
var captured = org.mockito.ArgumentCaptor.forClass(GemSocketsData.class);
verify(items.constructed().getFirst()).setData(eq(ItemStats.GEM_SOCKETS), captured.capture());
assertEquals(List.of(good), captured.getValue().getGems());
+ skin.verify(() -> GearSkin.keep(same(item), same(built)));
}
}
@@ -143,6 +145,7 @@ void outdatedRunesRebuildCurrentGearAndStampTheirRevisions() {
try (var nbt = mockStatic(NBTItem.class);
var stats = mockStatic(GearStatApplicator.class);
var model = mockStatic(GearModelResolver.class);
+ var skin = mockStatic(GearSkin.class, i -> i.getArgument(1));
var lore = mockStatic(WeaponLore.class);
var runes = mockStatic(RuneRefresher.class);
var items =
@@ -183,6 +186,7 @@ void incompatibleGemsAreHeldAndFailuresNeverReplaceOriginals() {
try (var nbt = mockStatic(NBTItem.class);
var stats = mockStatic(GearStatApplicator.class);
var model = mockStatic(GearModelResolver.class);
+ var skin = mockStatic(GearSkin.class, i -> i.getArgument(1));
var lore = mockStatic(WeaponLore.class);
var broken = mockStatic(GearBrokenMarker.class);
var items =
diff --git a/src/test/java/net/tfminecraft/magic/GearSkinTest.java b/src/test/java/net/tfminecraft/magic/GearSkinTest.java
new file mode 100644
index 0000000..a5b2cf4
--- /dev/null
+++ b/src/test/java/net/tfminecraft/magic/GearSkinTest.java
@@ -0,0 +1,66 @@
+package net.tfminecraft.magic;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import io.lumine.mythic.lib.api.item.NBTItem;
+import net.tfminecraft.magic.gear.GearSkin;
+import net.tfminecraft.magic.util.LegacyModelData;
+import net.tfminecraft.tlibs.objects.api.subapi.ItemSkinPreserver;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.junit.jupiter.api.*;
+import org.mockito.MockedStatic;
+
+class GearSkinTest {
+ MockedStatic preserver;
+ MockedStatic nbt;
+ MockedStatic models;
+
+ @BeforeEach
+ void mockStatics() {
+ preserver = mockStatic(ItemSkinPreserver.class);
+ nbt = mockStatic(NBTItem.class);
+ models = mockStatic(LegacyModelData.class);
+ }
+
+ @AfterEach
+ void closeStatics() {
+ preserver.close();
+ nbt.close();
+ models.close();
+ }
+
+ /** A weapon with skin data, an {@code amodel} tag and an optional current model. */
+ ItemStack weapon(boolean skinData, String amodel, Integer model, boolean hasMeta) {
+ var stack = mock(ItemStack.class);
+ var meta = hasMeta ? mock(ItemMeta.class) : null;
+ when(stack.getItemMeta()).thenReturn(meta);
+ preserver.when(() -> ItemSkinPreserver.hasSkinData(same(stack))).thenReturn(skinData);
+ var tags = mock(NBTItem.class);
+ when(tags.getString("amodel")).thenReturn(amodel);
+ nbt.when(() -> NBTItem.get(same(stack))).thenReturn(tags);
+ if (meta != null) {
+ models.when(() -> LegacyModelData.has(same(meta))).thenReturn(model != null);
+ if (model != null) models.when(() -> LegacyModelData.get(same(meta))).thenReturn(model);
+ }
+ return stack;
+ }
+
+ @Test
+ void rebuildsKeepAWornSkinAndDropAStaleTag() {
+ var rebuilt = mock(ItemStack.class);
+ var dressed = mock(ItemStack.class);
+ preserver.when(() -> ItemSkinPreserver.apply(any(), same(rebuilt))).thenReturn(dressed);
+
+ assertSame(dressed, GearSkin.keep(weapon(true, "66", 66, true), rebuilt));
+ assertSame(dressed, GearSkin.keep(weapon(true, null, null, true), rebuilt));
+ assertSame(dressed, GearSkin.keep(weapon(true, "", null, true), rebuilt));
+
+ assertSame(rebuilt, GearSkin.keep(weapon(true, "66", 2, true), rebuilt));
+ assertSame(rebuilt, GearSkin.keep(weapon(true, "66", null, true), rebuilt));
+ assertSame(rebuilt, GearSkin.keep(weapon(true, "66", null, false), rebuilt));
+ assertSame(rebuilt, GearSkin.keep(weapon(false, "66", 66, true), rebuilt));
+ assertNull(GearSkin.keep(weapon(true, "66", 66, true), null));
+ }
+}