More networking work

This commit is contained in:
DBotThePony 2024-02-08 19:59:39 +07:00
parent 0eea0fa13f
commit 8671c4bfad
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 58 additions and 80 deletions

View File

@ -2,7 +2,7 @@ kotlin.code.style=official
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
kotlinVersion=1.9.0 kotlinVersion=1.9.0
kommonsVersion=1.7.8 kommonsVersion=1.7.9
ffiVersion=2.2.13 ffiVersion=2.2.13
lwjglVersion=3.3.0 lwjglVersion=3.3.0

View File

@ -40,6 +40,7 @@ fun main() {
val server = IntegratedStarboundServer(File("./")) val server = IntegratedStarboundServer(File("./"))
val client = StarboundClient.create().get() val client = StarboundClient.create().get()
//val client2 = StarboundClient.create().get()
val world = ServerWorld(server, 0L, WorldGeometry(Vector2i(3000, 2000), true, false)) val world = ServerWorld(server, 0L, WorldGeometry(Vector2i(3000, 2000), true, false))
world.addChunkSource(LegacyChunkSource(db)) world.addChunkSource(LegacyChunkSource(db))
world.thread.start() world.thread.start()
@ -81,10 +82,12 @@ fun main() {
} }
client.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID()) client.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID())
//client2.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID())
} }
//ent.position += Vector2d(y = 14.0, x = -10.0) //ent.position += Vector2d(y = 14.0, x = -10.0)
client.camera.pos = Vector2d(238.0, 685.0) client.camera.pos = Vector2d(238.0, 685.0)
//client2.camera.pos = Vector2d(238.0, 685.0)
//client.camera.pos = Vector2f(0f, 0f) //client.camera.pos = Vector2f(0f, 0f)
//ent.spawn() //ent.spawn()

View File

