/// <summary>
        /// Called when a node is removed
        /// </summary>
        /// <param name="RemovedNode">The removed node</param>
        public void OnNodeRemoved(Node RemovedNode)
        {
            if (MDStatics.IsNetworkActive() == false)
            {
                return;
            }

            bool WasNetworked = NetworkedTypes.Remove(RemovedNode);

            if (WasNetworked == false)
            {
                WasNetworked = NetworkedScenes.Remove(RemovedNode);
            }

            if (WasNetworked == false)
            {
                return;
            }

            OrderedNetworkedNodes.Remove(RemovedNode);

            string NodePath = RemovedNode.GetPath();

            OnNetworkNodeRemoved(RemovedNode);
            if (this.IsMaster() && MDStatics.IsNetworkActive())
            {
                Rpc(nameof(RemoveAndFreeNode), NodePath);
            }
        }
        /// <summary>
        /// Spawn a network node
        /// </summary>
        /// <param name="ScenePath">The path to the scene to spawn</param>
        /// <param name="Parent">The parent that the new instance will be a child of</param>
        /// <param name="NodeName">The name of the new node</param>
        /// <param name="UseRandomName">If set to true a random number will be added at the end of the node name</param>
        /// <param name="NetworkMaster">The peer that should own this, default is server</param>
        /// <param name="SpawnPos">Where the spawn this node</param>
        /// <returns>The new node</returns>
        public Node SpawnNetworkedNode(string ScenePath, Node Parent, string NodeName, bool UseRandomName = true,
                                       int NetworkMaster = -1, Vector3?SpawnPos = null)
        {
            if (this.IsMaster() == false)
            {
                MDLog.Error(LOG_CAT, "Only server can spawn networked nodes");
                return(null);
            }

            if (!Parent.IsInsideTree())
            {
                MDLog.Error(LOG_CAT, $"Parent [{Parent.Name}] is not inside the tree");
                return(null);
            }

            NodeName = BuildNodeName(NodeName, UseRandomName);

            int    NodeMaster = NetworkMaster != -1 ? NetworkMaster : MDStatics.GetPeerId();
            string ParentPath = Parent.GetPath();

            Vector3 SpawnPosVal = SpawnPos.GetValueOrDefault();

            if (MDStatics.IsNetworkActive())
            {
                Rpc(nameof(SpawnNodeScene), ScenePath, ParentPath, NodeName, NodeMaster, SpawnPosVal);
            }

            return(SpawnNodeScene(ScenePath, ParentPath, NodeName, NodeMaster, SpawnPosVal));
        }
Beispiel #3
0
 /// <summary>
 /// Adds some basic information on creation, can be toggled in config
 /// </summary>
 public void AddBasicInfo()
 {
     AddOnScreenDebugInfo("FPS", () => Engine.GetFramesPerSecond().ToString(CultureInfo.InvariantCulture), Colors.Red);
     AddOnScreenDebugInfo("Static Memory", () => MDStatics.HumanReadableSize(OS.GetStaticMemoryUsage()),
                          Colors.Red);
     AddOnScreenDebugInfo("Network Active: ", () => MDStatics.IsNetworkActive().ToString(), Colors.Red);
     AddOnScreenDebugInfo("PeerId: ", () => MDStatics.GetPeerId().ToString(), Colors.Red);
 }
        /// <summary>
        /// Same as RsetUnreliable except it checks if the network is activate first
        /// </summary>
        /// <param name="PeerId">The peer to send to</param>
        /// <param name="Property">The property to set</param>
        /// <param name="Value">The value</param>
        public static void MDRsetUnreliableId(this Node Instance, int PeerId, string Property, object Value)
        {
            if (!MDStatics.IsNetworkActive() && !MDStatics.IsServer())
            {
                return;
            }

            MDStatics.GetReplicator().SendClockedRset(PeerId, MDReliability.Unreliable, Instance, Property, Value);
        }
