Some improvement to asset loading and feedback

This commit is contained in:
DBotThePony 2024-04-28 19:44:15 +07:00
parent 0854baa986
commit 4c959b82a9
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 123 additions and 57 deletions

View File

@ -4,6 +4,8 @@ import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import kotlinx.coroutines.future.asCompletableFuture import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.future.await import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -147,7 +149,7 @@ object Globals {
LOGGER.fatal("$path does not exist or is not a file, expect bad things to happen!") LOGGER.fatal("$path does not exist or is not a file, expect bad things to happen!")
} else { } else {
try { try {
AssetPathStack("/") { AssetPathStack(path) {
accept.set(adapter.value.fromJsonTree(file)) accept.set(adapter.value.fromJsonTree(file))
} }
} catch (err: Throwable) { } catch (err: Throwable) {
@ -161,6 +163,38 @@ object Globals {
return Starbound.GLOBAL_SCOPE.launch { load(path, accept, lazy(LazyThreadSafetyMode.NONE) { Starbound.gson.getAdapter(T::class.java) }) }.asCompletableFuture() return Starbound.GLOBAL_SCOPE.launch { load(path, accept, lazy(LazyThreadSafetyMode.NONE) { Starbound.gson.getAdapter(T::class.java) }) }.asCompletableFuture()
} }
private inline fun <reified T : Any> mapAdapter(filename: String): Lazy<TypeAdapter<ImmutableMap<String, T>>> {
val parent by lazy { Starbound.gson.getAdapter(T::class.java) }
return lazy(LazyThreadSafetyMode.NONE) {
object : TypeAdapter<ImmutableMap<String, T>>() {
override fun write(out: JsonWriter, value: ImmutableMap<String, T>) {
TODO("Not yet implemented")
}
override fun read(`in`: JsonReader): ImmutableMap<String, T> {
`in`.beginObject()
val builder = ImmutableMap.Builder<String, T>()
while (`in`.hasNext()) {
val name = `in`.nextName()
val element = Starbound.ELEMENTS_ADAPTER.read(`in`)
try {
builder.put(name, parent.fromJsonTree(element))
} catch (err: Throwable) {
LOGGER.error("Exception reading element at $name from $filename; it will not be loaded", err)
}
}
`in`.endObject()
return builder.build()
}
}
}
}
fun load(): List<Future<*>> { fun load(): List<Future<*>> {
val tasks = ArrayList<CompletableFuture<*>>() val tasks = ArrayList<CompletableFuture<*>>()
@ -186,10 +220,10 @@ object Globals {
tasks.add(load("/plants/bushDamage.config", ::bushDamage)) tasks.add(load("/plants/bushDamage.config", ::bushDamage))
tasks.add(load("/tiles/defaultDamage.config", ::tileDamage)) tasks.add(load("/tiles/defaultDamage.config", ::tileDamage))
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/dungeon_worlds.config", ::dungeonWorlds, lazy { Starbound.gson.mapAdapter() }) }.asCompletableFuture()) tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/dungeon_worlds.config", ::dungeonWorlds, mapAdapter("/dungeon_worlds.config")) }.asCompletableFuture())
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/currencies.config", ::currencies, lazy { Starbound.gson.mapAdapter() }) }.asCompletableFuture()) tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/currencies.config", ::currencies, mapAdapter("/currencies.config")) }.asCompletableFuture())
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/system_objects.config", ::systemObjects, lazy { Starbound.gson.mapAdapter() }) }.asCompletableFuture()) tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/system_objects.config", ::systemObjects, mapAdapter("/system_objects.config")) }.asCompletableFuture())
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/instance_worlds.config", ::instanceWorlds, lazy { Starbound.gson.mapAdapter() }) }.asCompletableFuture()) tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/instance_worlds.config", ::instanceWorlds, mapAdapter("/instance_worlds.config")) }.asCompletableFuture())
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/names/profanityfilter.config", ::profanityFilterInternal, lazy { Starbound.gson.listAdapter() }) }.asCompletableFuture()) tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/names/profanityfilter.config", ::profanityFilterInternal, lazy { Starbound.gson.listAdapter() }) }.asCompletableFuture())

View File

