技术教程

从零开始学习 TabooLib 插件开发,包含完整的代码示例与实战经验

TabooLib 概述

什么是 TabooLib?TabooLib 是一个基于 Kotlin 的 Minecraft 跨平台服务端插件开发框架,仅占 30+ KB 插件体积,却提供了完整的模块化开发能力。

跨平台支持

一套代码,多端运行

Bukkit/Spigot/Paper
BungeeCord
Velocity
Nukkit

模块化设计

按需加载,减少体积

Basic 配置文件/任务链
Bukkit 事件/命令
NMS 版本兼容
Kether 脚本引擎

开发效率

现代化 API 设计

注解驱动
链式调用
类型安全
Kotlin 协程支持

1. 快速入门

入门

1.1 模块选择与推荐组合

类型模块功能
基础Basic配置文件、任务调度
平台BukkitBukkit 事件、命令
工具BukkitUtil玩家操作、物品构建
协议NMS网络包、版本兼容
脚本Kether脚本引擎
界面BukkitUI箱子菜单

基础插件:Basic + Bukkit

功能插件:Basic + Bukkit + BukkitUtil + NMS

完整配置:Basic + Bukkit + BukkitUtil + NMS + Kether + BukkitUI

入门

1.2 插件主类写法

Kotlin (MyPlugin.kt)
package com.example.myplugin

import taboolib.common.platform.Plugin
import taboolib.platform.BukkitPlugin

object MyPlugin : BukkitPlugin() {

    override fun onEnable() {
        logger.info("插件已启动!")
    }

    override fun onDisable() {
        logger.info("插件已关闭!")
    }
}

为什么用 object?单例模式可以让你在任何地方直接访问插件实例,无需每次调用 getPlugin() 方法。

入门

1.3 plugin.yml 配置

YAML (plugin.yml)
name: MyPlugin
version: ${version}
main: com.example.myplugin.MyPlugin
api-version: "1.20"
author: YourName
description: 我的第一个 TabooLib 插件

taboolib:
  id: myplugin
  loader: kotlin

注意:TabooLib 6.x 需要 taboolib 配置块,否则无法加载!

2. Basic 模块详解

入门

2.1 @Config 注解完整参数

file: String

配置文件名,相对于插件目录。默认从 plugins/插件ID/ 目录下读取。

autoReload: Boolean = false

启用热重载。当文件在服务器运行时被外部修改,会自动重新加载。

migrate: String = ""

配置迁移路径。当插件更新需要迁移旧配置文件时使用。

bindResource: Boolean = true

是否绑定资源文件。如果为 true,文件不存在时会从 jar 包中释放。

Kotlin
// 基础用法
@Config(file = "config.yml")
lateinit var config: SecuredFile
    private set

// 带完整参数
@Config(file = "config.yml", autoReload = true, migrate = "%plugin%/config.yml.old")
lateinit var fullConfig: SecuredFile
    private set

// 读取配置
val name = config.getString("plugin.name")
val version = config.getInt("plugin.version")
val enabled = config.getBoolean("plugin.enabled", true)

// 读取列表
val list = config.getStringList("rewards")

// 写入配置
config.set("plugin.name", "MyPlugin")
config.save()
进阶

2.2 SecuredFile 完整 API 列表

方法返回类型说明
getString(path)String?读取字符串
getInt(path)Int读取整数
getDouble(path)Double读取浮点数
getBoolean(path)Boolean读取布尔值
getStringList(path)List<String>读取字符串列表
getConfigurationSection(path)MemorySection?读取配置节
getColoredString(path)String读取并转换颜色
set(path, value)-设置值
save()-保存到文件
reload()-重新加载配置
入门

2.3 submit 任务调度完整参数

参数类型默认值说明
delayLong0延迟执行时间(tick)
periodLong-重复执行周期(tick)
asyncBooleanfalse是否异步执行
ownerPlugin当前插件任务所属插件
Kotlin
import taboolib.common.platform.function.submit

// 延迟执行(1秒后)
submit(delay = 20) { player.sendMessage("Hello!") }

// 异步执行(不在主线程)
submit(async = true) {
    val result = database.query("SELECT * FROM players")
    submit { player.sendMessage("查询完成: $result") }
}

// 定时重复执行(每秒)
submit(period = 20) { world.players.forEach { it.health = it.maxHealth } }

// 取消任务
val task = submit(period = 20) { }
task.cancel()
踩坑记录:异步线程安全
⚠️ 重要:在异步任务中,绝对不能直接操作 Bukkit 对象!所有 Bukkit API 只能在主线程调用。

