Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/dependencies.sha256
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions .github/scripts/install-local-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 "$@"
1 change: 1 addition & 0 deletions .github/scripts/prepare-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 "$@"
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@
</exclusion>
</exclusions>
</dependency>
<!-- TLibs' ItemSkinPreserver needs NBTAPI types; the NBTAPI plugin provides them at runtime. -->
<dependency>
<groupId>local</groupId>
<artifactId>item-nbt-api-plugin</artifactId>
<version>2.16.1-tfmc-c571502e7686</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>local</groupId>
<artifactId>MMOItems</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static ItemStack rewriteSockets(ItemStack stack, int band) {
List<PartDef> parts = GearProvenance.resolveParts(stack);
List<String> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private static ItemStack rewrite(ItemStack stack, List<String> colours, List<Gem
return null;
}
GearType type = GearProvenance.archetypeOf(stack);
return GearModelResolver.apply(built, type, GearProvenance.resolveParts(stack));
return GearSkin.keep(stack, GearModelResolver.apply(built, type, GearProvenance.resolveParts(stack)));
} catch (Exception ex) {
Magic.plugin.getLogger().warning("[Magic] Gear refresh failed: " + ex.getMessage());
return null;
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/net/tfminecraft/magic/gear/GearSkin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.tfminecraft.magic.gear;

import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;

import io.lumine.mythic.lib.api.item.NBTItem;
import net.tfminecraft.magic.util.LegacyModelData;
import net.tfminecraft.tlibs.objects.api.subapi.ItemSkinPreserver;

/**
* Keeps an ArmourShop skin when a weapon is rebuilt. A rebuild starts from the MMOItems
* template and the part model scheme, so without this the skin and its scroll are lost.
*/
public final class GearSkin {

private GearSkin() {}

/** The rebuilt weapon wearing the old weapon's skin, or the rebuilt weapon unchanged when there was none. */
public static ItemStack keep(ItemStack old, ItemStack rebuilt) {
if (rebuilt == null || !isSkinned(old)) {
return rebuilt;
}
return ItemSkinPreserver.apply(old, rebuilt);
}

/**
* Rebuilds keep the {@code amodel} tag but reset the model, so a weapon that already lost
* its skin still carries the tag. Only a model that matches the tag counts as worn.
*/
static boolean isSkinned(ItemStack stack) {
if (!ItemSkinPreserver.hasSkinData(stack)) {
return false;
}
String amodel = NBTItem.get(stack).getString("amodel");
if (amodel == null || amodel.isEmpty()) {
return true;
}
ItemMeta meta = stack.getItemMeta();
return meta != null && LegacyModelData.has(meta)
&& amodel.equals(String.valueOf(LegacyModelData.get(meta)));
}
}
2 changes: 2 additions & 0 deletions src/test/java/net/tfminecraft/magic/GearItemCoverageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,14 @@ void socketRewriteKeepsIdentityAndLocksFirstAttunement() throws Exception {
assertSame(stack, GearItemBuilder.rewriteSockets(stack, 1));
tag(stack, GearKeys.socketsLocked(), PersistentDataType.BYTE, null);
try (var models = mockStatic(GearModelResolver.class);
var skin = mockStatic(GearSkin.class, i -> 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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)));
}
}

Expand All @@ -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 =
Expand Down Expand Up @@ -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 =
Expand Down
66 changes: 66 additions & 0 deletions src/test/java/net/tfminecraft/magic/GearSkinTest.java
Original file line number Diff line number Diff line change
@@ -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<ItemSkinPreserver> preserver;
MockedStatic<NBTItem> nbt;
MockedStatic<LegacyModelData> 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));
}
}
Loading