diff --git a/gradle.properties b/gradle.properties index 4ac44cbc..cf16460b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ kotlin.code.style=official org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m kotlinVersion=1.9.0 -kommonsVersion=1.7.8 +kommonsVersion=1.7.9 ffiVersion=2.2.13 lwjglVersion=3.3.0 diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index 18fffdec..8f27d449 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -40,6 +40,7 @@ fun main() { val server = IntegratedStarboundServer(File("./")) val client = StarboundClient.create().get() + //val client2 = StarboundClient.create().get() val world = ServerWorld(server, 0L, WorldGeometry(Vector2i(3000, 2000), true, false)) world.addChunkSource(LegacyChunkSource(db)) world.thread.start() @@ -81,10 +82,12 @@ fun main() { } client.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID()) + //client2.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID()) } //ent.position += Vector2d(y = 14.0, x = -10.0) client.camera.pos = Vector2d(238.0, 685.0) + //client2.camera.pos = Vector2d(238.0, 685.0) //client.camera.pos = Vector2f(0f, 0f) //ent.spawn() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt index 16946fa3..74fa7ab5 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/StarboundServer.kt @@ -13,7 +13,7 @@ import java.util.concurrent.locks.LockSupport import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock -abstract class StarboundServer(val root: File) : Closeable { +sealed class StarboundServer(val root: File) : Closeable { init { if (!root.exists()) { check(root.mkdirs()) { "Unable to create ${root.absolutePath}" } @@ -37,7 +37,7 @@ abstract class StarboundServer(val root: File) : Closeable { private set init { - thread.isDaemon = true + thread.isDaemon = this is IntegratedStarboundServer thread.start() } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/network/ServerConnection.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/network/ServerConnection.kt index 46580c9e..933fb08e 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/network/ServerConnection.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/network/ServerConnection.kt @@ -110,7 +110,7 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn if (pos !in tickets) { val ticket = world.permanentChunkTicket(pos) tickets[pos] = ticket - ticket.addListener(ChunkListener(pos)) + ticket.listener = ChunkListener(pos) pendingSend.add(pos) } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt index e71f43ac..502e7e4d 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt @@ -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 { fun cancel() val isCanceled: Boolean val pos: ChunkPos val id: Int - val chunk: ServerChunk? - - fun addListener(listener: IChunkListener) - fun removeListener(listener: IChunkListener) + var listener: IChunkListener? } interface ITimedTicket : ITicket, Comparable { @@ -208,7 +213,7 @@ class ServerWorld( } } - private inner class TicketList(val pos: ChunkPos) : IChunkListener, IChunkMapListener { + private inner class TicketList(val pos: ChunkPos) : IChunkListener { constructor(pos: Long) : this(ChunkPos(pos)) private var first = true @@ -216,7 +221,6 @@ class ServerWorld( private val temporary = ObjectAVLTreeSet() private var ticks = 0 private var nextTicketID = AtomicInteger() - private var weAreResponsibleForLoadingTheChunk = false val isValid: Boolean get() = temporary.isNotEmpty() || permanent.isNotEmpty() @@ -234,33 +238,21 @@ class ServerWorld( } override fun onEntityAdded(entity: AbstractEntity) { - permanent.forEach { it.onEntityAdded(entity) } - temporary.forEach { it.onEntityAdded(entity) } + permanent.forEach { it.listener?.onEntityAdded(entity) } + temporary.forEach { it.listener?.onEntityAdded(entity) } } override fun onEntityRemoved(entity: AbstractEntity) { - permanent.forEach { it.onEntityRemoved(entity) } - temporary.forEach { it.onEntityRemoved(entity) } + permanent.forEach { it.listener?.onEntityRemoved(entity) } + temporary.forEach { it.listener?.onEntityRemoved(entity) } } override fun onCellChanges(x: Int, y: Int, cell: ImmutableCell) { - permanent.forEach { it.onCellChanges(x, y, cell) } - temporary.forEach { it.onCellChanges(x, y, cell) } + permanent.forEach { it.listener?.onCellChanges(x, y, cell) } + temporary.forEach { it.listener?.onCellChanges(x, y, cell) } } - override fun onChunkCreated(chunk: ServerChunk) { - 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 { + abstract inner class AbstractTicket : ITicket { final override val id: Int = nextTicketID.getAndIncrement() final override val pos: ChunkPos get() = this@TicketList.pos @@ -273,11 +265,10 @@ class ServerWorld( if (geometry.x.inBoundsChunk(pos.x) && geometry.y.inBoundsChunk(pos.y)) { ticketLists.add(this@TicketList) - chunkMap.addListener(this@TicketList) - if (chunkProviders.isNotEmpty() && chunkMap[pos] == null) { - weAreResponsibleForLoadingTheChunk = true + val existing = chunkMap[pos] + if (chunkProviders.isNotEmpty() && existing == null) { chainOptionalFutures(chunkProviders) { if (!isValid) CompletableFuture.completedFuture(KOptional.empty()) else it.getTiles(pos) } .thenAccept(Consumer { tiles -> @@ -297,8 +288,12 @@ class ServerWorld( } }, mailbox) }) + } else if (existing != null) { + existing.addListener(this@TicketList) } } + } else { + chunkMap[pos]?.addListener(this@TicketList) } } @@ -308,7 +303,7 @@ class ServerWorld( lock.withLock { if (isCanceled) return isCanceled = true - chunk?.entities?.forEach { e -> listeners.forEach { it.onEntityRemoved(e) } } + chunk?.entities?.forEach { e -> listener?.onEntityRemoved(e) } onCancel() } } @@ -317,31 +312,15 @@ class ServerWorld( final override val chunk: ServerChunk? get() = chunkMap[pos] - private val listeners = ReferenceLinkedOpenHashSet() - - final override fun addListener(listener: IChunkListener) { - if (isCanceled) return - listeners.add(listener) - chunk?.entities?.forEach { listener.onEntityAdded(it) } - } - - final override fun removeListener(listener: IChunkListener) { - if (listeners.remove(listener)) { - chunk?.entities?.forEach { listener.onEntityRemoved(it) } + final override var listener: IChunkListener? = null + set(value) { + if (field != value) { + val chunk = chunk + chunk?.entities?.forEach { e -> field?.onEntityRemoved(e) } + chunk?.entities?.forEach { e -> value?.onEntityAdded(e) } + field = value + } } - } - - 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() { diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt index 3149bd90..16934559 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/Chunk.kt @@ -158,11 +158,21 @@ abstract class Chunk, This : Chunk, ChunkType : Chunk> { - fun onChunkCreated(chunk: ChunkType) { } - fun onChunkRemoved(chunk: ChunkType) { } - } + protected open fun onChunkCreated(chunk: ChunkType) { } + protected open fun onChunkRemoved(chunk: ChunkType) { } abstract inner class ChunkMap : Iterable { abstract operator fun get(x: Int, y: Int): ChunkType? @@ -71,16 +69,6 @@ abstract class World, ChunkType : Chunk>() - - fun addListener(listener: IChunkMapListener) { - listeners.add(listener) - } - - fun removeListener(listener: IChunkMapListener) { - listeners.remove(listener) - } - protected fun create(x: Int, y: Int): ChunkType { val pos = ChunkPos(x, y) val chunk = chunkFactory(pos) @@ -96,7 +84,6 @@ abstract class World, ChunkType : Chunk, ChunkType : Chunk, ChunkType : Chunk, ChunkType : Chunk, ChunkType : Chunk, ChunkType : Chunk val localHitboxes: Stream