diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 928c794c..cc88a75f 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -13,6 +13,7 @@ import ru.dbotthepony.kstarbound.json.VersionedJson import ru.dbotthepony.kstarbound.io.readVarInt import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.world.api.MutableCell +import ru.dbotthepony.kstarbound.world.entities.PlayerEntity import ru.dbotthepony.kstarbound.world.entities.WorldObject import ru.dbotthepony.kvector.vector.Vector2d import java.io.BufferedInputStream @@ -68,13 +69,20 @@ fun main() { Starbound.terminateLoading = true } + var ply: PlayerEntity? = null + Starbound.onInitialize { + ply = PlayerEntity(client.world!!) + + ply!!.position = Vector2d(225.0, 745.0) + ply!!.spawn() + //for (chunkX in 17 .. 18) { //for (chunkX in 14 .. 24) { for (chunkX in 0 .. 100) { //for (chunkX in 0 .. 17) { // for (chunkY in 21 .. 21) { - for (chunkY in 18 .. 24) { + for (chunkY in 0 .. 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())) @@ -121,7 +129,7 @@ fun main() { val rand = Random() - for (i in 0 until 128) { + for (i in 0 until 0) { val item = ItemEntity(client.world!!, Registries.items.keys.values.random().value) item.position = Vector2d(225.0 - i, 785.0) @@ -177,23 +185,22 @@ fun main() { } while (client.renderFrame()) { - Starbound.pollCallbacks() - - //ent.think(client.frameRenderTime) - //client.camera.pos.x = ent.position.x.toFloat() - //client.camera.pos.y = ent.position.y.toFloat() - - client.camera.pos += Vector2d( + /*client.camera.pos += Vector2d( (if (client.input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0), (if (client.input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) - ) + )*/ - //println(client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1) + if (ply != null) { + client.camera.pos = ply!!.position + + ply!!.velocity += Vector2d( + (if (client.input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0), + (if (client.input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + ) * 8.0 + } if (client.input.KEY_ESCAPE_PRESSED) { glfwSetWindowShouldClose(client.window, true) } - - //ent.wantsToDuck = client.input.KEY_DOWN_DOWN } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt index 579ad95d..31c0cc92 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Starbound.kt @@ -53,6 +53,7 @@ import ru.dbotthepony.kstarbound.util.JVMTimeSource import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.util.SBPattern import ru.dbotthepony.kstarbound.util.HashTableInterner +import ru.dbotthepony.kstarbound.util.MailboxExecutorService import ru.dbotthepony.kstarbound.util.WriteOnce import ru.dbotthepony.kstarbound.util.filterNotNull import ru.dbotthepony.kstarbound.util.set @@ -63,6 +64,7 @@ import java.lang.ref.Cleaner import java.text.DateFormat import java.util.concurrent.ForkJoinPool import java.util.concurrent.ForkJoinTask +import java.util.concurrent.locks.LockSupport import java.util.function.BiConsumer import java.util.function.BinaryOperator import java.util.function.Function @@ -76,8 +78,16 @@ object Starbound : ISBFileLocator { const val TICK_TIME_ADVANCE = 0.01666666666666664 const val TICK_TIME_ADVANCE_NANOS = 16_666_666L + val thread = Thread(::universeThread, "Starbound Universe") + val mailbox = MailboxExecutorService(thread) + + init { + thread.isDaemon = true + thread.start() + } + val CLEANER: Cleaner = Cleaner.create { - val t = Thread(it, "Starbound Global Cleaner Thread") + val t = Thread(it, "Starbound Global Cleaner") t.isDaemon = true t.priority = 2 t @@ -264,6 +274,7 @@ object Starbound : ISBFileLocator { } } + @Volatile var initializing = false private set var initialized = false @@ -452,9 +463,9 @@ object Starbound : ISBFileLocator { } initializing = true - Thread({ doInitialize(log, parallel) }, "Asset Loader").also { - it.isDaemon = true - it.start() + + mailbox.submit { + doInitialize(log, parallel) } } @@ -466,13 +477,19 @@ object Starbound : ISBFileLocator { } } - fun pollCallbacks() { - if (initialized && initCallbacks.isNotEmpty()) { - for (callback in initCallbacks) { - callback() + private fun universeThread() { + while (true) { + mailbox.executeQueuedTasks() + + if (initialized && initCallbacks.isNotEmpty()) { + for (callback in initCallbacks) { + callback() + } + + initCallbacks.clear() } - initCallbacks.clear() + LockSupport.park() } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt index 8f19fc62..0083091f 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/StarboundClient.kt @@ -80,6 +80,7 @@ import java.util.concurrent.locks.LockSupport import java.util.concurrent.locks.ReentrantLock import java.util.function.IntConsumer import kotlin.collections.ArrayList +import kotlin.concurrent.withLock import kotlin.math.absoluteValue import kotlin.math.roundToInt @@ -818,9 +819,11 @@ class StarboundClient : Closeable { viewportLighting.clear() val viewportLightingMem = viewportLightingMem - world.addLayers( - layers = layers, - size = viewportRectangle) + world.lock.withLock { + world.addLayers( + layers = layers, + size = viewportRectangle) + } if (viewportLightingMem != null && !fullbright) { val spos = screenToWorld(mouseCoordinates) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientWorld.kt index 6f3ae8c6..412ddca8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/world/ClientWorld.kt @@ -29,6 +29,7 @@ import ru.dbotthepony.kvector.vector.Vector2f import ru.dbotthepony.kvector.vector.Vector2i import java.util.concurrent.Callable import java.util.concurrent.Future +import kotlin.concurrent.withLock class ClientWorld( val client: StarboundClient, @@ -204,11 +205,7 @@ class ClientWorld( val renderRegions = Long2ObjectOpenHashMap() fun renderRegionKey(x: Int, y: Int): Long { - if (size == null) { - return x.toLong() shl 32 or y.toLong() - } else { - return positiveModulo(x, renderRegionsX).toLong() shl 32 or positiveModulo(y, renderRegionsY).toLong() - } + return positiveModulo(x, renderRegionsX).toLong() shl 32 or positiveModulo(y, renderRegionsY).toLong() } /** @@ -242,13 +239,18 @@ class ClientWorld( val index = renderRegionKey(ix, iy) if (seen.add(index)) { - renderRegions[index]?.let(action) + lock.withLock { + renderRegions[index]?.let(action) + } } } } else { val ix = pos.component1() / renderRegionWidth val iy = pos.component2() / renderRegionHeight - renderRegions[renderRegionKey(ix, iy)]?.let(action) + + lock.withLock { + renderRegions[renderRegionKey(ix, iy)]?.let(action) + } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/util/KOptional.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/util/KOptional.kt index f5a1f61f..a144bdb8 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/util/KOptional.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/util/KOptional.kt @@ -25,10 +25,20 @@ class KOptional private constructor(private val _value: T, val isPresent: Boo throw NoSuchElementException("No value is present") } - inline fun ifPresent(block: (T) -> Unit) { + inline fun ifPresent(block: (T) -> Unit): KOptional { if (isPresent) { block.invoke(value) } + + return this + } + + inline fun ifNotPresent(block: () -> Unit): KOptional { + if (!isPresent) { + block.invoke() + } + + return this } inline fun map(block: (T) -> R): KOptional { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt index f5d68009..a5a88b69 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt @@ -142,7 +142,7 @@ abstract class Chunk, This : Chunk, This : Chunk) { - world.entityListsLock.withLock { + world.lock.withLock { if (otherChunk == this) throw IllegalArgumentException("what?") @@ -176,7 +176,7 @@ abstract class Chunk, This : Chunk, ChunkType : Chunk>( val seed: Long, @@ -117,7 +114,14 @@ abstract class World, ChunkType : Chunk, ChunkType : Chunk, ChunkType : Chunk(divideUp(size.x, CHUNK_SIZE), divideUp(size.y, CHUNK_SIZE)) private fun getRaw(x: Int, y: Int): ChunkType { - return map[x, y] ?: create(x, y).also { map[x, y] = it } + return map[x, y] ?: lock.withLock { map[x, y] ?: create(x, y).also { map[x, y] = it } } } override fun compute(x: Int, y: Int): ChunkType? { @@ -176,13 +187,13 @@ abstract class World, ChunkType : Chunk, ChunkType : Chunk) { val oldChunk = field field = value - world.entityListsLock.withLock { + world.lock.withLock { if (oldChunk == null && value != null) { world.orphanedEntities.remove(this) value.addEntity(this) @@ -107,11 +107,14 @@ abstract class Entity(val world: World<*, *>) { throw IllegalStateException("Already spawned") isSpawned = true - world.entities.add(this) - chunk = world.chunkMap[world.chunkFromCell(position)] - if (chunk == null) { - world.orphanedEntities.add(this) + world.mailbox.execute { + world.entities.add(this) + chunk = world.chunkMap[world.chunkFromCell(position)] + + if (chunk == null) { + world.orphanedEntities.add(this) + } } } @@ -123,8 +126,10 @@ abstract class Entity(val world: World<*, *>) { mailbox.shutdownNow() if (isSpawned) { - world.entities.remove(this) - chunk?.removeEntity(this) + world.mailbox.execute { + world.entities.remove(this) + chunk?.removeEntity(this) + } } } @@ -163,7 +168,7 @@ abstract class Entity(val world: World<*, *>) { return } - val steps = roundTowardsPositiveInfinity(velocity.length / 30.0 / hitboxes.stream().map { it.aabb }.reduce(AABB::combine).get().let { it.width.coerceAtLeast(it.height).coerceAtLeast(0.1) }) + val steps = roundTowardsPositiveInfinity(velocity.length / 10.0 / hitboxes.stream().map { it.aabb }.reduce(AABB::combine).get().let { it.width.coerceAtLeast(it.height).coerceAtLeast(0.1) }) val dt = Starbound.TICK_TIME_ADVANCE / steps for (step in 0 until steps) { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/PlayerEntity.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/PlayerEntity.kt new file mode 100644 index 00000000..03ddb93b --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/PlayerEntity.kt @@ -0,0 +1,19 @@ +package ru.dbotthepony.kstarbound.world.entities + +import ru.dbotthepony.kstarbound.GlobalDefaults +import ru.dbotthepony.kstarbound.Starbound +import ru.dbotthepony.kstarbound.world.World +import ru.dbotthepony.kstarbound.world.physics.Poly + +class PlayerEntity(world: World<*, *>) : Entity(world) { + init { + movementParameters = GlobalDefaults.playerMovementParameters + + GlobalDefaults.playerMovementParameters.standingPoly.ifPresent { + //hitboxes.add(it) + hitboxes.add(Starbound.gson.fromJson("""[ [0.5625, 1.9375], [1.0625, 1.4375], [1.0625, -2.5625], [0.5625, -3.0625], [-0.5625, -3.0625], [-1.0625, -2.5625], [-1.0625, 1.4375], [-0.5625, 1.9375] ]""", Poly::class.java)) + }.ifNotPresent { + throw IllegalStateException("No player collision poly") + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt index 4bc6abe0..11cc03e6 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/WorldObject.kt @@ -23,6 +23,7 @@ import ru.dbotthepony.kstarbound.world.World import ru.dbotthepony.kstarbound.world.api.TileColor import ru.dbotthepony.kvector.vector.RGBAColor import ru.dbotthepony.kvector.vector.Vector2i +import kotlin.concurrent.withLock open class WorldObject( val world: World<*, *>, @@ -48,7 +49,7 @@ open class WorldObject( } } - val mailbox = MailboxExecutorService() + val mailbox = MailboxExecutorService(world.mailbox.thread) // // internal runtime properties @@ -129,16 +130,22 @@ open class WorldObject( fun spawn() { if (isSpawned) return isSpawned = true - world.objects.add(this) - innerSpawn() - invalidate() + + world.mailbox.execute { + world.objects.add(this) + innerSpawn() + invalidate() + } } open fun remove() { if (isRemoved || !isSpawned) return isRemoved = true - check(world.objects.remove(this)) - innerRemove() + + world.mailbox.execute { + check(world.objects.remove(this)) + innerRemove() + } } open fun thinkShared() {