Add ServerListInfoRequestEvent, switch from Gson to Moshi and fix tests
This commit is contained in:
parent
81a39c2e96
commit
7dd7bd9e6c
19 changed files with 229 additions and 94 deletions
|
@ -1,5 +1,6 @@
|
|||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("kapt")
|
||||
}
|
||||
|
||||
group = rootProject.group
|
||||
|
@ -13,13 +14,22 @@ repositories {
|
|||
val spekVersion = "2.0.12"
|
||||
|
||||
dependencies {
|
||||
// Kotlin
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
implementation(kotlin("reflect"))
|
||||
implementation("com.google.code.gson:gson:2.8.6")
|
||||
api("org.slf4j:slf4j-api:1.7.30")
|
||||
api("io.netty:netty-buffer:4.1.50.Final")
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8")
|
||||
|
||||
// JSON
|
||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.9.3")
|
||||
api("com.squareup.moshi:moshi:1.9.3")
|
||||
|
||||
// Logging
|
||||
api("org.slf4j:slf4j-api:1.7.30")
|
||||
|
||||
// Netty
|
||||
api("io.netty:netty-buffer:4.1.50.Final")
|
||||
|
||||
// Testing
|
||||
testImplementation("io.strikt:strikt-core:0.26.1")
|
||||
testImplementation("org.spekframework.spek2:spek-dsl-jvm:$spekVersion")
|
||||
testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:$spekVersion")
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package space.blokk
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import space.blokk.chat.ChatComponent
|
||||
import space.blokk.events.EventTargetGroup
|
||||
import space.blokk.net.Session
|
||||
import space.blokk.server.Server
|
||||
|
@ -18,4 +20,9 @@ object Blokk: BlokkProvider {
|
|||
private var provider: BlokkProvider? = null
|
||||
override val server get() = provider!!.server
|
||||
override val sessions get() = provider!!.sessions
|
||||
|
||||
val json = Moshi.Builder()
|
||||
.add(ChatComponent.Adapter)
|
||||
.add(ChatComponent.Color.Adapter)
|
||||
.build()!!
|
||||
}
|
||||
|
|
|
@ -1,23 +1,89 @@
|
|||
package space.blokk.chat
|
||||
|
||||
import com.squareup.moshi.*
|
||||
import space.blokk.Blokk
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
sealed class ChatComponent {
|
||||
abstract val extra: ChatComponent?
|
||||
abstract val bold: Boolean
|
||||
abstract val italic: Boolean
|
||||
abstract val underlined: Boolean
|
||||
abstract val strikethrough: Boolean
|
||||
abstract val obfuscated: Boolean
|
||||
abstract val color: Color?
|
||||
|
||||
/**
|
||||
* Cannot be an empty list. Use null instead.
|
||||
*/
|
||||
abstract val extra: List<ChatComponent>?
|
||||
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
if (extra?.isEmpty() == true) throw IllegalArgumentException("extra cannot be an empty list. Use null instead.")
|
||||
}
|
||||
|
||||
fun getExtraTypes(): List<KClass<out ChatComponent>> {
|
||||
val types = mutableListOf<KClass<out ChatComponent>>()
|
||||
var current = extra
|
||||
while (current != null) {
|
||||
types.add(current::class)
|
||||
current = current.extra
|
||||
val types = mutableSetOf<KClass<out ChatComponent>>()
|
||||
val extras = extra?.toMutableList() ?: return emptyList()
|
||||
while (extras.isNotEmpty()) {
|
||||
extras.toList().forEach {
|
||||
types.add(it::class)
|
||||
extras.remove(it)
|
||||
it.extra?.let { extra -> extras.addAll(extra) }
|
||||
}
|
||||
}
|
||||
|
||||
return types.toList()
|
||||
}
|
||||
|
||||
enum class Color() {
|
||||
BLACK,
|
||||
DARK_BLUE,
|
||||
DARK_GREEN,
|
||||
DARK_AQUA,
|
||||
DARK_RED,
|
||||
DARK_PURPLE,
|
||||
GOLD,
|
||||
GRAY,
|
||||
DARK_GRAY,
|
||||
BLUE,
|
||||
GREEN,
|
||||
AQUA,
|
||||
RED,
|
||||
LIGHT_PURPLE,
|
||||
YELLOW,
|
||||
WHITE;
|
||||
|
||||
object Adapter {
|
||||
@ToJson fun toJson(value: Color) = value.name.toLowerCase()
|
||||
@FromJson fun fromJson(value: String) = valueOf(value.toUpperCase())
|
||||
}
|
||||
}
|
||||
|
||||
object Adapter {
|
||||
@FromJson fun fromJson(reader: JsonReader): ChatComponent? {
|
||||
throw UnsupportedOperationException("ChatComponent cannot be deserialized.")
|
||||
}
|
||||
|
||||
@ToJson fun toJson(writer: JsonWriter, value: ChatComponent?) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (value == null) writer.nullValue()
|
||||
else (Blokk.json.adapter(value::class.java) as JsonAdapter<ChatComponent>).toJson(writer, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class TextComponent(val text: String, override val extra: ChatComponent? = null): ChatComponent() {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TextComponent(
|
||||
val text: String,
|
||||
override val bold: Boolean = false,
|
||||
override val italic: Boolean = false,
|
||||
override val underlined: Boolean = false,
|
||||
override val strikethrough: Boolean = false,
|
||||
override val obfuscated: Boolean = false,
|
||||
override val color: Color? = null,
|
||||
override val extra: List<ChatComponent>? = null
|
||||
): ChatComponent() {
|
||||
companion object {
|
||||
/**
|
||||
* Creates a new [TextComponent] instance using [text] and returns it.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package space.blokk.chat
|
||||
|
||||
/**
|
||||
* Legacy formatting codes. You should use [ChatComponent][space.blokk.chat.ChatComponent] whenever it's possible, but sometimes
|
||||
* these codes are required, for example in
|
||||
* Legacy formatting codes. You should use [ChatComponent][space.blokk.chat.ChatComponent] whenever it's possible,
|
||||
* but sometimes these codes are required, for example in
|
||||
* [the name of a player in a server list sample][space.blokk.net.protocols.status.ResponsePacket.Players.SampleEntry.name].
|
||||
*/
|
||||
enum class FormattingCode(private val char: Char) {
|
||||
|
|
|
@ -7,9 +7,9 @@ interface Cancellable {
|
|||
/**
|
||||
* Only executes [fn] if [isCancelled][Cancellable.isCancelled] is true.
|
||||
*/
|
||||
inline fun Cancellable.ifCancelled(fn: () -> Unit) = if (isCancelled) fn() else Unit
|
||||
inline fun <T: Cancellable, R> T.ifCancelled(fn: (T) -> R): R? = if (isCancelled) fn(this) else null
|
||||
|
||||
/**
|
||||
* Only executes [fn] if [isCancelled][Cancellable.isCancelled] is false.
|
||||
*/
|
||||
inline fun <T> Cancellable.ifNotCancelled(fn: () -> T): T? = if (!isCancelled) fn() else null
|
||||
inline fun <T: Cancellable, R> T.ifNotCancelled(fn: (T) -> R): R? = if (!isCancelled) fn(this) else null
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package space.blokk.events
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import space.blokk.Blokk
|
||||
import kotlinx.coroutines.async
|
||||
import space.blokk.plugins.Plugin
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
|
@ -17,21 +16,15 @@ class EventBus<EventT: Event>(private val eventClass: KClass<EventT>, private va
|
|||
/**
|
||||
* Invokes all previously registered event handlers sorted by their priority
|
||||
* and the order in which they were registered.
|
||||
*
|
||||
* @return [event]
|
||||
*/
|
||||
suspend fun <T: EventT> emitAndAwait(event: T): T {
|
||||
handlers.filter { it.eventType.isInstance(event) }.forEach {
|
||||
scope.launch {
|
||||
it.fn.callSuspend(it.listener, event)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T: EventT> emit(event: T): T {
|
||||
handlers.filter { it.eventType.isInstance(event) }.forEach { it.fn.callSuspend(it.listener, event) }
|
||||
return event
|
||||
}
|
||||
|
||||
fun <T: EventT> emit(event: T): T {
|
||||
Blokk.server.scope.launch { emitAndAwait(event) }
|
||||
return event
|
||||
}
|
||||
fun <T: EventT> emitAsync(event: T) = scope.async { emit(event) }
|
||||
|
||||
/**
|
||||
* Registers all [event handlers][EventHandler] in [listener] to be invoked when their corresponding event is emitted.
|
||||
|
|
|
@ -4,6 +4,6 @@ import space.blokk.events.Cancellable
|
|||
import space.blokk.net.Session
|
||||
import space.blokk.net.protocols.IncomingPacket
|
||||
|
||||
class SessionPacketReceivedEvent<T: IncomingPacket>(session: Session, var packet: T): SessionEvent(session), Cancellable {
|
||||
class PacketReceivedEvent<T: IncomingPacket>(session: Session, var packet: T): SessionEvent(session), Cancellable {
|
||||
override var isCancelled = false
|
||||
}
|
|
@ -4,6 +4,6 @@ import space.blokk.events.Cancellable
|
|||
import space.blokk.net.Session
|
||||
import space.blokk.net.protocols.OutgoingPacket
|
||||
|
||||
class SessionPacketSendEvent(session: Session, var packet: OutgoingPacket): SessionEvent(session), Cancellable {
|
||||
class PacketSendEvent(session: Session, var packet: OutgoingPacket): SessionEvent(session), Cancellable {
|
||||
override var isCancelled = false
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package space.blokk.net.events
|
||||
|
||||
import space.blokk.events.Cancellable
|
||||
import space.blokk.net.Session
|
||||
import space.blokk.net.protocols.status.ResponsePacket
|
||||
|
||||
class ServerListInfoRequestEvent(
|
||||
session: Session,
|
||||
var response: ResponsePacket
|
||||
): SessionEvent(session), Cancellable {
|
||||
override var isCancelled = false
|
||||
}
|
|
@ -1,28 +1,42 @@
|
|||
package space.blokk.net.protocols.status
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.squareup.moshi.JsonClass
|
||||
import io.netty.buffer.ByteBuf
|
||||
import space.blokk.Blokk
|
||||
import space.blokk.chat.TextComponent
|
||||
import space.blokk.net.MinecraftDataTypes
|
||||
import space.blokk.net.protocols.OutgoingPacket
|
||||
import space.blokk.net.protocols.OutgoingPacketCompanion
|
||||
import java.util.*
|
||||
import space.blokk.utils.toJson
|
||||
|
||||
data class ResponsePacket(
|
||||
val versionName: String,
|
||||
val protocolVersion: Int,
|
||||
val description: TextComponent,
|
||||
val players: Players,
|
||||
val favicon: String? = null
|
||||
) : OutgoingPacket() {
|
||||
constructor(
|
||||
versionName: String,
|
||||
protocolVersion: Int,
|
||||
players: Players,
|
||||
description: TextComponent,
|
||||
favicon: ByteArray
|
||||
): this(versionName, protocolVersion, description, players, Base64.getEncoder().encodeToString(favicon))
|
||||
/**
|
||||
* The name of the Minecraft version the server uses.
|
||||
*/
|
||||
val versionName: String,
|
||||
|
||||
/**
|
||||
* The number of the protocol version used by the server.
|
||||
* @see <a href="https://wiki.vg/Protocol_version_numbers">https://wiki.vg/Protocol_version_numbers</a>
|
||||
*/
|
||||
val protocolVersion: Int,
|
||||
|
||||
/**
|
||||
* The description of the server. Although this is a [TextComponent],
|
||||
* [legacy Formatting codes][space.blokk.chat.FormattingCode] must be used.
|
||||
*/
|
||||
val description: TextComponent,
|
||||
|
||||
/**
|
||||
* The players shown when hovering over the player count.
|
||||
*/
|
||||
val players: Players,
|
||||
|
||||
/**
|
||||
* The favicon of the server. This must be a base64 encoded 64x64 PNG image.
|
||||
*/
|
||||
val favicon: String? = null
|
||||
) : OutgoingPacket() {
|
||||
init {
|
||||
if (description.getExtraTypes().find { it != TextComponent::class } != null)
|
||||
throw Exception("description may only contain instances of TextComponent")
|
||||
|
@ -30,27 +44,27 @@ data class ResponsePacket(
|
|||
|
||||
override fun encode(dst: ByteBuf) {
|
||||
with(MinecraftDataTypes) {
|
||||
dst.writeString(gson.toJson(mapOf(
|
||||
"version" to mapOf(
|
||||
"name" to versionName,
|
||||
"protocol" to protocolVersion
|
||||
),
|
||||
"players" to players,
|
||||
"description" to description,
|
||||
"favicon" to favicon
|
||||
dst.writeString(Blokk.json.toJson(mapOf(
|
||||
"version" to mapOf(
|
||||
"name" to versionName,
|
||||
"protocol" to protocolVersion
|
||||
),
|
||||
"players" to players,
|
||||
"description" to description,
|
||||
"favicon" to favicon?.let { "data:image/png;base64,$it" }
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Players(val max: Int, val online: Int, val sample: List<SampleEntry>) {
|
||||
/**
|
||||
* @param name The name of the player. You can use [FormattingCode](space.blokk.chat.FormattingCode)s
|
||||
* @param id The UUID of the player as a string. You can use a random UUID as it doesn't get validated by the Minecraft client
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SampleEntry(val name: String, val id: String)
|
||||
}
|
||||
|
||||
companion object : OutgoingPacketCompanion<ResponsePacket>(0x00, ResponsePacket::class) {
|
||||
private val gson = GsonBuilder().disableHtmlEscaping().create()!!
|
||||
}
|
||||
companion object : OutgoingPacketCompanion<ResponsePacket>(0x00, ResponsePacket::class)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package space.blokk.utils
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
class Logger(name: String) {
|
||||
private val logger = LoggerFactory.getLogger(name)
|
||||
private val logger: Logger = LoggerFactory.getLogger(name)
|
||||
|
||||
fun error(msg: String, t: Throwable) = logger.error(msg, t)
|
||||
|
||||
infix fun error(msg: String) = logger.error(msg)
|
||||
infix fun info(msg: String) = logger.info(msg)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package space.blokk.utils
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
|
||||
fun Moshi.toJson(value: Map<*, *>) = adapter(Map::class.java).toJson(value)
|
|
@ -1,4 +1,7 @@
|
|||
package space.blokk.events
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.spekframework.spek2.Spek
|
||||
import org.spekframework.spek2.style.specification.describe
|
||||
import strikt.api.expectThat
|
||||
|
@ -14,7 +17,7 @@ private class SecondEvent: TestEvent()
|
|||
|
||||
object EventBusTest: Spek({
|
||||
describe("EventBus") {
|
||||
val eventBus by memoized { EventBus(TestEvent::class) }
|
||||
val eventBus by memoized { EventBus(TestEvent::class, CoroutineScope(Dispatchers.Default)) }
|
||||
|
||||
it("calls the handler exactly 1 time when the event is emitted 1 time") {
|
||||
var calledCount = 0
|
||||
|
@ -22,7 +25,7 @@ object EventBusTest: Spek({
|
|||
@EventHandler fun onFirstEvent(event: FirstEvent) { calledCount++ }
|
||||
})
|
||||
|
||||
eventBus.emit(FirstEvent())
|
||||
runBlocking { eventBus.emit(FirstEvent()) }
|
||||
expectThat(calledCount).isEqualTo(1)
|
||||
}
|
||||
|
||||
|
@ -32,9 +35,12 @@ object EventBusTest: Spek({
|
|||
@EventHandler fun onFirstEvent(event: FirstEvent) { calledCount++ }
|
||||
})
|
||||
|
||||
eventBus.emit(FirstEvent())
|
||||
eventBus.emit(FirstEvent())
|
||||
eventBus.emit(FirstEvent())
|
||||
|
||||
runBlocking {
|
||||
eventBus.emit(FirstEvent())
|
||||
eventBus.emit(FirstEvent())
|
||||
eventBus.emit(FirstEvent())
|
||||
}
|
||||
|
||||
expectThat(calledCount).isEqualTo(3)
|
||||
}
|
||||
|
@ -61,7 +67,7 @@ object EventBusTest: Spek({
|
|||
@EventHandler fun onFirstEvent(event: FirstEvent) { onFirstEventCalled = true }
|
||||
})
|
||||
|
||||
eventBus.emit(FirstEvent())
|
||||
runBlocking { eventBus.emit(FirstEvent()) }
|
||||
|
||||
expectThat(onTestEventCalled).isTrue()
|
||||
expectThat(onFirstEventCalled).isTrue()
|
||||
|
@ -75,7 +81,7 @@ object EventBusTest: Spek({
|
|||
|
||||
eventBus.unregister(listener)
|
||||
|
||||
eventBus.emit(FirstEvent())
|
||||
runBlocking { eventBus.emit(FirstEvent()) }
|
||||
|
||||
expectThat(called).isFalse()
|
||||
}
|
||||
|
@ -114,7 +120,7 @@ object EventBusTest: Spek({
|
|||
@EventHandler(EventPriority.HIGH) fun onFirstEventHigh(event: FirstEvent) { order.add(EventPriority.HIGH) }
|
||||
})
|
||||
|
||||
eventBus.emit(FirstEvent())
|
||||
runBlocking { eventBus.emit(FirstEvent()) }
|
||||
|
||||
expectThat(order).isSorted(Comparator.comparing<EventPriority, Int> { it.ordinal })
|
||||
}
|
||||
|
|
|
@ -2,8 +2,13 @@ package space.blokk
|
|||
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import space.blokk.chat.ChatComponent
|
||||
import space.blokk.chat.TextComponent
|
||||
import space.blokk.events.EventBus
|
||||
import space.blokk.events.EventHandler
|
||||
import space.blokk.events.Listener
|
||||
import space.blokk.net.BlokkSocketServer
|
||||
import space.blokk.net.events.ServerListInfoRequestEvent
|
||||
import space.blokk.server.Server
|
||||
import space.blokk.server.events.ServerEvent
|
||||
import space.blokk.utils.Logger
|
||||
|
@ -14,7 +19,7 @@ class BlokkServer internal constructor(): Server {
|
|||
override val scope = CoroutineScope(CoroutineName("BlokkServer"))
|
||||
override val eventBus = EventBus(ServerEvent::class, scope)
|
||||
val logger = Logger("BlokkServer")
|
||||
var blokkSocketServer = BlokkSocketServer(this); private set
|
||||
var socketServer = BlokkSocketServer(this); private set
|
||||
|
||||
// TODO: Read from config file
|
||||
val host = "0.0.0.0"
|
||||
|
@ -26,14 +31,14 @@ class BlokkServer internal constructor(): Server {
|
|||
providerField.isAccessible = true
|
||||
providerField.set(Blokk, object : BlokkProvider {
|
||||
override val server = this@BlokkServer
|
||||
override val sessions get() = this@BlokkServer.blokkSocketServer.allSessionsGroup
|
||||
override val sessions get() = this@BlokkServer.socketServer.allSessionsGroup
|
||||
})
|
||||
providerField.isAccessible = false
|
||||
}
|
||||
|
||||
fun start() {
|
||||
logger info "Starting BlokkServer (${if (VERSION == "development") VERSION else "v$VERSION"})"
|
||||
blokkSocketServer.bind()
|
||||
socketServer.bind()
|
||||
logger info "Listening on $host:$port"
|
||||
}
|
||||
|
||||
|
@ -44,6 +49,13 @@ class BlokkServer internal constructor(): Server {
|
|||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
val server = BlokkServer()
|
||||
Blokk.sessions.registerListener(object : Listener {
|
||||
@EventHandler
|
||||
fun onServerListInfoRequest(event: ServerListInfoRequestEvent) {
|
||||
event.response = event.response.copy(description = TextComponent(event.session.address.hostAddress, bold = true, underlined = true, color = ChatComponent.Color.RED))
|
||||
}
|
||||
})
|
||||
|
||||
server.start()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import io.netty.channel.Channel
|
|||
import kotlinx.coroutines.*
|
||||
import space.blokk.BlokkServer
|
||||
import space.blokk.events.*
|
||||
import space.blokk.net.events.PacketReceivedEvent
|
||||
import space.blokk.net.events.PacketSendEvent
|
||||
import space.blokk.net.events.SessionEvent
|
||||
import space.blokk.net.events.SessionPacketReceivedEvent
|
||||
import space.blokk.net.events.SessionPacketSendEvent
|
||||
import space.blokk.net.protocols.OutgoingPacket
|
||||
import space.blokk.net.protocols.Protocol
|
||||
import space.blokk.net.protocols.handshaking.HandshakingProtocol
|
||||
|
@ -23,45 +23,48 @@ class BlokkSession(private val channel: Channel) : Session {
|
|||
|
||||
override var currentProtocol: Protocol = HandshakingProtocol
|
||||
set(value) {
|
||||
logger debug "Switching protocol: $currentProtocol -> $value"
|
||||
if (value == field) return
|
||||
logger trace "Switching protocol: $currentProtocol -> $value"
|
||||
field = value
|
||||
}
|
||||
|
||||
override val scope = CoroutineScope(Dispatchers.Unconfined + CoroutineName(identifier))
|
||||
override val eventBus = EventBus(SessionEvent::class, scope)
|
||||
|
||||
var active: Boolean = true
|
||||
private var active: Boolean = true
|
||||
|
||||
init {
|
||||
eventBus.register(object : Listener {
|
||||
@EventHandler(priority = EventPriority.INTERNAL)
|
||||
suspend fun onSessionPacketReceived(event: SessionPacketReceivedEvent<*>) {
|
||||
suspend fun onSessionPacketReceived(event: PacketReceivedEvent<*>) {
|
||||
SessionPacketReceivedEventHandler.handle(event.session as BlokkSession, event.packet)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun onConnect() = scope.launch {
|
||||
if (BlokkServer.i.eventBus.emit(SessionInitializedEvent(this@BlokkSession)).isCancelled) channel.close()
|
||||
else BlokkServer.i.blokkSocketServer.allSessionsGroup.add(this@BlokkSession)
|
||||
logger trace "Connected"
|
||||
if (BlokkServer.i.eventBus.emitAsync(SessionInitializedEvent(this@BlokkSession)).isCancelled) channel.close()
|
||||
else BlokkServer.i.socketServer.allSessionsGroup.add(this@BlokkSession)
|
||||
}
|
||||
|
||||
fun onDisconnect() {
|
||||
if (!active) throw IllegalStateException("The session is not active anymore")
|
||||
logger trace "Disconnected"
|
||||
active = false
|
||||
scope.cancel("Disconnected")
|
||||
BlokkServer.i.blokkSocketServer.allSessionsGroup.remove(this)
|
||||
BlokkServer.i.socketServer.allSessionsGroup.remove(this)
|
||||
}
|
||||
|
||||
override suspend fun send(packet: OutgoingPacket) {
|
||||
if (!active) throw IllegalStateException("The session is not active anymore")
|
||||
|
||||
logger debug { "Sending packet: $packet" }
|
||||
val event = eventBus.emit(SessionPacketSendEvent(this@BlokkSession, packet))
|
||||
event.ifNotCancelled {
|
||||
logger trace { "Sending packet: $packet" }
|
||||
eventBus.emit(PacketSendEvent(this@BlokkSession, packet)).ifNotCancelled {
|
||||
try {
|
||||
channel.writeAndFlush(PacketMessage(this@BlokkSession, event.packet)).awaitSuspending()
|
||||
} catch (e: Throwable) {
|
||||
logger error { "Packet send failed: $e" }
|
||||
channel.writeAndFlush(PacketMessage(this@BlokkSession, it.packet)).awaitSuspending()
|
||||
} catch (t: Throwable) {
|
||||
logger.error("Packet send failed:", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,5 @@ class PacketCodec(private val session: BlokkSession): MessageToMessageCodec<Byte
|
|||
|
||||
// You can usually ignore connection errors as they are caused by modified clients such as hack clients
|
||||
if (BlokkServer.i.silentConnectionErrors) session.logger.debug(message) else session.logger.error(message)
|
||||
cause.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,15 @@ package space.blokk.net
|
|||
|
||||
import io.netty.channel.ChannelHandlerContext
|
||||
import io.netty.channel.SimpleChannelInboundHandler
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import space.blokk.net.events.SessionPacketReceivedEvent
|
||||
import kotlinx.coroutines.launch
|
||||
import space.blokk.net.events.PacketReceivedEvent
|
||||
import space.blokk.net.protocols.IncomingPacket
|
||||
|
||||
class PacketMessageHandler(private val session: BlokkSession): SimpleChannelInboundHandler<PacketMessage<*>>() {
|
||||
override fun channelRead0(ctx: ChannelHandlerContext, msg: PacketMessage<*>) {
|
||||
if (msg.packet !is IncomingPacket) throw Error("Only serverbound packets are allowed. This should never happen.")
|
||||
session.logger.debug { "Packet received: ${msg.packet}" }
|
||||
runBlocking {
|
||||
session.eventBus.emitAndAwait(SessionPacketReceivedEvent(session, msg.packet))
|
||||
}
|
||||
session.logger.trace { "Packet received: ${msg.packet}" }
|
||||
session.scope.launch { session.eventBus.emit(PacketReceivedEvent(session, msg.packet)) }
|
||||
|
||||
// TODO: Disconnect when invalid data is received
|
||||
}
|
||||
|
|
|
@ -2,24 +2,30 @@ package space.blokk.net.protocols.status
|
|||
|
||||
import space.blokk.chat.FormattingCode
|
||||
import space.blokk.chat.TextComponent
|
||||
import space.blokk.events.ifNotCancelled
|
||||
import space.blokk.net.PacketReceivedEventHandler
|
||||
import space.blokk.net.ProtocolPacketReceivedEventHandler
|
||||
import space.blokk.net.events.ServerListInfoRequestEvent
|
||||
import java.util.*
|
||||
|
||||
// NOTE: PacketReceivedEventHandler.of<T> MUST have T specified correctly, otherwise the code breaks at runtime
|
||||
|
||||
object StatusProtocolHandler: ProtocolPacketReceivedEventHandler(mapOf(
|
||||
RequestPacket to PacketReceivedEventHandler.of<RequestPacket> { session, _ ->
|
||||
session.send(ResponsePacket(
|
||||
session.eventBus.emit(ServerListInfoRequestEvent(
|
||||
session,
|
||||
// TODO: Use the real server data
|
||||
ResponsePacket(
|
||||
versionName = "1.15.2",
|
||||
protocolVersion = 578,
|
||||
description = TextComponent of "nice",
|
||||
description = TextComponent of "Hello World!",
|
||||
players = ResponsePacket.Players(
|
||||
10,
|
||||
10,
|
||||
listOf(ResponsePacket.Players.SampleEntry("${FormattingCode.AQUA}Gronkh", UUID.randomUUID().toString()))
|
||||
10,
|
||||
10,
|
||||
listOf(ResponsePacket.Players.SampleEntry("${FormattingCode.AQUA}Gronkh", UUID.randomUUID().toString()))
|
||||
)
|
||||
))
|
||||
)
|
||||
)).ifNotCancelled { session.send(it.response) }
|
||||
},
|
||||
PingPacket to PacketReceivedEventHandler.of<PingPacket> { session, packet ->
|
||||
session.send(PongPacket(packet.payload))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
plugins {
|
||||
kotlin("jvm") version "1.3.71"
|
||||
kotlin("kapt") version "1.3.72"
|
||||
}
|
||||
|
||||
group = "space.blokk"
|
||||
|
|
Reference in a new issue