예제 #1
0
        // note: original HLAPI HandleBytes function handled >1 message in a while loop, but this wasn't necessary
        //       anymore because NetworkServer/NetworkClient Update both use while loops to handle >1 data events per
        //       frame already.
        //       -> in other words, we always receive 1 message per Receive call, never two.
        //       -> can be tested easily with a 1000ms send delay and then logging amount received in while loops here
        //          and in NetworkServer/Client Update. HandleBytes already takes exactly one.
        /// <summary>
        /// This virtual function allows custom network connection classes to process data from the network before it is passed to the application.
        /// </summary>
        /// <param name="buffer">The data recieved.</param>
        public virtual void TransportReceive(ArraySegment <byte> buffer)
        {
#if MIRROR_PROFILING
            // -1 for now since we don't know the incoming segment channel
            NetworkProfiler.RecordTraffic(NetworkDirection.Incoming, -1, buffer.Count);
#endif

            // unpack message
            NetworkReader reader = new NetworkReader(buffer);
            if (MessagePacker.UnpackMessage(reader, out int msgType))
            {
                // logging
                if (logNetworkMessages)
                {
                    Debug.Log("ConnectionRecv con:" + connectionId + " msgType:" + msgType + " content:" + BitConverter.ToString(buffer.Array, buffer.Offset, buffer.Count));
                }

                // try to invoke the handler for that message
                if (InvokeHandler(msgType, reader))
                {
                    lastMessageTime = Time.time;
                }
            }
            else
            {
                Debug.LogError("Closed connection: " + connectionId + ". Invalid message header.");
                Disconnect();
            }
        }
예제 #2
0
        protected void SendRPCInternal(Type invokeClass, string rpcName, NetworkWriter writer, int channelId)
        {
            // this was in Weaver before
            if (!NetworkServer.active)
            {
                Debug.LogError("RPC Function " + rpcName + " called on Client.");
                return;
            }
            // This cannot use NetworkServer.active, as that is not specific to this object.
            if (!isServer)
            {
                Debug.LogWarning("ClientRpc " + rpcName + " called on un-spawned object: " + name);
                return;
            }

            // construct the message
            RpcMessage message = new RpcMessage
            {
                netId          = netId,
                componentIndex = ComponentIndex,
                functionHash   = GetMethodHash(invokeClass, rpcName), // type+func so Inventory.RpcUse != Equipment.RpcUse
                payload        = writer.ToArraySegment()              // segment to avoid reader allocations
            };

            NetworkServer.SendToReady(netIdentity, message, channelId);
#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Outgoing, typeof(RpcMessage), $"{invokeClass.GetType()}.{rpcName}", 1);
#endif
        }
예제 #3
0
        /// <summary>
        /// Signal that the client connection is ready to enter the game.
        /// <para>This could be for example when a client enters an ongoing game and has finished loading the current scene. The server should respond to the SYSTEM_READY event with an appropriate handler which instantiates the players object for example.</para>
        /// </summary>
        /// <param name="conn">The client connection which is ready.</param>
        /// <returns>True if succcessful</returns>
        public static bool Ready(NetworkConnection conn)
        {
            if (ready)
            {
                Debug.LogError("A connection has already been set as ready. There can only be one.");
                return(false);
            }

            if (LogFilter.Debug)
            {
                Debug.Log("ClientScene.Ready() called with connection [" + conn + "]");
            }
#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Outgoing, typeof(ReadyMessage), null, 1);
#endif
            if (conn != null)
            {
                conn.Send(new ReadyMessage());
                ready                   = true;
                readyConnection         = conn;
                readyConnection.isReady = true;
                return(true);
            }
            Debug.LogError("Ready() called with invalid connection object: conn=null");
            return(false);
        }
