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()
val thread = Thread(::universeThread, "Universe")
val thread = Thread(::universeThread, "Universe Thread")
val mailbox = 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) }

View File

@ -308,10 +308,6 @@ class ClientWorld(
}
}
override fun tick0() {
}
companion object {
val ring = listOf(
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.vector.Vector2d
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.SpawnTarget
import ru.dbotthepony.kstarbound.defs.WarpAction
import ru.dbotthepony.kstarbound.defs.WorldID
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.packets.StepUpdatePacket
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.ServerConnection
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?) {
if (players.any { it.client == client })
if (clients.any { it.client == client })
throw IllegalStateException("$client is already in $this")
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)
action.target.resolve(this)
else
@ -86,7 +84,7 @@ class ServerWorld private constructor(
client.send(PlayerWarpResultPacket(true, action, false))
client.tracker?.remove()
players.add(ServerWorldTracker(this, client, start))
clients.add(ServerWorldTracker(this, client, start))
}
fun acceptClient(player: ServerConnection, action: WarpAction? = null): CompletableFuture<Unit> {
@ -134,7 +132,7 @@ class ServerWorld private constructor(
if (isClosed.compareAndSet(false, true)) {
super.close()
spinner.unpause()
players.forEach { it.remove() }
clients.forEach { it.remove() }
if (worldID != WorldID.Limbo)
server.worlds.remove(worldID)
@ -180,10 +178,11 @@ class ServerWorld private constructor(
return topMost
}
override fun tick0() {
override fun tick() {
super.tick()
val packet = StepUpdatePacket(ticks)
players.forEach {
clients.forEach {
if (!isClosed.get()) {
it.send(packet)
@ -210,7 +209,7 @@ class ServerWorld private constructor(
val unloadable = entityIndex
.query(
chunk.aabb,
predicate = Predicate { it.isApplicableForUnloading && chunk.aabb.isInside(it.position) },
filter = Predicate { it.isApplicableForUnloading && chunk.aabb.isInside(it.position) },
distinct = true, withEdges = false)
storage.saveCells(it.pos, chunk.copyCells())
@ -230,7 +229,7 @@ class ServerWorld private constructor(
}
override fun broadcast(packet: IPacket) {
players.forEach {
clients.forEach {
it.send(packet)
}
}

View File

@ -113,6 +113,18 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
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() {
if (!client.worldStartAcknowledged)
return
@ -245,7 +257,7 @@ class ServerWorldTracker(val world: ServerWorld, val client: ServerConnection, p
if (isActuallyRemoved) return
isActuallyRemoved = true
world.players.remove(this)
world.clients.remove(this)
tickets.values.forEach { it.ticket.cancel() }
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
// 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
// have to be done in the future.
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> {
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 entriesDirect = ArrayList<T>()
for (actualRegion in geometry.split(rect).first) {
val xMin = geometry.x.chunkFromCell(actualRegion.mins.x)
@ -265,9 +267,17 @@ class SpatialIndex<T>(val geometry: WorldGeometry) {
for (y in yMin .. yMax) {
val sector = map[index(x, y)] ?: continue
for (entry in sector.entries) {
if (predicate.test(entry.value) && entry.intersects(actualRegion, withEdges)) {
entries.add(entry)
if (distinct) {
for (entry in sector.entries) {
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
} else {
if (entries.isEmpty())
return listOf()
return entries.map { it.value }
return entriesDirect
}
}
}

View File

@ -218,7 +218,6 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
val entities = Int2ObjectOpenHashMap<AbstractEntity>()
val entityIndex = SpatialIndex<AbstractEntity>(geometry)
val dynamicEntities = ArrayList<DynamicEntity>()
val tileEntities = ArrayList<TileEntity>()
var playerSpawnPosition = Vector2d.ZERO
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()}" }
}
fun tick() {
try {
ticks++
mailbox.executeQueuedTasks()
open fun tick() {
ticks++
mailbox.executeQueuedTasks()
Starbound.EXECUTOR.submit(ParallelPerform(dynamicEntities.spliterator(), {
if (!it.isRemote) {
it.movement.move()
}
})).join()
mailbox.executeQueuedTasks()
entities.values.forEach { it.tick() }
mailbox.executeQueuedTasks()
for (chunk in chunkMap) {
chunk.tick()
Starbound.EXECUTOR.submit(ParallelPerform(dynamicEntities.spliterator(), {
if (!it.isRemote) {
it.movement.move()
}
})).join()
mailbox.executeQueuedTasks()
tick0()
} catch(err: Throwable) {
throw RuntimeException("Ticking world $this", err)
mailbox.executeQueuedTasks()
entities.values.forEach { it.tick() }
mailbox.executeQueuedTasks()
for (chunk in chunkMap) {
chunk.tick()
}
mailbox.executeQueuedTasks()
}
protected abstract fun tick0()
protected abstract fun chunkFactory(pos: ChunkPos): ChunkType
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.syncher.MasterElement
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.SpatialIndex
import ru.dbotthepony.kstarbound.world.World
@ -109,7 +110,7 @@ abstract class AbstractEntity(path: String) : JsonDriven(path), Comparable<Abstr
onJoinWorld(world)
}
fun remove() {
fun remove(isDeath: Boolean = false) {
val world = innerWorld ?: throw IllegalStateException("Not in world")
world.ensureSameThread()
@ -119,6 +120,12 @@ abstract class AbstractEntity(path: String) : JsonDriven(path), Comparable<Abstr
spatialEntry?.remove()
spatialEntry = null
innerWorld = null
if (world is ServerWorld) {
world.clients.forEach {
it.forget(this, isDeath)
}
}
}
var isRemote: Boolean = false

View File

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