You can componentize and colorize any message
textOf("&bHello world!")TextComponent(*fromLegacyText(ChatColor.translateAlternateColorCodes("&", "&bHello world")))Let's send a message to a player (any Player, ProxiedPlayer, CommandSender is supported)
player.msg("&bHello world!")player.sendMessage(TextComponent(*fromLegacyText(ChatColor.translateAlternateColorCodes("&", "&bHello world"))))You can use listen() to easily listen to events
listen<PlayerJoinEvent>{
it.player.msg("Hello world!")
}server.pluginManager.registerEvent(object: Listener{
@EventHandler
fun onPlayerJoin(e: PlayerJoinEvent){
e.player.sendMessage("Hello world!")
}
}, this)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")
}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()
}
You can easily access a file or a subfolder with the get() operator applied to a File
val config = dataFolder["config.yml"]
val lang = dataFolder["langs"]["en_US.yml"]val config = File(dataFolder, "config.yml")
val lang = File(File(dataFolder, "langs"), "en_US.yml")If you want to specify the platform of any type, just add "Bungee" or "Bukkit" before its name
class MyPluginOnBungee: BungeePlugin(){
override fun onEnable() = info("Hello Bungee!")
}
class MyPluginOnBukkit: BukkitPlugin(){
override fun onEnable() = info("Hello Bukkit!")
}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.ymlYou 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"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 = falseor 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.friendsclass 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
}
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
info("Hello world!")logger.info("Hello world!")getLogger().info("Hello world!");// 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")
}// 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
}You can check for updates of your plugin using Spiget
Just use update() with your Spigot resource ID
update(15938)You can specify the color
update(15938, LIGHT_PURPLE)and the permission (the default permission is "rhaz.update")
update(15938, LIGHT_PURPLE, "myplugin.updates")Exceptions can be catched with a beautiful syntax
// 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")
}try{
throw Exception("An error occured")
} catch(ex: Exception){
warning(ex)
}// The callback can be ommited, the default one is ::printStackTrace
catch<CommandException>{
throw Exception("This won't be catched")
}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 :)")
}// 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(...)
}class RedException(message: String): Exception(){
override val message = "&c$message"
}
fun test(){
catch<RedException>(::warning){
throw RedException("This message will be red")
}
}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")You can use .not() to check inequality of any object
"object.not(other)":
- returns null if object == other
- returns object if object != other
val delay = config.getLong("delay").not(0) // Assignment + Check
?: return warning("Delay should not be 0")val delay = config.getLong("delay") // Assignment
if(delay == 0) return warning("Delay should not be 0") // Check