예제 #1
0
        /// <summary>
        /// Sends the named message
        /// </summary>
        /// <param name="name">The message name to send</param>
        /// <param name="clientIds">The clients to send to, sends to everyone if null</param>
        /// <param name="stream">The message stream containing the data</param>
        /// <param name="channel">The channel to send the data on</param>
        /// <param name="security">The security settings to apply to the message</param>
        public static void SendNamedMessage(string name, List <ulong> clientIds, Stream stream, string channel = null, SecuritySendFlags security = SecuritySendFlags.None)
        {
            ulong hash = NetworkedBehaviour.HashMethodName(name);

            using (PooledBitStream messageStream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(messageStream))
                {
                    writer.WriteUInt64Packed(hash);
                }

                messageStream.CopyFrom(stream);

                if (!NetworkingManager.Singleton.IsServer)
                {
                    if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
                    {
                        NetworkLog.LogWarning("Can not send named messages to multiple users as a client");
                    }
                    return;
                }

                InternalMessageSender.Send(MLAPIConstants.MLAPI_NAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, clientIds, messageStream, security, null);
            }
        }
예제 #2
0
        public void InvokeServerRPC(RPCDelegate method, Stream messageStream, byte channel = NetworkTransport.DEFAULT_CHANNEL)
        {
            if (!isClient)
            {
                //We are only a server and not a client
                Debug.LogError("Tried to invoke a ServerRPC without being a client. Only a client can invoke a server RPC.", this);
                return;
            }

            ulong hash = HashMethod(method.Method);

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt64Packed(networkID);
                    writer.WriteUInt64Packed(hash);

                    stream.CopyFrom(messageStream);

                    if (isHost)
                    {
                        messageStream.Position = 0;
                        //Invoke local
                        InvokeLocalServerRPC(hash, networkManager.clientID, stream);
                    }
                    else
                    {
                        MessageSender.Send(networkManager.serverID, m_NetworkBehaviourManager.serverRPCMessageType, channel, stream);
                    }
                }
            }
        }
예제 #3
0
        internal void SendClientRPCPerformance(ulong hash, uint clientId, Stream messageStream, string channel, SecuritySendFlags security)
        {
            if (!IsServer && IsRunning)
            {
                //We are NOT a server.
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("Only clients and host can invoke ClientRPC");
                }
                return;
            }

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt32Packed(NetworkId);
                    writer.WriteUInt16Packed(NetworkedObject.GetOrderIndex(this));
                    writer.WriteUInt64Packed(hash);

                    stream.CopyFrom(messageStream);

                    if (IsHost && clientId == NetworkingManager.Singleton.LocalClientId)
                    {
                        messageStream.Position = 0;
                        InvokeClientRPCLocal(hash, NetworkingManager.Singleton.LocalClientId, messageStream);
                    }
                    else
                    {
                        InternalMessageHandler.Send(clientId, MLAPIConstants.MLAPI_CLIENT_RPC, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, stream, security);
                    }
                }
            }
        }
        internal static void Send(uint clientId, byte messageType, string channelName, Stream messageStream, bool skipQueue = false)
        {
            if (NetworkingManager.singleton.isServer && clientId == NetworkingManager.singleton.ServerClientId)
            {
                return;
            }
            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteByte(messageType);
                    stream.CopyFrom(messageStream);

                    NetworkProfiler.StartEvent(TickType.Send, (uint)stream.Length, channelName, MLAPIConstants.MESSAGE_NAMES[messageType]);
                    byte error;
                    if (skipQueue)
                    {
                        netManager.NetworkConfig.NetworkTransport.QueueMessageForSending(clientId, stream.GetBuffer(), (int)stream.Length, MessageManager.channels[channelName], true, out error);
                    }
                    else
                    {
                        netManager.NetworkConfig.NetworkTransport.QueueMessageForSending(clientId, stream.GetBuffer(), (int)stream.Length, MessageManager.channels[channelName], false, out error);
                    }
                    NetworkProfiler.EndEvent();
                }
            }
        }
        internal static void Send(byte messageType, string channelName, uint clientIdToIgnore, Stream messageStream)
        {
            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteByte(messageType);
                    stream.CopyFrom(messageStream);

                    NetworkProfiler.StartEvent(TickType.Send, (uint)stream.Length, channelName, MLAPIConstants.MESSAGE_NAMES[messageType]);
                    for (int i = 0; i < netManager.ConnectedClientsList.Count; i++)
                    {
                        if (netManager.ConnectedClientsList[i].ClientId == clientIdToIgnore ||
                            (NetworkingManager.singleton.isServer && netManager.ConnectedClientsList[i].ClientId == NetworkingManager.singleton.ServerClientId))
                        {
                            continue;
                        }

                        byte error;
                        netManager.NetworkConfig.NetworkTransport.QueueMessageForSending(netManager.ConnectedClientsList[i].ClientId, stream.GetBuffer(), (int)stream.Length, MessageManager.channels[channelName], false, out error);
                    }
                    NetworkProfiler.EndEvent();
                }
            }
        }
