General update
This commit is contained in:
165
readme.md
Normal file
165
readme.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# KSpigot
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
KSpigot is a kotlin extension for the popular [spigot server software](https://spigotmc.org/) for minecraft.
|
||||||
|
KSpigot adds functionality missing in spigot and partly makes it possible to do it the kotlin way. Most of KSpigot's extensions are stable.
|
||||||
|
|
||||||
|
IMPORTANT:
|
||||||
|
Extensions marked with NMS annotations like `@NMS_GENERAL` or `@NMS_"VERSIONSTRING"` are unstable.
|
||||||
|
|
||||||
|
Extensions marked with the `@UnsafeImplementaion` annotation do not promise to always give the correct result, but are still useful and therefore included in the project. This readme DOES NOT contain any unsafe parts of KSpigot.
|
||||||
|
|
||||||
|
## First of all
|
||||||
|
|
||||||
|
**Create an instance of KSpigot (the constructor requires your plugin instance)** <br>
|
||||||
|
Do NOT create multiple instances!
|
||||||
|
```kotlin
|
||||||
|
val kSpigot = KSpigot(plugin)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Inside of your `onDisable()` method, call:**
|
||||||
|
```kotlin
|
||||||
|
kSpigot.shutdown()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Simple runnables and schedulers:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
bukkitAsync(kSpigot) { /* short form for async schedulers */ }
|
||||||
|
```
|
||||||
|
```kotlin
|
||||||
|
bukkitSync(kSpigot) { /* sync some code to bukkits main thread */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
bukkitRunnable(
|
||||||
|
kSpigot,
|
||||||
|
sync = false,
|
||||||
|
howoften = 5,
|
||||||
|
delay = 25,
|
||||||
|
period = 20
|
||||||
|
) { /* runnable code */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Powerful builders
|
||||||
|
|
||||||
|
#### For items
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val wand = KSpigotItems.buildItem(Material.GOLD_BLOCK) {
|
||||||
|
|
||||||
|
amount = 3
|
||||||
|
|
||||||
|
addEnchantment(Enchantment.KNOCKBACK, 2)
|
||||||
|
|
||||||
|
itemMeta {
|
||||||
|
|
||||||
|
displayName = "${ChatColor.GOLD}Magic wand"
|
||||||
|
unbreakable = true
|
||||||
|
|
||||||
|
addLore {
|
||||||
|
+ "This wand is truly special."
|
||||||
|
+ "Try it!"
|
||||||
|
}
|
||||||
|
|
||||||
|
customModelData = 1001
|
||||||
|
|
||||||
|
flag(ItemFlag.HIDE_UNBREAKABLE)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### For complex chat components
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
KSpigotChat.buildComponent {
|
||||||
|
|
||||||
|
text {
|
||||||
|
text = "You got a friend request! "
|
||||||
|
color = ChatColor.of("#4FEA40")
|
||||||
|
bold = true
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
text = "[Accept]"
|
||||||
|
color = ChatColor.WHITE
|
||||||
|
clickEvent = ClickEvent(ClickEvent.Action.RUN_COMMAND, "friend accept Foo")
|
||||||
|
hoverEventText {
|
||||||
|
text { text = "Click here to accept the friend request!"; color = ChatColor.RED }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
You can also access the builder by calling
|
||||||
|
```kotlin
|
||||||
|
commandSender.sendMessage { /* same in here as above */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### And more
|
||||||
|
|
||||||
|
A lot of additional things are also suitable for builders. Just like fireworks etc...
|
||||||
|
|
||||||
|
### NBTData support
|
||||||
|
Typesafe and consistent
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// load nbt
|
||||||
|
val nbt = entity.nbtData
|
||||||
|
|
||||||
|
// retrieve data via keys
|
||||||
|
val health = nbt["hearts", NBTDataType.INT]
|
||||||
|
|
||||||
|
// set data for a given key
|
||||||
|
nbt["custom", NBTDataType.DOUBLE] = 3.3
|
||||||
|
|
||||||
|
// save data to the entity
|
||||||
|
entity.nbtData = nbt
|
||||||
|
|
||||||
|
// serialization support
|
||||||
|
val serializedString = nbt.serialize()
|
||||||
|
val deserializeMethod1 = NBTData(serializedString)
|
||||||
|
val deserializeMethod2 = NBTData.deserialize(serializedString)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Simple extension methods / values (with kotlin getters)
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
entity.isGroundSolid
|
||||||
|
entity.isInWater
|
||||||
|
|
||||||
|
vector.isFinite
|
||||||
|
|
||||||
|
playerInteractEntityEvent.interactItem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Direction API
|
||||||
|
Handles the hassle of struggling with direction angles for you.
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val cardinal = CardinalDirection.fromLocation(loc) // NORTH, EAST, SOUTH, WEST
|
||||||
|
val vertical = VerticalDirection.fromLocation(loc) // UP, DOWN, STRAIGHT
|
||||||
|
|
||||||
|
// convert to BlockFace
|
||||||
|
val blockFace = cardinal.facing
|
||||||
|
```
|
||||||
|
|
||||||
|
### CustomItemIdentifiers
|
||||||
|
You want to mess with resourcepacks and extend your possibilities? <br>
|
||||||
|
Spigot is lacking a representation of custom items (via custom model data). This is what the data class `CustomItemIdentifier` is for!
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val identifier = CustomItemIdentifier(itemStack)
|
||||||
|
// or
|
||||||
|
val identifier = CustomItemIdentifier(1001, Material.IRON_NUGGET)
|
||||||
|
|
||||||
|
// get an itemstack with the custom model data applied
|
||||||
|
val stack = identifier.itemStack
|
||||||
|
```
|
||||||
|
|
||||||
|
> Any questions? Feel free to contact me!
|
@@ -0,0 +1,19 @@
|
|||||||
|
package net.axay.kspigot.extensions
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Bukkit.getOnlinePlayers
|
||||||
|
*/
|
||||||
|
val onlinePlayers: Collection<Player> get() = Bukkit.getOnlinePlayers()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Bukkit.getServer
|
||||||
|
*/
|
||||||
|
val server by lazy { Bukkit.getServer() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see Bukkit.getPluginManager
|
||||||
|
*/
|
||||||
|
val pluginManager by lazy { Bukkit.getPluginManager() }
|
@@ -0,0 +1,84 @@
|
|||||||
|
package net.axay.kspigot.extensions.bukkit
|
||||||
|
|
||||||
|
import net.axay.kspigot.extensions.onlinePlayers
|
||||||
|
import net.axay.kspigot.main.KSpigot
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.attribute.Attribute
|
||||||
|
import org.bukkit.entity.Damageable
|
||||||
|
import org.bukkit.entity.Entity
|
||||||
|
import org.bukkit.entity.LivingEntity
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the entity is completely in water.
|
||||||
|
*/
|
||||||
|
val LivingEntity.isInWater: Boolean get() = isFeetInWater && isHeadInWater
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the entities' head is in water.
|
||||||
|
*/
|
||||||
|
val LivingEntity.isHeadInWater: Boolean get() = this.eyeLocation.block.type == Material.WATER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the entities' feet are in water.
|
||||||
|
*/
|
||||||
|
val Entity.isFeetInWater: Boolean get() = this.location.block.type == Material.WATER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the entity stands on solid ground.
|
||||||
|
*/
|
||||||
|
val Entity.isGroundSolid: Boolean get() = this.location.add(0.0, -0.01, 0.0).block.type.isSolid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kills the damageable.
|
||||||
|
*/
|
||||||
|
fun Damageable.kill() {
|
||||||
|
health = 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the entities' health to the max possible value.
|
||||||
|
* @throws NullPointerException if the entity does not have a max health value
|
||||||
|
*/
|
||||||
|
fun LivingEntity.heal() {
|
||||||
|
health = getAttribute(Attribute.GENERIC_MAX_HEALTH)?.value
|
||||||
|
?: throw NullPointerException("The entity does not have a max health value!")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the players' foodLevel to the
|
||||||
|
* max possible value.
|
||||||
|
*/
|
||||||
|
fun Player.feed() {
|
||||||
|
foodLevel = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the players' saturation to the
|
||||||
|
* current max possible value.
|
||||||
|
*/
|
||||||
|
fun Player.saturate() {
|
||||||
|
saturation = foodLevel.toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feeds and saturates the player.
|
||||||
|
*/
|
||||||
|
fun Player.feedSaturate() {
|
||||||
|
foodLevel = 20
|
||||||
|
saturation = 20f
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the player for all [onlinePlayers].
|
||||||
|
*/
|
||||||
|
fun Player.disappear(kSpigot: KSpigot) {
|
||||||
|
onlinePlayers.filter { it != this }.forEach { it.hidePlayer(kSpigot, this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the player for all [onlinePlayers].
|
||||||
|
*/
|
||||||
|
fun Player.appear(kSpigot: KSpigot) {
|
||||||
|
onlinePlayers.filter { it != this }.forEach { it.showPlayer(kSpigot, this) }
|
||||||
|
}
|
@@ -1,5 +0,0 @@
|
|||||||
package net.axay.kspigot.extensions.bukkit
|
|
||||||
|
|
||||||
import org.bukkit.util.Vector
|
|
||||||
|
|
||||||
val Vector.isFinite: Boolean get() = x.isFinite() && y.isFinite() && z.isFinite()
|
|
@@ -1,8 +0,0 @@
|
|||||||
package net.axay.kspigot.extensions.entities
|
|
||||||
|
|
||||||
import org.bukkit.Material
|
|
||||||
import org.bukkit.entity.Entity
|
|
||||||
|
|
||||||
val Entity.isInWater: Boolean get() = this.location.block.type == Material.WATER
|
|
||||||
|
|
||||||
val Entity.isGroundSolid: Boolean get() = this.location.add(0.0, -0.01, 0.0).block.type.isSolid
|
|
@@ -28,14 +28,18 @@ val PlayerInteractEntityEvent.interactItem: ItemStack?
|
|||||||
@UnsafeImplementation
|
@UnsafeImplementation
|
||||||
val PlayerInteractEvent.clickedBlockExceptAir: Block?
|
val PlayerInteractEvent.clickedBlockExceptAir: Block?
|
||||||
get() {
|
get() {
|
||||||
val p: Player = this.player
|
return clickedBlock ?: kotlin.run {
|
||||||
return when (this.action) {
|
return@run if (this.action == Action.RIGHT_CLICK_AIR) {
|
||||||
Action.RIGHT_CLICK_BLOCK -> this.clickedBlock
|
|
||||||
Action.RIGHT_CLICK_AIR -> {
|
val p: Player = this.player
|
||||||
|
|
||||||
|
// check for sight blocking entities
|
||||||
for (nearbyEntity: Entity in p.getNearbyEntities(5.0, 5.0, 5.0))
|
for (nearbyEntity: Entity in p.getNearbyEntities(5.0, 5.0, 5.0))
|
||||||
if (p.hasLineOfSight(nearbyEntity)) return null
|
if (p.hasLineOfSight(nearbyEntity)) return@run null
|
||||||
|
|
||||||
|
// get first block in line of sight which is not air
|
||||||
p.getLineOfSight(null, 5).find { block -> !block.type.isAir }
|
p.getLineOfSight(null, 5).find { block -> !block.type.isAir }
|
||||||
}
|
|
||||||
else -> null
|
} else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -44,14 +44,14 @@ fun bukkitRunnable(
|
|||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
if (delay != null && delay >= 1)
|
if (delay != null && delay >= 1)
|
||||||
Bukkit.getScheduler().runTaskLater(kSpigot.plugin, mergedRunnable, delay)
|
Bukkit.getScheduler().runTaskLater(kSpigot, mergedRunnable, delay)
|
||||||
else
|
else
|
||||||
Bukkit.getScheduler().runTask(kSpigot.plugin, mergedRunnable)
|
Bukkit.getScheduler().runTask(kSpigot, mergedRunnable)
|
||||||
} else {
|
} else {
|
||||||
if (delay != null && delay >= 1)
|
if (delay != null && delay >= 1)
|
||||||
Bukkit.getScheduler().runTaskLaterAsynchronously(kSpigot.plugin, mergedRunnable, delay)
|
Bukkit.getScheduler().runTaskLaterAsynchronously(kSpigot, mergedRunnable, delay)
|
||||||
else
|
else
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(kSpigot.plugin, mergedRunnable)
|
Bukkit.getScheduler().runTaskAsynchronously(kSpigot, mergedRunnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (howoften > 1) {
|
} else if (howoften > 1) {
|
||||||
@@ -91,16 +91,16 @@ fun bukkitRunnable(
|
|||||||
kSpigot.kRunnableHolder.runnableEndCallbacks[bukkitRunnable] = endCallback
|
kSpigot.kRunnableHolder.runnableEndCallbacks[bukkitRunnable] = endCallback
|
||||||
|
|
||||||
if (sync)
|
if (sync)
|
||||||
bukkitRunnable.runTaskTimer(kSpigot.plugin, realDelay, realPeriod)
|
bukkitRunnable.runTaskTimer(kSpigot, realDelay, realPeriod)
|
||||||
else
|
else
|
||||||
bukkitRunnable.runTaskTimerAsynchronously(kSpigot.plugin, realDelay, realPeriod)
|
bukkitRunnable.runTaskTimerAsynchronously(kSpigot, realDelay, realPeriod)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bukkitSync(kSpigot: KSpigot, runnable: () -> Unit)
|
fun bukkitSync(kSpigot: KSpigot, runnable: () -> Unit)
|
||||||
= Bukkit.getScheduler().runTask(kSpigot.plugin, runnable)
|
= Bukkit.getScheduler().runTask(kSpigot, runnable)
|
||||||
|
|
||||||
fun bukkitAsync(kSpigot: KSpigot, runnable: () -> Unit)
|
fun bukkitAsync(kSpigot: KSpigot, runnable: () -> Unit)
|
||||||
= Bukkit.getScheduler().runTaskAsynchronously(kSpigot.plugin, runnable)
|
= Bukkit.getScheduler().runTaskAsynchronously(kSpigot, runnable)
|
67
src/main/kotlin/net/axay/kspigot/utils/KSpigotGeometry.kt
Normal file
67
src/main/kotlin/net/axay/kspigot/utils/KSpigotGeometry.kt
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package net.axay.kspigot.utils
|
||||||
|
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.util.Vector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LOCATION
|
||||||
|
*/
|
||||||
|
|
||||||
|
// INCREASE
|
||||||
|
// all
|
||||||
|
infix fun Location.increase(distance: Number) = add(distance, distance, distance)
|
||||||
|
infix fun Location.increase(vec: Vector) = add(vec)
|
||||||
|
// single
|
||||||
|
infix fun Location.increaseX(distance: Number) = add(distance, 0.0, 0.0)
|
||||||
|
infix fun Location.increaseY(distance: Number) = add(0.0, distance, 0.0)
|
||||||
|
infix fun Location.increaseZ(distance: Number) = add(0.0, 0.0, distance)
|
||||||
|
// pair
|
||||||
|
infix fun Location.increaseXY(distance: Number) = add(distance, distance, 0.0)
|
||||||
|
infix fun Location.increaseYZ(distance: Number) = add(0.0, distance, distance)
|
||||||
|
infix fun Location.increaseXZ(distance: Number) = add(distance, 0.0, distance)
|
||||||
|
|
||||||
|
// REDUCE
|
||||||
|
// all
|
||||||
|
infix fun Location.reduce(distance: Number) = substract(distance, distance, distance)
|
||||||
|
infix fun Location.reduce(vec: Vector) = subtract(vec)
|
||||||
|
// single
|
||||||
|
infix fun Location.reduceX(distance: Number) = substract(distance, 0.0, 0.0)
|
||||||
|
infix fun Location.reduceY(distance: Number) = substract(0.0, distance, 0.0)
|
||||||
|
infix fun Location.reduceZ(distance: Number) = substract(0.0, 0.0, distance)
|
||||||
|
// pair
|
||||||
|
infix fun Location.reduceXY(distance: Number) = substract(distance, distance, 0.0)
|
||||||
|
infix fun Location.reduceYZ(distance: Number) = substract(0.0, distance, distance)
|
||||||
|
infix fun Location.reduceXZ(distance: Number) = substract(distance, 0.0, distance)
|
||||||
|
|
||||||
|
// extensions
|
||||||
|
|
||||||
|
fun Location.add(x: Number, y: Number, z: Number) = add(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
fun Location.substract(x: Number, y: Number, z: Number) = subtract(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
|
||||||
|
// operator functions
|
||||||
|
operator fun Location.plus(vec: Vector) = add(vec)
|
||||||
|
operator fun Location.minus(vec: Vector) = subtract(vec)
|
||||||
|
operator fun Location.plus(loc: Location) = add(loc)
|
||||||
|
operator fun Location.minus(loc: Location) = subtract(loc)
|
||||||
|
|
||||||
|
/*
|
||||||
|
VECTOR
|
||||||
|
*/
|
||||||
|
|
||||||
|
val Vector.isFinite: Boolean get() = x.isFinite() && y.isFinite() && z.isFinite()
|
||||||
|
|
||||||
|
// fast construct
|
||||||
|
fun vec(x: Number = 0.0, y: Number = 0.0, z: Number = 0.0) = Vector(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
fun vecXY(x: Number, y: Number) = vec(x, y)
|
||||||
|
fun vecXZ(x: Number, z: Number) = vec(x, z = z)
|
||||||
|
fun vecYZ(y: Number, z: Number) = vec(y = y, z = z)
|
||||||
|
fun vecX(x: Number) = vec(x)
|
||||||
|
fun vecY(y: Number) = vec(y = y)
|
||||||
|
fun vecZ(z: Number) = vec(z = z)
|
||||||
|
|
||||||
|
// operator functions
|
||||||
|
operator fun Vector.plus(vec: Vector) = add(vec)
|
||||||
|
operator fun Vector.minus(vec: Vector) = subtract(vec)
|
||||||
|
operator fun Vector.times(vec: Vector) = multiply(vec)
|
@@ -14,7 +14,7 @@ interface RegisterableCommand : CommandExecutor {
|
|||||||
* false if not
|
* false if not
|
||||||
*/
|
*/
|
||||||
fun registerCommand(commandName: String, kSpigot: KSpigot): Boolean {
|
fun registerCommand(commandName: String, kSpigot: KSpigot): Boolean {
|
||||||
kSpigot.plugin.getCommand(commandName)?.let {
|
kSpigot.getCommand(commandName)?.let {
|
||||||
it.setExecutor(this)
|
it.setExecutor(this)
|
||||||
if (this is TabCompleter)
|
if (this is TabCompleter)
|
||||||
it.tabCompleter = this
|
it.tabCompleter = this
|
||||||
|
@@ -10,6 +10,6 @@ interface RegisterableListener : Listener {
|
|||||||
* Registers this listener
|
* Registers this listener
|
||||||
* for the given instance of [kSpigot].
|
* for the given instance of [kSpigot].
|
||||||
*/
|
*/
|
||||||
fun registerListener(kSpigot: KSpigot) = Bukkit.getPluginManager().registerEvents(this, kSpigot.plugin)
|
fun registerListener(kSpigot: KSpigot) = Bukkit.getPluginManager().registerEvents(this, kSpigot)
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user