diff --git a/test-plugin/build.gradle.kts b/test-plugin/build.gradle.kts index 603bdb0..fec00da 100644 --- a/test-plugin/build.gradle.kts +++ b/test-plugin/build.gradle.kts @@ -12,6 +12,7 @@ repositories { dependencies { implementation(project(":uranos-api")) + implementation(project(":uranos-packets")) } tasks { diff --git a/test-plugin/src/main/kotlin/space/uranos/testplugin/TestPlugin.kt b/test-plugin/src/main/kotlin/space/uranos/testplugin/TestPlugin.kt index 37677a4..57be41d 100644 --- a/test-plugin/src/main/kotlin/space/uranos/testplugin/TestPlugin.kt +++ b/test-plugin/src/main/kotlin/space/uranos/testplugin/TestPlugin.kt @@ -53,8 +53,10 @@ class TestPlugin: Plugin("Test", "1.0.0") { .withRotation(0f, 0f) .inside(world) - Uranos.scheduler.executeAfter(secondsToTicks(10)) { - Uranos.players.first().playerListName = TextComponent("Test", true, color = ChatColor.BLUE) + Uranos.scheduler.executeRepeating(secondsToTicks(1)) { + for (player in Uranos.players) { + player.playerListName = TextComponent(player.name, color = ChatColor.NAMED_COLORS.values.random()) + } } } } diff --git a/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt b/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt index e468a94..3367ca5 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/LivingEntity.kt @@ -24,7 +24,6 @@ abstract class LivingEntity : Entity(), Mobile { object DataStorageKeys { val position = createDataStorageKey("position") { entity: LivingEntity, value: Position -> // TODO: Send the position to players - println(value) null } } diff --git a/uranos-api/src/main/kotlin/space/uranos/entity/PlayerEntity.kt b/uranos-api/src/main/kotlin/space/uranos/entity/PlayerEntity.kt index f7d5c64..a043a71 100644 --- a/uranos-api/src/main/kotlin/space/uranos/entity/PlayerEntity.kt +++ b/uranos-api/src/main/kotlin/space/uranos/entity/PlayerEntity.kt @@ -3,6 +3,8 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file */ +@file:Suppress("LeakingThis") + package space.uranos.entity import space.uranos.Position @@ -24,7 +26,6 @@ open class PlayerEntity( final override val type: EntityType = Type override var velocity: Vector = Vector.ZERO - @Suppress("LeakingThis") override val dataStorage: DataStorage = DataStorage(this) init { diff --git a/uranos-api/src/main/kotlin/space/uranos/net/Session.kt b/uranos-api/src/main/kotlin/space/uranos/net/Session.kt index d80cfaf..f633486 100644 --- a/uranos-api/src/main/kotlin/space/uranos/net/Session.kt +++ b/uranos-api/src/main/kotlin/space/uranos/net/Session.kt @@ -117,6 +117,11 @@ abstract class Session { */ abstract suspend fun send(packet: OutgoingPacket) + /** + * Sends a packet the next tick. + */ + abstract fun sendNextTick(packet: OutgoingPacket) + /** * Sends a plugin message packet. */ diff --git a/uranos-api/src/main/kotlin/space/uranos/net/packet/Packet.kt b/uranos-api/src/main/kotlin/space/uranos/net/packet/Packet.kt index e32bccc..3dade4b 100644 --- a/uranos-api/src/main/kotlin/space/uranos/net/packet/Packet.kt +++ b/uranos-api/src/main/kotlin/space/uranos/net/packet/Packet.kt @@ -10,4 +10,14 @@ sealed class Packet { } abstract class IncomingPacket : Packet() + +interface Mergeable { + /** + * Merges this packet with [otherPacket] or returns null if the packets cannot be merged. + * + * If there are conflicts, the values from this packet should take precedence. + */ + fun mergeWith(otherPacket: OutgoingPacket): OutgoingPacket? +} + abstract class OutgoingPacket : Packet() diff --git a/uranos-packets/src/main/kotlin/space/uranos/net/packet/play/PlayerInfoPacket.kt b/uranos-packets/src/main/kotlin/space/uranos/net/packet/play/PlayerInfoPacket.kt index 8623619..0da001e 100644 --- a/uranos-packets/src/main/kotlin/space/uranos/net/packet/play/PlayerInfoPacket.kt +++ b/uranos-packets/src/main/kotlin/space/uranos/net/packet/play/PlayerInfoPacket.kt @@ -6,6 +6,7 @@ package space.uranos.net.packet.play import space.uranos.chat.TextComponent +import space.uranos.net.packet.Mergeable import space.uranos.net.packet.OutgoingPacket import space.uranos.player.GameMode import java.util.* @@ -13,7 +14,7 @@ import java.util.* /** * Informs the client about other players. */ -data class PlayerInfoPacket(val action: Action<*>) : OutgoingPacket() { +data class PlayerInfoPacket(val action: Action<*>) : OutgoingPacket(), Mergeable { sealed class Action { abstract val entries: Map @@ -63,11 +64,29 @@ data class PlayerInfoPacket(val action: Action<*>) : OutgoingPacket() { */ data class UpdateDisplayName(override val entries: Map) : Action() - data class RemovePlayer(override val entries: Map): Action() { - constructor(entries: List): this(entries.map { it to Unit }.toMap()) + data class RemovePlayer(override val entries: Map) : Action() { + constructor(entries: List) : this(entries.map { it to Unit }.toMap()) } } + override fun mergeWith(otherPacket: OutgoingPacket): OutgoingPacket? { + if (otherPacket !is PlayerInfoPacket) return null + + val action = when (otherPacket.action) { + is Action.AddPlayer -> (action as? Action.AddPlayer)?.let { Action.AddPlayer(otherPacket.action.entries + it.entries) } + is Action.UpdateGameMode -> (action as? Action.UpdateGameMode)?.let { Action.UpdateGameMode(otherPacket.action.entries + it.entries) } + is Action.UpdateLatency -> (action as? Action.UpdateLatency)?.let { Action.UpdateLatency(otherPacket.action.entries + it.entries) } + is Action.UpdateDisplayName -> (action as? Action.UpdateDisplayName)?.let { + Action.UpdateDisplayName( + otherPacket.action.entries + it.entries + ) + } + is Action.RemovePlayer -> (action as? Action.RemovePlayer)?.let { Action.RemovePlayer(otherPacket.action.entries + it.entries) } + } + + return action?.let { PlayerInfoPacket(it) } + } + /** * Latency constants for use in the [AddPlayer][Action.AddPlayer] and [UpdateLatency][Action.UpdateLatency] actions. */ diff --git a/uranos-server/src/main/kotlin/space/uranos/UranosScheduler.kt b/uranos-server/src/main/kotlin/space/uranos/UranosScheduler.kt index e121edc..5c9f316 100644 --- a/uranos-server/src/main/kotlin/space/uranos/UranosScheduler.kt +++ b/uranos-server/src/main/kotlin/space/uranos/UranosScheduler.kt @@ -57,9 +57,8 @@ class UranosScheduler : Scheduler { try { task.fn() - if (task.interval != null) { - task.ticksUntilExecution = task.interval - } else tasks.remove(task) + if (task.interval != null) task.ticksUntilExecution = task.interval + else tasks.remove(task) } catch (e: Exception) { tasks.remove(task) e.printStackTrace() diff --git a/uranos-server/src/main/kotlin/space/uranos/UranosServer.kt b/uranos-server/src/main/kotlin/space/uranos/UranosServer.kt index 88e1b62..2c37f88 100644 --- a/uranos-server/src/main/kotlin/space/uranos/UranosServer.kt +++ b/uranos-server/src/main/kotlin/space/uranos/UranosServer.kt @@ -90,10 +90,10 @@ class UranosServer internal constructor() : Server() { ) .build().loadConfigOrThrow() - override val minimumLogLevel = config.minLogLevel + override val minimumLogLevel = config.logging.minLevel override val developmentMode: Boolean = config.developmentMode - override val eventBus = UranosEventBus(developmentMode) + override val eventBus = UranosEventBus(config.logging.events) override val eventHandlerPositions = UranosEventHandlerPositionManager() override fun shutdown() { @@ -118,14 +118,15 @@ class UranosServer internal constructor() : Server() { logger info "Listening on ${config.host}:${config.port}" scheduler.start() - startDataStorageTicking() + startTicking() startPingSync() } - private fun startDataStorageTicking() { + private fun startTicking() { scheduler.executeRepeating(1, 0) { players.forEach { it.dataStorage.tick() } entities.forEach { it.dataStorage.tick() } + sessions.forEach { it.tick() } } } diff --git a/uranos-server/src/main/kotlin/space/uranos/config/UranosConfig.kt b/uranos-server/src/main/kotlin/space/uranos/config/UranosConfig.kt index df21611..d5dc40d 100644 --- a/uranos-server/src/main/kotlin/space/uranos/config/UranosConfig.kt +++ b/uranos-server/src/main/kotlin/space/uranos/config/UranosConfig.kt @@ -6,6 +6,7 @@ package space.uranos.config import space.uranos.logging.Logger +import space.uranos.net.packet.Packet import java.time.Duration data class UranosConfig( @@ -14,9 +15,21 @@ data class UranosConfig( val ignoreClientCausedErrors: Boolean, val authenticateAndEncrypt: Boolean, val developmentMode: Boolean, - val minLogLevel: Logger.Level, val packetCompressionThreshold: Int, val timeout: Duration, val packetsBufferSize: Int, - val pingUpdateInterval: Duration -) + val pingUpdateInterval: Duration, + val logging: LoggingConfig +) { + data class LoggingConfig( + val minLevel: Logger.Level, + val events: Boolean, + val packets: List, + val isBlacklist: Boolean + ) { + fun shouldLog(packet: Packet): Boolean { + val contained = packets.contains(packet::class.simpleName?.removeSuffix("Packet")) + return if (isBlacklist) !contained else contained + } + } +} diff --git a/uranos-server/src/main/kotlin/space/uranos/event/UranosEventBus.kt b/uranos-server/src/main/kotlin/space/uranos/event/UranosEventBus.kt index 23add3d..6696628 100644 --- a/uranos-server/src/main/kotlin/space/uranos/event/UranosEventBus.kt +++ b/uranos-server/src/main/kotlin/space/uranos/event/UranosEventBus.kt @@ -9,7 +9,7 @@ import space.uranos.logging.Logger import space.uranos.util.pluralizeWithCount import kotlin.system.measureTimeMillis -class UranosEventBus(private val developmentMode: Boolean) : EventBus() { +class UranosEventBus(private val logEvents: Boolean) : EventBus() { private val logger = Logger("EventBus") /** @@ -20,7 +20,7 @@ class UranosEventBus(private val developmentMode: Boolean) : EventBus() { * @return [event] */ override suspend fun emit(event: T): T { - if (developmentMode) { + if (logEvents) { var count = 0 val time = measureTimeMillis { for (handler in handlers) { diff --git a/uranos-server/src/main/kotlin/space/uranos/net/LoginAndJoinProcedure.kt b/uranos-server/src/main/kotlin/space/uranos/net/LoginAndJoinProcedure.kt index 38a3879..ad3a229 100644 --- a/uranos-server/src/main/kotlin/space/uranos/net/LoginAndJoinProcedure.kt +++ b/uranos-server/src/main/kotlin/space/uranos/net/LoginAndJoinProcedure.kt @@ -81,7 +81,7 @@ class LoginAndJoinProcedure(val session: UranosSession) { private suspend fun afterLogin() { val state: Session.State.LoginSucceeded = session.state.getOrFail() - session.launchPacketsChannelConsumer() + session.packetsAdapter.launchPacketsChannelConsumer() val event = session.server.eventBus.emit(SessionAfterLoginEvent(session)) val initialWorldAndLocation = event.initialWorldAndLocation diff --git a/uranos-server/src/main/kotlin/space/uranos/net/PacketMessageDuplexHandler.kt b/uranos-server/src/main/kotlin/space/uranos/net/PacketMessageDuplexHandler.kt index 05a0887..a2fd492 100644 --- a/uranos-server/src/main/kotlin/space/uranos/net/PacketMessageDuplexHandler.kt +++ b/uranos-server/src/main/kotlin/space/uranos/net/PacketMessageDuplexHandler.kt @@ -36,7 +36,7 @@ class PacketMessageDuplexHandler(private val session: UranosSession) : ChannelDu } override fun channelRead(ctx: ChannelHandlerContext, msg: Any) { - session.onPacketDataReceived(msg as ByteBuf) // ByteBuf must be released by the session + session.packetsAdapter.onPacketReceived(msg as ByteBuf) // ByteBuf must be released by the session } override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) { diff --git a/uranos-server/src/main/kotlin/space/uranos/net/PacketsAdapter.kt b/uranos-server/src/main/kotlin/space/uranos/net/PacketsAdapter.kt new file mode 100644 index 0000000..ece96dd --- /dev/null +++ b/uranos-server/src/main/kotlin/space/uranos/net/PacketsAdapter.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2020-2021 Moritz Ruth and Uranos contributors + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file + */ + +package space.uranos.net + +import io.netty.buffer.ByteBuf +import io.netty.util.ReferenceCountUtil +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import space.uranos.event.ifNotCancelled +import space.uranos.net.MinecraftProtocolDataTypes.readVarInt +import space.uranos.net.event.PacketReceivedEvent +import space.uranos.net.event.PacketSendEvent +import space.uranos.net.packet.IncomingPacket +import space.uranos.net.packet.Mergeable +import space.uranos.net.packet.OutgoingPacket +import space.uranos.util.awaitSuspending + +class PacketsAdapter(val session: UranosSession) { + private val packetsForNextTick = ArrayList() + + suspend fun tick() { + packetsForNextTick.forEach { send(it) } + packetsForNextTick.clear() + } + + fun stopProcessingIncomingPackets() { + packetDataChannel.close() + packetsChannel?.close() + } + + fun onPacketReceived(data: ByteBuf) { + packetDataChannel.offer(data) + } + + private suspend fun handlePacket(packet: IncomingPacket) { + if (session.server.config.logging.shouldLog(packet)) session.logger.trace { "Packet received: $packet" } + + session.server.eventBus.emit(PacketReceivedEvent(session, packet)).ifNotCancelled { + SessionPacketReceivedEventHandler.handle(session, packet) + } + } + + suspend fun sendNextTick(packet: OutgoingPacket) = withContext(session.coroutineContext) { + if (packet is Mergeable) { + for (i in packetsForNextTick.indices.reversed()) { + val merged = packet.mergeWith(packetsForNextTick[i]) + + if (merged != null) { + packetsForNextTick[i] = merged + return@withContext + } + } + } + + packetsForNextTick.add(packet) + } + + suspend fun send(packet: OutgoingPacket): Unit = withContext(session.coroutineContext) { + if (session.server.config.logging.shouldLog(packet)) session.logger.trace { "Sending packet: $packet" } + + session.server.eventBus.emit(PacketSendEvent(session, packet)).ifNotCancelled { + try { + session.channel.writeAndFlush(it.packet).awaitSuspending() + } catch (t: Throwable) { + if (session.channel.isActive) session.logger.error("Sending packet failed", t) + } + } + } + + private var packetsChannel: Channel? = null + private val packetDataChannel: Channel = Channel(Channel.UNLIMITED) + + fun launchPacketsChannelConsumer() { + val channel = Channel(session.server.config.packetsBufferSize) + packetsChannel = channel + + session.scope.launch { + for (packet in channel) { + handlePacket(packet) + } + } + } + + fun launchPacketDataChannelConsumer() { + session.scope.launch { + for (data in packetDataChannel) { + try { + handlePacketData(data) + } catch (e: Exception) { + session.failAndDisconnectBecauseOfClient(e) + } + } + } + } + + private suspend fun handlePacketData(data: ByteBuf) { + val packetID = data.readVarInt() + val codec = session.currentProtocol!!.incomingPacketCodecsByID[packetID] + + if (codec == null) { + val message = "Received an unknown packet (ID: 0x${packetID.toString(16).padStart(2, '0')})" + + if (session.server.config.developmentMode) + session.logger warn "$message. This will cause the client to disconnect in production mode." + else session.failAndDisconnectBecauseOfClient(message) + + return + } + + val packet = codec.decode(data) + ReferenceCountUtil.release(data) + + if (packetsChannel != null) { + // The protocol will not change anymore, so we can safely decode all pending packets with the current one + if (!packetsChannel!!.offer(packet)) { + session.logger.warn("The packet buffer is full (size: ${session.server.config.packetsBufferSize})") + session.disconnect(internalReason = "You sent more packets than the packet buffer can handle") + } + } else { + // The protocol could change because of this packet, so we must wait + handlePacket(packet) + } + } +} diff --git a/uranos-server/src/main/kotlin/space/uranos/net/UranosSession.kt b/uranos-server/src/main/kotlin/space/uranos/net/UranosSession.kt index 532fa01..bf6baec 100644 --- a/uranos-server/src/main/kotlin/space/uranos/net/UranosSession.kt +++ b/uranos-server/src/main/kotlin/space/uranos/net/UranosSession.kt @@ -6,19 +6,13 @@ package space.uranos.net import io.netty.buffer.ByteBuf -import io.netty.util.ReferenceCountUtil import kotlinx.coroutines.* -import kotlinx.coroutines.channels.Channel import space.uranos.UranosServer import space.uranos.chat.ChatColor import space.uranos.chat.ChatComponent import space.uranos.chat.TextComponent import space.uranos.event.* import space.uranos.logging.Logger -import space.uranos.net.MinecraftProtocolDataTypes.readVarInt -import space.uranos.net.event.PacketReceivedEvent -import space.uranos.net.event.PacketSendEvent -import space.uranos.net.packet.IncomingPacket import space.uranos.net.packet.OutgoingPacket import space.uranos.net.packet.Protocol import space.uranos.net.packet.handshaking.HandshakingProtocol @@ -29,7 +23,6 @@ import space.uranos.net.packet.play.PlayProtocol import space.uranos.net.packet.play.PlayerInfoPacket import space.uranos.net.packet.status.StatusProtocol import space.uranos.server.event.SessionInitializedEvent -import space.uranos.util.awaitSuspending import space.uranos.util.supervisorChild import java.net.InetAddress import java.net.InetSocketAddress @@ -37,14 +30,14 @@ import javax.crypto.SecretKey import kotlin.coroutines.CoroutineContext import kotlin.properties.Delegates -class UranosSession(private val channel: io.netty.channel.Channel, val server: UranosServer) : Session() { +class UranosSession(val channel: io.netty.channel.Channel, val server: UranosServer) : Session() { override val address: InetAddress = (channel.remoteAddress() as InetSocketAddress).address private val identifier = "UranosSession(${address.hostAddress})" val logger = Logger(identifier) override val coroutineContext: CoroutineContext = server.coroutineContext.supervisorChild(identifier) - private val scope = CoroutineScope(coroutineContext) + val scope = CoroutineScope(coroutineContext) override var brand: String? = null @@ -78,8 +71,25 @@ class UranosSession(private val channel: io.netty.channel.Channel, val server: U is State.Disconnected -> null } + val packetsAdapter = PacketsAdapter(this) + + override suspend fun send(packet: OutgoingPacket) = packetsAdapter.send(packet) + override fun sendNextTick(packet: OutgoingPacket) { + scope.launch { packetsAdapter.sendNextTick(packet) } + } + + suspend fun tick() { + packetsAdapter.tick() + } + + override suspend fun sendPluginMessage(channel: String, data: ByteBuf) { + if (this.currentProtocol != PlayProtocol) throw IllegalStateException("The session is not using the PLAY protocol") + send(OutgoingPluginMessagePacket(channel, data)) + data.release() + } + override suspend fun disconnect(reason: TextComponent, internalReason: String?) { - stopProcessingIncomingPackets() + packetsAdapter.stopProcessingIncomingPackets() if (currentProtocol == LoginProtocol || currentProtocol == PlayProtocol) { logger info "Disconnected" + (internalReason?.let { ". Internal reason: $it" } ?: "") @@ -106,74 +116,6 @@ class UranosSession(private val channel: io.netty.channel.Channel, val server: U channel.close() } - private var packetsChannel: Channel? = null - private val packetDataChannel: Channel = Channel(Channel.UNLIMITED) - - fun launchPacketsChannelConsumer() { - val channel = Channel(server.config.packetsBufferSize) - packetsChannel = channel - - scope.launch { - for (packet in channel) { - handlePacket(packet) - } - } - } - - private fun launchPacketDataChannelConsumer() { - scope.launch { - for (data in packetDataChannel) { - try { - handlePacketData(data) - } catch (e: Exception) { - failAndDisconnectBecauseOfClient(e) - } - } - } - } - - private fun stopProcessingIncomingPackets() { - packetDataChannel.close() - packetsChannel?.close() - } - - private suspend fun handlePacket(packet: IncomingPacket) { - logger.trace { "Packet received: $packet" } - - server.eventBus.emit(PacketReceivedEvent(this@UranosSession, packet)).ifNotCancelled { - SessionPacketReceivedEventHandler.handle(this@UranosSession, packet) - } - } - - private suspend fun handlePacketData(data: ByteBuf) { - val packetID = data.readVarInt() - val codec = currentProtocol!!.incomingPacketCodecsByID[packetID] - - if (codec == null) { - val message = "Received an unknown packet (ID: 0x${packetID.toString(16).padStart(2, '0')})" - - if (server.config.developmentMode) - logger warn "$message. This will cause the client to disconnect in production mode." - else failAndDisconnectBecauseOfClient(message) - - return - } - - val packet = codec.decode(data) - ReferenceCountUtil.release(data) - - if (packetsChannel != null) { - // The protocol will not change anymore, so we can safely decode all pending packets with the current one - if (!packetsChannel!!.offer(packet)) { - logger.warn("The packet buffer is full (size: ${server.config.packetsBufferSize})") - disconnect(internalReason = "You sent more packets than the packet buffer can handle") - } - } else { - // The protocol could change because of this packet, so we must wait - handlePacket(packet) - } - } - val joinProcedure = LoginAndJoinProcedure(this) var lastKeepAlivePacketTimestamp by Delegates.notNull() @@ -202,7 +144,7 @@ class UranosSession(private val channel: io.netty.channel.Channel, val server: U fun onConnect() = scope.launch { logger trace "Connected" - launchPacketDataChannelConsumer() + packetsAdapter.launchPacketDataChannelConsumer() state = State.WaitingForHandshake if (server.eventBus.emit(SessionInitializedEvent(this@UranosSession)).cancelled) channel.close() @@ -215,18 +157,14 @@ class UranosSession(private val channel: io.netty.channel.Channel, val server: U if (!expected && currentProtocol != HandshakingProtocol && currentProtocol != StatusProtocol) logger trace "The client disconnected unexpectedly" - stopProcessingIncomingPackets() + packetsAdapter.stopProcessingIncomingPackets() coroutineContext.cancel(DisconnectedCancellationException()) state = State.Disconnected server.sessions.remove(this@UranosSession) } } - fun onPacketDataReceived(packetData: ByteBuf) { - packetDataChannel.offer(packetData) - } - - internal fun failAndDisconnectBecauseOfClient(throwable: Throwable) = + fun failAndDisconnectBecauseOfClient(throwable: Throwable) = failAndDisconnectBecauseOfClient("${throwable::class.java.name}: ${throwable.message}", throwable) internal fun failAndDisconnectBecauseOfClient(message: String, throwable: Throwable? = null) { @@ -262,24 +200,6 @@ class UranosSession(private val channel: io.netty.channel.Channel, val server: U .addAfter("framing", "compression", CompressionCodec(server.config.packetCompressionThreshold)) } - override suspend fun send(packet: OutgoingPacket) = withContext(coroutineContext) { - logger.trace { "Sending packet: $packet" } - server.eventBus.emit(PacketSendEvent(this@UranosSession, packet)).ifNotCancelled { - try { - channel.writeAndFlush(it.packet).awaitSuspending() - } catch (t: Throwable) { - if (!channel.isActive) return@withContext - logger.error("Sending packet failed", t) - } - } - } - - override suspend fun sendPluginMessage(channel: String, data: ByteBuf) { - if (this.currentProtocol != PlayProtocol) throw IllegalStateException("The session is not using the PLAY protocol") - send(OutgoingPluginMessagePacket(channel, data)) - data.release() - } - companion object { const val KEEP_ALIVE_PACKET_INTERVAL = 1000 } diff --git a/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt b/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt index 70b8a0b..56d8ae8 100644 --- a/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt +++ b/uranos-server/src/main/kotlin/space/uranos/player/UranosPlayer.kt @@ -49,8 +49,7 @@ class UranosPlayer( val playerListName = createDataStorageKey("playerListName") { player: UranosPlayer, value: TextComponent? -> player.session.server.players.forEach { - // TODO: Make packets like PlayerInfoPacket mergeable - it.session.send(PlayerInfoPacket(PlayerInfoPacket.Action.UpdateDisplayName(mapOf(player.uuid to value)))) + it.session.sendNextTick(PlayerInfoPacket(PlayerInfoPacket.Action.UpdateDisplayName(mapOf(player.uuid to value)))) } null diff --git a/uranos-server/src/main/resources/default-config.yml b/uranos-server/src/main/resources/default-config.yml index ef8a11d..2e4d073 100644 --- a/uranos-server/src/main/resources/default-config.yml +++ b/uranos-server/src/main/resources/default-config.yml @@ -8,3 +8,8 @@ packetCompressionThreshold: 256 timeout: 30s packetsBufferSize: 20 pingUpdateInterval: 10s +logging: + minLevel: INFO + events: false + packets: [ ] + isBlacklist: false