예제 #6
0
        internal static void SendSpawnCallForObject(ulong clientId, NetworkedObject netObject, Stream payload)
        {
            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteBool(netObject.IsPlayerObject);
                    writer.WriteUInt64Packed(netObject.NetworkId);
                    writer.WriteUInt64Packed(netObject.OwnerClientId);

                    if (NetworkingManager.Singleton.NetworkConfig.UsePrefabSync)
                    {
                        writer.WriteUInt64Packed(netObject.PrefabHash);
                    }
                    else
                    {
                        writer.WriteBool(netObject.IsSceneObject == null ? true : netObject.IsSceneObject.Value);

                        if (netObject.IsSceneObject == null || netObject.IsSceneObject.Value)
                        {
                            writer.WriteUInt64Packed(netObject.NetworkedInstanceId);
                        }
                        else
                        {
                            writer.WriteUInt64Packed(netObject.PrefabHash);
                        }
                    }

                    writer.WriteBool(netObject.DestroyWithScene);

                    writer.WriteSinglePacked(netObject.transform.position.x);
                    writer.WriteSinglePacked(netObject.transform.position.y);
                    writer.WriteSinglePacked(netObject.transform.position.z);

                    writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.x);
                    writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.y);
                    writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.z);

                    writer.WriteBool(payload != null);

                    if (payload != null)
                    {
                        writer.WriteInt32Packed((int)payload.Length);
                    }

                    if (NetworkingManager.Singleton.NetworkConfig.EnableNetworkedVar)
                    {
                        netObject.WriteNetworkedVarData(stream, clientId);
                    }

                    if (payload != null)
                    {
                        stream.CopyFrom(payload);
                    }
                }

                InternalMessageHandler.Send(clientId, MLAPIConstants.MLAPI_ADD_OBJECT, "MLAPI_INTERNAL", stream, SecuritySendFlags.None, null);
            }
        }
예제 #7
0
        //Server only
        public void SpawnOnNetworkServer(NetworkBehaviour behaviour, ServerBehaviourConnectedDelegate connectedCallback, BehaviourDisconnectedDelegate disconnectCallback, NetworkBehaviourRPCDelegate localRPCCallback, OwnerChangeDelegate ownerChangeCallback, ulong owner, List <ulong> observers, Stream spawnPayload)
        {
            if (behaviour == null || !behaviour.isNetworkSpawned || behaviour.isNetworkReady)
            {
                throw new InvalidOperationException("NetworkBehaviourManager.SpawnOnNetworkServer is only allowed to be called internally by a Network Behaviour.");
            }

            ulong newNetworkID = GetNewNetworkID();

            if (!string.IsNullOrWhiteSpace(behaviour.uniqueID))
            {
                ulong uniqueHash = behaviour.uniqueID.GetStableHash(networkManager.config.rpcHashSize);
                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 + "'.");
                    }
                }
                m_HashedStrings.Add(uniqueHash, behaviour.uniqueID);
                m_BehaviourByHashedID.Add(uniqueHash, behaviour);
            }

            m_NetworkBehaviours.Add(new NetworkBehaviourReference()
            {
                networkBehaviour = behaviour, connectedServerCallback = connectedCallback, disconnectedDelegate = disconnectCallback, localRPCDelegate = localRPCCallback, ownerChangeDelegate = ownerChangeCallback
            });
            m_NetworkBehaviourDictionary.Add(newNetworkID, m_NetworkBehaviours[m_NetworkBehaviours.Count - 1]);

            //Spawn payload
            if (spawnPayload == null)
            {
                connectedCallback.Invoke(newNetworkID, owner, networkManager.serverID, observers, null);
            }
            else
            {
                using (PooledBitStream payloadStream = PooledBitStream.Get())
                {
                    payloadStream.CopyFrom(spawnPayload);
                    payloadStream.Position = 0;
                    connectedCallback.Invoke(newNetworkID, owner, networkManager.serverID, observers, payloadStream);
                }
            }
        }
예제 #8
0
        /// <summary>
        /// Sends a named message
        /// </summary>
        /// <param name="name">The message name to send</param>
        /// <param name="clientId">The client to send the message to</param>
        /// <param name="stream">The message stream containing the data</param>
        /// <param name="channel">The channel tos end the data on</param>
        /// <param name="security">The security settings to apply to the message</param>
        public static void SendNamedMessage(string name, ulong clientId, Stream stream, string channel = null, SecuritySendFlags security = SecuritySendFlags.None)
        {
            ulong hash = NetworkedBehaviour.HashMethodName(name);

            using (PooledBitStream messageStream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(messageStream))
                {
                    writer.WriteUInt64Packed(hash);
                }

                messageStream.CopyFrom(stream);

                InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_NAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, messageStream, security, null);
            }
        }
        private void DoVisibleShowWrite(PooledBitStream stream, Stream spawnPayload)
        {
            if (!isServer)
            {
                throw new NotServerException("Only the server can change visibility of a Network Behaviour.");
            }

            if (!isNetworkSpawned)
            {
                throw new NetworkException("This Network Behaviour is not spawned on the network. Make sure this Network Behaviour is spawned using the NetworkBehaviour.SpawnOnNetwork function before changing it's visiblity.");
            }

            //Do message
            using (PooledBitWriter writer = PooledBitWriter.Get(stream))
            {
                //Write behaviour info and type
                writer.WriteUInt64Packed(networkID);
                writer.WriteUInt64Packed(ownerID);
                writer.WriteUInt64Packed(RPCTypeDefinition.GetHashFromType(GetType()));
                if (string.IsNullOrWhiteSpace(uniqueID))
                {
                    writer.WriteBool(false);
                }
                else
                {
                    writer.WriteBool(true);
                    writer.WriteUInt64Packed(m_UniqueHash);
                }
                writer.WriteBool(ownerCanUnspawn);
                writer.WriteBool(destroyOnUnspawn);

                //Write payload
                writer.WriteBool(spawnPayload != null);

                if (spawnPayload != null)
                {
                    spawnPayload.Position = 0;
                    writer.WriteInt32Packed((int)spawnPayload.Length);
                    stream.CopyFrom(spawnPayload);
                }

                if (networkManager.enableLogging)
                {
                    Debug.Log("Sending to clients the new behaviour " + GetType());
                }
            }
        }
예제 #10
0
        public void InvokeClientRPC(RPCDelegate method, ulong clientID, Stream messageStream, byte channel = NetworkTransport.DEFAULT_CHANNEL)
        {
            if (!isServer)
            {
                //We are only a client
                Debug.LogError("Tried to invoke a ClientRPC without being the server. Only the server can invoke a client RPC.", this);
                return;
            }

            ulong hash = HashMethod(method.Method);

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt64Packed(networkID);
                    writer.WriteUInt64Packed(hash);

                    stream.CopyFrom(messageStream);

                    if (isHost && clientID == networkManager.clientID)
                    {
                        messageStream.Position = 0;
                        //Invoke local
                        InvokeLocalClientRPC(hash, networkManager.clientID, messageStream);
                    }
                    else
                    {
                        if (!IsNetworkVisibleTo(clientID))
                        {
                            if (IsClientPendingSpawn(clientID))
                            {
                                Debug.LogError("The target client ID '" + clientID + "' is still a pending observer and cannot invoke remote RPCs on it until this Network Behaviour is network visible to that client.", this);
                            }
                            else
                            {
                                Debug.LogError("The target client ID '" + clientID + "' is not an observer of this Network Behaviour.", this);
                            }
                            return;
                        }
                        MessageSender.Send(clientID, m_NetworkBehaviourManager.clientRPCMessageType, channel, stream);
                    }
                }
            }
        }
