Skip to content

Commit e539df7

Browse files
committed
- Kotlin init.
- Something serialization refactor.
1 parent 3acb30a commit e539df7

File tree

11 files changed

+389
-0
lines changed

11 files changed

+389
-0
lines changed

build.gradle.kts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import org.jetbrains.gradle.ext.RunConfigurationContainer
33
import java.util.*
44

55
plugins {
6+
kotlin("jvm") version "2.2.0"
7+
kotlin("plugin.serialization") version "2.2.0"
68
id("java-library")
79
id("maven-publish")
810
id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.7"
@@ -26,6 +28,10 @@ java {
2628
withJavadocJar()
2729
}
2830

31+
kotlin {
32+
jvmToolchain(8)
33+
}
34+
2935
// Most RFG configuration lives here, see the JavaDoc for com.gtnewhorizons.retrofuturagradle.MinecraftExtension
3036
minecraft {
3137
mcVersion.set("1.12.2")
@@ -181,6 +187,13 @@ dependencies {
181187
isTransitive = false
182188
}
183189

190+
// Kotlin Support
191+
runtimeOnly("io.github.chaosunity.forgelin:Forgelin-Continuous:2.2.0.0") {
192+
isTransitive = false
193+
}
194+
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.2.0")
195+
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
196+
184197
implementation("CraftTweaker2:CraftTweaker2-MC1120-Main:1.12-4.+")
185198
implementation(rfg.deobf("curse.maven:had-enough-items-557549:4810661"))
186199
implementation(rfg.deobf("curse.maven:zenutil-401178:5056679"))
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package github.kasuminova.mmce.common.serialize
2+
3+
import github.kasuminova.mmce.common.serialize.json.JsonRawDataStructure
4+
import github.kasuminova.mmce.common.serialize.raw.RawDataStructure
5+
import kotlinx.serialization.json.JsonObject
6+
7+
@Suppress("UNCHECKED_CAST")
8+
abstract class DataStructure {
9+
10+
val columns: MutableList<DataValue<*>> = mutableListOf()
11+
12+
fun loadFrom(json: JsonObject) = loadFrom(JsonRawDataStructure(json))
13+
14+
fun loadFrom(raw: RawDataStructure) {
15+
for (column in this.columns) {
16+
try {
17+
(column as DataValueImpl).solve(raw)
18+
} catch (e: Throwable) {
19+
throw IllegalArgumentException("Failed to parse column '${column.name}'", e)
20+
}
21+
}
22+
}
23+
24+
protected fun raw(name: String): DataValue<Any?> = DataValueImpl<Any?>(
25+
name = name,
26+
nullable = true,
27+
deserializer = AnyToAnyDeserializer as DataValueDeserializer<Any, Any?>,
28+
defProvider = { null }
29+
).also { columns += it }
30+
31+
protected fun byte(name: String): DataValue<Byte> = raw(name)
32+
.notnull()
33+
.map { value -> (value as? Number)?.toByte() ?: value.toString().toByte() }
34+
.def { 0 }
35+
36+
protected fun short(name: String): DataValue<Short> = raw(name)
37+
.notnull()
38+
.map { value -> (value as? Number)?.toShort() ?: value.toString().toShort() }
39+
.def { 0 }
40+
41+
protected fun integer(name: String): DataValue<Int> = raw(name)
42+
.notnull()
43+
.map { value -> (value as? Number)?.toInt() ?: value.toString().toInt() }
44+
.def { 0 }
45+
46+
protected fun long(name: String): DataValue<Long> = raw(name)
47+
.notnull()
48+
.map { value -> (value as? Number)?.toLong() ?: value.toString().toLong() }
49+
.def { 0L }
50+
51+
protected fun float(name: String): DataValue<Float> = raw(name)
52+
.notnull()
53+
.map { value -> (value as? Number)?.toFloat() ?: value.toString().toFloat() }
54+
.def { 0f }
55+
56+
protected fun double(name: String): DataValue<Double> = raw(name)
57+
.notnull()
58+
.map { value -> (value as? Number)?.toDouble() ?: value.toString().toDouble() }
59+
.def { 0.0 }
60+
61+
protected fun boolean(name: String): DataValue<Boolean> = raw(name)
62+
.notnull()
63+
.map { value -> (value as? Boolean) ?: value.toString().toBoolean() }
64+
.def { false }
65+
66+
protected fun string(name: String): DataValue<String> = raw(name)
67+
.notnull()
68+
.map { value -> value.toString() }
69+
.def { "" }
70+
71+
protected fun <T> list(name: String, mapping: (Any) -> T) = raw(name)
72+
.notnull()
73+
.map {
74+
if (it is List<*>) {
75+
it.map { e -> mapping(e!!) }
76+
} else {
77+
emptyList()
78+
}
79+
}
80+
81+
protected fun subData(name: String): DataValue<RawDataStructure?> = raw(name)
82+
.nullable()
83+
.map { value -> value as? RawDataStructure }
84+
85+
protected fun subStructure(name: String, structure: DataStructure) = raw(name)
86+
.nullable()
87+
.map { value -> value as? RawDataStructure }
88+
.map { raw ->
89+
structure.also { structure ->
90+
if (raw != null) {
91+
structure.loadFrom(raw)
92+
}
93+
}
94+
}
95+
96+
protected fun multipleStructure(name: String, provider: () -> DataStructure) = raw(name)
97+
.nullable()
98+
.map { value -> value as? RawDataStructure }
99+
.map { raw ->
100+
raw?.getStructure(name)?.let { structure ->
101+
structure.keySet().associateWith { key -> provider().also { data -> data.loadFrom(structure.getStructure(key)!!) } }
102+
}
103+
}
104+
105+
protected fun <E> enum(name: String, values: Array<E>): DataValue<E> where E : Enum<E> = raw(name)
106+
.notnull()
107+
.map { value ->
108+
val str = value.toString()
109+
values.firstOrNull { it.name == str }
110+
?: throw IllegalArgumentException("Value '$str' is not valid for enum '${values.firstOrNull()?.javaClass?.name}'")
111+
}
112+
113+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package github.kasuminova.mmce.common.serialize
2+
3+
import kotlin.reflect.KProperty
4+
5+
interface DataValue<V> {
6+
7+
val name: String
8+
9+
val nullable: Boolean
10+
11+
val value: V
12+
13+
val deserializer: DataValueDeserializer<Any, V>
14+
15+
fun nullable(): DataValue<V?>
16+
17+
fun notnull(): DataValue<V & Any>
18+
19+
fun def(provider: () -> V): DataValue<V>
20+
21+
fun <R> map(transform: (value: V, name: String) -> R): DataValue<R>
22+
23+
fun <R> map(transform: (value: V) -> R): DataValue<R> = map { v, _ -> transform(v) }
24+
25+
}
26+
27+
interface DataValueDeserializer<I, O> {
28+
29+
fun deserialize(input: I, name: String): O
30+
31+
}
32+
33+
@Suppress("NOTHING_TO_INLINE")
34+
inline operator fun <V> DataValue<V>.getValue(thisRef: Any?, property: KProperty<*>): V = value
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package github.kasuminova.mmce.common.serialize
2+
3+
@Suppress("UNCHECKED_CAST")
4+
class DataValueImpl<V>(
5+
override val name: String,
6+
override var nullable: Boolean = false,
7+
override var deserializer: DataValueDeserializer<Any, V>,
8+
var defProvider: () -> V
9+
) : DataValue<V> {
10+
11+
private var solved: Boolean = false
12+
13+
private var _cached: V? = null
14+
15+
override val value: V
16+
get() {
17+
if (!solved) {
18+
_cached = defProvider()
19+
require(!nullable && _cached == null) { "Value '$name' is not nullable, but got null" }
20+
solved = true
21+
}
22+
return _cached as V
23+
}
24+
25+
override fun nullable(): DataValue<V?> {
26+
nullable = true
27+
return this as DataValue<V?>
28+
}
29+
30+
override fun notnull(): DataValue<V & Any> {
31+
nullable = false
32+
return this as DataValue<V & Any>
33+
}
34+
35+
override fun def(provider: () -> V): DataValue<V> {
36+
defProvider = provider
37+
return this
38+
}
39+
40+
override fun <R> map(transform: (value: V, name: String) -> R): DataValue<R> {
41+
val prevDeserializer = deserializer
42+
val prevDefProvider = defProvider
43+
val ret = this as DataValueImpl<R>
44+
ret.deserializer = MappingDeserializer(prevDeserializer, transform)
45+
ret.defProvider = { transform(prevDefProvider(), name) }
46+
if (solved) {
47+
ret._cached = transform(_cached as V, name)
48+
}
49+
return ret
50+
}
51+
52+
fun solve(input: Any) {
53+
_cached = deserializer.deserialize(input, name)
54+
require(!nullable && _cached == null) { "Value '$name' is not nullable, but got null" }
55+
solved = true
56+
}
57+
58+
}
59+
60+
internal class MappingDeserializer<I, O, R>(val prev: DataValueDeserializer<I, O>, val transform: (O, String) -> R) : DataValueDeserializer<I, R> {
61+
62+
override fun deserialize(input: I, name: String): R = transform(prev.deserialize(input, name), name)
63+
64+
}
65+
66+
internal object AnyToAnyDeserializer : DataValueDeserializer<Any, Any> {
67+
68+
override fun deserialize(input: Any, name: String): Any = input
69+
70+
}
71+
72+
internal object NothingDeserializer : DataValueDeserializer<Any, Nothing?> {
73+
74+
override fun deserialize(input: Any, name: String): Nothing? = null
75+
76+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package github.kasuminova.mmce.common.serialize.crafting.requirement
2+
3+
import github.kasuminova.mmce.common.serialize.DataStructure
4+
import github.kasuminova.mmce.common.serialize.getValue
5+
6+
class EnergyRequirementData : DataStructure() {
7+
8+
val energyPerTick by long("energyPerTick")
9+
10+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package github.kasuminova.mmce.common.serialize.crafting.requirement
2+
3+
import github.kasuminova.mmce.common.serialize.DataStructure
4+
import github.kasuminova.mmce.common.serialize.getValue
5+
import hellfirepvp.modularmachinery.common.util.nbt.NBTJsonDeserializer
6+
import net.minecraft.item.ItemStack
7+
import net.minecraft.util.ResourceLocation
8+
import net.minecraftforge.fml.common.registry.ForgeRegistries
9+
10+
class ItemRequirementData : DataStructure() {
11+
12+
val item by string("item")
13+
.map { str ->
14+
val split = str.split(":")
15+
when (split.size) {
16+
1 -> Either(oreDict = split[0])
17+
2 -> {
18+
if (split[0] == "ore") {
19+
Either(oreDict = split[1])
20+
} else {
21+
val resLoc = ResourceLocation(split[0], split[1])
22+
val item = ForgeRegistries.ITEMS.getValue(resLoc) ?: error("Item $resLoc not found")
23+
val meta = split[1].split("@").getOrNull(1)?.toIntOrNull()
24+
if (meta != null) {
25+
Either(ItemStack(item, 1, meta))
26+
} else {
27+
Either(ItemStack(item))
28+
}
29+
}
30+
}
31+
32+
else -> error("Invalid item string: $str")
33+
}
34+
}
35+
36+
val amount by integer("amount")
37+
.def { 1 }
38+
39+
val chance by float("chance")
40+
.def { 1.0f }
41+
42+
val fuelTime by integer("time")
43+
.def { -1 }
44+
45+
val nbt by string("nbt")
46+
.map { nbtStr -> if (nbtStr.isBlank()) null else NBTJsonDeserializer.deserialize(nbtStr) }
47+
48+
val nbtDisplay by string("nbt-display")
49+
.map { nbtStr -> if (nbtStr.isBlank()) null else NBTJsonDeserializer.deserialize(nbtStr) }
50+
51+
data class Either(val stack: ItemStack? = null, val oreDict: String? = null, val fuelTime: Int? = null)
52+
53+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package github.kasuminova.mmce.common.serialize.json
2+
3+
import github.kasuminova.mmce.common.serialize.raw.RawData
4+
import kotlinx.serialization.json.JsonArray
5+
import kotlinx.serialization.json.JsonElement
6+
import kotlinx.serialization.json.JsonObject
7+
import kotlinx.serialization.json.JsonPrimitive
8+
9+
open class JsonRawData(val element: JsonElement) : RawData {
10+
11+
override fun get(): Any = when (element) {
12+
is JsonPrimitive -> element.content
13+
is JsonArray -> element.map { JsonRawData(it).get() }
14+
is JsonObject -> element.mapValues { JsonRawData(it.value).get() }
15+
}
16+
17+
override fun toString(): String = element.toString()
18+
19+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package github.kasuminova.mmce.common.serialize.json
2+
3+
import github.kasuminova.mmce.common.serialize.raw.RawData
4+
import github.kasuminova.mmce.common.serialize.raw.RawDataStructure
5+
import kotlinx.serialization.json.JsonArray
6+
import kotlinx.serialization.json.JsonObject
7+
import kotlinx.serialization.json.JsonPrimitive
8+
import kotlinx.serialization.json.boolean
9+
import kotlinx.serialization.json.double
10+
import kotlinx.serialization.json.float
11+
import kotlinx.serialization.json.int
12+
import kotlinx.serialization.json.long
13+
14+
class JsonRawDataStructure(val element: JsonObject) : RawDataStructure {
15+
16+
override fun get(): Any = element.toString()
17+
18+
override fun get(name: String): RawData? = element[name]?.let { JsonRawData(it) }
19+
20+
override fun keySet(): Set<String> = element.keys
21+
22+
override fun getString(name: String): String? = element[name]?.toString()
23+
24+
override fun getBoolean(name: String): Boolean? = element[name]?.let { if (it is JsonPrimitive) it.boolean else null }
25+
26+
override fun getInt(name: String): Int? = element[name]?.let { if (it is JsonPrimitive) it.int else null }
27+
28+
override fun getLong(name: String): Long? = element[name]?.let { if (it is JsonPrimitive) it.long else null }
29+
30+
override fun getFloat(name: String): Float? = element[name]?.let { if (it is JsonPrimitive) it.float else null }
31+
32+
override fun getDouble(name: String): Double? = element[name]?.let { if (it is JsonPrimitive) it.double else null }
33+
34+
override fun getStructure(name: String): RawDataStructure? = element[name]?.let { if (it is JsonObject) JsonRawDataStructure(it) else null }
35+
36+
override fun getList(name: String): List<RawData>? = element[name]?.let { if (it is JsonArray) it.map { e -> JsonRawData(e) } else null }
37+
38+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package github.kasuminova.mmce.common.serialize.raw
2+
3+
interface RawData {
4+
5+
fun get(): Any
6+
7+
}

0 commit comments

Comments
 (0)