KOptional type adapter, and movement controller defs structures
This commit is contained in:
parent
c0ecbe9a8b
commit
86782e259e
src/main/kotlin/ru/dbotthepony/kstarbound
@ -6,6 +6,7 @@ import com.google.gson.GsonBuilder
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.TypeAdapterFactory
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import java.util.Arrays
|
||||
import java.util.stream.Stream
|
||||
import kotlin.reflect.KProperty
|
||||
@ -43,3 +44,5 @@ operator fun <K, V> ImmutableMap.Builder<K, V>.set(key: K, value: V): ImmutableM
|
||||
|
||||
fun String.sintern(): String = Starbound.strings.intern(this)
|
||||
|
||||
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
|
||||
|
||||
|
@ -55,6 +55,7 @@ import ru.dbotthepony.kstarbound.io.json.EitherTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.FastutilTypeAdapterFactory
|
||||
import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.InternedStringAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.KOptionalTypeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.LongRangeAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.NothingAdapter
|
||||
import ru.dbotthepony.kstarbound.io.json.OneOfTypeAdapter
|
||||
@ -217,6 +218,8 @@ object Starbound : ISBFileLocator {
|
||||
registerTypeAdapterFactory(EitherTypeAdapter)
|
||||
// OneOf<>
|
||||
registerTypeAdapterFactory(OneOfTypeAdapter)
|
||||
// KOptional<>
|
||||
registerTypeAdapterFactory(KOptionalTypeAdapter)
|
||||
|
||||
// Pair<>
|
||||
registerTypeAdapterFactory(PairAdapterFactory)
|
||||
@ -347,6 +350,9 @@ object Starbound : ISBFileLocator {
|
||||
@Volatile
|
||||
var terminateLoading = false
|
||||
|
||||
var defaultMovementParameters = MovementParameters()
|
||||
private set
|
||||
|
||||
fun loadJsonAsset(path: String): JsonElement? {
|
||||
val filename: String
|
||||
val jsonPath: String?
|
||||
@ -1031,6 +1037,8 @@ object Starbound : ISBFileLocator {
|
||||
|
||||
tasks.forEach { it.join() }
|
||||
|
||||
defaultMovementParameters = gson.fromJson(jsonReader("/default_actor_movement.config"))!!
|
||||
|
||||
initializing = false
|
||||
initialized = true
|
||||
log.line("Finished loading in ${System.currentTimeMillis() - time}ms")
|
||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.api
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.gson.stream.JsonReader
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.io.StarboundPak
|
||||
import ru.dbotthepony.kstarbound.stream
|
||||
@ -37,6 +38,12 @@ fun interface ISBFileLocator {
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
*/
|
||||
fun reader(path: String) = locate(path).reader()
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if file is a directory
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
*/
|
||||
fun jsonReader(path: String) = locate(path).jsonReader()
|
||||
}
|
||||
|
||||
interface IStarboundFile : ISBFileLocator {
|
||||
@ -145,6 +152,12 @@ interface IStarboundFile : ISBFileLocator {
|
||||
*/
|
||||
fun reader(): Reader = InputStreamReader(BufferedInputStream(open()))
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if file is a directory
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
*/
|
||||
fun jsonReader(): JsonReader = JsonReader(reader()).also { it.isLenient = true }
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if file is a directory
|
||||
* @throws FileNotFoundException if file does not exist
|
||||
|
@ -1,10 +1,31 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.util.KOptional
|
||||
|
||||
@JsonFactory
|
||||
data class JumpProfile(
|
||||
val jumpSpeed: Double = 0.0,
|
||||
val jumpInitialPercentage: Double = 0.0,
|
||||
val jumpHoldTime: Double = 0.0,
|
||||
)
|
||||
val jumpSpeed: KOptional<Double> = KOptional.empty(),
|
||||
val jumpControlForce: KOptional<Double> = KOptional.empty(),
|
||||
val jumpInitialPercentage: KOptional<Double> = KOptional.empty(),
|
||||
val jumpHoldTime: KOptional<Double> = KOptional.empty(),
|
||||
val jumpTotalHoldTime: KOptional<Double> = KOptional.empty(),
|
||||
val multiJump: KOptional<Boolean> = KOptional.empty(),
|
||||
val reJumpDelay: KOptional<Double> = KOptional.empty(),
|
||||
val autoJump: KOptional<Boolean> = KOptional.empty(),
|
||||
val collisionCancelled: KOptional<Boolean> = KOptional.empty(),
|
||||
) {
|
||||
fun merge(other: JumpProfile): JumpProfile {
|
||||
return JumpProfile(
|
||||
jumpSpeed = jumpSpeed.or(other.jumpSpeed),
|
||||
jumpControlForce = jumpControlForce.or(other.jumpControlForce),
|
||||
jumpInitialPercentage = jumpInitialPercentage.or(other.jumpInitialPercentage),
|
||||
jumpHoldTime = jumpHoldTime.or(other.jumpHoldTime),
|
||||
jumpTotalHoldTime = jumpTotalHoldTime.or(other.jumpTotalHoldTime),
|
||||
multiJump = multiJump.or(other.multiJump),
|
||||
reJumpDelay = reJumpDelay.or(other.reJumpDelay),
|
||||
autoJump = autoJump.or(other.autoJump),
|
||||
collisionCancelled = collisionCancelled.or(other.collisionCancelled),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
|
||||
|
||||
@JsonFactory
|
||||
class MovementModifiers(
|
||||
val groundMovementModifier: Double = 1.0,
|
||||
val liquidMovementModifier: Double = 1.0,
|
||||
val speedModifier: Double = 1.0,
|
||||
val airJumpModifier: Double = 1.0,
|
||||
val liquidJumpModifier: Double = 1.0,
|
||||
val runningSuppressed: Boolean = false,
|
||||
val jumpingSuppressed: Boolean = false,
|
||||
val facingSuppressed: Boolean = false,
|
||||
val movementSuppressed: Boolean = false,
|
||||
) {
|
||||
fun combine(other: MovementModifiers): MovementModifiers {
|
||||
return MovementModifiers(
|
||||
groundMovementModifier = groundMovementModifier * other.groundMovementModifier,
|
||||
liquidMovementModifier = liquidMovementModifier * other.liquidMovementModifier,
|
||||
speedModifier = speedModifier * other.speedModifier,
|
||||
airJumpModifier = airJumpModifier * other.airJumpModifier,
|
||||
liquidJumpModifier = liquidJumpModifier * other.liquidJumpModifier,
|
||||
runningSuppressed = runningSuppressed || other.runningSuppressed,
|
||||
jumpingSuppressed = jumpingSuppressed || other.jumpingSuppressed,
|
||||
facingSuppressed = facingSuppressed || other.facingSuppressed,
|
||||
movementSuppressed = movementSuppressed || other.movementSuppressed,
|
||||
)
|
||||
}
|
||||
}
|
@ -1,22 +1,106 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.JsonAlias
|
||||
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.util.KOptional
|
||||
import ru.dbotthepony.kvector.vector.Vector2d
|
||||
|
||||
@JsonFactory
|
||||
data class MovementParameters(
|
||||
val flySpeed: Double? = null,
|
||||
val airFriction: Double? = null,
|
||||
val airJumpProfile: JumpProfile? = null,
|
||||
val airForce: Double? = null,
|
||||
val mass: KOptional<Double> = KOptional.empty(),
|
||||
val gravityMultiplier: KOptional<Double> = KOptional.empty(),
|
||||
val liquidBuoyancy: KOptional<Double> = KOptional.empty(),
|
||||
val airBuoyancy: KOptional<Double> = KOptional.empty(),
|
||||
val bounceFactor: KOptional<Double> = KOptional.empty(),
|
||||
val stopOnFirstBounce: KOptional<Boolean> = KOptional.empty(),
|
||||
val enableSurfaceSlopeCorrection: KOptional<Boolean> = KOptional.empty(),
|
||||
val slopeSlidingFactor: KOptional<Double> = KOptional.empty(),
|
||||
val maxMovementPerStep: KOptional<Double> = KOptional.empty(),
|
||||
val maximumCorrection: KOptional<Double> = KOptional.empty(),
|
||||
val speedLimit: KOptional<Double> = KOptional.empty(),
|
||||
|
||||
val runSpeed: Double? = null,
|
||||
val walkSpeed: Double? = null,
|
||||
val mass: Double? = null,
|
||||
@JsonAlias("collisionPoly")
|
||||
val standingPoly: KOptional<ImmutableList<Vector2d>> = KOptional.empty(),
|
||||
@JsonAlias("collisionPoly")
|
||||
val crouchingPoly: KOptional<ImmutableList<Vector2d>> = KOptional.empty(),
|
||||
|
||||
// TODO: А оно вообще используется? Как по мне движок старбаунда генерирует коллизию из пикселей текстуры
|
||||
val collisionPoly: ImmutableList<Vector2d>? = null,
|
||||
val crouchingPoly: ImmutableList<Vector2d>? = null,
|
||||
val standingPoly: ImmutableList<Vector2d>? = null,
|
||||
)
|
||||
val stickyCollision: KOptional<Boolean> = KOptional.empty(),
|
||||
val stickyForce: KOptional<Double> = KOptional.empty(),
|
||||
val walkSpeed: KOptional<Double> = KOptional.empty(),
|
||||
val runSpeed: KOptional<Double> = KOptional.empty(),
|
||||
val flySpeed: KOptional<Double> = KOptional.empty(),
|
||||
val airFriction: KOptional<Double> = KOptional.empty(),
|
||||
val liquidFriction: KOptional<Double> = KOptional.empty(),
|
||||
val minimumLiquidPercentage: KOptional<Double> = KOptional.empty(),
|
||||
val liquidImpedance: KOptional<Double> = KOptional.empty(),
|
||||
val normalGroundFriction: KOptional<Double> = KOptional.empty(),
|
||||
val ambulatingGroundFriction: KOptional<Double> = KOptional.empty(),
|
||||
val groundForce: KOptional<Double> = KOptional.empty(),
|
||||
val airForce: KOptional<Double> = KOptional.empty(),
|
||||
val liquidForce: KOptional<Double> = KOptional.empty(),
|
||||
|
||||
val airJumpProfile: JumpProfile = JumpProfile(),
|
||||
val liquidJumpProfile: JumpProfile = JumpProfile(),
|
||||
|
||||
val fallStatusSpeedMin: KOptional<Double> = KOptional.empty(),
|
||||
val fallThroughSustainFrames: KOptional<Int> = KOptional.empty(),
|
||||
val maximumPlatformCorrection: KOptional<Double> = KOptional.empty(),
|
||||
val maximumPlatformCorrectionVelocityFactor: KOptional<Double> = KOptional.empty(),
|
||||
val physicsEffectCategories: KOptional<ImmutableSet<String>> = KOptional.empty(),
|
||||
val groundMovementMinimumSustain: KOptional<Double> = KOptional.empty(),
|
||||
val groundMovementMaximumSustain: KOptional<Double> = KOptional.empty(),
|
||||
val groundMovementCheckDistance: KOptional<Double> = KOptional.empty(),
|
||||
|
||||
val collisionEnabled: KOptional<Boolean> = KOptional.empty(),
|
||||
val frictionEnabled: KOptional<Boolean> = KOptional.empty(),
|
||||
val gravityEnabled: KOptional<Boolean> = KOptional.empty(),
|
||||
|
||||
val pathExploreRate: KOptional<Double> = KOptional.empty(),
|
||||
) {
|
||||
fun merge(other: MovementParameters): MovementParameters {
|
||||
return MovementParameters(
|
||||
mass = mass.or(other.mass),
|
||||
gravityMultiplier = gravityMultiplier.or(other.gravityMultiplier),
|
||||
liquidBuoyancy = liquidBuoyancy.or(other.liquidBuoyancy),
|
||||
airBuoyancy = airBuoyancy.or(other.airBuoyancy),
|
||||
bounceFactor = bounceFactor.or(other.bounceFactor),
|
||||
stopOnFirstBounce = stopOnFirstBounce.or(other.stopOnFirstBounce),
|
||||
enableSurfaceSlopeCorrection = enableSurfaceSlopeCorrection.or(other.enableSurfaceSlopeCorrection),
|
||||
slopeSlidingFactor = slopeSlidingFactor.or(other.slopeSlidingFactor),
|
||||
maxMovementPerStep = maxMovementPerStep.or(other.maxMovementPerStep),
|
||||
maximumCorrection = maximumCorrection.or(other.maximumCorrection),
|
||||
speedLimit = speedLimit.or(other.speedLimit),
|
||||
standingPoly = standingPoly.or(other.standingPoly),
|
||||
crouchingPoly = crouchingPoly.or(other.crouchingPoly),
|
||||
stickyCollision = stickyCollision.or(other.stickyCollision),
|
||||
stickyForce = stickyForce.or(other.stickyForce),
|
||||
walkSpeed = walkSpeed.or(other.walkSpeed),
|
||||
runSpeed = runSpeed.or(other.runSpeed),
|
||||
flySpeed = flySpeed.or(other.flySpeed),
|
||||
airFriction = airFriction.or(other.airFriction),
|
||||
liquidFriction = liquidFriction.or(other.liquidFriction),
|
||||
minimumLiquidPercentage = minimumLiquidPercentage.or(other.minimumLiquidPercentage),
|
||||
liquidImpedance = liquidImpedance.or(other.liquidImpedance),
|
||||
normalGroundFriction = normalGroundFriction.or(other.normalGroundFriction),
|
||||
ambulatingGroundFriction = ambulatingGroundFriction.or(other.ambulatingGroundFriction),
|
||||
groundForce = groundForce.or(other.groundForce),
|
||||
airForce = airForce.or(other.airForce),
|
||||
liquidForce = liquidForce.or(other.liquidForce),
|
||||
airJumpProfile = airJumpProfile.merge(other.airJumpProfile),
|
||||
liquidJumpProfile = liquidJumpProfile.merge(other.liquidJumpProfile),
|
||||
fallStatusSpeedMin = fallStatusSpeedMin.or(other.fallStatusSpeedMin),
|
||||
fallThroughSustainFrames = fallThroughSustainFrames.or(other.fallThroughSustainFrames),
|
||||
maximumPlatformCorrection = maximumPlatformCorrection.or(other.maximumPlatformCorrection),
|
||||
maximumPlatformCorrectionVelocityFactor = maximumPlatformCorrectionVelocityFactor.or(other.maximumPlatformCorrectionVelocityFactor),
|
||||
physicsEffectCategories = physicsEffectCategories.or(other.physicsEffectCategories),
|
||||
groundMovementMinimumSustain = groundMovementMinimumSustain.or(other.groundMovementMinimumSustain),
|
||||
groundMovementMaximumSustain = groundMovementMaximumSustain.or(other.groundMovementMaximumSustain),
|
||||
groundMovementCheckDistance = groundMovementCheckDistance.or(other.groundMovementCheckDistance),
|
||||
collisionEnabled = collisionEnabled.or(other.collisionEnabled),
|
||||
frictionEnabled = frictionEnabled.or(other.frictionEnabled),
|
||||
gravityEnabled = gravityEnabled.or(other.gravityEnabled),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package ru.dbotthepony.kstarbound.io.json
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.TypeAdapterFactory
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import ru.dbotthepony.kstarbound.util.KOptional
|
||||
import java.lang.reflect.ParameterizedType
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
object KOptionalTypeAdapter : TypeAdapterFactory {
|
||||
override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType === KOptional.Nullable::class.java || type.rawType === KOptional::class.java || type.rawType === KOptional.NotNull::class.java) {
|
||||
val notnull = type.rawType === KOptional.NotNull::class.java
|
||||
val param = (type.type as? ParameterizedType ?: return null).actualTypeArguments[0]
|
||||
val token = TypeToken.get(param)
|
||||
val isBool = token.rawType === Boolean::class.java
|
||||
|
||||
return object : TypeAdapter<KOptional<Any?>>() {
|
||||
private val adapter0 = gson.getAdapter(token) as TypeAdapter<Any?>
|
||||
|
||||
override fun write(out: JsonWriter, value: KOptional<Any?>?) {
|
||||
if (value === null) {
|
||||
out.nullValue()
|
||||
} else if (value.isPresent) {
|
||||
adapter0.write(out, value.value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): KOptional<Any?> {
|
||||
if (isBool) {
|
||||
if (notnull) {
|
||||
return KOptional((adapter0.read(`in`) ?: throw JsonSyntaxException("This KOptional does not accept nulls")) as Boolean) as KOptional<Any?>
|
||||
} else {
|
||||
return KOptional(adapter0.read(`in`) as Boolean?) as KOptional<Any?>
|
||||
}
|
||||
} else {
|
||||
if (notnull) {
|
||||
return KOptional(adapter0.read(`in`) ?: throw JsonSyntaxException("This KOptional does not accept nulls"))
|
||||
} else {
|
||||
return KOptional(adapter0.read(`in`))
|
||||
}
|
||||
}
|
||||
}
|
||||
} as TypeAdapter<T>
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableMap
|
||||
import com.github.benmanes.caffeine.cache.Interner
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParseException
|
||||
@ -18,6 +19,8 @@ import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import it.unimi.dsi.fastutil.ints.IntList
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
@ -28,7 +31,9 @@ import ru.dbotthepony.kstarbound.defs.util.enrollMap
|
||||
import ru.dbotthepony.kstarbound.defs.util.flattenJsonElement
|
||||
import ru.dbotthepony.kstarbound.io.json.consumeNull
|
||||
import ru.dbotthepony.kstarbound.io.json.value
|
||||
import ru.dbotthepony.kstarbound.util.Either
|
||||
import java.lang.reflect.Constructor
|
||||
import java.util.function.Function
|
||||
import kotlin.jvm.internal.DefaultConstructorMarker
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.*
|
||||
@ -43,13 +48,14 @@ import kotlin.reflect.full.primaryConstructor
|
||||
class FactoryAdapter<T : Any> private constructor(
|
||||
val clazz: KClass<T>,
|
||||
val types: ImmutableList<ReferencedProperty<T, *>>,
|
||||
aliases: Map<String, String>,
|
||||
aliases: Map<String, List<String>>,
|
||||
val asJsonArray: Boolean,
|
||||
val storesJson: Boolean,
|
||||
val stringInterner: Interner<String>,
|
||||
val logMisses: Boolean,
|
||||
private val elements: TypeAdapter<JsonElement>
|
||||
) : TypeAdapter<T>() {
|
||||
private val name2index = Object2IntArrayMap<String>()
|
||||
private val name2index = Object2ObjectArrayMap<String, IntArrayList>()
|
||||
private val loggedMisses = ObjectArraySet<String>()
|
||||
|
||||
init {
|
||||
@ -57,14 +63,14 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
throw IllegalArgumentException("Can't have both flat properties and input be as json array")
|
||||
}
|
||||
|
||||
name2index.defaultReturnValue(-1)
|
||||
|
||||
for ((i, pair) in types.withIndex()) {
|
||||
name2index[pair.property.name] = i
|
||||
name2index.computeIfAbsent(pair.property.name, Function { IntArrayList() }).add(i)
|
||||
|
||||
aliases.entries.forEach {
|
||||
if (it.value == pair.property.name) {
|
||||
name2index[it.key] = i
|
||||
it.value.forEach { target ->
|
||||
if (target == pair.property.name) {
|
||||
name2index.computeIfAbsent(it.key, Function { IntArrayList() }).add(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,7 +215,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
// Если нам необходимо читать объект как набор данных массива, то давай
|
||||
if (asJsonArray) {
|
||||
if (storesJson) {
|
||||
val readArray = TypeAdapters.JSON_ELEMENT.read(reader)
|
||||
val readArray = elements.read(reader)
|
||||
|
||||
if (readArray !is JsonArray)
|
||||
throw JsonParseException("Expected JSON element to be an Array, ${readArray::class.qualifiedName} given")
|
||||
@ -252,7 +258,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
var json: JsonObject by Delegates.notNull()
|
||||
|
||||
if (storesJson || types.any { it.isFlat }) {
|
||||
val readMap = TypeAdapters.JSON_ELEMENT.read(reader)
|
||||
val readMap = elements.read(reader)
|
||||
|
||||
if (readMap !is JsonObject)
|
||||
throw JsonParseException("Expected JSON element to be a Map, ${readMap::class.qualifiedName} given")
|
||||
@ -268,38 +274,55 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
|
||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
||||
val name = reader.nextName()
|
||||
val fieldId = name2index.getInt(name)
|
||||
val fields = name2index[name]
|
||||
|
||||
if (fieldId == -1) {
|
||||
if (fields == null || fields.size == 0) {
|
||||
if (!storesJson && logMisses && loggedMisses.add(name)) {
|
||||
LOGGER.warn("${clazz.qualifiedName} has no property for storing $name")
|
||||
}
|
||||
|
||||
reader.skipValue()
|
||||
} else {
|
||||
val tuple = types[fieldId]
|
||||
val localReader: Either<JsonElement, JsonReader>
|
||||
|
||||
if (tuple.isFlat) {
|
||||
reader.skipValue()
|
||||
continue
|
||||
if (fields.size == 1) {
|
||||
localReader = Either.right(reader)
|
||||
} else {
|
||||
val readValue = elements.read(reader)
|
||||
localReader = Either.left(readValue)
|
||||
}
|
||||
|
||||
if (tuple.isIgnored) {
|
||||
if (loggedMisses.add(name)) {
|
||||
LOGGER.warn("${clazz.qualifiedName} can not load $name from JSON")
|
||||
val itr = fields.intIterator()
|
||||
|
||||
while (itr.hasNext()) {
|
||||
val fieldId = itr.nextInt()
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val reader = localReader.map({ JsonTreeReader(it) }, { it })
|
||||
val tuple = types[fieldId]
|
||||
|
||||
if (tuple.isFlat) {
|
||||
reader.skipValue()
|
||||
continue
|
||||
}
|
||||
|
||||
reader.skipValue()
|
||||
continue
|
||||
}
|
||||
if (tuple.isIgnored) {
|
||||
if (loggedMisses.add(name)) {
|
||||
LOGGER.warn("${clazz.qualifiedName} can not load $name from JSON")
|
||||
}
|
||||
|
||||
val (field, adapter) = tuple
|
||||
reader.skipValue()
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
readValues[fieldId] = adapter.read(reader)
|
||||
presentValues[fieldId] = true
|
||||
} catch(err: Throwable) {
|
||||
throw JsonSyntaxException("Reading field \"${field.name}\" near ${reader.path} for ${clazz.qualifiedName}", err)
|
||||
val (field, adapter) = tuple
|
||||
|
||||
try {
|
||||
readValues[fieldId] = adapter.read(reader)
|
||||
presentValues[fieldId] = true
|
||||
} catch(err: Throwable) {
|
||||
throw JsonSyntaxException("Reading field \"${field.name}\" near ${reader.path} for ${clazz.qualifiedName}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -391,7 +414,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
private var asList = false
|
||||
private var storesJson = false
|
||||
private val types = ArrayList<ReferencedProperty<T, *>>()
|
||||
private val aliases = Object2ObjectArrayMap<String, String>()
|
||||
private val aliases = Object2ObjectArrayMap<String, ArrayList<String>>()
|
||||
var stringInterner: Interner<String> = Interner { it }
|
||||
var logMisses = true
|
||||
|
||||
@ -422,7 +445,8 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
storesJson = storesJson,
|
||||
stringInterner = stringInterner,
|
||||
aliases = aliases,
|
||||
logMisses = logMisses
|
||||
logMisses = logMisses,
|
||||
elements = gson.getAdapter(JsonElement::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
@ -445,7 +469,7 @@ class FactoryAdapter<T : Any> private constructor(
|
||||
}
|
||||
|
||||
fun alias(alias: String, canonical: String): Builder<T> {
|
||||
aliases[alias] = canonical
|
||||
aliases.computeIfAbsent(alias, Function { ArrayList() }).add(canonical)
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.io.json.builder
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import ru.dbotthepony.kstarbound.util.KOptional
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.KProperty1
|
||||
@ -34,10 +35,23 @@ open class ReferencedProperty<T : Any, V>(
|
||||
private var isResolved = false
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@Suppress("DEPRECATION")
|
||||
fun resolve(gson: Gson) {
|
||||
if (!isResolved) {
|
||||
isResolved = true
|
||||
adapter = gson.getAdapter(TypeToken.get(property.returnType.javaType)) as TypeAdapter<V>
|
||||
val token = TypeToken.get(type.javaType)
|
||||
|
||||
if (token.rawType === KOptional::class.java && type.arguments.size == 1) {
|
||||
val subtype = type.arguments[0]
|
||||
|
||||
if (subtype.type!!.isMarkedNullable) {
|
||||
adapter = gson.getAdapter(TypeToken.getParameterized(KOptional.Nullable::class.java, subtype.type!!.javaType)) as TypeAdapter<V>
|
||||
} else {
|
||||
adapter = gson.getAdapter(TypeToken.getParameterized(KOptional.NotNull::class.java, subtype.type!!.javaType)) as TypeAdapter<V>
|
||||
}
|
||||
} else {
|
||||
adapter = gson.getAdapter(token) as TypeAdapter<V>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,10 +52,12 @@ class Either<L, R> private constructor(val left: KOptional<L>, val right: KOptio
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun <L, R> left(value: L): Either<L, R> {
|
||||
return Either(KOptional.of(value), KOptional.empty())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun <L, R> right(value: R): Either<L, R> {
|
||||
return Either(KOptional.empty(), KOptional.of(value))
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package ru.dbotthepony.kstarbound.util
|
||||
|
||||
fun <T> KOptional(value: T) = KOptional.of(value)
|
||||
fun KOptional(value: Boolean) = KOptional.of(value)
|
||||
fun KOptional(value: Boolean?) = KOptional.of(value)
|
||||
|
||||
/**
|
||||
* [java.util.Optional] supporting nulls
|
||||
@ -27,6 +29,39 @@ class KOptional<T> private constructor(private val _value: T, val isPresent: Boo
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <R> map(block: (T) -> R): KOptional<R> {
|
||||
if (isPresent) {
|
||||
return of(block.invoke(value))
|
||||
} else {
|
||||
return empty()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun orElse(value: T): T {
|
||||
if (isPresent) {
|
||||
return this.value
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
inline fun orElse(value: () -> T): T {
|
||||
if (isPresent) {
|
||||
return this.value
|
||||
} else {
|
||||
return value.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
infix fun or(value: KOptional<T>): KOptional<T> {
|
||||
if (isPresent) {
|
||||
return this
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return this === other || other is KOptional<*> && isPresent == other.isPresent && _value == other._value
|
||||
}
|
||||
@ -43,9 +78,17 @@ class KOptional<T> private constructor(private val _value: T, val isPresent: Boo
|
||||
}
|
||||
}
|
||||
|
||||
// gson hack since type token can't diff between nullable and non-null types
|
||||
@Deprecated("internal class")
|
||||
internal class Nullable<T : Any?> private constructor()
|
||||
@Deprecated("internal class")
|
||||
internal class NotNull<T : Any> private constructor()
|
||||
|
||||
companion object {
|
||||
private val EMPTY = KOptional(null, false)
|
||||
private val NULL = KOptional(null, true)
|
||||
private val TRUE = KOptional(true, true)
|
||||
private val FALSE = KOptional(false, true)
|
||||
|
||||
@JvmStatic
|
||||
fun <T> empty(): KOptional<T> {
|
||||
@ -60,5 +103,25 @@ class KOptional<T> private constructor(private val _value: T, val isPresent: Boo
|
||||
return KOptional(value, true)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun of(value: Boolean): KOptional<Boolean> {
|
||||
if (value) {
|
||||
return TRUE
|
||||
} else {
|
||||
return FALSE
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun of(value: Boolean?): KOptional<Boolean> {
|
||||
if (value == null) {
|
||||
return NULL as KOptional<Boolean>
|
||||
} else if (value) {
|
||||
return TRUE
|
||||
} else {
|
||||
return FALSE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ enum class RayFilterResult(val hit: Boolean, val write: Boolean) {
|
||||
|
||||
companion object {
|
||||
fun of(boolean: Boolean): RayFilterResult {
|
||||
return if (boolean) HIT else CONTINUE
|
||||
return if (boolean) HIT else SKIP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user