예제 #11
0
        /// <summary>
        /// Encrypts a message with AES with a given key and a random salt that gets encoded as the first 16 bytes of the encrypted buffer
        /// </summary>
        /// <param name="clearStream">The stream to be encrypted</param>
        /// <param name="clientId">The clientId whose AES key to use</param>
        /// <returns>The encrypted stream with encoded salt</returns>
        public static Stream EncryptStream(Stream clearStream, uint clientId)
        {
            using (RijndaelManaged aes = new RijndaelManaged())
            {
                aes.Key = NetworkingManager.singleton.isServer ? NetworkingManager.singleton.ConnectedClients[clientId].AesKey : NetworkingManager.singleton.clientAesKey;
                aes.GenerateIV();

                using (CryptoStream cs = new CryptoStream(clearStream, aes.CreateEncryptor(), CryptoStreamMode.Read))
                {
                    using (PooledBitStream outStream = PooledBitStream.Get())
                    {
                        outStream.Write(aes.IV, 0, 16);
                        outStream.CopyFrom(cs);
                        return(outStream);
                    }
                }
            }
        }
예제 #12
0
        /// <summary>
        /// Decrypts a message with AES with a given key and a salt that is encoded as the first 16 bytes of the buffer
        /// </summary>
        /// <param name="encryptedStream">The encrypted stream</param>
        /// <param name="clientId">The clientId whose AES key to use</param>
        /// <returns>The decrypted stream</returns>
        public static Stream DecryptStream(Stream encryptedStream, uint clientId)
        {
            encryptedStream.Read(IVBuffer, 0, 16);

            using (RijndaelManaged aes = new RijndaelManaged())
            {
                aes.IV  = IVBuffer;
                aes.Key = NetworkingManager.singleton.isServer ? NetworkingManager.singleton.ConnectedClients[clientId].AesKey : NetworkingManager.singleton.clientAesKey;
                using (CryptoStream cs = new CryptoStream(encryptedStream, aes.CreateDecryptor(), CryptoStreamMode.Read))
                {
                    using (PooledBitStream outStream = PooledBitStream.Get())
                    {
                        outStream.CopyFrom(cs);
                        return(outStream);
                    }
                }
            }
        }
예제 #13
0
        public void InvokeClientRPCAllExcept(RPCDelegate method, ulong clientIDToIgnore, Stream messageStream, byte channel = NetworkTransport.DEFAULT_CHANNEL)
        {
            if (!isServer)
            {
                //We are only a client
                Debug.LogError("Tried to invoke a ClientRPC without being the server. Only the server can invoke a client RPC.", this);
                return;
            }

            ulong hash = HashMethod(method.Method);

            using (PooledBitStream stream = PooledBitStream.Get())
            {
                using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                {
                    writer.WriteUInt64Packed(networkID);
                    writer.WriteUInt64Packed(hash);

                    stream.CopyFrom(messageStream);

                    using (HashSet <ulong> .Enumerator observers = GetObservers())
                    {
                        while (observers.MoveNext())
                        {
                            if (observers.Current == clientIDToIgnore)
                            {
                                continue;
                            }
                            if (observers.Current == networkManager.clientID && isHost)
                            {
                                //Invoke local
                                messageStream.Position = 0;
                                InvokeLocalClientRPC(hash, networkManager.clientID, messageStream);
                            }

                            //Send to remote observer
                            MessageSender.Send(observers.Current, m_NetworkBehaviourManager.clientRPCMessageType, channel, stream);
                        }
                    }
                }
            }
        }
