mirror of
https://gitlab.com/2009scape/rt4-client.git
synced 2025-12-15 02:50:23 -07:00
Update to use a centralized fetcher
This commit is contained in:
parent
6adc5135bc
commit
03a205f64b
9 changed files with 169 additions and 128 deletions
|
|
@ -0,0 +1,60 @@
|
|||
package KondoKit.network
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Lightweight helper for opening GET connections with shared defaults so callers don't repeat header setup.
|
||||
*/
|
||||
object HttpFetcher {
|
||||
const val DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||
|
||||
private val defaultHeaders = mapOf(
|
||||
"User-Agent" to DEFAULT_USER_AGENT,
|
||||
"Accept" to "*/*"
|
||||
)
|
||||
|
||||
fun openGetConnection(
|
||||
url: String,
|
||||
headers: Map<String, String> = emptyMap(),
|
||||
connectTimeoutMillis: Int? = null,
|
||||
readTimeoutMillis: Int? = null
|
||||
): HttpURLConnection {
|
||||
val connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
(defaultHeaders + headers).forEach { (key, value) ->
|
||||
connection.setRequestProperty(key, value)
|
||||
}
|
||||
connectTimeoutMillis?.let { connection.connectTimeout = it }
|
||||
readTimeoutMillis?.let { connection.readTimeout = it }
|
||||
return connection
|
||||
}
|
||||
|
||||
fun fetchString(
|
||||
url: String,
|
||||
headers: Map<String, String> = emptyMap(),
|
||||
connectTimeoutMillis: Int? = null,
|
||||
readTimeoutMillis: Int? = null
|
||||
): String {
|
||||
val connection = openGetConnection(url, headers, connectTimeoutMillis, readTimeoutMillis)
|
||||
val responseCode = connection.responseCode
|
||||
val responseStream = if (responseCode in 200..299) {
|
||||
connection.inputStream
|
||||
} else {
|
||||
connection.errorStream
|
||||
} ?: throw IOException("No response stream available for $url (HTTP $responseCode)")
|
||||
|
||||
val payload = responseStream.bufferedReader().use { it.readText() }
|
||||
if (responseCode !in 200..299) {
|
||||
throw HttpStatusException(url, responseCode, payload)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
class HttpStatusException(
|
||||
val url: String,
|
||||
val statusCode: Int,
|
||||
val body: String
|
||||
) : IOException("Request to $url failed with HTTP $statusCode")
|
||||
|
|
@ -10,9 +10,6 @@ object GitLabConfig {
|
|||
const val GITLAB_PROJECT_PATH = "2009scape/tools/client-plugins" // Using path from DownloadManager
|
||||
const val GITLAB_BRANCH = "master"
|
||||
|
||||
// HTTP headers and client settings
|
||||
const val CHROME_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||
|
||||
// Debug settings
|
||||
const val DEBUG = true // Set to false to disable debug logging
|
||||
|
||||
|
|
@ -34,4 +31,4 @@ object GitLabConfig {
|
|||
|
||||
fun getArchiveUrlWithRefType(pluginPath: String): String =
|
||||
"${getArchiveBaseUrl()}?path=$pluginPath&ref_type=heads"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
package KondoKit.pluginmanager
|
||||
|
||||
import KondoKit.views.ReflectiveEditorView
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import java.awt.EventQueue
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.concurrent.*
|
||||
import javax.swing.SwingUtilities
|
||||
import KondoKit.network.HttpFetcher
|
||||
import KondoKit.pluginmanager.GitLabConfig
|
||||
import KondoKit.util.JsonParser
|
||||
|
||||
object GitLabPluginFetcher {
|
||||
|
||||
|
|
@ -33,12 +31,7 @@ object GitLabPluginFetcher {
|
|||
val apiUrl = GitLabConfig.getRepositoryTreeUrl()
|
||||
debugLog("API URL: $apiUrl")
|
||||
|
||||
// Create URL connection
|
||||
val url = URL(apiUrl)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
connection.setRequestProperty("User-Agent", GitLabConfig.CHROME_USER_AGENT)
|
||||
debugLog("Set User-Agent header to: ${GitLabConfig.CHROME_USER_AGENT}")
|
||||
val connection = HttpFetcher.openGetConnection(apiUrl)
|
||||
|
||||
// Read response
|
||||
val responseCode = connection.responseCode
|
||||
|
|
@ -51,8 +44,7 @@ object GitLabPluginFetcher {
|
|||
debugLog("Response preview: ${response.take(200)}...")
|
||||
|
||||
// Parse JSON response
|
||||
val gson = Gson()
|
||||
val treeItems = gson.fromJson(response, Array<JsonObject>::class.java)
|
||||
val treeItems = JsonParser.fromJson(response, Array<JsonObject>::class.java)
|
||||
debugLog("Parsed ${treeItems.size} items from JSON")
|
||||
|
||||
// Filter for directories (trees) with mode "040000"
|
||||
|
|
@ -119,11 +111,7 @@ object GitLabPluginFetcher {
|
|||
val pluginUrl = GitLabConfig.getRawFileUrl(pluginFilePath)
|
||||
debugLog("Fetching plugin.properties from: $pluginUrl")
|
||||
|
||||
// Create URL connection
|
||||
val url = URL(pluginUrl)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
connection.setRequestProperty("User-Agent", GitLabConfig.CHROME_USER_AGENT)
|
||||
val connection = HttpFetcher.openGetConnection(pluginUrl)
|
||||
|
||||
// Read response
|
||||
val responseCode = connection.responseCode
|
||||
|
|
@ -179,4 +167,4 @@ object GitLabPluginFetcher {
|
|||
debugLog("Parsed properties - Author: $author, Version: $version, Description: $description")
|
||||
return PluginProperties(author, version, description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import java.util.zip.ZipEntry
|
|||
import java.util.zip.ZipInputStream
|
||||
import javax.swing.SwingUtilities
|
||||
import KondoKit.pluginmanager.GitLabConfig
|
||||
import KondoKit.network.HttpFetcher
|
||||
|
||||
/**
|
||||
* Manages downloading and installing plugins from GitLab with concurrent support
|
||||
|
|
@ -127,11 +128,12 @@ object PluginDownloadManager {
|
|||
}
|
||||
|
||||
// Create URL connection
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
connection.setRequestProperty("User-Agent", GitLabConfig.CHROME_USER_AGENT)
|
||||
// Add Accept header to avoid 406 errors
|
||||
connection.setRequestProperty("Accept", "*/*")
|
||||
val connection = try {
|
||||
HttpFetcher.openGetConnection(url.toString())
|
||||
} catch (e: Exception) {
|
||||
debugLog("Failed to open connection for plugin: ${plugin.path} with URL: $downloadUrl. Error: ${e.message}")
|
||||
continue
|
||||
}
|
||||
|
||||
debugLog("Connection opened for plugin: ${plugin.path}")
|
||||
debugLog("Request headers - User-Agent: ${connection.getRequestProperty("User-Agent")}")
|
||||
|
|
@ -367,4 +369,4 @@ object PluginDownloadManager {
|
|||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package KondoKit.util
|
||||
|
||||
import KondoKit.network.HttpFetcher
|
||||
|
||||
/**
|
||||
* Shared loader for GE price JSON from remote or bundled sources.
|
||||
*/
|
||||
object GEPriceService {
|
||||
private const val REMOTE_GE_URL = "https://cdn.2009scape.org/gedata/latest.json"
|
||||
|
||||
private data class RemoteGEItem(
|
||||
val item_id: String?,
|
||||
val value: String?
|
||||
)
|
||||
|
||||
private data class LocalGEItem(
|
||||
val id: String?,
|
||||
val grand_exchange_price: String?
|
||||
)
|
||||
|
||||
fun loadRemotePrices(): Map<String, String> {
|
||||
return try {
|
||||
val payload = HttpFetcher.fetchString(REMOTE_GE_URL)
|
||||
val items = JsonParser.fromJson<Array<RemoteGEItem>>(payload)
|
||||
items.mapNotNull { item ->
|
||||
val id = item.item_id
|
||||
val value = item.value
|
||||
if (id != null && value != null) id to value else null
|
||||
}.toMap()
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadLocalPrices(jsonProvider: () -> String?): Map<String, String> {
|
||||
return try {
|
||||
val json = jsonProvider() ?: return emptyMap()
|
||||
val items = JsonParser.fromJson<Array<LocalGEItem>>(json)
|
||||
items.mapNotNull { item ->
|
||||
val id = item.id
|
||||
val price = item.grand_exchange_price
|
||||
if (id != null && price != null) id to price else null
|
||||
}.toMap()
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package KondoKit.util
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
|
||||
/**
|
||||
* Centralizes JSON parsing to reuse the same Gson configuration everywhere.
|
||||
*/
|
||||
object JsonParser {
|
||||
val gson: Gson = Gson()
|
||||
|
||||
inline fun <reified T> fromJson(json: String): T {
|
||||
val type = object : TypeToken<T>() {}.type
|
||||
return gson.fromJson(json, type)
|
||||
}
|
||||
|
||||
fun <T> fromJson(json: String, classOfT: Class<T>): T = gson.fromJson(json, classOfT)
|
||||
|
||||
fun toJson(src: Any): String = gson.toJson(src)
|
||||
}
|
||||
|
|
@ -15,15 +15,13 @@ import KondoKit.plugin.Companion.POPUP_FOREGROUND
|
|||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
|
||||
import com.google.gson.Gson
|
||||
import KondoKit.util.JsonParser
|
||||
import plugin.api.API
|
||||
import rt4.Sprites
|
||||
import java.awt.*
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import KondoKit.network.HttpFetcher
|
||||
import KondoKit.network.HttpStatusException
|
||||
import java.net.SocketTimeoutException
|
||||
import java.net.URL
|
||||
import javax.swing.*
|
||||
import javax.swing.border.MatteBorder
|
||||
import kotlin.math.floor
|
||||
|
|
@ -235,37 +233,24 @@ object HiscoresView : View {
|
|||
|
||||
Thread {
|
||||
try {
|
||||
val url = URL(apiUrl)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
val response = HttpFetcher.fetchString(
|
||||
url = apiUrl,
|
||||
connectTimeoutMillis = 5000,
|
||||
readTimeoutMillis = 5000
|
||||
)
|
||||
|
||||
// If a request take longer than 5 seconds timeout.
|
||||
connection.connectTimeout = 5000
|
||||
connection.readTimeout = 5000
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
||||
val response = reader.use { it.readText() }
|
||||
reader.close()
|
||||
|
||||
SwingUtilities.invokeLater {
|
||||
updatePlayerDataStandalone(response, username, hiscoresPanel)
|
||||
|
||||
// Example of how to use setText now that the search field is accessible:
|
||||
// customSearchField?.setText(username) // You could set the text to the searched username
|
||||
}
|
||||
} else {
|
||||
SwingUtilities.invokeLater {
|
||||
showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
SwingUtilities.invokeLater {
|
||||
updatePlayerDataStandalone(response, username, hiscoresPanel)
|
||||
}
|
||||
} catch (e: SocketTimeoutException) {
|
||||
SwingUtilities.invokeLater {
|
||||
showToast(hiscoresPanel, "Request timed out", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
} catch (e: HttpStatusException) {
|
||||
SwingUtilities.invokeLater {
|
||||
showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Handle other errors
|
||||
SwingUtilities.invokeLater {
|
||||
showToast(hiscoresPanel, "Error fetching data!", JOptionPane.ERROR_MESSAGE)
|
||||
}
|
||||
|
|
@ -274,8 +259,7 @@ object HiscoresView : View {
|
|||
}
|
||||
|
||||
private fun updatePlayerDataStandalone(jsonResponse: String, username: String, hiscoresPanel: JPanel) {
|
||||
val gson = Gson()
|
||||
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
||||
val hiscoresResponse = JsonParser.fromJson<HiscoresResponse>(jsonResponse)
|
||||
updateHiscoresViewStandalone(hiscoresPanel, hiscoresResponse, username)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,7 @@ import java.awt.Font
|
|||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.BufferedReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import KondoKit.util.GEPriceService
|
||||
import java.text.DecimalFormat
|
||||
import javax.swing.*
|
||||
import kotlin.math.ceil
|
||||
|
|
@ -73,60 +71,13 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
|
|||
npcDeathSnapshots[npcID] = GroundSnapshot(preDeathSnapshot, Pair(x, z), 0)
|
||||
}
|
||||
|
||||
fun loadGEPrices(): Map<String, String> {
|
||||
fun loadGEPrices(): Map<String, String> {
|
||||
return if (useLiveGEPrices) {
|
||||
try {
|
||||
println("LootTracker: Loading Remote GE Prices")
|
||||
val url = URL("https://cdn.2009scape.org/gedata/latest.json")
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "GET"
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0")
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val inputStream = connection.inputStream
|
||||
val content = inputStream.bufferedReader().use(BufferedReader::readText)
|
||||
|
||||
val items = content.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
val gePrices = mutableMapOf<String, String>()
|
||||
|
||||
for (item in items) {
|
||||
val pairs = item.removeSurrounding("{", "}").split(",")
|
||||
val itemId = pairs.find { it.trim().startsWith("\"item_id\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
val value = pairs.find { it.trim().startsWith("\"value\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
if (itemId != null && value != null) {
|
||||
gePrices[itemId] = value
|
||||
}
|
||||
}
|
||||
|
||||
gePrices
|
||||
} else {
|
||||
emptyMap()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
println("LootTracker: Loading Remote GE Prices")
|
||||
GEPriceService.loadRemotePrices()
|
||||
} else {
|
||||
try {
|
||||
println("LootTracker: Loading Local GE Prices")
|
||||
Helpers.readResourceText("res/item_configs.json")?.let { json ->
|
||||
val items = json.trim().removeSurrounding("[", "]").split("},").map { it.trim() + "}" }
|
||||
val gePrices = mutableMapOf<String, String>()
|
||||
|
||||
for (item in items) {
|
||||
val pairs = item.removeSurrounding("{", "}").split(",")
|
||||
val id = pairs.find { it.trim().startsWith("\"id\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
val grandExchangePrice = pairs.find { it.trim().startsWith("\"grand_exchange_price\"") }?.split(":")?.get(1)?.trim()?.trim('\"')
|
||||
if (id != null && grandExchangePrice != null) {
|
||||
gePrices[id] = grandExchangePrice
|
||||
}
|
||||
}
|
||||
|
||||
gePrices
|
||||
} ?: emptyMap()
|
||||
} catch (e: Exception) {
|
||||
emptyMap()
|
||||
}
|
||||
println("LootTracker: Loading Local GE Prices")
|
||||
GEPriceService.loadLocalPrices { Helpers.readResourceText("res/item_configs.json") }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import KondoKit.plugin.Companion.playerXPMultiplier
|
|||
import KondoKit.plugin.Companion.primaryColor
|
||||
import KondoKit.plugin.Companion.secondaryColor
|
||||
import KondoKit.plugin.StateManager.focusedView
|
||||
import KondoKit.util.JsonParser
|
||||
import plugin.api.API
|
||||
import java.awt.*
|
||||
import java.awt.image.BufferedImage
|
||||
|
|
@ -43,20 +44,10 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback {
|
|||
|
||||
val npcHitpointsMap: Map<Int, Int> = try {
|
||||
val json = Helpers.readResourceText("res/npc_hitpoints_map.json") ?: "{}"
|
||||
val pairs = json.trim().removeSurrounding("{", "}").split(",")
|
||||
val map = mutableMapOf<Int, Int>()
|
||||
|
||||
for (pair in pairs) {
|
||||
val keyValue = pair.split(":")
|
||||
val id = keyValue[0].trim().trim('\"').toIntOrNull()
|
||||
val hitpoints = keyValue[1].trim()
|
||||
|
||||
if (id != null && hitpoints.isNotEmpty()) {
|
||||
map[id] = hitpoints.toIntOrNull() ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
val parsed: Map<String, Int> = JsonParser.fromJson(json)
|
||||
parsed.mapNotNull { (key, value) ->
|
||||
key.toIntOrNull()?.let { it to value }
|
||||
}.toMap()
|
||||
} catch (e: Exception) {
|
||||
println("XPTracker Error parsing NPC HP: ${e.message}")
|
||||
emptyMap()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue