Decompiled source of PerClientSync v1.0.0

PerClientSync.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using MonoMod.RuntimeDetour;
using RoR2;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PerClientSync")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+949f42a9f9d3432f3cbd1d90776cc3016e95b22e")]
[assembly: AssemblyProduct("PerClientSync")]
[assembly: AssemblyTitle("PerClientSync")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 PerClientSync
{
	public abstract class ClientView : MonoBehaviour
	{
		internal NetworkIdentity identity;

		internal readonly Dictionary<NetworkConnection, uint> pendingBits = new Dictionary<NetworkConnection, uint>();

		public abstract NetworkBehaviour Target { get; }

		public virtual uint PrivateBits => 0u;

		public virtual uint LeadingBits => 0u;

		protected internal abstract void Apply(NetworkConnection client);

		protected internal abstract void Restore();

		public void SetDirty(NetworkConnection client, uint dirtyBits)
		{
			if (client != null && dirtyBits != 0 && !PerClient.IsLocal(client))
			{
				pendingBits.TryGetValue(client, out var value);
				pendingBits[client] = value | dirtyBits;
			}
		}

		public void SetDirtyForAll(uint dirtyBits)
		{
			List<NetworkConnection> list = (Object.op_Implicit((Object)(object)identity) ? identity.m_Observers : null);
			if (list == null)
			{
				return;
			}
			foreach (NetworkConnection item in list)
			{
				SetDirty(item, dirtyBits);
			}
		}

		protected internal virtual void OnPlayerLinked(NetworkConnection client)
		{
			SetDirty(client, uint.MaxValue);
		}

		protected virtual void OnEnable()
		{
			Replication.Add(this);
		}

		protected virtual void OnDisable()
		{
			Replication.Remove(this);
			pendingBits.Clear();
		}
	}
	public abstract class ClientView<T> : ClientView where T : NetworkBehaviour
	{
		private T component;

		public T Component
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)component))
				{
					return component = ((Component)this).GetComponent<T>();
				}
				return component;
			}
		}

		public override NetworkBehaviour Target => (NetworkBehaviour)(object)Component;
	}
	public static class PerClient
	{
		public static NetworkConnection GetConnection(PlayerCharacterMasterController player)
		{
			if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)player.networkUser))
			{
				return null;
			}
			return ((NetworkBehaviour)player.networkUser).connectionToClient;
		}

		public static NetworkConnection GetConnection(NetworkUser user)
		{
			if (!Object.op_Implicit((Object)(object)user))
			{
				return null;
			}
			return ((NetworkBehaviour)user).connectionToClient;
		}

		public static PlayerCharacterMasterController GetPlayer(NetworkConnection client)
		{
			if (client == null)
			{
				return null;
			}
			foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances)
			{
				if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.networkUser) && ((NetworkBehaviour)instance.networkUser).connectionToClient == client)
				{
					return instance;
				}
			}
			return null;
		}

		public static bool IsLocal(NetworkConnection client)
		{
			if (client != null)
			{
				return Util.ConnectionIsLocal(client);
			}
			return false;
		}

		public static void Hide(NetworkIdentity identity, NetworkConnection client)
		{
			Replication.Hide(identity, client);
		}

		public static void Show(NetworkIdentity identity, NetworkConnection client)
		{
			Replication.Show(identity, client);
		}

		public static bool IsHidden(NetworkIdentity identity, NetworkConnection client)
		{
			if (Object.op_Implicit((Object)(object)identity))
			{
				return Replication.IsHidden(identity, client);
			}
			return false;
		}

		public static byte[] BuildUpdate(NetworkConnection client, NetworkBehaviour component, uint dirtyBits)
		{
			return Replication.BuildUpdate(client, component, dirtyBits);
		}

		public static bool SendUpdate(NetworkConnection client, NetworkBehaviour component, uint dirtyBits)
		{
			if (client == null || !client.isReady || IsLocal(client))
			{
				return false;
			}
			byte[] array = BuildUpdate(client, component, dirtyBits);
			return client.SendBytes(array, array.Length, component.GetNetworkChannel());
		}
	}
	[BepInPlugin("Def.PerClientSync", "PerClientSync", "1.0.0")]
	public class PerClientSyncPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "Def.PerClientSync";

		public const string PluginAuthor = "Def";

		public const string PluginName = "PerClientSync";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Replication.Hook();
		}
	}
	internal static class Replication
	{
		private static readonly Dictionary<NetworkIdentity, List<ClientView>> views = new Dictionary<NetworkIdentity, List<ClientView>>();

		private static readonly Dictionary<NetworkIdentity, HashSet<NetworkConnection>> hidden = new Dictionary<NetworkIdentity, HashSet<NetworkConnection>>();

		private static readonly List<Hook> hooks = new List<Hook>();

		private static readonly NetworkWriter update = new NetworkWriter();

		private static readonly NetworkWriter part = new NetworkWriter();

		private static NetworkIdentity spawning;

		private static NetworkConnection spawningFor;

		internal static void Hook()
		{
			Detour(typeof(NetworkIdentity), "UNetUpdate", Type.EmptyTypes, "NetworkIdentity_UNetUpdate");
			Detour(typeof(NetworkIdentity), "UNetSerializeAllVars", new Type[1] { typeof(NetworkWriter) }, "NetworkIdentity_UNetSerializeAllVars");
			Detour(typeof(NetworkIdentity), "AddObserver", new Type[1] { typeof(NetworkConnection) }, "NetworkIdentity_AddObserver");
			Detour(typeof(NetworkServer), "SendSpawnMessage", new Type[2]
			{
				typeof(NetworkIdentity),
				typeof(NetworkConnection)
			}, "NetworkServer_SendSpawnMessage");
			PlayerCharacterMasterController.onLinkedToNetworkUserServerGlobal += OnPlayerLinked;
		}

		private static void Detour(Type type, string method, Type[] parameters, string handler)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			MethodInfo method2 = type.GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameters, null);
			MethodInfo method3 = typeof(Replication).GetMethod(handler, BindingFlags.Static | BindingFlags.NonPublic);
			hooks.Add(new Hook((MethodBase)method2, method3));
		}

		internal static void Add(ClientView view)
		{
			NetworkIdentity component = ((Component)view).GetComponent<NetworkIdentity>();
			NetworkBehaviour target = view.Target;
			if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)target))
			{
				PerClientSyncPlugin.Log.LogError((object)(((object)view).GetType().Name + " on " + ((Object)view).name + " needs a NetworkIdentity and the component it shows."));
				return;
			}
			if (!views.TryGetValue(component, out var value))
			{
				value = new List<ClientView>();
				views[component] = value;
			}
			if (value.Any((ClientView other) => (Object)(object)other.Target == (Object)(object)target))
			{
				PerClientSyncPlugin.Log.LogError((object)(((object)view).GetType().Name + " on " + ((Object)view).name + ": its " + ((object)target).GetType().Name + " already has a view."));
			}
			else
			{
				view.identity = component;
				value.Add(view);
			}
		}

		internal static void Remove(ClientView view)
		{
			if (view.identity != null && views.TryGetValue(view.identity, out var value))
			{
				value.Remove(view);
				if (value.Count == 0)
				{
					views.Remove(view.identity);
				}
			}
			view.identity = null;
		}

		private static ClientView FindView(List<ClientView> objectViews, NetworkBehaviour behaviour)
		{
			foreach (ClientView objectView in objectViews)
			{
				if ((Object)(object)objectView.Target == (Object)(object)behaviour)
				{
					return objectView;
				}
			}
			return null;
		}

		private static void NetworkIdentity_UNetUpdate(Action<NetworkIdentity> orig, NetworkIdentity self)
		{
			if (views.Count == 0 || self.m_NetworkBehaviours == null || !views.TryGetValue(self, out var value))
			{
				orig(self);
				return;
			}
			try
			{
				Update(self, value);
			}
			catch (Exception arg)
			{
				PerClientSyncPlugin.Log.LogError((object)$"Could not update {((Object)self).name} for each client, so its views are removed: {arg}");
				foreach (ClientView item in value.ToList())
				{
					((Behaviour)item).enabled = false;
				}
			}
		}

		private static void Update(NetworkIdentity identity, List<ClientView> objectViews)
		{
			uint num = 0u;
			NetworkBehaviour[] networkBehaviours = identity.m_NetworkBehaviours;
			for (int i = 0; i < networkBehaviours.Length; i++)
			{
				int dirtyChannel = networkBehaviours[i].GetDirtyChannel();
				if (dirtyChannel != -1)
				{
					num |= (uint)(1 << (dirtyChannel & 0x1F));
				}
			}
			foreach (ClientView objectView in objectViews)
			{
				if (objectView.pendingBits.Count > 0)
				{
					num |= (uint)(1 << (objectView.Target.GetNetworkChannel() & 0x1F));
				}
			}
			if (num == 0)
			{
				return;
			}
			for (int j = 0; j < NetworkServer.numChannels; j++)
			{
				if ((num & (uint)(1 << j)) != 0)
				{
					UpdateChannel(identity, objectViews, j);
				}
			}
		}

		private static void UpdateChannel(NetworkIdentity identity, List<ClientView> objectViews, int channel)
		{
			NetworkBehaviour[] behavioursOfSameChannel = identity.GetBehavioursOfSameChannel(channel, false);
			int num = behavioursOfSameChannel.Length;
			ClientView[] array = new ClientView[num];
			uint[] array2 = new uint[num];
			byte[][] array3 = new byte[num][];
			bool flag = false;
			for (int i = 0; i < num; i++)
			{
				NetworkBehaviour val = behavioursOfSameChannel[i];
				array[i] = FindView(objectViews, val);
				array2[i] = val.m_SyncVarDirtyBits;
				if ((Object)(object)array[i] == (Object)null)
				{
					part.SeekZero();
					if (val.OnSerialize(part, false))
					{
						val.ClearAllDirtyBits();
						flag = true;
					}
					array3[i] = part.ToArray();
				}
			}
			byte[][] array4 = null;
			List<NetworkConnection> observers = identity.m_Observers;
			if (observers != null)
			{
				uint[] array5 = new uint[num];
				uint[] array6 = new uint[num];
				uint[] array7 = new uint[num];
				NetworkConnection[] array8 = observers.ToArray();
				foreach (NetworkConnection val2 in array8)
				{
					if (val2 == null || !val2.isReady || PerClient.IsLocal(val2))
					{
						continue;
					}
					bool flag2 = flag;
					bool flag3 = false;
					for (int k = 0; k < num; k++)
					{
						ClientView clientView = array[k];
						if (!((Object)(object)clientView == (Object)null))
						{
							clientView.pendingBits.TryGetValue(val2, out var value);
							array5[k] = (array2[k] & ~clientView.PrivateBits) | value;
							array6[k] = array5[k] & clientView.LeadingBits;
							array7[k] = array5[k] & ~clientView.LeadingBits;
							flag2 |= array5[k] != 0;
							flag3 |= array6[k] != 0;
						}
					}
					if (!flag2)
					{
						continue;
					}
					bool flag4 = true;
					if (flag3)
					{
						array4 = array4 ?? WriteEmptyParts(behavioursOfSameChannel, array);
						flag4 = !WriteUpdate(identity, val2, behavioursOfSameChannel, array, array6, array4, partsWrote: false) || val2.SendWriter(update, channel);
					}
					if (flag4)
					{
						flag4 = !WriteUpdate(identity, val2, behavioursOfSameChannel, array, array7, array3, flag) || val2.SendWriter(update, channel);
					}
					for (int l = 0; l < num; l++)
					{
						ClientView clientView2 = array[l];
						if (!((Object)(object)clientView2 == (Object)null))
						{
							if (flag4)
							{
								clientView2.pendingBits.Remove(val2);
							}
							else if (array5[l] != 0)
							{
								clientView2.pendingBits[val2] = array5[l];
							}
						}
					}
				}
			}
			for (int m = 0; m < num; m++)
			{
				ClientView clientView3 = array[m];
				if ((Object)(object)clientView3 == (Object)null)
				{
					continue;
				}
				if (array2[m] != 0)
				{
					behavioursOfSameChannel[m].ClearAllDirtyBits();
				}
				if (clientView3.pendingBits.Count <= 0)
				{
					continue;
				}
				foreach (NetworkConnection item in clientView3.pendingBits.Keys.Where((NetworkConnection client) => observers == null || !observers.Contains(client)).ToList())
				{
					clientView3.pendingBits.Remove(item);
				}
			}
		}

		private static byte[][] WriteEmptyParts(NetworkBehaviour[] behaviours, ClientView[] channelViews)
		{
			byte[][] array = new byte[behaviours.Length][];
			for (int i = 0; i < behaviours.Length; i++)
			{
				if ((Object)(object)channelViews[i] == (Object)null)
				{
					part.SeekZero();
					WriteWithBits(part, behaviours[i], 0u);
					array[i] = part.ToArray();
				}
			}
			return array;
		}

		private static bool WriteUpdate(NetworkIdentity identity, NetworkConnection client, NetworkBehaviour[] behaviours, ClientView[] channelViews, uint[] viewBits, byte[][] parts, bool partsWrote)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			update.StartMessage((short)8);
			update.Write(identity.netId);
			bool flag = partsWrote;
			for (int i = 0; i < behaviours.Length; i++)
			{
				short position = update.Position;
				if ((Object)(object)channelViews[i] == (Object)null)
				{
					update.Write(parts[i], parts[i].Length);
				}
				else
				{
					flag |= WriteForClient(update, behaviours[i], channelViews[i], client, viewBits[i], initialState: false);
				}
				if (NetworkIdentity.m_CheckSerializeSizes)
				{
					update.Write((byte)(update.Position - position));
				}
			}
			update.FinishMessage();
			return flag;
		}

		private static bool WriteForClient(NetworkWriter writer, NetworkBehaviour behaviour, ClientView view, NetworkConnection client, uint dirtyBits, bool initialState)
		{
			uint syncVarDirtyBits = behaviour.m_SyncVarDirtyBits;
			behaviour.m_SyncVarDirtyBits = dirtyBits;
			try
			{
				view.Apply(client);
				return behaviour.OnSerialize(writer, initialState);
			}
			finally
			{
				view.Restore();
				behaviour.m_SyncVarDirtyBits = syncVarDirtyBits;
			}
		}

		private static bool WriteWithBits(NetworkWriter writer, NetworkBehaviour behaviour, uint dirtyBits)
		{
			uint syncVarDirtyBits = behaviour.m_SyncVarDirtyBits;
			behaviour.m_SyncVarDirtyBits = dirtyBits;
			try
			{
				return behaviour.OnSerialize(writer, false);
			}
			finally
			{
				behaviour.m_SyncVarDirtyBits = syncVarDirtyBits;
			}
		}

		internal static byte[] BuildUpdate(NetworkConnection client, NetworkBehaviour component, uint dirtyBits)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			NetworkIdentity component2 = ((Component)component).GetComponent<NetworkIdentity>();
			List<ClientView> value;
			List<ClientView> list = ((client != null && !PerClient.IsLocal(client) && views.TryGetValue(component2, out value)) ? value : null);
			NetworkWriter val = new NetworkWriter();
			val.StartMessage((short)8);
			val.Write(component2.netId);
			NetworkBehaviour[] behavioursOfSameChannel = component2.GetBehavioursOfSameChannel(component.GetNetworkChannel(), false);
			foreach (NetworkBehaviour val2 in behavioursOfSameChannel)
			{
				short position = val.Position;
				uint dirtyBits2 = (((Object)(object)val2 == (Object)(object)component) ? dirtyBits : 0u);
				ClientView clientView = ((list != null) ? FindView(list, val2) : null);
				if ((Object)(object)clientView != (Object)null)
				{
					WriteForClient(val, val2, clientView, client, dirtyBits2, initialState: false);
				}
				else
				{
					WriteWithBits(val, val2, dirtyBits2);
				}
				if (NetworkIdentity.m_CheckSerializeSizes)
				{
					val.Write((byte)(val.Position - position));
				}
			}
			val.FinishMessage();
			return val.ToArray();
		}

		private static void NetworkServer_SendSpawnMessage(Action<NetworkServer, NetworkIdentity, NetworkConnection> orig, NetworkServer self, NetworkIdentity identity, NetworkConnection client)
		{
			if (client != null && IsHidden(identity, client))
			{
				return;
			}
			if (client == null || PerClient.IsLocal(client) || !views.TryGetValue(identity, out var value))
			{
				orig(self, identity, client);
				return;
			}
			NetworkIdentity val = spawning;
			NetworkConnection val2 = spawningFor;
			spawning = identity;
			spawningFor = client;
			try
			{
				orig(self, identity, client);
			}
			finally
			{
				spawning = val;
				spawningFor = val2;
			}
			foreach (ClientView item in value)
			{
				item.pendingBits.Remove(client);
			}
		}

		private static void NetworkIdentity_UNetSerializeAllVars(Action<NetworkIdentity, NetworkWriter> orig, NetworkIdentity self, NetworkWriter writer)
		{
			if (self != spawning || !views.TryGetValue(self, out var value))
			{
				orig(self, writer);
				return;
			}
			NetworkBehaviour[] networkBehaviours = self.m_NetworkBehaviours;
			foreach (NetworkBehaviour val in networkBehaviours)
			{
				ClientView clientView = FindView(value, val);
				if ((Object)(object)clientView != (Object)null)
				{
					WriteForClient(writer, val, clientView, spawningFor, val.m_SyncVarDirtyBits, initialState: true);
				}
				else
				{
					val.OnSerialize(writer, true);
				}
			}
		}

		private static void NetworkIdentity_AddObserver(Action<NetworkIdentity, NetworkConnection> orig, NetworkIdentity self, NetworkConnection client)
		{
			if (client == null || !IsHidden(self, client))
			{
				orig(self, client);
			}
		}

		private static void OnPlayerLinked(PlayerCharacterMasterController player)
		{
			NetworkConnection connection = PerClient.GetConnection(player);
			if (connection == null || PerClient.IsLocal(connection) || views.Count == 0)
			{
				return;
			}
			foreach (ClientView item in views.Values.SelectMany((List<ClientView> objectViews) => objectViews).ToList())
			{
				try
				{
					item.OnPlayerLinked(connection);
				}
				catch (Exception arg)
				{
					PerClientSyncPlugin.Log.LogError((object)$"{((object)item).GetType().Name} on {((Object)item).name} could not handle a linked player: {arg}");
				}
			}
		}

		internal static bool IsHidden(NetworkIdentity identity, NetworkConnection client)
		{
			if (hidden.Count > 0 && client != null && hidden.TryGetValue(identity, out var value))
			{
				return value.Contains(client);
			}
			return false;
		}

		internal static void Hide(NetworkIdentity identity, NetworkConnection client)
		{
			if (NetworkServer.active && Object.op_Implicit((Object)(object)identity) && client != null && !PerClient.IsLocal(client))
			{
				ForgetDestroyedObjects();
				if (!hidden.TryGetValue(identity, out var value))
				{
					value = new HashSet<NetworkConnection>();
					hidden[identity] = value;
				}
				if (value.Add(client) && identity.m_Observers != null && identity.m_Observers.Contains(client))
				{
					identity.RemoveObserver(client);
				}
			}
		}

		internal static void Show(NetworkIdentity identity, NetworkConnection client)
		{
			if (NetworkServer.active && Object.op_Implicit((Object)(object)identity) && client != null && hidden.TryGetValue(identity, out var value) && value.Remove(client))
			{
				if (value.Count == 0)
				{
					hidden.Remove(identity);
				}
				if (identity.m_Observers != null && client.isReady && !identity.m_Observers.Contains(client) && identity.OnCheckObserver(client))
				{
					identity.AddObserver(client);
				}
			}
		}

		private static void ForgetDestroyedObjects()
		{
			foreach (NetworkIdentity item in hidden.Keys.Where((NetworkIdentity identity) => !Object.op_Implicit((Object)(object)identity)).ToList())
			{
				hidden.Remove(item);
			}
		}
	}
}