예제 #4
0
        internal static void OnLocalClientObjectDestroy(NetworkConnection _, ObjectDestroyMessage msg)
        {
            if (LogFilter.Debug)
            {
                Debug.Log("ClientScene.OnLocalObjectObjDestroy netId:" + msg.netId);
            }
#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(ObjectDestroyMessage), msg.netId.ToString(), 1);
#endif
            NetworkIdentity.spawned.Remove(msg.netId);
        }
예제 #5
0
        internal static void OnObjectSpawnStarted(NetworkConnection _, ObjectSpawnStartedMessage msg)
        {
            if (LogFilter.Debug)
            {
                Debug.Log("SpawnStarted");
            }
#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(ObjectSpawnStartedMessage), null, 1);
#endif
            PrepareToSpawnSceneObjects();
            isSpawnFinished = false;
        }
예제 #6
0
        internal static void OnSpawnSceneObject(NetworkConnection _, SpawnSceneObjectMessage msg)
        {
            if (LogFilter.Debug)
            {
                Debug.Log("Client spawn scene handler instantiating [netId:" + msg.netId + " sceneId:" + msg.sceneId + " pos:" + msg.position);
            }

            // owner?
            if (msg.owner)
            {
                OnSpawnMessageForOwner(msg.netId);
            }

            if (NetworkIdentity.spawned.TryGetValue(msg.netId, out NetworkIdentity localObject) && localObject != null)
            {
                // this object already exists (was in the scene)
                localObject.Reset();
                ApplySpawnPayload(localObject, msg.position, msg.rotation, msg.scale, msg.payload, msg.netId);
#if MIRROR_PROFILING
                NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(SpawnSceneObjectMessage), localObject.gameObject.name, 1);
#endif
                return;
            }

            NetworkIdentity spawnedId = SpawnSceneObject(msg.sceneId);
            if (spawnedId == null)
            {
                Debug.LogError("Spawn scene object not found for " + msg.sceneId.ToString("X") + " SpawnableObjects.Count=" + spawnableObjects.Count);

                // dump the whole spawnable objects dict for easier debugging
                if (LogFilter.Debug)
                {
                    foreach (KeyValuePair <ulong, NetworkIdentity> kvp in spawnableObjects)
                    {
                        Debug.Log("Spawnable: SceneId=" + kvp.Key + " name=" + kvp.Value.name);
                    }
                }

                return;
            }

            if (LogFilter.Debug)
            {
                Debug.Log("Client spawn for [netId:" + msg.netId + "] [sceneId:" + msg.sceneId + "] obj:" + spawnedId.gameObject.name);
            }
#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(SpawnSceneObjectMessage), spawnedId.gameObject.name, 1);
#endif
            spawnedId.Reset();
            spawnedId.pendingOwner = msg.owner;
            ApplySpawnPayload(spawnedId, msg.position, msg.rotation, msg.scale, msg.payload, msg.netId);
        }
예제 #7
0
        // NetworkIdentity.UNetStaticUpdate is called from UnityEngine while LLAPI network is active.
        // If we want TCP then we need to call it manually. Probably best from NetworkManager, although this means that we can't use NetworkServer/NetworkClient without a NetworkManager invoking Update anymore.
        /// <summary>
        /// virtual so that inheriting classes' LateUpdate() can call base.LateUpdate() too
        /// </summary>
        public virtual void LateUpdate()
        {
            // call it while the NetworkManager exists.
            // -> we don't only call while Client/Server.Connected, because then we would stop if disconnected and the
            //    NetworkClient wouldn't receive the last Disconnect event, result in all kinds of issues
            NetworkServer.Update();
            NetworkClient.Update();
            UpdateScene();

#if MIRROR_PROFILING
            NetworkProfiler.Tick(Time.time);
#endif
        }
예제 #8
0
        internal static void OnLocalClientObjectHide(NetworkConnection _, ObjectHideMessage msg)
        {
            if (LogFilter.Debug)
            {
                Debug.Log("ClientScene::OnLocalObjectObjHide netId:" + msg.netId);
            }
#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(ObjectHideMessage), msg.netId.ToString(), 1);
#endif
            if (NetworkIdentity.spawned.TryGetValue(msg.netId, out NetworkIdentity localObject) && localObject != null)
            {
                localObject.OnSetLocalVisibility(false);
            }
        }
