Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CookieFx v0.1.1
CookieFx.dll
Decompiled a year 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.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Logging; using CookieFx.Client; using CookieFx.Network; using CookieFx.Network.Payloads; using CookieFx.Patches; using CookieFx.Platform; using CookieFx.Players; using CookieFx.Server; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CookieSylvia")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.1.0")] [assembly: AssemblyInformationalVersion("0.1.1")] [assembly: AssemblyProduct("CookieFx")] [assembly: AssemblyTitle("CookieFx")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CookieFx { internal static class Ensure { public static Guid ServerId(string path) { Guard.NotNullOrWhiteSpace(path, "path"); Directory.CreateDirectory(path); path = Path.Combine(path, ".ServerId"); try { using StreamReader streamReader = File.OpenText(path); if (Guid.TryParse(streamReader.ReadLine(), out var result)) { Logger.Info($"Loaded existing ServerId [{result:D}] from the current save file."); return result; } Logger.Error("ServerId in current save is corrupt and will be reassigned - clients will no longer recognize us."); } catch (FileNotFoundException) { } Guid guid = Guid.NewGuid(); Logger.Info($"Assigning new ServerId [{guid:D}] to current save file."); using StreamWriter streamWriter = File.CreateText(path); streamWriter.Write(guid.ToString("D")); return guid; } } [DebuggerStepThrough] public static class Guard { [DebuggerStepThrough] internal static class Throws { [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentNull(string? paramName) { throw new ArgumentNullException(paramName, "Parameter " + paramName + " must be not null."); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentNullable(string? paramName) { throw new ArgumentException(paramName, "Type parameter " + paramName + " must not be nullable."); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentEmptyGuid(string? paramName) { throw new ArgumentException("Parameter " + paramName + " must not be an empty Guid.", paramName); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentNullOrEmpty(string? value, string? paramName) { if (value == null) { throw new ArgumentNullException(paramName, "Parameter " + paramName + " must be not null."); } throw new ArgumentException("Parameter " + paramName + " must not be empty.", paramName); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentNullOrWhiteSpace(string? value, string? paramName) { if (value == null) { throw new ArgumentNullException(paramName, "Parameter " + paramName + " must be not null."); } throw new ArgumentException("Parameter " + paramName + " must not be empty or whitespace.", paramName); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentMustBeLessThan<T>(T value, T max, string? paramName) { throw new ArgumentOutOfRangeException(paramName, $"Parameter {paramName} ({typeof(T)}) must be less than {max}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentMustBeLessThanOrEqualTo<T>(T value, T max, string? paramName) { throw new ArgumentOutOfRangeException(paramName, $"Parameter {paramName} ({typeof(T)}) must be less than or equal to {max}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentMustBeGreaterThan<T>(T value, T min, string? paramName) { throw new ArgumentOutOfRangeException(paramName, $"Parameter {paramName} ({typeof(T)}) must be greater than {min}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentMustBeGreaterThanOrEqualTo<T>(T value, T min, string? paramName) { throw new ArgumentOutOfRangeException(paramName, $"Parameter {paramName} ({typeof(T)}) must be greater than or equal to {min}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentMustBeBetween<T>(T value, T min, T max, string? paramName) { throw new ArgumentOutOfRangeException(paramName, $"Parameter {paramName} ({typeof(T)}) must be between {min} and {max}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentMustBeBetweenOrEqualTo<T>(T value, T min, T max, string? paramName) { throw new ArgumentOutOfRangeException(paramName, $"Parameter {paramName} ({typeof(T)}) must be between or equal to {min} and {max}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentMustBeTrue(string? message, string? paramName) { if (message == null) { throw new ArgumentException("Paramter " + paramName + " must be true.", paramName); } throw new ArgumentException(message, paramName); } [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] public static void ArgumentMustBeFalse(string? message, string? paramName) { if (message == null) { throw new ArgumentException("Paramter " + paramName + " must be false.", paramName); } throw new ArgumentException(message, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotEmpty(Guid value, [CallerArgumentExpression("value")] string? paramName = null) { if (value == Guid.Empty) { Throws.ArgumentEmptyGuid(paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNull<T>([NotNull] T? value, [CallerArgumentExpression("value")] string? paramName = null) where T : class { if (value == null) { Throws.ArgumentNull(paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNull<T>([NotNull] T? value, [CallerArgumentExpression("value")] string? paramName = null) where T : struct { if (!value.HasValue) { Throws.ArgumentNull(paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNullAny<T>([NotNull] T? value, [CallerArgumentExpression("value")] string? paramName = null) { if (value == null) { Throws.ArgumentNull(paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNullable<T>() { if ((object)Nullable.GetUnderlyingType(typeof(T)) != null) { Throws.ArgumentNullable(typeof(T).Name); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNullable(Type type) { if ((object)Nullable.GetUnderlyingType(type) != null) { Throws.ArgumentNullable(type.Name); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNullOrEmpty(string? value, [CallerArgumentExpression("value")] string? paramName = null) { if (string.IsNullOrEmpty(value)) { Throws.ArgumentNullOrEmpty(value, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNullOrWhiteSpace(string? value, [CallerArgumentExpression("value")] string? paramName = null) { if (string.IsNullOrWhiteSpace(value)) { Throws.ArgumentNullOrWhiteSpace(value, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan<T>(T value, T max, [CallerArgumentExpression("value")] string? paramName = null) where T : IComparable<T> { if (value.CompareTo(max) >= 0) { Throws.ArgumentMustBeLessThan(value, max, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo<T>(T value, T max, [CallerArgumentExpression("value")] string? paramName = null) where T : IComparable<T> { if (value.CompareTo(max) > 0) { Throws.ArgumentMustBeLessThanOrEqualTo(value, max, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan<T>(T value, T min, [CallerArgumentExpression("value")] string? paramName = null) where T : IComparable<T> { if (value.CompareTo(min) <= 0) { Throws.ArgumentMustBeGreaterThan(value, min, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo<T>(T value, T min, [CallerArgumentExpression("value")] string? paramName = null) where T : IComparable<T> { if (value.CompareTo(min) < 0) { Throws.ArgumentMustBeGreaterThanOrEqualTo(value, min, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetween<T>(T value, T min, T max, [CallerArgumentExpression("value")] string? paramName = null) where T : IComparable<T> { if (value.CompareTo(min) <= 0 || value.CompareTo(max) >= 0) { Throws.ArgumentMustBeBetween(value, min, max, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo<T>(T value, T min, T max, [CallerArgumentExpression("value")] string? paramName = null) where T : IComparable<T> { if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) { Throws.ArgumentMustBeBetweenOrEqualTo(value, min, max, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IsTrue([DoesNotReturnIf(false)] bool condition, string? message = null, [CallerArgumentExpression("condition")] string? paramName = null) { if (!condition) { Throws.ArgumentMustBeTrue(message, paramName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IsFalse([DoesNotReturnIf(true)] bool condition, string? message = null, [CallerArgumentExpression("condition")] string? paramName = null) { if (condition) { Throws.ArgumentMustBeFalse(message, paramName); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("CookieFx", "CookieFx", "0.1.1")] public sealed class Plugin : BaseUnityPlugin { private void Awake() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown Logger.Source = new BepInLoggerSource(((BaseUnityPlugin)this).Logger); Harmony val = new Harmony("CookieFx"); val.PatchAll(typeof(Plugin).Assembly); try { MethodInfo methodInfo = AccessTools.Method("DeleteFileButton_BetterSaves:DeleteFile", (Type[])null, (Type[])null); if ((object)methodInfo != null) { MethodInfo methodInfo2 = AccessUtils.MethodInfo((Expression<Func<Func<IEnumerable<CodeInstruction>, IEnumerable<CodeInstruction>>>>)(() => SavePatches.DeleteFileBetterSaves)); val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[Compat] LCBetterSaves: Patched"); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[Compat] LCBetterSaves: Not found"); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"[Compat] LCBetterSaves: Failed\n{arg}"); } FuckThisShitPatches.PatchTheirStuff(val); ((BaseUnityPlugin)this).Logger.LogInfo((object)"CookieFx v0.1.1 loaded."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Icon by Kaede"); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Save Path: " + Application.persistentDataPath)); } } public static class Reflect { public static IEnumerable<Assembly> AllAssemblies() { return from assembly in AppDomain.CurrentDomain.GetAssemblies() where !assembly.FullName.StartsWith("Microsoft.VisualStudio") select assembly; } public static IEnumerable<Type> AllTypes() { return AllAssemblies().SelectMany((Assembly assembly) => assembly.AllTypes()); } public static IEnumerable<Type> AllTypesAssignableTo(Type baseType) { Type baseType2 = baseType; return AllAssemblies().SelectMany((Assembly assembly) => assembly.AllTypesAssignableTo(baseType2)); } public static IEnumerable<Type> AllTypes(this Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { Logger.Warning($"[Reflect] AllTypes in {assembly}: {ex.Message}\n{ex.StackTrace}"); return ex.Types.Where((Type type) => (object)type != null); } } public static IEnumerable<Type> AllTypesAssignableTo(this Assembly assembly, Type baseType) { Type baseType2 = baseType; Assembly assembly2 = assembly; return assembly2.AllTypes().Where(delegate(Type type) { try { return baseType2.IsAssignableFrom(type); } catch (TypeLoadException ex) { Logger.Warning($"[Reflect] AllTypesAssignableTo in {assembly2}: {ex.Message}\n{ex.StackTrace}"); return false; } }); } } internal static class PluginInfo { public const string Guid = "CookieFx"; public const string Name = "CookieFx"; public const string Version = "0.1.1"; } } namespace CookieFx.Server { public abstract class AbstractServer { public Guid ServerId { get; } public Networking Networking { get; } public abstract bool IsIntegrated { get; } public bool IsStopping { get; protected set; } public abstract ServerPlayer? Host { get; } public IEnumerable<ServerPlayer> Players => PlayerManager.Players; public abstract AbstractPlayerManager PlayerManager { get; } protected AbstractServer(Networking networking) { Guard.NotNull(networking, "networking"); Networking = networking; string serverSavePath = SaveUtils.ServerSavePath; ServerId = Ensure.ServerId(serverSavePath); Logger.Info("Starting server..."); } protected AbstractServer(Networking networking, Guid guid) { Guard.NotNull(networking, "networking"); Guard.NotEmpty(guid, "guid"); ServerId = guid; Networking = networking; Logger.Info("Starting server..."); } public virtual void Save() { foreach (ServerPlayer player in Players) { if (player.CanPersist) { player.OnSave(SaveUtils.ServerSavePath); } else { Logger.Error($"Failed to save {player} since they can't be persisted."); } } } public virtual void Stop() { IsStopping = true; PlayerUtils.Disconnect("Stopping Server"); } protected internal virtual void OnShutdown() { IsStopping = true; Logger.Info("Server is stopping..."); foreach (ServerPlayer player in PlayerManager.Players) { PluginUtils.OnPlayerUntracked(player); player.DetachAll(); } PlayerManager.Clear(); } } public sealed class DedicatedServer : AbstractServer { public override bool IsIntegrated => false; public override ServerPlayer? Host => null; public DedicatedPlayerManager DedicatedPlayerManager { get; } public override AbstractPlayerManager PlayerManager => DedicatedPlayerManager; private DedicatedServer(Networking networking) : base(networking) { DedicatedPlayerManager = new DedicatedPlayerManager(this); } internal static DedicatedServer StartServer(Networking networking) { return new DedicatedServer(networking); } } public sealed class IntegratedServer : AbstractServer { public override bool IsIntegrated => true; public GameClient Client { get; } public override ServerPlayer? Host => IntegratedPlayerManager.Host; public IntegratedPlayerManager IntegratedPlayerManager { get; } public override AbstractPlayerManager PlayerManager => IntegratedPlayerManager; internal IntegratedServer(Networking networking, GameClient client) : base(networking) { Guard.NotNull(client, "client"); Client = client; IntegratedPlayerManager = new IntegratedPlayerManager(this, client); } } } namespace CookieFx.Players { public abstract class AbstractClientPlayer : AbstractPlayer { public sealed override bool IsClient => true; public abstract bool IsLocal { get; } public override string CurrentSavePath => SaveUtils.ClientSavePath; } public abstract class AbstractPlayer { private readonly Dictionary<Type, IPlayerComponent> _components = new Dictionary<Type, IPlayerComponent>(); private readonly Dictionary<Type, string> _componentPaths = new Dictionary<Type, string>(); public abstract bool IsClient { get; } public abstract ulong NetworkId { get; } public abstract bool CanPersist { get; } public abstract ulong PersistentId { get; } public abstract string CurrentSavePath { get; } public IEnumerable<Type> ComponentTypes => _components.Keys; public IEnumerable<IPlayerComponent> Components => _components.Values; public bool TryAttach<T>(T component, out T? previous) where T : class, IPlayerComponent { if (TryAttach(typeof(T), component, out IPlayerComponent previous2)) { previous = (T)previous2; return true; } previous = null; return false; } public bool TryAttach<T>(T component) where T : class, IPlayerComponent { return TryAttach(typeof(T), component); } public T? Attach<T>(T component) where T : class, IPlayerComponent { return (T)Attach(typeof(T), component); } public T? GetOrNull<T>() where T : class, IPlayerComponent { return (T)GetOrNull(typeof(T)); } [return: NotNullIfNotNull("value")] public T? GetOrDefault<T>(T? value = null) where T : class, IPlayerComponent { return (T)GetOrDefault(typeof(T), value); } public T Get<T>() where T : class, IPlayerComponent { return (T)Get(typeof(T)); } public bool TryGet<T>([NotNullWhen(true)] out T? value) where T : class, IPlayerComponent { if (TryGet(typeof(T), out IPlayerComponent value2)) { value = (T)value2; return true; } value = null; return false; } public bool TryDetach<T>([NotNullWhen(true)] out T? component) where T : class, IPlayerComponent { if (TryDetach(typeof(T), out IPlayerComponent component2)) { component = (T)component2; return true; } component = null; return false; } public bool TryDetach<T>() where T : class, IPlayerComponent { return TryDetach(typeof(T)); } public T? Detach<T>() where T : class, IPlayerComponent { return (T)Detach(typeof(T)); } public bool TryAttach(Type type, IPlayerComponent component, out IPlayerComponent? previous) { Guard.NotNull(type, "type"); Guard.IsFalse(type.IsValueType, null, "type.IsValueType"); Guard.NotNull(component, "component"); Guard.IsTrue(type.IsAssignableFrom(component.GetType()), null, "type.IsAssignableFrom(component.GetType())"); if (!PluginUtils.TryGetPluginId(type.Assembly, out string pluginId)) { throw new InvalidOperationException("Cannot find a plugin for the assembly [" + type.Assembly.FullName + "]"); } if (_components.TryGetValue(type, out previous)) { OnSaveComponent(type, previous, CurrentSavePath); } string text = pluginId + "-" + component.ComponentId; OnLoadComponent(type, component, CurrentSavePath, text); if (!AboutToAttach(component)) { return false; } if (previous != null && !AboutToDetach(previous)) { previous = null; return false; } _components[type] = component; _componentPaths[type] = text; return true; } public bool TryAttach(Type type, IPlayerComponent component) { Guard.NotNull(type, "type"); Guard.IsFalse(type.IsValueType, null, "type.IsValueType"); Guard.NotNull(component, "component"); Guard.IsTrue(type.IsAssignableFrom(component.GetType()), null, "type.IsAssignableFrom(component.GetType())"); if (!PluginUtils.TryGetPluginId(type.Assembly, out string pluginId)) { throw new InvalidOperationException("Cannot find a plugin for the assembly [" + type.Assembly.FullName + "]"); } if (_components.TryGetValue(type, out IPlayerComponent value)) { OnSaveComponent(type, value, CurrentSavePath); } string text = pluginId + "-" + component.ComponentId; OnLoadComponent(type, component, CurrentSavePath, text); if (!AboutToAttach(component)) { return false; } if (value != null && !AboutToDetach(value)) { return false; } _components[type] = component; _componentPaths[type] = text; return true; } public IPlayerComponent? Attach(Type type, IPlayerComponent component) { Guard.NotNull(type, "type"); Guard.IsFalse(type.IsValueType, null, "type.IsValueType"); Guard.NotNull(component, "component"); Guard.IsTrue(type.IsAssignableFrom(component.GetType()), null, "type.IsAssignableFrom(component.GetType())"); if (!PluginUtils.TryGetPluginId(type.Assembly, out string pluginId)) { throw new InvalidOperationException("Cannot find a plugin for the assembly [" + type.Assembly.FullName + "]"); } if (_components.TryGetValue(type, out IPlayerComponent value)) { OnSaveComponent(type, value, CurrentSavePath); } string text = pluginId + "-" + component.ComponentId; OnLoadComponent(type, component, CurrentSavePath, text); if (!AboutToAttach(component)) { throw new InvalidOperationException("Cannot attach component [" + type.Name + "] since the component doesn't want to be attached."); } if (value != null && !AboutToDetach(value)) { throw new InvalidOperationException("Cannot replace component [" + type.Name + "] since the existing component doesn't want to be detached."); } _components[type] = component; _componentPaths[type] = text; return value; } public IPlayerComponent? GetOrNull(Type type) { Guard.NotNull(type, "type"); Guard.IsFalse(type.IsValueType, null, "type.IsValueType"); if (!_components.TryGetValue(type, out IPlayerComponent value)) { return null; } return value; } [return: NotNullIfNotNull("value")] public IPlayerComponent? GetOrDefault(Type type, IPlayerComponent? value = null) { return GetOrNull(type) ?? value; } public IPlayerComponent Get(Type type) { return GetOrNull(type) ?? throw new InvalidOperationException("Player doesn't have a [" + type.Name + "] component."); } public bool TryGet(Type type, [NotNullWhen(true)] out IPlayerComponent? value) { value = GetOrNull(type); return value != null; } public bool TryDetach(Type type, [NotNullWhen(true)] out IPlayerComponent? component) { Guard.NotNull(type, "type"); Guard.IsFalse(type.IsValueType, null, "type.IsValueType"); if (_components.TryGetValue(type, out component)) { OnSaveComponent(type, component, CurrentSavePath); if (AboutToDetach(component)) { _components.Remove(type); _componentPaths.Remove(type); return true; } } component = null; return false; } public bool TryDetach(Type type) { Guard.NotNull(type, "type"); Guard.IsFalse(type.IsValueType, null, "type.IsValueType"); if (_components.TryGetValue(type, out IPlayerComponent value)) { OnSaveComponent(type, value, CurrentSavePath); if (AboutToDetach(value)) { _components.Remove(type); _componentPaths.Remove(type); return true; } } return false; } public IPlayerComponent? Detach(Type type) { Guard.NotNull(type, "type"); Guard.IsFalse(type.IsValueType, null, "type.IsValueType"); if (_components.TryGetValue(type, out IPlayerComponent value)) { OnSaveComponent(type, value, CurrentSavePath); if (AboutToDetach(value)) { _components.Remove(type); _componentPaths.Remove(type); return value; } } throw new InvalidOperationException("Cannot detach component [" + type.Name + "] since the component isn't attached."); } internal void DetachAll(bool save = true) { foreach (var (type2, playerComponent2) in _components) { if (save && CanPersist) { OnSaveComponent(type2, playerComponent2, CurrentSavePath); } try { playerComponent2.OnPlayerDetach(this, forced: true); } catch (Exception ex) { Logger.Error("Component [" + playerComponent2.ComponentId + "] threw an exception during [Forced Detach]. " + ex.Message + "\n" + ex.StackTrace); } } _components.Clear(); } private bool AboutToAttach(IPlayerComponent component) { try { return component.OnPlayerAttach(this); } catch (Exception ex) { Logger.Error("Component [" + component.ComponentId + "] threw an exception during [Attach]. " + ex.Message + "\n" + ex.StackTrace); return false; } } private bool AboutToDetach(IPlayerComponent component) { try { return component.OnPlayerDetach(this, forced: false); } catch (Exception ex) { Logger.Error("Component [" + component.ComponentId + "] threw an exception during [Detach]. " + ex.Message + "\n" + ex.StackTrace); return true; } } protected internal void OnSave(string save) { foreach (var (type2, component) in _components) { OnSaveComponent(type2, component, save); } } protected internal void OnLoad(string save) { foreach (var (type2, component) in _components) { OnLoadComponent(type2, component, save, null); } } protected virtual void OnSaveComponent(Type type, IPlayerComponent component, string save) { Guard.NotNull(type, "type"); Guard.NotNull(component, "component"); Guard.NotNullOrWhiteSpace(save, "save"); if (!component.ShouldPersist(this)) { return; } if (!CanPersist) { throw new InvalidOperationException($"Tried to save [{_componentPaths[type]}] for {this} but the player cannot be persisted."); } string componentSavePath = GetComponentSavePath(type, save, null); try { Directory.CreateDirectory(componentSavePath); component.OnPlayerSave(this, componentSavePath); } catch (Exception ex) { Logger.Error("Component [" + component.ComponentId + "] threw an exception during [Save]. " + ex.Message + "\n" + ex.StackTrace); } } protected virtual void OnLoadComponent(Type type, IPlayerComponent component, string save, string? componentPath) { Guard.NotNull(type, "type"); Guard.NotNull(component, "component"); Guard.NotNullOrWhiteSpace(save, "save"); if (!component.ShouldPersist(this)) { return; } if (!CanPersist) { throw new InvalidOperationException($"Tried to load [{componentPath ?? _componentPaths[type]}] for {this} but the player cannot be persisted."); } string componentSavePath = GetComponentSavePath(type, save, componentPath); if (Directory.Exists(componentSavePath)) { try { component.OnPlayerLoad(this, componentSavePath); return; } catch (Exception ex) { Logger.Error("Component [" + component.ComponentId + "] threw an exception during [Load]. " + ex.Message + "\n" + ex.StackTrace); return; } } try { Directory.CreateDirectory(componentSavePath); component.OnPlayerDefaults(this, componentSavePath); } catch (Exception ex2) { Logger.Error("Component [" + component.ComponentId + "] threw an exception during [Defaults]. " + ex2.Message + "\n" + ex2.StackTrace); } } private string GetComponentSavePath(Type type, string save, string? component) { if (!CanPersist) { throw new InvalidOperationException($"Tried to find save path for component [{type.FullName}] but the player {this} cannot be persisted."); } string path = component ?? _componentPaths[type]; return Path.Combine(save, "Players", PersistentId.ToString(), path); } public abstract void Disconnect(string? reason); public abstract override string ToString(); } public sealed class ClientPlayer : AbstractClientPlayer { public override bool IsLocal => true; public override ulong NetworkId => Client.NetworkId; public override bool CanPersist => PersistentId != 0; public override ulong PersistentId { get; } public GameClient Client { get; } public NetworkSession.Server ServerSession { get; } internal ClientPlayer(Networking networking, GameClient client, NetworkSession.Server session) { Guard.NotNull(networking, "networking"); Guard.NotNull(client, "client"); Guard.NotNull(session, "session"); Client = client; ServerSession = session; PersistentId = session.PersistentId; } public override void Disconnect(string? reason = null) { ServerSession.Disconnect(reason); } public override string ToString() { return $"ClientPlayer(net:{NetworkId}, persist:{PersistentId})"; } } public interface IPlayerComponent { string ComponentId => GetType().FullName; bool ShouldPersist(AbstractPlayer player) { return true; } bool OnPlayerAttach(AbstractPlayer player) { return true; } bool OnPlayerDetach(AbstractPlayer player, bool forced) { return true; } void OnPlayerDefaults(AbstractPlayer player, string path) { } void OnPlayerSave(AbstractPlayer player, string path) { } void OnPlayerLoad(AbstractPlayer player, string path) { } } public sealed class OtherClientPlayer : AbstractClientPlayer { public override bool IsLocal => false; public override ulong NetworkId { get; } public override bool CanPersist => PersistentId != 0; public override ulong PersistentId { get; } internal OtherClientPlayer(ulong networkId, ulong persistentId) { NetworkId = networkId; PersistentId = persistentId; } public override void Disconnect(string? reason) { PlayerUtils.Disconnect(NetworkId, reason); if (reason == null) { Logger.Info($"Kicked {NetworkId}"); } else { Logger.Info($"Kicked {NetworkId} because: {reason}"); } } public override string ToString() { return $"OtherClientPlayer(net:{NetworkId}, persist:{PersistentId})"; } } public sealed class ServerPlayer : AbstractPlayer { public override bool IsClient => false; public override ulong NetworkId => Session.NetworkId; public override bool CanPersist => PersistentId != 0; public override ulong PersistentId { get; } public override string CurrentSavePath => SaveUtils.ServerSavePath; public AbstractServer Server { get; } public NetworkSession.Client Session { get; } internal ServerPlayer(AbstractServer server, NetworkSession.Client session) { Guard.NotNull(server, "server"); Guard.NotNull(session, "session"); Server = server; Session = session; if (!PlayerUtils.TryGetPersistentId(session.NetworkId, out var persistentId)) { Disconnect("Unable to find your PersistentId"); } else { PersistentId = persistentId; } } public override void Disconnect(string? reason) { Session.Disconnect(reason); } public override string ToString() { return $"ServerPlayer(net:{NetworkId}, persist:{PersistentId})"; } } public abstract class AbstractPlayerManager { [CompilerGenerated] private sealed class <>c__DisplayClass16_0 { public AbstractPlayerManager <>4__this; public Stopwatch timer; public ulong networkId; internal bool <SetupTimeout>b__0() { if (!<>4__this.Server.IsStopping && !(timer.Elapsed.TotalSeconds > 80.0) && <>4__this._tracked.ContainsKey(networkId)) { return <>4__this._players.ContainsKey(networkId); } return true; } } [CompilerGenerated] private sealed class <SetupTimeout>d__16 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public AbstractPlayerManager <>4__this; public ulong networkId; private <>c__DisplayClass16_0 <>8__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SetupTimeout>d__16(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; <>1__state = -2; } private bool MoveNext() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown int num = <>1__state; AbstractPlayerManager abstractPlayerManager = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass16_0(); <>8__1.<>4__this = <>4__this; <>8__1.networkId = networkId; <>8__1.timer = Stopwatch.StartNew(); <>2__current = (object)new WaitUntil((Func<bool>)(() => <>8__1.<>4__this.Server.IsStopping || <>8__1.timer.Elapsed.TotalSeconds > 80.0 || !<>8__1.<>4__this._tracked.ContainsKey(<>8__1.networkId) || <>8__1.<>4__this._players.ContainsKey(<>8__1.networkId))); <>1__state = 1; return true; case 1: { <>1__state = -1; <>8__1.timer.Stop(); if (abstractPlayerManager.Server.IsStopping || abstractPlayerManager._players.ContainsKey(<>8__1.networkId) || !abstractPlayerManager._tracked.TryGetValue(<>8__1.networkId, out NetworkSession.Client value)) { return false; } if (<>8__1.timer.Elapsed.TotalSeconds > 60.0) { value.Disconnect("Setup timed out. (Server-side)"); } return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } protected readonly Dictionary<ulong, ServerPlayer> _players = new Dictionary<ulong, ServerPlayer>(); protected readonly Dictionary<ulong, NetworkSession.Client> _tracked = new Dictionary<ulong, NetworkSession.Client>(); public AbstractServer Server { get; } public IEnumerable<ServerPlayer> Players => _players.Values; public IEnumerable<NetworkSession.Client> Sessions => _tracked.Values; protected AbstractPlayerManager(AbstractServer server) { Guard.NotNull(server, "server"); Server = server; Logger.Info("ServerPlayerManager setup"); } public IEnumerable<ServerPlayer> PlayersExcept(ulong networkId) { return _players.Values.Where((ServerPlayer p) => p.NetworkId != networkId); } public IEnumerable<ServerPlayer> PlayersExcept(params ulong[] networkIds) { ulong[] networkIds2 = networkIds; Guard.NotNull(networkIds2, "networkIds"); return _players.Values.Where((ServerPlayer p) => Array.IndexOf(networkIds2, p.NetworkId) == -1); } public virtual bool TryGetPlayer(ulong networkId, [NotNullWhen(true)] out ServerPlayer? player) { return _players.TryGetValue(networkId, out player); } public virtual bool TryGetSession(ulong networkId, [NotNullWhen(true)] out NetworkSession.Client? session) { return _tracked.TryGetValue(networkId, out session); } protected internal virtual void OnClientAdded(ulong networkId) { if (Server.IsStopping) { Logger.Warning($"Client {networkId} tried to connect while we're stopping."); return; } Networking networking = Server.Networking; Logger.Info($"Started tracking {networkId}"); if (_tracked.TryGetValue(networkId, out NetworkSession.Client value)) { Logger.Error($"{networkId} is already being tracked."); value.Disconnect("You are already tracked."); return; } value = NetworkSession.CreateClient(networking, networkId); _tracked.Add(networkId, value); ((MonoBehaviour)networking.Backend).StartCoroutine(SetupTimeout(networkId)); value.StartSetup(Server.Networking); } protected internal virtual void OnClientRemoved(ulong networkId) { if (!Server.IsStopping) { Logger.Info($"Stopped tracking {networkId}"); if (!_tracked.TryGetValue(networkId, out NetworkSession.Client value)) { Logger.Error($"{networkId} was already not tracked."); } else if (_players.ContainsKey(networkId)) { OnClientDisconnect(value); } } } [IteratorStateMachine(typeof(<SetupTimeout>d__16))] protected internal virtual IEnumerator SetupTimeout(ulong networkId) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SetupTimeout>d__16(0) { <>4__this = this, networkId = networkId }; } protected internal virtual void OnClientConnected(NetworkSession.Client session) { Guard.NotNull(session, "session"); ulong networkId = session.NetworkId; Logger.Info($"Player connected: {networkId}"); if (_players.TryGetValue(networkId, out ServerPlayer value)) { Logger.Error($"Player {networkId} is already in player list."); value.Disconnect("You are already connected."); return; } value = new ServerPlayer(Server, session); _players.Add(networkId, value); PluginUtils.OnPlayerTracked(value); Server.Networking.SendPlayerTracking(this, value, track: true); Server.Networking.InvokePlayerConnected(value); } protected internal virtual void OnClientDisconnect(NetworkSession.Client session) { Guard.NotNull(session, "session"); ulong networkId = session.NetworkId; Logger.Info($"Player disconnected: {networkId}"); if (!_players.TryGetValue(networkId, out ServerPlayer value)) { Logger.Error($"Player {networkId} was not found in player list."); return; } Server.Networking.InvokePlayerDisconnected(value); PluginUtils.OnPlayerUntracked(value); Server.Networking.SendPlayerTracking(this, value, track: false); value.DetachAll(); _players.Remove(networkId); } internal void Clear() { _players.Clear(); _tracked.Clear(); } } public sealed class DedicatedPlayerManager : AbstractPlayerManager { public new DedicatedServer Server { get; } public DedicatedPlayerManager(DedicatedServer server) : base(server) { Server = server; } } public sealed class IntegratedPlayerManager : AbstractPlayerManager { [CompilerGenerated] private sealed class <DelayedHostConnect>d__15 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public IntegratedPlayerManager <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedHostConnect>d__15(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown int num = <>1__state; IntegratedPlayerManager CS$<>8__locals0 = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; Logger.Info("Delaying host player."); <>2__current = (object)new WaitUntil((Func<bool>)(() => PlayerUtils.CanPersistLocal || CS$<>8__locals0.Client.IsStopping || CS$<>8__locals0.Server.IsStopping)); <>1__state = 1; return true; case 1: <>1__state = -1; if (!CS$<>8__locals0.Client.IsStopping && !CS$<>8__locals0.Server.IsStopping) { Logger.Info("Continuing delayed host player."); CS$<>8__locals0.OnClientConnected(CS$<>8__locals0.HostSession); } else { Logger.Warning("Host player interrupted."); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <OnHostReady>d__18 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public IntegratedPlayerManager <>4__this; public ulong clientId; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <OnHostReady>d__18(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown int num = <>1__state; IntegratedPlayerManager CS$<>8__locals0 = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitUntil((Func<bool>)(() => CS$<>8__locals0.HostSession.IsReady)); <>1__state = 1; return true; case 1: { <>1__state = -1; ServerPlayer serverPlayer2 = (CS$<>8__locals0.Host = new ServerPlayer(CS$<>8__locals0.Server, CS$<>8__locals0.HostSession)); CS$<>8__locals0._players.Add(clientId, serverPlayer2); PluginUtils.OnPlayerTracked(serverPlayer2); CS$<>8__locals0.Server.Networking.SendPlayerTracking(CS$<>8__locals0, serverPlayer2, track: true); CS$<>8__locals0.Server.Networking.InvokePlayerConnected(serverPlayer2); return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public new IntegratedServer Server { get; } public GameClient Client { get; } public NetworkSession.LocalClient HostSession { get; } public ServerPlayer? Host { get; private set; } public IntegratedPlayerManager(IntegratedServer server, GameClient client) : base(server) { Guard.NotNull(client, "client"); Server = server; Client = client; HostSession = (NetworkSession.CreateClient(server.Networking, client.NetworkId) as NetworkSession.LocalClient) ?? throw new InvalidOperationException("Not the host, this should never happen."); } protected internal override void OnClientAdded(ulong networkId) { if (networkId == HostSession.NetworkId) { ((MonoBehaviour)Server.Networking.Backend).StartCoroutine(DelayedHostConnect()); } else { base.OnClientAdded(networkId); } } [IteratorStateMachine(typeof(<DelayedHostConnect>d__15))] private IEnumerator DelayedHostConnect() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedHostConnect>d__15(0) { <>4__this = this }; } protected internal override void OnClientRemoved(ulong networkId) { if (networkId == HostSession.NetworkId) { OnClientDisconnect(HostSession); Host = null; } else { base.OnClientRemoved(networkId); } } protected internal override void OnClientConnected(NetworkSession.Client session) { Guard.NotNull(session, "session"); if (session == HostSession) { ulong networkId = session.NetworkId; Logger.Info($"Local Player connected: {networkId}"); if (_players.TryGetValue(networkId, out ServerPlayer value)) { Logger.Error($"Local Player {networkId} is already in player list."); value.Disconnect("You are already connected."); } else { session.StartSetup(Server.Networking); ((MonoBehaviour)Server.Networking.Backend).StartCoroutine(OnHostReady(networkId)); } } else { base.OnClientConnected(session); } } [IteratorStateMachine(typeof(<OnHostReady>d__18))] private IEnumerator OnHostReady(ulong clientId) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <OnHostReady>d__18(0) { <>4__this = this, clientId = clientId }; } } } namespace CookieFx.Platform { public static class AccessUtils { private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty; private const BindingFlags AllDeclared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty; public static MethodInfo? ImplementedMethod(Type implementation, Type type, string name, Type[]? parameters = null) { if ((object)implementation == null) { Logger.Warning("AccessUtils ImplementedMethod: implementation is null."); return null; } if ((object)type == null) { Logger.Warning("AccessUtils ImplementedMethod: type is null."); return null; } if (!type.IsAssignableFrom(implementation)) { Logger.Warning($"AccessUtils ImplementedMethod: {implementation} is not assignable to {type}."); return null; } MethodInfo methodInfo = ((parameters != null) ? type.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty, null, parameters, Array.Empty<ParameterModifier>()) : type.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty)); if ((object)methodInfo == null) { Logger.Warning($"AccessUtils ImplementedMethod: Could not find method for type {type} and name {name} and parameters {((parameters != null) ? GeneralExtensions.Description(parameters) : null)}."); return null; } InterfaceMapping interfaceMap = implementation.GetInterfaceMap(type); MethodInfo[] interfaceMethods = interfaceMap.InterfaceMethods; for (int i = 0; i < interfaceMap.InterfaceMethods.Length; i++) { if (interfaceMethods[i].Equals(methodInfo)) { return interfaceMap.TargetMethods[i]; } } Logger.Warning($"AccessUtils ImplementedMethod: Implementation for {methodInfo} not found in {implementation}."); return null; } public static MethodInfo? ImplementedMethod(Type implementation, MethodInfo method) { if ((object)implementation == null) { Logger.Warning("AccessUtils ImplementedMethod: implementation is null."); return null; } if ((object)method == null) { Logger.Warning("AccessUtils ImplementedMethod: method is null."); return null; } Type declaringType = method.DeclaringType; if (!declaringType.IsAssignableFrom(implementation)) { Logger.Warning($"AccessUtils ImplementedMethod: {implementation} is not assignable to {declaringType}."); return null; } InterfaceMapping interfaceMap = implementation.GetInterfaceMap(declaringType); MethodInfo[] interfaceMethods = interfaceMap.InterfaceMethods; for (int i = 0; i < interfaceMap.InterfaceMethods.Length; i++) { if (interfaceMethods[i].Equals(method)) { return interfaceMap.TargetMethods[i]; } } Logger.Warning($"AccessUtils ImplementedMethod: Implementation for {method} not found in {implementation}."); return null; } public static MethodInfo? MethodInfo(LambdaExpression lambda) { if (lambda == null) { Logger.Warning("AccessUtils MethodInfo: lambda is null."); return null; } if (lambda.Body is MethodCallExpression methodCallExpression) { return methodCallExpression.Method; } if (lambda.Body is UnaryExpression unaryExpression) { if (!(unaryExpression.Operand is MethodCallExpression methodCallExpression2)) { Logger.Warning("AccessUtils MethodInfo: Expected a method call in lambda expression."); return null; } if (!(methodCallExpression2.Object is ConstantExpression constantExpression)) { Logger.Warning("AccessUtils MethodInfo: Expected a method constant in lambda expression."); return null; } if (!(constantExpression.Value is MethodInfo result)) { Logger.Warning($"AccessUtils MethodInfo: Expected constant to be of type {typeof(MethodInfo)} in lambda expression."); return null; } return result; } Logger.Warning($"AccessUtils MethodInfo: {lambda.Body.Type} is not a supported expression type."); return null; } } public sealed class BepInLoggerSource : ILogger { private readonly ManualLogSource _source; public BepInLoggerSource(ManualLogSource source) { Guard.NotNull<ManualLogSource>(source, "source"); _source = source; } public void Debug(object? message) { _source.LogDebug(message); } public void Info(object? message) { _source.LogInfo(message); } public void Warning(object? message) { _source.LogWarning(message); } public void Error(object? message) { _source.LogError(message); } public void Fatal(object? message) { _source.LogFatal(message); } } public interface ICookieDependency { const string PluginId = "CookieFx"; protected internal bool TrackOtherClients => false; protected internal void OnPlayerTracked(AbstractPlayer player) { } protected internal void OnPlayerUntracked(AbstractPlayer player) { } } public interface ILogger { public sealed class Unity : ILogger { public static readonly Unity Shared = new Unity(); private Unity() { } public void Debug(object? message) { Debug.Log(message); } public void Info(object? message) { Debug.Log(message); } public void Warning(object? message) { Debug.LogWarning(message); } public void Error(object? message) { Debug.LogError(message); } public void Fatal(object? message) { Debug.LogError(message); } } void Debug(object? message); void Info(object? message); void Warning(object? message); void Error(object? message); void Fatal(object? message); } internal static class Logger { private static ILogger s_logger = ILogger.Unity.Shared; public static ILogger Source { get { return s_logger; } [param: AllowNull] set { s_logger = value ?? ILogger.Unity.Shared; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Debug(object message) { Source.Debug(message); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Info(object message) { Source.Info(message); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Warning(object message) { Source.Warning(message); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Error(object message) { Source.Error(message); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Fatal(object message) { Source.Fatal(message); } } public static class PlayerUtils { public static ulong LocalPersistentId { get { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (StartOfRound.Instance == null) { throw new NetworkConfigurationException("Attempting to use netcode while not connected to server."); } return StartOfRound.Instance.localPlayerController.playerSteamId; } } public static bool CanPersistLocal => StartOfRound.Instance != null; public static bool TryGetPersistentId(ulong networkId, out ulong persistentId) { if (StartOfRound.Instance == null) { persistentId = 0uL; return false; } if (!StartOfRound.Instance.ClientPlayerList.TryGetValue(networkId, out var value)) { persistentId = 0uL; return false; } PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[value]; persistentId = val.playerSteamId; return persistentId != 0; } public static bool CanPersist(ulong networkId) { if (CanPersistLocal && StartOfRound.Instance.ClientPlayerList.TryGetValue(networkId, out var value)) { return StartOfRound.Instance.allPlayerScripts[value].playerSteamId != 0; } return false; } public static bool TryGetPlayerScript(int playerId, [NotNullWhen(true)] out PlayerControllerB? script) { if (StartOfRound.Instance == null) { script = null; return false; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (playerId < 0 || playerId >= allPlayerScripts.Length) { script = null; return false; } script = allPlayerScripts[playerId]; return true; } public static bool TryGetPlayerScript(ulong networkId, [NotNullWhen(true)] out PlayerControllerB? script) { if (StartOfRound.Instance == null) { script = null; return false; } StartOfRound instance = StartOfRound.Instance; if (!instance.ClientPlayerList.TryGetValue(networkId, out var value)) { script = null; return false; } PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; if (value < 0 || value >= allPlayerScripts.Length) { script = null; return false; } script = allPlayerScripts[value]; return true; } public static void Disconnect(string? reason = null) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!Networking.IsConnected || GameNetworkManager.Instance == null) { throw new NetworkConfigurationException("Attempting to use netcode while not connected to a server."); } GameNetworkManager.Instance.disconnectionReasonMessage = reason; GameNetworkManager.Instance.Disconnect(); } public static void Disconnect(ulong networkId, string? reason = null) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!Networking.IsConnected || GameNetworkManager.Instance == null) { throw new NetworkConfigurationException("Attempting to use netcode while not connected to a server."); } if (networkId == Networking.Instance.LocalClientId) { Logger.Warning("Called non-local disconnect for local connection."); Disconnect(reason); return; } if (!Networking.Instance.IsServer && !Networking.Instance.IsHost) { throw new NetworkConfigurationException("Attempting to use server-only method on the client."); } Networking.Instance.Backend.DisconnectClient(networkId, reason); } } public static class PluginUtils { private static readonly Dictionary<Assembly, string?> PluginIdCache = new Dictionary<Assembly, string>(); private static readonly HashSet<ICookieDependency> Dependencies = new HashSet<ICookieDependency>(); public static void Register(ICookieDependency dependency) { Dependencies.Add(dependency); } public static bool TryGetPluginId(Assembly assembly, [NotNullWhen(true)] out string? pluginId) { if (PluginIdCache.TryGetValue(assembly, out string value)) { pluginId = value; return value != null; } BepInPlugin val = (from x in AccessTools.GetTypesFromAssembly(assembly) select MetadataHelper.GetMetadata(x)).FirstOrDefault((Func<BepInPlugin, bool>)((BepInPlugin x) => x != null)); if (val == null) { pluginId = null; PluginIdCache[assembly] = null; return false; } pluginId = val.GUID; PluginIdCache[assembly] = pluginId; return true; } public static void OnPlayerTracked(AbstractPlayer player) { foreach (ICookieDependency dependency in Dependencies) { try { if (dependency.TrackOtherClients || !(player is OtherClientPlayer)) { dependency.OnPlayerTracked(player); } } catch (Exception ex) { Assembly assembly = dependency.GetType().Assembly; if (!TryGetPluginId(assembly, out string pluginId)) { pluginId = assembly.FullName; } Logger.Error($"Player tracker by [{pluginId}] failed tracking net:{player.NetworkId} with {ex.Message}\n{ex.StackTrace}"); } } } public static void OnPlayerUntracked(AbstractPlayer player) { foreach (ICookieDependency dependency in Dependencies) { try { if (dependency.TrackOtherClients || !(player is OtherClientPlayer)) { dependency.OnPlayerUntracked(player); } } catch (Exception ex) { Assembly assembly = dependency.GetType().Assembly; if (!TryGetPluginId(assembly, out string pluginId)) { pluginId = assembly.FullName; } Logger.Error($"Player tracker by [{pluginId}] failed untracking net:{player.NetworkId} with {ex.Message}\n{ex.StackTrace}"); } } } } public static class SaveUtils { public static string ServerSavePath { get { //IL_0013: 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) if (!Networking.IsConnected || GameNetworkManager.Instance == null) { throw new NetworkConfigurationException("Attempting to use netcode while not hosting a server."); } if (!Networking.Instance.IsServer && !Networking.Instance.IsHost) { throw new NetworkConfigurationException("Attempting to use server-only method on the client."); } int saveFileNum = GameNetworkManager.Instance.saveFileNum; return Path.Combine(Application.persistentDataPath, "CookieFx", $"Save{saveFileNum + 1}"); } } public static string ClientSavePath { get { //IL_0013: 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) if (!Networking.IsConnected || GameNetworkManager.Instance == null) { throw new NetworkConfigurationException("Attempting to use netcode while not hosting a server."); } if (!Networking.Instance.IsClient && !Networking.Instance.IsHost) { throw new NetworkConfigurationException("Attempting to use client-only method on the server."); } Guid serverId = Networking.Instance.Client.ServerSession.ServerId; return Path.Combine(Application.persistentDataPath, "CookieFx", "Servers", serverId.ToString("D")); } } public static void DeleteSave(int slot) { string path = Path.Combine(Application.persistentDataPath, "CookieFx", $"Save{slot + 1}"); Logger.Warning($"Deleting save {slot + 1}"); if (Directory.Exists(path)) { Directory.Delete(path, recursive: true); } } } } namespace CookieFx.Patches { internal static class FuckThisShitPatches { [CompilerGenerated] private sealed class <OnStaticNetcodeLib>d__1 : IEnumerable<CodeInstruction>, IEnumerable, IEnumerator<CodeInstruction>, IEnumerator, IDisposable { private int <>1__state; private CodeInstruction <>2__current; private int <>l__initialThreadId; private ILGenerator generator; public ILGenerator <>3__generator; private IEnumerable<CodeInstruction> instructions; public IEnumerable<CodeInstruction> <>3__instructions; private MethodInfo <target>5__2; private Label <label>5__3; private IEnumerator<CodeInstruction> <>7__wrap3; CodeInstruction IEnumerator<CodeInstruction>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <OnStaticNetcodeLib>d__1(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 6) { try { } finally { <>m__Finally1(); } } <target>5__2 = null; <>7__wrap3 = null; <>1__state = -2; } private bool MoveNext() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_0117: 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_0132: Expected O, but got Unknown try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <target>5__2 = AccessTools.DeclaredMethod(typeof(FuckThisShitPatches), "TryStaticNetcodeLib", (Type[])null, (Type[])null); <label>5__3 = generator.DefineLabel(); <>2__current = new CodeInstruction(OpCodes.Ldarga_S, (object)2); <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = new CodeInstruction(OpCodes.Call, (object)<target>5__2); <>1__state = 2; return true; case 2: <>1__state = -1; <>2__current = new CodeInstruction(OpCodes.Brtrue, (object)<label>5__3); <>1__state = 3; return true; case 3: <>1__state = -1; <>2__current = new CodeInstruction(OpCodes.Ret, (object)null); <>1__state = 4; return true; case 4: <>1__state = -1; <>2__current = new CodeInstruction(OpCodes.Nop, (object)null) { labels = { <label>5__3 } }; <>1__state = 5; return true; case 5: <>1__state = -1; <>7__wrap3 = instructions.GetEnumerator(); <>1__state = -3; break; case 6: <>1__state = -3; break; } if (<>7__wrap3.MoveNext()) { CodeInstruction current = <>7__wrap3.Current; <>2__current = current; <>1__state = 6; return true; } <>m__Finally1(); <>7__wrap3 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap3 != null) { <>7__wrap3.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<CodeInstruction> IEnumerable<CodeInstruction>.GetEnumerator() { <OnStaticNetcodeLib>d__1 <OnStaticNetcodeLib>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <OnStaticNetcodeLib>d__ = this; } else { <OnStaticNetcodeLib>d__ = new <OnStaticNetcodeLib>d__1(0); } <OnStaticNetcodeLib>d__.instructions = <>3__instructions; <OnStaticNetcodeLib>d__.generator = <>3__generator; return <OnStaticNetcodeLib>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<CodeInstruction>)this).GetEnumerator(); } } internal static void PatchTheirStuff(Harmony harmony) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method("StaticNetcodeLib.Messaging.UnnamedMessageHandler:ReceiveMessage", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(FuckThisShitPatches), "OnStaticNetcodeLib", (Type[])null, (Type[])null); if ((object)methodInfo != null) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null); Logger.Info("[Compat] StaticNetcodeLib: Found"); } else { Logger.Info("[Compat] StaticNetcodeLib: Not found"); } } catch (Exception arg) { Logger.Error($"[Compat] StaticNetcodeLib: Failed\n{arg}"); } } [IteratorStateMachine(typeof(<OnStaticNetcodeLib>d__1))] internal static IEnumerable<CodeInstruction> OnStaticNetcodeLib(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <OnStaticNetcodeLib>d__1(-2) { <>3__instructions = instructions, <>3__generator = generator }; } private static bool TryStaticNetcodeLib(in FastBufferReader reader) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) FastBufferReader val = reader; int position = ((FastBufferReader)(ref val)).Position; try { val = reader; string text = default(string); ((FastBufferReader)(ref val)).ReadValueSafe(ref text, false); return text == "StaticNetcodeLib"; } catch { return false; } finally { val = reader; ((FastBufferReader)(ref val)).Seek(position); } } } [HarmonyPatch(typeof(NetworkManager))] [HarmonyPriority(500)] [HarmonyWrapSafe] internal static class NetworkManagerPatch { [HarmonyPatch("Initialize")] [HarmonyPostfix] public static void InitializePatch(NetworkManager __instance) { Networking.Initialize(__instance); } [HarmonyPatch("ShutdownInternal")] [HarmonyPrefix] public static void ShutdownPatch() { Networking.Shutdown(); } } [HarmonyPatch] [HarmonyPriority(500)] [HarmonyWrapSafe] internal static class SavePatches { [CompilerGenerated] private sealed class <DeleteFileBetterSaves>d__3 : IEnumerable<CodeInstruction>, IEnumerable, IEnumerator<CodeInstruction>, IEnumerator, IDisposable { private int <>1__state; private CodeInstruction <>2__current; private int <>l__initialThreadId; private IEnumerable<CodeInstruction> instructions; public IEnumerable<CodeInstruction> <>3__instructions; private MethodInfo <target>5__2; private FieldInfo <targetField>5__3; private MethodInfo <deleteCall>5__4; private bool <found>5__5; private IEnumerator<CodeInstruction> <>7__wrap5; private CodeInstruction <instruction>5__7; CodeInstruction IEnumerator<CodeInstruction>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DeleteFileBetterSaves>d__3(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 4u) { try { } finally { <>m__Finally1(); } } <target>5__2 = null; <targetField>5__3 = null; <deleteCall>5__4 = null; <>7__wrap5 = null; <instruction>5__7 = null; <>1__state = -2; } private bool MoveNext() { //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Expected O, but got Unknown //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; <target>5__2 = AccessTools.Method("LCBetterSaves.Plugin:InitializeBetterSaves", (Type[])null, (Type[])null); Type type = AccessTools.TypeByName("DeleteFileButton_BetterSaves"); <targetField>5__3 = AccessTools.DeclaredField(type, "fileToDelete"); <deleteCall>5__4 = AccessUtils.MethodInfo((Expression<Func<Action<int>>>)(() => DeleteFile)); <found>5__5 = false; <>7__wrap5 = instructions.GetEnumerator(); <>1__state = -3; break; } case 1: <>1__state = -3; <>2__current = new CodeInstruction(OpCodes.Ldfld, (object)<targetField>5__3); <>1__state = 2; return true; case 2: <>1__state = -3; <>2__current = new CodeInstruction(OpCodes.Call, (object)<deleteCall>5__4); <>1__state = 3; return true; case 3: <>1__state = -3; <>2__current = <instruction>5__7; <>1__state = 4; return true; case 4: <>1__state = -3; <found>5__5 = true; break; case 5: <>1__state = -3; <instruction>5__7 = null; break; } if (<>7__wrap5.MoveNext()) { <instruction>5__7 = <>7__wrap5.Current; if (!<found>5__5 && CodeInstructionExtensions.Calls(<instruction>5__7, <target>5__2)) { <>2__current = new CodeInstruction(OpCodes.Ldarg_0, (object)null); <>1__state = 1; return true; } <>2__current = <instruction>5__7; <>1__state = 5; return true; } <>m__Finally1(); <>7__wrap5 = null; if (!<found>5__5) { Logger.Error("Failed to patch LCBetterSaves delete file button."); } return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap5 != null) { <>7__wrap5.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<CodeInstruction> IEnumerable<CodeInstruction>.GetEnumerator() { <DeleteFileBetterSaves>d__3 <DeleteFileBetterSaves>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <DeleteFileBetterSaves>d__ = this; } else { <DeleteFileBetterSaves>d__ = new <DeleteFileBetterSaves>d__3(0); } <DeleteFileBetterSaves>d__.instructions = <>3__instructions; return <DeleteFileBetterSaves>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<CodeInstruction>)this).GetEnumerator(); } } [HarmonyPatch(typeof(GameNetworkManager), "SaveGame")] [HarmonyPrefix] private static void SaveGame() { if (Networking.IsConnected) { Networking instance = Networking.Instance; if (instance.IsClient) { Logger.Info("Saving client..."); instance.Client.Save(); } if (instance.IsServer) { Logger.Info("Saving server..."); instance.Server.Save(); } } } [HarmonyPatch(typeof(DeleteFileButton), "DeleteFile")] [HarmonyPostfix] private static void OnDeleteFile(DeleteFileButton __instance) { DeleteFile(__instance.fileToDelete); } private static void DeleteFile(int slot) { SaveUtils.DeleteSave(slot); } [IteratorStateMachine(typeof(<DeleteFileBetterSaves>d__3))] internal static IEnumerable<CodeInstruction> DeleteFileBetterSaves(IEnumerable<CodeInstruction> instructions) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DeleteFileBetterSaves>d__3(-2) { <>3__instructions = instructions }; } } } namespace CookieFx.Network { public static class ClientNetworking { private interface IHandler { object PayloadType { get; } Type Type { get; } void Invoke(ClientPlayer player, FastBufferReader reader); } private sealed class Handler<T> : IHandler where T : NetworkPayload<T> { public NetworkPayloadType<T> PayloadType { get; } public Action<ClientPlayer, T> Action { get; } object IHandler.PayloadType => PayloadType; Type IHandler.Type => typeof(T); internal Handler(NetworkPayloadType<T> type, Action<ClientPlayer, T> action) { Guard.NotNull(type, "type"); Guard.NotNull(action, "action"); PayloadType = type; Action = action; } public void Invoke(ClientPlayer player, FastBufferReader reader) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(player, "player"); T val; try { val = PayloadType.Factory(reader); Guard.NotNull(val, "payload"); } catch (Exception ex) { Logger.Error("Decoding clientbound payload [" + PayloadType.Channel + "] from server failed: " + ex.Message + "\n" + ex.StackTrace); player.Disconnect("Invalid Packet Data from Server"); return; } try { Action(player, val); } catch (Exception ex2) { Logger.Error("Invoking handler for clientbound payload [" + PayloadType.Channel + "] from server failed: " + ex2.Message + "\n" + ex2.StackTrace); player.Disconnect("Broken Handler"); } } public void InvokeLocal(ClientPlayer player, T payload) { Guard.NotNull(player, "player"); Guard.NotNull(payload, "payload"); try { Action(player, payload); } catch (Exception ex) { Logger.Error("Invoking handler for clientbound payload [" + PayloadType.Channel + "] from local server failed: " + ex.Message + "\n" + ex.StackTrace); player.Disconnect("Broken Handler"); } } } private static readonly Dictionary<string, IHandler> Handlers = new Dictionary<string, IHandler>(StringComparer.Ordinal); public static event Action<NetworkSession>? OnSetupComplete; public static event Action<NetworkSession, string>? OnServerChannelRegister; public static event Action<NetworkSession, string>? OnServerChannelUnregister; public static ICollection<string> GetReceived() { return Handlers.Keys; } public static ICollection<string> GetSendable() { return Networking.Instance?.Client?.ServerSession?.SendableChannels ?? new List<string>(); } public static bool CanSend(string channel) { Guard.NotNullOrEmpty(channel, "channel"); if (!Networking.IsConnected || !Networking.Instance.IsClient) { return false; } return Networking.Instance.Client.ServerSession.SendableChannels.Contains(channel); } public static bool Send<T>(T payload) where T : NetworkPayload<T> { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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) Guard.NotNull(payload, "payload"); if (!Networking.IsConnected) { throw new NetworkConfigurationException("Attempting to use netcode while not connected to a server."); } if (!Networking.Instance.IsClient) { throw new NetworkConfigurationException("Attempting to use client-only method on the server."); } if (payload.PayloadType.Side == NetworkSide.Clientbound) { throw new NetworkConfigurationException("Attempting to send clientbound payload to the server."); } if (CanSend(payload.PayloadType.Channel)) { SendCore(Networking.Instance.Client, payload); return true; } return false; } public static bool Send<T>(NetworkPayloadType<T> type, Func<T> factory) where T : NetworkPayload<T> { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(type, "type"); Guard.NotNull(factory, "factory"); if (!Networking.IsConnected) { throw new NetworkConfigurationException("Attempting to use netcode while not connected to a server."); } if (!Networking.Instance.IsClient) { throw new NetworkConfigurationException("Attempting to use client-only method on the server."); } if (type.Side == NetworkSide.Clientbound) { throw new NetworkConfigurationException("Attempting to send clientbound payload to the server."); } if (CanSend(type.Channel)) { T val = factory(); Guard.NotNull(val, "payload"); SendCore(Networking.Instance.Client, val); return true; } return false; } private static void SendCore<T>(GameClient client, T payload) where T : NetworkPayload<T> { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) T payload2 = payload; Networking networking2 = client.Networking; NetworkSession.Server serverSession = client.ServerSession; if (serverSession.IsLocal) { Logger.Debug("Invoking local server payload on channel [" + payload2.PayloadType.Channel + "]"); Networking.EnqueueTask(delegate(Networking networking) { ServerNetworking.InvokeLocal(networking, payload2); }); return; } FastBufferWriter writer = Networking.EncodePayload(payload2); try { networking2.SendBackendMessage(serverSession.NetworkId, writer); } finally { ((IDisposable)(FastBufferWriter)(ref writer)).Dispose(); } } public static Action<ClientPlayer, T>? Register<T>(NetworkPayloadType<T> type, Action<ClientPlayer, T> callback) where T : NetworkPayload<T> { //IL_0024: 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) Guard.NotNull(type, "type"); Guard.NotNull(callback, "callback"); if (type.Side == NetworkSide.Serverbound) { throw new NetworkConfigurationException("Attempting to register serverbound payload to the client."); } bool flag = false; Action<ClientPlayer, T> result = null; if (Handlers.TryGetValue(type.Channel, out IHandler value)) { flag = true; if (value is Handler<T> handler) { result = handler.Action; } } else if (Handlers.Count >= 8192) { throw new InvalidOperationException($"Client tried to register more than {8192} channels."); } Handlers[type.Channel] = new Handler<T>(type, callback); if (!flag && Networking.IsConnected) { Networking instance = Networking.Instance; if (!instance.IsClient) { throw new NetworkConfigurationException("Attempting to use client-only method on the server."); } NetworkSession.Server serverSession = instance.Client.ServerSession; if (serverSession.IsLocal) { OnChannelRegister(serverSession, type.Channel); } else if (serverSession.EagerlySyncChannels) { instance.SendChannel(serverSession.NetworkId, type.Channel, register: true); } } return result; } public static void Unregister(string channel) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) Guard.NotNullOrEmpty(channel, "channel"); if (Handlers.Remove(channel) && Networking.IsConnected) { Networking instance = Networking.Instance; if (!instance.IsClient) { throw new NetworkConfigurationException("Attempting to use client-only method on the server."); } NetworkSession.Server serverSession = instance.Client.ServerSession; if (serverSession.IsLocal) { OnChannelUnregister(serverSession, channel); } else if (serverSession.EagerlySyncChannels) { instance.SendChannel(serverSession.NetworkId, channel, register: false); } } } public static void Unregister<T>(NetworkPayloadType<T> type) where T : NetworkPayload<T> { Guard.NotNull(type, "type"); Unregister(type.Channel); } internal static void Invoke(Networking networking, string channel, FastBufferReader reader) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(networking, "networking"); Guard.NotNullOrWhiteSpace(channel, "channel"); if (!networking.IsClient) { throw new NetworkConfigurationException("Attempting to use client-only method on the server."); } GameClient client = networking.Client; IHandler value; if (!client.IsReady) { Logger.Warning("Received clientbound payload on channel [" + channel + "] from server before we are ready to accept them."); client.ServerSession.Disconnect("Sending payloads before finishing setup is disallowed."); } else if (Handlers.TryGetValue(channel, out value)) { value.Invoke(client.Player, reader); } else { Logger.Warning("Received clientbound payload on channel [" + channel + "] from server but it has no handlers."); } } internal static void InvokeLocal<T>(Networking networking, T payload) where T : NetworkPayload<T> { //IL_0023: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(networking, "networking"); Guard.NotNull(payload, "payload"); if (!networking.IsHost) { throw new NetworkConfigurationException("Attempting to use host-only method on non-host."); } string channel = payload.PayloadType.Channel; GameClient client = networking.Client; IHandler value; if (!client.IsReady) { Logger.Warning("Received clientbound payload on channel [" + channel + "] from server before we are ready to accept them."); client.ServerSession.Disconnect("Sending payloads before finishing setup is disallowed."); } else if (Handlers.TryGetValue(channel, out value)) { if (!(value is Handler<T> handler)) { Logger.Error($"Received clientbound payload on channel [{channel}] from local server but the handler is the wrong type. (Expected: {typeof(T)}, Registered: {value.Type})"); client.ServerSession.Disconnect("Unexpected Handler Type"); } else if (payload.PayloadType != value.PayloadType) { Logger.Error("Received clientbound payload on channel [" + channel + "] from local server but the payload type is not the expected reference."); client.ServerSession.Disconnect("Unexpected Type Reference"); } else { handler.InvokeLocal(client.Player, payload); } } else { Logger.Warning("Received clientbound payload on channel [" + channel + "] from local server but it has no handlers."); } } internal static void OnChannelRegister(NetworkSession session, string channel) { Delegate[] array = ClientNetworking.OnServerChannelRegister?.GetInvocationList(); if (array == null || array.Length == 0) { return; } Delegate[] array2 = array; foreach (Delegate @delegate in array2) { try { ((Action<NetworkSession, string>)@delegate)(session, channel); } catch (Exception ex) { Logger.Error("Client networking channel register [" + channel + "] from server failed: " + ex.Message + "\n" + ex.StackTrace); } } } internal static void OnChannelUnregister(NetworkSession session, string channel) { Delegate[] array = ClientNetworking.OnServerChannelUnregister?.GetInvocationList(); if (array == null || array.Length == 0) { return; } Delegate[] array2 = array; foreach (Delegate @delegate in array2) { try { ((Action<NetworkSession, string>)@delegate)(session, channel); } catch (Exception ex) { Logger.Error("Client networking channel unregister [" + channel + "] from server failed: " + ex.Message + "\n" + ex.StackTrace); } } } internal static void OnSetupSuccess(NetworkSession session) { Delegate[] array = ClientNetworking.OnSetupComplete?.GetInvocationList(); if (array == null || array.Length == 0) { return; } Delegate[] array2 = array; foreach (Delegate @delegate in array2) { try { ((Action<NetworkSession>)@delegate)(session); } catch (Exception ex) { Logger.Error("Client networking setup complete from server failed: " + ex.Message + "\n" + ex.StackTrace); } } } } public sealed class Networking { internal const int MaxChannels = 8192; internal const int MaxChannelNameSize = 128; internal static readonly byte[] LibraryIdentifier = Encoding.UTF8.GetBytes("CookieFx"); internal static readonly byte[] ChannelIdentifier = Encoding.UTF8.GetBytes("CookieCh"); internal static readonly int IdentifierSize = FastBufferWriter.GetWriteSize<byte>(LibraryIdentifier, -1, 0); private static readonly ConcurrentQueue<Action<Networking>> TaskQueue = new ConcurrentQueue<Action<Networking>>(); public const ulong ServerClientId = 0uL; [MemberNotNullWhen(true, "Instance")] public static bool IsConnected { [MemberNotNullWhen(true, "Instance")] get { return Instance != null; } } public static Networking? Instance { get; private set; } public ulong LocalClientId => Backend.LocalClientId; [MemberNotNullWhen(true, "Client")] [MemberNotNullWhen(false, "Server")] public bool IsClient { [MemberNotNullWhen(true, "Client")] [MemberNotNullWhen(false, "Server")] get { return Backend.IsClient; } } [MemberNotNullWhen(true, "Server")] [MemberNotNullWhen(false, "Client")] public bool IsServer { [MemberNotNullWhen(true, "Server")] [MemberNotNullWhen(false, "Client")] get { return Backend.IsServer; } } [MemberNotNullWhen(true, new string[] { "Client", "Server" })] public bool IsHost { [MemberNotNullWhen(true, new string[] { "Client", "Server" })] get { return Backend.IsHost; } } public NetworkManager Backend { get; } public GameClient? Client { get; private set; } public AbstractServer? Server { get; private set; } public static event Action<Networking>? OnInitialize; public static event Action<Networking>? OnShutdown; public static event Action<Networking, ServerPlayer>? OnPlayerConnected; public static event Action<Networking, ServerPlayer>? OnPlayerDisconnect; public static event Action<Networking>? OnTick; public static void EnqueueTask(Action<Networking> task) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(task, "task"); if (!IsConnected) { throw new NetworkConfigurationException("Attempting to use netcode while not connected to a server."); } TaskQueue.Enqueue(task); } public static void Initialize(NetworkManager backend) { Guard.NotNull<NetworkManager>(backend, "backend"); if (backend != NetworkManager.Singleton) { Logger.Warning("The Unity Network Manager is not a singleton, which is unexpected."); } if (Instance != null) { Logger.Info("Shutdown"); Instance.Destroy(); TaskQueue.Clear(); } Logger.Info("Initialize"); Instance = new Networking(backend); Instance.Startup(); } public static void Shutdown() { Logger.Info("Shutdown"); if (Instance != null) { Instance.Destroy(); Instance = null; TaskQueue.Clear(); } } private Networking(NetworkManager backend) { Guard.NotNull<NetworkManager>(backend, "backend"); Backend = backend; } private void Startup() { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown if (Backend.IsClient || Backend.IsHost) { Client = GameClient.StartClient(this); } if (Backend.IsServer || Backend.IsHost) { if (Client != null) { Server = Client.StartHost(); } else { Server = DedicatedServer.StartServer(this); } Backend.OnClientConnectedCallback += OnClientConnectedCallback; Backend.OnClientDisconnectCallback += OnClientDisconnectCallback; } Backend.NetworkTickSystem.Tick += OnNetworkTick; Backend.CustomMessagingManager.OnUnnamedMessage += new UnnamedMessageDelegate(OnBackendMessage); InvokeInitialize(); } private void Destroy() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown InvokeShutdown(); Client?.OnShuttingDown(); if (Server != null) { Server.OnShutdown(); Server = null; Backend.OnClientConnectedCallback -= OnClientConnectedCallback; Backend.OnClientDisconnectCallback -= OnClientDisconnectCallback; } Backend.NetworkTickSystem.Tick -= OnNetworkTick; Backend.CustomMessagingManager.OnUnnamedMessage -= new UnnamedMessageDelegate(OnBackendMessage); } public static int GetChannelHeaderSize(string channel) { return IdentifierSize + FastBufferWriter.GetWriteSize(channel, false); } internal static FastBufferWriter EncodePayload<T>(T payload) where T : NetworkPayload<T> { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) NetworkPayloadType<T> payloadType = payload.PayloadType; int estimatedSize = payload.GetEstimatedSize(); int num = payloadType.HeaderSize + estimatedSize; if (estimatedSize < 0) { throw new InvalidOperationException($"The payload on channel [{payloadType.Channel}] estimates a negative size of {estimatedSize} bytes."); } if (num > 65535) { Logger.Warning($"The payload on channel [{payloadType.Channel}] estimates of {num} bytes (of which {payloadType.HeaderSize} bytes is the header). This is larger than the recommended max size of {ushort.MaxValue} bytes. The message may be dropped during transit."); } int num2 = num * 2; if (num2 < num) { num2 = int.MaxValue; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(num, (Allocator)2, Math.Max(65535, num2)); try { if (!((FastBufferWriter)(ref val)).TryBeginWrite(num)) { throw new OutOfMemoryException($"The required buffer {((FastBufferWriter)(ref val)).MaxCapacity} is too small to write the payload of at least {num} bytes."); } ((FastBufferWriter)(ref val)).WriteUnmanagedSafe<byte>(ChannelIdentifier); ((FastBufferWriter)(ref val)).WriteValueSafe(payloadType.Channel, false); payload.Encode(val); return val; } catch { ((FastBufferWriter)(ref val)).Dispose(); throw; } } internal void SendBackendMessage(ulong networkId, FastBufferWriter writer) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Backend.CustomMessagingManager.SendUnnamedMessage(networkId, writer, (NetworkDelivery)4); } internal void SendBackendMessage(ulong[] networkId, FastBufferWriter writer) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (networkId.Length != 0) { if (networkId.Length == 1) { Backend.CustomMessagingManager.SendUnnamedMessage(networkId[0], writer, (NetworkDelivery)4); } else { Backend.CustomMessagingManager.SendUnnamedMessage((IReadOnlyList<ulong>)networkId, writer, (NetworkDelivery)4); } } } private void OnBackendMessage(ulong senderId, FastBufferReader reader) { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) if (!((FastBufferReader)(ref reader)).TryBeginRead(IdentifierSize)) { return; } byte[] array = default(byte[]); ((FastBufferReader)(ref reader)).ReadUnmanagedSafe<byte>(ref array); if (array.AsSpan().SequenceEqual(ChannelIdentifier)) { string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); if (senderId != 0L && !IsServer) { Logger.Error($"Received unexpected payload on channel [{text}] from client {senderId}, we only accept messages from the server."); } else if (senderId == Backend.LocalClientId) { Logger.Error("Received unexpected payload on channel [" + text + "] from local host or server, we only accept messages from others."); } else if (senderId == 0L) { Logger.Debug("Received client payload on channel [" + text + "] from server"); ClientNetworking.Invoke(this, text, reader); } else { Logger.Debug($"Received server payload [{text}] from client {senderId}"); ServerNetworking.Invoke(this, senderId, text, reader); } } else if (array.AsSpan().SequenceEqual(LibraryIdentifier)) { InternalPayloadType internalPayloadType = default(InternalPayloadType); ((FastBufferReader)(ref reader)).ReadValueSafe<InternalPayloadType>(ref internalPayloadType, default(ForEnums)); if (senderId != 0L && !IsServer) { Logger.Error($"Received unexpected internal payload [{internalPayloadType}] from client {senderId}. We only accept messages from the server."); return; } if (senderId == Backend.LocalClientId) { Logger.Error($"Received unexpected internal payload [{internalPayloadType}] from local host or server. We only accept encoded messages from non-local sources."); return; } Logger.Info(string.Format("Received internal payload [{0}] from {1}", internalPayloadType, (senderId == 0L) ? "server" : $"client {senderId}")); OnInternalMessage(senderId, internalPayloadType, reader); } } private void OnNetworkTick() { Action<Networking> result; while (TaskQueue.TryDequeue(out result)) { try { result(this); } catch (Exception ex) { Logger.Error("Network tick failed in task queue. " + ex.Message + "\n" + ex.StackTrace); } } Delegate[] array = Networking.OnTick?.GetInvocationList(); if (array == null || array.Length == 0) { return; } Delegate[] array2 = array; foreach (Delegate @delegate in array2) { try { ((Action<Networking>)@delegate)(this); } catch (Exception ex2) { Logger.Error("Network tick failed. " + ex2.Message +