From 083285c309e57b8fabdbe092f47e0cc45e1f80af Mon Sep 17 00:00:00 2001 From: LossyDragon Date: Thu, 18 Dec 2025 16:29:26 -0600 Subject: [PATCH] Implement client communication for remote control of another steam client. Add missing rpc methods found in ContentServerDirectory. --- .../generators/rpc/parser/ProtoParser.kt | 15 +- .../AllClientLogonInfo.kt | 21 ++ .../AllClientLogonInfoSession.kt | 39 +++ .../steamclientcommunication/ClientAppList.kt | 33 ++ .../ClientAppListAppData.kt | 93 +++++ .../ClientAppListDlcData.kt | 24 ++ .../steamclientcommunication/ClientInfo.kt | 45 +++ .../ClientLogonInfo.kt | 13 + .../InstalledAppsFilter.kt | 16 + .../steamclientcommunication/RunningGames.kt | 24 ++ .../SteamClientCommunication.kt | 326 ++++++++++++++++++ .../handlers/steamcontent/DepotPatchInfo.kt | 18 + .../steamcontent/GetPeerContentInfo.kt | 14 + .../steamcontent/RequestPeerContentServer.kt | 24 ++ .../handlers/steamcontent/SteamContent.kt | 129 +++++++ .../steamuser/callback/LoggedOnCallback.kt | 9 + .../steam/steamclient/SteamClient.kt | 20 ++ .../protobufs/webui/service_clientcomm.proto | 175 ++++++++++ .../javasteam/rpc/UnifiedInterfaceTest.kt | 8 +- 19 files changed, 1039 insertions(+), 7 deletions(-) create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/AllClientLogonInfo.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/AllClientLogonInfoSession.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppList.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppListAppData.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppListDlcData.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientInfo.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientLogonInfo.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/InstalledAppsFilter.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/RunningGames.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/SteamClientCommunication.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/DepotPatchInfo.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/GetPeerContentInfo.kt create mode 100644 src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/RequestPeerContentServer.kt create mode 100644 src/main/proto/in/dragonbra/javasteam/protobufs/webui/service_clientcomm.proto diff --git a/buildSrc/src/main/kotlin/in/dragonbra/generators/rpc/parser/ProtoParser.kt b/buildSrc/src/main/kotlin/in/dragonbra/generators/rpc/parser/ProtoParser.kt index 0d7ed77c..af0264ba 100644 --- a/buildSrc/src/main/kotlin/in/dragonbra/generators/rpc/parser/ProtoParser.kt +++ b/buildSrc/src/main/kotlin/in/dragonbra/generators/rpc/parser/ProtoParser.kt @@ -14,6 +14,7 @@ class ProtoParser(private val outputDir: File) { private val suppressAnnotation = AnnotationSpec .builder(Suppress::class) .addMember("%S", "KDocUnresolvedReference") // IntelliJ's seems to get confused with canonical names + .addMember("%S", "RemoveRedundantQualifierName") // Full Qualifier names are fine .addMember("%S", "RedundantVisibilityModifier") // KotlinPoet is an explicit API generator .addMember("%S", "unused") // All methods could be used. .build() @@ -85,6 +86,8 @@ class ProtoParser(private val outputDir: File) { private fun buildClass(file: File, service: Service) { val protoFileName = transformProtoFileName(file.name) + val parentPathName = file.parentFile.name + // Class Builder val steamUnifiedMessagesClassName = ClassName( "in.dragonbra.javasteam.steam.handlers.steamunifiedmessages", @@ -129,7 +132,7 @@ class ProtoParser(private val outputDir: File) { // HAS Response numResponse++ val className = ClassName( - packageName = "in.dragonbra.javasteam.protobufs.steamclient.$protoFileName", + packageName = "in.dragonbra.javasteam.protobufs.$parentPathName.$protoFileName", method.responseType ) responseBlock.addStatement( @@ -141,7 +144,7 @@ class ProtoParser(private val outputDir: File) { // NO Response numNotification++ val className = ClassName( - packageName = "in.dragonbra.javasteam.protobufs.steamclient.$protoFileName", + packageName = "in.dragonbra.javasteam.protobufs.$parentPathName.$protoFileName", method.requestType ) notificationBlock.addStatement( @@ -192,7 +195,7 @@ class ProtoParser(private val outputDir: File) { .addParameter( "request", ClassName( - packageName = "in.dragonbra.javasteam.protobufs.steamclient.$protoFileName", + packageName = "in.dragonbra.javasteam.protobufs.$parentPathName.$protoFileName", method.requestType ) ) @@ -207,14 +210,14 @@ class ProtoParser(private val outputDir: File) { packageName = "in.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback", "ServiceMethodResponse" ).parameterizedBy( - ClassName.bestGuess("in.dragonbra.javasteam.protobufs.steamclient.$protoFileName.${method.responseType}.Builder") + ClassName.bestGuess("in.dragonbra.javasteam.protobufs.$parentPathName.$protoFileName.${method.responseType}.Builder") ) ) ) funcBuilder.addStatement( format = "return unifiedMessages!!.sendMessage(\n%T.Builder::class.java,\n%S,\nrequest\n)", ClassName( - packageName = "in.dragonbra.javasteam.protobufs.steamclient.$protoFileName", + packageName = "in.dragonbra.javasteam.protobufs.$parentPathName.$protoFileName", method.responseType ), "${service.name}.${method.methodName}#1" @@ -223,7 +226,7 @@ class ProtoParser(private val outputDir: File) { funcBuilder.addStatement( format = "unifiedMessages!!.sendNotification<%T.Builder>(\n%S,\nrequest\n)", ClassName( - packageName = "in.dragonbra.javasteam.protobufs.steamclient.$protoFileName", + packageName = "in.dragonbra.javasteam.protobufs.$parentPathName.$protoFileName", method.requestType ), "${service.name}.${method.methodName}#1" diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/AllClientLogonInfo.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/AllClientLogonInfo.kt new file mode 100644 index 00000000..5094a2d9 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/AllClientLogonInfo.kt @@ -0,0 +1,21 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * Information about all active Steam clients on the network. + * @param sessions A list of active client sessions with their logon information. + * @param refetchIntervalSec The recommended interval in seconds to refetch this data. + */ +@JavaSteamAddition +data class AllClientLogonInfo( + val sessions: List, + val refetchIntervalSec: Int, +) { + override fun toString(): String = """ + AllClientLogonInfo( + sessions=$sessions, + refetchIntervalSec=$refetchIntervalSec + ) + """.trimIndent() +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/AllClientLogonInfoSession.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/AllClientLogonInfoSession.kt new file mode 100644 index 00000000..a330fbf3 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/AllClientLogonInfoSession.kt @@ -0,0 +1,39 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.enums.EGamingDeviceType +import `in`.dragonbra.javasteam.enums.EOSType +import `in`.dragonbra.javasteam.enums.ESteamRealm +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * Describes an active device running a Steam client. + * @param clientInstanceId Instance ID used to send remote signals to this client. + * @param protocolVersion Protocol version of the client. + * @param osName Name of the installed OS, such as "Linux 6.x". + * @param machineName Name of the device, such as "steamdeck". + * @param osType Type of installed OS, such as [EOSType].Linux6x. + * @param deviceType Type of the device, such as [EGamingDeviceType].StandardPC. + * @param realm Realm of the session. See [ESteamRealm]. + */ +@JavaSteamAddition +data class AllClientLogonInfoSession( + val clientInstanceId: Long, + val protocolVersion: Int, + val osName: String, + val machineName: String, + val osType: EOSType, + val deviceType: EGamingDeviceType, + val realm: ESteamRealm, +) { + override fun toString(): String = """ + AllClientLogonInfoSession( + clientInstanceId=$clientInstanceId, + protocolVersion=$protocolVersion, + osName='$osName', + machineName='$machineName', + osType=$osType, + deviceType=$deviceType, + realm=$realm + ) + """.trimIndent() +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppList.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppList.kt new file mode 100644 index 00000000..01fbd8a5 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppList.kt @@ -0,0 +1,33 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * Response containing the list of applications on a Steam client and related metadata. + * @param bytesAvailable Total bytes of storage available on the client. + * @param apps List of application data for each app on the client. + * @param clientInfo Information about the client machine and state. + * @param refetchIntervalSecFull Recommended interval in seconds to refetch the complete app list. + * @param refetchIntervalSecChanging Recommended interval in seconds to refetch when apps are changing state. + * @param refetchIntervalSecUpdating Recommended interval in seconds to refetch when apps are updating. + */ +@JavaSteamAddition +data class ClientAppList( + val bytesAvailable: Long, + val apps: List, + val clientInfo: ClientInfo, + val refetchIntervalSecFull: Int, + val refetchIntervalSecChanging: Int, + val refetchIntervalSecUpdating: Int, +) { + override fun toString(): String = """ + ClientAppList( + bytesAvailable=$bytesAvailable, + apps=$apps, + clientInfo=$clientInfo, + refetchIntervalSecFull=$refetchIntervalSecFull, + refetchIntervalSecChanging=$refetchIntervalSecChanging, + refetchIntervalSecUpdating=$refetchIntervalSecUpdating + ) + """.trimIndent() +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppListAppData.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppListAppData.kt new file mode 100644 index 00000000..4af2ed7f --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppListAppData.kt @@ -0,0 +1,93 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * Detailed information about an application on a Steam client, including its download/update state. + * @param appid The application ID. + * @param app The application name. + * @param category The application category. + * @param appType The type of application (e.g., game, tool, DLC). + * @param numDownloading Number of items currently downloading for this app. + * @param bytesDownloadRate Current download speed in bytes per second. + * @param bytesDownloaded Total bytes already downloaded. + * @param bytesToDownload Total bytes that need to be downloaded. + * @param dlcs List of DLC data associated with this application. + * @param favorite Whether this app is marked as a favorite. + * @param autoUpdate Whether automatic updates are enabled for this app. + * @param installed Whether the app is currently installed. + * @param downloadPaused Whether the download is currently paused. + * @param changing Whether the app is currently changing state (installing/updating/uninstalling). + * @param availableOnPlatform Whether the app is available on the current platform. + * @param bytesStaged Bytes that have been staged for installation. + * @param bytesToStage Total bytes that need to be staged. + * @param bytesRequired Total disk space required for the app. + * @param sourceBuildId The current build ID installed. + * @param targetBuildId The build ID being updated to. + * @param estimatedSecondsRemaining Estimated time remaining for the current operation in seconds. + * @param queuePosition Position in the download queue (-1 if not queued). + * @param uninstalling Whether the app is currently being uninstalled. + * @param rtTimeScheduled Scheduled time for the update/download (Unix timestamp). + * @param running Whether the app is currently running. + * @param updatePercentage Progress percentage of the current update/download operation. + */ +@JavaSteamAddition +data class ClientAppListAppData( + val appid: Int, + val app: String, + val category: String, + val appType: String, + val numDownloading: Int, + val bytesDownloadRate: Int, + val bytesDownloaded: Long, + val bytesToDownload: Long, + val dlcs: List, + val favorite: Boolean, + val autoUpdate: Boolean, + val installed: Boolean, + val downloadPaused: Boolean, + val changing: Boolean, + val availableOnPlatform: Boolean, + val bytesStaged: Long, + val bytesToStage: Long, + val bytesRequired: Long, + val sourceBuildId: Int, + val targetBuildId: Int, + val estimatedSecondsRemaining: Int, + val queuePosition: Int, + val uninstalling: Boolean, + val rtTimeScheduled: Int, + val running: Boolean, + val updatePercentage: Int, +) { + override fun toString(): String = """ + ClientAppListAppData( + appid=$appid, + app='$app', + category='$category', + appType='$appType', + numDownloading=$numDownloading, + bytesDownloadRate=$bytesDownloadRate, + bytesDownloaded=$bytesDownloaded, + bytesToDownload=$bytesToDownload, + dlcs=$dlcs, + favorite=$favorite, + autoUpdate=$autoUpdate, + installed=$installed, + downloadPaused=$downloadPaused, + changing=$changing, + availableOnPlatform=$availableOnPlatform, + bytesStaged=$bytesStaged, + bytesToStage=$bytesToStage, + bytesRequired=$bytesRequired, + sourceBuildId=$sourceBuildId, + targetBuildId=$targetBuildId, + estimatedSecondsRemaining=$estimatedSecondsRemaining, + queuePosition=$queuePosition, + uninstalling=$uninstalling, + rtTimeScheduled=$rtTimeScheduled, + running=$running, + updatePercentage=$updatePercentage + ) + """.trimIndent() +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppListDlcData.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppListDlcData.kt new file mode 100644 index 00000000..631fcc58 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientAppListDlcData.kt @@ -0,0 +1,24 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * Information about a DLC (Downloadable Content) associated with an application. + * @param appId The DLC's application ID. + * @param app The DLC's name. + * @param installed Installation status (0 = not installed, non-zero = installed). + */ +@JavaSteamAddition +data class ClientAppListDlcData( + val appId: Int, + val app: String, + val installed: Int, +) { + override fun toString(): String = """ + ClientAppListDlcData( + appId=$appId, + app='$app', + installed=$installed + ) + """.trimIndent() +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientInfo.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientInfo.kt new file mode 100644 index 00000000..b391fbf2 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientInfo.kt @@ -0,0 +1,45 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * Information about a Steam client machine and its current state. + * @param packageVersion The Steam client package version. + * @param os The operating system of the client. + * @param machineName The name of the client machine. + * @param ipPublic The client's public IP address. + * @param ipPrivate The client's private/local IP address. + * @param bytesAvailable Available disk space in bytes. + * @param runningGames List of games currently running on the client. + * @param protocolVersion The protocol version being used. + * @param clientCommVersion The client communication protocol version. + * @param localUsers List of local user IDs logged into the client. + */ +@JavaSteamAddition +data class ClientInfo( + val packageVersion: Int, + val os: String, + val machineName: String, + val ipPublic: String, + val ipPrivate: String, + val bytesAvailable: Long, + val runningGames: List, + val protocolVersion: Int, + val clientCommVersion: Int, + val localUsers: List, +) { + override fun toString(): String = """ + ClientInfo( + packageVersion=$packageVersion, + os='$os', + machineName='$machineName', + ipPublic='$ipPublic', + ipPrivate='$ipPrivate', + bytesAvailable=$bytesAvailable, + runningGames=$runningGames, + protocolVersion=$protocolVersion, + clientCommVersion=$clientCommVersion, + localUsers=$localUsers + ) + """.trimIndent() +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientLogonInfo.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientLogonInfo.kt new file mode 100644 index 00000000..95fcdb69 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/ClientLogonInfo.kt @@ -0,0 +1,13 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +/** + * Logon information for a specific Steam client. + * @param protocolVersion Protocol version of the client. + * @param os Operating system name. + * @param machineName Name of the client machine. + */ +data class ClientLogonInfo( + val protocolVersion: Int, + val os: String, + val machineName: String, +) diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/InstalledAppsFilter.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/InstalledAppsFilter.kt new file mode 100644 index 00000000..47306ae6 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/InstalledAppsFilter.kt @@ -0,0 +1,16 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +@JavaSteamAddition +enum class InstalledAppsFilter { + /** + * Return everything, including not installed apps + */ + None, + + /** + * Return only apps that are "in progress" - downloading, updating, scheduled + */ + Changing, +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/RunningGames.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/RunningGames.kt new file mode 100644 index 00000000..5274a34b --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/RunningGames.kt @@ -0,0 +1,24 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * Information about a game currently running on a Steam client. + * @param appId The application ID of the running game. + * @param extraInfo Additional information about the running game. + * @param timeRunningSec Duration in seconds that the game has been running. + */ +@JavaSteamAddition +data class RunningGames( + val appId: Int, + val extraInfo: String, + val timeRunningSec: Int, +) { + override fun toString(): String = """ + RunningGames( + appId=$appId, + extraInfo='$extraInfo', + timeRunningSec=$timeRunningSec + ) + """.trimIndent() +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/SteamClientCommunication.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/SteamClientCommunication.kt new file mode 100644 index 00000000..deb5c795 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamclientcommunication/SteamClientCommunication.kt @@ -0,0 +1,326 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamclientcommunication + +import `in`.dragonbra.javasteam.base.IPacketMsg +import `in`.dragonbra.javasteam.enums.EGamingDeviceType +import `in`.dragonbra.javasteam.enums.EOSType +import `in`.dragonbra.javasteam.enums.EResult +import `in`.dragonbra.javasteam.enums.ESteamRealm +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_EnableOrDisableDownloads_Request +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_GetAllClientLogonInfo_Request +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_GetClientAppList_Request +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_GetClientInfo_Request +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_GetClientLogonInfo_Request +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_InstallClientApp_Request +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_LaunchClientApp_Request +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_SetClientAppUpdateState_Request +import `in`.dragonbra.javasteam.protobufs.webui.ServiceClientcomm.CClientComm_UninstallClientApp_Request +import `in`.dragonbra.javasteam.rpc.service.ClientComm +import `in`.dragonbra.javasteam.steam.handlers.ClientMsgHandler +import `in`.dragonbra.javasteam.steam.handlers.steamunifiedmessages.SteamUnifiedMessages +import `in`.dragonbra.javasteam.util.JavaSteamAddition +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async + +/** + * Allows controlling of other running Steam clients. + */ +@Suppress("unused") +@JavaSteamAddition +class SteamClientCommunication : ClientMsgHandler() { + + private val clientComm: ClientComm by lazy { + val unifiedMessages = client.getHandler() + ?: throw NullPointerException("Unable to get SteamUnifiedMessages handler") + unifiedMessages.createService() + } + + /** + * Retrieves information about all active Steam clients connected to the network. + * Note: This returns all connected clients. Filter the results based on OS or device type as needed. + * @return Information about all active client sessions with recommended refetch interval. + */ + fun getAllClientLogonInfo(): Deferred = client.defaultScope.async { + val request = CClientComm_GetAllClientLogonInfo_Request.newBuilder().build() + + val message = clientComm.getAllClientLogonInfo(request).await() + val response = message.body.build() + + return@async AllClientLogonInfo( + sessions = response.sessionsList.map { + AllClientLogonInfoSession( + clientInstanceId = it.clientInstanceid, + protocolVersion = it.protocolVersion, + osName = it.osName, + machineName = it.machineName, + osType = EOSType.from(it.osType), + deviceType = EGamingDeviceType.from(it.deviceType), + realm = ESteamRealm.from(it.realm), + ) + }, + refetchIntervalSec = response.refetchIntervalSec, + ) + } + + /** + * Return the list of applications of the remote device. + * This is not the list of downloaded apps, but the whole "available" list with a flag indicating about it being downloaded. + * @param remoteId The remote session ID of the client to query. + * @param filters filters to choose, see [InstalledAppsFilter]. + * @param language language for localized app names (e.g., "english", "french"). + */ + @JvmOverloads + fun getClientAppList( + remoteId: Long, + filters: InstalledAppsFilter = InstalledAppsFilter.None, + language: String = "english", + ): Deferred = client.defaultScope.async { + val request = CClientComm_GetClientAppList_Request.newBuilder().apply { + this.clientInstanceid = remoteId + this.language = language + this.includeClientInfo = true + this.fields = "games" + this.filters = when (filters) { + InstalledAppsFilter.None -> "none" + InstalledAppsFilter.Changing -> "changing" + } + }.build() + + val message = clientComm.getClientAppList(request).await() + val response = message.body.build() + + return@async ClientAppList( + bytesAvailable = response.bytesAvailable, + apps = response.appsList.map { app -> + ClientAppListAppData( + appid = app.appid, + app = app.app, + category = app.category, + appType = app.appType, + numDownloading = app.numDownloading, + bytesDownloadRate = app.bytesDownloadRate, + bytesDownloaded = app.bytesDownloaded, + bytesToDownload = app.bytesToDownload, + dlcs = app.dlcsList.map { dlc -> + ClientAppListDlcData( + appId = dlc.appid, + app = dlc.app, + installed = dlc.installed, + ) + }, + favorite = app.favorite, + autoUpdate = app.autoUpdate, + installed = app.installed, + downloadPaused = app.downloadPaused, + changing = app.changing, + availableOnPlatform = app.availableOnPlatform, + bytesStaged = app.bytesStaged, + bytesToStage = app.bytesToStage, + bytesRequired = app.bytesRequired, + sourceBuildId = app.sourceBuildid, + targetBuildId = app.targetBuildid, + estimatedSecondsRemaining = app.estimatedSecondsRemaining, + queuePosition = app.queuePosition, + uninstalling = app.uninstalling, + rtTimeScheduled = app.rtTimeScheduled, + running = app.running, + updatePercentage = app.updatePercentage, + ) + }, + clientInfo = ClientInfo( + packageVersion = response.clientInfo.packageVersion, + os = response.clientInfo.os, + machineName = response.clientInfo.machineName, + ipPublic = response.clientInfo.ipPublic, + ipPrivate = response.clientInfo.ipPrivate, + bytesAvailable = response.clientInfo.bytesAvailable, + runningGames = response.clientInfo.runningGamesList.map { game -> + RunningGames( + appId = game.appid, + extraInfo = game.extraInfo, + timeRunningSec = game.timeRunningSec, + ) + }, + protocolVersion = response.clientInfo.protocolVersion, + clientCommVersion = response.clientInfo.clientcommVersion, + localUsers = response.clientInfo.localUsersList, + ), + refetchIntervalSecFull = response.refetchIntervalSecFull, + refetchIntervalSecChanging = response.refetchIntervalSecChanging, + refetchIntervalSecUpdating = response.refetchIntervalSecUpdating, + ) + } + + /** + * Adds the application to the remote installation queue. + * @param remoteId The remote session ID of the client to query. + * @param appId Application ID to install. + * @return **true** if successful, otherwise false. + */ + fun installClientApp(remoteId: Long, appId: Int): Deferred = client.defaultScope.async { + val request = CClientComm_InstallClientApp_Request.newBuilder().apply { + this.clientInstanceid = remoteId + this.appid = appId + }.build() + + val message = clientComm.installClientApp(request).await() + // Request has empty response. + + return@async message.result == EResult.OK + } + + /** + * Sets the update state of an app in remote installation queue. + * Action set to true will move the requested app to the top of the queue. + * @param remoteId The remote session ID of the client to query + * @param appId Application ID to update. + * @param action true to prioritize (move to top of queue), false otherwise. + * @return **true** if successful, otherwise false. + */ + @JvmOverloads + fun setClientAppUpdateState( + remoteId: Long, + appId: Int, + action: Boolean = false, + ): Deferred = + client.defaultScope.async { + val request = CClientComm_SetClientAppUpdateState_Request.newBuilder().apply { + this.clientInstanceid = remoteId + this.appid = appId + this.action = if (action) 1 else 0 + }.build() + + val message = clientComm.setClientAppUpdateState(request).await() + // Request has empty response. + + return@async message.result == EResult.OK + } + + /** + * Requests to uninstall the app from the device. + * @param remoteId The remote session ID of the client to query. + * @param appId Application ID to uninstall. + * @return **true** if successful, otherwise false. + */ + fun uninstallClientApp( + remoteId: Long, + appId: Int, + ): Deferred = client.defaultScope.async { + val request = CClientComm_UninstallClientApp_Request.newBuilder().apply { + this.clientInstanceid = remoteId + this.appid = appId + }.build() + + val message = clientComm.uninstallClientApp(request).await() + // Request has empty response. + + return@async message.result == EResult.OK + } + + /** + * Pauses or resumes downloads on the remote client. + * @param remoteId The remote session ID of the client to query. + * @param enable true to resume downloads, false to pause. + * @return **true** if successful, otherwise false. + */ + fun enableOrDisableDownloads( + remoteId: Long, + enable: Boolean, + ): Deferred = client.defaultScope.async { + val request = CClientComm_EnableOrDisableDownloads_Request.newBuilder().apply { + this.clientInstanceid = remoteId + this.enable = enable + }.build() + + val message = clientComm.enableOrDisableDownloads(request).await() + // Request has empty response. + + return@async message.result == EResult.OK + } + + /** + * Launches the application on the remote device. + * @param remoteId The remote session ID of the client to query. + * @param appId application ID to launch. + * @param parameters Optional launch parameters/query string (e.g., command line arguments). + * @return **true** if successful, otherwise false. + */ + @JvmOverloads + fun launchClientApp( + remoteId: Long, + appId: Int, + parameters: String? = null, + ): Deferred = client.defaultScope.async { + val request = CClientComm_LaunchClientApp_Request.newBuilder().apply { + this.clientInstanceid = remoteId + this.appid = appId + this.queryParams = parameters.orEmpty() + }.build() + + val message = clientComm.launchClientApp(request).await() + // Request has empty response. + + return@async message.result == EResult.OK + } + + /** + * Retrieves logon information for a specific Steam client. + * @param remoteId The remote session ID of the client to query. + * @return Client logon information including protocol version, OS, and machine name. + */ + fun getClientLogonInfo(remoteId: Long): Deferred = client.defaultScope.async { + val request = CClientComm_GetClientLogonInfo_Request.newBuilder().apply { + this.clientInstanceid = remoteId + }.build() + + val message = clientComm.getClientLogonInfo(request).await() + val response = message.body.build() + + return@async ClientLogonInfo( + protocolVersion = response.protocolVersion, + os = response.os, + machineName = response.machineName + ) + } + + /** + * Retrieves detailed information about a specific Steam client, including system info and running games. + * @param remoteId The remote session ID of the client to query. + * @return Detailed client information including hardware, network, and active games. + */ + fun getClientInfo(remoteId: Long): Deferred = client.defaultScope.async { + val request = CClientComm_GetClientInfo_Request.newBuilder().apply { + this.clientInstanceid = remoteId + }.build() + + val message = clientComm.getClientInfo(request).await() + val response = message.body.build() + + return@async ClientInfo( + packageVersion = response.clientInfo.packageVersion, + os = response.clientInfo.os, + machineName = response.clientInfo.machineName, + ipPublic = response.clientInfo.ipPublic, + ipPrivate = response.clientInfo.ipPrivate, + bytesAvailable = response.clientInfo.bytesAvailable, + runningGames = response.clientInfo.runningGamesList.map { game -> + RunningGames( + appId = game.appid, + extraInfo = game.extraInfo, + timeRunningSec = game.timeRunningSec, + ) + }, + protocolVersion = response.clientInfo.protocolVersion, + clientCommVersion = response.clientInfo.clientcommVersion, + localUsers = response.clientInfo.localUsersList, + ) + } + + /** + * Handles a client message. This should not be called directly. + * + * @param packetMsg The packet message that contains the data. + */ + override fun handleMsg(packetMsg: IPacketMsg) { + // not used + } +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/DepotPatchInfo.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/DepotPatchInfo.kt new file mode 100644 index 00000000..693af07e --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/DepotPatchInfo.kt @@ -0,0 +1,18 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamcontent + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * Information about a depot patch between two manifest versions. + * @param isAvailable Whether a patch is available for this upgrade path. + * @param patchSize Size of the patch file in bytes (delta to download). + * @param patchedChunksSize Total size in bytes of content that will be patched/modified. + */ +@JavaSteamAddition +data class DepotPatchInfo( + val isAvailable: Boolean, + val patchSize: Long, + val patchedChunksSize: Long, +) { + override fun toString(): String = "DepotPatchInfo(isAvailable=$isAvailable, patchSize=$patchSize, patchedChunksSize=$patchedChunksSize)" +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/GetPeerContentInfo.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/GetPeerContentInfo.kt new file mode 100644 index 00000000..f96187e2 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/GetPeerContentInfo.kt @@ -0,0 +1,14 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamcontent + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * TODO kdoc + * @param appIds + * @param ipPublic + */ +@JavaSteamAddition +data class GetPeerContentInfo( + val appIds: List, + val ipPublic: String, +) diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/RequestPeerContentServer.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/RequestPeerContentServer.kt new file mode 100644 index 00000000..b0438d37 --- /dev/null +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/RequestPeerContentServer.kt @@ -0,0 +1,24 @@ +package `in`.dragonbra.javasteam.steam.handlers.steamcontent + +import `in`.dragonbra.javasteam.util.JavaSteamAddition + +/** + * TODO kdoc + * @param serverPort + * @param installedDepots + * @param accessToken + */ +@JavaSteamAddition +data class RequestPeerContentServer( + val serverPort: Int, + val installedDepots: List, + val accessToken: Long, +) { + override fun toString(): String = """ + RequestPeerContentServer( + serverPort=$serverPort, + installedDepots=$installedDepots, + accessToken=$accessToken + ) + """.trimIndent() +} diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/SteamContent.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/SteamContent.kt index 335530c3..7b065796 100644 --- a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/SteamContent.kt +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamcontent/SteamContent.kt @@ -1,15 +1,22 @@ package `in`.dragonbra.javasteam.steam.handlers.steamcontent import `in`.dragonbra.javasteam.base.IPacketMsg +import `in`.dragonbra.javasteam.enums.EResult import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesContentsystemSteamclient.CContentServerDirectory_GetCDNAuthToken_Request +import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesContentsystemSteamclient.CContentServerDirectory_GetDepotPatchInfo_Request import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesContentsystemSteamclient.CContentServerDirectory_GetManifestRequestCode_Request +import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesContentsystemSteamclient.CContentServerDirectory_GetPeerContentInfo_Request import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesContentsystemSteamclient.CContentServerDirectory_GetServersForSteamPipe_Request +import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesContentsystemSteamclient.CContentServerDirectory_RequestPeerContentServer_Request import `in`.dragonbra.javasteam.rpc.service.ContentServerDirectory import `in`.dragonbra.javasteam.steam.cdn.AuthToken import `in`.dragonbra.javasteam.steam.cdn.Server import `in`.dragonbra.javasteam.steam.handlers.ClientMsgHandler import `in`.dragonbra.javasteam.steam.handlers.steamunifiedmessages.SteamUnifiedMessages import `in`.dragonbra.javasteam.steam.webapi.ContentServerDirectoryService +import `in`.dragonbra.javasteam.util.JavaSteamAddition +import `in`.dragonbra.javasteam.util.log.LogManager +import `in`.dragonbra.javasteam.util.log.Logger import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.async @@ -17,7 +24,13 @@ import kotlinx.coroutines.async /** * This handler is used for interacting with content server directory on the Steam network. */ +@Suppress("unused") class SteamContent : ClientMsgHandler() { + + companion object { + private val logger: Logger = LogManager.getLogger(SteamContent::class.java) + } + private val contentService: ContentServerDirectory by lazy { val unifiedMessages = client.getHandler(SteamUnifiedMessages::class.java) ?: throw NullPointerException("Unable to get SteamUnifiedMessages handler") @@ -48,6 +61,47 @@ class SteamContent : ClientMsgHandler() { return@async ContentServerDirectoryService.convertServerList(response) } + /** + * Retrieves patch information for upgrading a depot from one manifest version to another. + * @param appId The application ID. + * @param depotId The depot ID to get patch info for. + * @param sourceManifestId The current manifest ID to upgrade from. + * @param targetManifestId The target manifest ID to upgrade to. + * @param parentScope Coroutine scope for the async operation. + * @return A [DepotPatchInfo] containing patch availability, patch file size, and total patched content size. + */ + @JavaSteamAddition + @JvmOverloads + fun getDepotPatchInfo( + appId: Int, + depotId: Int, + sourceManifestId: Long, + targetManifestId: Long, + parentScope: CoroutineScope = client.defaultScope, + ): Deferred = parentScope.async { + val request = CContentServerDirectory_GetDepotPatchInfo_Request.newBuilder().apply { + this.appid = appId + this.depotid = depotId + this.sourceManifestid = sourceManifestId + this.targetManifestid = targetManifestId + }.build() + + val message = contentService.getDepotPatchInfo(request).await() + val response = message.body.build() + + if (message.result != EResult.OK) { + logger.error("getDepotPatchInfo got ${message.result}") + } + + return@async DepotPatchInfo( + isAvailable = response.isAvailable, + patchSize = response.patchSize, + patchedChunksSize = response.patchedChunksSize + ) + } + + // RPC getClientUpdateHosts() is not applicable for JavaSteam. This is used for Steam's own updating stuff. + /** * Request the manifest request code for the specified arguments. * @@ -118,6 +172,81 @@ class SteamContent : ClientMsgHandler() { return@async AuthToken(message) } + /** + * TODO kdoc + * @param remoteClientId + * @param steamId + * @param serverRemoteClientId + * @param appId + * @param currentBuildId + * @return A [RequestPeerContentServer] + */ + @JavaSteamAddition + @JvmOverloads + fun requestPeerContentServer( + remoteClientId: Long, + steamId: Long, + serverRemoteClientId: Long, + appId: Int, + currentBuildId: Int = 0, + parentScope: CoroutineScope = client.defaultScope, + ): Deferred = parentScope.async { + val request = CContentServerDirectory_RequestPeerContentServer_Request.newBuilder().apply { + this.remoteClientId = remoteClientId + this.steamid = steamId + this.serverRemoteClientId = serverRemoteClientId + this.appId = appId + this.currentBuildId = currentBuildId + }.build() + + val message = contentService.requestPeerContentServer(request).await() + val response = message.body.build() + + if (message.result != EResult.OK) { + logger.error("requestPeerContentServer got ${message.result}") + } + + return@async RequestPeerContentServer( + serverPort = response.serverPort, + installedDepots = response.installedDepotsList, + accessToken = response.accessToken, + ) + } + + /** + * TODO kdoc + * @param remoteClientId + * @param steamId + * @param serverRemoteClientId + * @return A [GetPeerContentInfo] + */ + @JavaSteamAddition + @JvmOverloads + fun getPeerContentInfo( + remoteClientId: Long, + steamId: Long, + serverRemoteClientId: Long, + parentScope: CoroutineScope = client.defaultScope, + ): Deferred = parentScope.async { + val request = CContentServerDirectory_GetPeerContentInfo_Request.newBuilder().apply { + this.remoteClientId = remoteClientId + this.steamid = steamId + this.serverRemoteClientId = serverRemoteClientId + }.build() + + val message = contentService.getPeerContentInfo(request).await() + val response = message.body.build() + + if (message.result != EResult.OK) { + logger.error("getPeerContentInfo got ${message.result}") + } + + return@async GetPeerContentInfo( + appIds = response.appidsList, + ipPublic = response.ipPublic, + ) + } + /** * Handles a client message. This should not be called directly. * diff --git a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/LoggedOnCallback.kt b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/LoggedOnCallback.kt index 66b21e4b..84ab0712 100644 --- a/src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/LoggedOnCallback.kt +++ b/src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/LoggedOnCallback.kt @@ -136,6 +136,13 @@ class LoggedOnCallback : CallbackMsg { var familyGroupId: Long = 0L private set + /** + * Gets the client instance ID. + * This is used for P2P content sharing operations. + */ + var clientInstanceId: Long = 0L + private set + constructor(packetMsg: IPacketMsg) { if (!packetMsg.isProto) { handleNonProtoLogon(packetMsg) @@ -185,6 +192,8 @@ class LoggedOnCallback : CallbackMsg { } familyGroupId = resp.familyGroupId + + clientInstanceId = resp.clientInstanceId } constructor(result: EResult) { diff --git a/src/main/java/in/dragonbra/javasteam/steam/steamclient/SteamClient.kt b/src/main/java/in/dragonbra/javasteam/steam/steamclient/SteamClient.kt index 9ea0928b..8fbda3db 100644 --- a/src/main/java/in/dragonbra/javasteam/steam/steamclient/SteamClient.kt +++ b/src/main/java/in/dragonbra/javasteam/steam/steamclient/SteamClient.kt @@ -85,6 +85,7 @@ class SteamClient @JvmOverloads constructor( addHandlerCore(SteamContent()) addHandlerCore(SteamAuthTicket()) addHandlerCore(SteamNotifications()) // JavaSteam Addition + // addHandlerCore(SteamClientCommunication()) // JavaSteam Addition, not enabled by default if (handlers.size != HANDLERS_COUNT) { logger.error("Handlers size didnt match handlers count (${handlers.size}) when initializing") @@ -109,11 +110,30 @@ class SteamClient @JvmOverloads constructor( addHandlerCore(handler) } + /** + * Kotlin Helper: + * Adds a new handler to the internal list of message handlers. + * @param T The handler to add. + */ + inline fun addHandler() { + val handler = T::class.java.getDeclaredConstructor().newInstance() + addHandler(handler) + } + private fun addHandlerCore(handler: ClientMsgHandler) { handler.setup(this) handlers[handler.javaClass] = handler } + /** + * Kotlin Helper: + * Removes a registered handler by name. + * @param T The handler name to remove. + */ + inline fun removeHandler() { + removeHandler(T::class.java) + } + /** * Removes a registered handler by name. * @param handler The handler name to remove. diff --git a/src/main/proto/in/dragonbra/javasteam/protobufs/webui/service_clientcomm.proto b/src/main/proto/in/dragonbra/javasteam/protobufs/webui/service_clientcomm.proto new file mode 100644 index 00000000..73c6ec45 --- /dev/null +++ b/src/main/proto/in/dragonbra/javasteam/protobufs/webui/service_clientcomm.proto @@ -0,0 +1,175 @@ +option java_package = "in.dragonbra.javasteam.protobufs.webui"; + +option optimize_for = SPEED; +option java_generic_services = false; + +message CClientComm_ClientData { + optional uint32 package_version = 1; + optional string os = 2; + optional string machine_name = 3; + optional string ip_public = 4; + optional string ip_private = 5; + optional uint64 bytes_available = 6; + repeated .CClientComm_ClientData_RunningGames running_games = 7; + optional uint32 protocol_version = 8; + optional uint32 clientcomm_version = 9; + repeated uint32 local_users = 10; +} + +message CClientComm_ClientData_RunningGames { + optional uint32 appid = 1; + optional string extra_info = 2; + optional uint32 time_running_sec = 3; +} + +message CClientComm_EnableOrDisableDownloads_Request { + optional uint64 client_instanceid = 1; + optional bool enable = 2; +} + +message CClientComm_EnableOrDisableDownloads_Response { +} + +message CClientComm_GetAllClientLogonInfo_Request { +} + +message CClientComm_GetAllClientLogonInfo_Response { + repeated .CClientComm_GetAllClientLogonInfo_Response_Session sessions = 1; + optional uint32 refetch_interval_sec = 2; +} + +message CClientComm_GetAllClientLogonInfo_Response_Session { + optional uint64 client_instanceid = 1; + optional uint32 protocol_version = 2; + optional string os_name = 3; + optional string machine_name = 4; + optional int32 os_type = 5; + optional int32 device_type = 6; + optional int32 realm = 7; +} + +message CClientComm_GetClientAppList_Request { + optional string fields = 1; + optional string filters = 2; + optional uint64 client_instanceid = 3; + optional bool include_client_info = 4; + optional string language = 5; + repeated uint32 filter_appids = 6; +} + +message CClientComm_GetClientAppList_Response { + optional uint64 bytes_available = 1; + repeated .CClientComm_GetClientAppList_Response_AppData apps = 2; + optional .CClientComm_ClientData client_info = 3; + optional uint32 refetch_interval_sec_full = 4; + optional uint32 refetch_interval_sec_changing = 5; + optional uint32 refetch_interval_sec_updating = 6; +} + +message CClientComm_GetClientAppList_Response_AppData { + optional uint32 appid = 1; + optional string app = 2; + optional string category = 3; + optional string app_type = 4; + optional uint32 num_downloading = 8; + optional uint32 bytes_download_rate = 11; + optional uint64 bytes_downloaded = 12; + optional uint64 bytes_to_download = 13; + repeated .CClientComm_GetClientAppList_Response_AppData_DLCData dlcs = 17; + optional bool favorite = 18; + optional bool auto_update = 19; + optional bool installed = 20; + optional bool download_paused = 21; + optional bool changing = 22; + optional bool available_on_platform = 23; + optional uint64 bytes_staged = 24; + optional uint64 bytes_to_stage = 25; + optional uint64 bytes_required = 26; + optional uint32 source_buildid = 27; + optional uint32 target_buildid = 28; + optional uint32 estimated_seconds_remaining = 29; + optional int32 queue_position = 30 [default = -1]; + optional bool uninstalling = 31; + optional uint32 rt_time_scheduled = 32; + optional bool running = 33; + optional uint32 update_percentage = 34; +} + +message CClientComm_GetClientAppList_Response_AppData_DLCData { + optional uint32 appid = 1; + optional string app = 2; + optional uint32 installed = 3; +} + +message CClientComm_GetClientInfo_Request { + optional uint64 client_instanceid = 1; +} + +message CClientComm_GetClientInfo_Response { + optional .CClientComm_ClientData client_info = 1; +} + +message CClientComm_GetClientLogonInfo_Request { + optional uint64 client_instanceid = 1; +} + +message CClientComm_GetClientLogonInfo_Response { + optional uint32 protocol_version = 1; + optional string os = 2; + optional string machine_name = 3; +} + +message CClientComm_InstallClientApp_Request { + optional uint32 appid = 1; + optional uint64 client_instanceid = 2; +} + +message CClientComm_InstallClientApp_Response { +} + +message CClientComm_LaunchClientApp_Request { + optional uint64 client_instanceid = 1; + optional uint32 appid = 2; + optional string query_params = 3; +} + +message CClientComm_LaunchClientApp_Response { +} + +message CClientComm_SetClientAppUpdateState_Request { + optional uint32 appid = 1; + optional uint32 action = 2; + optional uint64 client_instanceid = 3; +} + +message CClientComm_SetClientAppUpdateState_Response { +} + +message CClientComm_UninstallClientApp_Request { + optional uint32 appid = 1; + optional uint64 client_instanceid = 2; +} + +message CClientComm_UninstallClientApp_Response { +} + +service ClientComm { + // ePrivilege=1 + rpc EnableOrDisableDownloads (.CClientComm_EnableOrDisableDownloads_Request) returns (.CClientComm_EnableOrDisableDownloads_Response); + // bConstMethod=true, ePrivilege=1 + rpc GetAllClientLogonInfo (.CClientComm_GetAllClientLogonInfo_Request) returns (.CClientComm_GetAllClientLogonInfo_Response); + // bConstMethod=true, ePrivilege=1 + rpc GetClientAppList (.CClientComm_GetClientAppList_Request) returns (.CClientComm_GetClientAppList_Response); + // bConstMethod=true, ePrivilege=1 + rpc GetClientInfo (.CClientComm_GetClientInfo_Request) returns (.CClientComm_GetClientInfo_Response); + // bConstMethod=true, ePrivilege=1 + rpc GetClientLogonInfo (.CClientComm_GetClientLogonInfo_Request) returns (.CClientComm_GetClientLogonInfo_Response); + // ePrivilege=1 + rpc InstallClientApp (.CClientComm_InstallClientApp_Request) returns (.CClientComm_InstallClientApp_Response); + // ePrivilege=1 + rpc LaunchClientApp (.CClientComm_LaunchClientApp_Request) returns (.CClientComm_LaunchClientApp_Response); + // ePrivilege=1 + rpc SetClientAppUpdateState (.CClientComm_SetClientAppUpdateState_Request) returns (.CClientComm_SetClientAppUpdateState_Response); + // ePrivilege=1 + rpc UninstallClientApp (.CClientComm_UninstallClientApp_Request) returns (.CClientComm_UninstallClientApp_Response); +} diff --git a/src/test/java/in/dragonbra/javasteam/rpc/UnifiedInterfaceTest.kt b/src/test/java/in/dragonbra/javasteam/rpc/UnifiedInterfaceTest.kt index 1397fd3e..6e9378a8 100644 --- a/src/test/java/in/dragonbra/javasteam/rpc/UnifiedInterfaceTest.kt +++ b/src/test/java/in/dragonbra/javasteam/rpc/UnifiedInterfaceTest.kt @@ -45,7 +45,11 @@ class UnifiedInterfaceTest { /** * Any changes to then number of interfaces would need to reflect here. Otherwise, the test should fail. */ - val knownServiceTypes = arrayOf( + val webuiServices = arrayOf( + "ClientComm.kt" + ) + + val standardServices = arrayOf( // "AccountLinking.kt", "Authentication.kt", "AuthenticationSupport.kt", @@ -81,5 +85,7 @@ class UnifiedInterfaceTest { "PublishedFile.kt", "PublishedFileClient.kt", ) + + val knownServiceTypes = webuiServices + standardServices } }