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

Commit ba203a2

Browse files
committed
feat: add current session duration command and implement duration serialization
1 parent d7ff58b commit ba203a2

File tree

9 files changed

+97
-0
lines changed

9 files changed

+97
-0
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.java.URISerial
1111
import dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.java.UUIDSerializer
1212
import dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.java.UtfStringSerializer
1313
import dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.java.ZonedDateTimeSerializer
14+
import dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.kotlin.DurationSerializer
1415
import dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.nbt.CompoundTagSerializer
1516
import kotlinx.serialization.modules.SerializersModule
1617
import kotlinx.serialization.modules.contextual
@@ -31,6 +32,9 @@ object SurfCloudBufSerializer {
3132
contextual(ZonedDateTimeSerializer)
3233
contextual(Inet4AddressSerializer)
3334

35+
// Kotlin
36+
contextual(DurationSerializer)
37+
3438
// NBT
3539
contextual(CompoundTagSerializer)
3640
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.kotlin
2+
3+
import dev.slne.surf.cloud.api.common.netty.network.codec.kotlinx.CloudBufSerializer
4+
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.SurfByteBuf
5+
import kotlinx.serialization.Serializable
6+
import kotlinx.serialization.descriptors.PrimitiveKind
7+
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
8+
import kotlin.time.Duration
9+
10+
typealias SerializableDuration = @Serializable(with = DurationSerializer::class) Duration
11+
12+
object DurationSerializer : CloudBufSerializer<Duration>() {
13+
override val descriptor = PrimitiveSerialDescriptor("Duration", PrimitiveKind.LONG)
14+
15+
override fun serialize0(
16+
buf: SurfByteBuf,
17+
value: Duration
18+
) {
19+
buf.writeDuration(value)
20+
}
21+
22+
override fun deserialize0(buf: SurfByteBuf): Duration {
23+
return buf.readDuration()
24+
}
25+
}

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/netty/protocol/buffer/SurfByteBuf.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import java.util.function.Consumer
3838
import kotlin.contracts.ExperimentalContracts
3939
import kotlin.contracts.contract
4040
import kotlin.reflect.KClass
41+
import kotlin.time.Duration
42+
import kotlin.time.Duration.Companion.milliseconds
4143

4244
private const val NUMBER_BYTE: Byte = 0
4345
private const val NUMBER_SHORT: Byte = 1
@@ -660,6 +662,14 @@ open class SurfByteBuf(source: ByteBuf) : WrappedByteBuf(source) {
660662
return Class.forName(className, true, classLoader).kotlin.objectInstance
661663
?: throw DecoderException("Failed to read singleton: $className")
662664
}
665+
666+
fun <B: ByteBuf> writeDuration(buf: B, duration: Duration) {
667+
buf.writeLong(duration.inWholeMilliseconds)
668+
}
669+
670+
fun <B: ByteBuf> readDuration(buf: B): Duration {
671+
return buf.readLong().milliseconds
672+
}
663673
}
664674

665675

