Help developers build Minecraft plugins using the TabooLib 6.x framework (Kotlin/Java). Use when a user mentions "taboolib", "TabooLib plugin", "minecraft plugin with taboolib", "bukkit plugin framework", "create taboolib project", "taboolib command", "taboolib config", "taboolib UI", "taboolib database", "taboolib NMS", "taboolib kether", or asks about TabooLib module setup, @Awake lifecycle, or cross-version compatibility. Covers project setup, module selection, API usage, and troubleshooting.
TabooLib is a Kotlin-based cross-platform Minecraft plugin framework (supports Bukkit/Spigot/Paper, BungeeCord, Velocity). Current stable version: 6.1.2-beta12, Gradle plugin: 2.0.31.
Key traits:
@Awake, @SubscribeEvent, @Schedule, @Config)When user wants to create a new TabooLib plugin:
build.gradle.kts with the TabooLib Gradle plugin:import io.izzel.taboolib.gradle.*
plugins {
java
id("io.izzel.taboolib") version "2.0.31"
kotlin("jvm") version "2.0.0"
}
taboolib {
env {
install(UNIVERSAL, BUKKIT)
// Add modules as needed: CONFIGURATION, LANG, CHAT, NMS, NMS_UTIL, UI, DATABASE, KETHER
}
version { taboolib = "6.1.2-beta12" }
}
repositories {
mavenCentral()
}
dependencies {
compileOnly("ink.ptms.core:v12004:12004:mapped")
compileOnly("ink.ptms.core:v12004:12004:universal")
compileOnly(kotlin("stdlib"))
taboo("ink.ptms:um:1.2.0") // Universal Minecraft API
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
object, NOT class):import taboolib.common.platform.Plugin
object MyPlugin : Plugin() {
override fun onLoad() { }
override fun onEnable() { info("Plugin enabled!") }
override fun onActive() { } // Called after server fully started
override fun onDisable() { }
}
plugin.yml needed — TabooLib auto-generates it.Help user pick the right modules based on their needs:
| Need | Module Constant | Notes |
|------|----------------|-------|
| Config files (YAML/TOML/HOCON) | CONFIGURATION | Enables @Config, @ConfigNode |
| Multi-language / i18n | LANG | Requires CONFIGURATION |
| Rich text / clickable messages | CHAT | Components API |
| NMS / packet operations | NMS, NMS_UTIL | Cross-version abstraction |
| Chest GUI / Anvil GUI | UI | Requires CHAT + NMS |
| Database (MySQL/SQLite) | DATABASE | HikariCP connection pool |
| Kether scripting | KETHER | Requires CONFIGURATION |
| Bukkit utilities (ItemBuilder etc) | BUKKIT_ALL | XMaterial, ItemBuilder |
| PlaceholderAPI / Vault hooks | BUKKIT_HOOK | Third-party integration |
| BungeeCord messaging | PORTICUS | Cross-server communication |
Module dependencies (auto-resolved by Gradle plugin, but useful to know):
UI depends on CHAT + NMSLANG depends on CONFIGURATIONNMS_UTIL depends on NMSAlways follow these patterns when writing TabooLib code:
Pattern: Singleton Object + @Awake
@Awake
object PlayerManager {
private val cache = ConcurrentHashMap<UUID, PlayerData>()
@Awake(LifeCycle.ENABLE)
fun init() { /* startup logic */ }
@SubscribeEvent
fun onQuit(event: PlayerQuitEvent) {
cache.remove(event.player.uniqueId)
}
@Schedule(async = true, period = 6000L)
fun autoSave() {
cache.values.forEach { it.save() }
}
}
Pattern: Config Injection
@Awake
object Settings {
@Config("config.yml", autoReload = true)
lateinit var config: Configuration
@ConfigNode("debug")
var debug = false
@ConfigNode("settings.max-players")
var maxPlayers = 100
}
Pattern: Command DSL
command("myplugin", aliases = listOf("mp"), permission = "myplugin.use") {
literal("reload") {
execute<ProxyCommandSender> { sender, _, _ ->
Settings.config.reload()
sender.sendMessage("Reloaded!")
}
}
dynamic("player") {
suggestion<ProxyCommandSender> { _, _ ->
Bukkit.getOnlinePlayers().map { it.name }
}
execute<ProxyCommandSender> { sender, _, argument ->
sender.sendMessage("Target: $argument")
}
}
}
Pattern: Event Listening
// Annotation-based (recommended)
@Awake
object Listeners {
@SubscribeEvent
fun onJoin(event: PlayerJoinEvent) {
event.player.sendMessage("Welcome!")
}
@SubscribeEvent(priority = EventPriority.HIGHEST, ignoreCancelled = true)
fun onDamage(event: EntityDamageEvent) { /* ... */ }
}
// Functional style
listenEvent<PlayerJoinEvent> {
it.player.sendMessage("Welcome!")
}
Pattern: Chest UI
player.openMenu<Chest> {
title("My Menu")
rows(3)
map(
"# # # # # # # # #",
"# A # B # C # D #",
"# # # # # # # # #"
)
set('A', buildItem(XMaterial.DIAMOND) { name = "&bDiamond"; colored() })
onClick('A') { event -> event.clicker.sendMessage("Clicked!") }
handLocked(false)
}
Pattern: Database Table
val table = Table<HostSQL, Connection>("player_data", host) {
add("uuid") { type(ColumnTypeSQL.VARCHAR, 36) }
add("data") { type(ColumnTypeSQL.TEXT) }
}
// Always use async for DB operations
submitAsync {
table.createTable(dataSource)
table.select(dataSource) {
where { "uuid" eq player.uniqueId.toString() }
}.firstOrNull()
}
Pattern: Task Scheduling
// Delayed sync task (20 ticks = 1 second)
submit(delay = 20L) { /* runs on main thread */ }
// Async repeating task
submit(async = true, period = 20L) { /* every 1 second, off main thread */ }
// Annotation-based
@Schedule(async = true, period = 100L, delay = 20L)
fun periodicTask() { /* called automatically */ }
Pattern: NMS Version Guard
if (MinecraftVersion.isHigherOrEqual(MinecraftVersion.V1_17)) {
// 1.17+ specific code
}
// majorLegacy: 12004 = 1.20.4
val ver = MinecraftVersion.majorLegacy
Before the plugin is considered complete, verify:
build.gradle.kts has correct taboolib {} block with needed modulesobject extending Plugin(), not a class@Awake object, not global variablessubmitAsync {}@Config + @ConfigNode for auto-injectionUser says: "Create a TabooLib plugin with a config and a reload command"
Action:
build.gradle.kts with UNIVERSAL, BUKKIT, CONFIGURATIONPlugin object main classSettings object with @Configreload subcommandconfig.ymlUser says: "I need a shop GUI plugin with TabooLib"
Action:
UNIVERSAL, BUKKIT, CONFIGURATION, CHAT, NMS, NMS_UTIL, UI, BUKKIT_ALLmap() layoutUser says: "Player data storage plugin with MySQL"
Action:
UNIVERSAL, BUKKIT, CONFIGURATION, DATABASETable API@SubscribeEvent handlerssubmitAsync {} for all DB operationsconfig.yml with database connection settingstaboolib { env { install(...) } }references/module-map.md for dependencies.@Awake annotation on the object, or object is in a package not scanned@Awake is on the object declaration. TabooLib scans all classes in the plugin JAR.MinecraftVersion.isHigherOrEqual() guards. For packet operations, check field names per version.autoReload = true not set, or FileWatcher not activeautoReload = true in @Config. For manual reload call config.reload().handLocked(false) or wrong slot mappingmap() character assignments match set() and onClick() characters.io.izzel.taboolib plugin version matches, and mavenCentral() is in repositories.Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer