LogoArcartX Doc

快速开始

从环境要求到 Gradle 脚本模板,逐字段讲解 blink 配置 DSL,写出第一个能加载的插件。

本篇从零搭建 Blink 工程:配置环境、编写两个 Gradle 脚本、逐字段说明 blink { } DSL,构建第一个插件。跟着抄一遍,你就有了一个能被服务器加载的插件骨架。

读完本篇你能:

  • 准备正确的 JDK / Kotlin / spigot-api 版本组合
  • 写出 settings.gradle.ktsbuild.gradle.kts
  • 理解 blink { } 各字段含义与默认值
  • 仅靠注解完成一个可加载的插件(不写 JavaPlugin 子类、不写 plugin.yml

1. 环境要求

Blink 的代码生成与运行时基于固定版本基线,请严格对齐以避免编译期或运行期不兼容。

组件版本说明
JDK17生成的入口类字节码版本为 V17(见 BytecodeGenerator.ktcw.visit(V17, ...)),低于 17 无法运行
Kotlin1.8.22blink { }kotlinVersion 默认即 1.8.22,Kotlin stdlib 运行时由框架动态加载,不打包进 JAR
Gradle Shadowcom.github.johnrengelman.shadow 8.1.1Blink 在 afterEvaluate 阶段自动 apply,但建议显式声明以锁定版本
spigot-api与目标服务端匹配(如 1.20.4-R0.1-SNAPSHOTcompileOnly,不打包
Proteus(可选)priv.seventeen.artist.proteus 1.0.10仅当 obfuscate = true 时需要

这里要分清两个容易混淆的概念,apiVersionspigot-api 并不是一回事:

  • apiVersion 写进 plugin.ymlapi-version 字段(默认 "1.20"),声明插件按哪个 API 代际编写。
  • spigot-apidependencies { } 里声明的编译依赖,决定 import 哪些类。

两者通常对应同一大版本,但互不强制;spigot-apicompileOnly 声明即可,运行时由服务端提供。

关于 Kotlin runtime:BlinkPlugin.ktapply 时对 runtimeClasspath 做了 exclude(group = "org.jetbrains.kotlin"),stdlib 不进 JAR,而由 KotlinBootstrap 运行时动态加载。你无需手动添加 stdlib 依赖。


2. settings.gradle.kts 模板

pluginManagement 必须能解析到 priv.seventeen.artist.blink 插件,因此要加入 ArcartX 仓库。

pluginManagement {
    repositories {
        mavenLocal()
        maven("https://repo.arcartx.com/repository/maven-public/")
        gradlePluginPortal()
        mavenCentral()
    }
}
 
rootProject.name = "my-plugin"

可参考 test-plugin/settings.gradle.kts


3. build.gradle.kts 模板

下面是最小可用模板(不含混淆)。group 决定包名与 relocate 目标,见下文。

plugins {
    kotlin("jvm") version "1.8.22"
    id("priv.seventeen.artist.blink") version "1.3.11"
    id("com.github.johnrengelman.shadow") version "8.1.1"
}
 
group = "com.example.myplugin"
version = "1.0.0"
 
repositories {
    mavenLocal()
    maven("https://repo.arcartx.com/repository/maven-public/")
    mavenCentral()
    maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}
 
blink {
    name.set("MyPlugin")
    version.set("1.0.0")
    description.set("My first Blink plugin")
    authors.set(listOf("YourName"))
    apiVersion.set("1.20")
    packageName.set("com.example.myplugin")
}
 
dependencies {
    implementation("priv.seventeen.artist.blink:blink-common:1.3.11")
    compileOnly("org.spigotmc:spigot-api:1.20.4-R0.1-SNAPSHOT")
}
 
kotlin {
    jvmToolchain(17)
}
 
tasks.named("build") {
    dependsOn("shadowJar")
}

带混淆 / Aria / Asteroid 的完整示例见 test-plugin/build.gradle.kts

  • blink-commonimplementation:包含注解(@Awake@AutoListener 等)与运行时支撑类,注解扫描与生成的入口类都依赖它,需打包进 JAR。其中 priv.seventeen.artist.blink 包会被 relocate 到 <packageName>.blink,见下文。
  • spigot-apicompileOnly,运行时由服务端提供。
  • jvmToolchain(17) 确保用 JDK 17 编译,匹配字节码基线。

DSL 定义在 BlinkExtension.kt,全部字段为 Gradle Property / ListProperty,统一用 .set(...) 赋值。下表默认值取自该文件 init { } 块的 convention(...)

4.1 plugin.yml 元数据

这些字段最终写进生成的 plugin.yml(见 BlinkGenerateTask.ktbuildPluginYml)。

字段类型默认值说明
nameString""插件名。为空时回退为 Gradle 项目名 project.name
versionString"1.0.0"插件版本,写入 version
descriptionString""插件描述。非空时才写入 description
authorsList<String>[]作者列表。非空时写入 authors
apiVersionString"1.20"写入 api-version,YAML 中带引号输出
dependList<String>[]硬依赖插件,非空时写入 depend
softDependList<String>[]软依赖插件,非空时写入 softdepend
foliaSupportedBooleanfalsetrue 时写入 folia-supported: true
logPrefixString""自定义控制台日志前缀,非空时写入 blink-log-prefix,运行时由 BlinkLog 读取(见 BlinkLog.kt

4.2 包名与构建行为

字段类型默认值说明
packageNameString""生成入口类的目标包,也是 Shadow relocate 与 Proteus defaultPackage 的目标。为空时按编译产物推断(取第一个 class 文件的包),推断不出会报错。建议显式设置。
kotlinVersionString"1.8.22"运行时加载的 Kotlin stdlib 版本基线
librariesList<String>[]运行期由框架动态下载/加载的依赖(Maven 坐标)。写入 plugin.ymlblink-libraries 段,由 DependencyLoader 加载,不打包进 JAR。例如 listOf("com.google.code.gson:gson:2.10.1")

packageName 一处定三事:生成的 BlinkGeneratedMain 等入口类放在此包下、Shadow 把 priv.seventeen.artist.blink relocate 成 <packageName>.blink、Proteus 把所有类归并到此包。三者用同一值,留空则统一回退到 project.group。建议与 group 设成一致。

4.3 功能开关

字段类型默认值说明
enableScriptBooleanfalse启用内置 Nashorn JS 脚本引擎(ScriptManager
enableAriaBooleanfalse启用 Aria 脚本引擎。开启后插件在 afterEvaluate 自动以 compileOnly 引入 Aria(版本构建期自动解析最新 release,兜底见 BlinkPlugin.configureAria
enableAsteroidBooleanfalse启用 Asteroid 跨版本 NMS 桥接,同样自动以 compileOnly 引入 asteroid-nms

Aria 与 Asteroid 都是 compileOnly:编译时可用,运行时由各自的 SharedHost 部署到全 JVM 共享的 ClassLoader,既不打包也不 relocate。详见 AriaSharedHost.ktAsteroidSharedHost.kt

4.4 混淆相关

字段类型默认值说明
obfuscateBooleanfalsetrue 且已 apply Proteus 插件时,Blink 自动配置一整套混淆策略(名称/字符串/控制流/Kotlin 感知等,见 BlinkPlugin.configureProteus
obfuscateKeepList<String>[]追加到 Proteus keepClasses,这些类不被重命名。生成的三个入口类已自动 keep
obfuscateExcludeList<String>[]追加到 Proteus exclude,这些类完全不处理。META-INF/**<packageName>.blink.** 已自动 exclude

混淆细节与可覆盖的 proteus { } 字段见 Proteus 混淆


5. 写第一个插件

Blink 不需要你写 JavaPlugin 子类,也不需要写 plugin.yml。框架在编译后扫描注解,用 ASM 生成 BlinkGeneratedMain(继承 JavaPlugin)及 plugin.yml。你只写带注解的方法。

5.1 入口:用 @Awake 接管生命周期

@Awake 定义在 Awake.kt

annotation class Awake(
    val value: LifeCycle,
    val priority: Int = 0
)

LifeCycle 枚举有四个阶段(见 LifeCycle.kt):LOADENABLEACTIVEDISABLE。被注解的方法必须无参无返回(()V),否则扫描器会跳过并打印警告。推荐放在 object 单例里,框架直接静态调用。

package com.example.myplugin
 
import org.bukkit.Bukkit
import priv.seventeen.artist.blink.bukkitPlugin
import priv.seventeen.artist.blink.lifecycle.Awake
import priv.seventeen.artist.blink.lifecycle.LifeCycle
 
object Bootstrap {
 
    @Awake(LifeCycle.LOAD)
    fun onLoad() {
        bukkitPlugin.logger.info("Loading MyPlugin...")
    }
 
    @Awake(LifeCycle.ENABLE)
    fun onEnable() {
        bukkitPlugin.logger.info("MyPlugin enabled — ${Bukkit.getVersion()}")
    }
 
    // priority 越大越晚执行,可用于排序同阶段的多个方法
    @Awake(LifeCycle.ENABLE, priority = 10)
    fun afterEnable() {
        bukkitPlugin.logger.info("late init done")
    }
 
    @Awake(LifeCycle.DISABLE)
    fun onDisable() {
        bukkitPlugin.logger.info("MyPlugin disabled")
    }
}

bukkitPlugin 是框架在 onLoad 阶段注入的全局 JavaPlugin 引用(见 Blink.kt),随处可用。多阶段示例见 Bootstrap.kt

5.2 加事件监听:@AutoListener

@AutoListener 定义在 AutoListener.kt,方法的第一个参数类型即被监听的事件:

package com.example.myplugin
 
import org.bukkit.event.player.PlayerJoinEvent
import priv.seventeen.artist.blink.event.AutoListener
 
object JoinListener {
 
    @AutoListener
    fun onJoin(event: PlayerJoinEvent) {
        event.joinMessage = "§a+ §e${event.player.name}"
    }
}

@AutoListener 另有 priority(默认 EventPriority.NORMAL)和 ignoreCancelled(默认 false)两个可选参数。事件细节见 事件 @AutoListener

无需 implements Listener,也无需手动 registerEvents:扫描器读到方法签名后会生成对应的注册代码。


6. 构建

编译 → 注解扫描 → 生成入口类与 plugin.yml → 打包,整条链由 Gradle 任务串起。生成任务 blinkGenerate 依赖编译任务,所有 Jar 任务又依赖 blinkGenerate(见 BlinkPlugin.configureCodeGeneration),因此只需运行打包命令:

# Windows
gradlew.bat shadowJar

# 类 Unix
./gradlew shadowJar

产物在 build/libs/ 下。其中 priv.seventeen.artist.blink 已 relocate 到 <packageName>.blink,Kotlin stdlib、Aria、Asteroid 均未打包。

开启 obfuscate = true 时再运行混淆任务:

gradlew.bat obfuscate

构建产物与发布流程见 构建与发布


下一步