Exemplo n.º 1
0
        protected void SendServerRpcInternal(Type invokeClass, string cmdName, NetworkWriter writer, int channelId, bool requireAuthority = true)
        {
            // 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 (!Client.Active)
            {
                throw new InvalidOperationException($"ServerRpc Function {cmdName} called on server without an active client.");
            }

            // local players can always send ServerRpcs, regardless of authority, other objects must have authority.
            if (requireAuthority && !(IsLocalPlayer || HasAuthority))
            {
                throw new UnauthorizedAccessException($"Trying to send ServerRpc for object without authority. {invokeClass.ToString()}.{cmdName}");
            }

            if (Client.Connection == null)
            {
                throw new InvalidOperationException("Send ServerRpc attempted with no client running [client=" + ConnectionToServer + "].");
            }

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

            _ = Client.SendAsync(message, channelId);
        }
Exemplo n.º 2
0
        protected internal void SendRpcInternal(Type invokeClass, string rpcName, NetworkWriter writer, int channelId, bool excludeOwner)
        {
            // this was in Weaver before
            if (Server == null || !Server.Active)
            {
                throw new InvalidOperationException($"RPC Function {rpcName} called when server is not active.");
            }
            // This cannot use Server.active, as that is not specific to this object.
            if (!IsServer)
            {
                if (logger.WarnEnabled())
                {
                    logger.LogWarning("ClientRpc " + rpcName + " called on un-spawned object: " + name);
                }
                return;
            }

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

            // The public facing parameter is excludeOwner in [ClientRpc]
            // so we negate it here to logically align with SendToReady.
            bool includeOwner = !excludeOwner;

            NetIdentity.SendToRemoteObservers(message, includeOwner, channelId);
        }
Exemplo n.º 3
0
        protected void SendRPCInternal(Type invokeClass, string rpcName, NetworkWriter writer, int channelId, bool includeOwner)
        {
            // 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 = (byte)ComponentIndex,
                // type+func so Inventory.RpcUse != Equipment.RpcUse
                functionHash = RemoteCallHelper.GetMethodHash(invokeClass, rpcName),
                // segment to avoid reader allocations
                payload = writer.ToArraySegment()
            };

            NetworkServer.SendToReadyObservers(netIdentity, message, includeOwner, channelId);
        }
Exemplo n.º 4
0
        protected void SendCommandInternal(Type invokeClass, string cmdName, NetworkWriter writer, int channelId, bool ignoreAuthority = false)
        {
            // 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)
            {
                logger.LogError($"Command Function {cmdName} called without an active client.");
                return;
            }
            // local players can always send commands, regardless of authority, other objects must have authority.
            if (!(ignoreAuthority || isLocalPlayer || hasAuthority))
            {
                logger.LogWarning($"Trying to send command for object without authority. {invokeClass.ToString()}.{cmdName}");
                return;
            }

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

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

            ClientScene.readyConnection.Send(message, channelId);
        }
Exemplo n.º 5
0
        protected void SendRPCInternal(Type invokeClass, string rpcName, NetworkWriter writer, int channelId, bool excludeOwner)
        {
            // this was in Weaver before
            if (!NetworkServer.active)
            {
                logger.LogError("RPC Function " + rpcName + " called on Client.");
                return;
            }
            // This cannot use NetworkServer.active, as that is not specific to this object.
            if (!isServer)
            {
                logger.LogWarning("ClientRpc " + rpcName + " called on un-spawned object: " + name);
                return;
            }

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

            // The public facing parameter is excludeOwner in [ClientRpc]
            // so we negate it here to logically align with SendToReady.
            bool includeOwner = !excludeOwner;

            NetworkServer.SendToReady(netIdentity, message, includeOwner, channelId);
        }
Exemplo n.º 6
0
 static PlayerManager()
 {
     RemoteCallHelper.RegisterCommandDelegate(typeof(PlayerManager), "CmdDealCards", new CmdDelegate(PlayerManager.InvokeUserCode_CmdDealCards), false);
     RemoteCallHelper.RegisterCommandDelegate(typeof(PlayerManager), "CmdTapCard", new CmdDelegate(PlayerManager.InvokeUserCode_CmdTapCard), false);
     RemoteCallHelper.RegisterCommandDelegate(typeof(PlayerManager), "CmdPlayCard", new CmdDelegate(PlayerManager.InvokeUserCode_CmdPlayCard), false);
     RemoteCallHelper.RegisterRpcDelegate(typeof(PlayerManager), "RpcShowCard", new CmdDelegate(PlayerManager.InvokeUserCode_RpcShowCard));
 }
