Archived
1
0
Fork 0

Add easy option to cache encoded packets

This commit is contained in:
Moritz Ruth 2020-12-13 13:41:24 +01:00
parent 75888e9bc0
commit 5aeea48516
4 changed files with 67 additions and 35 deletions

View file

@ -1,6 +1,11 @@
package space.blokk.net.packet
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import java.time.Duration
import kotlin.reflect.KClass
abstract class PacketCodec<T : Packet>(val id: Int, val dataType: KClass<T>)
@ -9,6 +14,37 @@ abstract class IncomingPacketCodec<T : IncomingPacket>(id: Int, dataType: KClass
abstract fun decode(msg: ByteBuf): T
}
abstract class OutgoingPacketCodec<T : OutgoingPacket>(id: Int, dataType: KClass<T>) : PacketCodec<T>(id, dataType) {
abstract fun T.encode(dst: ByteBuf)
abstract class OutgoingPacketCodec<T : OutgoingPacket>(
id: Int,
dataType: KClass<T>,
cacheOptions: CacheOptions? = null
) : PacketCodec<T>(id, dataType) {
protected abstract fun T.encode(dst: ByteBuf)
private val cache: LoadingCache<T, ByteBuf>? = cacheOptions?.let { options ->
CacheBuilder.newBuilder()
.weakKeys()
.maximumSize(options.limit)
.expireAfterAccess(options.expirationDuration)
.concurrencyLevel(2)
.removalListener<T, ByteBuf> { it.value.release() }
.build(object : CacheLoader<T, ByteBuf>() {
override fun load(packet: T): ByteBuf {
val dst = Unpooled.buffer()
packet.encode(dst)
return dst
}
})
}
@JvmName("encodePacket")
fun encode(packet: T, dst: ByteBuf) {
if (cache == null) packet.encode(dst)
else dst.writeBytes(cache.get(packet))
}
data class CacheOptions(
val limit: Long,
val expirationDuration: Duration
)
}

View file

@ -31,6 +31,12 @@ dependencies {
}
tasks {
compileKotlin {
kotlinOptions.freeCompilerArgs = listOf(
"-Xopt-in=kotlin.time.ExperimentalTime"
)
}
test {
useJUnitPlatform()
}

View file

@ -1,44 +1,34 @@
package space.blokk.net.packet.play
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import space.blokk.net.MinecraftProtocolDataTypes.writeString
import space.blokk.net.MinecraftProtocolDataTypes.writeVarInt
import space.blokk.net.packet.OutgoingPacketCodec
import space.blokk.tags.Tag
import kotlin.time.Duration
import kotlin.time.toJavaDuration
object TagsPacketCodec : OutgoingPacketCodec<TagsPacket>(0x5C, TagsPacket::class) {
private val cache: LoadingCache<TagsPacket, ByteBuf> = CacheBuilder.newBuilder()
.maximumSize(3)
.weakKeys()
.removalListener<TagsPacket, ByteBuf> { it.value.release() }
.build(object : CacheLoader<TagsPacket, ByteBuf>() {
override fun load(packet: TagsPacket): ByteBuf {
val dst = Unpooled.buffer()
listOf(
packet.tags.filter { it.type == Tag.Type.BLOCKS },
packet.tags.filter { it.type == Tag.Type.ITEMS },
packet.tags.filter { it.type == Tag.Type.FLUIDS },
packet.tags.filter { it.type == Tag.Type.ENTITY_TYPES }
).forEach { tags ->
dst.writeVarInt(tags.size)
tags.forEach { tag ->
dst.writeString(tag.name)
dst.writeVarInt(tag.values.size)
tag.numericIDs.forEach { dst.writeVarInt(it) }
}
}
return dst
}
})
object TagsPacketCodec :
OutgoingPacketCodec<TagsPacket>(0x5C, TagsPacket::class, CacheOptions(3, Duration.INFINITE.toJavaDuration())) {
private val ORDER = listOf(
Tag.Type.BLOCKS,
Tag.Type.ITEMS,
Tag.Type.FLUIDS,
Tag.Type.ENTITY_TYPES
)
override fun TagsPacket.encode(dst: ByteBuf) {
dst.writeBytes(cache.get(this))
val tagsByType = tags.groupBy { it.type }
ORDER.forEach { type ->
val tags = tagsByType[type] ?: emptyList()
dst.writeVarInt(tags.size)
tags.forEach { tag ->
dst.writeString(tag.name)
dst.writeVarInt(tag.values.size)
tag.numericIDs.forEach { dst.writeVarInt(it) }
}
}
}
}

View file

@ -11,7 +11,7 @@ abstract class PacketMessage<T : Packet>(val session: BlokkSession, val packet:
class OutgoingPacketMessage<T : OutgoingPacket>(session: BlokkSession, packet: T) : PacketMessage<T>(session, packet) {
@Suppress("UNCHECKED_CAST")
val packetCodec = session.currentProtocol!!.getCodecByType(packet::class) as OutgoingPacketCodec<T>
fun encodePacket(dst: ByteBuf) = with(packetCodec) { packet.encode(dst) }
fun encodePacket(dst: ByteBuf) = packetCodec.encode(packet, dst)
}
class IncomingPacketMessage<T : IncomingPacket>(session: BlokkSession, packet: T) : PacketMessage<T>(session, packet)