Misc fixes
This commit is contained in:
parent
b1b5b598f0
commit
ba0d792e83
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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: деспавнинг?
|
||||
// просто, как бы, предметы не должны уж так сильно нагружать процессор
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user