3. Bukkit 模块详解

入门

3.1 @SubscribeEvent 完整参数

参数类型默认值说明
priorityEventPriorityNORMAL事件优先级
ignoreCancelledBooleanfalse是否忽略已取消的事件

EventPriority 优先级说明

优先级使用场景
LOWEST拦截、取消事件
NORMAL常规逻辑
HIGHEST最终修改
MONITOR统计、数据记录(不应修改事件)
Kotlin
import taboolib.common.platform.SubscribeEvent
import org.bukkit.event.EventPriority

// 基本用法
@SubscribeEvent
fun onJoin(event: PlayerJoinEvent) {
    val player = event.player
    player.sendMessage("欢迎来到服务器!")
}

// 指定优先级
@SubscribeEvent(priority = EventPriority.HIGHEST)
fun onHighJoin(event: PlayerJoinEvent) { }

// 忽略已取消的事件
@SubscribeEvent(ignoreCancelled = true)
fun onMove(event: PlayerMoveEvent) { }

// 组合使用
@SubscribeEvent(priority = EventPriority.HIGH, ignoreCancelled = true)
fun onPlayerCommand(event: PlayerCommandPreprocessEvent) { }
进阶

3.2 命令系统完整语法

方法说明
command(name)创建主命令
literal(name)创建字面子命令
dynamic(name)创建动态参数
execute<T>注册执行回调
suggestion<T>注册补全建议
permission设置权限要求
optional()标记为可选参数
Kotlin
import taboolib.common.platform.command.*

command("heal", permission = "myplugin.heal") {
    execute<Player> { player, _, _ ->
        player.health = player.maxHealth
        player.sendMessage("§a已恢复生命值!")
    }
}

command("teleport", permission = "myplugin.teleport") {
    literal("random") {
        execute<Player> { player, _, _ ->
            val randomPlayer = player.world.players.randomOrNull()
            if (randomPlayer != null) {
                player.teleport(randomPlayer.location)
            }
        }
    }
    
    dynamic("target", optional = true) {
        suggestion<Player> { _, _ -> server.onlinePlayers().map { it.name } }
        execute<Player> { player, _, args ->
            val target = server.getPlayer(args["target"]!!)
            if (target != null) player.teleport(target.location)
        }
    }
}

4. BukkitUtil 模块

入门

4.1 ItemBuilder 物品构建器完整 API

方法参数说明
name(text)String设置物品名称
lore(lines)String...设置描述文本
shiny()-添加发光效果
unbreakable()-设置不可破坏
nbt(key, value)String, Any添加 NBT 数据
enchant(enchant, level)Enchantment, Int添加附魔
skullOwner(player)ProxyPlayer/String设置头颅所有者
editMeta { }ItemMeta.() -> Unit直接编辑元数据
build()-构建物品
Kotlin
import taboolib.platform.util.*
import taboolib.platform.util.ItemBuilder
import org.bukkit.Material
import org.bukkit.enchantments.Enchantment

// 创建物品
val item = ItemBuilder(Material.DIAMOND_SWORD)
    .name("§b钻石剑")
    .lore("§7一把锋利的钻石剑", "§7攻击力: 10")
    .shiny()
    .nbt("custom", "value")
    .enchant(Enchantment.DAMAGE_ALL, 5)
    .build()

// 玩家头颅
val skull = ItemBuilder(Material.PLAYER_HEAD)
    .name("§e${player.name}")
    .skullOwner(player)
    .build()

// 玩家扩展函数
player.hasItem(Material.DIAMOND, 10)  // 检查物品
player.giveItem(ItemStack(Material.DIAMOND))  // 给予物品
进阶

4.2 聊天组件 HoverEvent / ClickEvent

Kotlin
import taboolib.module.chat.Component
import taboolib.module.chat.HoverEvent
import taboolib.module.chat.ClickEvent
import taboolib.module.chat.hoverAction
import taboolib.module.chat.clickAction

// 悬停显示文本
val component = Component.create("§6[点我打开链接]")
    .hover(hoverAction { showText("§e点击打开 GitHub") })
    .click(clickAction { openUrl("https://github.com") })

// 点击执行命令
val cmdComponent = Component.text("§e[传送]")
    .hover(hoverAction { showText("§7传送到主城") })
    .click(clickAction { runCommand("/spawn") })

