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

Commit 7f8f88f

Browse files
committed
refactor: update CloudPlayer management and implement offline player data requests
1 parent ca04e38 commit 7f8f88f

File tree

10 files changed

+190
-17
lines changed

10 files changed

+190
-17
lines changed

.github/workflows/qodana_code_quality.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ on:
99
jobs:
1010
qodana:
1111
runs-on: ubuntu-latest
12+
uses: production
1213
permissions:
1314
contents: write
1415
pull-requests: write

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ buildscript {
99
maven("https://repo.slne.dev/repository/maven-public/") { name = "maven-public" }
1010
}
1111
dependencies {
12-
classpath("dev.slne.surf:surf-api-gradle-plugin:1.21.4+")
12+
classpath("dev.slne.surf:surf-api-gradle-plugin:1.21.4-1.0.121")
1313
}
1414
}
1515

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ import net.kyori.adventure.sound.Sound
2727
import net.kyori.adventure.text.Component
2828
import net.querz.nbt.tag.CompoundTag
2929
import java.io.*
30+
import java.net.Inet4Address
3031
import java.net.InetSocketAddress
3132
import java.net.URI
33+
import java.time.Instant
34+
import java.time.ZoneId
35+
import java.time.ZonedDateTime
3236
import java.util.*
3337
import java.util.function.Consumer
3438
import kotlin.contracts.ExperimentalContracts
@@ -625,6 +629,37 @@ open class SurfByteBuf(source: ByteBuf) : WrappedByteBuf(source) {
625629
}
626630

627631
fun <B : ByteBuf> readCompoundTag(buf: B) = ExtraCodecs.COMPOUND_TAG_CODEC.decode(buf)
632+
633+
fun <B: ByteBuf> writeZonedDateTime(buf: B, time: ZonedDateTime) {
634+
writeVarLong(buf, time.toInstant().toEpochMilli())
635+
writeUtf(buf, time.zone.id)
636+
}
637+
638+
fun <B: ByteBuf> readZonedDateTime(buf: B): ZonedDateTime {
639+
val epoch = readVarLong(buf)
640+
val zone = readUtf(buf)
641+
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(epoch), ZoneId.of(zone))
642+
}
643+
644+
fun <B : ByteBuf> writeInet4Address(buf: B, address: Inet4Address) {
645+
writeByteArray(buf, address.address)
646+
}
647+
648+
fun <B : ByteBuf> readInet4Address(buf: B): Inet4Address {
649+
val address = readByteArray(buf)
650+
return Inet4Address.getByAddress(address) as Inet4Address
651+
}
652+
653+
fun <B : ByteBuf> writeSingleton(buf: B, singleton: Any) {
654+
require(singleton::class.objectInstance != null) { "Object must be a singleton" }
655+
writeUtf(buf, singleton::class.qualifiedName!!)
656+
}
657+
658+
fun <B : ByteBuf> readSingleton(buf: B): Any {
659+
val className = readUtf(buf)
660+
return Class.forName(className).kotlin.objectInstance
661+
?: throw DecoderException("Failed to read singleton: $className")
662+
}
628663
}
629664

630665

@@ -794,6 +829,12 @@ open class SurfByteBuf(source: ByteBuf) : WrappedByteBuf(source) {
794829
fun readInetSocketAddress() = readInetSocketAddress(this)
795830
fun writeCompoundTag(tag: CompoundTag) = writeCompoundTag(this, tag)
796831
fun readCompoundTag() = readCompoundTag(this)
832+
fun writeZonedDateTime(time: ZonedDateTime) = writeZonedDateTime(this, time)
833+
fun readZonedDateTime() = readZonedDateTime(this)
834+
fun writeInet4Address(address: Inet4Address) = writeInet4Address(this, address)
835+
fun readInet4Address() = readInet4Address(this)
836+
fun writeSingleton(singleton: Any) = writeSingleton(this, singleton)
837+
fun readSingleton() = readSingleton(this)
797838
// @formatter:on
798839
// endregion
799840

@@ -1017,6 +1058,15 @@ fun <B : ByteBuf> B.writeInetSocketAddress(address: InetSocketAddress) =
10171058
fun <B : ByteBuf> B.readCompoundTag() = SurfByteBuf.readCompoundTag(this)
10181059
fun <B : ByteBuf> B.writeCompoundTag(tag: CompoundTag) = SurfByteBuf.writeCompoundTag(this, tag)
10191060

1061+
fun <B : ByteBuf> B.readZonedDateTime() = SurfByteBuf.readZonedDateTime(this)
1062+
fun <B : ByteBuf> B.writeZonedDateTime(time: ZonedDateTime) = SurfByteBuf.writeZonedDateTime(this, time)
1063+
1064+
fun <B : ByteBuf> B.readInet4Address() = SurfByteBuf.readInet4Address(this)
1065+
fun <B : ByteBuf> B.writeInet4Address(address: Inet4Address) = SurfByteBuf.writeInet4Address(this, address)
1066+
1067+
fun <B : ByteBuf> B.readSingleton() = SurfByteBuf.readSingleton(this)
1068+
fun <B : ByteBuf> B.writeSingleton(singleton: Any) = SurfByteBuf.writeSingleton(this, singleton)
1069+
10201070
fun ByteBuf.wrap() = SurfByteBuf(this)
10211071
// endregion
10221072

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.slne.surf.cloud.core.client.player
22

3+
import dev.slne.surf.cloud.api.common.player.OfflineCloudPlayer
34
import dev.slne.surf.cloud.core.common.player.CloudPlayerManagerImpl
45
import dev.slne.surf.cloud.core.common.player.playerManagerImpl
56
import net.kyori.adventure.audience.Audience
@@ -43,6 +44,10 @@ abstract class CommonClientCloudPlayerManagerImpl<Platform: Audience, P : Client
4344
return player.serverUid
4445
}
4546

