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) { for (i in 0 .. 128) {
val item = ItemEntity(client.world!!, Registries.items.values.random().value) 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.spawn()
item.velocity = Vector2d(rand.nextDouble() * 32.0 - 16.0, rand.nextDouble() * 32.0 - 16.0) 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)) //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") throw RuntimeException("unreachable code")
} }
override val isClient: Boolean
get() = true
val renderRegionWidth = if (size == null) 16 else determineChunkSize(size.x) val renderRegionWidth = if (size == null) 16 else determineChunkSize(size.x)
val renderRegionHeight = if (size == null) 16 else determineChunkSize(size.y) val renderRegionHeight = if (size == null) 16 else determineChunkSize(size.y)
val renderRegionsX = if (size == null) 0 else size.x / renderRegionWidth val renderRegionsX = if (size == null) 0 else size.x / renderRegionWidth
@ -304,18 +307,7 @@ class ClientWorld(
} }
override fun thinkInner() { 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 { companion object {

View File

@ -22,6 +22,8 @@ interface BaseMovementParameters {
// horizontal ground without losing horizontal speed. // horizontal ground without losing horizontal speed.
val enableSurfaceSlopeCorrection: KOptional<Boolean> val enableSurfaceSlopeCorrection: KOptional<Boolean>
val slopeSlidingFactor: KOptional<Double> val slopeSlidingFactor: KOptional<Double>
// ignored
val maxMovementPerStep: KOptional<Double> val maxMovementPerStep: KOptional<Double>
val maximumCorrection: KOptional<Double> val maximumCorrection: KOptional<Double>
val speedLimit: 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.arrays.Object2DArray
import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Vector2d import ru.dbotthepony.kvector.vector.Vector2d
import java.util.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.collections.ArrayList
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
/** /**
@ -261,7 +258,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
protected open fun onEntityRemoved(entity: Entity) { } protected open fun onEntityRemoved(entity: Entity) { }
fun addEntity(entity: Entity) { fun addEntity(entity: Entity) {
world.entityMoveLock.withLock { world.entityListsLock.withLock {
if (!entities.add(entity)) { if (!entities.add(entity)) {
throw IllegalArgumentException("Already having having entity $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<*, *>) { fun transferEntity(entity: Entity, otherChunk: Chunk<*, *>) {
world.entityMoveLock.withLock { world.entityListsLock.withLock {
if (otherChunk == this) if (otherChunk == this)
throw IllegalArgumentException("what?") throw IllegalArgumentException("what?")
@ -295,7 +292,7 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
} }
fun removeEntity(entity: Entity) { fun removeEntity(entity: Entity) {
world.entityMoveLock.withLock { world.entityListsLock.withLock {
if (!entities.remove(entity)) { if (!entities.remove(entity)) {
throw IllegalArgumentException("Already not having entity $entity") throw IllegalArgumentException("Already not having entity $entity")
} }

View File

@ -1,6 +1,9 @@
package ru.dbotthepony.kstarbound.world 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.longs.Long2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import ru.dbotthepony.kstarbound.math.* 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.Entity
import ru.dbotthepony.kstarbound.world.entities.WorldObject import ru.dbotthepony.kstarbound.world.entities.WorldObject
import ru.dbotthepony.kstarbound.world.physics.CollisionPoly 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.Poly
import ru.dbotthepony.kvector.api.IStruct2d import ru.dbotthepony.kvector.api.IStruct2d
import ru.dbotthepony.kvector.api.IStruct2i 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") val random: RandomGenerator = RandomGenerator.of("Xoroshiro128PlusPlus")
var gravity = Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION) var gravity = Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION)
abstract val isClient: Boolean
// used by world chunks to synchronize entity additions/transfer // used by world chunks to synchronize entity additions/transfer
val entityMoveLock = ReentrantLock() val entityListsLock = ReentrantLock()
fun think() { fun think() {
try { try {
mailbox.executeQueuedTasks() mailbox.executeQueuedTasks()
val entities = ObjectArrayList(entities)
ForkJoinPool.commonPool().submit(ParallelPerform(entities.spliterator(), Entity::move)).join() ForkJoinPool.commonPool().submit(ParallelPerform(entities.spliterator(), Entity::move)).join()
mailbox.executeQueuedTasks() 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() thinkInner()
} catch(err: Throwable) { } catch(err: Throwable) {
throw RuntimeException("Ticking world $this", err) 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() protected abstract fun thinkInner()
/**
* Сущности, у которых нет чанка, но они находятся в этом мире
*/
val orphanedEntities = ReferenceOpenHashSet<Entity>() val orphanedEntities = ReferenceOpenHashSet<Entity>()
/**
* Сущности, находящиеся в этом мире
*/
val entities = ReferenceLinkedOpenHashSet<Entity>() val entities = ReferenceLinkedOpenHashSet<Entity>()
val objects = ReferenceLinkedOpenHashSet<WorldObject>() val objects = ReferenceLinkedOpenHashSet<WorldObject>()
protected abstract fun chunkFactory(pos: ChunkPos): ChunkType protected abstract fun chunkFactory(pos: ChunkPos): ChunkType
@ -285,6 +304,48 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
} }
companion object { 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 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.CollisionType
import ru.dbotthepony.kstarbound.world.physics.Poly import ru.dbotthepony.kstarbound.world.physics.Poly
import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.RGBAColor
import ru.dbotthepony.kvector.vector.Vector2d import ru.dbotthepony.kvector.vector.Vector2d
import java.util.EnumSet import java.util.EnumSet
import kotlin.concurrent.withLock
abstract class Entity(val world: World<*, *>) { abstract class Entity(val world: World<*, *>) {
var chunk: Chunk<*, *>? = null var chunk: Chunk<*, *>? = null
@ -35,14 +37,16 @@ abstract class Entity(val world: World<*, *>) {
val oldChunk = field val oldChunk = field
field = value field = value
if (oldChunk == null && value != null) { world.entityListsLock.withLock {
world.orphanedEntities.remove(this) if (oldChunk == null && value != null) {
value.addEntity(this) world.orphanedEntities.remove(this)
} else if (oldChunk != null && value == null) { value.addEntity(this)
world.orphanedEntities.add(this) } else if (oldChunk != null && value == null) {
oldChunk.removeEntity(this) world.orphanedEntities.add(this)
} else if (oldChunk != null && value != null) { oldChunk.removeEntity(this)
value.transferEntity(this, oldChunk) } 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 * this function is executed sequentially
*/ */
fun think() { open fun thinkShared() {
if (!isSpawned) {
throw IllegalStateException("Tried to think before spawning in world")
}
mailbox.executeQueuedTasks() mailbox.executeQueuedTasks()
thinkInner()
} }
protected abstract fun thinkInner() open fun thinkClient() {
}
open fun thinkServer() {
}
/** /**
* this function is executed in parallel * this function is executed in parallel
@ -146,6 +151,12 @@ abstract class Entity(val world: World<*, *>) {
var physicsSleepTicks = physicsSleepTicks var physicsSleepTicks = physicsSleepTicks
velocity += world.gravity * Starbound.TICK_TIME_ADVANCE velocity += world.gravity * Starbound.TICK_TIME_ADVANCE
movementParameters.speedLimit.ifPresent {
if (velocity.length > it) {
velocity = velocity.unitVector * it
}
}
if (hitboxes.isEmpty()) { if (hitboxes.isEmpty()) {
position += velocity * Starbound.TICK_TIME_ADVANCE position += velocity * Starbound.TICK_TIME_ADVANCE
return return
@ -161,7 +172,7 @@ abstract class Entity(val world: World<*, *>) {
val localHitboxes = hitboxes.map { it + position } val localHitboxes = hitboxes.map { it + position }
val polies = world.queryCollisions( 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 { ).filter {
if (collisionFilterMode) if (collisionFilterMode)
it.type in collisionFilter it.type in collisionFilter
@ -211,6 +222,15 @@ abstract class Entity(val world: World<*, *>) {
open fun render(client: StarboundClient = StarboundClient.current()) { open fun render(client: StarboundClient = StarboundClient.current()) {
hitboxes.forEach { (it + position).render(client) } 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 open var maxHealth = 0.0
@ -222,5 +242,6 @@ abstract class Entity(val world: World<*, *>) {
companion object { companion object {
const val PHYSICS_TICKS_UNTIL_SLEEP = 16 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 { init {
hitboxes.add(Poly(AABB.rectangle(Vector2d.ZERO, 0.75, 0.75))) 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.image.SpriteReference
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
import ru.dbotthepony.kstarbound.defs.`object`.ObjectOrientation import ru.dbotthepony.kstarbound.defs.`object`.ObjectOrientation
import ru.dbotthepony.kstarbound.util.MailboxExecutorService
import ru.dbotthepony.kstarbound.util.get import ru.dbotthepony.kstarbound.util.get
import ru.dbotthepony.kstarbound.util.set import ru.dbotthepony.kstarbound.util.set
import ru.dbotthepony.kstarbound.world.Side import ru.dbotthepony.kstarbound.world.Side
@ -47,6 +48,8 @@ open class WorldObject(
} }
} }
val mailbox = MailboxExecutorService()
// //
// internal runtime properties // internal runtime properties
// //
@ -139,6 +142,7 @@ open class WorldObject(
} }
open fun thinkShared() { open fun thinkShared() {
mailbox.executeQueuedTasks()
flickerPeriod?.update(Starbound.TICK_TIME_ADVANCE, world.random) flickerPeriod?.update(Starbound.TICK_TIME_ADVANCE, world.random)
} }