using OWML.Logging; using QuantumUNET.Messages; using QuantumUNET.Transport; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using UnityEngine; using UnityEngine.Networking; namespace QuantumUNET.Components { public sealed class QSBNetworkIdentity : MonoBehaviour { public bool IsClient { get; private set; } public bool IsServer => m_IsServer && QSBNetworkServer.active && m_IsServer; public bool HasAuthority { get; private set; } public NetworkInstanceId NetId { get; private set; } public NetworkSceneId SceneId => m_SceneId; public QSBNetworkConnection ClientAuthorityOwner { get; private set; } public NetworkHash128 AssetId => m_AssetId; public bool IsLocalPlayer { get; private set; } public short PlayerControllerId { get; private set; } = -1; public QSBNetworkConnection ConnectionToServer { get; private set; } public QSBNetworkConnection ConnectionToClient { get; private set; } public QSBNetworkIdentity RootIdentity { get; private set; } public List SubIdentities { get; private set; } = new List(); public bool ServerOnly { get => m_ServerOnly; set => m_ServerOnly = value; } public bool LocalPlayerAuthority { get => m_LocalPlayerAuthority; set => m_LocalPlayerAuthority = value; } public void SetRootIdentity(QSBNetworkIdentity newRoot) { if (RootIdentity != null) { RootIdentity.RemoveSubIdentity(this); } RootIdentity = newRoot; RootIdentity.AddSubIndentity(this); } internal void AddSubIndentity(QSBNetworkIdentity identityToAdd) => SubIdentities.Add(identityToAdd); internal void RemoveSubIdentity(QSBNetworkIdentity identityToRemove) => SubIdentities.Remove(identityToRemove); internal void SetDynamicAssetId(NetworkHash128 newAssetId) { if (!m_AssetId.IsValid() || m_AssetId.Equals(newAssetId)) { m_AssetId = newAssetId; } else { Debug.LogWarning("SetDynamicAssetId object already has an assetId <" + m_AssetId + ">"); } } internal void SetClientOwner(QSBNetworkConnection conn) { if (ClientAuthorityOwner != null) { Debug.LogError("SetClientOwner m_ClientAuthorityOwner already set!"); } ClientAuthorityOwner = conn; ClientAuthorityOwner.AddOwnedObject(this); } internal void ClearClientOwner() => ClientAuthorityOwner = null; internal void ForceAuthority(bool authority) { if (HasAuthority != authority) { HasAuthority = authority; if (authority) { OnStartAuthority(); } else { OnStopAuthority(); } } } public ReadOnlyCollection Observers { get { ReadOnlyCollection result; if (m_Observers == null) { result = null; } else { result = new ReadOnlyCollection(m_Observers); } return result; } } internal static NetworkInstanceId GetNextNetworkId() { var value = s_NextNetworkId; s_NextNetworkId += 1U; return new NetworkInstanceId(value); } private void CacheBehaviours() { if (m_NetworkBehaviours == null) { m_NetworkBehaviours = GetComponents(); } } internal static void AddNetworkId(uint id) { if (id >= s_NextNetworkId) { s_NextNetworkId = id + 1U; } } internal void SetNetworkInstanceId(NetworkInstanceId newNetId) { NetId = newNetId; if (newNetId.Value == 0U) { m_IsServer = false; } } public void ForceSceneId(int newSceneId) => m_SceneId = new NetworkSceneId((uint)newSceneId); internal void UpdateClientServer(bool isClientFlag, bool isServerFlag) { IsClient = (IsClient || isClientFlag); m_IsServer = (m_IsServer || isServerFlag); } internal void SetNotLocalPlayer() { IsLocalPlayer = false; if (!QSBNetworkServer.active || !QSBNetworkServer.localClientActive) { HasAuthority = false; } } internal void RemoveObserverInternal(QSBNetworkConnection conn) { if (m_Observers != null) { m_Observers.Remove(conn); m_ObserverConnections.Remove(conn.connectionId); } } public void OnDestroy() { if (m_IsServer && QSBNetworkServer.active) { QSBNetworkServer.Destroy(gameObject); } } internal void OnStartServer(bool allowNonZeroNetId) { if (!m_IsServer) { m_IsServer = true; if (m_LocalPlayerAuthority) { HasAuthority = false; } else { HasAuthority = true; } m_Observers = new List(); m_ObserverConnections = new HashSet(); CacheBehaviours(); if (NetId.IsEmpty()) { NetId = GetNextNetworkId(); } else if (!allowNonZeroNetId) { ModConsole.OwmlConsole.WriteLine(string.Concat(new object[] { "Object has non-zero netId ", NetId, " for ", gameObject })); return; } QSBNetworkServer.instance.SetLocalObjectOnServer(NetId, gameObject); for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; try { networkBehaviour.OnStartServer(); } catch (Exception ex) { Debug.LogError("Exception in OnStartServer:" + ex.Message + " " + ex.StackTrace); } } if (QSBNetworkClient.active && QSBNetworkServer.localClientActive) { QSBClientScene.SetLocalObject(NetId, gameObject); OnStartClient(); } if (HasAuthority) { OnStartAuthority(); } } } internal void OnStartClient() { if (!IsClient) { IsClient = true; } CacheBehaviours(); Debug.Log(string.Concat(new object[] { "OnStartClient ", gameObject, " GUID:", NetId, " localPlayerAuthority:", LocalPlayerAuthority })); for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; try { networkBehaviour.PreStartClient(); networkBehaviour.OnStartClient(); } catch (Exception ex) { Debug.LogError("Exception in OnStartClient:" + ex.Message + " " + ex.StackTrace); } } } internal void OnStartAuthority() { for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; try { networkBehaviour.OnStartAuthority(); } catch (Exception ex) { Debug.LogError("Exception in OnStartAuthority:" + ex.Message + " " + ex.StackTrace); } } } internal void OnStopAuthority() { for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; try { networkBehaviour.OnStopAuthority(); } catch (Exception ex) { Debug.LogError("Exception in OnStopAuthority:" + ex.Message + " " + ex.StackTrace); } } } internal void OnSetLocalVisibility(bool vis) { for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; try { networkBehaviour.OnSetLocalVisibility(vis); } catch (Exception ex) { Debug.LogError("Exception in OnSetLocalVisibility:" + ex.Message + " " + ex.StackTrace); } } } internal bool OnCheckObserver(QSBNetworkConnection conn) { for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; try { if (!networkBehaviour.OnCheckObserver(conn)) { return false; } } catch (Exception ex) { Debug.LogError("Exception in OnCheckObserver:" + ex.Message + " " + ex.StackTrace); } } return true; } internal void UNetSerializeAllVars(QSBNetworkWriter writer) { for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; networkBehaviour.OnSerialize(writer, true); } } internal void HandleClientAuthority(bool authority) { if (!LocalPlayerAuthority) { Debug.LogError("HandleClientAuthority " + gameObject + " does not have localPlayerAuthority"); } else { ForceAuthority(authority); } } private bool GetInvokeComponent(int cmdHash, Type invokeClass, out QSBNetworkBehaviour invokeComponent) { QSBNetworkBehaviour networkBehaviour = null; for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour2 = m_NetworkBehaviours[i]; if (networkBehaviour2.GetType() == invokeClass || networkBehaviour2.GetType().IsSubclassOf(invokeClass)) { networkBehaviour = networkBehaviour2; break; } } bool result; if (networkBehaviour == null) { var cmdHashHandlerName = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogError(string.Concat(new object[] { "Found no behaviour for incoming [", cmdHashHandlerName, "] on ", gameObject, ", the server and client should have the same NetworkBehaviour instances [netId=", NetId, "]." })); invokeComponent = null; result = false; } else { invokeComponent = networkBehaviour; result = true; } return result; } internal void HandleSyncEvent(int cmdHash, QSBNetworkReader reader) { if (gameObject == null) { var cmdHashHandlerName = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning(string.Concat(new object[] { "SyncEvent [", cmdHashHandlerName, "] received for deleted object [netId=", NetId, "]" })); } else if (!QSBNetworkBehaviour.GetInvokerForHashSyncEvent(cmdHash, out var invokeClass, out var cmdDelegate)) { var cmdHashHandlerName2 = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogError(string.Concat(new object[] { "Found no receiver for incoming [", cmdHashHandlerName2, "] on ", gameObject, ", the server and client should have the same NetworkBehaviour instances [netId=", NetId, "]." })); } else if (!GetInvokeComponent(cmdHash, invokeClass, out var obj)) { var cmdHashHandlerName3 = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning(string.Concat(new object[] { "SyncEvent [", cmdHashHandlerName3, "] handler not found [netId=", NetId, "]" })); } else { cmdDelegate(obj, reader); } } internal void HandleSyncList(int cmdHash, QSBNetworkReader reader) { if (gameObject == null) { var cmdHashHandlerName = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning(string.Concat(new object[] { "SyncList [", cmdHashHandlerName, "] received for deleted object [netId=", NetId, "]" })); } else if (!QSBNetworkBehaviour.GetInvokerForHashSyncList(cmdHash, out var invokeClass, out var cmdDelegate)) { var cmdHashHandlerName2 = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogError(string.Concat(new object[] { "Found no receiver for incoming [", cmdHashHandlerName2, "] on ", gameObject, ", the server and client should have the same NetworkBehaviour instances [netId=", NetId, "]." })); } else if (!GetInvokeComponent(cmdHash, invokeClass, out var obj)) { var cmdHashHandlerName3 = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning(string.Concat(new object[] { "SyncList [", cmdHashHandlerName3, "] handler not found [netId=", NetId, "]" })); } else { cmdDelegate(obj, reader); } } internal void HandleCommand(int cmdHash, QSBNetworkReader reader) { if (gameObject == null) { var cmdHashHandlerName = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning(string.Concat(new object[] { "Command [", cmdHashHandlerName, "] received for deleted object [netId=", NetId, "]" })); } else if (!QSBNetworkBehaviour.GetInvokerForHashCommand(cmdHash, out var invokeClass, out var cmdDelegate)) { var cmdHashHandlerName2 = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogError(string.Concat(new object[] { "Found no receiver for incoming [", cmdHashHandlerName2, "] on ", gameObject, ", the server and client should have the same NetworkBehaviour instances [netId=", NetId, "]." })); } else if (!GetInvokeComponent(cmdHash, invokeClass, out var obj)) { var cmdHashHandlerName3 = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning(string.Concat(new object[] { "Command [", cmdHashHandlerName3, "] handler not found [netId=", NetId, "]" })); } else { cmdDelegate(obj, reader); } } internal void HandleRPC(int cmdHash, QSBNetworkReader reader) { if (gameObject == null) { var cmdHashHandlerName = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning(string.Concat(new object[] { "ClientRpc [", cmdHashHandlerName, "] received for deleted object [netId=", NetId, "]" })); } else if (!QSBNetworkBehaviour.GetInvokerForHashClientRpc(cmdHash, out var invokeClass, out var cmdDelegate)) { var cmdHashHandlerName2 = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogError(string.Concat(new object[] { "Found no receiver for incoming [", cmdHashHandlerName2, "] on ", gameObject, ", the server and client should have the same NetworkBehaviour instances [netId=", NetId, "]." })); } else if (!GetInvokeComponent(cmdHash, invokeClass, out var obj)) { var cmdHashHandlerName3 = QSBNetworkBehaviour.GetCmdHashHandlerName(cmdHash); Debug.LogWarning(string.Concat(new object[] { "ClientRpc [", cmdHashHandlerName3, "] handler not found [netId=", NetId, "]" })); } else { cmdDelegate(obj, reader); } } internal void UNetUpdate() { var num = 0U; for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; var dirtyChannel = networkBehaviour.GetDirtyChannel(); if (dirtyChannel != -1) { num |= 1U << dirtyChannel; } } if (num != 0U) { var j = 0; while (j < QSBNetworkServer.numChannels) { if ((num & (1U << j)) != 0U) { s_UpdateWriter.StartMessage(8); s_UpdateWriter.Write(NetId); var flag = false; for (var k = 0; k < m_NetworkBehaviours.Length; k++) { var position = s_UpdateWriter.Position; var networkBehaviour2 = m_NetworkBehaviours[k]; if (networkBehaviour2.GetDirtyChannel() != j) { networkBehaviour2.OnSerialize(s_UpdateWriter, false); } else { if (networkBehaviour2.OnSerialize(s_UpdateWriter, false)) { networkBehaviour2.ClearAllDirtyBits(); flag = true; } var maxPacketSize = QSBNetworkServer.maxPacketSize; if (s_UpdateWriter.Position - position > maxPacketSize) { Debug.LogWarning(string.Concat(new object[] { "Large state update of ", s_UpdateWriter.Position - position, " bytes for netId:", NetId, " from script:", networkBehaviour2 })); } } } if (flag) { s_UpdateWriter.FinishMessage(); QSBNetworkServer.SendWriterToReady(gameObject, s_UpdateWriter, j); } } IL_197: j++; continue; goto IL_197; } } } internal void OnUpdateVars(QSBNetworkReader reader, bool initialState) { if (initialState && m_NetworkBehaviours == null) { m_NetworkBehaviours = GetComponents(); } for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; networkBehaviour.OnDeserialize(reader, initialState); } } internal void SetLocalPlayer(short localPlayerControllerId) { IsLocalPlayer = true; PlayerControllerId = localPlayerControllerId; var hasAuthority = HasAuthority; if (LocalPlayerAuthority) { HasAuthority = true; } for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; networkBehaviour.OnStartLocalPlayer(); if (LocalPlayerAuthority && !hasAuthority) { networkBehaviour.OnStartAuthority(); } } } internal void SetConnectionToServer(QSBNetworkConnection conn) => ConnectionToServer = conn; internal void SetConnectionToClient(QSBNetworkConnection conn, short newPlayerControllerId) { PlayerControllerId = newPlayerControllerId; ConnectionToClient = conn; } internal void OnNetworkDestroy() { var num = 0; while (m_NetworkBehaviours != null && num < m_NetworkBehaviours.Length) { var networkBehaviour = m_NetworkBehaviours[num]; networkBehaviour.OnNetworkDestroy(); num++; } m_IsServer = false; } internal void ClearObservers() { if (m_Observers != null) { var count = m_Observers.Count; for (var i = 0; i < count; i++) { var networkConnection = m_Observers[i]; networkConnection.RemoveFromVisList(this, true); } m_Observers.Clear(); m_ObserverConnections.Clear(); } } internal void AddObserver(QSBNetworkConnection conn) { if (m_Observers == null) { Debug.LogError("AddObserver for " + gameObject + " observer list is null"); } else if (m_ObserverConnections.Contains(conn.connectionId)) { Debug.Log(string.Concat(new object[] { "Duplicate observer ", conn.address, " added for ", gameObject })); } else { Debug.Log(string.Concat(new object[] { "Added observer ", conn.address, " added for ", gameObject })); m_Observers.Add(conn); m_ObserverConnections.Add(conn.connectionId); conn.AddToVisList(this); } } internal void RemoveObserver(QSBNetworkConnection conn) { if (m_Observers != null) { m_Observers.Remove(conn); m_ObserverConnections.Remove(conn.connectionId); conn.RemoveFromVisList(this, true); } } public void RebuildObservers(bool initialize) { if (m_Observers != null) { var flag = false; var flag2 = false; var hashSet = new HashSet(); var hashSet2 = new HashSet(m_Observers); for (var i = 0; i < m_NetworkBehaviours.Length; i++) { var networkBehaviour = m_NetworkBehaviours[i]; flag2 |= networkBehaviour.OnRebuildObservers(hashSet, initialize); } if (!flag2) { if (initialize) { for (var j = 0; j < QSBNetworkServer.connections.Count; j++) { var networkConnection = QSBNetworkServer.connections[j]; if (networkConnection != null) { if (networkConnection.isReady) { AddObserver(networkConnection); } } } for (var k = 0; k < QSBNetworkServer.localConnections.Count; k++) { var networkConnection2 = QSBNetworkServer.localConnections[k]; if (networkConnection2 != null) { if (networkConnection2.isReady) { AddObserver(networkConnection2); } } } } } else { foreach (var networkConnection3 in hashSet) { if (networkConnection3 != null) { if (!networkConnection3.isReady) { Debug.LogWarning(string.Concat(new object[] { "Observer is not ready for ", gameObject, " ", networkConnection3 })); } else if (initialize || !hashSet2.Contains(networkConnection3)) { networkConnection3.AddToVisList(this); Debug.Log(string.Concat(new object[] { "New Observer for ", gameObject, " ", networkConnection3 })); flag = true; } } } foreach (var networkConnection4 in hashSet2) { if (!hashSet.Contains(networkConnection4)) { networkConnection4.RemoveFromVisList(this, true); Debug.Log(string.Concat(new object[] { "Removed Observer for ", gameObject, " ", networkConnection4 })); flag = true; } } if (initialize) { for (var l = 0; l < QSBNetworkServer.localConnections.Count; l++) { if (!hashSet.Contains(QSBNetworkServer.localConnections[l])) { OnSetLocalVisibility(false); } } } if (flag) { m_Observers = new List(hashSet); m_ObserverConnections.Clear(); for (var m = 0; m < m_Observers.Count; m++) { m_ObserverConnections.Add(m_Observers[m].connectionId); } } } } } public bool RemoveClientAuthority(QSBNetworkConnection conn) { if (!IsServer) { ModConsole.OwmlConsole.WriteLine($"Warning - Cannot remove authority on client-side. (NetId:{NetId}, Gameobject:{gameObject.name})"); return false; } else if (ConnectionToClient != null) { Debug.LogError("RemoveClientAuthority cannot remove authority for a player object"); return false; } else if (ClientAuthorityOwner == null) { Debug.LogError("RemoveClientAuthority for " + gameObject + " has no clientAuthority owner."); return false; } else if (ClientAuthorityOwner != conn) { Debug.LogError("RemoveClientAuthority for " + gameObject + " has different owner."); return false; } ClientAuthorityOwner.RemoveOwnedObject(this); ClientAuthorityOwner = null; ForceAuthority(true); conn.Send(15, new QSBClientAuthorityMessage { netId = NetId, authority = false }); clientAuthorityCallback?.Invoke(conn, this, false); return true; } public bool AssignClientAuthority(QSBNetworkConnection conn) { if (!IsServer) { ModConsole.OwmlConsole.WriteLine($"Warning - Cannot assign authority on client-side. (NetId:{NetId}, Gameobject:{gameObject.name})"); return false; } else if (!LocalPlayerAuthority) { ModConsole.OwmlConsole.WriteLine($"Warning - Cannot assign authority on object without LocalPlayerAuthority. (NetId:{NetId}, Gameobject:{gameObject.name})"); return false; } else if (ClientAuthorityOwner != null && conn != ClientAuthorityOwner) { ModConsole.OwmlConsole.WriteLine("AssignClientAuthority for " + gameObject + " already has an owner. Use RemoveClientAuthority() first."); return false; } else if (conn == null) { ModConsole.OwmlConsole.WriteLine("AssignClientAuthority for " + gameObject + " owner cannot be null. Use RemoveClientAuthority() instead."); return false; } ClientAuthorityOwner = conn; ClientAuthorityOwner.AddOwnedObject(this); ForceAuthority(false); conn.Send(15, new QSBClientAuthorityMessage { netId = NetId, authority = true }); clientAuthorityCallback?.Invoke(conn, this, true); return true; } internal void MarkForReset() => m_Reset = true; internal void Reset() { if (m_Reset) { m_Reset = false; m_IsServer = false; IsClient = false; HasAuthority = false; NetId = (NetworkInstanceId)typeof(NetworkInstanceId).GetField("Zero", System.Reflection.BindingFlags.Static).GetValue(null); IsLocalPlayer = false; ConnectionToServer = null; ConnectionToClient = null; PlayerControllerId = -1; m_NetworkBehaviours = null; ClearObservers(); ClientAuthorityOwner = null; } } public static void UNetStaticUpdate() { QSBNetworkServer.Update(); QSBNetworkClient.UpdateClients(); QSBNetworkManagerUNET.UpdateScene(); } [SerializeField] private NetworkSceneId m_SceneId; [SerializeField] private NetworkHash128 m_AssetId; [SerializeField] private bool m_ServerOnly; [SerializeField] private bool m_LocalPlayerAuthority; private bool m_IsServer; private QSBNetworkBehaviour[] m_NetworkBehaviours; private HashSet m_ObserverConnections; private List m_Observers; private bool m_Reset = false; private static uint s_NextNetworkId = 1U; private static readonly QSBNetworkWriter s_UpdateWriter = new QSBNetworkWriter(); public static ClientAuthorityCallback clientAuthorityCallback; public delegate void ClientAuthorityCallback(QSBNetworkConnection conn, QSBNetworkIdentity uv, bool authorityState); } }