From f6324198ef756c277338fd880ba8a269d1fdb955 Mon Sep 17 00:00:00 2001 From: Graham Date: Wed, 27 Sep 2023 10:29:39 +0100 Subject: [PATCH] Add support for OSRS beta caches and server_version param Signed-off-by: Graham --- .../openrs2/archive/cache/CacheDownloader.kt | 42 ++++++++++++++----- .../kotlin/org/openrs2/archive/world/World.kt | 31 ++++++++++++++ .../org/openrs2/archive/world/WorldList.kt | 22 ++++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 archive/src/main/kotlin/org/openrs2/archive/world/World.kt create mode 100644 archive/src/main/kotlin/org/openrs2/archive/world/WorldList.kt diff --git a/archive/src/main/kotlin/org/openrs2/archive/cache/CacheDownloader.kt b/archive/src/main/kotlin/org/openrs2/archive/cache/CacheDownloader.kt index f7edcaf5ec..6a73c3cf68 100644 --- a/archive/src/main/kotlin/org/openrs2/archive/cache/CacheDownloader.kt +++ b/archive/src/main/kotlin/org/openrs2/archive/cache/CacheDownloader.kt @@ -5,11 +5,15 @@ import jakarta.inject.Singleton import org.openrs2.archive.cache.nxt.MusicStreamClient import org.openrs2.archive.game.GameDatabase import org.openrs2.archive.jav.JavConfig +import org.openrs2.archive.world.World +import org.openrs2.archive.world.WorldList import org.openrs2.buffer.ByteBufBodyHandler +import org.openrs2.buffer.use import org.openrs2.net.BootstrapFactory import org.openrs2.net.awaitSuspend import java.net.URI import java.net.http.HttpClient +import java.net.http.HttpRequest import kotlin.coroutines.suspendCoroutine @Singleton @@ -22,23 +26,25 @@ public class CacheDownloader @Inject constructor( ) { public suspend fun download(gameName: String, environment: String, language: String) { val game = gameDatabase.getGame(gameName, environment, language) ?: throw Exception("Game not found") - val url = game.url ?: throw Exception("URL not set") - var buildMajor = game.buildMajor ?: throw Exception("Current major build not set") - val config = JavConfig.download(client, url) val group = bootstrapFactory.createEventLoopGroup() try { suspendCoroutine { continuation -> val bootstrap = bootstrapFactory.createBootstrap(group) - val hostname: String val initializer = when (gameName) { "oldschool" -> { - val codebase = config.config[CODEBASE] ?: throw Exception("Codebase missing") - hostname = URI(codebase).host ?: throw Exception("Hostname missing") + var buildMajor = game.buildMajor + + hostname = if (environment == "beta") { + findOsrsWorld(config, World::isBeta) ?: throw Exception("Failed to find beta world") + } else { + val codebase = config.config[CODEBASE] ?: throw Exception("Codebase missing") + URI(codebase).host ?: throw Exception("Hostname missing") + } val serverVersion = config.params[OSRS_SERVER_VERSION] if (serverVersion != null) { @@ -52,7 +58,7 @@ public class CacheDownloader @Inject constructor( game.id, hostname, PORT, - buildMajor, + buildMajor ?: throw Exception("Current major build not set"), game.lastMasterIndexId, continuation, importer @@ -61,7 +67,8 @@ public class CacheDownloader @Inject constructor( } "runescape" -> { - var buildMinor = game.buildMinor ?: throw Exception("Current minor build not set") + var buildMajor = game.buildMajor + var buildMinor = game.buildMinor val serverVersion = config.config[NXT_SERVER_VERSION] if (serverVersion != null) { @@ -95,8 +102,8 @@ public class CacheDownloader @Inject constructor( game.id, hostname, PORT, - buildMajor, - buildMinor, + buildMajor ?: throw Exception("Current major build not set"), + buildMinor ?: throw Exception("Current minor build not set"), game.lastMasterIndexId, continuation, importer, @@ -118,8 +125,23 @@ public class CacheDownloader @Inject constructor( } } + private fun findOsrsWorld(config: JavConfig, predicate: (World) -> Boolean): String? { + val url = config.params[OSRS_WORLD_LIST_URL] ?: throw Exception("World list URL missing") + + val list = client.send(HttpRequest.newBuilder(URI(url)).build(), byteBufBodyHandler).body().use { buf -> + WorldList.read(buf) + } + + return list.worlds + .filter(predicate) + .map(World::hostname) + .shuffled() + .firstOrNull() + } + private companion object { private const val CODEBASE = "codebase" + private const val OSRS_WORLD_LIST_URL = "17" private const val OSRS_SERVER_VERSION = "25" private const val NXT_SERVER_VERSION = "server_version" private const val NXT_LIVE_HOSTNAME = "content.runescape.com" diff --git a/archive/src/main/kotlin/org/openrs2/archive/world/World.kt b/archive/src/main/kotlin/org/openrs2/archive/world/World.kt new file mode 100644 index 0000000000..7b3c7d3a2b --- /dev/null +++ b/archive/src/main/kotlin/org/openrs2/archive/world/World.kt @@ -0,0 +1,31 @@ +package org.openrs2.archive.world + +import io.netty.buffer.ByteBuf +import org.openrs2.buffer.readString + +public data class World( + public val id: Int, + public val flags: Int, + public val hostname: String, + public val activity: String, + public val country: Int, + public val players: Int +) { + public val isBeta: Boolean + get() = (flags and FLAG_BETA) != 0 + + public companion object { + private const val FLAG_BETA = 0x10000 + + public fun read(buf: ByteBuf): World { + val id = buf.readUnsignedShort() + val flags = buf.readInt() + val hostname = buf.readString() + val activity = buf.readString() + val country = buf.readUnsignedByte().toInt() + val players = buf.readShort().toInt() + + return World(id, flags, hostname, activity, country, players) + } + } +} diff --git a/archive/src/main/kotlin/org/openrs2/archive/world/WorldList.kt b/archive/src/main/kotlin/org/openrs2/archive/world/WorldList.kt new file mode 100644 index 0000000000..76c582c785 --- /dev/null +++ b/archive/src/main/kotlin/org/openrs2/archive/world/WorldList.kt @@ -0,0 +1,22 @@ +package org.openrs2.archive.world + +import io.netty.buffer.ByteBuf + +public data class WorldList( + public val worlds: List +) { + public companion object { + public fun read(buf: ByteBuf): WorldList { + buf.skipBytes(4) + + val count = buf.readUnsignedShort() + val worlds = buildList(count) { + for (i in 0 until count) { + add(World.read(buf)) + } + } + + return WorldList(worlds) + } + } +}