Skip to content
Merged
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 common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repositories {
}

dependencies {
protobuf("gg.grounds:library-grpc-contracts-player:0.4.0")
protobuf("gg.grounds:library-grpc-contracts-player:0.5.0")

testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.4")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.4")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package gg.grounds.player.presence

import gg.grounds.grpc.player.CountPlayersByProxyReply
import gg.grounds.grpc.player.CountPlayersByProxyRequest
import gg.grounds.grpc.player.CountPlayersByServerReply
import gg.grounds.grpc.player.CountPlayersByServerRequest
import gg.grounds.grpc.player.GetPlayerSessionRequest
Expand All @@ -25,7 +27,12 @@ private constructor(
private val channel: ManagedChannel,
private val stub: PlayerPresenceServiceGrpc.PlayerPresenceServiceBlockingStub,
) : AutoCloseable {
fun tryLogin(playerId: UUID, playerName: String = "", proxyId: String = ""): PlayerLoginResult {
fun tryLogin(
playerId: UUID,
playerName: String = "",
proxyId: String = "",
region: String = "",
): PlayerLoginResult {
return try {
val reply =
stub
Expand All @@ -35,6 +42,7 @@ private constructor(
.setPlayerId(playerId.toString())
.setPlayerName(playerName)
.setProxyId(proxyId)
.setRegion(region)
.build()
)
PlayerLoginResult.Success(reply)
Expand Down Expand Up @@ -136,6 +144,16 @@ private constructor(
}
}

fun countPlayersByProxy(): CountPlayersByProxyReply? {
return try {
stub
.withDeadlineAfter(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.countPlayersByProxy(CountPlayersByProxyRequest.newBuilder().build())
} catch (e: RuntimeException) {
null
}
}

fun updateServer(playerId: UUID, serverName: String): Boolean {
return try {
stub
Expand Down
4 changes: 2 additions & 2 deletions velocity/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ dependencies {
implementation(project(":common"))
// plugin-proxy owns the ProxyServiceRegistry at runtime — compileOnly, never shaded, or the
// registry this plugin writes into would be a different class from the one chat/social read.
compileOnly("gg.grounds:plugin-proxy-api:0.2.0")
compileOnly("gg.grounds:plugin-proxy-api:0.3.0")
implementation("tools.jackson.dataformat:jackson-dataformat-yaml:3.0.4")
implementation("tools.jackson.module:jackson-module-kotlin:3.0.4")
implementation("io.grpc:grpc-netty-shaded:1.78.0")

// compileOnly above is not visible to tests; PlayerSessionQueryImplTest needs the interface's
// types.
testImplementation("gg.grounds:plugin-proxy-api:0.2.0")
testImplementation("gg.grounds:plugin-proxy-api:0.3.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.13.4")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.13.4")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.4")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ class PlayerConnectionListener(
return EventTask.async {
// The name is what makes the session findable from another proxy — without it a player
// exists in presence but nobody can /msg them. PROXY_ID says which proxy holds them.
when (val result = playerPresenceService.tryLogin(playerId, name, proxyId())) {
when (
val result = playerPresenceService.tryLogin(playerId, name, proxyId(), region())
) {
is PlayerLoginResult.Success -> {
if (handleSuccess(event, name, playerId, result.reply)) {
return@async
Expand Down Expand Up @@ -160,6 +162,13 @@ class PlayerConnectionListener(

private fun proxyId(): String = System.getenv("PROXY_ID") ?: ""

/**
* Empty when the proxy declares no region. Passed through as-is rather than defaulted to
* something plausible: an invented location is worse than an absent one, and the session schema
* treats unknown as a normal value.
*/
private fun region(): String = System.getenv("REGION") ?: ""

private fun watchForAbandonedLogin(playerId: UUID, name: String) {
pendingLogins.add(playerId)
proxy.scheduler
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gg.grounds.presence

import gg.grounds.grpc.player.CountPlayersByProxyReply
import gg.grounds.grpc.player.CountPlayersByServerReply
import gg.grounds.grpc.player.PlayerLogoutReply
import gg.grounds.grpc.player.PlayerSessionInfo
Expand All @@ -22,9 +23,14 @@ class PlayerPresenceService : AutoCloseable {
client = GrpcPlayerPresenceClient.create(target)
}

fun tryLogin(playerId: UUID, playerName: String, proxyId: String): PlayerLoginResult {
fun tryLogin(
playerId: UUID,
playerName: String,
proxyId: String,
region: String,
): PlayerLoginResult {
return try {
client.tryLogin(playerId, playerName, proxyId)
client.tryLogin(playerId, playerName, proxyId, region)
} catch (e: RuntimeException) {
PlayerLoginResult.Error(e.message ?: e::class.java.name)
}
Expand Down Expand Up @@ -66,6 +72,14 @@ class PlayerPresenceService : AutoCloseable {
}
}

fun countPlayersByProxy(): CountPlayersByProxyReply? {
return try {
client.countPlayersByProxy()
} catch (e: RuntimeException) {
null
}
}

fun updateServer(playerId: UUID, serverName: String): Boolean {
return try {
client.updateServer(playerId, serverName)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package gg.grounds.presence

import gg.grounds.grpc.player.CountPlayersByProxyReply
import gg.grounds.grpc.player.CountPlayersByServerReply
import gg.grounds.proxy.api.NetworkPlayerCounts
import gg.grounds.proxy.api.NetworkProxyCounts
import gg.grounds.proxy.api.PlayerSessionInfo
import gg.grounds.proxy.api.PlayerSessionQuery
import gg.grounds.proxy.api.ProxyPlayers
import java.util.UUID

/**
Expand All @@ -28,6 +31,9 @@ class PlayerSessionQueryImpl(private val presenceService: PlayerPresenceService)
override fun countPlayersByServer(): NetworkPlayerCounts? =
presenceService.countPlayersByServer()?.let(::toNetworkPlayerCounts)

override fun countPlayersByProxy(): NetworkProxyCounts? =
presenceService.countPlayersByProxy()?.let(::toNetworkProxyCounts)

/**
* A session with no usable id or name tells the caller nothing — drop it rather than
* half-answer.
Expand All @@ -41,13 +47,27 @@ class PlayerSessionQueryImpl(private val presenceService: PlayerPresenceService)
proxyId = session.proxyId.takeIf { it.isNotEmpty() },
server = session.serverName.takeIf { it.isNotEmpty() },
connectedAt = session.connectedAtMillis,
region = session.region.takeIf { it.isNotEmpty() },
)
}

/**
* `servers` has one row per occupied backend server — a server nobody is on is absent, not a
* zero entry.
*/
/**
* `proxies` has one row per occupied proxy. An empty region string means the proxy declares
* none — mapped to null rather than kept as "", so callers have one shape for "unknown".
*/
internal fun toNetworkProxyCounts(reply: CountPlayersByProxyReply): NetworkProxyCounts =
NetworkProxyCounts(
proxies =
reply.proxiesList.map {
ProxyPlayers(it.proxyId, it.region.takeIf(String::isNotEmpty), it.players)
},
total = reply.total,
)

internal fun toNetworkPlayerCounts(reply: CountPlayersByServerReply): NetworkPlayerCounts =
NetworkPlayerCounts(
byServer = reply.serversList.associate { it.serverName to it.players },
Expand Down
Loading