Archived
1
0
Fork 0

Add ticker for suspending coroutines for a specific amount of ticks

This commit is contained in:
Moritz Ruth 2020-09-07 00:17:55 +02:00
parent 0725df25c8
commit 058c3b19be
No known key found for this signature in database
GPG key ID: AFD57E23E753841B
13 changed files with 96 additions and 19 deletions

View file

@ -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 {

View file

@ -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
/** /**

View file

@ -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

View file

@ -1,4 +1,4 @@
package space.blokk package space.blokk.players
enum class GameMode { enum class GameMode {
SURVIVAL, SURVIVAL,

View file

@ -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
}
} }

View 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)

View 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
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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

View 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)
}
}
}
}

View file

@ -9,17 +9,3 @@ version = "0.0.1"
repositories { repositories {
mavenCentral() mavenCentral()
} }
dependencies {
}
tasks {
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
}

View file

@ -3,7 +3,6 @@ pluginManagement {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
} }
} }
rootProject.name = "blokk" rootProject.name = "blokk"