// 建议补全
val suggestComponent = Component.text("§a[私聊]")
    .click(clickAction { suggestCommand("/msg ${player.name} ") })

5. NMS 模块

⚠️ NMS 警告:NMS(Net Minecraft Server)是版本相关的代码。不同服务端版本的 NMS 类路径和方法签名完全不同。

进阶

5.1 版本检测与兼容 API

Kotlin
import taboolib.library.nms.NMSRegistry

// 获取完整版本字符串
val version = NMSRegistry.getServerVersion()  // 如 "v1_20_R3"

// 获取主版本号
val majorVersion = NMSRegistry.getMajorVersion()  // 如 20

// 版本范围判断
when {
    majorVersion <= 8 -> { /* 1.8.x */ }
    majorVersion in 9..12 -> { /* 1.9 - 1.12 */ }
    majorVersion in 13..16 -> { /* 1.13 - 1.16 */ }
    majorVersion in 17..20 -> { /* 1.17 - 1.20 */ }
    majorVersion >= 21 -> { /* 1.21+ */ }
}

// 动态加载 NMS 类
val nmsClass = NMSRegistry.getNMSClass("PacketPlayOutChat")
进阶

5.2 ItemTag NBT 数据操作完整 API

方法类型说明
ItemTagData.ofString(value)String字符串类型
ItemTagData.ofInt(value)Int整数类型
ItemTagData.ofDouble(value)Double浮点数类型
ItemTagData.ofBoolean(value)Boolean布尔类型
ItemTagData.ofLong(value)Long长整数类型
ItemTagData.ofList(list)List列表类型
ItemTagData.ofCompound(tag)ItemTag复合标签
Kotlin
import taboolib.module.nms.ItemTag
import taboolib.module.nms.ItemTagData
import taboolib.module.nms.toNBT
import taboolib.module.nms.toItemTag

// 创建 NBT
val tag = ItemTag()
tag["customKey"] = ItemTagData.ofString("value")
tag["number"] = ItemTagData.ofInt(123)
tag["enabled"] = ItemTagData.ofBoolean(true)

// 应用到物品
val itemWithNBT = tag.toItemTag(itemStack)

// 读取物品的 NBT
val readTag = itemWithNBT.toNBT()
val customValue = readTag["customKey"]?.asString()
val intValue = readTag["number"]?.asInt()
踩坑记录:数据类型必须匹配
读取 NBT 时,必须使用正确的数据类型ofString() 保存的值必须用 .asString() 读取。

6. Kether 脚本引擎

Kether 简介:Kether 是一个以单行语句驱动的脚本语言,适合配置文件中的动作定义和 UI 菜单的点击事件。

进阶

6.1 内置动作完整列表

动作语法说明
telltell "消息"发送消息
castcast "类型"类型转换
cmd / commandcmd "/命令"执行命令
setset{ "key" to value }设置变量
getget{ "key" }获取变量
if / unlessif { } then { }条件判断
foreachforeach [list] { }循环遍历
looploop 10 { }循环执行
delaydelay 20 { }延迟执行
asyncasync { }异步执行
Kether
// 变量
set{ "name" to "苏屿" }
set{ "level" to 100 }

// 发送消息
tell "Hello World!"
tell color "&a绿色消息"
tell color inline "&bHello {{ player }}"

// 执行命令
cmd "/spawn" as console
cmd "/eco give {{ player }} 1000" as player

// 条件判断
if{ "%player_level%" >= 50 } then {
    tell "你达到了50级!"
} else {
    tell "继续加油!"
}

// 循环
foreach [1, 2, 3, 4, 5] {
    tell "%loop_value%"
}

// 延迟
delay 20 {
    tell "延迟后的消息"
}
高级

6.2 自定义 Kether 动作注册

Kotlin
import taboolib.module.kether.*
import taboolib.common.platform.ProxyPlayer

// 注册自定义动作
@KetherProperty("myplugin")
object MyActions {
    
    @KetherShell(shared = true)
    val heal = KetherAction.ofScript { script ->
        script.frame.newFrame {
            script.executor().thenAccept { value ->
                val amount = (value as? Number)?.toDouble() ?: 20.0
                val player = script.sender?.castSafely<ProxyPlayer>()
                player?.health = amount
            }
        }
    }
}

// 代码中调用 Kether
fun executeKetherScript(player: ProxyPlayer, script: String) {
    KetherShell.eval(script, sender = player).thenAccept { result ->
        println("Script result: $result")
    }
}

相关资源