Beispiel #5
0
        /// <summary>
        /// Same as Rset except it checks if the network is activate first
        /// </summary>
        /// <param name="Property">The property to set</param>
        /// <param name="Value">The value</param>
        public static void MDRset(this Node Instance, string Property, object Value)
        {
            if (!MDStatics.IsNetworkActive())
            {
                return;
            }

            MDStatics.GetReplicator().SendClockedRset(-1, MDReliability.Reliable, Instance, Property, Value);
        }
        /// <summary>
        /// Same as RpcUnreliableId except it checks if the network is activate first and takes game clock into account
        /// </summary>
        /// <param name="PeerId">The id of the peer to send to</param>
        /// <param name="Method">The method to call</param>
        /// <param name="Args">Arguments</param>
        public static void MDRpcUnreliableId(this Node Instance, int PeerId, string Method, params object[] Args)
        {
            if (!MDStatics.IsNetworkActive() && !MDStatics.IsServer())
            {
                return;
            }

            // Send through replicator
            MDStatics.GetReplicator().SendClockedRpc(PeerId, MDReliability.Unreliable, Instance, Method, Args);
        }
        //
        /// <summary>
        /// Same as Rpc except it checks if the network is activate first and takes game clock 1o account
        /// </summary>
        /// <param name="Method">The method to call</param>
        /// <param name="Args">Arguments</param>
        public static void MDRpc(this Node Instance, string Method, params object[] Args)
        {
            if (!MDStatics.IsNetworkActive())
            {
                return;
            }

            // Send through replicator
            MDStatics.GetReplicator().SendClockedRpc(-1, MDReliability.Reliable, Instance, Method, Args);
        }
Beispiel #8
0
        /// <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() || (MDStatics.IsServer() && !MDStatics.IsNetworkActive() && PeerId == MDStatics.GetServerId()))
            {
                // 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;
            }
        }
Beispiel #9
0
        // Broadcasts out replicated modified variables if we're the server, propagates changes recieved from the server if client.
        private void TickReplication()
        {
            bool paused = GetTree().Paused;

#if DEBUG
            using (MDProfiler Profiler = new MDProfiler("MDReplicator.TickReplication"))
#endif
            {
                if (MDStatics.IsNetworkActive() == false)
                {
                    return;
                }

                // First process any outstanding clocked calls
                CheckClockedRemoteCalls();

                int JIPPeerId = CheckForNewPlayer();

                HashSet <MDReplicatedMember> CurrentReplicationList = GroupManager.GetMembersToReplicate();

                for (int i = NodeList.Count - 1; i >= 0; --i)
                {
                    ReplicatedNode RepNode  = NodeList[i];
                    Node           Instance = RepNode.Instance.GetRef() as Node;
                    if (!IsInstanceValid(Instance))
                    {
                        NodeList.RemoveAt(i);
                    }
                    else
                    {
                        RepNode.CheckForNetworkChanges(Instance.GetNetworkMaster());

                        foreach (MDReplicatedMember RepMember in RepNode.Members)
                        {
                            RepMember.CheckForValueUpdate();
                            if (!RepMember.ShouldReplicate() ||
                                paused && !RepMember.ProcessWhilePaused ||
                                RepMember.GetReplicatedType() == MDReplicatedType.JoinInProgress && JIPPeerId == -1)
                            {
                                continue;
                            }

                            MDLog.CTrace(JIPPeerId != -1, LOG_CAT, $"Replicating {RepMember.GetUniqueKey()} to JIP Player {JIPPeerId}");
                            MDLog.CTrace(JIPPeerId == -1, LOG_CAT, $"Replicating {RepMember.GetUniqueKey()}");
                            RepMember.Replicate(JIPPeerId, CurrentReplicationList.Contains(RepMember));
                        }
                    }
                }
            }
        }
