using QuantumUNET.Components; using QuantumUNET.Logging; using QuantumUNET.Messages; using QuantumUNET.Transport; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using UnityEngine; using UnityEngine.Networking; namespace QuantumUNET { public class QNetworkServer { private QNetworkServer() { NetworkTransport.Init(); m_RemoveList = new HashSet(); m_ExternalConnections = new HashSet(); m_NetworkScene = new QNetworkScene(); m_SimpleServerSimple = new ServerSimpleWrapper(this); } public static List localConnections => instance.m_LocalConnectionsFakeList; public static int listenPort => instance.m_SimpleServerSimple.listenPort; public static int serverHostId => instance.m_SimpleServerSimple.serverHostId; public static ReadOnlyCollection connections => instance.m_SimpleServerSimple.connections; public static Dictionary handlers => instance.m_SimpleServerSimple.handlers; public static HostTopology hostTopology => instance.m_SimpleServerSimple.hostTopology; public static Dictionary objects => instance.m_NetworkScene.localObjects; public static bool dontListen { get; set; } public static QNetworkServer instance { get { if (s_Instance == null) { var obj = s_Sync; lock (obj) { if (s_Instance == null) { s_Instance = new QNetworkServer(); } } } return s_Instance; } } public static bool active { get; private set; } public static bool localClientActive => instance.m_LocalClientActive; public static int numChannels => instance.m_SimpleServerSimple.hostTopology.DefaultConfig.ChannelCount; public static Type networkConnectionClass => instance.m_SimpleServerSimple.networkConnectionClass; public static void SetNetworkConnectionClass() where T : QNetworkConnection => instance.m_SimpleServerSimple.SetNetworkConnectionClass(); public static bool Configure(ConnectionConfig config, int maxConnections) => instance.m_SimpleServerSimple.Configure(config, maxConnections); public static bool Configure(HostTopology topology) => instance.m_SimpleServerSimple.Configure(topology); public static void Reset() { NetworkTransport.Shutdown(); NetworkTransport.Init(); s_Instance = null; active = false; } public static void Shutdown() { if (s_Instance != null) { s_Instance.InternalDisconnectAll(); if (!dontListen) { s_Instance.m_SimpleServerSimple.Stop(); } s_Instance = null; } dontListen = false; active = false; } internal void RegisterMessageHandlers() { m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.ClientUpdateVars, OnUpdateVarsMessage); m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.Ready, OnClientReadyMessage); m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.Command, OnCommandMessage); m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.LocalPlayerTransform, QNetworkTransform.HandleTransform); m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.LocalChildTransform, QNetworkTransformChild.HandleChildTransform); m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.RemovePlayer, OnRemovePlayerMessage); m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.Animation, QNetworkAnimator.OnAnimationServerMessage); m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.AnimationParameters, QNetworkAnimator.OnAnimationParametersServerMessage); m_SimpleServerSimple.RegisterHandlerSafe(QMsgType.AnimationTrigger, QNetworkAnimator.OnAnimationTriggerServerMessage); maxPacketSize = hostTopology.DefaultConfig.PacketSize; } private static void OnUpdateVarsMessage(QNetworkMessage netMsg) { var networkInstanceId = netMsg.Reader.ReadNetworkId(); var typeName = netMsg.Reader.ReadString(); if (instance.m_NetworkScene.GetNetworkIdentity(networkInstanceId, out var networkIdentity)) { var allBehaviours = networkIdentity.GetNetworkBehaviours(); var target = allBehaviours.First(x => x.GetType().Name == typeName); target.OnDeserialize(netMsg.Reader, false); } else { Debug.LogError($"Did not find target for sync message for {networkInstanceId}"); } } public static bool Listen(int serverPort) => instance.InternalListen(null, serverPort); public static bool Listen(string ipAddress, int serverPort) => instance.InternalListen(ipAddress, serverPort); internal bool InternalListen(string ipAddress, int serverPort) { if (dontListen) { m_SimpleServerSimple.Initialize(); } else if (!m_SimpleServerSimple.Listen(ipAddress, serverPort)) { return false; } maxPacketSize = hostTopology.DefaultConfig.PacketSize; active = true; RegisterMessageHandlers(); return true; } internal int AddLocalClient(QLocalClient localClient) { int result; if (m_LocalConnectionsFakeList.Count != 0) { QLog.Error("Local Connection already exists"); result = -1; } else { m_LocalConnection = new QULocalConnectionToClient(localClient) { connectionId = 0 }; m_SimpleServerSimple.SetConnectionAtIndex(m_LocalConnection); m_LocalConnectionsFakeList.Add(m_LocalConnection); m_LocalConnection.InvokeHandlerNoData(32); result = 0; } return result; } internal void RemoveLocalClient(QNetworkConnection localClientConnection) { for (var i = 0; i < m_LocalConnectionsFakeList.Count; i++) { if (m_LocalConnectionsFakeList[i].connectionId == localClientConnection.connectionId) { m_LocalConnectionsFakeList.RemoveAt(i); break; } } if (m_LocalConnection != null) { m_LocalConnection.Disconnect(); m_LocalConnection.Dispose(); m_LocalConnection = null; } m_LocalClientActive = false; m_SimpleServerSimple.RemoveConnectionAtIndex(0); } internal void SetLocalObjectOnServer(NetworkInstanceId netId, GameObject obj) { QLog.Debug($"SetLocalObjectOnServer {netId} {obj}"); m_NetworkScene.SetLocalObject(netId, obj, false, true); } internal void ActivateLocalClientScene() { if (!m_LocalClientActive) { m_LocalClientActive = true; foreach (var networkIdentity in objects.Values) { if (!networkIdentity.IsClient) { QLog.Log($"ActivateClientScene {networkIdentity.NetId} {networkIdentity.gameObject}"); QClientScene.SetLocalObject(networkIdentity.NetId, networkIdentity.gameObject); networkIdentity.OnStartClient(); } } } } public static bool SendToAll(short msgType, QMessageBase msg) { QLog.Debug($"Server.SendToAll msgType:{msgType}"); var flag = true; foreach (var networkConnection in connections) { if (networkConnection != null) { flag &= networkConnection.Send(msgType, msg); } } return flag; } private static bool SendToObservers(GameObject contextObj, short msgType, QMessageBase msg) { QLog.Debug($"Server.SendToObservers id:{msgType}"); var flag = true; var component = contextObj.GetComponent(); bool result; if (component == null || component.Observers == null) { result = false; } else { var count = component.Observers.Count; for (var i = 0; i < count; i++) { var networkConnection = component.Observers[i]; flag &= networkConnection.Send(msgType, msg); } result = flag; } return result; } public static bool SendToReady(GameObject contextObj, short msgType, QMessageBase msg) { QLog.Debug($"Server.SendToReady id:{msgType}"); bool result; if (contextObj == null) { foreach (var networkConnection in connections) { if (networkConnection != null && networkConnection.isReady) { networkConnection.Send(msgType, msg); } } result = true; } else { var flag = true; var component = contextObj.GetComponent(); if (component == null || component.Observers == null) { result = false; } else { var count = component.Observers.Count; for (var j = 0; j < count; j++) { var networkConnection2 = component.Observers[j]; if (networkConnection2.isReady) { flag &= networkConnection2.Send(msgType, msg); } } result = flag; } } return result; } public static void SendWriterToReady(GameObject contextObj, QNetworkWriter writer, int channelId) { var arraySegment = writer.AsArraySegment(); if (arraySegment.Count > 32767) { throw new UnityException("NetworkWriter used buffer is too big!"); } SendBytesToReady(contextObj, arraySegment.Array, arraySegment.Count, channelId); } public static void SendBytesToReady(GameObject contextObj, byte[] buffer, int numBytes, int channelId) { if (contextObj == null) { var flag = true; foreach (var networkConnection in connections) { if (networkConnection != null && networkConnection.isReady) { if (!networkConnection.SendBytes(buffer, numBytes, channelId)) { flag = false; } } } if (!flag) { QLog.Error("SendBytesToReady failed"); } } else { var component = contextObj.GetComponent(); try { var flag2 = true; var count = component.Observers.Count; for (var j = 0; j < count; j++) { var networkConnection2 = component.Observers[j]; if (networkConnection2.isReady) { if (!networkConnection2.SendBytes(buffer, numBytes, channelId)) { flag2 = false; } } } if (!flag2) { QLog.Error($"SendBytesToReady failed for {contextObj}"); } } catch (NullReferenceException) { QLog.Error($"SendBytesToReady object {contextObj} has not been spawned"); } } } public static void SendBytesToPlayer(GameObject player, byte[] buffer, int numBytes, int channelId) { foreach (var networkConnection in connections) { if (networkConnection != null) { foreach (var controller in networkConnection.PlayerControllers) { if (controller.IsValid && controller.Gameobject == player) { networkConnection.SendBytes(buffer, numBytes, channelId); break; } } } } } public static bool SendUnreliableToAll(short msgType, QMessageBase msg) { QLog.Log($"Server.SendUnreliableToAll msgType:{msgType}"); var flag = true; foreach (var networkConnection in connections) { if (networkConnection != null) { flag &= networkConnection.SendUnreliable(msgType, msg); } } return flag; } public static bool SendUnreliableToReady(GameObject contextObj, short msgType, QMessageBase msg) { QLog.Log($"Server.SendUnreliableToReady id:{msgType}"); bool result; if (contextObj == null) { foreach (var networkConnection in connections) { if (networkConnection != null && networkConnection.isReady) { networkConnection.SendUnreliable(msgType, msg); } } result = true; } else { var flag = true; var component = contextObj.GetComponent(); var count = component.Observers.Count; for (var j = 0; j < count; j++) { var networkConnection2 = component.Observers[j]; if (networkConnection2.isReady) { flag &= networkConnection2.SendUnreliable(msgType, msg); } } result = flag; } return result; } public static bool SendByChannelToAll(short msgType, QMessageBase msg, int channelId) { QLog.Log($"Server.SendByChannelToAll id:{msgType}"); var flag = true; foreach (var networkConnection in connections) { if (networkConnection != null) { flag &= networkConnection.SendByChannel(msgType, msg, channelId); } } return flag; } public static bool SendByChannelToReady(GameObject contextObj, short msgType, QMessageBase msg, int channelId) { QLog.Log($"Server.SendByChannelToReady msgType:{msgType}"); bool result; if (contextObj == null) { foreach (var networkConnection in connections) { if (networkConnection != null && networkConnection.isReady) { networkConnection.SendByChannel(msgType, msg, channelId); } } result = true; } else { var flag = true; var component = contextObj.GetComponent(); var count = component.Observers.Count; for (var j = 0; j < count; j++) { var networkConnection2 = component.Observers[j]; if (networkConnection2.isReady) { flag &= networkConnection2.SendByChannel(msgType, msg, channelId); } } result = flag; } return result; } public static void DisconnectAll() => instance.InternalDisconnectAll(); internal void InternalDisconnectAll() { m_SimpleServerSimple.DisconnectAllConnections(); if (m_LocalConnection != null) { m_LocalConnection.Disconnect(); m_LocalConnection.Dispose(); m_LocalConnection = null; } m_LocalClientActive = false; } internal static void Update() => s_Instance?.InternalUpdate(); private void UpdateServerObjects() { foreach (var networkIdentity in objects.Values) { try { networkIdentity.UNetUpdate(); } catch (NullReferenceException) { } catch (MissingReferenceException) { } } if (m_RemoveListCount++ % 100 == 0) { CheckForNullObjects(); } } private void CheckForNullObjects() { foreach (var networkInstanceId in objects.Keys) { var networkIdentity = objects[networkInstanceId]; if (networkIdentity == null || networkIdentity.gameObject == null) { m_RemoveList.Add(networkInstanceId); } } if (m_RemoveList.Count > 0) { foreach (var key in m_RemoveList) { objects.Remove(key); } m_RemoveList.Clear(); } } internal void InternalUpdate() { m_SimpleServerSimple.Update(); if (dontListen) { m_SimpleServerSimple.UpdateConnections(); } UpdateServerObjects(); } private void OnConnected(QNetworkConnection conn) { QLog.Log($"Server accepted client:{conn.connectionId}"); conn.SetMaxDelay(m_MaxDelay); conn.InvokeHandlerNoData(32); SendCrc(conn); } private void OnDisconnected(QNetworkConnection conn) { conn.InvokeHandlerNoData(33); foreach (var controller in conn.PlayerControllers) { if (controller.Gameobject != null) { QLog.Warning("Player not destroyed when connection disconnected."); } } QLog.Log($"Server lost client:{conn.connectionId}"); conn.RemoveObservers(); conn.Dispose(); } private void OnData(QNetworkConnection conn, int receivedSize, int channelId) => conn.TransportReceive(m_SimpleServerSimple.messageBuffer, receivedSize, channelId); private void GenerateConnectError(int error) { QLog.Error($"UNet Server Connect Error: {error}"); GenerateError(null, error); } private void GenerateDataError(QNetworkConnection conn, int error) { QLog.Error($"UNet Server Data Error: {(NetworkError)error}"); GenerateError(conn, error); } private void GenerateDisconnectError(QNetworkConnection conn, int error) { QLog.Error($"UNet Server Disconnect Error: {(NetworkError)error} conn:[{conn}]:{conn.connectionId}"); GenerateError(conn, error); } private void GenerateError(QNetworkConnection conn, int error) { if (handlers.ContainsKey(34)) { var errorMessage = new QErrorMessage { errorCode = error }; var writer = new QNetworkWriter(); errorMessage.Serialize(writer); var reader = new QNetworkReader(writer); conn.InvokeHandler(34, reader, 0); } } public static void RegisterHandler(short msgType, QNetworkMessageDelegate handler) => instance.m_SimpleServerSimple.RegisterHandler(msgType, handler); public static void UnregisterHandler(short msgType) => instance.m_SimpleServerSimple.UnregisterHandler(msgType); public static void ClearHandlers() => instance.m_SimpleServerSimple.ClearHandlers(); public static void ClearSpawners() => QNetworkScene.ClearSpawners(); public static void SendToClientOfPlayer(GameObject player, short msgType, QMessageBase msg) { foreach (var networkConnection in connections) { if (networkConnection != null) { foreach (var controller in networkConnection.PlayerControllers) { if (controller.IsValid && controller.Gameobject == player) { networkConnection.Send(msgType, msg); return; } } } } QLog.Error($"Failed to send message to player object '{player.name}, not found in connection list"); } public static void SendToClient(int connectionId, short msgType, QMessageBase msg) { if (connectionId < connections.Count) { var networkConnection = connections[connectionId]; if (networkConnection != null) { networkConnection.Send(msgType, msg); return; } } QLog.Error($"Failed to send message to connection ID '{connectionId}, not found in connection list"); } public static bool AddPlayerForConnection(QNetworkConnection conn, GameObject player, short playerControllerId) => instance.InternalAddPlayerForConnection(conn, player, playerControllerId); internal bool InternalAddPlayerForConnection(QNetworkConnection conn, GameObject playerGameObject, short playerControllerId) { bool result; if (!GetNetworkIdentity(playerGameObject, out var networkIdentity)) { QLog.Log( $"AddPlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {playerGameObject}"); result = false; } else { networkIdentity.Reset(); if (!CheckPlayerControllerIdForConnection(conn, playerControllerId)) { result = false; } else { GameObject x = null; if (conn.GetPlayerController(playerControllerId, out var playerController)) { x = playerController.Gameobject; } if (x != null) { QLog.Log( $"AddPlayer: player object already exists for playerControllerId of {playerControllerId}"); result = false; } else { var playerController2 = new QPlayerController(playerGameObject, playerControllerId); conn.SetPlayerController(playerController2); networkIdentity.SetConnectionToClient(conn, playerController2.PlayerControllerId); SetClientReady(conn); if (SetupLocalPlayerForConnection(conn, networkIdentity, playerController2)) { result = true; } else { QLog.Log( $"Adding new playerGameObject object netId: {playerGameObject.GetComponent().NetId} asset ID {playerGameObject.GetComponent().AssetId}"); FinishPlayerForConnection(conn, networkIdentity, playerGameObject); if (networkIdentity.LocalPlayerAuthority) { networkIdentity.SetClientOwner(conn); } result = true; } } } } return result; } private static bool CheckPlayerControllerIdForConnection(QNetworkConnection conn, short playerControllerId) { bool result; if (playerControllerId < 0) { QLog.Error($"AddPlayer: playerControllerId of {playerControllerId} is negative"); result = false; } else if (playerControllerId > 32) { QLog.Log($"AddPlayer: playerControllerId of {playerControllerId} is too high. max is {32}"); result = false; } else { if (playerControllerId > 16) { QLog.Warning($"AddPlayer: playerControllerId of {playerControllerId} is unusually high"); } result = true; } return result; } private bool SetupLocalPlayerForConnection(QNetworkConnection conn, QNetworkIdentity uv, QPlayerController newPlayerController) { QLog.Log($"NetworkServer SetupLocalPlayerForConnection netID:{uv.NetId}"); bool result; if (conn is QULocalConnectionToClient ulocalConnectionToClient) { QLog.Log("NetworkServer AddPlayer handling ULocalConnectionToClient"); if (uv.NetId.IsEmpty()) { uv.OnStartServer(true); } uv.RebuildObservers(true); SendSpawnMessage(uv, null); ulocalConnectionToClient.LocalClient.AddLocalPlayer(newPlayerController); uv.SetClientOwner(conn); uv.ForceAuthority(true); uv.SetLocalPlayer(newPlayerController.PlayerControllerId); result = true; } else { result = false; } return result; } private static void FinishPlayerForConnection(QNetworkConnection conn, QNetworkIdentity uv, GameObject playerGameObject) { if (uv.NetId.IsEmpty()) { Spawn(playerGameObject); } conn.Send(4, new QOwnerMessage { NetId = uv.NetId, PlayerControllerId = uv.PlayerControllerId }); } private static bool GetNetworkIdentity(GameObject go, out QNetworkIdentity view) { view = go.GetComponent(); bool result; if (view == null) { QLog.Error("UNET failure. GameObject doesn't have NetworkIdentity."); result = false; } else { result = true; } return result; } public static void SetClientReady(QNetworkConnection conn) => instance.SetClientReadyInternal(conn); internal void SetClientReadyInternal(QNetworkConnection conn) { QLog.Log($"SetClientReadyInternal for conn:{conn.connectionId}"); if (conn.isReady) { QLog.Log($"SetClientReady conn {conn.connectionId} already ready"); } else { if (conn.PlayerControllers.Count == 0) { QLog.Warning("Ready with no player object"); } conn.isReady = true; if (conn is QULocalConnectionToClient) { QLog.Log("NetworkServer Ready handling ULocalConnectionToClient"); foreach (var networkIdentity in objects.Values) { if (networkIdentity != null && networkIdentity.gameObject != null) { var flag = networkIdentity.OnCheckObserver(conn); if (flag) { networkIdentity.AddObserver(conn); } if (!networkIdentity.IsClient) { QLog.Log("LocalClient.SetSpawnObject calling OnStartClient"); networkIdentity.OnStartClient(); } } } } else { QLog.Log($"Spawning {objects.Count} objects for conn {conn.connectionId}"); var objectSpawnFinishedMessage = new QObjectSpawnFinishedMessage { State = 0U }; conn.Send(12, objectSpawnFinishedMessage); foreach (var networkIdentity2 in objects.Values) { if (networkIdentity2 == null) { QLog.Warning("Invalid object found in server local object list (null NetworkIdentity)."); } else if (networkIdentity2.gameObject.activeSelf) { QLog.Debug( $"Sending spawn message for current server objects name='{networkIdentity2.gameObject.name}' netId={networkIdentity2.NetId}"); var flag2 = networkIdentity2.OnCheckObserver(conn); if (flag2) { networkIdentity2.AddObserver(conn); } } } objectSpawnFinishedMessage.State = 1U; conn.Send(12, objectSpawnFinishedMessage); } } } internal static void ShowForConnection(QNetworkIdentity uv, QNetworkConnection conn) { if (conn.isReady) { instance.SendSpawnMessage(uv, conn); } } internal static void HideForConnection(QNetworkIdentity uv, QNetworkConnection conn) => conn.Send(13, new QObjectDestroyMessage { NetId = uv.NetId }); public static void SetAllClientsNotReady() { foreach (var networkConnection in connections) { if (networkConnection != null) { SetClientNotReady(networkConnection); } } } public static void SetClientNotReady(QNetworkConnection conn) => instance.InternalSetClientNotReady(conn); internal void InternalSetClientNotReady(QNetworkConnection conn) { if (conn.isReady) { QLog.Log($"PlayerNotReady {conn}"); conn.isReady = false; conn.RemoveObservers(); var msg = new QNotReadyMessage(); conn.Send(36, msg); } } private static void OnClientReadyMessage(QNetworkMessage netMsg) { QLog.Log($"Default handler for ready message from {netMsg.Connection}"); SetClientReady(netMsg.Connection); } private static void OnRemovePlayerMessage(QNetworkMessage netMsg) { netMsg.ReadMessage(s_RemovePlayerMessage); netMsg.Connection.GetPlayerController(s_RemovePlayerMessage.PlayerControllerId, out var playerController); if (playerController != null) { netMsg.Connection.RemovePlayerController(s_RemovePlayerMessage.PlayerControllerId); Destroy(playerController.Gameobject); } else { QLog.Error( $"Received remove player message but could not find the player ID: {s_RemovePlayerMessage.PlayerControllerId}"); } } private static void OnCommandMessage(QNetworkMessage netMsg) { var cmdHash = (int)netMsg.Reader.ReadPackedUInt32(); var networkInstanceId = netMsg.Reader.ReadNetworkId(); var gameObject = FindLocalObject(networkInstanceId); if (gameObject == null) { QLog.Warning($"Instance not found when handling Command message [netId={networkInstanceId}]"); } else { var component = gameObject.GetComponent(); if (component == null) { QLog.Warning( $"NetworkIdentity deleted when handling Command message [netId={networkInstanceId}]"); } else { var flag = false; foreach (var playerController in netMsg.Connection.PlayerControllers) { if (playerController.Gameobject != null && playerController.Gameobject.GetComponent().NetId == component.NetId) { flag = true; break; } } if (!flag) { if (component.ClientAuthorityOwner != netMsg.Connection) { QLog.Warning($"Command for object without authority [netId={networkInstanceId}]"); return; } } QLog.Log($"OnCommandMessage for netId={networkInstanceId} conn={netMsg.Connection}"); component.HandleCommand(cmdHash, netMsg.Reader); } } } internal void SpawnObject(GameObject obj) { if (!active) { QLog.Error( $"SpawnObject for {obj}, NetworkServer is not active. Cannot spawn objects without an active server."); } else if (!GetNetworkIdentity(obj, out var networkIdentity)) { QLog.Error($"SpawnObject {obj} has no QSBNetworkIdentity. Please add a NetworkIdentity to {obj}"); } else { networkIdentity.Reset(); networkIdentity.OnStartServer(false); networkIdentity.RebuildObservers(true); } } internal void SendSpawnMessage(QNetworkIdentity uv, QNetworkConnection conn) { if (!uv.ServerOnly) { if (uv.SceneId.IsEmpty()) { var objectSpawnMessage = new QObjectSpawnMessage { NetId = uv.NetId, assetId = uv.AssetId, Position = uv.transform.position, Rotation = uv.transform.rotation }; var networkWriter = new QNetworkWriter(); uv.UNetSerializeAllVars(networkWriter); if (networkWriter.Position > 0) { objectSpawnMessage.Payload = networkWriter.ToArray(); } if (conn != null) { conn.Send(QMsgType.ObjectSpawn, objectSpawnMessage); } else { SendToReady(uv.gameObject, 3, objectSpawnMessage); } } else { var objectSpawnSceneMessage = new QObjectSpawnSceneMessage { NetId = uv.NetId, SceneId = uv.SceneId, Position = uv.transform.position }; var networkWriter2 = new QNetworkWriter(); uv.UNetSerializeAllVars(networkWriter2); if (networkWriter2.Position > 0) { objectSpawnSceneMessage.Payload = networkWriter2.ToArray(); } if (conn != null) { conn.Send(10, objectSpawnSceneMessage); } else { SendToReady(uv.gameObject, 3, objectSpawnSceneMessage); } } } } public static void DestroyPlayersForConnection(QNetworkConnection conn) { if (conn.PlayerControllers.Count == 0) { QLog.Warning("Empty player list given to NetworkServer.Destroy(), nothing to do."); } else { if (conn.ClientOwnedObjects != null) { var hashSet = new HashSet(conn.ClientOwnedObjects); foreach (var gameObject in hashSet.Select(FindLocalObject).Where(gameObject => gameObject != null)) { DestroyObject(gameObject); } } foreach (var playerController in conn.PlayerControllers) { if (playerController.IsValid) { if (!(playerController.UnetView == null)) { DestroyObject(playerController.UnetView, true); } playerController.Gameobject = null; } } conn.PlayerControllers.Clear(); } } private static void UnSpawnObject(GameObject obj) { if (obj == null) { QLog.Log("NetworkServer UnspawnObject is null"); } else if (GetNetworkIdentity(obj, out var uv)) { UnSpawnObject(uv); } } private static void UnSpawnObject(QNetworkIdentity uv) => DestroyObject(uv, false); private static void DestroyObject(GameObject obj) { if (obj == null) { QLog.Log("NetworkServer DestroyObject is null"); } else if (GetNetworkIdentity(obj, out var uv)) { DestroyObject(uv, true); } } private static void DestroyObject(QNetworkIdentity uv, bool destroyServerObject) { QLog.Log($"DestroyObject instance:{uv.NetId}"); if (objects.ContainsKey(uv.NetId)) { objects.Remove(uv.NetId); } uv.ClientAuthorityOwner?.RemoveOwnedObject(uv); var objectDestroyMessage = new QObjectDestroyMessage { NetId = uv.NetId }; SendToObservers(uv.gameObject, 1, objectDestroyMessage); uv.ClearObservers(); if (QNetworkClient.active && instance.m_LocalClientActive) { uv.OnNetworkDestroy(); QClientScene.SetLocalObject(objectDestroyMessage.NetId, null); } if (destroyServerObject) { UnityEngine.Object.Destroy(uv.gameObject); } uv.MarkForReset(); } public static void ClearLocalObjects() => objects.Clear(); public static void Spawn(GameObject obj) { if (VerifyCanSpawn(obj)) { instance.SpawnObject(obj); } } private static bool CheckForPrefab(GameObject obj) => false; private static bool VerifyCanSpawn(GameObject obj) { bool result; if (CheckForPrefab(obj)) { QLog.Error($"GameObject {obj.name} is a prefab, it can't be spawned. This will cause errors in builds."); result = false; } else { result = true; } return result; } public static bool SpawnWithClientAuthority(GameObject obj, GameObject player) { var component = player.GetComponent(); bool result; if (component == null) { QLog.Error("SpawnWithClientAuthority player object has no NetworkIdentity"); result = false; } else if (component.ConnectionToClient == null) { QLog.Error("SpawnWithClientAuthority player object is not a player."); result = false; } else { result = SpawnWithClientAuthority(obj, component.ConnectionToClient); } return result; } public static bool SpawnWithClientAuthority(GameObject obj, QNetworkConnection conn) { bool result; if (!conn.isReady) { QLog.Error("SpawnWithClientAuthority NetworkConnection is not ready!"); result = false; } else { Spawn(obj); var component = obj.GetComponent(); result = !(component == null) && component.IsServer && component.AssignClientAuthority(conn); } return result; } public static bool SpawnWithClientAuthority(GameObject obj, int assetId, QNetworkConnection conn) { Spawn(obj, assetId); var component = obj.GetComponent(); return !(component == null) && component.IsServer && component.AssignClientAuthority(conn); } public static void Spawn(GameObject obj, int assetId) { if (VerifyCanSpawn(obj)) { if (GetNetworkIdentity(obj, out var networkIdentity)) { networkIdentity.SetDynamicAssetId(assetId); } instance.SpawnObject(obj); } } public static void Destroy(GameObject obj) => DestroyObject(obj); public static void UnSpawn(GameObject obj) => UnSpawnObject(obj); internal bool InvokeBytes(QULocalConnectionToServer conn, byte[] buffer, int numBytes, int channelId) { var networkReader = new QNetworkReader(buffer); networkReader.ReadInt16(); var num = networkReader.ReadInt16(); bool result; if (handlers.ContainsKey(num) && m_LocalConnection != null) { m_LocalConnection.InvokeHandler(num, networkReader, channelId); result = true; } else { result = false; } return result; } internal bool InvokeHandlerOnServer(QULocalConnectionToServer conn, short msgType, QMessageBase msg, int channelId) { bool result; if (handlers.ContainsKey(msgType) && m_LocalConnection != null) { var writer = new QNetworkWriter(); msg.Serialize(writer); var reader = new QNetworkReader(writer); m_LocalConnection.InvokeHandler(msgType, reader, channelId); result = true; } else { QLog.Error($"Local invoke: Failed to find local connection to invoke handler on [connectionId={conn.connectionId}] for MsgId:{msgType}"); result = false; } return result; } public static GameObject FindLocalObject(NetworkInstanceId netId) => instance.m_NetworkScene.FindLocalObject(netId); private static bool ValidateSceneObject(QNetworkIdentity netId) => netId.gameObject.hideFlags != HideFlags.NotEditable && netId.gameObject.hideFlags != HideFlags.HideAndDontSave && !netId.SceneId.IsEmpty(); public static bool SpawnObjects() { bool result; if (!active) { result = true; } else { var objectsOfTypeAll = Resources.FindObjectsOfTypeAll(); foreach (var networkIdentity in objectsOfTypeAll) { if (ValidateSceneObject(networkIdentity)) { Debug.Log( $"SpawnObjects sceneId:{networkIdentity.SceneId} name:{networkIdentity.gameObject.name}"); networkIdentity.Reset(); networkIdentity.gameObject.SetActive(true); } } foreach (var networkIdentity2 in objectsOfTypeAll) { if (ValidateSceneObject(networkIdentity2)) { Spawn(networkIdentity2.gameObject); networkIdentity2.ForceAuthority(true); } } result = true; } return result; } private static void SendCrc(QNetworkConnection targetConnection) { if (QNetworkCRC.singleton != null) { if (QNetworkCRC.scriptCRCCheck) { var crcmessage = new QCRCMessage(); var list = new List(); foreach (var text in QNetworkCRC.singleton.scripts.Keys) { list.Add(new QCRCMessageEntry { name = text, channel = (byte)QNetworkCRC.singleton.scripts[text] }); } crcmessage.scripts = list.ToArray(); targetConnection.Send(14, crcmessage); } } } private static volatile QNetworkServer s_Instance; private static readonly object s_Sync = new UnityEngine.Object(); private bool m_LocalClientActive; private readonly List m_LocalConnectionsFakeList = new(); private QULocalConnectionToClient m_LocalConnection; private readonly QNetworkScene m_NetworkScene; private readonly HashSet m_ExternalConnections; public readonly ServerSimpleWrapper m_SimpleServerSimple; private float m_MaxDelay = 0.1f; private readonly HashSet m_RemoveList; private int m_RemoveListCount; private const int k_RemoveListInterval = 100; internal static ushort maxPacketSize; private static readonly QRemovePlayerMessage s_RemovePlayerMessage = new(); public class ServerSimpleWrapper : QNetworkServerSimple { public ServerSimpleWrapper(QNetworkServer server) => m_Server = server; public override void OnConnectError(int connectionId, byte error) => m_Server.GenerateConnectError(error); public override void OnDataError(QNetworkConnection conn, byte error) => m_Server.GenerateDataError(conn, error); public override void OnDisconnectError(QNetworkConnection conn, byte error) => m_Server.GenerateDisconnectError(conn, error); public override void OnConnected(QNetworkConnection conn) => m_Server.OnConnected(conn); public override void OnDisconnected(QNetworkConnection conn) => m_Server.OnDisconnected(conn); public override void OnData(QNetworkConnection conn, int receivedSize, int channelId) => m_Server.OnData(conn, receivedSize, channelId); private readonly QNetworkServer m_Server; } } }