예제 #9
0
        /// <summary>
        /// This virtual function allows custom network connection classes to process data send by the application before it goes to the network transport layer.
        /// </summary>
        /// <param name="channelId">Channel to send data on.</param>
        /// <param name="bytes">Data to send.</param>
        /// <returns></returns>
        public virtual bool TransportSend(int channelId, byte[] bytes)
        {
#if MIRROR_PROFILING
            NetworkProfiler.RecordTraffic(NetworkDirection.Outgoing, channelId, bytes.Length);
#endif
            if (Transport.activeTransport.ClientConnected())
            {
                return(Transport.activeTransport.ClientSend(channelId, bytes));
            }
            else if (Transport.activeTransport.ServerActive())
            {
                return(Transport.activeTransport.ServerSend(connectionId, channelId, bytes));
            }
            return(false);
        }
예제 #10
0
        internal static void OnClientAuthority(NetworkConnection _, ClientAuthorityMessage msg)
        {
            if (LogFilter.Debug)
            {
                Debug.Log("ClientScene.OnClientAuthority for netId: " + msg.netId);
            }

            if (NetworkIdentity.spawned.TryGetValue(msg.netId, out NetworkIdentity identity))
            {
#if MIRROR_PROFILING
                NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(ClientAuthorityMessage), identity.gameObject.name, 1);
#endif
                identity.HandleClientAuthority(msg.authority);
            }
        }
예제 #11
0
        ulong DirtyObjectBits()
        {
            ulong dirtyObjects = 0;

            for (int i = 0; i < syncObjects.Count; i++)
            {
                SyncObject syncObject = syncObjects[i];
                if (syncObject.IsDirty)
                {
                    dirtyObjects |= 1UL << i;
#if MIRROR_PROFILING
                    NetworkProfiler.RecordMessage(NetworkDirection.Outgoing, typeof(UpdateVarsMessage), syncObject.GetType().Name, 1);
#endif
                }
            }

            return(dirtyObjects);
        }
예제 #12
0
        internal static void OnObjectSpawnFinished(NetworkConnection _, ObjectSpawnFinishedMessage msg)
        {
            if (LogFilter.Debug)
            {
                Debug.Log("SpawnFinished");
            }
            NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(ObjectSpawnFinishedMessage), null, 1);

            // paul: Initialize the objects in the same order as they were initialized
            // in the server.   This is important if spawned objects
            // use data from scene objects
            foreach (NetworkIdentity identity in NetworkIdentity.spawned.Values.OrderBy(uv => uv.netId))
            {
                if (!identity.isClient)
                {
                    identity.OnStartClient();
                    CheckForOwner(identity);
                }
            }
            isSpawnFinished = true;
        }
예제 #13
0
        protected void SendEventInternal(Type invokeClass, string eventName, NetworkWriter writer, int channelId)
        {
            if (!NetworkServer.active)
            {
                Debug.LogWarning("SendEvent no server?");
                return;
            }

            // construct the message
            SyncEventMessage message = new SyncEventMessage
            {
                netId          = netId,
                componentIndex = ComponentIndex,
                functionHash   = GetMethodHash(invokeClass, eventName), // type+func so Inventory.RpcUse != Equipment.RpcUse
                payload        = writer.ToArraySegment()                // segment to avoid reader allocations
            };

            NetworkServer.SendToReady(netIdentity, message, channelId);
#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Outgoing, typeof(SyncEventMessage), $"{invokeClass.GetType()}.{eventName}", 1);
#endif
        }