47+
override fun getOfflinePlayer(uuid: UUID): OfflineCloudPlayer {
48+
return OfflineCloudPlayerImpl(uuid)
49+
}
50+
4651
abstract fun getAudience(uuid: UUID): Audience?
4752
}
4853

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package dev.slne.surf.cloud.core.client.player
2+
3+
import dev.slne.surf.cloud.api.client.netty.packet.fireAndAwaitOrThrow
4+
import dev.slne.surf.cloud.api.common.player.name.NameHistory
5+
import dev.slne.surf.cloud.core.common.netty.network.protocol.running.ServerboundRequestOfflinePlayerData
6+
import dev.slne.surf.cloud.core.common.netty.network.protocol.running.ServerboundRequestOfflinePlayerData.RequestType
7+
import dev.slne.surf.cloud.core.common.player.CommonOfflineCloudPlayerImpl
8+
import net.kyori.adventure.text.Component
9+
import java.net.InetAddress
10+
import java.time.ZonedDateTime
11+
import java.util.*
12+
13+
class OfflineCloudPlayerImpl(uuid: UUID) : CommonOfflineCloudPlayerImpl(uuid) {
14+
override suspend fun nameHistory(): NameHistory {
15+
TODO("Not yet implemented")
16+
}
17+
18+
override suspend fun lastServerRaw(): String? {
19+
return request(RequestType.LAST_SERVER)
20+
}
21+
22+
override suspend fun lastSeen(): ZonedDateTime? {
23+
return request(RequestType.LAST_SEEN)
24+
}
25+
26+
override suspend fun latestIpAddress(): InetAddress? {
27+
return request(RequestType.LATEST_IP_ADDRESS)
28+
}
29+
30+
override suspend fun displayName(): Component? {
31+
return request(RequestType.DISPLAY_NAME)
32+
}
33+
34+
override suspend fun <R> getLuckpermsMetaData(
35+
key: String,
36+
transformer: (String) -> R
37+
): R? {
38+
TODO("Not yet implemented")
39+
}
40+
41+
override suspend fun getLuckpermsMetaData(key: String): String? {
42+
TODO("Not yet implemented")
43+
}
44+
45+
private suspend fun <T> request(type: RequestType<T>): T {
46+
return ServerboundRequestOfflinePlayerData(uuid, type).fireAndAwaitOrThrow().value
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package dev.slne.surf.cloud.core.common.netty.network.protocol.running
2+
3+
import dev.slne.surf.cloud.api.common.netty.packet.RespondingNettyPacket
4+
import dev.slne.surf.cloud.api.common.netty.packet.ResponseNettyPacket
5+
import dev.slne.surf.cloud.api.common.netty.packet.packetCodec
6+
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.SurfByteBuf
7+
import dev.slne.surf.cloud.core.common.netty.network.protocol.running.ServerboundRequestOfflinePlayerData.RequestType
8+
import net.kyori.adventure.text.Component
9+
import java.net.Inet4Address
10+
import java.time.ZonedDateTime
11+
import java.util.*
12+
13+
class ServerboundRequestOfflinePlayerData<T>(
14+
val uuid: UUID,
15+
val requestType: RequestType<T>
16+
) : RespondingNettyPacket<ResponseRequestOfflinePlayerData<T>>() {
17+
companion object {
18+
val STREAM_CODEC = packetCodec(
19+
ServerboundRequestOfflinePlayerData<*>::write
20+
) { buf ->
21+
val uuid = buf.readUuid()
22+
val requestType = buf.readSingleton() as RequestType<*>
23+
ServerboundRequestOfflinePlayerData(uuid, requestType)
24+
}
25+
}
26+
27+
private fun write(buf: SurfByteBuf) {
28+
buf.writeUuid(uuid)
29+
buf.writeSingleton(requestType)
30+
}
31+
32+
@Suppress("ClassName")
33+
sealed class RequestType<T> {
34+
object LAST_SERVER : RequestType<String?>()
35+
object LAST_SEEN : RequestType<ZonedDateTime?>()
36+
object LATEST_IP_ADDRESS : RequestType<Inet4Address?>()
37+
object DISPLAY_NAME : RequestType<Component?>()
38+
}
39+
}
40+
41+
sealed class ResponseRequestOfflinePlayerData<T>(val value: T) : ResponseNettyPacket() {
42+
companion object {
43+
val STREAM_CODEC = packetCodec(
44+
ResponseRequestOfflinePlayerData<*>::encode,
45+
) { buf ->
46+
val requestType = buf.readSingleton() as RequestType<*>
47+
decode(buf, requestType)
48+
}
49+
50+
fun <T> decode(
51+
buf: SurfByteBuf,
52+
requestType: RequestType<T>
53+
): ResponseRequestOfflinePlayerData<T> {
54+
val type = when (requestType) {
55+
is RequestType.LAST_SERVER -> LastServer(buf.readNullableString())
56+
is RequestType.LAST_SEEN -> LastSeen(buf.readNullable { it.readZonedDateTime() })
57+
is RequestType.LATEST_IP_ADDRESS -> LatestIpAddress(buf.readNullable { it.readInet4Address() })
58+
is RequestType.DISPLAY_NAME -> DisplayName(buf.readNullable { it.readComponent() })
59+
}
60+
61+
return type as ResponseRequestOfflinePlayerData<T>
62+
}
63+
}
64+
65+
class LastServer(server: String?) : ResponseRequestOfflinePlayerData<String?>(server)
66+
class LastSeen(time: ZonedDateTime?) : ResponseRequestOfflinePlayerData<ZonedDateTime?>(time)
67+
class LatestIpAddress(ip: Inet4Address?) : ResponseRequestOfflinePlayerData<Inet4Address?>(ip)
68+
class DisplayName(name: Component?) : ResponseRequestOfflinePlayerData<Component?>(name)
69+
70+
fun encode(buf: SurfByteBuf) {
71+
when (this) {
72+
is LastServer -> buf.writeNullable(value)
73+
is LastSeen -> buf.writeNullable(value) { buf, value -> buf.writeZonedDateTime(value) }
74+
is LatestIpAddress -> buf.writeNullable(value) { buf, value -> buf.writeInet4Address(value) }
75+
is DisplayName -> buf.writeNullable(value) { buf, value -> buf.writeComponent(value) }
76+
}
77+
}
78+
}

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/player/CloudPlayerManagerImpl.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,6 @@ abstract class CloudPlayerManagerImpl<P : CommonCloudPlayerImpl> : CloudPlayerMa
109109
}
110110

111111
open fun terminate() {}
112-
113-
override fun getOfflinePlayer(uuid: UUID): OfflineCloudPlayer {
114-
TODO("Not yet implemented")
115-
}
116112
}
117113

