From 0c60dd6a02863f32b1b08906e0521a9b64f66595 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 28 Mar 2024 21:43:47 +0700 Subject: [PATCH] Make worlds be capable of loading from disk, and capable of sending to legacy clients --- .../kotlin/ru/dbotthepony/kstarbound/Main.kt | 2 +- .../ru/dbotthepony/kstarbound/Starbound.kt | 52 +++++-- .../kstarbound/defs/IThingWithDescription.kt | 8 + .../kstarbound/defs/PerlinNoiseParameters.kt | 1 + .../kstarbound/defs/world/Biome.kt | 16 +- .../kstarbound/defs/world/BiomeDefinition.kt | 13 +- .../kstarbound/defs/world/BiomePlaceables.kt | 66 ++++---- .../defs/world/BiomePlaceablesDefinition.kt | 23 +-- .../world/BiomePlacementDistributionType.kt | 12 +- .../kstarbound/defs/world/BushVariant.kt | 7 +- .../defs/world/CelestialParameters.kt | 2 + .../kstarbound/defs/world/GrassVariant.kt | 6 +- .../kstarbound/defs/world/Parallax.kt | 3 +- .../defs/world/TerrestrialWorldParameters.kt | 32 ++-- .../kstarbound/defs/world/TreeVariant.kt | 15 +- .../defs/world/VisitableWorldParameters.kt | 6 +- .../kstarbound/defs/world/WorldLayout.kt | 29 +++- .../kstarbound/defs/world/WorldStructure.kt | 10 +- .../kstarbound/defs/world/WorldTemplate.kt | 10 +- .../kstarbound/json/NativeLegacy.kt | 146 ++++++++++++++++++ .../kstarbound/json/builder/FactoryAdapter.kt | 22 ++- .../syncher/FloatingNetworkedElement.kt | 4 + .../kstarbound/server/ServerConnection.kt | 9 +- .../kstarbound/server/world/ServerWorld.kt | 15 +- .../kstarbound/util/AssetPathStack.kt | 6 +- .../util/random/AbstractPerlinNoise.kt | 6 +- .../kstarbound/util/random/EmptyNoise.kt | 21 +++ 27 files changed, 400 insertions(+), 142 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/json/NativeLegacy.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/util/random/EmptyNoise.kt diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 692d96ed..95e0e378 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -105,7 +105,7 @@ fun main() { Starbound.mailboxInitialized.submit { val server = IntegratedStarboundServer(File("./")) val world = ServerWorld.load(server, LegacyWorldStorage.file(db)).get() - //world.thread.start() + world.thread.start() //ply = PlayerEntity(client.world!!) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index 6da62d58..3cdedb3d 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -40,6 +40,7 @@ import ru.dbotthepony.kstarbound.defs.world.VisitableWorldParametersType import ru.dbotthepony.kstarbound.defs.world.BiomePlaceables import ru.dbotthepony.kstarbound.defs.world.BiomePlacementDistributionType import ru.dbotthepony.kstarbound.defs.world.BiomePlacementItemType +import ru.dbotthepony.kstarbound.defs.world.WorldLayout import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType import ru.dbotthepony.kstarbound.io.* import ru.dbotthepony.kstarbound.json.factory.MapsTypeAdapterFactory @@ -58,6 +59,7 @@ import ru.dbotthepony.kstarbound.json.factory.SingletonTypeAdapterFactory import ru.dbotthepony.kstarbound.math.* import ru.dbotthepony.kstarbound.server.world.UniverseChunk import ru.dbotthepony.kstarbound.item.ItemStack +import ru.dbotthepony.kstarbound.json.NativeLegacy import ru.dbotthepony.kstarbound.util.Directives import ru.dbotthepony.kstarbound.util.ExceptionLogger import ru.dbotthepony.kstarbound.util.SBPattern @@ -155,8 +157,30 @@ object Starbound : ISBFileLocator { @JvmField val STRINGS: Interner = interner(5) + // immeasurably lazy and fragile solution + var IS_WRITING_LEGACY_JSON: Boolean by ThreadLocal.withInitial { false } + private set + + fun writeLegacyJson(data: Any): JsonElement { + try { + IS_WRITING_LEGACY_JSON = true + return gson.toJsonTree(data) + } finally { + IS_WRITING_LEGACY_JSON = false + } + } + + fun writeLegacyJson(block: () -> T): T { + try { + IS_WRITING_LEGACY_JSON = true + return block.invoke() + } finally { + IS_WRITING_LEGACY_JSON = false + } + } + val gson: Gson = with(GsonBuilder()) { - serializeNulls() + // serializeNulls() setDateFormat(DateFormat.LONG) setFieldNamingPolicy(FieldNamingPolicy.IDENTITY) setPrettyPrinting() @@ -215,18 +239,20 @@ object Starbound : ISBFileLocator { registerTypeAdapter(ObjectDefinition::Adapter) registerTypeAdapter(StatModifier::Adapter) + registerTypeAdapterFactory(NativeLegacy.Companion) + // математические классы - registerTypeAdapter(AABBTypeAdapter) - registerTypeAdapter(AABBiTypeAdapter) - registerTypeAdapter(Vector2dTypeAdapter) - registerTypeAdapter(Vector2fTypeAdapter) - registerTypeAdapter(Vector2iTypeAdapter) - registerTypeAdapter(Vector3dTypeAdapter) - registerTypeAdapter(Vector3fTypeAdapter) - registerTypeAdapter(Vector3iTypeAdapter) - registerTypeAdapter(Vector4iTypeAdapter) - registerTypeAdapter(Vector4dTypeAdapter) - registerTypeAdapter(Vector4fTypeAdapter) + registerTypeAdapter(AABBTypeAdapter.nullSafe()) + registerTypeAdapter(AABBiTypeAdapter.nullSafe()) + registerTypeAdapter(Vector2dTypeAdapter.nullSafe()) + registerTypeAdapter(Vector2fTypeAdapter.nullSafe()) + registerTypeAdapter(Vector2iTypeAdapter.nullSafe()) + registerTypeAdapter(Vector3dTypeAdapter.nullSafe()) + registerTypeAdapter(Vector3fTypeAdapter.nullSafe()) + registerTypeAdapter(Vector3iTypeAdapter.nullSafe()) + registerTypeAdapter(Vector4iTypeAdapter.nullSafe()) + registerTypeAdapter(Vector4dTypeAdapter.nullSafe()) + registerTypeAdapter(Vector4fTypeAdapter.nullSafe()) registerTypeAdapterFactory(Line2d.Companion) registerTypeAdapterFactory(UniversePos.Companion) registerTypeAdapterFactory(AbstractPerlinNoise.Companion) @@ -269,7 +295,6 @@ object Starbound : ISBFileLocator { registerTypeAdapter(CelestialParameters::Adapter) registerTypeAdapter(Particle::Adapter) - registerTypeAdapterFactory(BiomePlacementDistributionType.DATA_ADAPTER) registerTypeAdapterFactory(BiomePlacementDistributionType.DEFINITION_ADAPTER) registerTypeAdapterFactory(BiomePlacementItemType.DATA_ADAPTER) registerTypeAdapterFactory(BiomePlacementItemType.DEFINITION_ADAPTER) @@ -278,6 +303,7 @@ object Starbound : ISBFileLocator { // register companion first, so it has lesser priority than dispatching adapter registerTypeAdapterFactory(VisitableWorldParametersType.Companion) registerTypeAdapterFactory(VisitableWorldParametersType.ADAPTER) + registerTypeAdapter(WorldLayout.Companion) Registries.registerAdapters(this) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/IThingWithDescription.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/IThingWithDescription.kt index 28e8e5fe..61d73089 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/IThingWithDescription.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/IThingWithDescription.kt @@ -10,6 +10,7 @@ import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonWriter import ru.dbotthepony.kommons.gson.consumeNull +import ru.dbotthepony.kommons.guava.immutableMap import ru.dbotthepony.kstarbound.json.builder.JsonImplementation @JsonImplementation(ThingDescription::class) @@ -84,6 +85,13 @@ data class ThingDescription( val EMPTY = ThingDescription() } + fun toMap(): ImmutableMap { + return immutableMap { + putAll(racialDescription) + put("description", description) + } + } + fun fixDescription(newDescription: String): ThingDescription { return copy( shortdescription = if (shortdescription == "...") newDescription else shortdescription, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/PerlinNoiseParameters.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/PerlinNoiseParameters.kt index 1d293c59..a3d007d5 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/PerlinNoiseParameters.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/PerlinNoiseParameters.kt @@ -23,6 +23,7 @@ data class PerlinNoiseParameters( } enum class Type(override val jsonName: String) : IStringSerializable { + UNITIALIZED("uninitialized"), PERLIN("perlin"), BILLOW("billow"), RIDGED_MULTI("ridgedMulti"); diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/Biome.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/Biome.kt index d227596f..8067d142 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/Biome.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/Biome.kt @@ -1,20 +1,18 @@ package ru.dbotthepony.kstarbound.defs.world import com.google.common.collect.ImmutableList -import ru.dbotthepony.kstarbound.Registry -import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier -import ru.dbotthepony.kstarbound.defs.tile.TileDefinition -import ru.dbotthepony.kstarbound.defs.world.AmbientNoisesDefinition -import ru.dbotthepony.kstarbound.defs.world.BiomePlaceables -import ru.dbotthepony.kstarbound.defs.world.Parallax +import ru.dbotthepony.kstarbound.json.NativeLegacy +import ru.dbotthepony.kstarbound.json.builder.JsonFactory +@JsonFactory data class Biome( val hueShift: Double = 0.0, + val materialHueShift: Int = 0, val baseName: String, val description: String, - val mainBlock: Registry.Entry? = null, - val subBlocks: ImmutableList> = ImmutableList.of(), - val ores: ImmutableList, Double>> = ImmutableList.of(), + val mainBlock: NativeLegacy.Tile, + val subBlocks: ImmutableList = ImmutableList.of(), + val ores: ImmutableList> = ImmutableList.of(), val musicTrack: AmbientNoisesDefinition? = null, val ambientNoises: AmbientNoisesDefinition? = null, val surfacePlaceables: BiomePlaceables = BiomePlaceables(), diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomeDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomeDefinition.kt index 6c8f8370..04668c49 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomeDefinition.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomeDefinition.kt @@ -11,9 +11,11 @@ import ru.dbotthepony.kstarbound.defs.AssetReference import ru.dbotthepony.kstarbound.defs.JsonConfigFunction import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.TileDefinition +import ru.dbotthepony.kstarbound.json.NativeLegacy import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.json.pairListAdapter import ru.dbotthepony.kstarbound.util.random.random +import ru.dbotthepony.kstarbound.world.positiveModulo import java.util.random.RandomGenerator @JsonFactory @@ -63,9 +65,10 @@ data class BiomeDefinition( baseName = name, description = description, hueShift = hueShift, + materialHueShift = ((positiveModulo(hueShift, 360.0) / 360.0) * 255.0).toInt(), - mainBlock = mainBlock?.entry, - subBlocks = subBlocks?.stream()?.map { it.entry }?.filterNotNull()?.collect(ImmutableList.toImmutableList()) ?: ImmutableList.of(), + mainBlock = NativeLegacy.Tile(mainBlock?.entry), + subBlocks = subBlocks?.stream()?.map { NativeLegacy.Tile(it.entry) }?.filterNotNull()?.collect(ImmutableList.toImmutableList()) ?: ImmutableList.of(), musicTrack = musicTrack, ambientNoises = ambientNoises, @@ -77,10 +80,10 @@ data class BiomeDefinition( ores = (ores?.value?.evaluate(threatLevel, oresAdapter)?.map { it.stream() .filter { it.second > 0.0 } - .map { Registries.tileModifiers[it.first] to it.second } - .filter { it.first != null } + .map { NativeLegacy.TileMod(Registries.tileModifiers.ref(it.first)) to it.second } + .filter { it.first.native.isPresent } .collect(ImmutableList.toImmutableList()) - }?.orElse(ImmutableList.of()) ?: ImmutableList.of()) as ImmutableList, Double>> + }?.orElse(ImmutableList.of()) ?: ImmutableList.of()) ) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceables.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceables.kt index 34ae2f4e..5faa2179 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceables.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceables.kt @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSet import com.google.gson.Gson import com.google.gson.JsonArray import com.google.gson.JsonElement +import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import com.google.gson.JsonSyntaxException import com.google.gson.TypeAdapter @@ -19,7 +20,9 @@ import ru.dbotthepony.kommons.gson.value import ru.dbotthepony.kstarbound.Registry import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.collect.WeightedList +import ru.dbotthepony.kstarbound.defs.PerlinNoiseParameters import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier +import ru.dbotthepony.kstarbound.json.NativeLegacy import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.json.builder.JsonFlat import ru.dbotthepony.kstarbound.json.listAdapter @@ -28,14 +31,14 @@ import java.util.stream.Stream @JsonFactory data class BiomePlaceables( - val grassMod: Registry.Entry? = null, - val ceilingGrassMod: Registry.Entry? = null, + val grassMod: NativeLegacy.TileMod = NativeLegacy.TileMod(null as Registry.Ref?), + val ceilingGrassMod: NativeLegacy.TileMod = NativeLegacy.TileMod(null as Registry.Ref?), val grassModDensity: Double = 0.0, val ceilingGrassModDensity: Double = 0.0, - val items: ImmutableList = ImmutableList.of(), + val itemDistributions: ImmutableList = ImmutableList.of(), ) { fun firstTreeVariant(): TreeVariant? { - return items.stream() + return itemDistributions.stream() .flatMap { it.data.itemStream() } .map { it as? Tree } .filterNotNull() @@ -107,7 +110,7 @@ data class BiomePlaceables( )) "grass" -> Grass(grassVariant.read(`in`)) "bush" -> Bush(bushVariant.read(`in`)) - "tree" -> Tree(trees.read(`in`)) + "treePair" -> Tree(trees.read(`in`)) "objectPool" -> Object(objects.read(`in`)) else -> throw JsonSyntaxException("Unknown biome placement item $type") } @@ -187,39 +190,28 @@ data class BiomePlaceables( } } - // -------- DISTRIBUTION - abstract class DistributionData { - abstract val type: BiomePlacementDistributionType - - abstract fun itemStream(): Stream - } - + // ТЕПЕРЬ УГАДАЙ + // МОЙ + // ПОЛ. + // БЛЯДЬ @JsonFactory - data class RandomDistribution( - val blockSeed: Long, - val randomItems: ImmutableList, - ) : DistributionData() { - override val type: BiomePlacementDistributionType - get() = BiomePlacementDistributionType.RANDOM - - override fun itemStream(): Stream { - return randomItems.stream() - } - } - - @JsonFactory - data class PeriodicDistribution( - val modulus: Int, - val modulusOffset: Int, - val densityFunction: AbstractPerlinNoise, - val modulusDistortion: AbstractPerlinNoise, - val weightedItems: ImmutableList>, - ) : DistributionData() { - override val type: BiomePlacementDistributionType - get() = BiomePlacementDistributionType.PERIODIC - - override fun itemStream(): Stream { - return weightedItems.stream().map { it.first } + data class DistributionData( + val distribution: BiomePlacementDistributionType, + val blockSeed: Long = 0L, + val blockProbability: Double = 0.0, + val randomItems: ImmutableList = ImmutableList.of(), + val modulus: Int = 0, + val modulusOffset: Int = 0, + val densityFunction: AbstractPerlinNoise = AbstractPerlinNoise.of(PerlinNoiseParameters()), + val modulusDistortion: AbstractPerlinNoise = AbstractPerlinNoise.of(PerlinNoiseParameters()), + val weightedItems: ImmutableList> = ImmutableList.of(), + ) { + fun itemStream(): Stream { + if (distribution == BiomePlacementDistributionType.RANDOM) { + return randomItems.stream() + } else { + return weightedItems.stream().map { it.first } + } } } } \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceablesDefinition.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceablesDefinition.kt index 34b18d58..18859c76 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceablesDefinition.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlaceablesDefinition.kt @@ -11,6 +11,8 @@ import ru.dbotthepony.kstarbound.collect.WeightedList import ru.dbotthepony.kstarbound.defs.AssetReference import ru.dbotthepony.kstarbound.defs.PerlinNoiseParameters import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier +import ru.dbotthepony.kstarbound.json.NativeLegacy +import ru.dbotthepony.kstarbound.json.builder.IStringSerializable import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.json.builder.JsonFlat import ru.dbotthepony.kstarbound.json.builder.JsonSingleton @@ -27,8 +29,8 @@ data class BiomePlaceablesDefinition( val ceilingGrassModDensity: Double = 0.0, val items: ImmutableList = ImmutableList.of(), ) { - enum class Placement { - FLOOR, CEILING, BACKGROUND, OCEAN; + enum class Placement(override val jsonName: String) : IStringSerializable { + FLOOR("floor"), CEILING("ceiling"), BACKGROUND("background"), OCEAN("ocean"); } // ----------- ITEMS @@ -251,8 +253,8 @@ data class BiomePlaceablesDefinition( abstract fun create(self: DistributionItem, biome: BiomeDefinition.CreationParams): BiomePlaceables.DistributionData } - @JsonSingleton - object RandomDistribution : DistributionData() { + @JsonFactory + data class RandomDistribution(val blockProbability: Double) : DistributionData() { override val type: BiomePlacementDistributionType get() = BiomePlacementDistributionType.RANDOM @@ -260,8 +262,10 @@ data class BiomePlaceablesDefinition( self: DistributionItem, biome: BiomeDefinition.CreationParams ): BiomePlaceables.DistributionData { - return BiomePlaceables.RandomDistribution( + return BiomePlaceables.DistributionData( + distribution = BiomePlacementDistributionType.RANDOM, blockSeed = biome.random.nextLong(), + blockProbability = blockProbability, randomItems = IntStream.range(0, self.variants) .mapToObj { self.data.create(biome) } .collect(ImmutableList.toImmutableList()) @@ -291,7 +295,8 @@ data class BiomePlaceablesDefinition( ): BiomePlaceables.DistributionData { val modulusOffset = if (modulus == 0) 0 else biome.random.nextInt(-modulus, modulus) - return BiomePlaceables.PeriodicDistribution( + return BiomePlaceables.DistributionData( + distribution = BiomePlacementDistributionType.PERIODIC, modulus = modulus, modulusOffset = modulusOffset, @@ -350,11 +355,11 @@ data class BiomePlaceablesDefinition( fun create(biome: BiomeDefinition.CreationParams): BiomePlaceables { return BiomePlaceables( - grassMod = grassMod.random(biome.random) { null }?.entry, - ceilingGrassMod = ceilingGrassMod.random(biome.random) { null }?.entry, + grassMod = NativeLegacy.TileMod(grassMod.random(biome.random) { null }?.entry), + ceilingGrassMod = NativeLegacy.TileMod(ceilingGrassMod.random(biome.random) { null }?.entry), grassModDensity = grassModDensity, ceilingGrassModDensity = ceilingGrassModDensity, - items = items.stream() + itemDistributions = items.stream() .map { it.create(biome) } .collect(ImmutableList.toImmutableList()) ) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlacementDistributionType.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlacementDistributionType.kt index 697f7c14..792df8da 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlacementDistributionType.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BiomePlacementDistributionType.kt @@ -7,16 +7,9 @@ import ru.dbotthepony.kstarbound.json.builder.IStringSerializable enum class BiomePlacementDistributionType( override val jsonName: String, val def: TypeToken, - val data: TypeToken, ) : IStringSerializable { - RANDOM("random", - TypeToken.get(BiomePlaceablesDefinition.RandomDistribution::class.java), - TypeToken.get(BiomePlaceables.RandomDistribution::class.java) - ), - PERIODIC("periodic", - TypeToken.get(BiomePlaceablesDefinition.PeriodicDistribution::class.java), - TypeToken.get(BiomePlaceables.PeriodicDistribution::class.java) - ); + RANDOM("random",TypeToken.get(BiomePlaceablesDefinition.RandomDistribution::class.java)), + PERIODIC("periodic", TypeToken.get(BiomePlaceablesDefinition.PeriodicDistribution::class.java)); override fun match(name: String): Boolean { return name.lowercase() == jsonName @@ -24,6 +17,5 @@ enum class BiomePlacementDistributionType( companion object { val DEFINITION_ADAPTER = DispatchingAdapter("type", { type }, { def }, entries) - val DATA_ADAPTER = DispatchingAdapter("type", { type }, { data }, entries) } } \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BushVariant.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BushVariant.kt index a12c8468..3aeb3391 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BushVariant.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/BushVariant.kt @@ -26,14 +26,13 @@ class BushVariant( val baseHueShift: Double, val modHueShift: Double, - @JsonFlat - val descriptions: ThingDescription, + val descriptions: ImmutableMap = ImmutableMap.of(), val ceiling: Boolean, val ephemeral: Boolean, val tileDamageParameters: TileDamageConfig, ) { - @JsonFactory + @JsonFactory(asList = true) data class Shape(val image: String, val mods: ImmutableList) @JsonFactory @@ -64,7 +63,7 @@ class BushVariant( directory = data.file?.computeDirectory() ?: "/", modHueShift = modHueShift, ceiling = data.value.ceiling, - descriptions = data.value.descriptions.fixDescription("${data.key} with $modName"), + descriptions = data.value.descriptions.fixDescription("${data.key} with $modName").toMap(), ephemeral = data.value.ephemeral, tileDamageParameters = (data.value.damageTable?.value ?: GlobalDefaults.bushDamage).copy(totalHealth = data.value.health), modName = modName, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/CelestialParameters.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/CelestialParameters.kt index 269e0ff5..458c1cdf 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/CelestialParameters.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/CelestialParameters.kt @@ -11,7 +11,9 @@ import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonWriter import ru.dbotthepony.kommons.gson.consumeNull +import ru.dbotthepony.kommons.gson.set import ru.dbotthepony.kommons.gson.value +import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.util.random.random import ru.dbotthepony.kstarbound.world.UniversePos diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/GrassVariant.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/GrassVariant.kt index 864f4bc7..f968d776 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/GrassVariant.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/GrassVariant.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.defs.world +import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableSet import com.google.gson.JsonElement import ru.dbotthepony.kstarbound.GlobalDefaults @@ -17,8 +18,7 @@ data class GrassVariant( val directory: String, val images: ImmutableSet = ImmutableSet.of(), val hueShift: Double, - @JsonFlat - val descriptions: ThingDescription, + val descriptions: ImmutableMap = ImmutableMap.of(), val ceiling: Boolean, val ephemeral: Boolean, val tileDamageParameters: TileDamageConfig, @@ -53,7 +53,7 @@ data class GrassVariant( ceiling = data.value.ceiling, ephemeral = data.value.ephemeral, hueShift = hueShift, - descriptions = data.value.descriptions.fixDescription(data.value.name), + descriptions = data.value.descriptions.fixDescription(data.value.name).toMap(), tileDamageParameters = (data.value.damageTable?.value ?: GlobalDefaults.grassDamage).copy(totalHealth = data.value.health) ) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/Parallax.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/Parallax.kt index 9c220453..4c3f5f34 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/Parallax.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/Parallax.kt @@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.defs.world import com.google.common.collect.ImmutableList import ru.dbotthepony.kommons.collect.filterNotNull +import ru.dbotthepony.kommons.gson.get import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.util.Either import ru.dbotthepony.kommons.vector.Vector2d @@ -80,7 +81,7 @@ class Parallax( if (isFoliage) { if (treeVariant == null) return null - if (!treeVariant.foliageSettings.parallaxFoliage) return null + if (!treeVariant.foliageSettings.asJsonObject.get("parallaxFoliage", false)) return null texPath = "${treeVariant.foliageDirectory}/parallax/${kind.replace("foliage/", "")}/" } else if (isStem) { if (treeVariant == null) return null diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/TerrestrialWorldParameters.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/TerrestrialWorldParameters.kt index 595e4f1e..bc346df0 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/TerrestrialWorldParameters.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/TerrestrialWorldParameters.kt @@ -1,10 +1,8 @@ package ru.dbotthepony.kstarbound.defs.world import com.google.common.collect.ImmutableList -import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableSet import com.google.gson.JsonArray -import com.google.gson.JsonNull import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import com.google.gson.TypeAdapter @@ -24,13 +22,11 @@ import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.collect.WeightedList import ru.dbotthepony.kstarbound.defs.JsonDriven import ru.dbotthepony.kstarbound.defs.PerlinNoiseParameters -import ru.dbotthepony.kstarbound.fromJson import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.json.pairAdapter import ru.dbotthepony.kstarbound.json.stream import ru.dbotthepony.kstarbound.util.binnedChoice import ru.dbotthepony.kstarbound.util.random.AbstractPerlinNoise -import ru.dbotthepony.kstarbound.util.random.PerlinNoise import ru.dbotthepony.kstarbound.util.random.nextRange import ru.dbotthepony.kstarbound.util.random.random import java.util.random.RandomGenerator @@ -100,13 +96,13 @@ class TerrestrialWorldParameters : VisitableWorldParameters() { val read = Starbound.gson.fromJson(data, StoreData::class.java) primaryBiome = read.primaryBiome - primarySurfaceLiquid = read.primarySurfaceLiquid + surfaceLiquid = read.surfaceLiquid sizeName = read.sizeName hueShift = read.hueShift skyColoring = read.skyColoring dayLength = read.dayLength - blockNoiseConfig = read.blockNoiseConfig - blendNoiseConfig = read.blendNoiseConfig + blockNoiseConfig = read.blockNoise + blendNoiseConfig = read.blendNoise blendSize = read.blendSize spaceLayer = read.spaceLayer atmosphereLayer = read.atmosphereLayer @@ -124,23 +120,23 @@ class TerrestrialWorldParameters : VisitableWorldParameters() { // original engine operate on liquids solely with IDs // and we also need to network this json to legacy clients. // what a shame :JC:. - if (this.primarySurfaceLiquid == null) { + if (this.surfaceLiquid == null) { primarySurfaceLiquid = if (isLegacy) Either.left(0) else null } else if (isLegacy) { - primarySurfaceLiquid = this.primarySurfaceLiquid!!.map({ it }, { Registries.liquid.get(it)!!.id })?.let { Either.left(it) } + primarySurfaceLiquid = this.surfaceLiquid!!.map({ it }, { Registries.liquid.get(it)!!.id })?.let { Either.left(it) } } else { - primarySurfaceLiquid = this.primarySurfaceLiquid + primarySurfaceLiquid = this.surfaceLiquid } val store = StoreData( primaryBiome = primaryBiome, - primarySurfaceLiquid = primarySurfaceLiquid, + surfaceLiquid = primarySurfaceLiquid, sizeName = sizeName, hueShift = hueShift, skyColoring = skyColoring, dayLength = dayLength, - blockNoiseConfig = blockNoiseConfig, - blendNoiseConfig = blendNoiseConfig, + blockNoise = blockNoiseConfig, + blendNoise = blendNoiseConfig, blendSize = blendSize, spaceLayer = spaceLayer, atmosphereLayer = atmosphereLayer, @@ -158,13 +154,13 @@ class TerrestrialWorldParameters : VisitableWorldParameters() { @JsonFactory data class StoreData( val primaryBiome: String, - val primarySurfaceLiquid: Either? = null, + val surfaceLiquid: Either? = null, val sizeName: String, val hueShift: Double, val skyColoring: SkyColoring, val dayLength: Double, - val blockNoiseConfig: BlockNoiseConfig? = null, - val blendNoiseConfig: PerlinNoiseParameters? = null, + val blockNoise: BlockNoiseConfig? = null, + val blendNoise: PerlinNoiseParameters? = null, val blendSize: Double, val spaceLayer: Layer, val atmosphereLayer: Layer, @@ -176,7 +172,7 @@ class TerrestrialWorldParameters : VisitableWorldParameters() { var primaryBiome: String by Delegates.notNull() private set - var primarySurfaceLiquid: Either? = null + var surfaceLiquid: Either? = null private set var sizeName: String by Delegates.notNull() private set @@ -454,7 +450,7 @@ class TerrestrialWorldParameters : VisitableWorldParameters() { parameters.sizeName = sizeName parameters.hueShift = primaryBiome.value.hueShift(random) - parameters.primarySurfaceLiquid = surfaceLayer.primaryRegion.oceanLiquid ?: surfaceLayer.primaryRegion.caveLiquid + parameters.surfaceLiquid = surfaceLayer.primaryRegion.oceanLiquid ?: surfaceLayer.primaryRegion.caveLiquid parameters.skyColoring = primaryBiome.value.skyColoring(random) parameters.dayLength = random.nextRange(params.dayLengthRange) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/TreeVariant.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/TreeVariant.kt index 47165105..a82785e4 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/TreeVariant.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/TreeVariant.kt @@ -1,6 +1,8 @@ package ru.dbotthepony.kstarbound.defs.world +import com.google.common.collect.ImmutableMap import com.google.gson.JsonElement +import com.google.gson.JsonNull import com.google.gson.JsonObject import ru.dbotthepony.kstarbound.GlobalDefaults import ru.dbotthepony.kstarbound.Registries @@ -24,11 +26,10 @@ data class TreeVariant( val foliageDirectory: String, // AGAIN. - val foliageSettings: FoliageData, + val foliageSettings: JsonElement, val foliageHueShift: Double, - @JsonFlat - val descriptions: ThingDescription, + val descriptions: ImmutableMap = ImmutableMap.of(), val ceiling: Boolean, val ephemeral: Boolean, @@ -78,11 +79,11 @@ data class TreeVariant( stemHueShift = stemHueShift, ceiling = data.value.ceiling, stemDropConfig = data.value.dropConfig.deepCopy(), - descriptions = data.value.descriptions.fixDescription(data.key), + descriptions = data.value.descriptions.fixDescription(data.key).toMap(), ephemeral = data.value.ephemeral, tileDamageParameters = (data.value.damageTable?.value ?: GlobalDefaults.treeDamage).copy(totalHealth = data.value.health), - foliageSettings = FoliageData("", shape = ""), + foliageSettings = JsonNull.INSTANCE, foliageDropConfig = JsonObject(), foliageName = "", foliageDirectory = "/", @@ -105,11 +106,11 @@ data class TreeVariant( stemHueShift = stemHueShift, ceiling = data.value.ceiling, stemDropConfig = data.value.dropConfig.deepCopy(), - descriptions = data.value.descriptions.fixDescription("${data.key} with ${fdata.key}"), + descriptions = data.value.descriptions.fixDescription("${data.key} with ${fdata.key}").toMap(), ephemeral = data.value.ephemeral, tileDamageParameters = (data.value.damageTable?.value ?: GlobalDefaults.treeDamage).copy(totalHealth = data.value.health), - foliageSettings = fdata.value, + foliageSettings = fdata.json, foliageDropConfig = fdata.value.dropConfig.deepCopy(), foliageName = fdata.key, foliageDirectory = fdata.file?.computeDirectory() ?: "/", diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/VisitableWorldParameters.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/VisitableWorldParameters.kt index 4f59e0e5..305b974c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/VisitableWorldParameters.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/VisitableWorldParameters.kt @@ -51,7 +51,7 @@ enum class VisitableWorldParametersType(override val jsonName: String, val token if (value == null) out.nullValue() else - out.value(value.toJson(false)) + out.value(value.toJson()) } override fun read(`in`: JsonReader): VisitableWorldParameters? { @@ -140,7 +140,7 @@ abstract class VisitableWorldParameters { this.weatherPool = read.weatherPool } - open fun toJson(data: JsonObject, isLegacy: Boolean) { + open fun toJson(data: JsonObject, isLegacy: Boolean = Starbound.IS_WRITING_LEGACY_JSON) { val store = StoreData( threatLevel, typeName, @@ -164,7 +164,7 @@ abstract class VisitableWorldParameters { data["type"] = type.jsonName } - fun toJson(isLegacy: Boolean): JsonObject { + fun toJson(isLegacy: Boolean = Starbound.IS_WRITING_LEGACY_JSON): JsonObject { val data = JsonObject() toJson(data, isLegacy) return data diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldLayout.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldLayout.kt index 80cc254f..1d8c5732 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldLayout.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldLayout.kt @@ -4,12 +4,17 @@ import com.google.common.collect.ImmutableList import com.google.gson.JsonArray import com.google.gson.JsonObject import com.google.gson.JsonPrimitive +import com.google.gson.TypeAdapter import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter import it.unimi.dsi.fastutil.doubles.DoubleArrayList import it.unimi.dsi.fastutil.ints.IntArrayList import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import ru.dbotthepony.kommons.gson.JsonArrayCollector +import ru.dbotthepony.kommons.gson.consumeNull import ru.dbotthepony.kommons.gson.set +import ru.dbotthepony.kommons.gson.value import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.util.AABBi import ru.dbotthepony.kommons.util.Either @@ -202,11 +207,11 @@ class WorldLayout { val layers: JsonArray, ) - fun toJson(isLegacy: Boolean): JsonObject { + fun toJson(): JsonObject { return Starbound.gson.toJsonTree(SerializedForm( worldSize, regionBlending, blockNoise, blendNoise, playerStartSearchRegions, biomes.list, terrainSelectors.list, - layers = layers.stream().map { it.toJson(isLegacy) }.collect(JsonArrayCollector) + layers = layers.stream().map { it.toJson(Starbound.IS_WRITING_LEGACY_JSON) }.collect(JsonArrayCollector) )) as JsonObject } @@ -410,4 +415,24 @@ class WorldLayout { biome.parallax?.fadeToSkyColor(skyColoring) } } + + companion object : TypeAdapter() { + private val objects by lazy { Starbound.gson.getAdapter(JsonObject::class.java) } + + override fun write(out: JsonWriter, value: WorldLayout?) { + if (value == null) + out.nullValue() + else + out.value(value.toJson()) + } + + override fun read(`in`: JsonReader): WorldLayout? { + if (`in`.consumeNull()) + return null + + val params = WorldLayout() + params.fromJson(objects.read(`in`)) + return params + } + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldStructure.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldStructure.kt index b87ac0a4..ebaa2633 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldStructure.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldStructure.kt @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableMap import com.google.gson.JsonElement import com.google.gson.JsonNull +import com.google.gson.JsonObject import ru.dbotthepony.kommons.util.AABBi import ru.dbotthepony.kommons.util.Either import ru.dbotthepony.kommons.vector.Vector2d @@ -15,7 +16,7 @@ import ru.dbotthepony.kstarbound.world.Direction1D data class WorldStructure( val region: AABBi = AABBi(Vector2i.ZERO, Vector2i.ZERO), val anchorPosition: Vector2i = Vector2i.ZERO, - val config: JsonElement = JsonNull.INSTANCE, + var config: JsonElement = JsonObject(), val backgroundOverlays: ImmutableList = ImmutableList.of(), val foregroundOverlays: ImmutableList = ImmutableList.of(), val backgroundBlocks: ImmutableList = ImmutableList.of(), @@ -23,6 +24,13 @@ data class WorldStructure( val objects: ImmutableList = ImmutableList.of(), val flaggedBlocks: ImmutableMap> = ImmutableMap.of(), ) { + init { + if (config == JsonNull.INSTANCE) { + // so it is not omitted + config = JsonObject() + } + } + @JsonFactory data class Overlay(val min: Vector2d, val image: String, val fullbright: Boolean) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldTemplate.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldTemplate.kt index c59274f3..aa5536de 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldTemplate.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/world/WorldTemplate.kt @@ -51,17 +51,17 @@ class WorldTemplate(val geometry: WorldGeometry) { val skyParameters: SkyParameters = SkyParameters(), val seed: Long = 0L, val size: Either, - val regionData: JsonElement = JsonNull.INSTANCE, + val regionData: WorldLayout? = null, //val customTerrainRegions: ) - fun toJson(isLegacy: Boolean): JsonObject { + fun toJson(): JsonObject { val data = Starbound.gson.toJsonTree(SerializedForm( celestialParameters, worldParameters, skyParameters, seed, - if (isLegacy) Either.right(geometry.size) else Either.left(geometry), + if (Starbound.IS_WRITING_LEGACY_JSON) Either.right(geometry.size) else Either.left(geometry), + worldLayout )) as JsonObject - data["regionData"] = worldLayout?.toJson(isLegacy) ?: JsonNull.INSTANCE return data } @@ -91,7 +91,7 @@ class WorldTemplate(val geometry: WorldGeometry) { template.worldParameters = load.worldParameters template.skyParameters = load.skyParameters template.seed = load.seed - template.worldLayout = load.regionData.let { if (it is JsonObject) WorldLayout().fromJson(it) else null } + template.worldLayout = load.regionData template.determineName() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/json/NativeLegacy.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/json/NativeLegacy.kt new file mode 100644 index 00000000..5455a0e7 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/json/NativeLegacy.kt @@ -0,0 +1,146 @@ +package ru.dbotthepony.kstarbound.json + +import com.google.gson.Gson +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.kommons.gson.consumeNull +import ru.dbotthepony.kommons.util.Either +import ru.dbotthepony.kommons.util.KOptional +import ru.dbotthepony.kstarbound.Registries +import ru.dbotthepony.kstarbound.Registry +import ru.dbotthepony.kstarbound.Starbound +import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials +import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier +import ru.dbotthepony.kstarbound.defs.tile.TileDefinition +import java.lang.reflect.Constructor +import java.lang.reflect.ParameterizedType + +/** + * Creates single point of reference for both native json and legacy json + * values + */ +abstract class NativeLegacy { + protected abstract fun computeLegacy(value: NATIVE): LEGACY + protected abstract fun computeNative(value: LEGACY): NATIVE + + protected var nativeValue: KOptional = KOptional() + protected var legacyValue: KOptional = KOptional() + + val legacy: LEGACY get() { + if (legacyValue.isEmpty) { + val compute = computeLegacy(nativeValue.value) + legacyValue = KOptional(compute) + return compute + } + + return legacyValue.value + } + + val native: NATIVE get() { + if (nativeValue.isEmpty) { + val compute = computeNative(legacyValue.value) + nativeValue = KOptional(compute) + return compute + } + + return nativeValue.value + } + + class Tile() : NativeLegacy, Int?>() { + constructor(tile: Registry.Entry?) : this() { + if (tile == null) + legacyValue = KOptional(65535) + else + nativeValue = KOptional(tile.ref) + } + + constructor(tile: Registry.Ref?) : this() { + if (tile == null) + legacyValue = KOptional(65535) + else + nativeValue = KOptional(tile) + } + + override fun computeLegacy(value: Registry.Ref): Int { + return value.value?.materialId ?: 65535 + } + + override fun computeNative(value: Int?): Registry.Ref { + value ?: return Registries.tiles.emptyRef + return Registries.tiles.ref(value) + } + } + + class TileMod() : NativeLegacy, Int?>() { + constructor(tile: Registry.Entry?) : this() { + if (tile == null) + legacyValue = KOptional(65535) + else + nativeValue = KOptional(tile.ref) + } + + constructor(tile: Registry.Ref?) : this() { + if (tile == null) + legacyValue = KOptional(65535) + else + nativeValue = KOptional(tile) + } + + override fun computeLegacy(value: Registry.Ref): Int { + return value.value?.modId ?: 65535 + } + + override fun computeNative(value: Int?): Registry.Ref { + value ?: return Registries.tileModifiers.emptyRef + return Registries.tileModifiers.ref(value) + } + } + + private class Adapter(gson: Gson, left: TypeToken, right: TypeToken, private val constructor: Constructor>) : TypeAdapter>() { + private val left = gson.getAdapter(left) + private val right = gson.getAdapter(right) + private val either = gson.getAdapter(TypeToken.getParameterized(Either::class.java, left.type, right.type)) as TypeAdapter> + + override fun write(out: JsonWriter, value: NativeLegacy?) { + if (value == null) + out.nullValue() + else if (Starbound.IS_WRITING_LEGACY_JSON || value.nativeValue.isEmpty) + right.write(out, value.legacy) + else + left.write(out, value.native) + } + + override fun read(`in`: JsonReader): NativeLegacy? { + if (`in`.consumeNull()) + return null + + val instance = constructor.newInstance() + val either = either.read(`in`) + instance.nativeValue = either.left + instance.legacyValue = either.right + return instance + } + } + + companion object : TypeAdapterFactory { + @Suppress("UNCHECKED_CAST") + override fun create(gson: Gson, type: TypeToken): TypeAdapter? { + if (NativeLegacy::class.java.isAssignableFrom(type.rawType)) { + val rawType = type.rawType as? Class<*> ?: throw RuntimeException("bro what the fuck?") + val superclass = rawType.genericSuperclass as? ParameterizedType ?: throw RuntimeException("bro what the fuck?") + + return Adapter( + gson, + TypeToken.get(superclass.actualTypeArguments[0]) as TypeToken, + TypeToken.get(superclass.actualTypeArguments[1]) as TypeToken, + rawType.getDeclaredConstructor() as Constructor> + ) as TypeAdapter + } + + return null + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/json/builder/FactoryAdapter.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/json/builder/FactoryAdapter.kt index 2c04b929..4255a75e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/json/builder/FactoryAdapter.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/json/builder/FactoryAdapter.kt @@ -148,10 +148,15 @@ class FactoryAdapter private constructor( return } - out.beginObject() + if (asJsonArray) + out.beginArray() + else + out.beginObject() for (type in types) { if (type.isFlat) { + check(!asJsonArray) + val (field, adapter) = type val result = (adapter as TypeAdapter).toJsonTree((field as KProperty1).get(value)) @@ -160,17 +165,26 @@ class FactoryAdapter private constructor( throw JsonSyntaxException("Expected JsonObject from adapter of ${type.name}, but got ${result::class.qualifiedName}") } - out.value(result) + for ((k, v) in result.entrySet()) { + out.name(k) + out.value(v) + } } } else { val (field, adapter) = type - out.name(field.name) + + if (!asJsonArray) + out.name(field.name) + @Suppress("unchecked_cast") (adapter as TypeAdapter).write(out, (field as KProperty1).get(value)) } } - out.endObject() + if (asJsonArray) + out.endArray() + else + out.endObject() } override fun read(reader: JsonReader): T? { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/FloatingNetworkedElement.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/FloatingNetworkedElement.kt index 8f41e4f1..06d80f34 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/FloatingNetworkedElement.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/syncher/FloatingNetworkedElement.kt @@ -121,6 +121,10 @@ class FloatingNetworkedElement(private var value: Double = 0.0, val ops: Ops, va } queue.clear() + + if (isInterpolating) { + queue.add(currentTime to value) + } } override fun writeInitial(data: DataOutputStream, isLegacy: Boolean) { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt index de985001..5906112a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt @@ -322,18 +322,23 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn shipWorld = it shipWorld.thread.start() send(PlayerWarpResultPacket(true, WarpAlias.OwnShip, false)) - shipWorld.acceptPlayer(this).thenAccept { + + server.worlds.first().acceptPlayer(this) + + /*shipWorld.acceptPlayer(this).thenAccept { for (conn in server.channels.connections) { if (conn.isLegacy && conn !== this) { conn.shipWorld.acceptPlayer(this) break } } + + server.worlds.first().acceptPlayer(this) }.exceptionally { LOGGER.error("Shipworld of $this rejected to accept its owner", it) disconnect("Shipworld rejected player warp request: $it") null - } + }*/ }.exceptionally { LOGGER.error("Error while initializing shipworld for $this", it) disconnect("Error while initializing shipworld for player: $it") diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt index 334f04f0..3605b29a 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt @@ -72,7 +72,7 @@ class ServerWorld private constructor( player.skyVersion = skyVersion player.send(WorldStartPacket( - templateData = template.toJson(true), + templateData = Starbound.writeLegacyJson { template.toJson() }, skyData = skyData.toByteArray(), weatherData = ByteArray(0), playerStart = playerSpawnPosition, @@ -86,7 +86,9 @@ class ServerWorld private constructor( localInterpolationMode = false, )) - player.sendAndFlush(CentralStructureUpdatePacket(Starbound.gson.toJsonTree(centralStructure))) + Starbound.writeLegacyJson { + player.sendAndFlush(CentralStructureUpdatePacket(Starbound.gson.toJsonTree(centralStructure))) + } } else { player.sendAndFlush(JoinWorldPacket(this)) } @@ -97,7 +99,9 @@ class ServerWorld private constructor( unpause() try { - return CompletableFuture.supplyAsync(Supplier { doAcceptPlayer(player) }, mailbox) + return CompletableFuture.supplyAsync(Supplier { doAcceptPlayer(player) }, mailbox).exceptionally { + LOGGER.error("Error while accepting new player into world", it) + } } catch (err: RejectedExecutionException) { return CompletableFuture.failedFuture(err) } @@ -123,7 +127,10 @@ class ServerWorld private constructor( check(!isClosed.get()) { "$this is invalid" } try { - return CompletableFuture.supplyAsync(Supplier { doRemovePlayer(player) }, mailbox) + return CompletableFuture.supplyAsync(Supplier { doRemovePlayer(player) }, mailbox).exceptionally { + LOGGER.error("Error while removing player from world", it) + null + } } catch (err: RejectedExecutionException) { return CompletableFuture.completedFuture(false) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/AssetPathStack.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/AssetPathStack.kt index 45d1b47b..e39e7dca 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/AssetPathStack.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/AssetPathStack.kt @@ -20,7 +20,7 @@ object AssetPathStack { push(value.substringBefore(':').substringBeforeLast('/')) } - fun last() = stack.lastOrNull() + fun last() = stack.lastOrNull() ?: "/" fun pop() = stack.removeLast() inline fun block(path: String, block: (String) -> T): T { @@ -44,11 +44,11 @@ object AssetPathStack { } fun remap(path: String): String { - return remap(checkNotNull(last()) { "Not reading an asset on current thread" }, path) + return remap(last(), path) } fun remapSafe(path: String): String { - return remap(last() ?: return path, path) + return remap(last(), path) } fun relativeTo(base: String, path: String): String { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/AbstractPerlinNoise.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/AbstractPerlinNoise.kt index 39c71096..8d6a61a6 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/AbstractPerlinNoise.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/AbstractPerlinNoise.kt @@ -43,12 +43,15 @@ abstract class AbstractPerlinNoise(val parameters: PerlinNoiseParameters) { protected val g3 by lazy { Double2DArray.allocate(parameters.scale * 2 + 2, 3) } init { - if (parameters.seed != null) { + if (parameters.seed != null && parameters.type != PerlinNoiseParameters.Type.UNITIALIZED) { init(parameters.seed) } } fun init(seed: Long) { + if (parameters.type == PerlinNoiseParameters.Type.UNITIALIZED) + return + isInitialized = true this.seed = seed @@ -218,6 +221,7 @@ abstract class AbstractPerlinNoise(val parameters: PerlinNoiseParameters) { PerlinNoiseParameters.Type.PERLIN -> PerlinNoise(parameters) PerlinNoiseParameters.Type.BILLOW -> BillowNoise(parameters) PerlinNoiseParameters.Type.RIDGED_MULTI -> RidgedNoise(parameters) + PerlinNoiseParameters.Type.UNITIALIZED -> EmptyNoise(parameters) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/EmptyNoise.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/EmptyNoise.kt new file mode 100644 index 00000000..5d29cad3 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/EmptyNoise.kt @@ -0,0 +1,21 @@ +package ru.dbotthepony.kstarbound.util.random + +import ru.dbotthepony.kstarbound.defs.PerlinNoiseParameters + +class EmptyNoise(parameters: PerlinNoiseParameters) : AbstractPerlinNoise(parameters) { + init { + require(parameters.type == PerlinNoiseParameters.Type.UNITIALIZED) + } + + override fun get(x: Double): Double { + return 0.0 + } + + override fun get(x: Double, y: Double): Double { + return 0.0 + } + + override fun get(x: Double, y: Double, z: Double): Double { + return 0.0 + } +}