KOptional type adapter, and movement controller defs structures
This commit is contained in:
parent
c0ecbe9a8b
commit
86782e259e
@ -6,6 +6,7 @@ import com.google.gson.GsonBuilder
|
|||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.TypeAdapterFactory
|
import com.google.gson.TypeAdapterFactory
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
import java.util.Arrays
|
import java.util.Arrays
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
import kotlin.reflect.KProperty
|
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)
|
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.FastutilTypeAdapterFactory
|
||||||
import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter
|
import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.InternedStringAdapter
|
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.LongRangeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.NothingAdapter
|
import ru.dbotthepony.kstarbound.io.json.NothingAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.OneOfTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.OneOfTypeAdapter
|
||||||
@ -217,6 +218,8 @@ object Starbound : ISBFileLocator {
|
|||||||
registerTypeAdapterFactory(EitherTypeAdapter)
|
registerTypeAdapterFactory(EitherTypeAdapter)
|
||||||
// OneOf<>
|
// OneOf<>
|
||||||
registerTypeAdapterFactory(OneOfTypeAdapter)
|
registerTypeAdapterFactory(OneOfTypeAdapter)
|
||||||
|
// KOptional<>
|
||||||
|
registerTypeAdapterFactory(KOptionalTypeAdapter)
|
||||||
|
|
||||||
// Pair<>
|
// Pair<>
|
||||||
registerTypeAdapterFactory(PairAdapterFactory)
|
registerTypeAdapterFactory(PairAdapterFactory)
|
||||||
@ -347,6 +350,9 @@ object Starbound : ISBFileLocator {
|
|||||||
@Volatile
|
@Volatile
|
||||||
var terminateLoading = false
|
var terminateLoading = false
|
||||||
|
|
||||||
|
var defaultMovementParameters = MovementParameters()
|
||||||
|
private set
|
||||||
|
|
||||||
fun loadJsonAsset(path: String): JsonElement? {
|
fun loadJsonAsset(path: String): JsonElement? {
|
||||||
val filename: String
|
val filename: String
|
||||||
val jsonPath: String?
|
val jsonPath: String?
|
||||||
@ -1031,6 +1037,8 @@ object Starbound : ISBFileLocator {
|
|||||||
|
|
||||||
tasks.forEach { it.join() }
|
tasks.forEach { it.join() }
|
||||||
|
|
||||||
|
defaultMovementParameters = gson.fromJson(jsonReader("/default_actor_movement.config"))!!
|
||||||
|
|
||||||
initializing = false
|
initializing = false
|
||||||
initialized = true
|
initialized = true
|
||||||
log.line("Finished loading in ${System.currentTimeMillis() - time}ms")
|
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.common.collect.ImmutableMap
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.io.StarboundPak
|
import ru.dbotthepony.kstarbound.io.StarboundPak
|
||||||
import ru.dbotthepony.kstarbound.stream
|
import ru.dbotthepony.kstarbound.stream
|
||||||
@ -37,6 +38,12 @@ fun interface ISBFileLocator {
|
|||||||
* @throws FileNotFoundException if file does not exist
|
* @throws FileNotFoundException if file does not exist
|
||||||
*/
|
*/
|
||||||
fun reader(path: String) = locate(path).reader()
|
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 {
|
interface IStarboundFile : ISBFileLocator {
|
||||||
@ -145,6 +152,12 @@ interface IStarboundFile : ISBFileLocator {
|
|||||||
*/
|
*/
|
||||||
fun reader(): Reader = InputStreamReader(BufferedInputStream(open()))
|
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 IllegalStateException if file is a directory
|
||||||
* @throws FileNotFoundException if file does not exist
|
* @throws FileNotFoundException if file does not exist
|
||||||
|
@ -1,10 +1,31 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs
|
package ru.dbotthepony.kstarbound.defs
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
|
||||||
|
import ru.dbotthepony.kstarbound.util.KOptional
|
||||||
|
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class JumpProfile(
|
data class JumpProfile(
|
||||||
val jumpSpeed: Double = 0.0,
|
val jumpSpeed: KOptional<Double> = KOptional.empty(),
|
||||||
val jumpInitialPercentage: Double = 0.0,
|
val jumpControlForce: KOptional<Double> = KOptional.empty(),
|
||||||
val jumpHoldTime: Double = 0.0,
|
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
|
package ru.dbotthepony.kstarbound.defs
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
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.io.json.builder.JsonFactory
|
||||||
|
import ru.dbotthepony.kstarbound.util.KOptional
|
||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
|
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class MovementParameters(
|
data class MovementParameters(
|
||||||
val flySpeed: Double? = null,
|
val mass: KOptional<Double> = KOptional.empty(),
|
||||||
val airFriction: Double? = null,
|
val gravityMultiplier: KOptional<Double> = KOptional.empty(),
|
||||||
val airJumpProfile: JumpProfile? = null,
|
val liquidBuoyancy: KOptional<Double> = KOptional.empty(),
|
||||||
val airForce: Double? = null,
|
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,
|
@JsonAlias("collisionPoly")
|
||||||
val walkSpeed: Double? = null,
|
val standingPoly: KOptional<ImmutableList<Vector2d>> = KOptional.empty(),
|
||||||
val mass: Double? = null,
|
@JsonAlias("collisionPoly")
|
||||||
|
val crouchingPoly: KOptional<ImmutableList<Vector2d>> = KOptional.empty(),
|
||||||
|
|
||||||
// TODO: А оно вообще используется? Как по мне движок старбаунда генерирует коллизию из пикселей текстуры
|
val stickyCollision: KOptional<Boolean> = KOptional.empty(),
|
||||||
val collisionPoly: ImmutableList<Vector2d>? = null,
|
val stickyForce: KOptional<Double> = KOptional.empty(),
|
||||||
val crouchingPoly: ImmutableList<Vector2d>? = null,
|
val walkSpeed: KOptional<Double> = KOptional.empty(),
|
||||||
val standingPoly: ImmutableList<Vector2d>? = null,
|
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.github.benmanes.caffeine.cache.Interner
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonNull
|
import com.google.gson.JsonNull
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonParseException
|
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.JsonToken
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
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.Object2IntArrayMap
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
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.defs.util.flattenJsonElement
|
||||||
import ru.dbotthepony.kstarbound.io.json.consumeNull
|
import ru.dbotthepony.kstarbound.io.json.consumeNull
|
||||||
import ru.dbotthepony.kstarbound.io.json.value
|
import ru.dbotthepony.kstarbound.io.json.value
|
||||||
|
import ru.dbotthepony.kstarbound.util.Either
|
||||||
import java.lang.reflect.Constructor
|
import java.lang.reflect.Constructor
|
||||||
|
import java.util.function.Function
|
||||||
import kotlin.jvm.internal.DefaultConstructorMarker
|
import kotlin.jvm.internal.DefaultConstructorMarker
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.*
|
||||||
@ -43,13 +48,14 @@ import kotlin.reflect.full.primaryConstructor
|
|||||||
class FactoryAdapter<T : Any> private constructor(
|
class FactoryAdapter<T : Any> private constructor(
|
||||||
val clazz: KClass<T>,
|
val clazz: KClass<T>,
|
||||||
val types: ImmutableList<ReferencedProperty<T, *>>,
|
val types: ImmutableList<ReferencedProperty<T, *>>,
|
||||||
aliases: Map<String, String>,
|
aliases: Map<String, List<String>>,
|
||||||
val asJsonArray: Boolean,
|
val asJsonArray: Boolean,
|
||||||
val storesJson: Boolean,
|
val storesJson: Boolean,
|
||||||
val stringInterner: Interner<String>,
|
val stringInterner: Interner<String>,
|
||||||
val logMisses: Boolean,
|
val logMisses: Boolean,
|
||||||
|
private val elements: TypeAdapter<JsonElement>
|
||||||
) : TypeAdapter<T>() {
|
) : TypeAdapter<T>() {
|
||||||
private val name2index = Object2IntArrayMap<String>()
|
private val name2index = Object2ObjectArrayMap<String, IntArrayList>()
|
||||||
private val loggedMisses = ObjectArraySet<String>()
|
private val loggedMisses = ObjectArraySet<String>()
|
||||||
|
|
||||||
init {
|
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")
|
throw IllegalArgumentException("Can't have both flat properties and input be as json array")
|
||||||
}
|
}
|
||||||
|
|
||||||
name2index.defaultReturnValue(-1)
|
|
||||||
|
|
||||||
for ((i, pair) in types.withIndex()) {
|
for ((i, pair) in types.withIndex()) {
|
||||||
name2index[pair.property.name] = i
|
name2index.computeIfAbsent(pair.property.name, Function { IntArrayList() }).add(i)
|
||||||
|
|
||||||
aliases.entries.forEach {
|
aliases.entries.forEach {
|
||||||
if (it.value == pair.property.name) {
|
it.value.forEach { target ->
|
||||||
name2index[it.key] = i
|
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 (asJsonArray) {
|
||||||
if (storesJson) {
|
if (storesJson) {
|
||||||
val readArray = TypeAdapters.JSON_ELEMENT.read(reader)
|
val readArray = elements.read(reader)
|
||||||
|
|
||||||
if (readArray !is JsonArray)
|
if (readArray !is JsonArray)
|
||||||
throw JsonParseException("Expected JSON element to be an Array, ${readArray::class.qualifiedName} given")
|
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()
|
var json: JsonObject by Delegates.notNull()
|
||||||
|
|
||||||
if (storesJson || types.any { it.isFlat }) {
|
if (storesJson || types.any { it.isFlat }) {
|
||||||
val readMap = TypeAdapters.JSON_ELEMENT.read(reader)
|
val readMap = elements.read(reader)
|
||||||
|
|
||||||
if (readMap !is JsonObject)
|
if (readMap !is JsonObject)
|
||||||
throw JsonParseException("Expected JSON element to be a Map, ${readMap::class.qualifiedName} given")
|
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) {
|
while (reader.peek() != JsonToken.END_OBJECT) {
|
||||||
val name = reader.nextName()
|
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)) {
|
if (!storesJson && logMisses && loggedMisses.add(name)) {
|
||||||
LOGGER.warn("${clazz.qualifiedName} has no property for storing $name")
|
LOGGER.warn("${clazz.qualifiedName} has no property for storing $name")
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.skipValue()
|
reader.skipValue()
|
||||||
} else {
|
} else {
|
||||||
val tuple = types[fieldId]
|
val localReader: Either<JsonElement, JsonReader>
|
||||||
|
|
||||||
if (tuple.isFlat) {
|
if (fields.size == 1) {
|
||||||
reader.skipValue()
|
localReader = Either.right(reader)
|
||||||
continue
|
} else {
|
||||||
|
val readValue = elements.read(reader)
|
||||||
|
localReader = Either.left(readValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tuple.isIgnored) {
|
val itr = fields.intIterator()
|
||||||
if (loggedMisses.add(name)) {
|
|
||||||
LOGGER.warn("${clazz.qualifiedName} can not load $name from JSON")
|
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()
|
if (tuple.isIgnored) {
|
||||||
continue
|
if (loggedMisses.add(name)) {
|
||||||
}
|
LOGGER.warn("${clazz.qualifiedName} can not load $name from JSON")
|
||||||
|
}
|
||||||
|
|
||||||
val (field, adapter) = tuple
|
reader.skipValue()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
val (field, adapter) = tuple
|
||||||
readValues[fieldId] = adapter.read(reader)
|
|
||||||
presentValues[fieldId] = true
|
try {
|
||||||
} catch(err: Throwable) {
|
readValues[fieldId] = adapter.read(reader)
|
||||||
throw JsonSyntaxException("Reading field \"${field.name}\" near ${reader.path} for ${clazz.qualifiedName}", err)
|
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 asList = false
|
||||||
private var storesJson = false
|
private var storesJson = false
|
||||||
private val types = ArrayList<ReferencedProperty<T, *>>()
|
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 stringInterner: Interner<String> = Interner { it }
|
||||||
var logMisses = true
|
var logMisses = true
|
||||||
|
|
||||||
@ -422,7 +445,8 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
storesJson = storesJson,
|
storesJson = storesJson,
|
||||||
stringInterner = stringInterner,
|
stringInterner = stringInterner,
|
||||||
aliases = aliases,
|
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> {
|
fun alias(alias: String, canonical: String): Builder<T> {
|
||||||
aliases[alias] = canonical
|
aliases.computeIfAbsent(alias, Function { ArrayList() }).add(canonical)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.io.json.builder
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import ru.dbotthepony.kstarbound.util.KOptional
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
import kotlin.reflect.KProperty1
|
import kotlin.reflect.KProperty1
|
||||||
@ -34,10 +35,23 @@ open class ReferencedProperty<T : Any, V>(
|
|||||||
private var isResolved = false
|
private var isResolved = false
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
fun resolve(gson: Gson) {
|
fun resolve(gson: Gson) {
|
||||||
if (!isResolved) {
|
if (!isResolved) {
|
||||||
isResolved = true
|
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 {
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
fun <L, R> left(value: L): Either<L, R> {
|
fun <L, R> left(value: L): Either<L, R> {
|
||||||
return Either(KOptional.of(value), KOptional.empty())
|
return Either(KOptional.of(value), KOptional.empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
fun <L, R> right(value: R): Either<L, R> {
|
fun <L, R> right(value: R): Either<L, R> {
|
||||||
return Either(KOptional.empty(), KOptional.of(value))
|
return Either(KOptional.empty(), KOptional.of(value))
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package ru.dbotthepony.kstarbound.util
|
package ru.dbotthepony.kstarbound.util
|
||||||
|
|
||||||
fun <T> KOptional(value: T) = KOptional.of(value)
|
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
|
* [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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
return this === other || other is KOptional<*> && isPresent == other.isPresent && _value == other._value
|
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 {
|
companion object {
|
||||||
private val EMPTY = KOptional(null, false)
|
private val EMPTY = KOptional(null, false)
|
||||||
private val NULL = KOptional(null, true)
|
private val NULL = KOptional(null, true)
|
||||||
|
private val TRUE = KOptional(true, true)
|
||||||
|
private val FALSE = KOptional(false, true)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun <T> empty(): KOptional<T> {
|
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)
|
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 {
|
companion object {
|
||||||
fun of(boolean: Boolean): RayFilterResult {
|
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