Misc fixes

This commit is contained in:
DBotThePony 2023-10-18 22:36:46 +07:00
parent b1b5b598f0
commit ba0d792e83
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 122 additions and 48 deletions

View File

@ -125,9 +125,11 @@ fun main() {
for (i in 0 .. 128) {
val item = ItemEntity(client.world!!, Registries.items.values.random().value)
item.position = Vector2d(225.0 - i, 685.0)
item.position = Vector2d(225.0 - i, 785.0)
item.spawn()
item.velocity = Vector2d(rand.nextDouble() * 32.0 - 16.0, rand.nextDouble() * 32.0 - 16.0)
item.mailbox.scheduleAtFixedRate({ item.velocity += Vector2d(rand.nextDouble() * 32.0 - 16.0, rand.nextDouble() * 32.0 - 16.0) }, 1000 + rand.nextLong(-100, 100), 1000 + rand.nextLong(-100, 100), TimeUnit.MILLISECONDS)
//item.movement.applyVelocity(Vector2d(rand.nextDouble() * 1000.0 - 500.0, rand.nextDouble() * 1000.0 - 500.0))
}

View File

@ -46,6 +46,9 @@ class ClientWorld(
throw RuntimeException("unreachable code")
}
override val isClient: Boolean
get() = true
val renderRegionWidth = if (size == null) 16 else determineChunkSize(size.x)
val renderRegionHeight = if (size == null) 16 else determineChunkSize(size.y)
val renderRegionsX = if (size == null) 0 else size.x / renderRegionWidth
@ -304,18 +307,7 @@ class ClientWorld(
}
override fun thinkInner() {
val entities = ObjectArrayList(entities)
for (ent in entities) {
ent.think()
}
val objects = ObjectArrayList(objects)
for (ent in objects) {
ent.thinkShared()
ent.thinkClient()
}
}
companion object {

View File

@ -22,6 +22,8 @@ interface BaseMovementParameters {
// horizontal ground without losing horizontal speed.
val enableSurfaceSlopeCorrection: KOptional<Boolean>
val slopeSlidingFactor: KOptional<Double>
// ignored
val maxMovementPerStep: KOptional<Double>
val maximumCorrection: KOptional<Double>
val speedLimit: KOptional<Double>

View File

@ -16,9 +16,6 @@ import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kvector.arrays.Object2DArray
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Vector2d
import java.util.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.collections.ArrayList
import kotlin.concurrent.withLock
/**
@ -261,7 +258,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
protected open fun onEntityRemoved(entity: Entity) { }
fun addEntity(entity: Entity) {
world.entityMoveLock.withLock {
world.entityListsLock.withLock {
if (!entities.add(entity)) {
throw IllegalArgumentException("Already having having entity $entity")
}
@ -272,7 +269,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
}
fun transferEntity(entity: Entity, otherChunk: Chunk<*, *>) {
world.entityMoveLock.withLock {
world.entityListsLock.withLock {
if (otherChunk == this)
throw IllegalArgumentException("what?")
@ -295,7 +292,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
}
fun removeEntity(entity: Entity) {
world.entityMoveLock.withLock {
world.entityListsLock.withLock {
if (!entities.remove(entity)) {
throw IllegalArgumentException("Already not having entity $entity")
}

View File

@ -1,6 +1,9 @@
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
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import ru.dbotthepony.kstarbound.math.*
@ -12,6 +15,7 @@ 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.kvector.api.IStruct2d
import ru.dbotthepony.kvector.api.IStruct2i
@ -233,16 +237,39 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
val random: RandomGenerator = RandomGenerator.of("Xoroshiro128PlusPlus")
var gravity = Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION)
abstract val isClient: Boolean
// used by world chunks to synchronize entity additions/transfer
val entityMoveLock = ReentrantLock()
val entityListsLock = ReentrantLock()
fun think() {
try {
mailbox.executeQueuedTasks()
val entities = ObjectArrayList(entities)
ForkJoinPool.commonPool().submit(ParallelPerform(entities.spliterator(), Entity::move)).join()
mailbox.executeQueuedTasks()
for (ent in entities) {
ent.thinkShared()
if (isClient)
ent.thinkClient()
else
ent.thinkServer()
}
val objects = ObjectArrayList(objects)
for (ent in objects) {
ent.thinkShared()
if (isClient)
ent.thinkClient()
else
ent.thinkServer()
}
mailbox.executeQueuedTasks()
thinkInner()
} catch(err: Throwable) {
throw RuntimeException("Ticking world $this", err)
@ -251,16 +278,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
protected abstract fun thinkInner()
/**
* Сущности, у которых нет чанка, но они находятся в этом мире
*/
val orphanedEntities = ReferenceOpenHashSet<Entity>()
/**
* Сущности, находящиеся в этом мире
*/
val entities = ReferenceLinkedOpenHashSet<Entity>()
val objects = ReferenceLinkedOpenHashSet<WorldObject>()
protected abstract fun chunkFactory(pos: ChunkPos): ChunkType
@ -285,6 +304,48 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
}
companion object {
private val POLY_VERTICES: ImmutableList<Vector2d> = 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<IntList> = 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)))
}
}