예제 #14
0
        internal static void SpawnObject(NetworkedObject netObject, uint?clientOwnerId = null, Stream payload = null, bool destroyWithScene = false)
        {
            if (netObject.isSpawned)
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("Object already spawned");
                }
                return;
            }
            else if (!netManager.isServer)
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("Only server can spawn objects");
                }
                return;
            }
            else if (SpawnManager.GetNetworkedPrefabIndexOfName(netObject.NetworkedPrefabName) == -1)
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("The prefab name " + netObject.NetworkedPrefabName + " does not exist as a networkedPrefab");
                }
                return;
            }
            else if (!netManager.NetworkConfig.HandleObjectSpawning)
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("NetworkConfig is set to not handle object spawning");
                }
                return;
            }
            uint netId = GetNetworkObjectId();

            netObject.NetworkId = netId;
            SpawnedObjects.Add(netId, netObject);
            SpawnedObjectsList.Add(netObject);
            netObject.isSpawned           = true;
            netObject.destroyWithScene    = destroyWithScene;
            netObject.sceneSpawnedInIndex = NetworkSceneManager.CurrentActiveSceneIndex;

            if (clientOwnerId != null)
            {
                netObject.OwnerClientId = clientOwnerId.Value;
                NetworkingManager.singleton.ConnectedClients[clientOwnerId.Value].OwnedObjects.Add(netObject);
            }

            if (payload == null)
            {
                netObject.InvokeBehaviourNetworkSpawn(null);
            }
            else
            {
                netObject.InvokeBehaviourNetworkSpawn(payload);
            }

            foreach (var client in netManager.ConnectedClients)
            {
                using (PooledBitStream stream = PooledBitStream.Get())
                {
                    using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                    {
                        writer.WriteBool(false);
                        writer.WriteUInt32Packed(netObject.NetworkId);
                        writer.WriteUInt32Packed(netObject.OwnerClientId);
                        writer.WriteUInt64Packed(netObject.NetworkedPrefabHash);

                        writer.WriteBool(netObject.destroyWithScene == null ? true : netObject.destroyWithScene.Value);
                        writer.WriteBool(netObject.SceneDelayedSpawn);
                        writer.WriteUInt32Packed(netObject.sceneSpawnedInIndex);

                        writer.WriteSinglePacked(netObject.transform.position.x);
                        writer.WriteSinglePacked(netObject.transform.position.y);
                        writer.WriteSinglePacked(netObject.transform.position.z);

                        writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.x);
                        writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.y);
                        writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.z);

                        writer.WriteBool(payload != null);
                        if (payload != null)
                        {
                            writer.WriteInt32Packed((int)payload.Length);
                        }

                        netObject.WriteNetworkedVarData(stream, client.Key);

                        if (payload != null)
                        {
                            stream.CopyFrom(payload);
                        }

                        InternalMessageHandler.Send(client.Key, MLAPIConstants.MLAPI_ADD_OBJECT, "MLAPI_INTERNAL", stream, SecuritySendFlags.None);
                    }
                }
            }
        }
예제 #15
0
        //Server only
        internal static void SpawnPlayerObject(NetworkedObject netObject, uint clientId, Stream payload = null)
        {
            if (netObject.isSpawned)
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("Object already spawned");
                }
                return;
            }
            else if (!netManager.isServer)
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("Only server can spawn objects");
                }
                return;
            }
            else if (!netManager.NetworkConfig.NetworkPrefabIds.ContainsKey(netObject.NetworkedPrefabName))
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("The prefab name " + netObject.NetworkedPrefabName + " does not exist as a networkedPrefab");
                }
                return;
            }
            else if (!netManager.NetworkConfig.HandleObjectSpawning)
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("NetworkConfig is set to not handle object spawning");
                }
                return;
            }
            else if (netManager.ConnectedClients[clientId].PlayerObject != null)
            {
                if (LogHelper.CurrentLogLevel <= LogLevel.Normal)
                {
                    LogHelper.LogWarning("Client already have a player object");
                }
                return;
            }
            uint netId = GetNetworkObjectId();

            netObject.NetworkId = netId;
            SpawnedObjects.Add(netId, netObject);
            SpawnedObjectsList.Add(netObject);
            netObject.isSpawned      = true;
            netObject.sceneObject    = false;
            netObject.isPlayerObject = true;
            netManager.ConnectedClients[clientId].PlayerObject = netObject;

            if (payload == null)
            {
                netObject.InvokeBehaviourNetworkSpawn(null);
            }
            else
            {
                netObject.InvokeBehaviourNetworkSpawn(payload);
            }

            foreach (var client in netManager.ConnectedClients)
            {
                using (PooledBitStream stream = PooledBitStream.Get())
                {
                    using (PooledBitWriter writer = PooledBitWriter.Get(stream))
                    {
                        writer.WriteBool(true);
                        writer.WriteUInt32Packed(netObject.NetworkId);
                        writer.WriteUInt32Packed(netObject.OwnerClientId);
                        writer.WriteInt32Packed(netManager.NetworkConfig.NetworkPrefabIds[netObject.NetworkedPrefabName]);
                        writer.WriteBool(netObject.sceneObject == null ? true : netObject.sceneObject.Value);

                        writer.WriteSinglePacked(netObject.transform.position.x);
                        writer.WriteSinglePacked(netObject.transform.position.y);
                        writer.WriteSinglePacked(netObject.transform.position.z);

                        writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.x);
                        writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.y);
                        writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.z);

                        writer.WriteBool(payload != null);

                        netObject.WriteNetworkedVarData(stream, client.Key);

                        if (payload != null)
                        {
                            stream.CopyFrom(payload);
                        }

                        InternalMessageHandler.Send(client.Key, MLAPIConstants.MLAPI_ADD_OBJECT, "MLAPI_INTERNAL", stream);
                    }
                }
            }
        }