Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3ff704c
add ipv6 tests
LossyDragon Jun 13, 2026
c27b48e
Add missing kdoc
LossyDragon Jun 13, 2026
c4476fa
Rework WebSocketConnection and remove one todo
LossyDragon Jun 13, 2026
f07f4a7
Rename .java to .kt
LossyDragon Jun 13, 2026
844d255
Apply formatting and analysis findings.
LossyDragon Jun 13, 2026
a561b0e
Optimze depotdownloader
LossyDragon Jun 13, 2026
5275f3d
Drastically improve DepotDownloader performance. Breaking: DownloadIt…
LossyDragon Jun 14, 2026
178454a
Rename .java to .kt
LossyDragon Jun 14, 2026
58647bf
Apply more audit recommendations for async stability
LossyDragon Jun 14, 2026
dc62f88
Post rebase fixes
LossyDragon Jun 15, 2026
7282362
Fix import
LossyDragon Jun 15, 2026
c51809b
Update dependencies
LossyDragon Jun 15, 2026
f8e1c17
quiet dokka warnings
LossyDragon Jun 15, 2026
9f909b5
Update remaining dependencies
LossyDragon Jun 15, 2026
8b29281
Try again
LossyDragon Jun 15, 2026
2ed004b
Get with the times! Target Jvm 17 since newer gradle and deps are now…
LossyDragon Jun 15, 2026
3b46299
Dont forget the runners
LossyDragon Jun 15, 2026
f412115
Try build caching
LossyDragon Jun 15, 2026
f66dfba
Rename .java to .kt
LossyDragon Jun 16, 2026
4cd3986
Port some more classes to kotlin, kdoc missing methods.
LossyDragon Jun 16, 2026
b330f8a
Rename .java to .kt
LossyDragon Jun 17, 2026
ea0186a
Port even more classes to kotlin
LossyDragon Jun 17, 2026
98ddcb7
Rename .java to .kt
LossyDragon Jun 17, 2026
6e02e92
Port even more classes to kotlin
LossyDragon Jun 17, 2026
e9e6d19
Rename .java to .kt
LossyDragon Jun 17, 2026
85ca62c
Port even more classes to kotlin
LossyDragon Jun 17, 2026
0af1ad4
Simplify weird hack I made for CallbackManager
LossyDragon Jun 17, 2026
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
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ ktlint_code_style = intellij_idea
ktlint_standard_package-name = disabled
ktlint_standard_multiline-expression-wrapping = disabled
ktlint_standard_trailing-comma-on-call-site = disabled
ktlint_standard_no-wildcard-imports = disabled