Exemplo n.º 7
0
        public void GetDelegate()
        {
            // registerdelegate is protected, but we can use
            // RegisterCommandDelegate which calls RegisterDelegate
            int registeredHash = RemoteCallHelper.RegisterDelegate(
                typeof(NetworkBehaviourDelegateComponent),
                nameof(NetworkBehaviourDelegateComponent.Delegate),
                MirrorInvokeType.Command,
                NetworkBehaviourDelegateComponent.Delegate,
                false);

            // get handler
            int         cmdHash  = RemoteCallHelper.GetMethodHash(typeof(NetworkBehaviourDelegateComponent), nameof(NetworkBehaviourDelegateComponent.Delegate));
            CmdDelegate func     = RemoteCallHelper.GetDelegate(cmdHash);
            CmdDelegate expected = NetworkBehaviourDelegateComponent.Delegate;

            Assert.That(func, Is.EqualTo(expected));

            // invalid hash should return null handler
            CmdDelegate funcNull = RemoteCallHelper.GetDelegate(1234);

            Assert.That(funcNull, Is.Null);

            // clean up
            RemoteCallHelper.RemoveDelegate(registeredHash);
        }
Exemplo n.º 8
0
        public void RegisterDelegateDoesntOverwrite()
        {
            // registerdelegate is protected, but we can use
            // RegisterCommandDelegate which calls RegisterDelegate
            int registeredHash1 = RemoteCallHelper.RegisterDelegate(
                typeof(NetworkBehaviourDelegateComponent),
                nameof(NetworkBehaviourDelegateComponent.Delegate),
                MirrorInvokeType.Command,
                NetworkBehaviourDelegateComponent.Delegate,
                false);

            // registering the exact same one should be fine. it should simply
            // do nothing.
            int registeredHash2 = RemoteCallHelper.RegisterDelegate(
                typeof(NetworkBehaviourDelegateComponent),
                nameof(NetworkBehaviourDelegateComponent.Delegate),
                MirrorInvokeType.Command,
                NetworkBehaviourDelegateComponent.Delegate,
                false);

            // registering the same name with a different callback shouldn't
            // work
            LogAssert.Expect(LogType.Error, $"Function {typeof(NetworkBehaviourDelegateComponent)}.{nameof(NetworkBehaviourDelegateComponent.Delegate)} and {typeof(NetworkBehaviourDelegateComponent)}.{nameof(NetworkBehaviourDelegateComponent.Delegate2)} have the same hash.  Please rename one of them");
            int registeredHash3 = RemoteCallHelper.RegisterDelegate(
                typeof(NetworkBehaviourDelegateComponent),
                nameof(NetworkBehaviourDelegateComponent.Delegate),
                MirrorInvokeType.Command,
                NetworkBehaviourDelegateComponent.Delegate2,
                false);

            // clean up
            RemoteCallHelper.RemoveDelegate(registeredHash1);
            RemoteCallHelper.RemoveDelegate(registeredHash2);
            RemoteCallHelper.RemoveDelegate(registeredHash3);
        }
Exemplo n.º 9
0
        protected void SendTargetRPCInternal(INetworkConnection conn, Type invokeClass, string rpcName, NetworkWriter writer, int channelId)
        {
            // this was in Weaver before
            if (!Server || !Server.Active)
            {
                throw new InvalidOperationException("RPC Function " + rpcName + " called on client.");
            }

            // connection parameter is optional. assign if null.
            if (conn == null)
            {
                conn = ConnectionToClient;
            }

            // This cannot use Server.active, as that is not specific to this object.
            if (!IsServer)
            {
                logger.LogWarning("ClientRpc " + rpcName + " called on un-spawned object: " + name);
                return;
            }

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

            conn.Send(message, channelId);
        }