118114
val playerManagerImpl get() = CloudPlayerManager.instance as CloudPlayerManagerImpl<*>

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/player/CommonOfflineCloudPlayerImpl.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ abstract class CommonOfflineCloudPlayerImpl(override val uuid: UUID) : OfflineCl
1313

1414
override suspend fun lastServer(): CloudServer? =
1515
lastServerRaw()?.let { CloudServerManager.retrieveServerByName(it) } as? CloudServer
16+
17+
override suspend fun playedBefore(): Boolean {
18+
return player != null || lastSeen() != null
19+
}
1620
}

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ class OfflineCloudPlayerImpl(override val uuid: UUID) : OfflineCloudPlayer {
4545
override suspend fun latestIpAddress(): InetAddress? =
4646
player?.latestIpAddress() ?: service.findLastIpAddress(uuid)
4747

48-
override suspend fun playedBefore(): Boolean {
49-
return player != null || lastSeen() != null
50-
}
51-
5248
override suspend fun displayName(): Component? {
5349
return player?.displayName() ?: serverManagerImpl.requestOfflineDisplayName(uuid)
5450
}

surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/service/CloudPlayerService.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@ import java.net.Inet4Address
1212
import java.time.ZonedDateTime
1313
import java.util.*
1414

15-
1615
@Service
1716
@Transactional
18-
class CloudPlayerService(
19-
private val cloudPlayerRepository: CloudPlayerRepository
20-
) {
17+
class CloudPlayerService(private val cloudPlayerRepository: CloudPlayerRepository) {
2118

2219
suspend fun findLastServer(uuid: UUID): String? = find(uuid)?.lastServer
2320
suspend fun updateLastServer(uuid: UUID, server: String) = update(uuid) { lastServer = server }
@@ -52,14 +49,12 @@ class CloudPlayerService(
5249
}
5350
}
5451

55-
@Transactional
56-
protected suspend fun update(uuid: UUID, block: suspend CloudPlayerEntity.() -> Unit) =
52+
private suspend fun update(uuid: UUID, block: suspend CloudPlayerEntity.() -> Unit) =
5753
withContext(PlayerDatabaseScope.context) {
5854
cloudPlayerRepository.findByUuid(uuid)?.apply { block() }
5955
?.let { cloudPlayerRepository.save(it) }
6056
}
6157

62-
@Transactional
63-
protected suspend fun find(uuid: UUID) =
58+
private suspend fun find(uuid: UUID) =
6459
withContext(PlayerDatabaseScope.context) { cloudPlayerRepository.findByUuid(uuid) }
6560
}

0 commit comments

Comments
 (0)