@@ -835,6 +845,8 @@ open class SurfByteBuf(source: ByteBuf) : WrappedByteBuf(source) {
835845
fun readInet4Address() = readInet4Address(this)
836846
fun writeSingleton(singleton: Any) = writeSingleton(this, singleton)
837847
fun readSingleton(classLoader: ClassLoader) = readSingleton(this, classLoader)
848+
fun writeDuration(duration: Duration) = writeDuration(this, duration)
849+
fun readDuration() = readDuration(this)
838850
// @formatter:on
839851
// endregion
840852

@@ -1067,6 +1079,9 @@ fun <B : ByteBuf> B.writeInet4Address(address: Inet4Address) = SurfByteBuf.write
10671079
fun <B : ByteBuf> B.readSingleton(classLoader: ClassLoader) = SurfByteBuf.readSingleton(this, classLoader)
10681080
fun <B : ByteBuf> B.writeSingleton(singleton: Any) = SurfByteBuf.writeSingleton(this, singleton)
10691081

1082+
fun <B : ByteBuf> B.writeDuration(duration: Duration) = SurfByteBuf.writeDuration(this, duration)
1083+
fun <B : ByteBuf> B.readDuration() = SurfByteBuf.readDuration(this)
1084+
10701085
fun ByteBuf.wrap() = SurfByteBuf(this)
10711086
// endregion
10721087

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/player/CloudPlayer.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import net.kyori.adventure.audience.Audience
99
import net.kyori.adventure.text.Component
1010
import java.net.Inet4Address
1111
import java.util.*
12+
import kotlin.time.Duration
1213

1314
/**
1415
* Represents a player connected to the cloud infrastructure.
@@ -36,6 +37,7 @@ interface CloudPlayer : Audience, OfflineCloudPlayer { // TODO: conversation but
3637
val connected get() = connectedToProxy || connectedToServer
3738

3839
suspend fun isAfk(): Boolean
40+
suspend fun currentSessionDuration(): Duration
3941

4042
/**
4143
* Performs modifications on the player's persistent data container.

surf-cloud-bukkit/src/main/kotlin/dev/slne/surf/cloud/bukkit/BukkitMain.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,26 @@ class BukkitMain : SuspendingJavaPlugin() {
343343
}
344344
}
345345
}
346+
347+
commandAPICommand("currentSessionDuration") {
348+
entitySelectorArgumentOnePlayer("player")
349+
anyExecutor { sender, args ->
350+
val player: Player by args
351+
val cloudPlayer = player.toCloudPlayer()
352+
launch {
353+
val currentSessionDuration = cloudPlayer?.currentSessionDuration()
354+
sender.sendText {
355+
appendPrefix()
356+
info("Current session duration for player ${player.name} (${player.uniqueId})")
357+
appendNewPrefixedLine {
358+
variableKey("Current Session Duration")
359+
spacer(": ")
360+
variableValue(currentSessionDuration?.toString() ?: "#Unknown")
361+
}
362+
}
363+
}
364+
}
365+
}
346366
}
347367

348368
@OptIn(ExperimentalContracts::class)

surf-cloud-core/surf-cloud-core-client/src/main/kotlin/dev/slne/surf/cloud/core/client/player/ClientCloudPlayerImpl.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ abstract class ClientCloudPlayerImpl<PlatformPlayer : Audience>(uuid: UUID) :
7777
return request<IsAFK>(DataRequestType.IS_AFK).isAfk
7878
}
7979

80+
override suspend fun currentSessionDuration(): Duration {
81+
return request<PlaytimeSession>(DataRequestType.PLAYTIME_SESSION).playtime
82+
}
83+
8084
override suspend fun <R> withPersistentData(block: PersistentPlayerDataContainer.() -> R): R {
8185
val response = ServerboundRequestPlayerPersistentDataContainer(uuid).fireAndAwaitOrThrow()
8286

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/netty/network/protocol/running/ServerboundRequestPlayerDataPacket.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import net.kyori.adventure.text.Component
1616
import java.net.Inet4Address
1717
import java.time.ZonedDateTime
1818
import java.util.*
19+
import kotlin.time.Duration
1920
import dev.slne.surf.cloud.api.common.player.name.NameHistory as ApiNameHistory
2021
import dev.slne.surf.cloud.api.common.player.playtime.Playtime as ApiPlaytime
2122

@@ -87,6 +88,12 @@ class ServerboundRequestPlayerDataPacket(val uuid: UUID, val type: DataRequestTy
8788
val player= player.player ?: error("Player is not online")
8889
return IsAFK(player.isAfk())
8990
}
91+
},
92+
PLAYTIME_SESSION(::PlaytimeSession) {
93+
override suspend fun readData(player: OfflineCloudPlayer): DataResponse {
94+
val player= player.player ?: error("Player is not online")
95+
return PlaytimeSession(player.currentSessionDuration())
96+
}
9097
};
9198

9299
abstract suspend fun readData(player: OfflineCloudPlayer): DataResponse
@@ -188,6 +195,14 @@ class ServerboundRequestPlayerDataResponse(val data: DataResponse) : ResponseNet
188195
buf.writeBoolean(isAfk)
189196
}
190197
}
198+
199+
class PlaytimeSession(val playtime: Duration) : DataResponse(DataRequestType.PLAYTIME_SESSION) {
200+
constructor(buf: SurfByteBuf) : this(buf.readDuration())
201+
202+
override fun write(buf: SurfByteBuf) {
203+
buf.writeDuration(playtime)
204+
}
205+
}
191206
}
192207

193208
inline fun <reified T> DataResponse.getGenericValue(): T = when (this) {
@@ -200,5 +215,6 @@ inline fun <reified T> DataResponse.getGenericValue(): T = when (this) {
200215
is NameHistory -> check(T::class == ApiNameHistory::class) { "Expected ApiNameHistory" }.let { history as T }
201216
is Playtime -> check(T::class == ApiPlaytime::class) { "Expected ApiPlaytime" }.let { playtime as T }
202217
is IsAFK -> check(T::class == Boolean::class) { "Expected Boolean" }.let { isAfk as T }
218+
is PlaytimeSession -> check(T::class == Duration::class) { "Expected Duration" }.let { playtime as T }
203219
else -> error("Unknown DataResponse type: ${this::class.simpleName}")
204220
}

surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/StandaloneCloudPlayerImpl.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ import net.kyori.adventure.title.TitlePart
4444
import net.querz.nbt.tag.CompoundTag
4545
import java.net.Inet4Address
4646
import java.time.ZonedDateTime
47+
import java.time.temporal.ChronoUnit
4748
import java.util.*
4849
import java.util.concurrent.TimeUnit
4950
import kotlin.time.Duration
51+
import kotlin.time.Duration.Companion.seconds
5052

5153
class StandaloneCloudPlayerImpl(uuid: UUID, val name: String, val ip: Inet4Address) :
5254
CommonCloudPlayerImpl(uuid) {
@@ -87,6 +89,8 @@ class StandaloneCloudPlayerImpl(uuid: UUID, val name: String, val ip: Inet4Addre
8789
var afk = false
8890
private set
8991

92+
var sessionStartTime: ZonedDateTime = ZonedDateTime.now()
93+
9094
fun savePlayerData(tag: CompoundTag) {
9195
if (!ppdc.empty) {
9296
tag.put("ppdc", ppdc.toTagCompound())
@@ -104,6 +108,11 @@ class StandaloneCloudPlayerImpl(uuid: UUID, val name: String, val ip: Inet4Addre
104108
return afk
105109
}
106110

111+
override suspend fun currentSessionDuration(): Duration {
112+
val duration = sessionStartTime.until(ZonedDateTime.now(), ChronoUnit.SECONDS)
113+
return duration.seconds
114+
}
115+
107116
fun updateAfkStatus(newValue: Boolean) {
108117
if (newValue == afk) return
109118
afk = newValue

surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/StandaloneCloudPlayerManagerImpl.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import dev.slne.surf.cloud.standalone.server.serverManagerImpl
1919
import dev.slne.surf.surfapi.core.api.util.logger
2020
import kotlinx.coroutines.*
2121
import java.net.Inet4Address
22+
import java.time.ZonedDateTime
2223
import java.util.*
2324
import kotlin.time.Duration.Companion.minutes
2425

@@ -149,6 +150,7 @@ class StandaloneCloudPlayerManagerImpl : CloudPlayerManagerImpl<StandaloneCloudP
149150
)
150151
}
151152

153+
player.sessionStartTime = ZonedDateTime.now()
152154
super.onNetworkConnect(uuid, player)
153155
}
154156

0 commit comments

Comments
 (0)