SpiKotlin – Kotlin Library for SpigotAPI

Usage

Simplified componentization

You can componentize and colorize any message

With SpiKotlin
textOf("&bHello world!")
Without SpiKotlin
TextComponent(*fromLegacyText(ChatColor.translateAlternateColorCodes("&", "&bHello world")))

Simplified messages sending

Let’s send a message to a player (any Player, ProxiedPlayer, CommandSender is supported)

With SpiKotlin
player.msg("&bHello world!")
Without SpiKotlin (not deprecated)
player.sendMessage(TextComponent(*fromLegacyText(ChatColor.translateAlternateColorCodes("&", "&bHello world"))))

Simplified event listening

You can use listen() to easily listen to events

With SpiKotlin

listen<PlayerJoinEvent>{
  it.player.msg("Hello world!")
}
Without SpiKotlin

server.pluginManager.registerEvent(object: Listener{
  @EventHandler
  fun onPlayerJoin(e: PlayerJoinEvent){
      e.player.sendMessage("Hello world!")
  }
}, this)
In Java

getServer().getPluginManager().registerEvents(new Listener() {
  @EventHandler
  public void onPlayerJoin(PlayerJoinEvent e){
      e.getPlayer().sendMessage("Hello world!");
  }
}, this);

You can also use priorities

listen<PlayerJoinEvent>(HIGHEST){
  it.player.msg("This will be the first")
}

Simplified scheduling

schedule(delay = 10){
    // this will be executed after 10 seconds
}

schedule(delay = 10, period = 20){
    // this will be executed after 10 seconds then each 20 seconds
}

schedule(async = true){
    // this will be executed asynchronously now
}

schedule(true, delay = 20){
    // this will be executed asynchronously after 20 seconds
}

schedule(true, period = 3, unit = TimeUnit.MINUTES){
    // this will be executed asynchronously each 3 minutes
}

// Tasks can cancel themselves
schedule(period = ...) {
    if(...) cancel()
}

Fast access to files of a directory

You can easily access a file or a subfolder with the get() operator applied to a File

With SpiKotlin

val config = dataFolder["config.yml"]
val lang = dataFolder["langs"]["en_US.yml"]
Without SpiKotlin

val config = File(dataFolder, "config.yml")
val lang = File(File(dataFolder, "langs"), "en_US.yml")

Simple configuration loading

Load the config from the data folder, otherwise, copy it from the resource “config.yml”.

If there is an error, do not continue.

val configFile = dataFolder["config.yml"]
val config = loadConfig(configFile, "configs/config.yml")
// Will load resource configs/config.yml

You can ommit the resource argument, it will copy the resource of the same name as the file

val configFile = dataFolder["config.yml"]
val config = loadConfig(configFile) // Will load resource "config.yml"

Delegated configuration

You can use delegated configuration to manage big configurations

class MyPlugin: BukkitPlugin(){

    object MyConfig: ConfigFile("config"){
        var debug by boolean("debug")
        var alertMessage by string("alert-message")
        var enabledWorlds by stringList("enabled-worlds")
    }

    override fun onEnable(){
        // Initialize the object with the current file
        init(MyConfig) // (it will copy the resource config.yml to the data folder)
        // We can access properties dynamically
        info("debug: " + MyConfig.debug)
        // Change them (if "var" is used)
        MyConfig.debug = false
        // Save them (if auto-saving is disabled)
        MyConfig.save()
        // Reload the config from the file
        MyConfig.reload()
    }
}

You have to init the configuration only if you’re using a resource

ConfigFile(file: File)
// This will load the config from the file
// No init() required

ConfigFile(path: String)
// This will copy the resource located at path to a file of the same path in the data folder
// Needs to be initialized with init(plugin) or plugin.init(config)
// You can ommit the .yml in the path
// You can specify the resource to copy with init(plugin, resourcePath)

You can pass multiple ConfigFile to the init() like

init(Options, Players, Worlds, ...)

By default, the configuration is saved every time a property is changed

You can disable auto-saving by doing

MyConfig.autoSave = false

or changing the constructor

object MyConfig: ConfigFile("config", false){
    ...
}

You can also use sections

