diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index dd9701f3..108f8a6c 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -69,7 +69,7 @@ fun main() { for (chunkX in 0 .. 100) { //for (chunkX in 0 .. 17) { // for (chunkY in 21 .. 21) { - for (chunkY in 0 .. 24) { + for (chunkY in 18 .. 24) { val data = db.read(byteArrayOf(1, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())) val data2 = db.read(byteArrayOf(2, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())) @@ -116,7 +116,7 @@ fun main() { val rand = Random() - for (i in 0 until 0) { + for (i in 0 until 128) { val item = ItemEntity(client.world!!, Registries.items.keys.values.random().value) item.position = Vector2d(225.0 - i, 785.0) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Registry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Registry.kt index dd2f14dd..f922f339 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Registry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Registry.kt @@ -167,6 +167,7 @@ class Registry(val name: String) { private inner class RefImpl(override val key: Either) : Ref() { override var entry: Entry? = null + var references = 0 override fun equals(other: Any?): Boolean { return this === other || other is Registry<*>.RefImpl && other.key == key && other.registry == registry @@ -192,7 +193,9 @@ class Registry(val name: String) { val ref = RefImpl(Either.left(it as String)) ref.entry = keysInternal[it] ref - }) + }).also { + it.references++ + } } fun ref(index: Int): Ref = lock.withLock { @@ -200,7 +203,9 @@ class Registry(val name: String) { val ref = RefImpl(Either.right(it)) ref.entry = idsInternal[it] ref - }) + }).also { + it.references++ + } } operator fun contains(index: String) = lock.withLock { index in keysInternal } @@ -211,14 +216,14 @@ class Registry(val name: String) { keyRefs.values.forEach { if (!it.isPresent) { - LOGGER.warn("Registry '$name' reference at '${it.key.left()}' is not bound to value, expect problems") + LOGGER.warn("Registry '$name' reference at '${it.key.left()}' is not bound to value, expect problems (referenced ${it.references} times)") any = false } } idRefs.values.forEach { if (!it.isPresent) { - LOGGER.warn("Registry '$name' reference with ID '${it.key.right()}' is not bound to value, expect problems") + LOGGER.warn("Registry '$name' reference with ID '${it.key.right()}' is not bound to value, expect problems (referenced ${it.references} times)") any = false } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt index c9f2de53..5584c09f 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/defs/image/Image.kt @@ -308,7 +308,7 @@ class Image private constructor( private val dataCache: AsyncLoadingCache = Caffeine.newBuilder() .expireAfterAccess(Duration.ofMinutes(1)) .weigher { key, value -> value.capacity() } - .maximumWeight(1_024L * 1_024L * 256L /* 256 МиБ */) + .maximumWeight((Runtime.getRuntime().maxMemory() / 4L).coerceIn(1_024L * 1_024L * 32L /* 32 МиБ */, 1_024L * 1_024L * 256L /* 256 МиБ */)) .scheduler(Scheduler.systemScheduler()) .buildAsync(CacheLoader { val getWidth = intArrayOf(0) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt index 4c7f38cb..64222808 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/World.kt @@ -15,11 +15,15 @@ import ru.dbotthepony.kstarbound.world.api.TileView import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kstarbound.world.entities.WorldObject import ru.dbotthepony.kstarbound.world.physics.CollisionPoly +import ru.dbotthepony.kstarbound.world.physics.CollisionType import ru.dbotthepony.kstarbound.world.physics.Poly +import ru.dbotthepony.kstarbound.world.physics.getBlockPlatforms +import ru.dbotthepony.kstarbound.world.physics.getBlocksMarchingSquares import ru.dbotthepony.kvector.api.IStruct2d import ru.dbotthepony.kvector.api.IStruct2i import ru.dbotthepony.kvector.arrays.Object2DArray import ru.dbotthepony.kvector.util2d.AABB +import ru.dbotthepony.kvector.util2d.AABBi import ru.dbotthepony.kvector.vector.Vector2d import ru.dbotthepony.kvector.vector.Vector2i import java.util.concurrent.ForkJoinPool @@ -95,6 +99,7 @@ abstract class World, ChunkType : Chunk() @@ -222,62 +227,11 @@ abstract class World, ChunkType : Chunk = ImmutableList.of( - Vector2d(0.5, 0.5), - Vector2d(0.5, 1.0), - Vector2d(0.5, 1.5), - Vector2d(1.0, 1.5), - Vector2d(1.5, 1.5), - Vector2d(1.5, 1.0), - Vector2d(1.5, 0.5), - Vector2d(1.0, 0.5), - Vector2d(1.0, 1.0) - ) - - private val POLY_INDICES: ImmutableList = ImmutableList.of( - IntList.of(9, 9, 9, 9, 9, 9), - IntList.of(1, 2, 3, 9, 9, 9), - IntList.of(3, 4, 5, 9, 9, 9), - IntList.of(1, 2, 4, 5, 9, 9), - IntList.of(7, 5, 6, 9, 9, 9), - IntList.of(1, 2, 3, 5, 6, 7), - IntList.of(7, 3, 4, 6, 9, 9), - IntList.of(1, 2, 4, 6, 7, 9), - IntList.of(0, 1, 7, 9, 9, 9), - IntList.of(0, 2, 3, 7, 9, 9), - IntList.of(0, 1, 3, 4, 5, 7), - IntList.of(0, 2, 4, 5, 7, 9), - IntList.of(0, 1, 5, 6, 9, 9), - IntList.of(0, 2, 3, 5, 6, 9), - IntList.of(0, 1, 3, 4, 6, 9), - IntList.of(0, 2, 4, 6, 9, 9), - // special cases for squared off top corners - IntList.of(5, 6, 7, 8, 9, 9), // top left corner - IntList.of(0, 1, 8, 7, 9, 9), // top right corner - // special cases for hollowed out bottom corners - IntList.of(0, 2, 3, 8, 9, 9), // lower left corner part 1 - IntList.of(0, 8, 5, 6, 9, 9), // lower left corner part 2 - IntList.of(0, 1, 8, 6, 9, 9), // lower right corner part 1 - IntList.of(6, 8, 3, 4, 9, 9) // lower right corner part 2 - ) - - private val BLOCK_POLY = Poly(listOf(Vector2d.ZERO, Vector2d(0.0, 1.0), Vector2d(1.0, 1.0), Vector2d(1.0, 0.0))) - private val PEAK = Poly(listOf(Vector2d(0.0, 0.0), Vector2d(0.5, 1.0), Vector2d(1.0, 0.0))) - private val PX_NY = Poly(listOf(Vector2d(0.0, 0.0), Vector2d(0.0, 1.0), Vector2d(1.0, 0.0))) - private val NX_NY = Poly(listOf(Vector2d(-1.0, 0.0), Vector2d(0.0, 1.0), Vector2d(0.0, 0.0))) - } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileAccess.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileAccess.kt index 1e280077..05817424 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileAccess.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/ITileAccess.kt @@ -1,12 +1,18 @@ package ru.dbotthepony.kstarbound.world.api +import ru.dbotthepony.kstarbound.world.physics.CollisionType +import ru.dbotthepony.kstarbound.world.physics.CollisionTypeGetter import ru.dbotthepony.kvector.api.IStruct2i // for getting tiles directly, avoiding manual layer specification -interface ITileAccess : ICellAccess { +interface ITileAccess : ICellAccess, CollisionTypeGetter { // relative - fun getTile(x: Int, y: Int): AbstractTileState? + fun getTile(x: Int, y: Int): AbstractTileState fun getTile(pos: IStruct2i) = getTile(pos.component1(), pos.component2()) - fun getTileDirect(x: Int, y: Int): AbstractTileState? + fun getTileDirect(x: Int, y: Int): AbstractTileState fun getTileDirect(pos: IStruct2i) = getTile(pos.component1(), pos.component2()) + + override fun collisionType(x: Int, y: Int): CollisionType { + return getTile(x, y).material.value.collisionKind + } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/TileView.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/TileView.kt index 2a65f795..d2409bd7 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/TileView.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/api/TileView.kt @@ -2,22 +2,22 @@ package ru.dbotthepony.kstarbound.world.api sealed class TileView(parent: ICellAccess) : ITileAccess, ICellAccess by parent { class Foreground(parent: ICellAccess) : TileView(parent) { - override fun getTile(x: Int, y: Int): AbstractTileState? { - return getCell(x, y)?.foreground + override fun getTile(x: Int, y: Int): AbstractTileState { + return getCell(x, y).foreground } - override fun getTileDirect(x: Int, y: Int): AbstractTileState? { - return getCellDirect(x, y)?.foreground + override fun getTileDirect(x: Int, y: Int): AbstractTileState { + return getCellDirect(x, y).foreground } } class Background(parent: ICellAccess) : TileView(parent) { - override fun getTile(x: Int, y: Int): AbstractTileState? { - return getCell(x, y)?.background + override fun getTile(x: Int, y: Int): AbstractTileState { + return getCell(x, y).background } - override fun getTileDirect(x: Int, y: Int): AbstractTileState? { - return getCellDirect(x, y)?.background + override fun getTileDirect(x: Int, y: Int): AbstractTileState { + return getCellDirect(x, y).background } } } \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt index 3c082725..460e6de8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt @@ -16,6 +16,7 @@ import ru.dbotthepony.kvector.vector.RGBAColor import ru.dbotthepony.kvector.vector.Vector2d import java.util.EnumSet import kotlin.concurrent.withLock +import kotlin.math.absoluteValue abstract class Entity(val world: World<*, *>) { var chunk: Chunk<*, *>? = null @@ -202,7 +203,7 @@ abstract class Entity(val world: World<*, *>) { // impulse? velocity += max.data.velocity * gravityDot * dt // friction - velocity *= 1.0 - gravityDot * 0.08 + velocity *= 1.0 - gravityDot.absoluteValue * 0.08 onTouch(response, max.axis, max.data) } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Functions.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Functions.kt new file mode 100644 index 00000000..85ccdbde --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Functions.kt @@ -0,0 +1,182 @@ +package ru.dbotthepony.kstarbound.world.physics + +import com.google.common.collect.ImmutableList +import it.unimi.dsi.fastutil.ints.IntList +import ru.dbotthepony.kvector.vector.Vector2d + +private val POLY_VERTICES: ImmutableList = ImmutableList.of( + Vector2d(0.5, 0.5), + Vector2d(0.5, 1.0), + Vector2d(0.5, 1.5), + Vector2d(1.0, 1.5), + Vector2d(1.5, 1.5), + Vector2d(1.5, 1.0), + Vector2d(1.5, 0.5), + Vector2d(1.0, 0.5), + Vector2d(1.0, 1.0) +) + +private val POLIES = listOf( + IntList.of(1, 2, 3), + IntList.of(3, 4, 5), + IntList.of(1, 2, 4, 5), + IntList.of(7, 5, 6), + IntList.of(1, 2, 3, 5, 6, 7), + IntList.of(7, 3, 4, 6), + IntList.of(1, 2, 4, 6, 7), + IntList.of(0, 1, 7), + IntList.of(0, 2, 3, 7), + IntList.of(0, 1, 3, 4, 5, 7), + IntList.of(0, 2, 4, 5, 7), + IntList.of(0, 1, 5, 6), + IntList.of(0, 2, 3, 5, 6), + IntList.of(0, 1, 3, 4, 6), + IntList.of(0, 2, 4, 6), + // special cases for squared off top corners + IntList.of(5, 6, 7, 8), // top left corner + IntList.of(0, 1, 8, 7), // top right corner + // special cases for hollowed out bottom corners + IntList.of(0, 2, 3, 8), // lower left corner part 1 + IntList.of(0, 8, 5, 6), // lower left corner part 2 + IntList.of(0, 1, 8, 6), // lower right corner part 1 + IntList.of(6, 8, 3, 4) // lower right corner part 2 +).map { Poly(it.map { POLY_VERTICES[it] }) } + +fun interface CollisionTypeGetter { + fun collisionType(x: Int, y: Int): CollisionType +} + +private fun MutableList.addBlock(x: Int, y: Int, getter: CollisionTypeGetter, index: Int) { + val poly = POLIES.getOrNull(index - 1) ?: return + + add(CollisionPoly( + poly + Vector2d(x.toDouble(), y.toDouble()), + getter.collisionType(x, y).coerceAtLeast(getter.collisionType(x + 1, y)).coerceAtLeast(getter.collisionType(x + 1, y + 1)).coerceAtLeast(getter.collisionType(x, y + 1)) + )) +} + +private fun MutableList.addBlock(poly: List, kind: CollisionType) { + add(CollisionPoly( + Poly(poly), + kind + )) +} + +fun getBlockPlatforms(x: Int, y: Int, getter: CollisionTypeGetter, kind: CollisionType, target: MutableList = arrayListOf()): MutableList { + // This was once simple and elegant and made sense but then I made it + // match the actual platform rendering more closely and now it's a big + // shitty pile of special cases again. RIP. + + if (getter.collisionType(x, y) != kind) return target + + val right = getter.collisionType(x + 1, y) == kind + val left = getter.collisionType(x - 1, y) == kind + + val downRight = getter.collisionType(x + 1, y - 1) == kind && getter.collisionType(x + 1, y) != kind + val downLeft = getter.collisionType(x - 1, y - 1) == kind && getter.collisionType(x - 1, y) != kind + + val upRight = getter.collisionType(x + 1, y + 1) == kind && !left && !right + val upLeft = getter.collisionType(x - 1, y + 1) == kind && !left && !right + + val above = getter.collisionType(x, y + 1) == kind + val below = getter.collisionType(x, y - 1) == kind + + val dx = x.toDouble() + val dy = y.toDouble() + + if (downRight && downLeft && upRight && upLeft) { + target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind) + target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind) + } else if (above && below) { + target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx + 1, dy + 1)), kind) + } else if (upLeft && downLeft && !upRight && !downRight) { + target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind) + } else if (upRight && downRight && !upLeft && !upRight) { // TODO: figure out what original starbound devs meant by this condition + target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind) + } else if (upRight && downLeft) { + target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind) + + // special case block for connecting flat platform above + if (above && getter.collisionType(x + 1, y + 1) == kind) + target.addBlock(listOf(Vector2d(dx + 1, dy + 1), Vector2d(dx + 2, dy + 2)), kind) + } else if (upLeft && downRight) { + target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind) + + // special case block for connecting flat platform above + if (above && getter.collisionType(x - 1, y + 1) == kind) + target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx - 1, dy + 2)), kind) + } else if (above && !downRight && !downLeft) { + target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx + 1, dy + 1)), kind) + } else if (upLeft && !upRight) { + target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind) + } else if (upRight && !upLeft) { + target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind) + } else if (downRight && (left || !below)) { + target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind) + } else if (downLeft && (right || !below)) { + target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind) + } else { + target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx + 1, dy + 1)), kind) + } + + return target +} + +fun getBlocksMarchingSquares(x: Int, y: Int, getter: CollisionTypeGetter, kind: CollisionType, target: MutableList = arrayListOf()): MutableList { + var neighborMask = 0 + + if (getter.collisionType(x, y + 1) >= kind) neighborMask = neighborMask or 1 + if (getter.collisionType(x + 1, y + 1) >= kind) neighborMask = neighborMask or 2 + if (getter.collisionType(x + 1, y) >= kind) neighborMask = neighborMask or 4 + if (getter.collisionType(x, y) >= kind) neighborMask = neighborMask or 8 + + when (neighborMask) { + 4 -> { + if (getter.collisionType(x + 2, y) >= kind && + getter.collisionType(x + 2, y + 1) < kind && + getter.collisionType(x, y - 1) < kind) { + + target.addBlock(x, y, getter, 16) + return target + } + } + + 8 -> { + if (getter.collisionType(x - 1, y) >= kind && + getter.collisionType(x - 1, y + 1) < kind && + getter.collisionType(x + 1, y - 1) < kind) { + + target.addBlock(x, y, getter, 17) + return target + } + } + + 13 -> { + if (getter.collisionType(x, y + 2) >= kind && + getter.collisionType(x + 1, y + 2) < kind && + getter.collisionType(x + 2, y) >= kind) { + + target.addBlock(x, y, getter, 18) + target.addBlock(x, y, getter, 19) + return target + } + } + + 14 -> { + if (getter.collisionType(x, y + 2) < kind && + getter.collisionType(x + 1, y + 2) >= kind && + getter.collisionType(x - 1, y) >= kind) { + + target.addBlock(x, y, getter, 20) + target.addBlock(x, y, getter, 21) + return target + } + } + } + + if (neighborMask != 0) { + target.addBlock(x, y, getter, neighborMask) + } + + return target +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Poly.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Poly.kt index 5515a022..c24296ca 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Poly.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Poly.kt @@ -22,7 +22,7 @@ import ru.dbotthepony.kvector.vector.Vector2d import kotlin.math.absoluteValue private fun calculateEdges(points: List): ImmutableList { - require(points.size >= 3) { "Provided poly is invalid (only ${points.size} points are defined)" } + require(points.size >= 2) { "Provided poly is invalid (only ${points.size} points are defined)" } val edges = ImmutableList.Builder()