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.Runtime.CompilerServices; 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 /// /// Returns true if only one server is started. /// /// public bool OneServerStarted() { int startedCount = 0; TransportManager tm = NetworkManager.TransportManager; //If using multipass check all transports. if (tm.Transport is Multipass mp) { foreach (Transport t in mp.Transports) { //Another transport is started, no need to load start scenes again. if (t.GetConnectionState(true) == LocalConnectionState.Started) startedCount++; } } //Not using multipass. else { if (tm.Transport.GetConnectionState(true) == LocalConnectionState.Started) startedCount = 1; } return (startedCount == 1); } /// /// 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 AnyServerStarted(int? excludedIndex = null) { TransportManager tm = NetworkManager.TransportManager; //If using multipass check all transports. if (tm.Transport is Multipass mp) { //Get transport which had state changed. Transport excludedTransport = (excludedIndex == null) ? null : mp.GetTransport(excludedIndex.Value); foreach (Transport t in mp.Transports) { /* Skip t if is the transport that had it's state changed. * We are looking for other transports already in started. */ if (t == excludedTransport) continue; //Another transport is started, no need to load start scenes again. if (t.GetConnectionState(true) == LocalConnectionState.Started) return true; } } //Not using multipass. else { return (tm.Transport.GetConnectionState(true) == LocalConnectionState.Started); } //Fall through, none started. return false; } /// /// Spawns an object over the network. Can only be called on the server. /// /// GameObject instance to spawn. /// Connection to give ownership to. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Spawn(GameObject go, NetworkConnection ownerConnection = null) { if (go == null) { NetworkManager.LogWarning($"GameObject cannot be spawned because it is null."); return; } NetworkObject nob = go.GetComponent(); Spawn(nob, ownerConnection); } /// /// 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) { Objects.Spawn(nob, ownerConnection); } /// /// 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. [MethodImpl(MethodImplOptions.AggressiveInlining)] 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 == null) ? networkObject.GetDefaultDespawnType() : despawnType.Value; Objects.Despawn(networkObject, resolvedDespawnType, 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); } } }