Exemplo n.º 10
0
        protected void SendCommandInternal(Type invokeClass, string cmdName, NetworkWriter writer, int channelId, bool requiresAuthority = true)
        {
            // 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 without an active client.");
                return;
            }

            // local players can always send commands, regardless of authority, other objects must have authority.
            if (!(!requiresAuthority || isLocalPlayer || hasAuthority))
            {
                Debug.LogWarning($"Trying to send command for object without authority. {invokeClass.ToString()}.{cmdName}");
                return;
            }

            // previously we used NetworkClient.readyConnection.
            // now we check .ready separately and use .connection instead.
            if (!NetworkClient.ready)
            {
                Debug.LogError("Send command attempted while NetworkClient is not ready.");
                return;
            }

            // IMPORTANT: can't use .connectionToServer here because calling
            // a command on other objects is allowed if requireAuthority is
            // false. other objects don't have a .connectionToServer.
            // => so we always need to use NetworkClient.connection instead.
            // => see also: https://github.com/vis2k/Mirror/issues/2629
            if (NetworkClient.connection == null)
            {
                Debug.LogError("Send command attempted with no client running.");
                return;
            }

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

            // IMPORTANT: can't use .connectionToServer here because calling
            // a command on other objects is allowed if requireAuthority is
            // false. other objects don't have a .connectionToServer.
            // => so we always need to use NetworkClient.connection instead.
            // => see also: https://github.com/vis2k/Mirror/issues/2629
            NetworkClient.connection.Send(message, channelId);
        }
Exemplo n.º 11
0
        protected void SendTargetRPCInternal(NetworkConnection conn, Type invokeClass, string rpcName, NetworkWriter writer, int channelId)
        {
            if (!NetworkServer.active)
            {
                Debug.LogError($"TargetRPC {rpcName} called when server not active");
                return;
            }

            if (!isServer)
            {
                Debug.LogWarning($"TargetRpc {rpcName} called on {name} but that object has not been spawned or has been unspawned");
                return;
            }

            // connection parameter is optional. assign if null.
            if (conn is null)
            {
                conn = connectionToClient;
            }

            // if still null
            if (conn is null)
            {
                Debug.LogError($"TargetRPC {rpcName} was given a null connection, make sure the object has an owner or you pass in the target connection");
                return;
            }

            if (!(conn is NetworkConnectionToClient))
            {
                Debug.LogError($"TargetRPC {rpcName} requires a NetworkConnectionToClient but was given {conn.GetType().Name}");
                return;
            }

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

            conn.Send(message, channelId);
        }
Exemplo n.º 12
0
        protected internal void SendServerRpcInternal(Type invokeClass, string cmdName, NetworkWriter writer, int channelId, bool requireAuthority = true)
        {
            ValidateServerRpc(invokeClass, cmdName, requireAuthority);

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

            Client.Send(message, channelId);
        }
        public static void Postfix(int cmdHash)
        {
            if (!DevTools.Instance.Config.LoggingNetworkMethods)
            {
                return;
            }
            if (!RemoteCallHelper.GetInvokerForHash(cmdHash, MirrorInvokeType.Command, out Invoker invoker))
            {
                return;
            }
            var methodName = invoker.invokeFunction.GetMethodName().Substring(15);

            if (DevTools.Instance.Config.DisabledLoggingNetworkMethods.Contains(methodName))
            {
                return;
            }
            Log.Debug($"[Receiving: {methodName}]");
        }
Exemplo n.º 14
0
        internal void OnRpcMessage(RpcMessage msg)
        {
            if (logger.LogEnabled())
            {
                logger.Log("ClientScene.OnRPCMessage hash:" + msg.functionHash + " netId:" + msg.netId);
            }

            Skeleton skeleton = RemoteCallHelper.GetSkeleton(msg.functionHash);

            if (skeleton.invokeType != MirrorInvokeType.ClientRpc)
            {
                throw new MethodInvocationException($"Invalid RPC call with id {msg.functionHash}");
            }
            if (client.Spawned.TryGetValue(msg.netId, out NetworkIdentity identity) && identity != null)
            {
                using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(msg.payload))
                    identity.HandleRemoteCall(skeleton, msg.componentIndex, networkReader);
            }
        }
Exemplo n.º 15
0
        protected void SendEventInternal(Type invokeClass, string eventName, NetworkWriter writer, int channelId)
        {
            if (!NetworkServer.active)
            {
                logger.LogWarning("SendEvent no server?");
                return;
            }

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

            NetworkServer.SendToReady(netIdentity, message, channelId);
        }
