diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index 7c60b88f..9827602e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -44,6 +44,7 @@ import ru.dbotthepony.kstarbound.io.json.AABBiTypeAdapter import ru.dbotthepony.kstarbound.io.json.EitherTypeAdapter import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter import ru.dbotthepony.kstarbound.io.json.InternedStringAdapter +import ru.dbotthepony.kstarbound.io.json.NothingAdapter import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter @@ -110,6 +111,8 @@ class Starbound : ISBFileLocator { registerTypeAdapter(InternedStringAdapter(stringInterner)) registerTypeAdapter(InternedJsonElementAdapter(stringInterner)) + registerTypeAdapter(Nothing::class.java, NothingAdapter) + // Обработчик @JsonImplementation registerTypeAdapterFactory(JsonImplementationTypeFactory) @@ -387,7 +390,7 @@ class Starbound : ISBFileLocator { loadStage(callback, _particles, ext2files["particle"] ?: listOf()) pathStack.block("/") { - playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java) + //playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java) } initializing = false diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt index bdf4c1be..fd46ba28 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientChunk.kt @@ -11,8 +11,8 @@ import ru.dbotthepony.kstarbound.client.gl.vertex.quad import ru.dbotthepony.kstarbound.client.gl.vertex.shadowLine import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer -import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer import ru.dbotthepony.kstarbound.client.render.GPULightRenderer +import ru.dbotthepony.kstarbound.client.render.LayeredRenderer import ru.dbotthepony.kstarbound.client.render.TileLayerList import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition import ru.dbotthepony.kstarbound.world.* @@ -166,22 +166,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk Unit, Int>>() - + inner class Renderer(val renderOrigin: ChunkPos = pos) : GPULightRenderer.ShadowGeometryRenderer { override fun renderHardGeometry( renderer: GPULightRenderer, lightPosition: Vector2f, @@ -309,7 +285,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk + layers.add(renderer.layer) { val relative = renderer.renderPos - posVector2d - it.push().translateWithMultiplication(relative.x.toFloat(), relative.y.toFloat()) + it.push().translateWithMultiplication(renderOrigin.x * CHUNK_SIZEf + relative.x.toFloat(), renderOrigin.y * CHUNK_SIZEf + relative.y.toFloat()) renderer.render(it) it.pop() - Unit - } to renderer.layer) + } } - layerQueue.add({ it: Matrix4fStack -> + layers.add(Z_LEVEL_LIQUID) { + it.push().translateWithMultiplication(renderOrigin.x * CHUNK_SIZEf, renderOrigin.y * CHUNK_SIZEf) val types = ArrayList() for (x in 0 until CHUNK_SIZE) { @@ -431,44 +415,10 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk= zPos) { - pair.first.invoke(stack) - - layerQueue.removeLast() - - if (layerQueue.isEmpty()) { - stack.pop() - return Int.MIN_VALUE - } - - pair = layerQueue.last() - } - - stack.pop() - return layerQueue.last().second - } - - override fun bottomMostZLevel(): Int { - if (layerQueue.isEmpty()) { - return Int.MIN_VALUE - } - - return layerQueue.last().second - } } private val entityRenderers = HashMap() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt index 74baba8f..6cc3b4d6 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientWorld.kt @@ -1,13 +1,7 @@ package ru.dbotthepony.kstarbound.client -import org.lwjgl.opengl.GL46.* -import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf -import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers -import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ -import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer -import ru.dbotthepony.kstarbound.client.render.renderLayeredList +import ru.dbotthepony.kstarbound.client.render.LayeredRenderer import ru.dbotthepony.kstarbound.math.encasingChunkPosAABB -import ru.dbotthepony.kstarbound.util.DoubleEdgeProgression import ru.dbotthepony.kstarbound.world.* import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kvector.util2d.AABB @@ -38,18 +32,17 @@ class ClientWorld( size: AABB, isScreenspaceRender: Boolean = true ) { - val determineRenderers = ArrayList() - + val layers = LayeredRenderer() client.lightRenderer.begin() for (chunk in collectPositionAware(size.encasingChunkPosAABB())) { - val renderer = chunk.second.OneShotRenderer(chunk.first) - determineRenderers.add(renderer) + val renderer = chunk.second.Renderer(chunk.first) + renderer.addLayers(layers) chunk.second.bake() //client.lightRenderer.addShadowGeometry(renderer) } - renderLayeredList(client.gl.matrixStack, determineRenderers) + layers.render(client.gl.matrixStack) /* for ((lightPosition, color) in listOf( (client.screenToWorld(client.mouseCoordinatesF)) to Color.RED, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt index 38c4fc31..4db0a9e0 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt @@ -18,6 +18,8 @@ import ru.dbotthepony.kstarbound.client.render.Camera import ru.dbotthepony.kstarbound.client.render.GPULightRenderer import ru.dbotthepony.kstarbound.client.render.TextAlignY import ru.dbotthepony.kstarbound.client.render.TileRenderers +import ru.dbotthepony.kstarbound.util.JVMTimeSource +import ru.dbotthepony.kstarbound.util.PausableTimeSource import ru.dbotthepony.kstarbound.util.formatBytesShort import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f import ru.dbotthepony.kvector.util2d.AABB @@ -25,6 +27,7 @@ import ru.dbotthepony.kvector.vector.Color import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.nfloat.Vector2f import ru.dbotthepony.kvector.vector.nfloat.Vector3f +import java.io.Closeable import java.nio.ByteBuffer import java.nio.ByteOrder import java.util.* @@ -32,7 +35,8 @@ import java.util.concurrent.locks.LockSupport import kotlin.collections.ArrayList import kotlin.math.roundToInt -class StarboundClient(val starbound: Starbound) : AutoCloseable { +class StarboundClient(val starbound: Starbound) : Closeable { + val time = PausableTimeSource(JVMTimeSource.INSTANCE) val window: Long val camera = Camera(this) val input = UserInput() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/DirectVertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/DirectVertexBuilder.kt index 614408f8..fc080f8c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/DirectVertexBuilder.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/DirectVertexBuilder.kt @@ -4,6 +4,10 @@ import org.lwjgl.opengl.GL46 import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject import ru.dbotthepony.kstarbound.util.ByteBufferOutputStream +/** + * Создаёт буфер для данных вне кучи, записывает данные напрямую в него, + * при [upload] загружает данные из буфера напрямую в память видеокарты. + */ open class DirectVertexBuilder>( attributes: GLAttributeList, type: GeometryType, diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt index 136ae88e..93bc17be 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/gl/vertex/StreamVertexBuilder.kt @@ -1,7 +1,9 @@ package ru.dbotthepony.kstarbound.client.gl.vertex import org.lwjgl.opengl.GL46 +import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.client.gl.GLStateTracker +import ru.dbotthepony.kstarbound.client.gl.GLTexture2D import ru.dbotthepony.kstarbound.client.gl.checkForGLError import java.io.Closeable @@ -48,4 +50,21 @@ class StreamVertexBuilder( vbo.close() ebo.close() } + + fun singleSprite(x: Float, y: Float, width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) { + begin() + + quadRotatedZ(x, y, width, height, z, 0f, 0f, angle, transformer) + + upload() + draw() + } + + fun singleSprite(width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) { + singleSprite(-width / 2f, -height / 2f, width / 2f, height / 2f, z, angle, transformer) + } + + fun singleSprite(width: Float, height: Float, angle: Double = 0.0, transformer: QuadVertexTransformer) { + singleSprite(-width / 2f, -height / 2f, width / 2f, height / 2f, 0f, angle, transformer) + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Animator.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Animator.kt new file mode 100644 index 00000000..e4566f84 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/Animator.kt @@ -0,0 +1,24 @@ +package ru.dbotthepony.kstarbound.client.render + +import ru.dbotthepony.kstarbound.client.ClientWorld +import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition +import ru.dbotthepony.kvector.matrix.Matrix4fStack + +class Animator( + val world: ClientWorld, + val def: AnimationDefinition +) { + val frameAnimator: FrameAnimator? + + init { + if (def.frames != null && def.animationCycle != null && def.frameNumber != null) { + frameAnimator = FrameAnimator(lastFrame = def.frameNumber - 1, time = world.client.time, animationCycle = def.animationCycle) + } else { + frameAnimator = null + } + } + + fun render(stack: Matrix4fStack) { + + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/FrameAnimator.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/FrameAnimator.kt index d1df4c7d..d73c9e3e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/FrameAnimator.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/FrameAnimator.kt @@ -1,20 +1,21 @@ package ru.dbotthepony.kstarbound.client.render -import org.lwjgl.glfw.GLFW.glfwGetTime +import ru.dbotthepony.kstarbound.util.ITimeSource +import ru.dbotthepony.kstarbound.util.JVMTimeSource /** * Таймер для анимирования набора спрайтов */ -open class FrameAnimator( +class FrameAnimator( /** * Первый кадр в анимации */ - var firstFrame: Int = 0, + val firstFrame: Int = 0, /** * Последний кадр в анимации */ - var lastFrame: Int, + val lastFrame: Int, /** * Сколько времени занимает один кадр @@ -25,38 +26,31 @@ open class FrameAnimator( * Зациклить ли анимацию */ var animationLoops: Boolean = true, + + val time: ITimeSource = JVMTimeSource.INSTANCE ) { var frame = 0 private set - /** - * Возвращает разницу между последним и первым кадром анимации - */ - val frameDiff get() = lastFrame - firstFrame - - private val initial = glfwGetTime() - private var lastRender = initial - - /** - * Сколько времени прошло с момента последнего кадра - */ - val delta get() = glfwGetTime() - lastRender - private var counter = 0.0 + private var lastRender = time.seconds /** - * Проверяет glfw таймер и продвигает фрейм анимации + * Разница между последним и первым кадром анимации */ - fun advance() { + val frameDiff = lastFrame - firstFrame + + /** + * Проверяет таймер на [seconds] и продвигает фрейм анимации + */ + fun advance(seconds: Double) { if (frameDiff == 0) return - if (frame + firstFrame >= lastFrame && !animationLoops) { + if (frame + firstFrame >= lastFrame && !animationLoops) return - } - counter += delta / animationCycle - lastRender = glfwGetTime() + counter += seconds / animationCycle if (counter >= 1.0) { val desired = frame + counter.toInt() @@ -69,4 +63,14 @@ open class FrameAnimator( } } } + + /** + * Проверяет таймер используя [time] и продвигает фрейм анимации + */ + fun advance() { + if (frameDiff != 0) { + advance(time.seconds - lastRender) + lastRender = time.seconds + } + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ILayeredRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ILayeredRenderer.kt deleted file mode 100644 index 46941cbb..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/ILayeredRenderer.kt +++ /dev/null @@ -1,76 +0,0 @@ -package ru.dbotthepony.kstarbound.client.render - -import ru.dbotthepony.kvector.matrix.Matrix4fStack - -/** - * Интерфейс для отрисовки комплексных объектов, в которых множество слоёв, который - * определяет лишь один метод: [renderLayer] - * - * Используется вместе с другими [ILayeredRenderer], где необходимо отрисовывать комплексную сцену, - * реализуя ручную сортировку геометрии. - */ -interface ILayeredRenderer { - /** - * Главный метод отрисовки данной стопки слоёв. Вызов данного метода может что-либо - * отрисовать, а может вообще ничего не отрисовать. - * - * zLevel всегда положителен, и указывает на то, какой слой (или все слои за) надо отрисовать, - * т.е. при вызове этого метода отрисовываются все слои, у которых z позиция >= [zPos] - * - * Возвращается zNew следующего слоя (такой, что [zPos] > zNew). - * - * Если следующего слоя нет, вернуть [Int.MIN_VALUE], и данный объект - * будет считаться отрисованным. - */ - fun renderLayerFromStack(zPos: Int, stack: Matrix4fStack): Int - - /** - * Возвращает наибольшее zPos в данной стопке. - * - * Если стопка пуста, то необходимо вернуть [Int.MIN_VALUE]. - * - * В зависимости от сцены, которую необходимо отрисовать, - * [renderLayerFromStack] может быть вызван сразу с этим же значением, - * если этот объект имеет самый дальний слой - */ - fun bottomMostZLevel(): Int -} - -fun renderLayeredList(transform: Matrix4fStack, potentialRenderers: List): Int { - val renderers = ArrayList(potentialRenderers.size) - var bottomMost = Int.MIN_VALUE - - for (render in potentialRenderers) { - val zLevel = render.bottomMostZLevel() - - if (zLevel > Int.MIN_VALUE) { - bottomMost = bottomMost.coerceAtLeast(zLevel) - renderers.add(render) - } - } - - var lastBottom = bottomMost - var renderCalls = 0 - - while (lastBottom != Int.MIN_VALUE && renderers.isNotEmpty()) { - var newBottom = Int.MIN_VALUE - - for (i in renderers.size - 1 downTo 0) { - val renderer = renderers[i] - - val newLevel = renderer.renderLayerFromStack(lastBottom, transform) - - renderCalls++ - - if (newLevel == Int.MIN_VALUE) { - renderers.removeAt(i) - } else { - newBottom = newBottom.coerceAtLeast(newLevel) - } - } - - lastBottom = newBottom - } - - return renderCalls -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/LayeredRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/LayeredRenderer.kt new file mode 100644 index 00000000..b29fcb49 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/LayeredRenderer.kt @@ -0,0 +1,30 @@ +package ru.dbotthepony.kstarbound.client.render + +import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap +import it.unimi.dsi.fastutil.ints.Int2ObjectFunction +import ru.dbotthepony.kvector.matrix.Matrix4fStack + +/** + * Позволяет вызывать отрисовщики в определённой (послойной) последовательности + */ +class LayeredRenderer { + private val layers = Int2ObjectAVLTreeMap Unit>>() + + /** + * Сортировка [layer] происходит от дальнего (БОЛЬШЕ!) к ближнему (МЕНЬШЕ!) + * + * Пример: + * `8 -> 6 -> 4 -> 1 -> -4 -> -7` + */ + fun add(layer: Int, renderer: (Matrix4fStack) -> Unit) { + layers.computeIfAbsent(-layer, Int2ObjectFunction { ArrayList() }).add(renderer) + } + + fun render(stack: Matrix4fStack) { + for (list in layers.values) { + for (renderer in list) { + renderer.invoke(stack) + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/SpriteAnimator.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/SpriteAnimator.kt deleted file mode 100644 index daf763f2..00000000 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/SpriteAnimator.kt +++ /dev/null @@ -1,56 +0,0 @@ -package ru.dbotthepony.kstarbound.client.render - -import com.google.common.collect.ImmutableList -import ru.dbotthepony.kstarbound.client.gl.GLStateTracker -import ru.dbotthepony.kstarbound.client.gl.GLTexture2D -import ru.dbotthepony.kstarbound.defs.image.AtlasConfiguration -import ru.dbotthepony.kstarbound.defs.image.ImageReference - -class SpriteAnimator( - val sprites: List, - animationCycle: Double, - animationLoops: Boolean = true, - firstFrame: Int = 0, - lastFrame: Int = sprites.size - 1 -) : FrameAnimator( - firstFrame = firstFrame, - lastFrame = lastFrame, - animationCycle = animationCycle, - animationLoops = animationLoops, -) { - constructor( - image: GLTexture2D, - atlas: AtlasConfiguration, - animationCycle: Double, - animationLoops: Boolean = true, - firstFrame: Int = 0, - lastFrame: Int = atlas.spriteList.size - 1 - ) : this( - atlas.spriteList.stream().map { it.bind(image) }.collect(ImmutableList.toImmutableList()), - animationCycle = animationCycle, - animationLoops = animationLoops, - firstFrame = firstFrame, - lastFrame = lastFrame, - ) - - constructor( - image: ImageReference, - state: GLStateTracker, - animationCycle: Double, - animationLoops: Boolean = true, - firstFrame: Int = 0, - lastFrame: Int = image.config.spriteList.size - 1 - ) : this(state.loadNamedTextureSafe(image.image.fullPath), image.config, animationCycle, animationLoops, firstFrame, lastFrame) - - val sprite get() = sprites[frame] -} - -fun ImageReference.makeSpriteAnimator( - state: GLStateTracker, - animationCycle: Double, - animationLoops: Boolean = true, - firstFrame: Int = 0, - lastFrame: Int = config.spriteList.size - 1 -): SpriteAnimator { - return SpriteAnimator(this, state, animationCycle, animationLoops, firstFrame, lastFrame) -} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt index 4fab5b79..38f9b6e5 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/render/entity/ItemRenderer.kt @@ -25,17 +25,7 @@ class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk for (texture in textures) { texture.bind() - val builder = state.flat2DTexturedQuads.small - - builder.begin() - - val width = (texture.width / PIXELS_IN_STARBOUND_UNITf) / 2f - val height = (texture.height / PIXELS_IN_STARBOUND_UNITf) / 2f - - builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, texture.transformer) - - builder.upload() - builder.draw() + state.flat2DTexturedQuads.small.singleSprite(texture.width / PIXELS_IN_STARBOUND_UNITf, texture.height / PIXELS_IN_STARBOUND_UNITf, entity.movement.angle, texture.transformer) } } } 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 8e578468..6dcf0b24 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimationDefinition.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/animation/AnimationDefinition.kt @@ -12,13 +12,14 @@ import ru.dbotthepony.kvector.vector.ndouble.Vector2d @JsonFactory data class AnimationDefinition( val frames: ImageReference? = null, - val animatedParts: AnimatedParts? = null, val variants: Int? = null, val frameNumber: Int? = null, val animationCycle: Double? = null, val offset: Vector2d? = null, - val sounds: ImmutableMap, CustomSound>> = ImmutableMap.of(), + val animatedParts: AnimatedParts? = null, + + val sounds: ImmutableMap> = ImmutableMap.of(), val transformationGroups: ImmutableMap = ImmutableMap.of(), val particleEmitters: ImmutableMap = ImmutableMap.of(), ) { @@ -27,12 +28,6 @@ data class AnimationDefinition( val interpolated: Boolean? = null ) - // TODO - @JsonFactory - data class CustomSound( - val sound: String? = null - ) - @JsonFactory data class AnimatedParts( val stateTypes: ImmutableMap = ImmutableMap.of(), @@ -41,6 +36,7 @@ data class AnimationDefinition( @JsonFactory data class StateType( val default: String, + val priority: Int = 0, val states: ImmutableMap = ImmutableMap.of(), ) { @JsonFactory diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/AtlasConfiguration.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/AtlasConfiguration.kt index e3f7617d..0c6a5df8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/AtlasConfiguration.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/AtlasConfiguration.kt @@ -9,6 +9,7 @@ import com.google.gson.JsonObject import com.google.gson.JsonSyntaxException import com.google.gson.internal.bind.TypeAdapters import com.google.gson.stream.JsonReader +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import ru.dbotthepony.kstarbound.api.ISBFileLocator import ru.dbotthepony.kstarbound.client.gl.GLTexture2D import ru.dbotthepony.kstarbound.io.json.stream @@ -18,7 +19,7 @@ import ru.dbotthepony.kvector.vector.nint.Vector4i import java.util.concurrent.ConcurrentHashMap /** - * Атлас спрайтов, собранный вручную артистом + * Конфигурация атласа спрайтов, собранный вручную артистом * * В файлах игры именуется frames * @@ -45,10 +46,26 @@ class AtlasConfiguration private constructor( */ val first: Sprite = sprites[sprites.keys.stream().sorted().findFirst().orElseThrow { NoSuchElementException("No a single key present in $name") }] ?: throw NoSuchElementException("IMPOSSIBRU in $name") + private val intIndexed = Int2ObjectOpenHashMap() + + init { + for ((k, v) in sprites.entries) { + val int = k.toIntOrNull() + + if (int != null) { + intIndexed[int] = v + } + } + } + operator fun get(name: String): Sprite? { return sprites[name] } + operator fun get(name: Int): Sprite? { + return intIndexed[name] + } + fun any(name: String): Sprite { return get(name) ?: first } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/NothingAdapter.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/NothingAdapter.kt new file mode 100644 index 00000000..a9dbd919 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/io/json/NothingAdapter.kt @@ -0,0 +1,16 @@ +package ru.dbotthepony.kstarbound.io.json + +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter + +object NothingAdapter : TypeAdapter() { + override fun write(out: JsonWriter, value: Nothing?) { + out.nullValue() + } + + override fun read(`in`: JsonReader): Nothing? { + `in`.skipValue() + return null + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/SBPattern.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/SBPattern.kt index c60d4ee2..488b37a7 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/SBPattern.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/SBPattern.kt @@ -52,6 +52,9 @@ class SBPattern private constructor( } fun resolve(values: (String) -> String?): String? { + if (names.isEmpty()) + return raw + val buffer = ArrayList(pieces.size) for (piece in pieces) { @@ -70,6 +73,9 @@ class SBPattern private constructor( } fun with(params: Map): SBPattern { + if (names.isEmpty()) + return this + val map = Object2ObjectArrayMap() map.putAll(this.params) @@ -89,14 +95,6 @@ class SBPattern private constructor( fun resolve(map0: (String) -> String?, map1: (String) -> String?): String? { return contents ?: map0.invoke(name!!) ?: map1.invoke(name!!) } - - fun resolve(map0: (String) -> String?): String? { - return contents ?: map0.invoke(name!!) - } - - fun resolve(): String? { - return contents - } } companion object : TypeAdapterFactory { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/TimeSource.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/TimeSource.kt new file mode 100644 index 00000000..f2135803 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/TimeSource.kt @@ -0,0 +1,68 @@ +package ru.dbotthepony.kstarbound.util + +interface ITimeSource { + /** + * Время в наносекундах + */ + val nanos: Long + + /** + * Время в микросекундах + */ + val micros: Long get() = nanos / 1_000L + + /** + * Время в миллисекундах + */ + val millis: Long get() = nanos / 1_000_000L + + /** + * Время в секундах, с точностью до микросекунд + */ + val seconds: Double get() = (nanos / 1_000L) / 1_000_000.0 +} + +class JVMTimeSource : ITimeSource { + private val origin = System.nanoTime() + + override val nanos: Long + get() = System.nanoTime() - origin + + companion object { + @JvmField + val INSTANCE = JVMTimeSource() + } +} + +class ArtificialTimeSource(nanos: Long = 0L) : ITimeSource { + override var nanos: Long = nanos + private set + + fun advance(nanos: Long) { + this.nanos += nanos + } +} + +class PausableTimeSource(private val parent: ITimeSource) : ITimeSource { + override val nanos: Long get() { + if (isPaused) { + return pausedSince - skipped + } else { + return parent.nanos - skipped + } + } + + private var isPaused = false + private var pausedSince = 0L + private var skipped = 0L + + fun pause() { + if (!isPaused) { + isPaused = true + pausedSince = parent.nanos + } else { + isPaused = false + skipped += parent.nanos - pausedSince + } + } +}