2using System.Collections.Generic;
12 static bool initialized;
13 public static int maxConnections;
22 public static Dictionary<int, NetworkConnectionToClient>
connections =
23 new Dictionary<int, NetworkConnectionToClient>();
26 internal static Dictionary<ushort, NetworkMessageDelegate> handlers =
27 new Dictionary<ushort, NetworkMessageDelegate>();
31 public static readonly Dictionary<uint, NetworkIdentity>
spawned =
32 new Dictionary<uint, NetworkIdentity>();
39 public static bool active {
get;
internal set; }
42 public static bool isLoadingScene;
53 public static Action<NetworkConnectionToClient> OnConnectedEvent;
54 public static Action<NetworkConnectionToClient> OnDisconnectedEvent;
55 public static Action<NetworkConnectionToClient, TransportError, string> OnErrorEvent;
58 static void Initialize()
70 if (aoi !=
null) aoi.Reset();
75 Debug.Assert(
Transport.
activeTransport !=
null,
"There was no active transport when calling NetworkServer.Listen, If you are calling Listen manually then make sure to set 'Transport.activeTransport' first");
76 AddTransportHandlers();
81 static void AddTransportHandlers()
84 Transport.activeTransport.OnServerConnected += OnTransportConnected;
85 Transport.activeTransport.OnServerDataReceived += OnTransportData;
86 Transport.activeTransport.OnServerDisconnected += OnTransportDisconnected;
87 Transport.activeTransport.OnServerError += OnTransportError;
90 static void RemoveTransportHandlers()
93 Transport.activeTransport.OnServerConnected -= OnTransportConnected;
94 Transport.activeTransport.OnServerDataReceived -= OnTransportData;
95 Transport.activeTransport.OnServerDisconnected -= OnTransportDisconnected;
96 Transport.activeTransport.OnServerError -= OnTransportError;
101 public static void ActivateHostScene()
108 identity.OnStartClient();
113 internal static void RegisterMessageHandlers()
115 RegisterHandler<ReadyMessage>(OnClientReadyMessage);
116 RegisterHandler<CommandMessage>(OnCommandMessage);
117 RegisterHandler<NetworkPingMessage>(
NetworkTime.OnServerPing,
false);
124 maxConnections = maxConns;
134 RegisterMessageHandlers();
138 static void CleanupSpawned()
145 if (identity !=
null)
153 DestroyObject(identity, DestroyMode.Reset);
154 identity.gameObject.SetActive(
false);
160 DestroyObject(identity, DestroyMode.Destroy);
170 [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
190 RemoveTransportHandlers();
198 isLoadingScene =
false;
203 connectionsCopy.Clear();
205 newObservers.Clear();
217 OnConnectedEvent =
null;
218 OnDisconnectedEvent =
null;
221 if (aoi !=
null) aoi.Reset();
249 Debug.LogError(
"Local Connection already exists");
257 internal static void RemoveLocalConnection()
290 Debug.LogWarning(
"Can not send using NetworkServer.SendToAll<T>(T msg) because NetworkServer is not active");
307 if (sendToReadyOnly && !conn.
isReady)
311 conn.Send(segment, channelId);
325 Debug.LogWarning(
"Can not send using NetworkServer.SendToReady<T>(T msg) because NetworkServer is not active");
329 SendToAll(message, channelId,
true);
349 conn.Send(segment, channelId);
375 if ((!isOwner || includeOwner) && conn.
isReady)
378 conn.Send(segment, channelId);
391 SendToReadyObservers(identity, message,
true, channelId);
396 static void OnTransportConnected(
int connectionId)
403 if (connectionId == 0)
405 Debug.LogError($
"Server.HandleConnect: invalid connectionId: {connectionId} . Needs to be != 0, because 0 is reserved for local player.");
443 OnConnectedEvent?.Invoke(conn);
451 if (handlers.TryGetValue(msgType, out NetworkMessageDelegate handler))
453 handler.Invoke(connection, reader, channelId);
454 connection.lastMessageTime = Time.time;
464 Debug.LogWarning($
"Unknown message id: {msgType} for connection: {connection}. This can happen if no handler was registered for this message.");
473 Debug.LogWarning($
"Invalid message header for connection: {connection}.");
481 internal static void OnTransportData(
int connectionId, ArraySegment<byte> data,
int channelId)
489 if (!connection.unbatcher.AddBatch(data))
491 Debug.LogWarning($
"NetworkServer: received Message was too short (messages should start with message id)");
492 connection.Disconnect();
506 while (!isLoadingScene &&
507 connection.unbatcher.GetNextMessage(out
NetworkReader reader, out
double remoteTimestamp))
513 connection.remoteTimeStamp = remoteTimestamp;
516 if (!UnpackAndInvoke(connection, reader, channelId))
526 Debug.LogWarning($
"NetworkServer: failed to unpack and invoke message. Disconnecting {connectionId}.");
527 connection.Disconnect();
535 Debug.LogWarning($
"NetworkServer: received Message was too short (messages should start with message id). Disconnecting {connectionId}");
536 connection.Disconnect();
556 if (!isLoadingScene && connection.unbatcher.BatchesCount > 0)
558 Debug.LogError($
"Still had {connection.unbatcher.BatchesCount} batches remaining after processing, even though processing was not interrupted by a scene change. This should never happen, as it would cause ever growing batches.\nPossible reasons:\n* A message didn't deserialize as much as it serialized\n*There was no message handler for a message id, so the reader wasn't read until the end.");
561 else Debug.LogError($
"HandleData Unknown connectionId:{connectionId}");
570 internal static void OnTransportDisconnected(
int connectionId)
581 if (OnDisconnectedEvent !=
null)
583 OnDisconnectedEvent.Invoke(conn);
594 static void OnTransportError(
int connectionId, TransportError error,
string reason)
598 Debug.LogWarning($
"Server Transport Error for connId={connectionId}: {error}: {reason}. This is fine.");
601 OnErrorEvent?.Invoke(conn, error, reason);
608 public static void RegisterHandler<T>(Action<NetworkConnectionToClient, T> handler,
bool requireAuthentication =
true)
612 if (handlers.ContainsKey(msgType))
614 Debug.LogWarning($
"NetworkServer.RegisterHandler replacing handler for {typeof(T).FullName}, id={msgType}. If replacement is intentional, use ReplaceHandler instead to avoid this warning.");
616 handlers[msgType] =
MessagePacking.WrapHandler(handler, requireAuthentication);
621 public static void RegisterHandler<T>(Action<NetworkConnectionToClient, T, int> handler,
bool requireAuthentication =
true)
625 if (handlers.ContainsKey(msgType))
627 Debug.LogWarning($
"NetworkServer.RegisterHandler replacing handler for {typeof(T).FullName}, id={msgType}. If replacement is intentional, use ReplaceHandler instead to avoid this warning.");
629 handlers[msgType] =
MessagePacking.WrapHandler(handler, requireAuthentication);
633 public static void ReplaceHandler<T>(Action<NetworkConnectionToClient, T> handler,
bool requireAuthentication =
true)
637 handlers[msgType] =
MessagePacking.WrapHandler(handler, requireAuthentication);
652 handlers.Remove(msgType);
658 internal static bool GetNetworkIdentity(GameObject go, out
NetworkIdentity identity)
661 if (identity ==
null)
663 Debug.LogError($
"GameObject {go.name} doesn't have NetworkIdentity.");
719 if (identity ==
null)
721 Debug.LogWarning($
"AddPlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {player}");
728 Debug.Log(
"AddPlayer: player object already exists");
734 conn.identity = identity;
737 identity.SetClientOwner(conn);
742 identity.hasAuthority =
true;
766 identity.assetId = assetId;
777 if (identity ==
null)
779 Debug.LogError($
"ReplacePlayer: playerGameObject has no NetworkIdentity. Please add a NetworkIdentity to {player}");
785 Debug.LogError($
"Cannot replace player for connection. New player is already owned by a different connection{player}");
794 conn.identity = identity;
797 identity.SetClientOwner(conn);
802 identity.hasAuthority =
true;
811 SpawnObserversForConnection(conn);
821 SendChangeOwnerMessage(previousPlayer, conn);
839 identity.assetId = assetId;
861 SpawnObserversForConnection(conn);
870 conn.isReady =
false;
871 conn.RemoveFromObservingsObservers();
898 SendSpawnMessage(identity, conn);
905 netId = identity.netId
916 if (destroyServerObject)
921 conn.identity =
null;
937 Debug.LogWarning(
"Command received while client is not ready.\nThis may be ignored if client intentionally set NotReady.");
948 Debug.LogWarning($
"Spawned object not found when handling Command message [netId={msg.netId}]");
956 if (requiresAuthority && identity.connectionToClient != conn)
958 Debug.LogWarning($
"Command for object without authority [netId={msg.netId}]");
965 identity.HandleRemoteCall(msg.componentIndex, msg.functionHash, RemoteCallType.Command, networkReader, conn as
NetworkConnectionToClient);
972 if (identity.NetworkBehaviours.Length == 0)
979 identity.OnSerializeAllSafely(
true, ownerWriter, observersWriter);
984 ArraySegment<byte> observersSegment = observersWriter.
ToArraySegment();
988 ArraySegment<byte> payload = isOwner ? ownerSegment : observersSegment;
1002 bool isOwner = identity.connectionToClient == conn;
1003 ArraySegment<byte> payload = CreateSpawnMessagePayload(isOwner, identity, ownerWriter, observersWriter);
1006 netId = identity.
netId,
1007 isLocalPlayer = conn.identity == identity,
1012 position = identity.transform.localPosition,
1013 rotation = identity.transform.localRotation,
1014 scale = identity.transform.localScale,
1028 if (!conn.
observing.Contains(identity))
return;
1034 netId = identity.
netId,
1035 isOwner = identity.connectionToClient == conn,
1036 isLocalPlayer = conn.identity == identity
1043 if (
Utils.IsPrefab(obj))
1045 Debug.LogError($
"GameObject {obj.name} is a prefab, it can't be spawned. Instantiate it first.");
1051 Debug.LogError($
"SpawnObject for {obj}, NetworkServer is not active. Cannot spawn objects without an active server.");
1056 if (identity ==
null)
1058 Debug.LogError($
"SpawnObject {obj} has no NetworkIdentity. Please add a NetworkIdentity to {obj}");
1062 if (identity.SpawnedFromInstantiate)
1074 identity.hasAuthority =
true;
1076 identity.OnStartServer();
1090 Debug.LogException(e);
1094 RebuildObservers(identity,
true);
1102 SpawnObject(obj, ownerConnection);
1107 public static void Spawn(GameObject obj, GameObject ownerPlayer)
1110 if (identity ==
null)
1112 Debug.LogError(
"Player object has no NetworkIdentity");
1118 Debug.LogError(
"Player object is not a player.");
1131 identity.assetId = assetId;
1133 SpawnObject(obj, ownerConnection);
1138 if (identity.gameObject.hideFlags == HideFlags.NotEditable ||
1139 identity.gameObject.hideFlags == HideFlags.HideAndDontSave)
1143 if (UnityEditor.EditorUtility.IsPersistent(identity.gameObject))
1148 return identity.sceneId != 0;
1166 if (ValidateSceneObject(identity))
1169 identity.gameObject.SetActive(
true);
1178 if (!identity.gameObject.activeInHierarchy)
1186 if (ValidateSceneObject(identity))
1197 if (identity.
netId == 0)
1228 if (identity.gameObject.activeSelf)
1242 if (identity.visible == Visibility.ForceShown)
1244 identity.AddObserver(conn);
1247 else if (identity.visible == Visibility.ForceHidden)
1252 else if (identity.visible == Visibility.Default)
1258 if (aoi.OnCheckObserver(identity, conn))
1259 identity.AddObserver(conn);
1264 identity.AddObserver(conn);
1283 public static void UnSpawn(GameObject obj) => DestroyObject(obj, DestroyMode.Reset);
1293 conn.DestroyOwnedObjects();
1299 conn.RemoveFromObservingsObservers();
1300 conn.identity =
null;
1306 enum DestroyMode {
Destroy, Reset }
1325 Debug.LogException(e);
1336 identity.ClearObservers();
1342 identity.OnStopLocalPlayer();
1344 identity.OnStopClient();
1348 identity.hasAuthority =
false;
1349 identity.NotifyAuthority();
1356 identity.OnStopServer();
1359 if (mode == DestroyMode.Destroy)
1361 identity.destroyCalled =
true;
1364 if (Application.isPlaying)
1366 UnityEngine.Object.Destroy(identity.gameObject);
1371 GameObject.DestroyImmediate(identity.gameObject);
1375 else if (mode == DestroyMode.Reset)
1381 static void DestroyObject(GameObject obj, DestroyMode mode)
1385 Debug.Log(
"NetworkServer DestroyObject is null");
1391 DestroyObject(identity, mode);
1399 public static void Destroy(GameObject obj) => DestroyObject(obj, DestroyMode.Destroy);
1405 internal static void AddAllReadyServerConnectionsToObservers(
NetworkIdentity identity)
1412 identity.AddObserver(conn);
1424 internal static readonly HashSet<NetworkConnectionToClient> newObservers =
1425 new HashSet<NetworkConnectionToClient>();
1428 static void RebuildObserversDefault(
NetworkIdentity identity,
bool initialize)
1435 if (identity.visible != Visibility.ForceHidden)
1437 AddAllReadyServerConnectionsToObservers(identity);
1443 static void RebuildObserversCustom(
NetworkIdentity identity,
bool initialize)
1446 newObservers.Clear();
1449 if (identity.visible != Visibility.ForceHidden)
1451 aoi.OnRebuildObservers(identity, newObservers);
1466 bool changed =
false;
1473 if (conn !=
null && conn.
isReady)
1478 conn.AddToObserving(identity);
1488 if (!newObservers.Contains(conn))
1491 conn.RemoveFromObserving(identity,
false);
1503 if (conn !=
null && conn.
isReady)
1534 aoi.SetHostVisibility(identity,
false);
1554 public static void RebuildObservers(
NetworkIdentity identity,
bool initialize)
1562 if (aoi ==
null || identity.visible == Visibility.ForceShown)
1564 RebuildObserversDefault(identity, initialize);
1569 RebuildObserversCustom(identity, initialize);
1582 bool owned = identity.connectionToClient == connection;
1589 if (serialization.ownerWriter.
Position > 0)
1590 return serialization.ownerWriter;
1596 if (serialization.observersWriter.
Position > 0)
1597 return serialization.observersWriter;
1614 if (identity !=
null)
1618 NetworkWriter serialization = GetEntitySerializationForConnection(identity, connection);
1619 if (serialization !=
null)
1623 netId = identity.
netId,
1626 connection.Send(message);
1633 else Debug.LogWarning($
"Found 'null' entry in observing list for connectionId={connection.connectionId}. Please call NetworkServer.Destroy to destroy networked objects. Don't use GameObject.Destroy.");
1640 internal static readonly List<NetworkConnectionToClient> connectionsCopy =
1641 new List<NetworkConnectionToClient>();
1643 static void Broadcast()
1653 connectionsCopy.Clear();
1665 BroadcastToConnection(connection);
1669 connection.Update();
1700 internal static void NetworkEarlyUpdate()
1707 internal static void NetworkLateUpdate()
virtual void OnDestroyed(NetworkIdentity identity)
Called on the server when a networked object is destroyed.
virtual void OnSpawned(NetworkIdentity identity)
Called on the server when a new networked object is spawned.
NetworkClient with connection to server.
static bool active
active is true while a client is connecting/connected
static readonly Dictionary< uint, NetworkIdentity > spawned
All spawned NetworkIdentities by netId.
Base NetworkConnection class for server-to-client and client-to-server connection.
readonly int connectionId
Unique identifier for this connection that is assigned by the transport layer.
NetworkIdentity identity
This connection's main object (usually the player object).
bool isReady
A server connection is ready after joining the game world.
override void Disconnect()
Disconnects this connection.
new readonly HashSet< NetworkIdentity > observing
NetworkIdentities that this connection can see
Profiling statistics for tool to subscribe to (profiler etc.)
NetworkIdentity identifies objects across the network.
bool isClient
Returns true if running as a client and this object was spawned by a server.
bool serverOnly
Make this object only exist when the game is running as a server (or host).
uint netId
The unique network Id of this object (unique at runtime).
Guid assetId
Prefab GUID used to spawn prefabs across the network.
ulong sceneId
Unique identifier for NetworkIdentity objects within a scene, used for spawning scene objects.
bool isLocalPlayer
Return true if this object represents the player on the local machine.
Dictionary< int, NetworkConnectionToClient > observers
The set of network connections (players) that can see this object.
void RemoveClientAuthority()
Removes ownership for an object.
NetworkConnectionToClient connectionToClient
Server's network connection to the client. This is only valid for client-owned objects (including the...
Network Reader for most simple types like floats, ints, buffers, structs, etc. Use NetworkReaderPool....
Pool of NetworkReaders to avoid allocations.
static NetworkReaderPooled Get(byte[] bytes)
Get the next reader in the pool. If pool is empty, creates a new Reader
Pooled NetworkReader, automatically returned to pool when using 'using'
NetworkServer handles remote connections and has a local connection for a local client.
static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, bool keepAuthority=false)
Replaces connection's player object. The old object is not destroyed.
static bool ReplacePlayerForConnection(NetworkConnectionToClient conn, GameObject player, Guid assetId, bool keepAuthority=false)
Replaces connection's player object. The old object is not destroyed.
static bool RemoveConnection(int connectionId)
Removes a connection by connectionId. Returns true if removed.
static void SendToAll< T >(T message, int channelId=Channels.Reliable, bool sendToReadyOnly=false)
Send a message to all clients, even those that haven't joined the world yet (non ready)
static void SendToReady< T >(T message, int channelId=Channels.Reliable)
Send a message to all clients which have joined the world (are ready).
static bool HasExternalConnections()
True if we have external connections (that are not host)
static bool SpawnObjects()
Spawns NetworkIdentities in the scene on the server.
static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameObject player, Guid assetId)
Called by server after AddPlayer message to add the player for the connection.
static void UnregisterHandler< T >()
Unregister a handler for a message type T.
static void Spawn(GameObject obj, NetworkConnection ownerConnection=null)
Spawn the given game object on all clients which are ready.
static bool AddConnection(NetworkConnectionToClient conn)
Add a connection and setup callbacks. Returns true if not added yet.
static void SetClientReady(NetworkConnectionToClient conn)
Flags client connection as ready (=joined world).
static Dictionary< int, NetworkConnectionToClient > connections
Dictionary of all server connections, with connectionId as key
static void Spawn(GameObject obj, Guid assetId, NetworkConnection ownerConnection=null)
Spawns an object and also assigns Client Authority to the specified client.
static bool AddPlayerForConnection(NetworkConnectionToClient conn, GameObject player)
Called by server after AddPlayer message to add the player for the connection.
static NetworkConnectionToClient localConnection
Connection to host mode client (if any)
static void UnSpawn(GameObject obj)
This takes an object that has been spawned and un-spawns it.
static void RemovePlayerForConnection(NetworkConnection conn, bool destroyServerObject)
Removes the player object from the connection
static void Listen(int maxConns)
Starts server and listens to incoming connections with max connections limit.
static void ClearHandlers()
Clears all registered message handlers.
static bool localClientActive
True is a local client is currently active on the server
static void ReplaceHandler< T >(Action< NetworkConnectionToClient, T > handler, bool requireAuthentication=true)
Replace a handler for message type T. Most should require authentication.
static bool active
active checks if the server has been started
static readonly Dictionary< uint, NetworkIdentity > spawned
All spawned NetworkIdentities by netId.
static void Spawn(GameObject obj, GameObject ownerPlayer)
Spawns an object and also assigns Client Authority to the specified client.
static void RegisterHandler< T >(Action< NetworkConnectionToClient, T > handler, bool requireAuthentication=true)
Register a handler for message type T. Most should require authentication.
static void DisconnectAll()
Disconnect all connections, including the local connection.
static void Destroy(GameObject obj)
Destroys this object and corresponding objects on all clients.
static bool dontListen
Single player mode can use dontListen to not accept incoming connections
static void DestroyPlayerForConnection(NetworkConnectionToClient conn)
Destroys all of the connection's owned objects on the server.
static void Shutdown()
Shuts down the server and disconnects all clients
static void SetAllClientsNotReady()
Marks all connected clients as no longer ready.
static void SendToReadyObservers< T >(NetworkIdentity identity, T message, bool includeOwner=true, int channelId=Channels.Reliable)
Send a message to only clients which are ready with option to include the owner of the object identit...
static void SetClientNotReady(NetworkConnectionToClient conn)
Marks the client of the connection to be not-ready.
Synchronizes server time to clients.
Network Writer for most simple types like floats, ints, buffers, structs, etc. Use NetworkWriterPool....
ArraySegment< byte > ToArraySegment()
Returns allocation-free ArraySegment until 'Position'.
int Position
Next position to write to the buffer
Pool of NetworkWriters to avoid allocations.
static NetworkWriterPooled Get()
Get a writer from the pool. Creates new one if pool is empty.
Pooled NetworkWriter, automatically returned to pool when using 'using'
Used to help manage remote calls for NetworkBehaviours
Abstract transport layer component
abstract void ServerStart()
Start listening for connections.
static Transport activeTransport
The current transport used by Mirror.
abstract void ServerStop()
Stop listening and disconnect all connections.
abstract void ServerDisconnect(int connectionId)
Disconnect a client from the server.