Skip to content
Open
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
92 changes: 44 additions & 48 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ 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"
}

group = "me.glaremasters"
version = "3.5.7.2-SNAPSHOT"

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
base {
archivesBaseName = "Guilds"
}
Expand Down Expand Up @@ -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")
Expand All @@ -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<DokkaTask>().configureEach {
Expand All @@ -93,14 +94,13 @@ tasks.withType<DokkaTask>().configureEach {
tasks {
build {
dependsOn(named("shadowJar"))
dependsOn(named("slimJar"))
}

indra {
mitLicense()

javaVersions {
target(8)
target(21)
}

github("guilds-plugin", "guilds") {
Expand All @@ -112,7 +112,7 @@ tasks {

compileKotlin {
kotlinOptions.javaParameters = true
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.jvmTarget = "21"
}

compileJava {
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.java.home=C:\\Program Files\\Java\\jdk-21
25 changes: 1 addition & 24 deletions src/main/java/me/glaremasters/guilds/Guilds.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
54 changes: 22 additions & 32 deletions src/main/java/me/glaremasters/guilds/guild/GuildSkull.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
* <p>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.</p>
*/
public class GuildSkull {
/**
* The texture input that should be used to render this skull. This may be:
* <ul>
* <li>a textures.minecraft.net texture hash</li>
* <li>a textures.minecraft.net URL</li>
* <li>a base64 "textures" property value</li>
* </ul>
*/
private final String serialized;

private transient ItemStack itemStack;

/**
Expand All @@ -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();
}

Expand All @@ -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);
}

/**
Expand All @@ -105,4 +95,4 @@ public ItemStack getItemStack() {
public String getSerialized() {
return serialized;
}
}
}
19 changes: 9 additions & 10 deletions src/main/java/me/glaremasters/guilds/utils/GuiUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -67,7 +64,10 @@ public static ItemStack createItem(String material, String name, List<String> lo
}

/**
* Create a skull item
* Create a skull item.
*
* <p>Paper/Spigot 1.21+ changed internal skull profile handling. Using reflection-based
* libraries to inject GameProfiles can break. We use supported Bukkit APIs instead.</p>
*
* @param member the member to get the skull from
* @param name the name of the item
Expand All @@ -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()));
Expand Down