Decompiled source of CookieFx v0.1.0
CookieFx.dll
Decompiled a month 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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [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("Debug")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("CookieFx")] [assembly: AssemblyTitle("CookieFx")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.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.0")] public sealed class Plugin : BaseUnityPlugin { private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: 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.0 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.0"; } } 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"); IPlayerComponent value; return _components.TryGetValue(type, out value) ? value : null; } [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 { 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); } } } protected internal virtual IEnumerator SetupTimeout(ulong networkId) { Stopwatch timer = Stopwatch.StartNew(); yield return (object)new WaitUntil((Func<bool>)(() => Server.IsStopping || timer.Elapsed.TotalSeconds > 80.0 || !_tracked.ContainsKey(networkId) || _players.ContainsKey(networkId))); timer.Stop(); if (!Server.IsStopping && !_players.ContainsKey(networkId) && _tracked.TryGetValue(networkId, out NetworkSession.Client session) && timer.Elapsed.TotalSeconds > 60.0) { session.Disconnect("Setup timed out. (Server-side)"); } } 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 { public new IntegratedServer Server { get; } public GameClient Client { get; } public NetworkSession.Client HostSession { get; } public ServerPlayer? Host { get; } public IntegratedPlayerManager(IntegratedServer server, GameClient client) : base(server) { Guard.NotNull(client, "client"); Server = server; Client = client; HostSession = NetworkSession.CreateClient(server.Networking, client.NetworkId); } protected internal override void OnClientAdded(ulong networkId) { if (networkId == HostSession.NetworkId) { ((MonoBehaviour)Server.Networking.Backend).StartCoroutine(DelayedHostConnect()); } else { base.OnClientAdded(networkId); } } private IEnumerator DelayedHostConnect() { Logger.Info("Delaying host player."); yield return (object)new WaitUntil((Func<bool>)(() => PlayerUtils.CanPersistLocal || Client.IsStopping || Server.IsStopping)); if (!Client.IsStopping && !Server.IsStopping) { Logger.Info("Continuing delayed host player."); OnClientConnected(HostSession); } else { Logger.Warning("Host player interrupted."); } } protected internal override void OnClientRemoved(ulong networkId) { if (networkId == HostSession.NetworkId) { OnClientDisconnect(HostSession); } 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."); return; } session.StartSetup(Server.Networking); value = new ServerPlayer(Server, HostSession); _players.Add(networkId, value); PluginUtils.OnPlayerTracked(value); Server.Networking.SendPlayerTracking(this, value, track: true); Server.Networking.InvokePlayerConnected(value); } else { base.OnClientConnected(session); } } } } 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_0013: 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."); } PlayerControllerB localPlayerController = StartOfRound.Instance.localPlayerController; return 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) { int value; return CanPersistLocal && StartOfRound.Instance.ClientPlayerList.TryGetValue(networkId, out value) && StartOfRound.Instance.allPlayerScripts[value].playerSteamId != 0; } public static bool TryGetPlayerScript(int playerId, [NotNullWhen(true)] out PlayerControllerB? script) { if (StartOfRound.Instance == null) { script = null; return false; } StartOfRound instance = StartOfRound.Instance; PlayerControllerB[] allPlayerScripts = 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_001d: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) 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 { internal static void PatchTheirStuff(Harmony harmony) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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}"); } } internal static IEnumerable<CodeInstruction> OnStaticNetcodeLib(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { MethodInfo target = AccessTools.DeclaredMethod(typeof(FuckThisShitPatches), "TryStaticNetcodeLib", (Type[])null, (Type[])null); Label label = generator.DefineLabel(); yield return new CodeInstruction(OpCodes.Ldarga_S, (object)2); yield return new CodeInstruction(OpCodes.Call, (object)target); yield return new CodeInstruction(OpCodes.Brtrue, (object)label); yield return new CodeInstruction(OpCodes.Ret, (object)null); yield return new CodeInstruction(OpCodes.Nop, (object)null) { labels = { label } }; foreach (CodeInstruction instruction in instructions) { yield return instruction; } } private static bool TryStaticNetcodeLib(in FastBufferReader reader) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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 { [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); } internal static IEnumerable<CodeInstruction> DeleteFileBetterSaves(IEnumerable<CodeInstruction> instructions) { MethodInfo target = AccessTools.Method("LCBetterSaves.Plugin:InitializeBetterSaves", (Type[])null, (Type[])null); Type fieldHolder = AccessTools.TypeByName("DeleteFileButton_BetterSaves"); FieldInfo targetField = AccessTools.DeclaredField(fieldHolder, "fileToDelete"); MethodInfo deleteCall = AccessUtils.MethodInfo((Expression<Func<Action<int>>>)(() => DeleteFile)); bool found = false; foreach (CodeInstruction instruction in instructions) { if (!found && CodeInstructionExtensions.Calls(instruction, target)) { yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Ldfld, (object)targetField); yield return new CodeInstruction(OpCodes.Call, (object)deleteCall); yield return instruction; found = true; } else { yield return instruction; } } if (!found) { Logger.Error("Failed to patch LCBetterSaves delete file button."); } } } } 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_0019: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: 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."); } GameClient client = instance.Client; NetworkSession.Server serverSession = 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_0042: 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."); } GameClient client = instance.Client; NetworkSession.Server serverSession = 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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_002c: 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_001f: 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_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: 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 == 0) { 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 + "\n" + ex2.StackTrace); } } } private void OnClientConnectedCallback(ulong clientId) { if (Server == null) { PlayerUtils.Disconnect(clientId, "No active server."); return; } Logger.Info($"Client added: {clientId}"); Server.PlayerManager.OnClientAdded(clientId); } private void OnClientDisconnectCallback(ulong clientId) { if (Server == null) { PlayerUtils.Disconnect(clientId, "No active server."); return; } Logger.Info($"Client removed: {clientId}"); Server.PlayerManager.OnClientRemoved(clientId); } internal void InvokePlayerConnected(ServerPlayer player) { Delegate[] array = Networking.OnPlayerConnected?.GetInvocationList(); if (array == null || array.Length == 0) { return; } Delegate[] array2 = array; foreach (Delegate @delegate in array2) { try { ((Action<Networking, ServerPlayer>)@delegate)(this, player); } catch (Exception ex) { Logger.Error("Networking event [Player Connected] failed. " + ex.Message + "\n" + ex.StackTrace); } } } internal void InvokePlayerDisconnected(ServerPlayer player) { Delegate[] array = Networking.OnPlayerDisconnect?.GetInvocationList(); if (array == null || array.Length == 0) { return; } Delegate[] array2 = array; foreach (Delegate @delegate in array2) { try { ((Action<Networking, ServerPlayer>)@delegate)(this, player); } catch (Exception ex) { Logger.Error("Networking event [Player Disconnect] failed. " + ex.Message + "\n" + ex.StackTrace); } } } internal void InvokeInitialize() { Delegate[] array = Networking.OnInitialize?.GetInvocationList(); if (array == null || array.Length == 0) { return; } Delegate[] array2 = array; foreach (Delegate @delegate in array2) { try { ((Action<Networking>)@delegate)(this); } catch (Exception ex) { Logger.Error("Networking event [Initialize] failed. " + ex.Message + "\n" + ex.StackTrace); } } } internal void InvokeShutdown() { Delegate[] array = Networking.OnShutdown?.GetInvocationList(); if (array == null || array.Length == 0) { return; } Delegate[] array2 = array; foreach (Delegate @delegate in array2) { try { ((Action<Networking>)@delegate)(this); } catch (Exception ex) { Logger.Error("Networking event [Shutdown] failed. " + ex.Message + "\n" + ex.StackTrace); } } } private void OnInternalMessage(ulong senderId, InternalPayloadType type, FastBufferReader reader) { //IL_005f: 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) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (!TryGetSession(senderId, out NetworkSession session)) { Logger.Error($"Couldn't find session for {senderId} while receiving internal payload."); PlayerUtils.Disconnect(senderId, "Unknown NetworkId"); return; } switch (type) { case InternalPayloadType.Setup: OnSetupRequest(session, reader); break; case InternalPayloadType.SetupComplete: session.OnSetupComplete(this); break; case InternalPayloadType.TrackPlayers: OnTrackPlayers(session, reader); break; case InternalPayloadType.UntrackPlayers: OnUntrackPlayers(session, reader); break; case InternalPayloadType.RegisterChannels: OnRegisterChannels(session, reader); break; case InternalPayloadType.UnregisterChannels: OnUnregisterChannels(session, reader); break; default: Logger.Error($"Received unknown internal payload [{type}] from [{senderId}]"); break; } } private bool TryGetSession(ulong senderId, [MaybeNullWhen(false)] out NetworkSession session) { if (senderId == 0) { if (!IsClient) { session = null; return false; } session = Client.ServerSession; return true; } if (!IsServer) { session = null; return false; } if (Server.PlayerManager.TryGetSession(senderId, out NetworkSession.Client session2)) { session = session2; return true; } session = null; return false; } internal static FastBufferWriter PrepareInternalPayload(InternalPayloadType type, int size) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) size += IdentifierSize + FastBufferWriter.GetWriteSize<InternalPayloadType>(ref type, default(ForStructs)); FastBufferWriter result = default(FastBufferWriter); ((FastBufferWriter)(ref result))..ctor(size, (Allocator)2, -1); try { if (!((FastBufferWriter)(ref result)).TryBeginWrite(size)) { throw new OutOfMemoryException($"The required buffer {((FastBufferWriter)(ref result)).MaxCapacity} is too small to write the internal payload of exactly {size} bytes."); } ((FastBufferWriter)(ref result)).WriteUnmanagedSafe<byte>(LibraryIdentifier); ((FastBufferWriter)(ref result)).WriteValueSafe<InternalPayloadType>(ref type, default(ForEnums)); return result; } catch { ((FastBufferWriter)(ref result)).Dispose(); throw; } } internal void SendAllPlayers(ulong networkId, AbstractPlayerManager manager) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(manager, "manager"); ServerPlayer[] array = manager.PlayersExcept(networkId).ToArray(); if (array.Length == 0) { return; } int num = FastBufferWriter.GetWriteSize<int>(); ServerPlayer[] array2 = array; foreach (ServerPlayer serverPlayer in array2) { num += FastBufferWriter.GetWriteSize<ulong>() + FastBufferWriter.GetWriteSize<ulong>(); } FastBufferWriter writer = PrepareInternalPayload(InternalPayloadType.TrackPlayers, num); try { int num2 = array.Length; ((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num2, default(ForPrimitives)); ServerPlayer[] array3 = array; foreach (ServerPlayer serverPlayer2 in array3) { ulong networkId2 = serverPlayer2.NetworkId; ((FastBufferWriter)(ref writer)).WriteValueSafe<ulong>(ref networkId2, default(ForPrimitives)); networkId2 = serverPlayer2.PersistentId; ((FastBufferWriter)(ref writer)).WriteValueSafe<ulong>(ref networkId2, default(ForPrimitives)); } Logger.Info(string.Format("Sent tracking for [{0}] to net:{1}", string.Join(", ", (IEnumerable<ServerPlayer>)array), networkId)); SendBackendMessage(networkId, writer); } finally { ((IDisposable)(FastBufferWriter)(ref writer)).Dispose(); } } internal void SendPlayerTracking(AbstractPlayerManager manager, ServerPlayer player, bool track) { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(manager, "manager"); Guard.NotNull(player, "player"); if (manager is IntegratedPlayerManager integratedPlayerManager && !player.Session.IsLocal) { if (track) { integratedPlayerManager.Client.PlayerManager.OnPlayerTracked(player.NetworkId, player.PersistentId); } else { integratedPlayerManager.Client.PlayerManager.OnPlayerUntracked(player.NetworkId); } } ulong[] array = (from p in manager.PlayersExcept(player.NetworkId) where p.Session.EagerlySyncPlayers && !p.Session.IsLocal select p.NetworkId).ToArray(); if (array.Length == 0) { return; } int num = FastBufferWriter.GetWriteSize<int>() + FastBufferWriter.GetWriteSize<ulong>(); if (track) { num += FastBufferWriter.GetWriteSize<ulong>(); } FastBufferWriter writer = PrepareInternalPayload(track ? InternalPayloadType.TrackPlayers : InternalPayloadType.UntrackPlayers, num); try { int num2 = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num2, default(ForPrimitives)); ulong networkId = player.NetworkId; ((FastBufferWriter)(ref writer)).WriteValueSafe<ulong>(ref networkId, default(ForPrimitives)); if (track) { networkId = player.PersistentId; ((FastBufferWriter)(ref writer)).WriteValueSafe<ulong>(ref networkId, default(ForPrimitives)); } Logger.Info(string.Format("Sent tracking for {0} to [{1}]", player, string.Join(", ", array))); SendBackendMessage(array, writer); } finally { ((IDisposable)(FastBufferWriter)(ref writer)).Dispose(); } } internal void SendChannel(ulong networkId, string channel, bool register) { SendChannel(new ulong[1] { networkId }, channel, register); } internal void SendChannel(ulong[] networkIds, string channel, bool register) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(networkIds, "networkIds"); Guard.NotNullOrEmpty(channel, "channel"); if (networkIds.Length == 0) { return; } int writeSize = FastBufferWriter.GetWriteSize<int>(); writeSize += FastBufferWriter.GetWriteSize(channel, false); FastBufferWriter writer = PrepareInternalPayload(register ? InternalPayloadType.RegisterChannels : InternalPayloadType.UnregisterChannels, writeSize); try { int num = 1; ((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref writer)).WriteValueSafe(channel, false); SendBackendMessage(networkIds, writer); } finally { ((IDisposable)(FastBufferWriter)(ref writer)).Dispose(); } } internal void SendChannels(ulong networkId, ICollection<string> channels, bool register) { SendChannels(new ulong[1] { networkId }, channels, register); } internal void SendChannels(ulong[] networkIds, ICollection<string> channels, bool register) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) Guard.NotNull(networkIds, "networkIds"); Guard.NotNull(channels, "channels"); if (networkIds.Length == 0 || channels.Count == 0) { return; } int num = FastBufferWriter.GetWriteSize<int>(); foreach (string channel in channels) { num += FastBufferWriter.GetWriteSize(channel, false); } FastBufferWriter writer = PrepareInternalPayload(register ? InternalPayloadType.RegisterChannels : InternalPayloadType.UnregisterChannels, num); try { int count = channels.Count; ((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref count, default(ForPrimitives)); foreach (string channel2 in channels) { ((FastBufferWriter)(ref writer)).WriteValueSafe(channel2, false); } SendBackendMessage(networkIds, writer); } finally { ((IDisposable)(FastBufferWriter)(ref writer)).Dispose(); } } private static void OnRegisterChannels(NetworkSession session, FastBufferReader reader) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) string text = ((session.NetworkId == 0L) ? "server" : $"client {session.NetworkId}"); Logger.Info("Registering channels from " + text); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); string item = default(string); for (int i = 0; i < num; i++) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref item, false); hashSet.Add(item); } if (num != hashSet.Count) { Logger.Warning("Received duplicate channels in register channels payload from " + text); } session.OnRegisterChannels(hashSet); } private static