Beispiel #10
0
        /// <summary>
        /// Notifies all clients that a new player has initialized
        /// </summary>
        /// <param name="Joiner">The PeerID of the joining client</param>
        protected void BroadcastNewPlayerInitialized(int Joiner)
        {
            if (MDStatics.IsNetworkActive())
            {
                foreach (int PeerId in Players.Keys)
                {
                    if (PeerId == SERVER_ID)
                    {
                        continue;
                    }

                    MDLog.Debug(LOG_CAT, $"Notifying Peer [{PeerId}] that Peer [{Joiner}] has initialized");
                    RpcId(PeerId, nameof(OnPlayerInitialized), Joiner);
                }
            }
        }
        /// <summary>
        /// Change the network master of a node. This only works on the server.
        /// </summary>
        /// <param name="Node">The node to change network master of</param>
        /// <param name="NewNetworkMaster">The new network master</param>
        /// <returns>True if network master was changed, false if not</returns>
        public bool ChangeNetworkMaster(Node Node, int NewNetworkMaster)
        {
            if (!GetAllPeerIds().Contains(NewNetworkMaster))
            {
                // Invalid network master
                return(false);
            }

            // Only server can change network master
            if (MDStatics.IsNetworkActive() && MDStatics.IsServer())
            {
                Node.SetNetworkMaster(NewNetworkMaster);
                Rpc(nameof(ChangeNetworkMasterOnClients), Node.GetPath(), NewNetworkMaster);
                return(true);
            }

            return(false);
        }
        /// <summary>
        /// Called when the ping timer times out, sends a ping request to the given client.
        /// </summary>
        public void OnPingTimerTimeout()
        {
            // Check if network is still active
            if (!MDStatics.IsNetworkActive())
            {
                MDLog.Trace(LOG_CAT, $"Network is no longer active");
                return;
            }

            // Send ping request
            if (GameSynchronizer.GameClock == null)
            {
                GameSynchronizer.RpcId(PeerId, MDGameSynchronizer.METHOD_REQUEST_PING, OS.GetTicksMsec());
            }
            else
            {
                int  maxPlayerPing = GameSynchronizer.GetMaxPlayerPing() + (int)Ping;
                uint estimate      = GameSynchronizer.GetPlayerTicksMsec(PeerId) + (uint)Ping;
                GameSynchronizer.RpcId(PeerId, MDGameSynchronizer.METHOD_REQUEST_PING, OS.GetTicksMsec(), estimate,
                                       GameSynchronizer.GameClock.GetTickAtTimeOffset(Ping), maxPlayerPing);
            }
        }
        /// <summary>
        /// Spawn a network node
        /// </summary>
        /// <param name="NodeType">The type of node to spawn</param>
        /// <param name="Parent">The parent that the new instance will be a child of</param>
        /// <param name="NodeName">The name of the new node</param>
        /// <param name="UseRandomName">If set to true a random number will be added at the end of the node name</param>
        /// <param name="NetworkMaster">The peer that should own this, default is server</param>
        /// <param name="SpawnPos">Where the spawn this node</param>
        /// <returns>The new node</returns>
        public Node SpawnNetworkedNode(Type NodeType, Node Parent, string NodeName, bool UseRandomName = true,
                                       int NetworkMaster = -1, Vector3?SpawnPos = null)
        {
            if (this.IsMaster() == false)
            {
                MDLog.Error(LOG_CAT, "Only server can spawn networked nodes");
                return(null);
            }

            if (!MDStatics.IsSameOrSubclass(NodeType, typeof(Node)))
            {
                MDLog.Error(LOG_CAT, $"Provided type [{NodeType.Name}] is not a subclass of Node");
                return(null);
            }

            if (!Parent.IsInsideTree())
            {
                MDLog.Error(LOG_CAT, $"Parent [{Parent.Name}] is not inside the tree");
                return(null);
            }

            NodeName = BuildNodeName(NodeName, UseRandomName);

            int    NodeMaster     = NetworkMaster != -1 ? NetworkMaster : MDStatics.GetPeerId();
            string NodeTypeString = NodeType.AssemblyQualifiedName;
            string ParentPath     = Parent.GetPath();

            Vector3 SpawnPosVal = SpawnPos.GetValueOrDefault();

            if (MDStatics.IsNetworkActive())
            {
                Rpc(nameof(SpawnNodeType), NodeTypeString, ParentPath, NodeName, NodeMaster, SpawnPosVal);
            }

            return(SpawnNodeType(NodeTypeString, ParentPath, NodeName, NodeMaster, SpawnPosVal));
        }
