using FishNet.Connection; using FishNet.Managing.Logging; using FishNet.Managing.Transporting; using FishNet.Object; using FishNet.Serializing; using FishNet.Transporting; using FishNet.Transporting.Multipass; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using GameKit.Dependencies.Utilities; using UnityEngine; namespace FishNet.Managing.Server { public sealed partial class ServerManager : MonoBehaviour { #region Public. /// /// Called when a client is removed from the server using Kick. This is invoked before the client is disconnected. /// NetworkConnection when available, clientId, and KickReason are provided. /// public event Action OnClientKick; #endregion /// /// Stores a cache and returns a boolean result. /// /// private bool StoreTransportCacheAndReturn(List cache, bool returnedValue) { CollectionCaches.Store(cache); return returnedValue; } /// /// Returns true if all server sockets have a local connection state of stopped. /// public bool AreAllServersStopped() { List transports = NetworkManager.TransportManager.GetAllTransports(includeMultipass: false); foreach (Transport t in transports) { if (t.GetConnectionState(server: true) != LocalConnectionState.Stopped) return StoreTransportCacheAndReturn(transports, returnedValue: false); } return StoreTransportCacheAndReturn(transports, returnedValue: true); } /// /// Returns true if only one server is started. /// /// public bool IsOnlyOneServerStarted() { List transports = NetworkManager.TransportManager.GetAllTransports(includeMultipass: false); int startedCount = 0; foreach (Transport t in transports) { if (t.GetConnectionState(true) == LocalConnectionState.Started) startedCount++; } return StoreTransportCacheAndReturn(transports, (startedCount == 1)); } [Obsolete("Use IsOnlyOneServerStarted().")] public bool OneServerStarted() => IsOnlyOneServerStarted(); /// /// Returns true if any server socket is in the started state. /// /// When set the transport will be ignored. This value is only used with Multipass. public bool IsAnyServerStarted(Transport excludedTransport) { List transports = NetworkManager.TransportManager.GetAllTransports(includeMultipass: false); foreach (Transport t in transports) { if (t == excludedTransport) continue; //Another transport is started, no need to load start scenes again. if (t.GetConnectionState(true) == LocalConnectionState.Started) return StoreTransportCacheAndReturn(transports, returnedValue: true); } return StoreTransportCacheAndReturn(transports, returnedValue: false); } /// /// Returns true if any server socket is in the started state. /// /// When set the transport on this index will be ignored. This value is only used with Multipass. public bool IsAnyServerStarted(int excludedIndex = TransportConsts.UNSET_TRANSPORT_INDEX) { Transport excludedTransport = null; if (excludedIndex != TransportConsts.UNSET_TRANSPORT_INDEX) { if (NetworkManager.TransportManager.Transport is Multipass mp) excludedTransport = mp.GetTransport(excludedIndex); } return IsAnyServerStarted(excludedTransport); } [Obsolete("Use IsAnyServerStarted.")] public bool AnyServerStarted(int excludedIndex = TransportConsts.UNSET_TRANSPORT_INDEX) => IsAnyServerStarted(excludedIndex); /// /// Spawns an object over the network. Can only be called on the server. /// /// GameObject instance to spawn. /// Connection to give ownership to. public void Spawn(GameObject go, NetworkConnection ownerConnection = null, UnityEngine.SceneManagement.Scene scene = default) { if (go == null) { NetworkManager.LogWarning($"GameObject cannot be spawned because it is null."); return; } NetworkObject nob = go.GetComponent(); Spawn(nob, ownerConnection, scene); } /// /// Spawns an object over the network. Can only be called on the server. /// /// MetworkObject instance to spawn. /// Connection to give ownership to. public void Spawn(NetworkObject nob, NetworkConnection ownerConnection = null, UnityEngine.SceneManagement.Scene scene = default) { if (!nob.GetIsSpawnable()) { NetworkManager.LogWarning($"NetworkObject {nob} cannot be spawned because it is not marked as spawnable."); return; } Objects.Spawn(nob, ownerConnection, scene); } /// /// Despawns an object over the network. Can only be called on the server. /// /// GameObject instance to despawn. /// Overrides the default DisableOnDespawn value for this single despawn. Scene objects will never be destroyed. public void Despawn(GameObject go, DespawnType? despawnType = null) { if (go == null) { NetworkManager.LogWarning($"GameObject cannot be despawned because it is null."); return; } NetworkObject nob = go.GetComponent(); Despawn(nob, despawnType); } /// /// Despawns an object over the network. Can only be called on the server. /// /// NetworkObject instance to despawn. /// Despawn override type. public void Despawn(NetworkObject networkObject, DespawnType? despawnType = null) { DespawnType resolvedDespawnType = (!despawnType.HasValue) ? networkObject.GetDefaultDespawnType() : despawnType.Value; Objects.Despawn(networkObject, resolvedDespawnType, asServer: true); } /// /// Kicks a connection immediately while invoking OnClientKick. /// /// Client to kick. /// Reason client is being kicked. /// How to print logging as. /// Optional message to be debug logged. public void Kick(NetworkConnection conn, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "") { if (!conn.IsValid) return; OnClientKick?.Invoke(conn, conn.ClientId, kickReason); if (conn.IsActive) conn.Disconnect(true); if (!string.IsNullOrEmpty(log)) NetworkManager.Log(loggingType, log); } /// /// Kicks a connection immediately while invoking OnClientKick. /// /// ClientId to kick. /// Reason client is being kicked. /// How to print logging as. /// Optional message to be debug logged. public void Kick(int clientId, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "") { OnClientKick?.Invoke(null, clientId, kickReason); NetworkManager.TransportManager.Transport.StopConnection(clientId, true); if (!string.IsNullOrEmpty(log)) NetworkManager.Log(loggingType, log); } /// /// Kicks a connection immediately while invoking OnClientKick. /// /// Client to kick. /// Reader to clear before kicking. /// Reason client is being kicked. /// How to print logging as. /// Optional message to be debug logged. public void Kick(NetworkConnection conn, Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "") { reader.Clear(); Kick(conn, kickReason, loggingType, log); } } }