@ -123,7 +123,7 @@ object Registries {
try { try {
val elem = JsonPatch.applyAsync(Starbound.ELEMENTS_ADAPTER.read(listedFile.asyncJsonReader().await()), patches[listedFile.computeFullPath()]) val elem = JsonPatch.applyAsync(Starbound.ELEMENTS_ADAPTER.read(listedFile.asyncJsonReader().await()), patches[listedFile.computeFullPath()])
AssetPathStack(listedFile.computeDirectory()) { AssetPathStack(listedFile.computeFullPath()) {
val read = adapter.fromJsonTree(elem) val read = adapter.fromJsonTree(elem)
val keys = keyProvider(read) val keys = keyProvider(read)

View File

@ -98,9 +98,13 @@ class AssetReference<V> {
} }
val value = json.thenApplyAsync({ j -> val value = json.thenApplyAsync({ j ->
AssetPathStack(fullPath.substringBefore(':').substringBeforeLast('/')) { if (j == null) {
adapter.fromJsonTree(j) null
} } else {
AssetPathStack(fullPath.substringBefore(':').substringBefore('?')) {
adapter.fromJsonTree(j)
}
}
}, Starbound.EXECUTOR) }, Starbound.EXECUTOR)
value.exceptionally { value.exceptionally {

View File

@ -10,6 +10,7 @@ import ru.dbotthepony.kstarbound.io.writeStruct2d
import ru.dbotthepony.kstarbound.json.builder.DispatchingAdapter import ru.dbotthepony.kstarbound.json.builder.DispatchingAdapter
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.json.builder.JsonFlat
import ru.dbotthepony.kstarbound.math.Line2d import ru.dbotthepony.kstarbound.math.Line2d
import ru.dbotthepony.kstarbound.math.vector.Vector2d import ru.dbotthepony.kstarbound.math.vector.Vector2d
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
@ -38,6 +39,7 @@ sealed class PhysicsForceRegion {
val xTargetVelocity: Double?, val xTargetVelocity: Double?,
val yTargetVelocity: Double?, val yTargetVelocity: Double?,
val controlForce: Double, val controlForce: Double,
@JsonFlat
override val filter: PhysicsCategoryFilter, override val filter: PhysicsCategoryFilter,
override val enabled: Boolean = true override val enabled: Boolean = true
) : PhysicsForceRegion() { ) : PhysicsForceRegion() {
@ -69,6 +71,7 @@ sealed class PhysicsForceRegion {
val innerRadius: Double, val innerRadius: Double,
val targetRadialVelocity: Double, val targetRadialVelocity: Double,
val controlForce: Double, val controlForce: Double,
@JsonFlat
override val filter: PhysicsCategoryFilter, override val filter: PhysicsCategoryFilter,
override val enabled: Boolean = true override val enabled: Boolean = true
) : PhysicsForceRegion() { ) : PhysicsForceRegion() {
@ -101,6 +104,7 @@ sealed class PhysicsForceRegion {
val gradient: Line2d, val gradient: Line2d,
val baseTargetVelocity: Double, val baseTargetVelocity: Double,
val baseControlForce: Double, val baseControlForce: Double,
@JsonFlat
override val filter: PhysicsCategoryFilter, override val filter: PhysicsCategoryFilter,
override val enabled: Boolean = true override val enabled: Boolean = true
) : PhysicsForceRegion() { ) : PhysicsForceRegion() {

View File

@ -72,10 +72,10 @@ data class DungeonDefinition(
tiles.spewWarnings(name) tiles.spewWarnings(name)
} }
private val directory = AssetPathStack.last() private val file = AssetPathStack.lastFile()
val actualParts: ImmutableList<DungeonPart> by lazy { val actualParts: ImmutableList<DungeonPart> by lazy {
AssetPathStack(directory) { AssetPathStack(file) {
val build = parts.stream().map { Starbound.gson.fromJson(it, DungeonPart::class.java) }.collect(ImmutableList.toImmutableList()) val build = parts.stream().map { Starbound.gson.fromJson(it, DungeonPart::class.java) }.collect(ImmutableList.toImmutableList())
build.forEach { it.bind(this) } build.forEach { it.bind(this) }

View File

@ -39,6 +39,7 @@ import ru.dbotthepony.kommons.gson.get
import ru.dbotthepony.kommons.gson.getObject import ru.dbotthepony.kommons.gson.getObject
import ru.dbotthepony.kstarbound.json.JsonPatch import ru.dbotthepony.kstarbound.json.JsonPatch
import ru.dbotthepony.kstarbound.math.vector.Vector2d import ru.dbotthepony.kstarbound.math.vector.Vector2d
import ru.dbotthepony.kstarbound.util.AssetPathStack
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.lang.ref.Reference import java.lang.ref.Reference
@ -378,11 +379,27 @@ class Image private constructor(
val file = Starbound.locate(it) val file = Starbound.locate(it)
if (!file.exists) { if (!file.exists) {
throw FileNotFoundException("No such file $it") val asset = AssetPathStack.lastFile()
if (asset == "/") {
logger.error("No such file $it")
} else {
logger.error("No such file $it (referenced in $asset)")
}
return@computeIfAbsent Optional.empty()
} }
if (!file.isFile) { if (!file.isFile) {
throw FileNotFoundException("File $it is a directory") val asset = AssetPathStack.lastFile()
if (asset == "/") {
logger.error("$it is a directory")
} else {
logger.error("$it is a directory (referenced in $asset)")
}
return@computeIfAbsent Optional.empty()
} }
val getWidth = intArrayOf(0) val getWidth = intArrayOf(0)
@ -400,7 +417,7 @@ class Image private constructor(
MemoryUtil.memPutByte(buf + i, readBuf[i]) MemoryUtil.memPutByte(buf + i, readBuf[i])
} }
return@STBIReadCallbackI read return@STBIReadCallbackI read.coerceAtLeast(0)
}) })
val skipCallback = STBISkipCallback.create { _, n -> stream.skip(n.toLong()) } val skipCallback = STBISkipCallback.create { _, n -> stream.skip(n.toLong()) }
@ -425,10 +442,18 @@ class Image private constructor(
eofCallback.free() eofCallback.free()
callback.free() callback.free()
if (!status) if (!status) {
throw IllegalArgumentException("File $file is not an image or it is corrupted") val asset = AssetPathStack.lastFile()
Optional.of(Image(file, it, getWidth[0], getHeight[0], getConfig(it))) if (asset == "/") {
logger.error("$file is not an image or it is corrupted")
} else {
logger.error("$file is not an image or it is corrupted (referenced in $asset)")
}
Optional.empty()
} else
Optional.of(Image(file, it, getWidth[0], getHeight[0], getConfig(it)))
} catch (err: Exception) { } catch (err: Exception) {
logger.error("Failed to load image at path $it", err) logger.error("Failed to load image at path $it", err)
Optional.empty() Optional.empty()

View File

@ -9,27 +9,23 @@ 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.JsonPrimitive import com.google.gson.JsonPrimitive
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.annotations.JsonAdapter import com.google.gson.annotations.JsonAdapter
import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.launch
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.util.Either import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.kstarbound.Registry import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.AssetPath import ru.dbotthepony.kstarbound.defs.AssetPath
import ru.dbotthepony.kstarbound.defs.JsonReference
import ru.dbotthepony.kstarbound.defs.actor.StatModifier import ru.dbotthepony.kstarbound.defs.actor.StatModifier
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
import ru.dbotthepony.kstarbound.defs.tile.TileDamageParameters import ru.dbotthepony.kstarbound.defs.tile.TileDamageParameters
import ru.dbotthepony.kstarbound.json.builder.JsonFactory import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kommons.gson.consumeNull import ru.dbotthepony.kommons.gson.consumeNull
import ru.dbotthepony.kstarbound.json.listAdapter import ru.dbotthepony.kstarbound.json.listAdapter
import ru.dbotthepony.kstarbound.json.stream
import ru.dbotthepony.kstarbound.util.PeriodicFunction import ru.dbotthepony.kstarbound.util.PeriodicFunction
import ru.dbotthepony.kommons.gson.contains import ru.dbotthepony.kommons.gson.contains
import ru.dbotthepony.kommons.gson.get import ru.dbotthepony.kommons.gson.get
@ -219,7 +215,7 @@ data class ObjectDefinition(
val orientations = ImmutableList.Builder<Supplier<ObjectOrientation>>() val orientations = ImmutableList.Builder<Supplier<ObjectOrientation>>()
val path = AssetPathStack.last() val path = AssetPathStack.lastFile()
for (v in ObjectOrientation.preprocess(read.getArray("orientations"))) { for (v in ObjectOrientation.preprocess(read.getArray("orientations"))) {
val future = Starbound.GLOBAL_SCOPE.async { this@Adapter.orientations.read(v as JsonObject, path) }.asCompletableFuture() val future = Starbound.GLOBAL_SCOPE.async { this@Adapter.orientations.read(v as JsonObject, path) }.asCompletableFuture()
@ -275,7 +271,7 @@ data class ObjectDefinition(
soundEffectRangeMultiplier = basic.soundEffectRangeMultiplier, soundEffectRangeMultiplier = basic.soundEffectRangeMultiplier,
price = basic.price, price = basic.price,
statusEffects = basic.statusEffects, statusEffects = basic.statusEffects,
touchDamage = Starbound.loadJsonAsset(basic.touchDamage, AssetPathStack.last()), touchDamage = Starbound.loadJsonAsset(basic.touchDamage, AssetPathStack.lastFolder()),
minimumLiquidLevel = basic.minimumLiquidLevel, minimumLiquidLevel = basic.minimumLiquidLevel,
maximumLiquidLevel = basic.maximumLiquidLevel, maximumLiquidLevel = basic.maximumLiquidLevel,
liquidCheckInterval = basic.liquidCheckInterval, liquidCheckInterval = basic.liquidCheckInterval,

View File

@ -9,10 +9,7 @@ import com.google.gson.JsonNull
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.annotations.JsonAdapter
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import kotlinx.coroutines.future.await import kotlinx.coroutines.future.await
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.gson.clear import ru.dbotthepony.kommons.gson.clear
@ -24,9 +21,7 @@ import ru.dbotthepony.kstarbound.math.vector.Vector2i
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.render.RenderLayer import ru.dbotthepony.kstarbound.client.render.RenderLayer
import ru.dbotthepony.kstarbound.defs.Drawable import ru.dbotthepony.kstarbound.defs.Drawable
import ru.dbotthepony.kstarbound.defs.JsonReference
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
import ru.dbotthepony.kommons.gson.consumeNull
import ru.dbotthepony.kstarbound.json.listAdapter import ru.dbotthepony.kstarbound.json.listAdapter
import ru.dbotthepony.kstarbound.json.setAdapter import ru.dbotthepony.kstarbound.json.setAdapter
import ru.dbotthepony.kommons.gson.contains import ru.dbotthepony.kommons.gson.contains
@ -37,11 +32,9 @@ import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import ru.dbotthepony.kstarbound.defs.tile.isEmptyTile import ru.dbotthepony.kstarbound.defs.tile.isEmptyTile
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyTile
import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.util.AssetPathStack
import ru.dbotthepony.kstarbound.world.Direction import ru.dbotthepony.kstarbound.world.Direction
import ru.dbotthepony.kstarbound.world.World import ru.dbotthepony.kstarbound.world.World
import java.util.concurrent.CompletableFuture
import kotlin.math.PI import kotlin.math.PI
data class ObjectOrientation( data class ObjectOrientation(
@ -154,12 +147,12 @@ data class ObjectOrientation(
private val spaces = gson.setAdapter<Vector2i>() private val spaces = gson.setAdapter<Vector2i>()
private val materialSpaces = gson.getAdapter(TypeToken.getParameterized(ImmutableList::class.java, TypeToken.getParameterized(Pair::class.java, Vector2i::class.java, String::class.java).type)) as TypeAdapter<ImmutableList<Pair<Vector2i, String>>> private val materialSpaces = gson.getAdapter(TypeToken.getParameterized(ImmutableList::class.java, TypeToken.getParameterized(Pair::class.java, Vector2i::class.java, String::class.java).type)) as TypeAdapter<ImmutableList<Pair<Vector2i, String>>>
suspend fun read(obj: JsonObject, folder: String): ObjectOrientation { suspend fun read(obj: JsonObject, file: String): ObjectOrientation {
val drawables = ArrayList<Drawable>() val drawables = ArrayList<Drawable>()
val flipImages = obj.get("flipImages", false) val flipImages = obj.get("flipImages", false)
val renderLayer = RenderLayer.parse(obj.get("renderLayer", "Object")) val renderLayer = RenderLayer.parse(obj.get("renderLayer", "Object"))
AssetPathStack(folder) { AssetPathStack(file) {
if ("imageLayers" in obj) { if ("imageLayers" in obj) {
for (value in obj["imageLayers"].asJsonArray) { for (value in obj["imageLayers"].asJsonArray) {
var result = this.drawables.fromJsonTree(value) var result = this.drawables.fromJsonTree(value)
@ -273,7 +266,7 @@ data class ObjectOrientation(
val lightPosition = obj["lightPosition"]?.let { vectorsi.fromJsonTree(it) } ?: Vector2i.ZERO val lightPosition = obj["lightPosition"]?.let { vectorsi.fromJsonTree(it) } ?: Vector2i.ZERO
val beamAngle = obj.get("beamAngle", 0.0) / 180.0 * PI val beamAngle = obj.get("beamAngle", 0.0) / 180.0 * PI
val statusEffectArea = obj["statusEffectArea"]?.let { vectorsd.fromJsonTree(it) } val statusEffectArea = obj["statusEffectArea"]?.let { vectorsd.fromJsonTree(it) }
val touchDamage = Starbound.loadJsonAsset(obj["touchDamage"] ?: JsonNull.INSTANCE, AssetPathStack.last()).await() val touchDamage = Starbound.loadJsonAsset(obj["touchDamage"] ?: JsonNull.INSTANCE, AssetPathStack.lastFolder()).await()
val emitters = ArrayList<ParticleEmissionEntry>() val emitters = ArrayList<ParticleEmissionEntry>()

View File

@ -1,7 +1,6 @@
package ru.dbotthepony.kstarbound.util package ru.dbotthepony.kstarbound.util
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import java.io.File
object AssetPathStack { object AssetPathStack {
private val _stack = object : ThreadLocal<ArrayDeque<String>>() { private val _stack = object : ThreadLocal<ArrayDeque<String>>() {
@ -10,20 +9,35 @@ object AssetPathStack {
} }
} }
private val stack: ArrayDeque<String> get() = _stack.get() private val _fileStack = object : ThreadLocal<ArrayDeque<String>>() {
override fun initialValue(): ArrayDeque<String> {
return ArrayDeque()
}
}
private val folderStack: ArrayDeque<String> get() = _stack.get()
private val fileStack: ArrayDeque<String> get() = _fileStack.get()
fun push(value: String) { fun push(value: String) {
stack.addLast(value) if (value.last() == '/') {
// already folder, we don't have filename
folderStack.addLast(value)
fileStack.addLast(value)
} else {
folderStack.addLast(value.substringBeforeLast('/') + "/")
fileStack.addLast(value)
}
} }
fun pushFilename(value: String) { fun lastFolder() = folderStack.lastOrNull() ?: "/"
push(value.substringBefore(':').substringBeforeLast('/')) fun lastFile() = fileStack.lastOrNull() ?: "/"
fun pop() {
folderStack.removeLast()
fileStack.removeLast()
} }
fun last() = stack.lastOrNull() ?: "/" inline operator fun <T> invoke(path: String, block: (String) -> T): T {
fun pop() = stack.removeLast()
inline fun <T> block(path: String, block: (String) -> T): T {
try { try {
push(path) push(path)
return block.invoke(path) return block.invoke(path)
@ -32,23 +46,12 @@ object AssetPathStack {
} }
} }
inline operator fun <T> 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
return Starbound.STRINGS.intern("$a/$b")
}
fun remap(path: String): String { fun remap(path: String): String {
return remap(last(), path) return relativeTo(lastFolder(), path)
} }
fun remapSafe(path: String): String { fun remapSafe(path: String): String {
return remap(last(), path) return relativeTo(lastFolder(), path)
} }
fun relativeTo(base: String, path: String): String { fun relativeTo(base: String, path: String): String {

View File

@ -8,7 +8,7 @@ import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.ObjectArrayList
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL11.GL_LINES import org.lwjgl.opengl.GL11.GL_LINES
import ru.dbotthepony.kommons.util.IStruct2d import ru.dbotthepony.kommons.util.IStruct2d
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
@ -30,6 +30,7 @@ import ru.dbotthepony.kstarbound.json.listAdapter
import ru.dbotthepony.kstarbound.math.Line2d import ru.dbotthepony.kstarbound.math.Line2d
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
import ru.dbotthepony.kstarbound.util.AssetPathStack
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import java.util.LinkedList import java.util.LinkedList
@ -417,6 +418,7 @@ class Poly private constructor(val edges: ImmutableList<Line2d>, val vertices: I
} }
companion object : TypeAdapterFactory { companion object : TypeAdapterFactory {
private val LOGGER = LogManager.getLogger()
val CODEC = nativeCodec(::read, Poly::write) val CODEC = nativeCodec(::read, Poly::write)
val LEGACY_CODEC = legacyCodec(::read, Poly::write) val LEGACY_CODEC = legacyCodec(::read, Poly::write)
@ -447,8 +449,13 @@ class Poly private constructor(val edges: ImmutableList<Line2d>, val vertices: I
else { else {
val list = list.read(`in`) val list = list.read(`in`)
if (list.isEmpty()) if (list.isEmpty()) {
// LOGGER.warn("Accepting empty poly from asset ${AssetPathStack.lastFile()} to match original engine behavior")
return EMPTY return EMPTY
} else if (list.size == 1) {
LOGGER.warn("Accepting degenerate poly with 1 points from asset ${AssetPathStack.lastFile()} to match original engine behavior")
return Poly(listOf(list[0], list[0], list[0], list[0]))
}
return Poly(list) return Poly(list)
} }