Skip to content
Open
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
11 changes: 7 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ okio = "3.16.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
Expand All @@ -52,15 +53,16 @@ xz = { module = "org.tukaani:xz", version.ref = "xz" }
zstd = { module = "com.github.luben:zstd-jni", version.ref = "zstd" }

# Depot Downloader
kotlin-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlin-serialization-json"}
okio = { module = "com.squareup.okio:okio", version.ref = "okio"}
kotlin-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlin-serialization-json" }
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }

# Tests
test-commons-codec = { module = "commons-codec:commons-codec", version.ref = "commonsCodec" }
test-commons-io = { module = "commons-io:commons-io", version.ref = "commons-io" }
test-mock-core = { module = "org.mockito:mockito-core", version.ref = "mockitoVersion" }
test-mock-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockitoVersion" }
test-mock-webserver3 = { module = "com.squareup.okhttp3:mockwebserver3-junit5", version.ref = "mockWebServer" }
tests-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutinesTest" }
tests-junit-bom = { module = "org.junit:junit-bom", version.ref = "junit5" }
tests-junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" }
tests-junit-platform = { module = "org.junit.platform:junit-platform-launcher" }
Expand All @@ -81,14 +83,15 @@ protobuf-gradle = { id = "com.google.protobuf", version.ref = "protobuf-gradle"
[bundles]
testing = [
"bouncyCastle",
"zstd",
"xz",
"test-commons-codec",
"test-commons-io",
"test-mock-core",
"test-mock-jupiter",
"test-mock-webserver3",
"tests-coroutines",
"tests-junit-jupiter",
"xz",
"zstd",
]

ktor = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ package `in`.dragonbra.javasteam.steam.handlers.steamgameserver
* @param token Gets or sets the authentication token used to log in as a game server.
* @param appID Gets or sets the AppID this gameserver will serve.
*/
data class LogOnDetails(var token: String?, var appID: Int)
data class LogOnDetails(var token: String, var appID: Int)
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ import `in`.dragonbra.javasteam.steam.handlers.steamgameserver.callback.TicketAu
import `in`.dragonbra.javasteam.steam.handlers.steamuser.callback.LoggedOnCallback
import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg
import `in`.dragonbra.javasteam.steam.steamclient.callbacks.DisconnectedCallback
import `in`.dragonbra.javasteam.types.AsyncJobSingle
import `in`.dragonbra.javasteam.types.SteamID
import `in`.dragonbra.javasteam.util.HardwareUtils
import `in`.dragonbra.javasteam.util.NetHelpers
import `in`.dragonbra.javasteam.util.NetHelpers.obfuscatePrivateIP
import `in`.dragonbra.javasteam.util.Utils
import `in`.dragonbra.javasteam.util.obfuscatePrivateIP
import java.net.Inet6Address

/**
Expand All @@ -34,26 +37,27 @@ class SteamGameServer : ClientMsgHandler() {
* Logs onto the Steam network as a persistent game server.
* The client should already have been connected at this point.
* Results are return in a [LoggedOnCallback].
*
* @param details The details to use for logging on.
* @return The Job ID of the request. This can be used to fine the appropriate [LoggedOnCallback]
* @throws IllegalArgumentException token is not set within [details].
*/
fun logOn(details: LogOnDetails) {
require(!details.token.isNullOrEmpty()) { "LogOn requires a game server token to be set in 'details'." }
@Throws(IllegalArgumentException::class)
fun logOn(details: LogOnDetails): AsyncJobSingle<LoggedOnCallback> {
require(!details.token.isBlank()) { "LogOn requires a game server token to be set in 'details'." }

val logon = ClientMsgProtobuf<CMsgClientLogon.Builder>(CMsgClientLogon::class.java, EMsg.ClientLogonGameServer)

if (!client.isConnected) {
LoggedOnCallback(EResult.NoConnection).also(client::postCallback)
return
client.postCallback(LoggedOnCallback(EResult.NoConnection, logon.sourceJobID))
return AsyncJobSingle(client, logon.sourceJobID)
}

val logon = ClientMsgProtobuf<CMsgClientLogon.Builder>(CMsgClientLogon::class.java, EMsg.ClientLogonGameServer)

val gsId = SteamID(0, 0, client.universe, EAccountType.GameServer)

logon.protoHeader.clientSessionid = 0
logon.protoHeader.steamid = gsId.convertToUInt64()

val localIp: CMsgIPAddress = NetHelpers.getMsgIPAddress(client.localIP!!)
logon.body.obfuscatedPrivateIp = NetHelpers.obfuscatePrivateIP(localIp)
logon.body.obfuscatedPrivateIp = NetHelpers.getMsgIPAddress(client.localIP!!).obfuscatePrivateIP()

logon.body.protocolVersion = MsgClientLogon.CurrentProtocol

Expand All @@ -64,6 +68,8 @@ class SteamGameServer : ClientMsgHandler() {
logon.body.gameServerToken = details.token

client.send(logon)

return AsyncJobSingle(client, logon.sourceJobID)
}

/**
Expand All @@ -72,23 +78,23 @@ class SteamGameServer : ClientMsgHandler() {
* Results are return in a [LoggedOnCallback].
*
* @param appId The AppID served by this game server, or 0 for the default.
* @return The Job ID of the request. This can be used to fine the appropriate [LoggedOnCallback]
*/
@JvmOverloads
fun logOnAnonymous(appId: Int = 0) {
fun logOnAnonymous(appId: Int = 0): AsyncJobSingle<LoggedOnCallback> {
val logon = ClientMsgProtobuf<CMsgClientLogon.Builder>(CMsgClientLogon::class.java, EMsg.ClientLogonGameServer)

if (!client.isConnected) {
client.postCallback(LoggedOnCallback(EResult.NoConnection))
return
client.postCallback(LoggedOnCallback(EResult.NoConnection, logon.sourceJobID))
return AsyncJobSingle(client, logon.sourceJobID)
}

val logon = ClientMsgProtobuf<CMsgClientLogon.Builder>(CMsgClientLogon::class.java, EMsg.ClientLogonGameServer)

val gsId = SteamID(0, 0, client.universe, EAccountType.AnonGameServer)

logon.protoHeader.clientSessionid = 0
logon.protoHeader.steamid = gsId.convertToUInt64()

val localIp: CMsgIPAddress = NetHelpers.getMsgIPAddress(client.localIP!!)
logon.body.obfuscatedPrivateIp = NetHelpers.obfuscatePrivateIP(localIp)
logon.body.obfuscatedPrivateIp = NetHelpers.getMsgIPAddress(client.localIP!!).obfuscatePrivateIP()

logon.body.protocolVersion = MsgClientLogon.CurrentProtocol

Expand All @@ -97,6 +103,8 @@ class SteamGameServer : ClientMsgHandler() {
logon.body.machineId = ByteString.copyFrom(HardwareUtils.getMachineID())

client.send(logon)

return AsyncJobSingle(client, logon.sourceJobID)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import `in`.dragonbra.javasteam.steam.handlers.steamuser.callback.WalletInfoCall
import `in`.dragonbra.javasteam.steam.handlers.steamuser.callback.WebAPIUserNonceCallback
import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg
import `in`.dragonbra.javasteam.steam.steamclient.callbacks.DisconnectedCallback
import `in`.dragonbra.javasteam.types.AsyncJobSingle
import `in`.dragonbra.javasteam.types.SteamID
import `in`.dragonbra.javasteam.util.HardwareUtils
import `in`.dragonbra.javasteam.util.JavaSteamAddition
Expand Down Expand Up @@ -55,21 +56,23 @@ class SteamUser : ClientMsgHandler() {
* Logs the client into the Steam3 network.
* The client should already have been connected at this point.
* Results are returned in a [LoggedOnCallback].
*
* @param details The details to use for logging on.
* @return The Job ID of the request. This can be used to find the appropriate [LoggedOnCallback]/
* @throws IllegalArgumentException Username or password are not set within [details].
*/
fun logOn(details: LogOnDetails) {
@Throws(IllegalArgumentException::class)
fun logOn(details: LogOnDetails): AsyncJobSingle<LoggedOnCallback> {
if (details.username.isEmpty() || (details.password.isNullOrEmpty() && details.accessToken.isNullOrEmpty())) {
throw IllegalArgumentException("LogOn requires a username and password or access token to be set in 'details'.")
}

val logon = ClientMsgProtobuf<CMsgClientLogon.Builder>(CMsgClientLogon::class.java, EMsg.ClientLogon)

if (!client.isConnected) {
client.postCallback(LoggedOnCallback(EResult.NoConnection))
return
client.postCallback(LoggedOnCallback(EResult.NoConnection, logon.sourceJobID))
return AsyncJobSingle(client, logon.sourceJobID)
}

val logon = ClientMsgProtobuf<CMsgClientLogon.Builder>(CMsgClientLogon::class.java, EMsg.ClientLogon)

val steamID = SteamID(details.accountID, details.accountInstance, client.universe, EAccountType.Individual)

if (details.loginID != null) {
Expand Down Expand Up @@ -141,24 +144,26 @@ class SteamUser : ClientMsgHandler() {
}

client.send(logon)

return AsyncJobSingle(client, logon.sourceJobID)
}

/**
* Logs the client into the Steam3 network as an anonymous user.
* The client should already have been connected at this point.
* Results are returned in a [LoggedOnCallback].
*
* @param details The details to use for logging on.
* @return The Job ID of the request. This can be used to find the appropriate [LoggedOnCallback]
*/
@JvmOverloads
fun logOnAnonymous(details: AnonymousLogOnDetails = AnonymousLogOnDetails()) {
fun logOnAnonymous(details: AnonymousLogOnDetails = AnonymousLogOnDetails()): AsyncJobSingle<LoggedOnCallback> {
val logon = ClientMsgProtobuf<CMsgClientLogon.Builder>(CMsgClientLogon::class.java, EMsg.ClientLogon)

if (!client.isConnected) {
client.postCallback(LoggedOnCallback(EResult.NoConnection))
return
client.postCallback(LoggedOnCallback(EResult.NoConnection, logon.sourceJobID))
return AsyncJobSingle(client, logon.sourceJobID)
}

val logon = ClientMsgProtobuf<CMsgClientLogon.Builder>(CMsgClientLogon::class.java, EMsg.ClientLogon)

val auId = SteamID(0, 0, client.universe, EAccountType.AnonUser)

logon.protoHeader.clientSessionid = 0
Expand All @@ -172,6 +177,8 @@ class SteamUser : ClientMsgHandler() {
logon.body.machineId = ByteString.copyFrom(HardwareUtils.getMachineID())

client.send(logon)

return AsyncJobSingle(client, logon.sourceJobID)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ class LoggedOffCallback(packetMsg: IPacketMsg) : CallbackMsg() {
SteammessagesClientserverLogin.CMsgClientLoggedOff::class.java,
packetMsg
)
jobID = loggedOff.targetJobID
result = EResult.from(loggedOff.body.eresult)
} else {
val loggedOff = ClientMsg(MsgClientLoggedOff::class.java, packetMsg)
jobID = loggedOff.targetJobID
result = loggedOff.body.result
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesParentalObjec
import `in`.dragonbra.javasteam.steam.handlers.steamuser.LogOnDetails
import `in`.dragonbra.javasteam.steam.handlers.steamuser.SteamUser
import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg
import `in`.dragonbra.javasteam.types.JobID
import `in`.dragonbra.javasteam.types.SteamID
import `in`.dragonbra.javasteam.util.NetHelpers
import `in`.dragonbra.javasteam.util.log.LogManager
Expand Down Expand Up @@ -161,6 +162,7 @@ class LoggedOnCallback : CallbackMsg {
)
val resp = loginResp.body

jobID = loginResp.targetJobID
result = EResult.from(resp.eresult)
extendedResult = EResult.from(resp.eresultExtended)

Expand Down Expand Up @@ -203,14 +205,16 @@ class LoggedOnCallback : CallbackMsg {
clientInstanceId = resp.clientInstanceId
}

constructor(result: EResult) {
constructor(result: EResult, jobID: JobID) {
this.result = result
this.jobID = jobID
}

private fun handleNonProtoLogon(packetMsg: IPacketMsg) {
val loginResp = ClientMsg(MsgClientLogOnResponse::class.java, packetMsg)
val resp = loginResp.body

jobID = loginResp.targetJobID
result = resp.result

outOfGameSecsPerHeartbeat = resp.outOfGameHeartbeatRateSec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,43 @@ class SteamUserStats : ClientMsgHandler() {
return AsyncJobSingle(this.client, msg.sourceJobID)
}

/**
* Asks the Steam back-end for a set of rows for the specified users in the leaderboard.
* Results are returned in a [LeaderboardEntriesCallback].
* The returned [AsyncJobSingle] can also be awaited to retrieve the callback result.
* @param appId The AppID to request leaderboard rows for.
* @param id ID of the leaderboard to view.
* @param users The IDs of each user to request leaderboard rows for.
* @return The Job ID of the request. This can be used to find the appropriate [LeaderboardEntriesCallback].
*/
fun downloadLeaderboardEntriesForUsers(
appId: Int,
id: Int,
users: List<SteamID>,
): AsyncJobSingle<LeaderboardEntriesCallback> {
val msg = ClientMsgProtobuf<CMsgClientLBSGetLBEntries.Builder>(
CMsgClientLBSGetLBEntries::class.java,
EMsg.ClientLBSGetLBEntries
).apply {
sourceJobID = client.getNextJobID()

// routing_appid has to be set correctly to receive a response
protoHeader.routingAppid = appId

body.appId = appId
body.leaderboardId = id
body.leaderboardDataRequest = ELeaderboardDataRequest.Users.code()

users.forEach { steamID ->
body.addSteamids(steamID.convertToUInt64())
}
}

client.send(msg)

return AsyncJobSingle(client, msg.sourceJobID)
}

/**
* Gets the Stats-Schema for the specified app. This schema includes Global Achievements and Stats,
* @param appId The appID of the game.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import `in`.dragonbra.javasteam.steam.handlers.steamuserstats.SteamUserStats
import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg

/**
* This callback is fired in response to [SteamUserStats.getLeaderboardEntries].
* This callback is fired in response to [SteamUserStats.getLeaderboardEntries]
* and [SteamUserStats.downloadLeaderboardEntriesForUsers].
*/
@Suppress("MemberVisibilityCanBePrivate")
class LeaderboardEntriesCallback(packetMsg: IPacketMsg) : CallbackMsg() {
Expand Down
Loading
Loading