Beispiel #14
0
        /// <summary>
        /// Registers the given instance's fields marked with [MDReplicated()]
        /// </summary>
        /// <param name="Instance">The node to register</param>
        public void RegisterReplication(Node Instance)
        {
            List <MemberInfo>         Members     = MDStatics.GetTypeMemberInfos(Instance);
            List <MDReplicatedMember> NodeMembers = new List <MDReplicatedMember>();

            foreach (MemberInfo Member in Members)
            {
                MDReplicated RepAttribute = Member.GetCustomAttribute(typeof(MDReplicated)) as MDReplicated;
                if (RepAttribute == null)
                {
                    continue;
                }

                MDReplicatedSetting[] Settings   = GetSettings(Member);
                MDReplicatedMember    NodeMember = CreateReplicatedMember(Member, RepAttribute, Instance, Settings);

                NodeMembers.Add(NodeMember);

                ProcessSettingsForMember(NodeMember, ParseParameters(typeof(Settings), Settings));

                GroupManager.AddReplicatedMember(NodeMember);

                MDLog.Debug(LOG_CAT, $"Adding Replicated Node {Instance.Name} Member {Member.Name}");

                if (HasRPCModeSet(Member) == false)
                {
                    Instance.RsetConfig(Member.Name, MultiplayerAPI.RPCMode.Puppet);
                }
            }

            if (NodeMembers.Count > 0)
            {
                NodeList.Add(new ReplicatedNode(Instance, NodeMembers));
                List <object> networkIdUpdates = new List <object>();
                foreach (MDReplicatedMember member in NodeMembers)
                {
                    string MemberUniqueKey = member.GetUniqueKey();
                    KeyToMemberMap.Add(MemberUniqueKey, member);

                    // Check if we have a buffer waiting for this member
                    if (NetworkIdKeyMap.ContainsKey(MemberUniqueKey))
                    {
                        NetworkIdKeyMap.CheckBuffer(NetworkIdKeyMap.GetValue(MemberUniqueKey), member);
                    }

                    if (MDStatics.IsServer())
                    {
                        if (!NetworkIdKeyMap.ContainsKey(member.GetUniqueKey()))
                        {
                            uint networkid = GetReplicationId();
                            MDLog.Trace(LOG_CAT, $"Adding NetworkIdKeyMap key [{member.GetUniqueKey()}] with id [{networkid}]");
                            NetworkIdKeyMap.AddNetworkKeyIdPair(networkid, member.GetUniqueKey());
                            NetworkIdKeyMap.CheckBuffer(networkid, member);
                            networkIdUpdates.Add(networkid);
                            networkIdUpdates.Add(member.GetUniqueKey());
                        }
                    }
                }

                if (MDStatics.IsNetworkActive() && networkIdUpdates.Count > 0)
                {
                    Rpc(nameof(UpdateNetworkIdMap), networkIdUpdates);
                }
            }
        }
 /// <summary>
 /// Returns true if the local peer is the network master of the node or we're not networking
 /// </summary>
 /// <returns>True if we are master of the node, false if not</returns>
 public static bool IsMaster(this Node Instance)
 {
     return(MDStatics.IsNetworkActive() == false || Instance.GetNetworkMaster() == MDStatics.GetPeerId());
 }
Beispiel #16
0
        /// <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() || (MDStatics.IsServer() && !MDStatics.IsNetworkActive() && PeerId == MDStatics.GetServerId()))
            {
                // 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;
            }
        }