From 4f42353794acf6520f45e43ad713984d54970e5e Mon Sep 17 00:00:00 2001
From: fllipeis
Date: Wed, 17 Dec 2025 20:44:35 +0100
Subject: [PATCH 01/10] refactor: migrate to platform CloudApi and remove
ControllerApi usage
---
build.gradle.kts | 8 ++--
gradle/libs.versions.toml | 23 +++++++---
.../plugin/api/shared/extension/Extension.kt | 4 +-
.../shared/pattern/ServerPatternIdentifier.kt | 46 ++++++-------------
.../argument/PropertiesArgumentsResolver.kt | 4 +-
.../group/PlayerCountArgumentsResolver.kt | 29 ++++++------
.../group/ServerCountArgumentsResolver.kt | 30 ++++++------
.../provider/AbstractPlaceholderProvider.kt | 12 ++---
.../provider/GroupPlaceholderProvider.kt | 16 +++----
.../provider/ServerPlaceholderProvider.kt | 6 +--
.../single/SingleGroupPlaceholderExecutor.kt | 22 ++++-----
.../single/SinglePlaceholderExecutor.kt | 12 ++---
.../single/SingleServerPlaceholderExecutor.kt | 14 +++---
13 files changed, 103 insertions(+), 123 deletions(-)
diff --git a/build.gradle.kts b/build.gradle.kts
index 945f334..54a111e 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -7,7 +7,7 @@ plugins {
val baseVersion = "0.0.1"
val commitHash = System.getenv("COMMIT_HASH")
val timestamp = System.currentTimeMillis() // Temporary to be able to build and publish directly out of fix branch with same commit hash
-val snapshotVersion = "${baseVersion}-dev.${timestamp}-${commitHash}"
+val snapshotVersion = "${baseVersion}-platform.${timestamp}-${commitHash}"
allprojects {
group = "app.simplecloud.plugin"
@@ -19,7 +19,6 @@ allprojects {
maven("https://libraries.minecraft.net")
maven("https://buf.build/gen/maven")
maven("https://repo.simplecloud.app/snapshots")
- maven("https://buf.build/gen/maven")
maven("https://repo.papermc.io/repository/maven-public")
}
}
@@ -31,8 +30,11 @@ subprojects {
dependencies {
testImplementation(rootProject.libs.kotlin.test)
compileOnly(rootProject.libs.kotlin.jvm)
- compileOnly(rootProject.libs.bundles.simpleCloudController)
+ compileOnly(rootProject.libs.kotlin.coroutines)
+ compileOnly(rootProject.libs.simplecloud.api)
compileOnly(rootProject.libs.bundles.adventure)
+ compileOnly(rootProject.libs.bundles.configurate)
+ compileOnly(rootProject.libs.slf4j.api)
}
kotlin {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 842a576..246a962 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,22 +1,31 @@
[versions]
-kotlin = "2.0.20"
-simpleCloudController = "0.0.30-dev.e3e27fc"
+kotlin = "2.1.0"
+kotlin-coroutines = "1.10.2"
+simplecloud-api = "0.1.0-platform.5-dev.1765910097897-3d59340"
sonatype-central-portal-publisher = "1.2.3"
adventure = "4.18.0"
+configurate = "4.2.0"
+slf4j = "2.0.17"
[libraries]
kotlin-jvm = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
+kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
-simpleCloudControllerApi = { module = "app.simplecloud.controller:controller-api", version.ref = "simpleCloudController" }
-simpleCloudControllerShared = { module = "app.simplecloud.controller:controller-shared", version.ref = "simpleCloudController" }
+simplecloud-api = { module = "app.simplecloud.api:api", version.ref = "simplecloud-api" }
adventure = { module = "net.kyori:adventure-api", version.ref = "adventure" }
-adventureMiniMessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
+adventure-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
+
+configurate-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate" }
+configurate-gson = { module = "org.spongepowered:configurate-gson", version.ref = "configurate" }
+configurate-extra-kotlin = { module = "org.spongepowered:configurate-extra-kotlin", version.ref = "configurate" }
+
+slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
[bundles]
-simpleCloudController = ["simpleCloudControllerApi", "simpleCloudControllerShared"]
-adventure = ["adventure", "adventureMiniMessage"]
+adventure = ["adventure", "adventure-minimessage"]
+configurate = ["configurate-yaml", "configurate-gson", "configurate-extra-kotlin"]
[plugins]
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/extension/Extension.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/extension/Extension.kt
index 913c9b7..2fdc7fc 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/extension/Extension.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/extension/Extension.kt
@@ -1,7 +1,7 @@
package app.simplecloud.plugin.api.shared.extension
-import app.simplecloud.controller.shared.group.Group
-import app.simplecloud.controller.shared.server.Server
+import app.simplecloud.api.group.Group
+import app.simplecloud.api.server.Server
import app.simplecloud.plugin.api.shared.placeholder.PlaceholderProvider
import app.simplecloud.plugin.api.shared.placeholder.argument.ArgumentsResolver
import net.kyori.adventure.text.Component
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/pattern/ServerPatternIdentifier.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/pattern/ServerPatternIdentifier.kt
index 1f06f4d..e1ce9b4 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/pattern/ServerPatternIdentifier.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/pattern/ServerPatternIdentifier.kt
@@ -1,29 +1,21 @@
package app.simplecloud.plugin.api.shared.pattern
-import app.simplecloud.controller.api.ControllerApi
-import app.simplecloud.controller.shared.group.Group
-import app.simplecloud.controller.shared.server.Server
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.group.Group
+import app.simplecloud.api.server.Server
import app.simplecloud.plugin.api.shared.pretty.StringPrettifier
-
-/**
- * @author Niklas Nieberler
- */
+import kotlinx.coroutines.future.await
class ServerPatternIdentifier(
private val pattern: String = "-",
regexPattern: String = pattern
.replace("", "(?[a-zA-Z]+)")
.replace("", "(?\\d+)"),
- private val controllerApi: ControllerApi.Coroutine = ControllerApi.createCoroutineApi()
+ private val cloudApi: CloudApi = CloudApi.create()
) {
private val regex = Regex(regexPattern)
- /**
- * Gets the group and numerical id matching the [pattern] in a [Pair]
- * @param name the server name
- * @param customRegex custom regex pattern
- */
fun parse(name: String, customRegex: Regex? = null): Pair {
val matchResult = customRegex?.matchEntire(name) ?: this.regex.matchEntire(name)
if (matchResult == null)
@@ -36,34 +28,26 @@ class ServerPatternIdentifier(
return Pair(groupName, numericalId)
}
- /**
- * Gets a server string as a set in the pattern
- * @param server replaces it to pattern
- */
fun parseServerToPattern(server: Server): String {
return this.pattern
- .replace("", server.group)
- .replace("", server.properties["pretty-name"] ?: StringPrettifier.prettify(server.group))
- .replace("", server.uniqueId)
- .replace("", server.uniqueId)
+ .replace("", server.serverBase.name)
+ .replace("", server.properties["pretty-name"]?.toString() ?: StringPrettifier.prettify(server.serverBase.name))
+ .replace("", server.serverId)
+ .replace("", server.serverId)
.replace("", server.numericalId.toString())
}
- /**
- * Gets the [Group] by the matching [pattern]
- * @param name the server name
- */
suspend fun getGroup(name: String): Group? {
val groupName = parse(name).first
- return this.controllerApi.getGroups().getGroupByName(groupName)
+ return try {
+ this.cloudApi.group().getGroupByName(groupName).await()
+ } catch (e: Exception) {
+ null
+ }
}
- /**
- * Gets the numerical id by the matching [pattern]
- * @param name the server name
- */
fun getNumericalId(name: String): Int {
return parse(name).second
}
-}
\ No newline at end of file
+}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/PropertiesArgumentsResolver.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/PropertiesArgumentsResolver.kt
index 5ae25e2..44badb3 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/PropertiesArgumentsResolver.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/PropertiesArgumentsResolver.kt
@@ -8,7 +8,7 @@ import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue
*/
class PropertiesArgumentsResolver(
- private val properties: Map
+ private val properties: Map
) : ArgumentsResolver {
override fun getKey() = "property"
@@ -17,7 +17,7 @@ class PropertiesArgumentsResolver(
val argumentName = arguments.popOr("property expected").value()
val defaultArgument = arguments.peek()?.value() ?: ""
val string = this.properties[argumentName] ?: defaultArgument
- return Tag.preProcessParsed(string)
+ return Tag.preProcessParsed(string.toString())
}
}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/group/PlayerCountArgumentsResolver.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/group/PlayerCountArgumentsResolver.kt
index ee13478..c319290 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/group/PlayerCountArgumentsResolver.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/group/PlayerCountArgumentsResolver.kt
@@ -1,21 +1,15 @@
package app.simplecloud.plugin.api.shared.placeholder.argument.group
-import app.simplecloud.controller.api.ControllerApi
-import app.simplecloud.controller.shared.group.Group
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.group.Group
+import app.simplecloud.api.server.ServerState
import app.simplecloud.plugin.api.shared.placeholder.argument.ArgumentsResolver
-import build.buf.gen.simplecloud.controller.v1.ServerState
+import kotlinx.coroutines.future.await
import net.kyori.adventure.text.minimessage.tag.Tag
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue
-import kotlin.collections.filter
-import kotlin.collections.firstOrNull
-import kotlin.collections.sumOf
-
-/**
- * @author Niklas Nieberler
- */
class PlayerCountArgumentsResolver(
- private val controllerApi: ControllerApi.Coroutine,
+ private val cloudApi: CloudApi,
private val group: Group,
) : ArgumentsResolver {
@@ -27,10 +21,13 @@ class PlayerCountArgumentsResolver(
return Tag.preProcessParsed(findPlayerCount(this.group, serverState).toString())
}
- private suspend fun findPlayerCount(group: Group, state: ServerState?): Long {
- return this.controllerApi.getServers().getServersByGroup(group)
- .filter { it.state == state }
- .sumOf { it.playerCount }
+ private suspend fun findPlayerCount(group: Group, state: ServerState?): Int {
+ val servers = this.cloudApi.server().getServersByGroup(group.name).await()
+ return if (state != null) {
+ servers.filter { it.state == state }.sumOf { it.playerCount }
+ } else {
+ servers.sumOf { it.playerCount }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/group/ServerCountArgumentsResolver.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/group/ServerCountArgumentsResolver.kt
index 5e2dd5c..2afedc6 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/group/ServerCountArgumentsResolver.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/argument/group/ServerCountArgumentsResolver.kt
@@ -1,20 +1,15 @@
package app.simplecloud.plugin.api.shared.placeholder.argument.group
-import app.simplecloud.controller.api.ControllerApi
-import app.simplecloud.controller.shared.group.Group
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.group.Group
+import app.simplecloud.api.server.ServerState
import app.simplecloud.plugin.api.shared.placeholder.argument.ArgumentsResolver
-import build.buf.gen.simplecloud.controller.v1.ServerState
+import kotlinx.coroutines.future.await
import net.kyori.adventure.text.minimessage.tag.Tag
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue
-import kotlin.collections.filter
-import kotlin.collections.firstOrNull
-
-/**
- * @author Niklas Nieberler
- */
class ServerCountArgumentsResolver(
- private val controllerApi: ControllerApi.Coroutine,
+ private val cloudApi: CloudApi,
private val group: Group,
) : ArgumentsResolver {
@@ -23,13 +18,16 @@ class ServerCountArgumentsResolver(
override suspend fun resolve(arguments: ArgumentQueue): Tag? {
val text = arguments.popOr("all").value()
val serverState = ServerState.entries.firstOrNull { it.name.equals(text, true) }
- return Tag.preProcessParsed(findPlayerCount(this.group, serverState).toString())
+ return Tag.preProcessParsed(findServerCount(this.group, serverState).toString())
}
- private suspend fun findPlayerCount(group: Group, state: ServerState?): Int {
- return this.controllerApi.getServers().getServersByGroup(group)
- .filter { it.state == state }
- .size
+ private suspend fun findServerCount(group: Group, state: ServerState?): Int {
+ val servers = this.cloudApi.server().getServersByGroup(group.name).await()
+ return if (state != null) {
+ servers.filter { it.state == state }.size
+ } else {
+ servers.size
+ }
}
-}
\ No newline at end of file
+}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/AbstractPlaceholderProvider.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/AbstractPlaceholderProvider.kt
index bffc9dd..d8b9427 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/AbstractPlaceholderProvider.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/AbstractPlaceholderProvider.kt
@@ -1,6 +1,6 @@
package app.simplecloud.plugin.api.shared.placeholder.provider
-import app.simplecloud.controller.api.ControllerApi
+import app.simplecloud.api.CloudApi
import app.simplecloud.plugin.api.shared.extension.text
import app.simplecloud.plugin.api.shared.placeholder.argument.ArgumentsResolver
import app.simplecloud.plugin.api.shared.placeholder.single.SinglePlaceholderExecutor
@@ -16,15 +16,15 @@ abstract class AbstractPlaceholderProvider(
private val executor: SinglePlaceholderExecutor,
) {
- private val controllerApi = ControllerApi.createCoroutineApi()
+ private val cloudApi = CloudApi.create()
/**
* Gets the list of all available [ArgumentsResolver]
- * @param controllerApi the instance of [ControllerApi.Coroutine]
+ * @param cloudApi the instance of [CloudApi]
* @param value for the placeholder
*/
abstract suspend fun getArgumentsResolvers(
- controllerApi: ControllerApi.Coroutine,
+ cloudApi: CloudApi,
value: T,
): List
@@ -39,10 +39,10 @@ abstract class AbstractPlaceholderProvider(
vararg argumentsResolver: ArgumentsResolver,
): TagResolver {
val availableArgumentsResolver = listOf(
- *getArgumentsResolvers(this.controllerApi, value).toTypedArray(),
+ *getArgumentsResolvers(this.cloudApi, value).toTypedArray(),
*argumentsResolver
)
- val singleTagResolver = this.executor.getTagResolver(this.controllerApi, value, prefix)
+ val singleTagResolver = this.executor.getTagResolver(this.cloudApi, value, prefix)
return TagResolver.resolver(
singleTagResolver,
*availableArgumentsResolver
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/GroupPlaceholderProvider.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/GroupPlaceholderProvider.kt
index 1d68f33..16e9726 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/GroupPlaceholderProvider.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/GroupPlaceholderProvider.kt
@@ -1,24 +1,20 @@
package app.simplecloud.plugin.api.shared.placeholder.provider
-import app.simplecloud.controller.api.ControllerApi
-import app.simplecloud.controller.shared.group.Group
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.group.Group
import app.simplecloud.plugin.api.shared.placeholder.argument.PropertiesArgumentsResolver
import app.simplecloud.plugin.api.shared.placeholder.argument.group.PlayerCountArgumentsResolver
import app.simplecloud.plugin.api.shared.placeholder.argument.group.ServerCountArgumentsResolver
import app.simplecloud.plugin.api.shared.placeholder.single.SingleGroupPlaceholderExecutor
-/**
- * @author Niklas Nieberler
- */
-
class GroupPlaceholderProvider : AbstractPlaceholderProvider(
SingleGroupPlaceholderExecutor()
) {
- override suspend fun getArgumentsResolvers(controllerApi: ControllerApi.Coroutine, value: Group) = listOf(
+ override suspend fun getArgumentsResolvers(cloudApi: CloudApi, value: Group) = listOf(
PropertiesArgumentsResolver(value.properties),
- PlayerCountArgumentsResolver(controllerApi, value),
- ServerCountArgumentsResolver(controllerApi, value)
+ PlayerCountArgumentsResolver(cloudApi, value),
+ ServerCountArgumentsResolver(cloudApi, value)
)
-}
\ No newline at end of file
+}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/ServerPlaceholderProvider.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/ServerPlaceholderProvider.kt
index c08f797..8a78b99 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/ServerPlaceholderProvider.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/ServerPlaceholderProvider.kt
@@ -1,7 +1,7 @@
package app.simplecloud.plugin.api.shared.placeholder.provider
-import app.simplecloud.controller.api.ControllerApi
-import app.simplecloud.controller.shared.server.Server
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.server.Server
import app.simplecloud.plugin.api.shared.placeholder.argument.*
import app.simplecloud.plugin.api.shared.placeholder.single.SingleServerPlaceholderExecutor
@@ -13,7 +13,7 @@ class ServerPlaceholderProvider : AbstractPlaceholderProvider(
SingleServerPlaceholderExecutor()
) {
- override suspend fun getArgumentsResolvers(controllerApi: ControllerApi.Coroutine, value: Server) = listOf(
+ override suspend fun getArgumentsResolvers(cloudApi: CloudApi, value: Server) = listOf(
PropertiesArgumentsResolver(value.properties)
)
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SingleGroupPlaceholderExecutor.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SingleGroupPlaceholderExecutor.kt
index d81439d..0eb254d 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SingleGroupPlaceholderExecutor.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SingleGroupPlaceholderExecutor.kt
@@ -1,30 +1,24 @@
package app.simplecloud.plugin.api.shared.placeholder.single
-import app.simplecloud.controller.api.ControllerApi
-import app.simplecloud.controller.shared.group.Group
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.group.Group
import app.simplecloud.plugin.api.shared.placeholder.async.AsyncPlaceholder
-
-/**
- * @author Niklas Nieberler
- */
+import kotlinx.coroutines.future.await
class SingleGroupPlaceholderExecutor : SinglePlaceholderExecutor {
- override fun getAsyncPlaceholders(controllerApi: ControllerApi.Coroutine) = listOf>(
+ override fun getAsyncPlaceholders(cloudApi: CloudApi) = listOf>(
AsyncPlaceholder("name") { it.name },
AsyncPlaceholder("type") { it.type },
AsyncPlaceholder("max_players") { it.maxPlayers },
AsyncPlaceholder("min_memory") { it.minMemory },
AsyncPlaceholder("max_memory") { it.maxMemory },
- AsyncPlaceholder("start_port") { it.startPort },
- AsyncPlaceholder("min_online_count") { it.minOnlineCount },
- AsyncPlaceholder("max_online_count") { it.maxOnlineCount },
- AsyncPlaceholder("online_players") { getOnlinePlayersByGroup(controllerApi, it) },
+ AsyncPlaceholder("online_players") { getOnlinePlayersByGroup(cloudApi, it) },
)
- private suspend fun getOnlinePlayersByGroup(controllerApi: ControllerApi.Coroutine, group: Group): Long {
- return controllerApi.getServers().getServersByGroup(group)
+ private suspend fun getOnlinePlayersByGroup(cloudApi: CloudApi, group: Group): Int {
+ return cloudApi.server().getServersByGroup(group.name).await()
.sumOf { it.playerCount }
}
-}
\ No newline at end of file
+}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SinglePlaceholderExecutor.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SinglePlaceholderExecutor.kt
index 5140084..bd80632 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SinglePlaceholderExecutor.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SinglePlaceholderExecutor.kt
@@ -1,6 +1,6 @@
package app.simplecloud.plugin.api.shared.placeholder.single
-import app.simplecloud.controller.api.ControllerApi
+import app.simplecloud.api.CloudApi
import app.simplecloud.plugin.api.shared.placeholder.async.AsyncPlaceholder
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver
@@ -12,22 +12,22 @@ interface SinglePlaceholderExecutor {
/**
* Gets a list with all available [AsyncPlaceholder]
- * @param controllerApi the instance of [ControllerApi.Coroutine]
+ * @param cloudApi the instance of [app.simplecloud.api.CloudApi]
*/
- fun getAsyncPlaceholders(controllerApi: ControllerApi.Coroutine): List>
+ fun getAsyncPlaceholders(cloudApi: CloudApi): List>
/**
* Gets a [TagResolver] with all available tag resolvers from the [getAsyncPlaceholders] method
- * @param controllerApi the instance of [ControllerApi.Coroutine]
+ * @param cloudApi the instance of [CloudApi]
* @param value for the placeholder
* @param prefix first name for the placeholder key
*/
suspend fun getTagResolver(
- controllerApi: ControllerApi.Coroutine,
+ cloudApi: CloudApi,
value: T,
prefix: String? = null,
): TagResolver {
- return TagResolver.resolver(getAsyncPlaceholders(controllerApi)
+ return TagResolver.resolver(getAsyncPlaceholders(cloudApi)
.map { it.invokeTagResolver(value, prefix) })
}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SingleServerPlaceholderExecutor.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SingleServerPlaceholderExecutor.kt
index 938642e..d66bdc4 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SingleServerPlaceholderExecutor.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SingleServerPlaceholderExecutor.kt
@@ -1,7 +1,7 @@
package app.simplecloud.plugin.api.shared.placeholder.single
-import app.simplecloud.controller.api.ControllerApi
-import app.simplecloud.controller.shared.server.Server
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.server.Server
import app.simplecloud.plugin.api.shared.placeholder.async.AsyncPlaceholder
import app.simplecloud.plugin.api.shared.pretty.StringPrettifier
@@ -11,14 +11,14 @@ import app.simplecloud.plugin.api.shared.pretty.StringPrettifier
class SingleServerPlaceholderExecutor : SinglePlaceholderExecutor {
- override fun getAsyncPlaceholders(controllerApi: ControllerApi.Coroutine) = listOf>(
- AsyncPlaceholder("id") { it.uniqueId },
+ override fun getAsyncPlaceholders(cloudApi: CloudApi) = listOf>(
+ AsyncPlaceholder("id") { it.serverId },
AsyncPlaceholder("numerical_id") { it.numericalId },
- AsyncPlaceholder("group_name") { it.group },
+ AsyncPlaceholder("group_name") { it.serverBase.name },
AsyncPlaceholder("group_pretty_name") {
- it.properties["pretty-name"] ?: StringPrettifier.prettify(it.group)
+ it.properties["pretty-name"] ?: StringPrettifier.prettify(it.serverBase.name)
},
- AsyncPlaceholder("type") { it.type },
+ AsyncPlaceholder("type") { it.serverBase.type },
AsyncPlaceholder("state") { it.state },
AsyncPlaceholder("ip") { it.ip },
AsyncPlaceholder("port") { it.port },
From e57ed95dcaf38f88de1593363c73a2e83c6923c2 Mon Sep 17 00:00:00 2001
From: fllipeis
Date: Wed, 17 Dec 2025 20:45:43 +0100
Subject: [PATCH 02/10] feat: add branch input support for release-dev workflow
---
.github/workflows/release-dev.yml | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml
index 35672f9..ce42184 100644
--- a/.github/workflows/release-dev.yml
+++ b/.github/workflows/release-dev.yml
@@ -2,6 +2,15 @@ name: Create Release
on:
workflow_dispatch:
+ inputs:
+ branch:
+ description: 'Branch to build and publish'
+ required: true
+ default: 'develop'
+ type: choice
+ options:
+ - develop
+ - feat/platform
jobs:
build:
@@ -12,7 +21,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
with:
- ref: develop # Ensure it works from the develop branch
+ ref: ${{ inputs.branch }}
- name: Set up JDK 21
uses: actions/setup-java@v3
@@ -56,7 +65,7 @@ jobs:
release_name: v${{ env.GRADLE_VERSION }}-dev.${{ env.COMMIT_HASH }}
draft: false
prerelease: true
- commitish: develop
+ commitish: ${{ inputs.branch }}
body: |
This release contains dev builds for all Gradle modules.
env:
From 95a0407305117f874d75820789285a475c5fd6d7 Mon Sep 17 00:00:00 2001
From: "niklas.nieberler"
Date: Mon, 6 Apr 2026 14:07:04 +0200
Subject: [PATCH 03/10] feat: add PersistentServerPlaceholder support
---
gradle/libs.versions.toml | 2 +-
.../plugin/api/shared/extension/Extension.kt | 9 ++++++
.../shared/placeholder/PlaceholderProvider.kt | 3 ++
.../PersistentServerPlaceholderProvider.kt | 20 +++++++++++++
...nglePersistentServerPlaceholderExecutor.kt | 29 +++++++++++++++++++
5 files changed, 62 insertions(+), 1 deletion(-)
create mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/PersistentServerPlaceholderProvider.kt
create mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SinglePersistentServerPlaceholderExecutor.kt
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 246a962..fc805a0 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,7 +1,7 @@
[versions]
kotlin = "2.1.0"
kotlin-coroutines = "1.10.2"
-simplecloud-api = "0.1.0-platform.5-dev.1765910097897-3d59340"
+simplecloud-api = "0.1.0-platform.22-dev.1774376168759-1e20f57"
sonatype-central-portal-publisher = "1.2.3"
adventure = "4.18.0"
configurate = "4.2.0"
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/extension/Extension.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/extension/Extension.kt
index 2fdc7fc..9ac234a 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/extension/Extension.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/extension/Extension.kt
@@ -1,6 +1,7 @@
package app.simplecloud.plugin.api.shared.extension
import app.simplecloud.api.group.Group
+import app.simplecloud.api.persistentserver.PersistentServer
import app.simplecloud.api.server.Server
import app.simplecloud.plugin.api.shared.placeholder.PlaceholderProvider
import app.simplecloud.plugin.api.shared.placeholder.argument.ArgumentsResolver
@@ -32,4 +33,12 @@ suspend fun String.appendToComponent(
vararg argumentsResolver: ArgumentsResolver
): Component {
return PlaceholderProvider.groupPlaceholderProvider.append(group, this, prefix, *argumentsResolver)
+}
+
+suspend fun String.appendToComponent(
+ persistentServer: PersistentServer,
+ prefix: String? = null,
+ vararg argumentsResolver: ArgumentsResolver
+): Component {
+ return PlaceholderProvider.persistentServerPlaceholderProvider.append(persistentServer, this, prefix, *argumentsResolver)
}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/PlaceholderProvider.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/PlaceholderProvider.kt
index ea02261..24205dd 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/PlaceholderProvider.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/PlaceholderProvider.kt
@@ -1,6 +1,7 @@
package app.simplecloud.plugin.api.shared.placeholder
import app.simplecloud.plugin.api.shared.placeholder.provider.GroupPlaceholderProvider
+import app.simplecloud.plugin.api.shared.placeholder.provider.PersistentServerPlaceholderProvider
import app.simplecloud.plugin.api.shared.placeholder.provider.ServerPlaceholderProvider
/**
@@ -13,4 +14,6 @@ object PlaceholderProvider {
val groupPlaceholderProvider = GroupPlaceholderProvider()
+ val persistentServerPlaceholderProvider = PersistentServerPlaceholderProvider()
+
}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/PersistentServerPlaceholderProvider.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/PersistentServerPlaceholderProvider.kt
new file mode 100644
index 0000000..1ade4b9
--- /dev/null
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/PersistentServerPlaceholderProvider.kt
@@ -0,0 +1,20 @@
+package app.simplecloud.plugin.api.shared.placeholder.provider
+
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.persistentserver.PersistentServer
+import app.simplecloud.plugin.api.shared.placeholder.argument.*
+import app.simplecloud.plugin.api.shared.placeholder.single.SinglePersistentServerPlaceholderExecutor
+
+/**
+ * @author Niklas Nieberler
+ */
+
+class PersistentServerPlaceholderProvider : AbstractPlaceholderProvider(
+ SinglePersistentServerPlaceholderExecutor()
+) {
+
+ override suspend fun getArgumentsResolvers(cloudApi: CloudApi, value: PersistentServer) = listOf(
+ PropertiesArgumentsResolver(value.properties)
+ )
+
+}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SinglePersistentServerPlaceholderExecutor.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SinglePersistentServerPlaceholderExecutor.kt
new file mode 100644
index 0000000..2e99d5c
--- /dev/null
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/single/SinglePersistentServerPlaceholderExecutor.kt
@@ -0,0 +1,29 @@
+package app.simplecloud.plugin.api.shared.placeholder.single
+
+import app.simplecloud.api.CloudApi
+import app.simplecloud.api.persistentserver.PersistentServer
+import app.simplecloud.api.server.Server
+import app.simplecloud.plugin.api.shared.placeholder.async.AsyncPlaceholder
+import app.simplecloud.plugin.api.shared.pretty.StringPrettifier
+
+/**
+ * @author Niklas Nieberler
+ */
+
+class SinglePersistentServerPlaceholderExecutor : SinglePlaceholderExecutor {
+
+ override fun getAsyncPlaceholders(cloudApi: CloudApi) = listOf>(
+ AsyncPlaceholder("id") { it.persistentServerId },
+ AsyncPlaceholder("name") { it.name },
+ AsyncPlaceholder("pretty_name") {
+ it.properties["pretty-name"] ?: StringPrettifier.prettify(it.name)
+ },
+ AsyncPlaceholder("type") { it.type },
+ AsyncPlaceholder("online_players") { it.playerCount },
+ AsyncPlaceholder("max_players") { it.maxPlayers },
+ AsyncPlaceholder("min_memory") { it.minMemory },
+ AsyncPlaceholder("max_memory") { it.maxMemory },
+ AsyncPlaceholder("motd") { it.properties["motd"] ?: "A Minecraft server" }
+ )
+
+}
\ No newline at end of file
From ae9df6d5c0435acff2866d824b32d66308e3c01e Mon Sep 17 00:00:00 2001
From: "niklas.nieberler"
Date: Mon, 6 Apr 2026 17:21:39 +0200
Subject: [PATCH 04/10] ref: create configs or config repositories
---
.../plugin/api/shared/config/ConfigFactory.kt | 270 -------------
.../config/ConfigurateWatcherRegistry.kt | 75 ++++
.../api/shared/config/ConfigurationFactory.kt | 51 +++
.../shared/config/YamlDirectoryRepository.kt | 96 +++++
.../api/shared/config/YamlFileConfigurator.kt | 50 +++
.../config/repository/DirectoryRepository.kt | 362 ------------------
.../config/repository/handler/FileHandler.kt | 15 -
.../repository/handler/PNGFileHandler.kt | 22 --
.../repository/handler/YamlFileHandler.kt | 50 ---
.../serializer}/GenericEnumSerializer.kt | 11 +-
.../exception/ConfigurationException.kt | 4 -
.../shared/exception/RepositoryException.kt | 3 -
.../shared/repository/LoadableRepository.kt | 7 -
.../api/shared/repository/Repository.kt | 13 -
.../ResourcedYamlDirectoryRepository.kt | 91 -----
.../repository/YamlDirectoryRepository.kt | 137 -------
16 files changed, 279 insertions(+), 978 deletions(-)
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigFactory.kt
create mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigurateWatcherRegistry.kt
create mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigurationFactory.kt
create mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlDirectoryRepository.kt
create mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/DirectoryRepository.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/FileHandler.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/PNGFileHandler.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/YamlFileHandler.kt
rename plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/{repository => config/serializer}/GenericEnumSerializer.kt (81%)
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/exception/ConfigurationException.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/exception/RepositoryException.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/LoadableRepository.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/Repository.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/ResourcedYamlDirectoryRepository.kt
delete mode 100644 plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/YamlDirectoryRepository.kt
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigFactory.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigFactory.kt
deleted file mode 100644
index b6f8d74..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigFactory.kt
+++ /dev/null
@@ -1,270 +0,0 @@
-package app.simplecloud.plugin.api.shared.config
-
-import app.simplecloud.plugin.api.shared.exception.ConfigurationException
-import app.simplecloud.plugin.api.shared.repository.GenericEnumSerializer
-import kotlinx.coroutines.*
-import org.slf4j.LoggerFactory
-import org.spongepowered.configurate.ConfigurationOptions
-import org.spongepowered.configurate.kotlin.objectMapperFactory
-import org.spongepowered.configurate.yaml.NodeStyle
-import org.spongepowered.configurate.yaml.YamlConfigurationLoader
-import java.io.File
-import java.nio.file.*
-import java.util.concurrent.atomic.AtomicReference
-import kotlin.coroutines.CoroutineContext
-
-/**
- * A configuration factory that loads, saves and watches configuration files.
- * The factory automatically reloads the configuration when the file changes.
- *
- * Features:
- * - Thread-safe configuration handling
- * - File watching with automatic reloading
- * - Configuration change callbacks
- * - Validation support
- *
- * Usage:
- * ```
- * // Using create
- * val factory = ConfigFactory.create(File("config.yaml"))
- *
- * // Using create with custom coroutineContext
- * val factory = ConfigFactory.create(File("config.json"), Dispatchers.Default)
- *
- * // Add change listener
- * factory.onConfigChanged { oldConfig, newConfig ->
- * println("Config updated from $oldConfig to $newConfig")
- * }
- *
- * // Save the modified config
- * factory.save(modifiedConfig)
- *
- * // Optionally with validation
- * factory.save(modifiedConfig) { config ->
- * config.someField.isNotEmpty() // return true/false for validation
- * }
- * ```
- */
-class ConfigFactory(
- private val file: File,
- private val configClass: Class,
- private val coroutineContext: CoroutineContext = Dispatchers.IO
-) : AutoCloseable {
-
- private val logger = LoggerFactory.getLogger(ConfigFactory::class.java)
- private val configRef = AtomicReference()
- private val path: Path = file.toPath()
- private var watchJob: Job? = null
- private val changeListeners = mutableListOf Unit>()
- private val saveLock = Object()
- private val scope = CoroutineScope(coroutineContext + SupervisorJob())
-
- private val configurationLoader = YamlConfigurationLoader.builder()
- .path(path)
- .nodeStyle(NodeStyle.BLOCK)
- .defaultOptions { options ->
- options.serializers { builder ->
- builder.registerAnnotatedObjects(objectMapperFactory())
- builder.register(Enum::class.java, GenericEnumSerializer)
- }
- }
- .build()
-
- /**
- * Loads existing configuration or creates a new one with default values.
- * @param defaultConfig The default configuration to use if no file exists
- * @param validator Optional validation function for the configuration
- */
- fun loadOrCreate(defaultConfig: T, validator: ((T) -> Boolean)? = null) {
- if (!configClass.isInstance(defaultConfig)) {
- throw IllegalArgumentException("Default config must be an instance of ${configClass.name}")
- }
-
- if (file.exists()) {
- loadConfig(validator)
- } else {
- createDefaultConfig(defaultConfig, validator)
- }
-
- registerWatcher()
- }
-
- /**
- * Checks if the configuration file exists.
- * @return true if the configuration file exists
- */
- fun exists(): Boolean = file.exists()
-
- /**
- * Adds a listener that will be called whenever the configuration changes.
- * @param listener The listener function that receives old and new config
- */
- fun onConfigChanged(listener: suspend (T?, T) -> Unit) {
- synchronized(changeListeners) {
- changeListeners.add(listener)
- }
- }
-
- private fun createDefaultConfig(defaultConfig: T, validator: ((T) -> Boolean)?) {
- path.parent?.let { Files.createDirectories(it) }
- Files.createFile(path)
-
- if (validator?.invoke(defaultConfig) == false) {
- throw ConfigurationException("Default configuration failed validation")
- }
-
- synchronized(saveLock) {
- try {
- val node = configurationLoader.createNode()
- node.set(configClass, defaultConfig)
- configurationLoader.save(node)
- runBlocking(coroutineContext) {
- updateConfig(defaultConfig)
- }
- } catch (e: Exception) {
- throw ConfigurationException("Failed to save default configuration", e)
- }
- }
- }
-
- /**
- * Gets the current configuration.
- * @throws IllegalStateException if configuration is not loaded
- */
- fun getConfig(): T {
- return configRef.get() ?: throw IllegalStateException("Configuration not loaded or invalid type")
- }
-
- /**
- * Manually reloads the configuration from disk.
- * @param validator Optional validation function for the loaded configuration
- * @throws ConfigurationException if loading or validation fails
- */
- @Throws(ConfigurationException::class)
- fun reloadConfig(validator: ((T) -> Boolean)? = null) {
- loadConfig(validator)
- }
-
- @Throws(ConfigurationException::class)
- private fun loadConfig(validator: ((T) -> Boolean)? = null) {
- try {
- val node = configurationLoader.load(ConfigurationOptions.defaults())
- val loadedConfig = node.get(configClass)
- ?: throw ConfigurationException("Failed to parse configuration file")
-
- if (validator?.invoke(loadedConfig) == false) {
- throw ConfigurationException("Configuration failed validation")
- }
-
- runBlocking(coroutineContext) {
- updateConfig(loadedConfig)
- }
- } catch (e: Exception) {
- throw ConfigurationException("Failed to load configuration", e)
- }
- }
-
- /**
- * Saves the provided configuration to disk.
- * @param config The configuration to save
- * @param validator Optional validation function to run before saving
- * @throws ConfigurationException if saving or validation fails
- */
- @Throws(ConfigurationException::class)
- fun save(config: T, validator: ((T) -> Boolean)? = null) {
- if (validator?.invoke(config) == false) {
- throw ConfigurationException("Configuration failed validation")
- }
-
- synchronized(saveLock) {
- try {
- val node = configurationLoader.createNode()
- node.set(configClass, config)
- configurationLoader.save(node)
- runBlocking(coroutineContext) {
- updateConfig(config)
- }
- } catch (e: Exception) {
- throw ConfigurationException("Failed to save configuration", e)
- }
- }
- }
-
- private suspend fun updateConfig(newConfig: T) {
- val oldConfig = configRef.get()
- configRef.set(newConfig)
-
- val listeners = synchronized(changeListeners) {
- changeListeners.toList()
- }
-
- listeners.forEach { listener ->
- try {
- withContext(coroutineContext) {
- listener(oldConfig, newConfig)
- }
- } catch (e: Exception) {
- logger.error("Error in config change listener", e)
- }
- }
- }
-
- private fun registerWatcher(): Job {
- val watchService = FileSystems.getDefault().newWatchService()
- path.parent?.register(
- watchService,
- StandardWatchEventKinds.ENTRY_CREATE,
- StandardWatchEventKinds.ENTRY_MODIFY
- )
-
- return scope.launch {
- watchService.use { watchService ->
- while (isActive) {
- val key = watchService.take()
- key.pollEvents().forEach { event ->
- handleWatchEvent(event)
- }
- if (!key.reset()) {
- break
- }
- }
- }
- }.also { watchJob = it }
- }
-
- private suspend fun handleWatchEvent(event: WatchEvent<*>) {
- val path = event.context() as? Path ?: return
- if (!file.name.contains(path.toString())) return
-
- when (event.kind()) {
- StandardWatchEventKinds.ENTRY_CREATE,
- StandardWatchEventKinds.ENTRY_MODIFY -> {
- delay(100)
- try {
- loadConfig()
- } catch (e: ConfigurationException) {
- logger.error("Failed to reload configuration: ${e.message}", e)
- }
- }
-
- else -> {}
- }
- }
-
- override fun close() {
- scope.cancel()
- watchJob?.cancel()
- }
-
- companion object {
- /**
- * Creates a new ConfigFactory instance.
- * @param file The configuration file
- * @param coroutineContext The coroutine context to use for async operations
- */
- inline fun create(
- file: File,
- coroutineContext: CoroutineContext = Dispatchers.IO
- ): ConfigFactory = ConfigFactory(file, T::class.java, coroutineContext)
- }
-}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigurateWatcherRegistry.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigurateWatcherRegistry.kt
new file mode 100644
index 0000000..8cc2660
--- /dev/null
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigurateWatcherRegistry.kt
@@ -0,0 +1,75 @@
+package app.simplecloud.plugin.api.shared.config
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import java.io.File
+import java.nio.file.FileSystems
+import java.nio.file.Path
+import java.nio.file.WatchEvent
+
+/**
+ * @author Niklas Nieberler
+ */
+
+class ConfigurateWatcherRegistry {
+
+ private val events = hashMapOf, (File) -> Unit>()
+ private var watcherRequirements: (Path) -> Boolean = { false }
+
+ /**
+ * Adds an event for the watcher
+ * @param event to register
+ * @param function to handle
+ */
+ fun withEvent(vararg event: WatchEvent.Kind<*>, function: (File) -> Unit): ConfigurateWatcherRegistry {
+ event.forEach { this.events[it] = function }
+ return this
+ }
+
+ /**
+ * Adds a requirement for the event watcher
+ * @param function the requirement
+ */
+ fun withWatcherRequirements(function: (Path) -> Boolean): ConfigurateWatcherRegistry {
+ this.watcherRequirements = function
+ return this
+ }
+
+ /**
+ * Registers a new [java.nio.file.WatchService] for a path
+ * @param path to register the watcher
+ */
+ fun register(path: Path): Job {
+ val watchService = FileSystems.getDefault().newWatchService()
+
+ path.parent.register(
+ watchService,
+ *this.events.keys.toTypedArray()
+ )
+
+ return CoroutineScope(Dispatchers.IO).launch {
+ while (isActive) {
+ val watchKey = watchService.take()
+ watchKey.pollEvents().forEach { watchEvent(path, it) }
+ watchKey.reset()
+ }
+ }
+ }
+
+ private fun watchEvent(directoryPath: Path, event: WatchEvent<*>) {
+ val path = event.context() as? Path ?: return
+ if (!path.toString().endsWith(".yml"))
+ return
+
+ val resolvedPath = directoryPath.resolve(path)
+ if (this.watcherRequirements(resolvedPath))
+ return
+
+ val kind = event.kind()
+ this.events[kind]?.invoke(resolvedPath.toFile())
+ }
+
+}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigurationFactory.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigurationFactory.kt
new file mode 100644
index 0000000..fe2cbf7
--- /dev/null
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/ConfigurationFactory.kt
@@ -0,0 +1,51 @@
+package app.simplecloud.plugin.api.shared.config
+
+import java.io.File
+
+/**
+ * @author Niklas Nieberler
+ */
+
+class ConfigurationFactory(
+ private val file: File,
+ javaClass: Class,
+) {
+
+ private val yamlFileConfigurator = YamlFileConfigurator(javaClass)
+
+ private var config: E? = null
+
+ /**
+ * Loads the config and if it does not exist, it creates a default config
+ * @param defaultConfig the default configuration
+ */
+ fun loadOrCreate(defaultConfig: E): E {
+ if (this.file.exists()) {
+ return loadConfiguration()
+ ?: throw NullPointerException("failed to save config")
+ }
+
+ this.yamlFileConfigurator.save(this.file, defaultConfig)
+ this.config = defaultConfig
+ return defaultConfig
+ }
+
+ /**
+ * Gets the cached config file
+ */
+ fun get(): E {
+ return this.config ?: throw NullPointerException("failed to find config")
+ }
+
+ fun save(entry: E) {
+ this.yamlFileConfigurator.save(this.file, entry)
+ this.config = entry
+ }
+
+ private fun loadConfiguration(): E? {
+ val configuration = this.yamlFileConfigurator.load(this.file)
+ this.config = configuration
+ return configuration
+ }
+
+}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlDirectoryRepository.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlDirectoryRepository.kt
new file mode 100644
index 0000000..68e8cff
--- /dev/null
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlDirectoryRepository.kt
@@ -0,0 +1,96 @@
+package app.simplecloud.plugin.api.shared.config
+
+import java.io.File
+import java.nio.file.*
+
+/**
+ * @author Niklas Nieberler
+ */
+
+abstract class YamlDirectoryRepository(
+ private val directory: Path,
+ javaClass: Class
+) {
+
+ private val yamlFileConfigurator = YamlFileConfigurator(javaClass)
+ private val fileDirectory = directory.toFile()
+
+ private val cachedEntities = mutableMapOf()
+
+ /**
+ * Saves an element to the file directory
+ * @param entity to save
+ */
+ abstract fun save(entity: E)
+
+ abstract fun find(identifier: I): E?
+
+ /**
+ * Loads all cached entities from a directory
+ */
+ fun findAll(): List {
+ return this.cachedEntities.values.toList()
+ }
+
+ /**
+ * Loads the yaml directory repository with all elements
+ * @return list of all file entities
+ */
+ fun load(): List {
+ if (!this.fileDirectory.exists())
+ this.fileDirectory.mkdirs()
+
+ registerWatcher()
+
+ return Files.walk(this.directory)
+ .toList()
+ .map { it.toFile() }
+ .filter { it.name.endsWith(".yml") || it.name.endsWith(".yaml") }
+ .mapNotNull { load(it) }
+ }
+
+ /**
+ * Loads an entity from a file
+ * @param file to load
+ */
+ fun load(file: File): E? {
+ val entity = this.yamlFileConfigurator.load(file) ?: return null
+ this.cachedEntities[file] = entity
+ return entity
+ }
+
+ /**
+ * Saves an entity to the yaml formation
+ * @param name of the file
+ * @param entity to save
+ */
+ protected fun save(name: String, entity: E) {
+ val file = this.directory.resolve(name).toFile()
+ this.yamlFileConfigurator.save(file, entity)
+ this.cachedEntities[file] = entity
+ }
+
+ /**
+ * Deletes an entity file
+ * @param entity to delete
+ */
+ fun delete(entity: E) {
+ val file = this.cachedEntities.keys
+ .find { this.cachedEntities[it] == entity } ?: return
+ delete(file)
+ }
+
+ private fun delete(file: File) {
+ file.delete()
+ this.cachedEntities.remove(file)
+ }
+
+ private fun registerWatcher() {
+ ConfigurateWatcherRegistry()
+ .withEvent(StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY) { load(it) }
+ .withEvent(StandardWatchEventKinds.ENTRY_DELETE) { delete(it) }
+ .withWatcherRequirements { Files.isDirectory(it) }
+ .register(this.directory)
+ }
+
+}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
new file mode 100644
index 0000000..a8d565d
--- /dev/null
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
@@ -0,0 +1,50 @@
+package app.simplecloud.plugin.api.shared.config
+
+import app.simplecloud.plugin.api.shared.config.serializer.GenericEnumSerializer
+import org.spongepowered.configurate.ConfigurationOptions
+import org.spongepowered.configurate.kotlin.objectMapperFactory
+import org.spongepowered.configurate.yaml.NodeStyle
+import org.spongepowered.configurate.yaml.YamlConfigurationLoader
+import java.io.File
+
+/**
+ * @author Niklas Nieberler
+ */
+
+class YamlFileConfigurator(
+ private val javaClass: Class
+) {
+
+ private val configurationLoaders = hashMapOf()
+
+ private val defaultConfigurationBuilder = YamlConfigurationLoader.builder()
+ .nodeStyle(NodeStyle.BLOCK)
+ .defaultOptions { options ->
+ options.serializers { builder ->
+ builder.registerAnnotatedObjects(objectMapperFactory())
+ builder.register(Enum::class.java, GenericEnumSerializer)
+ }
+ }
+
+ fun save(file: File, entity: E) {
+ val configurationLoader = getOrCreateConfigurationLoader(file)
+ val node = configurationLoader.createNode(ConfigurationOptions.defaults())
+ node.set(this.javaClass, entity)
+ configurationLoader.save(node)
+ }
+
+ fun load(file: File): E? {
+ val configurationLoader = getOrCreateConfigurationLoader(file)
+ val node = configurationLoader.load(ConfigurationOptions.defaults())
+ return node.get(this.javaClass)
+ }
+
+ fun getOrCreateConfigurationLoader(file: File): YamlConfigurationLoader {
+ return this.configurationLoaders.getOrPut(file) {
+ this.defaultConfigurationBuilder
+ .path(file.toPath())
+ .build()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/DirectoryRepository.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/DirectoryRepository.kt
deleted file mode 100644
index a3b7805..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/DirectoryRepository.kt
+++ /dev/null
@@ -1,362 +0,0 @@
-package app.simplecloud.plugin.api.shared.config.repository
-
-import app.simplecloud.plugin.api.shared.config.repository.handler.FileHandler
-import app.simplecloud.plugin.api.shared.exception.RepositoryException
-import kotlinx.coroutines.*
-import org.slf4j.LoggerFactory
-import java.io.File
-import java.io.FileOutputStream
-import java.net.URL
-import java.nio.file.*
-import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.atomic.AtomicReference
-import java.util.jar.JarFile
-import kotlin.coroutines.CoroutineContext
-import kotlin.io.path.pathString
-
-/**
- * A directory repository that manages files of a specific type.
- * Features:
- * - Thread-safe entity handling
- * - File watching with automatic reloading
- * - Entity change callbacks
- * - Validation support
- * - Automatic resource cleanup
- *
- * @param I The type of the identifier
- * @param T The type of the entity
- */
-class DirectoryRepository constructor(
- private val directory: Path,
- private val fileHandler: FileHandler,
- private val coroutineContext: CoroutineContext,
- private val validator: ((T) -> Boolean)? = null
-) : AutoCloseable {
-
- private val logger = LoggerFactory.getLogger(DirectoryRepository::class.java)
- private val entities = ConcurrentHashMap>()
- private val identifiers = ConcurrentHashMap()
- private val changeListeners = mutableListOf Unit>()
- private val errorHandlers = mutableListOf<(Exception) -> Unit>()
- private val saveLock = Object()
- private val scope = CoroutineScope(coroutineContext + SupervisorJob())
- private var watchJob: Job? = null
-
- init {
- if (!directory.toFile().exists()) {
- directory.toFile().mkdirs()
- }
- }
-
- /**
- * Loads all entities from the directory or creates them using default values.
- * @param defaultEntities Default entities to use if no existing files are found
- * @throws RepositoryException if loading or creation fails
- */
- @Throws(RepositoryException::class)
- fun loadOrCreate(defaultEntities: Map = emptyMap()) {
- try {
- if (Files.newDirectoryStream(directory).use { it.none() }) {
- loadFromResources(defaultEntities)
- }
-
- load()
- registerWatcher()
- } catch (e: Exception) {
- handleError(RepositoryException("Failed to load repository", e))
- }
- }
-
- private fun loadFromResources(
- defaultEntities: Map,
- writeBom: Boolean = true
- ) {
- val targetDirectory = File(directory.toUri()).apply { mkdirs() }
- val last = directory.pathString.split('/').last()
-
- val resourceUrl = DirectoryRepository::class.java.getResource("/$last") ?: run {
- logger.warn("$last folder not found in resources")
- return
- }
-
- when (resourceUrl.protocol) {
- "file" -> handleFileProtocol(resourceUrl, targetDirectory)
- "jar" -> handleJarProtocol(resourceUrl, targetDirectory, writeBom)
- else -> logger.error("Unsupported protocol: ${resourceUrl.protocol}")
- }
-
- defaultEntities.forEach { (id, entity) -> save(id, entity) }
- }
-
- private fun handleFileProtocol(resourceUrl: URL, targetDirectory: File) {
- val resourceDir = File(resourceUrl.toURI())
-
- if (resourceDir.exists()) {
- resourceDir.copyRecursively(targetDirectory, overwrite = true)
- } else {
- logger.warn("Resource directory does not exist: ${resourceUrl.path}")
- }
- }
-
- private fun handleJarProtocol(
- resourceUrl: URL,
- targetDirectory: File,
- writeBom: Boolean
- ) {
- val jarPath = resourceUrl.path.substringBefore("!").removePrefix("file:")
- try {
- JarFile(jarPath).use { jarFile ->
- val last = directory.pathString.split('/').last()
- var filesProcessed = 0
- var filesFailed = 0
-
- jarFile.entries().asSequence()
- .filter { it.name.startsWith("$last/") && !it.isDirectory }
- .forEach { entry ->
- val targetFile = File(targetDirectory, entry.name.removePrefix("$last/"))
- targetFile.parentFile.mkdirs()
- try {
- jarFile.getInputStream(entry).use { inputStream ->
- FileOutputStream(targetFile).use { fos ->
- if (writeBom) {
- fos.write(0xEF)
- fos.write(0xBB)
- fos.write(0xBF)
- }
- inputStream.copyTo(fos)
- }
- }
- filesProcessed++
- logger.debug("Successfully extracted: ${entry.name}")
- } catch (e: Exception) {
- filesFailed++
- logger.error("Error copying file ${entry.name}")
- }
- }
-
- logger.debug("Processed $filesProcessed files from JAR ($filesFailed failed)")
- }
- } catch (e: Exception) {
- logger.error("Error processing JAR file")
- }
- }
-
- private fun load() =
- Files.walk(directory)
- .filter { !it.toFile().isDirectory && it.toString().endsWith(fileHandler.fileExtension) }
- .forEach { loadFile(it.toFile()) }
-
- private fun loadFile(file: File) {
- try {
- logger.info("Loading file ${file.name}")
- fileHandler.load(file)?.let { entity ->
- logger.info("Reached First ${file.name}")
- if (validateEntity(entity)) {
- logger.info("Reached ${file.name}")
- entities[file] = AtomicReference(entity)
- }
- }
- } catch (e: Exception) {
- handleError(RepositoryException("Error loading file ${file.name}", e))
- }
- }
-
- /**
- * Saves an entity to the repository
- * @throws RepositoryException if saving fails or validation fails
- */
- @Throws(RepositoryException::class)
- fun save(identifier: I, entity: T) {
- if (!validateEntity(entity)) {
- throw RepositoryException("Entity validation failed")
- }
-
- synchronized(saveLock) {
- try {
- val file = getFile(identifier)
- file.parentFile?.mkdirs()
-
- fileHandler.save(file, entity)
- val oldEntity = entities[file]?.get()
- entities[file] = AtomicReference(entity)
- identifiers[file] = identifier
-
- scope.launch {
- notifyChangeListeners(identifier, oldEntity, entity)
- }
- } catch (e: Exception) {
- handleError(RepositoryException("Failed to save entity", e))
- throw e
- }
- }
- }
-
- /**
- * Deletes an entity from the repository
- */
- fun delete(identifier: I): Boolean {
- val file = getFile(identifier)
- if (!file.exists()) return false
-
- synchronized(saveLock) {
- return try {
- val deleted = file.delete()
-
- if (deleted) {
- val oldEntity = entities.remove(file)?.get()
- identifiers.remove(file)
- if (oldEntity != null) {
- scope.launch {
- notifyChangeListeners(identifier, oldEntity, null)
- }
- }
- }
-
- deleted
- } catch (e: Exception) {
- handleError(RepositoryException("Failed to delete entity", e))
- false
- }
- }
- }
-
- /**
- * Finds an entity by identifier
- */
- fun find(identifier: I): T? =
- entities[getFile(identifier)]?.get()
-
-
- /**
- * Gets all entities in the repository
- */
- fun getAll(): List = entities.values.mapNotNull { it.get() }
-
- /**
- * Gets all identifiers in the repository
- */
- fun getAllIdentifiers(): Set = identifiers.values.toSet()
-
- /**
- * Adds a listener for entity changes
- */
- fun onEntityChanged(listener: suspend (I, T?, T?) -> Unit) {
- synchronized(changeListeners) {
- changeListeners.add(listener)
- }
- }
-
- /**
- * Adds an error handler
- */
- fun onError(handler: (Exception) -> Unit) {
- synchronized(errorHandlers) {
- errorHandlers.add(handler)
- }
- }
-
- private fun validateEntity(entity: T): Boolean {
- return validator?.invoke(entity) ?: fileHandler.validate(entity)
- }
-
- private fun handleError(error: Exception) {
- logger.error(error.message, error)
- synchronized(errorHandlers) {
- errorHandlers.forEach { it(error) }
- }
- }
-
- private suspend fun notifyChangeListeners(identifier: I, oldEntity: T?, newEntity: T?) {
- val listeners = synchronized(changeListeners) { changeListeners.toList() }
-
- listeners.forEach { listener ->
- try {
- withContext(coroutineContext) {
- if (oldEntity != newEntity) {
- listener(identifier, oldEntity, newEntity)
- }
- }
- } catch (e: Exception) {
- handleError(RepositoryException("Error in change listener", e))
- }
- }
- }
-
- private fun getFile(identifier: I): File =
- directory.resolve("$identifier${fileHandler.fileExtension}").toFile()
-
-
- private fun registerWatcher(): Job {
- val watchService = FileSystems.getDefault().newWatchService()
- directory.register(
- watchService,
- StandardWatchEventKinds.ENTRY_CREATE,
- StandardWatchEventKinds.ENTRY_MODIFY,
- StandardWatchEventKinds.ENTRY_DELETE
- )
-
- return scope.launch {
- watchService.use { service ->
- while (isActive) {
- val key = service.take()
-
- key.pollEvents().forEach { event ->
- handleWatchEvent(event)
- }
-
- if (!key.reset()) break
- }
- }
- }.also { watchJob = it }
- }
-
- private suspend fun handleWatchEvent(event: WatchEvent<*>) {
- val path = event.context() as? Path ?: return
- if (!path.toString().endsWith(fileHandler.fileExtension)) return
-
- val resolvedPath = directory.resolve(path)
- if (Files.isDirectory(resolvedPath)) return
-
- when (event.kind()) {
- StandardWatchEventKinds.ENTRY_CREATE,
- StandardWatchEventKinds.ENTRY_MODIFY -> {
- delay(100)
- loadFile(resolvedPath.toFile())
- }
-
- StandardWatchEventKinds.ENTRY_DELETE -> {
- val file = resolvedPath.toFile()
- val identifier = identifiers[file]
- val oldEntity = entities.remove(file)?.get()
-
- if (identifier != null && oldEntity != null) {
- notifyChangeListeners(identifier, oldEntity, null)
- }
- }
-
- else -> {}
- }
- }
-
- override fun close() {
- scope.cancel()
- watchJob?.cancel()
- }
-
- companion object {
- /**
- * Creates a new DirectoryRepository instance
- */
- inline fun create(
- directory: Path,
- fileHandler: FileHandler,
- noinline validator: ((T) -> Boolean)? = null,
- coroutineContext: CoroutineContext = Dispatchers.IO
- ): DirectoryRepository = DirectoryRepository(
- directory = directory,
- fileHandler = fileHandler,
- validator = validator,
- coroutineContext = coroutineContext
- )
- }
-}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/FileHandler.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/FileHandler.kt
deleted file mode 100644
index a144ba6..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/FileHandler.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package app.simplecloud.plugin.api.shared.config.repository.handler
-
-import java.io.File
-
-interface FileHandler {
-
- val fileExtension: String
-
- fun load(file: File): T?
-
- fun save(file: File, entity: T)
-
- fun validate(entity: T): Boolean = true
-
-}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/PNGFileHandler.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/PNGFileHandler.kt
deleted file mode 100644
index 2135c4e..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/PNGFileHandler.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package app.simplecloud.plugin.api.shared.config.repository.handler
-
-import java.awt.image.BufferedImage
-import java.io.File
-import javax.imageio.ImageIO
-
-class PNGFileHandler : FileHandler {
-
- override val fileExtension: String = ".png"
-
- override fun load(file: File): BufferedImage? =
- runCatching { ImageIO.read(file) }.getOrNull()
-
-
- override fun save(file: File, entity: BufferedImage) {
- ImageIO.write(entity, "png", file)
- }
-
- override fun validate(entity: BufferedImage): Boolean =
- entity.width > 0 && entity.height > 0
-
-}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/YamlFileHandler.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/YamlFileHandler.kt
deleted file mode 100644
index 4a7b4bc..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/repository/handler/YamlFileHandler.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package app.simplecloud.plugin.api.shared.config.repository.handler
-
-import app.simplecloud.plugin.api.shared.repository.GenericEnumSerializer
-import org.spongepowered.configurate.kotlin.objectMapperFactory
-import org.spongepowered.configurate.yaml.NodeStyle
-import org.spongepowered.configurate.yaml.YamlConfigurationLoader
-import java.io.File
-
-class YamlFileHandler(
- private val clazz: Class
-) : FileHandler {
-
- override val fileExtension: String = ".yml"
-
- private val loaders = mutableMapOf()
-
- private fun getOrCreateLoader(file: File): YamlConfigurationLoader {
- return loaders.getOrPut(file) {
- YamlConfigurationLoader.builder()
- .path(file.toPath())
- .nodeStyle(NodeStyle.BLOCK)
- .defaultOptions { options ->
- options.serializers { builder ->
- builder.registerAnnotatedObjects(objectMapperFactory())
- builder.register(Enum::class.java, GenericEnumSerializer)
- }
- }
- .build()
- }
- }
-
- override fun load(file: File): T? {
- return try {
- val loader = getOrCreateLoader(file)
- val node = loader.load()
- node.get(clazz)
- } catch (e: Exception) {
- println("Error loading file ${file.name}: ${e.message}")
- e.printStackTrace()
- null
- }
- }
-
- override fun save(file: File, entity: T) {
- val loader = getOrCreateLoader(file)
- val node = loader.createNode()
- node.set(clazz, entity)
- loader.save(node)
- }
-}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/GenericEnumSerializer.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/serializer/GenericEnumSerializer.kt
similarity index 81%
rename from plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/GenericEnumSerializer.kt
rename to plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/serializer/GenericEnumSerializer.kt
index 26b4da9..59deb61 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/GenericEnumSerializer.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/serializer/GenericEnumSerializer.kt
@@ -1,23 +1,26 @@
-package app.simplecloud.plugin.api.shared.repository
+package app.simplecloud.plugin.api.shared.config.serializer
import org.spongepowered.configurate.ConfigurationNode
import org.spongepowered.configurate.serialize.SerializationException
import org.spongepowered.configurate.serialize.TypeSerializer
import java.lang.reflect.Type
+/**
+ * @author Niklas Nieberler
+ */
+
object GenericEnumSerializer : TypeSerializer> {
@Suppress("UNCHECKED_CAST")
override fun deserialize(type: Type, node: ConfigurationNode): Enum<*> {
val value = node.string ?: throw SerializationException("No value present in node")
- if (type !is Class<*> || !type.isEnum) {
+ if (type !is Class<*> || !type.isEnum)
throw SerializationException("Type is not an enum class")
- }
return try {
java.lang.Enum.valueOf(type as Class>, value)
- } catch (e: IllegalArgumentException) {
+ } catch (_: IllegalArgumentException) {
throw SerializationException("Invalid enum constant")
}
}
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/exception/ConfigurationException.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/exception/ConfigurationException.kt
deleted file mode 100644
index b22ab88..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/exception/ConfigurationException.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package app.simplecloud.plugin.api.shared.exception
-
-class ConfigurationException(message: String, cause: Throwable? = null) :
- Exception(message, cause)
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/exception/RepositoryException.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/exception/RepositoryException.kt
deleted file mode 100644
index a698774..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/exception/RepositoryException.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package app.simplecloud.plugin.api.shared.exception
-
-class RepositoryException(message: String, cause: Throwable? = null) : Exception(message, cause)
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/LoadableRepository.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/LoadableRepository.kt
deleted file mode 100644
index 822a537..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/LoadableRepository.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package app.simplecloud.plugin.api.shared.repository
-
-interface LoadableRepository : Repository {
-
- fun load(): List
-
-}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/Repository.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/Repository.kt
deleted file mode 100644
index 7bd8177..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/Repository.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package app.simplecloud.plugin.api.shared.repository
-
-interface Repository {
-
- fun delete(element: E): Boolean
-
- fun save(element: E)
-
- fun find(identifier: I): E?
-
- fun getAll(): List
-
-}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/ResourcedYamlDirectoryRepository.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/ResourcedYamlDirectoryRepository.kt
deleted file mode 100644
index 8436502..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/ResourcedYamlDirectoryRepository.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-package app.simplecloud.plugin.api.shared.repository
-
-import java.io.File
-import java.io.FileOutputStream
-import java.net.URL
-import java.nio.file.Path
-import java.util.jar.JarFile
-import kotlin.io.path.exists
-import kotlin.io.path.pathString
-
-/**
- * An extension of [YamlDirectoryRepository] that adds resource loading capabilities
- *
- * Key differences from [YamlDirectoryRepository]
- * - Automatically load default yml configuration files from resources if the target directory doesn't exist
- * - Supports both file and jar protocols for loading resources
- * - Handles both standalone file system resources and jar-packed resources
- *
- * Use this implementation of [YamlDirectoryRepository] when you need to
- * - Ship default configurations with your plugin
- * - Ensure configuration files exist on first run
- * - Handle both development (file) and production (jar) environments
- */
-abstract class ResourcedYamlDirectoryRepository(
- private val directory: Path,
- private val clazz: Class,
-) : YamlDirectoryRepository(directory, clazz) {
-
- override fun load(): List {
- if (!directory.exists()) loadDefaults()
-
- return super.load()
- }
-
- private fun loadDefaults() {
- val targetDirectory = File(directory.toUri()).apply { mkdirs() }
-
- val last = directory.pathString.split('/').last()
-
- val resourceUrl = YamlDirectoryRepository::class.java.getResource("/$last") ?: run {
- println("$last folder not found in resources")
- return
- }
-
- when (resourceUrl.protocol) {
- "file" -> handleFileProtocol(resourceUrl, targetDirectory)
- "jar" -> handleJarProtocol(resourceUrl, targetDirectory)
- else -> println("Unsupported protocol: ${resourceUrl.protocol}")
- }
- }
-
- private fun handleFileProtocol(resourceUrl: URL, targetDirectory: File) {
- val resourceDir = File(resourceUrl.toURI())
- if (resourceDir.exists()) {
- resourceDir.copyRecursively(targetDirectory, overwrite = true)
- } else {
- println("Resource directory does not exist: ${resourceUrl.path}")
- }
- }
-
- private fun handleJarProtocol(resourceUrl: URL, targetDirectory: File) {
- val jarPath = resourceUrl.path.substringBefore("!").removePrefix("file:")
- try {
- JarFile(jarPath).use { jarFile ->
- val last = directory.pathString.split('/').last()
- jarFile.entries().asSequence()
- .filter { it.name.startsWith("$last/") && !it.isDirectory }
- .forEach { entry ->
- val targetFile = File(targetDirectory, entry.name.removePrefix("$last/"))
- targetFile.parentFile.mkdirs()
- try {
- jarFile.getInputStream(entry).use { inputStream ->
- FileOutputStream(targetFile).use { fos ->
- fos.write(0xEF)
- fos.write(0xBB)
- fos.write(0xBF)
-
- inputStream.copyTo(fos)
- }
- }
- } catch (e: Exception) {
- println("Error copying file ${entry.name}: ${e.message}")
- }
- }
- }
- } catch (e: Exception) {
- println("Error processing JAR file: ${e.message}")
- e.printStackTrace()
- }
- }
-}
\ No newline at end of file
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/YamlDirectoryRepository.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/YamlDirectoryRepository.kt
deleted file mode 100644
index 732bd7b..0000000
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/repository/YamlDirectoryRepository.kt
+++ /dev/null
@@ -1,137 +0,0 @@
-package app.simplecloud.plugin.api.shared.repository
-
-import kotlinx.coroutines.*
-import org.spongepowered.configurate.ConfigurationOptions
-import org.spongepowered.configurate.kotlin.objectMapperFactory
-import org.spongepowered.configurate.loader.ParsingException
-import org.spongepowered.configurate.serialize.TypeSerializerCollection
-import org.spongepowered.configurate.yaml.NodeStyle
-import org.spongepowered.configurate.yaml.YamlConfigurationLoader
-import java.io.File
-import java.nio.file.FileSystems
-import java.nio.file.Files
-import java.nio.file.Path
-import java.nio.file.StandardWatchEventKinds
-
-abstract class YamlDirectoryRepository(
- private val directory: Path,
- private val clazz: Class,
-) : LoadableRepository {
-
- private val watchService = FileSystems.getDefault().newWatchService()
- private val loaders = mutableMapOf()
- private val entities = mutableMapOf()
-
- abstract fun getFileName(identifier: I): String
-
- override fun delete(element: E): Boolean {
- val file = entities.keys.find { entities[it] == element } ?: return false
- return deleteFile(file)
- }
-
- override fun getAll(): List {
- return entities.values.toList()
- }
-
- override fun load(): List {
- if (!directory.toFile().exists()) {
- directory.toFile().mkdirs()
- }
-
- registerWatcher()
-
- return Files.walk(directory)
- .toList()
- .filter { !it.toFile().isDirectory && it.toString().endsWith(".yml") }
- .mapNotNull { load(it.toFile()) }
- }
-
- private fun load(file: File): E? {
- try {
- val loader = getOrCreateLoader(file)
- val node = loader.load(ConfigurationOptions.defaults())
- val entity = node.get(clazz) ?: return null
- entities[file] = entity
- return entity
- } catch (ex: ParsingException) {
- val existedBefore = entities.containsKey(file)
- if (existedBefore) {
- return null
- }
-
- return null
- }
- }
-
- private fun deleteFile(file: File): Boolean {
- val deletedSuccessfully = file.delete()
- val removedSuccessfully = entities.remove(file) != null
- return deletedSuccessfully && removedSuccessfully
- }
-
- protected fun save(fileName: String, entity: E) {
- val file = directory.resolve(fileName).toFile()
- val loader = getOrCreateLoader(file)
- val node = loader.createNode(ConfigurationOptions.defaults())
- node.set(clazz, entity)
- loader.save(node)
- entities[file] = entity
- }
-
- open fun watchUpdateEvent(file: File) {}
-
- protected open fun addSerializers(builder: TypeSerializerCollection.Builder) {}
-
- private fun getOrCreateLoader(file: File): YamlConfigurationLoader {
- return loaders.getOrPut(file) {
- YamlConfigurationLoader.builder()
- .path(file.toPath())
- .nodeStyle(NodeStyle.BLOCK)
- .defaultOptions { options ->
- options.serializers { builder ->
- builder.registerAnnotatedObjects(objectMapperFactory())
- builder.register(Enum::class.java, GenericEnumSerializer)
- addSerializers(builder)
- }
- }.build()
- }
- }
-
- private fun registerWatcher(): Job {
- directory.register(
- watchService,
- StandardWatchEventKinds.ENTRY_CREATE,
- StandardWatchEventKinds.ENTRY_DELETE,
- StandardWatchEventKinds.ENTRY_MODIFY
- )
-
- return CoroutineScope(Dispatchers.IO).launch {
- while (isActive) {
- val key = watchService.take()
-
- key.pollEvents().forEach { event ->
- val path = event.context() as? Path ?: return@forEach
- if (!path.toString().endsWith(".yml")) return@forEach
-
- val resolvedPath = directory.resolve(path)
- if (Files.isDirectory(resolvedPath)) return@forEach
-
- when (event.kind()) {
- StandardWatchEventKinds.ENTRY_CREATE,
- StandardWatchEventKinds.ENTRY_MODIFY -> {
- delay(100)
- load(resolvedPath.toFile())
- watchUpdateEvent(resolvedPath.toFile())
- }
-
- StandardWatchEventKinds.ENTRY_DELETE -> {
- deleteFile(resolvedPath.toFile())
- }
- }
- }
-
- key.reset()
- }
- }
- }
-}
\ No newline at end of file
From 636c5a4732a2863a243615b4ae2af1ba5c943b4e Mon Sep 17 00:00:00 2001
From: "niklas.nieberler"
Date: Mon, 6 Apr 2026 17:35:56 +0200
Subject: [PATCH 05/10] feat: add watchUpdateEvent in YamlDirectoryRepository
---
.../plugin/api/shared/config/YamlDirectoryRepository.kt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlDirectoryRepository.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlDirectoryRepository.kt
index 68e8cff..c3ca904 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlDirectoryRepository.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlDirectoryRepository.kt
@@ -80,6 +80,8 @@ abstract class YamlDirectoryRepository(
delete(file)
}
+ open fun watchUpdateEvent(file: File) {}
+
private fun delete(file: File) {
file.delete()
this.cachedEntities.remove(file)
@@ -89,6 +91,7 @@ abstract class YamlDirectoryRepository(
ConfigurateWatcherRegistry()
.withEvent(StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY) { load(it) }
.withEvent(StandardWatchEventKinds.ENTRY_DELETE) { delete(it) }
+ .withEvent(StandardWatchEventKinds.ENTRY_MODIFY) { watchUpdateEvent(it) }
.withWatcherRequirements { Files.isDirectory(it) }
.register(this.directory)
}
From b364849c1e27fc2349b9a45632e60574e4895e54 Mon Sep 17 00:00:00 2001
From: "niklas.nieberler"
Date: Mon, 6 Apr 2026 17:53:45 +0200
Subject: [PATCH 06/10] fix: enum serializer in YamlFileConfigurator
---
.../simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt | 1 +
1 file changed, 1 insertion(+)
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
index a8d565d..225a463 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
@@ -23,6 +23,7 @@ class YamlFileConfigurator(
options.serializers { builder ->
builder.registerAnnotatedObjects(objectMapperFactory())
builder.register(Enum::class.java, GenericEnumSerializer)
+ builder.register({ type -> type is Class<*> && type.isEnum }, GenericEnumSerializer)
}
}
From 76f81abc8db8e6ed09440bd07eeb4f1f6d06e239 Mon Sep 17 00:00:00 2001
From: "niklas.nieberler"
Date: Thu, 9 Apr 2026 22:30:37 +0200
Subject: [PATCH 07/10] feat: add support for more values in
AbstractPlaceholderProvide
---
.../provider/AbstractPlaceholderProvider.kt | 36 ++++++++++++++-----
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/AbstractPlaceholderProvider.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/AbstractPlaceholderProvider.kt
index d8b9427..4a971de 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/AbstractPlaceholderProvider.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/placeholder/provider/AbstractPlaceholderProvider.kt
@@ -30,21 +30,21 @@ abstract class AbstractPlaceholderProvider(
/**
* Gets the sum of all [TagResolver]
- * @param value for the placeholder
+ * @param values for the placeholder
* @param prefix of the placeholder key
*/
suspend fun getTagResolver(
- value: T,
+ values: List,
prefix: String? = null,
vararg argumentsResolver: ArgumentsResolver,
): TagResolver {
- val availableArgumentsResolver = listOf(
- *getArgumentsResolvers(this.cloudApi, value).toTypedArray(),
- *argumentsResolver
- )
- val singleTagResolver = this.executor.getTagResolver(this.cloudApi, value, prefix)
+ val availableArgumentsResolver = buildList {
+ addAll(values.flatMap { getArgumentsResolvers(cloudApi, it) })
+ addAll(argumentsResolver)
+ }
+ val singleTagResolver = values.map { this.executor.getTagResolver(this.cloudApi, it, prefix) }
return TagResolver.resolver(
- singleTagResolver,
+ *singleTagResolver.toTypedArray(),
*availableArgumentsResolver
.map { convertArgumentsResolverToTagResolver(it, prefix) }
.toTypedArray()
@@ -65,7 +65,25 @@ abstract class AbstractPlaceholderProvider(
): Component {
return text(
string,
- getTagResolver(value, prefix, *argumentsResolver),
+ getTagResolver(listOf(value), prefix, *argumentsResolver),
+ )
+ }
+
+ /**
+ * Serializes the string to a [Component]
+ * @param values for the placeholder
+ * @param string the message
+ * @param prefix of the placeholder key
+ */
+ suspend fun append(
+ values: List,
+ string: String,
+ prefix: String? = null,
+ vararg argumentsResolver: ArgumentsResolver,
+ ): Component {
+ return text(
+ string,
+ getTagResolver(values, prefix, *argumentsResolver),
)
}
From 70e1581abca08d53c3b18cfc65de2ba520a9635f Mon Sep 17 00:00:00 2001
From: Janis
Date: Sat, 11 Apr 2026 18:37:50 +0200
Subject: [PATCH 08/10] fix: use loader options in YamlFileConfigurator
---
.../plugin/api/shared/config/YamlFileConfigurator.kt | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
index 225a463..c4e609e 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
@@ -1,7 +1,6 @@
package app.simplecloud.plugin.api.shared.config
import app.simplecloud.plugin.api.shared.config.serializer.GenericEnumSerializer
-import org.spongepowered.configurate.ConfigurationOptions
import org.spongepowered.configurate.kotlin.objectMapperFactory
import org.spongepowered.configurate.yaml.NodeStyle
import org.spongepowered.configurate.yaml.YamlConfigurationLoader
@@ -29,14 +28,14 @@ class YamlFileConfigurator(
fun save(file: File, entity: E) {
val configurationLoader = getOrCreateConfigurationLoader(file)
- val node = configurationLoader.createNode(ConfigurationOptions.defaults())
+ val node = configurationLoader.createNode()
node.set(this.javaClass, entity)
configurationLoader.save(node)
}
fun load(file: File): E? {
val configurationLoader = getOrCreateConfigurationLoader(file)
- val node = configurationLoader.load(ConfigurationOptions.defaults())
+ val node = configurationLoader.load()
return node.get(this.javaClass)
}
@@ -48,4 +47,4 @@ class YamlFileConfigurator(
}
}
-}
\ No newline at end of file
+}
From 486e8e793cc7ed38787f28177b0caa7c92c797b5 Mon Sep 17 00:00:00 2001
From: xXJanisXx
Date: Sun, 12 Apr 2026 00:08:18 +0200
Subject: [PATCH 09/10] chore: bump dependencies and move velocity api in the
version catalog
---
.idea/.gitignore | 10 ++++++++++
gradle/libs.versions.toml | 15 +++++++++++----
gradle/wrapper/gradle-wrapper.properties | 3 +--
plugin-velocity/build.gradle.kts | 4 ++--
4 files changed, 24 insertions(+), 8 deletions(-)
create mode 100644 .idea/.gitignore
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..30cf57e
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index fc805a0..1f8286b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,9 +1,14 @@
[versions]
-kotlin = "2.1.0"
+kotlin = "2.2.20"
kotlin-coroutines = "1.10.2"
-simplecloud-api = "0.1.0-platform.22-dev.1774376168759-1e20f57"
-sonatype-central-portal-publisher = "1.2.3"
-adventure = "4.18.0"
+
+sonatype-central-portal-publisher = "1.2.4"
+
+simplecloud-api = "0.1.0-platform.23-dev.1775473227066-1c0c25a"
+
+velocity = "3.5.0-SNAPSHOT"
+adventure = "4.26.1"
+
configurate = "4.2.0"
slf4j = "2.0.17"
@@ -14,6 +19,8 @@ kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core",
simplecloud-api = { module = "app.simplecloud.api:api", version.ref = "simplecloud-api" }
+velocity-api = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" }
+
adventure = { module = "net.kyori:adventure-api", version.ref = "adventure" }
adventure-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ed33eb3..221c4f9 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Sun Dec 29 18:17:48 CET 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/plugin-velocity/build.gradle.kts b/plugin-velocity/build.gradle.kts
index 3daeb6c..7468d5f 100644
--- a/plugin-velocity/build.gradle.kts
+++ b/plugin-velocity/build.gradle.kts
@@ -4,6 +4,6 @@ plugins {
dependencies {
compileOnly(project(":plugin-shared"))
- compileOnly("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
- kapt("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
+ compileOnly(libs.velocity.api)
+ kapt(libs.velocity.api)
}
\ No newline at end of file
From f45d1bc48f8b4cac6ec2909cccc333340c411cef Mon Sep 17 00:00:00 2001
From: xXJanisXx
Date: Sun, 12 Apr 2026 00:19:33 +0200
Subject: [PATCH 10/10] feat: support comments in configs
---
.../api/shared/config/YamlFileConfigurator.kt | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
index c4e609e..7dddd82 100644
--- a/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
+++ b/plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config/YamlFileConfigurator.kt
@@ -1,6 +1,7 @@
package app.simplecloud.plugin.api.shared.config
import app.simplecloud.plugin.api.shared.config.serializer.GenericEnumSerializer
+import org.spongepowered.configurate.CommentedConfigurationNode
import org.spongepowered.configurate.kotlin.objectMapperFactory
import org.spongepowered.configurate.yaml.NodeStyle
import org.spongepowered.configurate.yaml.YamlConfigurationLoader
@@ -27,18 +28,21 @@ class YamlFileConfigurator(
}
fun save(file: File, entity: E) {
- val configurationLoader = getOrCreateConfigurationLoader(file)
- val node = configurationLoader.createNode()
+ val (node, loader) = buildNode(file)
node.set(this.javaClass, entity)
- configurationLoader.save(node)
+ loader.save(node)
}
fun load(file: File): E? {
- val configurationLoader = getOrCreateConfigurationLoader(file)
- val node = configurationLoader.load()
+ val (node, _) = buildNode(file)
return node.get(this.javaClass)
}
+ fun buildNode(file: File): Pair {
+ val loader = getOrCreateConfigurationLoader(file)
+ return Pair(loader.load(), loader)
+ }
+
fun getOrCreateConfigurationLoader(file: File): YamlConfigurationLoader {
return this.configurationLoaders.getOrPut(file) {
this.defaultConfigurationBuilder