Decompiled source of SailwindCoop v0.4.3
Facepunch.Steamworks.Win64.dll
Decompiled 11 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Steamworks.Data; using Steamworks.Ugc; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] [assembly: AssemblyCompany("Garry Newman")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Facepunch.Steamworks.Win64")] [assembly: AssemblyTitle("Facepunch.Steamworks.Win64")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } } namespace Steamworks { internal struct CallResult<T> : INotifyCompletion where T : struct, ICallbackData { private SteamAPICall_t call; private ISteamUtils utils; private bool server; public bool IsCompleted { get { bool pbFailed = false; if (utils.IsAPICallCompleted(call, ref pbFailed) || pbFailed) { return true; } return false; } } public CallResult(SteamAPICall_t call, bool server) { this.call = call; this.server = server; utils = (server ? SteamSharedClass<SteamUtils>.InterfaceServer : SteamSharedClass<SteamUtils>.InterfaceClient) as ISteamUtils; if (utils == null) { utils = SteamSharedClass<SteamUtils>.Interface as ISteamUtils; } } public void OnCompleted(Action continuation) { Dispatch.OnCallComplete<T>(call, continuation, server); } public T? GetResult() { bool pbFailed = false; if (!utils.IsAPICallCompleted(call, ref pbFailed) || pbFailed) { return null; } T val = default(T); int dataSize = val.DataSize; IntPtr intPtr = Marshal.AllocHGlobal(dataSize); try { if (!utils.GetAPICallResult(call, intPtr, dataSize, (int)val.CallbackType, ref pbFailed) || pbFailed) { Dispatch.OnDebugCallback?.Invoke(val.CallbackType, "!GetAPICallResult or failed", server); return null; } Dispatch.OnDebugCallback?.Invoke(val.CallbackType, Dispatch.CallbackToString(val.CallbackType, intPtr, dataSize), server); return (T)Marshal.PtrToStructure(intPtr, typeof(T)); } finally { Marshal.FreeHGlobal(intPtr); } } internal CallResult<T> GetAwaiter() { return this; } } internal interface ICallbackData { CallbackType CallbackType { get; } int DataSize { get; } } public class AuthTicket : IDisposable { public byte[] Data; public uint Handle; public void Cancel() { if (Handle != 0) { SteamUser.Internal.CancelAuthTicket(Handle); } Handle = 0u; Data = null; } public void Dispose() { Cancel(); } } public static class Dispatch { [StructLayout(LayoutKind.Sequential, Pack = 8)] internal struct CallbackMsg_t { public HSteamUser m_hSteamUser; public CallbackType Type; public IntPtr Data; public int DataSize; } private struct ResultCallback { public Action continuation; public bool server; } private struct Callback { public Action<IntPtr> action; public bool server; } public static Action<CallbackType, string, bool> OnDebugCallback; public static Action<Exception> OnException; private static bool runningFrame = false; private static List<Action<IntPtr>> actionsToCall = new List<Action<IntPtr>>(); private static Dictionary<ulong, ResultCallback> ResultCallbacks = new Dictionary<ulong, ResultCallback>(); private static Dictionary<CallbackType, List<Callback>> Callbacks = new Dictionary<CallbackType, List<Callback>>(); internal static HSteamPipe ClientPipe { get; set; } internal static HSteamPipe ServerPipe { get; set; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] internal static extern void SteamAPI_ManualDispatch_Init(); [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] internal static extern void SteamAPI_ManualDispatch_RunFrame(HSteamPipe pipe); [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ManualDispatch_GetNextCallback(HSteamPipe pipe, [In][Out] ref CallbackMsg_t msg); [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] internal static extern bool SteamAPI_ManualDispatch_FreeLastCallback(HSteamPipe pipe); internal static void Init() { SteamAPI_ManualDispatch_Init(); } internal static void Frame(HSteamPipe pipe) { if (runningFrame) { return; } try { runningFrame = true; SteamAPI_ManualDispatch_RunFrame(pipe); SteamNetworkingUtils.OutputDebugMessages(); CallbackMsg_t msg = default(CallbackMsg_t); while (SteamAPI_ManualDispatch_GetNextCallback(pipe, ref msg)) { try { ProcessCallback(msg, pipe == ServerPipe); } finally { SteamAPI_ManualDispatch_FreeLastCallback(pipe); } } } catch (Exception obj) { OnException?.Invoke(obj); } finally { runningFrame = false; } } private static void ProcessCallback(CallbackMsg_t msg, bool isServer) { OnDebugCallback?.Invoke(msg.Type, CallbackToString(msg.Type, msg.Data, msg.DataSize), isServer); if (msg.Type == CallbackType.SteamAPICallCompleted) { ProcessResult(msg); } else { if (!Callbacks.TryGetValue(msg.Type, out var value)) { return; } actionsToCall.Clear(); foreach (Callback item in value) { if (item.server == isServer) { actionsToCall.Add(item.action); } } foreach (Action<IntPtr> item2 in actionsToCall) { item2(msg.Data); } actionsToCall.Clear(); } } internal static string CallbackToString(CallbackType type, IntPtr data, int expectedsize) { if (!CallbackTypeFactory.All.TryGetValue(type, out var value)) { return $"[{type} not in sdk]"; } object obj = data.ToType(value); if (obj == null) { return "[null]"; } string text = ""; FieldInfo[] fields = value.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fields.Length == 0) { return "[no fields]"; } int num = fields.Max((FieldInfo x) => x.Name.Length) + 1; if (num < 10) { num = 10; } FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { int num2 = num - fieldInfo.Name.Length; if (num2 < 0) { num2 = 0; } text += $"{new string(' ', num2)}{fieldInfo.Name}: {fieldInfo.GetValue(obj)}\n"; } return text.Trim(new char[1] { '\n' }); } private static void ProcessResult(CallbackMsg_t msg) { SteamAPICallCompleted_t steamAPICallCompleted_t = msg.Data.ToType<SteamAPICallCompleted_t>(); if (!ResultCallbacks.TryGetValue(steamAPICallCompleted_t.AsyncCall, out var value)) { OnDebugCallback?.Invoke((CallbackType)steamAPICallCompleted_t.Callback, "[no callback waiting/required]", arg3: false); return; } ResultCallbacks.Remove(steamAPICallCompleted_t.AsyncCall); value.continuation(); } internal static async void LoopClientAsync() { while (ClientPipe != 0) { Frame(ClientPipe); await Task.Delay(16); } } internal static async void LoopServerAsync() { while (ServerPipe != 0) { Frame(ServerPipe); await Task.Delay(32); } } internal static void OnCallComplete<T>(SteamAPICall_t call, Action continuation, bool server) where T : struct, ICallbackData { ResultCallbacks[call.Value] = new ResultCallback { continuation = continuation, server = server }; } internal static void Install<T>(Action<T> p, bool server = false) where T : ICallbackData { CallbackType callbackType = default(T).CallbackType; if (!Callbacks.TryGetValue(callbackType, out var value)) { value = new List<Callback>(); Callbacks[callbackType] = value; } value.Add(new Callback { action = delegate(IntPtr x) { p(x.ToType<T>()); }, server = server }); } internal static void ShutdownServer() { ServerPipe = 0; foreach (KeyValuePair<CallbackType, List<Callback>> callback in Callbacks) { Callbacks[callback.Key].RemoveAll((Callback x) => x.server); } ResultCallbacks = ResultCallbacks.Where((KeyValuePair<ulong, ResultCallback> x) => !x.Value.server).ToDictionary((KeyValuePair<ulong, ResultCallback> x) => x.Key, (KeyValuePair<ulong, ResultCallback> x) => x.Value); } internal static void ShutdownClient() { ClientPipe = 0; foreach (KeyValuePair<CallbackType, List<Callback>> callback in Callbacks) { Callbacks[callback.Key].RemoveAll((Callback x) => !x.server); } ResultCallbacks = ResultCallbacks.Where((KeyValuePair<ulong, ResultCallback> x) => x.Value.server).ToDictionary((KeyValuePair<ulong, ResultCallback> x) => x.Key, (KeyValuePair<ulong, ResultCallback> x) => x.Value); } } internal static class SteamAPI { internal static class Native { [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool SteamAPI_Init(); [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] public static extern void SteamAPI_Shutdown(); [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] public static extern HSteamPipe SteamAPI_GetHSteamPipe(); [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool SteamAPI_RestartAppIfNecessary(uint unOwnAppID); } internal static bool Init() { return Native.SteamAPI_Init(); } internal static void Shutdown() { Native.SteamAPI_Shutdown(); } internal static HSteamPipe GetHSteamPipe() { return Native.SteamAPI_GetHSteamPipe(); } internal static bool RestartAppIfNecessary(uint unOwnAppID) { return Native.SteamAPI_RestartAppIfNecessary(unOwnAppID); } } internal static class SteamGameServer { internal static class Native { [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] public static extern void SteamGameServer_RunCallbacks(); [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] public static extern void SteamGameServer_Shutdown(); [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] public static extern HSteamPipe SteamGameServer_GetHSteamPipe(); } internal static void RunCallbacks() { Native.SteamGameServer_RunCallbacks(); } internal static void Shutdown() { Native.SteamGameServer_Shutdown(); } internal static HSteamPipe GetHSteamPipe() { return Native.SteamGameServer_GetHSteamPipe(); } } internal static class SteamInternal { internal static class Native { [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool SteamInternal_GameServer_Init(uint unIP, ushort usPort, ushort usGamePort, ushort usQueryPort, int eServerMode, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersionString); } internal static bool GameServer_Init(uint unIP, ushort usPort, ushort usGamePort, ushort usQueryPort, int eServerMode, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersionString) { return Native.SteamInternal_GameServer_Init(unIP, usPort, usGamePort, usQueryPort, eServerMode, pchVersionString); } } public enum CallbackType { SteamServersConnected = 101, SteamServerConnectFailure = 102, SteamServersDisconnected = 103, ClientGameServerDeny = 113, GSPolicyResponse = 115, IPCFailure = 117, LicensesUpdated = 125, ValidateAuthTicketResponse = 143, MicroTxnAuthorizationResponse = 152, EncryptedAppTicketResponse = 154, GetAuthSessionTicketResponse = 163, GameWebCallback = 164, StoreAuthURLResponse = 165, MarketEligibilityResponse = 166, DurationControl = 167, GSClientApprove = 201, GSClientDeny = 202, GSClientKick = 203, GSClientAchievementStatus = 206, GSGameplayStats = 207, GSClientGroupStatus = 208, GSReputation = 209, AssociateWithClanResult = 210, ComputeNewPlayerCompatibilityResult = 211, PersonaStateChange = 304, GameOverlayActivated = 331, GameServerChangeRequested = 332, GameLobbyJoinRequested = 333, AvatarImageLoaded = 334, ClanOfficerListResponse = 335, FriendRichPresenceUpdate = 336, GameRichPresenceJoinRequested = 337, GameConnectedClanChatMsg = 338, GameConnectedChatJoin = 339, GameConnectedChatLeave = 340, DownloadClanActivityCountsResult = 341, JoinClanChatRoomCompletionResult = 342, GameConnectedFriendChatMsg = 343, FriendsGetFollowerCount = 344, FriendsIsFollowing = 345, FriendsEnumerateFollowingList = 346, SetPersonaNameResponse = 347, UnreadChatMessagesChanged = 348, FavoritesListChanged = 502, LobbyInvite = 503, LobbyEnter = 504, LobbyDataUpdate = 505, LobbyChatUpdate = 506, LobbyChatMsg = 507, LobbyGameCreated = 509, LobbyMatchList = 510, LobbyKicked = 512, LobbyCreated = 513, PSNGameBootInviteResult = 515, FavoritesListAccountsUpdated = 516, IPCountry = 701, LowBatteryPower = 702, SteamAPICallCompleted = 703, SteamShutdown = 704, CheckFileSignature = 705, GamepadTextInputDismissed = 714, DlcInstalled = 1005, RegisterActivationCodeResponse = 1008, NewUrlLaunchParameters = 1014, AppProofOfPurchaseKeyResponse = 1021, FileDetailsResult = 1023, UserStatsReceived = 1101, UserStatsStored = 1102, UserAchievementStored = 1103, LeaderboardFindResult = 1104, LeaderboardScoresDownloaded = 1105, LeaderboardScoreUploaded = 1106, NumberOfCurrentPlayers = 1107, UserStatsUnloaded = 1108, GSStatsUnloaded = 1108, UserAchievementIconFetched = 1109, GlobalAchievementPercentagesReady = 1110, LeaderboardUGCSet = 1111, GlobalStatsReceived = 1112, P2PSessionRequest = 1202, P2PSessionConnectFail = 1203, SteamNetConnectionStatusChangedCallback = 1221, SteamNetAuthenticationStatus = 1222, SteamRelayNetworkStatus = 1281, RemoteStorageAppSyncedClient = 1301, RemoteStorageAppSyncedServer = 1302, RemoteStorageAppSyncProgress = 1303, RemoteStorageAppSyncStatusCheck = 1305, RemoteStorageFileShareResult = 1307, RemoteStoragePublishFileResult = 1309, RemoteStorageDeletePublishedFileResult = 1311, RemoteStorageEnumerateUserPublishedFilesResult = 1312, RemoteStorageSubscribePublishedFileResult = 1313, RemoteStorageEnumerateUserSubscribedFilesResult = 1314, RemoteStorageUnsubscribePublishedFileResult = 1315, RemoteStorageUpdatePublishedFileResult = 1316, RemoteStorageDownloadUGCResult = 1317, RemoteStorageGetPublishedFileDetailsResult = 1318, RemoteStorageEnumerateWorkshopFilesResult = 1319, RemoteStorageGetPublishedItemVoteDetailsResult = 1320, RemoteStoragePublishedFileSubscribed = 1321, RemoteStoragePublishedFileUnsubscribed = 1322, RemoteStoragePublishedFileDeleted = 1323, RemoteStorageUpdateUserPublishedItemVoteResult = 1324, RemoteStorageUserVoteDetails = 1325, RemoteStorageEnumerateUserSharedWorkshopFilesResult = 1326, RemoteStorageSetUserPublishedFileActionResult = 1327, RemoteStorageEnumeratePublishedFilesByUserActionResult = 1328, RemoteStoragePublishFileProgress = 1329, RemoteStoragePublishedFileUpdated = 1330, RemoteStorageFileWriteAsyncComplete = 1331, RemoteStorageFileReadAsyncComplete = 1332, GSStatsReceived = 1800, GSStatsStored = 1801, HTTPRequestCompleted = 2101, HTTPRequestHeadersReceived = 2102, HTTPRequestDataReceived = 2103, ScreenshotReady = 2301, ScreenshotRequested = 2302, SteamUGCQueryCompleted = 3401, SteamUGCRequestUGCDetailsResult = 3402, CreateItemResult = 3403, SubmitItemUpdateResult = 3404, ItemInstalled = 3405, DownloadItemResult = 3406, UserFavoriteItemsListChanged = 3407, SetUserItemVoteResult = 3408, GetUserItemVoteResult = 3409, StartPlaytimeTrackingResult = 3410, StopPlaytimeTrackingResult = 3411, AddUGCDependencyResult = 3412, RemoveUGCDependencyResult = 3413, AddAppDependencyResult = 3414, RemoveAppDependencyResult = 3415, GetAppDependenciesResult = 3416, DeleteItemResult = 3417, SteamAppInstalled = 3901, SteamAppUninstalled = 3902, PlaybackStatusHasChanged = 4001, VolumeHasChanged = 4002, MusicPlayerWantsVolume = 4011, MusicPlayerSelectsQueueEntry = 4012, MusicPlayerSelectsPlaylistEntry = 4013, MusicPlayerRemoteWillActivate = 4101, MusicPlayerRemoteWillDeactivate = 4102, MusicPlayerRemoteToFront = 4103, MusicPlayerWillQuit = 4104, MusicPlayerWantsPlay = 4105, MusicPlayerWantsPause = 4106, MusicPlayerWantsPlayPrevious = 4107, MusicPlayerWantsPlayNext = 4108, MusicPlayerWantsShuffled = 4109, MusicPlayerWantsLooped = 4110, MusicPlayerWantsPlayingRepeatStatus = 4114, HTML_BrowserReady = 4501, HTML_NeedsPaint = 4502, HTML_StartRequest = 4503, HTML_CloseBrowser = 4504, HTML_URLChanged = 4505, HTML_FinishedRequest = 4506, HTML_OpenLinkInNewTab = 4507, HTML_ChangedTitle = 4508, HTML_SearchResults = 4509, HTML_CanGoBackAndForward = 4510, HTML_HorizontalScroll = 4511, HTML_VerticalScroll = 4512, HTML_LinkAtPosition = 4513, HTML_JSAlert = 4514, HTML_JSConfirm = 4515, HTML_FileOpenDialog = 4516, HTML_NewWindow = 4521, HTML_SetCursor = 4522, HTML_StatusText = 4523, HTML_ShowToolTip = 4524, HTML_UpdateToolTip = 4525, HTML_HideToolTip = 4526, HTML_BrowserRestarted = 4527, BroadcastUploadStart = 4604, BroadcastUploadStop = 4605, GetVideoURLResult = 4611, GetOPFSettingsResult = 4624, SteamInventoryResultReady = 4700, SteamInventoryFullUpdate = 4701, SteamInventoryDefinitionUpdate = 4702, SteamInventoryEligiblePromoItemDefIDs = 4703, SteamInventoryStartPurchaseResult = 4704, SteamInventoryRequestPricesResult = 4705, SteamParentalSettingsChanged = 5001, SearchForGameProgressCallback = 5201, SearchForGameResultCallback = 5202, RequestPlayersForGameProgressCallback = 5211, RequestPlayersForGameResultCallback = 5212, RequestPlayersForGameFinalResultCallback = 5213, SubmitPlayerResultResultCallback = 5214, EndGameResultCallback = 5215, JoinPartyCallback = 5301, CreateBeaconCallback = 5302, ReservationNotificationCallback = 5303, ChangeNumOpenSlotsCallback = 5304, AvailableBeaconLocationsUpdated = 5305, ActiveBeaconsUpdated = 5306, SteamRemotePlaySessionConnected = 5701, SteamRemotePlaySessionDisconnected = 5702 } internal static class CallbackTypeFactory { internal static Dictionary<CallbackType, Type> All = new Dictionary<CallbackType, Type> { { CallbackType.SteamServersConnected, typeof(SteamServersConnected_t) }, { CallbackType.SteamServerConnectFailure, typeof(SteamServerConnectFailure_t) }, { CallbackType.SteamServersDisconnected, typeof(SteamServersDisconnected_t) }, { CallbackType.ClientGameServerDeny, typeof(ClientGameServerDeny_t) }, { CallbackType.GSPolicyResponse, typeof(GSPolicyResponse_t) }, { CallbackType.IPCFailure, typeof(IPCFailure_t) }, { CallbackType.LicensesUpdated, typeof(LicensesUpdated_t) }, { CallbackType.ValidateAuthTicketResponse, typeof(ValidateAuthTicketResponse_t) }, { CallbackType.MicroTxnAuthorizationResponse, typeof(MicroTxnAuthorizationResponse_t) }, { CallbackType.EncryptedAppTicketResponse, typeof(EncryptedAppTicketResponse_t) }, { CallbackType.GetAuthSessionTicketResponse, typeof(GetAuthSessionTicketResponse_t) }, { CallbackType.GameWebCallback, typeof(GameWebCallback_t) }, { CallbackType.StoreAuthURLResponse, typeof(StoreAuthURLResponse_t) }, { CallbackType.MarketEligibilityResponse, typeof(MarketEligibilityResponse_t) }, { CallbackType.DurationControl, typeof(DurationControl_t) }, { CallbackType.GSClientApprove, typeof(GSClientApprove_t) }, { CallbackType.GSClientDeny, typeof(GSClientDeny_t) }, { CallbackType.GSClientKick, typeof(GSClientKick_t) }, { CallbackType.GSClientAchievementStatus, typeof(GSClientAchievementStatus_t) }, { CallbackType.GSGameplayStats, typeof(GSGameplayStats_t) }, { CallbackType.GSClientGroupStatus, typeof(GSClientGroupStatus_t) }, { CallbackType.GSReputation, typeof(GSReputation_t) }, { CallbackType.AssociateWithClanResult, typeof(AssociateWithClanResult_t) }, { CallbackType.ComputeNewPlayerCompatibilityResult, typeof(ComputeNewPlayerCompatibilityResult_t) }, { CallbackType.PersonaStateChange, typeof(PersonaStateChange_t) }, { CallbackType.GameOverlayActivated, typeof(GameOverlayActivated_t) }, { CallbackType.GameServerChangeRequested, typeof(GameServerChangeRequested_t) }, { CallbackType.GameLobbyJoinRequested, typeof(GameLobbyJoinRequested_t) }, { CallbackType.AvatarImageLoaded, typeof(AvatarImageLoaded_t) }, { CallbackType.ClanOfficerListResponse, typeof(ClanOfficerListResponse_t) }, { CallbackType.FriendRichPresenceUpdate, typeof(FriendRichPresenceUpdate_t) }, { CallbackType.GameRichPresenceJoinRequested, typeof(GameRichPresenceJoinRequested_t) }, { CallbackType.GameConnectedClanChatMsg, typeof(GameConnectedClanChatMsg_t) }, { CallbackType.GameConnectedChatJoin, typeof(GameConnectedChatJoin_t) }, { CallbackType.GameConnectedChatLeave, typeof(GameConnectedChatLeave_t) }, { CallbackType.DownloadClanActivityCountsResult, typeof(DownloadClanActivityCountsResult_t) }, { CallbackType.JoinClanChatRoomCompletionResult, typeof(JoinClanChatRoomCompletionResult_t) }, { CallbackType.GameConnectedFriendChatMsg, typeof(GameConnectedFriendChatMsg_t) }, { CallbackType.FriendsGetFollowerCount, typeof(FriendsGetFollowerCount_t) }, { CallbackType.FriendsIsFollowing, typeof(FriendsIsFollowing_t) }, { CallbackType.FriendsEnumerateFollowingList, typeof(FriendsEnumerateFollowingList_t) }, { CallbackType.SetPersonaNameResponse, typeof(SetPersonaNameResponse_t) }, { CallbackType.UnreadChatMessagesChanged, typeof(UnreadChatMessagesChanged_t) }, { CallbackType.FavoritesListChanged, typeof(FavoritesListChanged_t) }, { CallbackType.LobbyInvite, typeof(LobbyInvite_t) }, { CallbackType.LobbyEnter, typeof(LobbyEnter_t) }, { CallbackType.LobbyDataUpdate, typeof(LobbyDataUpdate_t) }, { CallbackType.LobbyChatUpdate, typeof(LobbyChatUpdate_t) }, { CallbackType.LobbyChatMsg, typeof(LobbyChatMsg_t) }, { CallbackType.LobbyGameCreated, typeof(LobbyGameCreated_t) }, { CallbackType.LobbyMatchList, typeof(LobbyMatchList_t) }, { CallbackType.LobbyKicked, typeof(LobbyKicked_t) }, { CallbackType.LobbyCreated, typeof(LobbyCreated_t) }, { CallbackType.PSNGameBootInviteResult, typeof(PSNGameBootInviteResult_t) }, { CallbackType.FavoritesListAccountsUpdated, typeof(FavoritesListAccountsUpdated_t) }, { CallbackType.IPCountry, typeof(IPCountry_t) }, { CallbackType.LowBatteryPower, typeof(LowBatteryPower_t) }, { CallbackType.SteamAPICallCompleted, typeof(SteamAPICallCompleted_t) }, { CallbackType.SteamShutdown, typeof(SteamShutdown_t) }, { CallbackType.CheckFileSignature, typeof(CheckFileSignature_t) }, { CallbackType.GamepadTextInputDismissed, typeof(GamepadTextInputDismissed_t) }, { CallbackType.DlcInstalled, typeof(DlcInstalled_t) }, { CallbackType.RegisterActivationCodeResponse, typeof(RegisterActivationCodeResponse_t) }, { CallbackType.NewUrlLaunchParameters, typeof(NewUrlLaunchParameters_t) }, { CallbackType.AppProofOfPurchaseKeyResponse, typeof(AppProofOfPurchaseKeyResponse_t) }, { CallbackType.FileDetailsResult, typeof(FileDetailsResult_t) }, { CallbackType.UserStatsReceived, typeof(UserStatsReceived_t) }, { CallbackType.UserStatsStored, typeof(UserStatsStored_t) }, { CallbackType.UserAchievementStored, typeof(UserAchievementStored_t) }, { CallbackType.LeaderboardFindResult, typeof(LeaderboardFindResult_t) }, { CallbackType.LeaderboardScoresDownloaded, typeof(LeaderboardScoresDownloaded_t) }, { CallbackType.LeaderboardScoreUploaded, typeof(LeaderboardScoreUploaded_t) }, { CallbackType.NumberOfCurrentPlayers, typeof(NumberOfCurrentPlayers_t) }, { CallbackType.UserStatsUnloaded, typeof(UserStatsUnloaded_t) }, { CallbackType.UserAchievementIconFetched, typeof(UserAchievementIconFetched_t) }, { CallbackType.GlobalAchievementPercentagesReady, typeof(GlobalAchievementPercentagesReady_t) }, { CallbackType.LeaderboardUGCSet, typeof(LeaderboardUGCSet_t) }, { CallbackType.GlobalStatsReceived, typeof(GlobalStatsReceived_t) }, { CallbackType.P2PSessionRequest, typeof(P2PSessionRequest_t) }, { CallbackType.P2PSessionConnectFail, typeof(P2PSessionConnectFail_t) }, { CallbackType.SteamNetConnectionStatusChangedCallback, typeof(SteamNetConnectionStatusChangedCallback_t) }, { CallbackType.SteamNetAuthenticationStatus, typeof(SteamNetAuthenticationStatus_t) }, { CallbackType.SteamRelayNetworkStatus, typeof(SteamRelayNetworkStatus_t) }, { CallbackType.RemoteStorageAppSyncedClient, typeof(RemoteStorageAppSyncedClient_t) }, { CallbackType.RemoteStorageAppSyncedServer, typeof(RemoteStorageAppSyncedServer_t) }, { CallbackType.RemoteStorageAppSyncProgress, typeof(RemoteStorageAppSyncProgress_t) }, { CallbackType.RemoteStorageAppSyncStatusCheck, typeof(RemoteStorageAppSyncStatusCheck_t) }, { CallbackType.RemoteStorageFileShareResult, typeof(RemoteStorageFileShareResult_t) }, { CallbackType.RemoteStoragePublishFileResult, typeof(RemoteStoragePublishFileResult_t) }, { CallbackType.RemoteStorageDeletePublishedFileResult, typeof(RemoteStorageDeletePublishedFileResult_t) }, { CallbackType.RemoteStorageEnumerateUserPublishedFilesResult, typeof(RemoteStorageEnumerateUserPublishedFilesResult_t) }, { CallbackType.RemoteStorageSubscribePublishedFileResult, typeof(RemoteStorageSubscribePublishedFileResult_t) }, { CallbackType.RemoteStorageEnumerateUserSubscribedFilesResult, typeof(RemoteStorageEnumerateUserSubscribedFilesResult_t) }, { CallbackType.RemoteStorageUnsubscribePublishedFileResult, typeof(RemoteStorageUnsubscribePublishedFileResult_t) }, { CallbackType.RemoteStorageUpdatePublishedFileResult, typeof(RemoteStorageUpdatePublishedFileResult_t) }, { CallbackType.RemoteStorageDownloadUGCResult, typeof(RemoteStorageDownloadUGCResult_t) }, { CallbackType.RemoteStorageGetPublishedFileDetailsResult, typeof(RemoteStorageGetPublishedFileDetailsResult_t) }, { CallbackType.RemoteStorageEnumerateWorkshopFilesResult, typeof(RemoteStorageEnumerateWorkshopFilesResult_t) }, { CallbackType.RemoteStorageGetPublishedItemVoteDetailsResult, typeof(RemoteStorageGetPublishedItemVoteDetailsResult_t) }, { CallbackType.RemoteStoragePublishedFileSubscribed, typeof(RemoteStoragePublishedFileSubscribed_t) }, { CallbackType.RemoteStoragePublishedFileUnsubscribed, typeof(RemoteStoragePublishedFileUnsubscribed_t) }, { CallbackType.RemoteStoragePublishedFileDeleted, typeof(RemoteStoragePublishedFileDeleted_t) }, { CallbackType.RemoteStorageUpdateUserPublishedItemVoteResult, typeof(RemoteStorageUpdateUserPublishedItemVoteResult_t) }, { CallbackType.RemoteStorageUserVoteDetails, typeof(RemoteStorageUserVoteDetails_t) }, { CallbackType.RemoteStorageEnumerateUserSharedWorkshopFilesResult, typeof(RemoteStorageEnumerateUserSharedWorkshopFilesResult_t) }, { CallbackType.RemoteStorageSetUserPublishedFileActionResult, typeof(RemoteStorageSetUserPublishedFileActionResult_t) }, { CallbackType.RemoteStorageEnumeratePublishedFilesByUserActionResult, typeof(RemoteStorageEnumeratePublishedFilesByUserActionResult_t) }, { CallbackType.RemoteStoragePublishFileProgress, typeof(RemoteStoragePublishFileProgress_t) }, { CallbackType.RemoteStoragePublishedFileUpdated, typeof(RemoteStoragePublishedFileUpdated_t) }, { CallbackType.RemoteStorageFileWriteAsyncComplete, typeof(RemoteStorageFileWriteAsyncComplete_t) }, { CallbackType.RemoteStorageFileReadAsyncComplete, typeof(RemoteStorageFileReadAsyncComplete_t) }, { CallbackType.GSStatsReceived, typeof(GSStatsReceived_t) }, { CallbackType.GSStatsStored, typeof(GSStatsStored_t) }, { CallbackType.HTTPRequestCompleted, typeof(HTTPRequestCompleted_t) }, { CallbackType.HTTPRequestHeadersReceived, typeof(HTTPRequestHeadersReceived_t) }, { CallbackType.HTTPRequestDataReceived, typeof(HTTPRequestDataReceived_t) }, { CallbackType.ScreenshotReady, typeof(ScreenshotReady_t) }, { CallbackType.ScreenshotRequested, typeof(ScreenshotRequested_t) }, { CallbackType.SteamUGCQueryCompleted, typeof(SteamUGCQueryCompleted_t) }, { CallbackType.SteamUGCRequestUGCDetailsResult, typeof(SteamUGCRequestUGCDetailsResult_t) }, { CallbackType.CreateItemResult, typeof(CreateItemResult_t) }, { CallbackType.SubmitItemUpdateResult, typeof(SubmitItemUpdateResult_t) }, { CallbackType.ItemInstalled, typeof(ItemInstalled_t) }, { CallbackType.DownloadItemResult, typeof(DownloadItemResult_t) }, { CallbackType.UserFavoriteItemsListChanged, typeof(UserFavoriteItemsListChanged_t) }, { CallbackType.SetUserItemVoteResult, typeof(SetUserItemVoteResult_t) }, { CallbackType.GetUserItemVoteResult, typeof(GetUserItemVoteResult_t) }, { CallbackType.StartPlaytimeTrackingResult, typeof(StartPlaytimeTrackingResult_t) }, { CallbackType.StopPlaytimeTrackingResult, typeof(StopPlaytimeTrackingResult_t) }, { CallbackType.AddUGCDependencyResult, typeof(AddUGCDependencyResult_t) }, { CallbackType.RemoveUGCDependencyResult, typeof(RemoveUGCDependencyResult_t) }, { CallbackType.AddAppDependencyResult, typeof(AddAppDependencyResult_t) }, { CallbackType.RemoveAppDependencyResult, typeof(RemoveAppDependencyResult_t) }, { CallbackType.GetAppDependenciesResult, typeof(GetAppDependenciesResult_t) }, { CallbackType.DeleteItemResult, typeof(DeleteItemResult_t) }, { CallbackType.SteamAppInstalled, typeof(SteamAppInstalled_t) }, { CallbackType.SteamAppUninstalled, typeof(SteamAppUninstalled_t) }, { CallbackType.PlaybackStatusHasChanged, typeof(PlaybackStatusHasChanged_t) }, { CallbackType.VolumeHasChanged, typeof(VolumeHasChanged_t) }, { CallbackType.MusicPlayerWantsVolume, typeof(MusicPlayerWantsVolume_t) }, { CallbackType.MusicPlayerSelectsQueueEntry, typeof(MusicPlayerSelectsQueueEntry_t) }, { CallbackType.MusicPlayerSelectsPlaylistEntry, typeof(MusicPlayerSelectsPlaylistEntry_t) }, { CallbackType.MusicPlayerRemoteWillActivate, typeof(MusicPlayerRemoteWillActivate_t) }, { CallbackType.MusicPlayerRemoteWillDeactivate, typeof(MusicPlayerRemoteWillDeactivate_t) }, { CallbackType.MusicPlayerRemoteToFront, typeof(MusicPlayerRemoteToFront_t) }, { CallbackType.MusicPlayerWillQuit, typeof(MusicPlayerWillQuit_t) }, { CallbackType.MusicPlayerWantsPlay, typeof(MusicPlayerWantsPlay_t) }, { CallbackType.MusicPlayerWantsPause, typeof(MusicPlayerWantsPause_t) }, { CallbackType.MusicPlayerWantsPlayPrevious, typeof(MusicPlayerWantsPlayPrevious_t) }, { CallbackType.MusicPlayerWantsPlayNext, typeof(MusicPlayerWantsPlayNext_t) }, { CallbackType.MusicPlayerWantsShuffled, typeof(MusicPlayerWantsShuffled_t) }, { CallbackType.MusicPlayerWantsLooped, typeof(MusicPlayerWantsLooped_t) }, { CallbackType.MusicPlayerWantsPlayingRepeatStatus, typeof(MusicPlayerWantsPlayingRepeatStatus_t) }, { CallbackType.HTML_BrowserReady, typeof(HTML_BrowserReady_t) }, { CallbackType.HTML_NeedsPaint, typeof(HTML_NeedsPaint_t) }, { CallbackType.HTML_StartRequest, typeof(HTML_StartRequest_t) }, { CallbackType.HTML_CloseBrowser, typeof(HTML_CloseBrowser_t) }, { CallbackType.HTML_URLChanged, typeof(HTML_URLChanged_t) }, { CallbackType.HTML_FinishedRequest, typeof(HTML_FinishedRequest_t) }, { CallbackType.HTML_OpenLinkInNewTab, typeof(HTML_OpenLinkInNewTab_t) }, { CallbackType.HTML_ChangedTitle, typeof(HTML_ChangedTitle_t) }, { CallbackType.HTML_SearchResults, typeof(HTML_SearchResults_t) }, { CallbackType.HTML_CanGoBackAndForward, typeof(HTML_CanGoBackAndForward_t) }, { CallbackType.HTML_HorizontalScroll, typeof(HTML_HorizontalScroll_t) }, { CallbackType.HTML_VerticalScroll, typeof(HTML_VerticalScroll_t) }, { CallbackType.HTML_LinkAtPosition, typeof(HTML_LinkAtPosition_t) }, { CallbackType.HTML_JSAlert, typeof(HTML_JSAlert_t) }, { CallbackType.HTML_JSConfirm, typeof(HTML_JSConfirm_t) }, { CallbackType.HTML_FileOpenDialog, typeof(HTML_FileOpenDialog_t) }, { CallbackType.HTML_NewWindow, typeof(HTML_NewWindow_t) }, { CallbackType.HTML_SetCursor, typeof(HTML_SetCursor_t) }, { CallbackType.HTML_StatusText, typeof(HTML_StatusText_t) }, { CallbackType.HTML_ShowToolTip, typeof(HTML_ShowToolTip_t) }, { CallbackType.HTML_UpdateToolTip, typeof(HTML_UpdateToolTip_t) }, { CallbackType.HTML_HideToolTip, typeof(HTML_HideToolTip_t) }, { CallbackType.HTML_BrowserRestarted, typeof(HTML_BrowserRestarted_t) }, { CallbackType.BroadcastUploadStart, typeof(BroadcastUploadStart_t) }, { CallbackType.BroadcastUploadStop, typeof(BroadcastUploadStop_t) }, { CallbackType.GetVideoURLResult, typeof(GetVideoURLResult_t) }, { CallbackType.GetOPFSettingsResult, typeof(GetOPFSettingsResult_t) }, { CallbackType.SteamInventoryResultReady, typeof(SteamInventoryResultReady_t) }, { CallbackType.SteamInventoryFullUpdate, typeof(SteamInventoryFullUpdate_t) }, { CallbackType.SteamInventoryDefinitionUpdate, typeof(SteamInventoryDefinitionUpdate_t) }, { CallbackType.SteamInventoryEligiblePromoItemDefIDs, typeof(SteamInventoryEligiblePromoItemDefIDs_t) }, { CallbackType.SteamInventoryStartPurchaseResult, typeof(SteamInventoryStartPurchaseResult_t) }, { CallbackType.SteamInventoryRequestPricesResult, typeof(SteamInventoryRequestPricesResult_t) }, { CallbackType.SteamParentalSettingsChanged, typeof(SteamParentalSettingsChanged_t) }, { CallbackType.SearchForGameProgressCallback, typeof(SearchForGameProgressCallback_t) }, { CallbackType.SearchForGameResultCallback, typeof(SearchForGameResultCallback_t) }, { CallbackType.RequestPlayersForGameProgressCallback, typeof(RequestPlayersForGameProgressCallback_t) }, { CallbackType.RequestPlayersForGameResultCallback, typeof(RequestPlayersForGameResultCallback_t) }, { CallbackType.RequestPlayersForGameFinalResultCallback, typeof(RequestPlayersForGameFinalResultCallback_t) }, { CallbackType.SubmitPlayerResultResultCallback, typeof(SubmitPlayerResultResultCallback_t) }, { CallbackType.EndGameResultCallback, typeof(EndGameResultCallback_t) }, { CallbackType.JoinPartyCallback, typeof(JoinPartyCallback_t) }, { CallbackType.CreateBeaconCallback, typeof(CreateBeaconCallback_t) }, { CallbackType.ReservationNotificationCallback, typeof(ReservationNotificationCallback_t) }, { CallbackType.ChangeNumOpenSlotsCallback, typeof(ChangeNumOpenSlotsCallback_t) }, { CallbackType.AvailableBeaconLocationsUpdated, typeof(AvailableBeaconLocationsUpdated_t) }, { CallbackType.ActiveBeaconsUpdated, typeof(ActiveBeaconsUpdated_t) }, { CallbackType.SteamRemotePlaySessionConnected, typeof(SteamRemotePlaySessionConnected_t) }, { CallbackType.SteamRemotePlaySessionDisconnected, typeof(SteamRemotePlaySessionDisconnected_t) } }; } internal class ISteamAppList : SteamInterface { internal ISteamAppList(bool IsGameServer) { SetupInterface(IsGameServer); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SteamAPI_SteamAppList_v001(); public override IntPtr GetUserInterfacePointer() { return SteamAPI_SteamAppList_v001(); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamAppList_GetNumInstalledApps")] private static extern uint _GetNumInstalledApps(IntPtr self); internal uint GetNumInstalledApps() { return _GetNumInstalledApps(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamAppList_GetInstalledApps")] private static extern uint _GetInstalledApps(IntPtr self, [In][Out] AppId[] pvecAppID, uint unMaxAppIDs); internal uint GetInstalledApps([In][Out] AppId[] pvecAppID, uint unMaxAppIDs) { return _GetInstalledApps(Self, pvecAppID, unMaxAppIDs); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamAppList_GetAppName")] private static extern int _GetAppName(IntPtr self, AppId nAppID, IntPtr pchName, int cchNameMax); internal int GetAppName(AppId nAppID, out string pchName) { IntPtr intPtr = Helpers.TakeMemory(); int result = _GetAppName(Self, nAppID, intPtr, 32768); pchName = Helpers.MemoryToString(intPtr); return result; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamAppList_GetAppInstallDir")] private static extern int _GetAppInstallDir(IntPtr self, AppId nAppID, IntPtr pchDirectory, int cchNameMax); internal int GetAppInstallDir(AppId nAppID, out string pchDirectory) { IntPtr intPtr = Helpers.TakeMemory(); int result = _GetAppInstallDir(Self, nAppID, intPtr, 32768); pchDirectory = Helpers.MemoryToString(intPtr); return result; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamAppList_GetAppBuildId")] private static extern int _GetAppBuildId(IntPtr self, AppId nAppID); internal int GetAppBuildId(AppId nAppID) { return _GetAppBuildId(Self, nAppID); } } internal class ISteamApps : SteamInterface { internal ISteamApps(bool IsGameServer) { SetupInterface(IsGameServer); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SteamAPI_SteamApps_v008(); public override IntPtr GetUserInterfacePointer() { return SteamAPI_SteamApps_v008(); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SteamAPI_SteamGameServerApps_v008(); public override IntPtr GetServerInterfacePointer() { return SteamAPI_SteamGameServerApps_v008(); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsSubscribed")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsSubscribed(IntPtr self); internal bool BIsSubscribed() { return _BIsSubscribed(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsLowViolence")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsLowViolence(IntPtr self); internal bool BIsLowViolence() { return _BIsLowViolence(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsCybercafe")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsCybercafe(IntPtr self); internal bool BIsCybercafe() { return _BIsCybercafe(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsVACBanned")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsVACBanned(IntPtr self); internal bool BIsVACBanned() { return _BIsVACBanned(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetCurrentGameLanguage")] private static extern Utf8StringPointer _GetCurrentGameLanguage(IntPtr self); internal string GetCurrentGameLanguage() { Utf8StringPointer utf8StringPointer = _GetCurrentGameLanguage(Self); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetAvailableGameLanguages")] private static extern Utf8StringPointer _GetAvailableGameLanguages(IntPtr self); internal string GetAvailableGameLanguages() { Utf8StringPointer utf8StringPointer = _GetAvailableGameLanguages(Self); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsSubscribedApp")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsSubscribedApp(IntPtr self, AppId appID); internal bool BIsSubscribedApp(AppId appID) { return _BIsSubscribedApp(Self, appID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsDlcInstalled")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsDlcInstalled(IntPtr self, AppId appID); internal bool BIsDlcInstalled(AppId appID) { return _BIsDlcInstalled(Self, appID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetEarliestPurchaseUnixTime")] private static extern uint _GetEarliestPurchaseUnixTime(IntPtr self, AppId nAppID); internal uint GetEarliestPurchaseUnixTime(AppId nAppID) { return _GetEarliestPurchaseUnixTime(Self, nAppID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsSubscribedFromFreeWeekend")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsSubscribedFromFreeWeekend(IntPtr self); internal bool BIsSubscribedFromFreeWeekend() { return _BIsSubscribedFromFreeWeekend(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetDLCCount")] private static extern int _GetDLCCount(IntPtr self); internal int GetDLCCount() { return _GetDLCCount(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BGetDLCDataByIndex")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BGetDLCDataByIndex(IntPtr self, int iDLC, ref AppId pAppID, [MarshalAs(UnmanagedType.U1)] ref bool pbAvailable, IntPtr pchName, int cchNameBufferSize); internal bool BGetDLCDataByIndex(int iDLC, ref AppId pAppID, [MarshalAs(UnmanagedType.U1)] ref bool pbAvailable, out string pchName) { IntPtr intPtr = Helpers.TakeMemory(); bool result = _BGetDLCDataByIndex(Self, iDLC, ref pAppID, ref pbAvailable, intPtr, 32768); pchName = Helpers.MemoryToString(intPtr); return result; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_InstallDLC")] private static extern void _InstallDLC(IntPtr self, AppId nAppID); internal void InstallDLC(AppId nAppID) { _InstallDLC(Self, nAppID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_UninstallDLC")] private static extern void _UninstallDLC(IntPtr self, AppId nAppID); internal void UninstallDLC(AppId nAppID) { _UninstallDLC(Self, nAppID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_RequestAppProofOfPurchaseKey")] private static extern void _RequestAppProofOfPurchaseKey(IntPtr self, AppId nAppID); internal void RequestAppProofOfPurchaseKey(AppId nAppID) { _RequestAppProofOfPurchaseKey(Self, nAppID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetCurrentBetaName")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _GetCurrentBetaName(IntPtr self, IntPtr pchName, int cchNameBufferSize); internal bool GetCurrentBetaName(out string pchName) { IntPtr intPtr = Helpers.TakeMemory(); bool result = _GetCurrentBetaName(Self, intPtr, 32768); pchName = Helpers.MemoryToString(intPtr); return result; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_MarkContentCorrupt")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _MarkContentCorrupt(IntPtr self, [MarshalAs(UnmanagedType.U1)] bool bMissingFilesOnly); internal bool MarkContentCorrupt([MarshalAs(UnmanagedType.U1)] bool bMissingFilesOnly) { return _MarkContentCorrupt(Self, bMissingFilesOnly); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetInstalledDepots")] private static extern uint _GetInstalledDepots(IntPtr self, AppId appID, [In][Out] DepotId_t[] pvecDepots, uint cMaxDepots); internal uint GetInstalledDepots(AppId appID, [In][Out] DepotId_t[] pvecDepots, uint cMaxDepots) { return _GetInstalledDepots(Self, appID, pvecDepots, cMaxDepots); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetAppInstallDir")] private static extern uint _GetAppInstallDir(IntPtr self, AppId appID, IntPtr pchFolder, uint cchFolderBufferSize); internal uint GetAppInstallDir(AppId appID, out string pchFolder) { IntPtr intPtr = Helpers.TakeMemory(); uint result = _GetAppInstallDir(Self, appID, intPtr, 32768u); pchFolder = Helpers.MemoryToString(intPtr); return result; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsAppInstalled")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsAppInstalled(IntPtr self, AppId appID); internal bool BIsAppInstalled(AppId appID) { return _BIsAppInstalled(Self, appID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetAppOwner")] private static extern SteamId _GetAppOwner(IntPtr self); internal SteamId GetAppOwner() { return _GetAppOwner(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetLaunchQueryParam")] private static extern Utf8StringPointer _GetLaunchQueryParam(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchKey); internal string GetLaunchQueryParam([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchKey) { Utf8StringPointer utf8StringPointer = _GetLaunchQueryParam(Self, pchKey); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetDlcDownloadProgress")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _GetDlcDownloadProgress(IntPtr self, AppId nAppID, ref ulong punBytesDownloaded, ref ulong punBytesTotal); internal bool GetDlcDownloadProgress(AppId nAppID, ref ulong punBytesDownloaded, ref ulong punBytesTotal) { return _GetDlcDownloadProgress(Self, nAppID, ref punBytesDownloaded, ref punBytesTotal); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetAppBuildId")] private static extern int _GetAppBuildId(IntPtr self); internal int GetAppBuildId() { return _GetAppBuildId(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_RequestAllProofOfPurchaseKeys")] private static extern void _RequestAllProofOfPurchaseKeys(IntPtr self); internal void RequestAllProofOfPurchaseKeys() { _RequestAllProofOfPurchaseKeys(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetFileDetails")] private static extern SteamAPICall_t _GetFileDetails(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pszFileName); internal CallResult<FileDetailsResult_t> GetFileDetails([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pszFileName) { SteamAPICall_t call = _GetFileDetails(Self, pszFileName); return new CallResult<FileDetailsResult_t>(call, base.IsServer); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_GetLaunchCommandLine")] private static extern int _GetLaunchCommandLine(IntPtr self, IntPtr pszCommandLine, int cubCommandLine); internal int GetLaunchCommandLine(out string pszCommandLine) { IntPtr intPtr = Helpers.TakeMemory(); int result = _GetLaunchCommandLine(Self, intPtr, 32768); pszCommandLine = Helpers.MemoryToString(intPtr); return result; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamApps_BIsSubscribedFromFamilySharing")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BIsSubscribedFromFamilySharing(IntPtr self); internal bool BIsSubscribedFromFamilySharing() { return _BIsSubscribedFromFamilySharing(Self); } } internal class ISteamClient : SteamInterface { internal ISteamClient(bool IsGameServer) { SetupInterface(IsGameServer); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_CreateSteamPipe")] private static extern HSteamPipe _CreateSteamPipe(IntPtr self); internal HSteamPipe CreateSteamPipe() { return _CreateSteamPipe(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_BReleaseSteamPipe")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BReleaseSteamPipe(IntPtr self, HSteamPipe hSteamPipe); internal bool BReleaseSteamPipe(HSteamPipe hSteamPipe) { return _BReleaseSteamPipe(Self, hSteamPipe); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_ConnectToGlobalUser")] private static extern HSteamUser _ConnectToGlobalUser(IntPtr self, HSteamPipe hSteamPipe); internal HSteamUser ConnectToGlobalUser(HSteamPipe hSteamPipe) { return _ConnectToGlobalUser(Self, hSteamPipe); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_CreateLocalUser")] private static extern HSteamUser _CreateLocalUser(IntPtr self, ref HSteamPipe phSteamPipe, AccountType eAccountType); internal HSteamUser CreateLocalUser(ref HSteamPipe phSteamPipe, AccountType eAccountType) { return _CreateLocalUser(Self, ref phSteamPipe, eAccountType); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_ReleaseUser")] private static extern void _ReleaseUser(IntPtr self, HSteamPipe hSteamPipe, HSteamUser hUser); internal void ReleaseUser(HSteamPipe hSteamPipe, HSteamUser hUser) { _ReleaseUser(Self, hSteamPipe, hUser); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamUser")] private static extern IntPtr _GetISteamUser(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamUser(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamUser(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamGameServer")] private static extern IntPtr _GetISteamGameServer(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamGameServer(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamGameServer(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_SetLocalIPBinding")] private static extern void _SetLocalIPBinding(IntPtr self, ref SteamIPAddress unIP, ushort usPort); internal void SetLocalIPBinding(ref SteamIPAddress unIP, ushort usPort) { _SetLocalIPBinding(Self, ref unIP, usPort); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamFriends")] private static extern IntPtr _GetISteamFriends(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamFriends(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamFriends(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamUtils")] private static extern IntPtr _GetISteamUtils(IntPtr self, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamUtils(HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamUtils(Self, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamMatchmaking")] private static extern IntPtr _GetISteamMatchmaking(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamMatchmaking(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamMatchmaking(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamMatchmakingServers")] private static extern IntPtr _GetISteamMatchmakingServers(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamMatchmakingServers(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamMatchmakingServers(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamGenericInterface")] private static extern IntPtr _GetISteamGenericInterface(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamGenericInterface(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamGenericInterface(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamUserStats")] private static extern IntPtr _GetISteamUserStats(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamUserStats(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamUserStats(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamGameServerStats")] private static extern IntPtr _GetISteamGameServerStats(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamGameServerStats(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamGameServerStats(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamApps")] private static extern IntPtr _GetISteamApps(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamApps(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamApps(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamNetworking")] private static extern IntPtr _GetISteamNetworking(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamNetworking(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamNetworking(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamRemoteStorage")] private static extern IntPtr _GetISteamRemoteStorage(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamRemoteStorage(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamRemoteStorage(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamScreenshots")] private static extern IntPtr _GetISteamScreenshots(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamScreenshots(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamScreenshots(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamGameSearch")] private static extern IntPtr _GetISteamGameSearch(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamGameSearch(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamGameSearch(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetIPCCallCount")] private static extern uint _GetIPCCallCount(IntPtr self); internal uint GetIPCCallCount() { return _GetIPCCallCount(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_SetWarningMessageHook")] private static extern void _SetWarningMessageHook(IntPtr self, IntPtr pFunction); internal void SetWarningMessageHook(IntPtr pFunction) { _SetWarningMessageHook(Self, pFunction); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_BShutdownIfAllPipesClosed")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _BShutdownIfAllPipesClosed(IntPtr self); internal bool BShutdownIfAllPipesClosed() { return _BShutdownIfAllPipesClosed(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamHTTP")] private static extern IntPtr _GetISteamHTTP(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamHTTP(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamHTTP(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamController")] private static extern IntPtr _GetISteamController(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamController(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamController(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamUGC")] private static extern IntPtr _GetISteamUGC(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamUGC(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamUGC(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamAppList")] private static extern IntPtr _GetISteamAppList(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamAppList(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamAppList(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamMusic")] private static extern IntPtr _GetISteamMusic(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamMusic(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamMusic(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamMusicRemote")] private static extern IntPtr _GetISteamMusicRemote(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamMusicRemote(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamHTMLSurface")] private static extern IntPtr _GetISteamHTMLSurface(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamHTMLSurface(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamInventory")] private static extern IntPtr _GetISteamInventory(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamInventory(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamInventory(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamVideo")] private static extern IntPtr _GetISteamVideo(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamVideo(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamVideo(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamParentalSettings")] private static extern IntPtr _GetISteamParentalSettings(IntPtr self, HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamParentalSettings(HSteamUser hSteamuser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamParentalSettings(Self, hSteamuser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamInput")] private static extern IntPtr _GetISteamInput(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamInput(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamInput(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamParties")] private static extern IntPtr _GetISteamParties(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamParties(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamParties(Self, hSteamUser, hSteamPipe, pchVersion); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamClient_GetISteamRemotePlay")] private static extern IntPtr _GetISteamRemotePlay(IntPtr self, HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion); internal IntPtr GetISteamRemotePlay(HSteamUser hSteamUser, HSteamPipe hSteamPipe, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchVersion) { return _GetISteamRemotePlay(Self, hSteamUser, hSteamPipe, pchVersion); } } internal class ISteamController : SteamInterface { internal ISteamController(bool IsGameServer) { SetupInterface(IsGameServer); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SteamAPI_SteamController_v007(); public override IntPtr GetUserInterfacePointer() { return SteamAPI_SteamController_v007(); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_Init")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _Init(IntPtr self); internal bool Init() { return _Init(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_Shutdown")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _Shutdown(IntPtr self); internal bool Shutdown() { return _Shutdown(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_RunFrame")] private static extern void _RunFrame(IntPtr self); internal void RunFrame() { _RunFrame(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetConnectedControllers")] private static extern int _GetConnectedControllers(IntPtr self, [In][Out] ControllerHandle_t[] handlesOut); internal int GetConnectedControllers([In][Out] ControllerHandle_t[] handlesOut) { return _GetConnectedControllers(Self, handlesOut); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetActionSetHandle")] private static extern ControllerActionSetHandle_t _GetActionSetHandle(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pszActionSetName); internal ControllerActionSetHandle_t GetActionSetHandle([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pszActionSetName) { return _GetActionSetHandle(Self, pszActionSetName); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_ActivateActionSet")] private static extern void _ActivateActionSet(IntPtr self, ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle); internal void ActivateActionSet(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle) { _ActivateActionSet(Self, controllerHandle, actionSetHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetCurrentActionSet")] private static extern ControllerActionSetHandle_t _GetCurrentActionSet(IntPtr self, ControllerHandle_t controllerHandle); internal ControllerActionSetHandle_t GetCurrentActionSet(ControllerHandle_t controllerHandle) { return _GetCurrentActionSet(Self, controllerHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_ActivateActionSetLayer")] private static extern void _ActivateActionSetLayer(IntPtr self, ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle); internal void ActivateActionSetLayer(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle) { _ActivateActionSetLayer(Self, controllerHandle, actionSetLayerHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_DeactivateActionSetLayer")] private static extern void _DeactivateActionSetLayer(IntPtr self, ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle); internal void DeactivateActionSetLayer(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle) { _DeactivateActionSetLayer(Self, controllerHandle, actionSetLayerHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_DeactivateAllActionSetLayers")] private static extern void _DeactivateAllActionSetLayers(IntPtr self, ControllerHandle_t controllerHandle); internal void DeactivateAllActionSetLayers(ControllerHandle_t controllerHandle) { _DeactivateAllActionSetLayers(Self, controllerHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetActiveActionSetLayers")] private static extern int _GetActiveActionSetLayers(IntPtr self, ControllerHandle_t controllerHandle, [In][Out] ControllerActionSetHandle_t[] handlesOut); internal int GetActiveActionSetLayers(ControllerHandle_t controllerHandle, [In][Out] ControllerActionSetHandle_t[] handlesOut) { return _GetActiveActionSetLayers(Self, controllerHandle, handlesOut); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetDigitalActionHandle")] private static extern ControllerDigitalActionHandle_t _GetDigitalActionHandle(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pszActionName); internal ControllerDigitalActionHandle_t GetDigitalActionHandle([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pszActionName) { return _GetDigitalActionHandle(Self, pszActionName); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetDigitalActionData")] private static extern DigitalState _GetDigitalActionData(IntPtr self, ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle); internal DigitalState GetDigitalActionData(ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle) { return _GetDigitalActionData(Self, controllerHandle, digitalActionHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetDigitalActionOrigins")] private static extern int _GetDigitalActionOrigins(IntPtr self, ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, ref ControllerActionOrigin originsOut); internal int GetDigitalActionOrigins(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, ref ControllerActionOrigin originsOut) { return _GetDigitalActionOrigins(Self, controllerHandle, actionSetHandle, digitalActionHandle, ref originsOut); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetAnalogActionHandle")] private static extern ControllerAnalogActionHandle_t _GetAnalogActionHandle(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pszActionName); internal ControllerAnalogActionHandle_t GetAnalogActionHandle([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pszActionName) { return _GetAnalogActionHandle(Self, pszActionName); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetAnalogActionData")] private static extern AnalogState _GetAnalogActionData(IntPtr self, ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle); internal AnalogState GetAnalogActionData(ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle) { return _GetAnalogActionData(Self, controllerHandle, analogActionHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetAnalogActionOrigins")] private static extern int _GetAnalogActionOrigins(IntPtr self, ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, ref ControllerActionOrigin originsOut); internal int GetAnalogActionOrigins(ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, ref ControllerActionOrigin originsOut) { return _GetAnalogActionOrigins(Self, controllerHandle, actionSetHandle, analogActionHandle, ref originsOut); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetGlyphForActionOrigin")] private static extern Utf8StringPointer _GetGlyphForActionOrigin(IntPtr self, ControllerActionOrigin eOrigin); internal string GetGlyphForActionOrigin(ControllerActionOrigin eOrigin) { Utf8StringPointer utf8StringPointer = _GetGlyphForActionOrigin(Self, eOrigin); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetStringForActionOrigin")] private static extern Utf8StringPointer _GetStringForActionOrigin(IntPtr self, ControllerActionOrigin eOrigin); internal string GetStringForActionOrigin(ControllerActionOrigin eOrigin) { Utf8StringPointer utf8StringPointer = _GetStringForActionOrigin(Self, eOrigin); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_StopAnalogActionMomentum")] private static extern void _StopAnalogActionMomentum(IntPtr self, ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction); internal void StopAnalogActionMomentum(ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction) { _StopAnalogActionMomentum(Self, controllerHandle, eAction); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetMotionData")] private static extern MotionState _GetMotionData(IntPtr self, ControllerHandle_t controllerHandle); internal MotionState GetMotionData(ControllerHandle_t controllerHandle) { return _GetMotionData(Self, controllerHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_TriggerHapticPulse")] private static extern void _TriggerHapticPulse(IntPtr self, ControllerHandle_t controllerHandle, SteamControllerPad eTargetPad, ushort usDurationMicroSec); internal void TriggerHapticPulse(ControllerHandle_t controllerHandle, SteamControllerPad eTargetPad, ushort usDurationMicroSec) { _TriggerHapticPulse(Self, controllerHandle, eTargetPad, usDurationMicroSec); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_TriggerRepeatedHapticPulse")] private static extern void _TriggerRepeatedHapticPulse(IntPtr self, ControllerHandle_t controllerHandle, SteamControllerPad eTargetPad, ushort usDurationMicroSec, ushort usOffMicroSec, ushort unRepeat, uint nFlags); internal void TriggerRepeatedHapticPulse(ControllerHandle_t controllerHandle, SteamControllerPad eTargetPad, ushort usDurationMicroSec, ushort usOffMicroSec, ushort unRepeat, uint nFlags) { _TriggerRepeatedHapticPulse(Self, controllerHandle, eTargetPad, usDurationMicroSec, usOffMicroSec, unRepeat, nFlags); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_TriggerVibration")] private static extern void _TriggerVibration(IntPtr self, ControllerHandle_t controllerHandle, ushort usLeftSpeed, ushort usRightSpeed); internal void TriggerVibration(ControllerHandle_t controllerHandle, ushort usLeftSpeed, ushort usRightSpeed) { _TriggerVibration(Self, controllerHandle, usLeftSpeed, usRightSpeed); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_SetLEDColor")] private static extern void _SetLEDColor(IntPtr self, ControllerHandle_t controllerHandle, byte nColorR, byte nColorG, byte nColorB, uint nFlags); internal void SetLEDColor(ControllerHandle_t controllerHandle, byte nColorR, byte nColorG, byte nColorB, uint nFlags) { _SetLEDColor(Self, controllerHandle, nColorR, nColorG, nColorB, nFlags); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_ShowBindingPanel")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _ShowBindingPanel(IntPtr self, ControllerHandle_t controllerHandle); internal bool ShowBindingPanel(ControllerHandle_t controllerHandle) { return _ShowBindingPanel(Self, controllerHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetInputTypeForHandle")] private static extern InputType _GetInputTypeForHandle(IntPtr self, ControllerHandle_t controllerHandle); internal InputType GetInputTypeForHandle(ControllerHandle_t controllerHandle) { return _GetInputTypeForHandle(Self, controllerHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetControllerForGamepadIndex")] private static extern ControllerHandle_t _GetControllerForGamepadIndex(IntPtr self, int nIndex); internal ControllerHandle_t GetControllerForGamepadIndex(int nIndex) { return _GetControllerForGamepadIndex(Self, nIndex); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetGamepadIndexForController")] private static extern int _GetGamepadIndexForController(IntPtr self, ControllerHandle_t ulControllerHandle); internal int GetGamepadIndexForController(ControllerHandle_t ulControllerHandle) { return _GetGamepadIndexForController(Self, ulControllerHandle); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetStringForXboxOrigin")] private static extern Utf8StringPointer _GetStringForXboxOrigin(IntPtr self, XboxOrigin eOrigin); internal string GetStringForXboxOrigin(XboxOrigin eOrigin) { Utf8StringPointer utf8StringPointer = _GetStringForXboxOrigin(Self, eOrigin); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetGlyphForXboxOrigin")] private static extern Utf8StringPointer _GetGlyphForXboxOrigin(IntPtr self, XboxOrigin eOrigin); internal string GetGlyphForXboxOrigin(XboxOrigin eOrigin) { Utf8StringPointer utf8StringPointer = _GetGlyphForXboxOrigin(Self, eOrigin); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetActionOriginFromXboxOrigin")] private static extern ControllerActionOrigin _GetActionOriginFromXboxOrigin(IntPtr self, ControllerHandle_t controllerHandle, XboxOrigin eOrigin); internal ControllerActionOrigin GetActionOriginFromXboxOrigin(ControllerHandle_t controllerHandle, XboxOrigin eOrigin) { return _GetActionOriginFromXboxOrigin(Self, controllerHandle, eOrigin); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_TranslateActionOrigin")] private static extern ControllerActionOrigin _TranslateActionOrigin(IntPtr self, InputType eDestinationInputType, ControllerActionOrigin eSourceOrigin); internal ControllerActionOrigin TranslateActionOrigin(InputType eDestinationInputType, ControllerActionOrigin eSourceOrigin) { return _TranslateActionOrigin(Self, eDestinationInputType, eSourceOrigin); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamController_GetControllerBindingRevision")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _GetControllerBindingRevision(IntPtr self, ControllerHandle_t controllerHandle, ref int pMajor, ref int pMinor); internal bool GetControllerBindingRevision(ControllerHandle_t controllerHandle, ref int pMajor, ref int pMinor) { return _GetControllerBindingRevision(Self, controllerHandle, ref pMajor, ref pMinor); } } internal class ISteamFriends : SteamInterface { internal ISteamFriends(bool IsGameServer) { SetupInterface(IsGameServer); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SteamAPI_SteamFriends_v017(); public override IntPtr GetUserInterfacePointer() { return SteamAPI_SteamFriends_v017(); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetPersonaName")] private static extern Utf8StringPointer _GetPersonaName(IntPtr self); internal string GetPersonaName() { Utf8StringPointer utf8StringPointer = _GetPersonaName(Self); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_SetPersonaName")] private static extern SteamAPICall_t _SetPersonaName(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchPersonaName); internal CallResult<SetPersonaNameResponse_t> SetPersonaName([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchPersonaName) { SteamAPICall_t call = _SetPersonaName(Self, pchPersonaName); return new CallResult<SetPersonaNameResponse_t>(call, base.IsServer); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetPersonaState")] private static extern FriendState _GetPersonaState(IntPtr self); internal FriendState GetPersonaState() { return _GetPersonaState(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendCount")] private static extern int _GetFriendCount(IntPtr self, int iFriendFlags); internal int GetFriendCount(int iFriendFlags) { return _GetFriendCount(Self, iFriendFlags); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendByIndex")] private static extern SteamId _GetFriendByIndex(IntPtr self, int iFriend, int iFriendFlags); internal SteamId GetFriendByIndex(int iFriend, int iFriendFlags) { return _GetFriendByIndex(Self, iFriend, iFriendFlags); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendRelationship")] private static extern Relationship _GetFriendRelationship(IntPtr self, SteamId steamIDFriend); internal Relationship GetFriendRelationship(SteamId steamIDFriend) { return _GetFriendRelationship(Self, steamIDFriend); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendPersonaState")] private static extern FriendState _GetFriendPersonaState(IntPtr self, SteamId steamIDFriend); internal FriendState GetFriendPersonaState(SteamId steamIDFriend) { return _GetFriendPersonaState(Self, steamIDFriend); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendPersonaName")] private static extern Utf8StringPointer _GetFriendPersonaName(IntPtr self, SteamId steamIDFriend); internal string GetFriendPersonaName(SteamId steamIDFriend) { Utf8StringPointer utf8StringPointer = _GetFriendPersonaName(Self, steamIDFriend); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendGamePlayed")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _GetFriendGamePlayed(IntPtr self, SteamId steamIDFriend, ref FriendGameInfo_t pFriendGameInfo); internal bool GetFriendGamePlayed(SteamId steamIDFriend, ref FriendGameInfo_t pFriendGameInfo) { return _GetFriendGamePlayed(Self, steamIDFriend, ref pFriendGameInfo); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendPersonaNameHistory")] private static extern Utf8StringPointer _GetFriendPersonaNameHistory(IntPtr self, SteamId steamIDFriend, int iPersonaName); internal string GetFriendPersonaNameHistory(SteamId steamIDFriend, int iPersonaName) { Utf8StringPointer utf8StringPointer = _GetFriendPersonaNameHistory(Self, steamIDFriend, iPersonaName); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendSteamLevel")] private static extern int _GetFriendSteamLevel(IntPtr self, SteamId steamIDFriend); internal int GetFriendSteamLevel(SteamId steamIDFriend) { return _GetFriendSteamLevel(Self, steamIDFriend); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetPlayerNickname")] private static extern Utf8StringPointer _GetPlayerNickname(IntPtr self, SteamId steamIDPlayer); internal string GetPlayerNickname(SteamId steamIDPlayer) { Utf8StringPointer utf8StringPointer = _GetPlayerNickname(Self, steamIDPlayer); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendsGroupCount")] private static extern int _GetFriendsGroupCount(IntPtr self); internal int GetFriendsGroupCount() { return _GetFriendsGroupCount(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendsGroupIDByIndex")] private static extern FriendsGroupID_t _GetFriendsGroupIDByIndex(IntPtr self, int iFG); internal FriendsGroupID_t GetFriendsGroupIDByIndex(int iFG) { return _GetFriendsGroupIDByIndex(Self, iFG); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendsGroupName")] private static extern Utf8StringPointer _GetFriendsGroupName(IntPtr self, FriendsGroupID_t friendsGroupID); internal string GetFriendsGroupName(FriendsGroupID_t friendsGroupID) { Utf8StringPointer utf8StringPointer = _GetFriendsGroupName(Self, friendsGroupID); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendsGroupMembersCount")] private static extern int _GetFriendsGroupMembersCount(IntPtr self, FriendsGroupID_t friendsGroupID); internal int GetFriendsGroupMembersCount(FriendsGroupID_t friendsGroupID) { return _GetFriendsGroupMembersCount(Self, friendsGroupID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendsGroupMembersList")] private static extern void _GetFriendsGroupMembersList(IntPtr self, FriendsGroupID_t friendsGroupID, [In][Out] SteamId[] pOutSteamIDMembers, int nMembersCount); internal void GetFriendsGroupMembersList(FriendsGroupID_t friendsGroupID, [In][Out] SteamId[] pOutSteamIDMembers, int nMembersCount) { _GetFriendsGroupMembersList(Self, friendsGroupID, pOutSteamIDMembers, nMembersCount); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_HasFriend")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _HasFriend(IntPtr self, SteamId steamIDFriend, int iFriendFlags); internal bool HasFriend(SteamId steamIDFriend, int iFriendFlags) { return _HasFriend(Self, steamIDFriend, iFriendFlags); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetClanCount")] private static extern int _GetClanCount(IntPtr self); internal int GetClanCount() { return _GetClanCount(Self); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetClanByIndex")] private static extern SteamId _GetClanByIndex(IntPtr self, int iClan); internal SteamId GetClanByIndex(int iClan) { return _GetClanByIndex(Self, iClan); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetClanName")] private static extern Utf8StringPointer _GetClanName(IntPtr self, SteamId steamIDClan); internal string GetClanName(SteamId steamIDClan) { Utf8StringPointer utf8StringPointer = _GetClanName(Self, steamIDClan); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetClanTag")] private static extern Utf8StringPointer _GetClanTag(IntPtr self, SteamId steamIDClan); internal string GetClanTag(SteamId steamIDClan) { Utf8StringPointer utf8StringPointer = _GetClanTag(Self, steamIDClan); return utf8StringPointer; } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetClanActivityCounts")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _GetClanActivityCounts(IntPtr self, SteamId steamIDClan, ref int pnOnline, ref int pnInGame, ref int pnChatting); internal bool GetClanActivityCounts(SteamId steamIDClan, ref int pnOnline, ref int pnInGame, ref int pnChatting) { return _GetClanActivityCounts(Self, steamIDClan, ref pnOnline, ref pnInGame, ref pnChatting); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_DownloadClanActivityCounts")] private static extern SteamAPICall_t _DownloadClanActivityCounts(IntPtr self, [In][Out] SteamId[] psteamIDClans, int cClansToRequest); internal CallResult<DownloadClanActivityCountsResult_t> DownloadClanActivityCounts([In][Out] SteamId[] psteamIDClans, int cClansToRequest) { SteamAPICall_t call = _DownloadClanActivityCounts(Self, psteamIDClans, cClansToRequest); return new CallResult<DownloadClanActivityCountsResult_t>(call, base.IsServer); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendCountFromSource")] private static extern int _GetFriendCountFromSource(IntPtr self, SteamId steamIDSource); internal int GetFriendCountFromSource(SteamId steamIDSource) { return _GetFriendCountFromSource(Self, steamIDSource); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_GetFriendFromSourceByIndex")] private static extern SteamId _GetFriendFromSourceByIndex(IntPtr self, SteamId steamIDSource, int iFriend); internal SteamId GetFriendFromSourceByIndex(SteamId steamIDSource, int iFriend) { return _GetFriendFromSourceByIndex(Self, steamIDSource, iFriend); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_IsUserInSource")] [return: MarshalAs(UnmanagedType.I1)] private static extern bool _IsUserInSource(IntPtr self, SteamId steamIDUser, SteamId steamIDSource); internal bool IsUserInSource(SteamId steamIDUser, SteamId steamIDSource) { return _IsUserInSource(Self, steamIDUser, steamIDSource); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_SetInGameVoiceSpeaking")] private static extern void _SetInGameVoiceSpeaking(IntPtr self, SteamId steamIDUser, [MarshalAs(UnmanagedType.U1)] bool bSpeaking); internal void SetInGameVoiceSpeaking(SteamId steamIDUser, [MarshalAs(UnmanagedType.U1)] bool bSpeaking) { _SetInGameVoiceSpeaking(Self, steamIDUser, bSpeaking); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_ActivateGameOverlay")] private static extern void _ActivateGameOverlay(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchDialog); internal void ActivateGameOverlay([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchDialog) { _ActivateGameOverlay(Self, pchDialog); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_ActivateGameOverlayToUser")] private static extern void _ActivateGameOverlayToUser(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchDialog, SteamId steamID); internal void ActivateGameOverlayToUser([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchDialog, SteamId steamID) { _ActivateGameOverlayToUser(Self, pchDialog, steamID); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_ActivateGameOverlayToWebPage")] private static extern void _ActivateGameOverlayToWebPage(IntPtr self, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchURL, ActivateGameOverlayToWebPageMode eMode); internal void ActivateGameOverlayToWebPage([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "Steamworks.Utf8StringToNative")] string pchURL, ActivateGameOverlayToWebPageMode eMode) { _ActivateGameOverlayToWebPage(Self, pchURL, eMode); } [DllImport("steam_api64", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SteamAPI_ISteamFriends_ActivateGameOverlayToStore")] private static extern void _ActivateGameOverlayToStore(IntPtr self, AppId nAppID, OverlayToStoreFlag eFlag); inte
LiteNetLib.dll
Decompiled 11 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using LiteNetLib.Layers; using LiteNetLib.Utils; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Ruslan Pyrch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright 2024 Ruslan Pyrch")] [assembly: AssemblyDescription("Lite reliable UDP library for .NET, Mono, and .NET Core")] [assembly: AssemblyFileVersion("1.3.1")] [assembly: AssemblyInformationalVersion("1.0.0+f57b9eafe72b924681a371d671af911478b6ab88")] [assembly: AssemblyProduct("LiteNetLib")] [assembly: AssemblyTitle("LiteNetLib")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/RevenantX/LiteNetLib")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.1.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsUnmanagedAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsByRefLikeAttribute : Attribute { } } namespace LiteNetLib { internal abstract class BaseChannel { protected readonly NetPeer Peer; protected readonly Queue<NetPacket> OutgoingQueue = new Queue<NetPacket>(64); private int _isAddedToPeerChannelSendQueue; public int PacketsInQueue => OutgoingQueue.Count; protected BaseChannel(NetPeer peer) { Peer = peer; } public void AddToQueue(NetPacket packet) { lock (OutgoingQueue) { OutgoingQueue.Enqueue(packet); } AddToPeerChannelSendQueue(); } protected void AddToPeerChannelSendQueue() { if (Interlocked.CompareExchange(ref _isAddedToPeerChannelSendQueue, 1, 0) == 0) { Peer.AddToReliableChannelSendQueue(this); } } public bool SendAndCheckQueue() { bool num = SendNextPackets(); if (!num) { Interlocked.Exchange(ref _isAddedToPeerChannelSendQueue, 0); } return num; } protected abstract bool SendNextPackets(); public abstract bool ProcessPacket(NetPacket packet); } internal enum ConnectionRequestResult { None, Accept, Reject, RejectForce } public class ConnectionRequest { private readonly NetManager _listener; private int _used; internal NetConnectRequestPacket InternalPacket; public readonly IPEndPoint RemoteEndPoint; public NetDataReader Data => InternalPacket.Data; internal ConnectionRequestResult Result { get; private set; } internal void UpdateRequest(NetConnectRequestPacket connectRequest) { if (connectRequest.ConnectionTime >= InternalPacket.ConnectionTime && (connectRequest.ConnectionTime != InternalPacket.ConnectionTime || connectRequest.ConnectionNumber != InternalPacket.ConnectionNumber)) { InternalPacket = connectRequest; } } private bool TryActivate() { return Interlocked.CompareExchange(ref _used, 1, 0) == 0; } internal ConnectionRequest(IPEndPoint remoteEndPoint, NetConnectRequestPacket requestPacket, NetManager listener) { InternalPacket = requestPacket; RemoteEndPoint = remoteEndPoint; _listener = listener; } public NetPeer AcceptIfKey(string key) { if (!TryActivate()) { return null; } try { if (Data.GetString() == key) { Result = ConnectionRequestResult.Accept; } } catch { NetDebug.WriteError("[AC] Invalid incoming data"); } if (Result == ConnectionRequestResult.Accept) { return _listener.OnConnectionSolved(this, null, 0, 0); } Result = ConnectionRequestResult.Reject; _listener.OnConnectionSolved(this, null, 0, 0); return null; } public NetPeer Accept() { if (!TryActivate()) { return null; } Result = ConnectionRequestResult.Accept; return _listener.OnConnectionSolved(this, null, 0, 0); } public void Reject(byte[] rejectData, int start, int length, bool force) { if (TryActivate()) { Result = (force ? ConnectionRequestResult.RejectForce : ConnectionRequestResult.Reject); _listener.OnConnectionSolved(this, rejectData, start, length); } } public void Reject(byte[] rejectData, int start, int length) { Reject(rejectData, start, length, force: false); } public void RejectForce(byte[] rejectData, int start, int length) { Reject(rejectData, start, length, force: true); } public void RejectForce() { Reject(null, 0, 0, force: true); } public void RejectForce(byte[] rejectData) { Reject(rejectData, 0, rejectData.Length, force: true); } public void RejectForce(NetDataWriter rejectData) { Reject(rejectData.Data, 0, rejectData.Length, force: true); } public void Reject() { Reject(null, 0, 0, force: false); } public void Reject(byte[] rejectData) { Reject(rejectData, 0, rejectData.Length, force: false); } public void Reject(NetDataWriter rejectData) { Reject(rejectData.Data, 0, rejectData.Length, force: false); } } public enum UnconnectedMessageType { BasicMessage, Broadcast } public enum DisconnectReason { ConnectionFailed, Timeout, HostUnreachable, NetworkUnreachable, RemoteConnectionClose, DisconnectPeerCalled, ConnectionRejected, InvalidProtocol, UnknownHost, Reconnect, PeerToPeerConnection, PeerNotFound } public struct DisconnectInfo { public DisconnectReason Reason; public SocketError SocketErrorCode; public NetPacketReader AdditionalData; } public interface INetEventListener { void OnPeerConnected(NetPeer peer); void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo); void OnNetworkError(IPEndPoint endPoint, SocketError socketError); void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod); void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType); void OnNetworkLatencyUpdate(NetPeer peer, int latency); void OnConnectionRequest(ConnectionRequest request); } public interface IDeliveryEventListener { void OnMessageDelivered(NetPeer peer, object userData); } public interface INtpEventListener { void OnNtpResponse(NtpPacket packet); } public interface IPeerAddressChangedListener { void OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress); } public class EventBasedNetListener : INetEventListener, IDeliveryEventListener, INtpEventListener, IPeerAddressChangedListener { public delegate void OnPeerConnected(NetPeer peer); public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo); public delegate void OnNetworkError(IPEndPoint endPoint, SocketError socketError); public delegate void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod); public delegate void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType); public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency); public delegate void OnConnectionRequest(ConnectionRequest request); public delegate void OnDeliveryEvent(NetPeer peer, object userData); public delegate void OnNtpResponseEvent(NtpPacket packet); public delegate void OnPeerAddressChangedEvent(NetPeer peer, IPEndPoint previousAddress); public event OnPeerConnected PeerConnectedEvent; public event OnPeerDisconnected PeerDisconnectedEvent; public event OnNetworkError NetworkErrorEvent; public event OnNetworkReceive NetworkReceiveEvent; public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent; public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent; public event OnConnectionRequest ConnectionRequestEvent; public event OnDeliveryEvent DeliveryEvent; public event OnNtpResponseEvent NtpResponseEvent; public event OnPeerAddressChangedEvent PeerAddressChangedEvent; public void ClearPeerConnectedEvent() { this.PeerConnectedEvent = null; } public void ClearPeerDisconnectedEvent() { this.PeerDisconnectedEvent = null; } public void ClearNetworkErrorEvent() { this.NetworkErrorEvent = null; } public void ClearNetworkReceiveEvent() { this.NetworkReceiveEvent = null; } public void ClearNetworkReceiveUnconnectedEvent() { this.NetworkReceiveUnconnectedEvent = null; } public void ClearNetworkLatencyUpdateEvent() { this.NetworkLatencyUpdateEvent = null; } public void ClearConnectionRequestEvent() { this.ConnectionRequestEvent = null; } public void ClearDeliveryEvent() { this.DeliveryEvent = null; } public void ClearNtpResponseEvent() { this.NtpResponseEvent = null; } public void ClearPeerAddressChangedEvent() { this.PeerAddressChangedEvent = null; } void INetEventListener.OnPeerConnected(NetPeer peer) { if (this.PeerConnectedEvent != null) { this.PeerConnectedEvent(peer); } } void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo) { if (this.PeerDisconnectedEvent != null) { this.PeerDisconnectedEvent(peer, disconnectInfo); } } void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode) { if (this.NetworkErrorEvent != null) { this.NetworkErrorEvent(endPoint, socketErrorCode); } } void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod) { if (this.NetworkReceiveEvent != null) { this.NetworkReceiveEvent(peer, reader, channelNumber, deliveryMethod); } } void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { if (this.NetworkReceiveUnconnectedEvent != null) { this.NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType); } } void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency) { if (this.NetworkLatencyUpdateEvent != null) { this.NetworkLatencyUpdateEvent(peer, latency); } } void INetEventListener.OnConnectionRequest(ConnectionRequest request) { if (this.ConnectionRequestEvent != null) { this.ConnectionRequestEvent(request); } } void IDeliveryEventListener.OnMessageDelivered(NetPeer peer, object userData) { if (this.DeliveryEvent != null) { this.DeliveryEvent(peer, userData); } } void INtpEventListener.OnNtpResponse(NtpPacket packet) { if (this.NtpResponseEvent != null) { this.NtpResponseEvent(packet); } } void IPeerAddressChangedListener.OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress) { if (this.PeerAddressChangedEvent != null) { this.PeerAddressChangedEvent(peer, previousAddress); } } } internal sealed class NetConnectRequestPacket { public const int HeaderSize = 18; public readonly long ConnectionTime; public byte ConnectionNumber; public readonly byte[] TargetAddress; public readonly NetDataReader Data; public readonly int PeerId; private NetConnectRequestPacket(long connectionTime, byte connectionNumber, int localId, byte[] targetAddress, NetDataReader data) { ConnectionTime = connectionTime; ConnectionNumber = connectionNumber; TargetAddress = targetAddress; Data = data; PeerId = localId; } public static int GetProtocolId(NetPacket packet) { return BitConverter.ToInt32(packet.RawData, 1); } public static NetConnectRequestPacket FromData(NetPacket packet) { if (packet.ConnectionNumber >= 4) { return null; } long connectionTime = BitConverter.ToInt64(packet.RawData, 5); int localId = BitConverter.ToInt32(packet.RawData, 13); int num = packet.RawData[17]; if (num != 16 && num != 28) { return null; } byte[] array = new byte[num]; Buffer.BlockCopy(packet.RawData, 18, array, 0, num); NetDataReader netDataReader = new NetDataReader(null, 0, 0); if (packet.Size > 18 + num) { netDataReader.SetSource(packet.RawData, 18 + num, packet.Size); } return new NetConnectRequestPacket(connectionTime, packet.ConnectionNumber, localId, array, netDataReader); } public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectTime, int localId) { NetPacket netPacket = new NetPacket(PacketProperty.ConnectRequest, connectData.Length + addressBytes.Size); FastBitConverter.GetBytes(netPacket.RawData, 1, 13); FastBitConverter.GetBytes(netPacket.RawData, 5, connectTime); FastBitConverter.GetBytes(netPacket.RawData, 13, localId); netPacket.RawData[17] = (byte)addressBytes.Size; for (int i = 0; i < addressBytes.Size; i++) { netPacket.RawData[18 + i] = addressBytes[i]; } Buffer.BlockCopy(connectData.Data, 0, netPacket.RawData, 18 + addressBytes.Size, connectData.Length); return netPacket; } } internal sealed class NetConnectAcceptPacket { public const int Size = 15; public readonly long ConnectionTime; public readonly byte ConnectionNumber; public readonly int PeerId; public readonly bool PeerNetworkChanged; private NetConnectAcceptPacket(long connectionTime, byte connectionNumber, int peerId, bool peerNetworkChanged) { ConnectionTime = connectionTime; ConnectionNumber = connectionNumber; PeerId = peerId; PeerNetworkChanged = peerNetworkChanged; } public static NetConnectAcceptPacket FromData(NetPacket packet) { if (packet.Size != 15) { return null; } long connectionTime = BitConverter.ToInt64(packet.RawData, 1); byte b = packet.RawData[9]; if (b >= 4) { return null; } byte b2 = packet.RawData[10]; if (b2 > 1) { return null; } int num = BitConverter.ToInt32(packet.RawData, 11); if (num < 0) { return null; } return new NetConnectAcceptPacket(connectionTime, b, num, b2 == 1); } public static NetPacket Make(long connectTime, byte connectNum, int localPeerId) { NetPacket netPacket = new NetPacket(PacketProperty.ConnectAccept, 0); FastBitConverter.GetBytes(netPacket.RawData, 1, connectTime); netPacket.RawData[9] = connectNum; FastBitConverter.GetBytes(netPacket.RawData, 11, localPeerId); return netPacket; } public static NetPacket MakeNetworkChanged(NetPeer peer) { NetPacket netPacket = new NetPacket(PacketProperty.PeerNotFound, 14); FastBitConverter.GetBytes(netPacket.RawData, 1, peer.ConnectTime); netPacket.RawData[9] = peer.ConnectionNum; netPacket.RawData[10] = 1; FastBitConverter.GetBytes(netPacket.RawData, 11, peer.RemoteId); return netPacket; } } internal static class NativeSocket { private static class WinSock { private const string LibName = "ws2_32.dll"; [DllImport("ws2_32.dll", SetLastError = true)] public static extern int recvfrom(IntPtr socketHandle, [In][Out] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [Out] byte[] socketAddress, [In][Out] ref int socketAddressSize); [DllImport("ws2_32.dll", SetLastError = true)] internal unsafe static extern int sendto(IntPtr socketHandle, byte* pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize); } private static class UnixSock { private const string LibName = "libc"; [DllImport("libc", SetLastError = true)] public static extern int recvfrom(IntPtr socketHandle, [In][Out] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [Out] byte[] socketAddress, [In][Out] ref int socketAddressSize); [DllImport("libc", SetLastError = true)] internal unsafe static extern int sendto(IntPtr socketHandle, byte* pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize); } public static readonly bool IsSupported; public static readonly bool UnixMode; public const int IPv4AddrSize = 16; public const int IPv6AddrSize = 28; public const int AF_INET = 2; public const int AF_INET6 = 10; private static readonly Dictionary<int, SocketError> NativeErrorToSocketError; static NativeSocket() { IsSupported = false; UnixMode = false; NativeErrorToSocketError = new Dictionary<int, SocketError> { { 13, SocketError.AccessDenied }, { 98, SocketError.AddressAlreadyInUse }, { 99, SocketError.AddressNotAvailable }, { 97, SocketError.AddressFamilyNotSupported }, { 11, SocketError.WouldBlock }, { 114, SocketError.AlreadyInProgress }, { 9, SocketError.OperationAborted }, { 125, SocketError.OperationAborted }, { 103, SocketError.ConnectionAborted }, { 111, SocketError.ConnectionRefused }, { 104, SocketError.ConnectionReset }, { 89, SocketError.DestinationAddressRequired }, { 14, SocketError.Fault }, { 112, SocketError.HostDown }, { 6, SocketError.HostNotFound }, { 113, SocketError.HostUnreachable }, { 115, SocketError.InProgress }, { 4, SocketError.Interrupted }, { 22, SocketError.InvalidArgument }, { 106, SocketError.IsConnected }, { 24, SocketError.TooManyOpenSockets }, { 90, SocketError.MessageSize }, { 100, SocketError.NetworkDown }, { 102, SocketError.NetworkReset }, { 101, SocketError.NetworkUnreachable }, { 23, SocketError.TooManyOpenSockets }, { 105, SocketError.NoBufferSpaceAvailable }, { 61, SocketError.NoData }, { 2, SocketError.AddressNotAvailable }, { 92, SocketError.ProtocolOption }, { 107, SocketError.NotConnected }, { 88, SocketError.NotSocket }, { 3440, SocketError.OperationNotSupported }, { 1, SocketError.AccessDenied }, { 32, SocketError.Shutdown }, { 96, SocketError.ProtocolFamilyNotSupported }, { 93, SocketError.ProtocolNotSupported }, { 91, SocketError.ProtocolType }, { 94, SocketError.SocketNotSupported }, { 108, SocketError.Disconnecting }, { 110, SocketError.TimedOut }, { 0, SocketError.Success } }; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { IsSupported = true; UnixMode = true; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { IsSupported = true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int RecvFrom(IntPtr socketHandle, byte[] pinnedBuffer, int len, byte[] socketAddress, ref int socketAddressSize) { if (!UnixMode) { return WinSock.recvfrom(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, ref socketAddressSize); } return UnixSock.recvfrom(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, ref socketAddressSize); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static int SendTo(IntPtr socketHandle, byte* pinnedBuffer, int len, byte[] socketAddress, int socketAddressSize) { if (!UnixMode) { return WinSock.sendto(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, socketAddressSize); } return UnixSock.sendto(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, socketAddressSize); } public static SocketError GetSocketError() { int lastWin32Error = Marshal.GetLastWin32Error(); if (UnixMode) { if (!NativeErrorToSocketError.TryGetValue(lastWin32Error, out var value)) { return SocketError.SocketError; } return value; } return (SocketError)lastWin32Error; } public static SocketException GetSocketException() { int lastWin32Error = Marshal.GetLastWin32Error(); if (UnixMode) { if (!NativeErrorToSocketError.TryGetValue(lastWin32Error, out var value)) { return new SocketException(-1); } return new SocketException((int)value); } return new SocketException(lastWin32Error); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short GetNativeAddressFamily(IPEndPoint remoteEndPoint) { if (!UnixMode) { return (short)remoteEndPoint.AddressFamily; } return (short)((remoteEndPoint.AddressFamily == AddressFamily.InterNetwork) ? 2 : 10); } } public enum NatAddressType { Internal, External } public interface INatPunchListener { void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); } public class EventBasedNatPunchListener : INatPunchListener { public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token); public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token); public event OnNatIntroductionRequest NatIntroductionRequest; public event OnNatIntroductionSuccess NatIntroductionSuccess; void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token) { if (this.NatIntroductionRequest != null) { this.NatIntroductionRequest(localEndPoint, remoteEndPoint, token); } } void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token) { if (this.NatIntroductionSuccess != null) { this.NatIntroductionSuccess(targetEndPoint, type, token); } } } public sealed class NatPunchModule { private struct RequestEventData { public IPEndPoint LocalEndPoint; public IPEndPoint RemoteEndPoint; public string Token; } private struct SuccessEventData { public IPEndPoint TargetEndPoint; public NatAddressType Type; public string Token; } private class NatIntroduceRequestPacket { public IPEndPoint Internal { [Preserve] get; [Preserve] set; } public string Token { [Preserve] get; [Preserve] set; } } private class NatIntroduceResponsePacket { public IPEndPoint Internal { [Preserve] get; [Preserve] set; } public IPEndPoint External { [Preserve] get; [Preserve] set; } public string Token { [Preserve] get; [Preserve] set; } } private class NatPunchPacket { public string Token { [Preserve] get; [Preserve] set; } public bool IsExternal { [Preserve] get; [Preserve] set; } } private readonly NetManager _socket; private readonly ConcurrentQueue<RequestEventData> _requestEvents = new ConcurrentQueue<RequestEventData>(); private readonly ConcurrentQueue<SuccessEventData> _successEvents = new ConcurrentQueue<SuccessEventData>(); private readonly NetDataReader _cacheReader = new NetDataReader(); private readonly NetDataWriter _cacheWriter = new NetDataWriter(); private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(256); private INatPunchListener _natPunchListener; public const int MaxTokenLength = 256; public bool UnsyncedEvents; internal NatPunchModule(NetManager socket) { _socket = socket; _netPacketProcessor.SubscribeReusable<NatIntroduceResponsePacket>(OnNatIntroductionResponse); _netPacketProcessor.SubscribeReusable<NatIntroduceRequestPacket, IPEndPoint>(OnNatIntroductionRequest); _netPacketProcessor.SubscribeReusable<NatPunchPacket, IPEndPoint>(OnNatPunch); } internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet) { lock (_cacheReader) { _cacheReader.SetSource(packet.RawData, 1, packet.Size); _netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint); } } public void Init(INatPunchListener listener) { _natPunchListener = listener; } private void Send<T>(T packet, IPEndPoint target) where T : class, new() { _cacheWriter.Reset(); _cacheWriter.Put((byte)16); _netPacketProcessor.Write(_cacheWriter, packet); _socket.SendRaw(_cacheWriter.Data, 0, _cacheWriter.Length, target); } public void NatIntroduce(IPEndPoint hostInternal, IPEndPoint hostExternal, IPEndPoint clientInternal, IPEndPoint clientExternal, string additionalInfo) { NatIntroduceResponsePacket natIntroduceResponsePacket = new NatIntroduceResponsePacket { Token = additionalInfo }; natIntroduceResponsePacket.Internal = hostInternal; natIntroduceResponsePacket.External = hostExternal; Send(natIntroduceResponsePacket, clientExternal); natIntroduceResponsePacket.Internal = clientInternal; natIntroduceResponsePacket.External = clientExternal; Send(natIntroduceResponsePacket, hostExternal); } public void PollEvents() { if (!UnsyncedEvents && _natPunchListener != null && (!_successEvents.IsEmpty || !_requestEvents.IsEmpty)) { SuccessEventData result; while (_successEvents.TryDequeue(out result)) { _natPunchListener.OnNatIntroductionSuccess(result.TargetEndPoint, result.Type, result.Token); } RequestEventData result2; while (_requestEvents.TryDequeue(out result2)) { _natPunchListener.OnNatIntroductionRequest(result2.LocalEndPoint, result2.RemoteEndPoint, result2.Token); } } } public void SendNatIntroduceRequest(string host, int port, string additionalInfo) { SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo); } public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo) { string localIp = NetUtils.GetLocalIp(LocalAddrType.IPv4); if (string.IsNullOrEmpty(localIp) || masterServerEndPoint.AddressFamily == AddressFamily.InterNetworkV6) { localIp = NetUtils.GetLocalIp(LocalAddrType.IPv6); } Send(new NatIntroduceRequestPacket { Internal = NetUtils.MakeEndPoint(localIp, _socket.LocalPort), Token = additionalInfo }, masterServerEndPoint); } private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint) { if (UnsyncedEvents) { _natPunchListener.OnNatIntroductionRequest(req.Internal, senderEndPoint, req.Token); return; } _requestEvents.Enqueue(new RequestEventData { LocalEndPoint = req.Internal, RemoteEndPoint = senderEndPoint, Token = req.Token }); } private void OnNatIntroductionResponse(NatIntroduceResponsePacket req) { NatPunchPacket natPunchPacket = new NatPunchPacket { Token = req.Token }; Send(natPunchPacket, req.Internal); _socket.Ttl = 2; _socket.SendRaw(new byte[1] { 17 }, 0, 1, req.External); _socket.Ttl = 255; natPunchPacket.IsExternal = true; Send(natPunchPacket, req.External); } private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint) { if (UnsyncedEvents) { _natPunchListener.OnNatIntroductionSuccess(senderEndPoint, req.IsExternal ? NatAddressType.External : NatAddressType.Internal, req.Token); return; } _successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Type = (req.IsExternal ? NatAddressType.External : NatAddressType.Internal), Token = req.Token }); } } public enum DeliveryMethod : byte { Unreliable = 4, ReliableUnordered = 0, Sequenced = 1, ReliableOrdered = 2, ReliableSequenced = 3 } public static class NetConstants { public const int DefaultWindowSize = 64; public const int SocketBufferSize = 1048576; public const int SocketTTL = 255; public const int HeaderSize = 1; public const int ChanneledHeaderSize = 4; public const int FragmentHeaderSize = 6; public const int FragmentedHeaderTotalSize = 10; public const ushort MaxSequence = 32768; public const ushort HalfMaxSequence = 16384; internal const int ProtocolId = 13; internal const int MaxUdpHeaderSize = 68; internal const int ChannelTypeCount = 4; internal static readonly int[] PossibleMtu = new int[6] { 1024, 1164, 1392, 1404, 1424, 1432 }; public static readonly int InitialMtu = PossibleMtu[0]; public static readonly int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1]; public static readonly int MaxUnreliableDataSize = MaxPacketSize - 1; public const byte MaxConnectionNumber = 4; } public class InvalidPacketException : ArgumentException { public InvalidPacketException(string message) : base(message) { } } public class TooBigPacketException : InvalidPacketException { public TooBigPacketException(string message) : base(message) { } } public enum NetLogLevel { Warning, Error, Trace, Info } public interface INetLogger { void WriteNet(NetLogLevel level, string str, params object[] args); } public static class NetDebug { public static INetLogger Logger = null; private static readonly object DebugLogLock = new object(); private static void WriteLogic(NetLogLevel logLevel, string str, params object[] args) { lock (DebugLogLock) { if (Logger == null) { Console.WriteLine(str, args); } else { Logger.WriteNet(logLevel, str, args); } } } [Conditional("DEBUG_MESSAGES")] internal static void Write(string str) { WriteLogic(NetLogLevel.Trace, str); } [Conditional("DEBUG_MESSAGES")] internal static void Write(NetLogLevel level, string str) { WriteLogic(level, str); } [Conditional("DEBUG_MESSAGES")] [Conditional("DEBUG")] internal static void WriteForce(string str) { WriteLogic(NetLogLevel.Trace, str); } [Conditional("DEBUG_MESSAGES")] [Conditional("DEBUG")] internal static void WriteForce(NetLogLevel level, string str) { WriteLogic(level, str); } internal static void WriteError(string str) { WriteLogic(NetLogLevel.Error, str); } } public sealed class NetPacketReader : NetDataReader { private NetPacket _packet; private readonly NetManager _manager; private readonly NetEvent _evt; internal NetPacketReader(NetManager manager, NetEvent evt) { _manager = manager; _evt = evt; } internal void SetSource(NetPacket packet, int headerSize) { if (packet != null) { _packet = packet; SetSource(packet.RawData, headerSize, packet.Size); } } internal void RecycleInternal() { Clear(); if (_packet != null) { _manager.PoolRecycle(_packet); } _packet = null; _manager.RecycleEvent(_evt); } public void Recycle() { if (!_manager.AutoRecycle) { RecycleInternal(); } } } internal sealed class NetEvent { public enum EType { Connect, Disconnect, Receive, ReceiveUnconnected, Error, ConnectionLatencyUpdated, Broadcast, ConnectionRequest, MessageDelivered, PeerAddressChanged } public NetEvent Next; public EType Type; public NetPeer Peer; public IPEndPoint RemoteEndPoint; public object UserData; public int Latency; public SocketError ErrorCode; public DisconnectReason DisconnectReason; public ConnectionRequest ConnectionRequest; public DeliveryMethod DeliveryMethod; public byte ChannelNumber; public readonly NetPacketReader DataReader; public NetEvent(NetManager manager) { DataReader = new NetPacketReader(manager, this); } } public class NetManager : IEnumerable<NetPeer>, IEnumerable { public struct NetPeerEnumerator : IEnumerator<NetPeer>, IEnumerator, IDisposable { private readonly NetPeer _initialPeer; private NetPeer _p; public NetPeer Current => _p; object IEnumerator.Current => _p; public NetPeerEnumerator(NetPeer p) { _initialPeer = p; _p = null; } public void Dispose() { } public bool MoveNext() { _p = ((_p == null) ? _initialPeer : _p.NextPeer); return _p != null; } public void Reset() { throw new NotSupportedException(); } } private struct IncomingData { public NetPacket Data; public IPEndPoint EndPoint; public DateTime TimeWhenGet; } private struct Slot { internal int HashCode; internal int Next; internal NetPeer Value; } private readonly List<IncomingData> _pingSimulationList = new List<IncomingData>(); private readonly Random _randomGenerator = new Random(); private const int MinLatencyThreshold = 5; private Thread _logicThread; private bool _manualMode; private readonly AutoResetEvent _updateTriggerEvent = new AutoResetEvent(initialState: true); private NetEvent _pendingEventHead; private NetEvent _pendingEventTail; private NetEvent _netEventPoolHead; private readonly INetEventListener _netEventListener; private readonly IDeliveryEventListener _deliveryEventListener; private readonly INtpEventListener _ntpEventListener; private readonly IPeerAddressChangedListener _peerAddressChangedListener; private readonly Dictionary<IPEndPoint, ConnectionRequest> _requestsDict = new Dictionary<IPEndPoint, ConnectionRequest>(); private readonly ConcurrentDictionary<IPEndPoint, NtpRequest> _ntpRequests = new ConcurrentDictionary<IPEndPoint, NtpRequest>(); private long _connectedPeersCount; private readonly List<NetPeer> _connectedPeerListCache = new List<NetPeer>(); private readonly PacketLayerBase _extraPacketLayer; private int _lastPeerId; private ConcurrentQueue<int> _peerIds = new ConcurrentQueue<int>(); private byte _channelsCount = 1; private readonly object _eventLock = new object(); private bool _dropPacket; public bool UnconnectedMessagesEnabled; public bool NatPunchEnabled; public int UpdateTime = 15; public int PingInterval = 1000; public int DisconnectTimeout = 5000; public bool SimulatePacketLoss; public bool SimulateLatency; public int SimulationPacketLossChance = 10; public int SimulationMinLatency = 30; public int SimulationMaxLatency = 100; public bool UnsyncedEvents; public bool UnsyncedReceiveEvent; public bool UnsyncedDeliveryEvent; public bool BroadcastReceiveEnabled; public int ReconnectDelay = 500; public int MaxConnectAttempts = 10; public bool ReuseAddress; public bool DontRoute; public readonly NetStatistics Statistics = new NetStatistics(); public bool EnableStatistics; public readonly NatPunchModule NatPunchModule; public bool AutoRecycle; public bool IPv6Enabled = true; public int MtuOverride; public bool MtuDiscovery; public bool UseNativeSockets; public bool DisconnectOnUnreachable; public bool AllowPeerAddressChange; private const int MaxPrimeArrayLength = 2147483587; private const int HashPrime = 101; private const int Lower31BitMask = int.MaxValue; private static readonly int[] Primes; private int[] _buckets; private Slot[] _slots; private int _count; private int _lastIndex; private int _freeList = -1; private NetPeer[] _peersArray = new NetPeer[32]; private readonly ReaderWriterLockSlim _peersLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private volatile NetPeer _headPeer; private NetPacket _poolHead; private int _poolCount; private readonly object _poolLock = new object(); public int PacketPoolSize = 1000; private const int ReceivePollingTime = 500000; private Socket _udpSocketv4; private Socket _udpSocketv6; private Thread _receiveThread; private IPEndPoint _bufferEndPointv4; private IPEndPoint _bufferEndPointv6; private const int SioUdpConnreset = -1744830452; private static readonly IPAddress MulticastAddressV6; public static readonly bool IPv6Support; internal bool NotConnected; public bool IsRunning { get; private set; } public int LocalPort { get; private set; } public NetPeer FirstPeer => _headPeer; public byte ChannelsCount { get { return _channelsCount; } set { if (value < 1 || value > 64) { throw new ArgumentException("Channels count must be between 1 and 64"); } _channelsCount = value; } } public List<NetPeer> ConnectedPeerList { get { GetPeersNonAlloc(_connectedPeerListCache, ConnectionState.Connected); return _connectedPeerListCache; } } public int ConnectedPeersCount => (int)Interlocked.Read(ref _connectedPeersCount); public int ExtraPacketSizeForLayer => _extraPacketLayer?.ExtraPacketSizeForLayer ?? 0; public int PoolCount => _poolCount; public short Ttl { get { return _udpSocketv4.Ttl; } internal set { _udpSocketv4.Ttl = value; } } public NetManager(INetEventListener listener, PacketLayerBase extraPacketLayer = null) { _netEventListener = listener; _deliveryEventListener = listener as IDeliveryEventListener; _ntpEventListener = listener as INtpEventListener; _peerAddressChangedListener = listener as IPeerAddressChangedListener; NatPunchModule = new NatPunchModule(this); _extraPacketLayer = extraPacketLayer; } internal void ConnectionLatencyUpdated(NetPeer fromPeer, int latency) { CreateEvent(NetEvent.EType.ConnectionLatencyUpdated, fromPeer, null, SocketError.Success, latency, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); } internal void MessageDelivered(NetPeer fromPeer, object userData) { if (_deliveryEventListener != null) { CreateEvent(NetEvent.EType.MessageDelivered, fromPeer, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, null, userData); } } internal void DisconnectPeerForce(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, NetPacket eventData) { DisconnectPeer(peer, reason, socketErrorCode, force: true, null, 0, 0, eventData); } private void DisconnectPeer(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, bool force, byte[] data, int start, int count, NetPacket eventData) { switch (peer.Shutdown(data, start, count, force)) { case ShutdownResult.None: return; case ShutdownResult.WasConnected: Interlocked.Decrement(ref _connectedPeersCount); break; } CreateEvent(NetEvent.EType.Disconnect, peer, null, socketErrorCode, 0, reason, null, DeliveryMethod.Unreliable, 0, eventData); } private void CreateEvent(NetEvent.EType type, NetPeer peer = null, IPEndPoint remoteEndPoint = null, SocketError errorCode = SocketError.Success, int latency = 0, DisconnectReason disconnectReason = DisconnectReason.ConnectionFailed, ConnectionRequest connectionRequest = null, DeliveryMethod deliveryMethod = DeliveryMethod.Unreliable, byte channelNumber = 0, NetPacket readerSource = null, object userData = null) { bool flag = UnsyncedEvents; switch (type) { case NetEvent.EType.Connect: Interlocked.Increment(ref _connectedPeersCount); break; case NetEvent.EType.MessageDelivered: flag = UnsyncedDeliveryEvent; break; } NetEvent netEvent; lock (_eventLock) { netEvent = _netEventPoolHead; if (netEvent == null) { netEvent = new NetEvent(this); } else { _netEventPoolHead = netEvent.Next; } } netEvent.Next = null; netEvent.Type = type; netEvent.DataReader.SetSource(readerSource, readerSource?.GetHeaderSize() ?? 0); netEvent.Peer = peer; netEvent.RemoteEndPoint = remoteEndPoint; netEvent.Latency = latency; netEvent.ErrorCode = errorCode; netEvent.DisconnectReason = disconnectReason; netEvent.ConnectionRequest = connectionRequest; netEvent.DeliveryMethod = deliveryMethod; netEvent.ChannelNumber = channelNumber; netEvent.UserData = userData; if (flag || _manualMode) { ProcessEvent(netEvent); return; } lock (_eventLock) { if (_pendingEventTail == null) { _pendingEventHead = netEvent; } else { _pendingEventTail.Next = netEvent; } _pendingEventTail = netEvent; } } private void ProcessEvent(NetEvent evt) { bool isNull = evt.DataReader.IsNull; switch (evt.Type) { case NetEvent.EType.Connect: _netEventListener.OnPeerConnected(evt.Peer); break; case NetEvent.EType.Disconnect: { DisconnectInfo disconnectInfo = default(DisconnectInfo); disconnectInfo.Reason = evt.DisconnectReason; disconnectInfo.AdditionalData = evt.DataReader; disconnectInfo.SocketErrorCode = evt.ErrorCode; DisconnectInfo disconnectInfo2 = disconnectInfo; _netEventListener.OnPeerDisconnected(evt.Peer, disconnectInfo2); break; } case NetEvent.EType.Receive: _netEventListener.OnNetworkReceive(evt.Peer, evt.DataReader, evt.ChannelNumber, evt.DeliveryMethod); break; case NetEvent.EType.ReceiveUnconnected: _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.BasicMessage); break; case NetEvent.EType.Broadcast: _netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.Broadcast); break; case NetEvent.EType.Error: _netEventListener.OnNetworkError(evt.RemoteEndPoint, evt.ErrorCode); break; case NetEvent.EType.ConnectionLatencyUpdated: _netEventListener.OnNetworkLatencyUpdate(evt.Peer, evt.Latency); break; case NetEvent.EType.ConnectionRequest: _netEventListener.OnConnectionRequest(evt.ConnectionRequest); break; case NetEvent.EType.MessageDelivered: _deliveryEventListener.OnMessageDelivered(evt.Peer, evt.UserData); break; case NetEvent.EType.PeerAddressChanged: { _peersLock.EnterUpgradeableReadLock(); IPEndPoint iPEndPoint = null; if (ContainsPeer(evt.Peer)) { _peersLock.EnterWriteLock(); RemovePeerFromSet(evt.Peer); iPEndPoint = new IPEndPoint(evt.Peer.Address, evt.Peer.Port); evt.Peer.FinishEndPointChange(evt.RemoteEndPoint); AddPeerToSet(evt.Peer); _peersLock.ExitWriteLock(); } _peersLock.ExitUpgradeableReadLock(); if (iPEndPoint != null && _peerAddressChangedListener != null) { _peerAddressChangedListener.OnPeerAddressChanged(evt.Peer, iPEndPoint); } break; } } if (isNull) { RecycleEvent(evt); } else if (AutoRecycle) { evt.DataReader.RecycleInternal(); } } internal void RecycleEvent(NetEvent evt) { evt.Peer = null; evt.ErrorCode = SocketError.Success; evt.RemoteEndPoint = null; evt.ConnectionRequest = null; lock (_eventLock) { evt.Next = _netEventPoolHead; _netEventPoolHead = evt; } } private void UpdateLogic() { List<NetPeer> list = new List<NetPeer>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); while (IsRunning) { try { float num = (float)((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0); num = ((num <= 0f) ? 0.001f : num); stopwatch.Restart(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > (float)DisconnectTimeout) { list.Add(netPeer); } else { netPeer.Update(num); } } if (list.Count > 0) { _peersLock.EnterWriteLock(); for (int i = 0; i < list.Count; i++) { RemovePeer(list[i], enableWriteLock: false); } _peersLock.ExitWriteLock(); list.Clear(); } ProcessNtpRequests(num); int num2 = UpdateTime - (int)stopwatch.ElapsedMilliseconds; if (num2 > 0) { _updateTriggerEvent.WaitOne(num2); } } catch (ThreadAbortException) { return; } catch (Exception ex2) { NetDebug.WriteError("[NM] LogicThread error: " + ex2); } } stopwatch.Stop(); } [Conditional("DEBUG")] private void ProcessDelayedPackets() { if (!SimulateLatency) { return; } DateTime utcNow = DateTime.UtcNow; lock (_pingSimulationList) { for (int i = 0; i < _pingSimulationList.Count; i++) { IncomingData incomingData = _pingSimulationList[i]; if (incomingData.TimeWhenGet <= utcNow) { HandleMessageReceived(incomingData.Data, incomingData.EndPoint); _pingSimulationList.RemoveAt(i); i--; } } } } private void ProcessNtpRequests(float elapsedMilliseconds) { List<IPEndPoint> list = null; foreach (KeyValuePair<IPEndPoint, NtpRequest> ntpRequest in _ntpRequests) { ntpRequest.Value.Send(_udpSocketv4, elapsedMilliseconds); if (ntpRequest.Value.NeedToKill) { if (list == null) { list = new List<IPEndPoint>(); } list.Add(ntpRequest.Key); } } if (list == null) { return; } foreach (IPEndPoint item in list) { _ntpRequests.TryRemove(item, out var _); } } public void ManualUpdate(float elapsedMilliseconds) { if (!_manualMode) { return; } for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > (float)DisconnectTimeout) { RemovePeer(netPeer, enableWriteLock: false); } else { netPeer.Update(elapsedMilliseconds); } } ProcessNtpRequests(elapsedMilliseconds); } internal NetPeer OnConnectionSolved(ConnectionRequest request, byte[] rejectData, int start, int length) { NetPeer actualValue = null; if (request.Result == ConnectionRequestResult.RejectForce) { if (rejectData != null && length > 0) { NetPacket netPacket = PoolGetWithProperty(PacketProperty.Disconnect, length); netPacket.ConnectionNumber = request.InternalPacket.ConnectionNumber; FastBitConverter.GetBytes(netPacket.RawData, 1, request.InternalPacket.ConnectionTime); if (netPacket.Size >= NetConstants.PossibleMtu[0]) { NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU!"); } else { Buffer.BlockCopy(rejectData, start, netPacket.RawData, 9, length); } SendRawAndRecycle(netPacket, request.RemoteEndPoint); } lock (_requestsDict) { _requestsDict.Remove(request.RemoteEndPoint); } } else { lock (_requestsDict) { if (!TryGetPeer(request.RemoteEndPoint, out actualValue)) { if (request.Result == ConnectionRequestResult.Reject) { actualValue = new NetPeer(this, request.RemoteEndPoint, GetNextPeerId()); actualValue.Reject(request.InternalPacket, rejectData, start, length); AddPeer(actualValue); } else { actualValue = new NetPeer(this, request, GetNextPeerId()); AddPeer(actualValue); CreateEvent(NetEvent.EType.Connect, actualValue, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); } } _requestsDict.Remove(request.RemoteEndPoint); } } return actualValue; } private int GetNextPeerId() { if (!_peerIds.TryDequeue(out var result)) { return _lastPeerId++; } return result; } private void ProcessConnectRequest(IPEndPoint remoteEndPoint, NetPeer netPeer, NetConnectRequestPacket connRequest) { if (netPeer != null) { ConnectRequestResult connectRequestResult = netPeer.ProcessConnectRequest(connRequest); switch (connectRequestResult) { default: return; case ConnectRequestResult.Reconnection: DisconnectPeerForce(netPeer, DisconnectReason.Reconnect, SocketError.Success, null); RemovePeer(netPeer, enableWriteLock: true); break; case ConnectRequestResult.NewConnection: RemovePeer(netPeer, enableWriteLock: true); break; case ConnectRequestResult.P2PLose: DisconnectPeerForce(netPeer, DisconnectReason.PeerToPeerConnection, SocketError.Success, null); RemovePeer(netPeer, enableWriteLock: true); break; } if (connectRequestResult != ConnectRequestResult.P2PLose) { connRequest.ConnectionNumber = (byte)((netPeer.ConnectionNum + 1) % 4); } } ConnectionRequest value; lock (_requestsDict) { if (_requestsDict.TryGetValue(remoteEndPoint, out value)) { value.UpdateRequest(connRequest); return; } value = new ConnectionRequest(remoteEndPoint, connRequest, this); _requestsDict.Add(remoteEndPoint, value); } CreateEvent(NetEvent.EType.ConnectionRequest, null, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, value, DeliveryMethod.Unreliable, 0); } private void OnMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint) { if (packet.Size == 0) { PoolRecycle(packet); return; } _dropPacket = false; if (!_dropPacket) { HandleMessageReceived(packet, remoteEndPoint); } } [Conditional("DEBUG")] private void HandleSimulateLatency(NetPacket packet, IPEndPoint remoteEndPoint) { if (!SimulateLatency) { return; } int num = _randomGenerator.Next(SimulationMinLatency, SimulationMaxLatency); if (num > 5) { lock (_pingSimulationList) { _pingSimulationList.Add(new IncomingData { Data = packet, EndPoint = remoteEndPoint, TimeWhenGet = DateTime.UtcNow.AddMilliseconds(num) }); } _dropPacket = true; } } [Conditional("DEBUG")] private void HandleSimulatePacketLoss() { if (SimulatePacketLoss && _randomGenerator.NextDouble() * 100.0 < (double)SimulationPacketLossChance) { _dropPacket = true; } } private void HandleMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint) { int size = packet.Size; if (EnableStatistics) { Statistics.IncrementPacketsReceived(); Statistics.AddBytesReceived(size); } if (_ntpRequests.Count > 0 && _ntpRequests.TryGetValue(remoteEndPoint, out var _)) { if (packet.Size >= 48) { byte[] array = new byte[packet.Size]; Buffer.BlockCopy(packet.RawData, 0, array, 0, packet.Size); NtpPacket ntpPacket = NtpPacket.FromServerResponse(array, DateTime.UtcNow); try { ntpPacket.ValidateReply(); } catch (InvalidOperationException) { ntpPacket = null; } if (ntpPacket != null) { _ntpRequests.TryRemove(remoteEndPoint, out var _); _ntpEventListener?.OnNtpResponse(ntpPacket); } } return; } if (_extraPacketLayer != null) { _extraPacketLayer.ProcessInboundPacket(ref remoteEndPoint, ref packet.RawData, ref packet.Size); if (packet.Size == 0) { return; } } if (!packet.Verify()) { NetDebug.WriteError("[NM] DataReceived: bad!"); PoolRecycle(packet); return; } switch (packet.Property) { case PacketProperty.ConnectRequest: if (NetConnectRequestPacket.GetProtocolId(packet) != 13) { SendRawAndRecycle(PoolGetWithProperty(PacketProperty.InvalidProtocol), remoteEndPoint); return; } break; case PacketProperty.Broadcast: if (BroadcastReceiveEnabled) { CreateEvent(NetEvent.EType.Broadcast, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, packet); } return; case PacketProperty.UnconnectedMessage: if (UnconnectedMessagesEnabled) { CreateEvent(NetEvent.EType.ReceiveUnconnected, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, packet); } return; case PacketProperty.NatMessage: if (NatPunchEnabled) { NatPunchModule.ProcessMessage(remoteEndPoint, packet); } return; } NetPeer actualValue = remoteEndPoint as NetPeer; bool flag = actualValue != null || TryGetPeer(remoteEndPoint, out actualValue); if (flag && EnableStatistics) { actualValue.Statistics.IncrementPacketsReceived(); actualValue.Statistics.AddBytesReceived(size); } switch (packet.Property) { case PacketProperty.ConnectRequest: { NetConnectRequestPacket netConnectRequestPacket = NetConnectRequestPacket.FromData(packet); if (netConnectRequestPacket != null) { ProcessConnectRequest(remoteEndPoint, actualValue, netConnectRequestPacket); } break; } case PacketProperty.PeerNotFound: if (flag) { if (actualValue.ConnectionState == ConnectionState.Connected) { if (packet.Size == 1) { actualValue.ResetMtu(); SendRaw(NetConnectAcceptPacket.MakeNetworkChanged(actualValue), remoteEndPoint); } else if (packet.Size == 2 && packet.RawData[1] == 1) { DisconnectPeerForce(actualValue, DisconnectReason.PeerNotFound, SocketError.Success, null); } } } else { if (packet.Size <= 1) { break; } bool flag2 = false; if (AllowPeerAddressChange) { NetConnectAcceptPacket netConnectAcceptPacket = NetConnectAcceptPacket.FromData(packet); if (netConnectAcceptPacket != null && netConnectAcceptPacket.PeerNetworkChanged && netConnectAcceptPacket.PeerId < _peersArray.Length) { _peersLock.EnterUpgradeableReadLock(); NetPeer netPeer = _peersArray[netConnectAcceptPacket.PeerId]; _peersLock.ExitUpgradeableReadLock(); if (netPeer != null && netPeer.ConnectTime == netConnectAcceptPacket.ConnectionTime && netPeer.ConnectionNum == netConnectAcceptPacket.ConnectionNumber) { if (netPeer.ConnectionState == ConnectionState.Connected) { netPeer.InitiateEndPointChange(); CreateEvent(NetEvent.EType.PeerAddressChanged, netPeer, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); } flag2 = true; } } } PoolRecycle(packet); if (!flag2) { NetPacket netPacket = PoolGetWithProperty(PacketProperty.PeerNotFound, 1); netPacket.RawData[1] = 1; SendRawAndRecycle(netPacket, remoteEndPoint); } } break; case PacketProperty.InvalidProtocol: if (flag && actualValue.ConnectionState == ConnectionState.Outgoing) { DisconnectPeerForce(actualValue, DisconnectReason.InvalidProtocol, SocketError.Success, null); } break; case PacketProperty.Disconnect: if (flag) { DisconnectResult disconnectResult = actualValue.ProcessDisconnect(packet); if (disconnectResult == DisconnectResult.None) { PoolRecycle(packet); break; } DisconnectPeerForce(actualValue, (disconnectResult == DisconnectResult.Disconnect) ? DisconnectReason.RemoteConnectionClose : DisconnectReason.ConnectionRejected, SocketError.Success, packet); } else { PoolRecycle(packet); } SendRawAndRecycle(PoolGetWithProperty(PacketProperty.ShutdownOk), remoteEndPoint); break; case PacketProperty.ConnectAccept: if (flag) { NetConnectAcceptPacket netConnectAcceptPacket2 = NetConnectAcceptPacket.FromData(packet); if (netConnectAcceptPacket2 != null && actualValue.ProcessConnectAccept(netConnectAcceptPacket2)) { CreateEvent(NetEvent.EType.Connect, actualValue, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); } } break; default: if (flag) { actualValue.ProcessPacket(packet); } else { SendRawAndRecycle(PoolGetWithProperty(PacketProperty.PeerNotFound), remoteEndPoint); } break; } } internal void CreateReceiveEvent(NetPacket packet, DeliveryMethod method, byte channelNumber, int headerSize, NetPeer fromPeer) { if (UnsyncedEvents || UnsyncedReceiveEvent || _manualMode) { NetEvent netEvent; lock (_eventLock) { netEvent = _netEventPoolHead; if (netEvent == null) { netEvent = new NetEvent(this); } else { _netEventPoolHead = netEvent.Next; } } netEvent.Next = null; netEvent.Type = NetEvent.EType.Receive; netEvent.DataReader.SetSource(packet, headerSize); netEvent.Peer = fromPeer; netEvent.DeliveryMethod = method; netEvent.ChannelNumber = channelNumber; ProcessEvent(netEvent); return; } lock (_eventLock) { NetEvent netEvent = _netEventPoolHead; if (netEvent == null) { netEvent = new NetEvent(this); } else { _netEventPoolHead = netEvent.Next; } netEvent.Next = null; netEvent.Type = NetEvent.EType.Receive; netEvent.DataReader.SetSource(packet, headerSize); netEvent.Peer = fromPeer; netEvent.DeliveryMethod = method; netEvent.ChannelNumber = channelNumber; if (_pendingEventTail == null) { _pendingEventHead = netEvent; } else { _pendingEventTail.Next = netEvent; } _pendingEventTail = netEvent; } } public void SendToAll(NetDataWriter writer, DeliveryMethod options) { SendToAll(writer.Data, 0, writer.Length, options); } public void SendToAll(byte[] data, DeliveryMethod options) { SendToAll(data, 0, data.Length, options); } public void SendToAll(byte[] data, int start, int length, DeliveryMethod options) { SendToAll(data, start, length, 0, options); } public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options) { SendToAll(writer.Data, 0, writer.Length, channelNumber, options); } public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options) { SendToAll(data, 0, data.Length, channelNumber, options); } public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options) { try { _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { netPeer.Send(data, start, length, channelNumber, options); } } finally { _peersLock.ExitReadLock(); } } public void SendToAll(NetDataWriter writer, DeliveryMethod options, NetPeer excludePeer) { SendToAll(writer.Data, 0, writer.Length, 0, options, excludePeer); } public void SendToAll(byte[] data, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, 0, data.Length, 0, options, excludePeer); } public void SendToAll(byte[] data, int start, int length, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, start, length, 0, options, excludePeer); } public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { SendToAll(writer.Data, 0, writer.Length, channelNumber, options, excludePeer); } public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { SendToAll(data, 0, data.Length, channelNumber, options, excludePeer); } public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) { try { _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if (netPeer != excludePeer) { netPeer.Send(data, start, length, channelNumber, options); } } } finally { _peersLock.ExitReadLock(); } } public bool Start() { return Start(0); } public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port) { return Start(addressIPv4, addressIPv6, port, manualMode: false); } public bool Start(string addressIPv4, string addressIPv6, int port) { IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4); IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6); return Start(addressIPv7, addressIPv8, port); } public bool Start(int port) { return Start(IPAddress.Any, IPAddress.IPv6Any, port); } public bool StartInManualMode(IPAddress addressIPv4, IPAddress addressIPv6, int port) { return Start(addressIPv4, addressIPv6, port, manualMode: true); } public bool StartInManualMode(string addressIPv4, string addressIPv6, int port) { IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4); IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6); return StartInManualMode(addressIPv7, addressIPv8, port); } public bool StartInManualMode(int port) { return StartInManualMode(IPAddress.Any, IPAddress.IPv6Any, port); } public bool SendUnconnectedMessage(byte[] message, IPEndPoint remoteEndPoint) { return SendUnconnectedMessage(message, 0, message.Length, remoteEndPoint); } public bool SendUnconnectedMessage(NetDataWriter writer, string address, int port) { IPEndPoint remoteEndPoint = NetUtils.MakeEndPoint(address, port); return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint); } public bool SendUnconnectedMessage(NetDataWriter writer, IPEndPoint remoteEndPoint) { return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint); } public bool SendUnconnectedMessage(byte[] message, int start, int length, IPEndPoint remoteEndPoint) { NetPacket packet = PoolGetWithData(PacketProperty.UnconnectedMessage, message, start, length); return SendRawAndRecycle(packet, remoteEndPoint) > 0; } public void TriggerUpdate() { _updateTriggerEvent.Set(); } public void PollEvents(int maxProcessedEvents = 0) { if (_manualMode) { if (_udpSocketv4 != null) { ManualReceive(_udpSocketv4, _bufferEndPointv4, maxProcessedEvents); } if (_udpSocketv6 != null && _udpSocketv6 != _udpSocketv4) { ManualReceive(_udpSocketv6, _bufferEndPointv6, maxProcessedEvents); } } else { if (UnsyncedEvents) { return; } NetEvent netEvent; lock (_eventLock) { netEvent = _pendingEventHead; _pendingEventHead = null; _pendingEventTail = null; } int num = 0; while (netEvent != null) { NetEvent next = netEvent.Next; ProcessEvent(netEvent); netEvent = next; num++; if (num == maxProcessedEvents) { break; } } } } public NetPeer Connect(string address, int port, string key) { return Connect(address, port, NetDataWriter.FromString(key)); } public NetPeer Connect(string address, int port, NetDataWriter connectionData) { IPEndPoint target; try { target = NetUtils.MakeEndPoint(address, port); } catch { CreateEvent(NetEvent.EType.Disconnect, null, null, SocketError.Success, 0, DisconnectReason.UnknownHost, null, DeliveryMethod.Unreliable, 0); return null; } return Connect(target, connectionData); } public NetPeer Connect(IPEndPoint target, string key) { return Connect(target, NetDataWriter.FromString(key)); } public NetPeer Connect(IPEndPoint target, NetDataWriter connectionData) { if (!IsRunning) { throw new InvalidOperationException("Client is not running"); } lock (_requestsDict) { if (_requestsDict.ContainsKey(target)) { return null; } byte connectNum = 0; if (TryGetPeer(target, out var actualValue)) { ConnectionState connectionState = actualValue.ConnectionState; if (connectionState == ConnectionState.Outgoing || connectionState == ConnectionState.Connected) { return actualValue; } connectNum = (byte)((actualValue.ConnectionNum + 1) % 4); RemovePeer(actualValue, enableWriteLock: true); } actualValue = new NetPeer(this, target, GetNextPeerId(), connectNum, connectionData); AddPeer(actualValue); return actualValue; } } public void Stop() { Stop(sendDisconnectMessages: true); } public void Stop(bool sendDisconnectMessages) { if (IsRunning) { for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { netPeer.Shutdown(null, 0, 0, !sendDisconnectMessages); } CloseSocket(); _updateTriggerEvent.Set(); if (!_manualMode) { _logicThread.Join(); _logicThread = null; } ClearPeerSet(); _peerIds = new ConcurrentQueue<int>(); _lastPeerId = 0; _connectedPeersCount = 0L; _pendingEventHead = null; _pendingEventTail = null; } } [Conditional("DEBUG")] private void ClearPingSimulationList() { lock (_pingSimulationList) { _pingSimulationList.Clear(); } } public int GetPeersCount(ConnectionState peerState) { int num = 0; _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if ((netPeer.ConnectionState & peerState) != 0) { num++; } } _peersLock.ExitReadLock(); return num; } public void GetPeersNonAlloc(List<NetPeer> peers, ConnectionState peerState) { peers.Clear(); _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { if ((netPeer.ConnectionState & peerState) != 0) { peers.Add(netPeer); } } _peersLock.ExitReadLock(); } public void DisconnectAll() { DisconnectAll(null, 0, 0); } public void DisconnectAll(byte[] data, int start, int count) { _peersLock.EnterReadLock(); for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) { DisconnectPeer(netPeer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, force: false, data, start, count, null); } _peersLock.ExitReadLock(); } public void DisconnectPeerForce(NetPeer peer) { DisconnectPeerForce(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, null); } public void DisconnectPeer(NetPeer peer) { DisconnectPeer(peer, null, 0, 0); } public void DisconnectPeer(NetPeer peer, byte[] data) { DisconnectPeer(peer, data, 0, data.Length); } public void DisconnectPeer(NetPeer peer, NetDataWriter writer) { DisconnectPeer(peer, writer.Data, 0, writer.Length); } public void DisconnectPeer(NetPeer peer, byte[] data, int start, int count) { DisconnectPeer(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, force: false, data, start, count, null); } public void CreateNtpRequest(IPEndPoint endPoint) { _ntpRequests.TryAdd(endPoint, new NtpRequest(endPoint)); } public void CreateNtpRequest(string ntpServerAddress, int port) { IPEndPoint iPEndPoint = NetUtils.MakeEndPoint(ntpServerAddress, port); _ntpRequests.TryAdd(iPEndPoint, new NtpRequest(iPEndPoint)); } public void CreateNtpRequest(string ntpServerAddress) { IPEndPoint iPEndPoint = NetUtils.MakeEndPoint(ntpServerAddress, 123); _ntpRequests.TryAdd(iPEndPoint, new NtpRequest(iPEndPoint)); } public NetPeerEnumerator GetEnumerator() { return new NetPeerEnumerator(_headPeer); } IEnumerator<NetPeer> IEnumerable<NetPeer>.GetEnumerator() { return new NetPeerEnumerator(_headPeer); } IEnumerator IEnumerable.GetEnumerator() { return new NetPeerEnumerator(_headPeer); } private static int HashSetGetPrime(int min) { int[] primes = Primes; foreach (int num in primes) { if (num >= min) { return num; } } for (int j = min | 1; j < int.MaxValue; j += 2) { if (IsPrime(j) && (j - 1) % 101 != 0) { return j; } } return min; static bool IsPrime(int candidate) { if (((uint)candidate & (true ? 1u : 0u)) != 0) { int num2 = (int)Math.Sqrt(candidate); for (int k = 3; k <= num2; k += 2) { if (candidate % k == 0) { return false; } } return true; } return candidate == 2; } } private void ClearPeerSet() { _peersLock.EnterWriteLock(); _headPeer = null; if (_lastIndex > 0) { Array.Clear(_slots, 0, _lastIndex); Array.Clear(_buckets, 0, _buckets.Length); _lastIndex = 0; _count = 0; _freeList = -1; } _peersArray = new NetPeer[32]; _peersLock.ExitWriteLock(); } private bool ContainsPeer(NetPeer item) { if (item == null) { NetDebug.WriteError($"Contains peer null: {item}"); return false; } if (_buckets != null) { int num = item.GetHashCode() & 0x7FFFFFFF; for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next) { if (_slots[num2].HashCode == num && _slots[num2].Value.Equals(item)) { return true; } } } return false; } public NetPeer GetPeerById(int id) { if (id < 0 || id >= _peersArray.Length) { return null; } return _peersArray[id]; } public bool TryGetPeerById(int id, out NetPeer peer) { peer = GetPeerById(id); return peer != null; } private void AddPeer(NetPeer peer) { if (peer == null) { NetDebug.WriteError($"Add peer null: {peer}"); return; } _peersLock.EnterWriteLock(); if (_headPeer != null) { peer.NextPeer = _headPeer; _headPeer.PrevPeer = peer; } _headPeer = peer; AddPeerToSet(peer); if (peer.Id >= _peersArray.Length) { int num = _peersArray.Length * 2; while (peer.Id >= num) { num *= 2; } Array.Resize(ref _peersArray, num); } _peersArray[peer.Id] = peer; _peersLock.ExitWriteLock(); } private void RemovePeer(NetPeer peer, bool enableWriteLock) { if (enableWriteLock) { _peersLock.EnterWriteLock(); } if (!RemovePeerFromSet(peer)) { if (enableWriteLock) { _peersLock.ExitWriteLock(); } return; } if (peer == _headPeer) { _headPeer = peer.NextPeer; } if (peer.PrevPeer != null) { peer.PrevPeer.NextPeer = peer.NextPeer; } if (peer.NextPeer != null) { peer.NextPeer.PrevPeer = peer.PrevPeer; } peer.PrevPeer = null; _peersArray[peer.Id] = null; _peerIds.Enqueue(peer.Id); if (enableWriteLock) { _peersLock.ExitWriteLock(); } } private bool RemovePeerFromSet(NetPeer peer) { if (_buckets == null || peer == null) { return false; } int num = peer.GetHashCode() & 0x7FFFFFFF; int num2 = num % _buckets.Length; int num3 = -1; for (int num4 = _buckets[num2] - 1; num4 >= 0; num4 = _slots[num4].Next) { if (_slots[num4].HashCode == num && _slots[num4].Value.Equals(peer)) { if (num3 < 0) { _buckets[num2] = _slots[num4].Next + 1; } else { _slots[num3].Next = _slots[num4].Next; } _slots[num4].HashCode = -1; _slots[num4].Value = null; _slots[num4].Next = _freeList; _count--; if (_count == 0) { _lastIndex = 0; _freeList = -1; } else { _freeList = num4; } return true; } num3 = num4; } return false; } private bool TryGetPeer(IPEndPoint endPoint, out NetPeer actualValue) { if (_buckets != null) { int num = endPoint.GetHashCode() & 0x7FFFFFFF; _peersLock.EnterReadLock(); for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next) { if (_slots[num2].HashCode == num && _slots[num2].Value.Equals(endPoint)) { actualValue = _slots[num2].Value; _peersLock.ExitReadLock(); return true; } } _peersLock.ExitReadLock(); } actualValue = null; return false; } private bool TryGetPeer(SocketAddress saddr, out NetPeer actualValue) { if (_buckets != null) { int num = saddr.GetHashCode() & 0x7FFFFFFF; _peersLock.EnterReadLock(); for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next) { if (_slots[num2].HashCode == num && _slots[num2].Value.Serialize().Equals(saddr)) { actualValue = _slots[num2].Value; _peersLock.ExitReadLock(); return true; } } _peersLock.ExitReadLock(); } actualValue = null; return false; } private bool AddPeerToSet(NetPeer value) { if (_buckets == null) { int num = HashSetGetPrime(0); _buckets = new int[num]; _slots = new Slot[num]; } int num2 = value.GetHashCode() & 0x7FFFFFFF; int num3 = num2 % _buckets.Length; for (int num4 = _buckets[num2 % _buckets.Length] - 1; num4 >= 0; num4 = _slots[num4].Next) { if (_slots[num4].HashCode == num2 && _slots[num4].Value.Equals(value)) { return false; } } int num5; if (_freeList >= 0) { num5 = _freeList; _freeList = _slots[num5].Next; } else { if (_lastIndex == _slots.Length) { int num6 = 2 * _count; num6 = (((uint)num6 > 2147483587u && 2147483587 > _count) ? 2147483587 : HashSetGetPrime(num6)); Slot[] array = new Slot[num6]; Array.Copy(_slots, 0, array, 0, _lastIndex); _buckets = new int[num6]; for (int i = 0; i < _lastIndex; i++) { int num7 = array[i].HashCode % num6; array[i].Next = _buckets[num7] - 1; _buckets[num7] = i + 1; } _slots = array; num3 = num2 % _buckets.Length; } num5 = _lastIndex; _lastIndex++; } _slots[num5].HashCode = num2; _slots[num5].Value = value; _slots[num5].Next = _buckets[num3] - 1; _buckets[num3] = num5 + 1; _count++; return true; } private NetPacket PoolGetWithData(PacketProperty property, byte[] data, int start, int length) { int headerSize = NetPacket.GetHeaderSize(property); NetPacket netPacket = PoolGetPacket(length + headerSize); netPacket.Property = property; Buffer.BlockCopy(data, start, netPacket.RawData, headerSize, length); return netPacket; } private NetPacket PoolGetWithProperty(PacketProperty property, int size) { NetPacket netPacket = PoolGetPacket(size + NetPacket.GetHeaderSize(property)); netPacket.Property = property; return netPacket; } private NetPacket PoolGetWithProperty(PacketProperty property) { NetPacket netPacket = PoolGetPacket(NetPacket.GetHeaderSize(property)); netPacket.Property = property; return netPacket; } internal NetPacket PoolGetPacket(int size) { if (size > NetConstants.MaxPacketSize) { return new NetPacket(size); } NetPacket poolHead; lock (_poolLock) { poolHead = _poolHead; if (poolHead == null) { return new NetPacket(size); } _poolHead = _poolHead.Next; _poolCount--; } poolHead.Size = size; if (poolHead.RawData.Length < size) { poolHead.RawData = new byte[size]; } return poolHead; } internal void PoolRecycle(NetPacket packet) { if (packet.RawData.Length > NetConstants.MaxPacketSize || _poolCount >= PacketPoolSize) { return; } packet.RawData[0] = 0; lock (_poolLock) { packet.Next = _poolHead; _poolHead = packet; _poolCount++; } } static NetManager() { Primes = new int[72] { 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 }; MulticastAddressV6 = IPAddress.Parse("ff02::1"); IPv6Support = Socket.OSSupportsIPv6; } private bool ProcessError(SocketException ex) { switch (ex.SocketErrorCode) { case SocketError.NotConnected: NotConnected = true; return true; case SocketError.OperationAborted: case SocketError.Interrupted: case SocketError.NotSocket: return true; default: NetDebug.WriteError($"[R]Error code: {(int)ex.SocketErrorCode} - {ex}"); CreateEvent(NetEvent.EType.Error, null, null, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); break; case SocketError.WouldBlock: case SocketError.MessageSize: case SocketError.NetworkReset: case SocketError.ConnectionReset: case SocketError.TimedOut: break; } return false; } private void ManualReceive(Socket socket, EndPoint bufferEndPoint, int maxReceive) { try { int num = 0; while (socket.Available > 0) { ReceiveFrom(socket, ref bufferEndPoint); num++; if (num == maxReceive) { break; } } } catch (SocketException ex) { ProcessError(ex); } catch (ObjectDisposedException) { } catch (Exception ex3) { NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex3); } } private void NativeReceiveLogic() { IntPtr handle = _udpSocketv4.Handle; IntPtr s2 = _udpSocketv6?.Handle ?? IntPtr.Zero; byte[] address2 = new byte[16]; byte[] address3 = new byte[28]; IPEndPoint tempEndPoint = new IPEndPoint(IPAddress.Any, 0); List<Socket> list = new List<Socket>(2); Socket udpSocketv = _udpSocketv4; Socket udpSocketv2 = _udpSocketv6; NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize); while (IsRunning) { try { if (udpSocketv2 == null) { if (!NativeReceiveFrom(handle, address2)) { break; } continue; } bool flag = false; if (udpSocketv.Available != 0 || list.Contains(udpSocketv)) { if (!NativeReceiveFrom(handle, address2)) { break; } flag = true; } if (udpSocketv2.Available != 0 || list.Contains(udpSocketv2)) { if (!NativeReceiveFrom(s2, address3)) { break; } flag = true; } list.Clear(); if (!flag) { list.Add(udpSocketv); list.Add(udpSocketv2); Socket.Select(list, null, null, 500000); } } catch (SocketException ex) { if (ProcessError(ex)) { break; } } catch (ObjectDisposedException) { break; } catch (ThreadAbortException) { break; } catch (Exception ex4) { NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex4); } } bool NativeReceiveFrom(IntPtr s, byte[] address) { int socketAddressSize = address.Length; packet.Size = NativeSocket.RecvFrom(s, packet.RawData, NetConstants.MaxPacketSize, address, ref socketAddressSize); if (packet.Size == 0) { return true; } if (packet.Size == -1) { return !ProcessError(new SocketException((int)NativeSocket.GetSocketError())); } short num = (short)((address[1] << 8) | address[0]); tempEndPoint.Port = (ushort)((address[2] << 8) | address[3]); if ((NativeSocket.UnixMode && num == 10) || (!NativeSocket.UnixMode && num == 23)) { uint num2 = (uint)((address[27] << 24) + (address[26] << 16) + (address[25] << 8) + address[24]); byte[] array = new byte[16]; Buffer.BlockCopy(address, 8, array, 0, 16); tempEndPoint.Address = new IPAddress(array, num2); } else { long newAddress = (uint)((address[4] & 0xFF) | ((address[5] << 8) & 0xFF00) | ((address[6] << 16) & 0xFF0000) | (address[7] << 24)); tempEndPoint.Address = new IPAddress(newAddress); } if (TryGetPeer(tempEndPoint, out var actualValue)) { OnMessageReceived(packet, actualValue); } else { OnMessageReceived(packet, tempEndPoint); tempEndPoint = new IPEndPoint(IPAddress.Any, 0); } packet = PoolGetPacket(NetConstants.MaxPacketSize); return true; } } private void ReceiveFrom(Socket s, ref EndPoint bufferEndPoint) { NetPacket netPacket = PoolGetPacket(NetConstants.MaxPacketSize); netPacket.Size = s.ReceiveFrom(netPacket.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, ref bufferEndPoint); OnMessageReceived(netPacket, (IPEndPoint)bufferEndPoint); } private void ReceiveLogic() { EndPoint bufferEndPoint = new IPEndPoint(IPAddress.Any, 0); EndPoint bufferEndPoint2 = new IPEndPoint(IPAddress.IPv6Any, 0); List<Socket> list = new List<Socket>(2); Socket udpSocketv = _udpSocketv4; Socket udpSocketv2 = _udpSocketv6; while (IsRunning) { try { if (udpSocketv2 == null) { if (udpSocketv.Available != 0 || udpSocketv.Poll(500000, SelectMode.SelectRead)) { ReceiveFrom(udpSocketv, ref bufferEndPoint); } continue; } bool flag = false; if (udpSocketv.Available != 0 || list.Contains(udpSocketv)) { ReceiveFrom(udpSocketv, ref bufferEndPoint); flag = true; } if (udpSocketv2.Available != 0 || list.Contains(udpSocketv2)) { ReceiveFrom(udpSocketv2, ref bufferEndPoint2); flag = true; } list.Clear(); if (!flag) { list.Add(udpSocketv); list.Add(udpSocketv2); Socket.Select(list, null, null, 500000); } } catch (SocketException ex) { if (ProcessError(ex)) { break; } } catch (ObjectDisposedException) { break; } catch (ThreadAbortException) { break; } catch (Exception ex4) { NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex4); } } } public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool manualMode) { if (IsRunning && !NotConnected) { return false; } NotConnected = false; _manualMode = manualMode; UseNativeSockets = UseNativeSockets && NativeSocket.IsSupported; _udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); if (!BindSocket(_udpSocketv4, new IPEndPoint(addressIPv4, port))) { return false; } LocalPort = ((IPEndPoint)_udpSocketv4.LocalEndPoint).Port; IsRunning = true; if (_manualMode) { _bufferEndPointv4 = new IPEndPoint(IPAddress.Any, 0); } if (IPv6Support && IPv6Enabled) { _udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort))) { if (_manualMode) { _bufferEndPointv6 = new IPEndPoint(IPAddress.IPv6Any, 0); } } else { _udpSocketv6 = null; } } if (!manualMode) { ThreadStart start = ReceiveLogic; if (UseNativeSockets) { start = NativeReceiveLogic; } _receiveThread = new Thread(start) { Name = $"ReceiveThread({LocalPort})", IsBackground = true }; _receiveThread.Start(); if (_logicThread == null) { _logicThread = new Thread(UpdateLogic) { Name = "LogicThread", IsBackground = true }; _logicThread.Start(); } } return true; } private bool BindSocket(Socket socket, IPEndPoint ep) { socket.ReceiveTimeout = 500; socket.SendTimeout = 500; socket.ReceiveBufferSize = 1048576; socket.SendBufferSize = 1048576; socket.Blocking = true; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { try { socket.IOControl(-1744830452, new byte[1], null); } catch { } } try { socket.ExclusiveAddressUse = !ReuseAddress; socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, ReuseAddress); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, DontRoute); } catch { } if (ep.AddressFamily == AddressFamily.InterNetwork) { Ttl = 255; try { socket.EnableBroadcast = true; } catch (SocketException ex) { NetDebug.WriteError($"[B]Broadcast error: {ex.SocketErrorCode}"); } if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { try { socket.DontFragment = true; } catch (SocketException ex2) { NetDebug.WriteError($"[B]DontFragment error: {ex2.SocketErrorCode}"); } } } try { socket.Bind(ep); if (ep.AddressFamily == AddressFamily.InterNetworkV6) { try { socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(MulticastAddressV6)); } catch (Exception) { } } } catch (SocketException ex4) { switch (ex4.SocketErrorCode) { case SocketError.AddressAlreadyInUse: if (socket.AddressFamily == AddressFamily.InterNetworkV6) { try { socket.DualMode = false; socket.Bind(ep); } catch (SocketException ex5) { NetDebug.WriteError($"[B]Bind exception: {ex5}, errorCode: {ex5.SocketErrorCode}"); return false; } return true; } break; case SocketError.AddressFamilyNotSupported: return true; } NetDebug.WriteError($"[B]Bind exception: {ex4}, errorCode: {ex4.SocketErrorCode}"); return false; } return true; } internal int SendRawAndRecycle(NetPacket packet, IPEndPoint remoteEndPoint) { int result = SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint); PoolRecycle(packet); return result; } internal int SendRaw(NetPacket packet, IPEndPoint remoteEndPoint) { return SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint); } internal unsafe int SendRaw(byte[] message, int start, int length, IPEndPoint remoteEndPoint) { if (!IsRunning) { return 0; } NetPacket netPacket = null; if (_extraPacketLayer != null) { netPacket = PoolGetPacket(length + _extraPacketLayer.ExtraPacketSizeForLayer); Buffer.BlockCopy(message, start, netPacket.RawData, 0, length); start = 0; _extraPacketLayer.ProcessOutBoundPacket(ref remoteEndPoint, ref netPacket.RawData, ref start, ref length); message = netPacket.RawData; } Socket socket = _udpSocketv4; if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support) { socket = _udpSocketv6; if (socket == null) { return 0; } } int num; try { if (UseNativeSockets && remoteEndPoint is NetPeer netPeer) { fixed (byte* pinnedBuffer = &message[start]) { num = NativeSocket.SendTo(socket.Handle, pinnedBuffer, length, netPeer.NativeAddress, netPeer.NativeAddress.Length); } if (num == -1) { throw NativeSocket.GetSocketException(); } } else { num = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint); } } catch (SocketException ex) { switch (ex.SocketErrorCode) { case SocketError.Interrupted: case SocketError.NoBufferSpaceAvailable: return 0; case SocketError.MessageSize: return 0; case SocketError.NetworkUnreachable: case SocketError.HostUnreachable: if (DisconnectOnUnreachable && remoteEndPoint is NetPeer peer) { DisconnectPeerForce(peer, (ex.SocketErrorCode == SocketError.HostUnreachable) ? DisconnectReason.HostUnreachable : DisconnectReason.NetworkUnreachable, ex.SocketErrorCode, null); } CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); return -1; case SocketError.Shutdown: CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0); return -1; default: NetDebug.WriteError($"[S] {ex}"); return -1; } } catch (Exception arg) { NetDebug.WriteError($"[S] {arg}"); return 0; } finally { if (netPacket != null) { PoolRecycle(netPacket); } } if (num <= 0) { return 0; } if (EnableStatistics) { Statistics.IncrementPacketsSent(); Statistics.AddBytesSent(length); } return num; } public bool SendBroadcast(NetDataWriter writer, int port) { return SendBroadcast(writer.Data, 0, writer.Length, port); } public bool SendBroadcast(byte[] data, int port) { return SendBroadcast(data, 0, data.Length, port); } public bool SendBroadcast(byte[] data, int start, int length, int port) { if (!IsRunning) { return false; } NetPacket netPacket; if (_extraPacketLayer != null) { int headerSize = NetPacket.GetHeaderSize(PacketProperty.Broadcast); netPacket = PoolGetPacket(headerSize + length + _extraPacketLayer.ExtraPacketSizeForLayer); netPacket.Property = PacketProperty.Broadcast; Buffer.BlockCopy(data, start, netPacket.RawData, headerSize, length); int offset = 0; int length2 = length + headerSize; IPEndPoint endPoint = null; _extraPacketLayer.ProcessOutBoundPacket(ref endPoint, ref netPacket.RawData, ref offset, ref length2); } else { netPacket = PoolGetWithData(PacketProperty.Broadcast, data, start, length); } bool flag = false; bool flag2 = false; try { flag = _udpSocketv4.SendTo(netPacket.RawData, 0, netPacket.Size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port)) > 0; if (_udpSocketv6 != null) { flag2 = _udpSocketv6.SendTo(netPacket.RawData, 0, netPacket.Size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port)) > 0; } } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.HostUnreachable) { return flag; } NetDebug.WriteError($"[S][MCAST] {ex}"); return flag; } catch (Exception arg) { NetDebug.WriteError($"[S][MCAST] {arg}"); return flag; } finally { PoolRecycle(netPacket); } return flag || flag2; } private void CloseSocket() { IsRunning = false; _udpSocketv4?.Close(); _udpSocketv6?.Close(); _udpSocketv4 = null; _udpSocketv6 = null; if (_receiveThread != null && _receiveThread != Thread.CurrentThread) { _receiveThread.Join(); } _receiveThread = null; } } internal enum PacketProperty : byte { Unreliable, Channeled, Ack, Ping, Pong, ConnectRequest, ConnectAccept, Disconnect, UnconnectedMessage, MtuCheck, MtuOk, Broadcast, Merged, ShutdownOk, PeerNotFound, InvalidProtocol, NatMessage, Empty } internal sealed class NetPacket { private static readonly int PropertiesCount; private static readonly int[] HeaderSizes; public byte[] RawData; public int Size; public object UserData; public NetPacket Next; public PacketProperty Property { get { return (PacketProperty)(RawData[0] & 0x1Fu); } set { RawData[0] = (byte)((RawData[0] & 0xE0u) | (uint)value); } } public byte ConnectionNumber { get { return (byte)((RawData[0] & 0x60) >> 5); } set { RawData[0] = (byte)((RawData[0] & 0x9Fu) | (uint)(value << 5)); } } public ushort Sequence { get { return BitConverter.ToUInt16(RawData, 1); } set { FastBitConverter.GetBytes(RawData, 1, value); } } public bool IsFragmented => (RawData[0] & 0x80) != 0; public byte ChannelId { get { return RawData[3]; } set { RawData[3] = value; } } public ushort FragmentId { get { return BitConverter.ToUInt16(RawData, 4); } set { FastBitConverter.GetBytes(RawData, 4, value); } } public ushort FragmentPart { get { return BitConverter.ToUInt16(RawData, 6); } set { FastBitConverter.GetBytes(RawData, 6, value); } } public ushort FragmentsTotal { get { return BitConverter.ToUInt16(RawData, 8); } set { FastBitConverter.GetBytes(RawData, 8, value); } } static NetPacket() { PropertiesCount = Enum.GetValues(typeof(PacketProperty)).Length; HeaderSizes = NetUtils.AllocatePinnedUninitializedArray<int>(PropertiesCount); for (int i = 0; i < HeaderSizes.Length; i++) { switch ((PacketProperty)(byte)i) { case PacketProperty.Channeled: case PacketProperty.Ack: HeaderSizes[i] = 4; break; case PacketProperty.Ping: HeaderSizes[i] = 3; break; case PacketProperty.ConnectRequest: HeaderSizes[i] = 18; break; case PacketProperty.ConnectAccept: HeaderSizes[i] = 15; break; case PacketProperty.Disconnect: HeaderSizes[i] = 9; break; case PacketProperty.Pong: HeaderSizes[i] = 11; break; default: HeaderSizes[i] = 1; break; } } } public void MarkFragmented() { RawData[0] |= 128; } public NetPacket(int size) { RawData = new byte[size]; Size = size; } public NetPacket(PacketProperty property, int size) { size += GetHeaderSize(property); RawData = new byte[size]; Property = property; Size = size; } public static int GetHeaderSize(PacketProperty property) { return HeaderSizes[(uint)property]; } public int GetHeaderSize() { return HeaderSizes[RawData[0] & 0x1F]; } public bool Verify() { byte b = (byte)(RawData[0] & 0x1Fu); if (b >= PropertiesCount) { return false; } int num = HeaderSizes[b]; bool flag = (RawData[0] & 0x80) != 0; if (Size >= num) { if (flag) { return Size >= num + 6; } return true; } return false; } } [Flags] public enum ConnectionState : byte { Outgoing = 2, Connected = 4, ShutdownRequested = 8, Disconnected = 0x10, EndPointChange = 0x20, Any = 0x2E } internal enum ConnectRequestResult { None, P2PLose, Reconnection, NewConnection } internal enum DisconnectResult { None, Reject, Disconnect } internal enum ShutdownResult { None, Success, WasConnected } public class NetPeer : IPEndPoint { private class IncomingFragments { public NetPacket[] Fragments; public int ReceivedCount; public int TotalSize; public byte ChannelId; } private int _rtt; private int _avgRtt; private int _rttCount; private double _resendDelay = 27.0; private float _pingSendTimer; private float _rttResetTimer; private readonly Stopwatch _pingTimer = new Stopwatch(); private volatile float _timeSinceLastPacket; private long _remoteDelta; private readonly object _shutdownLock = new object(); internal volatile NetPeer NextPeer; internal NetPeer PrevPeer; private NetPacket[] _unreliableSecondQueue; private NetPacket[] _unreliableChannel; private int _unreliablePendingCount; private readonly object _unreliableChannelLock = new object(); private readonly ConcurrentQueue<BaseChannel> _channelSendQueue; private readonly BaseChannel[] _channels; private int _mtu; private int _mtuIdx; private bool _finishMtu; private float _mtuCheckTimer; private int _mtuCheckAttempts; private const int MtuCheckDelay = 1000; private const int MaxMtuCheckAttempts = 4; private readonly object _mtuMutex = new object(); private int _fragmentId; private readonly Dictionary<ushort, IncomingFragments> _holdedFragments; private readonly Dictionary<ushort, ushort> _deliveredFragments; private readonly NetPacket _mergeData; private int _mergePos; private int _mergeCount; private int _connectAttempts; private float _connectTimer; private long _connectTime; private byte _connectNum; private ConnectionState _connectionState; private NetPacket _shutdownPacket; private const int ShutdownDelay = 300; private float _shutdownTimer; private readonly NetPacket _pingPacket; private readonly NetPacket _pongPacket; private readonly NetPacket _connectRequestPacket; private readonly NetPacket _connectAcceptPacket; public readonly NetManager NetManager; public readonly int Id; public object Tag; public readonly NetStatistics Statistics; private SocketAddress _cachedSocketAddr; private int _cachedHashCode; internal byte[] NativeAddress; internal byte ConnectionNum { get { return _connectNum; } private set { _connectNum = value; _mergeData.ConnectionNumber = value; _pingPacket.ConnectionNumber = value; _pongPacket.ConnectionNumber = value; } } public ConnectionState ConnectionState => _connectionState; internal long ConnectTime => _connectTime; public int RemoteId { get; private set; } public int Ping => _avgRtt / 2; public int RoundTripTime => _avgRtt; public int Mtu => _mtu; public long RemoteTimeDelta => _remoteDelta; public DateTime RemoteUtcTime => new DateTime(DateTime.UtcNow.Ticks + _remoteDelta); public float TimeSinceLastPacket => _timeSinceLastPacket; internal double ResendDelay => _resendDelay; public override SocketAddress Serialize() { return _cachedSocketAddr; } public override int GetHashCode() { return _cachedHashCode; } internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id) : base(remoteEndPoint.Address, remoteEndPoint.Port) { Id = id; Statistics = new NetStatistics(); NetManager = netManager; _cachedSocketAddr = base.Serialize(); if (NetManager.UseNativeSockets) { NativeAddress = new byte[_cachedSocketAddr.Size]; for (int i = 0; i < _cachedSocketAddr.Size; i++) { NativeAddress[i] = _cachedSocketAddr[i]; } } _cachedHashCode = base.GetHashCode(); ResetMtu(); _connectionState = ConnectionState.Connected; _mergeData = new NetPacket(PacketProperty.Merged, NetConstants.MaxPacketSize); _pongPacket = new NetPacket(PacketProperty.Pong, 0); _pingPacket = new NetPacket(PacketProperty.Ping, 0) { Sequence = 1 }; _unreliableSecondQueue = new NetPacket[8]; _unreliableChannel = new NetPacket[8]; _holdedFragments = new Dictionary<ushort, IncomingFragments>(); _deliveredFragments = new Dictionary<ushort, ushort>(); _channels = new BaseChannel[netManager.ChannelsCount * 4]; _channelSendQueue = new ConcurrentQueue<BaseChannel>(); } internal void InitiateEndPointChange() { ResetMtu(); _connectionState = ConnectionState.EndPointChange; } internal void FinishEndPointChange(IPEndPoint newEndPoint) { if (_connectionState != ConnectionState.EndPointChange) { return; } _connectionState = ConnectionState.Connected; base.Address = newEndPoint.Address; base.Port = newEndPoint.Port; if (NetManager.UseNativeSockets) { NativeAddress = new byte[_cachedSocketAddr.Size]; for (int i = 0; i < _cachedSocketAddr.Size; i++) { NativeAddress[i] = _cachedSocketAddr[i]; } } _cachedSocketAddr = base.Serialize(); _cachedHashCode = base.GetHashCode(); } internal void ResetMtu() { _finishMtu = !NetManager.MtuDiscovery; if (NetManager.MtuOverride > 0) { OverrideMtu(NetManager.MtuOverride); } else { SetMtu(0); } } private void SetMtu(int mtuIdx) { _mtuIdx = mtuIdx; _mtu = NetConstants.PossibleMtu[mtuIdx] - NetManager.ExtraPacketSizeForLayer; } private void OverrideMtu(int mtuValue) { _mtu = mtuValue; _finishMtu = true; } public int GetPacketsCountInReliableQueue(byte channelNumber, bool ordered) { int num = channelNumber * 4 + (ordered ? 2 : 0); BaseChannel baseChannel = _channels[num]; if (baseChannel == null) { return 0; } return ((ReliableChannel)baseChannel).PacketsInQueue; } public PooledPacket CreatePacketFromPool(DeliveryMethod deliveryMethod, byte channelNumber) { int mtu = _mtu; NetPacket netPacket = NetManager.PoolGetPacket(mtu); if (deliveryMethod == DeliveryMethod.Unreliable) { netPacket.Property = PacketProperty.Unreliable; return new PooledPacket(netPacket, mtu, 0); } netPacket.Property = PacketProperty.Channeled; return new PooledPacket(netPacket, mtu, (byte)((uint)(channelNumber * 4) + (uint)deliveryMethod)); } public void SendPooledPacket(PooledPacket packet, int userDataSize) { if (_connectionState == ConnectionState.Connected) { packet._packet.Size = packet.UserDataOffset + userDataSize; if (packet._packet.Property == PacketProperty.Channeled) { CreateChannel(packet._channelNumber).AddToQueue(packet._packet); } else { EnqueueUnreliable(packet._packet); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnqueueUnreliable(NetPacket packet) { lock (_unreliableChannelLock) { if (_unreliablePendingCount == _unreliableChannel.Length) { Array.Resize(ref _unreliableChannel, _unreliablePendingCount * 2); } _unreliableChannel[_unreliablePendingCount++] = packet; } } private BaseChannel CreateChannel(byte idx) { BaseChannel baseChannel = _channels[idx]; if (baseChannel != null) { return baseChannel; } switch ((DeliveryMethod)(byte)(idx % 4)) { case DeliveryMethod.ReliableUnordered: baseChannel = new ReliableChannel(this, ordered: false, idx); break; case DeliveryMethod.Sequenced: baseChannel = new SequencedChannel(this, reliable: false, idx); break; case DeliveryM
SailwindCoop.dll
Decompiled 11 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Crest; using HarmonyLib; using LiteNetLib; using LiteNetLib.Layers; using LiteNetLib.Utils; using PsychoticLab; using SailwindCoop.Avatar; using SailwindCoop.Net; using SailwindCoop.Runtime; using SailwindCoop.Sync; using Steamworks; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] [assembly: AssemblyCompany("SailwindCoop")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+52a7a5e4583061d0db58d0834560e7c8d753e6e4")] [assembly: AssemblyProduct("SailwindCoop")] [assembly: AssemblyTitle("SailwindCoop")] [assembly: AssemblyVersion("1.0.0.0")] namespace SailwindCoop { [BepInPlugin("com.sailwind.coop", "Sailwind LAN Co-op", "0.4.3")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "com.sailwind.coop"; public const string Version = "0.4.3"; public const string Edition = "Thunderstore"; internal static Plugin Instance { get; private set; } internal static CoopLog Logger { get; private set; } internal static CoopConfig Cfg { get; private set; } internal static string AvatarBundlePath => Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location), "avatar.bundle"); private void Awake() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) Instance = this; Cfg = new CoopConfig(((BaseUnityPlugin)this).Config); Logger = new CoopLog(((BaseUnityPlugin)this).Logger, Cfg.EnableLogging.Value); Cfg.EnableLogging.SettingChanged += delegate { Logger.Enabled = Cfg.EnableLogging.Value; }; AvatarCatalog.Initialize(); Logger.LogInfo("Sailwind LAN Co-op 0.4.3 (Thunderstore edition) loading..."); GameObject val = new GameObject("SailwindCoop"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent<CoopBehaviour>(); Logger.LogInfo("Sailwind LAN Co-op ready. Use the in-game Co-op menu."); } } public sealed class CoopConfig { public readonly ConfigEntry<string> ListenIp; public readonly ConfigEntry<int> Port; public readonly ConfigEntry<string> JoinIp; public readonly ConfigEntry<string> PlayerName; public readonly ConfigEntry<int> SnapshotHz; public readonly ConfigEntry<float> InterpDelayMs; public readonly ConfigEntry<float> AvatarVerticalOffset; public readonly ConfigEntry<float> HostAvatarVerticalOffset; public readonly ConfigEntry<int> MaxClients; public readonly ConfigEntry<int> DisconnectTimeoutMs; public readonly ConfigEntry<int> UpdateTimeMs; public readonly ConfigEntry<int> PingIntervalMs; public readonly ConfigEntry<int> ConnectAttempts; public readonly ConfigEntry<int> ReconnectDelayMs; public readonly ConfigEntry<int> CoopSaveSlot; public readonly ConfigEntry<bool> ForceHostSaveOnJoin; public readonly ConfigEntry<bool> PauseHostOnJoin; public readonly ConfigEntry<string> ModSyncExclude; public readonly ConfigEntry<bool> UseSteam; public readonly ConfigEntry<bool> SteamFriendsOnly; public readonly ConfigEntry<string> SteamJoinId; public readonly ConfigEntry<bool> EnableLogging; public readonly ConfigEntry<bool> EnableDebugPanel; public readonly ConfigEntry<KeyCode> MenuKey; public readonly ConfigEntry<KeyCode> EmoteKey; public readonly ConfigEntry<bool> EmoteHintShown; public readonly ConfigEntry<bool> SharedWallet; public readonly ConfigEntry<KeyCode> GiveKey; public readonly ConfigEntry<bool> GiveHintShown; public CoopConfig(ConfigFile c) { Port = c.Bind<int>("Network", "Port", 7777, "Host UDP port."); ListenIp = c.Bind<string>("Network", "ListenIp", "0.0.0.0", "IP/interface the host listens on (0.0.0.0 = all interfaces). Applied when starting the host: a specific address accepts connections only on that interface."); JoinIp = c.Bind<string>("Network", "JoinIp", "127.0.0.1", "Host IP for the client to join."); PlayerName = c.Bind<string>("Network", "PlayerName", "Player", "Displayed player name."); SnapshotHz = c.Bind<int>("Network", "SnapshotHz", 20, "State snapshot send rate (Hz), Stage 1+."); InterpDelayMs = c.Bind<float>("Network", "InterpDelayMs", 100f, "Interpolation buffer delay (ms), Stage 1+."); AvatarVerticalOffset = c.Bind<float>("Avatar", "VerticalOffset", -0.6f, "Vertical offset of the visual bundle model relative to the networked player position. Negative values move the model down."); HostAvatarVerticalOffset = c.Bind<float>("Avatar", "HostVerticalOffset", -0.6f, "Vertical offset of the host visual bundle model. Separate because the host root pose in Sailwind is usually higher than the client pose."); MaxClients = c.Bind<int>("Server", "MaxClients", 4, "Maximum number of clients connected to the host at once (1 = single guest only). Applied on incoming connections."); DisconnectTimeoutMs = c.Bind<int>("Server", "DisconnectTimeoutMs", 5000, "Timeout (ms) without packets from a peer before it is considered disconnected."); UpdateTimeMs = c.Bind<int>("Server", "UpdateTimeMs", 15, "Internal network manager update interval (ms). Lower = more frequent polling/sending, higher CPU load."); PingIntervalMs = c.Bind<int>("Server", "PingIntervalMs", 1000, "Ping interval (ms) for latency estimation and connection keepalive."); ConnectAttempts = c.Bind<int>("Client", "ConnectAttempts", 10, "How many times the client tries to reach the host before reporting a connection error."); ReconnectDelayMs = c.Bind<int>("Client", "ReconnectDelayMs", 500, "Delay (ms) between client connection attempts."); CoopSaveSlot = c.Bind<int>("Save", "CoopSaveSlot", 5, "Save slot (0..5) where the client writes the received host world and loads from it. WARNING: the local save in this slot on the client is overwritten. Join from the main menu."); ForceHostSaveOnJoin = c.Bind<bool>("Save", "ForceHostSaveOnJoin", true, "When a client joins, the host makes a fresh save so the client receives the current world (economy/objects/position). Disable to send the latest autosave without forcing a save."); PauseHostOnJoin = c.Bind<bool>("Save", "PauseHostOnJoin", true, "While the client loads the host world, the host world is paused (timeScale=0, like the settings menu) so items/anchor/moorings/waves match the snapshot on the client. The pause is lifted when the client reports loaded, disconnects, or after a 120 s timeout."); ModSyncExclude = c.Bind<string>("Mods", "ModSyncExclude", "gravydevsupreme.xunity.autotranslator,gravydevsupreme.xunity.resourceredirector", "Comma-separated plugin GUIDs that are never listed, compared or shared (personal mods such as a UI translator, or mods only the host needs). A folder containing an excluded plugin is skipped whole. Applied on both host and client."); UseSteam = c.Bind<bool>("Steam", "UseSteam", false, "The co-op menu opens in Steam mode: host for Steam friends and join them without an IP address. Steam must be running and own Sailwind. Switch it in the co-op menu (F8 -> Connection). With Steam/FriendsOnly off a Steam host also accepts LAN clients on its UDP port."); SteamFriendsOnly = c.Bind<bool>("Steam", "FriendsOnly", true, "Steam host: accept only players on your Steam friends list, and nobody through the LAN port. Turn off to let in anyone: by your Steam ID and on the LAN port. A host started in LAN mode is not affected."); SteamJoinId = c.Bind<string>("Steam", "JoinId", "", "SteamID64 of the host joined last (17 digits). Filled in from the co-op menu."); EnableLogging = c.Bind<bool>("Debug", "EnableLogging", false, "Write this mod's diagnostics to BepInEx/LogOutput.log. Off by default: a normal session stays silent and costs no disk I/O. Hard errors are still written even when this is off, but only a handful of lines - just enough to show that something broke. Toggle in-game from the co-op menu (F8 -> Logging); turn it on BEFORE reproducing a problem, otherwise the log will contain nothing useful about the mod."); EnableDebugPanel = c.Bind<bool>("Debug", "EnableDebugPanel", false, "Developer/test panel for gold/spawn/reputation/world tools. Keep false for public builds."); MenuKey = c.Bind<KeyCode>("UI", "MenuKey", (KeyCode)289, "Show/hide the co-op menu."); EmoteKey = c.Bind<KeyCode>("UI", "EmoteKey", (KeyCode)103, "Hold to open the emote wheel, move the mouse to a gesture and release. Works only in a co-op session. Change it if the key is bound to something else in the game."); SharedWallet = c.Bind<bool>("Economy", "SharedWallet", false, "Host: the whole crew uses the host's money. Every purchase, sale and reward of any player changes the host's wallet, and everyone sees the same balance. Guests' own money stays in their profiles and comes back when they leave or when this is turned off. Off: every player has a personal wallet and mission rewards are divided equally. Toggle it in the co-op menu (F8) while hosting."); GiveKey = c.Bind<KeyCode>("UI", "GiveKey", (KeyCode)104, "Look at a crewmate standing next to you and hold this key to hold out money: the mouse wheel changes the amount, the middle mouse button changes the currency. The other player looks at you and presses the same key to take it. Change it if the key is bound to something else in the game."); GiveHintShown = c.Bind<bool>("UI", "GiveHintShown", false, "The one-time on-screen hint about handing money over has been shown. Set to false to see it again."); EmoteHintShown = c.Bind<bool>("UI", "EmoteHintShown", false, "The one-time on-screen hint about the emote wheel has been shown. Set to false to see it again in the next session."); } } } namespace SailwindCoop.Sync { internal static class AnchorPatches { public static void Apply(Harmony harmony) { int patched = 0; PatchGuard.Run(delegate { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(GoPointer), "PickUpItem", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AnchorPatches), "PostPickup", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); patched++; }, Report); PatchGuard.Run(delegate { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_004e: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(GoPointer), "DropItem", (Type[])null, (Type[])null), new HarmonyMethod(typeof(AnchorPatches), "PreDrop", (Type[])null), new HarmonyMethod(typeof(AnchorPatches), "PostDrop", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); patched++; }, Report); PatchGuard.Run(delegate { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(Anchor), "ExtraFixedUpdate", (Type[])null, (Type[])null), new HarmonyMethod(typeof(AnchorPatches), "PrePhysics", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); patched++; }, Report); PatchHealth.Report("Anchor", patched, 3, patched + "/3"); } private static void Report(Exception error) { Plugin.Logger.LogWarning("[AnchorPatches] " + error.Message); } private static void PostPickup(PickupableItem item) { PatchGuard.Run(delegate { PickupableItem obj = item; Anchor val = (Anchor)(object)((obj is Anchor) ? obj : null); if (val != null) { AnchorSync.Instance?.NotifyPickup(val); } }, Report); } private static void PreDrop(GoPointer __instance, ref Anchor __state) { Anchor captured = null; PatchGuard.Run(delegate { ref Anchor reference = ref captured; PickupableItem heldItem = __instance.GetHeldItem(); reference = (Anchor)(object)((heldItem is Anchor) ? heldItem : null); }, Report); __state = captured; } private static void PostDrop(Anchor __state) { PatchGuard.Run(delegate { if ((Object)(object)__state != (Object)null) { AnchorSync.Instance?.NotifyDrop(__state); } }, Report); } private static bool PrePhysics(Anchor __instance) { return PatchGuard.Prefix(() => !(AnchorSync.Instance?.SuppressPhysics(__instance) ?? false), Report); } } internal sealed class AnchorStateGate { private uint _pending; private uint _lastRevision; private bool _haveRevision; public bool HasPending => _pending != 0; public void Begin(uint request) { _pending = request; } public bool Receive(uint revision, uint requester, uint request, uint localPlayer) { if (_pending != 0) { if (requester != localPlayer || request != _pending) { return false; } _pending = 0u; } if (_haveRevision && (int)(revision - _lastRevision) <= 0) { return false; } _lastRevision = revision; _haveRevision = true; return true; } } public sealed class AnchorSync { private readonly CoopNet _net; private readonly BoatContexts<AnchorSync> _fleet; private readonly Transform _boat; private readonly ushort _boatId = ushort.MaxValue; private readonly uint _layoutHash; private readonly RopeControllerAnchor _rope; private readonly Anchor _anchor; private readonly Rigidbody _body; private readonly CapsuleCollider _collider; private readonly NetTransform _slave = new NetTransform(); private readonly AnchorStateGate _gate = new AnchorStateGate(); private static readonly FieldInfo SetField = typeof(Anchor).GetField("set", BindingFlags.Instance | BindingFlags.NonPublic); private uint _holder; private uint _nextRequest; private uint _revision; private float _sendTimer; private bool _slaved; private bool _previousKinematic; private bool _previousTrigger; private RigidbodyInterpolation _previousInterpolation; private CoordFrame _frame; private bool _haveFrame; private bool _haveLast; private Vector3 _lastPos; private long _lastTick; public float SnapshotHz = 12f; private const float DeployedLength = 0.5f; private AnchorRequestMsg _sentRequest; private readonly ChangeStream _stream = new ChangeStream(); private AnchorStateMsg _sentState; private bool _resyncAsked; public static AnchorSync Instance { get; private set; } private bool LocalHeld { get { if ((Object)(object)_anchor != (Object)null) { return (Object)(object)((PickupableItem)_anchor).held != (Object)null; } return false; } } public bool HasAnchor { get { if (_fleet == null) { return (Object)(object)_anchor != (Object)null; } foreach (AnchorSync value in _fleet.Values) { if (value.HasAnchor) { return true; } } return false; } } public bool ClientSet { get; private set; } public bool Slaving { get { if (_fleet == null) { return _slaved; } foreach (AnchorSync value in _fleet.Values) { if (value.Slaving) { return true; } } return false; } } public string AnchorText { get { if (_fleet == null) { if (!((Object)(object)_anchor == (Object)null)) { return (LocalHeld ? "local hand" : ((_holder != 0) ? ("hand " + _holder) : (_anchor.IsSet() ? "set" : "free"))) + (_slaved ? " slaved" : "") + (_gate.HasPending ? " pending" : ""); } return "no anchor"; } return _fleet.Describe(delegate(AnchorSync c) { ushort boatId = c._boatId; return "boat " + boatId + ": " + c.AnchorText; }); } } private bool Connected { get { if (_net.State == LinkState.Connected && GameState.playing && !GameState.currentlyLoading) { return CoordSpace.Ready; } return false; } } public AnchorSync(CoopNet net) { _net = net; Instance = this; _fleet = new BoatContexts<AnchorSync>((Transform boat, ushort id) => new AnchorSync(net, boat, id), delegate(AnchorSync c) { c.Clear(); }, BindingStamp); } private static long BindingStamp(Transform boat) { RopeControllerAnchor[] componentsInChildren = ((Component)boat).GetComponentsInChildren<RopeControllerAnchor>(true); Component[] array = (Component[])(object)new Component[componentsInChildren.Length]; for (int i = 0; i < componentsInChildren.Length; i++) { array[i] = (Component)(object)(((Object)(object)componentsInChildren[i].joint != (Object)null) ? ((Component)componentsInChildren[i].joint).GetComponent<Anchor>() : null); } Component[][] array2 = new Component[2][]; Component[] array3 = (Component[])(object)componentsInChildren; array2[0] = array3; array2[1] = array; return BoatLayout.Stamp(array2); } private AnchorSync(CoopNet net, Transform boat, ushort id) { _net = net; _boat = boat; _boatId = id; _slave.InterpDelayMs = ((net.Role == Role.Host) ? 0f : 100f); _rope = ((Component)boat).GetComponentInChildren<RopeControllerAnchor>(true); _anchor = (((Object)(object)_rope != (Object)null && (Object)(object)_rope.joint != (Object)null) ? ((Component)_rope.joint).GetComponent<Anchor>() : null); _body = (((Object)(object)_anchor != (Object)null) ? ((Component)_anchor).GetComponent<Rigidbody>() : null); _collider = (((Object)(object)_anchor != (Object)null) ? ((Component)_anchor).GetComponent<CapsuleCollider>() : null); Component[][] array = new Component[1][]; Component[] componentsInChildren = (Component[])(object)((Component)boat).GetComponentsInChildren<RopeControllerAnchor>(true); array[0] = componentsInChildren; _layoutHash = BoatLayout.Hash(boat, array); Plugin.Logger.LogInfo("[AnchorSync] boat=" + id + " anchor=" + ((Object)(object)_anchor != (Object)null) + " (rope joint)"); } public void Tick(float dt) { if (_fleet != null) { foreach (AnchorSync value in _fleet.Values) { value.Tick(dt); } return; } if (!Connected || (Object)(object)_anchor == (Object)null) { return; } if (_net.Role == Role.Client) { AskResync(); } if (LocalHeld) { RestoreSlaved(); } _sendTimer += dt; if (_sendTimer < 1f / Mathf.Max(1f, SnapshotHz)) { return; } _sendTimer = 0f; if (_net.Role == Role.Host) { AnchorStateMsg anchorStateMsg = CaptureState(); StreamSend streamSend = _stream.Next(Changed(_sentState, anchorStateMsg)); if (streamSend != StreamSend.None) { _sentState = anchorStateMsg; _net.Broadcast(anchorStateMsg, (DeliveryMethod)((streamSend == StreamSend.Reliable) ? 2 : 4)); } } else if (_net.Role == Role.Client && LocalHeld) { SendRequest(held: true, transition: false); } } public void NotifyPickup(Anchor anchor) { if (InteractionContext.Suppressed) { return; } if (_fleet != null) { ForAnchor(anchor)?.NotifyPickup(anchor); } else if (Connected && !((Object)(object)anchor != (Object)(object)_anchor) && LocalHeld) { RestoreSlaved(); _holder = _net.MyNetId; _slave.Clear(); _haveFrame = false; _haveLast = false; MirrorSet(value: false); if ((Object)(object)_body != (Object)null) { _body.isKinematic = true; _body.drag = _anchor.anchorDrag; } if (_net.Role == Role.Client) { SendRequest(held: true, transition: true); } else if (_net.Role == Role.Host) { SendState(reliable: true); } CoopLog logger = Plugin.Logger; string text = _net.Role.ToString(); ushort boatId = _boatId; logger.LogInfo("[AnchorSync] pickup role=" + text + " boat=" + boatId); } } public void NotifyDrop(Anchor anchor) { if (InteractionContext.Suppressed) { return; } if (_fleet != null) { ForAnchor(anchor)?.NotifyDrop(anchor); } else { if (!Connected || (Object)(object)anchor != (Object)(object)_anchor) { return; } _holder = 0u; _slave.Clear(); _haveFrame = false; _haveLast = false; if ((Object)(object)_collider != (Object)null) { ((Collider)_collider).isTrigger = false; } if (_net.Role == Role.Client) { SendRequest(held: false, transition: true); EnsureSlaved(held: false); } else if (_net.Role == Role.Host) { RestoreSlaved(); if ((Object)(object)_body != (Object)null) { _body.isKinematic = _anchor.IsSet(); } SendState(reliable: true); } CoopLog logger = Plugin.Logger; string text = _net.Role.ToString(); ushort boatId = _boatId; logger.LogInfo("[AnchorSync] drop role=" + text + " boat=" + boatId); } } private AnchorSync ForAnchor(Anchor anchor) { if ((Object)(object)anchor == (Object)null) { return null; } foreach (AnchorSync value in _fleet.Values) { if ((Object)(object)value._anchor == (Object)(object)anchor) { return value; } } return null; } public bool PreserveRope(RopeControllerAnchor rope) { if (_fleet != null) { foreach (AnchorSync value in _fleet.Values) { if (value.PreserveRope(rope)) { return true; } } return false; } if ((Object)(object)rope == (Object)(object)_rope) { if (!LocalHeld) { return _gate.HasPending; } return true; } return false; } public bool SuppressPhysics(Anchor anchor) { if (_fleet != null) { return ForAnchor(anchor)?.SuppressPhysics(anchor) ?? false; } if (Connected && (Object)(object)anchor == (Object)(object)_anchor && !LocalHeld) { if (_net.Role != Role.Client) { return _holder != 0; } return true; } return false; } private void SendRequest(bool held, bool transition) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) uint num = 0u; if (transition) { num = ++_nextRequest; if (num == 0) { num = ++_nextRequest; } _gate.Begin(num); } bool flag = held && (Object)(object)GameState.currentBoat == (Object)(object)_boat; AnchorRequestMsg anchorRequestMsg = new AnchorRequestMsg { BoatIndex = _boatId, LayoutHash = _layoutHash, RequestId = num, Held = held, Frame = (flag ? CoordFrame.Boat : CoordFrame.World), Pos = (flag ? _boat.InverseTransformPoint(((Component)_anchor).transform.position) : CoordSpace.LocalToReal(((Component)_anchor).transform.position)), Rot = (flag ? (Quaternion.Inverse(_boat.rotation) * ((Component)_anchor).transform.rotation) : ((Component)_anchor).transform.rotation), Vel = ((!held && (Object)(object)_body != (Object)null) ? _body.velocity : Vector3.zero), RopeLength = ((RopeController)_rope).currentLength }; if (!transition && _sentRequest != null && _sentRequest.Frame == anchorRequestMsg.Frame && _sentRequest.RopeLength.Equals(anchorRequestMsg.RopeLength)) { Vector3 val = _sentRequest.Pos - anchorRequestMsg.Pos; if (((Vector3)(ref val)).sqrMagnitude <= 1E-06f && Quaternion.Angle(_sentRequest.Rot, anchorRequestMsg.Rot) <= 0.1f) { return; } } _sentRequest = anchorRequestMsg; _net.Broadcast(anchorRequestMsg, (DeliveryMethod)2); } public void OnAnchorRequest(AnchorRequestMsg msg, NetPeer peer) { //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) if (_fleet != null) { if (Connected && _net.Role == Role.Host && _net.PlayerNetIdForPeer(peer) != 0) { AnchorSync anchorSync = _fleet.Get(msg.BoatIndex); if (anchorSync != null && BoatLayout.Matches(msg.BoatIndex, msg.LayoutHash, anchorSync._layoutHash, "AnchorSync")) { anchorSync.OnAnchorRequest(msg, peer); } } } else { if ((Object)(object)_anchor == (Object)null || (Object)(object)_rope == (Object)null) { return; } uint num = _net.PlayerNetIdForPeer(peer); bool num2 = _holder != (msg.Held ? num : 0); _holder = (msg.Held ? num : 0u); ((RopeController)_rope).currentLength = msg.RopeLength; MirrorSet(value: false); if ((Object)(object)_body != (Object)null) { _body.drag = _anchor.anchorDrag; } if (num2 || !msg.Held) { _slave.Clear(); _haveFrame = false; _haveLast = false; } Buffer(_net.Clock.ServerTick, msg.Frame, msg.Pos, msg.Rot, msg.Vel); if (!LocalHeld) { EnsureSlaved(msg.Held); if (msg.Held) { _slave.Apply(((Component)_anchor).transform, _net.Clock.ServerTick); } if (!msg.Held) { _slave.Apply(((Component)_anchor).transform, _net.Clock.ServerTick); RestoreSlaved(); if ((Object)(object)_body != (Object)null) { _body.isKinematic = false; _body.velocity = ((msg.Frame == CoordFrame.Boat) ? _boat.TransformDirection(msg.Vel) : msg.Vel); } } } if (msg.RequestId != 0) { AnchorStateMsg anchorStateMsg = CaptureState(); anchorStateMsg.Frame = msg.Frame; anchorStateMsg.Pos = msg.Pos; anchorStateMsg.Rot = msg.Rot; anchorStateMsg.Vel = msg.Vel; anchorStateMsg.RequesterNetId = num; anchorStateMsg.RequestId = msg.RequestId; _net.Broadcast(anchorStateMsg, (DeliveryMethod)2); CoopLog logger = Plugin.Logger; string[] obj = new string[8] { "[AnchorSync] request boat=", null, null, null, null, null, null, null }; ushort boatId = _boatId; obj[1] = boatId.ToString(); obj[2] = " actor="; obj[3] = num.ToString(); obj[4] = " held="; obj[5] = msg.Held.ToString(); obj[6] = " request="; obj[7] = msg.RequestId.ToString(); logger.LogInfo(string.Concat(obj)); } } } private AnchorStateMsg CaptureState() { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) long serverTick = _net.Clock.ServerTick; uint num = (LocalHeld ? _net.MyNetId : _holder); bool flag = ((num == 0) ? (_anchor.IsSet() || ((RopeController)_rope).currentLength * _rope.maxLength > 0.5f) : (LocalHeld ? ((Object)(object)GameState.currentBoat != (Object)(object)_boat) : (_frame != CoordFrame.Boat))); Vector3 val = (flag ? CoordSpace.LocalToReal(((Component)_anchor).transform.position) : _boat.InverseTransformPoint(((Component)_anchor).transform.position)); Vector3 vel = Vector3.zero; if (flag && _haveLast && serverTick > _lastTick) { vel = (val - _lastPos) / ((float)(serverTick - _lastTick) / 1000f); } _lastPos = val; _lastTick = serverTick; _haveLast = flag; return new AnchorStateMsg { BoatIndex = _boatId, LayoutHash = _layoutHash, Tick = serverTick, Frame = ((!flag) ? CoordFrame.Boat : CoordFrame.World), Pos = val, Rot = (flag ? ((Component)_anchor).transform.rotation : (Quaternion.Inverse(_boat.rotation) * ((Component)_anchor).transform.rotation)), Vel = vel, Set = _anchor.IsSet(), HolderNetId = num, RopeLength = ((RopeController)_rope).currentLength, Revision = (_revision = SessionCounters.NextRevision()) }; } private void SendState(bool reliable = false) { AnchorStateMsg msg = (_sentState = CaptureState()); _stream.Sent(reliable); _net.Broadcast(msg, (DeliveryMethod)(reliable ? 2 : 4)); } public void Resync(ushort boat) { (_fleet?.Get(boat))?._stream.Reset(); } private void AskResync() { if (!_resyncAsked && GameState.playing && !GameState.currentlyLoading) { _resyncAsked = true; _net.Broadcast(new ResyncRequestMsg { BoatIndex = _boatId, Domain = ResyncDomain.Anchor }, (DeliveryMethod)2); } } private static bool Changed(AnchorStateMsg a, AnchorStateMsg b) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (a != null && a.Frame == b.Frame && a.Set == b.Set && a.HolderNetId == b.HolderNetId && a.RopeLength.Equals(b.RopeLength)) { Vector3 val = a.Pos - b.Pos; if (!(((Vector3)(ref val)).sqrMagnitude > 0.0001f)) { return Quaternion.Angle(a.Rot, b.Rot) > 0.1f; } } return true; } public void OnAnchorState(AnchorStateMsg msg, NetPeer peer) { //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) if (_fleet != null) { if (Connected && _net.IsHostPeer(peer)) { AnchorSync anchorSync = _fleet.Get(msg.BoatIndex); if (anchorSync != null && BoatLayout.Matches(msg.BoatIndex, msg.LayoutHash, anchorSync._layoutHash, "AnchorSync")) { anchorSync.OnAnchorState(msg, peer); } } } else if (!((Object)(object)_anchor == (Object)null) && _gate.Receive(msg.Revision, msg.RequesterNetId, msg.RequestId, _net.MyNetId) && !LocalHeld) { bool num = _holder != msg.HolderNetId; _holder = msg.HolderNetId; if (num) { _slave.Clear(); _haveFrame = false; } EnsureSlaved(_holder != 0); if (_holder != 0 || (msg.RequesterNetId == _net.MyNetId && msg.RequestId != 0)) { ((RopeController)_rope).currentLength = msg.RopeLength; } MirrorSet(msg.Set); Buffer(msg.Tick, msg.Frame, msg.Pos, msg.Rot, msg.Vel); } } private void MirrorSet(bool value) { ClientSet = value; try { SetField?.SetValue(_anchor, value); } catch (Exception ex) { CoopLog logger = Plugin.Logger; ushort boatId = _boatId; logger.LogWarning("[AnchorSync] Не удалось установить set лодки " + boatId + ": " + ex.Message); } } private void Buffer(long tick, CoordFrame frame, Vector3 pos, Quaternion rot, Vector3 vel) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (!_haveFrame || _frame != frame) { _slave.Clear(); _frame = frame; _haveFrame = true; _slave.ToWorldPos = ((frame == CoordFrame.Boat) ? ((Func<Vector3, Vector3>)((Vector3 p) => _boat.TransformPoint(p))) : new Func<Vector3, Vector3>(CoordSpace.RealToLocal)); _slave.ToWorldRot = ((frame == CoordFrame.Boat) ? ((Func<Quaternion, Quaternion>)((Quaternion r) => _boat.rotation * r)) : ((Func<Quaternion, Quaternion>)((Quaternion r) => r))); } _slave.Push(tick, pos, rot, vel); } public void ApplyRemote() { if (_fleet != null) { foreach (AnchorSync value in _fleet.Values) { value.ApplyRemote(); } return; } if (Connected && !LocalHeld && !_gate.HasPending && !((Object)(object)_anchor == (Object)null) && _slave.HasData && !(Time.timeScale <= 0.0001f) && (_net.Role != Role.Host || _holder != 0)) { EnsureSlaved(_holder != 0); _slave.Apply(((Component)_anchor).transform, _net.Clock.ServerTick); } } private void EnsureSlaved(bool held) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!_slaved) { if ((Object)(object)_body != (Object)null) { _previousKinematic = _body.isKinematic; _previousInterpolation = _body.interpolation; } if ((Object)(object)_collider != (Object)null) { _previousTrigger = ((Collider)_collider).isTrigger; } _slaved = true; } if ((Object)(object)_body != (Object)null) { _body.isKinematic = true; _body.interpolation = (RigidbodyInterpolation)0; } if ((Object)(object)_collider != (Object)null) { ((Collider)_collider).isTrigger = held; } } private void RestoreSlaved() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (_slaved) { if ((Object)(object)_body != (Object)null) { _body.isKinematic = _previousKinematic; _body.interpolation = _previousInterpolation; } if ((Object)(object)_collider != (Object)null) { ((Collider)_collider).isTrigger = _previousTrigger; } _slaved = false; _slave.Clear(); _haveFrame = false; } } public void ClearRemoteActor(uint actor) { if (_fleet != null) { foreach (AnchorSync item in _fleet.Existing) { item.ClearRemoteActor(actor); } return; } if (_net.Role == Role.Host && actor != 0 && _holder == actor) { _holder = 0u; RestoreSlaved(); _haveLast = false; if ((Object)(object)_body != (Object)null) { _body.isKinematic = _anchor.IsSet(); } if (Connected && (Object)(object)_anchor != (Object)null) { SendState(reliable: true); } } } public void InvalidateHull(ushort id) { _fleet?.Invalidate(id); } public void Clear() { if (_fleet != null) { _fleet.Clear(); return; } RestoreSlaved(); if ((Object)(object)_body != (Object)null && !LocalHeld) { _body.isKinematic = _anchor.IsSet(); } } } internal static class AtomicSaveFile { public static void Write(string path, Action<Stream> serialize, bool preserveBackup = false) { string text = path + "." + Guid.NewGuid().ToString("N") + ".tmp"; try { using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { serialize(fileStream); fileStream.Flush(flushToDisk: true); } if (File.Exists(path)) { File.Replace(text, path, preserveBackup ? null : (path + ".bak")); } else { File.Move(text, path); } } finally { try { if (File.Exists(text)) { File.Delete(text); } } catch { } } } public static T Read<T>(string path, Func<Stream, T> deserialize, out bool recovered) { recovered = false; try { using FileStream arg = File.OpenRead(path); return deserialize(arg); } catch (Exception ex) { try { using FileStream arg2 = File.OpenRead(path + ".bak"); T result = deserialize(arg2); recovered = true; return result; } catch (Exception ex2) { throw new AggregateException("Save and backup could not be read", ex, ex2); } } } } internal static class BoatActivityPatches { private static CoopLog.Repeat _errors; public static void Apply(Harmony harmony) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown MethodInfo method = typeof(BoatHorizonPerformanceSwitcher).GetMethod("SetHidden", BindingFlags.Instance | BindingFlags.NonPublic); if (method == null) { PatchHealth.Report("BoatActivity", 0, 1); return; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(BoatActivityPatches), "PreSetHidden", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); PatchHealth.Report("BoatActivity", 1, 1); } private static void PreSetHidden(BoatHorizon horizon, ref bool newState) { try { CoopBehaviour instance = CoopBehaviour.Instance; if ((((Object)(object)instance == (Object)null || instance.Net.Role != Role.Host || instance.Net.State != LinkState.Connected || (Object)(object)horizon == (Object)null) | newState) || GameState.currentlyLoading || !GameState.playing) { return; } Transform parent = ((Component)horizon).transform.parent; foreach (Transform item in BoatLocator.FindBoats()) { if (!((Object)(object)item == (Object)null) && !((Object)(object)parent == (Object)null) && ((Object)(object)item == (Object)(object)parent || item.IsChildOf(parent) || parent.IsChildOf(item))) { BoatAuthority instance2 = BoatAuthority.Instance; if (instance2 != null && instance2.AnyActorNear(item, horizon.fullySunkDistance)) { newState = true; } break; } } } catch (Exception detail) { try { Plugin.Logger?.ReportError("[BoatActivity] host activation failed", detail, ref _errors); } catch { } } } } internal sealed class BoatAuthority { private sealed class Pose { public ushort Boat; public CoordFrame Frame; public Vector3 Pos; public float Received; } private readonly CoopNet _net; private readonly Dictionary<uint, Pose> _poses = new Dictionary<uint, Pose>(); public static BoatAuthority Instance { get; private set; } public BoatAuthority(CoopNet net) { _net = net; Instance = this; } public static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } public static bool Finite(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (Finite(value.x) && Finite(value.y)) { return Finite(value.z); } return false; } public static bool Rotation(Quaternion value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (!Finite(value.x) || !Finite(value.y) || !Finite(value.z) || !Finite(value.w)) { return false; } float num = value.x * value.x + value.y * value.y + value.z * value.z + value.w * value.w; if (num > 0.5f) { return num < 1.5f; } return false; } public void Record(PlayerStateMsg msg, NetPeer peer) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) uint num = _net.PlayerNetIdForPeer(peer); if (num != 0 && num == msg.NetId && Finite(msg.Pos) && (msg.Frame == CoordFrame.Boat || msg.Frame == CoordFrame.World)) { _poses[num] = new Pose { Boat = msg.BoatIndex, Frame = msg.Frame, Pos = msg.Pos, Received = Time.unscaledTime }; } } public bool AnyActorNear(Transform boat, float radius) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Host || (Object)(object)boat == (Object)null || !Finite(radius) || !CoordSpace.Ready) { return false; } foreach (Pose value in _poses.Values) { if (Time.unscaledTime - value.Received > 5f) { continue; } Vector3 val2; if (value.Frame == CoordFrame.Boat) { Transform val = BoatLocator.FindByIndex(value.Boat); if ((Object)(object)val == (Object)null) { continue; } if ((Object)(object)val == (Object)(object)boat) { return true; } val2 = val.TransformPoint(value.Pos); } else { val2 = CoordSpace.RealToLocal(value.Pos); } Vector3 val3 = val2 - boat.position; if (((Vector3)(ref val3)).sqrMagnitude <= radius * radius) { return true; } } return false; } public void ClearActor(uint actor) { _poses.Remove(actor); } public void Clear() { _poses.Clear(); } } internal sealed class BoatContexts<T> where T : class { private sealed class Entry { public Transform Boat; public long Stamp; public T Context; } private readonly Dictionary<ushort, Entry> _entries = new Dictionary<ushort, Entry>(); private readonly Func<Transform, ushort, T> _create; private readonly Action<T> _clear; private readonly Func<Transform, long> _stamp; private float _nextScan; private int _missingWarnings; public IEnumerable<T> Values { get { if (!GameState.playing || GameState.currentlyLoading) { yield break; } Refresh(); foreach (KeyValuePair<ushort, Entry> entry in _entries) { if (!ShipyardSync.Suspended(entry.Key)) { yield return entry.Value.Context; } } } } public IEnumerable<T> Existing { get { foreach (Entry value in _entries.Values) { yield return value.Context; } } } public BoatContexts(Func<Transform, ushort, T> create, Action<T> clear, Func<Transform, long> stamp) { _create = create; _clear = clear; _stamp = stamp; } public string Describe(Func<T, string> describe) { StringBuilder stringBuilder = new StringBuilder(); foreach (T value in Values) { if (stringBuilder.Length > 0) { stringBuilder.Append(" | "); } stringBuilder.Append(describe(value)); } if (stringBuilder.Length <= 0) { return "no boats"; } return stringBuilder.ToString(); } public T Get(ushort id) { if (ShipyardSync.Suspended(id)) { return null; } Refresh(); if (_entries.TryGetValue(id, out var value)) { return value.Context; } if (Plugin.Logger != null && Plugin.Logger.ShouldReport(ref _missingWarnings)) { Plugin.Logger.LogWarning("[" + typeof(T).Name + "] Rejected unknown owned boat=" + id); } return null; } public void Refresh() { if (!GameState.playing || GameState.currentlyLoading || Time.unscaledTime < _nextScan) { return; } _nextScan = Time.unscaledTime + 1f; HashSet<ushort> hashSet = new HashSet<ushort>(); foreach (Transform item in BoatLocator.FindBoats()) { ushort num = BoatLocator.IndexOf(item); if ((Object)(object)item == (Object)null || num == ushort.MaxValue) { continue; } hashSet.Add(num); if (ShipyardSync.Suspended(num)) { continue; } long num2 = _stamp(item); if (_entries.TryGetValue(num, out var value)) { if ((Object)(object)value.Boat == (Object)(object)item && value.Stamp == num2) { continue; } _clear(value.Context); _entries.Remove(num); } _entries.Add(num, new Entry { Boat = item, Stamp = num2, Context = _create(item, num) }); } List<ushort> list = new List<ushort>(); foreach (KeyValuePair<ushort, Entry> entry in _entries) { if (!hashSet.Contains(entry.Key)) { _clear(entry.Value.Context); list.Add(entry.Key); } } foreach (ushort item2 in list) { _entries.Remove(item2); } } public void Clear() { foreach (Entry value in _entries.Values) { _clear(value.Context); } _entries.Clear(); _nextScan = 0f; } public void Invalidate(ushort id) { if (_entries.TryGetValue(id, out var value)) { _clear(value.Context); _entries.Remove(id); } _nextScan = 0f; } } internal static class BoatLayout { private static int _mismatchWarnings; public static bool Matches(ushort boat, uint remote, uint local, string domain) { if (remote == local) { return true; } if (Plugin.Logger != null && Plugin.Logger.ShouldReport(ref _mismatchWarnings)) { Plugin.Logger.LogWarning("[" + domain + "] Layout differs; using existing boat/object indices boat=" + boat + " remote=" + remote + " local=" + local); } return true; } public static uint Hash(Transform boat, params Component[][] groups) { uint hash = 2166136261u; foreach (Component[] obj in groups) { Add(ref hash, "|"); Component[] array = obj; foreach (Component val in array) { if ((Object)(object)val == (Object)null) { Add(ref hash, "null;"); continue; } string text = ""; Transform val2 = val.transform; while ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)boat) { int num = 0; if ((Object)(object)val2.parent != (Object)null) { for (int k = 0; k < val2.GetSiblingIndex(); k++) { if (((Object)val2.parent.GetChild(k)).name == ((Object)val2).name) { num++; } } } text = ((Object)val2).name + ":" + num + "/" + text; val2 = val2.parent; } Add(ref hash, ((object)val).GetType().FullName + ":" + text + ";"); } } return hash; } public static long Stamp(params Component[][] groups) { long num = 17L; foreach (Component[] array in groups) { foreach (Component val in array) { num = num * 31 + (((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0); } } return num; } public static uint MooringHash(PickupableBoatMooringRope[] ropes) { uint hash = 2166136261u; if (ropes != null) { foreach (PickupableBoatMooringRope val in ropes) { Add(ref hash, ((Object)(object)val != (Object)null) ? (((object)val).GetType().FullName + ":" + ((Object)val).name + ";") : "null;"); } } return hash; } private static void Add(ref uint hash, string text) { foreach (char c in text) { hash ^= c; hash *= 16777619u; } } } public sealed class BoatDamageSync { private readonly Transform _boundBoat; private readonly ushort _boatId = ushort.MaxValue; private uint _layoutHash; private readonly BoatContexts<BoatDamageSync> _fleet; private readonly CoopNet _net; private Transform _cachedBoat; private BoatDamage _damage; private BilgePump[] _pumps = Array.Empty<BilgePump>(); private readonly Dictionary<uint, HashSet<ushort>> _heldPumpsByActor = new Dictionary<uint, HashSet<ushort>>(); private readonly Dictionary<uint, float> _pumpSeen = new Dictionary<uint, float>(); private float _sendTimer; private uint _revision; private readonly ItemStateGate _stateGate = new ItemStateGate(); private string _lastPump = "—"; private long _lastPumpTick; private string _lastRepair = "—"; private long _lastRepairTick; private const float PumpInput = 50f; public float SnapshotHz = 4f; private readonly ChangeStream _stream = new ChangeStream(); private float _sentWater = float.NaN; private float _sentHull = float.NaN; private float _sentOakum = float.NaN; private float _sentIntake = float.NaN; private bool _sentSunk; private bool _resyncAsked; public static BoatDamageSync Instance { get; private set; } public string DamageText { get { if (_fleet != null) { return _fleet.Describe(delegate(BoatDamageSync c) { ushort boatId = c._boatId; return "boat " + boatId + ": " + c.DamageText; }); } if ((Object)(object)_damage == (Object)null) { return "no BoatDamage"; } string text = "—"; if (_lastPumpTick != 0L) { long num = _net.Clock.ServerTick - _lastPumpTick; if (num < 0) { num = 0L; } text = _lastPump + " " + num + "ms"; } string text2 = "—"; if (_lastRepairTick != 0L) { long num2 = _net.Clock.ServerTick - _lastRepairTick; if (num2 < 0) { num2 = 0L; } text2 = _lastRepair + " " + num2 + "ms"; } return "water=" + _damage.waterLevel.ToString("0.000") + " hull=" + _damage.hullDamage.ToString("0.000") + " oakum=" + _damage.oakum.ToString("0.0") + " pump " + ActivePumpCount() + "/" + _pumps.Length + " · " + text + " · " + text2; } } public BoatDamageSync(CoopNet net) { _net = net; Instance = this; _fleet = new BoatContexts<BoatDamageSync>((Transform boat, ushort id) => new BoatDamageSync(net, boat, id), delegate(BoatDamageSync c) { c.Clear(); }, delegate(Transform boat) { Component[][] array = new Component[2][]; Component[] componentsInChildren = (Component[])(object)((Component)boat).GetComponentsInChildren<BoatDamage>(true); array[0] = componentsInChildren; componentsInChildren = (Component[])(object)((Component)boat).GetComponentsInChildren<BilgePump>(true); array[1] = componentsInChildren; return BoatLayout.Stamp(array); }); } private BoatDamageSync(CoopNet net, Transform boat, ushort id) { _net = net; _boundBoat = boat; _boatId = id; RefreshBoat(); } public void Tick(float dt) { if (_fleet != null) { foreach (BoatDamageSync value in _fleet.Values) { value.Tick(dt); } return; } if (_net.State == LinkState.Connected) { RefreshBoat(); if (_net.Role == Role.Host) { ApplyRemotePumps(dt); SendSnapshot(dt); } else if (_net.Role == Role.Client) { AskResync(); } } } public void SetRemotePump(ushort boatId, BilgePump pump, bool down, uint actorNetId) { if (_fleet != null) { _fleet.Get(boatId)?.SetRemotePump(boatId, pump, down, actorNetId); } else { if (_net.Role != Role.Host) { return; } RefreshBoat(); int num = Array.IndexOf(_pumps, pump); if (num < 0 || actorNetId == 0) { return; } ushort num2 = (ushort)num; if (num2 >= _pumps.Length) { Plugin.Logger.LogWarning("[BoatDamageSync] Pump request #" + num2 + ": boat only has " + _pumps.Length); return; } if (!_heldPumpsByActor.TryGetValue(actorNetId, out var value)) { value = new HashSet<ushort>(); _heldPumpsByActor[actorNetId] = value; } bool num3 = (down ? value.Add(num2) : value.Remove(num2)); _pumpSeen[actorNetId] = Time.unscaledTime; if (value.Count == 0) { _heldPumpsByActor.Remove(actorNetId); } if (num3) { RememberPump((down ? "in down" : "in up") + " #" + num2 + " p" + actorNetId); Plugin.Logger.LogInfo("[BoatDamageSync] Pump #" + num2 + " from player " + actorNetId + ": " + (down ? "held" : "released")); } } } public void ClearRemoteActor(uint actorNetId) { if (_fleet != null) { foreach (BoatDamageSync item in _fleet.Existing) { item.ClearRemoteActor(actorNetId); } return; } _pumpSeen.Remove(actorNetId); if (_heldPumpsByActor.Remove(actorNetId)) { Plugin.Logger.LogInfo("[BoatDamageSync] Cleared pump holds for player " + actorNetId); } } public void OnDamageState(BoatDamageStateMsg msg, NetPeer fromPeer) { bool newRevision; bool newPose; if (_fleet != null) { if (GameState.playing && !GameState.currentlyLoading && _net.IsHostPeer(fromPeer)) { BoatDamageSync boatDamageSync = _fleet.Get(msg.BoatIndex); if (boatDamageSync != null && BoatLayout.Matches(msg.BoatIndex, msg.LayoutHash, boatDamageSync._layoutHash, "BoatDamageSync")) { boatDamageSync.OnDamageState(msg, fromPeer); } } } else if (_net.Role == Role.Client && _stateGate.Receive(msg.Revision, msg.Tick, 0u, 0u, 0u, out newRevision, out newPose)) { RefreshBoat(); if (!((Object)(object)_damage == (Object)null)) { _damage.waterLevel = msg.WaterLevel; _damage.hullDamage = msg.HullDamage; _damage.oakum = msg.Oakum; _damage.waterIntakeChunk = msg.WaterIntakeChunk; _damage.sunk = msg.Sunk; } } } public void NotifyLocalDamageAction(DamageAction action, float amount, BoatDamage damage) { if (InteractionContext.Suppressed) { return; } if (_fleet != null) { foreach (BoatDamageSync value in _fleet.Values) { if ((Object)(object)value._damage == (Object)(object)damage && (Object)(object)damage != (Object)null) { value.NotifyLocalDamageAction(action, amount, damage); break; } } return; } DamageRequestMsg damageRequestMsg = new DamageRequestMsg { BoatIndex = _boatId, LayoutHash = _layoutHash, Action = action, Amount = amount }; if (!ItemOperationCapture.Damage(damageRequestMsg) && _net.Role == Role.Client && _net.State == LinkState.Connected && !(amount <= 1E-05f)) { _net.Broadcast(damageRequestMsg, (DeliveryMethod)2); RememberRepair("out " + action.ToString() + " +" + amount.ToString("0.000")); } } public void OnDamageRequest(DamageRequestMsg msg, NetPeer fromPeer) { if (_fleet != null) { if (GameState.playing && !GameState.currentlyLoading && _net.Role == Role.Host && _net.PlayerNetIdForPeer(fromPeer) != 0) { BoatDamageSync boatDamageSync = _fleet.Get(msg.BoatIndex); if (boatDamageSync != null && BoatLayout.Matches(msg.BoatIndex, msg.LayoutHash, boatDamageSync._layoutHash, "BoatDamageSync")) { boatDamageSync.OnDamageRequest(msg, fromPeer); } } } else { if (_net.Role != Role.Host) { return; } RefreshBoat(); if (!((Object)(object)_damage == (Object)null)) { if (msg.Action == DamageAction.AddOakum) { BoatDamage damage = _damage; damage.oakum += msg.Amount; RememberRepair("in oakum +" + msg.Amount.ToString("0.000")); } else if (msg.Action == DamageAction.BailWater) { _damage.waterLevel = Mathf.Max(0f, _damage.waterLevel - msg.Amount); RememberRepair("in water -" + msg.Amount.ToString("0.000")); } BroadcastSnapshot(); } } } public void Resync(ushort boat) { (_fleet?.Get(boat))?._stream.Reset(); } private void AskResync() { if (!_resyncAsked && GameState.playing && !GameState.currentlyLoading) { _resyncAsked = true; _net.Broadcast(new ResyncRequestMsg { BoatIndex = _boatId, Domain = ResyncDomain.Damage }, (DeliveryMethod)2); } } private void SendSnapshot(float dt) { if ((Object)(object)_damage == (Object)null) { return; } float num = 1f / Mathf.Max(0.5f, SnapshotHz); _sendTimer += dt; if (!(_sendTimer < num)) { _sendTimer = 0f; bool changed = !_damage.waterLevel.Equals(_sentWater) || !_damage.hullDamage.Equals(_sentHull) || !_damage.oakum.Equals(_sentOakum) || !_damage.waterIntakeChunk.Equals(_sentIntake) || _damage.sunk != _sentSunk; StreamSend streamSend = _stream.Next(changed); if (streamSend != StreamSend.None) { BroadcastSnapshot(streamSend == StreamSend.Reliable, periodic: true); } } } internal BoatDamageStateMsg OperationState(ushort boatId) { if (_fleet != null) { return _fleet.Get(boatId)?.OperationState(boatId); } RefreshBoat(); if ((Object)(object)_damage == (Object)null) { return null; } _revision = SessionCounters.NextRevision(); return new BoatDamageStateMsg { BoatIndex = _boatId, LayoutHash = _layoutHash, Generation = BoatGenerationBook.Session.Get(_boatId), Revision = _revision, Tick = _net.Clock.ServerTick, WaterLevel = _damage.waterLevel, HullDamage = _damage.hullDamage, Oakum = _damage.oakum, WaterIntakeChunk = _damage.waterIntakeChunk, Sunk = _damage.sunk }; } internal void ApplyOperationDamage(DamageRequestMsg msg) { if (_fleet != null) { ShipyardSync instance = ShipyardSync.Instance; if (instance != null && instance.DeferEmbedded(msg, delegate { ApplyOperationDamage(msg); })) { return; } } if (_fleet != null) { _fleet.Get(msg.BoatIndex)?.ApplyOperationDamage(msg); return; } RefreshBoat(); if (!((Object)(object)_damage == (Object)null)) { if (msg.Action == DamageAction.AddOakum) { BoatDamage damage = _damage; damage.oakum += msg.Amount; } if (msg.Action == DamageAction.BailWater) { _damage.waterLevel = Mathf.Max(0f, _damage.waterLevel - msg.Amount); } } } internal void ApplyOperationState(BoatDamageStateMsg msg) { if (_fleet != null) { ShipyardSync instance = ShipyardSync.Instance; if (instance != null && instance.DeferEmbedded(msg, delegate { ApplyOperationState(msg); })) { return; } } if (_fleet != null) { _fleet.Get(msg.BoatIndex)?.ApplyOperationState(msg); } else if (BoatGenerationBook.Session.Compare(msg.BoatIndex, msg.Generation) == GenerationOrder.Current) { OnDamageState(msg, null); } } private void BroadcastSnapshot(bool reliable = false, bool periodic = false) { BoatDamageStateMsg boatDamageStateMsg = OperationState(_boatId); if (boatDamageStateMsg != null) { if (!periodic) { _stream.Sent(reliable); } _sentWater = boatDamageStateMsg.WaterLevel; _sentHull = boatDamageStateMsg.HullDamage; _sentOakum = boatDamageStateMsg.Oakum; _sentIntake = boatDamageStateMsg.WaterIntakeChunk; _sentSunk = boatDamageStateMsg.Sunk; _net.Broadcast(boatDamageStateMsg, (DeliveryMethod)(reliable ? 2 : 4)); } } private void ApplyRemotePumps(float dt) { List<uint> list = new List<uint>(); foreach (uint key in _heldPumpsByActor.Keys) { if (!_pumpSeen.TryGetValue(key, out var value) || Time.unscaledTime - value > 1f) { list.Add(key); } } foreach (uint item in list) { ClearRemoteActor(item); } if ((Object)(object)_damage == (Object)null || _damage.sunk || _heldPumpsByActor.Count == 0) { return; } bool flag = false; for (int i = 0; i < _pumps.Length; i++) { if (IsPumpHeld((ushort)i)) { BilgePump val = _pumps[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.damage == (Object)null)) { float waterLevel = val.damage.waterLevel; val.damage.waterLevel = Mathf.Clamp01(val.damage.waterLevel - dt * 50f * val.drainRate); RotatePumpVisual(val, dt); flag = flag || val.damage.waterLevel != waterLevel; } } } } private static void RotatePumpVisual(BilgePump pump, float dt) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)pump == (Object)null)) { float num = (((Object)(object)pump.damage != (Object)null && pump.damage.waterLevel > 0f) ? 0.75f : 2.25f); ((Component)pump).transform.Rotate(Vector3.forward, 50f * dt * 1.4f * pump.rotationSpeed * num, (Space)1); } } private bool IsPumpHeld(ushort index) { foreach (HashSet<ushort> value in _heldPumpsByActor.Values) { if (value.Contains(index)) { return true; } } return false; } private int ActivePumpCount() { int num = 0; for (int i = 0; i < _pumps.Length; i++) { if (IsPumpHeld((ushort)i)) { num++; } } return num; } private void RefreshBoat() { Transform boundBoat = _boundBoat; if (!((Object)(object)boundBoat == (Object)(object)_cachedBoat)) { _cachedBoat = boundBoat; _heldPumpsByActor.Clear(); _pumpSeen.Clear(); _sendTimer = 0f; if ((Object)(object)boundBoat == (Object)null) { _damage = null; _pumps = Array.Empty<BilgePump>(); return; } _damage = ((Component)boundBoat).GetComponent<BoatDamage>() ?? ((Component)boundBoat).GetComponentInParent<BoatDamage>() ?? ((Component)boundBoat).GetComponentInChildren<BoatDamage>(true); _pumps = ((Component)boundBoat).GetComponentsInChildren<BilgePump>(true); Component[][] array = new Component[2][]; Component[] componentsInChildren = (Component[])(object)((Component)boundBoat).GetComponentsInChildren<BoatDamage>(true); array[0] = componentsInChildren; componentsInChildren = (Component[])(object)_pumps; array[1] = componentsInChildren; _layoutHash = BoatLayout.Hash(boundBoat, array); Plugin.Logger.LogInfo("[BoatDamageSync] Boat changed: damage=" + ((Object)(object)_damage != (Object)null) + ", pumps=" + _pumps.Length + " ('" + ((Object)boundBoat).name + "')"); } } private void RememberPump(string text) { _lastPump = text; _lastPumpTick = _net.Clock.ServerTick; } private void RememberRepair(string text) { _lastRepair = text; _lastRepairTick = _net.Clock.ServerTick; } public void InvalidateHull(ushort id) { _fleet?.Invalidate(id); } public void Clear() { if (_fleet != null) { _fleet.Clear(); return; } _cachedBoat = null; _damage = null; _stateGate.Clear(); _revision = 0u; _pumps = Array.Empty<BilgePump>(); _heldPumpsByActor.Clear(); _pumpSeen.Clear(); _sendTimer = 0f; _lastPump = "—"; _lastPumpTick = 0L; _lastRepair = "—"; _lastRepairTick = 0L; } } public static class BoatDamagePatches { private struct WaterBailState { public float Water; public float Amount; public float Health; } private static void WarnPatch(string text) { try { Plugin.Logger?.LogWarning(text); } catch { } } public static void Apply(Harmony harmony) { bool flag = TryPatch(harmony, typeof(HullDamageButton), "OnItemClick", new Type[1] { typeof(PickupableItem) }, "PreHullOakum", "PostHullOakum"); bool flag2 = TryPatch(harmony, typeof(BoatDamageWaterButton), "OnItemClick", new Type[1] { typeof(PickupableItem) }, "PreWaterBail", "PostWaterBail"); bool flag3 = TryPatch(harmony, typeof(ShipItemOakum), "OnAltActivate", Type.EmptyTypes, "PreOakumAlt", "PostOakumAlt"); Plugin.Logger.LogInfo("[BoatDamagePatches] Damage patches: HullOakum=" + flag + ", WaterBail=" + flag2 + ", OakumAlt=" + flag3); PatchHealth.Report("Damage", (flag ? 1 : 0) + (flag2 ? 1 : 0) + (flag3 ? 1 : 0), 3); } private static bool TryPatch(Harmony harmony, Type type, string method, Type[] args, string prefixName, string postfixName) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown try { MethodInfo method2 = type.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (method2 == null) { WarnPatch("[BoatDamagePatches] Not found " + type.Name + "." + method); return false; } HarmonyMethod val = new HarmonyMethod(typeof(BoatDamagePatches).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic)); HarmonyMethod val2 = new HarmonyMethod(typeof(BoatDamagePatches).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method2, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { WarnPatch("[BoatDamagePatches] " + type.Name + "." + method + ": " + ex.Message); return false; } } private static void PreHullOakum(HullDamageButton __instance, out float __state) { __state = ReadOakum(GetHullDamage(__instance)); } private static void PostHullOakum(HullDamageButton __instance, float __state) { try { float num = ReadOakum(GetHullDamage(__instance)) - __state; if (num > 1E-05f) { BoatDamageSync.Instance?.NotifyLocalDamageAction(DamageAction.AddOakum, num, GetHullDamage(__instance)); } } catch (Exception ex) { WarnPatch("[BoatDamagePatches] PostHullOakum: " + ex.Message); } } private static void PreWaterBail(BoatDamageWaterButton __instance, PickupableItem __0, out WaterBailState __state) { __state = new WaterBailState { Water = ReadWater(GetWaterDamage(__instance)) }; ShipItemBottle val = (ShipItemBottle)(object)((__0 is ShipItemBottle) ? __0 : null); if ((Object)(object)val != (Object)null) { __state.Amount = ((ShipItem)val).amount; __state.Health = ((ShipItem)val).health; } } private static void PostWaterBail(BoatDamageWaterButton __instance, PickupableItem __0, WaterBailState __state) { try { float num = ReadWater(GetWaterDamage(__instance)); float num2 = __state.Water - num; ShipItemBottle val = (ShipItemBottle)(object)((__0 is ShipItemBottle) ? __0 : null); bool flag = (Object)(object)val != (Object)null && (Mathf.Abs(((ShipItem)val).amount - __state.Amount) > 0.0001f || Mathf.Abs(((ShipItem)val).health - __state.Health) > 0.0001f); if (num2 > 1E-05f) { BoatDamageSync.Instance?.NotifyLocalDamageAction(DamageAction.BailWater, num2, GetWaterDamage(__instance)); } if (flag) { ItemSync.Instance?.NotifyHeldItemStateChanged((ShipItem)(object)val, "bail-water"); } if (num2 > 1E-05f || flag) { Plugin.Logger.LogInfo("[BoatDamagePatches] WaterBail item=" + (((Object)(object)__0 != (Object)null) ? ((object)__0).GetType().Name : "null") + " delta=" + num2.ToString("0.####") + " amount " + __state.Amount.ToString("0.##") + "->" + (((Object)(object)val != (Object)null) ? ((ShipItem)val).amount.ToString("0.##") : "?") + " health " + __state.Health.ToString("0.##") + "->" + (((Object)(object)val != (Object)null) ? ((ShipItem)val).health.ToString("0.##") : "?")); } } catch (Exception ex) { WarnPatch("[BoatDamagePatches] PostWaterBail: " + ex.Message); } } private static void PreOakumAlt(ShipItemOakum __instance, out float __state) { __state = ReadOakum(CurrentBoatDamage()); } private static void PostOakumAlt(ShipItemOakum __instance, float __state) { try { float num = ReadOakum(CurrentBoatDamage()) - __state; if (num > 1E-05f) { BoatDamageSync.Instance?.NotifyLocalDamageAction(DamageAction.AddOakum, num, CurrentBoatDamage()); } } catch (Exception ex) { WarnPatch("[BoatDamagePatches] PostOakumAlt: " + ex.Message); } } private static BoatDamage GetHullDamage(HullDamageButton button) { HullDamageTexture val = (((Object)(object)button != (Object)null) ? ((Component)button).GetComponent<HullDamageTexture>() : null); if (!((Object)(object)val != (Object)null)) { return null; } return val.damage; } private static BoatDamage GetWaterDamage(BoatDamageWaterButton button) { BoatDamageWater val = (((Object)(object)button != (Object)null) ? ((Component)button).GetComponent<BoatDamageWater>() : null); if (!((Object)(object)val != (Object)null)) { return null; } return val.damage; } private static BoatDamage CurrentBoatDamage() { try { return ((Object)(object)GameState.currentBoat != (Object)null && (Object)(object)GameState.currentBoat.parent != (Object)null) ? ((Component)GameState.currentBoat.parent).GetComponent<BoatDamage>() : null; } catch { return null; } } private static float ReadOakum(BoatDamage damage) { if (!((Object)(object)damage != (Object)null)) { return 0f; } return damage.oakum; } private static float ReadWater(BoatDamage damage) { if (!((Object)(object)damage != (Object)null)) { return 0f; } return damage.waterLevel; } } public static class BoatLocator { private struct Entry { public ushort Id; public Transform Boat; public SaveableObject Saveable; public bool Purchasable; } public const ushort NoBoat = ushort.MaxValue; public const float CacheSeconds = 1f; private static readonly Dictionary<ushort, Entry> _byId = new Dictionary<ushort, Entry>(); private static readonly Dictionary<Transform, ushort> _idOf = new Dictionary<Transform, ushort>(); private static readonly List<Transform> _owned = new List<Transform>(); private static readonly List<Entry> _ownedScratch = new List<Entry>(); private static readonly List<KeyValuePair<string, Transform>> _hullScratch = new List<KeyValuePair<string, Transform>>(); private static float _cacheStamp = float.NegativeInfinity; private static bool _haveScan; private static int _setEpoch; private static string _lastSignature = ""; public static bool IndicesAuthoritative { get { EnsureScan(); return _owned.Count > 0; } } public static int SetEpoch => _setEpoch; public static List<Transform> FindBoats() { EnsureScan(); return _owned; } public static Transform FirstBoat() { List<Transform> list = FindBoats(); if (list.Count <= 0) { return null; } return list[0]; } public static void Invalidate() { _cacheStamp = float.NegativeInfinity; } public static Transform FindByIndex(ushort index) { if (index == ushort.MaxValue) { return null; } EnsureScan(); if (_byId.TryGetValue(index, out var value) && (Object)(object)value.Boat != (Object)null) { return value.Boat; } return null; } public static ushort IndexOf(Transform boat) { if ((Object)(object)boat == (Object)null) { return ushort.MaxValue; } EnsureScan(); if (!_idOf.TryGetValue(boat, out var value)) { return ushort.MaxValue; } return value; } public static int SceneIndexOf(ushort index) { if (index != ushort.MaxValue) { return index & 0xFF; } return -1; } private static void EnsureScan() { if (!_haveScan || !(Time.unscaledTime - _cacheStamp < 1f) || HasDestroyed()) { Rescan(); } } private static bool HasDestroyed() { foreach (KeyValuePair<ushort, Entry> item in _byId) { if ((Object)(object)item.Value.Boat == (Object)null || (Object)(object)item.Value.Saveable == (Object)null) { return true; } } return false; } private static void Rescan() { _byId.Clear(); _idOf.Clear(); _owned.Clear(); _ownedScratch.Clear(); SaveableObject[] array = null; try { if ((Object)(object)SaveLoadManager.instance != (Object)null) { array = SaveLoadManager.instance.GetCurrentObjects(); } } catch (Exception ex) { Plugin.Logger.LogWarning("[BoatLocator] SaveLoadManager registry unavailable: " + ex.Message); } if (array != null) { for (int i = 0; i < array.Length && i <= 255; i++) { SaveableObject val = array[i]; if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).GetComponent("BoatProbes") == (Object)null) { continue; } _hullScratch.Clear(); BoatEmbarkCollider[] componentsInChildren = ((Component)val).GetComponentsInChildren<BoatEmbarkCollider>(true); foreach (BoatEmbarkCollider val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null || (Object)(object)((Component)val2).transform.parent == (Object)null) { continue; } Transform parent = ((Component)val2).transform.parent; bool flag = false; for (int k = 0; k < _hullScratch.Count; k++) { if ((Object)(object)_hullScratch[k].Value == (Object)(object)parent) { flag = true; break; } } if (!flag) { _hullScratch.Add(new KeyValuePair<string, Transform>(PathOf(parent), parent)); } } if (_hullScratch.Count == 0) { continue; } _hullScratch.Sort((KeyValuePair<string, Transform> a, KeyValuePair<string, Transform> b) => string.CompareOrdinal(a.Key, b.Key)); bool purchasable = (Object)(object)((Component)val).GetComponent("PurchasableBoat") != (Object)null; for (int num = 0; num < _hullScratch.Count && num <= 255; num++) { ushort num2 = (ushort)((num << 8) | i); if (num2 != ushort.MaxValue) { Entry entry = new Entry { Id = num2, Boat = _hullScratch[num].Value, Saveable = val, Purchasable = purchasable }; _byId[num2] = entry; _idOf[entry.Boat] = num2; if (val.extraSetting) { _ownedScratch.Add(entry); } } } } } _ownedScratch.Sort((Entry a, Entry b) => (a.Purchasable != b.Purchasable) ? (a.Purchasable ? 1 : (-1)) : a.Id.CompareTo(b.Id)); for (int num3 = 0; num3 < _ownedScratch.Count; num3++) { _owned.Add(_ownedScratch[num3].Boat); } _haveScan = _byId.Count > 0; _cacheStamp = Time.unscaledTime; string text = Signature(withActivity: false); if (text != _lastSignature) { _setEpoch++; _lastSignature = text; if (_byId.Count > 0) { Plugin.Logger.LogInfo("[BoatLocator] Boats: " + _byId.Count + " saveable, owned " + Signature(withActivity: true)); } } } private static string Signature(bool withActivity) { if (_ownedScratch.Count == 0) { if (_byId.Count <= 0) { return ""; } return "[]"; } StringBuilder stringBuilder = new StringBuilder("["); for (int i = 0; i < _ownedScratch.Count; i++) { if (i > 0) { stringBuilder.Append(", "); } stringBuilder.Append('#').Append(_ownedScratch[i].Id).Append(' ') .Append(((Object)_ownedScratch[i].Boat).name); if (withActivity && !((Component)_ownedScratch[i].Boat).gameObject.activeInHierarchy) { stringBuilder.Append(" (hidden)"); } } return stringBuilder.Append(']').ToString(); } public static string PathOf(Transform t) { if ((Object)(object)t == (Object)null) { return ""; } string text = ((Object)t).name; Transform parent = t.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } public sealed class BoatSync { private sealed class HostBoat { public ushort Index; public uint NetId; public Transform Boat; public Vector3 LastRealPos; public long LastRealTick; public bool HaveLast; } private sealed class ClientBoat { public ushort Index; public Transform Boat; public readonly NetTransform Net = new NetTransform(); public Rigidbody Rb; public bool PrevKinematic; public RigidbodyInterpolation PrevInterp; public Component PhysSwitcher; public bool PrevPaused; public bool PrevSwitcherEnabled; public bool Engaged; } private const int SamplesBeforeEngage = 2; private readonly CoopNet _net; private readonly Dictionary<ushort, HostBoat> _hostBoats = new Dictionary<ushort, HostBoat>(); private readonly Dictionary<ushort, ClientBoat> _clientBoats = new Dictionary<ushort, ClientBoat>(); private readonly HashSet<ushort> _warnedUnknown = new HashSet<ushort>(); private float _sendTimer; private float _refreshTimer; private uint _firstBoatNetId; private static Type _physSwitcherType; public float InterpDelayMs = 100f; public int SnapshotHz = 20; public Func<Transform> LocalPlayerProvider; public Func<Transform> LocalBoatProvider; public Func<Transform> ResolveLocalBoatProvider; public bool IsSlaving { get { foreach (ClientBoat value in _clientBoats.Values) { if (value.Engaged) { return true; } } return false; } } public uint BoatNetId => _firstBoatNetId; public int BoatCount { get { if (_net.Role != Role.Host) { return _clientBoats.Count; } return _hostBoats.Count; } } public BoatSync(CoopNet net) { _net = net; } public void Tick(float dt) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Host || _net.State != LinkState.Connected || !CoordSpace.Ready) { return; } RefreshHostBoats(dt); if (_hostBoats.Count == 0) { return; } float num = 1f / (float)Mathf.Max(1, SnapshotHz); _sendTimer += dt; if (_sendTimer < num) { return; } _sendTimer = 0f; long serverTick = _net.Clock.ServerTick; foreach (HostBoat value in _hostBoats.Values) { if ((Object)(object)value.Boat == (Object)null || ShipyardSync.Suspended(value.Index)) { value.HaveLast = false; continue; } Vector3 val = CoordSpace.LocalToReal(value.Boat.position); Vector3 realVel = Vector3.zero; if (value.HaveLast) { float num2 = (float)(serverTick - value.LastRealTick) / 1000f; if (num2 > 0.0001f) { realVel = (val - value.LastRealPos) / num2; } } value.LastRealPos = val; value.LastRealTick = serverTick; value.HaveLast = true; _net.Broadcast(new BoatStateMsg { NetId = value.NetId, BoatIndex = value.Index, Tick = serverTick, RealPos = val, Rot = value.Boat.rotation, RealVel = realVel }, (DeliveryMethod)4); } } public void OnBoatState(BoatStateMsg msg, NetPeer fromPeer) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Client || !InWorld()) { return; } Transform val = BoatLocator.FindByIndex(msg.BoatIndex); if ((Object)(object)val == (Object)null) { if (!_warnedUnknown.Contains(msg.BoatIndex) && BoatLocator.IndicesAuthoritative) { _warnedUnknown.Add(msg.BoatIndex); Plugin.Logger.LogWarning("[BoatSync] Host streams boat #" + msg.BoatIndex + " (sceneIndex " + BoatLocator.SceneIndexOf(msg.BoatIndex) + ") but this world has no such boat - worlds differ?"); } } else { ClientBoat clientBoat = EnsureClientBoat(msg.BoatIndex, msg.NetId, val); clientBoat.Net.InterpDelayMs = InterpDelayMs; clientBoat.Net.Push(msg.Tick, msg.RealPos, msg.Rot, msg.RealVel); } } public void ApplyRemote() { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (_net.Role != Role.Client || !CoordSpace.Ready || Time.timeScale <= 0.0001f) { return; } foreach (ClientBoat value in _clientBoats.Values) { if ((Object)(object)value.Boat == (Object)null || !value.Net.HasData || ShipyardSync.Suspended(value.Index)) { continue; } if (!value.Engaged) { if (value.Net.SampleCount >= 2) { EngageSlave(value); } continue; } if ((Object)(object)value.Rb != (Object)null && (int)value.Rb.interpolation != 0) { value.Rb.interpolation = (RigidbodyInterpolation)0; } value.Net.Apply(value.Boat, _net.Clock.ServerTick); } } private static bool InWorld() { try { return GameState.playing && !GameState.currentlyLoading; } catch { return false; } } public Transform GetBoatByIndex(ushort index) { if (_net.Role == Role.Client && _clientBoats.TryGetValue(index, out var value) && (Object)(object)value.Boat != (Object)null) { return value.Boat; } if (_net.Role == Role.Host && _hostBoats.TryGetValue(index, out var value2) && (Object)(object)value2.Boat != (Object)null) { return value2.Boat; } return BoatLocator.FindByIndex(index); } private void RefreshHostBoats(float dt) { _refreshTimer += dt; if (_refreshTimer < 1f && _hostBoats.Count > 0) { return; } _refreshTimer = 0f; List<Transform> list = BoatLocator.FindBoats(); if (!BoatLocator.IndicesAuthoritative) { return; } HashSet<ushort> hashSet = new HashSet<ushort>(); for (int i = 0; i < list.Count; i++) { Transform val = list[i]; if ((Object)(object)val == (Object)null) { continue; } ushort num = BoatLocator.IndexOf(val); if (num == ushort.MaxValue) { continue; } hashSet.Add(num); if (!_hostBoats.TryGetValue(num, out var value)) { value = new HostBoat { Index = num, NetId = _net.Registry.AllocateId(), Boat = val }; _hostBoats[num] = value; _net.Registry.Register(value.NetId, NetObjKind.Boat, 0u, val); if (_firstBoatNetId == 0) { _firstBoatNetId = value.NetId; } Plugin.Logger.LogInfo("[BoatSync] Boat #" + num + " registered NetId=" + value.NetId + " ('" + BoatLocator.PathOf(val) + "')"); } else if ((Object)(object)value.Boat != (Object)(object)val) { value.Boat = val; value.HaveLast = false; _net.Registry.Register(value.NetId, NetObjKind.Boat, 0u, val); Plugin.Logger.LogInfo("[BoatSync] Boat #" + num + " rebound ('" + BoatLocator.PathOf(val) + "')"); } } List<ushort> list2 = new List<ushort>(); foreach (KeyValuePair<ushort, HostBoat> hostBoat in _hostBoats) { if (!hashSet.Contains(hostBoat.Key)) { list2.Add(hostBoat.Key); } } foreach (ushort item in list2) { _net.Registry.Remove(_hostBoats[item].NetId); _hostBoats.Remove(item); } } private ClientBoat EnsureClientBoat(ushort index, uint netId, Transform boat) { if (_clientBoats.TryGetValue(index, out var value)) { if ((Object)(object)value.Boat == (Object)(object)boat) { return value; } RestoreClientBoat(value); _clientBoats.Remove(index); } value = new ClientBoat { Index = index, Boat = boat }; _clientBoats[index] = value; _net.Registry.Register(netId, NetObjKind.Boat, 0u, boat); if (_firstBoatNetId == 0) { _firstBoatNetId = netId; } Plugin.Logger.LogInfo("[BoatSync] Client boat #" + index + " tracked: NetId=" + netId + " ('" + BoatLocator.PathOf(boat) + "'), buffering before slave mode"); return value; } private void EngageSlave(ClientBoat cb) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) Transform boat = cb.Boat; Transform val = ((ResolveLocalBoatProvider != null) ? ResolveLocalBoatProvider() : ((LocalBoatProvider != null) ? LocalBoatProvider() : null)); Transform val2 = ((LocalPlayerProvider != null) ? LocalPlayerProvider() : null); bool flag = (Object)(object)val2 != (Object)null && (Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)boat; Vector3 val3 = Vector3.zero; Quaternion val4 = Quaternion.identity; if (flag) { val3 = boat.InverseTransformPoint(val2.position); val4 = Quaternion.Inverse(boat.rotation) * val2.rotation; } Vector3 position = boat.position; cb.Rb = ((Component)boat).GetComponent<Rigidbody>(); if ((Object)(object)cb.Rb != (Object)null) { cb.PrevKinematic = cb.Rb.isKinematic; cb.PrevInterp = cb.Rb.interpolation; cb.Rb.isKinematic = true; cb.Rb.interpolation = (RigidbodyInterpolation)0; } TrySetPhysicsPaused(cb, paused: true); TrySetSwitcherEnabled(cb, slave: true); cb.Engaged = true; cb.Net.Apply(boat, _net.Clock.ServerTick); if (flag) { val2.position = boat.TransformPoint(val3); val2.rotation = boat.rotation * val4; } Physics.SyncTransforms(); float num = Vector3.Distance(position, boat.position); Plugin.Logger.LogInfo("[BoatSync] Client boat #" + cb.Index + " in slave mode: correction=" + num.ToString("F1") + " m, carriedPlayer=" + flag + ", rb=" + ((Object)(object)cb.Rb != (Object)null) + ", physSwitcher=" + ((Object)(object)cb.PhysSwitcher != (Object)null)); if (num > 2000f) { Plugin.Logger.LogWarning("[BoatSync] Boat #" + cb.Index + " correction is " + num.ToString("F0") + " m - the client very likely loaded a different world than the host's."); } } private void RestoreClientBoat(ClientBoat cb) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (cb == null) { return; } if (cb.Engaged) { if ((Object)(object)cb.Rb != (Object)null) { cb.Rb.isKinematic = cb.PrevKinematic; cb.Rb.interpolation = cb.PrevInterp; } TrySetSwitcherEnabled(cb, slave: false); TrySetPhysicsPaused(cb, paused: false, restore: true); cb.Engaged = false; } cb.Net.Clear(); cb.Rb = null; cb.PhysSwitcher = null; cb.Boat = null; } private void TrySetSwitcherEnabled(ClientBoat cb, bool slave) { try { if (cb == null || (Object)(object)cb.Boat == (Object)null) { return; } if ((Object)(object)cb.PhysSwitcher == (Object)null) { if (_physSwitcherType == null) { _physSwitcherType = Type.GetType("BoatPhysicsSwitcher, Assembly-CSharp"); } if (_physSwitcherType == null) { return; } cb.PhysSwitcher = ((Component)cb.Boat).GetComponentInChildren(_physSwitcherType); } Component physSwitcher = cb.PhysSwitcher; Behaviour val = (Behaviour)(object)((physSwitcher is Behaviour) ? physSwitcher : null); if (!((Object)(object)val == (Object)null)) { if (slave) { cb.PrevSwitcherEnabled = val.enabled; val.enabled = false; } else { val.enabled = cb.PrevSwitcherEnabled; } } } catch (Exception ex) { Plugin.Logger.LogWarning("[BoatSync] BoatPhysicsSwitcher enable-toggle failed: " + ex.Message); } } private void TrySetPhysicsPaused(ClientBoat cb, bool paused, bool restore = false) { try { if (cb == null || (Object)(object)cb.Boat == (Object)null) { return; } if (_physSwitcherType == null) { _physSwitcherType = Type.GetType("BoatPhysicsSwitcher, Assembly-CSharp"); } if (_physSwitcherType == null) { return; } if ((Object)(object)cb.PhysSwitcher == (Object)null) { cb.PhysSwitcher = ((Component)cb.Boat).GetComponentInChildren(_physSwitcherType); } if ((Object)(object)cb.PhysSwitcher == (Object)null) { return; } FieldInfo field = _physSwitcherType.GetField("paused"); PropertyInfo propertyInfo = ((field == null) ? _physSwitcherType.GetProperty("paused") : null); if (!(field == null) || !(propertyInfo == null)) { if (!restore) { cb.PrevPaused = ((field != null) ? ((bool)field.GetValue(cb.PhysSwitcher)) : ((bool)propertyInfo.GetValue(cb.PhysSwitcher, null))); } bool flag = (restore ? cb.PrevPaused : paused); if (field != null) { field.SetValue(cb.PhysSwitcher, flag); } else { propertyInfo.SetValue(cb.PhysSwitcher, flag, null); } } } catch (Exception ex) { Plugin.Logger.LogWarning("[BoatSync] BoatPhysicsSwitcher.paused unavailable: " + ex.Message); } } public void Clear() { foreach (ClientBoat value in _clientBoats.Values) { RestoreClientBoat(value); } _clientBoats.Clear(); _hostBoats.Clear(); _warnedUnknown.Clear(); _firstBoatNetId = 0u; _sendTimer = 0f; _refreshTimer = 0f; BoatLocator.Invalidate(); } } public sealed class BoatTeleport { private const BindingFlags Private = BindingFlags.Instance | BindingFlags.NonPublic; private readonly CoopNet _net; private readonly PlayerSync _players; private PlayerEmbarkerNew _emb; private float _nextEmbarkerScan; private bool _pending; private Transform _deckBoat; private Vector3 _deckPos; private FieldInfo _fCurrentBoat; private MethodInfo _mToWalkCol; private MethodInfo _mEmbark; private bool _reflected; public Action<bool, string> Done; public bool Available { get { if (GameState.playing) { return !GameState.currentlyLoading; } return false; } } public BoatTeleport(CoopNet net, PlayerSync players) { _net = net; _players = players; } public void Request() { _pending = true; } public void Tick() { if (!Available) { _pending = false; return; } RememberDeck(); if (_pending) { _pending = false; bool flag = false; string text; try { flag = Run(out text); } catch (Exception ex) { text = "Teleport failed: " + ex.Message; Plugin.Logger.LogWarning("[BoatTeleport] " + ex); } Plugin.Logger.LogInfo("[BoatTeleport] " + (flag ? "done: " : "refused: ") + text); Done?.Invoke(flag, text); } } private PlayerEmbarkerNew Embarker() { if ((Object)(object)_emb != (Object)null) { return _emb; } if (Time.unscaledTime < _nextEmbarkerScan) { return null; } _nextEmbarkerScan = Time.unscaledTime + 1f; _emb = Object.FindObjectOfType<PlayerEmbarkerNew>(); return _emb; } private void RememberDeck() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) PlayerEmbarkerNew val = Embarker(); CharacterController charController = Refs.charController; if (!((Object)(object)val == (Object)null) && !((Object)(object)charController == (Object)null)) { Transform debugOutCurrentBoat = val.debugOutCurrentBoat; if (!((Object)(object)debugOutCurrentBoat == (Object)null) && !((Object)(object)GameState.currentBoat != (Object)(object)debugOutCurrentBoat) && ((Collider)charController).enabled && charController.isGrounded) { _deckBoat = debugOutCurrentBoat; _deckPos = ((Component)charController).transform.localPosition; } } } private bool Run(out string text) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Expected O, but got Unknown PlayerEmbarkerNew val = Embarker(); CharacterController charController = Refs.charController; if ((Object)(object)val == (Object)null || (Object)(object)charController == (Object)null || (Object)(object)val.playerObserver == (Object)null) { text = "The player is not ready yet"; return false; } if (Object.op_Implicit((Object)(object)GameState.inBed) || !((Collider)charController).enabled) { text = "Not available while asleep or paused"; return false; } bool tooFar; Transform val2 = PickBoat(out tooFar); if ((Object)(object)val2 == (Object)null) { text = (tooFar ? "The boat is too far away to board" : "No boat to teleport to"); return false; } Transform val3 = WalkCopy(val2); if ((Object)(object)val3 == (Object)null) { text = "This boat cannot be boarded"; return false; } if (!PickSpot(val2, val3, charController, out var pos, out var spot)) { text = "No place to stand found on the boat"; return false; } ReleaseHands(); Transform transform = ((Component)charController).transform; bool flag = (Object)(object)GameState.currentBoat == (Object)(object)val2 && (Object)(object)transform.parent == (Object)(object)val3; ((Collider)charController).enabled = false; try { if (flag) { transform.localPosition = pos; val.playerObserver.localPosition = pos; } else if (Reflect()) { val.playerObserver.position = val2.TransformPoint(pos); _fCurrentBoat.SetValue(val, (object?)new EmbarkBoat(val2, val3)); _mToWalkCol.Invoke(val, null); _mEmbark.Invoke(val, null); } else { transform.position = val2.TransformPoint(pos); val.playerObserver.position = transform.position; } } finally { ((Collider)charController).enabled = true; } Physics.SyncTransforms();