little bit polishing

This commit is contained in:
DBotThePony 2024-03-30 15:02:12 +07:00
parent 6302661019
commit fd1a63a22c
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 65 additions and 53 deletions

View File

@ -110,7 +110,7 @@ object Starbound : ISBFileLocator {
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
val thread = Thread(::universeThread, "Universe") val thread = Thread(::universeThread, "Universe Thread")
val mailbox = MailboxExecutorService(thread).also { it.exceptionHandler = ExceptionLogger(LOGGER) } val mailbox = MailboxExecutorService(thread).also { it.exceptionHandler = ExceptionLogger(LOGGER) }
val mailboxBootstrapped = MailboxExecutorService(thread).also { it.exceptionHandler = ExceptionLogger(LOGGER) } val mailboxBootstrapped = MailboxExecutorService(thread).also { it.exceptionHandler = ExceptionLogger(LOGGER) }
val mailboxInitialized = MailboxExecutorService(thread).also { it.exceptionHandler = ExceptionLogger(LOGGER) } val mailboxInitialized = MailboxExecutorService(thread).also { it.exceptionHandler = ExceptionLogger(LOGGER) }

View File

@ -308,10 +308,6 @@ class ClientWorld(
} }
} }
override fun tick0() {
}
companion object { companion object {
val ring = listOf( val ring = listOf(
Vector2i(0, 0), Vector2i(0, 0),

View File

@ -9,7 +9,6 @@ import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.util.IStruct2i import ru.dbotthepony.kommons.util.IStruct2i
import ru.dbotthepony.kommons.vector.Vector2d import ru.dbotthepony.kommons.vector.Vector2d
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.SpawnTarget
import ru.dbotthepony.kstarbound.defs.WarpAction import ru.dbotthepony.kstarbound.defs.WarpAction
import ru.dbotthepony.kstarbound.defs.WorldID import ru.dbotthepony.kstarbound.defs.WorldID
import ru.dbotthepony.kstarbound.defs.tile.TileDamage import ru.dbotthepony.kstarbound.defs.tile.TileDamage
@ -20,7 +19,6 @@ import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.network.IPacket import ru.dbotthepony.kstarbound.network.IPacket
import ru.dbotthepony.kstarbound.network.packets.StepUpdatePacket import ru.dbotthepony.kstarbound.network.packets.StepUpdatePacket
import ru.dbotthepony.kstarbound.network.packets.clientbound.PlayerWarpResultPacket import ru.dbotthepony.kstarbound.network.packets.clientbound.PlayerWarpResultPacket
import ru.dbotthepony.kstarbound.network.packets.clientbound.WorldStopPacket
import ru.dbotthepony.kstarbound.server.StarboundServer import ru.dbotthepony.kstarbound.server.StarboundServer
import ru.dbotthepony.kstarbound.server.ServerConnection import ru.dbotthepony.kstarbound.server.ServerConnection
import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.util.AssetPathStack
@ -64,14 +62,14 @@ class ServerWorld private constructor(
} }
} }
val players = CopyOnWriteArrayList<ServerWorldTracker>() val clients = CopyOnWriteArrayList<ServerWorldTracker>()
private fun doAcceptClient(client: ServerConnection, action: WarpAction?) { private fun doAcceptClient(client: ServerConnection, action: WarpAction?) {
if (players.any { it.client == client }) if (clients.any { it.client == client })
throw IllegalStateException("$client is already in $this") throw IllegalStateException("$client is already in $this")
val start = if (action is WarpAction.Player) val start = if (action is WarpAction.Player)
players.firstOrNull { it.client.uuid == action.uuid }?.client?.playerEntity?.position clients.firstOrNull { it.client.uuid == action.uuid }?.client?.playerEntity?.position
else if (action is WarpAction.World) else if (action is WarpAction.World)
action.target.resolve(this) action.target.resolve(this)
else else
@ -86,7 +84,7 @@ class ServerWorld private constructor(
client.send(PlayerWarpResultPacket(true, action, false)) client.send(PlayerWarpResultPacket(true, action, false))
client.tracker?.remove() client.tracker?.remove()
players.add(ServerWorldTracker(this, client, start)) clients.add(ServerWorldTracker(this, client, start))
} }
fun acceptClient(player: ServerConnection, action: WarpAction? = null): CompletableFuture<Unit> { fun acceptClient(player: ServerConnection, action: WarpAction? = null): CompletableFuture<Unit> {
@ -134,7 +132,7 @@ class ServerWorld private constructor(
if (isClosed.compareAndSet(false, true)) { if (isClosed.compareAndSet(false, true)) {
super.close() super.close()
spinner.unpause() spinner.unpause()
players.forEach { it.remove() } clients.forEach { it.remove() }
if (worldID != WorldID.Limbo) if (worldID != WorldID.Limbo)
server.worlds.remove(worldID) server.worlds.remove(worldID)
@ -180,10 +178,11 @@ class ServerWorld private constructor(
return topMost return topMost
} }
override fun tick0() { override fun tick() {
super.tick()
val packet = StepUpdatePacket(ticks) val packet = StepUpdatePacket(ticks)
players.forEach { clients.forEach {
if (!isClosed.get()) { if (!isClosed.get()) {
it.send(packet) it.send(packet)
@ -210,7 +209,7 @@ class ServerWorld private constructor(
val unloadable = entityIndex val unloadable = entityIndex
.query( .query(
chunk.aabb, chunk.aabb,
predicate = Predicate { it.isApplicableForUnloading && chunk.aabb.isInside(it.position) }, filter = Predicate { it.isApplicableForUnloading && chunk.aabb.isInside(it.position) },
distinct = true, withEdges = false) distinct = true, withEdges = false)
storage.saveCells(it.pos, chunk.copyCells()) storage.saveCells(it.pos, chunk.copyCells())
@ -230,7 +229,7 @@ class ServerWorld private constructor(
} }
override fun broadcast(packet: IPacket) { override fun broadcast(packet: IPacket) {
players.forEach { clients.forEach {
it.send(packet) it.send(packet)
} }
} }

View File

@ -113,6 +113,18 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
return pos in tickets return pos in tickets
} }
fun isTracking(entity: AbstractEntity): Boolean {
return entityVersions.containsKey(entity.entityID)
}
fun forget(entity: AbstractEntity, isDeath: Boolean = false) {
val version = entityVersions.remove(entity.entityID)
if (version != -1L) {
send(EntityDestroyPacket(entity.entityID, entity.networkGroup.write(version, isLegacy = client.isLegacy).first, isDeath))
}
}
fun tick() { fun tick() {
if (!client.worldStartAcknowledged) if (!client.worldStartAcknowledged)
return return
@ -245,7 +257,7 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
if (isActuallyRemoved) return if (isActuallyRemoved) return
isActuallyRemoved = true isActuallyRemoved = true
world.players.remove(this) world.clients.remove(this)
tickets.values.forEach { it.ticket.cancel() } tickets.values.forEach { it.ticket.cancel() }
val itr = world.entities.int2ObjectEntrySet().iterator() val itr = world.entities.int2ObjectEntrySet().iterator()

View File

@ -17,6 +17,7 @@ import kotlin.concurrent.withLock
// Advantages of this system include allowing to put entities outside any chunks // Advantages of this system include allowing to put entities outside any chunks
// into index and getting rid of "orphaned entities" collection, // into index and getting rid of "orphaned entities" collection,
// and allowing one entity to have multiple bounding boxes,
// while also setting up ground for better spatial index strategies, if they // while also setting up ground for better spatial index strategies, if they
// have to be done in the future. // have to be done in the future.
class SpatialIndex<T>(val geometry: WorldGeometry) { class SpatialIndex<T>(val geometry: WorldGeometry) {
@ -242,17 +243,18 @@ class SpatialIndex<T>(val geometry: WorldGeometry) {
} }
/** /**
* [filter] might be invoked for same entry multiple times, regardless pf [distinct] * [filter] might be invoked for same entry multiple times, regardless of [distinct]
*/ */
fun query(rect: AABBi, filter: Predicate<T> = Predicate { true }, distinct: Boolean = true, withEdges: Boolean = true): List<T> { fun query(rect: AABBi, filter: Predicate<T> = Predicate { true }, distinct: Boolean = true, withEdges: Boolean = true): List<T> {
return query(rect.toDoubleAABB(), filter, distinct, withEdges) return query(rect.toDoubleAABB(), filter, distinct, withEdges)
} }
/** /**
* [predicate] might be invoked for same entry multiple times, regardless pf [distinct] * [filter] might be invoked for same entry multiple times, regardless of [distinct]
*/ */
fun query(rect: AABB, predicate: Predicate<T> = Predicate { true }, distinct: Boolean = true, withEdges: Boolean = true): List<T> { fun query(rect: AABB, filter: Predicate<T> = Predicate { true }, distinct: Boolean = true, withEdges: Boolean = true): List<T> {
val entries = ArrayList<Entry>() val entries = ArrayList<Entry>()
val entriesDirect = ArrayList<T>()
for (actualRegion in geometry.split(rect).first) { for (actualRegion in geometry.split(rect).first) {
val xMin = geometry.x.chunkFromCell(actualRegion.mins.x) val xMin = geometry.x.chunkFromCell(actualRegion.mins.x)
@ -265,9 +267,17 @@ class SpatialIndex<T>(val geometry: WorldGeometry) {
for (y in yMin .. yMax) { for (y in yMin .. yMax) {
val sector = map[index(x, y)] ?: continue val sector = map[index(x, y)] ?: continue
for (entry in sector.entries) { if (distinct) {
if (predicate.test(entry.value) && entry.intersects(actualRegion, withEdges)) { for (entry in sector.entries) {
entries.add(entry) if (filter.test(entry.value) && entry.intersects(actualRegion, withEdges)) {
entries.add(entry)
}
}
} else {
for (entry in sector.entries) {
if (filter.test(entry.value) && entry.intersects(actualRegion, withEdges)) {
entriesDirect.add(entry.value)
}
} }
} }
} }
@ -292,10 +302,7 @@ class SpatialIndex<T>(val geometry: WorldGeometry) {
return entries0 return entries0
} else { } else {
if (entries.isEmpty()) return entriesDirect
return listOf()
return entries.map { it.value }
} }
} }
} }

View File

@ -218,7 +218,6 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
val entities = Int2ObjectOpenHashMap<AbstractEntity>() val entities = Int2ObjectOpenHashMap<AbstractEntity>()
val entityIndex = SpatialIndex<AbstractEntity>(geometry) val entityIndex = SpatialIndex<AbstractEntity>(geometry)
val dynamicEntities = ArrayList<DynamicEntity>() val dynamicEntities = ArrayList<DynamicEntity>()
val tileEntities = ArrayList<TileEntity>()
var playerSpawnPosition = Vector2d.ZERO var playerSpawnPosition = Vector2d.ZERO
protected set protected set
@ -252,34 +251,28 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
check(isSameThread()) { "Trying to access $this from ${Thread.currentThread()}" } check(isSameThread()) { "Trying to access $this from ${Thread.currentThread()}" }
} }
fun tick() { open fun tick() {
try { ticks++
ticks++ mailbox.executeQueuedTasks()
mailbox.executeQueuedTasks()
Starbound.EXECUTOR.submit(ParallelPerform(dynamicEntities.spliterator(), { Starbound.EXECUTOR.submit(ParallelPerform(dynamicEntities.spliterator(), {
if (!it.isRemote) { if (!it.isRemote) {
it.movement.move() it.movement.move()
}
})).join()
mailbox.executeQueuedTasks()
entities.values.forEach { it.tick() }
mailbox.executeQueuedTasks()
for (chunk in chunkMap) {
chunk.tick()
} }
})).join()
mailbox.executeQueuedTasks() mailbox.executeQueuedTasks()
tick0()
} catch(err: Throwable) { entities.values.forEach { it.tick() }
throw RuntimeException("Ticking world $this", err) mailbox.executeQueuedTasks()
for (chunk in chunkMap) {
chunk.tick()
} }
mailbox.executeQueuedTasks()
} }
protected abstract fun tick0()
protected abstract fun chunkFactory(pos: ChunkPos): ChunkType protected abstract fun chunkFactory(pos: ChunkPos): ChunkType
override fun close() { override fun close() {

View File

@ -12,6 +12,7 @@ import ru.dbotthepony.kstarbound.defs.JsonDriven
import ru.dbotthepony.kstarbound.network.packets.EntityDestroyPacket import ru.dbotthepony.kstarbound.network.packets.EntityDestroyPacket
import ru.dbotthepony.kstarbound.network.syncher.MasterElement import ru.dbotthepony.kstarbound.network.syncher.MasterElement
import ru.dbotthepony.kstarbound.network.syncher.NetworkedGroup import ru.dbotthepony.kstarbound.network.syncher.NetworkedGroup
import ru.dbotthepony.kstarbound.server.world.ServerWorld
import ru.dbotthepony.kstarbound.world.LightCalculator import ru.dbotthepony.kstarbound.world.LightCalculator
import ru.dbotthepony.kstarbound.world.SpatialIndex import ru.dbotthepony.kstarbound.world.SpatialIndex
import ru.dbotthepony.kstarbound.world.World import ru.dbotthepony.kstarbound.world.World
@ -109,7 +110,7 @@ abstract class AbstractEntity(path: String) : JsonDriven(path), Comparable<Abstr
onJoinWorld(world) onJoinWorld(world)
} }
fun remove() { fun remove(isDeath: Boolean = false) {
val world = innerWorld ?: throw IllegalStateException("Not in world") val world = innerWorld ?: throw IllegalStateException("Not in world")
world.ensureSameThread() world.ensureSameThread()
@ -119,6 +120,12 @@ abstract class AbstractEntity(path: String) : JsonDriven(path), Comparable<Abstr
spatialEntry?.remove() spatialEntry?.remove()
spatialEntry = null spatialEntry = null
innerWorld = null innerWorld = null
if (world is ServerWorld) {
world.clients.forEach {
it.forget(this, isDeath)
}
}
} }
var isRemote: Boolean = false var isRemote: Boolean = false

View File

@ -25,11 +25,9 @@ abstract class TileEntity(path: String) : AbstractEntity(path) {
get() = tilePosition.toDoubleVector() get() = tilePosition.toDoubleVector()
override fun onJoinWorld(world: World<*, *>) { override fun onJoinWorld(world: World<*, *>) {
world.tileEntities.add(this)
tilePosition = tilePosition tilePosition = tilePosition
} }
override fun onRemove(world: World<*, *>) { override fun onRemove(world: World<*, *>) {
world.tileEntities.remove(this)
} }
} }