using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx.Configuration; using Damntry.Utils.ExtensionMethods; using Damntry.Utils.Logging; using Damntry.Utils.Maths; using Damntry.Utils.Reflection; using Damntry.UtilsBepInEx.IL; using Damntry.UtilsMirror.Attributes; using Damntry.UtilsMirror.Components; using Damntry.UtilsMirror.Helpers; using Damntry.UtilsMirror.SyncVar; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Damntry Mirror Globals")] [assembly: AssemblyDescription("Utils usable in any type of Mirror Unity project")] [assembly: AssemblyCompany("Damntry")] [assembly: AssemblyProduct("Damntry Mirror Globals")] [assembly: AssemblyCopyright("Copyright © Damntry 2026")] [assembly: AssemblyFileVersion("0.2.5.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyVersion("0.2.5.0")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Damntry.UtilsMirror { public static class MirrorUtils { public static bool IsSyncVarDirty(NetworkReader reader, long byteOffset, bool keepUnmodified) { int position = reader.Position; ulong num = NetworkReaderExtensions.ReadULong(reader); if (keepUnmodified) { reader.Position = position; } return (num & (ulong)byteOffset) != 0; } } } namespace Damntry.UtilsMirror.SyncVar { public enum EnableStatus { AllDisabled, LocallyOnly, RemotelyOnly, AllEnabled } public class BoolSyncVarSetting : SyncVarSetting { public override bool Value { get { if (base.ConfigEntry.Value) { if (base.IsSynced && !NetworkServer.active) { return base.Value; } return true; } return false; } set { SetValue(value, checkWritable: true); } } public EnableStatus Status { get { if (base.ConfigEntry.Value) { if (!Value) { return EnableStatus.LocallyOnly; } return EnableStatus.AllEnabled; } if (!Value) { return EnableStatus.AllDisabled; } return EnableStatus.RemotelyOnly; } } public bool IsEnabledLocally => base.ConfigEntry.Value; public BoolSyncVarSetting(bool defaultValue, ConfigEntry configEntry) : base(defaultValue, configEntry) { } } public interface ISyncVar { void SetToDefaultValue(Type declaringType, string syncVarName); bool Writable(); void InitializeSyncObject(NetworkBehaviour netBehaviour); } [Serializable] public class SyncVar : SyncObject, ISyncVar, IEquatable { [SerializeField] protected T _Value; public Action OnFinishSyncing; private NetworkBehaviour networkBehaviourContainer; public Action OnValueChangedCallback; private bool hookGuard; public virtual T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _Value; } set { SetValue(value, checkWritable: true); } } public bool IsSynced { get { if (NetworkClient.active && Object.op_Implicit((Object)(object)networkBehaviourContainer)) { return networkBehaviourContainer.netId != 0; } return false; } } public T DefaultValue { get; private set; } protected void SetValue(T value, bool checkWritable) { if ((!checkWritable || Writable()) && !Equals(value)) { T value2 = _Value; _Value = value; if (base.OnDirty != null) { base.OnDirty(); } if (OnValueChangedCallback != null && !hookGuard && Object.op_Implicit((Object)(object)networkBehaviourContainer) && !networkBehaviourContainer.authority) { hookGuard = true; InvokeCallback(value2, value); hookGuard = false; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void InvokeCallback(T oldValue, T newValue) { OnValueChangedCallback?.Invoke(oldValue, newValue); } public override void ClearChanges() { } public override void Reset() { } public virtual void SetToDefaultValue(Type declaringType, string syncVarName) { NetworkSpawnManager.DebugLog(() => "Setting SyncVar '" + declaringType.Name + "." + syncVarName + "' " + $"from {_Value} to its DefaultValue {DefaultValue}"); _Value = DefaultValue; } public SyncVar(T defaultValue) { InitValues(defaultValue, defaultValue, null); } public SyncVar(T value, T defaultValue) { InitValues(value, defaultValue, null); } public SyncVar(T value, T defaultValue, Action onValueChangedCallback) { OnValueChangedCallback = (Action)Delegate.Combine(OnValueChangedCallback, onValueChangedCallback); InitValues(value, defaultValue, null); } public SyncVar(NetworkBehaviour netBehaviour, T value, T defaultValue) { if ((Object)(object)netBehaviour == (Object)null) { throw new ArgumentNullException("netBehaviour"); } InitValues(value, defaultValue, netBehaviour); } public SyncVar(NetworkBehaviour netBehaviour, T value, T defaultValue, Action onValueChangedCallback) { if ((Object)(object)netBehaviour == (Object)null) { throw new ArgumentNullException("netBehaviour"); } OnValueChangedCallback = (Action)Delegate.Combine(OnValueChangedCallback, onValueChangedCallback); InitValues(value, defaultValue, netBehaviour); } private void InitValues(T value, T defaultValue, NetworkBehaviour netBehaviour) { _Value = value; DefaultValue = defaultValue; if (typeof(T).IsEnum) { RegisterCustomEnum(value); } if ((Object)(object)netBehaviour != (Object)null) { InitSyncObjectReflection(netBehaviour, markAsSynced: true); } } private void RegisterCustomEnum(TEnum value) { if (Writer.write == null) { Writer.write = delegate(NetworkWriter writer, TEnum value) { NetworkWriterExtensions.WriteLong(writer, EnumExtension.EnumToLong(value)); }; } if (Reader.read == null) { Reader.read = (NetworkReader reader) => EnumExtension.LongToEnumUnconstrained(NetworkReaderExtensions.ReadLong(reader)); } } public bool Writable() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Invalid comparison between Unknown and I4 if ((Object)(object)networkBehaviourContainer == (Object)null) { bool isNetworkOffline = !NetworkSpawnManager.IsNetworkOnline(); NetworkSpawnManager.DebugLog(() => $"Writable returned {isNetworkOffline} for a non networked SyncVar!"); return isNetworkOffline; } if (NetworkServer.active && NetworkClient.active) { if ((int)networkBehaviourContainer.syncDirection != 0) { return networkBehaviourContainer.netIdentity.isOwned; } return true; } if (NetworkServer.active) { return (int)networkBehaviourContainer.syncDirection == 0; } if (NetworkClient.active) { if (networkBehaviourContainer.netIdentity.netId != 0) { if ((int)networkBehaviourContainer.syncDirection == 1) { return networkBehaviourContainer.netIdentity.isOwned; } return false; } return true; } return true; } public virtual void InitializeSyncObject(NetworkBehaviour netBehaviour) { InitSyncObjectReflection(netBehaviour, markAsSynced: true); } protected void InitSyncObjectReflection(NetworkBehaviour netBehaviour, bool markAsSynced) { networkBehaviourContainer = netBehaviour; NetworkSpawnManager.DebugLog(() => "Initializing SyncVar of Value type " + typeof(T).Name + " in " + ((Object)netBehaviour).name); ReflectionHelper.CallMethod((object)netBehaviour, "InitSyncObject", new object[1] { this }); NetworkSpawnManager.DebugLog(() => "Finished initializing SyncVar of Value type " + typeof(T).Name + " in " + ((Object)netBehaviour).name); if (markAsSynced) { OnFinishSyncing?.Invoke(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator T(SyncVar field) { return field.Value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator SyncVar(T value) { return new SyncVar(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnSerializeAll(NetworkWriter writer) { writer.Write(Value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnSerializeDelta(NetworkWriter writer) { writer.Write(Value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnDeserializeAll(NetworkReader reader) { SetValue(reader.Read(), checkWritable: false); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnDeserializeDelta(NetworkReader reader) { SetValue(reader.Read(), checkWritable: false); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(T other) { return EqualityComparer.Default.Equals(_Value, other); } public override string ToString() { return Value.ToString(); } } public class SyncVarSetting : SyncVar, ISyncVar { public ConfigEntry ConfigEntry { get; private set; } public SyncVarSetting(T defaultValue, ConfigEntry configEntry) : base(defaultValue, defaultValue) { Init(configEntry); } public SyncVarSetting(T defaultValue, Action onValueChangedCallback, ConfigEntry configEntry) : base(defaultValue, defaultValue, onValueChangedCallback) { Init(configEntry); } private void Init(ConfigEntry configEntry) { if (configEntry == null) { throw new ArgumentNullException("configEntry", "SyncVarSetting ConfigEntry cannot be null. Make sure that Bepinex config binding has been completed successfully before this call. If not using a ConfigEntry is intended, use SyncVar instead."); } NetworkSpawnManager.DebugLog(() => "SyncVarSetting constructor. " + $"Setting SyncVar to its configEntry value ({configEntry.Value})."); ConfigEntry = configEntry; _Value = configEntry.Value; RegisterSyncvarSetting(); } public void RegisterSyncvarSetting() { UnregisterSyncvarSetting(); ConfigEntry.SettingChanged += SetValueFromConfig; } public void UnregisterSyncvarSetting() { ConfigEntry.SettingChanged -= SetValueFromConfig; } public override void InitializeSyncObject(NetworkBehaviour netBehaviour) { InitSyncObjectReflection(netBehaviour, markAsSynced: false); NetworkSpawnManager.DebugLog(() => "SyncVarSetting fully initialized. " + $"Setting SyncVar from {_Value} to its configEntry value ({ConfigEntry.Value})."); SetValueFromConfig(); OnFinishSyncing?.Invoke(); } private void SetValueFromConfig(object sender, EventArgs e) { SetValueFromConfig(); } public void SetValueFromConfig() { if (NetworkServer.active) { Value = ConfigEntry.Value; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator T(SyncVarSetting field) { return field.Value; } } } namespace Damntry.UtilsMirror.Helpers { public static class NetworkPrefabHelper { private static readonly string NetworkRootObjName = "NetworkedObjects"; public static GameObject GetNetworkReadyPrefab(string prefabName, out NetworkBehaviour networkBehaviour) where T : NetworkBehaviour { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown GameObject val = new GameObject(prefabName); val.SetActive(false); val.transform.parent = GetRootNetworkTransform(); val.AddComponent(); networkBehaviour = (NetworkBehaviour)(object)val.AddComponent(); return val; } private static Transform GetRootNetworkTransform() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown GameObject val = GameObject.Find(NetworkRootObjName); if (val == null) { val = new GameObject(NetworkRootObjName); } return val.transform; } public static bool AssetIdExistsAsPrefab(uint assetId) { return NetworkClient.prefabs.ContainsKey(assetId); } public static bool AssetIdAlreadyRegistered(uint assetId) { string text = "spawnHandlers"; Type typeFromHandle = typeof(NetworkClient); FieldInfo fieldInfo = AccessTools.Field(typeFromHandle, text); if (fieldInfo == null) { return true; } object value = fieldInfo.GetValue(null); if (value == null) { TimeLogger.Logger.LogError($"The field {typeFromHandle}.{text} value is null.", (LogCategories)512); return true; } if (!(value is Dictionary)) { TimeLogger.Logger.LogError($"The field {typeFromHandle}.{text} does not have " + "the expected Dictionary type.", (LogCategories)512); return true; } return ((Dictionary)value).ContainsKey(assetId); } public static bool IsPrefabGameObjectRegistered(GameObject prefabObj, uint expectedAssetId) { if (NetworkClient.prefabs != null && NetworkClient.prefabs.TryGetValue(expectedAssetId, out var value)) { return prefabObj == value; } return false; } public static bool IsPrefabHandlerRegistered(SpawnHandlerDelegate spawnHandlerDelegate, uint expectedAssetId) { if (FindSpawnhandlerByAssetId(expectedAssetId, out var IsFieldNotFound, out var spawnDelegate)) { return spawnHandlerDelegate == spawnDelegate; } return IsFieldNotFound; } public static bool FindSpawnhandlerByAssetId(uint assetId, out bool IsFieldNotFound, out SpawnHandlerDelegate spawnDelegate) { spawnDelegate = null; IsFieldNotFound = false; try { string text = "spawnHandlers"; FieldInfo field = typeof(NetworkClient).GetField("spawnHandlers", AccessTools.all); if (field != null) { return ((Dictionary)field.GetValue(null)).TryGetValue(assetId, out spawnDelegate); } TimeLogger.Logger.LogError("The field " + text + " could not be found in type " + typeof(NetworkClient).Name + ".", (LogCategories)512); } catch (Exception ex) { TimeLogger.Logger.LogException(ex, (LogCategories)512); } IsFieldNotFound = true; return false; } } public interface INetworkPrefabSpawner { Type NetworkBehaviourType { get; } uint DefinedAssetId { get; } bool IsSelfManagedSpawning { get; } void AddToPrefabs(); void Spawn(); } public class NetworkPrefabSpawner : INetworkPrefabSpawner where T : NetworkBehaviour { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnSpawnDelegate <>9__19_1; internal void b__19_1(GameObject spawned) { } } private GameObject prefabObj; private NetworkBehaviour networkBehaviourInstance; public uint DefinedAssetId { get; init; } public bool PrefabRegisterOk { get; private set; } public bool IsSelfManagedSpawning { get; init; } public Type NetworkBehaviourType { get; private set; } public NetworkPrefabSpawner(uint assetId, bool isSelfManagedSpawning) { NetworkBehaviourType = typeof(T); DefinedAssetId = assetId; IsSelfManagedSpawning = isSelfManagedSpawning; } public void AddToPrefabs() { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown string typeName = NetworkBehaviourType.Name; prefabObj = NetworkPrefabHelper.GetNetworkReadyPrefab(typeName, out networkBehaviourInstance); if ((Object)(object)prefabObj == (Object)null) { TimeLogger.Logger.LogError("A prefab object couldnt be created from type " + typeName, (LogCategories)512); return; } uint definedAssetId = DefinedAssetId; if (NetworkPrefabHelper.AssetIdExistsAsPrefab(definedAssetId)) { TimeLogger.Logger.LogError($"The specified assetId \"{definedAssetId}\" for the NetworkBehaviour " + "class " + typeName + " was already found in the NetworkClient.", (LogCategories)512); return; } if (NetworkPrefabHelper.AssetIdAlreadyRegistered(definedAssetId)) { TimeLogger.Logger.LogError($"The specified assetId \"{definedAssetId}\" for the NetworkBehaviour " + "class " + typeName + " has already been registered. Its possible there is a duplicated assetId in this project, or one coming from another mod, or added by the game at runtime.", (LogCategories)512); return; } SpawnHandlerDelegate val = (SpawnHandlerDelegate)((SpawnMessage msg) => prefabObj); object obj = <>c.<>9__19_1; if (obj == null) { UnSpawnDelegate val2 = delegate { }; <>c.<>9__19_1 = val2; obj = (object)val2; } UnSpawnDelegate val3 = (UnSpawnDelegate)obj; NetworkClient.RegisterPrefab(prefabObj, DefinedAssetId, val, val3); PrefabRegisterOk = NetworkPrefabHelper.IsPrefabHandlerRegistered(val, DefinedAssetId); if (!PrefabRegisterOk) { NetworkSpawnManager.DebugLog(() => "Prefab NOT registered for NetworkBehaviour " + typeName + "."); return; } NetworkSpawnManager.DebugLog(() => "Prefab was registered correctly for NetworkBehaviour " + typeName + "."); TriggerStartNetworkSession(NetworkSpawnManager.GetCurrentNetworkMode()); } private void TriggerStartNetworkSession(NetworkMode networkMode) { if (typeof(ISyncVarBehaviour).IsAssignableFrom(typeof(T))) { ((ISyncVarBehaviour)networkBehaviourInstance).StartNetworkSession(networkMode); } } public void Spawn() { if (IsSelfManagedSpawning && PrefabRegisterOk) { if ((Object)(object)prefabObj == (Object)null) { TimeLogger.Logger.LogFatal("The prefab object to spawn for type " + NetworkBehaviourType.Name + ". is null. Make sure to call AddToPrefabs() before spawning happens.", (LogCategories)512); } else { NetworkServer.Spawn(prefabObj, (NetworkConnection)null); } } } } public enum NetworkMode { NotInitialized, Offline, ServerOnly, ClientOnly, Host } [HarmonyPatch] public static class NetworkSpawnManager { private static readonly Harmony harmony; private static readonly Dictionary networkBehaviourRegistry; private static uint _assetIdSignature; public static bool NetworkDebugLog; public static event Action OnBeforeObjectSpawn; static NetworkSpawnManager() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown NetworkDebugLog = true; harmony = new Harmony(typeof(NetworkSpawnManager).FullName); networkBehaviourRegistry = new Dictionary(); } public static bool IsNetworkActive() { if (!NetworkServer.active) { return NetworkClient.active; } return true; } public static bool IsNetworkOnline() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if ((Object)(object)NetworkManager.singleton != (Object)null) { return (int)NetworkManager.singleton.mode > 0; } return false; } public static bool IsNetworkOnlineHosting() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if (!((Object)(object)NetworkManager.singleton != (Object)null) || (int)NetworkManager.singleton.mode != 3) { return (int)NetworkManager.singleton.mode == 1; } return true; } public static bool IsNetworkClientOnly() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if ((Object)(object)NetworkManager.singleton != (Object)null) { return (int)NetworkManager.singleton.mode == 2; } return false; } public static NetworkMode GetCurrentNetworkMode() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected I4, but got Unknown if ((Object)(object)NetworkManager.singleton == (Object)null) { return NetworkMode.NotInitialized; } NetworkManagerMode mode = NetworkManager.singleton.mode; return (int)mode switch { 0 => NetworkMode.Offline, 1 => NetworkMode.ServerOnly, 2 => NetworkMode.ClientOnly, 3 => NetworkMode.Host, _ => throw new NotImplementedException(), }; } public static void Initialize(int assetIdSignature) { if (assetIdSignature < 1 || assetIdSignature > 4293) { TimeLogger.Logger.LogFatal($"The value of assetIdSignature is {assetIdSignature} " + "but it must be between 1 and 4293, both inclusive. All related network functionality will not work.", (LogCategories)512); return; } _assetIdSignature = (uint)assetIdSignature; harmony.PatchAll(typeof(NetworkSpawnManager)); if (!harmony.GetPatchedMethods().Any()) { TimeLogger.Logger.LogFatal("NetworkSpawnManager patch failed. Modded network features wont work.", (LogCategories)512); } } [HarmonyPatch(typeof(NetworkServer), "SpawnObjects")] [HarmonyPatch(typeof(NetworkClient), "PrepareToSpawnSceneObjects")] [HarmonyPostfix] public static void OnStartNetworkSessionPostfix(MethodBase __originalMethod) { DebugLog(() => "OnStartNetworkSessionPostfix - Coming from method " + $"{__originalMethod.Name} - Network mode: {NetworkManager.singleton.mode}"); if (IsNetworkActive()) { DebugLog("Mirror network system is active. Adding network spawns"); NetworkSpawnManager.OnBeforeObjectSpawn?.Invoke(); AddNetworkSpawns(); } else { DebugLog("Mirror network system is NOT active. Skipping this session initialization"); } } public static void RegisterNetwork(uint assetId, bool isSelfManagedSpawning = true) where T : NetworkBehaviour { if (assetId == 0) { throw new ArgumentException("The assetId parameter cant be 0."); } if (MathMethods.CountDigits(assetId) > 6) { throw new ArgumentException("The assetId parameter must have less than 7 digits [1 to 999999]."); } Type typeFromHandle = typeof(T); if (networkBehaviourRegistry.ContainsKey(typeFromHandle)) { throw new InvalidOperationException("The NetworkBehaviour of type " + typeFromHandle.Name + " was already registered."); } NetworkPrefabSpawner value = new NetworkPrefabSpawner(GetAssetIdFromSignature(assetId), isSelfManagedSpawning); networkBehaviourRegistry.Add(typeFromHandle, value); } private static void AddNetworkSpawns() { foreach (KeyValuePair item in networkBehaviourRegistry) { item.Value.AddToPrefabs(); } if (NetworkServer.active) { BeginHostSpawning(); } } private static void BeginHostSpawning() { DebugLog(() => $"Spawning objects - NetworkServer active? {NetworkServer.active}"); if (!NetworkServer.active) { return; } foreach (KeyValuePair item in networkBehaviourRegistry) { item.Value.Spawn(); } } private static uint GetAssetIdFromSignature(uint assetId) { return _assetIdSignature * 1000000 + assetId; } public static void DebugLog(Func textLambda) { } public static void DebugLog(string text) { } } } namespace Damntry.UtilsMirror.Components { public interface ISyncVarBehaviour { void StartNetworkSession(NetworkMode networkMode); } public abstract class SyncVarNetworkBehaviour : NetworkBehaviour, ISyncVarBehaviour where T : SyncVarNetworkBehaviour { private static readonly BindingFlags SearchFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private readonly Type derivedType = typeof(T); private readonly List validSyncVarList = new List(); private static readonly Dictionary methodRedirects = new Dictionary(); private readonly Harmony harmony = new Harmony(typeof(SyncVarNetworkBehaviour).FullName); private static readonly bool IS_DEBUG_NETWORK_TESTS__ENABLED = false; protected virtual void OnStartNetworkSession(NetworkMode networkMode) { } protected virtual void OnSyncVarValuesDefaulted() { } protected virtual void OnSyncVarsNetworkReady() { } protected static ushort GetMirrorMethodHashCode(string methodName) { return (ushort)((uint)Extensions.GetStableHashCode(methodName) & 0xFFFFu); } public void StartNetworkSession(NetworkMode networkMode) { NetworkSpawnManager.DebugLog(() => "OnStartNetworkSession call begins for type " + derivedType.Name + "."); OnStartNetworkSession(networkMode); validSyncVarList.Clear(); foreach (MemberInfoHelper item in GetDerivedSyncVarsOfType(typeof(SyncVar<>))) { if (CheckSyncVarValidity(item, out var syncVarInstance)) { validSyncVarList.Add(syncVarInstance); syncVarInstance.SetToDefaultValue(((MemberInfo)(object)item).DeclaringType, ((MemberInfo)(object)item).Name); } } OnSyncVarValuesDefaulted(); } protected virtual void Awake() { foreach (ISyncVar validSyncVar in validSyncVarList) { InitializeSyncVar(validSyncVar, (T)this); } validSyncVarList.Clear(); NetworkSpawnManager.DebugLog(() => "OnSyncVarsNetworkReady call begins for type " + derivedType.Name + "."); OnSyncVarsNetworkReady(); } private List GetDerivedSyncVarsOfType(Type searchType) { IEnumerable enumerable = derivedType.GetFields(SearchFlags).Cast().Concat(derivedType.GetProperties(SearchFlags)) .Cast(); if (enumerable == null || enumerable.Count() == 0) { return new List(); } return (from mif in enumerable.Where((MemberInfo mi) => ReflectionExtension.HasCustomAttribute(mi)).Select((Func)((MemberInfo mi) => new MemberInfoHelper(mi))) where ReflectionExtension.IsSubclassOfRawGeneric(mif.MemberInfoType, searchType) select mif).ToList(); } private bool CheckSyncVarValidity(MemberInfoHelper syncVarInfoHelper, out ISyncVar syncVarInstance) { syncVarInstance = null; Type memberInfoType = syncVarInfoHelper.MemberInfoType; if (!memberInfoType.IsSubclassOf(typeof(SyncObject))) { TimeLogger.Logger.LogError("The var " + derivedType.Name + "." + ((MemberInfo)(object)syncVarInfoHelper).Name + " does not inherit from SyncObject and will be skipped. Make sure that the SyncVarNetworkAttribute annotation was intended.", (LogCategories)512); return false; } if (!typeof(ISyncVar).IsAssignableFrom(memberInfoType)) { TimeLogger.Logger.LogError("The var " + derivedType.Name + "." + ((MemberInfo)(object)syncVarInfoHelper).Name + " does not inherit from ISyncVar and will be skipped. Make sure that the SyncVarNetworkAttribute annotation was intended.", (LogCategories)512); return false; } syncVarInstance = (ISyncVar)syncVarInfoHelper.GetValueStaticAgnostic((object)(T)this); if (syncVarInstance == null) { TimeLogger.Logger.LogError("The var " + derivedType.Name + "." + ((MemberInfo)(object)syncVarInfoHelper).Name + " has not been instantiated and will be skipped. Make sure to call its constructor.", (LogCategories)512); return false; } return true; } private void InitializeSyncVar(ISyncVar syncVarInstance, T netBehaviourInstance) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!IsSyncVarNetworkInitialized((SyncObject)syncVarInstance)) { syncVarInstance.InitializeSyncObject((NetworkBehaviour)(object)netBehaviourInstance); } } private bool IsSyncVarNetworkInitialized(SyncObject syncVarInstance) { return base.syncObjects.Contains(syncVarInstance); } private Delegate GetOnChangeDelegate(string onChangeMethodName, T netBehaviourInstance, Type syncVarType) { Type type = syncVarType.GetGenericArguments()[0]; MethodInfo method = derivedType.GetMethod(onChangeMethodName, SearchFlags, null, new Type[2] { type, type }, null); if (method == null) { TimeLogger.Logger.LogWarning("Method \"" + onChangeMethodName + "\" could not be found in type " + derivedType.Name + ", or does not have the required signature: " + onChangeMethodName + "(" + syncVarType.Name + " oldValue, " + syncVarType.Name + " newValue).", (LogCategories)512); return null; } Delegate @delegate = Delegate.CreateDelegate(typeof(Action<, >).MakeGenericType(type, type), netBehaviourInstance, method); if ((object)@delegate == null) { TimeLogger.Logger.LogWarning("A delegate for method \"" + derivedType.Name + "." + onChangeMethodName + "\" could not be created.", (LogCategories)512); return null; } return @delegate; } public void CmdCall(string callingMethodName, bool requiresAuthority, T1 arg1) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); SendCmd(val, callingMethodName, requiresAuthority); } public void CmdCall(string callingMethodName, bool requiresAuthority, T1 arg1, T2 arg2) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); SendCmd(val, callingMethodName, requiresAuthority); } public void CmdCall(string callingMethodName, bool requiresAuthority, T1 arg1, T2 arg2, T3 arg3) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); ((NetworkWriter)val).Write(arg3); SendCmd(val, callingMethodName, requiresAuthority); } public void CmdCall(string callingMethodName, bool requiresAuthority, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); ((NetworkWriter)val).Write(arg3); ((NetworkWriter)val).Write(arg4); SendCmd(val, callingMethodName, requiresAuthority); } public void CmdCall(string callingMethodName, bool requiresAuthority, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); ((NetworkWriter)val).Write(arg3); ((NetworkWriter)val).Write(arg4); ((NetworkWriter)val).Write(arg5); SendCmd(val, callingMethodName, requiresAuthority); } public void CmdCall(string callingMethodName, bool requiresAuthority, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); ((NetworkWriter)val).Write(arg3); ((NetworkWriter)val).Write(arg4); ((NetworkWriter)val).Write(arg5); ((NetworkWriter)val).Write(arg6); SendCmd(val, callingMethodName, requiresAuthority); } public void RpcCall(string callingMethodName, bool includeOwner, T1 arg1) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); SendRpc(val, callingMethodName, includeOwner); } public void RpcCall(string callingMethodName, bool includeOwner, T1 arg1, T2 arg2) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); SendRpc(val, callingMethodName, includeOwner); } public void RpcCall(string callingMethodName, bool includeOwner, T1 arg1, T2 arg2, T3 arg3) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); ((NetworkWriter)val).Write(arg3); SendRpc(val, callingMethodName, includeOwner); } public void RpcCall(string callingMethodName, bool includeOwner, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); ((NetworkWriter)val).Write(arg3); ((NetworkWriter)val).Write(arg4); SendRpc(val, callingMethodName, includeOwner); } public void RpcCall(string callingMethodName, bool includeOwner, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); ((NetworkWriter)val).Write(arg3); ((NetworkWriter)val).Write(arg4); ((NetworkWriter)val).Write(arg5); SendRpc(val, callingMethodName, includeOwner); } public void RpcCall(string callingMethodName, bool includeOwner, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { NetworkWriterPooled val = NetworkWriterPool.Get(); ((NetworkWriter)val).Write(arg1); ((NetworkWriter)val).Write(arg2); ((NetworkWriter)val).Write(arg3); ((NetworkWriter)val).Write(arg4); ((NetworkWriter)val).Write(arg5); ((NetworkWriter)val).Write(arg6); SendRpc(val, callingMethodName, includeOwner); } private void SendCmd(NetworkWriterPooled writer, string methodName, bool requiresAuthority) { ((NetworkBehaviour)this).SendCommandInternal(methodName, (int)GetMirrorMethodHashCode(methodName), (NetworkWriter)(object)writer, 0, requiresAuthority); NetworkWriterPool.Return(writer); } private void SendRpc(NetworkWriterPooled writer, string methodName, bool includeOwner) { ((NetworkBehaviour)this).SendRPCInternal(methodName, (int)GetMirrorMethodHashCode(methodName), (NetworkWriter)(object)writer, 0, includeOwner); NetworkWriterPool.Return(writer); } private void InitializeRedirectsRPC_CMD() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown List<(MethodInfo, MethodInfo)> rPC_MethodTargets = GetRPC_MethodTargets(); LOG.TEMPWARNING($"{rPC_MethodTargets.Count} RPC methods", true); foreach (var item in rPC_MethodTargets) { if (!methodRedirects.ContainsKey(item.Item1)) { methodRedirects.Add(item.Item1, item.Item2); MethodInfo method = new Func, ILGenerator, MethodBase, IEnumerable>(TranspileRPC_Call).Method; harmony.Patch((MethodBase)item.Item1, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null); } } LOG.TEMPWARNING("Method patching finished.", true); } public override void OnStopClient() { harmony.UnpatchSelf(); methodRedirects.Clear(); } private List<(MethodInfo origMethod, MethodInfo targetMethod)> GetRPC_MethodTargets() { return (from mi in derivedType.GetMethods(SearchFlags) where ReflectionExtension.HasCustomAttribute((MemberInfo)mi) select (mi, GetMethodInfoFromRPCAttribute(mi)) into tup where tup.Item2 != null select tup).ToList(); } private MethodInfo GetMethodInfoFromRPCAttribute(MethodInfo methodInfo) { RPC_CallOnClientAttribute customAttribute = methodInfo.GetCustomAttribute(); if (Type.GetType(customAttribute.declaringType.AssemblyQualifiedName) == null) { TimeLogger.Logger.LogError("The type FullName could not be found.", (LogCategories)512); } MethodInfo methodInfo2 = AccessTools.Method(customAttribute.declaringType, customAttribute.targetMethodName, customAttribute.parameters, customAttribute.generics); if (methodInfo2 != null && !CompareMethodSignatures(methodInfo, methodInfo2)) { methodInfo2 = null; } return methodInfo2; } public static bool CompareMethodSignatures(MethodInfo mi1, MethodInfo mi2, bool compareDeclaringType = false) { List list = new List(); if (mi1.IsGenericMethod || mi2.IsGenericMethod) { list.Add("generic methods not supported"); } if (mi1.ReturnType != mi2.ReturnType) { list.Add("return type"); } if (mi1.GetParameters().Length != mi2.GetParameters().Length) { list.Add("number of parameters"); } else { for (int i = 0; i < mi1.GetParameters().Length; i++) { if (mi1.GetParameters()[i].ParameterType != mi2.GetParameters()[i].ParameterType) { list.Add($"param {i + 1} type"); } } } if (mi1.IsStatic != mi2.IsStatic) { list.Add("static modifier"); } if (list.Count > 0) { TimeLogger.Logger.LogError("The methods " + mi1.DeclaringType.Name + "." + mi1.Name + " and " + mi2.DeclaringType.Name + "." + mi2.Name + " need to have the same method signature. Fix the following differences: " + string.Join(", ", list), (LogCategories)32); } return list.Count == 0; } [HarmonyDebug] private static IEnumerable TranspileRPC_Call(IEnumerable instructions, ILGenerator generator, MethodBase originalMethod) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown methodRedirects.TryGetValue(originalMethod, out var value); CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null); LOG.TEMPWARNING("Before emitting delegate " + ILExtensionMethods.GetFormattedIL(val.InstructionEnumeration()), true); if (NetworkServer.active) { LOG.TEMPWARNING("Host. RPC network send logic.", true); val.Advance(1); InsertRPC_GenerationCall(val, generator, value.GetParameters(), value.IsStatic); } else if (NetworkClient.active) { LOG.TEMPWARNING("Client. Executing next.", true); EmitCallTargetMethod(val, value); } else { TimeLogger.Logger.LogError("RPC method " + originalMethod.Name + " was called when no Mirror network component was active.", (LogCategories)512); } LOG.TEMPWARNING("After emitting delegate " + ILExtensionMethods.GetFormattedIL(val.InstructionEnumeration()), true); return val.InstructionEnumeration(); } private static void InsertRPC_GenerationCall(CodeMatcher codeMatcher, ILGenerator generator, ParameterInfo[] parameters, bool isStatic) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown int num = parameters.Length; if (num > 0) { codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldc_I4, (object)num) }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Newarr, (object)typeof(object)) }); int num2 = ((!isStatic) ? 1 : 0); for (int i = 0; i < num; i++) { codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Dup, (object)null) }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldc_I4, (object)i) }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstructionNew.LoadArgument(i + num2, false) }); Type parameterType = parameters[i].ParameterType; if (parameterType.IsValueType) { codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Box, (object)parameterType) }); } codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Stelem_Ref, (object)null) }); } } codeMatcher.Insert((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate>((Action)MakeRPC_Call) }); } private static void MakeRPC_Call(object[] args) { LOG.TEMPWARNING(string.Format("MakeRPC_Call - {0} ({1}) params: {2}", args.Length, args[0], string.Join(", ", args)), true); NetworkWriterPooled val = NetworkWriterPool.Get(); for (int i = 0; i < args.Length; i++) { _ = args[i]; } NetworkWriterPool.Return(val); LOG.TEMPWARNING("MakeRPC_Call finished", true); } private static void EmitCallTargetMethod(CodeMatcher codeMatcher, MethodInfo targetMethod) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown codeMatcher.End().Advance(-1); LoadMethodArgIntoStack(codeMatcher, targetMethod); codeMatcher.Insert((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Call, (object)targetMethod) }); } private static void LoadMethodArgIntoStack(CodeMatcher codeMatcher, MethodInfo targetMethod) { int num = targetMethod.GetParameters().Count() + ((!targetMethod.IsStatic) ? 1 : 0); for (int i = 0; i < num; i++) { codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstructionNew.LoadArgument(i, false) }); } } [RPC_CallOnClient(typeof(SyncVarNetworkAttribute), "UpdateSupermarketNameOnClient", null)] private void UpdateSupermarketName(string marketName) { } private static void UpdateSupermarketNameOnClient() { } } } namespace Damntry.UtilsMirror.Attributes { [AttributeUsage(AttributeTargets.Method)] public class RPC_CallOnClientAttribute : Attribute { public Type declaringType; public string targetMethodName; public Type[] parameters; public Type[] generics; public RPC_CallOnClientAttribute(Type declaringType, string targetMethodName, Type[] parameters = null) { SetTargetMethod(declaringType, targetMethodName, parameters); } public RPC_CallOnClientAttribute(Type declaringType, string targetMethodName, Type[] parameters, Type[] generics) { SetTargetMethod(declaringType, targetMethodName, parameters, generics); } private void SetTargetMethod(Type declaringType, string targetMethodName, Type[] parameters = null, Type[] generics = null) { if (declaringType == null) { TimeLogger.Logger.LogError("Parameter declaringType is null.", (LogCategories)512); return; } if (string.IsNullOrEmpty(targetMethodName)) { TimeLogger.Logger.LogError("Parameter targetMethodName is null or empty.", (LogCategories)512); return; } this.declaringType = declaringType; this.targetMethodName = targetMethodName; this.parameters = parameters; this.generics = generics; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class SyncVarNetworkAttribute : Attribute { } }