diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ShulkerProxyAgentCommon.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ShulkerProxyAgentCommon.kt index 6d8ef2d8..6ae86d2e 100644 --- a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ShulkerProxyAgentCommon.kt +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ShulkerProxyAgentCommon.kt @@ -8,6 +8,8 @@ import io.shulkermc.proxyagent.adapters.filesystem.FileSystemAdapter import io.shulkermc.proxyagent.adapters.filesystem.LocalFileSystemAdapter import io.shulkermc.proxyagent.adapters.kubernetes.KubernetesGatewayAdapter import io.shulkermc.proxyagent.adapters.kubernetes.ImplKubernetesGatewayAdapter +import io.shulkermc.proxyagent.adapters.mojang.HttpMojangGatewayAdapter +import io.shulkermc.proxyagent.adapters.mojang.MojangGatewayAdapter import io.shulkermc.proxyagent.adapters.pubsub.PubSubAdapter import io.shulkermc.proxyagent.adapters.pubsub.RedisPubSubAdapter import io.shulkermc.proxyagent.api.ShulkerProxyAPI @@ -30,6 +32,7 @@ class ShulkerProxyAgentCommon(val proxyInterface: ProxyInterface, val logger: Lo // Adapters lateinit var kubernetesGateway: KubernetesGatewayAdapter lateinit var fileSystem: FileSystemAdapter + lateinit var mojangGateway: MojangGatewayAdapter lateinit var cache: CacheAdapter lateinit var pubSub: PubSubAdapter @@ -52,8 +55,9 @@ class ShulkerProxyAgentCommon(val proxyInterface: ProxyInterface, val logger: Lo this.jedisPool = this.createJedisPool() this.jedisPool.resource.use { jedis -> jedis.ping() } - this.fileSystem = LocalFileSystemAdapter() this.kubernetesGateway = ImplKubernetesGatewayAdapter(Configuration.PROXY_NAMESPACE, Configuration.PROXY_NAME) + this.fileSystem = LocalFileSystemAdapter() + this.mojangGateway = HttpMojangGatewayAdapter() this.cache = RedisCacheAdapter(this.jedisPool) this.pubSub = RedisPubSubAdapter(this.jedisPool) diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/HttpMojangGatewayAdapter.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/HttpMojangGatewayAdapter.kt new file mode 100644 index 00000000..480e233d --- /dev/null +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/HttpMojangGatewayAdapter.kt @@ -0,0 +1,49 @@ +package io.shulkermc.proxyagent.adapters.mojang + +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import java.net.HttpURLConnection +import java.net.URI +import java.util.Optional +import java.util.UUID + +class HttpMojangGatewayAdapter : MojangGatewayAdapter { + override fun getProfileFromName(playerName: String): Optional { + val url = URI("https://api.mojang.com/users/profiles/minecraft/$playerName").toURL() + val connection = url.openConnection() as HttpURLConnection + connection.requestMethod = "GET" + + val status = connection.responseCode + if (status != 200) return Optional.empty() + + val response = connection.inputStream.bufferedReader().use { it.readText() } + val responseJson = JsonParser.parseString(response).asJsonObject + return Optional.of(this.getProfileFromJson(responseJson)) + } + + override fun getProfileFromId(playerId: UUID): Optional { + val undashedPlayerId = playerId.toString().replace("-", "") + val url = URI("https://sessionserver.mojang.com/session/minecraft/profile/$undashedPlayerId").toURL() + val connection = url.openConnection() as HttpURLConnection + connection.requestMethod = "GET" + + val status = connection.responseCode + if (status != 200) return Optional.empty() + + val response = connection.inputStream.bufferedReader().use { it.readText() } + val responseJson = JsonParser.parseString(response).asJsonObject + return Optional.of(this.getProfileFromJson(responseJson)) + } + + private fun getProfileFromJson(json: JsonObject): MojangGatewayAdapter.MojangProfile { + val uuid = UUID.fromString( + json.get("id").asString.replaceFirst( + "(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", + "$1-$2-$3-$4-$5" + ) + ) + val name = json.get("name").asString + + return MojangGatewayAdapter.MojangProfile(uuid, name) + } +} diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/MojangGatewayAdapter.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/MojangGatewayAdapter.kt new file mode 100644 index 00000000..9b975ee8 --- /dev/null +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/MojangGatewayAdapter.kt @@ -0,0 +1,11 @@ +package io.shulkermc.proxyagent.adapters.mojang + +import java.util.Optional +import java.util.UUID + +interface MojangGatewayAdapter { + fun getProfileFromName(playerName: String): Optional + fun getProfileFromId(playerId: UUID): Optional + + data class MojangProfile(val playerId: UUID, val playerName: String) +} diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/api/ShulkerProxyAPIImpl.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/api/ShulkerProxyAPIImpl.kt index 53b95563..c302087e 100644 --- a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/api/ShulkerProxyAPIImpl.kt +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/api/ShulkerProxyAPIImpl.kt @@ -9,4 +9,33 @@ class ShulkerProxyAPIImpl(private val agent: ShulkerProxyAgentCommon) : ShulkerP override fun getPlayerPosition(playerId: UUID): Optional = this.agent.cache.getPlayerPosition(playerId) override fun isPlayerConnected(playerId: UUID): Boolean = this.agent.cache.isPlayerConnected(playerId) + override fun countOnlinePlayers(): Int = this.agent.cache.countOnlinePlayers() + + override fun getPlayerIdFromName(playerName: String): Optional { + val cachedValue = this.agent.cache.getPlayerIdFromName(playerName) + if (cachedValue.isPresent) return cachedValue + + val mojangProfile = this.agent.mojangGateway.getProfileFromName(playerName) + if (mojangProfile.isPresent) { + val playerId = mojangProfile.get().playerId + this.agent.cache.updateCachedPlayerName(playerId, playerName) + return Optional.of(playerId) + } + + return Optional.empty() + } + + override fun getPlayerNameFromId(playerId: UUID): Optional { + val cachedValue = this.agent.cache.getPlayerNameFromId(playerId) + if (cachedValue.isPresent) return cachedValue + + val mojangProfile = this.agent.mojangGateway.getProfileFromId(playerId) + if (mojangProfile.isPresent) { + val playerName = mojangProfile.get().playerName + this.agent.cache.updateCachedPlayerName(playerId, playerName) + return Optional.of(playerName) + } + + return Optional.empty() + } } diff --git a/packages/shulker-proxy-api/src/main/java/io/shulkermc/proxyagent/api/ShulkerProxyAPI.java b/packages/shulker-proxy-api/src/main/java/io/shulkermc/proxyagent/api/ShulkerProxyAPI.java index 2a642362..6031e536 100644 --- a/packages/shulker-proxy-api/src/main/java/io/shulkermc/proxyagent/api/ShulkerProxyAPI.java +++ b/packages/shulker-proxy-api/src/main/java/io/shulkermc/proxyagent/api/ShulkerProxyAPI.java @@ -9,10 +9,14 @@ public abstract class ShulkerProxyAPI { public static ShulkerProxyAPI INSTANCE; - abstract public Set getServersByTag(@NotNull String tag); + abstract public @NotNull Set getServersByTag(@NotNull String tag); - abstract public Optional getPlayerPosition(@NotNull UUID playerId); + abstract public @NotNull Optional getPlayerPosition(@NotNull UUID playerId); abstract public boolean isPlayerConnected(@NotNull UUID playerId); + abstract public int countOnlinePlayers(); + + abstract public @NotNull Optional getPlayerIdFromName(@NotNull String playerName); + abstract public @NotNull Optional getPlayerNameFromId(@NotNull UUID playerId); public record PlayerPosition(@NotNull String proxyName, @NotNull String serverName) {} }