6 changes: 3 additions & 3 deletions .github/workflows/javasteam-build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Checkout JavaSteam with Java 11
- name: Checkout JavaSteam with Java 17
uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@v4
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle, skip signing
run: ./gradlew build -x signMavenJavaPublication
run: ./gradlew build -x signMavenJavaPublication --build-cache
4 changes: 2 additions & 2 deletions .github/workflows/javasteam-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Checkout JavaSteam with Java 11
- name: Checkout JavaSteam with Java 17
uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '17'
distribution: 'temurin'
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@v4
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="javasteam [wrapper --gradle-version 8.14]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<configuration default="false" name="JavaSteam [wrapper --gradle-version 9.5.1]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
Expand All @@ -12,15 +12,18 @@
<list>
<option value="wrapper" />
<option value="--gradle-version" />
<option value="8.14" />
<option value="9.5.1" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<ExternalSystemDebugDisabled>false</ExternalSystemDebugDisabled>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<GradleProfilingDisabled>false</GradleProfilingDisabled>
<GradleCoverageDisabled>false</GradleCoverageDisabled>
<method v="2" />
</configuration>
</component>
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Maven Central](https://img.shields.io/maven-central/v/in.dragonbra/javasteam)](https://mvnrepository.com/artifact/in.dragonbra/javasteam)
[![Discord](https://img.shields.io/discord/420907597906968586.svg)](https://discord.gg/8F2JuTu)

Java port of [SteamKit2](https://github.com/SteamRE/SteamKit). JavaSteam targets Java 11.
Java port of [SteamKit2](https://github.com/SteamRE/SteamKit). JavaSteam targets Java 17.

## Download

Expand Down
4 changes: 2 additions & 2 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ dependencies {
implementation(gradleApi())

// https://mvnrepository.com/artifact/commons-io/commons-io
implementation("commons-io:commons-io:2.20.0")
implementation("commons-io:commons-io:2.22.0")
// https://mvnrepository.com/artifact/com.squareup/kotlinpoet
implementation("com.squareup:kotlinpoet:2.2.0")
implementation("com.squareup:kotlinpoet:2.3.0")
}

gradlePlugin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,24 @@ class ProtoParser(private val outputDir: File) {
}.build()
cBuilder.addFunction(funcHandleNotificationMsg)

val asyncJobSingleClass = ClassName(
packageName = "in.dragonbra.javasteam.types",
"AsyncJobSingle"
)
val serviceMethodResponseClass = ClassName(
packageName = "in.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback",
"ServiceMethodResponse"
)
val noResponseClass = ClassName(
packageName = "in.dragonbra.javasteam.protobufs.steamclient.SteammessagesUnifiedBaseSteamclient",
"NoResponse"
)

// Public Methods
service.methods.forEach { method ->
val funcBuilder =
FunSpec.builder(method.methodName.replaceFirstChar { it.lowercase(Locale.getDefault()) })
.addModifiers(KModifier.PUBLIC)
.addKdoc(
"""
|@param request The request.
|@see [${method.requestType}]
|@returns [AsyncJobSingle]<[ServiceMethodResponse]<[${method.responseType}]>>
""".trimMargin() // wow
)
.addParameter(
"request",
ClassName(
Expand All @@ -201,15 +207,15 @@ class ProtoParser(private val outputDir: File) {
)

if (method.responseType != "NoResponse") {
// I can't find a way to suppress or fix dokka warnings for this.
//funcBuilder.addKdoc(
// "@param request The request.\n@see [${method.requestType}]\n@returns [%T]<[%T]<[${method.responseType}]>>\n",
// asyncJobSingleClass,
// serviceMethodResponseClass
//)
funcBuilder.returns(
ClassName(
packageName = "in.dragonbra.javasteam.types",
"AsyncJobSingle"
).parameterizedBy(
ClassName(
packageName = "in.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback",
"ServiceMethodResponse"
).parameterizedBy(
asyncJobSingleClass.parameterizedBy(
serviceMethodResponseClass.parameterizedBy(
ClassName.bestGuess("in.dragonbra.javasteam.protobufs.$parentPathName.$protoFileName.${method.responseType}.Builder")
)
)
Expand All @@ -223,6 +229,12 @@ class ProtoParser(private val outputDir: File) {
"${service.name}.${method.methodName}#1"
)
} else {
funcBuilder.addKdoc(
"@param request The request.\n@returns [%T]<[%T]<[%T]>>\n",
asyncJobSingleClass,
serviceMethodResponseClass,
noResponseClass
)
funcBuilder.addStatement(
format = "unifiedMessages!!.sendNotification<%T.Builder>(\n%S,\nrequest\n)",
ClassName(
Expand Down
44 changes: 22 additions & 22 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,38 @@
# ****

[versions]
java = "11"
kotlin = "2.2.21" # https://kotlinlang.org/docs/releases.html#release-details
dokka = "2.0.0" # https://mvnrepository.com/artifact/org.jetbrains.dokka/dokka-gradle-plugin
kotlinter = "5.3.0" # https://plugins.gradle.org/plugin/org.jmailen.kotlinter
jacoco = "0.8.14" # https://www.eclemma.org/jacoco
java = "17"
kotlin = "2.4.0" # https://kotlinlang.org/docs/releases.html#release-details
dokka = "2.2.0" # https://mvnrepository.com/artifact/org.jetbrains.dokka/dokka-gradle-plugin
kotlinter = "5.5.0" # https://plugins.gradle.org/plugin/org.jmailen.kotlinter
jacoco = "0.8.15" # https://www.eclemma.org/jacoco

# Standard Library versions
commons-lang3 = "3.20.0" # https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
kotlin-coroutines = "1.10.2" # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
ktor = "3.3.3" # https://mvnrepository.com/artifact/io.ktor/ktor-client-cio
okHttp = "5.3.2" # https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
protobuf = "4.31.1" # https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
protobuf-gradle = "0.9.5" # https://mvnrepository.com/artifact/com.google.protobuf/protobuf-gradle-plugin
kotlin-coroutines = "1.11.0" # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
ktor = "3.5.0" # https://mvnrepository.com/artifact/io.ktor/ktor-client-cio
okHttp = "5.4.0" # https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
protobuf = "4.35.1" # https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
protobuf-gradle = "0.10.0" # https://mvnrepository.com/artifact/com.google.protobuf/protobuf-gradle-plugin
publishPlugin = "2.0.0" # https://mvnrepository.com/artifact/io.github.gradle-nexus/publish-plugin
xz = "1.11" # https://mvnrepository.com/artifact/org.tukaani/xz
zstd = "1.5.7-6" # https://search.maven.org/artifact/com.github.luben/zstd-jni
xz = "1.12" # https://mvnrepository.com/artifact/org.tukaani/xz
zstd = "1.5.7-11" # https://search.maven.org/artifact/com.github.luben/zstd-jni

# Depot Downloader
kotlin-serialization-json = "1.9.0" # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-json-jvm
okio = "3.16.0" # https://mvnrepository.com/artifact/com.squareup.okio/okio
kotlin-serialization-json = "1.11.0" # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-json-jvm
okio = "3.17.0" # https://mvnrepository.com/artifact/com.squareup.okio/okio

# Testing Lib versions
commons-io = "2.21.0" # https://mvnrepository.com/artifact/commons-io/commons-io
commonsCodec = "1.20.0" # https://mvnrepository.com/artifact/commons-codec/commons-codec
coroutinesTest = "1.10.2" # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-test
junit5 = "5.13.4" # https://mvnrepository.com/artifact/org.junit/junit-bom
mockWebServer = "5.1.0" # https://mvnrepository.com/artifact/com.squareup.okhttp3/mockwebserver3-junit5
mockitoVersion = "5.18.0" # https://mvnrepository.com/artifact/org.mockito/mockito-core
commons-io = "2.22.0" # https://mvnrepository.com/artifact/commons-io/commons-io
commonsCodec = "1.22.0" # https://mvnrepository.com/artifact/commons-codec/commons-codec
coroutinesTest = "1.11.0" # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-test
junit5 = "6.1.0" # https://mvnrepository.com/artifact/org.junit/junit-bom
mockWebServer = "5.4.0" # https://mvnrepository.com/artifact/com.squareup.okhttp3/mockwebserver3-junit5
mockitoVersion = "5.23.0" # https://mvnrepository.com/artifact/org.mockito/mockito-core

# Samples
bouncyCastle = "1.83" # https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on
gson = "2.13.2" # https://mvnrepository.com/artifact/com.google.code.gson/gson
bouncyCastle = "1.84" # https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on
gson = "2.14.0" # https://mvnrepository.com/artifact/com.google.code.gson/gson
qrCode = "1.0.1" # https://mvnrepository.com/artifact/pro.leaco.qrcode/console-qrcode

[libraries]
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
6 changes: 3 additions & 3 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions gradlew.bat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ class CDNClientPool(
}
}

/**
* Releases all resources held by this pool. Clears the server list and nulls out the CDN client.
* After closing, [getConnection] will throw [IllegalStateException].
*/
override fun close() {
logger?.debug("Closing...")

Expand All @@ -65,6 +69,13 @@ class CDNClientPool(
logger = null
}

/**
* Fetches the current CDN server list from Steam and resets the round-robin index.
* Servers are filtered to those eligible for [appId] and sorted by weighted load.
* Must be called before [getConnection]. Throws if no servers are returned.
* @param maxNumServers Optional cap on the number of servers to request. Null requests the default amount.
* @throws Exception if Steam returns an empty server list.
*/
@Throws(Exception::class)
suspend fun updateServerList(maxNumServers: Int? = null) = mutex.withLock {
val serversForSteamPipe = steamSession.steamContent!!.getServersForSteamPipe(
Expand Down Expand Up @@ -96,9 +107,18 @@ class CDNClientPool(
}
}

/** Returns true if the pool contains at least one server. */
fun hasServers(): Boolean = servers.get().isNotEmpty()

/**
* Returns the next server in round-robin order.
* @throws IllegalStateException if the server list is empty.
*/
fun getConnection(): Server {
val servers = servers.get()

if (servers.isEmpty()) throw IllegalStateException("No CDN servers available")

val index = nextServer.getAndIncrement()
val server = servers[index % servers.size]

Expand All @@ -107,6 +127,10 @@ class CDNClientPool(
return server
}

/**
* Returns a successfully used [server] to the pool.
* Call this after a chunk or manifest download completes without error.
*/
fun returnConnection(server: Server?) {
if (server == null) {
logger?.error("null server returned to cdn pool.")
Expand All @@ -118,6 +142,22 @@ class CDNClientPool(
// (SK) nothing to do, maybe remove from ContentServerPenalty?
}

/**
* Transiently skips [server] by advancing the round-robin index.
* Use for recoverable failures (HTTP 5xx, timeouts) where the server may succeed later.
*/
fun skipConnection(server: Server?) {
if (server == null) return

logger?.debug("Skipping connection: $server")

nextServer.incrementAndGet()
}

/**
* Permanently removes [server] from the pool.
* Use only for unrecoverable failures (e.g. DNS resolution failure) where the host is unreachable.
*/
fun returnBrokenConnection(server: Server?) {
if (server == null) {
logger?.error("null broken server returned to pool")
Expand All @@ -126,13 +166,11 @@ class CDNClientPool(

logger?.debug("Returning broken connection: $server")

val servers = servers.get()
val currentIndex = nextServer.get()

if (servers.isNotEmpty() && servers[currentIndex % servers.size] == server) {
nextServer.incrementAndGet()

// TODO: (SK) Add server to ContentServerPenalty
val updated = servers.updateAndGet { it.filter { s -> s != server } }
if (updated.isEmpty()) {
logger?.error("No CDN servers remaining after removing broken connection: $server")
}

// TODO: (SK) Add server to ContentServerPenalty
}
}
Loading
Loading