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 build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ tasks.shadowJar {
archiveClassifier.set("")
relocate("org.incendo", "com.minecraftcitiesnetwork.musicbox.internal.cloud")
relocate("org.bstats", "com.minecraftcitiesnetwork.musicbox.internal.bstats")
relocate("net.kyori.adventure.nbt", "com.minecraftcitiesnetwork.musicbox.internal.nbt")
mergeServiceFiles()
}
tasks.jar { archiveClassifier.set("thin") }
Expand Down
10 changes: 9 additions & 1 deletion plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies {
compileOnly("org.projectlombok:lombok:1.18.42")
annotationProcessor("org.projectlombok:lombok:1.18.42")
implementation("org.incendo:cloud-paper:2.0.1")
compileOnly("net.kyori:adventure-nbt:4.26.1")
implementation("net.kyori:adventure-nbt:4.26.1")
testImplementation("net.kyori:adventure-nbt:4.26.1")
testImplementation("com.github.MilkBowl:VaultAPI:1.7") { isTransitive = false }
implementation("org.bstats:bstats-bukkit:3.0.2")
Expand Down Expand Up @@ -42,3 +42,11 @@ tasks.register<Test>("paperRuntimeTest") {
doFirst { probeDirectory.mkdirs() }
maxHeapSize = "2g"
}

// Exercise the published artifact, not the test classpath's unshaded NBT dependency.
val packagedPlugin = rootProject.tasks.named<Jar>("shadowJar")
tasks.test {
dependsOn(packagedPlugin)
inputs.file(packagedPlugin.flatMap { it.archiveFile })
doFirst { systemProperty("musicbox.jar", packagedPlugin.get().archiveFile.get().asFile.absolutePath) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.minecraftcitiesnetwork.musicbox.utils.nbt;

import java.net.*;
import java.nio.file.*;
import java.io.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import org.bukkit.persistence.*;
import org.bukkit.NamespacedKey;
import net.kyori.adventure.nbt.*;
import static org.mockito.Mockito.*;

/** Runs the real JAR handler with no external Adventure NBT visibility. */
class PackagedDiscDataTest {
static ItemStack item(CompoundBinaryTag root) throws Exception {
var bytes = new ByteArrayOutputStream();
BinaryTagIO.writer().write(root, bytes, BinaryTagIO.Compression.GZIP);
var item = mock(ItemStack.class); var meta = mock(ItemMeta.class);
when(item.hasItemMeta()).thenReturn(true); when(item.getItemMeta()).thenReturn(meta);
when(meta.getPersistentDataContainer()).thenReturn(mock(PersistentDataContainer.class));
when(item.serializeAsBytes()).thenReturn(bytes.toByteArray());
return item;
}
@org.junit.jupiter.api.Test
void packagedHandlerReadsDiscsWithoutServerNbtClasses() throws Exception {
try (var loader = new URLClassLoader(new URL[]{Path.of(System.getProperty("musicbox.jar")).toUri().toURL()}, PackagedDiscDataTest.class.getClassLoader()) {
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("net.kyori.adventure.nbt.")) throw new ClassNotFoundException("External NBT intentionally unavailable: " + name);
if (name.startsWith("com.minecraftcitiesnetwork.musicbox.")) {
Class<?> c = findLoadedClass(name); if(c == null) c = findClass(name); if(resolve) resolveClass(c); return c;
}
return super.loadClass(name,resolve);
}
}) {
var type = loader.loadClass("com.minecraftcitiesnetwork.musicbox.utils.nbt.BukkitNbtHandler");
var handler = type.getConstructor().newInstance();
var get = type.getMethod("getNbt",ItemStack.class,String.class);
var pdc = item(CompoundBinaryTag.empty());
when(pdc.getItemMeta().getPersistentDataContainer().get(NamespacedKey.fromString("musicbox:song_hash"),PersistentDataType.INTEGER)).thenReturn(-197);
check(-197,get.invoke(handler,pdc,"song_hash")); verify(pdc,never()).serializeAsBytes();
var tag = CompoundBinaryTag.builder().putInt("song_hash",-123456).build();
var modern = item(CompoundBinaryTag.builder().put("components",CompoundBinaryTag.builder().put("minecraft:custom_data",tag).build()).build());
var legacy = item(CompoundBinaryTag.builder().put("tag",tag).build());
check(-123456,get.invoke(handler,modern,"song_hash")); check(-123456,get.invoke(handler,legacy,"song_hash"));
verify(modern,never()).setItemMeta(any()); verify(legacy,never()).setItemMeta(any());
var empty = item(CompoundBinaryTag.empty()); check(0,get.invoke(handler,empty,"song_hash"));
when(empty.serializeAsBytes()).thenReturn(new byte[]{1,2,3}); check(0,get.invoke(handler,empty,"song_hash"));
check(0,get.invoke(handler,null,"song_hash"));
var clone=mock(ItemStack.class); var meta=mock(ItemMeta.class); var data=mock(PersistentDataContainer.class);
when(pdc.clone()).thenReturn(clone); when(clone.getItemMeta()).thenReturn(meta); when(meta.getPersistentDataContainer()).thenReturn(data);
check(clone,type.getMethod("setNbt",ItemStack.class,String.class,Integer.class).invoke(handler,pdc,"song_hash",123));
verify(data).set(NamespacedKey.fromString("musicbox:song_hash"),PersistentDataType.INTEGER,123); verify(pdc,never()).setItemMeta(any());
}
}
static void check(Object expected,Object actual) { if(!java.util.Objects.equals(expected,actual)) throw new AssertionError(expected+" != "+actual); }
}
Loading