예제 #14
0
        static void DestroyObject(uint netId)
        {
            if (LogFilter.Debug)
            {
                Debug.Log("ClientScene.OnObjDestroy netId:" + netId);
            }

            if (NetworkIdentity.spawned.TryGetValue(netId, out NetworkIdentity localObject) && localObject != null)
            {
                NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(ObjectDestroyMessage), netId.ToString(), 1);

                localObject.OnNetworkDestroy();

                if (!InvokeUnSpawnHandler(localObject.assetId, localObject.gameObject))
                {
                    // default handling
                    if (localObject.sceneId == 0)
                    {
                        Object.Destroy(localObject.gameObject);
                    }
                    else
                    {
                        // scene object.. disable it in scene instead of destroying
                        localObject.gameObject.SetActive(false);
                        spawnableObjects[localObject.sceneId] = localObject;
                    }
                }
                NetworkIdentity.spawned.Remove(netId);
                localObject.MarkForReset();
            }
            else
            {
                if (LogFilter.Debug)
                {
                    Debug.LogWarning("Did not find target for destroy message for " + netId);
                }
            }
        }
예제 #15
0
        // this is called from message handler for Owner message
        internal static void InternalAddPlayer(NetworkIdentity identity)
        {
            if (LogFilter.Debug)
            {
                Debug.LogWarning("ClientScene.InternalAddPlayer");
            }

#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(AddPlayerMessage), identity.gameObject.name, 1);
#endif

            // NOTE: It can be "normal" when changing scenes for the player to be destroyed and recreated.
            // But, the player structures are not cleaned up, we'll just replace the old player
            localPlayer = identity;
            if (readyConnection != null)
            {
                readyConnection.playerController = identity;
            }
            else
            {
                Debug.LogWarning("No ready connection found for setting player controller during InternalAddPlayer");
            }
        }
예제 #16
0
        /// <summary>
        /// Removes the player from the game.
        /// </summary>
        /// <returns>True if succcessful</returns>
        public static bool RemovePlayer()
        {
            if (LogFilter.Debug)
            {
                Debug.Log("ClientScene.RemovePlayer() called with connection [" + readyConnection + "]");
            }

            if (readyConnection.playerController != null)
            {
                readyConnection.Send(new RemovePlayerMessage());
#if MIRROR_PROFILING
                NetworkProfiler.RecordMessage(NetworkDirection.Incoming, typeof(RemovePlayerMessage), readyConnection.playerController.gameObject.name, 1);
#endif

                Object.Destroy(readyConnection.playerController.gameObject);

                readyConnection.playerController = null;
                localPlayer = null;

                return(true);
            }
            return(false);
        }
예제 #17
0
        protected void SendCommandInternal(Type invokeClass, string cmdName, NetworkWriter writer, int channelId)
        {
            // this was in Weaver before
            // NOTE: we could remove this later to allow calling Cmds on Server
            //       to avoid Wrapper functions. a lot of people requested this.
            if (!NetworkClient.active)
            {
                Debug.LogError("Command Function " + cmdName + " called on server without an active client.");
                return;
            }
            // local players can always send commands, regardless of authority, other objects must have authority.
            if (!(isLocalPlayer || hasAuthority))
            {
                Debug.LogWarning("Trying to send command for object without authority.");
                return;
            }

            if (ClientScene.readyConnection == null)
            {
                Debug.LogError("Send command attempted with no client running [client=" + connectionToServer + "].");
                return;
            }

            // construct the message
            CommandMessage message = new CommandMessage
            {
                netId          = netId,
                componentIndex = ComponentIndex,
                functionHash   = GetMethodHash(invokeClass, cmdName), // type+func so Inventory.RpcUse != Equipment.RpcUse
                payload        = writer.ToArraySegment()              // segment to avoid reader allocations
            };

            ClientScene.readyConnection.Send(message, channelId);
#if MIRROR_PROFILING
            NetworkProfiler.RecordMessage(NetworkDirection.Outgoing, typeof(CommandMessage), $"{invokeClass.GetType()}.{cmdName}", 1);
#endif
        }