--[[ Access to Steam API using forked “luasteam” library with a few extensions. Available to scripts with I/O access, others will raise an error. Also raises an error if failed to get access to the Steam API. Breaking changes comparing to `luasteam`: - `core` part is removed, you don’t need to initialize Steam or run callbacks, AC is already doing it for you. - `SteamID` type is now a string, not userdata, to improve performance, simplify things and allow for effortless serialization. - `SteamListenSocketHandle` type is an integer. - `UGC` removed (Assetto Corsa doesn’t use it). - Events reworked a bit: now, you can subscribe and unsubscribe using a pattern like this: `local unsub = luasteam.friends.onGameOverlayActivated(function () end)`. Old approach will still work, but with one caveat: reading `luasteam.friends.onGameOverlayActivated` will always return that function for subscribing, so if your code uses existance of listeners for something, it will break. - `networkingSockets`: - `onConnectionChanged`: `state_old` renamed to `oldState`, `debug` renamed to `endDebug` to keep things consistent. - `getConnectionInfo` returns third argument with actually useful information, first argument is boolean. - `sendMessages`: first argument removed. - `receiveMessagesOnConnection`: returns a single argument. - `receiveMessagesOnPollGroup`: returns a single argument, field `msg` renamed to `data`. - `flags` renamed, dropping “Send_” prefix (old values are available for backwards compatibility). - Names of fields used in `sendMessages` changed to better match Steam API. - `input`: - `setLEDColor`: `rgb` instead of three colors, actual `flags` argument to match Steam API. ]] ---@diagnostic disable ---@alias SteamID string @64-bit Steam ID or another 64-bit number ---@alias SteamLeaderboard SteamID @Leaderboard handle ---@alias SteamAppID integer @App (or DLC) ID ---@alias SteamInputHandle SteamID|'all' @Input controller handle. Pass `'all'` for `STEAM_INPUT_HANDLE_ALL_CONTROLLERS` ---@alias SteamInputActionSetHandle SteamID @Action set handle ---@alias SteamInputAnalogActionHandle SteamID @Analog action handle ---@alias SteamInputDigitalActionHandle SteamID @Digital action handle ---@alias SteamListenSocketHandle integer @Listen socket handle ---@alias SteamConnectionHandle integer @Network connection handle ---@alias SteamPollGroupHandle integer @Poll group handle ---@alias SteamScreenshotHandle integer @Screenshot handle, valid for the duration of game process ---@alias SteamFriendsGroupID integer @ID of a friends group ---@alias SteamAuthTicket integer @Auth ticket handle ---@alias SteamResult 'None'|'OK'|'Fail'|'NoConnection'|'NoConnectionRetry'|'InvalidPassword'|'LoggedInElsewhere'|'InvalidProtocolVer'|'InvalidParam'|'FileNotFound'|'Busy'|'InvalidState'|'InvalidName'|'InvalidEmail'|'DuplicateName'|'AccessDenied'|'Timeout'|'Banned'|'AccountNotFound'|'InvalidSteamID'|'ServiceUnavailable'|'NotLoggedOn'|'Pending'|'EncryptionFailure'|'InsufficientPrivilege'|'LimitExceeded'|'Revoked'|'Expired'|'AlreadyRedeemed'|'DuplicateRequest'|'AlreadyOwned'|'IPNotFound'|'PersistFailed'|'LockingFailed'|'LogonSessionReplaced'|'ConnectFailed'|'HandshakeFailed'|'IOFailure'|'RemoteDisconnect'|'ShoppingCartNotFound'|'Blocked'|'Ignored'|'NoMatch'|'AccountDisabled'|'ServiceReadOnly'|'AccountNotFeatured'|'AdministratorOK'|'ContentVersion'|'TryAnotherCM'|'PasswordRequiredToKickSession'|'AlreadyLoggedInElsewhere'|'Suspended'|'Cancelled'|'DataCorruption'|'DiskFull'|'RemoteCallFailed'|'PasswordUnset'|'ExternalAccountUnlinked'|'PSNTicketInvalid'|'ExternalAccountAlreadyLinked'|'RemoteFileConflict'|'IllegalPassword'|'SameAsPreviousValue'|'AccountLogonDenied'|'CannotUseOldPassword'|'InvalidLoginAuthCode'|'AccountLogonDeniedNoMail'|'HardwareNotCapableOfIPT'|'IPTInitError'|'ParentalControlRestricted'|'FacebookQueryError'|'ExpiredLoginAuthCode'|'IPLoginRestrictionFailed'|'AccountLockedDown'|'AccountLogonDeniedVerifiedEmailRequired'|'NoMatchingURL'|'BadResponse'|'RequirePasswordReEntry'|'ValueOutOfRange'|'UnexpectedError'|'Disabled'|'InvalidCEGSubmission'|'RestrictedDevice'|'RegionLocked'|'RateLimitExceeded'|'AccountLoginDeniedNeedTwoFactor'|'ItemDeleted'|'AccountLoginDeniedThrottle'|'TwoFactorCodeMismatch'|'TwoFactorActivationCodeMismatch'|'AccountAssociatedToMultiplePartners'|'NotModified'|'NoMobileDevice'|'TimeNotSynced'|'SmsCodeFailed'|'AccountLimitExceeded'|'AccountActivityLimitExceeded'|'PhoneActivityLimitExceeded'|'RefundToWallet'|'EmailSendFailure'|'NotSettled'|'NeedCaptcha'|'GSLTDenied'|'GSOwnerDenied'|'InvalidItemType'|'IPBanned'|'GSLTExpired'|'InsufficientFunds'|'TooManyPending'|'NoSiteLicensesFound'|'WGNetworkSendExceeded'|'AccountNotFriends'|'LimitedUserAccount'|'CantRemoveItem'|'AccountDeleted'|'ExistingUserCancelledLicense'|'CommunityCooldown'|'NoLauncherSpecified'|'MustAgreeToSSA'|'LauncherMigrated'|'SteamRealmMismatch'|'InvalidSignature'|'ParseFailure'|'NoVerifiedPhone'|'InsufficientBattery'|'ChargerRequired'|'CachedCredentialInvalid'|'PhoneNumberIsVOIP' ---@alias SteamPersonaState 'offline'|'online'|'busy'|'away'|'snooze'|'lookingToTrade'|'lookingToPlay'|'invisible' ---@alias SteamLobbyType 'Private'|'FriendsOnly'|'Public'|'Invisible' ---@alias SteamConnectionState 'None'|'Connecting'|'FindingRoute'|'Connected'|'ClosedByPeer'|'ProblemDetectedLocally' ---@alias SteamComparisonType 'EqualToOrLessThan'|'LessThan'|'Equal'|'GreaterThan'|'EqualToOrGreaterThan'|'NotEqual' ---@alias SteamChatMessageType 'Invalid'|'ChatMsg'|'Typing'|'InviteGame'|'Emote'|'LeftConversation'|'Entered'|'WasKicked'|'WasBanned'|'Disconnected'|'HistoricalChat'|'LinkBlocked' ---@alias SteamLogLevel 'None'|'Bug'|'Error'|'Important'|'Warning'|'Msg'|'Verbose'|'Debug'|'Everything' ---@alias SteamConnectionRealTimeStatus {state: SteamConnectionState, ping: integer, qualityLocal: number, qualityRemote: number, outPacketsPerSec: number, outBytesPerSec: number, inPacketsPerSec: number, inBytesPerSec: number, sendRateBytesPerSecond: integer, pendingUnreliable: integer, pendingReliable: integer, sentUnackedReliable: integer} ---@alias SteamRelayNetworkStatus {pingMeasurementInProgress: boolean, avail: integer, availAnyRelay: integer, availNetworkConfig: integer} ---With this fork of `luasteam`, instead of setting those functions to listen to events, you can call them and pass them ---a listener. This way, unused listeners no longer have to check if the function in Lua script is declared, and backend ---can add more API for more listeners without slowing things down. ---@alias SteamEvent fun(data: T)|(fun(listener: fun(data: T)): ac.Disposable) ---Regular callback, pretty much the same as in actual Steam API. ---@alias SteamCallback fun(result: T?, ioFailure: boolean) ---@class SteamNetworkConfig ---@field TimeoutInitial integer? ---@field TimeoutConnected integer? ---@field SendBufferSize integer? ---@field RecvBufferSize integer? ---@field RecvBufferMessages integer? ---@field RecvMaxMessageSize integer? ---@field RecvMaxSegmentsPerPacket integer? ---@field SendRateMin integer? ---@field SendRateMax integer? ---@field NagleTime integer? ---@field IP_AllowWithoutAuth integer? ---@field IPLocalHost_AllowWithoutAuth integer? ---@field MTU_PacketSize integer? ---@field MTU_DataSize integer? ---@field Unencrypted integer? ---@field SymmetricConnect integer? ---@field LocalVirtualPort integer? ---@field DualWifi_Enable integer? @N/A with GNS. ---@field EnableDiagnosticsUI integer? @N/A with GNS. ---@field SendTimeSincePreviousPacket integer? ---@field PacketTraceMaxBytes integer? ---@field OutOfOrderCorrectionWindowMicroseconds integer? @N/A with GNS. ---@field P2P_STUN_ServerList string|string[]|nil ---@field P2P_TURN_ServerList string|string[]|nil ---@field P2P_TURN_UserList string|string[]|nil @If a single value has been passed, but `P2P_TURN_ServerList` is a list, that value will apply to each item of `P2P_TURN_ServerList` ---@field P2P_TURN_PassList string|string[]|nil @If a single value has been passed, but `P2P_TURN_ServerList` is a list, that value will apply to each item of `P2P_TURN_ServerList` ---@field P2P_Transport_ICE_Enable integer? ---@field P2P_Transport_ICE_Penalty integer? ---@field P2P_Transport_SDR_Penalty integer? ---@field P2P_Transport_ICE_Implementation integer? ---@class LuaSteamUser ---@field getPlayerSteamLevel fun(): number ---@field getSteamID fun(): SteamID ---@field isBehindNAT fun(): boolean ---@field isPhoneVerified fun(): boolean ---@field isPhoneRequiringVerification fun(): boolean ---@field isPhoneIdentifying fun(): boolean ---@field isTwoFactorEnabled fun(): boolean ---@field loggedOn fun(): boolean ---@field getAuthSessionTicket fun(identityRemote: string?): {ticket: SteamAuthTicket, hexTicket: string}|nil ---@field cancelAuthTicket fun(ticket: SteamAuthTicket) ---@field onAuthSessionTicketResponse SteamEvent<{handle: SteamAuthTicket, result: SteamResult}> ---@class LuaSteamFriends ---@field activateGameOverlay fun(dialog: 'friends'|'community'|'players'|'settings'|'officialgamegroup'|'stats'|'achievements') ---@field activateGameOverlayInviteDialog fun(lobby: SteamID) ---@field activateGameOverlayToStore fun(appID: SteamAppID, flag: 'None'|'AddToCart'|'AddToCartAndShow') ---@field activateGameOverlayToUser fun(dialog: 'steamid'|'chat'|'jointrade'|'stats'|'achievements'|'friendadd'|'friendremove'|'friendrequestaccept'|'friendrequestignore'|, steamID: SteamID) ---@field activateGameOverlayToWebPage fun(url: string, mode: 'Default'|'Modal') ---@field clearRichPresence fun() ---@field enumerateFollowingList fun(startIndex: integer, callback: SteamCallback<{result: SteamResult, totalResultCount: integer, steamIDs: SteamID[]}>) ---@field getCoplayFriend fun(index: integer): SteamID ---@field getCoplayFriendCount fun(): integer ---@field getFollowerCount fun(user: SteamID, callback: SteamCallback<{result: SteamResult, count: integer, steamID: SteamID}>) ---@field getFriendByIndex fun(index: integer, flags: integer): SteamID ---@field getFriendCount fun(flags: integer): number ---@field getFriendCoplayGame fun(steamIDFriend: SteamID): integer ---@field getFriendCoplayTime fun(steamIDFriend: SteamID): integer ---@field getFriendCountFromSource fun(sourceID: SteamID): integer ---@field getFriendFromSourceByIndex fun(sourceID: SteamID, index: integer): SteamID ---@field getFriendGamePlayed fun(steamIDFriend: SteamID): nil|{gameID: integer, steamIDLobby: SteamID} ---@field getFriendGameIDPlayed fun(steamIDFriend: SteamID): integer? @A shortcut, returns game ID or `nil` ---@field getFriendMessage fun(steamIDFriend: SteamID, messageID: integer): string?, SteamChatMessageType|nil @Returns `nil` if the current user is chat restricted, if the provided Steam ID is not a friend, or if the index provided in iMessageID is invalid ---@field getFriendPersonaName fun(steamIDFriend: SteamID): string ---@field getFriendPersonaNameHistory fun(steamIDFriend: SteamID, personaName: integer): string ---@field getFriendPersonaState fun(steamIDFriend: SteamID): SteamPersonaState ---@field getFriendRelationship fun(steamIDFriend: SteamID): 'None'|'Blocked'|'RequestRecipient'|'Friend'|'RequestInitiator'|'Ignored'|'IgnoredFriend'|'Suggested' ---@field getFriendRichPresence fun(steamIDFriend: SteamID, key: string): string ---@field getFriendRichPresenceKeyByIndex fun(steamIDFriend: SteamID, index: integer): string ---@field getFriendRichPresenceKeyCount fun(steamIDFriend: SteamID): integer ---@field getFriendsGroupCount fun(): integer ---@field getFriendsGroupIDByIndex fun(index: integer): SteamFriendsGroupID ---@field getFriendsGroupMembersCount fun(group: SteamFriendsGroupID): integer ---@field getFriendsGroupMembersList fun(group: SteamFriendsGroupID): SteamID[] ---@field getFriendsGroupName fun(group: SteamFriendsGroupID): string? ---@field getFriendSteamLevel fun(steamIDFriend: SteamID): integer ---@field getLargeFriendAvatar fun(steamIDFriend: SteamID): ui.ImageSource ---@field getMediumFriendAvatar fun(steamIDFriend: SteamID): ui.ImageSource ---@field getSmallFriendAvatar fun(steamIDFriend: SteamID): ui.ImageSource ---@field getPersonaName fun(): string ---@field getPersonaState fun(): SteamPersonaState ---@field getPlayerNickname fun(steamIDFriend: SteamID): string? ---@field hasFriend fun(steamIDFriend: SteamID, flags: integer): boolean ---@field inviteUserToGame fun(steamIDFriend: SteamID, connectString: string): boolean ---@field isFollowing fun(steamID: SteamID, callback: SteamCallback<{result: SteamResult, isFollowing: boolean, steamID: SteamID}>): boolean ---@field isUserInSource fun(steamID: SteamID, sourceID: SteamID): boolean ---@field replyToFriendMessage fun(steamIDFriend: SteamID, message: string): boolean ---@field requestFriendRichPresence fun(steamIDUser: SteamID) ---@field requestUserInformation fun(steamID: SteamID, nameOnly: boolean?): boolean ---@field setInGameVoiceSpeaking fun(steamIDUser: SteamID, speaking: boolean) ---@field setListenForFriendsMessages fun(interceptEnabled: boolean) ---@field setPlayedWith fun(steamIDFriend: SteamID) ---@field setRichPresence fun(key: string, value: string): boolean ---@field joinRequestHandled fun() @Another CSP extension, use this function from `onGameLobbyJoinRequested` or `onGameRichPresenceJoinRequested` handlers to stop CM from reacting to the invite. ---@field onAvatarImageLoaded SteamEvent<{steamID: SteamID, image: integer, wide: integer, tall: integer}> ---@field onFriendRichPresenceUpdate SteamEvent<{steamIDFriend: SteamID, appID: SteamAppID}> ---@field onGameConnectedFriendChatMsg SteamEvent<{steamIDUser: SteamID, messageID: integer}> ---@field onGameLobbyJoinRequested SteamEvent<{steamIDFriend: SteamID, steamIDLobby: SteamID}> ---@field onGameOverlayActivated SteamEvent<{active: boolean}> ---@field onGameRichPresenceJoinRequested SteamEvent<{steamIDFriend: SteamID, connect: string}> ---@field onGameServerChangeRequested SteamEvent<{server: string, password: string}> ---@field onPersonaStateChange SteamEvent<{steamID: SteamID, changeFlags: integer}> ---@class LuaSteamUtils ---@field getAppID fun(): number ---@field getCurrentBatteryPower fun(): integer ---@field getIPCountry fun(): string ---@field getSecondsSinceAppActive fun(): integer ---@field getSecondsSinceComputerActive fun(): integer ---@field getServerRealTime fun(): integer ---@field getSteamUILanguage fun(): string ---@field isOverlayEnabled fun(): boolean ---@field isSteamChinaLauncher fun(): boolean ---@field isSteamRunningInVR fun(): boolean ---@field isVRHeadsetStreamingEnabled fun(): boolean ---@field setOverlayNotificationInset fun(horizontalInset: integer, verticalInset: integer) ---@field setOverlayNotificationPosition fun(position: 'TopLeft'|'TopRight'|'BottomLeft'|'BottomRight') ---@field setVRHeadsetStreamingEnabled fun(enabled: boolean) ---@field startVRDashboard fun() ---@field initFilterText fun(): boolean ---@field filterText fun(context: 'Unknown'|'GameContent'|'Chat'|'Name', source: SteamID, message: string): string, integer @Filtered message and number of characters filtered out. ---@field getEnteredGamepadTextInput fun(length: integer): string ---@field getEnteredGamepadTextLength fun(): number ---@field isSteamInBigPictureMode fun(): boolean ---@field isSteamRunningOnSteamDeck fun(): boolean ---@field dismissFloatingGamepadTextInput fun(): boolean ---@field dismissGamepadTextInput fun(): boolean ---@field setGameLauncherMode fun(launcherMode: boolean) ---@field showGamepadTextInput fun(inputMode: 'Normal'|'Password', lineMode: 'SingleLine'|'MultipleLines', description: string, charMax: integer, existingText: string): boolean ---@field showFloatingGamepadTextInput fun(mode: 'SingleLine'|'MultipleLines'|'Email'|'Numeric', x: integer, y: integer, width: integer, height: integer): boolean ---@field onGamepadTextInputDismissed SteamEvent<{submitted: boolean, submittedText: number}> ---@field onFloatingGamepadTextInputDismissed SteamEvent<{}> ---@class LuaSteamUserStats ---@field getStatInt fun(name: string): boolean, number? ---@field getStatFloat fun(name: string): boolean, number? ---@field setStatInt fun(name: string, value: integer): boolean ---@field setStatFloat fun(name: string, value: number): boolean ---@field getAchievement fun(name: string): boolean, boolean? ---@field setAchievement fun(name: string): boolean ---@field resetAllStats fun(achievementsToo: boolean): boolean ---@field storeStats fun(): boolean ---@field findLeaderboard fun(name: string, callback: SteamCallback<{steamLeaderboard: SteamLeaderboard, leaderboardFound: boolean}>) ---@field findOrCreateLeaderboard fun(name: string, sortMethod: 'Ascending'|'Descending', displayType: 'Numeric'|'TimeSeconds'|'TimeMilliSeconds', callback: SteamCallback<{steamLeaderboard: SteamLeaderboard, leaderboardFound: boolean}>) ---@field getLeaderboardEntryCount fun(leaderboard: SteamLeaderboard): number ---@field getLeaderboardName fun(leaderboard: SteamLeaderboard): string? ---@field getLeaderboardSortMethod fun(leaderboard: SteamLeaderboard): 'Ascending'|'Descending'|nil ---@field getLeaderboardDisplayType fun(leaderboard: SteamLeaderboard): 'Numeric'|'TimeSeconds'|'TimeMilliSeconds'|nil ---@field uploadLeaderboardScore fun(leaderboard: SteamLeaderboard, uploadMethod: 'KeepBest'|'ForceUpdate', score: integer, details: string?, callback: SteamCallback<{success: boolean, steamLeaderboard: SteamLeaderboard, score: number, scoreChanged: boolean, globalRankNew: number, globalRankPrevious: number}>) ---@field downloadLeaderboardEntries fun(leaderboard: SteamLeaderboard, dataRequest: 'Global'|'GlobalAroundUser'|'Friends', start: integer?, end: integer?, callback: SteamCallback<{steamIDUser: SteamID, globalRank: number, score: number, details: string, UGC: userdata}[]>) ---@field onUserStatsReceived SteamEvent<{gameID: userdata, result: number, steamIDUser: SteamID}> ---@field onUserStatsStored SteamEvent<{gameID: userdata, result: number}> ---@field onUserAchievementStored SteamEvent<{gameID: userdata, achievementName: string, curProgress: number, maxProgress: number}> ---@class LuaSteamApps ---@field isAppInstalled fun(appID: SteamAppID): boolean ---@field isCybercafe fun(): boolean ---@field isDlcInstalled fun(appID: SteamAppID): boolean ---@field isLowViolence fun(): boolean ---@field isSubscribed fun(): boolean ---@field isSubscribedApp fun(appID: SteamAppID): boolean ---@field isSubscribedFromFamilySharing fun(): boolean ---@field isSubscribedFromFreeWeekend fun(): boolean ---@field isTimedTrial fun(): boolean, integer, integer ---@field isVACBanned fun(): boolean ---@field getAppBuildId fun(): integer ---@field getAppInstallDir fun(appID: SteamAppID): string ---@field getAppOwner fun(): SteamID ---@field getCurrentGameLanguage fun(): string ---@field getDLCCount fun(): integer ---@field getEarliestPurchaseUnixTime fun(appID: SteamAppID): integer ---@field getFileDetails fun(filename: string, callback: SteamCallback<{result: SteamResult, fileSize: integer, flags: integer, fileSHA1: binary}>) ---@field getLaunchCommandLine fun(): string ---@field getLaunchQueryParam fun(key: string): string ---@field installDLC fun(appID: SteamAppID) ---@field markContentCorrupt fun(missingFilesOnly: boolean) ---@field uninstallDLC fun(appID: SteamAppID) ---@field onDlcInstalled SteamEvent<{appID: SteamAppID}> ---@field onNewUrlLaunchParameters SteamEvent<{}> ---@class LuaSteamInput ---@field activateActionSet fun(inputHandle: SteamID|'all', actionSetHandle: SteamInputActionSetHandle) ---@field activateActionSetLayer fun(inputHandle: SteamInputHandle, actionSetLayerHandle: SteamInputActionSetHandle) ---@field deactivateActionSetLayer fun(inputHandle: SteamInputHandle, actionSetLayerHandle: SteamInputActionSetHandle) ---@field deactivateAllActionSetLayers fun(inputHandle: SteamInputHandle) ---@field getActiveActionSetLayers fun(inputHandle: SteamInputHandle): SteamInputActionSetHandle[] ---@field getActionSetHandle fun(actionSetName: string): SteamInputActionSetHandle ---@field getAnalogActionData fun(inputHandle: SteamInputHandle, analogActionHandle: SteamInputAnalogActionHandle): {mode: string, x: number, y: number, active: boolean} ---@field getAnalogActionHandle fun(actionName: string): SteamInputAnalogActionHandle ---@field getAnalogActionOrigins fun(inputHandle: SteamInputHandle, actionSetHandle: SteamInputActionSetHandle, analogActionHandle: SteamInputAnalogActionHandle): string[] ---@field getConnectedControllers fun(): SteamInputHandle[] ---@field getControllerForGamepadIndex fun(index: integer): SteamInputHandle ---@field getCurrentActionSet fun(inputHandle: SteamInputHandle): SteamInputActionSetHandle ---@field getDigitalActionData fun(inputHandle: SteamInputHandle, digitalActionHandle: SteamInputDigitalActionHandle): {state: boolean, active: boolean} ---@field getDigitalActionHandle fun(actionName: string): SteamInputDigitalActionHandle ---@field getDigitalActionOrigins fun(inputHandle: SteamInputHandle, actionSetHandle: SteamInputActionSetHandle, digitalActionHandle: SteamInputDigitalActionHandle): string[] ---@field getGamepadIndexForController fun(controllerHandle: SteamInputHandle): userdata ---@field getGlyphForActionOrigin_Legacy fun(origin: string): string ---@field getInputTypeForHandle fun(inputHandle: SteamInputHandle): string ---@field getMotionData fun(inputHandle: SteamInputHandle): {rotQuatX: number, rotQuatY: number, rotQuatZ: number, rotQuatW: number, posAccelX: number, posAccelY: number, posAccelZ: number, rotVelX: number, rotVelY: number, rotVelZ: number} ---@field getStringForActionOrigin fun(origin: string): string ---@field runFrame fun() ---@field setLEDColor fun(inputHandle: SteamInputHandle, color: rgb, flags: integer) ---@field showBindingPanel fun(inputHandle: SteamInputHandle): boolean ---@field stopAnalogActionMomentum fun(inputHandle: SteamInputHandle, action: SteamInputAnalogActionHandle) ---@field legacy_triggerHapticPulse fun(inputHandle: SteamInputHandle, targetPad: 'Left'|'Right', durationMicroSec: integer) ---@field legacy_triggerRepeatedHapticPulse fun(inputHandle: SteamInputHandle, targetPad: 'Left'|'Right', durationMicroSec: integer, offMicroSec: integer, repeat: integer) ---@field triggerVibration fun(inputHandle: SteamInputHandle, leftSpeed: integer, rightSpeed: integer) ---@field getActionOriginFromXboxOrigin fun(inputHandle: SteamInputHandle, origin: string): string ---@field translateActionOrigin fun(destinationInputType: string, sourceOrigin: string): string ---@field getDeviceBindingRevision fun(inputHandle: SteamInputHandle): boolean, integer?, integer? ---@field getRemotePlaySessionID fun(inputHandle: SteamInputHandle): number ---@field init fun(explicitlyCallRunFrame: boolean?): boolean ---@field shutdown fun(): boolean ---@class LuaSteamExtra ---@field parseUint64 fun(str: string): SteamID ---@field randomUint64 fun(): SteamID @Generate random 64-bit number which is also a valid person Steam ID (some of Steam API does those checks, not letting to use any 64-bit number). ---@field isAPIImplementationSuspicious fun(): boolean @Returns `true` if it seems like Steam API is not the original one, and thus some advanced API might misbehave. ---@class LuaSteamMatchmaking ---@field addFavoriteGame fun(appID: SteamAppID, ip: integer, connPort: integer, queryPort: integer, flags: integer, lastPlayedOnServer: integer): integer ---@field addRequestLobbyListCompatibleMembersFilter fun(lobby: SteamID) ---@field addRequestLobbyListDistanceFilter fun(distanceFilter: 'Close'|'Default'|'Far'|'Worldwide') ---@field addRequestLobbyListFilterSlotsAvailable fun(slotsAvailable: integer) ---@field addRequestLobbyListNearValueFilter fun(keyToMatch: string, valueToBeCloseTo: integer) ---@field addRequestLobbyListNumericalFilter fun(keyToMatch: string, valueToMatch: integer, comparisonType: SteamComparisonType) ---@field addRequestLobbyListResultCountFilter fun(maxResults: integer) ---@field addRequestLobbyListStringFilter fun(keyToMatch: string, valueToMatch: string, comparisonType: SteamComparisonType) ---@field createLobby fun(lobbyType: SteamLobbyType, maxMembers: integer, callback: SteamCallback<{steamIDLobby: SteamID, result: SteamResult}>) @If you’re creating public lobbies, please consider adding `csp.mode` as data so different scripts can filter each other. ---@field deleteLobbyData fun(lobby: SteamID, key: string): boolean ---@field getFavoriteGame fun(game: integer): {appID: SteamAppID, ip: number, connPort: number, queryPort: number, flags: number, lastPlayedOnServer: number}? ---@field getFavoriteGameCount fun(): number ---@field getLobbyByIndex fun(index: integer): SteamID ---@field getLobbyChatEntry fun(lobby: SteamID, chatID: integer): string, SteamID, SteamChatMessageType @Returns binary message data, user Steam ID and message type. ---@field getLobbyData fun(lobby: SteamID, key: string): string ---@field getLobbyDataByIndex fun(lobby: SteamID, lobbyData: integer): {key: string, value: string} | nil ---@field getLobbyDataCount fun(lobby: SteamID): number ---@field getLobbyGameServer fun(lobby: SteamID): {gameServerIP: number, gameServerPort: number, gameServer: SteamID} | nil ---@field getLobbyMemberByIndex fun(lobby: SteamID, index: integer): SteamID ---@field getLobbyMemberData fun(lobby: SteamID, user: SteamID, key: string): string ---@field getLobbyMemberLimit fun(lobby: SteamID): number ---@field getLobbyOwner fun(lobby: SteamID): SteamID ---@field getNumLobbyMembers fun(lobby: SteamID): number ---@field inviteUserToLobby fun(lobby: SteamID, invitee: SteamID): boolean ---@field joinLobby fun(lobby: SteamID, callback: SteamCallback<{steamIDLobby: SteamID, chatPermissions: number, locked: boolean, chatRoomEnterResponse: string}>) ---@field leaveLobby fun(lobby: SteamID) ---@field removeFavoriteGame fun(appID: SteamAppID, ip: integer, connPort: integer, queryPort: integer, flags: integer): boolean ---@field requestLobbyData fun(lobby: SteamID): boolean ---@field requestLobbyList fun(callback: SteamCallback<{lobbiesMatching: number}>) ---@field sendLobbyChatMsg fun(lobby: SteamID, msgBody: string): boolean ---@field setLinkedLobby fun(lobby: SteamID, lobbyDependent: SteamID): boolean ---@field setLobbyData fun(lobby: SteamID, key: string, value: string): boolean ---@field setLobbyGameServer fun(lobby: SteamID, gameServerIP: integer, gameServerPort: integer, gameServer: SteamID) ---@field setLobbyJoinable fun(lobby: SteamID, joinable: boolean): boolean ---@field setLobbyMemberData fun(lobby: SteamID, key: string, value: string) ---@field setLobbyMemberLimit fun(lobby: SteamID, maxMembers: integer): boolean ---@field setLobbyOwner fun(lobby: SteamID, newOwner: SteamID): boolean ---@field setLobbyType fun(lobby: SteamID, lobbyType: SteamLobbyType): boolean ---@field onFavoritesListChanged SteamEvent<{ip: number, queryPort: number, connPort: number, appID: SteamAppID, flags: number, add: boolean, accountId: number}> ---@field onLobbyChatMsg SteamEvent<{steamIDLobby: SteamID, steamIDUser: SteamID, chatEntryType: string, chatID: number}> ---@field onLobbyChatUpdate SteamEvent<{steamIDLobby: SteamID, userChanged: SteamID, steamIDMakingChange: SteamID, chatMemberStateChange: number}> ---@field onLobbyCreated SteamEvent<{result: SteamResult, steamIDLobby: SteamID}> ---@field onLobbyDataUpdate SteamEvent<{steamIDLobby: SteamID, steamIDMember: SteamID, success: boolean}> ---@field onLobbyEnter SteamEvent<{steamIDLobby: SteamID, chatPermissions: number, locked: boolean, chatRoomEnterResponse: string}> ---@field onLobbyGameCreated SteamEvent<{steamIDLobby: SteamID, steamIDGameServer: SteamID, ip: number, port: number}> ---@field onLobbyInvite SteamEvent<{steamIDUser: SteamID, steamIDLobby: SteamID, gameID: SteamID}> ---@field onLobbyKicked SteamEvent<{steamIDLobby: SteamID, steamIDAdmin: SteamID, kickedDueToDisconnect: boolean}> ---@field onLobbyMatchList SteamEvent<{lobbiesMatching: number}> ---@class LuaSteamMusic ---@field isEnabled fun(): boolean ---@field isPlaying fun(): boolean ---@field getPlaybackStatus fun(): 'Undefined'|'Playing'|'Paused'|'Idle' ---@field getVolume fun(): number ---@field pause fun() ---@field play fun() ---@field playNext fun() ---@field playPrevious fun() ---@field setVolume fun(volume: number) ---@field onPlaybackStatusHasChanged SteamEvent<{}> ---@field onVolumeHasChanged SteamEvent<{newVolume: number}> ---@class LuaSteamNetworkingMessages ---@field sendMessageToUser fun(userID: SteamID, data: binary, sendFlags: integer, remoteChannel: integer): SteamResult ---@field receiveMessagesOnChannel fun(channel: integer): integer, {data: string, identityPeer: SteamID}[] ---@field acceptSessionWithUser fun(userID: SteamID): boolean ---@field closeSessionWithUser fun(userID: SteamID): boolean ---@field closeChannelWithUser fun(userID: SteamID, channel: integer): boolean ---@field getSessionConnectionInfo fun(userID: SteamID): SteamConnectionState, {flags: integer, endReason: integer, endDebug: string}, SteamConnectionRealTimeStatus ---@field flags {Unreliable: 0, UnreliableNoNagle: 1, UnreliableNoDelay: 5, Reliable: 8, ReliableNoNagle: 9, UseCurrentThread: 16, AutoRestartBrokenSession: 32} ---@field onMessagesSessionRequest SteamEvent<{steamID: SteamID}> ---@field onMessagesSessionFailed SteamEvent<{steamID: SteamID, flags: integer, endReason: integer, endDebug: string}> ---@class LuaSteamNetworkingSockets ---@field createListenSocketIP fun(localAddress: string, config?: SteamNetworkConfig): SteamListenSocketHandle ---@field createListenSocketP2P fun(virtualPort: integer, config?: SteamNetworkConfig): SteamListenSocketHandle ---@field connectByIPAddress fun(address: string, config?: SteamNetworkConfig): SteamConnectionHandle ---@field connectP2P fun(steamID: SteamID, virtualPort: integer, config?: SteamNetworkConfig): SteamConnectionHandle ---@field acceptConnection fun(connection: SteamConnectionHandle): SteamResult ---@field closeConnection fun(connection: SteamConnectionHandle, enableLinger: boolean?, debug: string?): boolean ---@field closeListenSocket fun(socket: SteamListenSocketHandle): boolean ---@field sendMessageToConnection fun(connection: SteamConnectionHandle, data: string, sendFlags: integer): SteamResult ---@field receiveMessagesOnConnection fun(connection: SteamConnectionHandle): string[] ---@field initAuthentication fun(): integer ---@field getAuthenticationStatus fun(): integer, string @Status, debug message ---@field getConnectionInfo fun(connection: SteamConnectionHandle): boolean, string, {identityRemote: SteamID, state: SteamConnectionState, endReason: string, endDebug: string, flags: integer} @Result, connection description, more details ---@field getConnectionFlags fun(connection: SteamConnectionHandle): integer? @Not from Steam, just a helpful shortcut ---@field getConnectionIdentityRemote fun(connection: SteamConnectionHandle): SteamID? @Not from Steam, just a helpful shortcut ---@field getConnectionAvailableSendBufferSpace fun(connection: SteamConnectionHandle): integer @Not from Steam, just a helpful shortcut. Returns size in bytes that can be send to the connection right now, taking `SendBufferSize` and the size of pending data into account. ---@field getDetailedConnectionStatus fun(connection: SteamConnectionHandle): string? @Returns `nil` on failure ---@field getConnectionRealTimeStatus fun(connection: SteamConnectionHandle, lanesCount: integer?): SteamResult, SteamConnectionRealTimeStatus, {pendingUnreliable: integer, pendingReliable: integer, sentUnackedReliable: integer}[] @Pass `0` for lanes if you don’t need lanes information. ---@field setConnectionName fun(connection: SteamConnectionHandle, name: string) ---@field getConnectionName fun(connection: SteamConnectionHandle): string @Returns `nil` on failure ---@field configureConnectionLanes fun(connection: SteamConnectionHandle, lanes: integer|{priority: integer?, weight: integer?}[]): SteamResult ---@field getListenSocketAddress fun(connection: SteamConnectionHandle): string @Returns `nil` on failure ---@field getIdentity fun(): SteamID | nil ---@field createPollGroup fun(): SteamPollGroupHandle ---@field destroyPollGroup fun(pollGroup: SteamPollGroupHandle): boolean ---@field setConnectionPollGroup fun(connection: SteamConnectionHandle, pollGroup: SteamPollGroupHandle): boolean ---@field receiveMessagesOnPollGroup fun(pollGroup: SteamPollGroupHandle): {data: string, conn: SteamConnectionHandle}[] ---@field flushMessagesOnConnection fun(connection: SteamConnectionHandle): SteamResult ---@field sendMessages fun(messages: {conn: SteamConnectionHandle, flag: integer, data: string, idxLane: integer?}[]): boolean[] ---@field flags {Unreliable: 0, UnreliableNoNagle: 1, UnreliableNoDelay: 5, Reliable: 8, ReliableNoNagle: 9, UseCurrentThread: 16, AppendTimestamp: 512} @`AppendTimestamp` is a CSP flag, available only for `sendMessageToConnection()`. If set, it will send a message from a background thread, and will attach 8 bytes of `steam.networkingUtils.getLocalTimestamp()` to the end of the message right before sending it. ---@field onConnectionChanged SteamEvent<{state: SteamConnectionState, oldState: SteamConnectionState, endReason: integer, endDebug: string, connection: SteamConnectionHandle, identityRemote: SteamID}> ---@field onAuthenticationStatus SteamEvent<{status: integer, debugMsg: string}> ---@alias SteamPingLocation binary @Do not save this one, or pass it over wire! ---@class LuaSteamNetworkingUtils ---@field initRelayNetworkAccess fun() ---@field getRelayNetworkStatus fun(): integer, SteamRelayNetworkStatus ---@field getLocalPingLocation fun(): float, SteamPingLocation ---@field estimatePingTimeBetweenTwoLocations fun(location1: SteamPingLocation, location2: SteamPingLocation): integer ---@field estimatePingTimeFromLocalHost fun(location: SteamPingLocation): integer ---@field convertPingLocationToString fun(location: SteamPingLocation): string ---@field parsePingLocationString fun(encoded: string): SteamPingLocation? ---@field checkPingDataUpToDate fun(maxAgeSeconds: number): boolean ---@field getLocalTimestamp fun(): number ---@field setDebugOutputFunction fun(detailLevel: SteamLogLevel, callback: fun(level: SteamLogLevel, msg: string)): number ---@field setGlobalConfig fun(config: SteamNetworkConfig) ---@field setConfig fun(target: SteamConnectionHandle|SteamListenSocketHandle, config: SteamNetworkConfig) @Pass `false` or `math.nan` for numerical values to clear any overrides. ---@field onSteamRelayNetworkStatus SteamEvent ---@class LuaSteamScreenshots ---@field addScreenshotToLibrary fun(filename: string, thumbnailFilename: string, width: integer, height: integer): SteamScreenshotHandle ---@field addVRScreenshotToLibrary fun(type: 'None'|'Mono'|'Stereo'|'MonoCubemap'|'MonoPanorama'|'StereoPanorama', filename: string, vrFilename: string): SteamScreenshotHandle ---@field hookScreenshots fun(hook: boolean) @Careful with this one, in some configurations CSP also hooks to screenshots. ---@field isScreenshotsHooked fun(): boolean ---@field setLocation fun(screenshot: SteamScreenshotHandle, location: string): boolean ---@field tagUser fun(screenshot: SteamScreenshotHandle, user: SteamID): boolean ---@field triggerScreenshot fun() ---@field writeScreenshot fun(data: binary, width: integer, height: integer): SteamScreenshotHandle ---@field onScreenshotReady SteamEvent<{local: SteamScreenshotHandle, result: 'OK'|'Fail'|'IOFailure'}> ---@field onVolumeHasChanged SteamEvent<{}> ---@class LuaSteamNetworkingSocketsGNS : LuaSteamNetworkingSockets ---@field connectP2PCustomSignaling fun(steamID: SteamID, virtualPort: integer, config?: SteamNetworkConfig): SteamConnectionHandle ---@class LuaSteamGNS @If you don’t want to use Steam relay network, this thing will let you access https://github.com/ValveSoftware/GameNetworkingSockets. ---@field configure fun(namespace: string, steamID: SteamID) @Note: this `steamID` doesn’t have to match actual Steam ID, just should be a valid Steam ID for a user (there is a function in `.extra` generating a random user Steam ID if needed). ---@field getSignalingStatus fun(): string?, integer? @Return readable status of signaling connection. ---@field networkingMessages LuaSteamNetworkingMessages ---@field networkingSockets LuaSteamNetworkingSocketsGNS ---@field networkingUtils LuaSteamNetworkingUtils ---@class LuaSteam ---@field user LuaSteamUser ---@field friends LuaSteamFriends ---@field utils LuaSteamUtils ---@field userStats LuaSteamUserStats ---@field apps LuaSteamApps ---@field input LuaSteamInput ---@field extra LuaSteamExtra ---@field matchmaking LuaSteamMatchmaking ---@field music LuaSteamMusic ---@field screenshots LuaSteamScreenshots ---@field networkingMessages LuaSteamNetworkingMessages ---@field networkingSockets LuaSteamNetworkingSockets ---@field networkingUtils LuaSteamNetworkingUtils ---@field GNS LuaSteamGNS ---@type LuaSteam local luasteam = __util.native() local messages1 local function receiveMessages1(mode, connection) if not messages1 then ffi.cdef [[ typedef struct { double messageNumber; double receivedTime; const char* data; int size; uint32_t connection; uint16_t flags; uint16_t idxLane; int channel; } luasteam_msg_item; typedef struct { luasteam_msg_item messages[32]; } luasteam_msg_arr; int lj_luasteam_next_message(int mode, uint32_t connection, luasteam_msg_arr* items); ]] messages1 = ffi.new('luasteam_msg_arr') end local ret = ffi.C.lj_luasteam_next_message(mode, connection, messages1) return ret, messages1.messages end ---Alternative version of receiving messages, uses FFI to speed things up and avoid allocations. Please note: returned messages ---are only alive for a short time, and invalidate after another one of accelerated `luasteam` functions has been used. ---@param connection SteamConnectionHandle ---@return integer, {size: integer, flags: integer, idxLane: integer, channel: integer, messageNumber: integer, receivedTime: integer, data: integer[]}[] @Number of messages (or -1 if state is invalid), 0-based array of FFI structures for messages. Field `.data` is `const char*`, so, use `ffi.string()` with `.size` to convert `.data` into string. function luasteam.networkingSockets.receiveMessagesOnConnection1(connection) return receiveMessages1(0, connection) end ---Alternative version of receiving messages on poll group, uses FFI to speed things up and avoid allocations. Please note: returned messages ---are only alive for a short time, and invalidate after another one of accelerated `luasteam` functions has been used. ---@param connection SteamConnectionHandle ---@return integer, {size: integer, flags: integer, idxLane: integer, channel: integer, messageNumber: integer, receivedTime: integer, data: integer[]}[] @Number of messages (or -1 if state is invalid), 0-based array of FFI structures for messages. Field `.data` is `const char*`, so, use `ffi.string()` with `.size` to convert `.data` into string. function luasteam.networkingSockets.receiveMessagesOnPollGroup1(connection) return receiveMessages1(1, connection) end ---Alternative version of receiving messages, uses FFI to speed things up and avoid allocations. Please note: returned messages ---are only alive for a short time, and invalidate after another one of accelerated `luasteam` functions has been used. ---@param connection SteamConnectionHandle ---@return integer, {size: integer, flags: integer, idxLane: integer, channel: integer, messageNumber: integer, data: integer[]}[] @Number of messages (or -1 if state is invalid), 0-based array of FFI structures for messages. Field `.data` is `const char*`, so, use `ffi.string()` with `.size` to convert `.data` into string. function luasteam.GNS.networkingSockets.receiveMessagesOnConnection1(connection) return receiveMessages1(2, connection) end ---Alternative version of receiving messages on poll group, uses FFI to speed things up and avoid allocations. Please note: returned messages ---are only alive for a short time, and invalidate after another one of accelerated `luasteam` functions has been used. ---@param connection SteamConnectionHandle ---@return integer, {size: integer, flags: integer, idxLane: integer, channel: integer, messageNumber: integer, data: integer[]}[] @Number of messages (or -1 if state is invalid), 0-based array of FFI structures for messages. Field `.data` is `const char*`, so, use `ffi.string()` with `.size` to convert `.data` into string. function luasteam.GNS.networkingSockets.receiveMessagesOnPollGroup1(connection) return receiveMessages1(3, connection) end -- With this silly hack, we can both do old `luasteam.friends.onGameOverlayActivated = function() end` -- and new `local unsub = luasteam.friends.onGameOverlayActivated(function () end)`. local eventsStore = {} local mt = { __index = function(moduleTable, key) return key:sub(1, 2) == 'on' and eventsStore[moduleTable].events[key] or nil end, __newindex = function(moduleTable, key, value) if key:sub(1, 2) == 'on' then local store = eventsStore[moduleTable] if store.prevListeners[key] then store.prevListeners[key]() end store.prevListeners[key] = type(value) == 'function' and store.events[key](value) or nil else rawset(moduleTable, key, value) end end } for _, module in pairs(luasteam) do local eventsToAlter = {} for k, v in pairs(module) do if k:startsWith('on') then eventsToAlter[k] = v end end if next(eventsToAlter) then eventsStore[module] = {events = eventsToAlter, prevListeners = {}} for k, fn in pairs(eventsToAlter) do module[k] = nil end setmetatable(module, mt) end end return luasteam