├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.yml ├── pull_request_template.md └── workflows │ ├── javasteam-build-pr.yml │ └── javasteam-build-push.yml ├── .gitignore ├── .run ├── javasteam [Generated Classes].run.xml ├── javasteam [_buildSrc_ktlintFormat].run.xml ├── javasteam [build].run.xml ├── javasteam [clean build].run.xml ├── javasteam [clean].run.xml ├── javasteam [formatKotlin].run.xml └── javasteam [wrapper --gradle-version 8.12].run.xml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── in │ └── dragonbra │ └── generators │ ├── rpc │ ├── RpcGenPlugin.kt │ ├── RpcGenTask.kt │ └── parser │ │ ├── ProtoParser.kt │ │ ├── Service.kt │ │ └── ServiceMethod.kt │ ├── steamlanguage │ ├── SteamLanguageGenPlugin.kt │ ├── SteamLanguageGenTask.kt │ ├── generator │ │ └── JavaGen.kt │ └── parser │ │ ├── LanguageParser.kt │ │ ├── node │ │ ├── ClassNode.kt │ │ ├── EnumNode.kt │ │ ├── Node.kt │ │ └── PropNode.kt │ │ ├── symbol │ │ ├── StrongSymbol.kt │ │ ├── Symbol.kt │ │ ├── SymbolLocator.kt │ │ └── WeakSymbol.kt │ │ └── token │ │ ├── Token.kt │ │ ├── TokenAnalyzer.kt │ │ └── TokenSourceInfo.kt │ ├── util │ └── JavaFileWriter.kt │ └── versions │ ├── VersionGenPlugin.kt │ ├── VersionGenTask.kt │ └── generator │ └── JavaGen.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── javasteam-cs ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── proto │ └── in │ └── dragonbra │ └── javasteam │ └── protobufs │ └── cs │ ├── base_gcmessages.proto │ ├── cstrike15_gcmessages.proto │ ├── econ_gcmessages.proto │ ├── engine_gcmessages.proto │ ├── gcsdk_gcmessages.proto │ ├── gcsystemmsgs.proto │ ├── netmessages.proto │ ├── network_connection.proto │ ├── networkbasetypes.proto │ └── steammessages.proto ├── javasteam-samples ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── java │ └── in │ └── dragonbra │ └── javasteamsamples │ ├── _000_authentication │ └── SampleLogonAuthentication.java │ ├── _001_authenticationwithqrcode │ └── SampleLogonQRAuthentication.java │ ├── _002_webcookie │ └── SampleWebCookie.java │ ├── _003_serverlist │ └── SampleServerList.java │ ├── _004_legacylogin │ └── SampleLogon.java │ ├── _005_legacysteamguard │ └── SampleSteamGuardRememberMe.java │ ├── _010_extending │ ├── MyHandler.java │ └── SampleExtending.java │ ├── _011_debuglog │ └── SampleDebugLog.java │ ├── _013_unifiedmessages │ └── SampleUnifiedMessages.java │ ├── _014_steammatchmaking │ └── SampleSteamMatchmaking.java │ ├── _020_friends │ └── SampleFriends.java │ ├── _021_webapi │ └── SampleWebApi.java │ ├── _022_pics │ └── SamplePics.java │ ├── _023_downloadapp │ └── SampleDownloadApp.java │ ├── _024_downloaduserfiles │ └── SampleDownloadUserFiles.java │ └── _999_TestBed │ └── TestBed.java ├── javasteam-tf ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── proto │ └── in │ └── dragonbra │ └── javasteam │ └── protobufs │ └── tf │ ├── base_gcmessages.proto │ ├── econ_gcmessages.proto │ ├── gcsdk_gcmessages.proto │ ├── gcsystemmsgs.proto │ ├── steammessages.proto │ ├── tf_gcmessages.proto │ └── tf_proto_def_messages.proto ├── settings.gradle.kts ├── src ├── main │ ├── java │ │ └── in │ │ │ └── dragonbra │ │ │ └── javasteam │ │ │ ├── base │ │ │ ├── AClientMsgProtobuf.java │ │ │ ├── AbstractMsgBase.java │ │ │ ├── ClientGCMsg.java │ │ │ ├── ClientGCMsgProtobuf.java │ │ │ ├── ClientMsg.java │ │ │ ├── ClientMsgProtobuf.java │ │ │ ├── GCMsgBase.java │ │ │ ├── IClientGCMsg.java │ │ │ ├── IClientMsg.java │ │ │ ├── IGCSerializableHeader.java │ │ │ ├── IGCSerializableMessage.java │ │ │ ├── IPacketGCMsg.java │ │ │ ├── IPacketMsg.java │ │ │ ├── ISteamSerializable.java │ │ │ ├── ISteamSerializableHeader.java │ │ │ ├── ISteamSerializableMessage.java │ │ │ ├── Msg.java │ │ │ ├── MsgBase.java │ │ │ ├── PacketClientGCMsg.java │ │ │ ├── PacketClientGCMsgProtobuf.java │ │ │ ├── PacketClientMsg.java │ │ │ ├── PacketClientMsgProtobuf.java │ │ │ └── PacketMsg.java │ │ │ ├── networking │ │ │ └── steam3 │ │ │ │ ├── Connection.java │ │ │ │ ├── DisconnectedEventArgs.java │ │ │ │ ├── EnvelopeEncryptedConnection.java │ │ │ │ ├── IConnectionFactory.java │ │ │ │ ├── INetFilterEncryption.java │ │ │ │ ├── NetFilterEncryption.java │ │ │ │ ├── NetFilterEncryptionWithHMAC.java │ │ │ │ ├── NetMsgEventArgs.java │ │ │ │ ├── ProtocolTypes.java │ │ │ │ ├── TcpConnection.java │ │ │ │ ├── UdpConnection.java │ │ │ │ ├── UdpPacket.java │ │ │ │ └── WebSocketConnection.kt │ │ │ ├── steam │ │ │ ├── CMClient.java │ │ │ ├── authentication │ │ │ │ ├── AccessTokenGenerateResult.kt │ │ │ │ ├── AuthPollResult.kt │ │ │ │ ├── AuthSession.kt │ │ │ │ ├── AuthSessionDetails.kt │ │ │ │ ├── AuthenticationException.kt │ │ │ │ ├── CredentialsAuthSession.kt │ │ │ │ ├── IAuthenticator.kt │ │ │ │ ├── IChallengeUrlChanged.kt │ │ │ │ ├── QrAuthSession.kt │ │ │ │ ├── SteamAuthentication.kt │ │ │ │ └── UserConsoleAuthenticator.kt │ │ │ ├── cdn │ │ │ │ ├── AuthToken.kt │ │ │ │ ├── Client.kt │ │ │ │ ├── ClientLancache.kt │ │ │ │ ├── ClientPool.kt │ │ │ │ ├── DepotChunk.kt │ │ │ │ └── Server.kt │ │ │ ├── contentdownloader │ │ │ │ ├── ChunkMatch.kt │ │ │ │ ├── ContentDownloader.kt │ │ │ │ ├── DepotDownloadCounter.kt │ │ │ │ ├── DepotDownloadInfo.kt │ │ │ │ ├── DepotFilesData.kt │ │ │ │ ├── FileManifestProvider.kt │ │ │ │ ├── FileStreamData.kt │ │ │ │ ├── GlobalDownloadCounter.kt │ │ │ │ ├── IManifestProvider.kt │ │ │ │ ├── MemoryManifestProvider.kt │ │ │ │ └── ProgressCallback.kt │ │ │ ├── discovery │ │ │ │ ├── FileServerListProvider.kt │ │ │ │ ├── IServerListProvider.kt │ │ │ │ ├── MemoryServerListProvider.kt │ │ │ │ ├── NullServerListProvider.kt │ │ │ │ ├── ServerInfo.kt │ │ │ │ ├── ServerQuality.kt │ │ │ │ ├── ServerRecord.kt │ │ │ │ └── SmartCMServerList.kt │ │ │ ├── handlers │ │ │ │ ├── ClientMsgHandler.kt │ │ │ │ ├── steamapps │ │ │ │ │ ├── AppProcessInfo.kt │ │ │ │ │ ├── GamePlayedInfo.kt │ │ │ │ │ ├── License.kt │ │ │ │ │ ├── PICSChangeData.kt │ │ │ │ │ ├── PICSProductInfo.kt │ │ │ │ │ ├── PICSRequest.kt │ │ │ │ │ ├── SteamApps.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── AppOwnershipTicketCallback.kt │ │ │ │ │ │ ├── CheckAppBetaPasswordCallback.kt │ │ │ │ │ │ ├── DepotKeyCallback.kt │ │ │ │ │ │ ├── FreeLicenseCallback.kt │ │ │ │ │ │ ├── GameConnectTokensCallback.kt │ │ │ │ │ │ ├── GuestPassListCallback.kt │ │ │ │ │ │ ├── LegacyGameKeyCallback.kt │ │ │ │ │ │ ├── LicenseListCallback.kt │ │ │ │ │ │ ├── PICSChangesCallback.kt │ │ │ │ │ │ ├── PICSProductInfoCallback.kt │ │ │ │ │ │ ├── PICSTokensCallback.kt │ │ │ │ │ │ ├── PrivateBetaCallback.kt │ │ │ │ │ │ ├── PurchaseResponseCallback.kt │ │ │ │ │ │ ├── RedeemGuestPassResponseCallback.kt │ │ │ │ │ │ └── VACStatusCallback.kt │ │ │ │ ├── steamauthticket │ │ │ │ │ ├── SteamAuthTicket.kt │ │ │ │ │ ├── TicketInfo.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── TicketAcceptedCallback.kt │ │ │ │ │ │ └── TicketAuthCompleteCallback.kt │ │ │ │ ├── steamcloud │ │ │ │ │ ├── AppFileChangeList.kt │ │ │ │ │ ├── AppFileInfo.kt │ │ │ │ │ ├── AppUploadBatchResponse.kt │ │ │ │ │ ├── FileDownloadInfo.kt │ │ │ │ │ ├── FileUploadBlockDetails.kt │ │ │ │ │ ├── FileUploadInfo.kt │ │ │ │ │ ├── HttpHeaders.kt │ │ │ │ │ ├── PendingRemoteOperation.kt │ │ │ │ │ ├── SteamCloud.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── ShareFileCallback.kt │ │ │ │ │ │ ├── SingleFileInfoCallback.kt │ │ │ │ │ │ └── UGCDetailsCallback.kt │ │ │ │ ├── steamcontent │ │ │ │ │ └── SteamContent.kt │ │ │ │ ├── steamfriends │ │ │ │ │ ├── ChatMemberInfo.kt │ │ │ │ │ ├── Event.kt │ │ │ │ │ ├── Friend.kt │ │ │ │ │ ├── FriendMessage.kt │ │ │ │ │ ├── NameInstance.kt │ │ │ │ │ ├── NameTableInstance.kt │ │ │ │ │ ├── PlayerNickname.kt │ │ │ │ │ ├── SteamFriends.kt │ │ │ │ │ ├── cache │ │ │ │ │ │ └── FriendCache.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── AliasHistoryCallback.kt │ │ │ │ │ │ ├── ChatActionResultCallback.kt │ │ │ │ │ │ ├── ChatEnterCallback.kt │ │ │ │ │ │ ├── ChatInviteCallback.kt │ │ │ │ │ │ ├── ChatMemberInfoCallback.kt │ │ │ │ │ │ ├── ChatMsgCallback.kt │ │ │ │ │ │ ├── ChatRoomInfoCallback.kt │ │ │ │ │ │ ├── ClanStateCallback.kt │ │ │ │ │ │ ├── FriendAddedCallback.kt │ │ │ │ │ │ ├── FriendMsgCallback.kt │ │ │ │ │ │ ├── FriendMsgEchoCallback.kt │ │ │ │ │ │ ├── FriendMsgHistoryCallback.kt │ │ │ │ │ │ ├── FriendsListCallback.kt │ │ │ │ │ │ ├── IgnoreFriendCallback.kt │ │ │ │ │ │ ├── NicknameCallback.kt │ │ │ │ │ │ ├── NicknameListCallback.kt │ │ │ │ │ │ ├── PersonaChangeCallback.kt │ │ │ │ │ │ ├── PersonaStateCallback.kt │ │ │ │ │ │ └── ProfileInfoCallback.kt │ │ │ │ ├── steamgamecoordinator │ │ │ │ │ ├── SteamGameCoordinator.kt │ │ │ │ │ └── callback │ │ │ │ │ │ └── MessageCallback.kt │ │ │ │ ├── steamgameserver │ │ │ │ │ ├── LogOnDetails.kt │ │ │ │ │ ├── StatusDetails.kt │ │ │ │ │ ├── SteamGameServer.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── StatusReplyCallback.kt │ │ │ │ │ │ └── TicketAuthCallback.kt │ │ │ │ ├── steammasterserver │ │ │ │ │ ├── QueryDetails.kt │ │ │ │ │ ├── Server.kt │ │ │ │ │ ├── SteamMasterServer.kt │ │ │ │ │ └── callback │ │ │ │ │ │ └── QueryCallback.kt │ │ │ │ ├── steammatchmaking │ │ │ │ │ ├── Filter.kt │ │ │ │ │ ├── Lobby.kt │ │ │ │ │ ├── LobbyCache.kt │ │ │ │ │ ├── Member.kt │ │ │ │ │ ├── SteamMatchmaking.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── CreateLobbyCallback.kt │ │ │ │ │ │ ├── GetLobbyListCallback.kt │ │ │ │ │ │ ├── JoinLobbyCallback.kt │ │ │ │ │ │ ├── LeaveLobbyCallback.kt │ │ │ │ │ │ ├── LobbyDataCallback.kt │ │ │ │ │ │ ├── SetLobbyDataCallback.kt │ │ │ │ │ │ ├── SetLobbyOwnerCallback.kt │ │ │ │ │ │ ├── UserJoinedLobbyCallback.kt │ │ │ │ │ │ └── UserLeftLobbyCallback.kt │ │ │ │ ├── steamnetworking │ │ │ │ │ ├── SteamNetworking.kt │ │ │ │ │ └── callback │ │ │ │ │ │ └── NetworkingCertificateCallback.kt │ │ │ │ ├── steamnotifications │ │ │ │ │ ├── Notification.kt │ │ │ │ │ ├── SteamNotifications.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── CommentNotificationsCallback.kt │ │ │ │ │ │ ├── ItemAnnouncementsCallback.kt │ │ │ │ │ │ ├── OfflineMessageNotificationCallback.kt │ │ │ │ │ │ └── UserNotificationsCallback.kt │ │ │ │ ├── steamscreenshots │ │ │ │ │ ├── ScreenshotDetails.kt │ │ │ │ │ ├── SteamScreenshots.kt │ │ │ │ │ └── callback │ │ │ │ │ │ └── ScreenshotAddedCallback.kt │ │ │ │ ├── steamunifiedmessages │ │ │ │ │ ├── SteamUnifiedMessages.kt │ │ │ │ │ ├── UnifiedService.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── ServiceMethodNotification.kt │ │ │ │ │ │ └── ServiceMethodResponse.kt │ │ │ │ ├── steamuser │ │ │ │ │ ├── AnonymousLogOnDetails.kt │ │ │ │ │ ├── ChatMode.kt │ │ │ │ │ ├── LogOnDetails.kt │ │ │ │ │ ├── SteamUser.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── AccountInfoCallback.kt │ │ │ │ │ │ ├── EmailAddrInfoCallback.kt │ │ │ │ │ │ ├── LoggedOffCallback.kt │ │ │ │ │ │ ├── LoggedOnCallback.kt │ │ │ │ │ │ ├── MarketingMessageCallback.kt │ │ │ │ │ │ ├── PlayingSessionStateCallback.kt │ │ │ │ │ │ ├── SessionTokenCallback.kt │ │ │ │ │ │ ├── VanityURLChangedCallback.kt │ │ │ │ │ │ ├── WalletInfoCallback.kt │ │ │ │ │ │ └── WebAPIUserNonceCallback.kt │ │ │ │ ├── steamuserstats │ │ │ │ │ ├── LeaderboardEntry.kt │ │ │ │ │ ├── SteamUserStats.kt │ │ │ │ │ └── callback │ │ │ │ │ │ ├── FindOrCreateLeaderboardCallback.kt │ │ │ │ │ │ ├── LeaderboardEntriesCallback.kt │ │ │ │ │ │ └── NumberOfPlayersCallback.kt │ │ │ │ └── steamworkshop │ │ │ │ │ ├── EnumerationUserDetails.kt │ │ │ │ │ ├── SteamWorkshop.kt │ │ │ │ │ └── callback │ │ │ │ │ └── UserActionPublishedFilesCallback.kt │ │ │ ├── steamclient │ │ │ │ ├── AsyncJobFailedException.kt │ │ │ │ ├── AsyncJobManager.kt │ │ │ │ ├── SteamClient.kt │ │ │ │ ├── callbackmgr │ │ │ │ │ ├── Callback.kt │ │ │ │ │ ├── CallbackBase.kt │ │ │ │ │ ├── CallbackManager.kt │ │ │ │ │ └── CallbackMsg.kt │ │ │ │ ├── callbacks │ │ │ │ │ ├── ConnectedCallback.kt │ │ │ │ │ └── DisconnectedCallback.kt │ │ │ │ └── configuration │ │ │ │ │ ├── ISteamConfigurationBuilder.kt │ │ │ │ │ ├── SteamConfiguration.kt │ │ │ │ │ ├── SteamConfigurationBuilder.kt │ │ │ │ │ └── SteamConfigurationState.kt │ │ │ └── webapi │ │ │ │ ├── ContentServerDirectoryService.kt │ │ │ │ ├── SteamDirectory.kt │ │ │ │ └── WebAPI.java │ │ │ ├── types │ │ │ ├── AsyncJob.kt │ │ │ ├── AsyncJobMultiple.kt │ │ │ ├── AsyncJobSingle.kt │ │ │ ├── BitVector64.java │ │ │ ├── ChunkData.kt │ │ │ ├── DepotManifest.kt │ │ │ ├── FileData.kt │ │ │ ├── GameID.java │ │ │ ├── GlobalID.java │ │ │ ├── JobID.java │ │ │ ├── KVTextReader.kt │ │ │ ├── KeyValue.java │ │ │ ├── MessageObject.java │ │ │ ├── Steam3Manifest.kt │ │ │ ├── SteamID.java │ │ │ └── UGCHandle.java │ │ │ └── util │ │ │ ├── CollectionUtils.java │ │ │ ├── HardwareUtils.java │ │ │ ├── IDebugNetworkListener.java │ │ │ ├── KeyDictionary.java │ │ │ ├── MsgUtil.java │ │ │ ├── NetHelpers.kt │ │ │ ├── NetHookNetworkListener.java │ │ │ ├── Passable.kt │ │ │ ├── SteamKitWebRequestException.kt │ │ │ ├── Strings.java │ │ │ ├── Utils.java │ │ │ ├── VZipUtil.kt │ │ │ ├── VZstdUtil.kt │ │ │ ├── WebHelpers.java │ │ │ ├── ZipUtil.kt │ │ │ ├── compat │ │ │ ├── ByteArrayOutputStreamCompat.kt │ │ │ ├── Consumer.java │ │ │ ├── InputStreamCompat.kt │ │ │ └── ObjectsCompat.java │ │ │ ├── crypto │ │ │ ├── AsnKeyParser.java │ │ │ ├── AsnParser.java │ │ │ ├── BerDecodeException.java │ │ │ ├── CryptoException.java │ │ │ ├── CryptoHelper.java │ │ │ └── RSACrypto.java │ │ │ ├── event │ │ │ ├── Event.java │ │ │ ├── EventArgs.java │ │ │ ├── EventHandler.java │ │ │ └── ScheduledFunction.java │ │ │ ├── log │ │ │ ├── DefaultLogListener.java │ │ │ ├── LogListener.java │ │ │ ├── LogManager.java │ │ │ └── Logger.java │ │ │ └── stream │ │ │ ├── BinaryReader.java │ │ │ ├── BinaryWriter.java │ │ │ ├── MemoryStream.java │ │ │ └── SeekOrigin.java │ ├── proto │ │ └── in │ │ │ └── dragonbra │ │ │ └── javasteam │ │ │ └── protobufs │ │ │ ├── steam │ │ │ └── discovery │ │ │ │ └── basic_server_list.proto │ │ │ └── steamclient │ │ │ ├── clientmetrics.proto │ │ │ ├── content_manifest.proto │ │ │ ├── encrypted_app_ticket.proto │ │ │ ├── enums.proto │ │ │ ├── steammessages_auth.steamclient.proto │ │ │ ├── steammessages_base.proto │ │ │ ├── steammessages_chat.steamclient.proto │ │ │ ├── steammessages_client_objects.proto │ │ │ ├── steammessages_clientmetrics.steamclient.proto │ │ │ ├── steammessages_clientserver.proto │ │ │ ├── steammessages_clientserver_2.proto │ │ │ ├── steammessages_clientserver_appinfo.proto │ │ │ ├── steammessages_clientserver_friends.proto │ │ │ ├── steammessages_clientserver_gameservers.proto │ │ │ ├── steammessages_clientserver_lbs.proto │ │ │ ├── steammessages_clientserver_login.proto │ │ │ ├── steammessages_clientserver_mms.proto │ │ │ ├── steammessages_clientserver_ucm.proto │ │ │ ├── steammessages_clientserver_uds.proto │ │ │ ├── steammessages_clientserver_ufs.proto │ │ │ ├── steammessages_clientserver_userstats.proto │ │ │ ├── steammessages_cloud.steamclient.proto │ │ │ ├── steammessages_contentsystem.steamclient.proto │ │ │ ├── steammessages_familygroups.steamclient.proto │ │ │ ├── steammessages_friendmessages.steamclient.proto │ │ │ ├── steammessages_gamenotifications.steamclient.proto │ │ │ ├── steammessages_inventory.steamclient.proto │ │ │ ├── steammessages_parental.steamclient.proto │ │ │ ├── steammessages_parental_objects.proto │ │ │ ├── steammessages_player.steamclient.proto │ │ │ ├── steammessages_remoteclient_service.steamclient.proto │ │ │ ├── steammessages_remoteclient_service_messages.proto │ │ │ ├── steammessages_twofactor.steamclient.proto │ │ │ ├── steammessages_unified_base.steamclient.proto │ │ │ └── steammessages_useraccount.steamclient.proto │ └── steamd │ │ └── in │ │ └── dragonbra │ │ └── javasteam │ │ ├── emsg.steamd │ │ ├── enums.steamd │ │ ├── eresult.steamd │ │ ├── gamecoordinator.steamd │ │ ├── header.steamd │ │ ├── netheader.steamd │ │ └── steammsg.steamd └── test │ ├── java │ └── in │ │ └── dragonbra │ │ └── javasteam │ │ ├── ConnectedSteamClient.java │ │ ├── PacketTests.java │ │ ├── TestBase.java │ │ ├── TestPackets.java │ │ ├── base │ │ └── ClientMsgTest.java │ │ ├── rpc │ │ └── UnifiedInterfaceTest.kt │ │ ├── steam │ │ ├── CMClientTest.java │ │ ├── DummyClient.java │ │ ├── cdn │ │ │ ├── CDNClientTest.java │ │ │ └── DepotChunkTest.java │ │ ├── discovery │ │ │ ├── FileServerListProviderTest.java │ │ │ ├── ServerRecordTest.java │ │ │ └── SmartCMServerListTest.java │ │ ├── handlers │ │ │ ├── HandlerTestBase.java │ │ │ ├── steamapps │ │ │ │ └── SteamAppsTest.java │ │ │ ├── steamfriends │ │ │ │ ├── FriendCacheTest.java │ │ │ │ └── SteamFriendsTest.java │ │ │ └── steamuser │ │ │ │ └── SteamUserTest.java │ │ ├── steamclient │ │ │ ├── SteamClientTest.java │ │ │ ├── callbackmgr │ │ │ │ └── CallbackManagerTest.java │ │ │ └── configuration │ │ │ │ ├── SteamConfigurationConfiguredObjectTest.java │ │ │ │ └── SteamConfigurationDefaultTest.java │ │ └── webapi │ │ │ ├── SteamDirectoryTest.java │ │ │ └── WebAPITest.java │ │ ├── types │ │ ├── AsyncJobTest.kt │ │ ├── DepotManifestTest.java │ │ ├── GameIDTest.java │ │ ├── KeyValueTest.java │ │ └── SteamIDTests.java │ │ └── util │ │ ├── CollectionsTest.java │ │ ├── HardwareUtilsTest.java │ │ ├── MsgUtilTest.java │ │ ├── NetHelpersTest.java │ │ ├── PassableTest.java │ │ ├── StringsTest.java │ │ ├── UtilsTest.java │ │ ├── WebHelpersTest.java │ │ ├── compat │ │ ├── ByteArrayOutputStreamCompatTest.java │ │ ├── ConsumerTest.java │ │ ├── InputStreamCompatTest.kt │ │ └── ObjectsCompatTest.java │ │ ├── crypto │ │ ├── AsnKeyParserTest.java │ │ ├── CryptoHelperTest.java │ │ └── RSACryptoTest.java │ │ ├── event │ │ └── EventTest.java │ │ └── stream │ │ ├── BinaryReaderTest.java │ │ └── BinaryWriterTest.java │ └── resources │ ├── depot │ ├── depot_232250_chunk_7b8567d9b3c09295cdbf4978c32b348d8e76c750.bin │ ├── depot_3441461_chunk_9e72678e305540630a665b93e1463bc3983eb55a.bin │ ├── depot_440_1118032470228587934.manifest │ ├── depot_440_1118032470228587934_decrypted.manifest │ ├── depot_440_1118032470228587934_v4.manifest │ └── depot_440_chunk_bac8e2657470b2eb70d6ddcd6c07004be8738697.bin │ ├── packets │ ├── 001_in_8904_k_EMsgClientPICSProductInfoResponse_app480_metadata.bin │ ├── 002_in_8904_k_EMsgClientPICSProductInfoResponse_app480.bin │ └── 003_in_8904_k_EMsgClientPICSProductInfoResponse_sub0.bin │ ├── testpackets │ ├── ClientAMGetPersonaNameHistoryResponse.bin │ ├── ClientClanState.bin │ ├── ClientFSGetFriendMessageHistoryResponse.bin │ ├── ClientFriendsList.bin │ ├── ClientLicenseList.bin │ ├── ClientMarketingMessageUpdate2.bin │ ├── ClientPICSAccessTokenResponse.bin │ ├── ClientPICSChangesSinceResponse.bin │ ├── ClientPICSProductInfoResponse.bin │ ├── ClientPersonaState.bin │ ├── ClientUpdateGuestPassesList.bin │ ├── ClientUpdateMachineAuth.bin │ ├── ClientVACBanStatus.bin │ └── ClientWalletInfoUpdate.bin │ ├── testresponses │ └── GetCMListForConnect.vdf │ └── textkeyvalues │ └── appinfo_utf8.txt └── tools └── javasteam-bot ├── .gitignore ├── app.js ├── package-lock.json └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | 8 | [*.{kt,kts}] 9 | ij_kotlin_packages_to_use_import_on_demand = java.util.**, in.dragonbra.javasteam.protobufs.steamclient.**, kotlinx.coroutines.** 10 | indent_size = 4 11 | max-line-length = 120 12 | 13 | # Not sure which method to properly choose 14 | # https://pinterest.github.io/ktlint/latest/rules/standard/#function-signature 15 | ktlint_standard_function-signature = disabled 16 | 17 | ktlint_code_style = intellij_idea 18 | ktlint_standard_package-name = disabled 19 | ktlint_standard_multiline-expression-wrapping = disabled 20 | ktlint_standard_trailing-comma-on-call-site = disabled 21 | 22 | 23 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | Please explain the changes you made here. 3 | 4 | ### Checklist 5 | - [ ] Code compiles correctly 6 | - [ ] All tests passing 7 | - [ ] Samples run successfully 8 | - [ ] Extended the README / documentation, if necessary 9 | -------------------------------------------------------------------------------- /.github/workflows/javasteam-build-pr.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 7 | # Artifacts: https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts 8 | 9 | name: Java PR CI/CD 10 | 11 | on: 12 | pull_request: 13 | branches: [ "master" ] 14 | paths-ignore: 15 | - '**.md' 16 | - 'tools/**' 17 | - 'javasteam-samples/**' 18 | 19 | permissions: 20 | contents: read 21 | 22 | jobs: 23 | build: 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Checkout JavaSteam with Java 11 29 | uses: actions/setup-java@v4 30 | with: 31 | java-version: '11' 32 | distribution: 'temurin' 33 | - name: Validate Gradle wrapper 34 | uses: gradle/actions/wrapper-validation@v4 35 | - name: Setup Gradle 36 | uses: gradle/actions/setup-gradle@v4 37 | - name: Build with Gradle, skip signing 38 | run: ./gradlew build -x signMavenJavaPublication 39 | -------------------------------------------------------------------------------- /.github/workflows/javasteam-build-push.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 7 | # Artifacts: https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts 8 | 9 | name: Java Push CI/CD 10 | 11 | on: 12 | push: 13 | branches: [ "master" ] 14 | paths-ignore: 15 | - '**.md' 16 | - 'tools/**' 17 | 18 | permissions: 19 | contents: read 20 | 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Checkout JavaSteam with Java 11 28 | uses: actions/setup-java@v4 29 | with: 30 | java-version: '11' 31 | distribution: 'temurin' 32 | - name: Validate Gradle wrapper 33 | uses: gradle/actions/wrapper-validation@v4 34 | - name: Setup Gradle 35 | uses: gradle/actions/setup-gradle@v4 36 | - name: Build with Gradle, skip signing 37 | run: ./gradlew build -x signMavenJavaPublication 38 | - uses: actions/upload-artifact@v4 39 | with: 40 | name: JavaSteam 41 | path: build/libs 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | # Ignore Gradle GUI config 25 | gradle-app.setting 26 | 27 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 28 | !gradle-wrapper.jar 29 | 30 | # Cache of project 31 | .gradletasknamecache 32 | 33 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 34 | # gradle/wrapper/gradle-wrapper.properties 35 | 36 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 37 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 38 | 39 | /.idea/ 40 | *.iml 41 | 42 | ## File-based project format: 43 | *.iws 44 | 45 | ## Plugin-specific files: 46 | 47 | # IntelliJ 48 | out/ 49 | 50 | # mpeltonen/sbt-idea plugin 51 | .idea_modules/ 52 | 53 | # JIRA plugin 54 | atlassian-ide-plugin.xml 55 | 56 | # Cursive Clojure plugin 57 | .idea/replstate.xml 58 | 59 | # Crashlytics plugin (for Android Studio and IntelliJ) 60 | com_crashlytics_export_strings.xml 61 | crashlytics.properties 62 | crashlytics-build.properties 63 | fabric.properties 64 | 65 | # JavaSteam 66 | .gradle 67 | /build/ 68 | /netlogs/ 69 | /buildSrc/build/ 70 | /javasteam-tf/build/ 71 | 72 | # JavaSteam Samples 73 | cellid.txt 74 | loginkey.txt 75 | sentry.bin 76 | server_list.bin 77 | /steamapps/ 78 | /userfiles/ 79 | 80 | # Kotlin 2.0 81 | /.kotlin/sessions/ 82 | *.salive 83 | 84 | # MacOS 85 | .DS_Store 86 | -------------------------------------------------------------------------------- /.run/javasteam [Generated Classes].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 19 | 21 | true 22 | true 23 | false 24 | false 25 | 26 | 27 | -------------------------------------------------------------------------------- /.run/javasteam [_buildSrc_ktlintFormat].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/javasteam [build].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/javasteam [clean build].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 17 | 19 | true 20 | true 21 | false 22 | false 23 | 24 | 25 | -------------------------------------------------------------------------------- /.run/javasteam [clean].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/javasteam [formatKotlin].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/javasteam [wrapper --gradle-version 8.12].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 18 | 20 | true 21 | true 22 | false 23 | false 24 | 25 | 26 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Issues 2 | 3 | Make issues to report problems or request new features. Try to follow the [issue template](https://github.com/Longi94/JavaSteam/blob/master/ISSUE_TEMPLATE.md). 4 | 5 | ## Pull requests 6 | 7 | Make pull requests against the `development` branch. Try to follow the [pull request template](https://github.com/Longi94/JavaSteam/blob/master/PULL_REQUEST_TEMPLATE.md). 8 | 9 | ## Style 10 | 11 | Most of the time reformatting the code in IntelliJ with default settings will suffice (CTRL + ALT + L) 12 | - Use 4 spaces for indenation 13 | - Curly brackets do not start in a new line 14 | - Break a statement into multiple lines if it's too long (120 char width) 15 | - Try to include javadoc where applicable 16 | - You can use the existing codebase as a style guide 17 | - Kotlin classes use Tab, Indent, and Continuation at 4 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Long Tran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | `java-gradle-plugin` 4 | } 5 | 6 | version = "1.0.0" 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation(gradleApi()) 14 | 15 | // https://mvnrepository.com/artifact/commons-io/commons-io 16 | implementation("commons-io:commons-io:2.18.0") 17 | // https://mvnrepository.com/artifact/com.squareup/kotlinpoet 18 | implementation("com.squareup:kotlinpoet:2.0.0") 19 | } 20 | 21 | gradlePlugin { 22 | plugins { 23 | create("steamlanguagegen") { 24 | id = "steamlanguagegen" 25 | implementationClass = "in.dragonbra.generators.steamlanguage.SteamLanguageGenPlugin" 26 | } 27 | create("projectversiongen") { 28 | id = "projectversiongen" 29 | implementationClass = "in.dragonbra.generators.versions.VersionGenPlugin" 30 | } 31 | create("rpcinterfacegen") { 32 | id = "rpcinterfacegen" 33 | implementationClass = "in.dragonbra.generators.rpc.RpcGenPlugin" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/rpc/RpcGenPlugin.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.rpc 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | 6 | class RpcGenPlugin : Plugin { 7 | override fun apply(project: Project) { 8 | project.tasks.register("generateRpcMethods", RpcGenTask::class.java) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/rpc/parser/Service.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.rpc.parser 2 | 3 | data class Service(val name: String, val methods: List) 4 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/rpc/parser/ServiceMethod.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.rpc.parser 2 | 3 | data class ServiceMethod(val methodName: String, val requestType: String, val responseType: String) 4 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/SteamLanguageGenPlugin.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | 6 | @SuppressWarnings("GroovyUnusedDeclaration") 7 | class SteamLanguageGenPlugin : Plugin { 8 | override fun apply(project: Project) { 9 | project.tasks.register("generateSteamLanguage", SteamLanguageGenTask::class.java) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/node/ClassNode.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.node 2 | 3 | import `in`.dragonbra.generators.steamlanguage.parser.symbol.Symbol 4 | 5 | data class ClassNode( 6 | var ident: Symbol? = null, 7 | var parent: Symbol? = null, 8 | var emit: Boolean = false 9 | ) : Node() 10 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/node/EnumNode.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.node 2 | 3 | import `in`.dragonbra.generators.steamlanguage.parser.symbol.Symbol 4 | 5 | class EnumNode( 6 | var flags: String? = null, 7 | var type: Symbol? = null 8 | ) : Node() 9 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/node/Node.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.node 2 | 3 | open class Node( 4 | val childNodes: MutableList = mutableListOf(), 5 | var name: String = "" 6 | ) 7 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/node/PropNode.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.node 2 | 3 | import `in`.dragonbra.generators.steamlanguage.parser.symbol.Symbol 4 | 5 | class PropNode( 6 | var flags: String? = null, 7 | var flagsOpt: String? = null, 8 | var type: Symbol? = null, 9 | var default: MutableList = mutableListOf(), 10 | var obsolete: String? = null, 11 | var emit: Boolean = true 12 | ) : Node() 13 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/symbol/StrongSymbol.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.symbol 2 | 3 | import `in`.dragonbra.generators.steamlanguage.parser.node.Node 4 | 5 | data class StrongSymbol( 6 | val clazz: Node, 7 | val prop: Node? = null 8 | ) : Symbol 9 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/symbol/Symbol.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.symbol 2 | 3 | interface Symbol 4 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/symbol/WeakSymbol.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.symbol 2 | 3 | data class WeakSymbol(val identifier: String) : Symbol 4 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/token/Token.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.token 2 | 3 | data class Token( 4 | val name: String?, 5 | val value: String, 6 | val source: TokenSourceInfo? = null 7 | ) 8 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/steamlanguage/parser/token/TokenSourceInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.steamlanguage.parser.token 2 | 3 | data class TokenSourceInfo( 4 | val fileName: String, 5 | val startLineNumber: Int, 6 | val startColumnNumber: Int, 7 | val endLineNumber: Int, 8 | val endColumnNumber: Int 9 | ) 10 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/util/JavaFileWriter.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.util 2 | 3 | import java.io.File 4 | import java.io.FileWriter 5 | import java.io.IOException 6 | 7 | class JavaFileWriter(file: File) : FileWriter(file) { 8 | 9 | companion object { 10 | private const val INDENTATION = " " 11 | } 12 | 13 | private var indent: String = "" 14 | 15 | fun indent() { 16 | indent += INDENTATION 17 | } 18 | 19 | fun unindent() { 20 | indent = indent.substring(INDENTATION.length) 21 | } 22 | 23 | @Throws(IOException::class) 24 | fun writeln(string: String) { 25 | write(indent) 26 | write(string) 27 | writeln() 28 | } 29 | 30 | @Throws(IOException::class) 31 | fun writeln() { 32 | write("\n") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/versions/VersionGenPlugin.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.versions 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | 6 | class VersionGenPlugin : Plugin { 7 | override fun apply(project: Project) { 8 | project.tasks.register("generateProjectVersion", VersionGenTask::class.java) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/versions/VersionGenTask.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.versions 2 | 3 | import `in`.dragonbra.generators.versions.generator.JavaGen 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.file.DirectoryProperty 6 | import org.gradle.api.provider.Property 7 | import org.gradle.api.tasks.Input 8 | import org.gradle.api.tasks.OutputDirectory 9 | import org.gradle.api.tasks.TaskAction 10 | import javax.inject.Inject 11 | 12 | abstract class VersionGenTask : DefaultTask { 13 | 14 | private companion object { 15 | private const val CLASS_NAME = "Versions" 16 | private const val PACKAGE = "in.dragonbra.javasteam.util" 17 | } 18 | 19 | @Input 20 | abstract fun getClassName(): Property 21 | 22 | @Input 23 | abstract fun getPackage(): Property 24 | 25 | @OutputDirectory 26 | abstract fun getOutputDir(): DirectoryProperty 27 | 28 | @Inject 29 | constructor() { 30 | getClassName().convention(CLASS_NAME) 31 | getPackage().convention(PACKAGE) 32 | getOutputDir().convention( 33 | project.layout.buildDirectory.dir( 34 | "generated/source/javasteam/main/java/in/dragonbra/javasteam/util" 35 | ) 36 | ) 37 | } 38 | 39 | @TaskAction 40 | fun generate() { 41 | println("Generating version class") 42 | JavaGen(getPackage().get(), getOutputDir().get().asFile).run { 43 | emit(getClassName().get(), project.version.toString()) 44 | flush() 45 | close() 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/in/dragonbra/generators/versions/generator/JavaGen.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.generators.versions.generator 2 | 3 | import `in`.dragonbra.generators.util.JavaFileWriter 4 | import java.io.Closeable 5 | import java.io.File 6 | import java.io.Flushable 7 | import java.io.IOException 8 | 9 | class JavaGen( 10 | private val pkg: String, 11 | private val destination: File 12 | ) : Closeable, Flushable { 13 | 14 | private var writer: JavaFileWriter? = null 15 | 16 | @Throws(IOException::class) 17 | fun emit(classname: String, version: String) { 18 | if (!destination.exists() && !destination.isDirectory() && !destination.mkdirs()) { 19 | throw IllegalStateException("Couldn't create folders") 20 | } 21 | 22 | val file = File(destination, "$classname.java") 23 | 24 | writer = JavaFileWriter(file) 25 | writer?.run { 26 | writeln("package $pkg;") 27 | writeln() 28 | writeln("public class $classname {") 29 | indent() 30 | writeln("public static String getVersion() {") 31 | indent() 32 | writeln("return \"$version\";") 33 | unindent() 34 | writeln("}") 35 | unindent() 36 | writeln("}") 37 | } 38 | } 39 | 40 | override fun close() { 41 | writer?.close() 42 | } 43 | 44 | override fun flush() { 45 | writer?.flush() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # These are usually filled out in your global gradle properties 2 | org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled 3 | 4 | signing.keyId= 5 | signing.password= 6 | signing.secretKeyRingFile=~/.gnupg/secring.gpg 7 | 8 | ossrhUsername= 9 | ossrhPassword= 10 | 11 | org.gradle.jvmargs=-Xmx2048m 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /javasteam-cs/.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /javasteam-cs/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.protobuf.gradle) 3 | id("java") 4 | id("maven-publish") 5 | id("signing") 6 | } 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | java { 13 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.get()) 14 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.get()) 15 | withSourcesJar() 16 | withJavadocJar() 17 | } 18 | 19 | /* Protobufs */ 20 | protobuf.protoc { 21 | artifact = libs.protobuf.protoc.get().toString() 22 | } 23 | 24 | /* Java Docs */ 25 | tasks.javadoc { 26 | exclude("**/in/dragonbra/javasteam/protobufs/**") 27 | } 28 | 29 | dependencies { 30 | implementation(libs.protobuf.java) 31 | } 32 | 33 | // TODO promote to actual lib? 34 | -------------------------------------------------------------------------------- /javasteam-cs/src/main/proto/in/dragonbra/javasteam/protobufs/cs/engine_gcmessages.proto: -------------------------------------------------------------------------------- 1 | import "google/protobuf/descriptor.proto"; 2 | 3 | option java_package = "in.dragonbra.javasteam.protobufs.cs"; 4 | 5 | option optimize_for = SPEED; 6 | option java_generic_services = false; 7 | 8 | message CEngineGotvSyncPacket { 9 | optional uint64 match_id = 1; 10 | optional uint32 instance_id = 2; 11 | optional uint32 signupfragment = 3; 12 | optional uint32 currentfragment = 4; 13 | optional float tickrate = 5; 14 | optional uint32 tick = 6; 15 | optional float rtdelay = 8; 16 | optional float rcvage = 9; 17 | optional float keyframe_interval = 10; 18 | optional uint32 cdndelay = 11; 19 | } 20 | -------------------------------------------------------------------------------- /javasteam-samples/.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /javasteam-samples/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | java { 10 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.get()) 11 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.get()) 12 | } 13 | 14 | dependencies { 15 | implementation(rootProject) 16 | 17 | implementation(libs.bouncyCastle) 18 | implementation(libs.gson) 19 | implementation(libs.kotlin.coroutines) 20 | implementation(libs.okHttp) 21 | implementation(libs.protobuf.java) // To access protobufs directly as shown in Sample #2 22 | implementation(libs.qrCode) 23 | implementation(libs.zstd) // Content Downloading. 24 | } 25 | -------------------------------------------------------------------------------- /javasteam-tf/.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "javasteam" 2 | include(":javasteam-samples") 3 | include(":javasteam-tf") 4 | include(":javasteam-cs") 5 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/base/IGCSerializableHeader.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.base; 2 | 3 | /** 4 | * @author lngtr 5 | * @since 2018-02-21 6 | */ 7 | public interface IGCSerializableHeader extends ISteamSerializable { 8 | void setEMsg(int msg); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/base/IGCSerializableMessage.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.base; 2 | 3 | /** 4 | * @author lngtr 5 | * @since 2018-02-21 6 | */ 7 | public interface IGCSerializableMessage extends ISteamSerializable { 8 | int getEMsg(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/base/IPacketGCMsg.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.base; 2 | 3 | import in.dragonbra.javasteam.types.JobID; 4 | 5 | /** 6 | * Represents a simple unified interface into game coordinator messages recieved from the network. 7 | * This is contrasted with {@link IClientGCMsg} in that this interface is packet body agnostic 8 | * and only allows simple access into the header. This interface is also immutable, and the underlying 9 | * data cannot be modified. 10 | */ 11 | public interface IPacketGCMsg { 12 | 13 | /** 14 | * Gets a value indicating whether this packet message is protobuf backed. 15 | * 16 | * @return true if this instance is protobuf backed; otherwise, false. 17 | */ 18 | boolean isProto(); 19 | 20 | /** 21 | * Gets the network message type of this packet message. 22 | * 23 | * @return The message type. 24 | */ 25 | int getMsgType(); 26 | 27 | /** 28 | * Gets the target job id for this packet message. 29 | * 30 | * @return The target job id. 31 | */ 32 | JobID getTargetJobID(); 33 | 34 | /** 35 | * Gets the source job id for this packet message. 36 | * 37 | * @return The source job id. 38 | */ 39 | JobID getSourceJobID(); 40 | 41 | /** 42 | * Gets the underlying data that represents this client message. 43 | * 44 | * @return The data. 45 | */ 46 | byte[] getData(); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/base/IPacketMsg.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.base; 2 | 3 | import in.dragonbra.javasteam.enums.EMsg; 4 | 5 | /** 6 | * Represents a simple unified interface into client messages recieved from the network. 7 | * This is contrasted with {@link IClientMsg} in that this interface is packet body agnostic 8 | * and only allows simple access into the header. This interface is also immutable, and the underlying 9 | * data cannot be modified. 10 | */ 11 | public interface IPacketMsg { 12 | /** 13 | * Gets a value indicating whether this packet message is protobuf backed. 14 | * 15 | * @return true if this instance is protobuf backed; otherwise, false 16 | */ 17 | boolean isProto(); 18 | 19 | /** 20 | * Gets the network message type of this packet message. 21 | * 22 | * @return The message type. 23 | */ 24 | EMsg getMsgType(); 25 | 26 | /** 27 | * Gets the target job id for this packet message. 28 | * 29 | * @return The target job id. 30 | */ 31 | long getTargetJobID(); 32 | 33 | /** 34 | * Gets the source job id for this packet message. 35 | * 36 | * @return The source job id. 37 | */ 38 | long getSourceJobID(); 39 | 40 | /** 41 | * Gets the underlying data that represents this client message. 42 | * 43 | * @return The data. 44 | */ 45 | byte[] getData(); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/base/ISteamSerializable.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.base; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | /** 8 | * @author lngtr 9 | * @since 2018-02-21 10 | */ 11 | public interface ISteamSerializable { 12 | 13 | void serialize(OutputStream stream) throws IOException; 14 | 15 | void deserialize(InputStream stream) throws IOException; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/base/ISteamSerializableHeader.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.base; 2 | 3 | import in.dragonbra.javasteam.enums.EMsg; 4 | 5 | /** 6 | * @author lngtr 7 | * @since 2018-02-21 8 | */ 9 | public interface ISteamSerializableHeader extends ISteamSerializable { 10 | void setEMsg(EMsg msg); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/base/ISteamSerializableMessage.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.base; 2 | 3 | import in.dragonbra.javasteam.enums.EMsg; 4 | 5 | /** 6 | * @author lngtr 7 | * @since 2018-02-21 8 | */ 9 | public interface ISteamSerializableMessage extends ISteamSerializable { 10 | EMsg getEMsg(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/networking/steam3/DisconnectedEventArgs.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.networking.steam3; 2 | 3 | import in.dragonbra.javasteam.util.event.EventArgs; 4 | 5 | /** 6 | * @author lngtr 7 | * @since 2018-02-20 8 | */ 9 | public class DisconnectedEventArgs extends EventArgs { 10 | 11 | private final boolean userInitiated; 12 | 13 | public DisconnectedEventArgs(boolean userInitiated) { 14 | this.userInitiated = userInitiated; 15 | } 16 | 17 | public boolean isUserInitiated() { 18 | return userInitiated; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/networking/steam3/INetFilterEncryption.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.networking.steam3; 2 | 3 | /** 4 | * @author lngtr 5 | * @since 2018-02-24 6 | */ 7 | public interface INetFilterEncryption { 8 | byte[] processIncoming(byte[] data); 9 | byte[] processOutgoing(byte[] data); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/networking/steam3/NetFilterEncryption.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.networking.steam3; 2 | 3 | import in.dragonbra.javasteam.util.crypto.CryptoException; 4 | import in.dragonbra.javasteam.util.crypto.CryptoHelper; 5 | import in.dragonbra.javasteam.util.log.LogManager; 6 | import in.dragonbra.javasteam.util.log.Logger; 7 | 8 | /** 9 | * @author lngtr 10 | * @since 2018-02-24 11 | */ 12 | public class NetFilterEncryption implements INetFilterEncryption { 13 | 14 | private static final Logger logger = LogManager.getLogger(NetFilterEncryption.class); 15 | 16 | private final byte[] sessionKey; 17 | 18 | public NetFilterEncryption(byte[] sessionKey) { 19 | if (sessionKey.length != 32) { 20 | logger.debug("AES session key was not 32 bytes!"); 21 | } 22 | this.sessionKey = sessionKey; 23 | } 24 | 25 | @Override 26 | public byte[] processIncoming(byte[] data) { 27 | try { 28 | return CryptoHelper.symmetricDecrypt(data, sessionKey); 29 | } catch (CryptoException e) { 30 | throw new IllegalStateException("Unable to decrypt incoming packet", e); 31 | } 32 | } 33 | 34 | @Override 35 | public byte[] processOutgoing(byte[] data) { 36 | try { 37 | return CryptoHelper.symmetricEncrypt(data, sessionKey); 38 | } catch (CryptoException e) { 39 | throw new IllegalStateException("Unable to encrypt outgoing packet", e); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/networking/steam3/NetFilterEncryptionWithHMAC.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.networking.steam3; 2 | 3 | import in.dragonbra.javasteam.util.crypto.CryptoException; 4 | import in.dragonbra.javasteam.util.crypto.CryptoHelper; 5 | import in.dragonbra.javasteam.util.log.LogManager; 6 | import in.dragonbra.javasteam.util.log.Logger; 7 | 8 | /** 9 | * @author lngtr 10 | * @since 2018-02-24 11 | */ 12 | public class NetFilterEncryptionWithHMAC implements INetFilterEncryption { 13 | 14 | private static final Logger logger = LogManager.getLogger(NetFilterEncryptionWithHMAC.class); 15 | 16 | private final byte[] sessionKey; 17 | private final byte[] hmacSecret; 18 | 19 | public NetFilterEncryptionWithHMAC(byte[] sessionKey) { 20 | if (sessionKey.length != 32) { 21 | logger.debug("AES session key was not 32 bytes!"); 22 | } 23 | this.sessionKey = sessionKey; 24 | this.hmacSecret = new byte[16]; 25 | System.arraycopy(sessionKey, 0, hmacSecret, 0, hmacSecret.length); 26 | } 27 | 28 | @Override 29 | public byte[] processIncoming(byte[] data) { 30 | try { 31 | return CryptoHelper.symmetricDecryptHMACIV(data, sessionKey, hmacSecret); 32 | } catch (CryptoException e) { 33 | throw new IllegalStateException("Unable to decrypt incoming packet", e); 34 | } 35 | } 36 | 37 | @Override 38 | public byte[] processOutgoing(byte[] data) { 39 | try { 40 | return CryptoHelper.symmetricEncryptWithHMACIV(data, sessionKey, hmacSecret); 41 | } catch (CryptoException e) { 42 | throw new IllegalStateException("Unable to encrypt outgoing packet", e); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/networking/steam3/NetMsgEventArgs.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.networking.steam3; 2 | 3 | import in.dragonbra.javasteam.util.event.EventArgs; 4 | 5 | import java.net.InetSocketAddress; 6 | 7 | /** 8 | * @author lngtr 9 | * @since 2018-02-20 10 | */ 11 | @SuppressWarnings("unused") 12 | public class NetMsgEventArgs extends EventArgs { 13 | 14 | private final byte[] data; 15 | 16 | private final InetSocketAddress endPoint; 17 | 18 | public NetMsgEventArgs(byte[] data, InetSocketAddress endPoint) { 19 | this.data = data; 20 | this.endPoint = endPoint; 21 | } 22 | 23 | public NetMsgEventArgs withData(byte[] data) { 24 | return new NetMsgEventArgs(data, this.endPoint); 25 | } 26 | 27 | public byte[] getData() { 28 | return data; 29 | } 30 | 31 | public InetSocketAddress getEndPoint() { 32 | return endPoint; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/networking/steam3/ProtocolTypes.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.networking.steam3; 2 | 3 | import java.util.EnumSet; 4 | 5 | /** 6 | * @author lngtr 7 | * @since 2018-02-20 8 | */ 9 | public enum ProtocolTypes { 10 | 11 | TCP(1), 12 | 13 | UDP(1 << 1), 14 | 15 | WEB_SOCKET(1 << 2); 16 | 17 | public static final EnumSet ALL = EnumSet.of(TCP, UDP, WEB_SOCKET); 18 | 19 | private final int code; 20 | 21 | ProtocolTypes(int code) { 22 | this.code = code; 23 | } 24 | 25 | public int code() { 26 | return this.code; 27 | } 28 | 29 | public static EnumSet from(int code) { 30 | EnumSet set = EnumSet.noneOf(ProtocolTypes.class); 31 | for (ProtocolTypes e : ProtocolTypes.values()) { 32 | if ((e.code & code) == e.code) { 33 | set.add(e); 34 | } 35 | } 36 | return set; 37 | } 38 | 39 | public static int code(EnumSet flags) { 40 | int code = 0; 41 | for (ProtocolTypes flag : flags) { 42 | code |= flag.code; 43 | } 44 | return code; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/authentication/AccessTokenGenerateResult.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.authentication 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesAuthSteamclient.CAuthentication_AccessToken_GenerateForApp_Response 4 | import `in`.dragonbra.javasteam.steam.handlers.steamuser.LogOnDetails 5 | 6 | /** 7 | * Represents access token generation result. 8 | */ 9 | class AccessTokenGenerateResult(response: CAuthentication_AccessToken_GenerateForApp_Response.Builder) { 10 | 11 | /** 12 | * New refresh token. 13 | * This can be provided to [LogOnDetails.accessToken] 14 | */ 15 | val refreshToken: String = response.refreshToken 16 | 17 | /** 18 | * New token subordinate to [AccessTokenGenerateResult.refreshToken] 19 | */ 20 | val accessToken: String = response.accessToken 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/authentication/AuthPollResult.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.authentication 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesAuthSteamclient.CAuthentication_PollAuthSessionStatus_Response 4 | 5 | /** 6 | * Represents authentication poll result. 7 | */ 8 | class AuthPollResult(response: CAuthentication_PollAuthSessionStatus_Response.Builder) { 9 | 10 | /** 11 | * Account name of authenticating account. 12 | */ 13 | val accountName: String = response.accountName 14 | 15 | /** 16 | * New refresh token. 17 | */ 18 | val refreshToken: String = response.refreshToken 19 | 20 | /** 21 | * Gets or Sets the new token subordinate to [refreshToken]. 22 | */ 23 | val accessToken: String = response.accessToken 24 | 25 | /** 26 | * May contain remembered machine ID for future login, usually when account uses email based Steam Guard. 27 | * Supply it in [AuthSessionDetails.guardData] for future logins to avoid resending an email. 28 | * This value should be stored per account. 29 | */ 30 | val newGuardData: String? = response.newGuardData 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/authentication/AuthenticationException.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.authentication 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | 5 | /** 6 | * Thrown when [SteamAuthentication] fails to authenticate. 7 | */ 8 | @Suppress("unused") 9 | class AuthenticationException : Exception { 10 | 11 | /** 12 | * The result of the authentication request. 13 | */ 14 | var result: EResult? = null 15 | private set 16 | 17 | /** 18 | * Initializes a new instance of the [AuthenticationException] class. 19 | */ 20 | constructor() : super() 21 | 22 | /** 23 | * Initializes a new instance of the [AuthenticationException] class. 24 | * @param message The message that describes the error. 25 | */ 26 | constructor(message: String) : super(message) 27 | 28 | /** 29 | * Initializes a new instance of the [AuthenticationException] class. 30 | * @param message The message that describes the error. 31 | * @param result The result code that describes the error. 32 | */ 33 | constructor(message: String, result: EResult) : super("$message with result: $result") { 34 | this.result = result 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/authentication/IAuthenticator.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.authentication 2 | 3 | import java.util.concurrent.CompletableFuture 4 | 5 | /** 6 | * Represents an authenticator to be used with [SteamAuthentication]. 7 | */ 8 | interface IAuthenticator { 9 | 10 | /** 11 | * This method is called when the account being logged into requires 2-factor authentication using the authenticator app. 12 | * @param previousCodeWasIncorrect True when previously provided code was incorrect. 13 | * @return The 2-factor auth code used to log in. This is the code that can be received from the authenticator app. 14 | */ 15 | fun getDeviceCode(previousCodeWasIncorrect: Boolean): CompletableFuture 16 | 17 | /** 18 | * This method is called when the account being logged into uses Steam Guard email authentication. This code is sent to the user's email. 19 | * @param email The email address that the Steam Guard email was sent to. 20 | * @param previousCodeWasIncorrect True when previously provided code was incorrect. 21 | * @return The Steam Guard auth code used to log in. 22 | */ 23 | fun getEmailCode(email: String?, previousCodeWasIncorrect: Boolean): CompletableFuture 24 | 25 | /** 26 | * This method is called when the account being logged has the Steam Mobile App and accepts authentication notification prompts. 27 | * @return Return true to poll until the authentication is accepted, return false to fall back to entering a code. 28 | */ 29 | fun acceptDeviceConfirmation(): CompletableFuture 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/authentication/IChallengeUrlChanged.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.authentication 2 | 3 | /** 4 | * Interface to tell the listening class that the QR Challenge URL has changed. 5 | */ 6 | fun interface IChallengeUrlChanged { 7 | fun onChanged(qrAuthSession: QrAuthSession?) 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/cdn/AuthToken.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.cdn 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesContentsystemSteamclient.CContentServerDirectory_GetCDNAuthToken_Response 5 | import `in`.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback.ServiceMethodResponse 6 | import java.util.Date 7 | 8 | /** 9 | * This is received when a CDN auth token is received 10 | */ 11 | class AuthToken(message: ServiceMethodResponse) { 12 | 13 | /** 14 | * Result of the operation 15 | */ 16 | val result: EResult 17 | 18 | /** 19 | * CDN auth token 20 | */ 21 | val token: String 22 | 23 | /** 24 | * Token expiration date 25 | */ 26 | val expiration: Date 27 | 28 | init { 29 | val response = message.body.build() 30 | result = message.result 31 | token = response.token 32 | expiration = Date(response.expirationTime * 1000L) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/ChunkMatch.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | import `in`.dragonbra.javasteam.types.ChunkData 4 | 5 | data class ChunkMatch( 6 | val oldChunk: ChunkData, 7 | val newChunk: ChunkData, 8 | ) 9 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/DepotDownloadCounter.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | data class DepotDownloadCounter( 4 | var completeDownloadSize: Long = 0, 5 | var sizeDownloaded: Long = 0, 6 | var depotBytesCompressed: Long = 0, 7 | var depotBytesUncompressed: Long = 0, 8 | ) 9 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/DepotDownloadInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | @Suppress("ArrayInDataClass") 4 | data class DepotDownloadInfo( 5 | val depotId: Int, 6 | val appId: Int, 7 | val manifestId: Long, 8 | val branch: String, 9 | val installDir: String, 10 | val depotKey: ByteArray?, 11 | ) 12 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/DepotFilesData.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | import `in`.dragonbra.javasteam.types.DepotManifest 4 | 5 | data class DepotFilesData( 6 | val depotDownloadInfo: DepotDownloadInfo, 7 | val depotCounter: DepotDownloadCounter, 8 | val stagingDir: String, 9 | val manifest: DepotManifest, 10 | val previousManifest: DepotManifest?, 11 | ) 12 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/FileStreamData.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | import kotlinx.coroutines.sync.Semaphore 4 | import java.nio.channels.FileChannel 5 | 6 | data class FileStreamData( 7 | var fileStream: FileChannel?, 8 | val fileLock: Semaphore, 9 | var chunksToDownload: Int, 10 | ) 11 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/GlobalDownloadCounter.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | data class GlobalDownloadCounter( 4 | var totalBytesCompressed: Long = 0, 5 | var totalBytesUncompressed: Long = 0, 6 | ) 7 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/IManifestProvider.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | import `in`.dragonbra.javasteam.types.DepotManifest 4 | 5 | /** 6 | * An interface for persisting depot manifests for Steam content downloading 7 | * 8 | * @author Oxters 9 | * @since 2024-11-06 10 | */ 11 | interface IManifestProvider { 12 | 13 | /** 14 | * Ask a provider to fetch a specific depot manifest 15 | * @return A [Pair] object with a [DepotManifest] and its checksum if it exists otherwise null 16 | */ 17 | fun fetchManifest(depotID: Int, manifestID: Long): DepotManifest? 18 | 19 | /** 20 | * Ask a provider to fetch the most recent manifest used of a depot 21 | * @return A [Pair] object with a [DepotManifest] and its checksum if it exists otherwise null 22 | */ 23 | fun fetchLatestManifest(depotID: Int): DepotManifest? 24 | 25 | /** 26 | * Ask a provider to set the most recent manifest ID used of a depot 27 | */ 28 | fun setLatestManifestId(depotID: Int, manifestID: Long) 29 | 30 | /** 31 | * Update the persistent depot manifest 32 | * @param manifest The depot manifest 33 | * @return The checksum of the depot manifest 34 | */ 35 | fun updateManifest(manifest: DepotManifest) 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/MemoryManifestProvider.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | import `in`.dragonbra.javasteam.types.DepotManifest 4 | 5 | /** 6 | * @author Oxters 7 | * @since 2024-11-06 8 | */ 9 | class MemoryManifestProvider : IManifestProvider { 10 | 11 | private val depotManifests = mutableMapOf>() 12 | 13 | private val latestManifests = mutableMapOf() 14 | 15 | override fun fetchManifest(depotID: Int, manifestID: Long): DepotManifest? = 16 | depotManifests[depotID]?.get(manifestID) 17 | 18 | override fun fetchLatestManifest(depotID: Int): DepotManifest? = 19 | latestManifests[depotID]?.let { fetchManifest(depotID, it) } 20 | 21 | override fun setLatestManifestId(depotID: Int, manifestID: Long) { 22 | latestManifests[depotID] = manifestID 23 | } 24 | 25 | override fun updateManifest(manifest: DepotManifest) { 26 | depotManifests.getOrPut(manifest.depotID) { mutableMapOf() }[manifest.manifestGID] = manifest 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/contentdownloader/ProgressCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.contentdownloader 2 | 3 | /** 4 | * Interface for Java to implement for progress updates 5 | */ 6 | fun interface ProgressCallback { 7 | fun onProgress(progress: Float) 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/discovery/IServerListProvider.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.discovery 2 | 3 | import java.time.Instant 4 | 5 | /** 6 | * An interface for persisting the server list for connection discovery 7 | */ 8 | interface IServerListProvider { 9 | 10 | /** 11 | * When the server list was last refreshed, used to determine if the server list should be refreshed from the Steam Directory 12 | * This should return DateTime with the UTC kind 13 | */ 14 | val lastServerListRefresh: Instant 15 | 16 | /** 17 | * Ask a provider to fetch any servers that it has available 18 | * @return A list of IPEndPoints representing servers 19 | */ 20 | fun fetchServerList(): List 21 | 22 | /** 23 | * Update the persistent list of endpoints 24 | * @param endpoints List of endpoints 25 | */ 26 | fun updateServerList(endpoints: List) 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/discovery/MemoryServerListProvider.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.discovery 2 | 3 | import java.time.Instant 4 | 5 | /** 6 | * A server list provider that uses an in-memory list 7 | */ 8 | class MemoryServerListProvider : IServerListProvider { 9 | 10 | private var servers: List = listOf() 11 | 12 | private var lastUpdated: Instant = Instant.MIN 13 | 14 | /** 15 | * Returns the last time the server list was updated 16 | */ 17 | override val lastServerListRefresh: Instant 18 | get() = lastUpdated 19 | 20 | /** 21 | * Returns the stored server list in memory 22 | * @return List of servers if persisted, otherwise an empty list 23 | */ 24 | override fun fetchServerList(): List = servers 25 | 26 | /** 27 | * Stores the supplied list of servers in memory 28 | * @param endpoints List of endpoints (servers) 29 | */ 30 | override fun updateServerList(endpoints: List) { 31 | servers = endpoints 32 | lastUpdated = Instant.now() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/discovery/NullServerListProvider.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.discovery 2 | 3 | import java.time.Instant 4 | 5 | /** 6 | * @author lngtr 7 | * @since 2018-02-20 8 | * 9 | * @constructor A server list provider that returns an empty list, for consumers that populate the server list themselves 10 | */ 11 | @Suppress("unused") 12 | class NullServerListProvider : IServerListProvider { 13 | 14 | /** 15 | * Always returns [Instant.MIN] 16 | */ 17 | override val lastServerListRefresh: Instant 18 | get() = Instant.MIN 19 | 20 | /** 21 | * No-op implementation that returns an empty server list 22 | * @return an Empty server list 23 | */ 24 | override fun fetchServerList(): List = emptyList() 25 | 26 | /** 27 | * No-op implementation that does not persist server list 28 | * @param endpoints Server list 29 | */ 30 | override fun updateServerList(endpoints: List) { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/discovery/ServerInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.discovery 2 | 3 | import `in`.dragonbra.javasteam.networking.steam3.ProtocolTypes 4 | import java.time.Instant 5 | 6 | /** 7 | * @author lngtr 8 | * @since 2018-02-20 9 | */ 10 | class ServerInfo(val record: ServerRecord, val protocol: ProtocolTypes) { 11 | var lastBadConnectionTimeUtc: Instant? = null 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/discovery/ServerQuality.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.discovery 2 | 3 | /** 4 | * Currently marked quality of a server. All servers start off as Undetermined. 5 | */ 6 | enum class ServerQuality { 7 | /** 8 | * Known good server. 9 | */ 10 | GOOD, 11 | 12 | /** 13 | * Known bad server. 14 | */ 15 | BAD, 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/ClientMsgHandler.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers 2 | 3 | import `in`.dragonbra.javasteam.base.IPacketMsg 4 | import `in`.dragonbra.javasteam.steam.steamclient.SteamClient 5 | import `in`.dragonbra.javasteam.steam.steamclient.callbacks.DisconnectedCallback 6 | 7 | /** 8 | * This class implements the base requirements every message handler should inherit from. 9 | * 10 | * @constructor Initializes a new instance of the [ClientMsgHandler] class. 11 | */ 12 | abstract class ClientMsgHandler { 13 | 14 | /** 15 | * Gets the underlying [SteamClient] for use in sending replies. 16 | */ 17 | protected lateinit var client: SteamClient 18 | 19 | fun setup(client: SteamClient) { 20 | this.client = client 21 | } 22 | 23 | /** 24 | * Gets or Sets whether the related [SteamClient] should imminently expect the server to close the connection. 25 | * If this is true when the connection is closed, the [DisconnectedCallback]'s 26 | * [DisconnectedCallback.isUserInitiated] property will be set to **true**. 27 | */ 28 | protected var isExpectDisconnection: Boolean 29 | get() = client.isExpectDisconnection 30 | set(expectDisconnection) { 31 | client.isExpectDisconnection = expectDisconnection 32 | } 33 | 34 | /** 35 | * Handles a client message. This should not be called directly. 36 | * @param packetMsg The packet message that contains the data. 37 | */ 38 | abstract fun handleMsg(packetMsg: IPacketMsg) 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/AppProcessInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps 2 | 3 | data class AppProcessInfo( 4 | val processId: Int, 5 | val processIdParent: Int, 6 | val parentIsSteam: Boolean, 7 | ) 8 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/GamePlayedInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps 2 | 3 | import java.net.InetAddress 4 | 5 | @Suppress("ArrayInDataClass") 6 | data class GamePlayedInfo( 7 | val steamIdGs: Long = 0, 8 | val gameId: Long, 9 | val deprecatedGameIpAddress: Int = 0, 10 | val gamePort: Int = 0, 11 | val isSecure: Boolean = false, 12 | val token: ByteArray = byteArrayOf(), 13 | val gameExtraInfo: String = "", 14 | val gameDataBlob: ByteArray? = null, 15 | val processId: Int, 16 | val streamingProviderId: Int = 0, 17 | val gameFlags: Int = 0, 18 | val ownerId: Int, 19 | val vrHmdVendor: String = "", 20 | val vrHmdModel: String = "", 21 | val launchOptionType: Int = 0, 22 | val primaryControllerType: Int = -1, 23 | val primarySteamControllerSerial: String = "", 24 | val totalSteamControllerCount: Int = 0, 25 | val totalNonSteamControllerCount: Int = 0, 26 | val controllerWorkshopFileId: Long = 0, 27 | val launchSource: Int = 0, 28 | val vrHmdRuntime: Int = 0, 29 | val gameIpAddress: InetAddress? = null, 30 | val controllerConnectionType: Int = 0, 31 | val gameOsPlatform: Int = -1, 32 | val gameBuildId: Int, 33 | val compatToolId: Int = 0, 34 | val compatToolCmd: String = "", 35 | val compatToolBuildId: Int = 0, 36 | val betaName: String = "", 37 | val dlcContext: Int = 0, 38 | val processIdList: List = emptyList(), 39 | ) 40 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/PICSChangeData.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverAppinfo.CMsgClientPICSChangesSinceResponse 4 | 5 | /** 6 | * Holds the change data for a single app or package 7 | */ 8 | @Suppress("MemberVisibilityCanBePrivate", "unused") 9 | class PICSChangeData { 10 | 11 | /** 12 | * Gets the app or package ID this change data represents 13 | */ 14 | val id: Int 15 | 16 | /** 17 | * Gets the current change number of this app 18 | */ 19 | val changeNumber: Int 20 | 21 | /** 22 | * Gets signals if an access token is needed for this request 23 | */ 24 | val isNeedsToken: Boolean 25 | 26 | constructor(change: CMsgClientPICSChangesSinceResponse.AppChange) { 27 | id = change.appid 28 | changeNumber = change.changeNumber 29 | isNeedsToken = change.needsToken 30 | } 31 | 32 | constructor(change: CMsgClientPICSChangesSinceResponse.PackageChange) { 33 | id = change.packageid 34 | changeNumber = change.changeNumber 35 | isNeedsToken = change.needsToken 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/PICSRequest.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps 2 | 3 | /** 4 | * Represents a PICS request used for [SteamApps.picsGetProductInfo] 5 | * 6 | * @constructor Instantiate a PICS product info request for a given app and/or package id and an access token 7 | * @param id App or package ID PICS access token 8 | * @param accessToken 9 | * 10 | * @property id Gets or sets the ID of the app or package being requested 11 | * @property accessToken Gets or sets the access token associated with the request 12 | */ 13 | class PICSRequest @JvmOverloads constructor(var id: Int = 0, var accessToken: Long = 0L) 14 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/AppOwnershipTicketCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver.CMsgClientGetAppOwnershipTicketResponse 7 | import `in`.dragonbra.javasteam.steam.handlers.steamapps.SteamApps 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | 10 | /** 11 | * This callback is received in response to calling [SteamApps.getAppOwnershipTicket] 12 | */ 13 | class AppOwnershipTicketCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the result of requesting the ticket. 17 | */ 18 | val result: EResult 19 | 20 | /** 21 | * Gets the AppID this ticket is for. 22 | */ 23 | val appID: Int 24 | 25 | /** 26 | * Gets the ticket data. 27 | */ 28 | val ticket: ByteArray 29 | 30 | init { 31 | val ticketResponse = ClientMsgProtobuf( 32 | CMsgClientGetAppOwnershipTicketResponse::class.java, 33 | packetMsg 34 | ) 35 | val msg = ticketResponse.body 36 | 37 | jobID = ticketResponse.targetJobID 38 | 39 | result = EResult.from(msg.eresult) 40 | appID = msg.appId 41 | ticket = msg.ticket.toByteArray() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/CheckAppBetaPasswordCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientCheckAppBetaPasswordResponse 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | import `in`.dragonbra.javasteam.util.Strings 9 | 10 | /** 11 | * This callback is received when a beta password check has been completed 12 | */ 13 | class CheckAppBetaPasswordCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the result of the operation. 17 | */ 18 | val result: EResult 19 | 20 | /** 21 | * Gets a map of beta names to their encryption keys. 22 | */ 23 | val betaPasswords: Map 24 | 25 | init { 26 | val response = ClientMsgProtobuf( 27 | CMsgClientCheckAppBetaPasswordResponse::class.java, 28 | packetMsg 29 | ) 30 | val msg = response.body 31 | 32 | jobID = response.targetJobID 33 | 34 | result = EResult.from(msg.eresult) 35 | betaPasswords = msg.betapasswordsList.associate { it.betaname to Strings.decodeHex(it.betapassword) } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/DepotKeyCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientGetDepotDecryptionKeyResponse 7 | import `in`.dragonbra.javasteam.steam.handlers.steamapps.SteamApps 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | 10 | /** 11 | * This callback is received in response to calling [SteamApps.getDepotDecryptionKey] 12 | */ 13 | class DepotKeyCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the result of requesting this encryption key. 17 | */ 18 | val result: EResult 19 | 20 | /** 21 | * Gets the DepotID this encryption key is for. 22 | */ 23 | val depotID: Int 24 | 25 | /** 26 | * Gets the encryption key for this depot. 27 | */ 28 | val depotKey: ByteArray 29 | 30 | init { 31 | val keyResponse = ClientMsgProtobuf( 32 | CMsgClientGetDepotDecryptionKeyResponse::class.java, 33 | packetMsg 34 | ) 35 | val msg = keyResponse.body 36 | 37 | jobID = keyResponse.targetJobID 38 | 39 | result = EResult.from(msg.eresult) 40 | depotID = msg.depotId 41 | depotKey = msg.depotEncryptionKey.toByteArray() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/FreeLicenseCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientRequestFreeLicenseResponse 7 | import `in`.dragonbra.javasteam.steam.handlers.steamapps.SteamApps 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | import java.util.* 10 | 11 | /** 12 | * This callback is received in response to calling [SteamApps.requestFreeLicense], informing the client of newly granted packages, if any. 13 | */ 14 | @Suppress("MemberVisibilityCanBePrivate") 15 | class FreeLicenseCallback(packetMsg: IPacketMsg) : CallbackMsg() { 16 | 17 | /** 18 | * Gets the result of the message. 19 | */ 20 | val result: EResult 21 | 22 | /** 23 | * Gets the list of granted apps. 24 | */ 25 | val grantedApps: List 26 | 27 | /** 28 | * Gets the list of granted packages. 29 | */ 30 | val grantedPackages: List 31 | 32 | init { 33 | val grantedLicenses = ClientMsgProtobuf( 34 | CMsgClientRequestFreeLicenseResponse::class.java, 35 | packetMsg 36 | ) 37 | val msg = grantedLicenses.body 38 | 39 | jobID = grantedLicenses.targetJobID 40 | 41 | result = EResult.from(msg.eresult) 42 | 43 | grantedApps = Collections.unmodifiableList(msg.grantedAppidsList) 44 | grantedPackages = Collections.unmodifiableList(msg.grantedPackageidsList) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/GameConnectTokensCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver.CMsgClientGameConnectTokens 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | 8 | /** 9 | * This callback is fired when the client receives a list of game connect tokens. 10 | */ 11 | class GameConnectTokensCallback(packetMsg: IPacketMsg) : CallbackMsg() { 12 | 13 | /** 14 | * Gets a count of tokens to keep. 15 | */ 16 | val tokensToKeep: Int 17 | 18 | /** 19 | * Gets the list of tokens. 20 | */ 21 | val tokens: List 22 | 23 | init { 24 | val gcTokens = ClientMsgProtobuf( 25 | CMsgClientGameConnectTokens::class.java, 26 | packetMsg 27 | ) 28 | val msg = gcTokens.body 29 | 30 | tokensToKeep = msg.maxTokensToKeep 31 | tokens = msg.tokensList.map { it.toByteArray() } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/LegacyGameKeyCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsg 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.generated.MsgClientGetLegacyGameKeyResponse 7 | import `in`.dragonbra.javasteam.steam.handlers.steamapps.SteamApps 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | import java.nio.charset.StandardCharsets 10 | 11 | /** 12 | * This callback is received in response to calling [SteamApps.getLegacyGameKey]. 13 | */ 14 | class LegacyGameKeyCallback(packetMsg: IPacketMsg) : CallbackMsg() { 15 | 16 | /** 17 | * Gets the result of requesting this game key. 18 | */ 19 | val result: EResult 20 | 21 | /** 22 | * Gets the appid that this game key is for. 23 | */ 24 | val appID: Int 25 | 26 | /** 27 | * Gets the game key. 28 | */ 29 | var key: String? = null 30 | 31 | init { 32 | val keyResponse = ClientMsg(MsgClientGetLegacyGameKeyResponse::class.java, packetMsg) 33 | val msg = keyResponse.body 34 | 35 | jobID = keyResponse.targetJobID 36 | appID = msg.appId 37 | result = msg.result 38 | 39 | if (msg.length > 0) { 40 | val length: Int = msg.length - 1 41 | val payload = keyResponse.payload.toByteArray() 42 | key = String(payload, 0, length, StandardCharsets.US_ASCII) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/LicenseListCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver.CMsgClientLicenseList 7 | import `in`.dragonbra.javasteam.steam.handlers.steamapps.License 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | 10 | /** 11 | * This callback is fired during logon, informing the client of it's available licenses. 12 | */ 13 | class LicenseListCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the result of the message. 17 | */ 18 | val result: EResult 19 | 20 | /** 21 | * Gets the license list. 22 | */ 23 | val licenseList: List 24 | 25 | init { 26 | val licenseListResp = ClientMsgProtobuf( 27 | CMsgClientLicenseList::class.java, 28 | packetMsg 29 | ) 30 | val msg = licenseListResp.body 31 | 32 | result = EResult.from(msg.eresult) 33 | 34 | licenseList = msg.licensesList.map { License(it) } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/PrivateBetaCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverAppinfo.CMsgClientPICSPrivateBetaResponse 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | import `in`.dragonbra.javasteam.types.KeyValue 9 | import `in`.dragonbra.javasteam.util.stream.MemoryStream 10 | 11 | /** 12 | * This callback is received when a private beta request has been completed 13 | */ 14 | class PrivateBetaCallback(packetMsg: IPacketMsg) : CallbackMsg() { 15 | 16 | /** 17 | * Result of the operation 18 | */ 19 | val result: EResult 20 | 21 | /** 22 | * Gets the keyvalue info to be merged into main appinfo 23 | */ 24 | val depotSection: KeyValue 25 | 26 | init { 27 | val response = ClientMsgProtobuf( 28 | CMsgClientPICSPrivateBetaResponse::class.java, 29 | packetMsg 30 | ) 31 | val msg = response.body 32 | 33 | jobID = response.targetJobID 34 | 35 | result = EResult.from(msg.eresult) 36 | 37 | depotSection = KeyValue() 38 | 39 | if (msg.depotSection != null && msg.depotSection.size() > 0) { 40 | // we don't want to read the trailing null byte 41 | MemoryStream(msg.depotSection.toByteArray(), 0, msg.depotSection.size() - 1).use { ms -> 42 | depotSection.readAsText(ms) 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/RedeemGuestPassResponseCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientRedeemGuestPassResponse 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | 9 | /** 10 | * This callback is received in response to activating a guest pass or a gift. 11 | */ 12 | @Suppress("MemberVisibilityCanBePrivate") 13 | class RedeemGuestPassResponseCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets Result of the operation 17 | */ 18 | val result: EResult 19 | 20 | /** 21 | * Gets Result of the operation 22 | */ 23 | val packageID: Int 24 | 25 | /** 26 | * Gets App ID which must be owned to activate this guest pass. 27 | */ 28 | val mustOwnAppID: Int 29 | 30 | init { 31 | val redeemedGuestPass = ClientMsgProtobuf( 32 | CMsgClientRedeemGuestPassResponse::class.java, 33 | packetMsg 34 | ) 35 | val msg = redeemedGuestPass.body 36 | 37 | jobID = redeemedGuestPass.targetJobID 38 | result = EResult.from(msg.eresult) 39 | packageID = msg.packageId 40 | mustOwnAppID = msg.mustOwnAppid 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamapps/callback/VACStatusCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamapps.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsg 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.generated.MsgClientVACBanStatus 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | import `in`.dragonbra.javasteam.util.log.LogManager 8 | import `in`.dragonbra.javasteam.util.stream.BinaryReader 9 | import java.io.ByteArrayInputStream 10 | import java.util.* 11 | 12 | /** 13 | * This callback is fired when the client receives its VAC banned status. 14 | */ 15 | class VACStatusCallback(packetMsg: IPacketMsg) : CallbackMsg() { 16 | 17 | companion object { 18 | private val logger = LogManager.getLogger(VACStatusCallback::class.java) 19 | } 20 | 21 | /** 22 | * Gets a list of VAC banned apps the client is banned from. 23 | */ 24 | val bannedApps: List 25 | 26 | init { 27 | val vacStatus = ClientMsg(MsgClientVACBanStatus::class.java, packetMsg) 28 | val msg = vacStatus.body 29 | 30 | val tempList: MutableList = ArrayList() 31 | 32 | try { 33 | BinaryReader(ByteArrayInputStream(vacStatus.payload.toByteArray())).use { br -> 34 | for (i in 0 until msg.numBans) { 35 | tempList.add(br.readInt()) 36 | } 37 | } 38 | } catch (e: Exception) { 39 | logger.error("failed to read bans", e) 40 | } 41 | 42 | bannedApps = tempList.toList() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamauthticket/TicketInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamauthticket 2 | 3 | import `in`.dragonbra.javasteam.util.Utils 4 | import java.io.Closeable 5 | 6 | /** 7 | * Represents a valid authorized session ticket. 8 | * @param handler The [SteamAuthTicket] handler. 9 | * @param appID Application the ticket was generated for. 10 | * @param ticket Bytes of the valid Session Ticket. 11 | * 12 | * @author Lossy 13 | * @since 2025-05-22 14 | */ 15 | class TicketInfo internal constructor( 16 | private val handler: SteamAuthTicket, 17 | internal val appID: Int, 18 | val ticket: ByteArray, 19 | ) : Closeable { 20 | 21 | /** 22 | * [No kDoc] 23 | */ 24 | internal val ticketCRC: Long = Utils.crc32(ticket) 25 | 26 | /** 27 | * Discards the ticket. 28 | */ 29 | override fun close() { 30 | handler.cancelAuthTicket(this) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamauthticket/callback/TicketAcceptedCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamauthticket.callback 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver 4 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 5 | import `in`.dragonbra.javasteam.types.JobID 6 | 7 | /** 8 | * This callback is fired when Steam accepts our auth ticket as valid. 9 | * 10 | * @author Lossy 11 | * @since 2025-05-22 12 | */ 13 | @Suppress("unused") 14 | class TicketAcceptedCallback( 15 | jobID: JobID, 16 | body: SteammessagesClientserver.CMsgClientAuthListAck.Builder, 17 | ) : CallbackMsg() { 18 | /** 19 | * A [List] of AppIDs of the games that have generated tickets. 20 | */ 21 | val appIDs: List = body.appIdsList 22 | 23 | /** 24 | * A [List] of CRC32 hashes of activated tickets. 25 | */ 26 | val activeTicketsCRC: List = body.ticketCrcList 27 | 28 | /** 29 | * Number of message in sequence. 30 | */ 31 | val messageSequence: Int = body.messageSequence 32 | 33 | init { 34 | this.jobID = jobID 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/AppFileChangeList.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesCloudSteamclient.CCloud_GetAppFileChangelist_Response 4 | 5 | @Suppress("unused") 6 | class AppFileChangeList(response: CCloud_GetAppFileChangelist_Response.Builder) { 7 | val currentChangeNumber: Long = response.currentChangeNumber 8 | val files: List = response.filesList.map { AppFileInfo(it) } 9 | val isOnlyDelta: Boolean = response.isOnlyDelta 10 | val pathPrefixes: List = response.pathPrefixesList 11 | val machineNames: List = response.machineNamesList 12 | val appBuildIDHwm: Long = response.appBuildidHwm 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/AppFileInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.Enums.ECloudStoragePersistState 4 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesCloudSteamclient.CCloud_AppFileInfo 5 | import java.util.Date 6 | 7 | class AppFileInfo(response: CCloud_AppFileInfo) { 8 | val filename: String = response.fileName 9 | val shaFile: ByteArray = response.shaFile.toByteArray() 10 | val timestamp: Date = Date(response.timeStamp * 1000L) 11 | val rawFileSize: Int = response.rawFileSize 12 | val persistState: ECloudStoragePersistState = response.persistState 13 | val platformsToSync: Int = response.platformsToSync 14 | val pathPrefixIndex: Int = response.pathPrefixIndex 15 | val machineNameIndex: Int = response.machineNameIndex 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/AppUploadBatchResponse.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesCloudSteamclient.CCloud_BeginAppUploadBatch_Response 4 | 5 | @Suppress("unused") 6 | class AppUploadBatchResponse(response: CCloud_BeginAppUploadBatch_Response.Builder) { 7 | val batchID: Long = response.batchId 8 | val appChangeNumber: Long = response.appChangeNumber 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/FileDownloadInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesCloudSteamclient.CCloud_ClientFileDownload_Response 4 | import java.util.Date 5 | 6 | class FileDownloadInfo(response: CCloud_ClientFileDownload_Response.Builder) { 7 | val appID: Int = response.appid 8 | val fileSize: Int = response.fileSize 9 | val rawFileSize: Int = response.rawFileSize 10 | val shaFile: ByteArray = response.shaFile.toByteArray() 11 | val timestamp: Date = Date(response.timeStamp * 1000L) 12 | val isExplicitDelete: Boolean = response.isExplicitDelete 13 | val urlHost: String = response.urlHost 14 | val urlPath: String = response.urlPath 15 | val useHttps: Boolean = response.useHttps 16 | val requestHeaders: List = response.requestHeadersList.map { HttpHeaders(it.name, it.value) } 17 | val encrypted: Boolean = response.encrypted 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/FileUploadBlockDetails.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesCloudSteamclient.ClientCloudFileUploadBlockDetails 4 | 5 | class FileUploadBlockDetails(blockDetails: ClientCloudFileUploadBlockDetails) { 6 | val urlHost: String = blockDetails.urlHost 7 | val urlPath: String = blockDetails.urlPath 8 | val useHttps: Boolean = blockDetails.useHttps 9 | val httpMethod: Int = blockDetails.httpMethod 10 | val requestHeaders: List = blockDetails.requestHeadersList.map { HttpHeaders(it.name, it.value) } 11 | val blockOffset: Long = blockDetails.blockOffset 12 | val blockLength: Int = blockDetails.blockLength 13 | val explicitBodyData: ByteArray = blockDetails.explicitBodyData.toByteArray() 14 | val mayParallelize: Boolean = blockDetails.mayParallelize 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/FileUploadInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesCloudSteamclient.CCloud_ClientBeginFileUpload_Response 4 | 5 | class FileUploadInfo(response: CCloud_ClientBeginFileUpload_Response.Builder) { 6 | val encryptFile = response.encryptFile 7 | val blockRequests = response.blockRequestsList.map { FileUploadBlockDetails(it) } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/HttpHeaders.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud 2 | 3 | data class HttpHeaders( 4 | val name: String, 5 | val value: String, 6 | ) 7 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/PendingRemoteOperation.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud 2 | 3 | import `in`.dragonbra.javasteam.enums.EOSType 4 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientObjects.CCloud_PendingRemoteOperation 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientObjects.ECloudPendingRemoteOperation 6 | 7 | class PendingRemoteOperation(operation: CCloud_PendingRemoteOperation) { 8 | val operation: ECloudPendingRemoteOperation = operation.operation 9 | val machineName: String = operation.machineName 10 | val clientId: Long = operation.clientId 11 | val timeLastUpdated: Int = operation.timeLastUpdated 12 | val osType: EOSType = EOSType.from(operation.osType) ?: EOSType.Unknown 13 | val deviceType: Int = operation.deviceType 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamcloud/callback/ShareFileCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamcloud.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverUfs.CMsgClientUFSShareFileResponse 7 | import `in`.dragonbra.javasteam.steam.handlers.steamcloud.SteamCloud 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | 10 | /** 11 | * This callback is received in response to calling [SteamCloud.shareFile]. 12 | */ 13 | class ShareFileCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the result of the request. 17 | */ 18 | val result: EResult 19 | 20 | /** 21 | * Gets the resulting UGC handle. 22 | */ 23 | val ugcId: Long 24 | 25 | init { 26 | val shareResponse = ClientMsgProtobuf( 27 | CMsgClientUFSShareFileResponse::class.java, 28 | packetMsg 29 | ) 30 | val msg = shareResponse.body 31 | 32 | jobID = shareResponse.targetJobID 33 | 34 | result = EResult.from(msg.eresult) 35 | 36 | ugcId = msg.hcontent 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/ChatMemberInfo.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends 2 | 3 | import `in`.dragonbra.javasteam.enums.EChatPermission 4 | import `in`.dragonbra.javasteam.enums.EClanPermission 5 | import `in`.dragonbra.javasteam.types.KeyValue 6 | import `in`.dragonbra.javasteam.types.MessageObject 7 | import `in`.dragonbra.javasteam.types.SteamID 8 | import java.util.* 9 | 10 | /** 11 | * Represents the details of a user which is a member of a chatroom. 12 | */ 13 | @Suppress("unused") 14 | class ChatMemberInfo : MessageObject { 15 | 16 | /** 17 | * Initializes a new instance of the [ChatMemberInfo] class. 18 | * 19 | * @param keyValues The KeyValue backing store for this member info. 20 | */ 21 | constructor(keyValues: KeyValue?) : super(keyValues) 22 | 23 | /** 24 | * Initializes a new instance of the [ChatMemberInfo] class. 25 | */ 26 | constructor() : super() 27 | 28 | /** 29 | * Gets the clan permission details of this chat member. 30 | */ 31 | val details: EnumSet 32 | get() = keyValues.get("Details").asEnum(EClanPermission::class.java, EnumSet.of(EClanPermission.Nobody)) 33 | 34 | /** 35 | * Gets the permissions this user has with the chatroom. 36 | */ 37 | val permissions: EnumSet 38 | get() = keyValues.get("Details").asEnum(EChatPermission::class.java, EChatPermission.EveryoneDefault) 39 | 40 | /** 41 | * @return the [SteamID] of this user. 42 | */ 43 | val steamID: SteamID 44 | get() = SteamID(keyValues.get("SteamID").asLong()) 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/Event.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver.CMsgClientClanState 4 | import `in`.dragonbra.javasteam.types.GameID 5 | import `in`.dragonbra.javasteam.types.GlobalID 6 | import java.util.* 7 | 8 | /** 9 | * Represents an event or announcement that was posted by a clan. 10 | */ 11 | @Suppress("unused") 12 | class Event(clanEvent: CMsgClientClanState.Event) { 13 | 14 | /** 15 | * Gets the globally unique ID for this specific event. 16 | */ 17 | val id: GlobalID = GlobalID(clanEvent.gid) 18 | 19 | /** 20 | * Gets the event time. 21 | */ 22 | val eventTime: Date = Date(clanEvent.eventTime * 1000L) 23 | 24 | /** 25 | * Gets the headline of the event. 26 | */ 27 | val headline: String = clanEvent.headline 28 | 29 | /** 30 | * Gets the [GameID] associated with this event, if any. 31 | */ 32 | val gameID: GameID = GameID(clanEvent.gameId) 33 | 34 | /** 35 | * Gets a value indicating whether this event was just posted. 36 | * 37 | * Gets **true** if the event was just posted; otherwise, **false**. 38 | */ 39 | val isJustPosted: Boolean = clanEvent.justPosted 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/Friend.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends 2 | 3 | import `in`.dragonbra.javasteam.enums.EFriendRelationship 4 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverFriends.CMsgClientFriendsList 5 | import `in`.dragonbra.javasteam.types.SteamID 6 | 7 | /** 8 | * Represents a single friend entry in a client's friendlist. 9 | */ 10 | class Friend(friend: CMsgClientFriendsList.Friend) { 11 | 12 | /** 13 | * Gets the [SteamID] of the friend. 14 | */ 15 | val steamID: SteamID = SteamID(friend.ulfriendid) 16 | 17 | /** 18 | * Gets the relationship to this friend. 19 | */ 20 | val relationship: EFriendRelationship = EFriendRelationship.from(friend.efriendrelationship) ?: EFriendRelationship.None 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/FriendMessage.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends 2 | 3 | import `in`.dragonbra.javasteam.types.SteamID 4 | import java.util.* 5 | 6 | /** 7 | * Represents a single Message sent to or received from a friend 8 | * 9 | * @param steamID The SteamID of the User that wrote the message. 10 | * @param unread Whether the message has been read, i.e., is an offline message. 11 | * @param message The actual message. 12 | * @param timestamp The time (in UTC) when the message was sent. 13 | */ 14 | class FriendMessage( 15 | val steamID: SteamID, 16 | val unread: Boolean, 17 | val message: String, 18 | val timestamp: Date, 19 | ) 20 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/NameInstance.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver.CMsgClientAMGetPersonaNameHistoryResponse 4 | import java.util.* 5 | 6 | /** 7 | * Represents a name in a name table 8 | */ 9 | class NameInstance(instance: CMsgClientAMGetPersonaNameHistoryResponse.NameTableInstance.NameInstance) { 10 | /** 11 | * Gets the name 12 | */ 13 | val name: String = instance.name 14 | 15 | /** 16 | * Gets the time stamp this name was first used 17 | */ 18 | val nameSince: Date = Date(instance.nameSince * 1000L) 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/NameTableInstance.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver.CMsgClientAMGetPersonaNameHistoryResponse 5 | import `in`.dragonbra.javasteam.types.SteamID 6 | 7 | /** 8 | * Represents a name table of an account. 9 | */ 10 | class NameTableInstance(instance: CMsgClientAMGetPersonaNameHistoryResponse.NameTableInstance) { 11 | 12 | /** 13 | * Gets the result of querying this name table 14 | */ 15 | val result: EResult = EResult.from(instance.eresult) 16 | 17 | /** 18 | * Gets the steam id this name table belongs to 19 | */ 20 | val steamID: SteamID = SteamID(instance.steamid) 21 | 22 | /** 23 | * Gets the names in this name table 24 | */ 25 | var names: List = instance.namesList.map { NameInstance(it) } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/PlayerNickname.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverFriends.CMsgClientPlayerNicknameList 4 | import `in`.dragonbra.javasteam.types.SteamID 5 | 6 | /** 7 | * Represents a nickname of a friend 8 | */ 9 | class PlayerNickname(nickname: CMsgClientPlayerNicknameList.PlayerNickname) { 10 | 11 | /** 12 | * Gets the steam id of the friend 13 | */ 14 | val steamID: SteamID = SteamID(nickname.steamid) 15 | 16 | /** 17 | * Gets the nickname of the friend 18 | */ 19 | val nickname: String = nickname.nickname 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/AliasHistoryCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver.CMsgClientAMGetPersonaNameHistoryResponse 6 | import `in`.dragonbra.javasteam.steam.handlers.steamfriends.NameTableInstance 7 | import `in`.dragonbra.javasteam.steam.handlers.steamfriends.SteamFriends 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | 10 | /** 11 | * Callback fired in response to calling [SteamFriends.requestAliasHistory]. 12 | */ 13 | class AliasHistoryCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the responses to the steam ids 17 | */ 18 | val responses: List 19 | 20 | init { 21 | val resp = ClientMsgProtobuf( 22 | CMsgClientAMGetPersonaNameHistoryResponse::class.java, 23 | packetMsg 24 | ) 25 | jobID = resp.targetJobID 26 | 27 | responses = resp.body.responsesList.map { NameTableInstance(it) } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/ChatMsgCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsg 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EChatEntryType 6 | import `in`.dragonbra.javasteam.generated.MsgClientChatMsg 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | import `in`.dragonbra.javasteam.types.SteamID 9 | import java.nio.charset.StandardCharsets 10 | 11 | /** 12 | * This callback is fired when a chat room message arrives. 13 | */ 14 | @Suppress("MemberVisibilityCanBePrivate") 15 | class ChatMsgCallback(packetMsg: IPacketMsg) : CallbackMsg() { 16 | 17 | /** 18 | * Gets the [SteamID] of the chatter. 19 | */ 20 | val chatterID: SteamID 21 | 22 | /** 23 | * Gets the [SteamID] of the chat room. 24 | */ 25 | val chatRoomID: SteamID 26 | 27 | /** 28 | * Gets chat entry type. 29 | */ 30 | val chatMsgType: EChatEntryType 31 | 32 | /** 33 | * Gets the message. 34 | */ 35 | val message: String 36 | 37 | init { 38 | val chatMsg = ClientMsg(MsgClientChatMsg::class.java, packetMsg) 39 | val msg = chatMsg.body 40 | 41 | chatterID = msg.steamIdChatter 42 | chatRoomID = msg.steamIdChatRoom 43 | 44 | chatMsgType = msg.chatMsgType 45 | 46 | // trim any extra null chars from the end 47 | val payload = chatMsg.payload 48 | message = String(payload.toByteArray(), StandardCharsets.UTF_8).replace("\u0000+$".toRegex(), "") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/ChatRoomInfoCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsg 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EChatInfoType 6 | import `in`.dragonbra.javasteam.generated.MsgClientChatRoomInfo 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | import `in`.dragonbra.javasteam.types.SteamID 9 | 10 | /** 11 | * This callback is fired in response to chat room info being received. 12 | */ 13 | class ChatRoomInfoCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets [SteamID] of the chat room. 17 | */ 18 | val chatRoomID: SteamID 19 | 20 | /** 21 | * Gets the info type. 22 | */ 23 | val type: EChatInfoType 24 | 25 | init { 26 | val roomInfo = ClientMsg(MsgClientChatRoomInfo::class.java, packetMsg) 27 | val msg = roomInfo.body 28 | 29 | chatRoomID = msg.steamIdChat 30 | type = msg.type 31 | 32 | // todo: (SK) handle inner payload based on the type similar to ChatMemberInfoCallback 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/FriendAddedCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverFriends.CMsgClientAddFriendResponse 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | import `in`.dragonbra.javasteam.types.SteamID 9 | 10 | /** 11 | * This callback is fired in response to adding a user to your friends list. 12 | */ 13 | class FriendAddedCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the result of the request. 17 | */ 18 | val result: EResult 19 | 20 | /** 21 | * Gets the [SteamID] of the friend that was added. 22 | */ 23 | val steamID: SteamID 24 | 25 | /** 26 | * Gets the persona name of the friend. 27 | */ 28 | val personaName: String 29 | 30 | init { 31 | val friendResponse = ClientMsgProtobuf( 32 | CMsgClientAddFriendResponse::class.java, 33 | packetMsg 34 | ) 35 | val msg = friendResponse.body 36 | 37 | result = EResult.from(msg.eresult) 38 | 39 | steamID = SteamID(msg.steamIdAdded) 40 | 41 | personaName = msg.personaNameAdded 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/FriendsListCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverFriends.CMsgClientFriendsList 4 | import `in`.dragonbra.javasteam.steam.handlers.steamfriends.Friend 5 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 6 | 7 | /** 8 | * This callback is fired when the client receives a list of friends. 9 | */ 10 | class FriendsListCallback(msg: CMsgClientFriendsList.Builder) : CallbackMsg() { 11 | 12 | /** 13 | * Gets a value indicating whether this [FriendsListCallback] is an incremental update. 14 | * @return **true** if incremental; otherwise, **false**. 15 | */ 16 | val isIncremental: Boolean = msg.bincremental 17 | 18 | /** 19 | * Gets the friend list. 20 | */ 21 | val friendList: List = msg.friendsList.map { Friend(it) } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/IgnoreFriendCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsg 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.generated.MsgClientSetIgnoreFriendResponse 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | 9 | /** 10 | * This callback is fired in response to an attempt at ignoring a friend. 11 | */ 12 | class IgnoreFriendCallback(packetMsg: IPacketMsg) : CallbackMsg() { 13 | 14 | /** 15 | * Gets the result of ignoring a friend. 16 | */ 17 | val result: EResult 18 | 19 | init { 20 | val response = ClientMsg(MsgClientSetIgnoreFriendResponse::class.java, packetMsg) 21 | 22 | jobID = response.targetJobID 23 | result = response.body.result 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/NicknameCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverFriends.CMsgClientSetPlayerNicknameResponse 7 | import `in`.dragonbra.javasteam.steam.handlers.steamfriends.SteamFriends 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | 10 | /** 11 | * This callback is fired in response to setting a nickname of a player by calling [SteamFriends.setFriendNickname]. 12 | */ 13 | class NicknameCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the result of setting a nickname 17 | */ 18 | val result: EResult 19 | 20 | init { 21 | val response = ClientMsgProtobuf( 22 | CMsgClientSetPlayerNicknameResponse::class.java, 23 | packetMsg 24 | ) 25 | 26 | jobID = response.targetJobID 27 | result = EResult.from(response.body.eresult) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/NicknameListCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverFriends.CMsgClientPlayerNicknameList 6 | import `in`.dragonbra.javasteam.steam.handlers.steamfriends.PlayerNickname 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | 9 | /** 10 | * This callback is fired when the client receives a list of friend nicknames. 11 | */ 12 | class NicknameListCallback(packetMsg: IPacketMsg) : CallbackMsg() { 13 | 14 | /** 15 | * Gets the list of nicknames 16 | */ 17 | val nicknames: List 18 | 19 | init { 20 | val response = ClientMsgProtobuf( 21 | CMsgClientPlayerNicknameList::class.java, 22 | packetMsg 23 | ) 24 | 25 | nicknames = response.body.nicknamesList.map { PlayerNickname(it) } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamfriends/callback/PersonaChangeCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamfriends.callback 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverFriends.CMsgPersonaChangeResponse 5 | import `in`.dragonbra.javasteam.steam.handlers.steamfriends.SteamFriends 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | import `in`.dragonbra.javasteam.types.JobID 8 | 9 | /** 10 | * This callback is fired in response to setting this client's persona name or state 11 | * with [SteamFriends.setPersonaName] or [SteamFriends.setPersonaState]. 12 | */ 13 | class PersonaChangeCallback(jobID: JobID, msg: CMsgPersonaChangeResponse.Builder) : CallbackMsg() { 14 | 15 | /** 16 | * Gets the result of changing this client's persona information. 17 | */ 18 | val result: EResult 19 | 20 | /** 21 | * Gets the name of this client according to Steam. 22 | */ 23 | val name: String 24 | 25 | init { 26 | this.jobID = jobID 27 | 28 | result = EResult.from(msg.result) 29 | name = msg.playerName 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamgameserver/LogOnDetails.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamgameserver 2 | 3 | /** 4 | * Represents the details required to log into Steam3 as a game server. 5 | * 6 | * @param token Gets or sets the authentication token used to log in as a game server. 7 | * @param appID Gets or sets the AppID this gameserver will serve. 8 | */ 9 | data class LogOnDetails(var token: String?, var appID: Int) 10 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamgameserver/StatusDetails.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamgameserver 2 | 3 | import `in`.dragonbra.javasteam.enums.EServerFlags 4 | import java.net.InetAddress 5 | import java.util.* 6 | 7 | /** 8 | * Represents the details of the game server's current status. 9 | * 10 | * @param appID Gets or sets the AppID this game server is serving. 11 | * @param serverFlags Gets or sets the server's basic state as flags. 12 | * @param gameDirectory Gets or sets the directory the game data is in. 13 | * @param address Gets or sets the IP address the game server listens on. 14 | * @param port Gets or sets the port the game server listens on. 15 | * @param queryPort Gets or sets the port the game server responds to queries on. 16 | * @param version Gets or sets the current version of the game server. 17 | */ 18 | class StatusDetails( 19 | var appID: Int, 20 | var serverFlags: EnumSet, 21 | var gameDirectory: String?, 22 | var address: InetAddress?, 23 | var port: Int, 24 | var queryPort: Int, 25 | var version: String?, 26 | ) 27 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamgameserver/callback/StatusReplyCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamgameserver.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverGameservers.CMsgGSStatusReply 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | 8 | /** 9 | * This callback is fired when the game server receives a status reply. 10 | */ 11 | class StatusReplyCallback(packetMsg: IPacketMsg) : CallbackMsg() { 12 | 13 | /** 14 | * Gets a value indicating whether this game server is VAC secure. 15 | */ 16 | val isSecure: Boolean 17 | 18 | init { 19 | val statusReply = ClientMsgProtobuf( 20 | CMsgGSStatusReply::class.java, 21 | packetMsg 22 | ) 23 | 24 | isSecure = statusReply.body.isSecure 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammasterserver/QueryDetails.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammasterserver 2 | 3 | import `in`.dragonbra.javasteam.enums.ERegionCode 4 | import java.net.InetAddress 5 | 6 | /** 7 | * Details used when performing a server list query. 8 | * 9 | * @param appID Gets or sets the AppID used when querying servers. 10 | * @param filter Gets or sets the filter used for querying the master server. Check [Master Server Query Protocol](https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol) for details on how the filter is structured. 11 | * @param region Gets or sets the region that servers will be returned from. 12 | * @param geoLocatedIP Gets or sets the IP address that will be GeoIP located. This is done to return servers closer to this location. 13 | * @param maxServers Gets or sets the maximum number of servers to return. 14 | */ 15 | data class QueryDetails( 16 | var appID: Int, 17 | var filter: String?, 18 | var region: ERegionCode, 19 | var geoLocatedIP: InetAddress?, 20 | var maxServers: Int, 21 | ) 22 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammasterserver/Server.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammasterserver 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverGameservers.CMsgGMSClientServerQueryResponse 4 | import `in`.dragonbra.javasteam.util.NetHelpers 5 | import java.net.InetSocketAddress 6 | 7 | /** 8 | * Represents a single server. 9 | */ 10 | @Suppress("unused") 11 | class Server(server: CMsgGMSClientServerQueryResponse.Server) { 12 | 13 | /** 14 | * Gets the IP endpoint of the server. 15 | */ 16 | val endPoint: InetSocketAddress = InetSocketAddress(NetHelpers.getIPAddress(server.serverIp.v4), server.queryPort) 17 | 18 | /** 19 | * Gets the number of Steam authenticated players on this server 20 | */ 21 | val authedPlayers: Int = server.authPlayers 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammasterserver/callback/QueryCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammasterserver.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverGameservers.CMsgGMSClientServerQueryResponse 6 | import `in`.dragonbra.javasteam.steam.handlers.steammasterserver.Server 7 | import `in`.dragonbra.javasteam.steam.handlers.steammasterserver.SteamMasterServer 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | 10 | /** 11 | * This callback is received in response to calling [SteamMasterServer.serverQuery]. 12 | */ 13 | class QueryCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * @return the list of servers 17 | */ 18 | val servers: List 19 | 20 | init { 21 | val queryResponse = ClientMsgProtobuf( 22 | CMsgGMSClientServerQueryResponse::class.java, 23 | packetMsg 24 | ) 25 | val msg = queryResponse.body 26 | 27 | jobID = queryResponse.targetJobID 28 | 29 | servers = msg.serversList.map { Server(it) } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/Member.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking 2 | 3 | import `in`.dragonbra.javasteam.types.SteamID 4 | 5 | /** 6 | * Represents a Steam user within a lobby. 7 | * @param steamID SteamID of the lobby member. 8 | * @param personaName Steam persona of the lobby member. 9 | * @param metadata Metadata attached to the lobby member. 10 | * 11 | * @author Lossy 12 | * @since 2025-05-21 13 | */ 14 | data class Member( 15 | val steamID: SteamID, 16 | val personaName: String, 17 | val metadata: Map = emptyMap(), 18 | ) { 19 | /** 20 | * Checks to see if this lobby member is equal to another. Only the SteamID of the lobby member is taken into account. 21 | * @return true, if obj is [Member] with a matching SteamID. Otherwise, false. 22 | */ 23 | override fun equals(other: Any?): Boolean { 24 | if (other is Member) { 25 | return steamID == other.steamID 26 | } 27 | return false 28 | } 29 | 30 | /** 31 | * Hash code of the lobby member. Only the SteamID of the lobby member is taken into account. 32 | * @return The hash code of this lobby member. 33 | */ 34 | override fun hashCode(): Int = steamID.hashCode() 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/CreateLobbyCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.SteamMatchmaking 5 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 6 | import `in`.dragonbra.javasteam.types.JobID 7 | import `in`.dragonbra.javasteam.types.SteamID 8 | 9 | /** 10 | * This callback is fired in response to [SteamMatchmaking.createLobby]. 11 | * 12 | * @param appID ID of the app the created lobby belongs to. 13 | * @param result The result of the request. 14 | * @param lobbySteamID The SteamID of the created lobby. 15 | * 16 | * @author Lossy 17 | * @since 2025-05-21 18 | */ 19 | class CreateLobbyCallback( 20 | jobID: JobID, 21 | val appID: Int, 22 | val result: EResult, 23 | val lobbySteamID: SteamID, 24 | ) : CallbackMsg() { 25 | init { 26 | this.jobID = jobID 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/GetLobbyListCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.Lobby 5 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.SteamMatchmaking 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | import `in`.dragonbra.javasteam.types.JobID 8 | 9 | /** 10 | * This callback is fired in response to [SteamMatchmaking.getLobbyList] 11 | * 12 | * @param appID ID of the app the lobbies belongs to. 13 | * @param result The result of the request. 14 | * @param lobbies The list of lobbies matching the criteria specified with [SteamMatchmaking.getLobbyList]. 15 | * 16 | * @author Lossy 17 | * @since 2025-05-21 18 | */ 19 | class GetLobbyListCallback( 20 | jobID: JobID, 21 | val appID: Int, 22 | val result: EResult, 23 | val lobbies: List, 24 | ) : CallbackMsg() { 25 | init { 26 | this.jobID = jobID 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/JoinLobbyCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.enums.EChatRoomEnterResponse 4 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.Lobby 5 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.SteamMatchmaking 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | import `in`.dragonbra.javasteam.types.JobID 8 | 9 | /** 10 | * This callback is fired in response to [SteamMatchmaking.joinLobby]. 11 | * 12 | * @param appID ID of the app the targeted lobby belongs to. 13 | * @param chatRoomEnterResponse The result of the request. 14 | * @param lobby The joined [Lobby], when [chatRoomEnterResponse] equals [EChatRoomEnterResponse.Success], otherwise null 15 | * 16 | * @author Lossy 17 | * @since 2025-05-21 18 | */ 19 | class JoinLobbyCallback( 20 | jobID: JobID, 21 | val appID: Int, 22 | val chatRoomEnterResponse: EChatRoomEnterResponse, 23 | val lobby: Lobby?, 24 | ) : CallbackMsg() { 25 | init { 26 | this.jobID = jobID 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/LeaveLobbyCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.SteamMatchmaking 5 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 6 | import `in`.dragonbra.javasteam.types.JobID 7 | import `in`.dragonbra.javasteam.types.SteamID 8 | 9 | /** 10 | * This callback is fired in response to [SteamMatchmaking.leaveLobby]. 11 | * 12 | * @param appID ID of the app the targeted lobby belongs to. 13 | * @param result The result of the request. 14 | * @param lobbySteamID The SteamID of the targeted Lobby. 15 | * 16 | * @author Lossy 17 | * @since 2025-05-21 18 | */ 19 | class LeaveLobbyCallback( 20 | jobID: JobID, 21 | val appID: Int, 22 | val result: EResult, 23 | val lobbySteamID: SteamID, 24 | ) : CallbackMsg() { 25 | init { 26 | this.jobID = jobID 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/LobbyDataCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.Lobby 4 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.SteamMatchmaking 5 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 6 | import `in`.dragonbra.javasteam.types.JobID 7 | 8 | /** 9 | * This callback is fired in response to [SteamMatchmaking.getLobbyData], 10 | * as well as whenever Steam sends us updated lobby data. 11 | * 12 | * @param appID ID of the app the updated lobby belongs to. 13 | * @param lobby The lobby that was updated. 14 | * 15 | * @author Lossy 16 | * @since 2025-05-21 17 | */ 18 | class LobbyDataCallback( 19 | jobID: JobID, 20 | val appID: Int, 21 | val lobby: Lobby, 22 | ) : CallbackMsg() { 23 | init { 24 | this.jobID = jobID 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/SetLobbyDataCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.SteamMatchmaking 5 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 6 | import `in`.dragonbra.javasteam.types.JobID 7 | import `in`.dragonbra.javasteam.types.SteamID 8 | 9 | /** 10 | * This callback is fired in response to [SteamMatchmaking.setLobbyData]. 11 | * 12 | * @param appID ID of the app the targeted lobby belongs to. 13 | * @param result The result of the request. 14 | * @param lobbySteamID The SteamID of the targeted Lobby. 15 | * 16 | * @author Lossy 17 | * @since 2025-05-21 18 | */ 19 | class SetLobbyDataCallback( 20 | jobID: JobID, 21 | val appID: Int, 22 | val result: EResult, 23 | val lobbySteamID: SteamID, 24 | ) : CallbackMsg() { 25 | init { 26 | this.jobID = jobID 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/SetLobbyOwnerCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.enums.EResult 4 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.SteamMatchmaking 5 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 6 | import `in`.dragonbra.javasteam.types.JobID 7 | import `in`.dragonbra.javasteam.types.SteamID 8 | 9 | /** 10 | * This callback is fired in response to [SteamMatchmaking.setLobbyOwner]. 11 | * 12 | * @param appID ID of the app the targeted lobby belongs to. 13 | * @param result The result of the request. 14 | * @param lobbySteamID The SteamID of the targeted Lobby. 15 | * 16 | * @author Lossy 17 | * @since 2025-05-21 18 | */ 19 | class SetLobbyOwnerCallback( 20 | jobID: JobID, 21 | val appID: Int, 22 | val result: EResult, 23 | val lobbySteamID: SteamID, 24 | ) : CallbackMsg() { 25 | init { 26 | this.jobID = jobID 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/UserJoinedLobbyCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.Member 4 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 5 | import `in`.dragonbra.javasteam.types.SteamID 6 | 7 | /** 8 | * This callback is fired whenever Steam informs us a user has joined a lobby. 9 | * 10 | * @param appID ID of the app the lobby belongs to. 11 | * @param lobbySteamID The SteamID of the lobby that a member joined. 12 | * @param user The lobby member that joined. 13 | * 14 | * @author Lossy 15 | * @since 2025-05-21 16 | */ 17 | class UserJoinedLobbyCallback( 18 | val appID: Int, 19 | val lobbySteamID: SteamID, 20 | val user: Member, 21 | ) : CallbackMsg() { 22 | // No job set declared in SK. 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steammatchmaking/callback/UserLeftLobbyCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.callback 2 | 3 | import `in`.dragonbra.javasteam.steam.handlers.steammatchmaking.Member 4 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 5 | import `in`.dragonbra.javasteam.types.SteamID 6 | 7 | /** 8 | * This callback is fired whenever Steam informs us a user has left a lobby. 9 | * 10 | * @param appID ID of the app the lobby belongs to. 11 | * @param lobbySteamID The SteamID of the lobby that a member left. 12 | * @param user The lobby member that left. 13 | * 14 | * @author Lossy 15 | * @since 2025-05-21 16 | */ 17 | class UserLeftLobbyCallback( 18 | val appID: Int, 19 | val lobbySteamID: SteamID, 20 | val user: Member, 21 | ) : CallbackMsg() { 22 | // No job set declared in SK. 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamnotifications/Notification.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamnotifications 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientUserNotifications 4 | 5 | /** 6 | * Represents a notification. 7 | */ 8 | class Notification(notification: CMsgClientUserNotifications.Notification) { 9 | /** 10 | * @return the number of notifications 11 | */ 12 | val count: Int = notification.count 13 | 14 | /** 15 | * @return the type of the notification 16 | */ 17 | val type: Int = notification.userNotificationType 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamnotifications/callback/CommentNotificationsCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamnotifications.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientCommentNotifications 6 | import `in`.dragonbra.javasteam.steam.handlers.steamnotifications.SteamNotifications 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | 9 | /** 10 | * Fired in response to calling [SteamNotifications.requestCommentNotifications]. 11 | */ 12 | class CommentNotificationsCallback(packetMsg: IPacketMsg) : CallbackMsg() { 13 | /** 14 | * @return the number of new comments 15 | */ 16 | val commentCount: Int 17 | 18 | /** 19 | * @return the number of new comments on the users profile 20 | */ 21 | val commentOwnerCount: Int 22 | 23 | /** 24 | * @return the number of new comments on subscribed threads 25 | */ 26 | val commentSubscriptionsCount: Int 27 | 28 | init { 29 | val resp = ClientMsgProtobuf( 30 | CMsgClientCommentNotifications::class.java, 31 | packetMsg 32 | ) 33 | val msg = resp.body 34 | 35 | commentCount = msg.countNewComments 36 | commentOwnerCount = msg.countNewCommentsOwner 37 | commentSubscriptionsCount = msg.countNewCommentsSubscriptions 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamnotifications/callback/ItemAnnouncementsCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamnotifications.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientItemAnnouncements 6 | import `in`.dragonbra.javasteam.steam.handlers.steamnotifications.SteamNotifications 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | 9 | /** 10 | * Fired in response to calling [SteamNotifications.requestItemAnnouncements]. 11 | */ 12 | class ItemAnnouncementsCallback(packetMsg: IPacketMsg) : CallbackMsg() { 13 | /** 14 | * @return the number of new items 15 | */ 16 | val count: Int 17 | 18 | init { 19 | val resp = ClientMsgProtobuf( 20 | CMsgClientItemAnnouncements::class.java, 21 | packetMsg 22 | ) 23 | val msg = resp.body 24 | 25 | count = msg.countNewItems 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamnotifications/callback/OfflineMessageNotificationCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamnotifications.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientOfflineMessageNotification 6 | import `in`.dragonbra.javasteam.steam.handlers.steamnotifications.SteamNotifications 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | import `in`.dragonbra.javasteam.types.SteamID 9 | 10 | /** 11 | * Fired in response to calling [SteamNotifications.requestOfflineMessageCount]. 12 | */ 13 | class OfflineMessageNotificationCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Get the number of new messages 17 | */ 18 | val messageCount: Int 19 | 20 | /** 21 | * Gets the ids of friends the new messages belong to 22 | */ 23 | val friendsWithOfflineMessages: List 24 | 25 | init { 26 | val resp = ClientMsgProtobuf( 27 | CMsgClientOfflineMessageNotification::class.java, 28 | packetMsg 29 | ) 30 | val msg = resp.body 31 | 32 | messageCount = msg.offlineMessages 33 | friendsWithOfflineMessages = msg.friendsWithOfflineMessagesList.map { SteamID(it.toLong()) } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamnotifications/callback/UserNotificationsCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamnotifications.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientUserNotifications 6 | import `in`.dragonbra.javasteam.steam.handlers.steamnotifications.Notification 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | 9 | /** 10 | * Fired when the client receives user notifications. 11 | */ 12 | class UserNotificationsCallback(packetMsg: IPacketMsg) : CallbackMsg() { 13 | 14 | /** 15 | * Gets the notifications 16 | */ 17 | val notifications: List 18 | 19 | init { 20 | val resp = ClientMsgProtobuf( 21 | CMsgClientUserNotifications::class.java, 22 | packetMsg 23 | ) 24 | val msg = resp.body 25 | 26 | notifications = msg.notificationsList.map(::Notification) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamscreenshots/ScreenshotDetails.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamscreenshots 2 | 3 | import `in`.dragonbra.javasteam.enums.EUCMFilePrivacyState 4 | import `in`.dragonbra.javasteam.types.GameID 5 | import java.util.* 6 | 7 | /** 8 | * Represents the details required to add a screenshot 9 | * 10 | * @param gameID Gets or sets the Steam game ID this screenshot belongs to 11 | * @param ufsImageFilePath Gets or sets the UFS image filepath. 12 | * @param usfThumbnailFilePath Gets or sets the UFS thumbnail filepath. 13 | * @param caption Gets or sets the screenshot caption 14 | * @param privacy Gets or sets the screenshot privacy 15 | * @param width Gets or sets the screenshot width 16 | * @param height Gets or sets the screenshot height 17 | * @param creationTime Gets or sets the creation time 18 | * @param isContainsSpoilers Gets or sets whether the screenshot contains spoilers 19 | */ 20 | data class ScreenshotDetails( 21 | var gameID: GameID?, 22 | var ufsImageFilePath: String?, 23 | var usfThumbnailFilePath: String?, 24 | var caption: String?, 25 | var privacy: EUCMFilePrivacyState, 26 | var width: Int, 27 | var height: Int, 28 | var creationTime: Date, 29 | var isContainsSpoilers: Boolean, 30 | ) 31 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamscreenshots/callback/ScreenshotAddedCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamscreenshots.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverUcm.CMsgClientUCMAddScreenshotResponse 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | import `in`.dragonbra.javasteam.types.UGCHandle 9 | 10 | /** 11 | * This callback is fired when a new screenshot is added. 12 | */ 13 | @Suppress("MemberVisibilityCanBePrivate") 14 | class ScreenshotAddedCallback(packetMsg: IPacketMsg) : CallbackMsg() { 15 | 16 | /** 17 | * Gets the result. 18 | */ 19 | val result: EResult 20 | 21 | /** 22 | * Gets the screenshot ID of the newly added screenshot. 23 | */ 24 | val screenshotID: UGCHandle 25 | 26 | init { 27 | val resp = ClientMsgProtobuf( 28 | CMsgClientUCMAddScreenshotResponse::class.java, 29 | packetMsg 30 | ) 31 | val msg = resp.body 32 | 33 | jobID = resp.targetJobID 34 | 35 | result = EResult.from(msg.eresult) 36 | screenshotID = UGCHandle(msg.screenshotid) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamunifiedmessages/callback/ServiceMethodNotification.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback 2 | 3 | import com.google.protobuf.AbstractMessage 4 | import com.google.protobuf.GeneratedMessage 5 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 6 | import `in`.dragonbra.javasteam.base.PacketClientMsgProtobuf 7 | import `in`.dragonbra.javasteam.steam.handlers.steamunifiedmessages.SteamUnifiedMessages 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | import `in`.dragonbra.javasteam.types.JobID 10 | 11 | /** 12 | * @author Lossy 13 | * @since 2024-10-22 14 | * 15 | * This callback represents a service notification received though [SteamUnifiedMessages]. 16 | */ 17 | @Suppress("MemberVisibilityCanBePrivate") 18 | class ServiceMethodNotification>( 19 | clazz: Class, 20 | packetMsg: PacketClientMsgProtobuf, 21 | ) : CallbackMsg() { 22 | 23 | /** 24 | * The name of the job, in the format Service.Method#Version. 25 | */ 26 | val jobName: String 27 | 28 | /** 29 | * The protobuf body. 30 | */ 31 | val body: T 32 | 33 | init { 34 | jobID = JobID.INVALID 35 | jobName = packetMsg.header.proto.targetJobName 36 | body = ClientMsgProtobuf(clazz, packetMsg).body 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamunifiedmessages/callback/ServiceMethodResponse.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback 2 | 3 | import com.google.protobuf.AbstractMessage 4 | import com.google.protobuf.GeneratedMessage 5 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 6 | import `in`.dragonbra.javasteam.base.PacketClientMsgProtobuf 7 | import `in`.dragonbra.javasteam.enums.EResult 8 | import `in`.dragonbra.javasteam.steam.handlers.steamunifiedmessages.SteamUnifiedMessages 9 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 10 | import `in`.dragonbra.javasteam.types.JobID 11 | 12 | /** 13 | * @author Lossy 14 | * @since 2024-10-22 15 | * 16 | * This callback is returned in response to a service method sent through [SteamUnifiedMessages]. 17 | */ 18 | @Suppress("MemberVisibilityCanBePrivate") 19 | class ServiceMethodResponse>( 20 | clazz: Class, 21 | packetMsg: PacketClientMsgProtobuf, 22 | ) : CallbackMsg() { 23 | 24 | /** 25 | * The result of the message. 26 | */ 27 | val result: EResult 28 | 29 | /** 30 | * The protobuf body. 31 | */ 32 | val body: T 33 | 34 | init { 35 | val protoHeader = packetMsg.header.proto 36 | jobID = JobID(protoHeader.jobidTarget) 37 | result = EResult.from(protoHeader.eresult) 38 | body = ClientMsgProtobuf(clazz, packetMsg).body 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/AnonymousLogOnDetails.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuser 2 | 3 | import `in`.dragonbra.javasteam.enums.EOSType 4 | import `in`.dragonbra.javasteam.util.Utils 5 | 6 | /** 7 | * Represents the details required to log into Steam3 as an anonymous user. 8 | * 9 | * @param cellID Gets or sets the CellID. 10 | * @param clientOSType Gets or sets the client operating system type. 11 | * @param clientLanguage Gets or sets the client language. 12 | */ 13 | data class AnonymousLogOnDetails( 14 | var cellID: Int? = null, 15 | var clientOSType: EOSType = Utils.getOSType(), 16 | var clientLanguage: String = "english", 17 | ) 18 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/ChatMode.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuser 2 | 3 | /** 4 | * Represents the chat mode for logging into Steam. 5 | */ 6 | @Suppress("unused") 7 | enum class ChatMode(val mode: Int) { 8 | /** 9 | * The default chat mode. 10 | */ 11 | DEFAULT(0), 12 | 13 | /** 14 | * The chat mode for new Steam group chat. 15 | */ 16 | NEW_STEAM_CHAT(2), 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/EmailAddrInfoCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuser.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientEmailAddrInfo 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | 8 | /** 9 | * This callback is received when email information is received from the network. 10 | */ 11 | @Suppress("MemberVisibilityCanBePrivate") 12 | class EmailAddrInfoCallback(packetMsg: IPacketMsg) : CallbackMsg() { 13 | 14 | /** 15 | * Gets the email address of this account. 16 | */ 17 | val emailAddress: String 18 | 19 | /** 20 | * Gets a value indicating validated email or not. 21 | */ 22 | val isEmailValidated: Boolean 23 | 24 | init { 25 | val emailAddrInfo = ClientMsgProtobuf( 26 | CMsgClientEmailAddrInfo::class.java, 27 | packetMsg 28 | ) 29 | val msg = emailAddrInfo.body 30 | 31 | emailAddress = msg.emailAddress 32 | isEmailValidated = msg.emailIsValidated 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/LoggedOffCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuser.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsg 4 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 5 | import `in`.dragonbra.javasteam.base.IPacketMsg 6 | import `in`.dragonbra.javasteam.enums.EResult 7 | import `in`.dragonbra.javasteam.generated.MsgClientLoggedOff 8 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverLogin 9 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 10 | 11 | /** 12 | * This callback is returned when the client is told to log off by the server. 13 | */ 14 | class LoggedOffCallback(packetMsg: IPacketMsg) : CallbackMsg() { 15 | 16 | /** 17 | * Gets the result of the log off. 18 | */ 19 | val result: EResult 20 | 21 | init { 22 | if (packetMsg.isProto) { 23 | val loggedOff = ClientMsgProtobuf( 24 | SteammessagesClientserverLogin.CMsgClientLoggedOff::class.java, 25 | packetMsg 26 | ) 27 | result = EResult.from(loggedOff.body.eresult) 28 | } else { 29 | val loggedOff = ClientMsg(MsgClientLoggedOff::class.java, packetMsg) 30 | result = loggedOff.body.result 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/PlayingSessionStateCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuser.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | 8 | /** 9 | * This callback is received when another client starts or stops playing a game. 10 | * While blocked, sending ClientGamesPlayed message will log you off with LoggedInElsewhere result. 11 | */ 12 | @Suppress("MemberVisibilityCanBePrivate") 13 | class PlayingSessionStateCallback(packetMsg: IPacketMsg) : CallbackMsg() { 14 | 15 | /** 16 | * Indicates whether playing is currently blocked by another client. 17 | */ 18 | val isPlayingBlocked: Boolean 19 | 20 | /** 21 | * When blocked, gets the appid which is currently being played. 22 | */ 23 | val playingAppID: Int 24 | 25 | init { 26 | val playingSessionState = ClientMsgProtobuf( 27 | SteammessagesClientserver2.CMsgClientPlayingSessionState::class.java, 28 | packetMsg 29 | ) 30 | val msg = playingSessionState.body 31 | 32 | jobID = playingSessionState.targetJobID 33 | 34 | isPlayingBlocked = msg.playingBlocked 35 | playingAppID = msg.playingApp 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/SessionTokenCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuser.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver.CMsgClientSessionToken 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | 8 | /** 9 | * This callback is fired when the client receives its unique Steam3 session token. 10 | * This token is used for authenticated content downloading in Steam2. 11 | */ 12 | class SessionTokenCallback(packetMsg: IPacketMsg) : CallbackMsg() { 13 | 14 | /** 15 | * @return the Steam3 session token used for authenticating to various other services. 16 | */ 17 | val sessionToken: Long 18 | 19 | init { 20 | val sessToken = ClientMsgProtobuf(CMsgClientSessionToken::class.java, packetMsg) 21 | 22 | sessionToken = sessToken.body.token 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/VanityURLChangedCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuser.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgClientVanityURLChangedNotification 6 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 7 | 8 | /** 9 | * This callback is received when users' vanity url changes. 10 | */ 11 | class VanityURLChangedCallback(packetMsg: IPacketMsg) : CallbackMsg() { 12 | 13 | /** 14 | * Gets the new vanity url. 15 | */ 16 | val vanityUrl: String 17 | 18 | init { 19 | val vanityUrl = ClientMsgProtobuf( 20 | CMsgClientVanityURLChangedNotification::class.java, 21 | packetMsg 22 | ) 23 | 24 | jobID = vanityUrl.targetJobID 25 | 26 | this.vanityUrl = vanityUrl.body.vanityUrl 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuser/callback/WebAPIUserNonceCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuser.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverLogin.CMsgClientRequestWebAPIAuthenticateUserNonceResponse 7 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 8 | 9 | /** 10 | * This callback is received when requesting a new WebAPI authentication user nonce. 11 | */ 12 | class WebAPIUserNonceCallback(packetMsg: IPacketMsg) : CallbackMsg() { 13 | 14 | /** 15 | * Gets the result of the request. 16 | */ 17 | val result: EResult 18 | 19 | /** 20 | * Gets the authentication nonce. 21 | */ 22 | val nonce: String 23 | 24 | init { 25 | val userNonce = ClientMsgProtobuf( 26 | CMsgClientRequestWebAPIAuthenticateUserNonceResponse::class.java, 27 | packetMsg 28 | ) 29 | val body = userNonce.body 30 | 31 | jobID = userNonce.targetJobID 32 | 33 | result = EResult.from(body.eresult) 34 | nonce = body.webapiAuthenticateUserNonce 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuserstats/callback/LeaderboardEntriesCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuserstats.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserverLbs.CMsgClientLBSGetLBEntriesResponse 7 | import `in`.dragonbra.javasteam.steam.handlers.steamuserstats.LeaderboardEntry 8 | import `in`.dragonbra.javasteam.steam.handlers.steamuserstats.SteamUserStats 9 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 10 | 11 | /** 12 | * This callback is fired in response to [SteamUserStats.getLeaderboardEntries]. 13 | */ 14 | @Suppress("MemberVisibilityCanBePrivate") 15 | class LeaderboardEntriesCallback(packetMsg: IPacketMsg?) : CallbackMsg() { 16 | 17 | /** 18 | * Gets the result of the request. 19 | */ 20 | val result: EResult 21 | 22 | /** 23 | * Gets how many entries there are for requested leaderboard. 24 | */ 25 | val entryCount: Int 26 | 27 | /** 28 | * Gets the list of leaderboard entries this response contains. 29 | */ 30 | val entries: List 31 | 32 | init { 33 | val msg = ClientMsgProtobuf( 34 | CMsgClientLBSGetLBEntriesResponse::class.java, 35 | packetMsg 36 | ) 37 | val resp = msg.body 38 | 39 | jobID = msg.targetJobID 40 | 41 | result = EResult.from(resp.eresult) 42 | entryCount = resp.leaderboardEntryCount 43 | 44 | entries = resp.entriesList.map { LeaderboardEntry(it) } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamuserstats/callback/NumberOfPlayersCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamuserstats.callback 2 | 3 | import `in`.dragonbra.javasteam.base.ClientMsgProtobuf 4 | import `in`.dragonbra.javasteam.base.IPacketMsg 5 | import `in`.dragonbra.javasteam.enums.EResult 6 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesClientserver2.CMsgDPGetNumberOfCurrentPlayersResponse 7 | import `in`.dragonbra.javasteam.steam.handlers.steamuserstats.SteamUserStats 8 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 9 | 10 | /** 11 | * This callback is fired in response to [SteamUserStats.getNumberOfCurrentPlayers]. 12 | */ 13 | @Suppress("MemberVisibilityCanBePrivate") 14 | class NumberOfPlayersCallback(packetMsg: IPacketMsg?) : CallbackMsg() { 15 | 16 | /** 17 | * Gets the result of the request. 18 | */ 19 | val result: EResult 20 | 21 | /** 22 | * Gets the current number of players according to Steam. 23 | */ 24 | val numPlayers: Int 25 | 26 | init { 27 | val msg = ClientMsgProtobuf( 28 | CMsgDPGetNumberOfCurrentPlayersResponse::class.java, 29 | packetMsg 30 | ) 31 | val resp = msg.body 32 | 33 | jobID = msg.targetJobID 34 | result = EResult.from(resp.eresult) 35 | numPlayers = resp.playerCount 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/handlers/steamworkshop/EnumerationUserDetails.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.handlers.steamworkshop 2 | 3 | import `in`.dragonbra.javasteam.enums.EWorkshopFileAction 4 | 5 | /** 6 | * Represents the details of an enumeration request used for the local user's files. 7 | * 8 | * @param appID Gets or Sets the AppID of the workshop to enumerate. 9 | * @param startIndex Gets or Sets the start index. 10 | * @param userAction Gets or Sets the user action to filter by. This value is only used by [SteamWorkshop.enumeratePublishedFilesByUserAction] 11 | */ 12 | data class EnumerationUserDetails( 13 | var appID: Int, 14 | var startIndex: Int, 15 | var userAction: EWorkshopFileAction, 16 | ) 17 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/steamclient/AsyncJobFailedException.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.steamclient 2 | 3 | /** 4 | * Thrown when Steam encounters a remote error with a pending [in.dragonbra.javasteam.types.AsyncJob]. 5 | * @author Lossy 6 | * @since 2023-03-17 7 | */ 8 | @Suppress("unused") 9 | class AsyncJobFailedException : Exception { 10 | constructor() : super() 11 | constructor(message: String?) : super(message) 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/steamclient/callbackmgr/Callback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.steamclient.callbackmgr 2 | 3 | import `in`.dragonbra.javasteam.types.JobID 4 | import `in`.dragonbra.javasteam.util.compat.Consumer 5 | import java.io.Closeable 6 | 7 | @Suppress("unused") 8 | class Callback @JvmOverloads constructor( 9 | override val callbackType: Class, 10 | private val onRun: Consumer?, 11 | private var mgr: CallbackManager? = null, 12 | val jobID: JobID = JobID.INVALID, 13 | ) : CallbackBase(), 14 | Closeable { 15 | 16 | init { 17 | mgr?.register(this) 18 | } 19 | 20 | override fun close() { 21 | mgr?.unregister(this) 22 | mgr = null 23 | } 24 | 25 | @Suppress("UNCHECKED_CAST") 26 | override fun run(callback: Any) { 27 | val cb = callback as TCall 28 | if ((cb.jobID == jobID || jobID == JobID.INVALID) && onRun != null) { 29 | onRun.accept(cb) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/steamclient/callbackmgr/CallbackBase.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.steamclient.callbackmgr 2 | 3 | /** 4 | * This is the base class for the utility [Callback] class. 5 | * This is for internal use only, and shouldn't be used directly. 6 | */ 7 | abstract class CallbackBase { 8 | abstract val callbackType: Class<*> 9 | abstract fun run(callback: Any) 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/steamclient/callbackmgr/CallbackMsg.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.steamclient.callbackmgr 2 | 3 | import `in`.dragonbra.javasteam.types.JobID 4 | 5 | /** 6 | * Represents the base object all callbacks are based off. 7 | * 8 | * @constructor Initializes a new instance of the [CallbackMsg] class. 9 | * 10 | * @author lngtr 11 | * @since 2018-02-22 12 | */ 13 | abstract class CallbackMsg { 14 | 15 | /** 16 | * Gets or sets the job ID this callback refers to. If it is not a job callback, it will be [JobID.INVALID]. 17 | */ 18 | var jobID: JobID = JobID.INVALID 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/steamclient/callbacks/ConnectedCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.steamclient.callbacks 2 | 3 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 4 | 5 | /** 6 | * This callback is received after attempting to connect to the Steam network. 7 | */ 8 | class ConnectedCallback : CallbackMsg() 9 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/steamclient/callbacks/DisconnectedCallback.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.steamclient.callbacks 2 | 3 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 4 | 5 | /** 6 | * This callback is received when the steamclient is physically disconnected from the Steam network. 7 | * 8 | * @constructor If true, the disconnection was initiated by calling [CMClient.disconnect]. 9 | * If false, the disconnection was the cause of something not user-controlled, such as a network failure or 10 | * a forcible disconnection by the remote server. 11 | */ 12 | class DisconnectedCallback(val isUserInitiated: Boolean) : CallbackMsg() 13 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/steamclient/configuration/SteamConfigurationState.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.steamclient.configuration 2 | 3 | import `in`.dragonbra.javasteam.enums.EClientPersonaStateFlag 4 | import `in`.dragonbra.javasteam.enums.EUniverse 5 | import `in`.dragonbra.javasteam.networking.steam3.IConnectionFactory 6 | import `in`.dragonbra.javasteam.networking.steam3.ProtocolTypes 7 | import `in`.dragonbra.javasteam.steam.contentdownloader.IManifestProvider 8 | import `in`.dragonbra.javasteam.steam.discovery.IServerListProvider 9 | import okhttp3.OkHttpClient 10 | import java.util.* 11 | 12 | /** 13 | * @author lngtr 14 | * @since 2018-02-20 15 | */ 16 | data class SteamConfigurationState( 17 | var connectionFactory: IConnectionFactory, 18 | var isAllowDirectoryFetch: Boolean, 19 | var cellID: Int, 20 | var connectionTimeout: Long, 21 | var defaultPersonaStateFlags: EnumSet, 22 | var httpClient: OkHttpClient, 23 | var protocolTypes: EnumSet, 24 | var serverListProvider: IServerListProvider, 25 | var depotManifestProvider: IManifestProvider, 26 | var universe: EUniverse, 27 | var webAPIBaseAddress: String, 28 | var webAPIKey: String?, 29 | ) 30 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/steam/webapi/ContentServerDirectoryService.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.steam.webapi 2 | 3 | import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesContentsystemSteamclient.CContentServerDirectory_GetServersForSteamPipe_Response 4 | import `in`.dragonbra.javasteam.steam.cdn.Server 5 | 6 | /** 7 | * Helper class to load servers from the Content Server Directory Service Web API. 8 | */ 9 | object ContentServerDirectoryService { 10 | 11 | internal fun convertServerList( 12 | response: CContentServerDirectory_GetServersForSteamPipe_Response, 13 | ): List = response.serversList.map { child -> 14 | val httpsSupport = child.httpsSupport 15 | val protocol = if (httpsSupport == "mandatory") { 16 | Server.ConnectionProtocol.HTTPS 17 | } else { 18 | Server.ConnectionProtocol.HTTP 19 | } 20 | 21 | Server( 22 | protocol = protocol, 23 | host = child.host, 24 | vHost = child.vhost, 25 | port = if (protocol == Server.ConnectionProtocol.HTTPS) 443 else 80, 26 | type = child.type, 27 | sourceID = child.sourceId, 28 | cellID = child.cellId, 29 | load = child.load, 30 | weightedLoad = child.weightedLoad, 31 | numEntries = child.numEntriesInClientList, 32 | steamChinaOnly = child.steamChinaOnly, 33 | useAsProxy = child.useAsProxy, 34 | proxyRequestPathTemplate = child.proxyRequestPathTemplate, 35 | allowedAppIds = child.allowedAppIdsList.toIntArray() 36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/types/AsyncJob.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.types 2 | 3 | import `in`.dragonbra.javasteam.steam.steamclient.SteamClient 4 | import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackMsg 5 | import java.time.Instant 6 | 7 | /** 8 | * The base class for awaitable versions of a [JobID]. 9 | * Should not be used or constructed directly, but rather with [AsyncJobSingle] or [AsyncJobMultiple] 10 | * 11 | * @author Lossy 12 | * @since 2023-03-17 13 | */ 14 | abstract class AsyncJob(val client: SteamClient, val jobID: JobID) { 15 | 16 | private var jobStart = Instant.now() 17 | 18 | var timeout: Long = 10000 // 10 Seconds 19 | 20 | val isTimedOut: Boolean 21 | get() = Instant.now() >= jobStart.plusMillis(timeout) 22 | 23 | protected fun registerJob(client: SteamClient) { 24 | client.startJob(this) 25 | } 26 | 27 | abstract fun addResult(callback: CallbackMsg): Boolean 28 | 29 | abstract fun setFailed(dueToRemoteFailure: Boolean) 30 | 31 | fun heartbeat() { 32 | timeout += 10000 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/types/BitVector64.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.types; 2 | 3 | public class BitVector64 { 4 | 5 | private Long data; 6 | 7 | public BitVector64() { 8 | } 9 | 10 | public BitVector64(long value) { 11 | data = value; 12 | } 13 | 14 | public Long getData() { 15 | return data; 16 | } 17 | 18 | public void setData(Long data) { 19 | this.data = data; 20 | } 21 | 22 | public long getMask(short bitOffset, long valueMask) { 23 | return data >> bitOffset & valueMask; 24 | } 25 | 26 | public void setMask(short bitOffset, long valueMask, long value) { 27 | data = (data & ~(valueMask << bitOffset)) | ((value & valueMask) << bitOffset); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/types/ChunkData.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.types 2 | 3 | /** 4 | * Represents a single chunk within a file. 5 | * 6 | * @constructor Initializes a new instance of the [ChunkData] class. 7 | * @constructor Initializes a new instance of the [ChunkData] class with specified values. 8 | * 9 | * @param chunkID Gets or sets the SHA-1 hash chunk id. 10 | * @param checksum Gets or sets the expected Adler32 checksum of this chunk. 11 | * @param offset Gets or sets the chunk offset. 12 | * @param compressedLength Gets or sets the compressed length of this chunk. 13 | * @param uncompressedLength Gets or sets the decompressed length of this chunk. 14 | */ 15 | @Suppress("ArrayInDataClass") 16 | data class ChunkData( 17 | var chunkID: ByteArray? = null, 18 | var checksum: Int = 0, 19 | var offset: Long = 0, 20 | var compressedLength: Int = 0, 21 | var uncompressedLength: Int = 0, 22 | ) 23 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/types/JobID.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.types; 2 | 3 | /** 4 | * Represents an identifier of a network task known as a job. 5 | */ 6 | public class JobID extends GlobalID { 7 | 8 | /** 9 | * Represents an invalid JobID. 10 | */ 11 | public static final JobID INVALID = new JobID(); 12 | 13 | /** 14 | * Initializes a new instance of the {@link JobID} class. 15 | */ 16 | public JobID() { 17 | super(); 18 | } 19 | 20 | /** 21 | * Initializes a new instance of the {@link JobID} class. 22 | * 23 | * @param gid The Job ID to initialize this instance with. 24 | */ 25 | public JobID(long gid) { 26 | super(gid); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/types/UGCHandle.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.types; 2 | 3 | /** 4 | * Represents a single unique handle to a piece of User Generated Content. 5 | */ 6 | public class UGCHandle extends GlobalID { 7 | 8 | /** 9 | * Initializes a new instance of the {@link UGCHandle} class. 10 | */ 11 | public UGCHandle() { 12 | super(); 13 | } 14 | 15 | /** 16 | * Initializes a new instance of the {@link UGCHandle} class. 17 | * 18 | * @param ugcId The UGC ID. 19 | */ 20 | public UGCHandle(long ugcId) { 21 | super(ugcId); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/CollectionUtils.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import in.dragonbra.javasteam.util.compat.ObjectsCompat; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * @author lngtr 9 | * @since 2018-02-19 10 | */ 11 | public class CollectionUtils { 12 | public static T getKeyByValue(Map map, E value) { 13 | for (Map.Entry entry : map.entrySet()) { 14 | if (ObjectsCompat.equals(value, entry.getValue())) { 15 | return entry.getKey(); 16 | } 17 | } 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/IDebugNetworkListener.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import in.dragonbra.javasteam.enums.EMsg; 4 | 5 | /** 6 | * This is a debug utility, do not use it to implement your business logic. 7 | *

