Universe thread, more synchronization (to allow more pararllel code), initiate player movement test once again
This commit is contained in:
parent
8af66ff359
commit
1526a1c127
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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<RenderRegion>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,20 @@ class KOptional<T> 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<T> {
|
||||
if (isPresent) {
|
||||
block.invoke(value)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
inline fun ifNotPresent(block: () -> Unit): KOptional<T> {
|
||||
if (!isPresent) {
|
||||
block.invoke()
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
inline fun <R> map(block: (T) -> R): KOptional<R> {
|
||||
|
@ -142,7 +142,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
protected open fun onEntityRemoved(entity: Entity) { }
|
||||
|
||||
fun addEntity(entity: Entity) {
|
||||
world.entityListsLock.withLock {
|
||||
world.lock.withLock {
|
||||
if (!entities.add(entity)) {
|
||||
throw IllegalArgumentException("Already having having entity $entity")
|
||||
}
|
||||
@ -153,7 +153,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
}
|
||||
|
||||
fun transferEntity(entity: Entity, otherChunk: Chunk<*, *>) {
|
||||
world.entityListsLock.withLock {
|
||||
world.lock.withLock {
|
||||
if (otherChunk == this)
|
||||
throw IllegalArgumentException("what?")
|
||||
|
||||
@ -176,7 +176,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
||||
}
|
||||
|
||||
fun removeEntity(entity: Entity) {
|
||||
world.entityListsLock.withLock {
|
||||
world.lock.withLock {
|
||||
if (!entities.remove(entity)) {
|
||||
throw IllegalArgumentException("Already not having entity $entity")
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package ru.dbotthepony.kstarbound.world
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import it.unimi.dsi.fastutil.ints.IntList
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet
|
||||
@ -16,19 +14,18 @@ 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
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.random.RandomGenerator
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, ChunkType>>(
|
||||
val seed: Long,
|
||||
@ -117,7 +114,14 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
|
||||
override fun compute(x: Int, y: Int): ChunkType? {
|
||||
if (!this@World.x.inBoundsChunk(x) || !this@World.y.inBoundsChunk(y)) return null
|
||||
return map[ChunkPos.toLong(x, y)] ?: create(x, y).also { map[ChunkPos.toLong(x, y)] = it }
|
||||
|
||||
val index = ChunkPos.toLong(x, y)
|
||||
|
||||
val get = map[index] ?: lock.withLock {
|
||||
map[index] ?: create(x, y).also { map[index] = it }
|
||||
}
|
||||
|
||||
return get
|
||||
}
|
||||
|
||||
override fun setCell(x: Int, y: Int, cell: AbstractCell): Boolean {
|
||||
@ -126,7 +130,14 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
val iy = this@World.y.cell(y)
|
||||
val cx = this@World.x.chunkFromCell(ix)
|
||||
val cy = this@World.y.chunkFromCell(iy)
|
||||
return (map[ChunkPos.toLong(cx, cy)] ?: create(cx, cy).also { map[ChunkPos.toLong(cx, cy)] = it }).setCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK, cell)
|
||||
|
||||
val index = ChunkPos.toLong(cx, cy)
|
||||
|
||||
val get = map[index] ?: lock.withLock {
|
||||
map[index] ?: create(cx, cy).also { map[index] = it }
|
||||
}
|
||||
|
||||
return get.setCell(ix and CHUNK_SIZE_MASK, iy and CHUNK_SIZE_MASK, cell)
|
||||
}
|
||||
|
||||
override fun remove(x: Int, y: Int) {
|
||||
@ -138,7 +149,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
private val map = Object2DArray.nulls<ChunkType>(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<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
var gravity = Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION)
|
||||
abstract val isClient: Boolean
|
||||
|
||||
// used by world chunks to synchronize entity additions/transfer
|
||||
val entityListsLock = ReentrantLock()
|
||||
// used to synchronize read/writes to various world state stuff/memory structure
|
||||
val lock = ReentrantLock()
|
||||
|
||||
fun think() {
|
||||
try {
|
||||
mailbox.executeQueuedTasks()
|
||||
val entities = ObjectArrayList(entities)
|
||||
val entities = lock.withLock { ObjectArrayList(entities) }
|
||||
ForkJoinPool.commonPool().submit(ParallelPerform(entities.spliterator(), Entity::move)).join()
|
||||
mailbox.executeQueuedTasks()
|
||||
|
||||
@ -195,7 +206,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
ent.thinkServer()
|
||||
}
|
||||
|
||||
val objects = ObjectArrayList(objects)
|
||||
mailbox.executeQueuedTasks()
|
||||
val objects = lock.withLock { ObjectArrayList(objects) }
|
||||
|
||||
for (ent in objects) {
|
||||
ent.thinkShared()
|
||||
|
@ -38,7 +38,7 @@ abstract class Entity(val world: World<*, *>) {
|
||||
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) {
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user