支持 Leaf 1.21.11 并内置 TabooLib 依赖

This commit is contained in:
Coldsmiles
2026-10-06 07:42:37 +08:00
parent 74d831efe7
commit a3a3569747
6 changed files with 420 additions and 12 deletions
+125 -7
View File
@@ -1,9 +1,7 @@
import io.izzel.taboolib.gradle.*
plugins {
`java-library`
`maven-publish`
id("io.izzel.taboolib") version "2.0.11"
id("io.izzel.taboolib") version "2.0.2-community-1"
id("org.jetbrains.kotlin.jvm") version "1.7.10"
}
@@ -23,12 +21,27 @@ taboolib {
bukkitNodes["update-info"] = "支持1.12.2物品附加值"
}
env {
install(NMS_UTIL, UI)
install(EXPANSION_COMMAND_HELPER, BUKKIT_ALL)
install(DATABASE, CHAT, CONFIGURATION, LANG, NMS, KETHER)
// TabooLib 6.2 split the legacy bundles into versioned modules. The
// 6.2 platform contains the Bukkit 1.21 compatibility layer.
install(
"bukkit-ui",
"minecraft-command-helper",
"bukkit-hook",
"bukkit-util",
"bukkit-xseries",
"platform-bukkit",
"platform-bukkit-impl",
"database",
"minecraft-chat",
"basic-configuration",
"minecraft-i18n",
"minecraft-kether"
)
repoTabooLib = "https://maven.xbaimiao.com/repository/maven-public/"
}
version {
taboolib = "6.1.1-beta17"
taboolib = "6.2.0-beta33"
}
// relocate("kotlin", "libs.kotlin171")
relocate("com.xbaimiao.ktor", "com.github.xbaimiao.shoppro.stat")
@@ -40,6 +53,111 @@ repositories {
maven("https://r.irepo.space/maven/")
}
val bundledRuntime by configurations.creating
val taboolibRuntimeModules = listOf(
"common",
"platform-bukkit",
"common-env",
"common-util",
"common-legacy-api",
"common-platform-api",
"bukkit-ui",
"minecraft-command-helper",
"bukkit-hook",
"bukkit-util",
"bukkit-xseries",
"platform-bukkit-impl",
"database",
"minecraft-chat",
"basic-configuration",
"minecraft-i18n",
"minecraft-kether"
)
dependencies {
// These files are embedded as resources and installed before TabooLib's
// primitive loader runs, so a server can start without Maven access.
add("bundledRuntime", "me.lucko:jar-relocator:1.7")
add("bundledRuntime", "org.ow2.asm:asm:9.6")
add("bundledRuntime", "org.ow2.asm:asm-util:9.6")
add("bundledRuntime", "org.ow2.asm:asm-commons:9.6")
add("bundledRuntime", "org.tabooproject.reflex:reflex:1.1.7")
add("bundledRuntime", "org.tabooproject.reflex:analyser:1.1.7")
add("bundledRuntime", "org.jetbrains.kotlin:kotlin-stdlib:1.7.10")
add("bundledRuntime", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10")
add("bundledRuntime", "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3")
taboolibRuntimeModules.forEach { module ->
add("bundledRuntime", "io.izzel.taboolib:$module:6.2.0-beta33")
}
}
val prepareBundledRuntime by tasks.registering {
val output = layout.buildDirectory.dir("generated/bundled-runtime")
outputs.dir(output)
doLast {
val root = output.get().asFile
root.deleteRecursively()
val resourceRoot = root.resolve("META-INF/taboolib/bundled")
val index = linkedMapOf<String, String>()
configurations["bundledRuntime"].resolvedConfiguration.resolvedArtifacts.forEach { artifact ->
val module = artifact.moduleVersion.id
val relative = "${module.group.replace('.', '/')}/${module.name}/${module.version}/${module.name}-${module.version}.jar"
val destination = resourceRoot.resolve(relative)
destination.parentFile.mkdirs()
artifact.file.copyTo(destination, overwrite = true)
index[relative] = relative
// The TabooLib build metadata in this project is generated by the
// Kotlin 1.7.10 Gradle plugin, while Reflex 1.1.7 resolves a newer
// compatible stdlib transitively. Install the resolved bytes at
// the exact paths PrimitiveSettings requests as well.
if (module.group == "org.jetbrains.kotlin" &&
(module.name == "kotlin-stdlib" || module.name == "kotlin-stdlib-jdk8")) {
val requested = "${module.group.replace('.', '/')}/${module.name}/1.7.10/${module.name}-1.7.10.jar"
val requestedDestination = resourceRoot.resolve(requested)
requestedDestination.parentFile.mkdirs()
artifact.file.copyTo(requestedDestination, overwrite = true)
index[requested] = requested
val requestedPom = requested.removeSuffix(".jar") + ".pom"
val requestedPomFile = resourceRoot.resolve(requestedPom)
requestedPomFile.parentFile.mkdirs()
requestedPomFile.writeText(
"<project><modelVersion>4.0.0</modelVersion>" +
"<groupId>${module.group}</groupId><artifactId>${module.name}</artifactId>" +
"<version>1.7.10</version></project>",
Charsets.UTF_8
)
index[requestedPom] = requestedPom
}
// RuntimeEnvDependency checks POM + SHA-1 files for Kotlin and
// coroutine roots. A minimal local POM is sufficient because all
// requested root JARs are bundled explicitly.
if (module.group == "org.jetbrains.kotlin" || module.group == "org.jetbrains.kotlinx") {
val pomRelative = relative.removeSuffix(".jar") + ".pom"
val pom = resourceRoot.resolve(pomRelative)
pom.parentFile.mkdirs()
pom.writeText(
"<project><modelVersion>4.0.0</modelVersion>" +
"<groupId>${module.group}</groupId><artifactId>${module.name}</artifactId>" +
"<version>${module.version}</version></project>",
Charsets.UTF_8
)
index[pomRelative] = pomRelative
}
}
val indexFile = root.resolve("META-INF/taboolib/bundled-libraries.properties")
indexFile.parentFile.mkdirs()
indexFile.writeText(index.entries.joinToString("\n") { (target, resource) -> "$target=$resource" } + "\n", Charsets.UTF_8)
}
}
tasks.processResources {
dependsOn(prepareBundledRuntime)
from(prepareBundledRuntime)
}
dependencies {
compileOnly("com.mojang:authlib:3.16.29")
compileOnly("ink.ptms:nms-all:1.0.0")
+1 -1
View File
@@ -1,2 +1,2 @@
group=com.github.xbaimiao.shoppro
version=1.3.5-SNAPSHOT
version=1.3.6-SNAPSHOT
+8
View File
@@ -1 +1,9 @@
pluginManagement {
repositories {
maven("https://maven.xbaimiao.com/repository/maven-public/")
gradlePluginPortal()
mavenCentral()
}
}
rootProject.name = "ShopPro"
@@ -0,0 +1,129 @@
package taboolib.common;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.util.Properties;
/**
* Installs the libraries bundled by the build into TabooLib's normal cache
* before PrimitiveLoader starts resolving dependencies.
*/
public final class BundledLibraryBootstrap {
private static final String INDEX = "META-INF/taboolib/bundled-libraries.properties";
private static final String RESOURCE_PREFIX = "META-INF/taboolib/bundled/";
private BundledLibraryBootstrap() {
}
public static void install() {
Properties index = new Properties();
try (InputStream input = BundledLibraryBootstrap.class.getClassLoader().getResourceAsStream(INDEX)) {
if (input == null) {
return;
}
index.load(input);
} catch (IOException ignored) {
return;
}
String libraryRoot = "libraries";
try (InputStream input = BundledLibraryBootstrap.class.getClassLoader()
.getResourceAsStream("META-INF/taboolib/env.properties")) {
if (input != null) {
Properties env = new Properties();
env.load(input);
libraryRoot = env.getProperty("file-libs", libraryRoot);
}
} catch (IOException ignored) {
// The generated defaults already use "libraries".
}
File root = new File(libraryRoot);
for (String relativePath : index.stringPropertyNames()) {
String resourcePath = index.getProperty(relativePath);
if (resourcePath == null || resourcePath.isEmpty()) {
continue;
}
File target = new File(root, relativePath.replace('/', File.separatorChar));
try {
boolean managedArtifact = relativePath.endsWith(".jar") || relativePath.endsWith(".pom");
if (!target.isFile() || target.length() == 0L || (managedArtifact && !isSha1Valid(target))) {
File parent = target.getParentFile();
if (parent != null && !parent.exists() && !parent.mkdirs() && !parent.isDirectory()) {
continue;
}
try (InputStream input = BundledLibraryBootstrap.class.getClassLoader()
.getResourceAsStream(RESOURCE_PREFIX + resourcePath);
OutputStream output = Files.newOutputStream(target.toPath())) {
if (input == null) {
continue;
}
byte[] buffer = new byte[8192];
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
}
}
if (managedArtifact) {
writeSha1(target);
}
} catch (IOException ignored) {
// PrimitiveLoader will report a clear download/load error if a
// bundled file cannot be installed.
}
}
}
private static void writeSha1(File file) throws IOException {
File sha1 = new File(file.getPath() + ".sha1");
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
try (InputStream input = Files.newInputStream(file.toPath())) {
byte[] buffer = new byte[8192];
int read;
while ((read = input.read(buffer)) != -1) {
digest.update(buffer, 0, read);
}
}
StringBuilder value = new StringBuilder(40);
for (byte part : digest.digest()) {
value.append(String.format("%02x", part & 0xff));
}
Files.write(sha1.toPath(), value.toString().getBytes(java.nio.charset.StandardCharsets.US_ASCII));
} catch (java.security.NoSuchAlgorithmException ex) {
throw new IOException("SHA-1 is unavailable", ex);
}
}
private static boolean isSha1Valid(File file) {
File sha1 = new File(file.getPath() + ".sha1");
if (!sha1.isFile() || sha1.length() == 0L) {
return false;
}
try {
String expected = new String(Files.readAllBytes(sha1.toPath()), java.nio.charset.StandardCharsets.US_ASCII)
.trim().toLowerCase(java.util.Locale.ROOT);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
try (InputStream input = Files.newInputStream(file.toPath())) {
byte[] buffer = new byte[8192];
int read;
while ((read = input.read(buffer)) != -1) {
digest.update(buffer, 0, read);
}
}
StringBuilder actual = new StringBuilder(40);
for (byte part : digest.digest()) {
actual.append(String.format("%02x", part & 0xff));
}
return actual.toString().equals(expected);
} catch (Exception ignored) {
return false;
}
}
}
@@ -0,0 +1,100 @@
package taboolib.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;
import static java.lang.System.getProperty;
/** Runtime settings used by the embedded TabooLib primitive loader. */
public class PrimitiveSettings {
static {
// This runs before any PrimitiveLoader dependency check.
BundledLibraryBootstrap.install();
}
public static final String ID = "!taboolib".substring(1);
public static final Properties RUNTIME_PROPERTIES = getProperties("env", true);
public static final Properties VERSION_PROPERTIES = getProperties("version", false);
public static final String KOTLIN_VERSION = VERSION_PROPERTIES.getProperty(
"!kotlin".substring(1), getProperty("taboolib.kotlin.stdlib", "1.8.22"));
public static final String KOTLIN_COROUTINES_VERSION = VERSION_PROPERTIES.getProperty(
"!kotlin-coroutines".substring(1), getProperty("taboolib.kotlin.coroutines", "1.7.3"));
public static final String TABOOLIB_VERSION = VERSION_PROPERTIES.getProperty(
ID, getProperty("taboolib.version", "skip"));
public static final boolean SKIP_KOTLIN_RELOCATE = VERSION_PROPERTIES.getProperty(
"skip-kotlin-relocate", getProperty("taboolib.skip-relocate.kotlin", "false")).equals("true");
public static final boolean SKIP_TABOOLIB_RELOCATE = VERSION_PROPERTIES.getProperty(
"skip-taboolib-relocate", getProperty("taboolib.skip-relocate.self", "false")).equals("true");
public static final boolean IS_DEV_MODE = TABOOLIB_VERSION.endsWith("-dev")
|| getProperty("taboolib.dev", "false").equals("true");
public static final boolean IS_DEBUG_MODE = IS_DEV_MODE
|| RUNTIME_PROPERTIES.getProperty("debug", getProperty("taboolib.debug", "false")).equals("true");
public static final boolean IS_FORCE_DOWNLOAD_IN_DEV_MODE = RUNTIME_PROPERTIES
.getProperty("force-download-in-dev", "true").equals("true");
public static final String REPO_CENTRAL = RUNTIME_PROPERTIES.getProperty(
"repo-central", getProperty("taboolib.repo.central", "https://maven.aliyun.com/repository/central"));
public static final String REPO_TABOOLIB = RUNTIME_PROPERTIES.getProperty(
"repo-taboolib", getProperty("taboolib.repo.self", "https://maven.xbaimiao.com/repository/maven-public/"));
public static final String REPO_REFLEX = RUNTIME_PROPERTIES.getProperty(
"repo-reflex", getProperty("taboolib.repo.reflex", REPO_TABOOLIB));
public static final String FILE_LIBS = RUNTIME_PROPERTIES.getProperty("file-libs", "libraries");
public static final String FILE_ASSETS = RUNTIME_PROPERTIES.getProperty("file-assets", "assets");
public static boolean IS_ISOLATED_MODE = RUNTIME_PROPERTIES
.getProperty("enable-isolated-classloader", "false").equals("true");
public static final String[] INSTALL_MODULES = RUNTIME_PROPERTIES
.getProperty("module", getProperty("taboolib.module", "")).split(",");
public static String formatVersion(String value) {
return value.replaceAll("[._-]", "");
}
public static String getRelocatedKotlinVersion() {
return "kotlin" + formatVersion(KOTLIN_VERSION);
}
public static String getRelocatedKotlinCoroutinesVersion() {
return "kotlin" + formatVersion(KOTLIN_VERSION) + "x.coroutines"
+ formatVersion(KOTLIN_COROUTINES_VERSION);
}
private static Properties getProperties(String name, boolean allowGlobal) {
Properties properties = new Properties();
URL url = PrimitiveSettings.class.getClassLoader()
.getResource("META-INF/taboolib/" + name + ".properties");
if (url != null) {
try (InputStreamHolder ignored = new InputStreamHolder(url.openStream())) {
properties.load(ignored.input);
} catch (IOException ignored) {
}
}
if (allowGlobal) {
File globalFile = new File(name + ".properties");
if (globalFile.isFile()) {
try (FileInputStream input = new FileInputStream(globalFile)) {
properties.load(input);
} catch (IOException ignored) {
}
}
}
return properties;
}
/** Small closeable holder to keep the try-with-resources block Java 8 compatible. */
private static final class InputStreamHolder implements AutoCloseable {
private final java.io.InputStream input;
private InputStreamHolder(java.io.InputStream input) {
this.input = input;
}
@Override
public void close() throws IOException {
input.close();
}
}
}
@@ -2,6 +2,7 @@ package com.github.xbaimiao.shoppro.util
import com.mojang.authlib.GameProfile
import com.mojang.authlib.properties.Property
import org.bukkit.Bukkit
import org.bukkit.ChatColor
import org.bukkit.Color
import org.bukkit.Material
@@ -18,6 +19,8 @@ import taboolib.library.reflex.Reflex.Companion.setProperty
import taboolib.library.xseries.XMaterial
import taboolib.module.chat.colored
import taboolib.platform.util.isAir
import java.net.URL
import java.nio.charset.StandardCharsets
import java.util.*
@@ -195,6 +198,58 @@ open class ItemBuilder {
return null
}
/**
* Bukkit 1.21 stores skull profiles as ResolvableProfile instead of the
* authlib GameProfile used by older CraftBukkit versions. Prefer the
* public PlayerProfile API when it is available and keep the reflective
* field fallback for older servers.
*/
private fun setSkullTexture(skull: SkullMeta, texture: SkullTexture) {
val applied = runCatching {
val url = textureUrl(texture.textures) ?: return@runCatching false
val createProfile = Bukkit::class.java.methods.firstOrNull {
it.name == "createPlayerProfile" &&
it.parameterTypes.size == 1 &&
it.parameterTypes[0] == UUID::class.java
} ?: return@runCatching false
val profile = createProfile.invoke(null, texture.uuid ?: UUID.randomUUID())
val textures = profile.javaClass.methods.firstOrNull {
it.name == "getTextures" && it.parameterTypes.isEmpty()
}?.invoke(profile) ?: return@runCatching false
val setSkin = textures.javaClass.methods.firstOrNull {
it.name == "setSkin" &&
it.parameterTypes.size == 1 &&
it.parameterTypes[0] == URL::class.java
} ?: return@runCatching false
setSkin.invoke(textures, URL(url))
val setProfile = skull.javaClass.methods.firstOrNull {
it.name == "setPlayerProfile" &&
it.parameterTypes.size == 1
} ?: return@runCatching false
setProfile.invoke(skull, profile)
true
}.getOrDefault(false)
if (!applied) {
runCatching {
skull.setProperty("profile", GameProfile(texture.uuid, "test").also {
it.properties.put("textures", Property("textures", texture.textures))
})
}
}
}
private fun textureUrl(value: String): String? {
if (value.startsWith("http://") || value.startsWith("https://")) {
return value
}
val decoded = runCatching {
String(Base64.getDecoder().decode(value), StandardCharsets.UTF_8)
}.getOrNull() ?: return null
return Regex("\\\"url\\\"\\s*:\\s*\\\"([^\\\"]+)\\\"")
.find(decoded)?.groupValues?.get(1)
}
/**
* 上色
*/
@@ -255,9 +310,7 @@ open class ItemBuilder {
itemMeta.owner = skullOwner
}
if (skullTexture != null) {
itemMeta.setProperty("profile", GameProfile(skullTexture!!.uuid, "test").also {
it.properties.put("textures", Property("textures", skullTexture!!.textures))
})
setSkullTexture(itemMeta, skullTexture!!)
}
}
}