1.0.0.3 - new modules
Added utility, menu, thirdparty and exposed modules A few little fixes
This commit is contained in:
1 parent
e5a7e08e56
commit
9655d10025
63 files changed
+5334
-12
No files matched your search
@@ -1,15 +1,97 @@
|
|||||||
# KotlinBukkitKit
|
# KotlinBukkitKit
|
||||||
KotlinBukkitKit is an API for Bukkit/SpigotAPI using features Kotlin. DevSrSouza / KotlinBukkitAPI.
|
KotlinBukkitKit is an API for Bukkit/SpigotAPI using features Kotlin.
|
||||||
|
Thanks for DevSrSouza / KotlinBukkitAPI.
|
||||||
|
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
implementation("tech.carcadex:kotlinbukkitkit-architecture:1.0.0.2")
|
implementation("tech.carcadex:kotlinbukkitkit-architecture:1.0.0.3")
|
||||||
implementation("tech.carcadex:kotlinbukkitkit-extensions:1.0.0.2")
|
implementation("tech.carcadex:kotlinbukkitkit-extensions:1.0.0.3")
|
||||||
implementation("tech.carcadex:kotlinbukkitkit-serialization:1.0.0.2")
|
implementation("tech.carcadex:kotlinbukkitkit-serialization:1.0.0.3")
|
||||||
implementation("tech.carcadex:kotlinbukkitkit-genref:1.0.0.2")
|
implementation("tech.carcadex:kotlinbukkitkit-messages:1.0.0.3")
|
||||||
implementation("tech.carcadex:kotlinbukkitkit-messages:1.0.0.2")
|
implementation("tech.carcadex:kotlinbukkitkit-commands:1.0.0.3")
|
||||||
implementation("tech.carcadex:kotlinbukkitkit-commands:1.0.0.2")
|
implementation("tech.carcadex:kotlinbukkitkit-utility:1.0.0.3")
|
||||||
|
implementation("tech.carcadex:kotlinbukkitkit-exposed:1.0.0.3")
|
||||||
|
|
||||||
|
compileOnly("tech.carcadex:kotlinbukkitkit-genref:1.0.0.3")
|
||||||
|
ksp("tech.carcadex:kotlinbukkitkit-genref:1.0.0.3")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Documentation will be there: https://red-tea.gitbook.io/kotlinbukkitkit/
|
Documentation will be there: https://red-tea.gitbook.io/kotlinbukkitkit/
|
||||||
|
|
||||||
|
|
||||||
|
<img src="https://github.com/DevSrSouza/KotlinBukkitAPI/raw/master/icon.png?size=96" alt="KotlinBukkitAPI" title="KotlinBukkitAPI" align="right"/>
|
||||||
|
|
||||||
|
## KotlinBukkitKit
|
||||||
|
|
||||||
|

