Performance improvement bits
This commit is contained in:
parent
164040b45b
commit
a46269aa15
@ -25,6 +25,7 @@ import ru.dbotthepony.kstarbound.world.Universe
|
||||
import ru.dbotthepony.kstarbound.world.UniversePos
|
||||
import ru.dbotthepony.kstarbound.world.WorldGeometry
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.random.RandomGenerator
|
||||
|
||||
@ -299,17 +300,19 @@ class WorldTemplate(val geometry: WorldGeometry) {
|
||||
}
|
||||
|
||||
private val cellCache = Caffeine.newBuilder()
|
||||
.maximumSize(50_000L)
|
||||
//.expireAfterAccess(Duration.ofMinutes(1))
|
||||
//.executor(Starbound.EXECUTOR)
|
||||
//.scheduler(Starbound) // don't specify scheduler since this cache is accessed very frequently
|
||||
.maximumSize(150_000L)
|
||||
.expireAfterAccess(Duration.ofMinutes(1))
|
||||
.executor(Starbound.EXECUTOR)
|
||||
.scheduler(Starbound)
|
||||
.build<Vector2i, CellInfo> { (x, y) -> cellInfo0(x, y) }
|
||||
|
||||
fun cellInfo(x: Int, y: Int): CellInfo {
|
||||
worldLayout ?: return CellInfo(x, y)
|
||||
return cellCache.get(Vector2i(x, y))
|
||||
}
|
||||
|
||||
fun cellInfo(pos: Vector2i): CellInfo {
|
||||
worldLayout ?: return CellInfo(pos.x, pos.y)
|
||||
return cellCache.get(pos)
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.ItemDropEntity
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.function.Predicate
|
||||
@ -68,8 +69,9 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
||||
private var idleTicks = 0
|
||||
private var ticks = 0
|
||||
private val targetState = Channel<ChunkState>(Int.MAX_VALUE)
|
||||
private val permanent = ArrayList<Ticket>()
|
||||
private val permanent = CopyOnWriteArrayList<Ticket>()
|
||||
private val temporary = ObjectAVLTreeSet<TimedTicket>()
|
||||
private val temporaryList = CopyOnWriteArrayList<TimedTicket>()
|
||||
private var nextTicketID = 0
|
||||
// ticket lock because tickets *could* be canceled (or created) concurrently
|
||||
// BUT, front-end ticket creation in ServerWorld is expected to be called only on ServerWorld's thread
|
||||
@ -285,29 +287,13 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
||||
override fun cellChanges(x: Int, y: Int, cell: ImmutableCell) {
|
||||
super.cellChanges(x, y, cell)
|
||||
|
||||
val permanent: List<Ticket>
|
||||
val temporary: List<TimedTicket>
|
||||
|
||||
ticketsLock.withLock {
|
||||
permanent = ObjectArrayList(this.permanent)
|
||||
temporary = ObjectArrayList(this.temporary)
|
||||
}
|
||||
|
||||
permanent.forEach { if (it.targetState <= state) it.listener?.onCellChanges(x, y, cell) }
|
||||
temporary.forEach { if (it.targetState <= state) it.listener?.onCellChanges(x, y, cell) }
|
||||
temporaryList.forEach { if (it.targetState <= state) it.listener?.onCellChanges(x, y, cell) }
|
||||
}
|
||||
|
||||
private fun onTileHealthUpdate(x: Int, y: Int, isBackground: Boolean, health: TileHealth) {
|
||||
val permanent: List<Ticket>
|
||||
val temporary: List<TimedTicket>
|
||||
|
||||
ticketsLock.withLock {
|
||||
permanent = ObjectArrayList(this.permanent)
|
||||
temporary = ObjectArrayList(this.temporary)
|
||||
}
|
||||
|
||||
permanent.forEach { if (it.targetState <= state) it.listener?.onTileHealthUpdate(x, y, isBackground, health) }
|
||||
temporary.forEach { if (it.targetState <= state) it.listener?.onTileHealthUpdate(x, y, isBackground, health) }
|
||||
temporaryList.forEach { if (it.targetState <= state) it.listener?.onTileHealthUpdate(x, y, isBackground, health) }
|
||||
}
|
||||
|
||||
private abstract inner class AbstractTicket(val targetState: ChunkState) : ITicket {
|
||||
@ -360,11 +346,13 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
||||
|
||||
init {
|
||||
temporary.add(this)
|
||||
temporaryList.add(this)
|
||||
this@ServerChunk.targetState.trySend(targetState)
|
||||
}
|
||||
|
||||
override fun cancel0() {
|
||||
temporary.remove(this)
|
||||
temporaryList.remove(this)
|
||||
}
|
||||
|
||||
override fun prolong(ticks: Int) {
|
||||
@ -375,7 +363,12 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
||||
|
||||
temporary.remove(this)
|
||||
expiresAt += ticks
|
||||
if (timeRemaining > 0) temporary.add(this)
|
||||
|
||||
if (timeRemaining > 0) {
|
||||
temporary.add(this)
|
||||
} else {
|
||||
temporaryList.remove(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -385,16 +378,8 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
||||
require(newState >= state) { "Tried to downgrade $this state from $state to $newState" }
|
||||
this.state = newState
|
||||
|
||||
val permanent: List<Ticket>
|
||||
val temporary: List<TimedTicket>
|
||||
|
||||
ticketsLock.withLock {
|
||||
permanent = ObjectArrayList(this.permanent)
|
||||
temporary = ObjectArrayList(this.temporary)
|
||||
}
|
||||
|
||||
permanent.forEach { if (it.targetState <= state) it.chunk.complete(this) }
|
||||
temporary.forEach { if (it.targetState <= state) it.chunk.complete(this) }
|
||||
temporaryList.forEach { if (it.targetState <= state) it.chunk.complete(this) }
|
||||
}
|
||||
|
||||
data class DamageResult(val result: TileDamageResult, val health: TileHealth? = null, val stateBefore: AbstractCell? = null)
|
||||
@ -498,6 +483,7 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
||||
val ticket = temporary.first()
|
||||
ticket.isCanceled = true
|
||||
temporary.remove(ticket)
|
||||
temporaryList.remove(ticket)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,10 @@ import it.unimi.dsi.fastutil.objects.Object2IntAVLTreeMap
|
||||
import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet
|
||||
import ru.dbotthepony.kommons.util.AABB
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kommons.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.tile.TileEntity
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.function.Predicate
|
||||
|
||||
@ -19,7 +23,7 @@ import java.util.function.Predicate
|
||||
// and allowing one entity to have multiple bounding boxes,
|
||||
// while also setting up ground for better spatial index strategies, if they
|
||||
// have to be done in the future.
|
||||
class SpatialIndex<T>(val geometry: WorldGeometry) {
|
||||
class EntityIndex(val geometry: WorldGeometry) {
|
||||
private val lock = Any()
|
||||
private val map = Long2ObjectOpenHashMap<Sector>()
|
||||
private val factory = Long2ObjectFunction { Sector(it) }
|
||||
@ -55,7 +59,7 @@ class SpatialIndex<T>(val geometry: WorldGeometry) {
|
||||
}
|
||||
}
|
||||
|
||||
inner class Entry(val value: T) : Comparable<Entry> {
|
||||
inner class Entry(val value: AbstractEntity) : Comparable<Entry> {
|
||||
private val sectors = Object2IntAVLTreeMap<Sector>()
|
||||
val id = counter.getAndIncrement()
|
||||
private val fixtures = ArrayList<Fixture>(1)
|
||||
@ -246,8 +250,8 @@ class SpatialIndex<T>(val geometry: WorldGeometry) {
|
||||
}
|
||||
}
|
||||
|
||||
fun query(rect: AABB, filter: Predicate<T> = Predicate { true }, withEdges: Boolean = true): List<T> {
|
||||
val entriesDirect = ArrayList<T>()
|
||||
fun query(rect: AABB, filter: Predicate<in AbstractEntity> = Predicate { true }, withEdges: Boolean = true): List<AbstractEntity> {
|
||||
val entriesDirect = ArrayList<AbstractEntity>()
|
||||
|
||||
iterate(rect, withEdges = withEdges, visitor = {
|
||||
if (filter.test(it)) entriesDirect.add(it)
|
||||
@ -256,23 +260,33 @@ class SpatialIndex<T>(val geometry: WorldGeometry) {
|
||||
return entriesDirect
|
||||
}
|
||||
|
||||
fun any(rect: AABB, filter: Predicate<T> = Predicate { true }, withEdges: Boolean = true): Boolean {
|
||||
fun any(rect: AABB, filter: Predicate<in AbstractEntity> = Predicate { true }, withEdges: Boolean = true): Boolean {
|
||||
return walk(rect, withEdges = withEdges, visitor = {
|
||||
if (filter.test(it)) KOptional(true) else KOptional()
|
||||
}).orElse(false)
|
||||
}
|
||||
|
||||
fun all(rect: AABB, filter: Predicate<T> = Predicate { true }, withEdges: Boolean = true): Boolean {
|
||||
fun all(rect: AABB, filter: Predicate<in AbstractEntity> = Predicate { true }, withEdges: Boolean = true): Boolean {
|
||||
return walk(rect, withEdges = withEdges, visitor = {
|
||||
if (!filter.test(it)) KOptional(false) else KOptional()
|
||||
}).orElse(true)
|
||||
}
|
||||
|
||||
fun iterate(rect: AABB, visitor: (T) -> Unit, withEdges: Boolean = true) {
|
||||
fun first(rect: AABB, filter: Predicate<in AbstractEntity> = Predicate { true }, withEdges: Boolean = true): AbstractEntity? {
|
||||
return walk(rect, withEdges = withEdges, visitor = {
|
||||
if (filter.test(it)) KOptional(it) else KOptional()
|
||||
}).orNull()
|
||||
}
|
||||
|
||||
fun tileEntityAt(pos: Vector2i): TileEntity? {
|
||||
return first(AABB(pos.toDoubleVector(), pos.toDoubleVector() + Vector2d.POSITIVE_XY), Predicate { it is TileEntity && pos in it.occupySpaces }) as TileEntity?
|
||||
}
|
||||
|
||||
fun iterate(rect: AABB, visitor: (AbstractEntity) -> Unit, withEdges: Boolean = true) {
|
||||
walk<Unit>(rect, { visitor(it); KOptional() }, withEdges)
|
||||
}
|
||||
|
||||
fun <V> walk(rect: AABB, visitor: (T) -> KOptional<V>, withEdges: Boolean = true): KOptional<V> {
|
||||
fun <V> walk(rect: AABB, visitor: (AbstractEntity) -> KOptional<V>, withEdges: Boolean = true): KOptional<V> {
|
||||
val seen = IntAVLTreeSet()
|
||||
|
||||
for (actualRegion in geometry.split(rect).first) {
|
@ -17,10 +17,7 @@ import ru.dbotthepony.kommons.util.AABB
|
||||
import ru.dbotthepony.kommons.util.AABBi
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kommons.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.Registry
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyTile
|
||||
import ru.dbotthepony.kstarbound.defs.world.WorldStructure
|
||||
import ru.dbotthepony.kstarbound.defs.world.WorldTemplate
|
||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||
@ -111,6 +108,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
private val map = Long2ObjectOpenHashMap<ChunkType>()
|
||||
// see CONCURRENT_SPARSE_CHUNK_MAP
|
||||
private val lock = Any()
|
||||
private val list = CopyOnWriteArrayList<ChunkType>()
|
||||
|
||||
override fun get(x: Int, y: Int): ChunkType? {
|
||||
if (!geometry.x.inBoundsChunk(x) || !geometry.y.inBoundsChunk(y)) return null
|
||||
@ -130,10 +128,18 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
|
||||
if (CONCURRENT_SPARSE_CHUNK_MAP) {
|
||||
synchronized(lock) {
|
||||
return map[index] ?: chunkFactory(ChunkPos(x, y)).also { map[index] = it; onChunkCreated(it) }
|
||||
return map[index] ?: chunkFactory(ChunkPos(x, y)).also {
|
||||
list.add(it)
|
||||
map[index] = it
|
||||
onChunkCreated(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return map[index] ?: chunkFactory(ChunkPos(x, y)).also { map[index] = it; onChunkCreated(it) }
|
||||
return map[index] ?: chunkFactory(ChunkPos(x, y)).also {
|
||||
list.add(it)
|
||||
map[index] = it
|
||||
onChunkCreated(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,6 +153,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
if (chunk != null) {
|
||||
chunk.remove()
|
||||
onChunkRemoved(chunk)
|
||||
list.add(chunk)
|
||||
map.remove(index)
|
||||
}
|
||||
}
|
||||
@ -156,19 +163,14 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
if (chunk != null) {
|
||||
chunk.remove()
|
||||
onChunkRemoved(chunk)
|
||||
list.add(chunk)
|
||||
map.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun chunks(): List<ChunkType> {
|
||||
if (CONCURRENT_SPARSE_CHUNK_MAP) {
|
||||
synchronized(lock) {
|
||||
return ObjectArrayList(map.values)
|
||||
}
|
||||
} else {
|
||||
return ObjectArrayList(map.values)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
@ -177,11 +179,15 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
|
||||
inner class ArrayChunkMap : ChunkMap() {
|
||||
private val map = Object2DArray.nulls<ChunkType>(divideUp(geometry.size.x, CHUNK_SIZE), divideUp(geometry.size.y, CHUNK_SIZE))
|
||||
private val existing = ObjectAVLTreeSet<ChunkPos>()
|
||||
private val list = CopyOnWriteArrayList<ChunkType>()
|
||||
|
||||
override fun compute(x: Int, y: Int): ChunkType? {
|
||||
if (!geometry.x.inBoundsChunk(x) || !geometry.y.inBoundsChunk(y)) return null
|
||||
return map[x, y] ?: chunkFactory(ChunkPos(x, y)).also { existing.add(ChunkPos(x, y)); map[x, y] = it; onChunkCreated(it) }
|
||||
return map[x, y] ?: chunkFactory(ChunkPos(x, y)).also {
|
||||
list.add(it)
|
||||
map[x, y] = it
|
||||
onChunkCreated(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(x: Int, y: Int): ChunkType? {
|
||||
@ -198,17 +204,17 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
if (chunk != null) {
|
||||
chunk.remove()
|
||||
onChunkRemoved(chunk)
|
||||
existing.remove(ChunkPos(x, y))
|
||||
list.remove(chunk)
|
||||
map[x, y] = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun chunks(): List<ChunkType> {
|
||||
return existing.map { (x, y) -> map[x, y] ?: throw ConcurrentModificationException() }
|
||||
return list
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = existing.size
|
||||
get() = list.size
|
||||
}
|
||||
|
||||
val chunkMap: ChunkMap = if (geometry.size.x <= 32000 && geometry.size.y <= 32000) ArrayChunkMap() else SparseChunkMap()
|
||||
@ -227,7 +233,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
|
||||
val entities = Int2ObjectOpenHashMap<AbstractEntity>()
|
||||
val entityList = CopyOnWriteArrayList<AbstractEntity>()
|
||||
val entityIndex = SpatialIndex<AbstractEntity>(geometry)
|
||||
val entityIndex = EntityIndex(geometry)
|
||||
val dynamicEntities = ArrayList<DynamicEntity>()
|
||||
|
||||
var playerSpawnPosition = Vector2d.ZERO
|
||||
|
@ -7,7 +7,6 @@ import ru.dbotthepony.kommons.io.koptional
|
||||
import ru.dbotthepony.kommons.util.AABB
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||
import ru.dbotthepony.kstarbound.client.world.ClientWorld
|
||||
@ -22,7 +21,7 @@ import ru.dbotthepony.kstarbound.network.syncher.networkedData
|
||||
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
||||
import ru.dbotthepony.kstarbound.util.MailboxExecutorService
|
||||
import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||
import ru.dbotthepony.kstarbound.world.SpatialIndex
|
||||
import ru.dbotthepony.kstarbound.world.EntityIndex
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import java.io.DataOutputStream
|
||||
import java.util.function.Consumer
|
||||
@ -104,7 +103,7 @@ abstract class AbstractEntity(path: String) : JsonDriven(path), Comparable<Abstr
|
||||
val networkGroup = MasterElement(NetworkedGroup())
|
||||
abstract fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean)
|
||||
|
||||
protected var spatialEntry: SpatialIndex<AbstractEntity>.Entry? = null
|
||||
protected var spatialEntry: EntityIndex.Entry? = null
|
||||
private set
|
||||
|
||||
/**
|
||||
|
@ -2,11 +2,10 @@ package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import ru.dbotthepony.kommons.math.RGBAColor
|
||||
import ru.dbotthepony.kommons.util.AABB
|
||||
import ru.dbotthepony.kstarbound.Globals
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.RenderLayer
|
||||
import ru.dbotthepony.kstarbound.world.SpatialIndex
|
||||
import ru.dbotthepony.kstarbound.world.EntityIndex
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
|
||||
/**
|
||||
@ -43,7 +42,7 @@ abstract class DynamicEntity(path: String) : AbstractEntity(path) {
|
||||
}
|
||||
}
|
||||
|
||||
protected var metaFixture: SpatialIndex<AbstractEntity>.Entry.Fixture? = null
|
||||
protected var metaFixture: EntityIndex.Entry.Fixture? = null
|
||||
private set
|
||||
|
||||
override fun onJoinWorld(world: World<*, *>) {
|
||||
|
@ -23,7 +23,7 @@ import ru.dbotthepony.kstarbound.network.syncher.networkedData
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFixedPoint
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFloat
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedPoly
|
||||
import ru.dbotthepony.kstarbound.world.SpatialIndex
|
||||
import ru.dbotthepony.kstarbound.world.EntityIndex
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionPoly
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||
@ -37,9 +37,9 @@ import kotlin.math.sin
|
||||
open class MovementController() {
|
||||
private var world0: World<*, *>? = null
|
||||
val world: World<*, *> get() = world0!!
|
||||
private var spatialEntry: SpatialIndex<*>.Entry? = null
|
||||
private var spatialEntry: EntityIndex.Entry? = null
|
||||
|
||||
fun initialize(world: World<*, *>, entry: SpatialIndex<*>.Entry?) {
|
||||
fun initialize(world: World<*, *>, entry: EntityIndex.Entry?) {
|
||||
fixtures.clear()
|
||||
spatialEntry?.remove()
|
||||
this.world0 = world
|
||||
@ -135,7 +135,7 @@ open class MovementController() {
|
||||
private val relativeXSurfaceVelocity = networkGroup.add(networkedFixedPoint(0.0125).also { it.interpolator = Interpolator.Linear })
|
||||
private val relativeYSurfaceVelocity = networkGroup.add(networkedFixedPoint(0.0125).also { it.interpolator = Interpolator.Linear })
|
||||
|
||||
private val fixtures = ArrayList<SpatialIndex<*>.Entry.Fixture>()
|
||||
private val fixtures = ArrayList<EntityIndex.Entry.Fixture>()
|
||||
var fixturesChangeset: Int = 0
|
||||
private set
|
||||
|
||||
|
@ -24,9 +24,6 @@ import ru.dbotthepony.kstarbound.network.syncher.networkedEnumExtraStupid
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedEventCounter
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFixedPoint
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedString
|
||||
import ru.dbotthepony.kstarbound.world.SpatialIndex
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.Animator
|
||||
import ru.dbotthepony.kstarbound.world.entities.HumanoidActorEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.StatusController
|
||||
|
@ -195,21 +195,27 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
val chatPortrait by networkedString().also { networkGroup.upstream.add(it) }
|
||||
val chatConfig by networkedJsonElement().also { networkGroup.upstream.add(it) }
|
||||
|
||||
inner class WireNode(val position: Vector2i) {
|
||||
inner class WireNode(val position: Vector2i, val isInput: Boolean) {
|
||||
var state by networkedBoolean().also { networkGroup.upstream.add(it) }
|
||||
val connections = NetworkedList(WireConnection.CODEC).also { networkGroup.upstream.add(it) }
|
||||
|
||||
fun addConnection(connection: WireConnection) {
|
||||
if (connection !in connections) {
|
||||
connections.add(connection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val inputNodes: ImmutableList<WireNode> = lookupProperty(JsonPath("inputNodes")) { JsonArray() }
|
||||
.asJsonArray
|
||||
.stream()
|
||||
.map { WireNode(vectors.fromJsonTree(it)) }
|
||||
.map { WireNode(vectors.fromJsonTree(it), true) }
|
||||
.collect(ImmutableList.toImmutableList())
|
||||
|
||||
val outputNodes: ImmutableList<WireNode> = lookupProperty(JsonPath("outputNodes")) { JsonArray() }
|
||||
.asJsonArray
|
||||
.stream()
|
||||
.map { WireNode(vectors.fromJsonTree(it)) }
|
||||
.map { WireNode(vectors.fromJsonTree(it), false) }
|
||||
.collect(ImmutableList.toImmutableList())
|
||||
|
||||
val offeredQuests = NetworkedList(QuestArcDescriptor.CODEC, QuestArcDescriptor.LEGACY_CODEC).also { networkGroup.upstream.add(it) }
|
||||
|
@ -1,20 +1,22 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities.wire
|
||||
|
||||
import ru.dbotthepony.kommons.io.StreamCodec
|
||||
import ru.dbotthepony.kommons.io.readVector2i
|
||||
import ru.dbotthepony.kommons.io.writeStruct2i
|
||||
import ru.dbotthepony.kommons.io.writeVarLong
|
||||
import ru.dbotthepony.kommons.io.readSignedVarInt
|
||||
import ru.dbotthepony.kommons.io.readVarInt
|
||||
import ru.dbotthepony.kommons.io.writeSignedVarInt
|
||||
import ru.dbotthepony.kommons.io.writeVarInt
|
||||
import ru.dbotthepony.kommons.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.network.syncher.SizeTCodec
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
|
||||
data class WireConnection(val entityLocation: Vector2i, val index: Int = -1) {
|
||||
constructor(stream: DataInputStream) : this(stream.readVector2i(), SizeTCodec.read(stream).toInt())
|
||||
data class WireConnection(val entityLocation: Vector2i, val index: Int = 0) {
|
||||
constructor(stream: DataInputStream) : this(Vector2i(stream.readSignedVarInt(), stream.readSignedVarInt()), stream.readVarInt())
|
||||
|
||||
fun write(stream: DataOutputStream) {
|
||||
stream.writeStruct2i((entityLocation))
|
||||
SizeTCodec.write(stream, index.toLong())
|
||||
stream.writeSignedVarInt(entityLocation.x)
|
||||
stream.writeSignedVarInt(entityLocation.y)
|
||||
stream.writeVarInt(index)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -58,9 +58,9 @@ class KarstCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters
|
||||
}
|
||||
|
||||
private val layers = Caffeine.newBuilder()
|
||||
.maximumSize(512L)
|
||||
.maximumSize(2048L)
|
||||
.softValues()
|
||||
.expireAfterAccess(Duration.ofSeconds(10))
|
||||
.expireAfterAccess(Duration.ofMinutes(1))
|
||||
.scheduler(Starbound)
|
||||
.executor(Starbound.EXECUTOR)
|
||||
.build<Int, Layer>(::Layer)
|
||||
@ -127,9 +127,9 @@ class KarstCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters
|
||||
}
|
||||
|
||||
private val sectors = Caffeine.newBuilder()
|
||||
.maximumSize(256L)
|
||||
.maximumSize(512L)
|
||||
.softValues()
|
||||
.expireAfterAccess(Duration.ofSeconds(10))
|
||||
.expireAfterAccess(Duration.ofMinutes(1))
|
||||
.scheduler(Starbound)
|
||||
.executor(Starbound.EXECUTOR)
|
||||
.build<Vector2i, Sector>(::Sector)
|
||||
|
@ -178,9 +178,9 @@ class WormCaveTerrainSelector(data: Data, parameters: TerrainSelectorParameters)
|
||||
}
|
||||
|
||||
private val sectors = Caffeine.newBuilder()
|
||||
.maximumSize(256L)
|
||||
.maximumSize(512L)
|
||||
.softValues()
|
||||
.expireAfterAccess(Duration.ofSeconds(10))
|
||||
.expireAfterAccess(Duration.ofMinutes(1))
|
||||
.scheduler(Starbound)
|
||||
.executor(Starbound.EXECUTOR)
|
||||
.build<Vector2i, Sector>(::Sector)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.kstarbound.test
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
@ -8,8 +9,12 @@ import ru.dbotthepony.kommons.util.AABB
|
||||
import ru.dbotthepony.kommons.util.AABBi
|
||||
import ru.dbotthepony.kommons.vector.Vector2d
|
||||
import ru.dbotthepony.kommons.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.world.SpatialIndex
|
||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||
import ru.dbotthepony.kstarbound.json.JsonPath
|
||||
import ru.dbotthepony.kstarbound.world.EntityIndex
|
||||
import ru.dbotthepony.kstarbound.world.WorldGeometry
|
||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||
import java.io.DataOutputStream
|
||||
|
||||
object WorldTests {
|
||||
@Test
|
||||
@ -42,9 +47,29 @@ object WorldTests {
|
||||
@DisplayName("Spatial index test")
|
||||
fun spatialIndex() {
|
||||
val geometry = WorldGeometry(Vector2i(3000, 2000), true, false)
|
||||
val index = SpatialIndex<Int>(geometry)
|
||||
val index = EntityIndex(geometry)
|
||||
|
||||
val entry = index.Entry(0)
|
||||
val entry = index.Entry(object : AbstractEntity("/") {
|
||||
override fun lookupProperty(path: JsonPath, orElse: () -> JsonElement): JsonElement {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setProperty0(key: JsonPath, value: JsonElement) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override val position: Vector2d
|
||||
get() = TODO("Not yet implemented")
|
||||
override val type: EntityType
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override val metaBoundingBox: AABB
|
||||
get() = TODO("Not yet implemented")
|
||||
})
|
||||
|
||||
// simple
|
||||
entry.fixture.move(AABB(
|
||||
|
Loading…
Reference in New Issue
Block a user