diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index fe63bb6..03fcfb7 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index f6a4583..cdafb20 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -6,7 +6,7 @@
-
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index c92c679..b1addab 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,23 +1,25 @@
plugins {
- id 'org.jetbrains.kotlin.jvm' version '1.9.23'
- id 'kr.entree.spigradle' version '2.4.3'
- id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.23'
+ id 'org.jetbrains.kotlin.jvm' version '2.2.0'
+ id 'io.typst.spigradle' version '3.0.4'
+ id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.0'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'maven-publish'
id 'signing'
}
group = 'io.typst'
-version = '3.0.2'
+version = '3.1.0'
repositories {
mavenCentral()
+ spigotmc()
}
dependencies {
compileOnly spigot('1.16.5')
- implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3'
- testImplementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3'
+ implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0'
+ implementation 'com.charleskorn.kaml:kaml-jvm:0.96.0'
+ testImplementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0'
testImplementation 'org.jetbrains.kotlin:kotlin-test'
}
@@ -25,18 +27,11 @@ test {
useJUnitPlatform()
}
-compileKotlin {
- kotlinOptions.jvmTarget = '1.8'
-}
-
-compileTestKotlin {
- kotlinOptions.jvmTarget = '1.8'
+kotlin {
+ jvmToolchain(17)
}
spigot {
- debug {
- buildVersion '1.16.5'
- }
apiVersion '1.16'
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1121b15..cf54908 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/src/main/kotlin/io/typst/bukkit/kotlin/serialization/BukkitConfigSerializableSerializer.kt b/src/main/kotlin/io/typst/bukkit/kotlin/serialization/BukkitConfigSerializableSerializer.kt
new file mode 100644
index 0000000..3567cf1
--- /dev/null
+++ b/src/main/kotlin/io/typst/bukkit/kotlin/serialization/BukkitConfigSerializableSerializer.kt
@@ -0,0 +1,124 @@
+package io.typst.bukkit.kotlin.serialization
+
+import com.charleskorn.kaml.*
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.descriptors.buildClassSerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.json.*
+import org.bukkit.configuration.serialization.ConfigurationSerializable
+import org.bukkit.configuration.serialization.ConfigurationSerialization
+
+private val ROOT = YamlPath.root
+
+object BukkitConfigSerializableSerializer : KSerializer {
+ override val descriptor: SerialDescriptor =
+ buildClassSerialDescriptor("BukkitConfigurationSerializable")
+
+ fun serialize(value: ConfigurationSerializable): Map {
+ val map: MutableMap = value.serialize()
+ val alias = ConfigurationSerialization.getAlias(value.javaClass)
+ return map + (ConfigurationSerialization.SERIALIZED_TYPE_KEY to alias)
+ }
+
+ override fun serialize(encoder: Encoder, value: ConfigurationSerializable) {
+ val bukkitMap = serialize(value)
+ when (encoder) {
+ is JsonEncoder -> encoder.encodeJsonElement(mapToJson(bukkitMap))
+ else -> {
+ val node: YamlNode = mapToYaml(bukkitMap)
+ encoder.encodeSerializableValue(YamlNode.serializer(), node)
+ }
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): ConfigurationSerializable {
+ val map: Map = when (decoder) {
+ is JsonDecoder -> {
+ val elem = decoder.decodeJsonElement()
+ (jsonToAny(elem) as Map)
+ }
+
+ else -> {
+ val node: YamlNode = decoder.decodeSerializableValue(YamlNode.serializer())
+ (yamlToAny(node) as Map)
+ }
+ }
+ return ConfigurationSerialization.deserializeObject(map)
+ ?: error("Failed to deserialize ConfigurationSerializable (missing '==' alias?)")
+ }
+
+ // -------------------- JSON <-> Any --------------------
+ private fun mapToJson(map: Map): JsonObject = buildJsonObject {
+ for ((k, v) in map) put(k, anyToJson(v))
+ }
+
+ private fun listToJson(list: List<*>): JsonArray = JsonArray(list.map { anyToJson(it) })
+
+ private fun anyToJson(v: Any?): JsonElement = when (v) {
+ null -> JsonNull
+ is JsonElement -> v
+ is String -> JsonPrimitive(v)
+ is Boolean -> JsonPrimitive(v)
+ is Number -> JsonPrimitive(v)
+ is Map<*, *> -> mapToJson(v as Map)
+ is List<*> -> listToJson(v)
+ is ConfigurationSerializable -> mapToJson(serialize(v))
+ else -> JsonPrimitive(v.toString())
+ }
+
+ private fun jsonToAny(elem: JsonElement): Any? = when (elem) {
+ is JsonNull -> null
+ is JsonPrimitive -> when {
+ elem.isString -> elem.content
+ elem.booleanOrNull != null -> elem.boolean
+ elem.longOrNull != null -> if (elem.content.contains('.')) elem.double else elem.long
+ else -> elem.content
+ }
+
+ is JsonObject -> elem.mapValues { jsonToAny(it.value) }
+ is JsonArray -> elem.map { jsonToAny(it) }
+ }
+
+ // -------------------- YAML(KAML 0.96) <-> Any --------------------
+ private fun mapToYaml(map: Map): YamlMap =
+ YamlMap(map.map { (k, v) -> YamlScalar(k, ROOT) to anyToYaml(v) }.toMap(), ROOT)
+
+ private fun listToYaml(list: List<*>): YamlList =
+ YamlList(list.map { anyToYaml(it) }, ROOT)
+
+ private fun anyToYaml(v: Any?): YamlNode = when (v) {
+ null -> YamlNull(ROOT)
+ is YamlNode -> v
+ is String -> YamlScalar(v, ROOT)
+ is Boolean -> YamlScalar(v.toString(), ROOT) // 스칼라는 문자열 컨텐트
+ is Number -> YamlScalar(v.toString(), ROOT)
+ is Map<*, *> -> mapToYaml(v as Map)
+ is List<*> -> listToYaml(v)
+ is ConfigurationSerializable -> mapToYaml(serialize(v))
+ else -> YamlScalar(v.toString(), ROOT)
+ }
+
+ private fun yamlToAny(node: YamlNode): Any? = when (node) {
+ is YamlNull -> null
+ is YamlScalar -> parseYamlScalar(node.content)
+ is YamlList -> node.items.map { yamlToAny(it) }
+ is YamlMap -> node.entries
+ .mapKeys { (k, _) -> (yamlToAny(k) ?: "").toString() }
+ .mapValues { (_, v) -> yamlToAny(v) }
+
+ is YamlTaggedNode -> {
+ yamlToAny(node.innerNode)
+ }
+ }
+
+ private fun parseYamlScalar(s: String): Any {
+ val lower = s.lowercase()
+ if (lower == "true" || lower == "false") return lower == "true"
+ val num = s.replace("_", "")
+ num.toLongOrNull()?.let { return it }
+ num.toDoubleOrNull()?.let { return it }
+ return s
+ }
+}
diff --git a/src/main/kotlin/io/typst/bukkit/kotlin/serialization/ConfigAnySerializer.kt b/src/main/kotlin/io/typst/bukkit/kotlin/serialization/ConfigAnySerializer.kt
deleted file mode 100644
index 695aba8..0000000
--- a/src/main/kotlin/io/typst/bukkit/kotlin/serialization/ConfigAnySerializer.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package io.typst.bukkit.kotlin.serialization
-
-import kotlinx.serialization.ExperimentalSerializationApi
-import kotlinx.serialization.InternalSerializationApi
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.SerializationException
-import kotlinx.serialization.builtins.ListSerializer
-import kotlinx.serialization.builtins.MapSerializer
-import kotlinx.serialization.builtins.serializer
-import kotlinx.serialization.descriptors.PolymorphicKind
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.descriptors.buildSerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-import kotlinx.serialization.json.*
-import org.bukkit.configuration.serialization.ConfigurationSerializable
-import org.bukkit.configuration.serialization.ConfigurationSerialization
-
-@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
-internal object ConfigAnySerializer : KSerializer {
- override val descriptor: SerialDescriptor =
- buildSerialDescriptor("BukkitConfigAny", PolymorphicKind.OPEN)
-
- override fun deserialize(decoder: Decoder): Any? {
- // TODO: use decodeStructure instead of decodeJsonElement
- val jsonDecoder = decoder as? JsonDecoder ?: return null
- val element = jsonDecoder.decodeJsonElement()
- try {
- return exactValue(element)
- } catch (ex: SerializationException) {
- throw SerializationException("Error while deserializing: $element", ex)
- }
- }
-
- private fun exactValue(x: JsonElement): Any? {
- return when (x) {
- is JsonPrimitive ->
- if (x.isString) {
- x.content
- } else x.intOrNull ?: x.longOrNull ?: x.doubleOrNull ?: x.booleanOrNull
- ?: throw SerializationException("Illegal input: $x")
-
- JsonNull -> null
- is JsonArray -> x.map(::exactValue)
- is JsonObject -> {
- val map = x.map { (k, v) ->
- k to exactValue(v)
- }.toMap()
- if (map.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) {
- ConfigurationSerialization.deserializeObject(map)
- } else map
- }
- }
- }
-
- override fun serialize(encoder: Encoder, value: Any?) {
- if (value == null) {
- encoder.encodeNull()
- return
- }
- val roundedValue = roundValue(value)
- val serializer = findSerializer(roundedValue)
- encoder.encodeSerializableValue(serializer, roundedValue)
- }
-
- private fun roundValue(x: Any): Any =
- if (x is Number) {
- if (x is Double || x is Float) {
- x.toDouble()
- } else x.toLong()
- } else if (x is StringBuilder || x is StringBuffer) {
- x.toString()
- } else if (x is Collection<*>) {
- x.toList()
- } else if (x is Map<*, *>) {
- LinkedHashMap(x)
- } else x
-
- @Suppress("UNCHECKED_CAST")
- private fun findSerializer(x: Any): KSerializer =
- when (x) {
- is Boolean -> Boolean.serializer()
- is Double -> Double.serializer()
- is Long -> Long.serializer()
- is String -> String.serializer()
- is Map<*, *> ->
- if (x.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) {
- ConfigSerializableSerializer
- } else MapSerializer(String.serializer(), ConfigAnySerializer)
-
- is Collection<*> -> ListSerializer(ConfigAnySerializer)
- is ConfigurationSerializable -> ConfigSerializableSerializer
- else -> throw IllegalArgumentException("Unknown type ${x.javaClass.name} $x")
- } as KSerializer
-}
diff --git a/src/main/kotlin/io/typst/bukkit/kotlin/serialization/ConfigSerializableSerializer.kt b/src/main/kotlin/io/typst/bukkit/kotlin/serialization/ConfigSerializableSerializer.kt
deleted file mode 100644
index 0d050c0..0000000
--- a/src/main/kotlin/io/typst/bukkit/kotlin/serialization/ConfigSerializableSerializer.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package io.typst.bukkit.kotlin.serialization
-
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.builtins.MapSerializer
-import kotlinx.serialization.builtins.serializer
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-import org.bukkit.configuration.serialization.ConfigurationSerializable
-import org.bukkit.configuration.serialization.ConfigurationSerialization
-
-object ConfigSerializableSerializer : KSerializer {
- private val mapSerializer: KSerializer