Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
kotlin.code.style=official
kotlin.stdlib.default.dependency=false
org.gradle.parallel=true
version=2.4.1
version=2.4.2
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.slne.surf.core.api.common

import dev.slne.surf.api.core.util.requiredService
import dev.slne.surf.core.api.common.cache.OfflinePlayerNameCache
import dev.slne.surf.core.api.common.event.SurfEvent
import dev.slne.surf.core.api.common.player.SurfPlayer
import dev.slne.surf.core.api.common.server.CommonSurfServer
Expand Down Expand Up @@ -46,6 +47,8 @@ interface SurfCoreApi {
fun fireEvent(event: SurfEvent)
fun subscribe(eventClass: KClass<out SurfEvent>, handler: (SurfEvent) -> Unit)

suspend fun loadOfflinePlayerNameEntries(): List<OfflinePlayerNameCache.Entry>

/**
* Sends a request to connect the specified player to the given server and awaits the result.
* This method can only send a player to a backend server, not a proxy.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package dev.slne.surf.core.api.common.cache

import dev.slne.surf.api.core.serializer.java.uuid.SerializableUUID
import dev.slne.surf.api.core.util.logger
import dev.slne.surf.core.api.common.SurfCoreApi
import kotlinx.coroutines.*
import kotlinx.serialization.Serializable
import kotlin.time.Duration.Companion.minutes

object OfflinePlayerNameCache {
private val logger = logger()

@Volatile
private var sortedNames: List<String> = emptyList()
private val CACHE_REFRESH_INTERVAL = 5.minutes

suspend fun refresh() {
sortedNames = SurfCoreApi.loadOfflinePlayerNameEntries().map { it.name }
.sortedWith(String.CASE_INSENSITIVE_ORDER)
}

fun findByPrefix(prefix: String): List<String> {
val lower = prefix.lowercase()
val snapshot = sortedNames

var lo = 0
var hi = snapshot.size
while (lo < hi) {
val mid = (lo + hi) ushr 1
if (snapshot[mid].lowercase() < lower) lo = mid + 1 else hi = mid
}

return buildList {
var i = lo
while (i < snapshot.size && snapshot[i].lowercase().startsWith(lower)) {
add(snapshot[i])
i++
}
}
}
Comment thread
TheBjoRedCraft marked this conversation as resolved.

fun startPulling(instanceScope: CoroutineScope) = instanceScope.launch(Dispatchers.IO) {
while (isActive) {
runCatching {
refresh()
}.onFailure {
logger.atFine().log("Failed to refresh offline player name cache: ${it.message}")
}
delay(CACHE_REFRESH_INTERVAL)
}
}

@Serializable
data class Entry(
val uuid: SerializableUUID,
val name: String
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,38 @@ import dev.jorel.commandapi.arguments.CustomArgument
import dev.jorel.commandapi.arguments.StringArgument
import dev.slne.surf.api.core.util.logger
import dev.slne.surf.core.api.common.SurfCoreApi
import dev.slne.surf.core.api.common.cache.OfflinePlayerNameCache
import dev.slne.surf.core.api.common.player.SurfPlayer
import dev.slne.surf.core.api.paper.CorePlayerStatusAccess
import kotlinx.coroutines.*
import kotlinx.coroutines.future.asDeferred
import kotlinx.coroutines.future.future

private const val SUGGESTION_LIMIT = 500

class SurfOfflinePlayerArgument(nodeName: String) :
CustomArgument<Deferred<SurfPlayer?>, String>(StringArgument(nodeName), { info ->
scope.future {
SurfCoreApi.getOfflinePlayer(info.input)
}.asDeferred()
}) {
init {
this.replaceSuggestions(
ArgumentSuggestions.stringCollection { viewerInfo ->
SurfCoreApi.getOnlinePlayers()
.filter { CorePlayerStatusAccess.hasAccess(viewerInfo.sender, it) }
.mapNotNull { it.lastKnownName }
replaceSuggestions(
ArgumentSuggestions.stringCollectionAsync { viewerInfo ->
scope.future {
val input = viewerInfo.currentArg ?: ""

val onlinePlayerNames = SurfCoreApi.getOnlinePlayers()
.filter { CorePlayerStatusAccess.hasAccess(viewerInfo.sender, it) }
.mapNotNull { it.lastKnownName }
.filter { input.isEmpty() || it.startsWith(input, ignoreCase = true) }

val onlineSet = onlinePlayerNames.toHashSet()
val offlinePlayerNames = OfflinePlayerNameCache.findByPrefix(input)
.filter { it !in onlineSet }

(onlinePlayerNames + offlinePlayerNames).take(SUGGESTION_LIMIT)
}
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import dev.jorel.commandapi.arguments.CommandAPIArgumentType
import dev.jorel.commandapi.executors.CommandArguments
import dev.slne.surf.api.core.util.logger
import dev.slne.surf.core.api.common.SurfCoreApi
import dev.slne.surf.core.api.common.cache.OfflinePlayerNameCache
import dev.slne.surf.core.api.common.player.SurfPlayer
import kotlinx.coroutines.*
import kotlinx.coroutines.future.asDeferred
import kotlinx.coroutines.future.future

private const val SUGGESTION_LIMIT = 500

@Suppress("UNCHECKED_CAST")
open class SurfOfflinePlayerArgument(nodeName: String) :
Argument<Deferred<SurfPlayer?>>(nodeName, StringArgumentType::string) {
Expand All @@ -36,9 +39,20 @@ open class SurfOfflinePlayerArgument(nodeName: String) :

init {
replaceSuggestions(
ArgumentSuggestions.stringCollection { _ ->
SurfCoreApi.getOnlinePlayers()
.mapNotNull { it.lastKnownName }
ArgumentSuggestions.stringCollectionAsync { viewerInfo ->
scope.future {
val input = viewerInfo.currentArg ?: ""

val onlineNames = SurfCoreApi.getOnlinePlayers()
.mapNotNull { it.lastKnownName }
.filter { input.isEmpty() || it.startsWith(input, ignoreCase = true) }

val onlinePlayerNames = onlineNames.toHashSet()
val offlinePlayerNames = OfflinePlayerNameCache.findByPrefix(input)
.filter { it !in onlinePlayerNames }

(onlineNames + offlinePlayerNames).take(SUGGESTION_LIMIT)
}
}
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dev.slne.surf.core.client

import dev.slne.surf.core.core.common.SurfCoreApiImpl
import dev.slne.surf.core.core.common.rabbit.packet.player.load.LoadOfflinePlayerNameEntriesRequestPacket

abstract class SurfCoreApiClientImpl : SurfCoreApiImpl() {
override suspend fun loadOfflinePlayerNameEntries() =
ClientCoreInstance.rabbitApi.sendRequest(LoadOfflinePlayerNameEntriesRequestPacket()).entries
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dev.slne.surf.core.core.common.rabbit.packet.player

import dev.slne.surf.core.api.common.cache.OfflinePlayerNameCache
import dev.slne.surf.rabbitmq.api.packet.RabbitResponsePacket
import kotlinx.serialization.Serializable

@Serializable
data class ManyOfflinePlayerNamesResponsePacket(
val entries: List<OfflinePlayerNameCache.Entry>
) : RabbitResponsePacket()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dev.slne.surf.core.core.common.rabbit.packet.player.load

import dev.slne.surf.core.core.common.rabbit.packet.player.ManyOfflinePlayerNamesResponsePacket
import dev.slne.surf.rabbitmq.api.packet.RabbitRequestPacket
import kotlinx.serialization.Serializable

@Serializable
class LoadOfflinePlayerNameEntriesRequestPacket :
RabbitRequestPacket<ManyOfflinePlayerNamesResponsePacket>()
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ class SurfCoreApiMicroserviceImpl : SurfCoreApiImpl() {
override fun getCurrentServerName() = "surf-core"
override fun getCurrentServerDisplayName() = "surf-core"
override fun getCurrentServerCategory() = "microservice"

override suspend fun loadOfflinePlayerNameEntries() = error("Not available on microservice")
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package dev.slne.surf.core.microservice.database.repository

import dev.slne.surf.core.api.common.cache.OfflinePlayerNameCache
import dev.slne.surf.core.api.common.player.SurfPlayer
import dev.slne.surf.core.microservice.database.tables.SurfPlayersTable
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.ResultRow
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.eq
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.select
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.upsert
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.toList
import java.time.OffsetDateTime
import java.util.*

Expand Down Expand Up @@ -47,6 +51,22 @@ object SurfPlayerRepository {
Unit
}

suspend fun loadOfflinePlayerNameEntries() = suspendTransaction {
SurfPlayersTable.select(SurfPlayersTable.name, SurfPlayersTable.uuid).mapNotNull {
val name = it[SurfPlayersTable.name]
val uuid = it[SurfPlayersTable.uuid]

if (name == null) {
return@mapNotNull null
}

OfflinePlayerNameCache.Entry(
uuid,
name
)
}.toList()
}

private fun createPlayerByRow(row: ResultRow) = SurfPlayer(
uuid = row[SurfPlayersTable.uuid],
lastKnownName = row[SurfPlayersTable.name],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dev.slne.surf.core.microservice.rabbit

import dev.slne.surf.core.core.common.rabbit.packet.player.ManyOfflinePlayerNamesResponsePacket
import dev.slne.surf.core.core.common.rabbit.packet.player.OptionalSurfPlayerResponsePacket
import dev.slne.surf.core.core.common.rabbit.packet.player.load.LoadOfflinePlayerNameEntriesRequestPacket
import dev.slne.surf.core.core.common.rabbit.packet.player.load.LoadPlayerByNameRequestPacket
import dev.slne.surf.core.core.common.rabbit.packet.player.load.LoadPlayerByUuidRequestPacket
import dev.slne.surf.core.core.common.rabbit.packet.player.save.SaveSurfPlayerRequestPacket
Expand Down Expand Up @@ -51,4 +53,14 @@ object SurfPlayerHandler {
)
}
}

@RabbitHandler
fun handleLoadOfflinePlayerNameEntriesRequest(request: LoadOfflinePlayerNameEntriesRequestPacket) =
request.launch {
request.respond(
ManyOfflinePlayerNamesResponsePacket(
SurfPlayerRepository.loadOfflinePlayerNameEntries()
)
)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package dev.slne.surf.core.paper

import com.github.shynixn.mccoroutine.folia.SuspendingJavaPlugin
import com.github.shynixn.mccoroutine.folia.scope
import dev.slne.surf.api.core.luckperms.LuckPermsAccess
import dev.slne.surf.api.paper.event.register
import dev.slne.surf.api.paper.util.getPrefixedName
import dev.slne.surf.core.api.common.cache.OfflinePlayerNameCache
import dev.slne.surf.core.api.common.event.SurfServerOnlineEvent
import dev.slne.surf.core.api.common.event.SurfServerStoppingEvent
import dev.slne.surf.core.api.common.server.SurfServer
Expand Down Expand Up @@ -54,6 +56,7 @@ class PaperMain : SuspendingJavaPlugin() {
luckPerms()

surfServerInformationSyncTask.start()
OfflinePlayerNameCache.startPulling(plugin.scope)
}

override suspend fun onDisableAsync() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package dev.slne.surf.core.paper.api

import com.google.auto.service.AutoService
import dev.slne.surf.core.api.common.SurfCoreApi
import dev.slne.surf.core.core.common.SurfCoreApiImpl
import dev.slne.surf.core.client.SurfCoreApiClientImpl
import dev.slne.surf.core.paper.surfServerConfig
import net.kyori.adventure.util.Services

@AutoService(SurfCoreApi::class)
class SurfCoreApiPaperImpl : SurfCoreApiImpl(), Services.Fallback {
class SurfCoreApiPaperImpl : SurfCoreApiClientImpl(), Services.Fallback {
override fun getCurrentServerName() = surfServerConfig.serverName
override fun getCurrentServerCategory() = surfServerConfig.serverCategory
override fun getCurrentServerDisplayName() = surfServerConfig.serverDisplayName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dev.slne.surf.core.velocity

import com.github.shynixn.mccoroutine.velocity.SuspendingPluginContainer
import com.github.shynixn.mccoroutine.velocity.registerSuspend
import com.github.shynixn.mccoroutine.velocity.scope
import com.google.inject.Inject
import com.velocitypowered.api.event.EventManager
import com.velocitypowered.api.event.Subscribe
Expand All @@ -13,6 +14,7 @@ import com.velocitypowered.api.plugin.annotation.DataDirectory
import com.velocitypowered.api.proxy.ProxyServer
import dev.slne.surf.api.core.messages.adventure.buildText
import dev.slne.surf.core.api.common.SurfCoreApi
import dev.slne.surf.core.api.common.cache.OfflinePlayerNameCache
import dev.slne.surf.core.api.common.event.SurfServerOnlineEvent
import dev.slne.surf.core.api.common.event.SurfServerStartEvent
import dev.slne.surf.core.api.common.event.SurfServerStoppingEvent
Expand Down Expand Up @@ -103,6 +105,7 @@ class VelocityMain @Inject constructor(
coreCommand()

surfPlayerSyncTask.start()
OfflinePlayerNameCache.startPulling(pluginContainer.scope)
}

@Subscribe
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import dev.slne.surf.core.api.common.SurfCoreApi
import dev.slne.surf.core.api.common.player.SurfPlayer
import dev.slne.surf.core.api.common.server.SurfServer
import dev.slne.surf.core.api.common.server.connection.SurfServerConnectResult
import dev.slne.surf.core.core.common.SurfCoreApiImpl
import dev.slne.surf.core.client.SurfCoreApiClientImpl
import dev.slne.surf.core.velocity.plugin
import dev.slne.surf.core.velocity.redis.handler.convertResult
import dev.slne.surf.core.velocity.surfServerConfig
Expand All @@ -14,7 +14,7 @@ import net.kyori.adventure.util.Services
import kotlin.jvm.optionals.getOrNull

@AutoService(SurfCoreApi::class)
class SurfCoreApiVelocityImpl : SurfCoreApiImpl(), Services.Fallback {
class SurfCoreApiVelocityImpl : SurfCoreApiClientImpl(), Services.Fallback {
override fun getCurrentServerName() = surfServerConfig.serverName
override fun getCurrentServerCategory() = surfServerConfig.serverCategory
override fun getCurrentServerDisplayName() = surfServerConfig.serverDisplayName
Expand All @@ -32,7 +32,7 @@ class SurfCoreApiVelocityImpl : SurfCoreApiImpl(), Services.Fallback {
SurfServerConnectResult.Status.SERVER_NOT_FOUND,
null
)

return player.createConnectionRequest(velocityServer)
.connect()
.await()
Expand Down