Пример #1
0
        private void OnPlayerJoinedEvent(int PeerId)
        {
            // Check if this is our own join message or if we are a client
            if (PeerId == MDStatics.GetPeerId() || MDStatics.IsClient())
            {
                return;
            }

            MDGameSynchPeerInfo PeerInfo = new MDGameSynchPeerInfo(this, PeerId);

            PeerSynchInfo.Add(PeerId, PeerInfo);

            if (IsPauseOnJoin())
            {
                PauseGame();
                foreach (int peerid in GameSession.GetAllPeerIds().Where(peerid => peerid != MDStatics.GetServerId() && PeerId != peerid))
                {
                    // Don't do this for the connecting peer or the server
                    RpcId(peerid, nameof(PauseGame));
                }
            }

            // Start synch check timer
            Timer timer = (Timer)GetNodeOrNull(ALL_PLAYERS_SYNCHED_TIMER_NAME);

            if (timer == null)
            {
                timer = this.CreateUnpausableTimer(ALL_PLAYERS_SYNCHED_TIMER_NAME, false, SYNCH_TIMER_CHECK_INTERVAL, true,
                                                   this, nameof(CheckAllClientsSynched));
                timer.Start();
            }
        }
Пример #2
0
 public ReplicatedNode(Node InInstance, List <MDReplicatedMember> InMembers)
 {
     Instance      = Godot.Object.WeakRef(InInstance);
     Members       = InMembers;
     NetworkMaster = InInstance.GetNetworkMaster();
     CheckedPeerId = MDStatics.GetPeerId();
 }
Пример #3
0
        /// <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));
        }
Пример #4
0
 /// <summary>
 /// Adds some basic information on creation, can be toggled in config
 /// </summary>
 public static void AddBasicInfo()
 {
     AddOnScreenDebugInfo(BASIC_DEBUG_CAT, "FPS", () => Engine.GetFramesPerSecond().ToString(CultureInfo.InvariantCulture), Colors.Cyan);
     AddOnScreenDebugInfo(BASIC_DEBUG_CAT, "Static Memory", () => MDStatics.HumanReadableMemorySize(OS.GetStaticMemoryUsage()), Colors.Cyan);
     AddOnScreenDebugInfo(BASIC_DEBUG_CAT, "Peer Type: ", () => MDStatics.GetNetMode().ToString(), Colors.Cyan);
     AddOnScreenDebugInfo(BASIC_DEBUG_CAT, "PeerId: ", () => MDStatics.GetPeerId().ToString(), Colors.Cyan);
 }
Пример #5
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);
 }
Пример #6
0
        private void ClientOnConnected()
        {
            MDLog.Info(LOG_CAT, "Client connected to server");
            int PeerId = MDStatics.GetPeerId();

            OnPlayerJoined_Internal(PeerId);
            OnSessionStartedEvent();
            IsSessionStarted = true;
        }
Пример #7
0
        /// <summary>
        /// Checks if we should replicate this to other clients and sets our internal state accordingly.
        /// </summary>
        public void CheckIfShouldReplicate()
        {
            MasterAttribute     MasterAtr     = Member.GetCustomAttribute(typeof(MasterAttribute)) as MasterAttribute;
            MasterSyncAttribute MasterSyncAtr = Member.GetCustomAttribute(typeof(MasterSyncAttribute)) as MasterSyncAttribute;
            Node Node     = NodeRef.GetRef() as Node;
            bool IsMaster = MDStatics.GetPeerId() == Node.GetNetworkMaster();

            IsShouldReplicate = (IsMaster && MasterAtr == null && MasterSyncAtr == null) || (IsMaster == false && (MasterAtr != null || MasterSyncAtr != null));
        }
Пример #8
0
        public void CheckForNetworkChanges(int CurrentMaster)
        {
            int CurrentPeerId = MDStatics.GetPeerId();

            if (CurrentMaster != NetworkMaster || CheckedPeerId != CurrentPeerId)
            {
                Members.ForEach(member => member.CheckIfShouldReplicate());
                NetworkMaster = CurrentMaster;
                CheckedPeerId = CurrentPeerId;
            }
        }
Пример #9
0
 internal void BeginInitialization()
 {
     MDLog.Debug(LOG_CAT, $"Starting initialization for PeerId [{PeerId}]");
     if (PeerId == MDStatics.GetPeerId())
     {
         OnServerRequestedInitialization();
     }
     else
     {
         // Send message to owning client from server to request initialization data
         RpcId(PeerId, nameof(OnServerRequestedInitialization));
     }
 }
