UNPKG

steam-user

Version:

Steam client for Individual and AnonUser Steam account types

758 lines (686 loc) 44.3 kB
const ByteBuffer = require('bytebuffer'); const SteamID = require('steamid'); const Zlib = require('zlib'); const SteamUserConnection = require('./02-connection.js'); const Schema = require('../protobufs/generated/_load.js'); const EMsg = require('../enums/EMsg.js'); const EResult = require('../enums/EResult.js'); // steam-session dependencies const {LoginSession, EAuthTokenPlatformType} = require('steam-session'); const CMAuthTransport = require('./classes/CMAuthTransport'); const JOBID_NONE = '18446744073709551615'; const PROTO_MASK = 0x80000000; const VERBOSE_EMSG_LIST = [ EMsg.Multi, EMsg.ClientHeartBeat ]; const protobufs = {}; protobufs[EMsg.Multi] = Schema.CMsgMulti; protobufs[EMsg.ClientHeartBeat] = Schema.CMsgClientHeartBeat; protobufs[EMsg.ClientHello] = Schema.CMsgClientHello; protobufs[EMsg.ClientLogon] = Schema.CMsgClientLogon; protobufs[EMsg.ClientLogonGameServer] = Schema.CMsgClientLogon; protobufs[EMsg.ClientLogOnResponse] = Schema.CMsgClientLogonResponse; protobufs[EMsg.ClientLogOff] = Schema.CMsgClientLogOff; protobufs[EMsg.ClientLoggedOff] = Schema.CMsgClientLoggedOff; protobufs[EMsg.ClientUpdateMachineAuth] = Schema.CMsgClientUpdateMachineAuth; protobufs[EMsg.ClientUpdateMachineAuthResponse] = Schema.CMsgClientUpdateMachineAuthResponse; protobufs[EMsg.ClientNewLoginKey] = Schema.CMsgClientNewLoginKey; protobufs[EMsg.ClientNewLoginKeyAccepted] = Schema.CMsgClientNewLoginKeyAccepted; protobufs[EMsg.ClientRequestWebAPIAuthenticateUserNonce] = Schema.CMsgClientRequestWebAPIAuthenticateUserNonce; protobufs[EMsg.ClientRequestWebAPIAuthenticateUserNonceResponse] = Schema.CMsgClientRequestWebAPIAuthenticateUserNonceResponse; protobufs[EMsg.ClientCMList] = Schema.CMsgClientCMList; protobufs[EMsg.ClientItemAnnouncements] = Schema.CMsgClientItemAnnouncements; protobufs[EMsg.ClientRequestItemAnnouncements] = Schema.CMsgClientRequestItemAnnouncements; protobufs[EMsg.ClientCommentNotifications] = Schema.CMsgClientCommentNotifications; protobufs[EMsg.ClientRequestCommentNotifications] = Schema.CMsgClientRequestCommentNotifications; protobufs[EMsg.ClientUserNotifications] = Schema.CMsgClientUserNotifications; protobufs[EMsg.ClientFSOfflineMessageNotification] = Schema.CMsgClientOfflineMessageNotification; protobufs[EMsg.ClientFSRequestOfflineMessageCount] = Schema.CMsgClientRequestOfflineMessageCount; protobufs[EMsg.ClientGamesPlayed] = Schema.CMsgClientGamesPlayed; protobufs[EMsg.ClientGamesPlayedWithDataBlob] = Schema.CMsgClientGamesPlayed; protobufs[EMsg.ClientAccountInfo] = Schema.CMsgClientAccountInfo; protobufs[EMsg.ClientEmailAddrInfo] = Schema.CMsgClientEmailAddrInfo; protobufs[EMsg.ClientIsLimitedAccount] = Schema.CMsgClientIsLimitedAccount; protobufs[EMsg.ClientWalletInfoUpdate] = Schema.CMsgClientWalletInfoUpdate; protobufs[EMsg.ClientLicenseList] = Schema.CMsgClientLicenseList; protobufs[EMsg.ClientServiceMethodLegacy] = Schema.CMsgClientServiceMethodLegacy; protobufs[EMsg.ClientServiceMethodLegacyResponse] = Schema.CMsgClientServiceMethodLegacyResponse; protobufs[EMsg.ClientGMSServerQuery] = Schema.CMsgClientGMSServerQuery; protobufs[EMsg.GMSClientServerQueryResponse] = Schema.CMsgGMSClientServerQueryResponse; protobufs[EMsg.ClientPICSChangesSinceRequest] = Schema.CMsgClientPICSChangesSinceRequest; protobufs[EMsg.ClientPICSChangesSinceResponse] = Schema.CMsgClientPICSChangesSinceResponse; protobufs[EMsg.ClientPICSProductInfoRequest] = Schema.CMsgClientPICSProductInfoRequest; protobufs[EMsg.ClientPICSProductInfoResponse] = Schema.CMsgClientPICSProductInfoResponse; protobufs[EMsg.ClientPICSAccessTokenRequest] = Schema.CMsgClientPICSAccessTokenRequest; protobufs[EMsg.ClientPICSAccessTokenResponse] = Schema.CMsgClientPICSAccessTokenResponse; protobufs[EMsg.EconTrading_InitiateTradeRequest] = Schema.CMsgTrading_InitiateTradeRequest; protobufs[EMsg.EconTrading_InitiateTradeResponse] = Schema.CMsgTrading_InitiateTradeResponse; protobufs[EMsg.EconTrading_CancelTradeRequest] = Schema.CMsgTrading_CancelTradeRequest; protobufs[EMsg.EconTrading_InitiateTradeProposed] = Schema.CMsgTrading_InitiateTradeRequest; protobufs[EMsg.EconTrading_InitiateTradeResult] = Schema.CMsgTrading_InitiateTradeResponse; protobufs[EMsg.EconTrading_StartSession] = Schema.CMsgTrading_StartSession; protobufs[EMsg.ClientChangeStatus] = Schema.CMsgClientChangeStatus; protobufs[EMsg.ClientAddFriend] = Schema.CMsgClientAddFriend; protobufs[EMsg.ClientAddFriendResponse] = Schema.CMsgClientAddFriendResponse; protobufs[EMsg.ClientRemoveFriend] = Schema.CMsgClientRemoveFriend; protobufs[EMsg.ClientFSGetFriendsSteamLevels] = Schema.CMsgClientFSGetFriendsSteamLevels; protobufs[EMsg.ClientFSGetFriendsSteamLevelsResponse] = Schema.CMsgClientFSGetFriendsSteamLevelsResponse; protobufs[EMsg.ClientPersonaState] = Schema.CMsgClientPersonaState; protobufs[EMsg.ClientClanState] = Schema.CMsgClientClanState; protobufs[EMsg.ClientFriendsList] = Schema.CMsgClientFriendsList; protobufs[EMsg.ClientRequestFriendData] = Schema.CMsgClientRequestFriendData; protobufs[EMsg.ClientFriendMsg] = Schema.CMsgClientFriendMsg; protobufs[EMsg.ClientChatInvite] = Schema.CMsgClientChatInvite; protobufs[EMsg.ClientFriendMsgIncoming] = Schema.CMsgClientFriendMsgIncoming; protobufs[EMsg.ClientFriendMsgEchoToSender] = Schema.CMsgClientFriendMsgIncoming; protobufs[EMsg.ClientFSGetFriendMessageHistory] = Schema.CMsgClientChatGetFriendMessageHistory; protobufs[EMsg.ClientFSGetFriendMessageHistoryResponse] = Schema.CMsgClientChatGetFriendMessageHistoryResponse; protobufs[EMsg.ClientFriendsGroupsList] = Schema.CMsgClientFriendsGroupsList; protobufs[EMsg.AMClientCreateFriendsGroup] = Schema.CMsgClientCreateFriendsGroup; protobufs[EMsg.AMClientCreateFriendsGroupResponse] = Schema.CMsgClientCreateFriendsGroupResponse; protobufs[EMsg.AMClientDeleteFriendsGroup] = Schema.CMsgClientDeleteFriendsGroup; protobufs[EMsg.AMClientDeleteFriendsGroupResponse] = Schema.CMsgClientDeleteFriendsGroupResponse; protobufs[EMsg.AMClientRenameFriendsGroup] = Schema.CMsgClientManageFriendsGroup; protobufs[EMsg.AMClientRenameFriendsGroupResponse] = Schema.CMsgClientManageFriendsGroupResponse; protobufs[EMsg.AMClientAddFriendToGroup] = Schema.CMsgClientAddFriendToGroup; protobufs[EMsg.AMClientAddFriendToGroupResponse] = Schema.CMsgClientAddFriendToGroupResponse; protobufs[EMsg.AMClientRemoveFriendFromGroup] = Schema.CMsgClientRemoveFriendFromGroup; protobufs[EMsg.AMClientRemoveFriendFromGroupResponse] = Schema.CMsgClientRemoveFriendFromGroupResponse; protobufs[EMsg.ClientPlayerNicknameList] = Schema.CMsgClientPlayerNicknameList; protobufs[EMsg.AMClientSetPlayerNickname] = Schema.CMsgClientSetPlayerNickname; protobufs[EMsg.AMClientSetPlayerNicknameResponse] = Schema.CMsgClientSetPlayerNicknameResponse; protobufs[EMsg.ClientRegisterKey] = Schema.CMsgClientRegisterKey; protobufs[EMsg.ClientPurchaseResponse] = Schema.CMsgClientPurchaseResponse; protobufs[EMsg.ClientRequestFreeLicense] = Schema.CMsgClientRequestFreeLicense; protobufs[EMsg.ClientRequestFreeLicenseResponse] = Schema.CMsgClientRequestFreeLicenseResponse; protobufs[EMsg.ClientGetNumberOfCurrentPlayersDP] = Schema.CMsgDPGetNumberOfCurrentPlayers; protobufs[EMsg.ClientGetNumberOfCurrentPlayersDPResponse] = Schema.CMsgDPGetNumberOfCurrentPlayersResponse; protobufs[EMsg.ClientGetAppOwnershipTicket] = Schema.CMsgClientGetAppOwnershipTicket; protobufs[EMsg.ClientGetAppOwnershipTicketResponse] = Schema.CMsgClientGetAppOwnershipTicketResponse; protobufs[EMsg.ClientGameConnectTokens] = Schema.CMsgClientGameConnectTokens; protobufs[EMsg.ClientAuthList] = Schema.CMsgClientAuthList; protobufs[EMsg.ClientAuthListAck] = Schema.CMsgClientAuthListAck; protobufs[EMsg.ClientTicketAuthComplete] = Schema.CMsgClientTicketAuthComplete; protobufs[EMsg.ClientRequestEncryptedAppTicket] = Schema.CMsgClientRequestEncryptedAppTicket; protobufs[EMsg.ClientRequestEncryptedAppTicketResponse] = Schema.CMsgClientRequestEncryptedAppTicketResponse; protobufs[EMsg.ClientCurrentUIMode] = Schema.CMsgClientUIMode; protobufs[EMsg.ClientVanityURLChangedNotification] = Schema.CMsgClientVanityURLChangedNotification; protobufs[EMsg.ClientAMGetPersonaNameHistory] = Schema.CMsgClientAMGetPersonaNameHistory; protobufs[EMsg.ClientAMGetPersonaNameHistoryResponse] = Schema.CMsgClientAMGetPersonaNameHistoryResponse; //protobufs[EMsg.ClientUnlockStreaming] = Schema.CMsgAMUnlockStreaming; //protobufs[EMsg.ClientUnlockStreamingResponse] = Schema.CMsgAMUnlockStreamingResponse; protobufs[EMsg.ClientGetDepotDecryptionKey] = Schema.CMsgClientGetDepotDecryptionKey; protobufs[EMsg.ClientGetDepotDecryptionKeyResponse] = Schema.CMsgClientGetDepotDecryptionKeyResponse; protobufs[EMsg.ClientGetCDNAuthToken] = Schema.CMsgClientGetCDNAuthToken; protobufs[EMsg.ClientGetCDNAuthTokenResponse] = Schema.CMsgClientGetCDNAuthTokenResponse; protobufs[EMsg.ClientCheckAppBetaPassword] = Schema.CMsgClientCheckAppBetaPassword; protobufs[EMsg.ClientCheckAppBetaPasswordResponse] = Schema.CMsgClientCheckAppBetaPasswordResponse; protobufs[EMsg.ClientKickPlayingSession] = Schema.CMsgClientKickPlayingSession; protobufs[EMsg.ClientPlayingSessionState] = Schema.CMsgClientPlayingSessionState; protobufs[EMsg.ClientToGC] = Schema.CMsgGCClient; protobufs[EMsg.ClientFromGC] = Schema.CMsgGCClient; protobufs[EMsg.ClientRichPresenceUpload] = Schema.CMsgClientRichPresenceUpload; protobufs[EMsg.ClientRichPresenceRequest] = Schema.CMsgClientRichPresenceRequest; protobufs[EMsg.ClientRichPresenceInfo] = Schema.CMsgClientRichPresenceInfo; protobufs[EMsg.ClientGetEmoticonList] = Schema.CMsgClientGetEmoticonList; protobufs[EMsg.ClientEmoticonList] = Schema.CMsgClientEmoticonList; protobufs[EMsg.ClientGetAuthorizedDevicesResponse] = Schema.CMsgClientGetAuthorizedDevices; protobufs[EMsg.ClientAuthorizeLocalDeviceRequest] = Schema.CMsgClientAuthorizeLocalDeviceRequest; protobufs[EMsg.ClientAuthorizeLocalDeviceResponse] = Schema.CMsgClientAuthorizeLocalDevice; protobufs[EMsg.ClientDeauthorizeDeviceRequest] = Schema.CMsgClientDeauthorizeDeviceRequest; protobufs[EMsg.ClientDeauthorizeDevice] = Schema.CMsgClientDeauthorizeDevice; protobufs[EMsg.ClientUseLocalDeviceAuthorizations] = Schema.CMsgClientUseLocalDeviceAuthorizations; // Unified protobufs protobufs['GameServers.GetServerList#1_Request'] = Schema.CGameServers_GetServerList_Request; protobufs['GameServers.GetServerList#1_Response'] = Schema.CGameServers_GetServerList_Response; protobufs['GameServers.GetServerSteamIDsByIP#1_Request'] = Schema.CGameServers_GetServerSteamIDsByIP_Request; protobufs['GameServers.GetServerSteamIDsByIP#1_Response'] = Schema.CGameServers_IPsWithSteamIDs_Response; protobufs['GameServers.GetServerIPsBySteamID#1_Request'] = Schema.CGameServers_GetServerIPsBySteamID_Request; protobufs['GameServers.GetServerIPsBySteamID#1_Response'] = Schema.CGameServers_IPsWithSteamIDs_Response; protobufs['TwoFactor.AddAuthenticator#1_Request'] = Schema.CTwoFactor_AddAuthenticator_Request; protobufs['TwoFactor.AddAuthenticator#1_Response'] = Schema.CTwoFactor_AddAuthenticator_Response; protobufs['TwoFactor.FinalizeAddAuthenticator#1_Request'] = Schema.CTwoFactor_FinalizeAddAuthenticator_Request; protobufs['TwoFactor.FinalizeAddAuthenticator#1_Response'] = Schema.CTwoFactor_FinalizeAddAuthenticator_Response; protobufs['TwoFactor.SendEmail#1_Request'] = Schema.CTwoFactor_SendEmail_Request; protobufs['TwoFactor.SendEmail#1_Response'] = Schema.CTwoFactor_SendEmail_Response; protobufs['TwoFactor.RemoveAuthenticator#1_Request'] = Schema.CTwoFactor_RemoveAuthenticator_Request; protobufs['TwoFactor.RemoveAuthenticator#1_Response'] = Schema.CTwoFactor_RemoveAuthenticator_Response; protobufs['Credentials.GetSteamGuardDetails#1_Request'] = Schema.CCredentials_GetSteamGuardDetails_Request; protobufs['Credentials.GetSteamGuardDetails#1_Response'] = Schema.CCredentials_GetSteamGuardDetails_Response; protobufs['Credentials.GetAccountAuthSecret#1_Request'] = Schema.CCredentials_GetAccountAuthSecret_Request; protobufs['Credentials.GetAccountAuthSecret#1_Response'] = Schema.CCredentials_GetAccountAuthSecret_Response; protobufs['Credentials.GetCredentialChangeTimeDetails#1_Request'] = Schema.CCredentials_LastCredentialChangeTime_Request; protobufs['Credentials.GetCredentialChangeTimeDetails#1_Response'] = Schema.CCredentials_LastCredentialChangeTime_Response; protobufs['PublishedFile.GetDetails#1_Request'] = Schema.CPublishedFile_GetDetails_Request; protobufs['PublishedFile.GetDetails#1_Response'] = Schema.CPublishedFile_GetDetails_Response; protobufs['Player.GetGameBadgeLevels#1_Request'] = Schema.CPlayer_GetGameBadgeLevels_Request; protobufs['Player.GetGameBadgeLevels#1_Response'] = Schema.CPlayer_GetGameBadgeLevels_Response; protobufs['Player.GetNicknameList#1_Request'] = Schema.CPlayer_GetNicknameList_Request; protobufs['Player.GetNicknameList#1_Response'] = Schema.CPlayer_GetNicknameList_Response; protobufs['Player.GetEmoticonList#1_Request'] = Schema.CPlayer_GetEmoticonList_Request; protobufs['Player.GetEmoticonList#1_Response'] = Schema.CPlayer_GetEmoticonList_Response; protobufs['Player.GetPrivacySettings#1_Request'] = Schema.CPlayer_GetPrivacySettings_Request; protobufs['Player.GetPrivacySettings#1_Response'] = Schema.CPlayer_GetPrivacySettings_Response; protobufs['Player.GetOwnedGames#1_Request'] = Schema.CPlayer_GetOwnedGames_Request; protobufs['Player.GetOwnedGames#1_Response'] = Schema.CPlayer_GetOwnedGames_Response; protobufs['Player.GetProfileItemsOwned#1_Request'] = Schema.CPlayer_GetProfileItemsOwned_Request; protobufs['Player.GetProfileItemsOwned#1_Response'] = Schema.CPlayer_GetProfileItemsOwned_Response; protobufs['Player.GetProfileItemsEquipped#1_Request'] = Schema.CPlayer_GetProfileItemsEquipped_Request; protobufs['Player.GetProfileItemsEquipped#1_Response'] = Schema.CPlayer_GetProfileItemsEquipped_Response; protobufs['Player.GetProfileBackground#1_Request'] = Schema.CPlayer_GetProfileBackground_Request; protobufs['Player.GetProfileBackground#1_Response'] = Schema.CPlayer_GetProfileBackground_Response; protobufs['Player.SetProfileBackground#1_Request'] = Schema.CPlayer_SetProfileBackground_Request; protobufs['Player.SetProfileBackground#1_Response'] = Schema.CPlayer_SetProfileBackground_Response; protobufs['PlayerClient.NotifyFriendNicknameChanged#1'] = Schema.CPlayer_FriendNicknameChanged_Notification; protobufs['Econ.GetAssetClassInfo#1_Request'] = Schema.CEcon_GetAssetClassInfo_Request; protobufs['Econ.GetAssetClassInfo#1_Response'] = Schema.CEcon_GetAssetClassInfo_Response; protobufs['Store.GetLocalizedNameForTags#1_Request'] = Schema.CStore_GetLocalizedNameForTags_Request; protobufs['Store.GetLocalizedNameForTags#1_Response'] = Schema.CStore_GetLocalizedNameForTags_Response; protobufs['Econ.GetTradeOfferAccessToken#1_Request'] = Schema.CEcon_GetTradeOfferAccessToken_Request; protobufs['Econ.GetTradeOfferAccessToken#1_Response'] = Schema.CEcon_GetTradeOfferAccessToken_Response; protobufs['ChatRoom.CreateChatRoomGroup#1_Request'] = Schema.CChatRoom_CreateChatRoomGroup_Request; protobufs['ChatRoom.CreateChatRoomGroup#1_Response'] = Schema.CChatRoom_CreateChatRoomGroup_Response; protobufs['ChatRoom.SaveChatRoomGroup#1_Request'] = Schema.CChatRoom_SaveChatRoomGroup_Request; protobufs['ChatRoom.SaveChatRoomGroup#1_Response'] = Schema.CChatRoom_SaveChatRoomGroup_Response; protobufs['ChatRoom.RenameChatRoomGroup#1_Request'] = Schema.CChatRoom_RenameChatRoomGroup_Request; protobufs['ChatRoom.RenameChatRoomGroup#1_Response'] = Schema.CChatRoom_RenameChatRoomGroup_Response; protobufs['ChatRoom.SetChatRoomGroupTagline#1_Request'] = Schema.CChatRoom_SetChatRoomGroupTagline_Request; protobufs['ChatRoom.SetChatRoomGroupTagline#1_Response'] = Schema.CChatRoom_SetChatRoomGroupTagline_Response; protobufs['ChatRoom.SetChatRoomGroupAvatar#1_Request'] = Schema.CChatRoom_SetChatRoomGroupAvatar_Request; protobufs['ChatRoom.SetChatRoomGroupAvatar#1_Response'] = Schema.CChatRoom_SetChatRoomGroupAvatar_Response; protobufs['ChatRoom.MuteUserInGroup#1_Request'] = Schema.CChatRoom_MuteUser_Request; protobufs['ChatRoom.MuteUserInGroup#1_Response'] = Schema.CChatRoom_MuteUser_Response; protobufs['ChatRoom.KickUserFromGroup#1_Request'] = Schema.CChatRoom_KickUser_Request; protobufs['ChatRoom.KickUserFromGroup#1_Response'] = Schema.CChatRoom_KickUser_Response; protobufs['ChatRoom.SetUserBanState#1_Request'] = Schema.CChatRoom_SetUserBanState_Request; protobufs['ChatRoom.SetUserBanState#1_Response'] = Schema.CChatRoom_SetUserBanState_Response; protobufs['ChatRoom.RevokeInviteToGroup#1_Request'] = Schema.CChatRoom_RevokeInvite_Request; protobufs['ChatRoom.RevokeInviteToGroup#1_Response'] = Schema.CChatRoom_RevokeInvite_Response; protobufs['ChatRoom.CreateRole#1_Request'] = Schema.CChatRoom_CreateRole_Request; protobufs['ChatRoom.CreateRole#1_Response'] = Schema.CChatRoom_CreateRole_Response; protobufs['ChatRoom.GetRoles#1_Request'] = Schema.CChatRoom_GetRoles_Request; protobufs['ChatRoom.GetRoles#1_Response'] = Schema.CChatRoom_GetRoles_Response; protobufs['ChatRoom.RenameRole#1_Request'] = Schema.CChatRoom_RenameRole_Request; protobufs['ChatRoom.RenameRole#1_Response'] = Schema.CChatRoom_RenameRole_Response; protobufs['ChatRoom.ReorderRole#1_Request'] = Schema.CChatRoom_ReorderRole_Request; protobufs['ChatRoom.ReorderRole#1_Response'] = Schema.CChatRoom_ReorderRole_Response; protobufs['ChatRoom.DeleteRole#1_Request'] = Schema.CChatRoom_DeleteRole_Request; protobufs['ChatRoom.DeleteRole#1_Response'] = Schema.CChatRoom_DeleteRole_Response; protobufs['ChatRoom.GetRoleActions#1_Request'] = Schema.CChatRoom_GetRoleActions_Request; protobufs['ChatRoom.GetRoleActions#1_Response'] = Schema.CChatRoom_GetRoleActions_Response; protobufs['ChatRoom.ReplaceRoleActions#1_Request'] = Schema.CChatRoom_ReplaceRoleActions_Request; protobufs['ChatRoom.ReplaceRoleActions#1_Response'] = Schema.CChatRoom_ReplaceRoleActions_Response; protobufs['ChatRoom.AddRoleToUser#1_Request'] = Schema.CChatRoom_AddRoleToUser_Request; protobufs['ChatRoom.AddRoleToUser#1_Response'] = Schema.CChatRoom_AddRoleToUser_Response; protobufs['ChatRoom.GetRolesForUser#1_Request'] = Schema.CChatRoom_GetRolesForUser_Request; protobufs['ChatRoom.GetRolesForUser#1_Response'] = Schema.CChatRoom_GetRolesForUser_Response; protobufs['ChatRoom.DeleteRoleFromUser#1_Request'] = Schema.CChatRoom_DeleteRoleFromUser_Request; protobufs['ChatRoom.DeleteRoleFromUser#1_Response'] = Schema.CChatRoom_DeleteRoleFromUser_Response; protobufs['ChatRoom.JoinChatRoomGroup#1_Request'] = Schema.CChatRoom_JoinChatRoomGroup_Request; protobufs['ChatRoom.JoinChatRoomGroup#1_Response'] = Schema.CChatRoom_JoinChatRoomGroup_Response; protobufs['ChatRoom.InviteFriendToChatRoomGroup#1_Request'] = Schema.CChatRoom_InviteFriendToChatRoomGroup_Request; protobufs['ChatRoom.InviteFriendToChatRoomGroup#1_Response'] = Schema.CChatRoom_InviteFriendToChatRoomGroup_Response; protobufs['ChatRoom.LeaveChatRoomGroup#1_Request'] = Schema.CChatRoom_LeaveChatRoomGroup_Request; protobufs['ChatRoom.LeaveChatRoomGroup#1_Response'] = Schema.CChatRoom_LeaveChatRoomGroup_Response; protobufs['ChatRoom.CreateChatRoom#1_Request'] = Schema.CChatRoom_CreateChatRoom_Request; protobufs['ChatRoom.CreateChatRoom#1_Response'] = Schema.CChatRoom_CreateChatRoom_Response; protobufs['ChatRoom.DeleteChatRoom#1_Request'] = Schema.CChatRoom_DeleteChatRoom_Request; protobufs['ChatRoom.DeleteChatRoom#1_Response'] = Schema.CChatRoom_DeleteChatRoom_Response; protobufs['ChatRoom.RenameChatRoom#1_Request'] = Schema.CChatRoom_RenameChatRoom_Request; protobufs['ChatRoom.RenameChatRoom#1_Response'] = Schema.CChatRoom_RenameChatRoom_Response; protobufs['ChatRoom.SendChatMessage#1_Request'] = Schema.CChatRoom_SendChatMessage_Request; protobufs['ChatRoom.SendChatMessage#1_Response'] = Schema.CChatRoom_SendChatMessage_Response; protobufs['ChatRoom.JoinVoiceChat#1_Request'] = Schema.CChatRoom_JoinVoiceChat_Request; protobufs['ChatRoom.JoinVoiceChat#1_Response'] = Schema.CChatRoom_JoinVoiceChat_Response; protobufs['ChatRoom.LeaveVoiceChat#1_Request'] = Schema.CChatRoom_LeaveVoiceChat_Request; protobufs['ChatRoom.LeaveVoiceChat#1_Response'] = Schema.CChatRoom_LeaveVoiceChat_Response; protobufs['ChatRoom.GetMessageHistory#1_Request'] = Schema.CChatRoom_GetMessageHistory_Request; protobufs['ChatRoom.GetMessageHistory#1_Response'] = Schema.CChatRoom_GetMessageHistory_Response; protobufs['ChatRoom.GetMyChatRoomGroups#1_Request'] = Schema.CChatRoom_GetMyChatRoomGroups_Request; protobufs['ChatRoom.GetMyChatRoomGroups#1_Response'] = Schema.CChatRoom_GetMyChatRoomGroups_Response; protobufs['ChatRoom.GetChatRoomGroupState#1_Request'] = Schema.CChatRoom_GetChatRoomGroupState_Request; protobufs['ChatRoom.GetChatRoomGroupState#1_Response'] = Schema.CChatRoom_GetChatRoomGroupState_Response; protobufs['ChatRoom.GetChatRoomGroupSummary#1_Request'] = Schema.CChatRoom_GetChatRoomGroupSummary_Request; protobufs['ChatRoom.GetChatRoomGroupSummary#1_Response'] = Schema.CChatRoom_GetChatRoomGroupSummary_Response; protobufs['ChatRoom.AckChatMessage#1'] = Schema.CChatRoom_AckChatMessage_Notification; protobufs['ChatRoom.CreateInviteLink#1_Request'] = Schema.CChatRoom_CreateInviteLink_Request; protobufs['ChatRoom.CreateInviteLink#1_Response'] = Schema.CChatRoom_CreateInviteLink_Response; protobufs['ChatRoom.GetInviteLinkInfo#1_Request'] = Schema.CChatRoom_GetInviteLinkInfo_Request; protobufs['ChatRoom.GetInviteLinkInfo#1_Response'] = Schema.CChatRoom_GetInviteLinkInfo_Response; protobufs['ChatRoom.GetInviteInfo#1_Request'] = Schema.CChatRoom_GetInviteInfo_Request; protobufs['ChatRoom.GetInviteInfo#1_Response'] = Schema.CChatRoom_GetInviteInfo_Response; protobufs['ChatRoom.GetInviteLinksForGroup#1_Request'] = Schema.CChatRoom_GetInviteLinksForGroup_Request; protobufs['ChatRoom.GetInviteLinksForGroup#1_Response'] = Schema.CChatRoom_GetInviteLinksForGroup_Response; protobufs['ChatRoom.GetBanList#1_Request'] = Schema.CChatRoom_GetBanList_Request; protobufs['ChatRoom.GetBanList#1_Response'] = Schema.CChatRoom_GetBanList_Response; protobufs['ChatRoom.GetInviteList#1_Request'] = Schema.CChatRoom_GetInviteList_Request; protobufs['ChatRoom.GetInviteList#1_Response'] = Schema.CChatRoom_GetInviteList_Response; protobufs['ChatRoom.DeleteInviteLink#1_Request'] = Schema.CChatRoom_DeleteInviteLink_Request; protobufs['ChatRoom.DeleteInviteLink#1_Response'] = Schema.CChatRoom_DeleteInviteLink_Response; protobufs['ChatRoom.SetSessionActiveChatRoomGroups#1_Request'] = Schema.CChatRoom_SetSessionActiveChatRoomGroups_Request; protobufs['ChatRoom.SetSessionActiveChatRoomGroups#1_Response'] = Schema.CChatRoom_SetSessionActiveChatRoomGroups_Response; protobufs['ChatRoom.SetUserChatGroupPreferences#1_Request'] = Schema.CChatRoom_SetUserChatGroupPreferences_Request; protobufs['ChatRoom.SetUserChatGroupPreferences#1_Response'] = Schema.CChatRoom_SetUserChatGroupPreferences_Response; protobufs['ChatRoom.DeleteChatMessages#1_Request'] = Schema.CChatRoom_DeleteChatMessages_Request; protobufs['ChatRoom.DeleteChatMessages#1_Response'] = Schema.CChatRoom_DeleteChatMessages_Response; protobufs['ChatRoomClient.NotifyIncomingChatMessage#1'] = Schema.CChatRoom_IncomingChatMessage_Notification; protobufs['ChatRoomClient.NotifyChatMessageModified#1'] = Schema.CChatRoom_ChatMessageModified_Notification; protobufs['ChatRoomClient.NotifyMemberStateChange#1'] = Schema.CChatRoom_MemberStateChange_Notification; protobufs['ChatRoomClient.NotifyChatRoomHeaderStateChange#1'] = Schema.CChatRoom_ChatRoomHeaderState_Notification; protobufs['ChatRoomClient.NotifyChatRoomGroupRoomsChange#1'] = Schema.CChatRoom_ChatRoomGroupRoomsChange_Notification; protobufs['ChatRoomClient.NotifyShouldRejoinChatRoomVoiceChat#1'] = Schema.CChatRoom_NotifyShouldRejoinChatRoomVoiceChat_Notification; protobufs['ChatRoomClient.NotifyChatGroupUserStateChanged#1'] = Schema.ChatRoomClient_NotifyChatGroupUserStateChanged_Notification; protobufs['ChatRoomClient.NotifyAckChatMessageEcho#1'] = Schema.CChatRoom_AckChatMessage_Notification; protobufs['ClanChatRooms.GetClanChatRoomInfo#1_Request'] = Schema.CClanChatRooms_GetClanChatRoomInfo_Request; protobufs['ClanChatRooms.GetClanChatRoomInfo#1_Response'] = Schema.CClanChatRooms_GetClanChatRoomInfo_Response; protobufs['FriendMessages.GetRecentMessages#1_Request'] = Schema.CFriendMessages_GetRecentMessages_Request; protobufs['FriendMessages.GetRecentMessages#1_Response'] = Schema.CFriendMessages_GetRecentMessages_Response; protobufs['FriendMessages.GetActiveMessageSessions#1_Request'] = Schema.CFriendsMessages_GetActiveMessageSessions_Request; protobufs['FriendMessages.GetActiveMessageSessions#1_Response'] = Schema.CFriendsMessages_GetActiveMessageSessions_Response; protobufs['FriendMessages.SendMessage#1_Request'] = Schema.CFriendMessages_SendMessage_Request; protobufs['FriendMessages.SendMessage#1_Response'] = Schema.CFriendMessages_SendMessage_Response; protobufs['FriendMessages.AckMessage#1'] = Schema.CFriendMessages_AckMessage_Notification; protobufs['FriendMessages.IsInFriendsUIBeta#1_Request'] = Schema.CFriendMessages_IsInFriendsUIBeta_Request; protobufs['FriendMessages.IsInFriendsUIBeta#1_Response'] = Schema.CFriendMessages_IsInFriendsUIBeta_Response; protobufs['FriendMessagesClient.IncomingMessage#1'] = Schema.CFriendMessages_IncomingMessage_Notification; protobufs['FriendMessagesClient.NotifyAckMessageEcho#1'] = Schema.CFriendMessages_AckMessage_Notification; protobufs['Community.GetAppRichPresenceLocalization#1_Request'] = Schema.CCommunity_GetAppRichPresenceLocalization_Request; protobufs['Community.GetAppRichPresenceLocalization#1_Response'] = Schema.CCommunity_GetAppRichPresenceLocalization_Response; protobufs['UserAccount.CreateFriendInviteToken#1_Request'] = Schema.CUserAccount_CreateFriendInviteToken_Request; protobufs['UserAccount.CreateFriendInviteToken#1_Response'] = Schema.CUserAccount_CreateFriendInviteToken_Response; protobufs['UserAccount.GetFriendInviteTokens#1_Request'] = Schema.CUserAccount_GetFriendInviteTokens_Request; protobufs['UserAccount.GetFriendInviteTokens#1_Response'] = Schema.CUserAccount_GetFriendInviteTokens_Response; protobufs['UserAccount.ViewFriendInviteToken#1_Request'] = Schema.CUserAccount_ViewFriendInviteToken_Request; protobufs['UserAccount.ViewFriendInviteToken#1_Response'] = Schema.CUserAccount_ViewFriendInviteToken_Response; protobufs['UserAccount.RedeemFriendInviteToken#1_Request'] = Schema.CUserAccount_RedeemFriendInviteToken_Request; protobufs['UserAccount.RedeemFriendInviteToken#1_Response'] = Schema.CUserAccount_RedeemFriendInviteToken_Response; protobufs['UserAccount.RevokeFriendInviteToken#1_Request'] = Schema.CUserAccount_RevokeFriendInviteToken_Request; protobufs['UserAccount.RevokeFriendInviteToken#1_Response'] = Schema.CUserAccount_RevokeFriendInviteToken_Response; protobufs['DeviceAuth.GetOwnAuthorizedDevices#1_Request'] = Schema.CDeviceAuth_GetOwnAuthorizedDevices_Request; protobufs['DeviceAuth.GetOwnAuthorizedDevices#1_Response'] = Schema.CDeviceAuth_GetOwnAuthorizedDevices_Response; protobufs['DeviceAuth.AddAuthorizedBorrowers#1_Request'] = Schema.CDeviceAuth_AddAuthorizedBorrowers_Request; protobufs['DeviceAuth.AddAuthorizedBorrowers#1_Response'] = Schema.CDeviceAuth_AddAuthorizedBorrowers_Response; protobufs['DeviceAuth.RemoveAuthorizedBorrowers#1_Request'] = Schema.CDeviceAuth_RemoveAuthorizedBorrowers_Request; protobufs['DeviceAuth.RemoveAuthorizedBorrowers#1_Response'] = Schema.CDeviceAuth_RemoveAuthorizedBorrowers_Response; protobufs['DeviceAuth.GetAuthorizedBorrowers#1_Request'] = Schema.CDeviceAuth_GetAuthorizedBorrowers_Request; protobufs['DeviceAuth.GetAuthorizedBorrowers#1_Response'] = Schema.CDeviceAuth_GetAuthorizedBorrowers_Response; protobufs['ContentServerDirectory.GetManifestRequestCode#1_Request'] = Schema.CContentServerDirectory_GetManifestRequestCode_Request; protobufs['ContentServerDirectory.GetManifestRequestCode#1_Response'] = Schema.CContentServerDirectory_GetManifestRequestCode_Response; protobufs['SteamNotificationClient.NotificationsReceived#1'] = Schema.CSteamNotification_NotificationsReceived_Notification; protobufs['SteamNotification.MarkNotificationsRead#1'] = Schema.CSteamNotification_MarkNotificationsRead_Notification; // Make sure that every protobuf we used actually exists let hadMissingProtobuf = false; for (let i in protobufs) { if (typeof protobufs[i] != 'undefined') { continue; } // The protobuf doesn't exist if (i.match(/^\d+$/)) { i = EMsg[i] || i; } console.error(`Protobuf for message ${i} does not exist!`); hadMissingProtobuf = true; } if (hadMissingProtobuf) { throw new Error('One or more protobuf schemas are missing'); } class SteamUserMessages extends SteamUserConnection { /** * Encode a protobuf. * @param {object} proto - The protobuf class * @param {object} data - The data to serialize * @returns {Buffer} * @protected */ static _encodeProto(proto, data) { return proto.encode(data).finish(); } /** * Decode a protobuf. * @param {object} proto - The protobuf class * @param {Buffer|ByteBuffer} encoded - The data to decode * @returns {object} * @protected */ static _decodeProto(proto, encoded) { if (ByteBuffer.isByteBuffer(encoded)) { encoded = encoded.toBuffer(); } let decoded = proto.decode(encoded); let objNoDefaults = proto.toObject(decoded, {longs: String}); let objWithDefaults = proto.toObject(decoded, {defaults: true, longs: String}); return replaceDefaults(objNoDefaults, objWithDefaults); function replaceDefaults(noDefaults, withDefaults) { if (Array.isArray(withDefaults)) { return withDefaults.map((val, idx) => replaceDefaults(noDefaults[idx], val)); } for (let i in withDefaults) { if (!Object.hasOwnProperty.call(withDefaults, i)) { continue; } if (withDefaults[i] && typeof withDefaults[i] === 'object' && !Buffer.isBuffer(withDefaults[i])) { // Covers both object and array cases, both of which will work // Won't replace empty arrays, but that's desired behavior withDefaults[i] = replaceDefaults(noDefaults[i], withDefaults[i]); } else if (typeof noDefaults[i] === 'undefined' && isReplaceableDefaultValue(withDefaults[i])) { withDefaults[i] = null; } } return withDefaults; } function isReplaceableDefaultValue(val) { if (Buffer.isBuffer(val) && val.length == 0) { // empty buffer is replaceable return true; } if (Array.isArray(val)) { // empty array is not replaceable (empty repeated fields) return false; } if (val === '0') { // Zero as a string is replaceable (64-bit integer) return true; } // Anything falsy is true return !val; } } /** * @param {int|object} emsgOrHeader * @param {object|Buffer|ByteBuffer} body * @param {function} [callback] * @protected */ _send(emsgOrHeader, body, callback) { // header fields: msg, proto, sourceJobID, targetJobID let header = typeof emsgOrHeader === 'object' ? emsgOrHeader : {msg: emsgOrHeader}; let emsg = header.msg; let canWeSend = this.steamID || ( this._tempSteamID && [EMsg.ChannelEncryptResponse, EMsg.ClientLogon, EMsg.ClientHello, EMsg.ServiceMethodCallFromClientNonAuthed].includes(emsg) ); if (!canWeSend) { // We're disconnected, drop it this.emit('debug', 'Dropping outgoing message ' + emsg + ' because we\'re not logged on.'); return; } const Proto = protobufs[emsg]; if (Proto) { header.proto = header.proto || {}; body = SteamUserMessages._encodeProto(Proto, body); } else if (ByteBuffer.isByteBuffer(body)) { body = body.toBuffer(); } let jobIdSource = null; if (callback) { jobIdSource = ++this._currentJobID; this._jobs.add(jobIdSource.toString(), callback); } let emsgName = EMsg[emsg] || emsg; if ([EMsg.ServiceMethodCallFromClient, EMsg.ServiceMethodCallFromClientNonAuthed].includes(emsg) && header.proto && header.proto.target_job_name) { emsgName = header.proto.target_job_name; } this.emit(VERBOSE_EMSG_LIST.includes(emsg) ? 'debug-verbose' : 'debug', 'Sending message: ' + emsgName); // Make the header let hdrBuf; if (header.msg == EMsg.ChannelEncryptResponse) { // since we're setting up the encrypted channel, we use this very minimal header hdrBuf = ByteBuffer.allocate(4 + 8 + 8, ByteBuffer.LITTLE_ENDIAN); hdrBuf.writeUint32(header.msg); hdrBuf.writeUint64(header.targetJobID || JOBID_NONE); hdrBuf.writeUint64(jobIdSource || header.sourceJobID || JOBID_NONE); } else if (header.proto) { // if we have a protobuf header, use that let shouldIncludeSessionId = ![EMsg.ClientHello, EMsg.ServiceMethodCallFromClientNonAuthed].includes(header.msg); header.proto.client_sessionid = shouldIncludeSessionId ? (this._sessionID || 0) : 0; header.proto.steamid = shouldIncludeSessionId ? (this.steamID || this._tempSteamID).getSteamID64() : '0'; header.proto.jobid_source = jobIdSource || header.proto.jobid_source || header.sourceJobID || JOBID_NONE; header.proto.jobid_target = header.proto.jobid_target || header.targetJobID || JOBID_NONE; let hdrProtoBuf = SteamUserMessages._encodeProto(Schema.CMsgProtoBufHeader, header.proto); hdrBuf = ByteBuffer.allocate(4 + 4 + hdrProtoBuf.length, ByteBuffer.LITTLE_ENDIAN); hdrBuf.writeUint32(header.msg | PROTO_MASK); hdrBuf.writeUint32(hdrProtoBuf.length); hdrBuf.append(hdrProtoBuf); } else { // this is the standard non-protobuf extended header hdrBuf = ByteBuffer.allocate(4 + 1 + 2 + 8 + 8 + 1 + 8 + 4, ByteBuffer.LITTLE_ENDIAN); hdrBuf.writeUint32(header.msg); hdrBuf.writeByte(36); hdrBuf.writeUint16(2); hdrBuf.writeUint64(header.targetJobID || JOBID_NONE); hdrBuf.writeUint64(jobIdSource || header.sourceJobID || JOBID_NONE); hdrBuf.writeByte(239); hdrBuf.writeUint64((this.steamID || this._tempSteamID).getSteamID64()); hdrBuf.writeUint32(this._sessionID || 0); } let outputBuffer = Buffer.concat([hdrBuf.flip().toBuffer(), body]); this.emit('debug-traffic-outgoing', outputBuffer, header.msg); this._connection.send(outputBuffer); } /** * Handles a raw binary netmessage by parsing the header and routing it appropriately * @param {Buffer|string} buffer * @param {BaseConnection} [conn] * @param {string} [multiId] * @protected */ _handleNetMessage(buffer, conn, multiId) { if (conn && conn != this._connection) { let ghostConnId = conn.connectionType[0] + conn.connectionId; let expectedConnId = this._connection ? (this._connection.connectionType[0] + this._connection.connectionId) : 'NO CONNECTION'; this.emit('debug', `Received net message from ghost connection ${ghostConnId} (expected ${expectedConnId})`); return; } if (this._useMessageQueue && !multiId) { // Multi sub-messages skip the queue because we need messages contained in a decoded multi to be processed first this._incomingMessageQueue.push(arguments); this.emit('debug', `Enqueued incoming message; queue size is now ${this._incomingMessageQueue.length}`); return; } if (buffer === '__CLOSE__') { // This is an enqueued connection closure this._handleConnectionClose(conn); return; } let buf = ByteBuffer.wrap(buffer, ByteBuffer.LITTLE_ENDIAN); let rawEMsg = buf.readUint32(); let eMsg = rawEMsg & ~PROTO_MASK; let isProtobuf = !!(rawEMsg & PROTO_MASK); this.emit('debug-traffic-incoming', buffer, eMsg); let header = {msg: eMsg}; if ([EMsg.ChannelEncryptRequest, EMsg.ChannelEncryptResult].includes(eMsg)) { // for encryption setup, we just have a very small header with two fields header.targetJobID = buf.readUint64().toString(); header.sourceJobID = buf.readUint64().toString(); } else if (isProtobuf) { // decode the protobuf header let headerLength = buf.readUint32(); header.proto = SteamUserMessages._decodeProto(Schema.CMsgProtoBufHeader, buf.slice(buf.offset, buf.offset + headerLength)); buf.skip(headerLength); header.targetJobID = header.proto.jobid_target && header.proto.jobid_target.toString(); header.sourceJobID = header.proto.jobid_source && header.proto.jobid_source.toString(); header.steamID = header.proto.steamid && header.proto.steamid.toString(); header.sessionID = header.proto.client_sessionid; } else { // decode the extended header buf.skip(3); // 1 byte for header size (fixed at 36), 2 bytes for header version (fixed at 2) header.targetJobID = buf.readUint64().toString(); header.sourceJobID = buf.readUint64().toString(); buf.skip(1); // 1 byte for header canary (fixed at 239) header.steamID = buf.readUint64().toString(); header.sessionID = buf.readUint32(); } let sessionID = (header.proto && header.proto.client_sessionid) || header.sessionID; let steamID = (header.proto && header.proto.steamid) || header.steamID; let ourCurrentSteamID = this.steamID ? this.steamID.toString() : null; if (steamID && sessionID && (sessionID != this._sessionID || steamID.toString() != ourCurrentSteamID) && steamID != 0) { // TODO if we get a new sessionid, should we check if it matches a previously-closed session? probably not necessary... this._sessionID = sessionID; this.steamID = new SteamID(steamID.toString()); delete this._tempSteamID; } this._handleMessage(header, buf.slice(), conn, multiId); } /** * Handles and routes a parsed message * @param {object} header * @param {ByteBuffer} bodyBuf * @param {BaseConnection} [conn] * @param {string} [multiId] * @protected */ _handleMessage(header, bodyBuf, conn, multiId) { // Is this a multi? If yes, short-circuit and just process it now. if (header.msg == EMsg.Multi) { this._processMulti(header, SteamUserMessages._decodeProto(protobufs[EMsg.Multi], bodyBuf), conn); return; } let msgName = EMsg[header.msg] || header.msg; let handlerName = header.msg; let debugPrefix = multiId ? `[${multiId}] ` : (conn ? `[${conn.connectionType[0]}${conn.connectionId}] ` : ''); let isServiceMethodMsg = [EMsg.ServiceMethod, EMsg.ServiceMethodResponse].includes(header.msg); if (isServiceMethodMsg) { if (header.proto && header.proto.target_job_name) { handlerName = msgName = header.proto.target_job_name; if (header.msg == EMsg.ServiceMethodResponse) { handlerName += '_Response'; msgName += '_Response'; } } else { this.emit('debug', debugPrefix + 'Got ' + (header.msg == EMsg.ServiceMethod ? 'ServiceMethod' : 'ServiceMethodResponse') + ' without target_job_name'); return; } } if (!isServiceMethodMsg && header.proto && header.proto.target_job_name) { this.emit('debug', debugPrefix + 'Got unknown target_job_name ' + header.proto.target_job_name + ' for msg ' + msgName); } if (!this._handlerManager.hasHandler(handlerName) && this._jobs.get(header.targetJobID.toString()) === null) { this.emit(VERBOSE_EMSG_LIST.includes(header.msg) ? 'debug-verbose' : 'debug', debugPrefix + 'Unhandled message: ' + msgName); return; } let body = bodyBuf; if (protobufs[handlerName]) { body = SteamUserMessages._decodeProto(protobufs[handlerName], bodyBuf); } let handledMessageDebugMsg = debugPrefix + 'Handled message: ' + msgName; if (header.msg == EMsg.ClientLogOnResponse) { handledMessageDebugMsg += ` (${EResult[body.eresult] || body.eresult})`; } this.emit(VERBOSE_EMSG_LIST.includes(header.msg) ? 'debug-verbose' : 'debug', handledMessageDebugMsg); let cb = null; if (header.sourceJobID != JOBID_NONE) { // this message expects a response. make a callback we can pass to the end-user. cb = (emsgOrHeader, body) => { // once invoked the callback should set the jobid_target let responseHeader = typeof emsgOrHeader === 'object' ? emsgOrHeader : {msg: emsgOrHeader}; let emsg = responseHeader.msg; if (protobufs[emsg]) { responseHeader.proto = {jobid_target: header.sourceJobID}; } else { responseHeader.targetJobID = header.sourceJobID; } this._send(responseHeader, body); }; } let jobCallback = this._jobs.get(header.targetJobID.toString()); if (jobCallback) { // this is a response to something, so invoke the appropriate callback jobCallback.call(this, body, header, cb); } else { this._handlerManager.emit(this, handlerName, body, header, cb); } } /** * @param {object} header * @param {object} body * @param {object} conn * @returns {Promise<void>} * @protected */ async _processMulti(header, body, conn) { let multiId = conn.connectionType[0] + conn.connectionId + '#' + (++this._multiCount); this.emit('debug-verbose', `=== Processing ${body.size_unzipped ? 'gzipped multi msg' : 'multi msg'} ${multiId} (${body.message_body.length} bytes) ===`); let payload = body.message_body; // Enable the message queue while we're unzipping the message (or waiting until the next event loop cycle). // This prevents any messages from getting processed out of order. this._useMessageQueue = true; if (body.size_unzipped) { try { payload = await new Promise((resolve, reject) => { Zlib.gunzip(payload, (err, unzipped) => { if (err) { return reject(err); } resolve(unzipped); }); }); } catch (ex) { this.emit('error', ex); this._disconnect(true); return; } } else { // Await a setImmediate promise to guarantee that multi msg processing always takes at least one iteration of the event loop. // This avoids message queue processing shenanigans. Waiting until the next iteration of the event loop enables // _handleNetMessage at the end of this method to return immediately, which will thus exit the clear-queue loop // because the queue got re-enabled. Prevents the queue from being cleared in multiple places at once. await new Promise(resolve => setImmediate(resolve)); } if (!this._connection || this._connection != conn) { this.emit('debug', `=== Bailing out on processing multi msg ${multiId} because our connection is ${!this._connection ? 'lost' : 'different'}! ===`); return; } while (payload.length && (this.steamID || this._tempSteamID)) { let subSize = payload.readUInt32LE(0); this._handleNetMessage(payload.slice(4, 4 + subSize), conn, multiId); payload = payload.slice(4 + subSize); } this.emit('debug-verbose', `=== Finished processing multi msg ${multiId}; now clearing queue of size ${this._incomingMessageQueue.length} ===`); // Go ahead and process anything in the queue now. First disable the message queue. We don't need to worry about // newly-received messages sneaking in ahead of the queue being cleared, since message processing is synchronous. // If we encounter another multi msg, the message queue will get re-enabled. this._useMessageQueue = false; // Continue to pop items from the message queue until it's empty, or it gets re-enabled. If the message queue gets // re-enabled, immediately stop popping items from it to avoid stuff getting out of order. while (this._incomingMessageQueue.length > 0 && !this._useMessageQueue) { this._handleNetMessage.apply(this, this._incomingMessageQueue.shift()); } if (this._incomingMessageQueue.length > 0) { this.emit('debug-verbose', `[${multiId}] Message queue processing ended early with ${this._incomingMessageQueue.length} elements remaining`); } } /** * Send a unified message. * @param {string} methodName - In format Interface.Method#Version, e.g. Foo.DoThing#1 * @param {object} methodData * @param {function} [callback] * @protected */ _sendUnified(methodName, methodData, callback) { let Proto = protobufs[methodName + (callback ? '_Request' : '')]; let header = { msg: EMsg.ServiceMethodCallFromClient, proto: { target_job_name: methodName } }; this._send(header, SteamUserMessages._encodeProto(Proto, methodData), callback); } _getLoginSession() { if (!this._loginSession) { let options = { transport: new CMAuthTransport(this), machineId: this._logOnDetails?.machine_id }; let customMachineName = this._logOnDetails?.machine_name || ''; if (customMachineName.length > 0) { options.machineFriendlyName = customMachineName; } this._loginSession = new LoginSession(EAuthTokenPlatformType.SteamClient, options); } return this._loginSession; } } module.exports = SteamUserMessages;