object MySection: ConfigSection(MyConfig, "mysection"){
    val host by string("host")
    val port by int("port")
}

And parameters

inner class PlayerInfo(uuid: UUID): ConfigFile(dataFolder["$uuid.yml"]){
    val friends by stringList("friends")
    // ...
}

val Player.info get() = PlayerInfo(uniqueId)
val Player.friends get() = info.friends

class SocketConfig(id: String): ConfigSection(MyConfig, "sockets.$id"){
    val host by string("host")
    val port by int("port")
}

fun address(id: String){
    val config = SocketConfig(id)
    return config.host + ":" + config.port
}

Fast logging

You can use info(), warning() and severe() with String or Exception to log them in the console

You can also use logToFile() to write to a file named “log.txt” in your plugin’s data folder

With SpiKotlin
info("Hello world!")
Without SpiKotlin
logger.info("Hello world!")
In Java
getLogger().info("Hello world!");

Simplified commands

With SpiKotlin

// Example on Bukkit
command("hello"){ sender, args ->
    if(args.isEmpty())
        sender.msg("&cWrong arguments, usage: $usage")
    else sender.msg("&bHello!")
}

// You can apply the executor directly to the sender
command("test") { 
    args -> msg("&bYou said $args") 
}
Without SpiKotlin

// Bungee
proxy.pluginManager.registerCommand(this,
    object: Command("hello"){
        override fun execute(sender: CommandSender, args: Array<String>){
            sender.sendMessage("§bHello!")
        }
    }
)

// Bukkit
getCommand("hello").executor = CommandExecutor {
    sender, command, label, args ->
    sender.sendMessage("§bHello!")
    true
}

Advanced exception catching

Exceptions can be catched with a beautiful syntax

With SpiKotlin

// This will catch any exception and log it as a warning
catch<Exception>(::warning){
    // ex() is a short replacement of Exception()
    throw ex("An error occured")
}
Without SpiKotlin

try{
    throw Exception("An error occured")
} catch(ex: Exception){
    warning(ex)
}
Catch only specific exceptions

// The callback can be ommited, the default one is ::printStackTrace
catch<CommandException>{
    throw Exception("This won't be catched")
}
Catch and redirect to the sender

val sender: CommandSender = ...
catch<Exception>(sender::msg){
    if(sender !is Player)
        throw ex("&cYou're not a player!")
    sender.gamemode = GameMode.CREATIVE
    sender.msg("&bYou're now in creative mode :)")
}
Custom callbacks

// Tell the admins about the exception
val admins = server.onlinePlayers.filter{it.hasPermission("test.admin")}
fun tellToAdmins(ex: Exception) = admins.forEach{it.msg(ex)}

catch<Exception>(::tellToAdmins){
    throw ex("Alert!")
}


// Anonymous callback that prepend the warning with "An error occured"
catch<Exception>({ warning("An error occured: ${it.message}")}){
    throw ex(...)
}
Custom exceptions

class RedException(message: String): Exception(){
    override val message = "&c$message"
}

fun test(){
    catch<RedException>(::warning){
        throw RedException("This message will be red")
    }    
}
Catching with result, callback and default value

fun default(ex: Exception) =
    {warning(ex); "This is the default message"}()

val msg = catch(::default){
    val line1 = read() ?: throw ex("Could not read first line")
    val line2 = read() ?: throw ex("Could not read second line")
    val line3 = read() ?: throw ex("Could not read third line")
    info("Sucessfully read three lines")
    "$line1, $line2, $line3"
    // Return the three lines separated by "," 
}

// Will print "This is the default message"
// if one of the three lines could not be read
info("The message is: $msg")

Short inequality checks

You can use .not() to check inequality of any object

“object.not(other)”:

  • returns null if object == other
  • returns object if object != other
With SpiKotlin

val delay = config.getLong("delay").not(0) // Assignment + Check
    ?: return warning("Delay should not be 0")
Without SpiKotlin

val delay = config.getLong("delay")  // Assignment
if(delay == 0) return warning("Delay should not be 0") // Check

License

SpiKotlin is referenced or quoted from KUtils made by hazae41. KUtils License: LICENSE

GitHub

View Github