-
Notifications
You must be signed in to change notification settings - Fork 0
Local Addon Guide
KSL addons allow you to extend the scripting engine with custom services, context extensions, and default imports. This is perfect when you want to expose functionality from your Java/Kotlin plugin to KSL scripts without modifying the core plugin.
-
Register Services: Expose objects that scripts can access via
service<MyService>("key") -
Extend Context: Add custom methods/properties to
BukkitScriptContextthat all scripts can use - Add Default Imports: Automatically import packages in every script
- Hook into Lifecycle: React when scripts are loaded/unloaded
Add KSL as a dependency in your build.gradle.kts:
dependencies {
compileOnly("ru.privateserver:ksl:1.0.0") // Replace with actual version/artifact
}Or if you're using Maven:
<dependency>
<groupId>ru.privateserver</groupId>
<artifactId>ksl</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>Important: Mark KSL as softdepend in your plugin.yml:
name: MyAwesomeAddon
version: 1.0.0
main: com.example.MyAwesomeAddon
softdepend: [KotlinScriptLoader]Create a class that implements the KSLAddon interface:
package com.example
import ru.privateserver.ksl.KSLAddon
import ru.privateserver.ksl.KSLAPI
class MyAddon : KSLAddon {
override val addonId = "MyAwesomeAddon"
override val addonVersion = "1.0.0"
override val addonDescription = "Adds custom weather service to KSL"
override fun onLoad(api: KSLAPI) {
// Register your services here
}
override fun onUnload() {
// Cleanup when addon is unloaded
}
}In your main plugin class, register the addon when KSL is available:
import org.bukkit.plugin.java.JavaPlugin
import ru.privateserver.ksl.KSL
class MyAwesomeAddon : JavaPlugin() {
private val addon = MyAddon()
override fun onEnable() {
if (KSL.isAvailable) {
KSL.api.registerAddon(addon)
logger.info("Registered with KSL!")
} else {
logger.warning("KSL not found - addon will not work")
}
}
override fun onDisable() {
if (KSL.isAvailable) {
KSL.api.unregisterAddon(addon.addonId)
}
}
}Services are objects that scripts can access using the service<T>(key) function:
class WeatherService {
fun isRaining(world: String): Boolean {
// Your logic here
return false
}
fun setWeather(world: String, weather: String) {
// Your logic here
}
}
class MyAddon : KSLAddon {
override val addonId = "MyAwesomeAddon"
override val addonVersion = "1.0.0"
override val addonDescription = "Weather service for KSL"
private val weatherService = WeatherService()
override fun onLoad(api: KSLAPI) {
// Register the service
api.registerService("weather", weatherService)
api.kslPlugin.logger.info("Weather service registered!")
}
override fun onUnload() {
// Service will be automatically unregistered
}
}Usage in scripts:
val weather = service<WeatherService>("weather")
if (weather?.isRaining("world") == true) {
broadcastMM("<blue>It's raining!")
}Want to add custom methods that all scripts can use directly? Implement KSLContextExtension:
import ru.privateserver.ksl.KSLContextExtension
import ru.privateserver.ksl.BukkitScriptContext
import org.bukkit.entity.Player
class MyContextExtension : KSLContextExtension {
override val extensionId = "MyAddonExtension"
override fun onContextCreated(context: BukkitScriptContext) {
// Add extension functions to the context
context.apply {
// Add a custom method accessible in all scripts
fun Player.isVIP(): Boolean {
return this.hasPermission("myaddon.vip")
}
fun getAddonData(key: String): String? {
// Your custom logic
return "value"
}
}
}
override fun onContextDestroyed(scriptName: String) {
// Cleanup when script is unloaded
}
}Register it in your addon:
class MyAddon : KSLAddon {
override val addonId = "MyAwesomeAddon"
override val addonVersion = "1.0.0"
override val addonDescription = "Custom context extensions"
override fun onLoad(api: KSLAPI) {
val extension = MyContextExtension()
api.registerContextExtension(extension)
}
override fun onUnload() {
// Extension will be automatically unregistered
}
}Usage in scripts:
onEvent<PlayerJoinEvent> {
if (player.isVIP()) {
player.sendRichMessage("<gold>Welcome VIP!")
}
val data = getAddonData("some_key")
player.sendMessage("Data: $data")
}Make certain packages available in all scripts automatically:
override fun onLoad(api: KSLAPI) {
// Add imports that will be available in all scripts
api.addDefaultImports(
"com.example.mypackage.*",
"com.example.utils.*",
"kotlinx.coroutines.*"
)
}Now scripts can use these classes without explicit imports.
Here's a full working addon that combines services and context extensions:
package com.example
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin
import ru.privateserver.ksl.*
// Service class
class EconomyService {
private val balances = mutableMapOf<String, Double>()
fun getBalance(player: Player): Double =
balances[player.uniqueId.toString()] ?: 0.0
fun setBalance(player: Player, amount: Double) {
balances[player.uniqueId.toString()] = amount
}
fun addMoney(player: Player, amount: Double) {
val current = getBalance(player)
setBalance(player, current + amount)
}
}
// Context extension
class EconomyContextExtension : KSLContextExtension {
override val extensionId = "EconomyExtension"
override fun onContextCreated(context: BukkitScriptContext) {
context.apply {
// Extension property
var Player.customBalance: Double
get() = service<EconomyService>("economy")?.getBalance(this) ?: 0.0
set(value) {
service<EconomyService>("economy")?.setBalance(this, value)
}
// Extension function
fun Player.giveMoney(amount: Double) {
service<EconomyService>("economy")?.addMoney(this, amount)
sendRichMessage("<green>+$${amount}")
}
}
}
override fun onContextDestroyed(scriptName: String) {
// Cleanup if needed
}
}
// Main addon class
class EconomyAddon : KSLAddon {
override val addonId = "EconomyAddon"
override val addonVersion = "1.0.0"
override val addonDescription = "Custom economy system for KSL scripts"
private val economyService = EconomyService()
override fun onLoad(api: KSLAPI) {
// Register service
api.registerService("economy", economyService)
// Register context extension
api.registerContextExtension(EconomyContextExtension())
// Add default imports
api.addDefaultImports("com.example.economy.*")
api.kslPlugin.logger.info("Economy addon loaded!")
}
override fun onUnload() {
api.kslPlugin.logger.info("Economy addon unloaded!")
}
}
// Plugin main class
class MyEconomyPlugin : JavaPlugin() {
private val addon = EconomyAddon()
override fun onEnable() {
if (KSL.isAvailable) {
KSL.api.registerAddon(addon)
}
}
override fun onDisable() {
if (KSL.isAvailable) {
KSL.api.unregisterAddon(addon.addonId)
}
}
}Script usage:
onEvent<PlayerJoinEvent> {
player.customBalance = 100.0
player.sendRichMessage("<yellow>Your balance: <green>$${player.customBalance}")
}
registerCommand("pay") { player, args ->
val target = Bukkit.getPlayer(args[0])
val amount = args[1].toDouble()
target?.giveMoney(amount)
player.sendRichMessage("<green>Paid $${amount} to ${target?.name}")
}-
Always check
KSL.isAvailablebefore accessing the API - Use unique keys for services to avoid conflicts
-
Clean up resources in
onUnload()andonContextDestroyed() - Document your services so script writers know how to use them
- Handle nulls gracefully - services might not be registered
Check if your addon is loaded:
/ksl addons
View registered services:
/ksl services
- Check out the API Reference for all available methods
- See Examples for real-world addon implementations
- Learn about Sandbox & Security to understand how addons interact with script restrictions
Happy addon development! π