View File

@ -12,8 +12,10 @@ import ru.dbotthepony.kstarbound.world.physics.CollisionPoly
import ru.dbotthepony.kstarbound.world.physics.CollisionType
import ru.dbotthepony.kstarbound.world.physics.Poly
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.RGBAColor
import ru.dbotthepony.kvector.vector.Vector2d
import java.util.EnumSet
import kotlin.concurrent.withLock
abstract class Entity(val world: World<*, *>) {
var chunk: Chunk<*, *>? = null
@ -35,14 +37,16 @@ abstract class Entity(val world: World<*, *>) {
val oldChunk = field
field = value
if (oldChunk == null && value != null) {
world.orphanedEntities.remove(this)
value.addEntity(this)
} else if (oldChunk != null && value == null) {
world.orphanedEntities.add(this)
oldChunk.removeEntity(this)
} else if (oldChunk != null && value != null) {
value.transferEntity(this, oldChunk)
world.entityListsLock.withLock {
if (oldChunk == null && value != null) {
world.orphanedEntities.remove(this)
value.addEntity(this)
} else if (oldChunk != null && value == null) {
world.orphanedEntities.add(this)
oldChunk.removeEntity(this)
} else if (oldChunk != null && value != null) {
value.transferEntity(this, oldChunk)
}
}
}
@ -126,16 +130,17 @@ abstract class Entity(val world: World<*, *>) {
/**
* this function is executed sequentially
*/
fun think() {
if (!isSpawned) {
throw IllegalStateException("Tried to think before spawning in world")
}
open fun thinkShared() {
mailbox.executeQueuedTasks()
thinkInner()
}
protected abstract fun thinkInner()
open fun thinkClient() {
}
open fun thinkServer() {
}
/**
* this function is executed in parallel
@ -146,6 +151,12 @@ abstract class Entity(val world: World<*, *>) {
var physicsSleepTicks = physicsSleepTicks
velocity += world.gravity * Starbound.TICK_TIME_ADVANCE
movementParameters.speedLimit.ifPresent {
if (velocity.length > it) {
velocity = velocity.unitVector * it
}
}
if (hitboxes.isEmpty()) {
position += velocity * Starbound.TICK_TIME_ADVANCE
return
@ -161,7 +172,7 @@ abstract class Entity(val world: World<*, *>) {
val localHitboxes = hitboxes.map { it + position }
val polies = world.queryCollisions(
localHitboxes.stream().map { it.aabb }.reduce(AABB::combine).get().enlarge(1.0, 1.0)
localHitboxes.stream().map { it.aabb }.reduce(AABB::combine).get().enlarge(2.0, 2.0)
).filter {
if (collisionFilterMode)
it.type in collisionFilter
@ -211,6 +222,15 @@ abstract class Entity(val world: World<*, *>) {
open fun render(client: StarboundClient = StarboundClient.current()) {
hitboxes.forEach { (it + position).render(client) }
world.queryCollisions(
hitboxes.stream().map { (it + position).aabb }.reduce(AABB::combine).get().enlarge(2.0, 2.0)
).filter {
if (collisionFilterMode)
it.type in collisionFilter
else
it.type !in collisionFilter
}.forEach { it.poly.render(client, BLOCK_COLLISION_COLOR) }
}
open var maxHealth = 0.0
@ -222,5 +242,6 @@ abstract class Entity(val world: World<*, *>) {
companion object {
const val PHYSICS_TICKS_UNTIL_SLEEP = 16
val BLOCK_COLLISION_COLOR = RGBAColor(65, 179, 217)
}
}

View File

@ -10,9 +10,4 @@ class ItemEntity(world: World<*, *>, val def: IItemDefinition) : Entity(world) {
init {
hitboxes.add(Poly(AABB.rectangle(Vector2d.ZERO, 0.75, 0.75)))
}
override fun thinkInner() {
// TODO: деспавнинг?
// просто, как бы, предметы не должны уж так сильно нагружать процессор
}
}

View File

@ -14,6 +14,7 @@ import ru.dbotthepony.kstarbound.defs.JsonDriven
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
import ru.dbotthepony.kstarbound.defs.`object`.ObjectOrientation
import ru.dbotthepony.kstarbound.util.MailboxExecutorService
import ru.dbotthepony.kstarbound.util.get
import ru.dbotthepony.kstarbound.util.set
import ru.dbotthepony.kstarbound.world.Side
@ -47,6 +48,8 @@ open class WorldObject(
}
}
val mailbox = MailboxExecutorService()
//
// internal runtime properties
//
@ -139,6 +142,7 @@ open class WorldObject(
}
open fun thinkShared() {
mailbox.executeQueuedTasks()
flickerPeriod?.update(Starbound.TICK_TIME_ADVANCE, world.random)
}