internal static void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject, Stream payload) { //Currently, if this is called and the clientId (destination) is the server's client Id, this case //will be checked within the below Send function. To avoid unwarranted allocation of a PooledNetworkBuffer //placing this check here. [NSS] if (NetworkManager.Singleton.IsServer && clientId == NetworkManager.Singleton.ServerClientId) { return; } var rpcQueueContainer = NetworkManager.Singleton.RpcQueueContainer; var buffer = PooledNetworkBuffer.Get(); WriteSpawnCallForObject(buffer, clientId, networkObject, payload); var queueItem = new RpcFrameQueueItem { UpdateStage = NetworkUpdateStage.Update, QueueItemType = RpcQueueContainer.QueueItemType.CreateObject, NetworkId = 0, NetworkBuffer = buffer, NetworkChannel = NetworkChannel.Internal, ClientNetworkIds = new[] { clientId } }; rpcQueueContainer.AddToInternalMLAPISendQueue(queueItem); }
public void SendWithThreshold() { const int k_BatchThreshold = 256; const int k_QueueItemCount = 128; var sendBatcher = new RpcBatcher(); var sendStreamQueue = new Queue <NetworkBuffer>(); for (int i = 0; i < k_QueueItemCount; ++i) { var randomData = Encoding.ASCII.GetBytes(Guid.NewGuid().ToString()); var queueItem = new RpcFrameQueueItem { NetworkId = 123, ClientNetworkIds = new ulong[] { 123 }, NetworkChannel = NetworkChannel.ChannelUnused + 123, QueueItemType = i % 2 == 0 ? RpcQueueContainer.QueueItemType.ServerRpc : RpcQueueContainer.QueueItemType.ClientRpc, MessageData = new ArraySegment <byte>(randomData, 0, randomData.Length) }; sendBatcher.QueueItem(queueItem); sendBatcher.SendItems(k_BatchThreshold, (networkId, sendStream) => { var queueStream = new NetworkBuffer(); sendStream.Buffer.CopyTo(queueStream); sendStreamQueue.Enqueue(queueStream); }); } // batch the rest sendBatcher.SendItems(/* thresholdBytes = */ 0, (networkId, sendStream) => { var queueStream = new NetworkBuffer(); sendStream.Buffer.CopyTo(queueStream); sendStreamQueue.Enqueue(queueStream); }); var recvBatcher = new RpcBatcher(); var recvItemCounter = 0; foreach (var recvStream in sendStreamQueue) { recvStream.Position = 0; // todo: revisit // The following line is sub-optimal // The stream returned by SendItems() includes: // - 8 bits for the MLAPI message types. // ReceiveItems expects those to have been stripped by the receive code. // In order to replicate this behaviour, we'll read 8 bits before calling ReceiveItems() recvStream.ReadByte(); recvBatcher.ReceiveItems(recvStream, (stream, type, id, time) => ++ recvItemCounter, default, default, default);
internal static void OnDestroyObject(ulong networkId, bool destroyGameObject) { if (ReferenceEquals(NetworkManager.Singleton, null)) { return; } //Removal of spawned object if (!SpawnedObjects.ContainsKey(networkId)) { Debug.LogWarning($"Trying to destroy object {networkId} but it doesn't seem to exist anymore!"); return; } var sobj = SpawnedObjects[networkId]; if (!sobj.IsOwnedByServer && !sobj.IsPlayerObject && NetworkManager.Singleton.ConnectedClients.ContainsKey(sobj.OwnerClientId)) { //Someone owns it. for (int i = NetworkManager.Singleton.ConnectedClients[sobj.OwnerClientId].OwnedObjects.Count - 1; i > -1; i--) { if (NetworkManager.Singleton.ConnectedClients[sobj.OwnerClientId].OwnedObjects[i].NetworkObjectId == networkId) { NetworkManager.Singleton.ConnectedClients[sobj.OwnerClientId].OwnedObjects.RemoveAt(i); } } } sobj.IsSpawned = false; if (NetworkManager.Singleton != null && NetworkManager.Singleton.IsServer) { if (NetworkManager.Singleton.NetworkConfig.RecycleNetworkIds) { ReleasedNetworkObjectIds.Enqueue(new ReleasedNetworkId() { NetworkId = networkId, ReleaseTime = Time.unscaledTime }); } var rpcQueueContainer = NetworkManager.Singleton.RpcQueueContainer; if (rpcQueueContainer != null) { if (!ReferenceEquals(sobj, null)) { // As long as we have any remaining clients, then notify of the object being destroy. if (NetworkManager.Singleton.ConnectedClientsList.Count > 0) { var buffer = PooledNetworkBuffer.Get(); using (var writer = PooledNetworkWriter.Get(buffer)) { writer.WriteUInt64Packed(networkId); var queueItem = new RpcFrameQueueItem { UpdateStage = NetworkUpdateStage.PostLateUpdate, QueueItemType = RpcQueueContainer.QueueItemType.DestroyObject, NetworkId = networkId, NetworkBuffer = buffer, NetworkChannel = NetworkChannel.Internal, ClientNetworkIds = NetworkManager.Singleton.ConnectedClientsList.Select(c => c.ClientId).ToArray() }; rpcQueueContainer.AddToInternalMLAPISendQueue(queueItem); } } } } } var gobj = sobj.gameObject; if (destroyGameObject && !ReferenceEquals(gobj, null)) { if (CustomDestroyHandlers.ContainsKey(sobj.PrefabHash)) { CustomDestroyHandlers[sobj.PrefabHash](sobj); OnDestroyObject(networkId, false); } else { MonoBehaviour.Destroy(gobj); } } // for some reason, we can get down here and SpawnedObjects for this // networkId will no longer be here, even as we check this at the start // of the function if (SpawnedObjects.ContainsKey(networkId)) { SpawnedObjectsList.Remove(sobj); SpawnedObjects.Remove(networkId); } }