8 | * This interface is used for logging network messages sent to and received from the Steam server that the client is connected to. 9 | */ 10 | public interface IDebugNetworkListener { 11 | 12 | /** 13 | * Called when a packet is received from the Steam server. 14 | * 15 | * @param msgType Network message type of this packet message. 16 | * @param data Raw packet data that was received. 17 | */ 18 | void onIncomingNetworkMessage(EMsg msgType, byte[] data); 19 | 20 | /** 21 | * Called when a packet is about to be sent to the Steam server. 22 | * 23 | * @param msgType Network message type of this packet message. 24 | * @param data Raw packet data that was received. 25 | */ 26 | void onOutgoingNetworkMessage(EMsg msgType, byte[] data); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/Passable.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.util 2 | 3 | class Passable @JvmOverloads constructor(var value: T? = null) 4 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/SteamKitWebRequestException.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.util 2 | 3 | import okhttp3.Headers 4 | import okhttp3.Response 5 | import java.lang.Exception 6 | 7 | /** 8 | * Thrown when an HTTP request fails. 9 | */ 10 | @Suppress("MemberVisibilityCanBePrivate") 11 | class SteamKitWebRequestException(message: String) : Exception(message) { 12 | 13 | /** 14 | * Represents the status code of the HTTP response. 15 | */ 16 | var statusCode: Int = 0 17 | 18 | /** 19 | * Represents the collection of HTTP response headers. 20 | */ 21 | var headers: Headers? = null 22 | private set 23 | 24 | /** 25 | * Initializes a new instance of the [SteamKitWebRequestException] class. 26 | * @param message The message that describes the error. 27 | * @param response HTTP response message including the status code and data. 28 | */ 29 | constructor(message: String, response: Response) : this(message) { 30 | statusCode = response.code 31 | headers = response.headers 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/Strings.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import java.math.BigInteger; 4 | 5 | /** 6 | * @author lngtr 7 | * @since 2018-02-19 8 | */ 9 | public class Strings { 10 | 11 | /** 12 | * the constant 2^64 13 | */ 14 | private static final BigInteger TWO_64 = BigInteger.ONE.shiftLeft(64); 15 | 16 | public static boolean isNullOrEmpty(String str) { 17 | return str == null || str.isEmpty(); 18 | } 19 | 20 | public String asUnsignedDecimalString(long l) { 21 | BigInteger b = BigInteger.valueOf(l); 22 | if (b.signum() < 0) { 23 | b = b.add(TWO_64); 24 | } 25 | return b.toString(); 26 | } 27 | 28 | private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); 29 | 30 | public static String toHex(byte[] bytes) { 31 | char[] hexChars = new char[bytes.length * 2]; 32 | for (int j = 0; j < bytes.length; j++) { 33 | int v = bytes[j] & 0xFF; 34 | hexChars[j * 2] = HEX_ARRAY[v >>> 4]; 35 | hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; 36 | } 37 | return new String(hexChars); 38 | } 39 | 40 | public static byte[] decodeHex(String s) { 41 | int len = s.length(); 42 | byte[] data = new byte[len / 2]; 43 | for (int i = 0; i < len; i += 2) { 44 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 45 | + Character.digit(s.charAt(i + 1), 16)); 46 | } 47 | return data; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/WebHelpers.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | 5 | /** 6 | * @author lngtr 7 | * @since 2018-04-16 8 | */ 9 | public class WebHelpers { 10 | 11 | private static boolean isUrlSafeChar(char ch) { 12 | return ch >= 'a' && ch <= 'z' || 13 | ch >= 'A' && ch <= 'Z' || 14 | ch >= '0' && ch <= '9' || 15 | ch == '-' || 16 | ch == '.' || 17 | ch == '_'; 18 | } 19 | 20 | public static String urlEncode(String input) { 21 | return urlEncode(input.getBytes(StandardCharsets.UTF_8)); 22 | } 23 | 24 | public static String urlEncode(byte[] input) { 25 | StringBuilder encoded = new StringBuilder(input.length * 2); 26 | 27 | for (byte i : input) { 28 | char inch = (char) i; 29 | 30 | if (isUrlSafeChar(inch)) { 31 | encoded.append(inch); 32 | } else if (inch == ' ') { 33 | encoded.append('+'); 34 | } else { 35 | encoded.append(String.format("%%%02X", i)); 36 | } 37 | } 38 | 39 | return encoded.toString(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/ZipUtil.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.util 2 | 3 | import `in`.dragonbra.javasteam.util.compat.readNBytesCompat 4 | import `in`.dragonbra.javasteam.util.stream.MemoryStream 5 | import java.util.zip.ZipInputStream 6 | 7 | object ZipUtil { 8 | 9 | @JvmStatic 10 | fun decompress(ms: MemoryStream, destination: ByteArray, verifyChecksum: Boolean = true): Int { 11 | ZipInputStream(ms, Charsets.UTF_8).use { zip -> 12 | val entry = zip.nextEntry 13 | ?: throw IllegalArgumentException("Did not find any zip entries in the given stream") 14 | 15 | val sizeDecompressed = entry.size.toInt() 16 | 17 | if (destination.size < sizeDecompressed) { 18 | throw IllegalArgumentException("The destination buffer is smaller than the decompressed data size.") 19 | } 20 | 21 | val bytesRead = zip.readNBytesCompat(destination, 0, sizeDecompressed) 22 | 23 | if (zip.nextEntry != null) { 24 | throw IllegalArgumentException("Given stream should only contain one zip entry") 25 | } 26 | 27 | if (verifyChecksum && Utils.crc32(destination.sliceArray(0 until sizeDecompressed)) != entry.crc) { 28 | throw Exception("Checksum validation failed for decompressed file") 29 | } 30 | 31 | return bytesRead 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/compat/ByteArrayOutputStreamCompat.kt: -------------------------------------------------------------------------------- 1 | package `in`.dragonbra.javasteam.util.compat 2 | 3 | import java.io.ByteArrayOutputStream 4 | 5 | /** 6 | * Compatibility class to provide compatibility with Java ByteArrayOutputStream. 7 | * 8 | * @author Lossy 9 | * @since 30/12/2024 10 | */ 11 | object ByteArrayOutputStreamCompat { 12 | 13 | @JvmStatic 14 | fun toString(byteArrayOutputStream: ByteArrayOutputStream): String = 15 | String(byteArrayOutputStream.toByteArray(), 0, byteArrayOutputStream.size()) 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/compat/Consumer.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.compat; 2 | 3 | public interface Consumer { 4 | void accept(T t); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/compat/ObjectsCompat.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.compat; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * @author steev 7 | * @since 2018-03-21 8 | */ 9 | public class ObjectsCompat { 10 | public static boolean equals(Object a, Object b) { 11 | return Objects.equals(a, b); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/crypto/BerDecodeException.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.crypto; 2 | 3 | @SuppressWarnings("unused") 4 | public final class BerDecodeException extends Exception { 5 | 6 | private final int _position; 7 | 8 | public BerDecodeException() { 9 | _position = 0; 10 | } 11 | 12 | public BerDecodeException(String message) { 13 | super(message); 14 | _position = 0; 15 | } 16 | 17 | public BerDecodeException(String message, Exception ex) { 18 | super(message, ex); 19 | _position = 0; 20 | } 21 | 22 | public BerDecodeException(String message, int position) { 23 | super(message); 24 | _position = position; 25 | } 26 | 27 | public BerDecodeException(String message, int position, Exception ex) { 28 | super(message, ex); 29 | _position = position; 30 | } 31 | 32 | public int get_position() { 33 | return _position; 34 | } 35 | 36 | @Override 37 | public String getMessage() { 38 | return super.getMessage() + String.format(" (Position %d)%s", _position, System.lineSeparator()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/crypto/CryptoException.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.crypto; 2 | 3 | /** 4 | * @author lngtr 5 | * @since 2018-03-02 6 | */ 7 | public class CryptoException extends Exception { 8 | public CryptoException() { 9 | } 10 | 11 | public CryptoException(String message) { 12 | super(message); 13 | } 14 | 15 | public CryptoException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | public CryptoException(Throwable cause) { 20 | super(cause); 21 | } 22 | 23 | public CryptoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 24 | super(message, cause, enableSuppression, writableStackTrace); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/event/Event.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.event; 2 | 3 | import java.util.HashSet; 4 | 5 | public class Event { 6 | 7 | protected final HashSet> handlers = new HashSet<>(); 8 | 9 | public void addEventHandler(EventHandler handler) { 10 | synchronized (handlers) { 11 | handlers.add(handler); 12 | } 13 | } 14 | 15 | public void removeEventHandler(EventHandler handler) { 16 | synchronized (handlers) { 17 | handlers.remove(handler); 18 | } 19 | } 20 | 21 | public void handleEvent(Object sender, T e) { 22 | synchronized (handlers) { 23 | for (final EventHandler handler : handlers) { 24 | handler.handleEvent(sender, e); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/event/EventArgs.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.event; 2 | 3 | public class EventArgs { 4 | 5 | public static final EventArgs EMPTY = new EventArgs(); 6 | 7 | public EventArgs() { 8 | } 9 | } -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/event/EventHandler.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.event; 2 | 3 | public interface EventHandler { 4 | void handleEvent(Object sender, T e); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/event/ScheduledFunction.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.event; 2 | 3 | import java.util.Timer; 4 | import java.util.TimerTask; 5 | 6 | /** 7 | * @author lngtr 8 | * @since 2018-02-20 9 | */ 10 | public class ScheduledFunction { 11 | 12 | private long delay; 13 | 14 | private final Runnable func; 15 | 16 | private Timer timer; 17 | 18 | private boolean bStarted = false; 19 | 20 | public ScheduledFunction(Runnable func, long delay) { 21 | this.delay = delay; 22 | this.func = func; 23 | } 24 | 25 | public void start() { 26 | if (!bStarted) { 27 | timer = new Timer(); 28 | timer.scheduleAtFixedRate(new TimerTask() { 29 | @Override 30 | public void run() { 31 | if (func != null) { 32 | func.run(); 33 | } 34 | } 35 | }, 0, delay); 36 | bStarted = true; 37 | } 38 | } 39 | 40 | public void stop() { 41 | if (bStarted) { 42 | timer.cancel(); 43 | timer = null; 44 | bStarted = false; 45 | } 46 | } 47 | 48 | public long getDelay() { 49 | return delay; 50 | } 51 | 52 | public void setDelay(long delay) { 53 | this.delay = delay; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/log/LogListener.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.log; 2 | 3 | /** 4 | * @author lngtr 5 | * @since 2018-03-02 6 | */ 7 | public interface LogListener { 8 | void onLog(Class clazz, String message, Throwable throwable); 9 | 10 | void onError(Class clazz, String message, Throwable throwable); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/log/LogManager.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.log; 2 | 3 | import java.util.HashMap; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author lngtr 10 | * @since 2018-03-02 11 | */ 12 | public class LogManager { 13 | 14 | static final List LOG_LISTENERS = new LinkedList<>(); 15 | 16 | private static final Map, Logger> LOGGERS = new HashMap<>(); 17 | 18 | /** 19 | * Gets the {@link Logger} instance of the specified class. 20 | * 21 | * @param clazz the class, must not be null. 22 | * @return the logger instance. 23 | */ 24 | public static Logger getLogger(Class clazz) { 25 | return LOGGERS.computeIfAbsent(clazz, k -> new Logger(clazz)); 26 | } 27 | 28 | /** 29 | * Adds a log listener that will be notified of logging events. 30 | * You can use the {@link DefaultLogListener} that prints logs to the standard output in a format similar to Log4j2 31 | * 32 | * @param listener the listener. 33 | */ 34 | public static void addListener(LogListener listener) { 35 | if (listener != null) { 36 | LOG_LISTENERS.add(listener); 37 | } 38 | } 39 | 40 | /** 41 | * Remove a log listener. 42 | * 43 | * @param listener the listener. 44 | */ 45 | public static void removeListener(LogListener listener) { 46 | LOG_LISTENERS.remove(listener); 47 | } 48 | 49 | private LogManager() { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/log/Logger.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.log; 2 | 3 | /** 4 | * @author lngtr 5 | * @since 2018-03-02 6 | */ 7 | public class Logger { 8 | 9 | private final Class clazz; 10 | 11 | Logger(Class clazz) { 12 | if (clazz == null) { 13 | throw new IllegalArgumentException("class is null"); 14 | } 15 | this.clazz = clazz; 16 | } 17 | 18 | public void debug(Throwable throwable) { 19 | debug(null, throwable); 20 | } 21 | 22 | public void debug(String message) { 23 | debug(message, null); 24 | } 25 | 26 | public void debug(String message, Throwable throwable) { 27 | for (LogListener listener : LogManager.LOG_LISTENERS) { 28 | if (listener != null) { 29 | listener.onLog(clazz, message, throwable); 30 | } 31 | } 32 | } 33 | 34 | public void error(Throwable throwable) { 35 | error(null, throwable); 36 | } 37 | 38 | public void error(String message) { 39 | error(message, null); 40 | } 41 | 42 | public void error(String message, Throwable throwable) { 43 | for (LogListener listener : LogManager.LOG_LISTENERS) { 44 | if (listener != null) { 45 | listener.onError(clazz, message, throwable); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/in/dragonbra/javasteam/util/stream/SeekOrigin.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.stream; 2 | 3 | /** 4 | * @author lngtr 5 | * @since 2018-02-21 6 | */ 7 | public enum SeekOrigin { 8 | BEGIN, 9 | CURRENT, 10 | END 11 | } 12 | -------------------------------------------------------------------------------- /src/main/proto/in/dragonbra/javasteam/protobufs/steam/discovery/basic_server_list.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | option java_package = "in.dragonbra.javasteam.protobufs.steam.discovery"; 4 | option java_outer_classname = "BasicServerListProtos"; 5 | 6 | option optimize_for = SPEED; 7 | 8 | message BasicServerList { 9 | repeated BasicServer servers = 1; 10 | } 11 | 12 | message BasicServer { 13 | required string address = 1; 14 | required int32 port = 2; 15 | required int32 protocol = 3; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/proto/in/dragonbra/javasteam/protobufs/steamclient/clientmetrics.proto: -------------------------------------------------------------------------------- 1 | option java_package = "in.dragonbra.javasteam.protobufs.steamclient"; 2 | 3 | option optimize_for = SPEED; 4 | 5 | message CClientMetrics_ClientBootstrap_RequestInfo { 6 | optional string original_hostname = 1; 7 | optional string actual_hostname = 2; 8 | optional string path = 3; 9 | optional string base_name = 4; 10 | optional bool success = 5; 11 | optional uint32 status_code = 6; 12 | optional string address_of_request_url = 7; 13 | optional uint32 response_time_ms = 8; 14 | optional uint64 bytes_received = 9; 15 | optional uint32 num_retries = 10; 16 | } 17 | 18 | message CClientMetrics_ClientBootstrap_Summary { 19 | optional uint32 launcher_type = 1; 20 | optional uint32 steam_realm = 2; 21 | optional string beta_name = 3; 22 | optional bool download_completed = 4; 23 | optional uint32 total_time_ms = 6; 24 | repeated .CClientMetrics_ClientBootstrap_RequestInfo manifest_requests = 7; 25 | repeated .CClientMetrics_ClientBootstrap_RequestInfo package_requests = 8; 26 | } 27 | 28 | message CClientMetrics_ContentDownloadResponse_Counts { 29 | optional uint32 class_100 = 1; 30 | optional uint32 class_200 = 2; 31 | optional uint32 class_300 = 3; 32 | optional uint32 class_400 = 4; 33 | optional uint32 class_500 = 5; 34 | optional uint32 no_response = 6; 35 | optional uint32 class_unknown = 7; 36 | } 37 | 38 | message CClientMetrics_ContentDownloadResponse_HostCounts { 39 | optional string hostname = 1; 40 | optional uint32 source_type = 2; 41 | optional .CClientMetrics_ContentDownloadResponse_Counts counts = 3; 42 | } 43 | 44 | message CClientMetrics_ContentDownloadResponse_Hosts { 45 | repeated .CClientMetrics_ContentDownloadResponse_HostCounts hosts = 1; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/proto/in/dragonbra/javasteam/protobufs/steamclient/encrypted_app_ticket.proto: -------------------------------------------------------------------------------- 1 | option java_package = "in.dragonbra.javasteam.protobufs.steamclient"; 2 | 3 | option optimize_for = SPEED; 4 | option java_generic_services = false; 5 | 6 | message EncryptedAppTicket { 7 | optional uint32 ticket_version_no = 1; 8 | optional uint32 crc_encryptedticket = 2; 9 | optional uint32 cb_encrypteduserdata = 3; 10 | optional uint32 cb_encrypted_appownershipticket = 4; 11 | optional bytes encrypted_ticket = 5; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/proto/in/dragonbra/javasteam/protobufs/steamclient/steammessages_clientserver_ufs.proto: -------------------------------------------------------------------------------- 1 | import "in/dragonbra/javasteam/protobufs/steamclient/steammessages_base.proto"; 2 | 3 | option java_package = "in.dragonbra.javasteam.protobufs.steamclient"; 4 | 5 | option optimize_for = SPEED; 6 | option java_generic_services = false; 7 | 8 | message CMsgClientUFSGetUGCDetails { 9 | optional fixed64 hcontent = 1 [default = 18446744073709551615]; 10 | } 11 | 12 | message CMsgClientUFSGetUGCDetailsResponse { 13 | optional int32 eresult = 1 [default = 2]; 14 | optional string url = 2; 15 | optional uint32 app_id = 3; 16 | optional string filename = 4; 17 | optional fixed64 steamid_creator = 5; 18 | optional uint32 file_size = 6; 19 | optional uint32 compressed_file_size = 7; 20 | optional string rangecheck_host = 8; 21 | optional string file_encoded_sha1 = 9; 22 | } 23 | 24 | message CMsgClientUFSGetSingleFileInfo { 25 | optional uint32 app_id = 1; 26 | optional string file_name = 2; 27 | } 28 | 29 | message CMsgClientUFSGetSingleFileInfoResponse { 30 | optional int32 eresult = 1 [default = 2]; 31 | optional uint32 app_id = 2; 32 | optional string file_name = 3; 33 | optional bytes sha_file = 4; 34 | optional uint64 time_stamp = 5; 35 | optional uint32 raw_file_size = 6; 36 | optional bool is_explicit_delete = 7; 37 | } 38 | 39 | message CMsgClientUFSShareFile { 40 | optional uint32 app_id = 1; 41 | optional string file_name = 2; 42 | } 43 | 44 | message CMsgClientUFSShareFileResponse { 45 | optional int32 eresult = 1 [default = 2]; 46 | optional fixed64 hcontent = 2 [default = 18446744073709551615]; 47 | } 48 | -------------------------------------------------------------------------------- /src/main/proto/in/dragonbra/javasteam/protobufs/steamclient/steammessages_unified_base.steamclient.proto: -------------------------------------------------------------------------------- 1 | import "google/protobuf/descriptor.proto"; 2 | 3 | option java_package = "in.dragonbra.javasteam.protobufs.steamclient"; 4 | 5 | option optimize_for = SPEED; 6 | option java_generic_services = false; 7 | 8 | extend .google.protobuf.MessageOptions { 9 | optional string message_description = 51000; 10 | optional bool force_emit_message = 50026 [default = false]; 11 | } 12 | 13 | extend .google.protobuf.FieldOptions { 14 | optional string description = 50000; 15 | } 16 | 17 | extend .google.protobuf.ServiceOptions { 18 | optional string service_description = 50000; 19 | optional .EProtoExecutionSite service_execution_site = 50008 [default = k_EProtoExecutionSiteUnknown]; 20 | optional .EProtoServiceType service_type = 50025 [default = k_EProtoServiceTypeSteamMessages]; 21 | optional bool force_emit_service = 50026 [default = false]; 22 | } 23 | 24 | extend .google.protobuf.MethodOptions { 25 | optional string method_description = 50000; 26 | } 27 | 28 | extend .google.protobuf.EnumOptions { 29 | optional string enum_description = 50000; 30 | } 31 | 32 | extend .google.protobuf.EnumValueOptions { 33 | optional string enum_value_description = 50000; 34 | } 35 | 36 | enum EProtoExecutionSite { 37 | k_EProtoExecutionSiteUnknown = 0; 38 | k_EProtoExecutionSiteSteamClient = 2; 39 | } 40 | 41 | enum EProtoServiceType { 42 | k_EProtoServiceTypeSteamMessages = 0; 43 | k_EProtoServiceTypeVRGamepadUIMessages = 1; 44 | } 45 | 46 | message NoResponse { 47 | } 48 | -------------------------------------------------------------------------------- /src/main/steamd/in/dragonbra/javasteam/gamecoordinator.steamd: -------------------------------------------------------------------------------- 1 | class MsgGCHdrProtoBuf 2 | { 3 | protomaskgc uint msg = 0; 4 | int headerLength; 5 | 6 | proto SteamKit2.GC.Internal.CMsgProtoBufHeader proto; 7 | }; 8 | 9 | class MsgGCHdr 10 | { 11 | ushort headerVersion = 1; 12 | 13 | ulong targetJobID = ulong.MaxValue; 14 | ulong sourceJobID = ulong.MaxValue; 15 | }; 16 | -------------------------------------------------------------------------------- /src/main/steamd/in/dragonbra/javasteam/header.steamd: -------------------------------------------------------------------------------- 1 | #import "emsg.steamd" 2 | #import "eresult.steamd" 3 | #import "enums.steamd" 4 | #import "netheader.steamd" 5 | 6 | class MsgHdr 7 | { 8 | EMsg msg = EMsg::Invalid; 9 | 10 | ulong targetJobID = ulong.MaxValue; 11 | ulong sourceJobID = ulong.MaxValue; 12 | }; 13 | 14 | class ExtendedClientMsgHdr 15 | { 16 | EMsg msg = EMsg::Invalid; 17 | 18 | byte headerSize = 36; 19 | 20 | ushort headerVersion = 2; 21 | 22 | ulong targetJobID = ulong.MaxValue; 23 | ulong sourceJobID = ulong.MaxValue; 24 | 25 | byte headerCanary = 239; 26 | 27 | steamidmarshal ulong steamID; 28 | int sessionID; 29 | }; 30 | 31 | class MsgHdrProtoBuf 32 | { 33 | protomask EMsg msg = EMsg::Invalid; 34 | int headerLength; 35 | 36 | proto SteamKit2.Internal.CMsgProtoBufHeader proto; 37 | }; 38 | -------------------------------------------------------------------------------- /src/main/steamd/in/dragonbra/javasteam/netheader.steamd: -------------------------------------------------------------------------------- 1 | enum EUdpPacketType 2 | { 3 | Invalid = 0; 4 | 5 | ChallengeReq = 1; 6 | Challenge = 2; 7 | Connect = 3; 8 | Accept = 4; 9 | Disconnect = 5; 10 | Data = 6; 11 | Datagram = 7; 12 | Max = 8; 13 | }; 14 | 15 | class UdpHeader 16 | { 17 | const uint MAGIC = 0x31305356; 18 | 19 | uint magic = UdpHeader::MAGIC; 20 | 21 | ushort payloadSize; 22 | EUdpPacketType packetType = EUdpPacketType::Invalid; 23 | byte flags; 24 | 25 | uint sourceConnID = 512; 26 | uint destConnID; 27 | 28 | uint seqThis; 29 | uint seqAck; 30 | 31 | uint packetsInMsg; 32 | uint msgStartSeq; 33 | 34 | uint msgSize; 35 | }; 36 | 37 | class ChallengeData 38 | { 39 | const uint CHALLENGE_MASK = 0xA426DF2B; 40 | 41 | uint challengeValue; 42 | uint serverLoad; 43 | }; 44 | 45 | class ConnectData 46 | { 47 | const uint CHALLENGE_MASK = ChallengeData::CHALLENGE_MASK; 48 | 49 | uint challengeValue; 50 | }; 51 | 52 | class Accept 53 | { 54 | }; 55 | 56 | class Datagram 57 | { 58 | }; 59 | 60 | class Disconnect 61 | { 62 | }; 63 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/ConnectedSteamClient.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam; 2 | 3 | import in.dragonbra.javasteam.steam.steamclient.SteamClient; 4 | 5 | public class ConnectedSteamClient { 6 | public static SteamClient get() { 7 | var client = new SteamClient(); 8 | client.setIsConnected(true); 9 | 10 | return client; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/TestBase.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam; 2 | 3 | import in.dragonbra.javasteam.util.log.DefaultLogListener; 4 | import in.dragonbra.javasteam.util.log.LogManager; 5 | import org.junit.jupiter.api.BeforeAll; 6 | 7 | /** 8 | * @author lngtr 9 | * @since 2018-02-25 10 | */ 11 | public abstract class TestBase { 12 | @BeforeAll 13 | public static void beforeClass() { 14 | LogManager.addListener(new DefaultLogListener()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/steam/DummyClient.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.steam; 2 | 3 | import in.dragonbra.javasteam.base.IClientMsg; 4 | import in.dragonbra.javasteam.steam.steamclient.configuration.SteamConfiguration; 5 | 6 | /** 7 | * @author lngtr 8 | * @since 2018-02-25 9 | */ 10 | public class DummyClient extends CMClient { 11 | public DummyClient() { 12 | super(SteamConfiguration.createDefault()); 13 | } 14 | 15 | public void dummyDisconnect() { 16 | disconnect(); 17 | onClientDisconnected(true); 18 | } 19 | 20 | public void handleClientMsg(IClientMsg clientMsg) { 21 | onClientMsgReceived(getPacketMsg(clientMsg.serialize())); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/types/GameIDTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.types; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | /** 9 | * @author lngtr 10 | * @since 2019-01-14 11 | */ 12 | public class GameIDTest { 13 | 14 | @Test 15 | public void modCrcCorrect() { 16 | GameID gameId = new GameID(420, "Research and Development"); 17 | 18 | assertTrue(gameId.isMod()); 19 | assertEquals(420, gameId.getAppID()); 20 | assertEquals(new GameID(0x8db24e81010001a4L), gameId); 21 | 22 | GameID gameId2 = new GameID(215, "hidden"); 23 | 24 | assertTrue(gameId2.isMod()); 25 | assertEquals(215, gameId2.getAppID()); 26 | assertEquals(new GameID(0x885de9bd010000d7L), gameId2); 27 | } 28 | 29 | @Test 30 | public void shortcutCrcCorrect() { 31 | GameID gameId = new GameID("\"C:\\Program Files (x86)\\Git\\mingw64\\bin\\wintoast.exe\"", "Git for Windows"); 32 | 33 | assertTrue(gameId.isShortcut()); 34 | assertEquals(new GameID(0xb102133802000000L), gameId); 35 | } 36 | } -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/CollectionsTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.HashMap; 8 | 9 | public class CollectionsTest { 10 | 11 | private HashMap values; 12 | 13 | @BeforeEach 14 | public void setUp() { 15 | values = new HashMap<>(); 16 | values.put(1, "Some Value"); 17 | values.put(2, "A Value"); 18 | values.put(3, "All the values"); 19 | } 20 | 21 | @Test 22 | public void getKeyByValueExistingValue() { 23 | var result = CollectionUtils.getKeyByValue(values, "A Value"); 24 | Assertions.assertEquals(2, result); 25 | } 26 | 27 | @Test 28 | public void getKeyByValueNullValue() { 29 | var nullResult = CollectionUtils.getKeyByValue(values, "Null Value"); 30 | Assertions.assertNull(nullResult); 31 | 32 | var nullResult2 = CollectionUtils.getKeyByValue(values, null); 33 | Assertions.assertNull(nullResult2); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/HardwareUtilsTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.lang.reflect.Field; 8 | 9 | public class HardwareUtilsTest { 10 | 11 | @BeforeEach 12 | public void setUp() throws NoSuchFieldException, IllegalAccessException { 13 | // Ehh.... This resets the 'MACHINE_NAME' field for every test. 14 | Field field = HardwareUtils.class.getDeclaredField("MACHINE_NAME"); 15 | field.setAccessible(true); 16 | field.set(null, null); 17 | } 18 | 19 | @Test 20 | public void machineNameWithTag() { 21 | var name = HardwareUtils.getMachineName(true); 22 | System.out.println(name); 23 | Assertions.assertTrue(name.contains(" (JavaSteam)")); 24 | } 25 | 26 | @Test 27 | public void machineNameWithNoTag() { 28 | var name = HardwareUtils.getMachineName(); 29 | System.out.println(name); 30 | Assertions.assertFalse(name.contains(" (JavaSteam)")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/MsgUtilTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import in.dragonbra.javasteam.enums.EMsg; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class MsgUtilTest { 8 | 9 | @Test 10 | public void isProtoBuf() { 11 | var result = MsgUtil.isProtoBuf(-2147482868); // ClientLicenseList 12 | Assertions.assertTrue(result); 13 | } 14 | 15 | @Test 16 | public void isNotProtoBuf() { 17 | var result = MsgUtil.isProtoBuf(798); // ClientUpdateGuestPassesList 18 | Assertions.assertFalse(result); 19 | } 20 | 21 | @Test 22 | public void getMsgAsServiceMethodResponse() { 23 | var result = MsgUtil.getMsg(-2147483501); // ServiceMethodResponse 24 | Assertions.assertEquals(EMsg.ServiceMethodResponse, result); 25 | } 26 | 27 | @Test 28 | public void getMsgAsClientUpdateGuestPassesList() { 29 | var result = MsgUtil.getMsg(798); // ClientUpdateGuestPassesList 30 | Assertions.assertEquals(EMsg.ClientUpdateGuestPassesList, result); 31 | } 32 | 33 | @Test 34 | public void getMsgAsWrongMsg() { 35 | var result = MsgUtil.getMsg(-2147483501); // ServiceMethodResponse 36 | Assertions.assertNotEquals(EMsg.ClientUpdateGuestPassesList, result); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/PassableTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class PassableTest { 7 | 8 | @Test 9 | public void BooleanPassable() { 10 | var passableValue = new Passable<>(false); 11 | 12 | Assertions.assertFalse(passableValue.getValue()); 13 | 14 | passableValue.setValue(true); 15 | Assertions.assertTrue(passableValue.getValue()); 16 | 17 | passableValue.setValue(null); 18 | Assertions.assertNull(passableValue.getValue()); 19 | } 20 | 21 | @Test 22 | public void IntegerPassable() { 23 | var passableValue = new Passable(null); 24 | 25 | Assertions.assertNull(passableValue.getValue()); 26 | 27 | passableValue.setValue(1); 28 | Assertions.assertEquals(1, passableValue.getValue()); 29 | 30 | passableValue.setValue(2); 31 | Assertions.assertEquals(2, passableValue.getValue()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/StringsTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class StringsTest { 7 | 8 | @Test 9 | public void toHex() { 10 | byte[] byteArray = {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF}; 11 | Assertions.assertEquals("DEADBEEF", Strings.toHex(byteArray)); 12 | } 13 | 14 | @Test 15 | public void decodeHex() { 16 | byte[] byteArray = {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF}; 17 | Assertions.assertArrayEquals(byteArray, Strings.decodeHex("deadbeef")); 18 | } 19 | 20 | @Test 21 | public void invalid_decodeHex() { 22 | Assertions.assertThrows(StringIndexOutOfBoundsException.class, () -> Strings.decodeHex("odd")); 23 | } 24 | 25 | @Test 26 | public void isNullOrEmpty() { 27 | Assertions.assertTrue(Strings.isNullOrEmpty(null)); 28 | Assertions.assertTrue(Strings.isNullOrEmpty("")); 29 | Assertions.assertFalse(Strings.isNullOrEmpty("Hello World!")); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/UtilsTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | /** 7 | * @author lngtr 8 | * @since 2019-01-15 9 | */ 10 | public class UtilsTest { 11 | 12 | // Note: We can only set the "os.name" once, but "os.version" can be changed on the fly. 13 | // This breaks testing for EOSTypes. 14 | // If you create an EOSType test and run it individually after setting the properties above, it passes. 15 | 16 | @Test 17 | public void crc32() { 18 | long result = Utils.crc32("test_string"); 19 | Assertions.assertEquals(0x0967B587, result); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/WebHelpersTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util; 2 | 3 | import in.dragonbra.javasteam.TestBase; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | 8 | /** 9 | * @author lngtr 10 | * @since 2018-04-16 11 | */ 12 | public class WebHelpersTest extends TestBase { 13 | 14 | @Test 15 | public void urlEncodeWithString() { 16 | String result = WebHelpers.urlEncode("encrypt THIS sTrInG1234 \10 \11 \12"); 17 | Assertions.assertEquals("encrypt+THIS+sTrInG1234+%08+%09+%0A", result); 18 | } 19 | 20 | @Test 21 | public void urlEncodeWithByteArray() { 22 | var input = "encrypt THIS sTrInG1234 \10 \11 \12".getBytes(); 23 | String result = WebHelpers.urlEncode(input); 24 | Assertions.assertEquals("encrypt+THIS+sTrInG1234+%08+%09+%0A", result); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/compat/ConsumerTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.compat; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class ConsumerTest { 7 | 8 | @Test 9 | void testConsumerString() { 10 | Consumer consumer = s -> Assertions.assertEquals("Test String", s); 11 | 12 | consumer.accept("Test String"); 13 | } 14 | 15 | @Test 16 | void testConsumerBoolean() { 17 | Consumer consumer = b -> Assertions.assertEquals(true, b); 18 | 19 | consumer.accept(true); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/compat/ObjectsCompatTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.compat; 2 | 3 | import in.dragonbra.javasteam.TestBase; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertFalse; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | public class ObjectsCompatTest extends TestBase { 10 | 11 | @SuppressWarnings("ConstantValue") 12 | @Test 13 | public void testEquals() { 14 | final String a = "aaa"; 15 | final String b = "bbb"; 16 | final String c = null; 17 | 18 | assertFalse(ObjectsCompat.equals(a, b)); 19 | assertFalse(ObjectsCompat.equals(a, c)); 20 | assertFalse(ObjectsCompat.equals(c, a)); 21 | 22 | assertTrue(ObjectsCompat.equals(a, a)); 23 | assertTrue(ObjectsCompat.equals(b, b)); 24 | assertTrue(ObjectsCompat.equals(c, c)); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/crypto/RSACryptoTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.crypto; 2 | 3 | import in.dragonbra.javasteam.enums.EUniverse; 4 | import in.dragonbra.javasteam.util.KeyDictionary; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import javax.crypto.Cipher; 10 | import javax.crypto.NoSuchPaddingException; 11 | import java.security.NoSuchAlgorithmException; 12 | import java.security.NoSuchProviderException; 13 | 14 | public class RSACryptoTest { 15 | 16 | private RSACrypto rsaCrypto; 17 | 18 | @BeforeEach 19 | public void setUp() { 20 | var pubKey = KeyDictionary.getPublicKey(EUniverse.Public); 21 | rsaCrypto = new RSACrypto(pubKey); 22 | } 23 | 24 | @Test 25 | public void encrypt() { 26 | var input = CryptoHelper.generateRandomBlock(32); 27 | var encrypted = rsaCrypto.encrypt(input); 28 | 29 | Assertions.assertNotNull(encrypted); 30 | } 31 | 32 | @Test 33 | public void cipherInstance() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException { 34 | var cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", CryptoHelper.SEC_PROV); 35 | Assertions.assertNotNull(cipher); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/in/dragonbra/javasteam/util/event/EventTest.java: -------------------------------------------------------------------------------- 1 | package in.dragonbra.javasteam.util.event; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class EventTest { 7 | 8 | static class TestEventHandler implements EventHandler { 9 | boolean eventHandled = false; 10 | Object sender = null; 11 | EventArgs eventArgs = null; 12 | 13 | @Override 14 | public void handleEvent(Object sender, EventArgs e) { 15 | this.eventHandled = true; 16 | this.sender = sender; 17 | this.eventArgs = e; 18 | } 19 | } 20 | 21 | @Test 22 | void addEventHandlerAndHandleEvent() { 23 | var event = new Event<>(); 24 | var handler = new TestEventHandler(); 25 | 26 | event.addEventHandler(handler); 27 | 28 | event.handleEvent(this, EventArgs.EMPTY); 29 | 30 | Assertions.assertTrue(handler.eventHandled); 31 | Assertions.assertEquals(this, handler.sender); 32 | Assertions.assertEquals(EventArgs.EMPTY, handler.eventArgs); 33 | } 34 | 35 | @Test 36 | void removeEventHandler() { 37 | var event = new Event<>(); 38 | var handler = new TestEventHandler(); 39 | 40 | event.addEventHandler(handler); 41 | event.removeEventHandler(handler); 42 | 43 | event.handleEvent(this, EventArgs.EMPTY); 44 | 45 | Assertions.assertFalse(handler.eventHandled); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/resources/depot/depot_232250_chunk_7b8567d9b3c09295cdbf4978c32b348d8e76c750.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/depot/depot_232250_chunk_7b8567d9b3c09295cdbf4978c32b348d8e76c750.bin -------------------------------------------------------------------------------- /src/test/resources/depot/depot_3441461_chunk_9e72678e305540630a665b93e1463bc3983eb55a.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/depot/depot_3441461_chunk_9e72678e305540630a665b93e1463bc3983eb55a.bin -------------------------------------------------------------------------------- /src/test/resources/depot/depot_440_1118032470228587934.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/depot/depot_440_1118032470228587934.manifest -------------------------------------------------------------------------------- /src/test/resources/depot/depot_440_1118032470228587934_decrypted.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/depot/depot_440_1118032470228587934_decrypted.manifest -------------------------------------------------------------------------------- /src/test/resources/depot/depot_440_1118032470228587934_v4.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/depot/depot_440_1118032470228587934_v4.manifest -------------------------------------------------------------------------------- /src/test/resources/depot/depot_440_chunk_bac8e2657470b2eb70d6ddcd6c07004be8738697.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/depot/depot_440_chunk_bac8e2657470b2eb70d6ddcd6c07004be8738697.bin -------------------------------------------------------------------------------- /src/test/resources/packets/001_in_8904_k_EMsgClientPICSProductInfoResponse_app480_metadata.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/packets/001_in_8904_k_EMsgClientPICSProductInfoResponse_app480_metadata.bin -------------------------------------------------------------------------------- /src/test/resources/packets/002_in_8904_k_EMsgClientPICSProductInfoResponse_app480.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/packets/002_in_8904_k_EMsgClientPICSProductInfoResponse_app480.bin -------------------------------------------------------------------------------- /src/test/resources/packets/003_in_8904_k_EMsgClientPICSProductInfoResponse_sub0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/packets/003_in_8904_k_EMsgClientPICSProductInfoResponse_sub0.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientAMGetPersonaNameHistoryResponse.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientAMGetPersonaNameHistoryResponse.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientClanState.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientClanState.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientFSGetFriendMessageHistoryResponse.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientFSGetFriendMessageHistoryResponse.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientFriendsList.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientFriendsList.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientLicenseList.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientLicenseList.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientMarketingMessageUpdate2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientMarketingMessageUpdate2.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientPICSAccessTokenResponse.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientPICSAccessTokenResponse.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientPICSChangesSinceResponse.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientPICSChangesSinceResponse.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientPICSProductInfoResponse.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientPICSProductInfoResponse.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientPersonaState.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientPersonaState.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientUpdateGuestPassesList.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientUpdateGuestPassesList.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientUpdateMachineAuth.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientUpdateMachineAuth.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientVACBanStatus.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientVACBanStatus.bin -------------------------------------------------------------------------------- /src/test/resources/testpackets/ClientWalletInfoUpdate.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Longi94/JavaSteam/12de8e0194bbe5210d73be82140e4edd7adf37f1/src/test/resources/testpackets/ClientWalletInfoUpdate.bin -------------------------------------------------------------------------------- /src/test/resources/textkeyvalues/appinfo_utf8.txt: -------------------------------------------------------------------------------- 1 | "appinfo" 2 | { 3 | "appid" "1234567" 4 | "common" 5 | { 6 | "name" "Some Cool Game®: UTF8 Special® II | Java Steam™ 2.0" 7 | "name_localized" 8 | { 9 | "koreana" "적절하게 인코딩할 임의의 텍스트 | 2.0" 10 | "schinese" "《一些随机文本》 | 《®2.0》" 11 | "tchinese" "《一些隨機文本》 | 《™2.0》" 12 | } 13 | "random_text" 14 | { 15 | "set_1" "контрольная работа" 16 | "set_2" "δοκιμή" 17 | "set_3" "امتحان" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tools/javasteam-bot/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # Typescript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # next.js build output 63 | .next 64 | 65 | *.pem -------------------------------------------------------------------------------- /tools/javasteam-bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javasteam-bot", 3 | "version": "1.0.0", 4 | "main": "app.js", 5 | "description": "Polls the SteamKit2 GitHub repo for new merged pull requests.", 6 | "dependencies": { 7 | "@octokit/rest": "^20.0.2", 8 | "jsonwebtoken": "^9.0.2", 9 | "request": "^2.83.0", 10 | "yargs": "^17.7.2" 11 | }, 12 | "repository": "https://github.com/Longi94/JavaSteam", 13 | "license": "MIT" 14 | } 15 | --------------------------------------------------------------------------------