|
||||||
|
[](https://github.com/DevSrSouza/KotlinBukkitAPI/stargazers)
|
||||||
|
[](https://github.com/DevSrSouza/KotlinBukkitAPI/issues)
|
||||||
|
[](https://github.com/DevSrSouza/KotlinBukkitAPI/commit)
|
||||||
|
[](https://discord.gg/pyrh4dUPZD)
|
||||||
|
[](https://choosealicense.com/licenses/mit)
|
||||||
|
|
||||||
|
KotlinBukkitKit is an API for Bukkit/SpigotAPI using the cool and nifty features Kotlin has to make your life more easier.
|
||||||
|
|
||||||
|
### Contents:
|
||||||
|
|
||||||
|
* [Documentation](https://red-tea.gitbook.io/kotlinbukkitkit)
|
||||||
|
* [IDEA Plugin](https://github.com/CarcadeX/KotlinBukkitKit-Tooling)
|
||||||
|
|
||||||
|
|
||||||
|
# Samples
|
||||||
|
|
||||||
|
## [We have more Samples in the Documentation](https://red-tea.gitbook.io/kotlinbukkitkit)
|
||||||
|
|
||||||
|
KotlinBukkitAPI goes beyond this samples, and you can find all of it in the [wiki/documentation](https://red-tea.gitbook.io/kotlinbukkitkit).
|
||||||
|
|
||||||
|
Event DSL sample
|
||||||
|
```kotlin
|
||||||
|
plugin.events {
|
||||||
|
event<PlayerJoinEvent> {
|
||||||
|
player.msg("&3Welcome ${player.name}".translateColor())
|
||||||
|
}
|
||||||
|
|
||||||
|
event<PlayerQuitEvent> {
|
||||||
|
broadcast("&eThe player &c${player.name} &eleft :(".translateColor())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Simple Command DSL example
|
||||||
|
```kotlin
|
||||||
|
plugin.simpleCommand("hi") {
|
||||||
|
sender.msg("&eHallo".translateColor())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Item meta DSL and other stuff
|
||||||
|
```kotlin
|
||||||
|
val gem = item(Material.DIAMOND).apply {
|
||||||
|
amount = 5
|
||||||
|
meta<ItemMeta> {
|
||||||
|
displayName = "&bGem".translateColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val encbook = item(Material.ENCHANTED_BOOK).meta<EnchantmentStorageMeta> {
|
||||||
|
displayName = "&4&lThe powerful BOOK".translateColor()
|
||||||
|
addStoredEnchant(Enchantment.DAMAGE_ALL, 10, true) // putting sharpness 10 to the book
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Another approach:
|
||||||
|
```kotlin
|
||||||
|
val gem = item(Material.DIAMOND, amount = 5).displayName("&bGem".translateColor())
|
||||||
|
|
||||||
|
val encbook = metadataItem<EnchantmentStorageMeta>(Material.ENCHANTED_BOOK) {
|
||||||
|
displayName = "&4&lThe powerful BOOK".translateColor()
|
||||||
|
addStoredEnchant(Enchantment.DAMAGE_ALL, 10, true) // putting sharpness 10 to the book
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## IDEA Plugin - project wizard
|
||||||
|
|
||||||
|
You can find it on a <a href="https://plugins.jetbrains.com/plugin/24043-kotlinbukkitkit-tooling">official plugin page</a>
|
||||||
|

|
||||||
+7
-2
@@ -1,12 +1,12 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.9.0"
|
kotlin("jvm") version "1.9.23"
|
||||||
id("io.codearte.nexus-staging") version "0.30.0"
|
id("io.codearte.nexus-staging") version "0.30.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "tech.carcadex"
|
group = "tech.carcadex"
|
||||||
version = "1.0.0.2"
|
version = project.findProperty("version").toString()
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -34,4 +34,9 @@ tasks.register("publishAll") {
|
|||||||
dependsOn(tasks.getByPath("serialization:publish"))
|
dependsOn(tasks.getByPath("serialization:publish"))
|
||||||
dependsOn(tasks.getByPath("genref:publish"))
|
dependsOn(tasks.getByPath("genref:publish"))
|
||||||
dependsOn(tasks.getByPath("messages:publish"))
|
dependsOn(tasks.getByPath("messages:publish"))
|
||||||
|
dependsOn(tasks.getByPath("commands:publish"))
|
||||||
|
dependsOn(tasks.getByPath("utility:publish"))
|
||||||
|
dependsOn(tasks.getByPath("exposed:publish"))
|
||||||
|
dependsOn(tasks.getByPath("thirdparty:publish"))
|
||||||
|
dependsOn(tasks.getByPath("menu:publish"))
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.commands
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.architecture.KotlinPlugin
|
||||||
|
import tech.carcadex.kotlinbukkitkit.commands.dsl.builder.simpleCommand
|
||||||
|
|
||||||
|
fun KotlinPlugin.start() {
|
||||||
|
simpleCommand("test", permission = "test.use") {
|
||||||
|
val arg1 = string(0)
|
||||||
|
val arg2 = integer(1)
|
||||||
|
sender.sendMessage(arg1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
kotlin("jvm")
|
||||||
|
kotlin("plugin.serialization") version "1.8.21"
|
||||||
|
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||||
|
id("maven-publish")
|
||||||
|
id("signing")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "tech.carcadex"
|
||||||
|
val globalArtifactId = "kotlinbukkitkit-exposed"
|
||||||
|
val globalName = "kotlinbukkitkit-exposed"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
name = "spigotmc-repo"
|
||||||
|
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.jetbrains.exposed", "exposed-core", "0.45.0")
|
||||||
|
implementation("org.jetbrains.exposed", "exposed-dao", "0.45.0")
|
||||||
|
implementation("com.zaxxer", "HikariCP", "4.0.3")
|
||||||
|
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT")
|
||||||
|
api(project(":architecture"))
|
||||||
|
api(project(":extensions"))
|
||||||
|
api(project(":utility"))
|
||||||
|
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets.main {
|
||||||
|
kotlin.srcDir("build/generated/ksp/main/kotlin")
|
||||||
|
}
|
||||||
|
sourceSets.test {
|
||||||
|
kotlin.srcDir("build/generated/ksp/test/kotlin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withJavadocJar()
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("maven") {
|
||||||
|
artifactId = globalArtifactId
|
||||||
|
groupId = "tech.carcadex"
|
||||||
|
version = version
|
||||||
|
from(components["java"])
|
||||||
|
versionMapping {
|
||||||
|
usage("java-api") {
|
||||||
|
fromResolutionOf("runtimeClasspath")
|
||||||
|
}
|
||||||
|
usage("java-runtime") {
|
||||||
|
fromResolutionResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pom {
|
||||||
|
artifactId = globalArtifactId
|
||||||
|
groupId = "tech.carcadex"
|
||||||
|
version = version
|
||||||
|
name.set(globalArtifactId)
|
||||||
|
description.set(globalArtifactId)
|
||||||
|
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name.set("MIT")
|
||||||
|
url.set("https://mit-license.org/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id.set("itzRedTea")
|
||||||
|
name.set("Red Tea")
|
||||||
|
email.set("red__tea@outlook.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection.set("scm:https://github.com/CarcadeX/$globalName.git")
|
||||||
|
developerConnection.set("git@github.com:CarcadeX/$globalName.git")
|
||||||
|
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||||
|
val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
|
||||||
|
credentials {
|
||||||
|
username = "itzRedTea"
|
||||||
|
password = project.findProperty("password").toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signing {
|
||||||
|
useGpgCmd()
|
||||||
|
sign(publishing.publications.getByName("maven"))
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.io.File
|
||||||
|
import java.sql.SQLException
|
||||||
|
|
||||||
|
public interface DatabaseTypeConfigHeader {
|
||||||
|
public val type: String
|
||||||
|
public val hostname: String
|
||||||
|
public val port: Short
|
||||||
|
public val database: String
|
||||||
|
public val user: String
|
||||||
|
public val password: String
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
public data class DatabaseTypeConfig(
|
||||||
|
override var type: String = "H2",
|
||||||
|
override var hostname: String = "localhost",
|
||||||
|
override var port: Short = 3306,
|
||||||
|
override var database: String = "kbapi_database",
|
||||||
|
override var user: String = "root",
|
||||||
|
override var password: String = "12345",
|
||||||
|
) : DatabaseTypeConfigHeader
|
||||||
|
|
||||||
|
public fun databaseTypeFrom(dataFolder: File, config: DatabaseTypeConfig): DatabaseType {
|
||||||
|
val type = DatabaseType.byName(config.type)
|
||||||
|
if (type != null) {
|
||||||
|
return when (type) {
|
||||||
|
DatabaseType.H2::class -> DatabaseType.H2(
|
||||||
|
dataFolder,
|
||||||
|
config.database,
|
||||||
|
)
|
||||||
|
DatabaseType.SQLite::class -> DatabaseType.SQLite(
|
||||||
|
dataFolder,
|
||||||
|
config.database,
|
||||||
|
)
|
||||||
|
DatabaseType.MySQL::class -> DatabaseType.MySQL(
|
||||||
|
config.hostname,
|
||||||
|
config.port,
|
||||||
|
config.database,
|
||||||
|
config.user,
|
||||||
|
config.password,
|
||||||
|
)
|
||||||
|
DatabaseType.PostgreSQL::class -> DatabaseType.PostgreSQL(
|
||||||
|
config.hostname,
|
||||||
|
config.port,
|
||||||
|
config.database,
|
||||||
|
config.user,
|
||||||
|
config.password,
|
||||||
|
)
|
||||||
|
DatabaseType.SQLServer::class -> DatabaseType.SQLServer(
|
||||||
|
config.hostname,
|
||||||
|
config.port,
|
||||||
|
config.database,
|
||||||
|
config.user,
|
||||||
|
config.password,
|
||||||
|
)
|
||||||
|
else -> throw SQLException("SQL type not finded or supported.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw SQLException("SQL type not finded or supported.")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed
|
||||||
|
|
||||||
|
import com.zaxxer.hikari.HikariConfig
|
||||||
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import java.sql.SQLException
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
private const val KEY_FILE = "{file}"
|
||||||
|
|
||||||
|
public sealed class DatabaseType(
|
||||||
|
public val name: String,
|
||||||
|
public val jdbc: String,
|
||||||
|
public val driverClass: String,
|
||||||
|
public val driverLink: String,
|
||||||
|
) {
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val fileDatabases: Map<String, KClass<out DatabaseType>> =
|
||||||
|
mapOf("h2" to H2::class, "sqlite" to SQLite::class)
|
||||||
|
public val remoteDatabases: Map<String, KClass<out DatabaseType>> =
|
||||||
|
mapOf("mysql" to MySQL::class, "postgresql" to PostgreSQL::class, "sqlserver" to SQLServer::class)
|
||||||
|
public val databases: Map<String, KClass<out DatabaseType>> = fileDatabases + remoteDatabases
|
||||||
|
|
||||||
|
public fun byName(name: String): KClass<out DatabaseType>? = databases[name.toLowerCase()]
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract fun dataSource(): HikariDataSource
|
||||||
|
|
||||||
|
public abstract fun config(): HikariConfig
|
||||||
|
|
||||||
|
public abstract class FileDatabaseType(
|
||||||
|
name: String,
|
||||||
|
jdbc: String,
|
||||||
|
driverClass: String,
|
||||||
|
driverLink: String,
|
||||||
|
public val dataFolder: File,
|
||||||
|
public val file: String,
|
||||||
|
public val databaseExtension: String,
|
||||||
|
public val needFileCreation: Boolean,
|
||||||
|
) : DatabaseType(name, jdbc, driverClass, driverLink) {
|
||||||
|
|
||||||
|
private val realFile = File(dataFolder, "$file.$databaseExtension")
|
||||||
|
|
||||||
|
override fun dataSource(): HikariDataSource {
|
||||||
|
if (needFileCreation && !realFile.exists()) realFile.createNewFile()
|
||||||
|
|
||||||
|
loadDependency()
|
||||||
|
|
||||||
|
return HikariDataSource(config())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun config(): HikariConfig = HikariConfig().apply {
|
||||||
|
driverClassName = driverClass
|
||||||
|
jdbcUrl = jdbc.replace(KEY_FILE, realFile.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class RemoteDatabaseType(
|
||||||
|
name: String,
|
||||||
|
jdbc: String,
|
||||||
|
driverClass: String,
|
||||||
|
driverLink: String,
|
||||||
|
public val hostname: String,
|
||||||
|
public val port: Short,
|
||||||
|
public val database: String,
|
||||||
|
public val username: String,
|
||||||
|
public val password: String,
|
||||||
|
) : DatabaseType(name, jdbc, driverClass, driverLink) {
|
||||||
|
override fun dataSource(): HikariDataSource {
|
||||||
|
loadDependency()
|
||||||
|
|
||||||
|
return HikariDataSource(config())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun config(): HikariConfig = HikariConfig().apply {
|
||||||
|
driverClassName = driverClass
|
||||||
|
jdbcUrl = jdbc
|
||||||
|
username = this@RemoteDatabaseType.username
|
||||||
|
password = this@RemoteDatabaseType.password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class H2(
|
||||||
|
dataFolder: File,
|
||||||
|
file: String,
|
||||||
|
) : FileDatabaseType(
|
||||||
|
"H2",
|
||||||
|
"jdbc:h2:file:./$KEY_FILE",
|
||||||
|
"org.h2.Driver",
|
||||||
|
"https://repo1.maven.org/maven2/com/h2database/h2/1.4.199/h2-1.4.199.jar",
|
||||||
|
dataFolder,
|
||||||
|
file,
|
||||||
|
"h2.db",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
|
public class SQLite(
|
||||||
|
dataFolder: File,
|
||||||
|
file: String,
|
||||||
|
) : FileDatabaseType(
|
||||||
|
"SQLite",
|
||||||
|
"jdbc:sqlite:./$KEY_FILE",
|
||||||
|
"org.sqlite.JDBC",
|
||||||
|
"https://bitbucket.org/xerial/sqlite-jdbc/downloads/sqlite-jdbc-3.23.1.jar",
|
||||||
|
dataFolder,
|
||||||
|
file,
|
||||||
|
"sqlite.db",
|
||||||
|
true,
|
||||||
|
) {
|
||||||
|
// https://github.com/brettwooldridge/HikariCP/issues/393#issuecomment-135580191
|
||||||
|
override fun config(): HikariConfig {
|
||||||
|
return super.config().apply {
|
||||||
|
connectionTestQuery = "SELECT 1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MySQL(
|
||||||
|
hostname: String,
|
||||||
|
port: Short,
|
||||||
|
database: String,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
) : RemoteDatabaseType(
|
||||||
|
"MySQL",
|
||||||
|
"jdbc:mysql://$hostname:$port/$database",
|
||||||
|
"com.mysql.jdbc.Driver",
|
||||||
|
"https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.11/mysql-connector-java-8.0.11.jar",
|
||||||
|
hostname, port, database,
|
||||||
|
username, password,
|
||||||
|
)
|
||||||
|
|
||||||
|
public class PostgreSQL(
|
||||||
|
hostname: String,
|
||||||
|
port: Short,
|
||||||
|
database: String,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
) : RemoteDatabaseType(
|
||||||
|
"PostgreSQL",
|
||||||
|
"jdbc:postgresql://$hostname:$port/$database",
|
||||||
|
"org.postgresql.Driver",
|
||||||
|
"https://jdbc.postgresql.org/download/postgresql-42.2.2.jre7.jar",
|
||||||
|
hostname, port, database,
|
||||||
|
username, password,
|
||||||
|
)
|
||||||
|
|
||||||
|
public class SQLServer(
|
||||||
|
hostname: String,
|
||||||
|
port: Short,
|
||||||
|
database: String,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
) : RemoteDatabaseType(
|
||||||
|
"SQLServer",
|
||||||
|
"jdbc:sqlserver://$hostname:$port;databaseName=$database",
|
||||||
|
"com.microsoft.sqlserver.jdbc.SQLServerDriver",
|
||||||
|
"https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/6.4.0.jre8/mssql-jdbc-6.4.0.jre8.jar",
|
||||||
|
hostname, port, database,
|
||||||
|
username, password,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val libsFolder = File("klibs").apply { mkdirs() }
|
||||||
|
private val jarFile = File(libsFolder, "$name-Driver.jar")
|
||||||
|
|
||||||
|
protected fun loadDependency() {
|
||||||
|
try {
|
||||||
|
Class.forName(driverClass)
|
||||||
|
} catch (e: ClassNotFoundException) {
|
||||||
|
if (jarFile.exists()) {
|
||||||
|
loadDriver()
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
downloadDriver()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw SQLException("Cant download the driver dependencie of $name")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
loadDriver()
|
||||||
|
Class.forName(driverClass)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
jarFile.delete()
|
||||||
|
throw SQLException("Cant load the driver dependencies of $name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadDriver() {
|
||||||
|
val url = jarFile.toURI().toURL()
|
||||||
|
val classLoader = DatabaseType::class.java.classLoader as URLClassLoader
|
||||||
|
val method = URLClassLoader::class.java.getDeclaredMethod("addURL", URL::class.java)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(classLoader, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun downloadDriver() {
|
||||||
|
val input = URL(driverLink).openStream()
|
||||||
|
if (jarFile.exists()) {
|
||||||
|
if (jarFile.isDirectory) {
|
||||||
|
throw IOException("File '" + jarFile.name + "' is a directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jarFile.canWrite()) {
|
||||||
|
throw IOException("File '" + jarFile.name + "' cannot be written")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val parent = jarFile.parentFile
|
||||||
|
if (parent != null && !parent.exists() && !parent.mkdirs()) {
|
||||||
|
throw IOException("File '" + jarFile.name + "' could not be created")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val output = FileOutputStream(jarFile)
|
||||||
|
|
||||||
|
val buffer = ByteArray(4096)
|
||||||
|
var n = input.read(buffer)
|
||||||
|
while (-1 != n) {
|
||||||
|
output.write(buffer, 0, n)
|
||||||
|
|
||||||
|
n = input.read(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
input.close()
|
||||||
|
output.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.block.Block
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun Entity<*>.block(column: Column<String>): ExposedDelegate<Block> = BlockExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("blockNullable")
|
||||||
|
public fun Entity<*>.block(column: Column<String?>): ExposedDelegate<Block?> = BlockExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public fun Entity<*>.block(
|
||||||
|
worldColumn: Column<String>,
|
||||||
|
xColumn: Column<Int>,
|
||||||
|
yColumn: Column<Int>,
|
||||||
|
zColumn: Column<Int>,
|
||||||
|
): ExposedDelegate<Block> = BlockMultiColumnExposedDelegate(worldColumn, xColumn, yColumn, zColumn)
|
||||||
|
|
||||||
|
@JvmName("blockNullable")
|
||||||
|
public fun Entity<*>.nullableBlock(
|
||||||
|
worldColumn: Column<String?>,
|
||||||
|
xColumn: Column<Int?>,
|
||||||
|
yColumn: Column<Int?>,
|
||||||
|
zColumn: Column<Int?>,
|
||||||
|
): ExposedDelegate<Block?> = BlockMultiColumnExposedDelegateNullable(worldColumn, xColumn, yColumn, zColumn)
|
||||||
|
|
||||||
|
public class BlockExposedDelegate(
|
||||||
|
public val column: Column<String>,
|
||||||
|
) : ExposedDelegate<Block> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Block {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data.split(";")
|
||||||
|
val worldName = slices[0]
|
||||||
|
val world = requireNotNull(Bukkit.getWorld(worldName)) { "World '$worldName' retrieving from database unavailable." }
|
||||||
|
return world.getBlockAt(
|
||||||
|
slices[1].toInt(),
|
||||||
|
slices[2].toInt(),
|
||||||
|
slices[3].toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Block,
|
||||||
|
) {
|
||||||
|
val parsed = value.run { "${world.name};$x;$y;$z" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlockExposedDelegateNullable(
|
||||||
|
public val column: Column<String?>,
|
||||||
|
) : ExposedDelegate<Block?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Block? {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data?.split(";")
|
||||||
|
return slices?.let {
|
||||||
|
val worldName = it[0]
|
||||||
|
val world = requireNotNull(Bukkit.getWorld(worldName)) { "World '$worldName' retrieving from database unavailable." }
|
||||||
|
world.getBlockAt(
|
||||||
|
it[1].toInt(),
|
||||||
|
it[2].toInt(),
|
||||||
|
it[3].toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Block?,
|
||||||
|
) {
|
||||||
|
val parsed = value?.run { "${world.name};$x;$y;$z" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlockMultiColumnExposedDelegate(
|
||||||
|
public val worldColumn: Column<String>,
|
||||||
|
public val xColumn: Column<Int>,
|
||||||
|
public val yColumn: Column<Int>,
|
||||||
|
public val zColumn: Column<Int>,
|
||||||
|
) : ExposedDelegate<Block> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Block {
|
||||||
|
val worldName = entity.run { worldColumn.getValue(this, desc) }
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val y = entity.run { yColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
val world = requireNotNull(Bukkit.getWorld(worldName)) { "World '$worldName' retrieving from database unavailable." }
|
||||||
|
|
||||||
|
return world.getBlockAt(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Block,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
value.apply {
|
||||||
|
worldColumn.setValue(entity, desc, world.name)
|
||||||
|
xColumn.setValue(entity, desc, x)
|
||||||
|
yColumn.setValue(entity, desc, y)
|
||||||
|
zColumn.setValue(entity, desc, z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlockMultiColumnExposedDelegateNullable(
|
||||||
|
public val worldColumn: Column<String?>,
|
||||||
|
public val xColumn: Column<Int?>,
|
||||||
|
public val yColumn: Column<Int?>,
|
||||||
|
public val zColumn: Column<Int?>,
|
||||||
|
) : ExposedDelegate<Block?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Block? {
|
||||||
|
val worldName = entity.run { worldColumn.getValue(this, desc) }
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val y = entity.run { yColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return if (
|
||||||
|
worldName != null &&
|
||||||
|
x != null && y != null && z != null
|
||||||
|
) {
|
||||||
|
val world = requireNotNull(Bukkit.getWorld(worldName)) { "World '$worldName' retrieving from database unavailable." }
|
||||||
|
|
||||||
|
world.getBlockAt(x, y, z)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Block?,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
worldColumn.setValue(entity, desc, value?.world?.name)
|
||||||
|
xColumn.setValue(entity, desc, value?.x)
|
||||||
|
yColumn.setValue(entity, desc, value?.y)
|
||||||
|
zColumn.setValue(entity, desc, value?.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.BlockPos
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun Entity<*>.blockPos(column: Column<String>): ExposedDelegate<BlockPos> = BlockPosExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("blockPosNullable")
|
||||||
|
public fun Entity<*>.blockPos(column: Column<String?>): ExposedDelegate<BlockPos?> = BlockPosExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public fun Entity<*>.blockPos(
|
||||||
|
xColumn: Column<Int>,
|
||||||
|
yColumn: Column<Int>,
|
||||||
|
zColumn: Column<Int>,
|
||||||
|
): ExposedDelegate<BlockPos> = BlockPosMultiColumnExposedDelegate(xColumn, yColumn, zColumn)
|
||||||
|
|
||||||
|
@JvmName("blockPosNullable")
|
||||||
|
public fun Entity<*>.nullableBlockPos(
|
||||||
|
xColumn: Column<Int?>,
|
||||||
|
yColumn: Column<Int?>,
|
||||||
|
zColumn: Column<Int?>,
|
||||||
|
): ExposedDelegate<BlockPos?> = BlockPosMultiColumnExposedDelegateNullable(xColumn, yColumn, zColumn)
|
||||||
|
|
||||||
|
public class BlockPosExposedDelegate(
|
||||||
|
public val column: Column<String>,
|
||||||
|
) : ExposedDelegate<BlockPos> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): BlockPos {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data.split(";")
|
||||||
|
return BlockPos(
|
||||||
|
slices[0].toInt(),
|
||||||
|
slices[1].toInt(),
|
||||||
|
slices[2].toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: BlockPos,
|
||||||
|
) {
|
||||||
|
val parsed = value.run { "$x;$y;$z" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlockPosExposedDelegateNullable(
|
||||||
|
public val column: Column<String?>,
|
||||||
|
) : ExposedDelegate<BlockPos?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): BlockPos? {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data?.split(";")
|
||||||
|
return slices?.let {
|
||||||
|
BlockPos(
|
||||||
|
it[0].toInt(),
|
||||||
|
it[1].toInt(),
|
||||||
|
it[2].toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: BlockPos?,
|
||||||
|
) {
|
||||||
|
val parsed = value?.run { "$x;$y;$z" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlockPosMultiColumnExposedDelegate(
|
||||||
|
public val xColumn: Column<Int>,
|
||||||
|
public val yColumn: Column<Int>,
|
||||||
|
public val zColumn: Column<Int>,
|
||||||
|
) : ExposedDelegate<BlockPos> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): BlockPos {
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val y = entity.run { yColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return BlockPos(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: BlockPos,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
value.apply {
|
||||||
|
xColumn.setValue(entity, desc, x)
|
||||||
|
yColumn.setValue(entity, desc, y)
|
||||||
|
zColumn.setValue(entity, desc, z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlockPosMultiColumnExposedDelegateNullable(
|
||||||
|
public val xColumn: Column<Int?>,
|
||||||
|
public val yColumn: Column<Int?>,
|
||||||
|
public val zColumn: Column<Int?>,
|
||||||
|
) : ExposedDelegate<BlockPos?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): BlockPos? {
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val y = entity.run { yColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return if (
|
||||||
|
x != null && y != null && z != null
|
||||||
|
) {
|
||||||
|
BlockPos(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: BlockPos?,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
xColumn.setValue(entity, desc, value?.x)
|
||||||
|
yColumn.setValue(entity, desc, value?.y)
|
||||||
|
zColumn.setValue(entity, desc, value?.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun <T> ExposedDelegate<T>.cached(): ExposedDelegate<T> = CachedExposedDelegate(this)
|
||||||
|
|
||||||
|
public class CachedExposedDelegate<T>(
|
||||||
|
private val delegate: ExposedDelegate<T>,
|
||||||
|
) : ExposedDelegate<T> {
|
||||||
|
private var cache: T? = null
|
||||||
|
private var isCached: Boolean = false
|
||||||
|
|
||||||
|
override fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): T {
|
||||||
|
if (!isCached) {
|
||||||
|
cache = delegate.getValue(entity, desc)
|
||||||
|
isCached = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: T,
|
||||||
|
) {
|
||||||
|
delegate.setValue(entity, desc, value)
|
||||||
|
cache = value
|
||||||
|
isCached = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.Chunk
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun Entity<*>.chunk(column: Column<String>): ExposedDelegate<Chunk> = ChunkExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("chunkNullable")
|
||||||
|
public fun Entity<*>.chunk(column: Column<String?>): ExposedDelegate<Chunk?> = ChunkExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public fun Entity<*>.chunk(
|
||||||
|
worldColumn: Column<String>,
|
||||||
|
xColumn: Column<Int>,
|
||||||
|
zColumn: Column<Int>,
|
||||||
|
): ExposedDelegate<Chunk> = ChunkMultiColumnExposedDelegate(worldColumn, xColumn, zColumn)
|
||||||
|
|
||||||
|
@JvmName("chunkNullable")
|
||||||
|
public fun Entity<*>.chunk(
|
||||||
|
worldColumn: Column<String?>,
|
||||||
|
xColumn: Column<Int?>,
|
||||||
|
zColumn: Column<Int?>,
|
||||||
|
): ExposedDelegate<Chunk?> = ChunkMultiColumnExposedDelegateNullable(worldColumn, xColumn, zColumn)
|
||||||
|
|
||||||
|
public class ChunkExposedDelegate(
|
||||||
|
public val column: Column<String>,
|
||||||
|
) : ExposedDelegate<Chunk> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Chunk {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data.split(";")
|
||||||
|
val worldName = slices[0]
|
||||||
|
val world = requireNotNull(Bukkit.getWorld(worldName)) { "World '$worldName' retrieving from database unavailable." }
|
||||||
|
return world.getChunkAt(
|
||||||
|
slices[1].toInt(),
|
||||||
|
slices[2].toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Chunk,
|
||||||
|
) {
|
||||||
|
val parsed = value.run { "${world.name};$x;$z" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChunkExposedDelegateNullable(
|
||||||
|
public val column: Column<String?>,
|
||||||
|
) : ExposedDelegate<Chunk?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Chunk? {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data?.split(";")
|
||||||
|
return slices?.let {
|
||||||
|
val worldName = it[0]
|
||||||
|
val world = requireNotNull(Bukkit.getWorld(worldName)) { "World '$worldName' retrieving from database unavailable." }
|
||||||
|
world.getChunkAt(
|
||||||
|
it[1].toInt(),
|
||||||
|
it[2].toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Chunk?,
|
||||||
|
) {
|
||||||
|
val parsed = value?.run { "${world.name};$x;$z" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChunkMultiColumnExposedDelegate(
|
||||||
|
public val worldColumn: Column<String>,
|
||||||
|
public val xColumn: Column<Int>,
|
||||||
|
public val zColumn: Column<Int>,
|
||||||
|
) : ExposedDelegate<Chunk> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Chunk {
|
||||||
|
val worldName = entity.run { worldColumn.getValue(this, desc) }
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
val world = requireNotNull(Bukkit.getWorld(worldName)) { "World '$worldName' retrieving from database unavailable." }
|
||||||
|
|
||||||
|
return world.getChunkAt(x, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Chunk,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
value.apply {
|
||||||
|
worldColumn.setValue(entity, desc, world.name)
|
||||||
|
xColumn.setValue(entity, desc, x)
|
||||||
|
zColumn.setValue(entity, desc, z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChunkMultiColumnExposedDelegateNullable(
|
||||||
|
public val worldColumn: Column<String?>,
|
||||||
|
public val xColumn: Column<Int?>,
|
||||||
|
public val zColumn: Column<Int?>,
|
||||||
|
) : ExposedDelegate<Chunk?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Chunk? {
|
||||||
|
val worldName = entity.run { worldColumn.getValue(this, desc) }
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return if (
|
||||||
|
worldName != null &&
|
||||||
|
x != null && z != null
|
||||||
|
) {
|
||||||
|
val world = requireNotNull(Bukkit.getWorld(worldName)) { "World '$worldName' retrieving from database unavailable." }
|
||||||
|
|
||||||
|
world.getChunkAt(x, z)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Chunk?,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
worldColumn.setValue(entity, desc, value?.world?.name)
|
||||||
|
xColumn.setValue(entity, desc, value?.x)
|
||||||
|
zColumn.setValue(entity, desc, value?.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.ChunkPos
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun Entity<*>.chunkPos(column: Column<String>): ExposedDelegate<ChunkPos> = ChunkPosExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("chunkPosNullable")
|
||||||
|
public fun Entity<*>.chunkPos(column: Column<String?>): ExposedDelegate<ChunkPos?> = ChunkPosExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public fun Entity<*>.chunkPos(
|
||||||
|
xColumn: Column<Int>,
|
||||||
|
zColumn: Column<Int>,
|
||||||
|
): ExposedDelegate<ChunkPos> = ChunkPosMultiColumnExposedDelegate(xColumn, zColumn)
|
||||||
|
|
||||||
|
@JvmName("chunkPosNullable")
|
||||||
|
public fun Entity<*>.chunkPos(
|
||||||
|
xColumn: Column<Int?>,
|
||||||
|
zColumn: Column<Int?>,
|
||||||
|
): ExposedDelegate<ChunkPos?> = ChunkPosMultiColumnExposedDelegateNullable(xColumn, zColumn)
|
||||||
|
|
||||||
|
public class ChunkPosExposedDelegate(
|
||||||
|
public val column: Column<String>,
|
||||||
|
) : ExposedDelegate<ChunkPos> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): ChunkPos {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data.split(";")
|
||||||
|
return ChunkPos(
|
||||||
|
slices[0].toInt(),
|
||||||
|
slices[1].toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: ChunkPos,
|
||||||
|
) {
|
||||||
|
val parsed = value.run { "$x;$z" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChunkPosExposedDelegateNullable(
|
||||||
|
public val column: Column<String?>,
|
||||||
|
) : ExposedDelegate<ChunkPos?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): ChunkPos? {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data?.split(";")
|
||||||
|
return slices?.let {
|
||||||
|
ChunkPos(
|
||||||
|
it[0].toInt(),
|
||||||
|
it[1].toInt(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: ChunkPos?,
|
||||||
|
) {
|
||||||
|
val parsed = value?.run { "$x;$z" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChunkPosMultiColumnExposedDelegate(
|
||||||
|
public val xColumn: Column<Int>,
|
||||||
|
public val zColumn: Column<Int>,
|
||||||
|
) : ExposedDelegate<ChunkPos> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): ChunkPos {
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return ChunkPos(x, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: ChunkPos,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
value.apply {
|
||||||
|
xColumn.setValue(entity, desc, x)
|
||||||
|
zColumn.setValue(entity, desc, z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChunkPosMultiColumnExposedDelegateNullable(
|
||||||
|
public val xColumn: Column<Int?>,
|
||||||
|
public val zColumn: Column<Int?>,
|
||||||
|
) : ExposedDelegate<ChunkPos?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): ChunkPos? {
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return if (
|
||||||
|
x != null && z != null
|
||||||
|
) {
|
||||||
|
ChunkPos(
|
||||||
|
x,
|
||||||
|
z,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: ChunkPos?,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
xColumn.setValue(entity, desc, value?.x)
|
||||||
|
zColumn.setValue(entity, desc, value?.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public interface ExposedDelegate<T> {
|
||||||
|
|
||||||
|
public operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): T
|
||||||
|
|
||||||
|
public operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: T,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import org.bukkit.util.io.BukkitObjectInputStream
|
||||||
|
import org.bukkit.util.io.BukkitObjectOutputStream
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import org.jetbrains.exposed.sql.statements.api.ExposedBlob
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: This not save NBT and could break version to version, if you pretend
|
||||||
|
* to upgrade your server and this keep the same, I can't guarantee that.
|
||||||
|
*/
|
||||||
|
public fun Entity<*>.itemStack(column: Column<ExposedBlob>): ExposedDelegate<ItemStack> = ItemStackExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("itemStackNullable")
|
||||||
|
public fun Entity<*>.itemStack(column: Column<ExposedBlob?>): ExposedDelegate<ItemStack?> = ItemStackExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public class ItemStackExposedDelegate(
|
||||||
|
public val column: Column<ExposedBlob>,
|
||||||
|
) : ExposedDelegate<ItemStack> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): ItemStack {
|
||||||
|
val blob = entity.run { column.getValue(this, desc) }
|
||||||
|
return toItemStack(blob.bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: ItemStack,
|
||||||
|
) {
|
||||||
|
val byteArray = toByteArray(value)
|
||||||
|
val blob = ExposedBlob(byteArray)
|
||||||
|
entity.apply { column.setValue(this, desc, blob) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ItemStackExposedDelegateNullable(
|
||||||
|
public val column: Column<ExposedBlob?>,
|
||||||
|
) : ExposedDelegate<ItemStack?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): ItemStack? {
|
||||||
|
val blob = entity.run { column.getValue(this, desc) }
|
||||||
|
return blob?.bytes?.let { toItemStack(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: ItemStack?,
|
||||||
|
) {
|
||||||
|
val byteArray = value?.let { toByteArray(it) }
|
||||||
|
val blob = byteArray?.let { ExposedBlob(it) }
|
||||||
|
entity.apply { column.setValue(this, desc, blob) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toByteArray(item: ItemStack): ByteArray {
|
||||||
|
val outputStream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
BukkitObjectOutputStream(outputStream).use {
|
||||||
|
it.writeObject(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputStream.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toItemStack(byteArray: ByteArray): ItemStack {
|
||||||
|
return ByteArrayInputStream(byteArray).use {
|
||||||
|
BukkitObjectInputStream(it).use {
|
||||||
|
it.readObject() as ItemStack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.world
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun Entity<*>.location(column: Column<String>): ExposedDelegate<Location> = LocationExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("locationNullable")
|
||||||
|
public fun Entity<*>.location(column: Column<String?>): ExposedDelegate<Location?> = LocationExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public fun Entity<*>.location(
|
||||||
|
worldColumn: Column<String>,
|
||||||
|
xColumn: Column<Double>,
|
||||||
|
yColumn: Column<Double>,
|
||||||
|
zColumn: Column<Double>,
|
||||||
|
yawColumn: Column<Float>,
|
||||||
|
pitchColumn: Column<Float>,
|
||||||
|
): ExposedDelegate<Location> = LocationMultiColumnExposedDelegate(worldColumn, xColumn, yColumn, zColumn, yawColumn, pitchColumn)
|
||||||
|
|
||||||
|
@JvmName("locationNullable")
|
||||||
|
public fun Entity<*>.location(
|
||||||
|
worldColumn: Column<String?>,
|
||||||
|
xColumn: Column<Double?>,
|
||||||
|
yColumn: Column<Double?>,
|
||||||
|
zColumn: Column<Double?>,
|
||||||
|
yawColumn: Column<Float?>,
|
||||||
|
pitchColumn: Column<Float?>,
|
||||||
|
): ExposedDelegate<Location?> = LocationMultiColumnExposedDelegateNullable(worldColumn, xColumn, yColumn, zColumn, yawColumn, pitchColumn)
|
||||||
|
|
||||||
|
public class LocationExposedDelegate(
|
||||||
|
public val column: Column<String>,
|
||||||
|
) : ExposedDelegate<Location> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Location {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data.split(";")
|
||||||
|
return Location(
|
||||||
|
Bukkit.getWorld(slices[0]),
|
||||||
|
slices[1].toDouble(),
|
||||||
|
slices[2].toDouble(),
|
||||||
|
slices[3].toDouble(),
|
||||||
|
slices[4].toFloat(),
|
||||||
|
slices[5].toFloat(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Location,
|
||||||
|
) {
|
||||||
|
val world = requireNotNull(value.world) { "Location with nullable world can't be stored in database" }
|
||||||
|
val parsed = value.run { "${world.name};$x;$y;$z;$yaw;$pitch" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocationExposedDelegateNullable(
|
||||||
|
public val column: Column<String?>,
|
||||||
|
) : ExposedDelegate<Location?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Location? {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data?.split(";")
|
||||||
|
return slices?.let {
|
||||||
|
Location(
|
||||||
|
Bukkit.getWorld(it[0]),
|
||||||
|
it[1].toDouble(),
|
||||||
|
it[2].toDouble(),
|
||||||
|
it[3].toDouble(),
|
||||||
|
it[4].toFloat(),
|
||||||
|
it[5].toFloat(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Location?,
|
||||||
|
) {
|
||||||
|
val parsed = value?.run {
|
||||||
|
val world = requireNotNull(world) { "Location with nullable world can't be stored in database" }
|
||||||
|
"${world.name};$x;$y;$z;$yaw;$pitch"
|
||||||
|
}
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocationMultiColumnExposedDelegate(
|
||||||
|
public val worldColumn: Column<String>,
|
||||||
|
public val xColumn: Column<Double>,
|
||||||
|
public val yColumn: Column<Double>,
|
||||||
|
public val zColumn: Column<Double>,
|
||||||
|
public val yawColumn: Column<Float>,
|
||||||
|
public val pitchColumn: Column<Float>,
|
||||||
|
) : ExposedDelegate<Location> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Location {
|
||||||
|
val worldName = entity.run { worldColumn.getValue(this, desc) }
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val y = entity.run { yColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
val yaw = entity.run { yawColumn.getValue(this, desc) }
|
||||||
|
val pitch = entity.run { pitchColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return Location(
|
||||||
|
Bukkit.getWorld(worldName),
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Location,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
value.apply {
|
||||||
|
val world = requireNotNull(world) { "Location with nullable world can't be stored in database" }
|
||||||
|
worldColumn.setValue(entity, desc, world.name)
|
||||||
|
xColumn.setValue(entity, desc, x)
|
||||||
|
yColumn.setValue(entity, desc, y)
|
||||||
|
zColumn.setValue(entity, desc, z)
|
||||||
|
yawColumn.setValue(entity, desc, yaw)
|
||||||
|
pitchColumn.setValue(entity, desc, pitch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocationMultiColumnExposedDelegateNullable(
|
||||||
|
public val worldColumn: Column<String?>,
|
||||||
|
public val xColumn: Column<Double?>,
|
||||||
|
public val yColumn: Column<Double?>,
|
||||||
|
public val zColumn: Column<Double?>,
|
||||||
|
public val yawColumn: Column<Float?>,
|
||||||
|
public val pitchColumn: Column<Float?>,
|
||||||
|
) : ExposedDelegate<Location?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): Location? {
|
||||||
|
val worldName = entity.run { worldColumn.getValue(this, desc) }
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val y = entity.run { yColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
val yaw = entity.run { yawColumn.getValue(this, desc) }
|
||||||
|
val pitch = entity.run { pitchColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return if (
|
||||||
|
worldName != null &&
|
||||||
|
x != null && y != null && z != null &&
|
||||||
|
yaw != null && pitch != null
|
||||||
|
) {
|
||||||
|
Location(Bukkit.getWorld(worldName), x, y, z, yaw, pitch)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: Location?,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
worldColumn.setValue(entity, desc, value?.world?.name)
|
||||||
|
xColumn.setValue(entity, desc, value?.x)
|
||||||
|
yColumn.setValue(entity, desc, value?.y)
|
||||||
|
zColumn.setValue(entity, desc, value?.z)
|
||||||
|
yawColumn.setValue(entity, desc, value?.yaw)
|
||||||
|
pitchColumn.setValue(entity, desc, value?.pitch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+177
@@ -0,0 +1,177 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.LocationPos
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun Entity<*>.locationPos(column: Column<String>): ExposedDelegate<LocationPos> = LocationPosExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("locationPosNullable")
|
||||||
|
public fun Entity<*>.locationPos(column: Column<String?>): ExposedDelegate<LocationPos?> = LocationPosExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public fun Entity<*>.locationPos(
|
||||||
|
xColumn: Column<Double>,
|
||||||
|
yColumn: Column<Double>,
|
||||||
|
zColumn: Column<Double>,
|
||||||
|
yawColumn: Column<Float>,
|
||||||
|
pitchColumn: Column<Float>,
|
||||||
|
): ExposedDelegate<LocationPos> = LocationPosMultiColumnExposedDelegate(xColumn, yColumn, zColumn, yawColumn, pitchColumn)
|
||||||
|
|
||||||
|
@JvmName("locationPosNullable")
|
||||||
|
public fun Entity<*>.locationPos(
|
||||||
|
xColumn: Column<Double?>,
|
||||||
|
yColumn: Column<Double?>,
|
||||||
|
zColumn: Column<Double?>,
|
||||||
|
yawColumn: Column<Float?>,
|
||||||
|
pitchColumn: Column<Float?>,
|
||||||
|
): ExposedDelegate<LocationPos?> = LocationPosMultiColumnExposedDelegateNullable(xColumn, yColumn, zColumn, yawColumn, pitchColumn)
|
||||||
|
|
||||||
|
public class LocationPosExposedDelegate(
|
||||||
|
public val column: Column<String>,
|
||||||
|
) : ExposedDelegate<LocationPos> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): LocationPos {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data.split(";")
|
||||||
|
return LocationPos(
|
||||||
|
slices[0].toDouble(),
|
||||||
|
slices[1].toDouble(),
|
||||||
|
slices[2].toDouble(),
|
||||||
|
slices[3].toFloat(),
|
||||||
|
slices[4].toFloat(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: LocationPos,
|
||||||
|
) {
|
||||||
|
val parsed = value.run { "$x;$y;$z;$yaw;$pitch" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocationPosExposedDelegateNullable(
|
||||||
|
public val column: Column<String?>,
|
||||||
|
) : ExposedDelegate<LocationPos?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): LocationPos? {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
val slices = data?.split(";")
|
||||||
|
return slices?.let {
|
||||||
|
LocationPos(
|
||||||
|
it[0].toDouble(),
|
||||||
|
it[1].toDouble(),
|
||||||
|
it[2].toDouble(),
|
||||||
|
it[3].toFloat(),
|
||||||
|
it[4].toFloat(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: LocationPos?,
|
||||||
|
) {
|
||||||
|
val parsed = value?.run { "$x;$y;$z;$yaw;$pitch" }
|
||||||
|
entity.apply { column.setValue(this, desc, parsed) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocationPosMultiColumnExposedDelegate(
|
||||||
|
public val xColumn: Column<Double>,
|
||||||
|
public val yColumn: Column<Double>,
|
||||||
|
public val zColumn: Column<Double>,
|
||||||
|
public val yawColumn: Column<Float>,
|
||||||
|
public val pitchColumn: Column<Float>,
|
||||||
|
) : ExposedDelegate<LocationPos> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): LocationPos {
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val y = entity.run { yColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
val yaw = entity.run { yawColumn.getValue(this, desc) }
|
||||||
|
val pitch = entity.run { pitchColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return LocationPos(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: LocationPos,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
value.apply {
|
||||||
|
xColumn.setValue(entity, desc, x)
|
||||||
|
yColumn.setValue(entity, desc, y)
|
||||||
|
zColumn.setValue(entity, desc, z)
|
||||||
|
yawColumn.setValue(entity, desc, yaw)
|
||||||
|
pitchColumn.setValue(entity, desc, pitch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LocationPosMultiColumnExposedDelegateNullable(
|
||||||
|
public val xColumn: Column<Double?>,
|
||||||
|
public val yColumn: Column<Double?>,
|
||||||
|
public val zColumn: Column<Double?>,
|
||||||
|
public val yawColumn: Column<Float?>,
|
||||||
|
public val pitchColumn: Column<Float?>,
|
||||||
|
) : ExposedDelegate<LocationPos?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): LocationPos? {
|
||||||
|
val x = entity.run { xColumn.getValue(this, desc) }
|
||||||
|
val y = entity.run { yColumn.getValue(this, desc) }
|
||||||
|
val z = entity.run { zColumn.getValue(this, desc) }
|
||||||
|
val yaw = entity.run { yawColumn.getValue(this, desc) }
|
||||||
|
val pitch = entity.run { pitchColumn.getValue(this, desc) }
|
||||||
|
|
||||||
|
return if (
|
||||||
|
x != null && y != null && z != null &&
|
||||||
|
yaw != null && pitch != null
|
||||||
|
) {
|
||||||
|
LocationPos(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: LocationPos?,
|
||||||
|
) {
|
||||||
|
entity.apply {
|
||||||
|
xColumn.setValue(entity, desc, value?.x)
|
||||||
|
yColumn.setValue(entity, desc, value?.y)
|
||||||
|
zColumn.setValue(entity, desc, value?.z)
|
||||||
|
yawColumn.setValue(entity, desc, value?.yaw)
|
||||||
|
pitchColumn.setValue(entity, desc, value?.pitch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.offlinePlayer
|
||||||
|
import org.bukkit.OfflinePlayer
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun Entity<*>.offlinePlayer(column: Column<UUID>): ExposedDelegate<OfflinePlayer> = OfflinePlayerExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("offlinePlayerNullable")
|
||||||
|
public fun Entity<*>.offlinePlayer(column: Column<UUID?>): ExposedDelegate<OfflinePlayer?> = OfflinePlayerExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public class OfflinePlayerExposedDelegate(
|
||||||
|
public val column: Column<UUID>,
|
||||||
|
) : ExposedDelegate<OfflinePlayer> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): OfflinePlayer {
|
||||||
|
val uuid = entity.run { column.getValue(this, desc) }
|
||||||
|
return offlinePlayer(uuid)
|
||||||
|
}
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(entity: Entity<ID>, desc: KProperty<*>, value: OfflinePlayer) {
|
||||||
|
entity.apply { column.setValue(this, desc, value.uniqueId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OfflinePlayerExposedDelegateNullable(
|
||||||
|
public val column: Column<UUID?>,
|
||||||
|
) : ExposedDelegate<OfflinePlayer?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): OfflinePlayer? {
|
||||||
|
val uuid = entity.run { column.getValue(this, desc) }
|
||||||
|
return uuid?.let { offlinePlayer(it) }
|
||||||
|
}
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: OfflinePlayer?,
|
||||||
|
) {
|
||||||
|
entity.apply { column.setValue(this, desc, value?.uniqueId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.exposed.delegate
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.World
|
||||||
|
import org.jetbrains.exposed.dao.Entity
|
||||||
|
import org.jetbrains.exposed.sql.Column
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
public fun Entity<*>.world(column: Column<String>): ExposedDelegate<World> = WorldExposedDelegate(column)
|
||||||
|
|
||||||
|
@JvmName("worldNullable")
|
||||||
|
public fun Entity<*>.world(column: Column<String?>): ExposedDelegate<World?> = WorldExposedDelegateNullable(column)
|
||||||
|
|
||||||
|
public class WorldExposedDelegate(
|
||||||
|
public val column: Column<String>,
|
||||||
|
) : ExposedDelegate<World> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): World {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
return requireNotNull(Bukkit.getWorld(data)) { "World '$data' retrieving from database not loaded at server." }
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: World,
|
||||||
|
) {
|
||||||
|
entity.apply { column.setValue(this, desc, value.name) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WorldExposedDelegateNullable(
|
||||||
|
public val column: Column<String?>,
|
||||||
|
) : ExposedDelegate<World?> {
|
||||||
|
override operator fun <ID : Comparable<ID>> getValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
): World? {
|
||||||
|
val data = entity.run { column.getValue(this, desc) }
|
||||||
|
return data?.let { Bukkit.getWorld(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun <ID : Comparable<ID>> setValue(
|
||||||
|
entity: Entity<ID>,
|
||||||
|
desc: KProperty<*>,
|
||||||
|
value: World?,
|
||||||
|
) {
|
||||||
|
entity.apply { column.setValue(this, desc, value?.name) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
version=1.0.0.2
|
version=1.0.0.3
|
||||||
|
|
||||||
signing.gnupg.executable=***REMOVED***
|
signing.gnupg.executable=***REMOVED***
|
||||||
signing.gnupg.useLegacyGpg=false
|
signing.gnupg.useLegacyGpg=false
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
kotlin("jvm")
|
||||||
|
kotlin("plugin.serialization") version "1.8.21"
|
||||||
|
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||||
|
id("maven-publish")
|
||||||
|
id("signing")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "tech.carcadex"
|
||||||
|
val globalArtifactId = "kotlinbukkitkit-menu"
|
||||||
|
val globalName = "kotlinbukkitkit-menu"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
name = "spigotmc-repo"
|
||||||
|
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT")
|
||||||
|
api(project(":architecture"))
|
||||||
|
api(project(":extensions"))
|
||||||
|
api(project(":utility"))
|
||||||
|
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets.main {
|
||||||
|
kotlin.srcDir("build/generated/ksp/main/kotlin")
|
||||||
|
}
|
||||||
|
sourceSets.test {
|
||||||
|
kotlin.srcDir("build/generated/ksp/test/kotlin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withJavadocJar()
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("maven") {
|
||||||
|
artifactId = globalArtifactId
|
||||||
|
groupId = "tech.carcadex"
|
||||||
|
version = version
|
||||||
|
from(components["java"])
|
||||||
|
versionMapping {
|
||||||
|
usage("java-api") {
|
||||||
|
fromResolutionOf("runtimeClasspath")
|
||||||
|
}
|
||||||
|
usage("java-runtime") {
|
||||||
|
fromResolutionResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pom {
|
||||||
|
artifactId = globalArtifactId
|
||||||
|
groupId = "tech.carcadex"
|
||||||
|
version = version
|
||||||
|
name.set(globalArtifactId)
|
||||||
|
description.set(globalArtifactId)
|
||||||
|
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name.set("MIT")
|
||||||
|
url.set("https://mit-license.org/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id.set("itzRedTea")
|
||||||
|
name.set("Red Tea")
|
||||||
|
email.set("red__tea@outlook.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection.set("scm:https://github.com/CarcadeX/$globalName.git")
|
||||||
|
developerConnection.set("git@github.com:CarcadeX/$globalName.git")
|
||||||
|
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||||
|
val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
|
||||||
|
credentials {
|
||||||
|
username = "itzRedTea"
|
||||||
|
password = project.findProperty("password").toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signing {
|
||||||
|
useGpgCmd()
|
||||||
|
sign(publishing.publications.getByName("maven"))
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.Inventory
|
||||||
|
import org.bukkit.inventory.InventoryHolder
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public interface Menu<T : Slot> : WithPlugin<Plugin>, InventoryHolder {
|
||||||
|
|
||||||
|
public var title: String
|
||||||
|
public var lines: Int
|
||||||
|
public var cancelOnClick: Boolean
|
||||||
|
|
||||||
|
public val viewers: Map<Player, Inventory>
|
||||||
|
public val slots: TreeMap<Int, T>
|
||||||
|
|
||||||
|
public val data: WeakHashMap<String, Any>
|
||||||
|
public val playerData: WeakHashMap<Player, WeakHashMap<String, Any>>
|
||||||
|
|
||||||
|
public val eventHandler: MenuEventHandler
|
||||||
|
|
||||||
|
public var baseSlot: T
|
||||||
|
public var updateDelay: Long
|
||||||
|
|
||||||
|
public fun setSlot(slot: Int, slotObj: T)
|
||||||
|
|
||||||
|
public fun update(players: Set<Player> = viewers.keys)
|
||||||
|
public fun updateSlot(slot: Slot, players: Set<Player> = viewers.keys)
|
||||||
|
|
||||||
|
public fun update(vararg players: Player): Unit = update(players.toSet())
|
||||||
|
public fun updateSlot(slot: Slot, vararg players: Player): Unit = updateSlot(slot, players.toSet())
|
||||||
|
|
||||||
|
public fun openToPlayer(vararg players: Player)
|
||||||
|
|
||||||
|
public fun clearData() {
|
||||||
|
data.clear()
|
||||||
|
for (slot in slotsWithBaseSlot())
|
||||||
|
slot.clearSlotData()
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun clearPlayerData(player: Player) {
|
||||||
|
playerData.remove(player)
|
||||||
|
for (slot in slotsWithBaseSlot())
|
||||||
|
slot.clearPlayerData(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun close(player: Player, closeInventory: Boolean = true)
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.architecture.PluginDisableAware
|
||||||
|
import tech.carcadex.kotlinbukkitkit.architecture.PluginDisableAwareController
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.KListener
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.onlinePlayers
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.registerEvents
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent
|
||||||
|
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||||
|
import org.bukkit.event.inventory.InventoryDragEvent
|
||||||
|
import org.bukkit.event.inventory.InventoryType
|
||||||
|
import org.bukkit.event.player.PlayerPickupItemEvent
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
|
||||||
|
internal object MenuControllerPlugins : PluginDisableAwareController<MenuController>() {
|
||||||
|
override val factory: (plugin: Plugin) -> MenuController = { MenuController(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MenuController(
|
||||||
|
override val plugin: Plugin,
|
||||||
|
) : KListener<Plugin>, PluginDisableAware {
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerEvents(plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisable() {
|
||||||
|
onlinePlayers.forEach {
|
||||||
|
val menu = getMenuFromPlayer(it)
|
||||||
|
?.takeIf { it.plugin.name == plugin.name }
|
||||||
|
menu?.close(it, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true)
|
||||||
|
fun clickInventoryEvent(event: InventoryClickEvent) {
|
||||||
|
if (event.view.type == InventoryType.CHEST) {
|
||||||
|
val player = event.whoClicked as Player
|
||||||
|
val inv = event.inventory
|
||||||
|
|
||||||
|
val menu = getMenuFromInventory(event.inventory)?.takeIfHasPlayer(player)
|
||||||
|
|
||||||
|
if (menu != null) {
|
||||||
|
if (menu.plugin.name != plugin.name) return
|
||||||
|
|
||||||
|
if (event.rawSlot == event.slot) {
|
||||||
|
val clickedSlot = event.slot + 1
|
||||||
|
val slot = menu.slotOrBaseSlot(clickedSlot)
|
||||||
|
|
||||||
|
val interact = MenuPlayerSlotInteract(
|
||||||
|
menu, clickedSlot, slot,
|
||||||
|
player, inv, slot.cancel,
|
||||||
|
event.click, event.action,
|
||||||
|
event.currentItem, event.cursor,
|
||||||
|
event.hotbarButton,
|
||||||
|
)
|
||||||
|
|
||||||
|
slot.eventHandler.interact(interact)
|
||||||
|
|
||||||
|
if (interact.canceled) event.isCancelled = true
|
||||||
|
} else {
|
||||||
|
// TODO move item
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true)
|
||||||
|
fun clickInteractEvent(event: InventoryDragEvent) {
|
||||||
|
if (event.view.type == InventoryType.CHEST) {
|
||||||
|
val player = event.whoClicked as Player
|
||||||
|
val menu = getMenuFromInventory(event.inventory)?.takeIfHasPlayer(player)
|
||||||
|
if (menu != null) {
|
||||||
|
if (menu.plugin.name != plugin.name) return
|
||||||
|
val pass = event.inventorySlots.firstOrNull { it in event.rawSlots }
|
||||||
|
if (pass != null) event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onClose(event: InventoryCloseEvent) {
|
||||||
|
if (event.view.type == InventoryType.CHEST) {
|
||||||
|
val player = event.player as Player
|
||||||
|
val menu = getMenuFromPlayer(player)
|
||||||
|
if (menu != null) {
|
||||||
|
if (menu.plugin.name != plugin.name) return
|
||||||
|
menu.close(player, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true)
|
||||||
|
fun onPickupItemEvent(event: PlayerPickupItemEvent) {
|
||||||
|
val menu = getMenuFromPlayer(event.player)
|
||||||
|
if (menu != null) {
|
||||||
|
if (menu.plugin.name != plugin.name) return
|
||||||
|
event.isCancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu
|
||||||
|
|
||||||
|
public interface MenuEventHandler {
|
||||||
|
|
||||||
|
public fun update(update: MenuPlayerUpdate)
|
||||||
|
|
||||||
|
public fun close(close: MenuPlayerClose)
|
||||||
|
|
||||||
|
public fun moveToMenu(moveToMenu: MenuPlayerMoveTo)
|
||||||
|
|
||||||
|
public fun preOpen(preOpen: MenuPlayerPreOpen)
|
||||||
|
|
||||||
|
public fun open(open: MenuPlayerOpen)
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.Inventory
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public interface MenuPlayer {
|
||||||
|
public val menu: Menu<*>
|
||||||
|
public val player: Player
|
||||||
|
|
||||||
|
public fun putPlayerData(key: String, value: Any) {
|
||||||
|
val map = menu.playerData[player]
|
||||||
|
if (map != null) {
|
||||||
|
map[key] = value
|
||||||
|
} else menu.playerData[player] = WeakHashMap<String, Any>().apply { put(key, value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getPlayerData(key: String): Any? = menu.playerData.get(player)?.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MenuPlayerInventory : MenuPlayer {
|
||||||
|
public val inventory: Inventory
|
||||||
|
|
||||||
|
public fun close(): Unit = player.closeInventory()
|
||||||
|
|
||||||
|
public fun getItem(line: Int, slot: Int): ItemStack? {
|
||||||
|
return getItem(calculateSlot(line, slot))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getItem(slot: Int): ItemStack? = inventory.getItem(slot - 1)
|
||||||
|
|
||||||
|
public fun setItem(line: Int, slot: Int, item: ItemStack?) {
|
||||||
|
setItem(calculateSlot(line, slot), item)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun setItem(slot: Int, item: ItemStack?) {
|
||||||
|
inventory.setItem(rawSlot(slot), item)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getPlayerItem(line: Int, slot: Int): ItemStack? {
|
||||||
|
return getPlayerItem(calculateSlot(line, slot))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getPlayerItem(slot: Int): ItemStack? = player.inventory.getItem(rawSlot(slot))
|
||||||
|
|
||||||
|
public fun setPlayerItem(line: Int, slot: Int, item: ItemStack?) {
|
||||||
|
setPlayerItem(calculateSlot(line, slot), item)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun setPlayerItem(slot: Int, item: ItemStack?) {
|
||||||
|
player.inventory.setItem(rawSlot(slot), item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLineRange(line: Int) = calculateSlot(line, 1)..calculateEndLine(line)
|
||||||
|
|
||||||
|
public fun getLine(line: Int): List<ItemStack?> = getLineRange(line).map { getItem(it) }
|
||||||
|
|
||||||
|
public fun getPlayerLine(line: Int): List<ItemStack?> = getLineRange(line).map { getPlayerItem(it) }
|
||||||
|
|
||||||
|
public fun updateToPlayer() {
|
||||||
|
menu.update(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MenuPlayerCancellable : MenuPlayer {
|
||||||
|
public var canceled: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class MenuPlayerInteract(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val player: Player,
|
||||||
|
override val inventory: Inventory,
|
||||||
|
override var canceled: Boolean,
|
||||||
|
) : MenuPlayerInventory, MenuPlayerCancellable
|
||||||
|
|
||||||
|
public class MenuPlayerPreOpen(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val player: Player,
|
||||||
|
override var canceled: Boolean = false,
|
||||||
|
) : MenuPlayerCancellable
|
||||||
|
|
||||||
|
public class MenuPlayerOpen(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val player: Player,
|
||||||
|
override val inventory: Inventory,
|
||||||
|
) : MenuPlayerInventory
|
||||||
|
|
||||||
|
public class MenuPlayerUpdate(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val player: Player,
|
||||||
|
override val inventory: Inventory,
|
||||||
|
public var title: String,
|
||||||
|
) : MenuPlayerInventory
|
||||||
|
|
||||||
|
public class MenuPlayerClose(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val player: Player,
|
||||||
|
) : MenuPlayer
|
||||||
|
|
||||||
|
public interface MenuPlayerMove : MenuPlayerInventory, MenuPlayerCancellable {
|
||||||
|
public val toMoveSlot: Int
|
||||||
|
public val toMoveItem: ItemStack?
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MenuPlayerMoveTo(
|
||||||
|
menu: Menu<*>,
|
||||||
|
player: Player,
|
||||||
|
inventory: Inventory,
|
||||||
|
canceled: Boolean,
|
||||||
|
override val toMoveSlot: Int,
|
||||||
|
override val toMoveItem: ItemStack?,
|
||||||
|
public var targetSlot: Int,
|
||||||
|
) : MenuPlayerInteract(menu, player, inventory, canceled), MenuPlayerMove {
|
||||||
|
|
||||||
|
public var targetCurrentItem: ItemStack?
|
||||||
|
set(value) {
|
||||||
|
setItem(targetSlot, value)
|
||||||
|
}
|
||||||
|
get() = getItem(targetSlot)
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.Inventory
|
||||||
|
|
||||||
|
public fun calculateSlot(line: Int, slot: Int): Int = calculateStartLine(line) + slot
|
||||||
|
|
||||||
|
public fun calculateStartLine(line: Int): Int = calculateEndLine(line) - 9
|
||||||
|
public fun calculateEndLine(line: Int): Int = line * 9
|
||||||
|
|
||||||
|
public fun rawSlot(slot: Int): Int = slot - 1
|
||||||
|
|
||||||
|
public fun Menu<*>.slotOrBaseSlot(slot: Int): Slot = slots[slot] ?: baseSlot
|
||||||
|
public fun Menu<*>.rangeOfSlots(): IntRange = 1..calculateEndLine(lines)
|
||||||
|
public fun Menu<*>.viewersFromPlayers(players: Set<Player>): Map<Player, Inventory> = viewers.filterKeys { it in players }
|
||||||
|
|
||||||
|
public fun Menu<*>.slotsWithBaseSlot(): List<Slot> = slots.values + baseSlot
|
||||||
|
|
||||||
|
public fun Menu<*>.hasPlayer(player: Player): Boolean = viewers.containsKey(player)
|
||||||
|
public fun Menu<*>.takeIfHasPlayer(player: Player): Menu<*>? = if (hasPlayer(player)) this else null
|
||||||
|
|
||||||
|
public fun inventoryIsMenu(inventory: Inventory): Boolean = inventory.holder is Menu<*>
|
||||||
|
|
||||||
|
public fun getMenuFromInventory(inventory: Inventory): Menu<*>? = inventory.holder as? Menu<*>
|
||||||
|
|
||||||
|
public fun getMenuFromPlayer(player: Player): Menu<*>? {
|
||||||
|
return getMenuFromInventory(player.openInventory.topInventory)?.takeIfHasPlayer(player)
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public fun MenuDSL.putPlayerData(player: Player, key: String, value: Any): Any? = playerData.getOrPut(player, { WeakHashMap() }).put(key, value)
|
||||||
|
|
||||||
|
public fun MenuDSL.getPlayerData(player: Player, key: String): Any? = playerData.get(player)?.get(key)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.Menu
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.calculateSlot
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
|
public inline fun MenuDSL.slot(
|
||||||
|
line: Int,
|
||||||
|
slot: Int,
|
||||||
|
item: ItemStack?,
|
||||||
|
block: SlotDSL.() -> Unit = {},
|
||||||
|
): SlotDSL = slot(calculateSlot(line, slot), item, block)
|
||||||
|
|
||||||
|
public inline fun MenuDSL.slot(
|
||||||
|
slot: Int,
|
||||||
|
item: ItemStack?,
|
||||||
|
block: SlotDSL.() -> Unit = {},
|
||||||
|
): SlotDSL = baseSlot.clone(item).apply(block).also {
|
||||||
|
setSlot(slot, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MenuDSL : Menu<SlotDSL> {
|
||||||
|
|
||||||
|
override val eventHandler: MenuEventHandlerDSL
|
||||||
|
|
||||||
|
public fun onUpdate(update: MenuPlayerUpdateEvent) {
|
||||||
|
eventHandler.updateCallbacks.add(update)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onClose(close: MenuPlayerCloseEvent) {
|
||||||
|
eventHandler.closeCallbacks.add(close)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onMoveToMenu(moveToMenu: MenuPlayerMoveToEvent) {
|
||||||
|
eventHandler.moveToMenuCallbacks.add(moveToMenu)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun preOpen(preOpen: MenuPlayerPreOpenEvent) {
|
||||||
|
eventHandler.preOpenCallbacks.add(preOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onOpen(open: MenuPlayerOpenEvent) {
|
||||||
|
eventHandler.openCallbacks.add(open)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.task
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuControllerPlugins
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerClose
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerOpen
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerPreOpen
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerUpdate
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSLImpl
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotEventHandlerDSL
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.rangeOfSlots
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slotOrBaseSlot
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.viewersFromPlayers
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.Inventory
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public inline fun WithPlugin<*>.menu(
|
||||||
|
displayName: String,
|
||||||
|
lines: Int,
|
||||||
|
cancelOnClick: Boolean = true,
|
||||||
|
block: MenuDSL.() -> Unit,
|
||||||
|
): MenuDSL = plugin.menu(displayName, lines, cancelOnClick, block)
|
||||||
|
|
||||||
|
public inline fun Plugin.menu(
|
||||||
|
displayName: String,
|
||||||
|
lines: Int,
|
||||||
|
cancelOnClick: Boolean = true,
|
||||||
|
block: MenuDSL.() -> Unit,
|
||||||
|
): MenuDSL = menu(displayName, lines, this, cancelOnClick, block)
|
||||||
|
|
||||||
|
public inline fun menu(
|
||||||
|
displayName: String,
|
||||||
|
lines: Int,
|
||||||
|
plugin: Plugin,
|
||||||
|
cancelOnClick: Boolean = true,
|
||||||
|
block: MenuDSL.() -> Unit,
|
||||||
|
): MenuDSL = MenuDSLImpl(plugin, displayName, lines, cancelOnClick).apply(block)
|
||||||
|
|
||||||
|
public class MenuDSLImpl(
|
||||||
|
public override val plugin: Plugin,
|
||||||
|
override var title: String,
|
||||||
|
override var lines: Int,
|
||||||
|
override var cancelOnClick: Boolean,
|
||||||
|
) : MenuDSL {
|
||||||
|
|
||||||
|
init {
|
||||||
|
MenuControllerPlugins.ensureInitialized(plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var task: BukkitTask? = null
|
||||||
|
override var updateDelay: Long = 0
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
removeTask()
|
||||||
|
if (value > 0 && viewers.isNotEmpty()) {
|
||||||
|
setNewTask()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val viewers: MutableMap<Player, Inventory> = WeakHashMap()
|
||||||
|
override val slots: TreeMap<Int, SlotDSL> = TreeMap()
|
||||||
|
|
||||||
|
override val data: WeakHashMap<String, Any> = WeakHashMap<String, Any>()
|
||||||
|
override val playerData: WeakHashMap<Player, WeakHashMap<String, Any>> = WeakHashMap<Player, WeakHashMap<String, Any>>()
|
||||||
|
|
||||||
|
override val eventHandler: MenuEventHandlerDSL = MenuEventHandlerDSL(this)
|
||||||
|
|
||||||
|
override var baseSlot: SlotDSL = SlotDSLImpl(null, cancelOnClick, SlotEventHandlerDSL())
|
||||||
|
|
||||||
|
override fun setSlot(slot: Int, slotObj: SlotDSL) {
|
||||||
|
slots.put(slot, slotObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(players: Set<Player>) {
|
||||||
|
val viewers = viewersFromPlayers(players)
|
||||||
|
for ((player, inventory) in viewers) {
|
||||||
|
val update = MenuPlayerUpdate(this, player, inventory, title)
|
||||||
|
eventHandler.update(update)
|
||||||
|
|
||||||
|
// TODO title
|
||||||
|
/*if(update.title != title)*/
|
||||||
|
|
||||||
|
for (i in rangeOfSlots()) {
|
||||||
|
val slot = slotOrBaseSlot(i)
|
||||||
|
updateSlotOnlyPos(i, slot, player, inventory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateSlot(slot: Slot, players: Set<Player>) {
|
||||||
|
val slots: Map<Int, SlotDSL> = if (slot === baseSlot) {
|
||||||
|
rangeOfSlots().mapNotNull { if (slots[it] == null) it to slot else null }.toMap()
|
||||||
|
} else {
|
||||||
|
rangeOfSlots().mapNotNull { if (slot === slots[it]) it to slot else null }.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((player, inventory) in viewersFromPlayers(players)) {
|
||||||
|
for ((pos, s) in slots) {
|
||||||
|
updateSlotOnlyPos(pos, s, player, inventory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSlotOnlyPos(pos: Int, slot: Slot, player: Player, inventory: Inventory) {
|
||||||
|
val slotUpdate = MenuPlayerSlotUpdate(this, pos, slot, player, inventory)
|
||||||
|
slot.eventHandler.update(slotUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openToPlayer(vararg players: Player) {
|
||||||
|
for (player in players) {
|
||||||
|
close(player, false)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val inv = inventory
|
||||||
|
|
||||||
|
viewers.put(player, inv)
|
||||||
|
|
||||||
|
val preOpen = MenuPlayerPreOpen(this, player)
|
||||||
|
eventHandler.preOpen(preOpen)
|
||||||
|
|
||||||
|
if (preOpen.canceled) return
|
||||||
|
|
||||||
|
for (i in rangeOfSlots()) {
|
||||||
|
val slot = slotOrBaseSlot(i)
|
||||||
|
|
||||||
|
val render = MenuPlayerSlotRender(this, i, slot, player, inv)
|
||||||
|
|
||||||
|
slot.eventHandler.render(render)
|
||||||
|
}
|
||||||
|
|
||||||
|
player.openInventory(inv)
|
||||||
|
|
||||||
|
val open = MenuPlayerOpen(this, player, inventory)
|
||||||
|
eventHandler.open(open)
|
||||||
|
|
||||||
|
if (task == null && updateDelay > 0 && viewers.isNotEmpty()) {
|
||||||
|
setNewTask()
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
removePlayer(player, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getInventory(): Inventory {
|
||||||
|
val slots = rangeOfSlots()
|
||||||
|
val inventory = Bukkit.createInventory(this, slots.endInclusive, title)
|
||||||
|
|
||||||
|
for (i in slots) {
|
||||||
|
val slot = slotOrBaseSlot(i)
|
||||||
|
|
||||||
|
val item = slot.item?.clone()
|
||||||
|
inventory.setItem(i - 1, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return inventory
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removePlayer(player: Player, closeInventory: Boolean): Boolean {
|
||||||
|
if (closeInventory) player.closeInventory()
|
||||||
|
|
||||||
|
val viewing = viewers.remove(player) != null
|
||||||
|
if (viewing) {
|
||||||
|
clearPlayerData(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close(player: Player, closeInventory: Boolean) {
|
||||||
|
if (removePlayer(player, closeInventory)) {
|
||||||
|
val menuClose = MenuPlayerClose(this, player)
|
||||||
|
eventHandler.close(menuClose)
|
||||||
|
|
||||||
|
if (task != null && updateDelay > 0 && viewers.isEmpty()) {
|
||||||
|
removeTask()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setNewTask() {
|
||||||
|
task = task(repeatDelay = updateDelay) { update() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeTask() {
|
||||||
|
task?.cancel()
|
||||||
|
task = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuEventHandler
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerClose
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerMoveTo
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerOpen
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerPreOpen
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerUpdate
|
||||||
|
|
||||||
|
public typealias MenuPlayerUpdateEvent = MenuPlayerUpdate.() -> Unit
|
||||||
|
public typealias MenuPlayerCloseEvent = MenuPlayerClose.() -> Unit
|
||||||
|
public typealias MenuPlayerMoveToEvent = MenuPlayerMoveTo.() -> Unit
|
||||||
|
|
||||||
|
public typealias MenuPlayerPreOpenEvent = MenuPlayerPreOpen.() -> Unit
|
||||||
|
public typealias MenuPlayerOpenEvent = MenuPlayerOpen.() -> Unit
|
||||||
|
|
||||||
|
public class MenuEventHandlerDSL(public val menu: MenuDSL) : MenuEventHandler {
|
||||||
|
|
||||||
|
internal val updateCallbacks: MutableList<MenuPlayerUpdateEvent> = mutableListOf()
|
||||||
|
internal val closeCallbacks: MutableList<MenuPlayerCloseEvent> = mutableListOf()
|
||||||
|
internal val moveToMenuCallbacks: MutableList<MenuPlayerMoveToEvent> = mutableListOf()
|
||||||
|
internal val preOpenCallbacks: MutableList<MenuPlayerPreOpenEvent> = mutableListOf()
|
||||||
|
internal val openCallbacks: MutableList<MenuPlayerOpenEvent> = mutableListOf()
|
||||||
|
|
||||||
|
override fun update(update: MenuPlayerUpdate) {
|
||||||
|
for (callback in updateCallbacks) {
|
||||||
|
callback(update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close(close: MenuPlayerClose) {
|
||||||
|
for (callback in closeCallbacks) {
|
||||||
|
callback(close)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun moveToMenu(moveToMenu: MenuPlayerMoveTo) {
|
||||||
|
for (callback in moveToMenuCallbacks) {
|
||||||
|
callback(moveToMenu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun preOpen(preOpen: MenuPlayerPreOpen) {
|
||||||
|
for (callback in preOpenCallbacks) {
|
||||||
|
callback(preOpen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun open(open: MenuPlayerOpen) {
|
||||||
|
for (callback in openCallbacks) {
|
||||||
|
callback(open)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSL
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.collections.ObservableCollection
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.lang.IllegalArgumentException
|
||||||
|
import java.util.TreeMap
|
||||||
|
import java.util.WeakHashMap
|
||||||
|
|
||||||
|
public typealias ItemsProvider<T> = () -> ObservableCollection<T>
|
||||||
|
public typealias ItemsAdapter<T> = MenuPlayer.(List<T>) -> List<T>
|
||||||
|
|
||||||
|
public enum class Orientation { HORIZONTAL, VERTICAL }
|
||||||
|
|
||||||
|
public inline fun <T> MenuPagination<T>.slot(
|
||||||
|
builder: PaginationSlotDSL<T>.() -> Unit,
|
||||||
|
) {
|
||||||
|
for (paginationSlot in paginationSlots.values) {
|
||||||
|
paginationSlot.builder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun MenuDSL.setPlayerOpenPage(player: Player, page: Int) {
|
||||||
|
playerData.put(
|
||||||
|
player,
|
||||||
|
WeakHashMap<String, Any>().apply { put(PAGINATION_OPEN_PAGE_KEY, page) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
public inline fun <T> MenuDSL.pagination(
|
||||||
|
itemsProvider: ObservableCollection<T>,
|
||||||
|
nextPageSlot: SlotDSL,
|
||||||
|
previousPageSlot: SlotDSL,
|
||||||
|
autoUpdateSwitchPageSlot: Boolean = true,
|
||||||
|
startLine: Int = 1,
|
||||||
|
endLine: Int = lines - 1,
|
||||||
|
startSlot: Int = 1,
|
||||||
|
endSlot: Int = 9,
|
||||||
|
orientation: Orientation = Orientation.HORIZONTAL,
|
||||||
|
noinline itemsAdapterOnOpen: ItemsAdapter<T>? = null,
|
||||||
|
noinline itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
|
||||||
|
builder: MenuPaginationImpl<T>.() -> Unit,
|
||||||
|
): MenuPaginationImpl<T> {
|
||||||
|
return pagination(
|
||||||
|
{ itemsProvider },
|
||||||
|
nextPageSlot,
|
||||||
|
previousPageSlot,
|
||||||
|
autoUpdateSwitchPageSlot,
|
||||||
|
startLine,
|
||||||
|
endLine,
|
||||||
|
startSlot,
|
||||||
|
endSlot,
|
||||||
|
orientation,
|
||||||
|
itemsAdapterOnOpen,
|
||||||
|
itemsAdapterOnUpdate,
|
||||||
|
builder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun <T> MenuDSL.pagination(
|
||||||
|
noinline itemsProvider: ItemsProvider<T>,
|
||||||
|
nextPageSlot: SlotDSL,
|
||||||
|
previousPageSlot: SlotDSL,
|
||||||
|
autoUpdateSwitchPageSlot: Boolean = true,
|
||||||
|
startLine: Int = 1,
|
||||||
|
endLine: Int = lines - 1,
|
||||||
|
startSlot: Int = 1,
|
||||||
|
endSlot: Int = 9,
|
||||||
|
orientation: Orientation = Orientation.HORIZONTAL,
|
||||||
|
noinline itemsAdapterOnOpen: ItemsAdapter<T>? = null,
|
||||||
|
noinline itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
|
||||||
|
builder: MenuPaginationImpl<T>.() -> Unit,
|
||||||
|
): MenuPaginationImpl<T> {
|
||||||
|
if (startSlot > endSlot) throw IllegalArgumentException()
|
||||||
|
if (startLine > endLine) throw IllegalArgumentException()
|
||||||
|
|
||||||
|
return MenuPaginationImpl(
|
||||||
|
this,
|
||||||
|
itemsProvider,
|
||||||
|
nextPageSlot,
|
||||||
|
previousPageSlot,
|
||||||
|
autoUpdateSwitchPageSlot,
|
||||||
|
startLine,
|
||||||
|
endLine,
|
||||||
|
startSlot,
|
||||||
|
endSlot,
|
||||||
|
orientation,
|
||||||
|
itemsAdapterOnOpen,
|
||||||
|
itemsAdapterOnUpdate,
|
||||||
|
).apply(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MenuPagination<T> {
|
||||||
|
public val menu: MenuDSL
|
||||||
|
public val paginationSlots: TreeMap<Int, PaginationSlotDSL<T>>
|
||||||
|
public val paginationEventHandler: PaginationEventHandler
|
||||||
|
|
||||||
|
public val itemsProvider: ItemsProvider<T>
|
||||||
|
|
||||||
|
public val nextPageSlot: SlotDSL
|
||||||
|
public val previousPageSlot: SlotDSL
|
||||||
|
|
||||||
|
public val autoUpdateSwitchPageSlot: Boolean
|
||||||
|
|
||||||
|
public val startLine: Int
|
||||||
|
public val endLine: Int
|
||||||
|
|
||||||
|
public val startSlot: Int
|
||||||
|
public val endSlot: Int
|
||||||
|
|
||||||
|
public val orientation: Orientation
|
||||||
|
|
||||||
|
public val itemsAdapterOnOpen: ItemsAdapter<T>?
|
||||||
|
public val itemsAdapterOnUpdate: ItemsAdapter<T>?
|
||||||
|
|
||||||
|
public fun onPageChange(pageChange: MenuPlayerPageChangeEvent) {
|
||||||
|
paginationEventHandler.pageChangeCallbacks.add(pageChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onPageAvailable(pageAvailable: MenuPlayerPageAvailableEvent) {
|
||||||
|
paginationEventHandler.pageAvailableCallbacks.add(pageAvailable)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun hasPreviousPage(player: Player): Boolean
|
||||||
|
|
||||||
|
public fun hasNextPage(player: Player): Boolean
|
||||||
|
|
||||||
|
public fun getPlayerCurrentPage(player: Player): Int
|
||||||
|
|
||||||
|
public fun updateItemsToPlayer(menuPlayer: MenuPlayerInventory)
|
||||||
|
}
|
||||||
+265
@@ -0,0 +1,265 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.calculateSlot
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSL
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot.PaginationSlotDSLImpl
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
internal const val PAGINATION_OPEN_PAGE_KEY = "PAGINATION:open_page"
|
||||||
|
|
||||||
|
public class MenuPaginationImpl<T>(
|
||||||
|
override val menu: MenuDSL,
|
||||||
|
override val itemsProvider: ItemsProvider<T>,
|
||||||
|
override val nextPageSlot: SlotDSL,
|
||||||
|
override val previousPageSlot: SlotDSL,
|
||||||
|
override val autoUpdateSwitchPageSlot: Boolean,
|
||||||
|
override val startLine: Int = 1,
|
||||||
|
override val endLine: Int = menu.lines - 1,
|
||||||
|
override val startSlot: Int = 1,
|
||||||
|
override val endSlot: Int = 9,
|
||||||
|
override val orientation: Orientation = Orientation.HORIZONTAL,
|
||||||
|
override val itemsAdapterOnOpen: ItemsAdapter<T>? = null,
|
||||||
|
override val itemsAdapterOnUpdate: ItemsAdapter<T>? = null,
|
||||||
|
) : MenuPagination<T> {
|
||||||
|
override val paginationEventHandler: PaginationEventHandler = PaginationEventHandler()
|
||||||
|
|
||||||
|
override val paginationSlots: TreeMap<Int, PaginationSlotDSL<T>> = TreeMap()
|
||||||
|
internal val currentPlayerPage = WeakHashMap<Player, Int>()
|
||||||
|
|
||||||
|
// should be null when is not mapped (for not allocate a new list everytime)
|
||||||
|
internal val currentPlayerItems = WeakHashMap<Player, List<T>>()
|
||||||
|
|
||||||
|
internal val itemSlotData = WeakHashMap<T, WeakHashMap<String, Any>>()
|
||||||
|
internal val itemPlayerSlotData = WeakHashMap<T, WeakHashMap<Player, WeakHashMap<String, Any>>>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
// setup next page button click
|
||||||
|
nextPageSlot.onClick {
|
||||||
|
if (hasNextPage(player)) {
|
||||||
|
nextPage(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup previous page button click
|
||||||
|
previousPageSlot.onClick {
|
||||||
|
if (hasPreviousPage(player)) {
|
||||||
|
previousPage(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.preOpen {
|
||||||
|
// adapting items for player
|
||||||
|
val items = itemsAdapterOnOpen?.let { it(getPlayerItems(player).toList()) }
|
||||||
|
if (items != null) {
|
||||||
|
currentPlayerItems[player] = items
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the menu page if player informed in [MenuDSL.setPlayerOpenPage]
|
||||||
|
val openPage = menu.playerData.get(player)?.get(PAGINATION_OPEN_PAGE_KEY) as? Int?
|
||||||
|
if (openPage != null) {
|
||||||
|
// check if openPage is valid or cancel and trigger a event
|
||||||
|
if (isPageAvailable(player, openPage)) {
|
||||||
|
currentPlayerPage[player] = openPage
|
||||||
|
} else {
|
||||||
|
canceled = true
|
||||||
|
paginationEventHandler.pageAvailable(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleaning player data on menu close
|
||||||
|
menu.onClose {
|
||||||
|
currentPlayerPage.remove(player)
|
||||||
|
currentPlayerItems.remove(player)
|
||||||
|
|
||||||
|
for (value in itemPlayerSlotData.values) {
|
||||||
|
value.remove(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if enabled, update next and previous slots when page change
|
||||||
|
if (autoUpdateSwitchPageSlot) {
|
||||||
|
onPageChange {
|
||||||
|
menu.updateSlot(nextPageSlot, player)
|
||||||
|
menu.updateSlot(previousPageSlot, player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup pagination slots
|
||||||
|
for (line in startLine..endLine) {
|
||||||
|
for (slotPos in startSlot..endSlot) {
|
||||||
|
val menuSlot = calculateSlot(line, slotPos)
|
||||||
|
val slotRoot = menu.slot(menuSlot, null)
|
||||||
|
|
||||||
|
val paginationSlot = PaginationSlotDSLImpl(
|
||||||
|
this,
|
||||||
|
slotRoot,
|
||||||
|
)
|
||||||
|
|
||||||
|
slotRoot.apply {
|
||||||
|
fun getCurrentItemForPlayer(player: Player): T? {
|
||||||
|
return getCurrentItemForPlayer(
|
||||||
|
menuSlot,
|
||||||
|
getPlayerCurrentPage(player),
|
||||||
|
player,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onRender {
|
||||||
|
paginationSlot.paginationEventHandler.handleRender(
|
||||||
|
getCurrentItemForPlayer(player),
|
||||||
|
this,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onUpdate {
|
||||||
|
paginationSlot.paginationEventHandler.handleUpdate(
|
||||||
|
getCurrentItemForPlayer(player),
|
||||||
|
this,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick {
|
||||||
|
paginationSlot.paginationEventHandler.handleInteract(
|
||||||
|
getCurrentItemForPlayer(player),
|
||||||
|
this,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paginationSlots.put(menuSlot, paginationSlot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasPreviousPage(player: Player): Boolean {
|
||||||
|
return isPageAvailable(player, getPlayerCurrentPage(player) - 1)
|
||||||
|
}
|
||||||
|
override fun hasNextPage(player: Player): Boolean {
|
||||||
|
return isPageAvailable(player, getPlayerCurrentPage(player) + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPlayerCurrentPage(player: Player): Int = currentPlayerPage.getOrDefault(player, 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update current item list calling itemsUpdateFilter
|
||||||
|
*/
|
||||||
|
override fun updateItemsToPlayer(menuPlayer: MenuPlayerInventory) {
|
||||||
|
val player = menuPlayer.player
|
||||||
|
|
||||||
|
// checking if adapter is not null, other wise, ignore
|
||||||
|
if (itemsAdapterOnUpdate != null) {
|
||||||
|
val items = itemsAdapterOnUpdate.let { menuPlayer.it(itemsProvider().toList()) }
|
||||||
|
|
||||||
|
currentPlayerItems[player] = items
|
||||||
|
|
||||||
|
// setting player to the last page if he is not in a page with items after adapting
|
||||||
|
if (!isPageAvailable(player, getPlayerCurrentPage(player))) {
|
||||||
|
currentPlayerPage[player] = maxPages(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
// calling update slot to the PaginationSlotDSL
|
||||||
|
forEachSlot { slotPos, pageSlot ->
|
||||||
|
val currentPage = getPlayerCurrentPage(player)
|
||||||
|
|
||||||
|
val item: T? = getCurrentItemForPlayer(slotPos, currentPage, player)
|
||||||
|
|
||||||
|
pageSlot.updateSlot(item, item, slotPos, menuPlayer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// calling event because this remove items causing no more page needed
|
||||||
|
paginationEventHandler.pageChange(menuPlayer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getCurrentItemForPlayer(slotPos: Int, page: Int, player: Player): T? {
|
||||||
|
val items = getPlayerItems(player)
|
||||||
|
|
||||||
|
val startLineNotUsage = startLine - 1
|
||||||
|
val lineEndSlotNotUsage = 9 - endSlot
|
||||||
|
val startSlotNotUsage = startSlot - 1
|
||||||
|
|
||||||
|
val startLineSlotsUsage = startLineNotUsage * 9
|
||||||
|
val currentLine = (slotPos / 9) + 1
|
||||||
|
val currentLineUsage = currentLine - startLineNotUsage
|
||||||
|
|
||||||
|
val currentUsageStartSlots = startSlotNotUsage * currentLineUsage
|
||||||
|
val currentEndSlotUsage = if (currentLineUsage > 1) lineEndSlotNotUsage * (currentLineUsage - 1) else 0
|
||||||
|
|
||||||
|
val slotItemIndex = pageStartIndex(page) + (slotPos - startLineSlotsUsage - currentUsageStartSlots - currentEndSlotUsage)
|
||||||
|
|
||||||
|
return items.asSequence().elementAtOrNull(slotItemIndex - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun nextPage(menuPlayerInventory: MenuPlayerInventory) {
|
||||||
|
val nextPage = getPlayerCurrentPage(menuPlayerInventory.player) + 1
|
||||||
|
|
||||||
|
changePage(menuPlayerInventory, nextPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun previousPage(menuPlayerInventory: MenuPlayerInventory) {
|
||||||
|
val previousPage = getPlayerCurrentPage(menuPlayerInventory.player) - 1
|
||||||
|
|
||||||
|
changePage(menuPlayerInventory, previousPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changePage(menuPlayerInventory: MenuPlayerInventory, nextPage: Int) {
|
||||||
|
val player = menuPlayerInventory.player
|
||||||
|
|
||||||
|
val currentPage = getPlayerCurrentPage(player)
|
||||||
|
|
||||||
|
currentPlayerPage[player] = nextPage
|
||||||
|
|
||||||
|
forEachSlot { slotPos, pageSlot ->
|
||||||
|
val actualItem: T? = getCurrentItemForPlayer(slotPos, currentPage, player)
|
||||||
|
|
||||||
|
val nextItem: T? = getCurrentItemForPlayer(slotPos, nextPage, player)
|
||||||
|
|
||||||
|
pageSlot.updateSlot(actualItem, nextItem, slotPos, menuPlayerInventory, true)
|
||||||
|
|
||||||
|
paginationEventHandler.pageChange(menuPlayerInventory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun forEachSlot(
|
||||||
|
block: (slotPos: Int, pageSlot: PaginationSlotDSLImpl<T>) -> Unit,
|
||||||
|
) {
|
||||||
|
for (line in startLine..endLine) {
|
||||||
|
for (slot in startSlot..endSlot) {
|
||||||
|
val slotPos = calculateSlot(line, slot)
|
||||||
|
val pageSlot = paginationSlots[slotPos] as? PaginationSlotDSLImpl<T>
|
||||||
|
?: continue
|
||||||
|
|
||||||
|
block(slotPos, pageSlot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pageStartIndex(page: Int) = ((page - 1) * maxSlotPerPage())
|
||||||
|
|
||||||
|
private fun getPlayerItems(player: Player) = currentPlayerItems[player] ?: itemsProvider()
|
||||||
|
|
||||||
|
private fun maxSlotPerPage() = (endLine - startLine + 1) * (endSlot - startSlot + 1)
|
||||||
|
|
||||||
|
private fun maxPages(player: Player): Int {
|
||||||
|
val itemsCount = getPlayerItems(player).size
|
||||||
|
|
||||||
|
val maxPerPage = maxSlotPerPage()
|
||||||
|
|
||||||
|
var pages = (itemsCount / maxPerPage).toInt()
|
||||||
|
val mod = itemsCount % maxPerPage
|
||||||
|
|
||||||
|
if (mod > 0) pages++
|
||||||
|
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isPageAvailable(player: Player, page: Int): Boolean {
|
||||||
|
val maxPages = maxPages(player)
|
||||||
|
|
||||||
|
return page in 1..maxPages
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
|
||||||
|
|
||||||
|
public typealias MenuPlayerPageChangeEvent = MenuPlayer.() -> Unit
|
||||||
|
public typealias MenuPlayerPageAvailableEvent = MenuPlayer.() -> Unit
|
||||||
|
|
||||||
|
public class PaginationEventHandler {
|
||||||
|
internal val pageChangeCallbacks: MutableList<MenuPlayerPageChangeEvent> = mutableListOf()
|
||||||
|
internal val pageAvailableCallbacks: MutableList<MenuPlayerPageAvailableEvent> = mutableListOf()
|
||||||
|
|
||||||
|
public fun pageChange(pageChange: MenuPlayer) {
|
||||||
|
for (callback in pageChangeCallbacks) {
|
||||||
|
callback(pageChange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun pageAvailable(pageAvailable: MenuPlayer) {
|
||||||
|
for (callback in pageAvailableCallbacks) {
|
||||||
|
callback(pageAvailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public interface PaginationSlotDSL<T> {
|
||||||
|
public val slotRoot: SlotDSL
|
||||||
|
|
||||||
|
public val paginationEventHandler: PaginationSlotEventHandler<T>
|
||||||
|
|
||||||
|
public val slotData: WeakHashMap<String, Any>
|
||||||
|
public val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the interaction with this slot
|
||||||
|
*/
|
||||||
|
public var cancel: Boolean
|
||||||
|
|
||||||
|
public fun onPageChange(pageChange: MenuPlayerSlotPageChangeEvent<T>) {
|
||||||
|
paginationEventHandler.pageChangeCallbacks.add(pageChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onClick(click: MenuPlayerPageSlotInteractEvent<T>) {
|
||||||
|
paginationEventHandler.interactCallbacks.add(click)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onRender(render: MenuPlayerPageSlotRenderEvent<T>) {
|
||||||
|
paginationEventHandler.renderCallbacks.add(render)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onUpdate(update: MenuPlayerPageSlotUpdateEvent<T>) {
|
||||||
|
paginationEventHandler.updateCallbacks.add(update)
|
||||||
|
}
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.MenuPaginationImpl
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.slot.SlotDSL
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public class PaginationSlotDSLImpl<T>(
|
||||||
|
private val pagination: MenuPaginationImpl<T>,
|
||||||
|
override val slotRoot: SlotDSL,
|
||||||
|
) : PaginationSlotDSL<T> {
|
||||||
|
override val paginationEventHandler: PaginationSlotEventHandler<T> = PaginationSlotEventHandler<T>()
|
||||||
|
|
||||||
|
override var cancel: Boolean
|
||||||
|
get() = slotRoot.cancel
|
||||||
|
set(value) { slotRoot.cancel = value }
|
||||||
|
|
||||||
|
override val slotData: WeakHashMap<String, Any>
|
||||||
|
get() = slotRoot.slotData
|
||||||
|
override val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
|
||||||
|
get() = slotRoot.playerSlotData
|
||||||
|
|
||||||
|
internal fun updateSlot(
|
||||||
|
actualItem: T?,
|
||||||
|
nextItem: T?,
|
||||||
|
slotPos: Int,
|
||||||
|
menuPlayerInventory: MenuPlayerInventory,
|
||||||
|
isPageChange: Boolean = false,
|
||||||
|
) {
|
||||||
|
if (isPageChange) {
|
||||||
|
relocateSlotData(actualItem, nextItem)
|
||||||
|
|
||||||
|
// triggering event
|
||||||
|
paginationEventHandler.handlePageChange(
|
||||||
|
actualItem,
|
||||||
|
MenuPlayerSlotPageChange(
|
||||||
|
pagination.menu,
|
||||||
|
slotPos,
|
||||||
|
slotRoot,
|
||||||
|
menuPlayerInventory.player,
|
||||||
|
menuPlayerInventory.inventory,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleaning item in the inventory slot
|
||||||
|
menuPlayerInventory.setItem(slotPos, null)
|
||||||
|
|
||||||
|
paginationEventHandler.handleRender(
|
||||||
|
nextItem,
|
||||||
|
MenuPlayerSlotRender(
|
||||||
|
pagination.menu,
|
||||||
|
slotPos,
|
||||||
|
slotRoot,
|
||||||
|
menuPlayerInventory.player,
|
||||||
|
menuPlayerInventory.inventory,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun relocateSlotData(actualItem: T?, nextItem: T?) {
|
||||||
|
if (actualItem != null) {
|
||||||
|
// caching the current Data from Slot
|
||||||
|
val slotData = WeakHashMap(slotData)
|
||||||
|
val playerSlotData = WeakHashMap(playerSlotData)
|
||||||
|
|
||||||
|
if (slotData.isNotEmpty()) {
|
||||||
|
pagination.itemSlotData[actualItem] = slotData
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerSlotData.isNotEmpty()) {
|
||||||
|
pagination.itemPlayerSlotData[actualItem] = playerSlotData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleaning current data in the Slot
|
||||||
|
slotData.clear()
|
||||||
|
playerSlotData.clear()
|
||||||
|
|
||||||
|
if (nextItem != null) {
|
||||||
|
val nextSlotData = pagination.itemSlotData[nextItem]
|
||||||
|
val nextPlayerSlotData = pagination.itemPlayerSlotData[nextItem]
|
||||||
|
|
||||||
|
if (nextSlotData != null) {
|
||||||
|
slotData.putAll(nextSlotData)
|
||||||
|
}
|
||||||
|
if (nextPlayerSlotData != null) {
|
||||||
|
playerSlotData.putAll(nextPlayerSlotData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.pagination.slot
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.Menu
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerInventorySlot
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.Inventory
|
||||||
|
|
||||||
|
public typealias MenuPlayerSlotPageChangeEvent<T> = MenuPlayerSlotPageChange.(T?) -> Unit
|
||||||
|
public typealias MenuPlayerPageSlotInteractEvent<T> = MenuPlayerSlotInteract.(T?) -> Unit
|
||||||
|
public typealias MenuPlayerPageSlotRenderEvent<T> = MenuPlayerSlotRender.(T?) -> Unit
|
||||||
|
public typealias MenuPlayerPageSlotUpdateEvent<T> = MenuPlayerSlotUpdate.(T?) -> Unit
|
||||||
|
|
||||||
|
public class PaginationSlotEventHandler<T> {
|
||||||
|
internal val pageChangeCallbacks = mutableListOf<MenuPlayerSlotPageChangeEvent<T>>()
|
||||||
|
internal val interactCallbacks = mutableListOf<MenuPlayerPageSlotInteractEvent<T>>()
|
||||||
|
internal val renderCallbacks = mutableListOf<MenuPlayerPageSlotRenderEvent<T>>()
|
||||||
|
internal val updateCallbacks = mutableListOf<MenuPlayerPageSlotUpdateEvent<T>>()
|
||||||
|
|
||||||
|
public fun handlePageChange(currentItem: T?, pageChange: MenuPlayerSlotPageChange) {
|
||||||
|
for (callback in pageChangeCallbacks) {
|
||||||
|
callback(pageChange, currentItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun handleRender(currentItem: T?, render: MenuPlayerSlotRender) {
|
||||||
|
for (callback in renderCallbacks) {
|
||||||
|
callback(render, currentItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun handleUpdate(currentItem: T?, update: MenuPlayerSlotUpdate) {
|
||||||
|
for (callback in updateCallbacks) {
|
||||||
|
callback(update, currentItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun handleInteract(currentItem: T?, interact: MenuPlayerSlotInteract) {
|
||||||
|
for (callback in interactCallbacks) {
|
||||||
|
callback(interact, currentItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MenuPlayerSlotPageChange(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val slotPos: Int,
|
||||||
|
override val slot: Slot,
|
||||||
|
override val player: Player,
|
||||||
|
override val inventory: Inventory,
|
||||||
|
) : MenuPlayerInventorySlot
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.Slot
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
|
||||||
|
public interface SlotDSL : Slot {
|
||||||
|
|
||||||
|
override val eventHandler: SlotEventHandlerDSL
|
||||||
|
|
||||||
|
public fun onClick(click: MenuPlayerSlotInteractEvent) {
|
||||||
|
eventHandler.interactCallbacks.add(click)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onRender(render: MenuPlayerSlotRenderEvent) {
|
||||||
|
eventHandler.renderCallbacks.add(render)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onUpdate(update: MenuPlayerSlotUpdateEvent) {
|
||||||
|
eventHandler.updateCallbacks.add(update)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onMoveToSlot(moveToSlot: MenuPlayerSlotMoveToEvent) {
|
||||||
|
eventHandler.moveToSlotCallbacks.add(moveToSlot)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clone(item: ItemStack?): SlotDSL
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.dsl.MenuDSL
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public fun MenuDSL.newSlot(item: ItemStack?, builder: SlotDSL.() -> Unit): SlotDSL {
|
||||||
|
return SlotDSLImpl(item, cancelOnClick).apply(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SlotDSLImpl(
|
||||||
|
override val item: ItemStack?,
|
||||||
|
override var cancel: Boolean,
|
||||||
|
override val eventHandler: SlotEventHandlerDSL = SlotEventHandlerDSL(),
|
||||||
|
) : SlotDSL {
|
||||||
|
|
||||||
|
override val slotData: WeakHashMap<String, Any> = WeakHashMap()
|
||||||
|
override val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>> = WeakHashMap()
|
||||||
|
|
||||||
|
override fun clone(item: ItemStack?): SlotDSL = SlotDSLImpl(item, cancel, eventHandler.clone())
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.dsl.slot
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotInteract
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotMoveTo
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotRender
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.MenuPlayerSlotUpdate
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.slot.SlotEventHandler
|
||||||
|
|
||||||
|
public typealias MenuPlayerSlotInteractEvent = MenuPlayerSlotInteract.() -> Unit
|
||||||
|
public typealias MenuPlayerSlotRenderEvent = MenuPlayerSlotRender.() -> Unit
|
||||||
|
public typealias MenuPlayerSlotUpdateEvent = MenuPlayerSlotUpdate.() -> Unit
|
||||||
|
public typealias MenuPlayerSlotMoveToEvent = MenuPlayerSlotMoveTo.() -> Unit
|
||||||
|
|
||||||
|
public class SlotEventHandlerDSL : SlotEventHandler {
|
||||||
|
|
||||||
|
internal val interactCallbacks = mutableListOf<MenuPlayerSlotInteractEvent>()
|
||||||
|
internal val renderCallbacks = mutableListOf<MenuPlayerSlotRenderEvent>()
|
||||||
|
internal val updateCallbacks = mutableListOf<MenuPlayerSlotUpdateEvent>()
|
||||||
|
internal val moveToSlotCallbacks = mutableListOf<MenuPlayerSlotMoveToEvent>()
|
||||||
|
|
||||||
|
override fun interact(interact: MenuPlayerSlotInteract) {
|
||||||
|
for (callback in interactCallbacks) {
|
||||||
|
callback(interact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(render: MenuPlayerSlotRender) {
|
||||||
|
for (callback in renderCallbacks) {
|
||||||
|
callback(render)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(update: MenuPlayerSlotUpdate) {
|
||||||
|
for (callback in updateCallbacks) {
|
||||||
|
callback(update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun moveToSlot(moveToSlot: MenuPlayerSlotMoveTo) {
|
||||||
|
for (callback in moveToSlotCallbacks) {
|
||||||
|
callback(moveToSlot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clone(): SlotEventHandlerDSL {
|
||||||
|
return SlotEventHandlerDSL().also {
|
||||||
|
it.interactCallbacks.addAll(interactCallbacks)
|
||||||
|
it.renderCallbacks.addAll(renderCallbacks)
|
||||||
|
it.updateCallbacks.addAll(updateCallbacks)
|
||||||
|
it.moveToSlotCallbacks.addAll(moveToSlotCallbacks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.slot
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public interface Slot {
|
||||||
|
|
||||||
|
public val item: ItemStack?
|
||||||
|
|
||||||
|
public val eventHandler: SlotEventHandler
|
||||||
|
|
||||||
|
public val slotData: WeakHashMap<String, Any>
|
||||||
|
public val playerSlotData: WeakHashMap<Player, WeakHashMap<String, Any>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the interaction with this slot
|
||||||
|
*/
|
||||||
|
public var cancel: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a clone of Slot without slotData and playerSlotData
|
||||||
|
*/
|
||||||
|
public fun clone(item: ItemStack?): Slot
|
||||||
|
|
||||||
|
public fun clearSlotData() {
|
||||||
|
slotData.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun clearPlayerData(player: Player) {
|
||||||
|
playerSlotData.remove(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.slot
|
||||||
|
|
||||||
|
public interface SlotEventHandler {
|
||||||
|
|
||||||
|
public fun interact(interact: MenuPlayerSlotInteract)
|
||||||
|
|
||||||
|
public fun render(render: MenuPlayerSlotRender)
|
||||||
|
|
||||||
|
public fun update(update: MenuPlayerSlotUpdate)
|
||||||
|
|
||||||
|
public fun moveToSlot(moveToSlot: MenuPlayerSlotMoveTo)
|
||||||
|
|
||||||
|
public fun clone(): SlotEventHandler
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.menu.slot
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.Menu
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayer
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInteract
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerInventory
|
||||||
|
import tech.carcadex.kotlinbukkitkit.menu.MenuPlayerMove
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.inventory.ClickType
|
||||||
|
import org.bukkit.event.inventory.InventoryAction
|
||||||
|
import org.bukkit.inventory.Inventory
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import java.util.WeakHashMap
|
||||||
|
|
||||||
|
public val MenuPlayerSlot.rawSlotPos: Int get() = slotPos - 1
|
||||||
|
|
||||||
|
public interface MenuPlayerSlot : MenuPlayer {
|
||||||
|
public val slotPos: Int
|
||||||
|
public val slot: Slot
|
||||||
|
|
||||||
|
public fun putPlayerSlotData(key: String, value: Any) {
|
||||||
|
slot.playerSlotData.getOrPut(player) {
|
||||||
|
WeakHashMap()
|
||||||
|
}[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getPlayerSlotData(key: String): Any? = slot.playerSlotData.get(player)?.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MenuPlayerInventorySlot : MenuPlayerSlot, MenuPlayerInventory {
|
||||||
|
|
||||||
|
public var showingItem: ItemStack?
|
||||||
|
get() = getItem(slotPos)?.takeUnless { it.type == Material.AIR }
|
||||||
|
set(value) = setItem(slotPos, value)
|
||||||
|
|
||||||
|
public fun updateSlotToPlayer() {
|
||||||
|
menu.updateSlot(slot, player)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun updateSlot() {
|
||||||
|
menu.updateSlot(slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public open class MenuPlayerSlotInteract(
|
||||||
|
menu: Menu<*>,
|
||||||
|
override val slotPos: Int,
|
||||||
|
override val slot: Slot,
|
||||||
|
player: Player,
|
||||||
|
inventory: Inventory,
|
||||||
|
canceled: Boolean,
|
||||||
|
public val click: ClickType,
|
||||||
|
public val action: InventoryAction,
|
||||||
|
public val clicked: ItemStack?,
|
||||||
|
public val cursor: ItemStack?,
|
||||||
|
public val hotbarKey: Int,
|
||||||
|
) : MenuPlayerInteract(menu, player, inventory, canceled), MenuPlayerInventorySlot
|
||||||
|
|
||||||
|
public class MenuPlayerSlotMoveTo(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val slotPos: Int,
|
||||||
|
override val slot: Slot,
|
||||||
|
override val player: Player,
|
||||||
|
override val inventory: Inventory,
|
||||||
|
override var canceled: Boolean,
|
||||||
|
override val toMoveSlot: Int,
|
||||||
|
override var toMoveItem: ItemStack?,
|
||||||
|
) : MenuPlayerInventorySlot, MenuPlayerMove
|
||||||
|
|
||||||
|
public class MenuPlayerSlotRender(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val slotPos: Int,
|
||||||
|
override val slot: Slot,
|
||||||
|
override val player: Player,
|
||||||
|
override val inventory: Inventory,
|
||||||
|
) : MenuPlayerInventorySlot
|
||||||
|
|
||||||
|
public class MenuPlayerSlotUpdate(
|
||||||
|
override val menu: Menu<*>,
|
||||||
|
override val slotPos: Int,
|
||||||
|
override val slot: Slot,
|
||||||
|
override val player: Player,
|
||||||
|
override val inventory: Inventory,
|
||||||
|
) : MenuPlayerInventorySlot
|
||||||
@@ -38,12 +38,12 @@ public interface Message extends ComponentLike {
|
|||||||
@NotNull
|
@NotNull
|
||||||
List<Component> asComponentList();
|
List<Component> asComponentList();
|
||||||
/**
|
/**
|
||||||
* Get unparsed value of message
|
* @return unparsed value of message
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
List<String> asUnparsedStringList();
|
List<String> asUnparsedStringList();
|
||||||
/**
|
/**
|
||||||
* Get unparsed value of message joined to one string
|
* @return unparsed value of message joined to one string
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
String asUnparsedString(@NotNull String newLine);
|
String asUnparsedString(@NotNull String newLine);
|
||||||
|
|||||||
@@ -7,3 +7,7 @@ include("serialization")
|
|||||||
include("genref")
|
include("genref")
|
||||||
include("messages")
|
include("messages")
|
||||||
include("commands")
|
include("commands")
|
||||||
|
include("utility")
|
||||||
|
include("exposed")
|
||||||
|
include("thirdparty")
|
||||||
|
include("menu")
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
Vendored
+116
@@ -0,0 +1,116 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
kotlin("jvm")
|
||||||
|
kotlin("plugin.serialization") version "1.8.21"
|
||||||
|
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||||
|
id("maven-publish")
|
||||||
|
id("signing")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "tech.carcadex"
|
||||||
|
val globalArtifactId = "kotlinbukkitkit-thirdparty"
|
||||||
|
val globalName = "kotlinbukkitkit-thirdparty"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
name = "spigotmc-repo"
|
||||||
|
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT")
|
||||||
|
api(project(":architecture"))
|
||||||
|
api(project(":utility"))
|
||||||
|
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets.main {
|
||||||
|
kotlin.srcDir("build/generated/ksp/main/kotlin")
|
||||||
|
}
|
||||||
|
sourceSets.test {
|
||||||
|
kotlin.srcDir("build/generated/ksp/test/kotlin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withJavadocJar()
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("maven") {
|
||||||
|
artifactId = globalArtifactId
|
||||||
|
groupId = "tech.carcadex"
|
||||||
|
version = version
|
||||||
|
from(components["java"])
|
||||||
|
versionMapping {
|
||||||
|
usage("java-api") {
|
||||||
|
fromResolutionOf("runtimeClasspath")
|
||||||
|
}
|
||||||
|
usage("java-runtime") {
|
||||||
|
fromResolutionResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pom {
|
||||||
|
artifactId = globalArtifactId
|
||||||
|
groupId = "tech.carcadex"
|
||||||
|
version = version
|
||||||
|
name.set(globalArtifactId)
|
||||||
|
description.set(globalArtifactId)
|
||||||
|
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name.set("MIT")
|
||||||
|
url.set("https://mit-license.org/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id.set("itzRedTea")
|
||||||
|
name.set("Red Tea")
|
||||||
|
email.set("red__tea@outlook.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection.set("scm:https://github.com/CarcadeX/$globalName.git")
|
||||||
|
developerConnection.set("git@github.com:CarcadeX/$globalName.git")
|
||||||
|
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||||
|
val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
|
||||||
|
credentials {
|
||||||
|
username = "itzRedTea"
|
||||||
|
password = project.findProperty("password").toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signing {
|
||||||
|
useGpgCmd()
|
||||||
|
sign(publishing.publications.getByName("maven"))
|
||||||
|
}
|
||||||
Vendored
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.bungeecord
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
import org.bukkit.plugin.messaging.PluginMessageListener
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
|
|
||||||
|
object BungeeCordController: PluginMessageListener {
|
||||||
|
|
||||||
|
private val queue = mutableListOf<BungeeCordRequest>()
|
||||||
|
private var initialized = false;
|
||||||
|
private var plugin: Plugin? = null
|
||||||
|
|
||||||
|
fun init(plugin: Plugin) {
|
||||||
|
if(initialized) return
|
||||||
|
this.plugin = plugin
|
||||||
|
Bukkit.getServer().messenger.registerOutgoingPluginChannel(plugin, "BungeeCord")
|
||||||
|
Bukkit.getServer().messenger.registerIncomingPluginChannel(plugin, "BungeeCord", this)
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Plugin.initBungeeCordController() {
|
||||||
|
init(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPluginMessageReceived(channel: String, player: Player, message: ByteArray) {
|
||||||
|
if (channel != "BungeeCord") return
|
||||||
|
|
||||||
|
val buffer = ByteBuffer.wrap(message)
|
||||||
|
val subChannel = buffer.readUTF()
|
||||||
|
val request = queue.firstOrNull { it.subChannel == subChannel }
|
||||||
|
if(request?.responseCallback != null) {
|
||||||
|
val infoBuffer = buffer.slice()
|
||||||
|
val info = ByteArray(infoBuffer.remaining())
|
||||||
|
infoBuffer.get(info)
|
||||||
|
request.responseCallback.invoke(info)
|
||||||
|
queue.remove(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendBungeeCord(player: Player, message: ByteArray) {
|
||||||
|
if(!initialized) {
|
||||||
|
Bukkit.getLogger().severe("Could not execute sendBungeeCord method, because BungeeCordController" +
|
||||||
|
" has not been initialized!")
|
||||||
|
}
|
||||||
|
player.sendPluginMessage(plugin!!, "BungeeCord", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addToQueue(request: BungeeCordRequest) = queue.add(request)
|
||||||
|
|
||||||
|
private fun ByteBuffer.readUTF() = String(ByteArray(short.toInt()).apply { get(this) }, Charset.forName("UTF-8"))
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.bungeecord
|
||||||
|
|
||||||
|
import com.google.common.io.ByteStreams
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
|
||||||
|
typealias ResponseCallback = (message: ByteArray) -> Unit
|
||||||
|
|
||||||
|
class BungeeCordRequest(
|
||||||
|
val player: Player,
|
||||||
|
val subChannel: String,
|
||||||
|
val request: ByteArray? = null,
|
||||||
|
val responseCallback: ResponseCallback? = null
|
||||||
|
) {
|
||||||
|
fun send() {
|
||||||
|
val out = ByteStreams.newDataOutput()
|
||||||
|
out.writeUTF(subChannel)
|
||||||
|
if (request != null) out.write(request)
|
||||||
|
|
||||||
|
player.sendBungeeCord(out.toByteArray())
|
||||||
|
|
||||||
|
if (responseCallback != null) BungeeCordController.addToQueue(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.bungeecord
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import com.google.common.io.ByteStreams
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
|
// TODO: replace controller with KotlinPlugin lifecycle extension
|
||||||
|
|
||||||
|
public val Player.bungeecord get() = BungeeCord(this)
|
||||||
|
|
||||||
|
public fun Player.sendBungeeCord(message: ByteArray) = BungeeCordController.sendBungeeCord(this, message)
|
||||||
|
|
||||||
|
@JvmInline
|
||||||
|
public value class BungeeCord(private val player: Player) {
|
||||||
|
|
||||||
|
public fun sendToServer(server: String) = BungeeCordRequest(
|
||||||
|
player,
|
||||||
|
"Connect",
|
||||||
|
ByteStreams.newDataOutput().apply{writeUTF(server)}.toByteArray()
|
||||||
|
).send()
|
||||||
|
|
||||||
|
public fun retrieveIp(callback: (address: String, port: Int) -> Unit) = BungeeCordRequest(player, "IP") {
|
||||||
|
val input = ByteStreams.newDataInput(it)
|
||||||
|
val ip = input.readUTF()
|
||||||
|
val port = input.readInt()
|
||||||
|
callback(ip, port)
|
||||||
|
}.send()
|
||||||
|
|
||||||
|
public suspend fun retrieveIp(): Pair<String, Int> = suspendCoroutine { continuation ->
|
||||||
|
retrieveIp { address, port -> continuation.resume(address to port) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun onlinePlayerAt(server: String = "ALL", callback: (playerCount: Int) -> Unit)
|
||||||
|
= BungeeCordRequest(player, "PlayerCount", ByteStreams.newDataOutput().apply{writeUTF(server)}.toByteArray()) {
|
||||||
|
val input = ByteStreams.newDataInput(it)
|
||||||
|
val serverSendTo = input.readUTF()
|
||||||
|
val playerCount = input.readInt()
|
||||||
|
callback(playerCount)
|
||||||
|
}.send()
|
||||||
|
|
||||||
|
public suspend fun onlinePlayerAt(server: String = "ALL"): Int = suspendCoroutine { continuation ->
|
||||||
|
onlinePlayerAt { continuation.resume(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun retrieveServerName(callback: (server: String) -> Unit) = BungeeCordRequest(player, "GetServer") {
|
||||||
|
val input = ByteStreams.newDataInput(it)
|
||||||
|
val server = input.readUTF()
|
||||||
|
callback(server)
|
||||||
|
}.send()
|
||||||
|
|
||||||
|
public suspend fun retrieveServerName(): String = suspendCoroutine { continuation ->
|
||||||
|
retrieveServerName { continuation.resume(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun kickPlayer(player: String, reason: String) = BungeeCordRequest(this.player, "KickPlayer",
|
||||||
|
ByteStreams.newDataOutput().apply{writeUTF(player);writeUTF(reason)}.toByteArray()).send()
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
kotlin("jvm")
|
||||||
|
kotlin("plugin.serialization") version "1.8.21"
|
||||||
|
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||||
|
id("maven-publish")
|
||||||
|
id("signing")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "tech.carcadex"
|
||||||
|
val globalArtifactId = "kotlinbukkitkit-utility"
|
||||||
|
val globalName = "kotlinbukkitkit-utility"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
name = "spigotmc-repo"
|
||||||
|
url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT")
|
||||||
|
api(project(":architecture"))
|
||||||
|
api(project(":extensions"))
|
||||||
|
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(8)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets.main {
|
||||||
|
kotlin.srcDir("build/generated/ksp/main/kotlin")
|
||||||
|
}
|
||||||
|
sourceSets.test {
|
||||||
|
kotlin.srcDir("build/generated/ksp/test/kotlin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withJavadocJar()
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("maven") {
|
||||||
|
artifactId = globalArtifactId
|
||||||
|
groupId = "tech.carcadex"
|
||||||
|
version = version
|
||||||
|
from(components["java"])
|
||||||
|
versionMapping {
|
||||||
|
usage("java-api") {
|
||||||
|
fromResolutionOf("runtimeClasspath")
|
||||||
|
}
|
||||||
|
usage("java-runtime") {
|
||||||
|
fromResolutionResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pom {
|
||||||
|
artifactId = globalArtifactId
|
||||||
|
groupId = "tech.carcadex"
|
||||||
|
version = version
|
||||||
|
name.set(globalArtifactId)
|
||||||
|
description.set(globalArtifactId)
|
||||||
|
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name.set("MIT")
|
||||||
|
url.set("https://mit-license.org/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id.set("itzRedTea")
|
||||||
|
name.set("Red Tea")
|
||||||
|
email.set("red__tea@outlook.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection.set("scm:https://github.com/CarcadeX/$globalName.git")
|
||||||
|
developerConnection.set("git@github.com:CarcadeX/$globalName.git")
|
||||||
|
url.set("https://github.com/CarcadeX/KotlinBukkitKit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||||
|
val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
|
||||||
|
credentials {
|
||||||
|
username = "itzRedTea"
|
||||||
|
password = project.findProperty("password").toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signing {
|
||||||
|
useGpgCmd()
|
||||||
|
sign(publishing.publications.getByName("maven"))
|
||||||
|
}
|
||||||
+352
@@ -0,0 +1,352 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.collections
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.scheduler
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
|
||||||
|
public typealias OnExpireCallback<T> = (T) -> Unit
|
||||||
|
|
||||||
|
public fun <E> Plugin.expirationListOf(): ExpirationList<E> = ExpirationListImpl(this)
|
||||||
|
|
||||||
|
public fun <E> WithPlugin<*>.expirationListOf(): ExpirationList<E> = plugin.expirationListOf<E>()
|
||||||
|
|
||||||
|
public fun <E> expirationListOf(expireTime: Int, plugin: Plugin, vararg elements: E): ExpirationList<E> = plugin.expirationListOf<E>().apply { elements.forEach { add(it, expireTime) } }
|
||||||
|
|
||||||
|
public fun <E> Plugin.expirationListOf(expireTime: Int, vararg elements: E): ExpirationList<E> = expirationListOf(expireTime, this, elements = *elements)
|
||||||
|
|
||||||
|
public fun <E> WithPlugin<*>.expirationListOf(expireTime: Int, vararg elements: E): ExpirationList<E> = plugin.expirationListOf(expireTime, *elements)
|
||||||
|
|
||||||
|
public fun <E> expirationListOf(expireTime: Int, plugin: Plugin, vararg elements: Pair<E, OnExpireCallback<E>>): ExpirationList<E> = plugin.expirationListOf<E>().apply { elements.forEach { (element, onExpire) -> add(element, expireTime, onExpire) } }
|
||||||
|
|
||||||
|
public fun <E> Plugin.expirationListOf(expireTime: Int, vararg elements: Pair<E, OnExpireCallback<E>>): ExpirationList<E> = expirationListOf(expireTime, this, elements = *elements)
|
||||||
|
|
||||||
|
public fun <E> WithPlugin<*>.expirationListOf(expireTime: Int, vararg elements: Pair<E, OnExpireCallback<E>>): ExpirationList<E> = plugin.expirationListOf(expireTime, *elements)
|
||||||
|
|
||||||
|
public interface ExpirationList<E> : MutableIterable<E> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the count of elements in this list.
|
||||||
|
*/
|
||||||
|
public val size: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `true` if the list contains no elements, `false` otherwise.
|
||||||
|
*/
|
||||||
|
public fun isEmpty(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time missing to expire or `null` if the list don't contain the element
|
||||||
|
*/
|
||||||
|
public fun missingTime(element: E): Int?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified element is contained in this list.
|
||||||
|
*/
|
||||||
|
public operator fun contains(element: E): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the element at the specified index in the list.
|
||||||
|
*/
|
||||||
|
public operator fun get(index: Int): E?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the first occurrence of the e element in the list, or -1 if the specified
|
||||||
|
* element is not contained in the list.
|
||||||
|
*/
|
||||||
|
public fun indexOf(element: E): Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the first element in the list or `null` if the list is empty
|
||||||
|
*/
|
||||||
|
public fun first(): E?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the last element in the list or `null` if the list is empty
|
||||||
|
*/
|
||||||
|
public fun last(): E?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all elements from this list.
|
||||||
|
*/
|
||||||
|
public fun clear()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the element to the list with an expiration time.
|
||||||
|
*
|
||||||
|
* [expireTime] in seconds
|
||||||
|
* callback [onExpire] is called when the element expires.
|
||||||
|
*/
|
||||||
|
public fun add(element: E, expireTime: Int, onExpire: OnExpireCallback<E>? = null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the element in the start of list with an expiration time.
|
||||||
|
*
|
||||||
|
* [expireTime] in seconds
|
||||||
|
* callback [onExpire] is called when the element expires.
|
||||||
|
*/
|
||||||
|
public fun addFirst(element: E, expireTime: Int, onExpire: OnExpireCallback<E>? = null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an element at the specified [index] from the list.
|
||||||
|
*
|
||||||
|
* @return the element that has been removed or `null` if
|
||||||
|
* the element is not contained in the list.
|
||||||
|
*/
|
||||||
|
public fun removeAt(index: Int): E?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an element from the list
|
||||||
|
*
|
||||||
|
* @return `true` if the element was removed.
|
||||||
|
*/
|
||||||
|
public fun remove(element: E): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the first element from the list.
|
||||||
|
*
|
||||||
|
* @return the element if was removed.
|
||||||
|
*/
|
||||||
|
public fun removeFirst(): E?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the last element from the list.
|
||||||
|
*
|
||||||
|
* @return the element if was removed.
|
||||||
|
*/
|
||||||
|
public fun removeLast(): E?
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExpirationNode<E>(var element: E, val expireTime: Int) {
|
||||||
|
|
||||||
|
var next: ExpirationNode<E>? = null
|
||||||
|
var previous: ExpirationNode<E>? = null
|
||||||
|
|
||||||
|
var onExpire: OnExpireCallback<E>? = null
|
||||||
|
val startTime: Long = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExpirationListImpl<E>(private val plugin: Plugin) : ExpirationList<E> {
|
||||||
|
|
||||||
|
private var head: ExpirationNode<E>? = null
|
||||||
|
private var tail: ExpirationNode<E>? = null
|
||||||
|
|
||||||
|
private var task: BukkitTask? = null
|
||||||
|
private var emptyCount: Byte = 0
|
||||||
|
|
||||||
|
override val size: Int get() = _size
|
||||||
|
private var _size: Int = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun isEmpty(): Boolean = _size == 0
|
||||||
|
|
||||||
|
override fun missingTime(element: E): Int? {
|
||||||
|
return getNodeByElement(element)
|
||||||
|
?.let { it.expireTime - ((System.currentTimeMillis() - it.startTime) / 1000) }
|
||||||
|
?.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun contains(element: E): Boolean = indexOf(element) > -1
|
||||||
|
|
||||||
|
override operator fun get(index: Int): E? {
|
||||||
|
return getNode(index)?.element
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun first(): E? = head?.element
|
||||||
|
|
||||||
|
override fun last(): E? = tail?.element
|
||||||
|
|
||||||
|
override operator fun iterator(): MutableIterator<E> {
|
||||||
|
return object : MutableIterator<E> {
|
||||||
|
private val nodeIterator = nodeIterator()
|
||||||
|
|
||||||
|
override fun hasNext() = nodeIterator.hasNext()
|
||||||
|
override fun next() = nodeIterator.next().element
|
||||||
|
override fun remove() = nodeIterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun indexOf(element: E): Int {
|
||||||
|
if (head == null) return -1
|
||||||
|
var node = head
|
||||||
|
var count = 0
|
||||||
|
do {
|
||||||
|
if (node?.element === element) {
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
node = node?.next
|
||||||
|
count++
|
||||||
|
} while (node != null)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
head = null
|
||||||
|
tail = null
|
||||||
|
_size = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(element: E, expireTime: Int, onExpire: OnExpireCallback<E>?) {
|
||||||
|
if (expireTime <= 0) throw IllegalArgumentException("expireTime need to be greater then 0")
|
||||||
|
val newNode = ExpirationNode(element, expireTime).also { it.onExpire = onExpire }
|
||||||
|
if (head == null) {
|
||||||
|
head = newNode
|
||||||
|
tail = newNode
|
||||||
|
} else {
|
||||||
|
tail?.next = newNode
|
||||||
|
tail = newNode
|
||||||
|
}
|
||||||
|
_size++
|
||||||
|
generateTask()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addFirst(element: E, expireTime: Int, onExpire: OnExpireCallback<E>?) {
|
||||||
|
if (expireTime <= 0) throw IllegalArgumentException("expireTime need to be greater then 0")
|
||||||
|
val newNode = ExpirationNode(element, expireTime).also { it.onExpire = onExpire }
|
||||||
|
if (head == null) {
|
||||||
|
head = newNode
|
||||||
|
tail = newNode
|
||||||
|
} else {
|
||||||
|
head?.previous = newNode
|
||||||
|
head = newNode
|
||||||
|
}
|
||||||
|
_size++
|
||||||
|
generateTask()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAt(index: Int): E? {
|
||||||
|
return getNode(index)?.also {
|
||||||
|
removeNode(it)
|
||||||
|
}?.element
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: E): Boolean = getNodeByElement(element)?.let { true.apply { removeNode(it) } } ?: false
|
||||||
|
|
||||||
|
override fun removeFirst(): E? {
|
||||||
|
val next = head?.next
|
||||||
|
val headElement = head?.element
|
||||||
|
if (next == null) {
|
||||||
|
tail = null
|
||||||
|
head = null
|
||||||
|
} else {
|
||||||
|
head = next
|
||||||
|
next.previous = null
|
||||||
|
}
|
||||||
|
return headElement
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeLast(): E? {
|
||||||
|
val previous = tail?.previous
|
||||||
|
val tailElement = tail?.element
|
||||||
|
if (previous == null) {
|
||||||
|
tail = null
|
||||||
|
head = null
|
||||||
|
} else {
|
||||||
|
tail = previous
|
||||||
|
previous.next = null
|
||||||
|
}
|
||||||
|
return tailElement
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNode(index: Int): ExpirationNode<E>? {
|
||||||
|
if (index < 0 || index >= _size) return null
|
||||||
|
|
||||||
|
val mid = _size / 2
|
||||||
|
return if (index > mid) {
|
||||||
|
getFromSpecificSide(index - mid, tail) { it?.previous }
|
||||||
|
} else {
|
||||||
|
getFromSpecificSide(index, head) { it?.next }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getNodeByElement(element: E): ExpirationNode<E>? {
|
||||||
|
if (head == null) return null
|
||||||
|
var node = head
|
||||||
|
do {
|
||||||
|
if (node?.element === element) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
node = node?.next
|
||||||
|
} while (node != null)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun getFromSpecificSide(
|
||||||
|
count: Int,
|
||||||
|
start: ExpirationNode<E>?,
|
||||||
|
next: (ExpirationNode<E>?) -> ExpirationNode<E>?,
|
||||||
|
): ExpirationNode<E> {
|
||||||
|
var index = 0
|
||||||
|
var current = start
|
||||||
|
while (index != count) {
|
||||||
|
current = next(current)
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
return current!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nodeIterator(): MutableIterator<ExpirationNode<E>> {
|
||||||
|
return object : MutableIterator<ExpirationNode<E>> {
|
||||||
|
|
||||||
|
private var current = head
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return current != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): ExpirationNode<E> {
|
||||||
|
val aux = current
|
||||||
|
current = current?.next
|
||||||
|
return aux!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove() {
|
||||||
|
if (current != null) {
|
||||||
|
removeNode(current!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkTime(current: Long, node: ExpirationNode<E>) = ((current - node.startTime) / 1000) - node.expireTime >= 0
|
||||||
|
|
||||||
|
private fun generateTask() {
|
||||||
|
if (task == null) {
|
||||||
|
task = scheduler {
|
||||||
|
if (isEmpty()) {
|
||||||
|
emptyCount++
|
||||||
|
} else {
|
||||||
|
emptyCount = 0
|
||||||
|
val current = System.currentTimeMillis()
|
||||||
|
for (node in nodeIterator()) {
|
||||||
|
if (checkTime(current, node)) {
|
||||||
|
if (node.onExpire != null) node.onExpire?.invoke(node.element)
|
||||||
|
removeNode(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (emptyCount > 9) {
|
||||||
|
cancel()
|
||||||
|
task = null
|
||||||
|
}
|
||||||
|
}.runTaskTimer(plugin, 0L, 20L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeNode(node: ExpirationNode<E>) {
|
||||||
|
if (node === head && node === tail) {
|
||||||
|
head = null
|
||||||
|
tail = null
|
||||||
|
} else if (node === head) {
|
||||||
|
head = node.next?.apply { previous = null }
|
||||||
|
} else if (node === tail) {
|
||||||
|
tail = node.previous?.apply { next = null }
|
||||||
|
} else {
|
||||||
|
node.apply {
|
||||||
|
previous?.next = next
|
||||||
|
next?.previous = previous
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_size--
|
||||||
|
}
|
||||||
|
}
|
||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.collections
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.scheduler
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.extensions.now
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
import org.bukkit.scheduler.BukkitTask
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
public typealias OnExpireMapCallback<K, V> = (K, V) -> Unit
|
||||||
|
|
||||||
|
public fun <K, V> Plugin.expirationMapOf(): ExpirationMap<K, V> = ExpirationMapImpl(this)
|
||||||
|
|
||||||
|
public fun <K, V> WithPlugin<*>.expirationMapOf(): ExpirationMap<K, V> = plugin.expirationMapOf<K, V>()
|
||||||
|
|
||||||
|
public fun <K, V> expirationMapOf(expireTime: Long, plugin: Plugin, vararg elements: Pair<K, V>): ExpirationMap<K, V> = plugin.expirationMapOf<K, V>().apply { elements.forEach { (key, value) -> put(key, value, expireTime) } }
|
||||||
|
|
||||||
|
public fun <K, V> Plugin.expirationMapOf(expireTime: Long, vararg elements: Pair<K, V>): ExpirationMap<K, V> = expirationMapOf(expireTime, this, elements = *elements)
|
||||||
|
|
||||||
|
public fun <K, V> WithPlugin<*>.expirationMapOf(expireTime: Long, vararg elements: Pair<K, V>): ExpirationMap<K, V> = plugin.expirationMapOf(expireTime, *elements)
|
||||||
|
|
||||||
|
public fun <K, V> expirationMapOf(expireTime: Long, plugin: Plugin, vararg elements: Triple<K, V, OnExpireMapCallback<K, V>>): ExpirationMap<K, V> = plugin.expirationMapOf<K, V>().apply { elements.forEach { (key, value, onExpire) -> put(key, value, expireTime, onExpire) } }
|
||||||
|
|
||||||
|
public fun <K, V> Plugin.expirationMapOf(expireTime: Long, vararg elements: Triple<K, V, OnExpireMapCallback<K, V>>): ExpirationMap<K, V> = expirationMapOf(expireTime, this, elements = *elements)
|
||||||
|
|
||||||
|
public fun <K, V> WithPlugin<*>.expirationMapOf(expireTime: Long, vararg elements: Triple<K, V, OnExpireMapCallback<K, V>>): ExpirationMap<K, V> = plugin.expirationMapOf(expireTime, *elements)
|
||||||
|
|
||||||
|
public interface ExpirationMap<K, V> : MutableMap<K, V>, WithPlugin<Plugin> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the missing time on seconds to expire the key,
|
||||||
|
* -1 if was not specified the expiration time before(permanent key) or
|
||||||
|
* `null` if the key is not contained in the map.
|
||||||
|
*/
|
||||||
|
public fun missingTime(key: K): Long?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set expiration time to the key and returns `true` if the key is found
|
||||||
|
* or false if the key is not contained in the map.
|
||||||
|
*/
|
||||||
|
public fun expire(key: K, time: Long): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set expiration time to the key and returns `true` if the key is found
|
||||||
|
* or false if the key is not contained in the map.
|
||||||
|
*
|
||||||
|
* [time] in seconds.
|
||||||
|
* [callback] is called when the key expires.
|
||||||
|
*/
|
||||||
|
public fun expire(key: K, time: Long, callback: OnExpireMapCallback<K, V>): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates the specified [value] with the specified [key] in the map
|
||||||
|
* with an expiration time.
|
||||||
|
*
|
||||||
|
* @return the previous value associated with the key, or `null` if the key was not present in the map.
|
||||||
|
*/
|
||||||
|
public fun put(key: K, value: V, time: Long): V?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates the specified [value] with the specified [key] in the map
|
||||||
|
* with an expiration time.
|
||||||
|
*
|
||||||
|
* [time] in seconds.
|
||||||
|
* [callback] is called when the key expires.
|
||||||
|
*
|
||||||
|
* @return the previous value associated with the key, or `null` if the key was not present in the map.
|
||||||
|
*/
|
||||||
|
public fun put(key: K, value: V, time: Long, callback: OnExpireMapCallback<K, V>): V?
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExpirationMapImpl<K, V>(
|
||||||
|
override val plugin: Plugin,
|
||||||
|
private val initialMap: MutableMap<K, V> = WeakHashMap(),
|
||||||
|
) : ExpirationMap<K, V>, MutableMap<K, V> by initialMap {
|
||||||
|
|
||||||
|
private val putTime: MutableMap<K, Long> = mutableMapOf()
|
||||||
|
private val expiration: MutableMap<K, Long> = mutableMapOf()
|
||||||
|
private val whenExpire: MutableMap<K, OnExpireMapCallback<K, V>> = mutableMapOf()
|
||||||
|
|
||||||
|
override fun missingTime(key: K): Long? {
|
||||||
|
return if (containsKey(key)) {
|
||||||
|
(expiration.get(key) ?: return -1) - ((now() - putTime.getOrPut(key) { now() }) / 1000)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ex(key: K, time: Long) {
|
||||||
|
putTime.put(key, now())
|
||||||
|
expiration.put(key, time)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun whenEx(key: K, callback: OnExpireMapCallback<K, V>) {
|
||||||
|
whenExpire.put(key, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun expire(key: K, time: Long): Boolean {
|
||||||
|
if (containsKey(key)) {
|
||||||
|
ex(key, time)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun expire(key: K, time: Long, callback: OnExpireMapCallback<K, V>): Boolean {
|
||||||
|
if (expire(key, time)) {
|
||||||
|
whenEx(key, callback)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(key: K, value: V): V? {
|
||||||
|
val result = initialMap.put(key, value)
|
||||||
|
generateTask()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(key: K, value: V, time: Long): V? {
|
||||||
|
if (time <= 0) throw IllegalArgumentException("expiration time can't be negative or zero")
|
||||||
|
val result = put(key, value)
|
||||||
|
ex(key, time)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(key: K, value: V, time: Long, callback: OnExpireMapCallback<K, V>): V? {
|
||||||
|
if (time <= 0) throw IllegalArgumentException("expiration time can't be negative or zero")
|
||||||
|
val result = put(key, value)
|
||||||
|
ex(key, time)
|
||||||
|
whenEx(key, callback)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(key: K): V? {
|
||||||
|
val result = initialMap.remove(key)
|
||||||
|
if (result != null) {
|
||||||
|
putTime.remove(key)
|
||||||
|
expiration.remove(key)
|
||||||
|
whenExpire.remove(key)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkTime(current: Long, key: K) = ((current - (putTime.getOrPut(key) { current })) / 1000) - (expiration.get(key) ?: 0) >= 0
|
||||||
|
|
||||||
|
private var task: BukkitTask? = null
|
||||||
|
private var emptyCount: Byte = 0
|
||||||
|
private fun generateTask() {
|
||||||
|
if (task == null) {
|
||||||
|
task = scheduler {
|
||||||
|
if (isEmpty()) {
|
||||||
|
emptyCount++
|
||||||
|
} else {
|
||||||
|
emptyCount = 0
|
||||||
|
val current = now()
|
||||||
|
for ((key, value) in entries) {
|
||||||
|
if (checkTime(current, key)) {
|
||||||
|
whenExpire.remove(key)?.invoke(key, value)
|
||||||
|
initialMap.remove(key)
|
||||||
|
putTime.remove(key)
|
||||||
|
expiration.remove(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (emptyCount > 5) {
|
||||||
|
cancel()
|
||||||
|
task = null
|
||||||
|
}
|
||||||
|
}.runTaskTimer(plugin, 0L, 20L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+238
@@ -0,0 +1,238 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.collections
|
||||||
|
|
||||||
|
public typealias ObservableListener = (ObservableAction) -> Unit
|
||||||
|
public enum class ObservableAction { ADD, SET, REPLACE, REMOVE, CLEAR }
|
||||||
|
|
||||||
|
public fun <T> observableListOf(mutableList: MutableList<T>): ObservableList<T> = mutableList.asObservable()
|
||||||
|
public fun <T> observableSetOf(mutableSet: MutableSet<T>): ObservableSet<T> = mutableSet.asObservable()
|
||||||
|
public fun <K, V> observableMapOf(mutableMap: MutableMap<K, V>): ObservableMap<K, V> = mutableMap.asObservable()
|
||||||
|
|
||||||
|
public fun <T> MutableList<T>.asObservable(): ObservableList<T> = ObservableList(this)
|
||||||
|
public fun <T> MutableSet<T>.asObservable(): ObservableSet<T> = ObservableSet(this)
|
||||||
|
public fun <K, V> MutableMap<K, V>.asObservable(): ObservableMap<K, V> = ObservableMap(this)
|
||||||
|
|
||||||
|
public class ObservableList<T>(
|
||||||
|
private val list: MutableList<T>,
|
||||||
|
) : MutableList<T> by list, ObservableCollection<T> {
|
||||||
|
|
||||||
|
override val listeners: MutableList<ObservableListener> = mutableListOf<ObservableListener>()
|
||||||
|
|
||||||
|
override fun add(element: T): Boolean {
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
return list.add(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(index: Int, element: T) {
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
return list.add(index, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addAll(index: Int, elements: Collection<T>): Boolean {
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
return list.addAll(index, elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addAll(elements: Collection<T>): Boolean {
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
return list.addAll(elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
list.clear()
|
||||||
|
runListeners(ObservableAction.CLEAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun listIterator(): MutableListIterator<T> {
|
||||||
|
return ObservableMutableListIterator(
|
||||||
|
list.listIterator(),
|
||||||
|
::runListeners,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun listIterator(index: Int): MutableListIterator<T> {
|
||||||
|
return ObservableMutableListIterator(
|
||||||
|
list.listIterator(index),
|
||||||
|
::runListeners,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: T): Boolean {
|
||||||
|
return list.remove(element).ifTrue {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAll(elements: Collection<T>): Boolean {
|
||||||
|
return list.removeAll(elements).ifTrue {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAt(index: Int): T {
|
||||||
|
return list.removeAt(index).apply {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retainAll(elements: Collection<T>): Boolean {
|
||||||
|
return list.retainAll(elements).ifTrue {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(index: Int, element: T): T {
|
||||||
|
return list.set(index, element).apply {
|
||||||
|
runListeners(ObservableAction.SET)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObservableMutableListIterator<T>(
|
||||||
|
private val iterator: MutableListIterator<T>,
|
||||||
|
private val runListeners: ObservableListener,
|
||||||
|
) : MutableListIterator<T> by iterator {
|
||||||
|
override fun add(element: T) {
|
||||||
|
iterator.add(element)
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove() {
|
||||||
|
iterator.remove()
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(element: T) {
|
||||||
|
iterator.set(element)
|
||||||
|
runListeners(ObservableAction.SET)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObservableSet<T>(
|
||||||
|
private val set: MutableSet<T>,
|
||||||
|
) : MutableSet<T> by set, ObservableCollection<T> {
|
||||||
|
|
||||||
|
override val listeners: MutableList<ObservableListener> = mutableListOf<ObservableListener>()
|
||||||
|
|
||||||
|
override fun add(element: T): Boolean {
|
||||||
|
return set.add(element).ifTrue {
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addAll(elements: Collection<T>): Boolean {
|
||||||
|
return set.addAll(elements).ifTrue {
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
set.clear()
|
||||||
|
runListeners(ObservableAction.CLEAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): MutableIterator<T> {
|
||||||
|
return ObservableMutableIterator(
|
||||||
|
set.iterator(),
|
||||||
|
::runListeners,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: T): Boolean {
|
||||||
|
return set.remove(element).ifTrue {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAll(elements: Collection<T>): Boolean {
|
||||||
|
return set.removeAll(elements).ifTrue {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retainAll(elements: Collection<T>): Boolean {
|
||||||
|
return set.retainAll(elements).ifTrue {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObservableMutableIterator<T>(
|
||||||
|
private val iterator: MutableIterator<T>,
|
||||||
|
private val runListeners: ObservableListener,
|
||||||
|
) : MutableIterator<T> by iterator {
|
||||||
|
override fun remove() {
|
||||||
|
iterator.remove()
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObservableMap<K, V>(
|
||||||
|
private val map: MutableMap<K, V>,
|
||||||
|
) : MutableMap<K, V> by map, ObservableHolder {
|
||||||
|
|
||||||
|
override val listeners: MutableList<ObservableListener> = mutableListOf<ObservableListener>()
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
map.clear()
|
||||||
|
runListeners(ObservableAction.CLEAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(key: K, value: V): V? {
|
||||||
|
return put(key, value).apply {
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putAll(from: Map<out K, V>) {
|
||||||
|
putAll(from)
|
||||||
|
runListeners(ObservableAction.ADD)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putIfAbsent(key: K, value: V): V? {
|
||||||
|
return map.putIfAbsent(key, value).also {
|
||||||
|
if (it == null) runListeners(ObservableAction.ADD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(key: K): V? {
|
||||||
|
return map.remove(key)?.also {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(key: K, value: V): Boolean {
|
||||||
|
return map.remove(key, value).ifTrue {
|
||||||
|
runListeners(ObservableAction.REMOVE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun replace(key: K, first: V, second: V): Boolean {
|
||||||
|
return map.replace(key, first, second).ifTrue {
|
||||||
|
runListeners(ObservableAction.REPLACE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun replace(key: K, value: V): V? {
|
||||||
|
return map.replace(key, value)?.also {
|
||||||
|
runListeners(ObservableAction.REPLACE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ObservableCollection<T> : MutableCollection<T>, ObservableHolder
|
||||||
|
|
||||||
|
public interface ObservableHolder {
|
||||||
|
public val listeners: MutableList<ObservableListener>
|
||||||
|
|
||||||
|
public fun observe(listener: ObservableListener) {
|
||||||
|
listeners.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun runListeners(action: ObservableAction) {
|
||||||
|
for (listener in listeners) {
|
||||||
|
listener(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun Boolean.ifTrue(block: () -> Unit) = apply { block() }
|
||||||
+270
@@ -0,0 +1,270 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.collections
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.architecture.extensions.WithPlugin
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.KListener
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.event
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.registerEvents
|
||||||
|
import tech.carcadex.kotlinbukkitkit.extensions.unregisterListener
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.player.PlayerKickEvent
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
|
public typealias WhenPlayerQuitCollectionCallback = Player.() -> Unit
|
||||||
|
public typealias WhenPlayerQuitMapCallback<V> = Player.(V) -> Unit
|
||||||
|
|
||||||
|
// List
|
||||||
|
|
||||||
|
public fun Plugin.onlinePlayerListOf(): OnlinePlayerList = OnlinePlayerList(this)
|
||||||
|
|
||||||
|
public fun WithPlugin<*>.onlinePlayerListOf(): OnlinePlayerList = plugin.onlinePlayerListOf()
|
||||||
|
|
||||||
|
public fun onlinePlayerListOf(vararg players: Player, plugin: Plugin): OnlinePlayerList = OnlinePlayerList(plugin).apply { addAll(players) }
|
||||||
|
|
||||||
|
public fun Plugin.onlinePlayerListOf(vararg players: Player): OnlinePlayerList = onlinePlayerListOf(*players, plugin = this)
|
||||||
|
|
||||||
|
public fun WithPlugin<*>.onlinePlayerListOf(vararg players: Player): OnlinePlayerList = plugin.onlinePlayerListOf(*players)
|
||||||
|
|
||||||
|
public fun onlinePlayerListOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>, plugin: Plugin): OnlinePlayerList = OnlinePlayerList(plugin).apply { pair.forEach { (player, whenPlayerQuit) -> add(player, whenPlayerQuit) } }
|
||||||
|
|
||||||
|
public fun Plugin.onlinePlayerListOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>): OnlinePlayerList = onlinePlayerListOf(*pair, plugin = this)
|
||||||
|
|
||||||
|
public fun WithPlugin<*>.onlinePlayerListOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>): OnlinePlayerList = plugin.onlinePlayerListOf(*pair)
|
||||||
|
|
||||||
|
// Set
|
||||||
|
|
||||||
|
public fun Plugin.onlinePlayerSetOf(): OnlinePlayerSet = OnlinePlayerSet(this)
|
||||||
|
|
||||||
|
public fun WithPlugin<*>.onlinePlayerSetOf(): OnlinePlayerSet = plugin.onlinePlayerSetOf()
|
||||||
|
|
||||||
|
public fun onlinePlayerSetOf(vararg players: Player, plugin: Plugin): OnlinePlayerSet = OnlinePlayerSet(plugin).apply { addAll(players) }
|
||||||
|
|
||||||
|
public fun Plugin.onlinePlayerSetOf(vararg players: Player): OnlinePlayerSet = onlinePlayerSetOf(*players, plugin = this)
|
||||||
|
|
||||||
|
public fun WithPlugin<*>.onlinePlayerSetOf(vararg players: Player): OnlinePlayerSet = plugin.onlinePlayerSetOf(*players)
|
||||||
|
|
||||||
|
public fun onlinePlayerSetOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>, plugin: Plugin): OnlinePlayerSet = OnlinePlayerSet(plugin).apply { pair.forEach { (player, whenPlayerQuit) -> add(player, whenPlayerQuit) } }
|
||||||
|
|
||||||
|
public fun Plugin.onlinePlayerSetOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>): OnlinePlayerSet = onlinePlayerSetOf(*pair, plugin = this)
|
||||||
|
|
||||||
|
public fun WithPlugin<*>.onlinePlayerSetOf(vararg pair: Pair<Player, WhenPlayerQuitCollectionCallback>): OnlinePlayerSet = plugin.onlinePlayerSetOf(*pair)
|
||||||
|
|
||||||
|
// Map
|
||||||
|
|
||||||
|
public fun <V> Plugin.onlinePlayerMapOf(): OnlinePlayerMap<V> = OnlinePlayerMap<V>(this)
|
||||||
|
|
||||||
|
public fun <V> WithPlugin<*>.onlinePlayerMapOf(): OnlinePlayerMap<V> = plugin.onlinePlayerMapOf<V>()
|
||||||
|
|
||||||
|
public fun <V> onlinePlayerMapOf(vararg pair: Pair<Player, V>, plugin: Plugin): OnlinePlayerMap<V> = OnlinePlayerMap<V>(plugin).apply { putAll(pair) }
|
||||||
|
|
||||||
|
public fun <V> Plugin.onlinePlayerMapOf(vararg pair: Pair<Player, V>): OnlinePlayerMap<V> = onlinePlayerMapOf(*pair, plugin = this)
|
||||||
|
|
||||||
|
public fun <V> WithPlugin<*>.onlinePlayerMapOf(vararg pair: Pair<Player, V>): OnlinePlayerMap<V> = plugin.onlinePlayerMapOf(*pair)
|
||||||
|
|
||||||
|
public fun <V> onlinePlayerMapOf(vararg triple: Triple<Player, V, WhenPlayerQuitMapCallback<V>>, plugin: Plugin): OnlinePlayerMap<V> = OnlinePlayerMap<V>(plugin).apply { triple.forEach { (player, value, whenPlayerQuit) -> put(player, value, whenPlayerQuit) } }
|
||||||
|
|
||||||
|
public fun <V> Plugin.onlinePlayerMapOf(vararg triple: Triple<Player, V, WhenPlayerQuitMapCallback<V>>): OnlinePlayerMap<V> = onlinePlayerMapOf(*triple, plugin = this)
|
||||||
|
|
||||||
|
public fun <V> WithPlugin<*>.onlinePlayerMapOf(vararg triple: Triple<Player, V, WhenPlayerQuitMapCallback<V>>): OnlinePlayerMap<V> = plugin.onlinePlayerMapOf(*triple)
|
||||||
|
|
||||||
|
public class OnlinePlayerList(override val plugin: Plugin) : LinkedList<Player>(), OnlinePlayerCollection {
|
||||||
|
private val whenQuit: MutableList<Pair<Player, WhenPlayerQuitCollectionCallback>> = LinkedList()
|
||||||
|
|
||||||
|
override fun add(player: Player, whenPlayerQuitCallback: Player.() -> Unit): Boolean {
|
||||||
|
if (super<OnlinePlayerCollection>.add(player, whenPlayerQuitCallback)) {
|
||||||
|
whenQuit.add(player to whenPlayerQuitCallback)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(element: Player): Boolean {
|
||||||
|
if (isEmpty()) registerEvents(plugin)
|
||||||
|
return super<LinkedList>.add(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun quit(player: Player): Boolean {
|
||||||
|
if (super.quit(player)) {
|
||||||
|
val iterator = whenQuit.iterator()
|
||||||
|
for (pair in iterator) {
|
||||||
|
if (pair.first == player) {
|
||||||
|
iterator.remove()
|
||||||
|
pair.second.invoke(pair.first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeFirst(): Player {
|
||||||
|
return super.removeFirst().also {
|
||||||
|
checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeLastOccurrence(p0: Any?): Boolean {
|
||||||
|
return super.removeLastOccurrence(p0).also {
|
||||||
|
if (it) checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAt(p0: Int): Player {
|
||||||
|
return super.removeAt(p0).also {
|
||||||
|
checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: Player): Boolean {
|
||||||
|
if (super.remove(element)) {
|
||||||
|
checkRegistration()
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeLast(): Player {
|
||||||
|
return super.removeLast().also {
|
||||||
|
checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OnlinePlayerSet(override val plugin: Plugin) : HashSet<Player>(), OnlinePlayerCollection {
|
||||||
|
private val whenQuit: MutableMap<Player, WhenPlayerQuitCollectionCallback> = mutableMapOf()
|
||||||
|
|
||||||
|
override fun add(player: Player, whenPlayerQuitCallback: WhenPlayerQuitCollectionCallback): Boolean {
|
||||||
|
if (super<OnlinePlayerCollection>.add(player, whenPlayerQuitCallback)) {
|
||||||
|
whenQuit.put(player, whenPlayerQuitCallback)
|
||||||
|
|
||||||
|
checkRegistration()
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(element: Player): Boolean {
|
||||||
|
return super<HashSet>.add(element).also {
|
||||||
|
if (it) checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(element: Player): Boolean {
|
||||||
|
return super.remove(element).also {
|
||||||
|
if (it) checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun quit(player: Player): Boolean {
|
||||||
|
if (super.quit(player)) {
|
||||||
|
whenQuit.remove(player)?.also { block ->
|
||||||
|
block.invoke(player)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnlinePlayerCollection : MutableCollection<Player>, KListener<Plugin> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new Player to the collection with a callback for when the player quits the server.
|
||||||
|
*/
|
||||||
|
public fun add(player: Player, whenPlayerQuit: WhenPlayerQuitCollectionCallback): Boolean {
|
||||||
|
return add(player).also {
|
||||||
|
if (it) checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the player from the collection, calling the [WhenPlayerQuitCollectionCallback] provided.
|
||||||
|
*/
|
||||||
|
public fun quit(player: Player): Boolean {
|
||||||
|
return remove(player).also {
|
||||||
|
if (it) checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the collection calling all [WhenPlayerQuitCollectionCallback] from the Players.
|
||||||
|
*/
|
||||||
|
public fun clearQuiting() {
|
||||||
|
toMutableList().forEach {
|
||||||
|
quit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun OnlinePlayerCollection.checkRegistration() {
|
||||||
|
if (size == 1) {
|
||||||
|
event<PlayerQuitEvent> { quit(player) }
|
||||||
|
event<PlayerKickEvent> { quit(player) }
|
||||||
|
} else if (size == 0) {
|
||||||
|
unregisterListener()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OnlinePlayerMap<V>(override val plugin: Plugin) : HashMap<Player, V>(), KListener<Plugin> {
|
||||||
|
private val whenQuit: HashMap<Player, WhenPlayerQuitMapCallback<V>> = hashMapOf()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts a Player to the map with a [value] and a callback for when the player quits the server.
|
||||||
|
*/
|
||||||
|
public fun put(key: Player, value: V, whenPlayerQuit: WhenPlayerQuitMapCallback<V>): V? {
|
||||||
|
whenQuit.put(key, whenPlayerQuit)
|
||||||
|
return put(key, value).also {
|
||||||
|
checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the player from the map, calling the [WhenPlayerQuitMapCallback] provided.
|
||||||
|
*/
|
||||||
|
public fun quit(player: Player) {
|
||||||
|
remove(player)?.also {
|
||||||
|
whenQuit.remove(player)?.also { block ->
|
||||||
|
block.invoke(player, it)
|
||||||
|
}
|
||||||
|
checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the map calling all [WhenPlayerQuitMapCallback] from the Players.
|
||||||
|
*/
|
||||||
|
public fun clearQuiting() {
|
||||||
|
keys.toMutableList().forEach {
|
||||||
|
quit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(key: Player): V? {
|
||||||
|
return super.remove(key).also {
|
||||||
|
checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(key: Player, value: V): Boolean {
|
||||||
|
return super.remove(key, value).also {
|
||||||
|
checkRegistration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkRegistration() {
|
||||||
|
if (size == 1) {
|
||||||
|
event<PlayerQuitEvent> { quit(player) }
|
||||||
|
event<PlayerKickEvent> { quit(player) }
|
||||||
|
} else if (size == 0) {
|
||||||
|
unregisterListener()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.extensions
|
||||||
|
|
||||||
|
public fun Collection<String>.containsIgnoreCase(
|
||||||
|
element: String,
|
||||||
|
): Boolean = any { it.equals(element, true) }
|
||||||
|
|
||||||
|
public fun <T> MutableCollection<T>.clear(onRemove: (T) -> Unit) {
|
||||||
|
toMutableList().forEach {
|
||||||
|
remove(it)
|
||||||
|
onRemove(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun Array<String>.containsIgnoreCase(
|
||||||
|
element: String,
|
||||||
|
): Boolean = any { it.equals(element, true) }
|
||||||
|
|
||||||
|
public fun <V> Map<String, V>.containsKeyIgnoreCase(
|
||||||
|
key: String,
|
||||||
|
): Boolean = keys.containsIgnoreCase(key)
|
||||||
|
|
||||||
|
public fun <V> Map<String, V>.getIgnoreCase(
|
||||||
|
key: String,
|
||||||
|
): V? = entries.find { it.key.equals(key, true) }?.value
|
||||||
|
|
||||||
|
public fun <K, V> MutableMap<K, V>.clear(onRemove: (K, V) -> Unit) {
|
||||||
|
keys.toMutableSet().forEach { onRemove(it, remove(it)!!) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.extensions
|
||||||
|
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.DurationUnit
|
||||||
|
|
||||||
|
public fun now(): Long = System.currentTimeMillis()
|
||||||
|
public fun nowNano(): Long = System.nanoTime()
|
||||||
|
|
||||||
|
public val Long.ticks: Duration get() = toDouble().ticks
|
||||||
|
public val Int.ticks: Duration get() = toDouble().ticks
|
||||||
|
public val Double.ticks: Duration get() = tickToMilliseconds(this).milliseconds
|
||||||
|
|
||||||
|
public val Duration.inTicks: Double get() = millisecondsToTick(toDouble(DurationUnit.MILLISECONDS))
|
||||||
|
|
||||||
|
public fun Duration.toLongTicks(): Long = inTicks.toLong()
|
||||||
|
|
||||||
|
private fun tickToMilliseconds(value: Double): Double = value * 50.0
|
||||||
|
private fun millisecondsToTick(value: Double): Double = value / 50.0
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.extensions
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.BlockPos
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.ChunkPos
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.VectorComparable
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.asBlockPos
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.asBukkitBlock
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.asBukkitChunk
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.asBukkitLocation
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.types.asPos
|
||||||
|
import org.bukkit.Chunk
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.block.Block
|
||||||
|
|
||||||
|
// BUKKIT THINGS
|
||||||
|
|
||||||
|
public operator fun PosRange<*, BlockPos>.contains(other: Location): Boolean = contains(other.asPos())
|
||||||
|
public operator fun PosRange<*, BlockPos>.contains(other: Block): Boolean = contains(other.asPos())
|
||||||
|
public operator fun PosRange<*, ChunkPos>.contains(other: Chunk): Boolean = contains(other.asPos())
|
||||||
|
|
||||||
|
public operator fun Location.rangeTo(other: Location): PosRange<Location, BlockPos> {
|
||||||
|
return PosRange(this.asBlockPos(), other.asBlockPos()) {
|
||||||
|
RangeIteratorWithFactor<Location, BlockPos>(
|
||||||
|
this,
|
||||||
|
other,
|
||||||
|
{ it.asBukkitLocation(world) },
|
||||||
|
{ it.asBlockPos() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun Block.rangeTo(other: Block): PosRange<Block, BlockPos> {
|
||||||
|
return PosRange(this.asPos(), other.asPos()) {
|
||||||
|
RangeIteratorWithFactor<Block, BlockPos>(
|
||||||
|
this,
|
||||||
|
other,
|
||||||
|
{ it.asBukkitBlock(world) },
|
||||||
|
{ it.asPos() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public operator fun Chunk.rangeTo(other: Chunk): PosRange<Chunk, ChunkPos> {
|
||||||
|
return PosRange(this.asPos(), other.asPos()) {
|
||||||
|
RangeIteratorWithFactor<Chunk, ChunkPos>(
|
||||||
|
this,
|
||||||
|
other,
|
||||||
|
{ it.asBukkitChunk(world) },
|
||||||
|
{ it.asPos() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PosRange<T, POS : VectorComparable<POS>>(
|
||||||
|
public val first: POS,
|
||||||
|
public val last: POS,
|
||||||
|
private val buildIterator: () -> Iterator<T>,
|
||||||
|
) : ClosedRange<POS>, Iterable<T> {
|
||||||
|
override val endInclusive: POS get() = last
|
||||||
|
override val start: POS get() = first
|
||||||
|
|
||||||
|
override fun contains(value: POS): Boolean {
|
||||||
|
val firstAxis = first.axis()
|
||||||
|
val lastAxis = last.axis()
|
||||||
|
return value.axis().withIndex().all { (index, it) ->
|
||||||
|
it >= firstAxis[index] && it <= lastAxis[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<T> = buildIterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PosRangeIterator<T : VectorComparable<T>>(
|
||||||
|
first: T,
|
||||||
|
last: T,
|
||||||
|
private val factor: (axis: IntArray) -> T,
|
||||||
|
) : Iterator<T> {
|
||||||
|
private val firstAxis = first.axis()
|
||||||
|
private val lastAxis = last.axis()
|
||||||
|
private val closedAxisRanges = firstAxis.mapIndexed { index, it ->
|
||||||
|
IntProgression.fromClosedRange(it.toInt(), lastAxis[index].toInt(), 1)
|
||||||
|
}
|
||||||
|
private val iteratorAxis = closedAxisRanges.map { it.iterator() }.toTypedArray()
|
||||||
|
|
||||||
|
private val actualAxis = iteratorAxis.toList().subList(0, iteratorAxis.size - 1)
|
||||||
|
.map { it.nextInt() }
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
return iteratorAxis.any { it.hasNext() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): T {
|
||||||
|
val lastIndex = iteratorAxis.size - 1
|
||||||
|
val last = iteratorAxis[lastIndex]
|
||||||
|
if (last.hasNext()) {
|
||||||
|
val axis = IntArray(actualAxis.size) { actualAxis[it] } + last.nextInt()
|
||||||
|
return factor(axis)
|
||||||
|
}
|
||||||
|
for (i in lastIndex - 1 downTo 0) {
|
||||||
|
val axis = iteratorAxis[i]
|
||||||
|
if (axis.hasNext()) {
|
||||||
|
actualAxis[i] = axis.nextInt()
|
||||||
|
iteratorAxis[i + 1] = closedAxisRanges[i + 1].iterator()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RangeIteratorWithFactor<T, POS : VectorComparable<POS>>(
|
||||||
|
start: T,
|
||||||
|
end: T,
|
||||||
|
private val factor: (POS) -> T,
|
||||||
|
private val posFactor: (T) -> POS,
|
||||||
|
) : Iterator<T> {
|
||||||
|
public val iterator: PosRangeIterator<POS> = PosRangeIterator(posFactor(start), posFactor(end), posFactor(start)::factor)
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean = iterator.hasNext()
|
||||||
|
override fun next(): T = factor(iterator.next())
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.extensions
|
||||||
|
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
public fun <E> Collection<E>.randomOrNull(): E? = runCatching { random() }.getOrNull()
|
||||||
|
public fun <E> Collection<E>.randomIndex(): Int = if (size > 0) Random.nextInt(size) else -1
|
||||||
|
|
||||||
|
public fun <E> Collection<E>.randomize(): MutableList<E> {
|
||||||
|
val oldList = toMutableList()
|
||||||
|
val newList = mutableListOf<E>()
|
||||||
|
|
||||||
|
for (i in 0 until size) {
|
||||||
|
val index = oldList.randomIndex()
|
||||||
|
newList.add(oldList.get(index))
|
||||||
|
oldList.removeAt(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newList
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.extensions
|
||||||
|
|
||||||
|
private val unicodeRegex = "((\\\\u)([0-9]{4}))".toRegex()
|
||||||
|
|
||||||
|
public fun String.javaUnicodeToCharacter(): String = unicodeRegex.replace(this) {
|
||||||
|
String(charArrayOf(it.destructured.component3().toInt(16).toChar()))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun <T> T.print(): T = also { println(it) }
|
||||||
|
|
||||||
|
public fun String.centralize(
|
||||||
|
length: Int,
|
||||||
|
spacer: String = " ",
|
||||||
|
prefix: String = "",
|
||||||
|
suffix: String = "",
|
||||||
|
): String {
|
||||||
|
if (this.length >= length) return this
|
||||||
|
val part = prefix + spacer.repeat((length - this.length) / 2) + suffix
|
||||||
|
return part + this + part
|
||||||
|
}
|
||||||
|
|
||||||
|
public val TRUE_CASES: Array<String> = arrayOf("true")
|
||||||
|
get() = field.clone()
|
||||||
|
public val FALSE_CASES: Array<String> = arrayOf("false")
|
||||||
|
get() = field.clone()
|
||||||
|
|
||||||
|
public fun String.toBooleanOrNull(
|
||||||
|
trueCases: Array<String> = TRUE_CASES,
|
||||||
|
falseCases: Array<String> = FALSE_CASES,
|
||||||
|
): Boolean? = when {
|
||||||
|
trueCases.any { it.equals(this, true) } -> true
|
||||||
|
falseCases.any { it.equals(this, true) } -> false
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.types
|
||||||
|
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.extensions.PosRange
|
||||||
|
import tech.carcadex.kotlinbukkitkit.utility.extensions.PosRangeIterator
|
||||||
|
import org.bukkit.Chunk
|
||||||
|
import org.bukkit.Location
|
||||||
|
import org.bukkit.World
|
||||||
|
import org.bukkit.block.Block
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
public fun blockPosOf(x: Int, y: Int, z: Int): BlockPos = BlockPos(x, y, z)
|
||||||
|
public fun locationPosOf(x: Double, y: Double, z: Double, yaw: Float = 0f, pitch: Float = 0f): LocationPos = LocationPos(x, y, z, yaw, pitch)
|
||||||
|
public fun chunkPosOf(x: Int, z: Int): ChunkPos = ChunkPos(x, z)
|
||||||
|
|
||||||
|
public fun Location.asPos(): LocationPos = LocationPos(x, y, z, yaw, pitch)
|
||||||
|
public fun LocationPos.asBukkitBlock(world: World): Block = world.getBlockAt(x.toInt(), y.toInt(), z.toInt())
|
||||||
|
public fun LocationPos.asBukkitLocation(world: World): Location = Location(world, x, y, z)
|
||||||
|
public fun LocationPos.asBlockPos(): BlockPos = BlockPos(x.toInt(), y.toInt(), z.toInt())
|
||||||
|
|
||||||
|
public fun Block.asPos(): BlockPos = BlockPos(x, y, z)
|
||||||
|
public fun Location.asBlockPos(): BlockPos = BlockPos(blockX, blockY, blockZ)
|
||||||
|
public fun BlockPos.asBukkitBlock(world: World): Block = world.getBlockAt(x, y, z)
|
||||||
|
public fun BlockPos.asBukkitLocation(world: World?): Location = Location(world, x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
public fun BlockPos.asLocationPos(): LocationPos = LocationPos(x.toDouble(), y.toDouble(), z.toDouble(), 0f, 0f)
|
||||||
|
public fun BlockPos.asChunkPos(): ChunkPos = ChunkPos(x shr 4, z shr 4)
|
||||||
|
|
||||||
|
public fun Chunk.asPos(): ChunkPos = ChunkPos(x, z)
|
||||||
|
public fun ChunkPos.asBukkitChunk(world: World): Chunk = world.getChunkAt(x, z)
|
||||||
|
|
||||||
|
public data class BlockPos(
|
||||||
|
var x: Int,
|
||||||
|
var y: Int,
|
||||||
|
var z: Int,
|
||||||
|
) : VectorComparable<BlockPos> {
|
||||||
|
override fun axis(): DoubleArray = doubleArrayOf(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
override fun factor(axis: IntArray): BlockPos = BlockPos(axis[0], axis[1], axis[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
public data class LocationPos(
|
||||||
|
var x: Double,
|
||||||
|
var y: Double,
|
||||||
|
var z: Double,
|
||||||
|
val yaw: Float,
|
||||||
|
val pitch: Float,
|
||||||
|
) : VectorComparable<LocationPos> {
|
||||||
|
override fun axis(): DoubleArray = doubleArrayOf(x, y, z)
|
||||||
|
override fun factor(axis: IntArray): LocationPos = LocationPos(axis[0].toDouble(), axis[1].toDouble(), axis[2].toDouble(), yaw, pitch)
|
||||||
|
}
|
||||||
|
|
||||||
|
public data class ChunkPos(
|
||||||
|
var x: Int,
|
||||||
|
var z: Int,
|
||||||
|
) : VectorComparable<ChunkPos> {
|
||||||
|
override fun axis(): DoubleArray = doubleArrayOf(x.toDouble(), z.toDouble())
|
||||||
|
override fun factor(axis: IntArray): ChunkPos = ChunkPos(axis[0], axis[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface VectorComparable<T : VectorComparable<T>> : Comparable<T> {
|
||||||
|
public fun axis(): DoubleArray
|
||||||
|
public fun factor(axis: IntArray): T
|
||||||
|
|
||||||
|
public operator fun rangeTo(other: T): PosRange<T, T> {
|
||||||
|
return PosRange(this as T, other) { PosRangeIterator(this, other, ::factor) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compareTo(other: T): Int {
|
||||||
|
val selfAxis = axis()
|
||||||
|
val otherAxis = other.axis()
|
||||||
|
val pairAxis = selfAxis.mapIndexed { index, axis -> axis to otherAxis[index] }
|
||||||
|
val (d1, d2) = calculatePythagoras(*pairAxis.toTypedArray())
|
||||||
|
return d1.compareTo(d2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Pair<Int, Int>.toDouble() = first.toDouble() to second.toDouble()
|
||||||
|
private fun Pair<Double, Double>.toInt() = first.toInt() to second.toInt()
|
||||||
|
private fun calculatePythagoras(vararg positions: Pair<Double, Double>): Pair<Double, Double> {
|
||||||
|
val pow = positions.map { (x1, x2) -> (x1 * x1) to (x2 * x2) }
|
||||||
|
|
||||||
|
val x1Sum = pow.sumOf { (x, _) -> x }
|
||||||
|
val x2Sum = pow.sumOf { (_, x) -> x }
|
||||||
|
|
||||||
|
val d1 = sqrt(x1Sum)
|
||||||
|
val d2 = sqrt(x2Sum)
|
||||||
|
|
||||||
|
return d1 to d2
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.types
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
public const val DEFAULT_SPACER: Char = ' '
|
||||||
|
public const val DEFAULT_FORMAT_STYLE: String = "%YEAR %MONTH %WEEK %DAY %HOUR %MIN %SEC"
|
||||||
|
|
||||||
|
public val Long.formatter: TimeFormat get() = TimeFormat(this)
|
||||||
|
public val Int.formatter: TimeFormat get() = TimeFormat(this.toLong())
|
||||||
|
|
||||||
|
public val TimeFormat.PTBR: String get() = format(PTBRFormat)
|
||||||
|
public val TimeFormat.EN: String get() = format(ENFormat)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
public data class FormatLang(
|
||||||
|
val second: String,
|
||||||
|
val seconds: String,
|
||||||
|
val minute: String,
|
||||||
|
val minutes: String,
|
||||||
|
val hour: String,
|
||||||
|
val hours: String,
|
||||||
|
val day: String,
|
||||||
|
val days: String,
|
||||||
|
val week: String,
|
||||||
|
val weeks: String,
|
||||||
|
val month: String,
|
||||||
|
val months: String,
|
||||||
|
val year: String,
|
||||||
|
val years: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param time in second
|
||||||
|
*/
|
||||||
|
@JvmInline
|
||||||
|
public value class TimeFormat(private val time: Long) {
|
||||||
|
public fun format(
|
||||||
|
lang: FormatLang,
|
||||||
|
formatStyle: String = DEFAULT_FORMAT_STYLE,
|
||||||
|
formatSpacer: Char = DEFAULT_SPACER,
|
||||||
|
): String {
|
||||||
|
val seconds = time % 60
|
||||||
|
val minutes = time / 60 % 60
|
||||||
|
val hours = time / 3600 % 24
|
||||||
|
val days = time / 86400 % 7
|
||||||
|
val weeks = time / 604800 % 4
|
||||||
|
val months = time / 2419200 % 12
|
||||||
|
val years = time / 29030400
|
||||||
|
|
||||||
|
var formated = formatStyle.replace("%SEC", if (seconds > 0) "$seconds ${if (seconds > 1) lang.seconds else lang.second}" else "", true)
|
||||||
|
|
||||||
|
formated = formated.replace("%MIN", if (minutes > 0) "$minutes ${if (minutes > 1) lang.minutes else lang.minute}" else "", true)
|
||||||
|
|
||||||
|
formated = formated.replace("%HOUR", if (hours > 0) "$hours ${if (hours > 1) lang.hours else lang.hour}" else "", true)
|
||||||
|
|
||||||
|
formated = formated.replace("%DAY", if (days > 0) "$days ${if (days > 1) lang.days else lang.day}" else "", true)
|
||||||
|
|
||||||
|
formated = formated.replace("%WEEK", if (weeks > 0) "$weeks ${if (weeks > 1) lang.weeks else lang.week}" else "", true)
|
||||||
|
|
||||||
|
formated = formated.replace("%MONTH", if (months > 0) "$months ${if (months > 1) lang.months else lang.month}" else "", true)
|
||||||
|
|
||||||
|
formated = formated.replace("%YEAR", if (years > 0) "$years ${if (years > 1) lang.years else lang.year}" else "", true)
|
||||||
|
|
||||||
|
formated = formated.replace("$formatSpacer$formatSpacer", "$formatSpacer")
|
||||||
|
|
||||||
|
return formated.trim { it == formatSpacer }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public val PTBRFormat: FormatLang = FormatLang(
|
||||||
|
second = "segundo",
|
||||||
|
seconds = "segundos",
|
||||||
|
|
||||||
|
minute = "minuto",
|
||||||
|
minutes = "minutos",
|
||||||
|
|
||||||
|
hour = "hora",
|
||||||
|
hours = "horas",
|
||||||
|
|
||||||
|
day = "dia",
|
||||||
|
days = "dias",
|
||||||
|
|
||||||
|
week = "semana",
|
||||||
|
weeks = "semanas",
|
||||||
|
|
||||||
|
month = "mes",
|
||||||
|
months = "meses",
|
||||||
|
|
||||||
|
year = "ano",
|
||||||
|
years = "anos",
|
||||||
|
)
|
||||||
|
|
||||||
|
public val ENFormat: FormatLang = FormatLang(
|
||||||
|
second = "second",
|
||||||
|
seconds = "seconds",
|
||||||
|
|
||||||
|
minute = "minute",
|
||||||
|
minutes = "minutes",
|
||||||
|
|
||||||
|
hour = "hour",
|
||||||
|
hours = "hours",
|
||||||
|
|
||||||
|
day = "day",
|
||||||
|
days = "days",
|
||||||
|
|
||||||
|
week = "week",
|
||||||
|
weeks = "weeks",
|
||||||
|
|
||||||
|
month = "month",
|
||||||
|
months = "months",
|
||||||
|
|
||||||
|
year = "year",
|
||||||
|
years = "years",
|
||||||
|
)
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package tech.carcadex.kotlinbukkitkit.utility.utils
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
public class HashcodeComparator<T> : Comparator<T> {
|
||||||
|
override fun compare(p0: T, p1: T): Int {
|
||||||
|
return p0.hashCode().compareTo(p1.hashCode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object PluginComparator : Comparator<Plugin> {
|
||||||
|
override fun compare(p0: Plugin, p1: Plugin): Int {
|
||||||
|
return p0.name.compareTo(p1.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object PlayerComparator : Comparator<Player> {
|
||||||
|
override fun compare(p0: Player, p1: Player): Int {
|
||||||
|
return p0.uniqueId.compareTo(p1.uniqueId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public val KClassComparator: HashcodeComparator<KClass<*>> = HashcodeComparator<KClass<*>>()
|
||||||
Reference in new issue
Block a user