@ -13,7 +13,7 @@ import java.util.concurrent.locks.LockSupport
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
abstract class StarboundServer(val root: File) : Closeable { sealed class StarboundServer(val root: File) : Closeable {
init { init {
if (!root.exists()) { if (!root.exists()) {
check(root.mkdirs()) { "Unable to create ${root.absolutePath}" } check(root.mkdirs()) { "Unable to create ${root.absolutePath}" }
@ -37,7 +37,7 @@ abstract class StarboundServer(val root: File) : Closeable {
private set private set
init { init {
thread.isDaemon = true thread.isDaemon = this is IntegratedStarboundServer
thread.start() thread.start()
} }

View File

@ -110,7 +110,7 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
if (pos !in tickets) { if (pos !in tickets) {
val ticket = world.permanentChunkTicket(pos) val ticket = world.permanentChunkTicket(pos)
tickets[pos] = ticket tickets[pos] = ticket
ticket.addListener(ChunkListener(pos)) ticket.listener = ChunkListener(pos)
pendingSend.add(pos) pendingSend.add(pos)
} }
} }

View File

@ -185,16 +185,21 @@ class ServerWorld(
} }
} }
override fun onChunkCreated(chunk: ServerChunk) {
ticketMap[chunk.pos.toLong()]?.let { chunk.addListener(it) }
}
override fun onChunkRemoved(chunk: ServerChunk) {
ticketMap[chunk.pos.toLong()]?.let { chunk.removeListener(it) }
}
interface ITicket { interface ITicket {
fun cancel() fun cancel()
val isCanceled: Boolean val isCanceled: Boolean
val pos: ChunkPos val pos: ChunkPos
val id: Int val id: Int
val chunk: ServerChunk? val chunk: ServerChunk?
var listener: IChunkListener?
fun addListener(listener: IChunkListener)
fun removeListener(listener: IChunkListener)
} }
interface ITimedTicket : ITicket, Comparable<ITimedTicket> { interface ITimedTicket : ITicket, Comparable<ITimedTicket> {
@ -208,7 +213,7 @@ class ServerWorld(
} }
} }
private inner class TicketList(val pos: ChunkPos) : IChunkListener, IChunkMapListener<ServerChunk> { private inner class TicketList(val pos: ChunkPos) : IChunkListener {
constructor(pos: Long) : this(ChunkPos(pos)) constructor(pos: Long) : this(ChunkPos(pos))
private var first = true private var first = true
@ -216,7 +221,6 @@ class ServerWorld(
private val temporary = ObjectAVLTreeSet<TimedTicket>() private val temporary = ObjectAVLTreeSet<TimedTicket>()
private var ticks = 0 private var ticks = 0
private var nextTicketID = AtomicInteger() private var nextTicketID = AtomicInteger()
private var weAreResponsibleForLoadingTheChunk = false
val isValid: Boolean val isValid: Boolean
get() = temporary.isNotEmpty() || permanent.isNotEmpty() get() = temporary.isNotEmpty() || permanent.isNotEmpty()
@ -234,33 +238,21 @@ class ServerWorld(
} }
override fun onEntityAdded(entity: AbstractEntity) { override fun onEntityAdded(entity: AbstractEntity) {
permanent.forEach { it.onEntityAdded(entity) } permanent.forEach { it.listener?.onEntityAdded(entity) }
temporary.forEach { it.onEntityAdded(entity) } temporary.forEach { it.listener?.onEntityAdded(entity) }
} }
override fun onEntityRemoved(entity: AbstractEntity) { override fun onEntityRemoved(entity: AbstractEntity) {
permanent.forEach { it.onEntityRemoved(entity) } permanent.forEach { it.listener?.onEntityRemoved(entity) }
temporary.forEach { it.onEntityRemoved(entity) } temporary.forEach { it.listener?.onEntityRemoved(entity) }
} }
override fun onCellChanges(x: Int, y: Int, cell: ImmutableCell) { override fun onCellChanges(x: Int, y: Int, cell: ImmutableCell) {
permanent.forEach { it.onCellChanges(x, y, cell) } permanent.forEach { it.listener?.onCellChanges(x, y, cell) }
temporary.forEach { it.onCellChanges(x, y, cell) } temporary.forEach { it.listener?.onCellChanges(x, y, cell) }
} }
override fun onChunkCreated(chunk: ServerChunk) { abstract inner class AbstractTicket : ITicket {
if (chunk.pos == pos) {
chunk.addListener(this)
}
}
override fun onChunkRemoved(chunk: ServerChunk) {
if (chunk.pos == pos) {
chunk.removeListener(this)
}
}
abstract inner class AbstractTicket : ITicket, IChunkListener {
final override val id: Int = nextTicketID.getAndIncrement() final override val id: Int = nextTicketID.getAndIncrement()
final override val pos: ChunkPos final override val pos: ChunkPos
get() = this@TicketList.pos get() = this@TicketList.pos
@ -273,11 +265,10 @@ class ServerWorld(
if (geometry.x.inBoundsChunk(pos.x) && geometry.y.inBoundsChunk(pos.y)) { if (geometry.x.inBoundsChunk(pos.x) && geometry.y.inBoundsChunk(pos.y)) {
ticketLists.add(this@TicketList) ticketLists.add(this@TicketList)
chunkMap.addListener(this@TicketList)
if (chunkProviders.isNotEmpty() && chunkMap[pos] == null) { val existing = chunkMap[pos]
weAreResponsibleForLoadingTheChunk = true
if (chunkProviders.isNotEmpty() && existing == null) {
chainOptionalFutures(chunkProviders) chainOptionalFutures(chunkProviders)
{ if (!isValid) CompletableFuture.completedFuture(KOptional.empty()) else it.getTiles(pos) } { if (!isValid) CompletableFuture.completedFuture(KOptional.empty()) else it.getTiles(pos) }
.thenAccept(Consumer { tiles -> .thenAccept(Consumer { tiles ->
@ -297,8 +288,12 @@ class ServerWorld(
} }
}, mailbox) }, mailbox)
}) })
} else if (existing != null) {
existing.addListener(this@TicketList)
} }
} }
} else {
chunkMap[pos]?.addListener(this@TicketList)
} }
} }
@ -308,7 +303,7 @@ class ServerWorld(
lock.withLock { lock.withLock {
if (isCanceled) return if (isCanceled) return
isCanceled = true isCanceled = true
chunk?.entities?.forEach { e -> listeners.forEach { it.onEntityRemoved(e) } } chunk?.entities?.forEach { e -> listener?.onEntityRemoved(e) }
onCancel() onCancel()
} }
} }
@ -317,31 +312,15 @@ class ServerWorld(
final override val chunk: ServerChunk? final override val chunk: ServerChunk?
get() = chunkMap[pos] get() = chunkMap[pos]
private val listeners = ReferenceLinkedOpenHashSet<IChunkListener>() final override var listener: IChunkListener? = null
set(value) {
final override fun addListener(listener: IChunkListener) { if (field != value) {
if (isCanceled) return val chunk = chunk
listeners.add(listener) chunk?.entities?.forEach { e -> field?.onEntityRemoved(e) }
chunk?.entities?.forEach { listener.onEntityAdded(it) } chunk?.entities?.forEach { e -> value?.onEntityAdded(e) }
} field = value
}
final override fun removeListener(listener: IChunkListener) {
if (listeners.remove(listener)) {
chunk?.entities?.forEach { listener.onEntityRemoved(it) }
} }
}
final override fun onEntityAdded(entity: AbstractEntity) {
listeners.forEach { it.onEntityAdded(entity) }
}
final override fun onEntityRemoved(entity: AbstractEntity) {
listeners.forEach { it.onEntityRemoved(entity) }
}
final override fun onCellChanges(x: Int, y: Int, cell: ImmutableCell) {
listeners.forEach { it.onCellChanges(x, y, cell) }
}
} }
inner class Ticket : AbstractTicket() { inner class Ticket : AbstractTicket() {

View File

@ -158,11 +158,21 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
} }
fun addListener(subscriber: IChunkListener): Boolean { fun addListener(subscriber: IChunkListener): Boolean {
return subscribers.add(subscriber) if (subscribers.add(subscriber)) {
entities.forEach { subscriber.onEntityAdded(it) }
return true
}
return false
} }
fun removeListener(subscriber: IChunkListener): Boolean { fun removeListener(subscriber: IChunkListener): Boolean {
return subscribers.remove(subscriber) if (subscribers.remove(subscriber)) {
entities.forEach { subscriber.onEntityRemoved(it) }
return true
}
return false
} }
fun addEntity(entity: AbstractEntity) { fun addEntity(entity: AbstractEntity) {

View File

@ -53,10 +53,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
return chunkMap.setCell(x, y, cell) return chunkMap.setCell(x, y, cell)
} }
interface IChunkMapListener<ChunkType : Chunk<*, *>> { protected open fun onChunkCreated(chunk: ChunkType) { }
fun onChunkCreated(chunk: ChunkType) { } protected open fun onChunkRemoved(chunk: ChunkType) { }
fun onChunkRemoved(chunk: ChunkType) { }
}
abstract inner class ChunkMap : Iterable<ChunkType> { abstract inner class ChunkMap : Iterable<ChunkType> {
abstract operator fun get(x: Int, y: Int): ChunkType? abstract operator fun get(x: Int, y: Int): ChunkType?
@ -71,16 +69,6 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
operator fun get(pos: ChunkPos) = get(pos.x, pos.y) operator fun get(pos: ChunkPos) = get(pos.x, pos.y)
protected val listeners = ReferenceOpenHashSet<IChunkMapListener<ChunkType>>()
fun addListener(listener: IChunkMapListener<ChunkType>) {
listeners.add(listener)
}
fun removeListener(listener: IChunkMapListener<ChunkType>) {
listeners.remove(listener)
}
protected fun create(x: Int, y: Int): ChunkType { protected fun create(x: Int, y: Int): ChunkType {
val pos = ChunkPos(x, y) val pos = ChunkPos(x, y)
val chunk = chunkFactory(pos) val chunk = chunkFactory(pos)
@ -96,7 +84,6 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
ent.chunk = chunk ent.chunk = chunk
} }
listeners.forEach { it.onChunkCreated(chunk) }
return chunk return chunk
} }
@ -123,7 +110,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
if (!geometry.x.inBoundsChunk(x) || !geometry.y.inBoundsChunk(y)) return null if (!geometry.x.inBoundsChunk(x) || !geometry.y.inBoundsChunk(y)) return null
val index = ChunkPos.toLong(x, y) val index = ChunkPos.toLong(x, y)
val get = map[index] ?: create(x, y).also { map[index] = it } val get = map[index] ?: create(x, y).also { map[index] = it; onChunkCreated(it) }
return get return get
} }
@ -146,7 +133,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
if (chunk != null) { if (chunk != null) {
chunk.remove() chunk.remove()
listeners.forEach { it.onChunkRemoved(chunk) } onChunkRemoved(chunk)
map.remove(index) map.remove(index)
} }
} }
@ -169,7 +156,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
override fun compute(x: Int, y: Int): ChunkType? { override fun compute(x: Int, y: Int): ChunkType? {
if (!geometry.x.inBoundsChunk(x) || !geometry.y.inBoundsChunk(y)) return null if (!geometry.x.inBoundsChunk(x) || !geometry.y.inBoundsChunk(y)) return null
return map[x, y] ?: create(x, y).also { existing.add(ChunkPos(x, y)); map[x, y] = it } return map[x, y] ?: create(x, y).also { existing.add(ChunkPos(x, y)); map[x, y] = it; onChunkCreated(it) }
} }
override fun getCell(x: Int, y: Int): AbstractCell { override fun getCell(x: Int, y: Int): AbstractCell {
@ -199,7 +186,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
if (chunk != null) { if (chunk != null) {
chunk.remove() chunk.remove()
listeners.forEach { it.onChunkRemoved(chunk) } onChunkRemoved(chunk)
existing.remove(ChunkPos(x, y)) existing.remove(ChunkPos(x, y))
map[x, y] = null map[x, y] = null
} }
@ -266,7 +253,6 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
} }
protected abstract fun thinkInner() protected abstract fun thinkInner()
protected abstract fun chunkFactory(pos: ChunkPos): ChunkType protected abstract fun chunkFactory(pos: ChunkPos): ChunkType
override fun close() { override fun close() {

View File

@ -17,7 +17,7 @@ import kotlin.math.acos
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
abstract class AbstractMovementController { abstract class AbstractMovementController() {
abstract val world: World<*, *> abstract val world: World<*, *>
val localHitboxes: Stream<Poly> val localHitboxes: Stream<Poly>