Add ticker for suspending coroutines for a specific amount of ticks
This commit is contained in:
parent
0725df25c8
commit
058c3b19be
13 changed files with 96 additions and 19 deletions
|
@ -41,6 +41,9 @@ dependencies {
|
||||||
tasks {
|
tasks {
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
|
kotlinOptions.freeCompilerArgs = listOf(
|
||||||
|
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
compileTestKotlin {
|
compileTestKotlin {
|
||||||
|
|
|
@ -9,6 +9,9 @@ import space.blokk.utils.UUIDAdapter
|
||||||
import space.blokk.utils.UUIDWithoutHyphensAdapter
|
import space.blokk.utils.UUIDWithoutHyphensAdapter
|
||||||
|
|
||||||
interface BlokkProvider {
|
interface BlokkProvider {
|
||||||
|
/**
|
||||||
|
* The [Server] instance.
|
||||||
|
*/
|
||||||
val server: Server
|
val server: Server
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package space.blokk.net.protocols.play
|
package space.blokk.net.protocols.play
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf
|
import io.netty.buffer.ByteBuf
|
||||||
import space.blokk.GameMode
|
|
||||||
import space.blokk.net.MinecraftDataTypes.writeString
|
import space.blokk.net.MinecraftDataTypes.writeString
|
||||||
import space.blokk.net.MinecraftDataTypes.writeVarInt
|
import space.blokk.net.MinecraftDataTypes.writeVarInt
|
||||||
import space.blokk.net.protocols.OutgoingPacket
|
import space.blokk.net.protocols.OutgoingPacket
|
||||||
import space.blokk.net.protocols.OutgoingPacketCompanion
|
import space.blokk.net.protocols.OutgoingPacketCompanion
|
||||||
|
import space.blokk.players.GameMode
|
||||||
import space.blokk.worlds.WorldDimension
|
import space.blokk.worlds.WorldDimension
|
||||||
import space.blokk.worlds.WorldType
|
import space.blokk.worlds.WorldType
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package space.blokk
|
package space.blokk.players
|
||||||
|
|
||||||
enum class GameMode {
|
enum class GameMode {
|
||||||
SURVIVAL,
|
SURVIVAL,
|
|
@ -1,6 +1,7 @@
|
||||||
package space.blokk.server
|
package space.blokk.server
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.channels.ReceiveChannel
|
||||||
import space.blokk.events.EventBus
|
import space.blokk.events.EventBus
|
||||||
import space.blokk.events.EventTarget
|
import space.blokk.events.EventTarget
|
||||||
import space.blokk.server.events.ServerEvent
|
import space.blokk.server.events.ServerEvent
|
||||||
|
@ -8,4 +9,10 @@ import space.blokk.server.events.ServerEvent
|
||||||
interface Server : EventTarget<ServerEvent> {
|
interface Server : EventTarget<ServerEvent> {
|
||||||
override val eventBus: EventBus<ServerEvent>
|
override val eventBus: EventBus<ServerEvent>
|
||||||
val scope: CoroutineScope
|
val scope: CoroutineScope
|
||||||
|
|
||||||
|
fun createTickChannel(): ReceiveChannel<Unit>
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TICKS_PER_SECOND = 20
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
17
blokk-api/src/main/kotlin/space/blokk/utils/DelayTicks.kt
Normal file
17
blokk-api/src/main/kotlin/space/blokk/utils/DelayTicks.kt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package space.blokk.utils
|
||||||
|
|
||||||
|
import space.blokk.Blokk
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suspends for [ticks].
|
||||||
|
*/
|
||||||
|
suspend fun delayTicks(ticks: Long) {
|
||||||
|
val channel = Blokk.server.createTickChannel()
|
||||||
|
(0..ticks).forEach { _ -> channel.receive() }
|
||||||
|
channel.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suspends for one tick.
|
||||||
|
*/
|
||||||
|
suspend fun delayTick() = delayTicks(1)
|
16
blokk-api/src/main/kotlin/space/blokk/worlds/World.kt
Normal file
16
blokk-api/src/main/kotlin/space/blokk/worlds/World.kt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package space.blokk.worlds
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A minecraft world, sometimes also called level.
|
||||||
|
*/
|
||||||
|
interface World {
|
||||||
|
/**
|
||||||
|
* The [dimension][WorldDimension] of the world.
|
||||||
|
*/
|
||||||
|
var dimension: WorldDimension
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [type][WorldType] of the world.
|
||||||
|
*/
|
||||||
|
var type: WorldType
|
||||||
|
}
|
|
@ -48,6 +48,9 @@ dependencies {
|
||||||
tasks {
|
tasks {
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
|
kotlinOptions.freeCompilerArgs = listOf(
|
||||||
|
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import space.blokk.net.events.ServerListInfoRequestEvent
|
||||||
import space.blokk.server.Server
|
import space.blokk.server.Server
|
||||||
import space.blokk.server.events.ServerEvent
|
import space.blokk.server.events.ServerEvent
|
||||||
import space.blokk.utils.EncryptionUtils
|
import space.blokk.utils.EncryptionUtils
|
||||||
|
import space.blokk.utils.Ticker
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
@ -28,7 +29,8 @@ class BlokkServer internal constructor() : Server {
|
||||||
|
|
||||||
override val scope = CoroutineScope(CoroutineName("BlokkServer"))
|
override val scope = CoroutineScope(CoroutineName("BlokkServer"))
|
||||||
override val eventBus = EventBus(ServerEvent::class, scope)
|
override val eventBus = EventBus(ServerEvent::class, scope)
|
||||||
val logger = Logger("BlokkServer")
|
|
||||||
|
val logger = Logger("Server")
|
||||||
var socketServer = BlokkSocketServer(); private set
|
var socketServer = BlokkSocketServer(); private set
|
||||||
|
|
||||||
val keyPair: KeyPair
|
val keyPair: KeyPair
|
||||||
|
@ -64,7 +66,14 @@ class BlokkServer internal constructor() : Server {
|
||||||
)
|
)
|
||||||
.build().loadConfigOrThrow<BlokkConfig>()
|
.build().loadConfigOrThrow<BlokkConfig>()
|
||||||
|
|
||||||
|
private val ticker = Ticker(scope)
|
||||||
|
override fun createTickChannel() = ticker.createTickChannel()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
setProvider()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setProvider() {
|
||||||
val providerField = Blokk::class.java.getDeclaredField("provider")
|
val providerField = Blokk::class.java.getDeclaredField("provider")
|
||||||
providerField.isAccessible = true
|
providerField.isAccessible = true
|
||||||
providerField.set(Blokk, object : BlokkProvider {
|
providerField.set(Blokk, object : BlokkProvider {
|
||||||
|
@ -86,6 +95,8 @@ class BlokkServer internal constructor() : Server {
|
||||||
|
|
||||||
socketServer.bind()
|
socketServer.bind()
|
||||||
logger info "Listening on ${config.host}:${config.port}"
|
logger info "Listening on ${config.host}:${config.port}"
|
||||||
|
|
||||||
|
ticker.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package space.blokk.net
|
||||||
|
|
||||||
import io.netty.buffer.Unpooled
|
import io.netty.buffer.Unpooled
|
||||||
import space.blokk.BlokkServer
|
import space.blokk.BlokkServer
|
||||||
import space.blokk.GameMode
|
|
||||||
import space.blokk.chat.TextComponent
|
import space.blokk.chat.TextComponent
|
||||||
import space.blokk.net.MinecraftDataTypes.writeString
|
import space.blokk.net.MinecraftDataTypes.writeString
|
||||||
import space.blokk.net.protocols.login.EncryptionRequestPacket
|
import space.blokk.net.protocols.login.EncryptionRequestPacket
|
||||||
|
@ -13,6 +12,7 @@ import space.blokk.net.protocols.play.JoinGamePacket
|
||||||
import space.blokk.net.protocols.play.PlayProtocol
|
import space.blokk.net.protocols.play.PlayProtocol
|
||||||
import space.blokk.net.protocols.play.PlayerAbilitiesPacket
|
import space.blokk.net.protocols.play.PlayerAbilitiesPacket
|
||||||
import space.blokk.net.protocols.play.ServerDifficultyPacket
|
import space.blokk.net.protocols.play.ServerDifficultyPacket
|
||||||
|
import space.blokk.players.GameMode
|
||||||
import space.blokk.utils.AuthenticationHelper
|
import space.blokk.utils.AuthenticationHelper
|
||||||
import space.blokk.utils.EncryptionUtils
|
import space.blokk.utils.EncryptionUtils
|
||||||
import space.blokk.worlds.WorldDifficulty
|
import space.blokk.worlds.WorldDifficulty
|
||||||
|
|
32
blokk-server/src/main/kotlin/space/blokk/utils/Ticker.kt
Normal file
32
blokk-server/src/main/kotlin/space/blokk/utils/Ticker.kt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package space.blokk.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.channels.ReceiveChannel
|
||||||
|
import space.blokk.server.Server
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
class Ticker(serverScope: CoroutineScope) {
|
||||||
|
private val scope = serverScope + Executors
|
||||||
|
.newSingleThreadExecutor { Thread(it).also { thread -> thread.name = "Ticker" } }
|
||||||
|
.asCoroutineDispatcher()
|
||||||
|
|
||||||
|
private val channels: MutableSet<Channel<Unit>> = Collections.synchronizedSet(mutableSetOf<Channel<Unit>>())
|
||||||
|
|
||||||
|
fun createTickChannel(): ReceiveChannel<Unit> {
|
||||||
|
return Channel<Unit>().also { channel ->
|
||||||
|
channel.invokeOnClose { channels.remove(channel) }
|
||||||
|
channels.add(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
scope.launch {
|
||||||
|
while (true) {
|
||||||
|
channels.toSet().forEach { it.send(Unit) }
|
||||||
|
delay(1000L / Server.TICKS_PER_SECOND)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,17 +9,3 @@ version = "0.0.1"
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ pluginManagement {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "blokk"
|
rootProject.name = "blokk"
|
||||||
|
|
Reference in a new issue