private void SendClockedCall(int PeerId, ClockedRemoteCall.TypeOfCall Type, MDReliability Reliability,
                              string NodePath, string Method, MDRemoteMode Mode, params object[] Parameters)
 {
     MDLog.Trace(LOG_CAT, $"Sending clocked call {Method} on {NodePath} with parameters ({MDStatics.GetParametersAsString(Parameters)})");
     if (Reliability == MDReliability.Reliable)
     {
         if (PeerId != -1)
         {
             RpcId(PeerId, nameof(ClockedCall), GameClock.GetTick(), Type, NodePath, Method, Mode, Parameters);
         }
         else
         {
             Rpc(nameof(ClockedCall), GameClock.GetTick(), Type, NodePath, Method, Mode, Parameters);
         }
     }
     else
     {
         if (PeerId != -1)
         {
             RpcUnreliableId(PeerId, nameof(ClockedCall), GameClock.GetTick(), Type, NodePath, Method, Mode,
                             Parameters);
         }
         else
         {
             RpcUnreliable(nameof(ClockedCall), GameClock.GetTick(), Type, NodePath, Method, Mode, Parameters);
         }
     }
 }
 public ClockedRemoteCall(uint Tick, TypeOfCall Type, WeakRef Node, String Name, MDRemoteMode Mode, int SenderPeerId,
                          params object[] Parameters)
 {
     this.Tick         = Tick;
     this.Type         = Type;
     this.Node         = Node;
     this.Name         = Name;
     this.Mode         = Mode;
     this.Parameters   = Parameters;
     this.SenderPeerId = SenderPeerId;
 }
        /// <summary>
        /// Sends a clocked rset to another client
        /// </summary>
        /// <param name="PeerId">The peer to send to</param>
        /// <param name="Reliability">Reliability to send at</param>
        /// <param name="Target">The node that is the target of our rpc call</param>
        /// <param name="MemberName">The name of the member to set</param>
        /// <param name="Value">The value to set</param>
        public void SendClockedRset(int PeerId, MDReliability Reliability, Node Target, string MemberName, object Value)
        {
            if (PeerId == MDStatics.GetPeerId())
            {
                // This is to ourselves so just set
                Target.SetMemberValue(MemberName, Value);
                return;
            }
            MDRemoteMode     Mode      = MDStatics.GetMemberRpcType(Target, MemberName);
            MemberInfo       info      = MDStatics.GetMemberInfo(Target, MemberName);
            IMDDataConverter Converter = MDStatics.GetConverterForType(info.GetUnderlyingType());

            switch (Mode)
            {
            case MDRemoteMode.Master:
                if (!Target.IsNetworkMaster())
                {
                    // Remote invoke master only
                    SendClockedCall(PeerId, ClockedRemoteCall.TypeOfCall.RSET, Reliability, Target.GetPath(),
                                    MemberName, Mode, Converter.ConvertForSending(Value, true));
                }

                break;

            case MDRemoteMode.MasterSync:
                if (!Target.IsNetworkMaster())
                {
                    // Remote invoke master only
                    SendClockedCall(PeerId, ClockedRemoteCall.TypeOfCall.RSET, Reliability, Target.GetPath(),
                                    MemberName, Mode, Converter.ConvertForSending(Value, true));
                }

                Target.SetMemberValue(MemberName, Value);
                break;

            case MDRemoteMode.Puppet:
            case MDRemoteMode.Remote:
                // Remote invoke
                SendClockedCall(PeerId, ClockedRemoteCall.TypeOfCall.RSET, Reliability, Target.GetPath(),
                                MemberName, Mode, Converter.ConvertForSending(Value, true));
                break;

            case MDRemoteMode.PuppetSync:
            case MDRemoteMode.RemoteSync:
                // Remote invoke and local invoke
                SendClockedCall(PeerId, ClockedRemoteCall.TypeOfCall.RSET, Reliability, Target.GetPath(),
                                MemberName, Mode, Converter.ConvertForSending(Value, true));
                Target.SetMemberValue(MemberName, Value);
                break;
            }
        }
        private void ClockedCall(uint Tick, ClockedRemoteCall.TypeOfCall Type, string NodePath, string Method,
                                 MDRemoteMode Mode, params object[] Parameters)
        {
            Node Target = GetNodeOrNull(NodePath);

            if (Target == null)
            {
                MDLog.Warn(LOG_CAT, $"Could not find target [{NodePath}] for ClockedRpcCall.");
                return;
            }
            MDLog.Trace(LOG_CAT, $"Got clocked call {Method} on {NodePath} with parameters ({MDStatics.GetParametersAsString(Parameters)})");
            ClockedRemoteCall RemoteCall = new ClockedRemoteCall(Tick, Type, WeakRef(Target), Method, Mode, Multiplayer.GetRpcSenderId(), Parameters);

            // Check if we should already invoke this (if the time has already passed)
            if (!RemoteCall.Invoke(GameClock.GetRemoteTick()))
            {
                ClockedRemoteCallList.Add(RemoteCall);
            }
        }
        /// <summary>
        /// Sends a clocked RPC call to another client
        /// </summary>
        /// <param name="PeerId">The peer to send to</param>
        /// <param name="Reliability">Reliability to send at</param>
        /// <param name="Target">The node that is the target of our rpc call</param>
        /// <param name="Method">The method we want to invoke on the node</param>
        /// <param name="Parameters">Parameters for the call</param>
        public void SendClockedRpc(int PeerId, MDReliability Reliability, Node Target, string Method,
                                   params object[] Parameters)
        {
            if (PeerId == MDStatics.GetPeerId())
            {
                // This is to ourselves so just invoke
                RpcSenderId = PeerId;
                Target.Invoke(Method, Parameters);
                RpcSenderId = -1;
                return;
            }
            MDRemoteMode Mode         = MDStatics.GetMethodRpcType(Target, Method, Parameters);
            int          MethodNumber = MDStatics.GetMethodNumber(Target, Method, Parameters);

            if (MethodNumber == -1)
            {
                // Abort
                MDLog.Fatal(LOG_CAT, $"Could not find method number for {Target.GetType().ToString()}#{Method}({MDStatics.GetParametersAsString(Parameters)})");
                return;
            }
            MethodInfo MethodInfo = MDStatics.GetMethodInfo(Target, MethodNumber);

            object[] SendingParams = MDStatics.ConvertParametersForSending(MethodInfo, Parameters);
            switch (Mode)
            {
            case MDRemoteMode.Master:
                if (!Target.IsNetworkMaster())
                {
                    // Remote invoke master only
                    SendClockedCall(PeerId, ClockedRemoteCall.TypeOfCall.RPC, Reliability, Target.GetPath(), MethodNumber.ToString(),
                                    Mode, SendingParams);
                }

                break;

            case MDRemoteMode.MasterSync:
                if (!Target.IsNetworkMaster())
                {
                    // Remote invoke master only
                    SendClockedCall(PeerId, ClockedRemoteCall.TypeOfCall.RPC, Reliability, Target.GetPath(), MethodNumber.ToString(),
                                    Mode, SendingParams);
                }

                Target.Invoke(Method, Parameters);
                break;

            case MDRemoteMode.Puppet:
            case MDRemoteMode.Remote:
                // Remote invoke
                SendClockedCall(PeerId, ClockedRemoteCall.TypeOfCall.RPC, Reliability, Target.GetPath(), MethodNumber.ToString(),
                                Mode, SendingParams);
                break;

            case MDRemoteMode.PuppetSync:
            case MDRemoteMode.RemoteSync:
                // Remote invoke and local invoke
                SendClockedCall(PeerId, ClockedRemoteCall.TypeOfCall.RPC, Reliability, Target.GetPath(), MethodNumber.ToString(),
                                Mode, SendingParams);
                Target.Invoke(Method, Parameters);
                break;
            }
        }