//Do unspawn on pending behaviour //Client only private void DoLocalUnspawn(PendingNetworkBehaviour pendingBehaviour, bool destroy) { if (networkManager.enableLogging) { Debug.Log("Do Local Unspawn Pending called"); } m_LocalPendingBehaviours.Remove(pendingBehaviour.uniqueHash.Value); if (pendingBehaviour.uniqueHash != null) { m_HashedStrings.Remove(pendingBehaviour.uniqueHash.Value); m_BehaviourByHashedID.Remove(pendingBehaviour.uniqueHash.Value); } m_LocalPendingBehavioursList.Remove(pendingBehaviour.reference.networkBehaviour); pendingBehaviour.reference.disconnectedDelegate.Invoke(pendingBehaviour.reference.networkBehaviour.ownerCanUnspawn, destroy); }
public void HandleSpawnMessage(ulong clientID, Stream stream, float receiveTime) { using (PooledBitReader reader = PooledBitReader.Get(stream)) { ulong networkID = reader.ReadUInt64Packed(); //Network ID ulong ownerID = reader.ReadUInt64Packed(); //Owner Type behaviourType = RPCTypeDefinition.GetTypeFromHash(reader.ReadUInt64Packed()); bool hasUniqueHash = reader.ReadBool(); ulong uniqueHash = 0; if (hasUniqueHash) { uniqueHash = reader.ReadUInt64Packed(); } bool ownerCanUnspawn = reader.ReadBool(); bool destroyOnUnspawn = reader.ReadBool(); //Read spawn payload PooledBitStream payloadStream = null; if (reader.ReadBool()) { payloadStream = PooledBitStream.Get(); int payloadLength = reader.ReadInt32Packed(); payloadStream.CopyUnreadFrom(stream, payloadLength); stream.Position += payloadLength; payloadStream.Position = 0; } if (networkManager.enableLogging) { string s = "Received add object event from server. Object: " + behaviourType.ToString() + " | Network ID: " + networkID + " | Owner: " + ownerID + " | Has Unique Hash: " + hasUniqueHash + " | "; if (hasUniqueHash) { s += uniqueHash + " | "; } s += "Owner Can Unspawn: " + ownerCanUnspawn + " | Destroy On Unspawn: " + destroyOnUnspawn; Debug.Log(s); } if (hasUniqueHash) { if (m_LocalPendingBehaviours.TryGetValue(uniqueHash, out PendingNetworkBehaviour pendingBehaviour)) { if (pendingBehaviour.reference.networkBehaviour.GetType() != behaviourType) { Debug.LogError("Received add object message where the remote network behaviour type(" + behaviourType.ToString() + ") does not match up with local network behaviour type (" + pendingBehaviour.reference.networkBehaviour.GetType() + ") with same unique ID(" + pendingBehaviour.reference.networkBehaviour.uniqueID + ").", pendingBehaviour.reference.networkBehaviour); return; } //Clean up pending m_LocalPendingBehaviours.Remove(uniqueHash); m_LocalPendingBehavioursList.Remove(pendingBehaviour.reference.networkBehaviour); OnObjectConnectSuccess(pendingBehaviour.reference, networkID, ownerID, ownerCanUnspawn, destroyOnUnspawn, payloadStream); if (payloadStream != null) { payloadStream.Dispose(); } } else if (m_RemotePendingBehavioursHashes.ContainsKey(uniqueHash)) { Debug.LogError("Received duplicate 'add object' message for hash '" + uniqueHash + "'."); return; } else if (m_RemotePendingBehaviours.ContainsKey(networkID)) { Debug.LogError("Recevied duplicate 'add object' message for network ID '" + networkID + "'."); return; } else { PendingNetworkBehaviour pendingBehaviourReference = new PendingNetworkBehaviour() { isRemoteBehaviour = true, uniqueHash = uniqueHash, ownerID = ownerID, networkID = networkID, ownerCanUnspawn = ownerCanUnspawn, destroyOnUnspawn = destroyOnUnspawn, spawnPayload = payloadStream }; m_RemotePendingBehavioursHashes.Add(uniqueHash, pendingBehaviourReference); m_RemotePendingBehaviours.Add(networkID, pendingBehaviourReference); } } else //No unique hash { //Build network behaviour GameObject behaviourObject = new GameObject("Server Network Object"); //All this stuff just in case the instantiate behaviour also instantiates other network behaviours in its awake function m_TrackAwakeSpawns = true; NetworkBehaviour serverBehaviour = (NetworkBehaviour)behaviourObject.AddComponent(behaviourType); m_TrackAwakeSpawns = false; if (m_BehavioursAwaitingSpawn.Count > 0) { while (m_BehavioursAwaitingSpawn.Count > 0) { NetworkBehaviourReference reference = m_BehavioursAwaitingSpawn.Dequeue(); if (reference.networkBehaviour == serverBehaviour) { OnObjectConnectSuccess(reference, networkID, ownerID, ownerCanUnspawn, destroyOnUnspawn, payloadStream); if (payloadStream != null) { payloadStream.Dispose(); } } else { SpawnOnNetworkClient(reference.networkBehaviour, reference.connectedClientCallback, reference.disconnectedDelegate, reference.localRPCDelegate, reference.ownerChangeDelegate); } } } if (!serverBehaviour.isNetworkSpawned) { serverBehaviour.uniqueID = "NETWORK_SERVER_BEHAVIOUR_" + networkID.ToString(); serverBehaviour.SpawnOnNetwork(); ulong hash = serverBehaviour.uniqueID.GetStableHash(networkManager.config.rpcHashSize); PendingNetworkBehaviour pendingBehaviour = m_LocalPendingBehaviours[hash]; //Clean up pending m_LocalPendingBehaviours.Remove(uniqueHash); m_LocalPendingBehavioursList.Remove(serverBehaviour); OnObjectConnectSuccess(pendingBehaviour.reference, networkID, ownerID, ownerCanUnspawn, destroyOnUnspawn, payloadStream); if (payloadStream != null) { payloadStream.Dispose(); } } } } }
//Client only public void SpawnOnNetworkClient(NetworkBehaviour behaviour, ClientBehaviourConnectedDelegate connectedCallback, BehaviourDisconnectedDelegate disconnectedCallback, NetworkBehaviourRPCDelegate localRPCCallback, OwnerChangeDelegate ownerChangeCallback) { if (behaviour == null || !behaviour.isNetworkSpawned || behaviour.isNetworkReady || m_LocalPendingBehavioursList.Contains(behaviour) || string.IsNullOrWhiteSpace(behaviour.uniqueID)) { throw new InvalidOperationException("NetworkBehaviourManager.SpawnNetworkClient is only allowed to be called internally by a Network Behaviour."); } //Check if this is the Unique Behaviour being spawned in HandleAddObjectMessage where the Unique ID is blank and SpawnOnNetwork is called in Awake. if (m_TrackAwakeSpawns) { m_BehavioursAwaitingSpawn.Enqueue(new NetworkBehaviourReference() { networkBehaviour = behaviour, connectedClientCallback = connectedCallback, disconnectedDelegate = disconnectedCallback, localRPCDelegate = localRPCCallback, ownerChangeDelegate = ownerChangeCallback }); return; } ulong uniqueHash = behaviour.uniqueID.GetStableHash(networkManager.config.rpcHashSize); if (m_RemotePendingBehavioursHashes.TryGetValue(uniqueHash, out PendingNetworkBehaviour pendingBehaviour)) { m_RemotePendingBehavioursHashes.Remove(uniqueHash); m_RemotePendingBehaviours.Remove(pendingBehaviour.networkID); OnObjectConnectSuccess(new NetworkBehaviourReference() { networkBehaviour = behaviour, connectedClientCallback = connectedCallback, disconnectedDelegate = disconnectedCallback, localRPCDelegate = localRPCCallback, ownerChangeDelegate = ownerChangeCallback }, pendingBehaviour.networkID, pendingBehaviour.ownerID, pendingBehaviour.ownerCanUnspawn, pendingBehaviour.destroyOnUnspawn, pendingBehaviour.spawnPayload); pendingBehaviour.spawnPayload?.Dispose(); } else if (m_LocalPendingBehaviours.TryGetValue(uniqueHash, out pendingBehaviour)) { //Check if the unique hash has already been added by this client if (m_BehaviourByHashedID.TryGetValue(uniqueHash, out NetworkBehaviour otherBehaviour)) { if (!BehaviourWasDestroyed(otherBehaviour)) { throw new NetworkException("A Network Behaviour already has the unique ID '" + behaviour.uniqueID + "'."); } } if (m_HashedStrings.TryGetValue(uniqueHash, out string otherUniqueID)) { if (otherUniqueID == behaviour.uniqueID) { throw new NetworkException("A Network Behaviour already has the unique ID '" + behaviour.uniqueID + "'."); } else { //This occurs when 2 different strings hash to the same value from MLAPI.Hashing.GetStableHash. throw new NetworkException("A hash collision occurred. Either change the unique ID or increase the hash size in the config. '" + behaviour.uniqueID + "' and '" + otherUniqueID + "' both hashed to '" + uniqueHash + "'."); } } } else { if (m_BehaviourByHashedID.TryGetValue(uniqueHash, out NetworkBehaviour otherBehaviour)) { if (!BehaviourWasDestroyed(otherBehaviour)) { throw new NetworkException("A Network Behaviour already has the unique ID '" + behaviour.uniqueID + "'."); } } if (m_HashedStrings.TryGetValue(uniqueHash, out string otherUniqueID)) { if (otherUniqueID == behaviour.uniqueID) { throw new NetworkException("A Network Behaviour already has the unique ID '" + behaviour.uniqueID + "'."); } else { //This occurs when 2 different strings hash to the same value from MLAPI.Hashing.GetStableHash. throw new NetworkException("A hash collision occurred. Either change the unique ID or increase the hash size in the config. '" + behaviour.uniqueID + "' and '" + otherUniqueID + "' both hashed to '" + uniqueHash + "'."); } } //Add to pending pendingBehaviour = new PendingNetworkBehaviour() { isRemoteBehaviour = false, uniqueHash = uniqueHash, ownerID = networkManager.serverID, networkID = 0, reference = new NetworkBehaviourReference { networkBehaviour = behaviour, connectedClientCallback = connectedCallback, disconnectedDelegate = disconnectedCallback, localRPCDelegate = localRPCCallback, ownerChangeDelegate = ownerChangeCallback } }; m_LocalPendingBehavioursList.Add(pendingBehaviour.reference.networkBehaviour); m_LocalPendingBehaviours.Add(uniqueHash, pendingBehaviour); m_HashedStrings.Add(uniqueHash, behaviour.uniqueID); m_BehaviourByHashedID.Add(uniqueHash, behaviour); } }