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);
}
}
}