Exemplo n.º 16
0
        /// <summary>
        /// Handle ServerRpc from specific player, this could be one of multiple players on a single client
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="msg"></param>
        void OnServerRpcMessage(INetworkConnection conn, ServerRpcMessage msg)
        {
            if (!SpawnedObjects.TryGetValue(msg.netId, out NetworkIdentity identity) || identity is null)
            {
                if (logger.WarnEnabled())
                {
                    logger.LogWarning("Spawned object not found when handling ServerRpc message [netId=" + msg.netId + "]");
                }
                return;
            }
            Skeleton skeleton = RemoteCallHelper.GetSkeleton(msg.functionHash);

            if (skeleton.invokeType != MirageInvokeType.ServerRpc)
            {
                throw new MethodInvocationException($"Invalid ServerRpc for id {msg.functionHash}");
            }

            // ServerRpcs can be for player objects, OR other objects with client-authority
            // -> so if this connection's controller has a different netId then
            //    only allow the ServerRpc if clientAuthorityOwner
            if (skeleton.cmdRequireAuthority && identity.ConnectionToClient != conn)
            {
                if (logger.WarnEnabled())
                {
                    logger.LogWarning("ServerRpc for object without authority [netId=" + msg.netId + "]");
                }
                return;
            }

            if (logger.LogEnabled())
            {
                logger.Log("OnServerRpcMessage for netId=" + msg.netId + " conn=" + conn);
            }

            using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(msg.payload))
            {
                networkReader.ObjectLocator = this;
                identity.HandleRemoteCall(skeleton, msg.componentIndex, networkReader, conn, msg.replyId);
            }
        }
Exemplo n.º 17
0
        protected internal UniTask <T> SendServerRpcWithReturn <T>(Type invokeClass, string cmdName, NetworkWriter writer, int channelId, bool requireAuthority = true)
        {
            ValidateServerRpc(invokeClass, cmdName, requireAuthority);

            (UniTask <T> task, int id) = ClientObjectManager.CreateReplyTask <T>();

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

            Client.Send(message, channelId);

            return(task);
        }
        public void HandleRpc()
        {
            CreateNetworked(out GameObject _, out NetworkIdentity identity, out RpcTestNetworkBehaviour comp0);

            // register the command delegate, otherwise it's not found
            int registeredHash = RemoteCallHelper.RegisterDelegate(typeof(RpcTestNetworkBehaviour),
                                                                   nameof(RpcTestNetworkBehaviour.RpcGenerated),
                                                                   MirrorInvokeType.ClientRpc,
                                                                   RpcTestNetworkBehaviour.RpcGenerated);

            // identity needs to be in spawned dict, otherwise command handler
            // won't find it
            NetworkIdentity.spawned[identity.netId] = identity;

            // call HandleRpc and check if the rpc was called in the component
            int           functionHash = RemoteCallHelper.GetMethodHash(typeof(RpcTestNetworkBehaviour), nameof(RpcTestNetworkBehaviour.RpcGenerated));
            NetworkReader payload      = new NetworkReader(new byte[0]);

            identity.HandleRemoteCall(0, functionHash, MirrorInvokeType.ClientRpc, payload);
            Assert.That(comp0.called, Is.EqualTo(1));

            // try wrong component index. rpc shouldn't be called again.
            // warning is expected
            LogAssert.ignoreFailingMessages = true;
            identity.HandleRemoteCall(1, functionHash, MirrorInvokeType.ClientRpc, payload);
            LogAssert.ignoreFailingMessages = false;
            Assert.That(comp0.called, Is.EqualTo(1));

            // try wrong function hash. rpc shouldn't be called again.
            // warning is expected
            LogAssert.ignoreFailingMessages = true;
            identity.HandleRemoteCall(0, functionHash + 1, MirrorInvokeType.ClientRpc, payload);
            LogAssert.ignoreFailingMessages = false;
            Assert.That(comp0.called, Is.EqualTo(1));

            // clean up
            RemoteCallHelper.RemoveDelegate(registeredHash);
        }
Exemplo n.º 19
0
 public virtual bool InvokeSyncEvent(int eventHash, NetworkReader reader)
 {
     return(RemoteCallHelper.InvokeHandlerDelegate(eventHash, MirrorInvokeType.SyncEvent, reader, this));
 }
Exemplo n.º 20
0
 public virtual bool InvokeRPC(int rpcHash, NetworkReader reader)
 {
     return(RemoteCallHelper.InvokeHandlerDelegate(rpcHash, MirrorInvokeType.ClientRpc, reader, this));
 }
Exemplo n.º 21
0
 public virtual bool InvokeCommand(int cmdHash, NetworkReader reader)
 {
     return(RemoteCallHelper.InvokeHandlerDelegate(cmdHash, MirrorInvokeType.Command, reader, this));
 }
