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); }
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); }
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); }
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); }
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); }
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)); }
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); }
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); }
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); }
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); }
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); }
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}]"); }
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); } }
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); }
/// <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); } }
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); }
public virtual bool InvokeSyncEvent(int eventHash, NetworkReader reader) { return(RemoteCallHelper.InvokeHandlerDelegate(eventHash, MirrorInvokeType.SyncEvent, reader, this)); }
public virtual bool InvokeRPC(int rpcHash, NetworkReader reader) { return(RemoteCallHelper.InvokeHandlerDelegate(rpcHash, MirrorInvokeType.ClientRpc, reader, this)); }
public virtual bool InvokeCommand(int cmdHash, NetworkReader reader) { return(RemoteCallHelper.InvokeHandlerDelegate(cmdHash, MirrorInvokeType.Command, reader, this)); }
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); }