diff --git a/build.gradle.kts b/build.gradle.kts index 9633f1e5d..25ab53202 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,16 +31,16 @@ subprojects { compileOnly("com.google.guava:guava:21.0") compileOnly("com.google.code.gson:gson:2.8.7") compileOnly("org.apache.commons:commons-lang3:3.5") - compileOnly("org.tabooproject.reflex:reflex:1.1.7") - compileOnly("org.tabooproject.reflex:analyser:1.1.7") + compileOnly("org.tabooproject.reflex:reflex:1.1.8") + compileOnly("org.tabooproject.reflex:analyser:1.1.8") // 测试依赖 testImplementation(kotlin("stdlib")) testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") testImplementation("com.google.guava:guava:21.0") testImplementation("com.google.code.gson:gson:2.8.7") testImplementation("org.apache.commons:commons-lang3:3.5") - testImplementation("org.tabooproject.reflex:reflex:1.1.7") - testImplementation("org.tabooproject.reflex:analyser:1.1.7") + testImplementation("org.tabooproject.reflex:reflex:1.1.8") + testImplementation("org.tabooproject.reflex:analyser:1.1.8") } java { diff --git a/common-env/src/main/java/taboolib/common/env/RuntimeEnv.java b/common-env/src/main/java/taboolib/common/env/RuntimeEnv.java index 7da4b3e70..89a2e7678 100644 --- a/common-env/src/main/java/taboolib/common/env/RuntimeEnv.java +++ b/common-env/src/main/java/taboolib/common/env/RuntimeEnv.java @@ -35,7 +35,7 @@ public class RuntimeEnv { * 用于初始化 Kotlin 环境 */ static void init() { - PrimitiveIO.debug("RuntimeEnv loaded in {0}ms.", TabooLib.execution(() -> { + PrimitiveIO.debug("RuntimeEnv 加载完成,用时 {0} 毫秒。", TabooLib.execution(() -> { List rel = new ArrayList<>(); boolean loadKotlin = !KOTLIN_VERSION.equals("null"); boolean loadKotlinCoroutines = !KOTLIN_COROUTINES_VERSION.equals("null"); diff --git a/common-env/src/main/java/taboolib/common/env/aether/AetherResolver.java b/common-env/src/main/java/taboolib/common/env/aether/AetherResolver.java index 99bcb48e3..5f3f75e4d 100644 --- a/common-env/src/main/java/taboolib/common/env/aether/AetherResolver.java +++ b/common-env/src/main/java/taboolib/common/env/aether/AetherResolver.java @@ -47,6 +47,7 @@ * @since 2024/7/20 20:31 */ @SuppressWarnings("deprecation") + public class AetherResolver { private static final Map resolverMap = Maps.newConcurrentMap(); diff --git a/common-platform-api/src/main/kotlin/taboolib/common/platform/AwakeClass.kt b/common-platform-api/src/main/kotlin/taboolib/common/platform/AwakeClass.kt new file mode 100644 index 000000000..bb8737192 --- /dev/null +++ b/common-platform-api/src/main/kotlin/taboolib/common/platform/AwakeClass.kt @@ -0,0 +1,13 @@ +package taboolib.common.platform + +import org.tabooproject.reflex.serializer.BinarySerializable +import org.tabooproject.reflex.serializer.BinaryWriter + +class AwakeClass(val name: String, val isClassVisitor: Boolean, val platformService: List) : BinarySerializable { + + override fun writeTo(writer: BinaryWriter) { + writer.writeNullableString(name) + writer.writeBoolean(isClassVisitor) + writer.writeList(platformService) { writer.writeNullableString(it) } + } +} \ No newline at end of file diff --git a/common-platform-api/src/main/kotlin/taboolib/common/platform/PlatformFactory.kt b/common-platform-api/src/main/kotlin/taboolib/common/platform/PlatformFactory.kt index d079fc783..40c13c848 100644 --- a/common-platform-api/src/main/kotlin/taboolib/common/platform/PlatformFactory.kt +++ b/common-platform-api/src/main/kotlin/taboolib/common/platform/PlatformFactory.kt @@ -1,18 +1,16 @@ package taboolib.common.platform -import taboolib.common.LifeCycle -import taboolib.common.PrimitiveIO -import taboolib.common.PrimitiveSettings -import taboolib.common.TabooLib +import org.tabooproject.reflex.ReflexClass +import org.tabooproject.reflex.serializer.BinaryReader +import org.tabooproject.reflex.serializer.BinaryWriter +import taboolib.common.* import taboolib.common.env.RuntimeEnv import taboolib.common.inject.ClassVisitor import taboolib.common.inject.ClassVisitorHandler -import taboolib.common.io.runningClassMapInJar -import taboolib.common.io.runningClasses -import taboolib.common.io.runningClassesWithoutLibrary -import taboolib.common.io.runningExactClasses +import taboolib.common.io.* import taboolib.common.platform.function.registerLifeCycleTask import taboolib.common.platform.function.unregisterCommands +import taboolib.common.util.t import java.util.concurrent.ConcurrentHashMap @Suppress("UNCHECKED_CAST") @@ -50,51 +48,10 @@ object PlatformFactory { } val time = System.nanoTime() - var injected = 0 - // 加载运行环境 - for (cls in includedClasses) { - try { - injected += RuntimeEnv.ENV.inject(cls) - } catch (_: NoClassDefFoundError) { - } catch (ex: Throwable) { - ex.printStackTrace() - } - } - - // 加载接口 - for (cls in includedClasses) { - // 插件实例 - if (cls.structure.superclass?.name == Plugin::class.java.name) { - Plugin.setInstance((cls.getInstance() ?: cls.newInstance()) as Plugin) - } - // 自唤醒 - if (cls.hasAnnotation(Awake::class.java)) { - val instance = cls.getInstance() ?: cls.newInstance() - if (instance != null) { - // 依赖注入接口 - if (ClassVisitor::class.java.isInstance(instance)) { - ClassVisitorHandler.register(instance as ClassVisitor) - } - // 平台服务 - cls.interfaces.filter { it.hasAnnotation(PlatformService::class.java) }.forEach { - serviceMap[it.name!!] = instance - } - awokenMap[cls.name!!] = instance - } else { - PrimitiveIO.error("Failed to awake class: ${cls.name}") - } - } - } - - // 调试信息 - if (PrimitiveSettings.IS_DEBUG_MODE) { - PrimitiveIO.debug("PlatformFactory initialized. ({0}ms)", (System.nanoTime() - time) / 1_000_000) - PrimitiveIO.debug("Awakened: {0}", awokenMap.size) - PrimitiveIO.debug("Injected: {0}", injected) - PrimitiveIO.debug("Service : {0}", serviceMap.size) - serviceMap.forEach { (k, v) -> - PrimitiveIO.debug(" = {0} ({1})", k.substringAfterLast('.'), v.javaClass.simpleName) - } + // 是否有缓存 + val useCache = BinaryCache.read("inject/platform", BinaryCache.primarySrcVersion) { injectByCache(it, time) } + if (useCache == null) { + inject(includedClasses, time) } } @@ -154,4 +111,117 @@ object PlatformFactory { inline fun registerService(instance: T) { serviceMap[T::class.java.name] = instance } + + private fun injectByCache(bytes: ByteArray, time: Long) { + val reader = BinaryReader(bytes) + // 依赖注入 + reader.readList { reader.readString() }.forEach { RuntimeEnv.ENV.inject(runningClassMap[it]!!) } + + // 代理主类 + val mainName = reader.readNullableString() + if (mainName != null) { + val main = runningClassMap[mainName]!! + Plugin.setInstance((main.getInstance() ?: main.newInstance()) as Plugin) + } + + // 自唤醒 + reader.readList { + AwakeClass(reader.readString(), reader.readBoolean(), reader.readList { reader.readString() }) + }.forEach { + val cls = runningClassMap[it.name]!! + val instance = cls.getInstance() ?: cls.newInstance() + // 是依赖注入接口 + if (it.isClassVisitor) { + ClassVisitorHandler.register(instance as ClassVisitor) + } + // 是平台服务 + it.platformService.forEach { name -> + serviceMap[name] = instance!! + } + awokenMap[it.name] = instance!! + } + + // 调试信息 + if (PrimitiveSettings.IS_DEBUG_MODE) { + PrimitiveIO.debug("跨平台服务初始化完成,用时 {0} 毫秒。(使用 BinaryCache)", (System.nanoTime() - time) / 1_000_000) + } + } + + private fun inject(includedClasses: Set, time: Long) { + var injected = 0 + val writer = BinaryWriter() + + val envList = arrayListOf() + var main: String? = null + val awakeClassList = arrayListOf() + + // 加载运行环境 + for (cls in includedClasses) { + try { + val i = RuntimeEnv.ENV.inject(cls) + if (i > 0) { + injected += i + envList += cls.name!! + } + } catch (_: NoClassDefFoundError) { + } catch (ex: Throwable) { + ex.printStackTrace() + } + } + + // 加载接口 + for (cls in includedClasses) { + // 插件实例 + if (cls.structure.superclass?.name == Plugin::class.java.name) { + Plugin.setInstance((cls.getInstance() ?: cls.newInstance()) as Plugin) + main = cls.name + } + // 自唤醒 + if (cls.hasAnnotation(Awake::class.java)) { + val instance = cls.getInstance() ?: cls.newInstance() + if (instance != null) { + // 依赖注入接口 + var isClassVisitor = false + if (ClassVisitor::class.java.isInstance(instance)) { + isClassVisitor = true + ClassVisitorHandler.register(instance as ClassVisitor) + } + // 平台服务 + val platformService = arrayListOf() + cls.interfaces.filter { it.hasAnnotation(PlatformService::class.java) }.forEach { + platformService += it.name!! + serviceMap[it.name!!] = instance + } + awokenMap[cls.name!!] = instance + awakeClassList += AwakeClass(cls.name!!, isClassVisitor, platformService) + } else { + PrimitiveIO.error( + """ + 无法激活 ${cls.name} 的 @Awake 注解 + Failed to enforce @Awake annotation on ${cls.name} + """.t() + ) + } + } + } + + // 写入缓存 + writer.writeList(envList) { writer.writeNullableString(it) } + writer.writeNullableString(main) + writer.writeList(awakeClassList) + + // 保存缓存 + BinaryCache.save("inject/platform", BinaryCache.primarySrcVersion, writer.toByteArray()) + + // 调试信息 + if (PrimitiveSettings.IS_DEBUG_MODE) { + PrimitiveIO.debug("跨平台服务初始化完成,用时 {0} 毫秒。", (System.nanoTime() - time) / 1_000_000) + PrimitiveIO.debug(" 唤醒: {0}", awokenMap.size) + PrimitiveIO.debug(" 注入: {0}", injected) + PrimitiveIO.debug(" 服务: {0}", serviceMap.size) + serviceMap.forEach { (k, v) -> + PrimitiveIO.debug(" = {0} ({1})", k.substringAfterLast('.'), v.javaClass.simpleName) + } + } + } } \ No newline at end of file diff --git a/common-reflex/build.gradle.kts b/common-reflex/build.gradle.kts index 8224cd335..769ae56c1 100644 --- a/common-reflex/build.gradle.kts +++ b/common-reflex/build.gradle.kts @@ -1,15 +1,15 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar dependencies { - implementation("org.tabooproject.reflex:reflex:1.1.7") - implementation("org.tabooproject.reflex:analyser:1.1.7") + implementation("org.tabooproject.reflex:reflex:1.1.8") + implementation("org.tabooproject.reflex:analyser:1.1.8") } tasks { withType { dependencies { - include(dependency("org.tabooproject.reflex:reflex:1.1.7")) - include(dependency("org.tabooproject.reflex:analyser:1.1.7")) + include(dependency("org.tabooproject.reflex:reflex:1.1.8")) + include(dependency("org.tabooproject.reflex:analyser:1.1.8")) } relocate("org.taboooproject", "taboolib.library") } diff --git a/common-util/src/main/java/taboolib/common/inject/ClassVisitorHandler.java b/common-util/src/main/java/taboolib/common/inject/ClassVisitorHandler.java index ed15f57a7..36fcc4348 100644 --- a/common-util/src/main/java/taboolib/common/inject/ClassVisitorHandler.java +++ b/common-util/src/main/java/taboolib/common/inject/ClassVisitorHandler.java @@ -42,7 +42,7 @@ static void init() { // 注册任务 TabooLib.registerLifeCycleTask(lifeCycle, priority, () -> ClassVisitorHandler.injectAll(lifeCycle)); } - PrimitiveIO.debug("ClassVisitorHandler initialized."); + PrimitiveIO.debug("ClassVisitorHandler 初始化完成。"); } /** @@ -72,7 +72,7 @@ public static Set getClasses() { } classes = cache; }); - PrimitiveIO.debug("ClassVisitor loaded {0} classes. ({1}ms)", classes.size(), time); + PrimitiveIO.debug("ClassVisitor 收集到 {0} 个有效类,用时 {1} 毫秒。", classes.size(), time); } return classes; } diff --git a/common-util/src/main/kotlin/taboolib/common/BinaryCache.kt b/common-util/src/main/kotlin/taboolib/common/BinaryCache.kt new file mode 100644 index 000000000..fa4d36f87 --- /dev/null +++ b/common-util/src/main/kotlin/taboolib/common/BinaryCache.kt @@ -0,0 +1,78 @@ +package taboolib.common + +import taboolib.common.io.digest +import taboolib.common.io.groupId +import taboolib.common.io.newFile +import taboolib.common.util.t +import java.io.File + +object BinaryCache { + + val primarySrcVersion = try { + File(BinaryCache::class.java.getProtectionDomain().codeSource.location.file).digest() + } catch (ex: Throwable) { + "unknown" + } + + fun read(name: String, version: String, block: (bytes: ByteArray) -> T): T? { + // 是否有缓存文件 + val cacheFile = getCacheFile().resolve("binary/${name}.cache") + if (cacheFile.exists()) { + // 检查版本 + val metaFile = getCacheFile().resolve("binary/${name}.cache.sha1") + val sha1 = if (metaFile.exists()) metaFile.readText() else "" + if (sha1 == version) { + // 从缓存中读取 + try { + return block(cacheFile.readBytes()) + } catch (ex: Throwable) { + PrimitiveIO.warning( + """ + 无法从缓存文件 "${cacheFile.name}" 中读取类信息。 + Failed to read class information from cache file "${cacheFile.name}". + """.t() + ) + ex.printStackTrace() + drop(name) + } + } + } + return null + } + + fun save(name: String, version: String, bytes: ByteArray) = save(name, version) { bytes } + + fun save(name: String, version: String, block: () -> ByteArray) { + val cacheFile = getCacheFile().resolve("binary/${name}.cache") + val metaFile = getCacheFile().resolve("binary/${name}.cache.sha1") + try { + newFile(cacheFile).writeBytes(block()) + newFile(metaFile).writeText(version) + } catch (ex: Throwable) { + PrimitiveIO.warning( + """ + 无法将类信息写入缓存文件 "${cacheFile.name}"。 + Failed to write class information to cache file "${cacheFile.name}". + """.t() + ) + ex.printStackTrace() + } + } + + fun drop(name: String) { + try { + getCacheFile().resolve("binary/${name}.cache").delete() + getCacheFile().resolve("binary/${name}.cache.sha1").delete() + } catch (ex: Throwable) { + ex.printStackTrace() + } + } + + fun getCacheFile(): File { + val file = File("cache/taboolib/${groupId}") + if (!file.exists()) { + file.mkdirs() + } + return file + } +} \ No newline at end of file diff --git a/common-util/src/main/kotlin/taboolib/common/io/FileDigest.kt b/common-util/src/main/kotlin/taboolib/common/io/FileDigest.kt index 1ddb41f1a..00f1c8f05 100644 --- a/common-util/src/main/kotlin/taboolib/common/io/FileDigest.kt +++ b/common-util/src/main/kotlin/taboolib/common/io/FileDigest.kt @@ -1,9 +1,11 @@ package taboolib.common.io import java.io.File -import java.io.FileInputStream import java.math.BigInteger +import java.nio.ByteBuffer +import java.nio.channels.FileChannel import java.nio.charset.StandardCharsets +import java.nio.file.StandardOpenOption import java.security.MessageDigest /** @@ -12,9 +14,18 @@ import java.security.MessageDigest * @param algorithm 算法类型(可使用:md5, sha-1, sha-256 等) * @return 数字签名 */ -fun String.digest(algorithm: String): String { +fun String.digest(algorithm: String = "sha-1"): String { + return toByteArray(StandardCharsets.UTF_8).digest(algorithm) +} + +/** + * 取 ByteArray 的数字签名 + * @param algorithm 算法类型(可使用:md5, sha-1, sha-256 等) + * @return 数字签名 + */ +fun ByteArray.digest(algorithm: String = "sha-1"): String { val digest = MessageDigest.getInstance(algorithm) - digest.update(toByteArray(StandardCharsets.UTF_8)) + digest.update(this) return BigInteger(1, digest.digest()).toString(16) } @@ -24,13 +35,14 @@ fun String.digest(algorithm: String): String { * @param algorithm 算法类型(可使用:md5, sha-1, sha-256 等) * @return 数字签名 */ -fun File.digest(algorithm: String): String { - return FileInputStream(this).use { +fun File.digest(algorithm: String = "sha-1"): String { + return FileChannel.open(toPath(), StandardOpenOption.READ).use { channel -> val digest = MessageDigest.getInstance(algorithm) - val buffer = ByteArray(1024) - var length: Int - while (it.read(buffer, 0, 1024).also { i -> length = i } != -1) { - digest.update(buffer, 0, length) + val buffer = ByteBuffer.allocateDirect(1024 * 1024) + while (channel.read(buffer) != -1) { + buffer.flip() + digest.update(buffer) + buffer.clear() } BigInteger(1, digest.digest()).toString(16) } diff --git a/common-util/src/main/kotlin/taboolib/common/io/ProjectScanner.kt b/common-util/src/main/kotlin/taboolib/common/io/ProjectScanner.kt index 0d6729bd4..6c515584a 100644 --- a/common-util/src/main/kotlin/taboolib/common/io/ProjectScanner.kt +++ b/common-util/src/main/kotlin/taboolib/common/io/ProjectScanner.kt @@ -2,9 +2,11 @@ package taboolib.common.io import org.tabooproject.reflex.LazyClass import org.tabooproject.reflex.ReflexClass +import org.tabooproject.reflex.ReflexClassMap import taboolib.common.ClassAppender import taboolib.common.PrimitiveIO import taboolib.common.TabooLib +import taboolib.common.BinaryCache import taboolib.common.util.execution import java.io.File import java.net.JarURLConnection @@ -20,7 +22,7 @@ import java.util.jar.JarFile */ val runningClassMapInJar by lazy(LazyThreadSafetyMode.NONE) { val (map, time) = execution { - val map = TabooLib::class.java.protectionDomain.codeSource.location.getClasses() + val map = HashMap(TabooLib::class.java.protectionDomain.codeSource.location.getClasses()) // 额外扫描入口 System.getProperty("taboolib.scan")?.split(',')?.forEach { name -> if (name.isEmpty()) return@forEach @@ -33,7 +35,7 @@ val runningClassMapInJar by lazy(LazyThreadSafetyMode.NONE) { } map } - PrimitiveIO.debug("Loaded {0} classes in ({1}ms).", map.size, time) + PrimitiveIO.debug("ProjectScanner 扫描到 {0} 个类,用时 {1} 毫秒。", map.size, time) map } @@ -96,7 +98,7 @@ val runningResourcesInJar by lazy(LazyThreadSafetyMode.NONE) { } map } - PrimitiveIO.debug("Loaded {0} resources in ({1}ms).", map.size, time) + PrimitiveIO.debug("ProjectScanner 扫描到 {0} 个资源文件,用时 {1} 毫秒。", map.size, time) map } @@ -128,7 +130,7 @@ var extraLoadedResources = ConcurrentHashMap() /** * 获取 URL 下的所有类 */ -fun URL.getClasses(classLoader: ClassLoader = ClassAppender.getClassLoader()): MutableMap { +fun URL.getClasses(classLoader: ClassLoader = ClassAppender.getClassLoader()): Map { val classes = ConcurrentHashMap() val srcFile = try { File(toURI()) @@ -139,6 +141,15 @@ fun URL.getClasses(classLoader: ClassLoader = ClassAppender.getClassLoader()): M } // 是文件 if (srcFile.isFile) { + val srcVersion = srcFile.digest() + // 从二进制缓存中读取 + val classMap = BinaryCache.read(srcFile.nameWithoutExtension, srcVersion) { + val classMap = ReflexClassMap.deserializeFromBytes(it) { Class.forName(it, false, classLoader) } + ReflexClass.reflexClassCacheMap += classMap + classMap + } + if (classMap != null) return classMap + // 从文件中解析 JarFile(srcFile).use { jar -> jar.stream() .parallel() @@ -149,6 +160,8 @@ fun URL.getClasses(classLoader: ClassLoader = ClassAppender.getClassLoader()): M classes[className] = ReflexClass.of(lc, jar.getInputStream(it)) } } + // 保存 + BinaryCache.save(srcFile.nameWithoutExtension, srcVersion) { ReflexClassMap.serializeToBytes(classes) } } // 是目录 else { diff --git a/common-util/src/test/kotlin/taboolib/common/util/Hash.kt b/common-util/src/test/kotlin/taboolib/common/util/Hash.kt new file mode 100644 index 000000000..c99ff1739 --- /dev/null +++ b/common-util/src/test/kotlin/taboolib/common/util/Hash.kt @@ -0,0 +1,17 @@ +package taboolib.common.util + +import taboolib.common.io.digest +import java.io.File +import kotlin.time.ExperimentalTime +import kotlin.time.measureTime + +@OptIn(ExperimentalTime::class) +fun main() { + val file = File("C:/Users/sky/Desktop/Code/Adyeshach/plugin/build/libs/Adyeshach-2.0.25.jar") + println(measureTime { + repeat(1) { file.digest("sha-1") } + }) + println(measureTime { + repeat(100) { file.digest("sha-1") } + }) +} \ No newline at end of file diff --git a/common/src/main/java/taboolib/common/PrimitiveLoader.java b/common/src/main/java/taboolib/common/PrimitiveLoader.java index afad21a93..3728a8ee1 100644 --- a/common/src/main/java/taboolib/common/PrimitiveLoader.java +++ b/common/src/main/java/taboolib/common/PrimitiveLoader.java @@ -52,10 +52,10 @@ public class PrimitiveLoader { */ static List deps() { List deps = new ArrayList<>(); - deps.add(new String[]{"me.lucko", "jar-relocator", "1.7"}); - deps.add(new String[]{"org.ow2.asm", "asm", "9.6"}); - deps.add(new String[]{"org.ow2.asm", "asm-util", "9.6"}); - deps.add(new String[]{"org.ow2.asm", "asm-commons", "9.6"}); + deps.add(new String[]{"!me.lucko".substring(1), "jar-relocator", "1.7"}); + deps.add(new String[]{"!org.ow2.asm".substring(1), "asm", "9.6"}); + deps.add(new String[]{"!org.ow2.asm".substring(1), "asm-util", "9.6"}); + deps.add(new String[]{"!org.ow2.asm".substring(1), "asm-commons", "9.6"}); return deps; } @@ -83,7 +83,7 @@ static List rule() { */ public static void init() throws Throwable { // 开发版本 - PrimitiveIO.debug("Running in development mode.", PrimitiveIO.getRunningFileName()); + PrimitiveIO.debug("当前以调试模式运行。", PrimitiveIO.getRunningFileName()); long time = TabooLib.execution(() -> { // 基础依赖是否隔离加载 boolean isIsolated = PrimitiveLoader.class.getClassLoader() instanceof IsolatedClassLoader; @@ -97,10 +97,10 @@ public static void init() throws Throwable { load(REPO_CENTRAL, i[0], i[1], i[2], IS_ISOLATED_MODE, true, rule()); } // 加载反射模块 - load(REPO_REFLEX, TABOOPROJECT_GROUP + ".reflex", "reflex", "1.1.7", IS_ISOLATED_MODE, true, rule()); - load(REPO_REFLEX, TABOOPROJECT_GROUP + ".reflex", "analyser", "1.1.7", IS_ISOLATED_MODE, true, rule()); + load(REPO_REFLEX, TABOOPROJECT_GROUP + ".reflex", "reflex", "1.1.8", IS_ISOLATED_MODE, true, rule()); + load(REPO_REFLEX, TABOOPROJECT_GROUP + ".reflex", "analyser", "1.1.8", IS_ISOLATED_MODE, true, rule()); }); - PrimitiveIO.debug("Base dependencies loaded in {0} ms.", time); + PrimitiveIO.debug("基础依赖加载完成,用时 {0} 毫秒。", time); // 加载完整模块 loadAll(); } @@ -184,7 +184,7 @@ static void loadAll() throws Throwable { load(REPO_TABOOLIB, TABOOLIB_GROUP, i, TABOOLIB_VERSION, IS_ISOLATED_MODE, false, rule); } }); - PrimitiveIO.debug("All dependencies loaded in {0} ms.", time); + PrimitiveIO.debug("所有依赖加载完成,用时 {0} 毫秒。", time); } /** @@ -230,7 +230,7 @@ static void loadFile(File file, boolean isIsolated, boolean isExternal, List { // 记录生命周期 @@ -81,7 +87,7 @@ public static void lifeCycle(LifeCycle lifeCycle) { } } }); - PrimitiveIO.debug("LifeCycle \"{0}\" completed in {1} ms.", lifeCycle, time); + PrimitiveIO.debug("生命周期 \"{0}\" 用时 {1} 毫秒。", lifeCycle, time); } /** diff --git a/gradle.properties b/gradle.properties index 8c32ad51c..0dcc4523c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=taboolib -version=6.2.1-df22fb1 +version=6.2.2 kotlin.incremental=true kotlin.incremental.java=true kotlin.caching.enabled=true diff --git a/module/basic/basic-configuration/src/main/kotlin/taboolib/module/configuration/ConfigLoader.kt b/module/basic/basic-configuration/src/main/kotlin/taboolib/module/configuration/ConfigLoader.kt index 8d3cd5af1..402060167 100644 --- a/module/basic/basic-configuration/src/main/kotlin/taboolib/module/configuration/ConfigLoader.kt +++ b/module/basic/basic-configuration/src/main/kotlin/taboolib/module/configuration/ConfigLoader.kt @@ -70,9 +70,9 @@ class ConfigLoader : ClassVisitor(1) { field.set(findInstance(owner), conf) // 自动重载 if (configAnno.property("autoReload", false)) { - PrimitiveIO.debug("Listening config file changes: ${file.absolutePath}") + PrimitiveIO.debug("正在监听文件变更: ${file.absolutePath}") FileWatcher.INSTANCE.addSimpleListener(file) { - PrimitiveIO.debug("Config file changed: ${file.absolutePath}") + PrimitiveIO.debug("文件变更: ${file.absolutePath}") if (file.exists()) { conf.loadFromFile(file) } @@ -85,7 +85,7 @@ class ConfigLoader : ClassVisitor(1) { } files[name] = configFile // 开发模式 - PrimitiveIO.debug("Loaded config file: ${file.absolutePath}") + PrimitiveIO.debug("加载配置文件: ${file.absolutePath}") } } } diff --git a/module/bukkit-nms/build.gradle.kts b/module/bukkit-nms/build.gradle.kts index 6adf66849..1526db1c3 100644 --- a/module/bukkit-nms/build.gradle.kts +++ b/module/bukkit-nms/build.gradle.kts @@ -4,6 +4,7 @@ dependencies { compileOnly(project(":common-util")) compileOnly(project(":common-platform-api")) compileOnly(project(":platform:platform-bukkit")) + compileOnly(project(":platform:platform-bukkit-impl")) // 服务端 compileOnly("ink.ptms.core:v12101:12101-minimize:mapped") compileOnly("ink.ptms.core:v11604:11604") diff --git a/module/bukkit-nms/src/main/java/taboolib/module/nms/LightReflection.java b/module/bukkit-nms/src/main/java/taboolib/module/nms/LightReflection.java index 05a02d78c..570c359cc 100644 --- a/module/bukkit-nms/src/main/java/taboolib/module/nms/LightReflection.java +++ b/module/bukkit-nms/src/main/java/taboolib/module/nms/LightReflection.java @@ -52,7 +52,7 @@ public Class getClass(String name, boolean initialize, ClassLoader classLoade return forName(name, initialize, classLoader); } }); - PrimitiveIO.debug("PaperClassFinder Injected"); + PrimitiveIO.debug("PaperClassFinder 已生效。"); } /** diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/AsmClassTranslation.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/AsmClassTranslation.kt index 41227416c..a2ad88631 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/AsmClassTranslation.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/AsmClassTranslation.kt @@ -4,7 +4,12 @@ import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.commons.ClassRemapper import taboolib.common.TabooLib +import taboolib.common.BinaryCache +import taboolib.common.io.digest import taboolib.common.io.taboolibPath +import taboolib.common.platform.function.debug +import taboolib.common.util.execution +import taboolib.common.util.t import taboolib.module.nms.remap.RemapTranslation import taboolib.module.nms.remap.RemapTranslationLegacy import taboolib.module.nms.remap.RemapTranslationTabooLib @@ -37,20 +42,41 @@ class AsmClassTranslation(val source: String) { inputStream = TabooLib::class.java.classLoader.getResourceAsStream(source.replace('.', '/') + ".class") } if (inputStream == null) { - error("Cannot find class: $source") + error( + """ + 没有找到将被转译的类 $source + No class found to be translated $source + """.t() + ) } - val classReader = ClassReader(inputStream) - val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) - // 若当前运行环境为 Paper 时使用新版转换器 - val remapper = if (MinecraftVersion.isUniversalCraftBukkit) { - // 若转译对象为 TabooLib 类,需要特殊处理 - if (source.startsWith(taboolibPath)) RemapTranslationTabooLib() else RemapTranslation() + val bytes = inputStream.readBytes() + val srcVersion = bytes.digest() + // 若存在缓存则直接读取 + val (cacheClass, cost) = execution { BinaryCache.read("remap/$source", srcVersion) { AsmClassLoader.createNewClass(source, it) } } + if (cacheClass != null) { + debug("[AsmClassTranslation] 从缓存中加载 $source,用时 $cost 毫秒。") + return cacheClass } - // 使用旧版本转译器 - else { - RemapTranslationLegacy() + // 转译 + val (newClass, cost2) = execution { + val classReader = ClassReader(bytes) + val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) + // 若当前运行环境为 Paper 时使用新版转换器 + val remapper = if (MinecraftVersion.isUniversalCraftBukkit) { + // 若转译对象为 TabooLib 类,需要特殊处理 + if (source.startsWith(taboolibPath)) RemapTranslationTabooLib() else RemapTranslation() + } + // 使用旧版本转译器 + else { + RemapTranslationLegacy() + } + classReader.accept(ClassRemapper(classWriter, remapper), 0) + val newBytes = classWriter.toByteArray() + // 缓存 + BinaryCache.save("remap/$source", srcVersion) { newBytes } + AsmClassLoader.createNewClass(source, newBytes) } - classReader.accept(ClassRemapper(classWriter, remapper), 0) - return AsmClassLoader.createNewClass(source, classWriter.toByteArray()) + debug("[AsmClassTranslation] 转译 $source,用时 $cost2 毫秒。") + return newClass } } \ No newline at end of file diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/Mapping.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/Mapping.kt index 293eb7954..105bf4944 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/Mapping.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/Mapping.kt @@ -7,6 +7,7 @@ import taboolib.common.io.runningResources import taboolib.common.platform.function.warning import taboolib.common.util.t import taboolib.common.util.unsafeLazy +import taboolib.platform.bukkit.Exchanges import java.io.InputStream import java.util.* @@ -101,7 +102,7 @@ class Mapping( } } } - PrimitiveIO.debug("Spigot Mapping Loaded. (${System.currentTimeMillis() - time}ms)") + PrimitiveIO.debug("Spigot 映射表已加载,用时 {0} 毫秒。", System.currentTimeMillis() - time) PrimitiveIO.debug("Classes: ${mapping.classMapSpigotS2F.size}, Fields: ${mapping.fields.size}, Methods: ${mapping.methods.size}") return mapping // endregion @@ -152,7 +153,7 @@ class Mapping( } } } - PrimitiveIO.debug("Paper Mapping Loaded. ({0}ms)", System.currentTimeMillis() - time) + PrimitiveIO.debug("Paper 映射表已加载,用时 {0} 毫秒。", System.currentTimeMillis() - time) PrimitiveIO.debug("Classes: {0}, Fields: {1}, Methods: {2}", mapping.classMapSpigotToMojang.size, mapping.fields.size, mapping.methods.size) return mapping // endregion diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/MinecraftLanguage.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/MinecraftLanguage.kt index 44e0fa94f..2f5796352 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/MinecraftLanguage.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/MinecraftLanguage.kt @@ -13,9 +13,9 @@ import taboolib.common.platform.Awake import taboolib.common.platform.Platform import taboolib.common.platform.PlatformSide import taboolib.common.platform.function.info -import taboolib.common.platform.function.warning import taboolib.common.util.MatrixList import taboolib.common.util.t +import taboolib.platform.bukkit.Exchanges import java.io.File import java.net.URL import java.util.* @@ -115,7 +115,7 @@ object MinecraftLanguage { var resourceUrl = "https://resources.download.minecraft.net" /** 支持的语言文件 */ - val supportedLanguage = arrayListOf("zh_cn", "zh_tw", "en_gb", "en_us") + val supportedLanguage = arrayListOf("zh_cn", "zh_tw", "en_gb") /** 语言文件 */ val files = hashMapOf() @@ -124,7 +124,7 @@ object MinecraftLanguage { * 获取语言文件 */ fun getLanguageFile(locale: String): LanguageFile? { - return files[locale] + return if (locale == "en_us") files["en_gb"] else files[locale] } /** @@ -218,6 +218,13 @@ object MinecraftLanguage { // 获取语言文件文本并写入本地文件 newFile(file).writeText(URL("$resourceUrl/${langHash.substring(0, 2)}/$langHash").readText()) break + } else { + PrimitiveIO.warning( + """ + 未能找到 Minecraft 语言文件 ... $language + Minecraft language not found ... $language + """.t() + ) } } } diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/MinecraftVersion.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/MinecraftVersion.kt index daa8dc783..c965a6bc5 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/MinecraftVersion.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/MinecraftVersion.kt @@ -16,6 +16,7 @@ import taboolib.common.util.t import taboolib.common.util.unsafeLazy import taboolib.module.nms.remap.RemapReflexPaper import taboolib.module.nms.remap.RemapReflexSpigot +import taboolib.platform.bukkit.Exchanges import java.io.FileInputStream @Inject diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/PacketImpl.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/PacketImpl.kt index 64b78a730..810dd5df7 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/PacketImpl.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/PacketImpl.kt @@ -5,6 +5,7 @@ import org.tabooproject.reflex.Reflex.Companion.setProperty import taboolib.common.platform.function.warning import taboolib.common.util.orNull import taboolib.common.util.t +import taboolib.platform.bukkit.Exchanges import java.util.* import java.util.concurrent.ConcurrentHashMap diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/ProtocolHandler.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/ProtocolHandler.kt index e7220f1fc..ec726c699 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/ProtocolHandler.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/ProtocolHandler.kt @@ -15,6 +15,7 @@ import taboolib.common.platform.function.debug import taboolib.common.platform.function.getOpenContainers import taboolib.common.platform.function.pluginId import taboolib.platform.BukkitPlugin +import taboolib.platform.bukkit.Exchanges /** * @author 坏黑 @@ -44,7 +45,7 @@ object ProtocolHandler : OpenListener { * 这类工具现均已停止维护,可能因服务端改动频繁维护成本极高。 * 未来可能会选择使用 retrooper/packetevents,但它是个巨无霸。 */ - private var instance: LightInjector? = null + var instance: LightInjector? = null /** * 当前插件是否已经注入数据包监听器 @@ -110,7 +111,7 @@ object ProtocolHandler : OpenListener { Exchanges[PACKET_LISTENER] = pluginId Exchanges["$PACKET_LISTENER/plugin/$pluginId"] = null updateContainer() - debug("LightInjector initialized.") + debug("LightInjector 初始化完成。") } /** @@ -128,7 +129,7 @@ object ProtocolHandler : OpenListener { // 只有在 Exchanges 中标记本插件,才会收到共享的数据包事件 if (isPacketEventListened()) { Exchanges["$PACKET_LISTENER/plugin/$pluginId"] = true - debug("LightInjector already initialized by other plugin.") + debug("LightInjector 已在其他插件中初始化。") } } else { injectPacketListener() diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapReflex.kt b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapReflex.kt index 38ea6f075..aefb63213 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapReflex.kt +++ b/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/remap/RemapReflex.kt @@ -5,6 +5,7 @@ import taboolib.common.io.isDevelopmentMode import taboolib.common.io.newFile import taboolib.common.platform.function.getDataFolder import taboolib.module.nms.MinecraftVersion +import taboolib.platform.bukkit.Exchanges import java.util.concurrent.ConcurrentHashMap /** @@ -21,9 +22,9 @@ abstract class RemapReflex : ReflexRemapper { val major = MinecraftVersion.major // 缓存信息 - val fieldRemapCacheMap = ConcurrentHashMap() - val methodRemapCacheMap = ConcurrentHashMap() - val descriptorTypeCacheMap = ConcurrentHashMap>>() + val fieldRemapCacheMap = Exchanges.getOrPut("reflex_remap_field_cache") { ConcurrentHashMap() } + val methodRemapCacheMap = Exchanges.getOrPut("reflex_remap_method_cache") { ConcurrentHashMap() } + val descriptorTypeCacheMap = Exchanges.getOrPut("reflex_remap_descriptor_type_cache") { ConcurrentHashMap>>() } // 映射信息 val spigotMapping = MinecraftVersion.spigotMapping diff --git a/platform/platform-afybroker/src/main/java/taboolib/platform/AfyBrokerPlugin.java b/platform/platform-afybroker/src/main/java/taboolib/platform/AfyBrokerPlugin.java index 33a6fc974..bb13edc0a 100644 --- a/platform/platform-afybroker/src/main/java/taboolib/platform/AfyBrokerPlugin.java +++ b/platform/platform-afybroker/src/main/java/taboolib/platform/AfyBrokerPlugin.java @@ -31,7 +31,7 @@ public class AfyBrokerPlugin extends net.afyer.afybroker.server.plugin.Plugin { private static AfyBrokerPlugin instance; static { - PrimitiveIO.debug("Initialization completed. ({0}ms)", TabooLib.execution(() -> { + PrimitiveIO.debug("AfyBroker 插件初始化完成,用时 {0} 毫秒。", TabooLib.execution(() -> { try { // 初始化 IsolatedClassLoader IsolatedClassLoader.init(AfyBrokerPlugin.class); diff --git a/platform/platform-application/src/main/java/taboolib/platform/App.java b/platform/platform-application/src/main/java/taboolib/platform/App.java index 6ce0d1d5b..f68246566 100644 --- a/platform/platform-application/src/main/java/taboolib/platform/App.java +++ b/platform/platform-application/src/main/java/taboolib/platform/App.java @@ -39,7 +39,7 @@ public static void init() { // 初始化环境参数 setupEnv(); // 启动 TabooLib - PrimitiveIO.debug("Initialization completed. ({0}ms)", TabooLib.execution(() -> { + PrimitiveIO.debug("App 初始化完成,用时 {0} 毫秒。", TabooLib.execution(() -> { // 初始化 IsolatedClassLoader IsolatedClassLoader.init(App.class); // 生命周期任务 diff --git a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/Exchanges.kt b/platform/platform-bukkit-impl/src/main/kotlin/taboolib/platform/bukkit/Exchanges.kt similarity index 99% rename from module/bukkit-nms/src/main/kotlin/taboolib/module/nms/Exchanges.kt rename to platform/platform-bukkit-impl/src/main/kotlin/taboolib/platform/bukkit/Exchanges.kt index 21c81df65..55dacb99e 100644 --- a/module/bukkit-nms/src/main/kotlin/taboolib/module/nms/Exchanges.kt +++ b/platform/platform-bukkit-impl/src/main/kotlin/taboolib/platform/bukkit/Exchanges.kt @@ -1,4 +1,4 @@ -package taboolib.module.nms +package taboolib.platform.bukkit import org.bukkit.Bukkit import org.bukkit.Server diff --git a/platform/platform-bukkit-impl/src/main/kotlin/taboolib/platform/bukkit/ParallelSystem.kt b/platform/platform-bukkit-impl/src/main/kotlin/taboolib/platform/bukkit/ParallelSystem.kt new file mode 100644 index 000000000..defd106c9 --- /dev/null +++ b/platform/platform-bukkit-impl/src/main/kotlin/taboolib/platform/bukkit/ParallelSystem.kt @@ -0,0 +1,246 @@ +package taboolib.platform.bukkit + +import org.bukkit.Bukkit +import org.bukkit.event.player.AsyncPlayerPreLoginEvent +import org.tabooproject.reflex.ClassMethod +import org.tabooproject.reflex.ReflexClass +import taboolib.common.Inject +import taboolib.common.LifeCycle +import taboolib.common.TabooLib +import taboolib.common.inject.ClassVisitor +import taboolib.common.platform.Awake +import taboolib.common.platform.function.pluginId +import taboolib.common.platform.function.registerBukkitListener +import taboolib.common.util.t +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +/** + * 一种在 Bukkit 插件环境下利用 Exchanges 实现的并行初始化设想 + * + * 正常情况下,插件的初始化是串行的,即插件 A 初始化完成后才会初始化插件 B + * ``` + * A --> B --> C + * ``` + * + * 假如插件 B 在启动过程中创建任何数据库的连接,就会导致整个服务器的启动时间变长 + * 并且插件 C 可能并不需要等待 B 连上数据库才能开始启动 + * + * 为了解决这个问题,我设想了 Parallel 工具,以提供一种并行列队,来实现 A 和 B 的并行初始化,而 C 仍然可以依赖 A 的数据: + * ``` + * // 所有插件的初始化列队都必须在 INIT 时注册 + * // 注册一个名为 "task_a" 的任务,在 ENABLE 生命周期下执行,这个任务名必须在所有插件的所有任务中唯一 + * parallel(id = "task_a", runOn = LifeCycle.ENABLE) { + * // 加载插件数据 + * } + * + * // 注册一个名为匿名任务,立即执行 + * parallel { + * // 连接数据库 + * } + * + * // 注册一个匿名任务,依赖 "task_a" + * parallel(dependOn = "task_a") { + * // 读取插件 A 的数据 + * } + * ``` + * + * 那么,原本的串行初始化就变成了并行初始化: + * ``` + * A' --> C' + * B' + * ``` + * + * 同时,为了解决并行数据加载完成之前,玩家提前进入服务器的问题 + * 将会借助 Exchanges 系统选举出一个主插件,负责在所有并行任务完成之前阻止玩家进入服务器 + * + * 注意别引用到其他插件的 parallel 函数。 + * 这个工具仅适用于 Bukkit 插件环境,不适用于 BungeeCord 及其他环境。 + * + * 还有一种 @Parallel 注解,可以直接使一个方法成为并行任务 + * ``` + * @Parallel + * fun taskA() { + * // 加载插件数据 + * } + * ``` + */ +@Awake +@Inject +object ParallelSystem : ClassVisitor(0) { + + val localTaskMap = ConcurrentHashMap() + + val globalTaskMap: MutableMap> + get() = Exchanges.getOrPut("parallel_task_map") { ConcurrentHashMap() } + + val executorService: ExecutorService + get() = Exchanges.getOrPut("parallel_executor_service") { Executors.newFixedThreadPool(Bukkit.getPluginManager().plugins.size * 2) } + + fun registerTask(id: String, dependOn: List, lifeCycle: LifeCycle, block: () -> Unit): CompletableFuture { + // 不允许在 INIT 之后注册 + if (TabooLib.getCurrentLifeCycle() > LifeCycle.INIT) { + error( + """ + 并行任务必须在 INIT 或更早的生命周期下注册。 + Parallel task must be registered in INIT or earlier life cycle. + """.t() + ) + } + // 检查是否存在相同的 ID + if (globalTaskMap.containsKey(id)) { + error( + """ + 并行任务 ID 重复:$id + Parallel task ID duplicate: $id + """.t() + ) + } + val task = Task(id, dependOn, lifeCycle, block) + localTaskMap[id] = task + globalTaskMap[id] = task.future + return task.future + } + + fun runTask(lifeCycle: LifeCycle) { + // 获取符合生命周期的任务 + val tasks = localTaskMap.values.filter { it.lifeCycle == lifeCycle } + // 向中心化的 ExecutorService 提交任务 + tasks.forEach { task -> + if (task.dependOn.isEmpty()) { + // 没有依赖的任务直接提交到线程池 + executorService.submit { + try { + task.block() + task.future.complete(null) + } catch (e: Throwable) { + e.printStackTrace() + task.future.completeExceptionally(e) + } + } + } else { + // 有依赖的任务,等待依赖完成后执行 + val dependencies = task.dependOn.map { dependTaskId -> + // 软依赖 + if (dependTaskId.endsWith('?')) { + val id = dependTaskId.substring(0, dependTaskId.length - 1) + globalTaskMap[id] ?: CompletableFuture.completedFuture(null) + } else { + globalTaskMap[dependTaskId] ?: error( + """ + 并行任务 ${task.id} 的依赖不存在:$dependTaskId + Parallel task ${task.id} depends not found: $dependTaskId + """.t() + ) + } + } + CompletableFuture.allOf(*dependencies.toTypedArray()).thenRunAsync({ + try { + task.block() + task.future.complete(null) + } catch (e: Throwable) { + e.printStackTrace() + task.future.completeExceptionally(e) + } + }, executorService) + } + } + } + + override fun visit(method: ClassMethod, owner: ReflexClass) { + if (method.isAnnotationPresent(Parallel::class.java)) { + val annotation = method.getAnnotation(Parallel::class.java) + val id = annotation.property("id", "").ifEmpty { "anonymous_${UUID.randomUUID()}" } + val dependOn = annotation.list("dependOn") + val lifeCycle = annotation.enum("lifeCycle", LifeCycle.ENABLE) + val obj = findInstance(owner) + registerTask(id, dependOn, lifeCycle) { + if (obj == null) { + method.invokeStatic() + } else { + method.invoke(obj) + } + } + } + } + + override fun getLifeCycle(): LifeCycle { + return LifeCycle.INIT + } + + @Awake(LifeCycle.INIT) + private fun onLoad() { + TabooLib.registerLifeCycleTask(LifeCycle.LOAD, 0) { runTask(LifeCycle.LOAD) } + TabooLib.registerLifeCycleTask(LifeCycle.ENABLE, 0) { runTask(LifeCycle.ENABLE) } + TabooLib.registerLifeCycleTask(LifeCycle.ACTIVE, 0) { runTask(LifeCycle.ACTIVE) } + } + + @Awake(LifeCycle.ENABLE) + private fun onEnable() { + // 首个执行到 ENABLE 阶段的插件负责拦截玩家进入 + if (Exchanges.contains("parallel_main_plugin")) { + return + } + Exchanges["parallel_main_plugin"] = pluginId + registerBukkitListener(AsyncPlayerPreLoginEvent::class.java) { + if (globalTaskMap.values.any { !it.isDone }) { + it.disallow( + AsyncPlayerPreLoginEvent.Result.KICK_OTHER, """ + 服务器正在启动。 + Server is starting. + """.t() + ) + } + } + } + + class Task(val id: String, val dependOn: List, val lifeCycle: LifeCycle, val block: () -> Any?) { + + val future = CompletableFuture() + } +} + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class Parallel(val id: String = "", val dependOn: Array = [], val runOn: LifeCycle = LifeCycle.ENABLE) + +/** + * 注册一个并行任务 + * + * @param id 任务 ID,默认为随机 UUID + * @param runOn 任务执行的生命周期,默认为 ENABLE + * @param block 任务执行的代码块 + * @return 任务执行的 Future 对象 + */ +fun parallel(id: String = "anonymous_${UUID.randomUUID()}", runOn: LifeCycle = LifeCycle.ENABLE, block: () -> Unit): CompletableFuture { + return parallel(id, emptyList(), runOn, block) +} + +/** + * 注册一个带依赖的并行任务 + * + * @param id 任务 ID,默认为随机 UUID + * @param dependOn 依赖的任务 ID + * @param runOn 任务执行的生命周期,默认为 ENABLE + * @param block 任务执行的代码块 + * @return 任务执行的 Future 对象 + */ +fun parallel(id: String = "anonymous_${UUID.randomUUID()}", dependOn: String, runOn: LifeCycle = LifeCycle.ENABLE, block: () -> Unit): CompletableFuture { + return parallel(id, listOf(dependOn), runOn, block) +} + +/** + * 注册一个带多个依赖的并行任务 + * + * @param id 任务 ID,默认为随机 UUID + * @param dependOn 依赖的任务 ID 列表 + * @param runOn 任务执行的生命周期,默认为 ENABLE + * @param block 任务执行的代码块 + * @return 任务执行的 Future 对象 + */ +fun parallel(id: String = "anonymous_${UUID.randomUUID()}", dependOn: List, runOn: LifeCycle = LifeCycle.ENABLE, block: () -> Unit): CompletableFuture { + return ParallelSystem.registerTask(id, dependOn, runOn, block) +} \ No newline at end of file diff --git a/platform/platform-bukkit/src/main/java/taboolib/platform/BukkitPlugin.java b/platform/platform-bukkit/src/main/java/taboolib/platform/BukkitPlugin.java index e2a96cd8d..479f61147 100644 --- a/platform/platform-bukkit/src/main/java/taboolib/platform/BukkitPlugin.java +++ b/platform/platform-bukkit/src/main/java/taboolib/platform/BukkitPlugin.java @@ -34,7 +34,7 @@ public class BukkitPlugin extends JavaPlugin { private static BukkitPlugin instance; static { - PrimitiveIO.debug("Initialization completed. ({0}ms)", TabooLib.execution(() -> { + PrimitiveIO.debug("Bukkit 插件初始化完成,用时 {0} 毫秒。", TabooLib.execution(() -> { try { // 初始化 IsolatedClassLoader IsolatedClassLoader.init(BukkitPlugin.class); diff --git a/platform/platform-bukkit/src/main/java/taboolib/platform/IllegalAccess.java b/platform/platform-bukkit/src/main/java/taboolib/platform/IllegalAccess.java index 1dc1b74ce..bf348be1d 100644 --- a/platform/platform-bukkit/src/main/java/taboolib/platform/IllegalAccess.java +++ b/platform/platform-bukkit/src/main/java/taboolib/platform/IllegalAccess.java @@ -19,7 +19,7 @@ public class IllegalAccess { */ @SuppressWarnings("DataFlowIssue") public static void inject() { - PrimitiveIO.debug("Injected illegal access warning. ({0}ms)", TabooLib.execution(() -> { + PrimitiveIO.debug("已屏蔽 IllegalAccess 警告,用时 {0} 毫秒。", TabooLib.execution(() -> { try { ClassLoader classLoader = BukkitPlugin.class.getClassLoader(); // 获取插件描述文件 diff --git a/platform/platform-bungee/src/main/java/taboolib/platform/BungeePlugin.java b/platform/platform-bungee/src/main/java/taboolib/platform/BungeePlugin.java index 36a233bb8..cbd08edce 100644 --- a/platform/platform-bungee/src/main/java/taboolib/platform/BungeePlugin.java +++ b/platform/platform-bungee/src/main/java/taboolib/platform/BungeePlugin.java @@ -32,7 +32,7 @@ public class BungeePlugin extends net.md_5.bungee.api.plugin.Plugin { private static BungeePlugin instance; static { - PrimitiveIO.debug("Initialization completed. ({0}ms)", TabooLib.execution(() -> { + PrimitiveIO.debug("BungeeCord 插件初始化完成,用时 {0} 毫秒。", TabooLib.execution(() -> { // 初始化 IsolatedClassLoader try { IsolatedClassLoader.init(BungeePlugin.class); diff --git a/platform/platform-velocity/src/main/java/taboolib/platform/VelocityPlugin.java b/platform/platform-velocity/src/main/java/taboolib/platform/VelocityPlugin.java index 4bf99c6b7..2f4aad421 100644 --- a/platform/platform-velocity/src/main/java/taboolib/platform/VelocityPlugin.java +++ b/platform/platform-velocity/src/main/java/taboolib/platform/VelocityPlugin.java @@ -42,7 +42,7 @@ public class VelocityPlugin { private static VelocityPlugin instance; static { - PrimitiveIO.debug("Initialization completed. ({0}ms)", TabooLib.execution(() -> { + PrimitiveIO.debug("Velocity 插件初始化完成,用时 {0} 毫秒。", TabooLib.execution(() -> { try { // 初始化 IsolatedClassLoader IsolatedClassLoader.init(VelocityPlugin.class);