Пример #10
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())
            {
                // 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;
            }
        }
Пример #11
0
        private void OnPlayerInitializedEvent(int PeerId)
        {
            // Check if this is our own join message or if we are a client
            if (PeerId == MDStatics.GetPeerId() || MDStatics.IsClient())
            {
                return;
            }

            OnSynchStartedEvent(IsPauseOnJoin());
            foreach (int peerid in GameSession.GetAllPeerIds().Where(peerid => peerid != MDStatics.GetServerId()))
            {
                // Synch just started so set everyone to 0%
                OnPlayerSynchStatusUpdateEvent(peerid, 0f);
                RpcId(peerid, nameof(RpcReceiveNodeCount), NodeList.Count);
            }
        }
Пример #12
0
        public override void Replicate(int JoinInProgressPeerId, bool IsIntervalReplicationTime)
        {
            IMDCommandReplicator CommandReplicator = GetCommandReplicator();
            Node Instance = NodeRef.GetRef() as Node;

            if (CommandReplicator == null)
            {
                MDLog.Error(LOG_CAT, $"Command replicator is null for member {Instance.GetPath()}#{Member.Name}");
                return;
            }

            if ((GetReplicatedType() == MDReplicatedType.Interval && IsIntervalReplicationTime) || (GetReplicatedType() == MDReplicatedType.OnChange))
            {
                // We do a check here to see if anything has updated
                GetCommandReplicator().MDShouldBeReplicated();
                List <object[]> commands = GetCommandReplicator().MDGetCommands();
                if (commands.Count > 0)
                {
                    // Do replication to all except joining peer if we got one
                    commands.ForEach(value =>
                    {
                        foreach (int PeerId in GameSession.GetAllPeerIds())
                        {
                            if (PeerId != JoinInProgressPeerId && PeerId != MDStatics.GetPeerId())
                            {
                                ReplicateCommandToPeer(value, PeerId);
                            }
                        }
                    });

                    CheckCallLocalOnChangeCallback();
                }
            }

            if (JoinInProgressPeerId != -1)
            {
                // Replicate current data to joining peer and send current command we are at
                List <object[]> newPlayerCommands = CommandReplicator.MDGetCommandsForNewPlayer();
                newPlayerCommands.ForEach(value => ReplicateCommandToPeer(value, JoinInProgressPeerId));
            }
        }
Пример #13
0
        private void OnPlayerJoined(int PeerId)
        {
            // Skip local player
            if (PeerId == MDStatics.GetPeerId())
            {
                return;
            }

            MDLog.Debug(LOG_CAT, $"Registered JIPPlayer with Id: {PeerId}");
            JIPPlayers.Enqueue(new NewPlayer(PeerId, OS.GetTicksMsec()));
            if (MDStatics.IsServer())
            {
                List <object> networkIdUpdates = new List <object>();
                foreach (uint key in NetworkIdKeyMap.GetKeys())
                {
                    networkIdUpdates.Add(key);
                    networkIdUpdates.Add(NetworkIdKeyMap.GetValue(key));
                }

                RpcId(PeerId, nameof(UpdateNetworkIdMap), networkIdUpdates);
            }
        }
Пример #14
0
        /// <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));
        }
Пример #15
0
 /// <summary>
 /// Get the playerinfo for the local player
 /// </summary>
 /// <returns>The player info of the local player</returns>
 public MDPlayerInfo GetMyPlayerInfo()
 {
     return(Players[MDStatics.GetPeerId()]);
 }
Пример #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())
            {
                // 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;
            }
        }
