diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index dfa59cf5..3d51df8a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -176,6 +176,8 @@ fun main() { item.spawn() item.movement.applyVelocity(Vector2d(rand.nextDouble() * 1000.0 - 500.0, rand.nextDouble() * 1000.0 - 500.0)) } + + // println(Starbound.statusEffects["firecharge"]) } //ent.position += Vector2d(y = 14.0, x = -10.0) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index 99cc3c4a..125b21a3 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -31,11 +31,13 @@ import ru.dbotthepony.kstarbound.defs.item.LiquidItemPrototype import ru.dbotthepony.kstarbound.defs.item.MaterialItemPrototype import ru.dbotthepony.kstarbound.defs.liquid.LiquidDefinition import ru.dbotthepony.kstarbound.defs.parallax.ParallaxPrototype +import ru.dbotthepony.kstarbound.defs.particle.ParticleDefinition import ru.dbotthepony.kstarbound.defs.player.PlayerDefinition import ru.dbotthepony.kstarbound.defs.projectile.* import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.io.* +import ru.dbotthepony.kstarbound.io.json.EitherTypeAdapter import ru.dbotthepony.kstarbound.io.json.builder.EnumAdapter import ru.dbotthepony.kstarbound.io.json.builder.BuilderAdapter import ru.dbotthepony.kstarbound.io.json.builder.FactoryAdapter @@ -89,7 +91,11 @@ object Starbound { private val parallax = Object2ObjectOpenHashMap() private val functions = Object2ObjectOpenHashMap() private val species = Object2ObjectOpenHashMap() - private val statusEffects = Object2ObjectOpenHashMap() + private val _statusEffects = Object2ObjectOpenHashMap() + private val _particles = Object2ObjectOpenHashMap() + + val particles: Map = Collections.unmodifiableMap(_particles) + val statusEffects: Map = Collections.unmodifiableMap(_statusEffects) private val items = Object2ObjectOpenHashMap() @@ -145,6 +151,8 @@ object Starbound { // автоматическое создание FactoryAdapter по @аннотациям .registerTypeAdapterFactory(FactoryAdapter.Companion) + .registerTypeAdapterFactory(EitherTypeAdapter) + .also(::addStarboundJsonAdapters) .registerTypeAdapterFactory(IItemDefinition.InventoryIcon.Factory(pathStack)) @@ -170,7 +178,8 @@ object Starbound { .add(functions::get) .add(items::get) .add(species::get) - .add(statusEffects::get) + .add(_statusEffects::get) + .add(_particles::get) ) .create() @@ -430,8 +439,9 @@ object Starbound { loadStage(callback, TileDefinition::class.java, TileDefinition::materialName, tiles::put, TileDefinition::materialId, tilesByMaterialID::put, ext2files["material"] ?: listOf(), "materials") loadStage(callback, MaterialModifier::class.java, MaterialModifier::modName, tileModifiers::put, MaterialModifier::modId, tileModifiersByID::put, ext2files["matmod"] ?: listOf(), "material modifier definitions") loadStage(callback, LiquidDefinition::class.java, LiquidDefinition::name, liquid::put, LiquidDefinition::liquidId, liquidByID::put, ext2files["liquid"] ?: listOf(), "liquid definitions") - loadStage(callback, StatusEffectDefinition::class.java, StatusEffectDefinition::name, statusEffects::put, ext2files["statuseffect"] ?: listOf(), "status effects") + loadStage(callback, StatusEffectDefinition::class.java, StatusEffectDefinition::name, _statusEffects::put, ext2files["statuseffect"] ?: listOf(), "status effects") loadStage(callback, Species::class.java, Species::kind, species::put, ext2files["species"] ?: listOf(), "species") + loadStage(callback, ParticleDefinition::class.java, ParticleDefinition::kind, _particles::put, ext2files["particle"] ?: listOf(), "particles") pathStack.block("/") { playerDefinition = GSON.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/RegistryReference.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/RegistryReference.kt index 3f9bad39..37009a7d 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/RegistryReference.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/RegistryReference.kt @@ -7,6 +7,7 @@ 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.JsonToken import com.google.gson.stream.JsonWriter import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap import java.lang.ref.Reference @@ -45,12 +46,22 @@ class RegistryReferenceFactory : TypeAdapterFactory { } class RegistryReferenceTypeAdapter(val resolver: (String) -> T?, val strings: TypeAdapter) : TypeAdapter>() { - override fun write(out: JsonWriter, value: RegistryReference) { - strings.write(out, value.name) + override fun write(out: JsonWriter, value: RegistryReference?) { + if (value == null) + out.nullValue() + else + strings.write(out, value.name) } - override fun read(`in`: JsonReader): RegistryReference { - return RegistryReference(strings.read(`in`) ?: throw JsonSyntaxException("Can't have null as registry name"), resolver) + override fun read(`in`: JsonReader): RegistryReference? { + if (`in`.peek() == JsonToken.NULL) + return null + + if (`in`.peek() == JsonToken.STRING) { + return RegistryReference(strings.read(`in`)!!, resolver) + } + + throw JsonSyntaxException("Expecting string for registry reference, ${`in`.peek()} given, near ${`in`.path}") } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimationDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimationDefinition.kt index 06a8035e..c1d09930 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimationDefinition.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimationDefinition.kt @@ -3,14 +3,15 @@ package ru.dbotthepony.kstarbound.defs.animation import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableMap import ru.dbotthepony.kstarbound.defs.image.SpriteReference +import ru.dbotthepony.kstarbound.defs.particle.ParticleEmitter import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory +import ru.dbotthepony.kstarbound.util.Either import ru.dbotthepony.kvector.vector.ndouble.Vector2d -import ru.dbotthepony.kvector.vector.ndouble.Vector4d @JsonFactory data class AnimationDefinition( val animatedParts: AnimatedParts? = null, - val sounds: ImmutableMap = ImmutableMap.of(), + val sounds: ImmutableMap, CustomSound>> = ImmutableMap.of(), val transformationGroups: ImmutableMap = ImmutableMap.of(), val particleEmitters: ImmutableMap = ImmutableMap.of(), ) { @@ -19,27 +20,16 @@ data class AnimationDefinition( val interpolated: Boolean? = null ) + // TODO @JsonFactory - data class Sound( - // TODO + data class CustomSound( val sound: String? = null ) - @JsonFactory - data class ParticleEmitter( - val enabled: Boolean = true, - val emissionRate: Double = 1.0, - val count: Int = 1, - val offset: Vector2d? = null, - val offsetRegion: Vector4d? = null, - val transformationGroups: ImmutableList = ImmutableList.of(), - val particles: ImmutableList - ) - @JsonFactory data class AnimatedParts( - val stateTypes: ImmutableMap, - val parts: ImmutableMap, + val stateTypes: ImmutableMap = ImmutableMap.of(), + val parts: ImmutableMap = ImmutableMap.of(), ) { @JsonFactory data class StateType( diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/ParticleDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/ParticleDefinition.kt deleted file mode 100644 index 2570e195..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/ParticleDefinition.kt +++ /dev/null @@ -1,46 +0,0 @@ -package ru.dbotthepony.kstarbound.defs.animation - -import ru.dbotthepony.kstarbound.defs.AssetReference -import ru.dbotthepony.kstarbound.defs.image.SpriteReference -import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory -import ru.dbotthepony.kvector.vector.ndouble.Vector2d -import ru.dbotthepony.kvector.vector.ndouble.Vector4d - -@JsonFactory -data class ParticleDefinition( - val count: Int = 1, - val offset: Vector2d? = null, - val offsetRegion: Vector4d? = null, - val particle: Config, -) { - @JsonFactory - data class Config( - val type: ParticleType, - val animation: AssetReference? = null, - val image: SpriteReference? = null, - val position: Vector2d = Vector2d.ZERO, - val offsetRegion: Vector4d = Vector4d.ZERO, - val initialVelocity: Vector2d = Vector2d.ZERO, - val finalVelocity: Vector2d = Vector2d.ZERO, - val approach: Vector2d = Vector2d.ZERO, - val angularVelocity: Double = 0.0, - val destructionAction: DestructionAction? = null, - val destructionTime: Double? = null, - val fade: Double = 0.0, - val size: Double = 1.0, - val layer: ParticleLayer? = null, - val timeToLive: Double = 1.0, - val variance: Variance = Variance.EMPTY - ) - - @JsonFactory - data class Variance( - val initialVelocity: Vector2d = Vector2d.ZERO, - val position: Vector2d = Vector2d.ZERO, - val angularVelocity: Double = 0.0 - ) { - companion object { - val EMPTY = Variance() - } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/ParticleType.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/ParticleType.kt index 9fe40fa9..5b717af9 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/ParticleType.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/ParticleType.kt @@ -2,5 +2,7 @@ package ru.dbotthepony.kstarbound.defs.animation enum class ParticleType { ANIMATED, - TEXTURED + TEXTURED, + EMBER, + TEXT } \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/SpriteReference.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/SpriteReference.kt index 93cda66b..4776a215 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/SpriteReference.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/SpriteReference.kt @@ -23,12 +23,22 @@ data class SpriteReference( } class Adapter(val remapper: AssetPathStack) : TypeAdapter() { - override fun write(out: JsonWriter, value: SpriteReference) { - out.value(value.image + ":" + value.sprite.raw) + override fun write(out: JsonWriter, value: SpriteReference?) { + if (value == null) + out.nullValue() + else + out.value(value.image + ":" + value.sprite.raw) } - override fun read(`in`: JsonReader): SpriteReference { - return parse(remapper.remap(`in`.nextString())) + override fun read(`in`: JsonReader): SpriteReference? { + val value = `in`.nextString() ?: return null + + // TODO: если нам надо принудительно удалить спрайт с вышестоящей ступени, как это сделать? + // а ещё, зачем вы это сделали. + if (value == "") + return null + + return parse(remapper.remap(value)) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/IParticleConfig.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/IParticleConfig.kt new file mode 100644 index 00000000..1000c108 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/IParticleConfig.kt @@ -0,0 +1,40 @@ +package ru.dbotthepony.kstarbound.defs.particle + +import com.google.common.collect.ImmutableList +import ru.dbotthepony.kstarbound.defs.animation.DestructionAction +import ru.dbotthepony.kstarbound.defs.animation.ParticleLayer +import ru.dbotthepony.kstarbound.io.json.builder.JsonImplementation +import ru.dbotthepony.kstarbound.util.ChainedProperty +import ru.dbotthepony.kstarbound.util.SBPattern +import ru.dbotthepony.kvector.vector.ndouble.Vector2d + +@JsonImplementation(ParticleConfig::class) +interface IParticleConfig : IParticleVariance { + val finalVelocity: Vector2d? + val destructionAction: DestructionAction? + val destructionTime: Double? + val fade: Double? + val layer: ParticleLayer? + val timeToLive: Double? + val variance: IParticleVariance? + val text: SBPattern? + + companion object { + fun chain(vararg particles: IParticleConfig): IParticleConfig { + val chain = IParticleVariance.chain(*particles) + @Suppress("name_shadowing") + val particles = ImmutableList.copyOf(particles) + + return object : IParticleConfig, IParticleVariance by chain { + override val finalVelocity by ChainedProperty(IParticleConfig::finalVelocity, particles) + override val destructionAction by ChainedProperty(IParticleConfig::destructionAction, particles) + override val destructionTime by ChainedProperty(IParticleConfig::destructionTime, particles) + override val fade by ChainedProperty(IParticleConfig::fade, particles) + override val layer by ChainedProperty(IParticleConfig::layer, particles) + override val timeToLive by ChainedProperty(IParticleConfig::timeToLive, particles) + override val variance by ChainedProperty(IParticleConfig::variance, particles) + override val text by ChainedProperty(IParticleConfig::text, particles) + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/IParticleVariance.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/IParticleVariance.kt new file mode 100644 index 00000000..cc5ccf9e --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/IParticleVariance.kt @@ -0,0 +1,38 @@ +package ru.dbotthepony.kstarbound.defs.particle + +import com.google.common.collect.ImmutableList +import ru.dbotthepony.kstarbound.io.json.builder.JsonImplementation +import ru.dbotthepony.kstarbound.util.ChainedProperty +import ru.dbotthepony.kvector.vector.Color +import ru.dbotthepony.kvector.vector.ndouble.Vector2d +import ru.dbotthepony.kvector.vector.ndouble.Vector4d + +@JsonImplementation(ParticleVariance::class) +interface IParticleVariance { + val offset: Vector2d? + val position: Vector2d? + val offsetRegion: Vector4d? + val initialVelocity: Vector2d? + val approach: Vector2d? + val angularVelocity: Double? + val size: Double? + val color: Color? + + companion object { + fun chain(vararg particles: IParticleVariance): IParticleVariance { + @Suppress("name_shadowing") + val particles = ImmutableList.copyOf(particles) + + return object : IParticleVariance { + override val offset by ChainedProperty(IParticleVariance::offset, particles) + override val position by ChainedProperty(IParticleVariance::position, particles) + override val offsetRegion by ChainedProperty(IParticleVariance::offsetRegion, particles) + override val initialVelocity by ChainedProperty(IParticleVariance::initialVelocity, particles) + override val approach by ChainedProperty(IParticleVariance::approach, particles) + override val angularVelocity by ChainedProperty(IParticleVariance::angularVelocity, particles) + override val size by ChainedProperty(IParticleVariance::size, particles) + override val color by ChainedProperty(IParticleVariance::color, particles) + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleConfig.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleConfig.kt new file mode 100644 index 00000000..f7afd12d --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleConfig.kt @@ -0,0 +1,36 @@ +package ru.dbotthepony.kstarbound.defs.particle + +import ru.dbotthepony.kstarbound.defs.AssetReference +import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition +import ru.dbotthepony.kstarbound.defs.animation.DestructionAction +import ru.dbotthepony.kstarbound.defs.animation.ParticleLayer +import ru.dbotthepony.kstarbound.defs.animation.ParticleType +import ru.dbotthepony.kstarbound.defs.image.SpriteReference +import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory +import ru.dbotthepony.kstarbound.util.SBPattern +import ru.dbotthepony.kvector.vector.Color +import ru.dbotthepony.kvector.vector.ndouble.Vector2d +import ru.dbotthepony.kvector.vector.ndouble.Vector4d + +@JsonFactory +data class ParticleConfig( + val type: ParticleType, + val animation: AssetReference? = null, + val image: SpriteReference? = null, + override val offset: Vector2d? = null, + override val position: Vector2d? = null, + override val offsetRegion: Vector4d? = null, + override val initialVelocity: Vector2d? = null, + override val finalVelocity: Vector2d? = null, + override val approach: Vector2d? = null, + override val angularVelocity: Double? = null, + override val destructionAction: DestructionAction? = null, + override val destructionTime: Double? = null, + override val fade: Double? = null, + override val size: Double? = null, + override val layer: ParticleLayer? = null, + override val timeToLive: Double? = null, + override val variance: IParticleVariance? = null, + override val color: Color? = null, + override val text: SBPattern? = null, +) : IParticleConfig diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleCreator.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleCreator.kt new file mode 100644 index 00000000..abeea140 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleCreator.kt @@ -0,0 +1,32 @@ +package ru.dbotthepony.kstarbound.defs.particle + +import ru.dbotthepony.kstarbound.defs.RegistryReference +import ru.dbotthepony.kstarbound.defs.animation.DestructionAction +import ru.dbotthepony.kstarbound.defs.animation.ParticleLayer +import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory +import ru.dbotthepony.kstarbound.util.Either +import ru.dbotthepony.kvector.vector.Color +import ru.dbotthepony.kvector.vector.ndouble.Vector2d +import ru.dbotthepony.kvector.vector.ndouble.Vector4d + +@JsonFactory +data class ParticleCreator( + val count: Int = 1, + val particle: Either, IParticleConfig>, + + //override val offset: Vector2d? = null, + //override val position: Vector2d? = null, + //override val offsetRegion: Vector4d? = null, + //override val initialVelocity: Vector2d? = null, + //override val finalVelocity: Vector2d? = null, + //override val approach: Vector2d? = null, + //override val angularVelocity: Double? = null, + //override val destructionAction: DestructionAction? = null, + //override val destructionTime: Double? = null, + //override val fade: Double? = null, + //override val size: Double? = null, + //override val layer: ParticleLayer? = null, + //override val timeToLive: Double? = null, + //override val variance: IParticleVariance? = null, + //override val color: Color? = null, +) //: IParticleConfig diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleDefinition.kt new file mode 100644 index 00000000..b93bbced --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleDefinition.kt @@ -0,0 +1,9 @@ +package ru.dbotthepony.kstarbound.defs.particle + +import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory + +@JsonFactory +data class ParticleDefinition( + val kind: String, + val definition: IParticleConfig +) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleEmitter.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleEmitter.kt new file mode 100644 index 00000000..ebba476f --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleEmitter.kt @@ -0,0 +1,36 @@ +package ru.dbotthepony.kstarbound.defs.particle + +import com.google.common.collect.ImmutableList +import ru.dbotthepony.kstarbound.defs.animation.DestructionAction +import ru.dbotthepony.kstarbound.defs.animation.ParticleLayer +import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory +import ru.dbotthepony.kstarbound.util.SBPattern +import ru.dbotthepony.kvector.vector.Color +import ru.dbotthepony.kvector.vector.ndouble.Vector2d +import ru.dbotthepony.kvector.vector.ndouble.Vector4d + +@JsonFactory +data class ParticleEmitter( + val enabled: Boolean = true, + val emissionRate: Double = 1.0, + val count: Int = 1, + val transformationGroups: ImmutableList = ImmutableList.of(), + val particles: ImmutableList, + + override val offset: Vector2d? = null, + override val position: Vector2d? = null, + override val offsetRegion: Vector4d? = null, + override val initialVelocity: Vector2d? = null, + override val finalVelocity: Vector2d? = null, + override val approach: Vector2d? = null, + override val angularVelocity: Double? = null, + override val destructionAction: DestructionAction? = null, + override val destructionTime: Double? = null, + override val fade: Double? = null, + override val size: Double? = null, + override val layer: ParticleLayer? = null, + override val timeToLive: Double? = null, + override val variance: IParticleVariance? = null, + override val color: Color? = null, + override val text: SBPattern? = null, +) : IParticleConfig diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleVariance.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleVariance.kt new file mode 100644 index 00000000..cb8651f0 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/particle/ParticleVariance.kt @@ -0,0 +1,18 @@ +package ru.dbotthepony.kstarbound.defs.particle + +import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory +import ru.dbotthepony.kvector.vector.Color +import ru.dbotthepony.kvector.vector.ndouble.Vector2d +import ru.dbotthepony.kvector.vector.ndouble.Vector4d + +@JsonFactory +data class ParticleVariance( + override val offset: Vector2d? = null, + override val position: Vector2d? = null, + override val offsetRegion: Vector4d? = null, + override val initialVelocity: Vector2d? = null, + override val approach: Vector2d? = null, + override val angularVelocity: Double? = null, + override val size: Double? = null, + override val color: Color? = null, +) : IParticleVariance diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/EitherTypeAdapter.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/EitherTypeAdapter.kt new file mode 100644 index 00000000..dd34ffae --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/EitherTypeAdapter.kt @@ -0,0 +1,57 @@ +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.JsonToken +import com.google.gson.stream.JsonWriter +import ru.dbotthepony.kstarbound.util.Either +import java.lang.reflect.ParameterizedType + +/** + * При объявлении [Either] для (де)сериализации *НЕОБХОДИМО* объявить + * такое *левое* свойство, которое имеет [TypeAdapter] который не "засоряет" JsonReader + */ +object EitherTypeAdapter : TypeAdapterFactory { + override fun create(gson: Gson, type: TypeToken): TypeAdapter? { + if (type.rawType == Either::class.java) { + val params = type.type as? ParameterizedType ?: return null + val (left, right) = params.actualTypeArguments + + return object : TypeAdapter>() { + private val leftAdapter = gson.getAdapter(TypeToken.get(left)) as TypeAdapter + private val rightAdapter = gson.getAdapter(TypeToken.get(right)) as TypeAdapter + + override fun write(out: JsonWriter, value: Either?) { + if (value == null) + out.nullValue() + else + value.consume({ leftAdapter.write(out, it) }, { rightAdapter.write(out, it) }) + } + + override fun read(`in`: JsonReader): Either? { + if (`in`.peek() == JsonToken.NULL) + return null + + return try { + Either.left(leftAdapter.read(`in`)) + } catch(leftError: Throwable) { + try { + Either.right(rightAdapter.read(`in`)) + } catch(rightError: Throwable) { + val error = JsonSyntaxException("Can't read Either of values (left is $left, right is $right)") + error.addSuppressed(leftError) + error.addSuppressed(rightError) + throw error + } + } + } + } as TypeAdapter + } + + return null + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/AssetPathStack.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/AssetPathStack.kt index 82f39883..63ece0bc 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/AssetPathStack.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/AssetPathStack.kt @@ -30,6 +30,8 @@ class AssetPathStack(private val interner: Interner? = null) { inline operator fun invoke(path: String, block: (String) -> T) = block(path, block) private fun remap(a: String, b: String): String { + if (b.isEmpty()) + return a if (b[0] == '/') return b diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/ChainedProperty.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/ChainedProperty.kt new file mode 100644 index 00000000..2838036f --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/ChainedProperty.kt @@ -0,0 +1,25 @@ +package ru.dbotthepony.kstarbound.util + +import com.google.common.collect.ImmutableList +import java.util.* +import java.util.stream.Stream +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +class ChainedProperty(private val getter: (R) -> T?, private val receivers: ImmutableList) : ReadOnlyProperty { + constructor(getter: (R) -> T?, receivers: Stream) : this(getter, receivers.collect(ImmutableList.toImmutableList())) + constructor(getter: (R) -> T?, receivers: Array) : this(getter, ImmutableList.copyOf(receivers)) + constructor(getter: (R) -> T?, receivers: List) : this(getter, ImmutableList.copyOf(receivers)) + + override fun getValue(thisRef: Any?, property: KProperty<*>): T? { + for (receiver in receivers) { + val value = getter.invoke(receiver) + + if (value != null) { + return value + } + } + + return null + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/Either.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/Either.kt new file mode 100644 index 00000000..8f735733 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/Either.kt @@ -0,0 +1,34 @@ +package ru.dbotthepony.kstarbound.util + +import ru.dbotthepony.kstarbound.io.json.EitherTypeAdapter + +/** + * Представляет собой контейнер с "или тот или другой" значениями + * + * JSON адаптер реализуется через [EitherTypeAdapter] + */ +data class Either(val left: L?, val right: R?) { + init { + require(left != null || right != null) { "Both inputs are null" } + require(!(left != null && right != null)) { "Both inputs are not null" } + } + + inline fun consume(left: (L) -> Unit, right: (R) -> Unit) { + if (this.left != null) + left.invoke(this.left) + else + right.invoke(this.right!!) + } + + companion object { + @JvmStatic + fun left(value: L): Either { + return Either(left = value ?: throw NullPointerException("Left is null"), right = null) + } + + @JvmStatic + fun right(value: R): Either { + return Either(left = null, right = value ?: throw NullPointerException("Right is null")) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/SBPattern.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/SBPattern.kt index 4304cb25..217be2ad 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/SBPattern.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/SBPattern.kt @@ -126,7 +126,11 @@ class SBPattern private constructor( val open = raw.indexOf('<', startIndex = i) if (open == -1) { - pieces.add(Piece(contents = raw.substring(i))) + if (i == 0) + pieces.add(Piece(contents = raw)) + else + pieces.add(Piece(contents = raw.substring(i))) + break } else { val closing = raw.indexOf('>', startIndex = open + 1)