diff --git a/build.gradle.kts b/build.gradle.kts index 59ba55526..162792fc2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,6 @@ plugins { id("net.kyori.indra.publishing") version "3.1.3" id("net.kyori.indra.license-header") version "3.1.3" id("com.gradleup.shadow") version "8.3.5" - id("io.github.slimjar") version "1.3.0" id("xyz.jpenilla.run-paper") version "2.3.1" id("com.github.ben-manes.versions") version "0.51.0" id("org.jetbrains.dokka") version "1.9.20" @@ -19,7 +18,11 @@ plugins { group = "me.glaremasters" version = "3.5.7.2-SNAPSHOT" - +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } +} base { archivesBaseName = "Guilds" } @@ -48,10 +51,10 @@ repositories { content { includeGroup("org.codemc.worldguardwrapper") } } maven("https://repo.glaremasters.me/repository/public/") + maven("https://repo.papermc.io/repository/maven-public/") } dependencies { - implementation("io.github.slimjar:slimjar:1.2.7") implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT") implementation("org.bstats:bstats-bukkit:3.0.2") implementation("co.aikar:taskchain-bukkit:3.7.2") @@ -66,12 +69,10 @@ dependencies { implementation("org.jdbi:jdbi3-sqlobject:3.8.2") implementation("org.mariadb.jdbc:mariadb-java-client:2.7.2") - compileOnly("org.spigotmc:spigot-api:1.21.4-R0.1-SNAPSHOT") + compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT") compileOnly("net.milkbowl:vault:1.7") compileOnly("me.clip:placeholderapi:2.11.6") compileOnly("com.mojang:authlib:1.5.21") - - slim("org.jetbrains.kotlin:kotlin-stdlib") } tasks.withType().configureEach { @@ -93,14 +94,13 @@ tasks.withType().configureEach { tasks { build { dependsOn(named("shadowJar")) - dependsOn(named("slimJar")) } indra { mitLicense() javaVersions { - target(8) + target(21) } github("guilds-plugin", "guilds") { @@ -112,7 +112,7 @@ tasks { compileKotlin { kotlinOptions.javaParameters = true - kotlinOptions.jvmTarget = "1.8" + kotlinOptions.jvmTarget = "21" } compileJava { @@ -131,49 +131,45 @@ tasks { } shadowJar { - fun relocates(vararg dependencies: String) { - dependencies.forEach { - val split = it.split(".") - val name = split.last() - relocate(it, "me.glaremasters.guilds.libs.$name") - } - } - - relocates( - "io.github.slimjar" - ) - - minimize() + // REQUIRED: bStats must be relocated or it will refuse to run + relocate("org.bstats", "me.glaremasters.guilds.libs.bstats") + + // Optional but recommended: keep other shaded libs out of the global namespace + relocate("co.aikar.taskchain", "me.glaremasters.guilds.libs.taskchain") + relocate("co.aikar.acf", "me.glaremasters.guilds.libs.acf") + relocate("dev.triumphteam.gui", "me.glaremasters.guilds.libs.triumphgui") + relocate("ch.jalu.configme", "me.glaremasters.guilds.libs.configme") + relocate("org.codemc.worldguardwrapper", "me.glaremasters.guilds.libs.worldguardwrapper") + relocate("com.dumptruckman.minecraft", "me.glaremasters.guilds.libs.jsonconfiguration") + relocate("net.kyori.adventure", "me.glaremasters.guilds.libs.adventure") + relocate("net.kyori.examination", "me.glaremasters.guilds.libs.examination") + relocate("net.kyori.option", "me.glaremasters.guilds.libs.option") + relocate("com.zaxxer.hikari", "me.glaremasters.guilds.libs.hikari") + relocate("org.jdbi", "me.glaremasters.guilds.libs.jdbi") + relocate("org.mariadb", "me.glaremasters.guilds.libs.mariadb") + + // NOTE: minimize can break runtime if it strips "unused" classes loaded reflectively. + // Start with it OFF while debugging. + // minimize() archiveClassifier.set(null as String?) archiveFileName.set("Guilds-${project.version}.jar") - destinationDirectory.set(rootProject.tasks.shadowJar.get().destinationDirectory.get()) - } - - slimJar { - fun relocates(vararg dependencies: String) { - dependencies.forEach { - val split = it.split(".") - val name = split.last() - relocate(it, "me.glaremasters.guilds.libs.$name") - } - } - - relocates( - "org.bstats", - "co.aikar.commands", - "co.aikar.locales", - "co.aikar.taskchain", - "ch.jalu.configme", - "com.zaxxer.hikari", - "org.jdbi", - "org.mariadb.jdbc", - "dev.triumphteam.gui", - "net.kyori", - "com.cryptomorin.xseries", - "kotlin" - ) } + //shadowJar { + //fun relocates(vararg dependencies: String) { + //dependencies.forEach { + //val split = it.split(".") + //val name = split.last() + //relocate(it, "me.glaremasters.guilds.libs.$name") + //} + //} + + //minimize() + + //archiveClassifier.set(null as String?) + //archiveFileName.set("Guilds-${project.version}.jar") + // keep Gradle Shadow default destination (build/libs) + //} processResources { expand("version" to rootProject.version) diff --git a/gradle.properties b/gradle.properties index e110e410f..e69f1c6a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8' +org.gradle.java.home=C:\\Program Files\\Java\\jdk-21 diff --git a/src/main/java/me/glaremasters/guilds/Guilds.java b/src/main/java/me/glaremasters/guilds/Guilds.java index 924b0411c..a8145e8e5 100644 --- a/src/main/java/me/glaremasters/guilds/Guilds.java +++ b/src/main/java/me/glaremasters/guilds/Guilds.java @@ -30,9 +30,6 @@ import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import io.github.slimjar.app.builder.ApplicationBuilder; -import io.github.slimjar.resolver.data.Repository; -import io.github.slimjar.resolver.mirrors.SimpleMirrorSelector; import me.glaremasters.guilds.acf.ACFHandler; import me.glaremasters.guilds.actions.ActionHandler; import me.glaremasters.guilds.api.GuildsAPI; @@ -112,27 +109,7 @@ public static GuildsAPI getApi() { @Override public void onLoad() { - final Logger logger = getLogger(); - final File dependencyDirectory = new File(getDataFolder(), "Libraries"); - logger.log(Level.INFO, "Loading Libraries..."); - logger.log(Level.INFO, "Note: This might take a few minutes on first run. Kindly ensure internet connectivity."); - final Instant startInstant = Instant.now(); - try { - ApplicationBuilder - .appending("Guilds") - .downloadDirectoryPath(dependencyDirectory.toPath()) - .internalRepositories(Lists.newArrayList( - new Repository(new URL("https://repo.glaremasters.me/repository/public/")), - new Repository(new URL(SimpleMirrorSelector.DEFAULT_CENTRAL_MIRROR_URL)))) - .build(); - final Instant endInstant = Instant.now(); - final long timeTaken = Duration.between(startInstant, endInstant).toMillis(); - final double timeTakenSeconds = timeTaken / 1000.0; - logger.log(Level.INFO, "Loaded libraries in {0} seconds", timeTakenSeconds); - } catch (IOException | ReflectiveOperationException | URISyntaxException | NoSuchAlgorithmException exception) { - logger.log(Level.SEVERE, "Unable to load dependencies... Please ensure an active Internet connection on first run!"); - exception.printStackTrace(); - } + } @Override diff --git a/src/main/java/me/glaremasters/guilds/guild/GuildSkull.java b/src/main/java/me/glaremasters/guilds/guild/GuildSkull.java index dcf3210d7..1a4824e23 100644 --- a/src/main/java/me/glaremasters/guilds/guild/GuildSkull.java +++ b/src/main/java/me/glaremasters/guilds/guild/GuildSkull.java @@ -23,20 +23,28 @@ */ package me.glaremasters.guilds.guild; -import com.cryptomorin.xseries.profiles.builder.XSkull; -import com.cryptomorin.xseries.profiles.objects.ProfileInputType; -import com.cryptomorin.xseries.profiles.objects.Profileable; +import me.glaremasters.guilds.utils.HeadUtils; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import java.util.Base64; -import java.util.Objects; - /** * Represents a guild skull, which is a player head in Minecraft that has a custom texture. + * + *

Important: Paper/Spigot 1.21+ changed internal skull profile handling. The older + * reflection-based approach (setting a GameProfile field on SkullMeta) breaks on newer + * servers. This class intentionally uses supported Bukkit APIs instead.

*/ public class GuildSkull { + /** + * The texture input that should be used to render this skull. This may be: + * + */ private final String serialized; + private transient ItemStack itemStack; /** @@ -45,26 +53,19 @@ public class GuildSkull { * @param player the player whose head will be used for the guild skull */ public GuildSkull(Player player) { - final Profileable playerProfile = Profileable.of(player); - - itemStack = XSkull.createItem().profile(playerProfile).apply(); - serialized = XSkull.of(itemStack.getItemMeta()).getProfileValue(); + this.itemStack = HeadUtils.createPlayerSkull(player); + // Best-effort persistence: if we can extract a textures value, keep it; otherwise fall back to a known hash. + this.serialized = HeadUtils.extractTexturesValue(this.itemStack) + .orElse(HeadUtils.DEFAULT_FALLBACK_HASH); } /** * Creates a guild skull from a texture string. * - * @param texture the texture string, which should be a Minecraft resource location string + * @param texture the texture string */ public GuildSkull(String texture) { - final ProfileInputType type = ProfileInputType.typeOf(texture); - - if (type == null) { - this.serialized = Base64.getEncoder().encodeToString(Objects.requireNonNull(texture).getBytes()); - } else { - this.serialized = texture; - } - + this.serialized = texture; this.itemStack = createSkull(); } @@ -74,18 +75,7 @@ public GuildSkull(String texture) { * @return the guild skull */ public ItemStack createSkull() { - final ProfileInputType type = ProfileInputType.typeOf(serialized); - - if (type == null) { - final ProfileInputType backupType = ProfileInputType.typeOf("c10591e6909e6a281b371836e462d67a2c78fa0952e910f32b41a26c48c1757c"); - final Profileable backupProfile = Profileable.of(backupType, serialized); - - return XSkull.createItem().profile(backupProfile).apply(); - } - - final Profileable playerProfile = Profileable.of(type, serialized); - - return XSkull.createItem().profile(playerProfile).apply(); + return HeadUtils.createTexturedSkull(serialized); } /** @@ -105,4 +95,4 @@ public ItemStack getItemStack() { public String getSerialized() { return serialized; } -} \ No newline at end of file +} diff --git a/src/main/java/me/glaremasters/guilds/utils/GuiUtils.java b/src/main/java/me/glaremasters/guilds/utils/GuiUtils.java index a5c52b050..a16cad785 100644 --- a/src/main/java/me/glaremasters/guilds/utils/GuiUtils.java +++ b/src/main/java/me/glaremasters/guilds/utils/GuiUtils.java @@ -24,9 +24,6 @@ package me.glaremasters.guilds.utils; import com.cryptomorin.xseries.XMaterial; -import com.cryptomorin.xseries.profiles.builder.XSkull; -import com.cryptomorin.xseries.profiles.objects.ProfileInputType; -import com.cryptomorin.xseries.profiles.objects.Profileable; import me.glaremasters.guilds.guild.GuildMember; import org.bukkit.Material; import org.bukkit.inventory.ItemFlag; @@ -67,7 +64,10 @@ public static ItemStack createItem(String material, String name, List lo } /** - * Create a skull item + * Create a skull item. + * + *

Paper/Spigot 1.21+ changed internal skull profile handling. Using reflection-based + * libraries to inject GameProfiles can break. We use supported Bukkit APIs instead.

* * @param member the member to get the skull from * @param name the name of the item @@ -79,15 +79,14 @@ public static ItemStack createSkullItem(final GuildMember member, final String n throw new IllegalArgumentException("Member UUID cannot be null"); } - Profileable playerProfile = Profileable.of(member.getUuid()); - if (playerProfile == null) { - final ProfileInputType backupType = ProfileInputType.typeOf("c10591e6909e6a281b371836e462d67a2c78fa0952e910f32b41a26c48c1757c"); - playerProfile = Profileable.of(backupType, "c10591e6909e6a281b371836e462d67a2c78fa0952e910f32b41a26c48c1757c"); + ItemStack itemStack; + try { + itemStack = HeadUtils.createPlayerSkull(member.getUuid()); + } catch (Exception ex) { + itemStack = HeadUtils.createTexturedSkull(HeadUtils.DEFAULT_FALLBACK_HASH); } - final ItemStack itemStack = XSkull.createItem().profile(playerProfile).apply(); final ItemMeta meta = itemStack.getItemMeta(); - meta.setDisplayName(StringUtils.color(name)); if (!lore.isEmpty()) { meta.setLore(lore.stream().map(StringUtils::color).collect(Collectors.toList()));