Exemplo n.º 22
0
        public void CommandMessageCallsCommandTest()
        {
            // listen
            NetworkServer.Listen(1);
            Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));

            // add connection
            LocalConnectionToClient connection = new LocalConnectionToClient();

            connection.connectionToServer = new LocalConnectionToServer();
            NetworkServer.AddConnection(connection);

            // set as authenticated, otherwise removeplayer is rejected
            connection.isAuthenticated = true;

            // add an identity with two networkbehaviour components
            GameObject      go       = new GameObject();
            NetworkIdentity identity = go.AddComponent <NetworkIdentity>();

            identity.netId = 42;
            // for authority check
            identity.connectionToClient = connection;
            CommandTestNetworkBehaviour comp0 = go.AddComponent <CommandTestNetworkBehaviour>();

            Assert.That(comp0.called, Is.EqualTo(0));
            CommandTestNetworkBehaviour comp1 = go.AddComponent <CommandTestNetworkBehaviour>();

            Assert.That(comp1.called, Is.EqualTo(0));
            connection.identity = identity;

            // register the command delegate, otherwise it's not found
            int registeredHash = RemoteCallHelper.RegisterDelegate(typeof(CommandTestNetworkBehaviour),
                                                                   nameof(CommandTestNetworkBehaviour.CommandGenerated),
                                                                   MirrorInvokeType.Command,
                                                                   CommandTestNetworkBehaviour.CommandGenerated,
                                                                   true);

            // identity needs to be in spawned dict, otherwise command handler
            // won't find it
            NetworkIdentity.spawned[identity.netId] = identity;

            // serialize a removeplayer message into an arraysegment
            CommandMessage message = new CommandMessage
            {
                componentIndex = 0,
                functionHash   = RemoteCallHelper.GetMethodHash(typeof(CommandTestNetworkBehaviour), nameof(CommandTestNetworkBehaviour.CommandGenerated)),
                netId          = identity.netId,
                payload        = new ArraySegment <byte>(new byte[0])
            };
            NetworkWriter writer = new NetworkWriter();

            MessagePacking.Pack(message, writer);
            ArraySegment <byte> segment = writer.ToArraySegment();

            // call transport.OnDataReceived with the message
            // -> calls NetworkServer.OnRemovePlayerMessage
            //    -> destroys conn.identity and sets it to null
            Transport.activeTransport.OnServerDataReceived.Invoke(0, segment, 0);

            // was the command called in the first component, not in the second one?
            Assert.That(comp0.called, Is.EqualTo(1));
            Assert.That(comp1.called, Is.EqualTo(0));

            //  send another command for the second component
            comp0.called           = 0;
            message.componentIndex = 1;
            writer = new NetworkWriter();
            MessagePacking.Pack(message, writer);
            segment = writer.ToArraySegment();
            Transport.activeTransport.OnServerDataReceived.Invoke(0, segment, 0);

            // was the command called in the second component, not in the first one?
            Assert.That(comp0.called, Is.EqualTo(0));
            Assert.That(comp1.called, Is.EqualTo(1));

            // sending a command without authority should fail
            // (= if connectionToClient is not what we received the data on)
            // set wrong authority
            identity.connectionToClient = new LocalConnectionToClient();
            comp0.called = 0;
            comp1.called = 0;
            Transport.activeTransport.OnServerDataReceived.Invoke(0, segment, 0);
            Assert.That(comp0.called, Is.EqualTo(0));
            Assert.That(comp1.called, Is.EqualTo(0));
            // restore authority
            identity.connectionToClient = connection;

            // sending a component with wrong netId should fail
            // wrong netid
            message.netId += 1;
            writer         = new NetworkWriter();
            // need to serialize the message again with wrong netid
            MessagePacking.Pack(message, writer);
            ArraySegment <byte> segmentWrongNetId = writer.ToArraySegment();

            comp0.called = 0;
            comp1.called = 0;
            Transport.activeTransport.OnServerDataReceived.Invoke(0, segmentWrongNetId, 0);
            Assert.That(comp0.called, Is.EqualTo(0));
            Assert.That(comp1.called, Is.EqualTo(0));

            // clean up
            NetworkIdentity.spawned.Clear();
            RemoteCallHelper.RemoveDelegate(registeredHash);
            NetworkServer.Shutdown();
            // destroy the test gameobject AFTER server was stopped.
            // otherwise isServer is true in OnDestroy, which means it would try
            // to call Destroy(go). but we need to use DestroyImmediate in
            // Editor
            GameObject.DestroyImmediate(go);
        }