Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit 2e4004d

Browse files
committed
feat: define and integrate STREAM_CODEC for enhanced packet serialization
- Added `STREAM_CODEC` to multiple packet classes (e.g., Clientbound, Serverbound) for consistent and efficient serialization. - Replaced legacy codec implementations with `StreamCodec.composite` for standardization. - Introduced `SealedCodecBuilder` for sealed class codec construction, improving flexibility and reducing boilerplate. - Updated packet listener methods to handle newly implemented codecs (e.g., `handlePong`, `handleAttachedNote`). - Refactored `ConnectionImpl` to skip processing responses handled by `RespondingPacketSendHandler`. - Removed deprecated `@Serializable` annotation where unnecessary.
1 parent 33e8944 commit 2e4004d

27 files changed

+492
-157
lines changed

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/netty/network/codec/ByteBufCodecs.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,15 @@ object ByteBufCodecs {
516516
return CodecOperation { size -> collection(::ObjectArrayList, size) }
517517
}
518518

519+
fun <B : ByteBuf, V> makeImmutableList(): CodecOperation<B, MutableList<V>, List<V>> {
520+
return CodecOperation { size ->
521+
size.map(
522+
{ Collections.unmodifiableList(it) },
523+
::ObjectArrayList
524+
)
525+
}
526+
}
527+
519528
fun <B : ByteBuf, V> list(maxSize: Int): CodecOperation<B, V, MutableList<V>> {
520529
return CodecOperation { size -> collection(::ObjectArrayList, size, maxSize) }
521530
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package dev.slne.surf.cloud.api.common.netty.network.codec
2+
3+
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.checkDecodedNotNull
4+
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.checkEncodedNotNull
5+
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
6+
7+
class SealedCodecBuilder<B, T : Any>(private val idOf: (T) -> Int) {
8+
private val entries = Int2ObjectOpenHashMap<StreamCodec<B, out T>>()
9+
10+
fun <S : T> variant(id: Int, codec: StreamCodec<B, S>) {
11+
entries[id] = codec
12+
}
13+
14+
fun build(idCodec: StreamCodec<B, Int>): StreamCodec<B, T> {
15+
entries.trim()
16+
17+
return object : StreamCodec<B, T> {
18+
override fun encode(buf: B, value: T) {
19+
val id = idOf(value)
20+
idCodec.encode(buf, id)
21+
val codec = entries[id]
22+
checkEncodedNotNull(codec) { "Unknown id: $id" }
23+
@Suppress("UNCHECKED_CAST")
24+
(codec as StreamCodec<B, T>).encode(buf, value)
25+
}
26+
27+
override fun decode(buf: B): T {
28+
val id = idCodec.decode(buf)
29+
val codec = entries[id]
30+
checkDecodedNotNull(codec) { "Unknown id: $id" }
31+
return codec.decode(buf)
32+
}
33+
}
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package dev.slne.surf.cloud.api.common.netty.network.codec
2+
3+
import dev.slne.surf.cloud.api.common.util.annotation.InternalApi
4+
import io.netty.buffer.ByteBuf
5+
import kotlin.reflect.full.companionObjectInstance
6+
7+
typealias SealedVariantCodecProviderBufComplex<T> = SealedVariantCodecProvider.Complex<ByteBuf, T>
8+
typealias SealedVariantCodecProviderBufSimple<T> = SealedVariantCodecProvider.Simple<ByteBuf, T>
9+
10+
sealed interface SealedVariantCodecProvider<B, T : Any> {
11+
val id: Int
12+
13+
interface Complex<B, T : Any> : SealedVariantCodecProvider<B, T> {
14+
fun localCodec(parent: StreamCodec<B, T>): StreamCodec<B, out T>
15+
}
16+
17+
interface Simple<B, T : Any> : SealedVariantCodecProvider<B, T> {
18+
@Suppress("PropertyName")
19+
val STREAM_CODEC: StreamCodec<B, out T>
20+
}
21+
22+
companion object {
23+
@InternalApi
24+
data class Provider<B, T : Any>(
25+
val id: Int,
26+
val codecSupplier: (parent: StreamCodec<B, T>) -> StreamCodec<B, out T>
27+
)
28+
29+
@InternalApi
30+
fun <B, T : Any> findProvider(klass: Class<out T>): Provider<B, T>? {
31+
val kClass = klass.kotlin
32+
fun providerFromInstance(instance: Any): Provider<B, T>? {
33+
if (instance is Simple<*, *>) {
34+
@Suppress("UNCHECKED_CAST")
35+
val simpleInstance = instance as Simple<B, T>
36+
return Provider(simpleInstance.id) { simpleInstance.STREAM_CODEC }
37+
} else if (instance is Complex<*, *>) {
38+
@Suppress("UNCHECKED_CAST")
39+
val complexInstance = instance as Complex<B, T>
40+
return Provider(complexInstance.id, complexInstance::localCodec)
41+
}
42+
43+
return null
44+
}
45+
46+
47+
kClass.objectInstance?.let { providerFromInstance(it) }?.let { return it }
48+
kClass.companionObjectInstance?.let { providerFromInstance(it) }?.let { return it }
49+
50+
return null
51+
}
52+
}
53+
}

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/netty/network/codec/StreamCodec.kt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package dev.slne.surf.cloud.api.common.netty.network.codec
22

3+
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.checkEncoded
4+
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.checkEncodedNotNull
35
import io.netty.buffer.ByteBuf
6+
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
7+
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
8+
import java.lang.reflect.Modifier
49
import java.util.function.BiFunction
510
import java.util.function.Function
611
import java.util.function.UnaryOperator
12+
import kotlin.reflect.KClass
713

814
interface StreamCodec<B, V> : StreamDecoder<B, V>, StreamEncoder<B, V> {
915
fun <O> apply(function: CodecOperation<B, V, O>) = function.apply(this)
@@ -65,6 +71,72 @@ interface StreamCodec<B, V> : StreamDecoder<B, V>, StreamEncoder<B, V> {
6571
}
6672
}
6773

74+
inline fun <B, T : Any> forSealedClass(
75+
idCodec: StreamCodec<B, Int>,
76+
noinline idOf: (T) -> Int,
77+
crossinline build: SealedCodecBuilder<B, T>.(self: StreamCodec<B, T>) -> Unit
78+
): StreamCodec<B, T> = recursive { self ->
79+
val b = SealedCodecBuilder<B, T>(idOf)
80+
b.build(self)
81+
b.build(idCodec)
82+
}
83+
84+
fun <B, T : Any> forSealedClassAuto(
85+
idCodec: StreamCodec<B, Int>,
86+
root: KClass<T>
87+
): StreamCodec<B, T> = recursive<B, T> { parent ->
88+
val idToCodec = Int2ObjectOpenHashMap<StreamCodec<B, out T>>()
89+
val clazzToId = Object2IntOpenHashMap<Class<out T>>().apply {
90+
defaultReturnValue(-1)
91+
}
92+
93+
fun <T : Any> KClass<out T>.finalLeaves(): List<Class<out T>> {
94+
fun walk(targetClass: Class<out T>, classes: MutableList<Class<out T>>) {
95+
val perms = targetClass.permittedSubclasses
96+
if (perms.isNullOrEmpty()) {
97+
classes += targetClass; return
98+
}
99+
perms.forEach { sub ->
100+
@Suppress("UNCHECKED_CAST")
101+
val subC = sub as Class<out T>
102+
if (Modifier.isFinal(subC.modifiers)) classes += subC
103+
else walk(subC, classes)
104+
}
105+
}
106+
107+
val root = this.java
108+
return buildList { walk(root, this) }
109+
}
110+
111+
for (leaf in root.finalLeaves()) {
112+
val provider = SealedVariantCodecProvider.findProvider<B, T>(leaf)
113+
?: error("No SealedVariantCodecProvider on $leaf (object or companion)")
114+
val codec = provider.codecSupplier(parent)
115+
require(!idToCodec.containsKey(provider.id)) { "Duplicate id ${provider.id}" }
116+
idToCodec[provider.id] = codec
117+
clazzToId[leaf] = provider.id
118+
}
119+
120+
ofMember(
121+
{ value, buf ->
122+
val id = clazzToId.getInt(value.javaClass)
123+
checkEncoded(id != -1) { "Unknown class ${value::class}" }
124+
val codec = idToCodec.get(id)
125+
checkEncodedNotNull(codec) { "Unknown id $id" }
126+
127+
idCodec.encode(buf, id)
128+
@Suppress("UNCHECKED_CAST")
129+
(codec as StreamCodec<B, T>).encode(buf, value)
130+
},
131+
{ buf ->
132+
val id = idCodec.decode(buf)
133+
val codec = idToCodec.get(id)
134+
checkEncodedNotNull(codec) { "Unknown id $id" }
135+
codec.decode(buf)
136+
}
137+
)
138+
}
139+
68140
@JvmStatic
69141
fun <B, C, T1> composite(
70142
codec: StreamCodec<in B, T1>,

0 commit comments

Comments
 (0)