Пример #17
0
        private void CheckSynchStatus(Timer timer)
        {
            // Only do this on clients
            if (!this.IsClient())
            {
                timer.RemoveAndFree();
                return;
            }

            if (NodeCount < 0)
            {
                MDLog.Debug(LOG_CAT, "We got no node count");
                // We don't know how many nodes we got yet
                return;
            }

            int SynchedNodes = NodeCount;

            if (NodeList.Count < NodeCount)
            {
                MDLog.Debug(LOG_CAT, "We still don't have all nodes");
                // We still don't have all nodes
                SynchedNodes = NodeList.Count;
            }
            else if (!NodeSynchCompleted)
            {
                // This is the first time we synched all nodes, notify the server
                MDLog.Debug(LOG_CAT, "Node synch complete, notifying server");
                NodeSynchCompleted = true;
                RpcId(GameSession.GetNetworkMaster(), nameof(NotifyAllNodesSynched));
            }

            int NotSynchedNodes = 0;

            // Check node custom logic to see if synch is done
            foreach (Node node in NodeList)
            {
                if (!(node is IMDSynchronizedNode) || ((IMDSynchronizedNode)node).IsSynchronizationComplete())
                {
                    continue;
                }

                // We are not synched
                MDLog.Trace(LOG_CAT, $"A node is still synching: {node.GetPath()}");
                SynchedNodes--;
                NotSynchedNodes++;
            }

            if (SynchedNodes == NodeCount)
            {
                // We are done synching
                SynchronizationState = SynchronizationStates.SYNCRHONIZED;
                RpcId(MDStatics.GetServerId(), nameof(ClientSynchDone));
                MDLog.Debug(LOG_CAT, "We are done synching notifying server");
                // Set ourselves to done
                OnPlayerSynchStatusUpdateEvent(MDStatics.GetPeerId(), 1f);
                timer.RemoveAndFree();
            }
            else
            {
                float percentage = (float)SynchedNodes / NodeCount;
                MDLog.Debug(LOG_CAT,
                            $"We have {NotSynchedNodes} nodes that are still synching. Current status: {percentage * 100}%");
                // Notify the server of how many nodes we got synched
                RpcId(GameSession.GetNetworkMaster(), nameof(ClientSynchStatus), SynchedNodes);

                // Update our own UI
                OnPlayerSynchStatusUpdateEvent(MDStatics.GetPeerId(), percentage);
            }
        }
Пример #18
0
        /// <summary>
        /// Logs the message (supports formatting) in accordance with the LogProperties set for the specified log category
        /// </summary>
        /// <param name="CategoryName">The category to log in</param>
        /// <param name="LogLevel">The log level</param>
        /// <param name="Message">The message</param>
        /// <param name="Args">Arguments</param>
        public static void Log(string CategoryName, MDLogLevel LogLevel, string Message, params object[] Args)
        {
            // TODO - Get calling method's name automatically: https://stackoverflow.com/a/5443690
            bool            LogFile    = true;
            bool            LogConsole = true;
            MDConfiguration Config     = MDStatics.GetGameInstance().GetConfiguration();

            if (Config.HasValue(MDConfiguration.ConfigurationSections.Logging, CategoryName))
            {
                MDLogLevel ConfigLogLevel = Config.GetEnum <MDLogLevel>(MDConfiguration.ConfigurationSections.Logging, CategoryName, MDLogLevel.Trace);
                LogFile    = ConfigLogLevel <= LogLevel || LogLevel == MDLogLevel.Force;
                LogConsole = ConfigLogLevel <= LogLevel || LogLevel == MDLogLevel.Force;
            }
            else if (LogProperties.ContainsKey(CategoryName))
            {
                MDLogProperties LogProps = LogProperties[CategoryName];
                LogFile    = LogProps.FileLogLevel <= LogLevel || LogLevel == MDLogLevel.Force;
                LogConsole = LogProps.ConsoleLogLevel <= LogLevel || LogLevel == MDLogLevel.Force;
            }
            else
            {
                MDLogLevel DefaultLogLevel = Config.GetEnum <MDLogLevel>(MDConfiguration.ConfigurationSections.Logging, MDConfiguration.DEFAULT_LOG_LEVEL, MDLogLevel.Trace);
                LogFile    = DefaultLogLevel <= LogLevel || LogLevel == MDLogLevel.Force;
                LogConsole = DefaultLogLevel <= LogLevel || LogLevel == MDLogLevel.Force;
            }

            if (LogFile || LogConsole)
            {
                int       PeerID   = MDStatics.GetPeerId();
                MDNetMode NetMode  = MDStatics.GetNetMode();
                string    ClientID = "PEER " + PeerID;
                if (NetMode == MDNetMode.Standalone)
                {
                    ClientID = "STANDALONE";
                }
                else if (NetMode == MDNetMode.Server)
                {
                    ClientID = "SERVER";
                }

                string FullMessage = "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "]" +
                                     "[" + (Godot.Engine.GetIdleFrames() % 1000).ToString("D3") + "]" +
                                     "[" + ClientID + "] " +
                                     "[" + CategoryName + "::" + LogLevel + "] " +
                                     string.Format(Message, Args);

                if (LogFile)
                {
                    LogToFile(FullMessage);
                }

                if (LogConsole)
                {
                    LogToConsole(FullMessage);
                }
            }

            if (LogLevel == MDLogLevel.Fatal)
            {
                DebugBreak();
            }
        }
Пример #19
0
 /// <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());
 }