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 || isServer)) // Wappen: Fix for Mirror3->6 { 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); }
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); }
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; } // previously we used NetworkClient.readyConnection. // now we check .ready separately. if (!NetworkClient.ready) { // Unreliable Cmds from NetworkTransform may be generated, // or client may have been set NotReady intentionally, so // only warn if on the reliable channel. if (channelId == Channels.Reliable) { Debug.LogWarning("Send command attempted while NetworkClient is not ready.\nThis may be ignored if client intentionally set NotReady."); } 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}.{cmdName}"); 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 = (byte)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); }