Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.
Merged
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ dependencies {
// val kotlinVersion = "2.1.0"
// val dokkaVersion = "2.0.0-Beta"

implementation("org.springframework.boot:org.springframework.boot.gradle.plugin:3.5.3")
implementation("org.springframework.boot:org.springframework.boot.gradle.plugin:3.5.6")
// implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
// implementation("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
// implementation("org.jetbrains.kotlin:kotlin-lombok:$kotlinVersion")
Expand Down
98 changes: 98 additions & 0 deletions buildSrc/src/main/kotlin/generate-stream-codec.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import java.nio.file.Files
import java.nio.file.StandardOpenOption

val streamCodecPackage = "dev.slne.surf.cloud.api.common.netty.network.codec"
val compositeStartFrom = 2
val compositeMaxArity = 20

val generatedDir = layout.buildDirectory.dir("generated/sources/streamcodec")

plugins {
java
}

val genComposites = tasks.register("generateStreamCodecComposites") {
group = "generate"

outputs.dir(generatedDir)
inputs.property("startFrom", compositeStartFrom)
inputs.property("maxArity", compositeMaxArity)

doLast {
require(compositeMaxArity >= compositeStartFrom) { "maxArity ($compositeMaxArity) must be >= startFrom ($compositeStartFrom)" }
val file = generatedDir.get().file("StreamCodecComposites.kt").asFile
file.parentFile.mkdirs()
val content = buildString {
appendLine("// GENERATED FILE — do not edit manually")
appendLine("package $streamCodecPackage")
appendLine()
appendLine("import java.util.function.Function")
appendLine()
appendLine("@Suppress(\"UNCHECKED_CAST\", \"RedundantVisibilityModifier\")")
appendLine("private object _CompositeGenSupport {")
appendLine(" @JvmStatic inline fun <B, T> dec(c: StreamCodec<in B, T>, buf: B): T = (c as StreamCodec<B, T>).decode(buf)")
appendLine(" @JvmStatic inline fun <B, T> enc(c: StreamCodec<in B, T>, buf: B, v: T) = (c as StreamCodec<B, T>).encode(buf, v)")
appendLine("}")
appendLine()


fun genArity(n: Int) {
val typeParams = (1..n).joinToString(", ") { "T$it" }
val params = buildString {
for (i in 1..n) {
appendLine(" codec$i: StreamCodec<in B, T$i>,")
appendLine(" from$i: Function<C, T$i>,")
}
append(" to: (")
append((1..n).joinToString(", ") { "T$it" })
appendLine(") -> C")
}
val decodeVars = (1..n).joinToString("\n") { i ->
" val o$i: T$i = _CompositeGenSupport.dec(codec$i, buf)"
}
val encodeLines = (1..n).joinToString("\n") { i ->
" _CompositeGenSupport.enc(codec$i, buf, from$i.apply(value))"
}
val toArgs = (1..n).joinToString(", ") { "o$it" }

appendLine("public fun <B, C, $typeParams> StreamCodec.Companion.composite(")
appendLine(params)
appendLine("): StreamCodec<B, C> {")
appendLine(" return object : StreamCodec<B, C> {")
appendLine(" override fun decode(buf: B): C {")
appendLine(decodeVars)
appendLine(" return to($toArgs)")
appendLine(" }")
appendLine()
appendLine(" override fun encode(buf: B, value: C) {")
appendLine(encodeLines)
appendLine(" }")
appendLine(" }")
appendLine("}")
appendLine()
}

for (n in compositeStartFrom..compositeMaxArity) genArity(n)

}
Files.write(
file.toPath(),
content.toByteArray(),
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE
)

println("Generated ${file.relativeTo(project.projectDir)} with composite ${compositeStartFrom}..$compositeMaxArity")
}
}

sourceSets {
named("main") {
java.srcDir(generatedDir)
}
}

tasks.named("compileKotlin") {
mustRunAfter(genComposites)
}
10 changes: 5 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
[versions]
velocity-version = "3.4.0-SNAPSHOT"
aide-reflection = "1.3"
netty = "4.2.3.Final"
netty-tcnative = "2.0.72.Final"
netty = "4.2.6.Final"
netty-tcnative = "2.0.74.Final"
datafixerupper = "8.0.16"
#byte-buddy = "1.15.10"
exposed = "0.61.0"
maven-impl = "4.0.0-rc-2"
maven-resolver = "2.0.5"
jline = "3.30.4"
jline = "3.30.6"
brigadier = "1.3.10"
terminalconsoleappender = "1.3.0"
bson-kotlinx = "5.4.0"
aspectjweaver = "1.9.22.1"
zstd-jni = "1.5.7-4"
luckperms-api = "5.4"
luckperms-api = "5.5"
reactive-streams = "1.0.4"
ehcache = "3.10.8"
kotlin-byte-buf-serializer = "1.1.0"
voicechat-api = "2.5.27"
voicechat-api = "2.6.0"
discord-webhooks = "1.0"
konf = "1.1.2"

Expand Down
3 changes: 2 additions & 1 deletion surf-cloud-api/surf-cloud-api-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import dev.slne.surf.surfapi.gradle.util.slneReleases
plugins {
`exclude-kotlin`
id("dev.slne.surf.surfapi.gradle.core")
`generate-stream-codec`
}

dependencies {
Expand Down Expand Up @@ -51,4 +52,4 @@ publishing {
repositories {
slneReleases()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package dev.slne.surf.cloud.api.common.netty.network.codec

import dev.slne.surf.cloud.api.common.netty.protocol.buffer.*
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.types.Utf8String
import dev.slne.surf.cloud.api.common.util.ByIdMap
import dev.slne.surf.cloud.api.common.util.createUnresolvedInetSocketAddress
import io.netty.buffer.ByteBuf
import net.kyori.adventure.key.Key
import net.kyori.adventure.nbt.BinaryTagIO
import net.kyori.adventure.sound.Sound
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.math.BigDecimal
import java.math.BigInteger
import java.math.MathContext
import java.net.Inet4Address
import java.net.InetSocketAddress
import java.net.URI
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.*
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds

object ByteBufCodecs {
val BOOLEAN_CODEC = streamCodec(ByteBuf::writeBoolean, ByteBuf::readBoolean)
val BYTE_CODEC = streamCodec({ buf, byte -> buf.writeByte(byte.toInt()) }, ByteBuf::readByte)
val SHORT_CODEC =
streamCodec({ buf, short -> buf.writeShort(short.toInt()) }, ByteBuf::readShort)

val INT_CODEC = streamCodec(ByteBuf::writeInt, ByteBuf::readInt)
val VAR_INT_CODEC = streamCodec(ByteBuf::writeVarInt, ByteBuf::readVarInt)
val FLOAT_CODEC = streamCodec(ByteBuf::writeFloat, ByteBuf::readFloat)
val DOUBLE_CODEC = streamCodec(ByteBuf::writeDouble, ByteBuf::readDouble)
val LONG_CODEC = streamCodec(ByteBuf::writeLong, ByteBuf::readLong)
val VAR_LONG_CODEC = streamCodec(ByteBuf::writeVarLong, ByteBuf::readVarLong)
val STRING_CODEC = Utf8String.STREAM_CODEC


val UUID_CODEC = streamCodec(ByteBuf::writeUuid, ByteBuf::readUuid)
val BYTE_ARRAY_CODEC = streamCodec(ByteBuf::writeByteArray, ByteBuf::readByteArray)

val OPTIONAL_LONG_CODEC = streamCodec<ByteBuf, OptionalLong>({ buf, optionalLong ->
BOOLEAN_CODEC.encode(buf, optionalLong.isPresent)
optionalLong.ifPresent { LONG_CODEC.encode(buf, it) }
}) { buf ->
if (BOOLEAN_CODEC.decode(buf)) {
OptionalLong.of(LONG_CODEC.decode(buf))
} else {
OptionalLong.empty()
}
}

val KEY_CODEC = streamCodecComposite(STRING_CODEC, Key::asString, Key::key)


private val SOUND_SOURCE_BY_ID = ByIdMap.continuous(
{ it.ordinal },
Sound.Source.entries.toTypedArray(),
ByIdMap.OutOfBoundsStrategy.ZERO
)
val SOUND_CODEC = streamCodecComposite(
KEY_CODEC,
Sound::name,
idMapper(SOUND_SOURCE_BY_ID) { it.ordinal },
{ it.source() },
FLOAT_CODEC,
Sound::volume,
FLOAT_CODEC,
Sound::pitch,
OPTIONAL_LONG_CODEC,
Sound::seed
) { type, source, volume, pitch, seed ->
Sound.sound()
.type(type)
.source(source)
.volume(volume)
.pitch(pitch)
.seed(seed)
.build()
}

val COMPONENT_CODEC = streamCodecComposite(
STRING_CODEC,
{ GsonComponentSerializer.gson().serialize(it.compact()) },
{ GsonComponentSerializer.gson().deserialize(it) }
)

val COMPOUND_TAG_CODEC = streamCodecComposite(BYTE_ARRAY_CODEC, { tag ->
ByteArrayOutputStream().use { out ->
BinaryTagIO.writer().write(tag, out, BinaryTagIO.Compression.GZIP)
out.toByteArray()
}
}, { bytes ->
ByteArrayInputStream(bytes).use { input ->
BinaryTagIO.unlimitedReader().read(input, BinaryTagIO.Compression.GZIP)
}
})

val URI_CODEC = streamCodecComposite(STRING_CODEC, URI::toString, URI::create)
val INET_SOCKET_ADDRESS_CODEC = streamCodecComposite(
STRING_CODEC,
InetSocketAddress::getHostString,
INT_CODEC,
InetSocketAddress::getPort,
::createUnresolvedInetSocketAddress
)
val INET_4_ADDRESS_CODEC = streamCodecComposite(
BYTE_ARRAY_CODEC,
Inet4Address::getAddress
) { Inet4Address.getByAddress(it) as Inet4Address }

val ZONED_DATE_TIME_CODEC = streamCodecComposite(
LONG_CODEC,
{ it.toInstant().toEpochMilli() },
STRING_CODEC,
{ it.zone.id },
{ epoch, zoneId -> ZonedDateTime.ofInstant(Instant.ofEpochMilli(epoch), ZoneId.of(zoneId)) }
)

val DURATION_CODEC = streamCodecComposite(
LONG_CODEC,
Duration::inWholeMilliseconds
) { it.milliseconds }

val BIG_INTEGER_CODEC =
streamCodecComposite(BYTE_ARRAY_CODEC, BigInteger::toByteArray, ::BigInteger)
val BIG_DECIMAL_CODEC = streamCodecComposite(
BIG_INTEGER_CODEC,
BigDecimal::unscaledValue,
VAR_INT_CODEC,
BigDecimal::scale,
VAR_INT_CODEC,
BigDecimal::precision
) { unscaledValue, scale, precision ->
BigDecimal(unscaledValue, scale, MathContext(precision))
}


fun <E : Enum<E>> enumStreamCodec(clazz: Class<E>): StreamCodec<ByteBuf, E> =
streamCodec(ByteBuf::writeEnum) { it.readEnum(clazz) }

inline fun <reified E : Enum<E>> enumStreamCodec(): StreamCodec<ByteBuf, E> =
streamCodec(ByteBuf::writeEnum) { it.readEnum<E>() }


fun <T> idMapper(
idLookup: (Int) -> T,
idGetter: (T) -> Int
): StreamCodec<ByteBuf, T> = object : StreamCodec<ByteBuf, T> {
override fun decode(buf: ByteBuf): T = idLookup(buf.readVarInt())
override fun encode(buf: ByteBuf, value: T) {
buf.writeVarInt(idGetter(value))
}
}
}
Loading