/// <summary>
        /// Returns the attribute object for the specified type,
        /// climbing the hierarchy until Node is reached or the attribute is found
        /// </summary>
        /// <param name="InstanceType">The type to search</param>
        /// <typeparam name="T">The type to find</typeparam>
        /// <returns>The attribute object for the specified type or null if not found</returns>
        public static T FindClassAttributeInNode <T>(Type InstanceType) where T : Attribute
        {
            // Check the buffer
            string key = $"{InstanceType.Name}#{typeof(T).Name}";

            if (ClassAttributeCache.ContainsKey(key))
            {
                return((T)ClassAttributeCache[key]);
            }

            if (MDStatics.IsSameOrSubclass(InstanceType, typeof(Node)) == false)
            {
                return(null);
            }

            T FoundAtr = Attribute.GetCustomAttribute(InstanceType, typeof(T)) as T;

            if (FoundAtr != null && InstanceType != typeof(Node) && InstanceType.BaseType != null)
            {
                FoundAtr = FindClassAttributeInNode <T>(InstanceType.BaseType);
            }

            if (FoundAtr != null)
            {
                // Add to buffer if found
                ClassAttributeCache.Add(key, FoundAtr);
                return(FoundAtr);
            }

            ClassAttributeCache.Add(key, null);

            return(null);
        }
        /// <summary>
        /// Returns a list of all the unique members for a Node, including the hierarchy
        /// </summary>
        /// <param name="Instance">The object type to find for</param>
        /// <returns>List of members</returns>
        public static IList <MemberInfo> GetMemberInfos(Type Instance)
        {
            string key = Instance.Name;

            if (!GetMemberInfosCache.ContainsKey(key))
            {
                List <MemberInfo> Members = new List <MemberInfo>();
                while (Instance != null && !MDStatics.IsInGodotNamespace(Instance))
                {
                    Members.AddRange(Instance.GetFields(MDStatics.BindFlagsAll));
                    Members.AddRange(Instance.GetProperties(MDStatics.BindFlagsAll));
                    Instance = Instance.BaseType;
                }

                List <MemberInfo> DeDupedMembers = new List <MemberInfo>();
                foreach (MemberInfo Member in Members)
                {
                    bool IsUnique = DeDupedMembers.All(
                        DeDupedMember =>
                        DeDupedMember.DeclaringType != Member.DeclaringType || DeDupedMember.Name != Member.Name);

                    if (IsUnique)
                    {
                        DeDupedMembers.Add(Member);
                    }
                }

                // Convert to read only so our cache is never modified
                GetMemberInfosCache.Add(key, DeDupedMembers.AsReadOnly());
            }

            return(GetMemberInfosCache[key]);
        }
Exemple #3
0
 public ReplicatedNode(Node InInstance, List <MDReplicatedMember> InMembers)
 {
     Instance      = Godot.Object.WeakRef(InInstance);
     Members       = InMembers;
     NetworkMaster = InInstance.GetNetworkMaster();
     CheckedPeerId = MDStatics.GetPeerId();
 }
Exemple #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);
 }
        /// <summary>
        /// Returns a list of all the unique methods for a Node, including the hierarchy
        /// </summary>
        /// <param name="Instance">The object type to find for</param>
        /// <returns>List of methodss</returns>
        public static IList <MethodInfo> GetMethodInfos(Type Instance)
        {
            string key = Instance.Name;

            if (!GetMethodInfosCache.ContainsKey(key))
            {
                List <MethodInfo> Methods = new List <MethodInfo>();
                while (Instance != null && !MDStatics.IsInGodotNamespace(Instance))
                {
                    Methods.AddRange(Instance.GetMethods(MDStatics.BindFlagsAll));
                    Instance = Instance.BaseType;
                }

                List <MethodInfo> DeDupedMethods = new List <MethodInfo>();
                foreach (MethodInfo Method in Methods)
                {
                    bool IsUnique = DeDupedMethods.All(DeDupedMethod => DeDupedMethod.DeclaringType != Method.DeclaringType || DeDupedMethod.Name != Method.Name);

                    if (IsUnique)
                    {
                        DeDupedMethods.Add(Method);
                    }
                }

                GetMethodInfosCache.Add(key, DeDupedMethods.AsReadOnly());
            }

            return(GetMethodInfosCache[key]);
        }
Exemple #6
0
        public object ConvertBackToObject(object CurrentObject, object[] Parameters)
        {
            MDLog.Trace(LOG_CAT, $"MDCommandReplicator converting back ({MDStatics.GetParametersAsString(Parameters)})");

            if (Parameters.Length == 1 && Parameters[0] == null)
            {
                return(null);
            }

            T obj;

            if (CurrentObject != null)
            {
                // Replace values in existing object
                obj = (T)CurrentObject;
            }
            else
            {
                // Return a new object
                obj = (T)Activator.CreateInstance(typeof(T));
            }

            for (int i = 0; i < Parameters.Length; i++)
            {
                // Get the length of the data
                int length = Convert.ToInt32(Parameters[i].ToString());

                // Extract parameters and apply to the command replicator
                object[] converterParams = Parameters.SubArray(i + 1, i + length);
                obj.MDProcessCommand(converterParams);
                i += length;
            }

            return((T)obj);
        }
        // Create and initialize the player object
        private MDPlayerInfo GetOrCreatePlayerObject(int PeerId)
        {
            if (Players.ContainsKey(PeerId))
            {
                return(Players[PeerId]);
            }

            Type PlayerType = GameInstance.GetPlayerInfoType();

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

            MDPlayerInfo Player = Activator.CreateInstance(PlayerType) as MDPlayerInfo;

            Player.SetPeerId(PeerId);
            Player.PauseMode = PauseModeEnum.Process;
            AddChild(Player);
            Players.Add(PeerId, Player);

            OnPlayerInfoCreated(Player);

            return(Player);
        }
Exemple #8
0
        /// <summary>
        /// Parses the settings we know about
        /// </summary>
        /// <param name="SettingsValues">A setting array that has been run through MDReplicator.ParseParameters</param>
        protected void ParseSettings(MDReplicatedSetting[] SettingsValues)
        {
            foreach (MDReplicatedSetting setting in SettingsValues)
            {
                switch ((Settings)setting.Key)
                {
                case Settings.OnValueChangedEvent:
                    Node Node = NodeRef.GetRef() as Node;
                    OnValueChangedMethodCallback = Node.GetType().GetMethodRecursive(setting.Value.ToString());
                    if (OnValueChangedMethodCallback == null)
                    {
                        OnValueChangedEventCallback = Node.GetType().GetEvent(setting.Value.ToString());
                    }
                    MDLog.CError(OnValueChangedMethodCallback == null && OnValueChangedEventCallback == null, LOG_CAT, $"Failed to find method or event with name {setting.Value.ToString()} on Node {Node.GetPath()}");
                    break;

                case Settings.Converter:
                    Type DataConverterType = Type.GetType(setting.Value.ToString());
                    DataConverter = MDStatics.CreateConverterOfType(DataConverterType);
                    break;

                case Settings.CallOnValueChangedEventLocally:
                    ShouldCallOnValueChangedCallbackLocally = setting.Value as bool? ?? false;
                    break;
                }
            }

            // We got no data converter, get default one
            if (DataConverter == null)
            {
                DataConverter = MDStatics.GetConverterForType(Member.GetUnderlyingType());
            }
        }
        /// <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));
        }
        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();
            }
        }
        private void CheckAllClientsSynched(Timer timer)
        {
            if (PeerSynchInfo.Values.Where(peerInfo => peerInfo.CompletedSynch == false).ToList().Count > 0)
            {
                MDLog.Debug(LOG_CAT, "All clients are not synched yet");
                return;
            }

            // Check if we still need to wait for a better confidence on the TickMsec value
            if (PeerSynchInfo.Values.Where(peerInfo => peerInfo.IsClientMSecConfident() == false).ToList().Count > 0)
            {
                MDLog.Debug(LOG_CAT, "Still waiting for a more secure msec value");
                return;
            }

            MDLog.Debug(LOG_CAT, "All clients synched, sending unpause signal");
            // Alright tell all clients to unpause in a bit
            foreach (int peerid in GameSession.GetAllPeerIds())
            {
                // Get our current game tick
                uint tickToUnpause = GameClock?.GetTick() != null?GameClock.GetTick() : 0;

                if (peerid != MDStatics.GetServerId())
                {
                    RpcId(peerid, nameof(UnpauseAtTickMsec),
                          GetPlayerTicksMsec(peerid) + GetUnpauseCountdownDurationMSec(), tickToUnpause);
                }
            }

            UnpauseAtTickMsec(OS.GetTicksMsec() + GetUnpauseCountdownDurationMSec(), 0);
            PeerSynchInfo.Values.ToList().ForEach(value => value.CompletedSynch = false);
            timer.RemoveAndFree();
        }
        /// <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);
            }
        }
        // Registers a new node to MDFramework systems
        private void RegisterNewNode(Node Instance)
        {
            Type type = Instance.GetType();

            // Ignore nodes in Godot namespace as they won't have any attributes
            if (MDStatics.IsInGodotNamespace(type))
            {
                return;
            }

            MDAutoRegister AutoRegAtr = MDStatics.FindClassAttributeInNode <MDAutoRegister>(Instance.GetType());

            if (RequireAutoRegister() && AutoRegAtr == null ||
                AutoRegAtr != null && AutoRegAtr.RegisterType == MDAutoRegisterType.None)
            {
                return;
            }

            Instance.PopulateBindNodes();
            Instance.RegisterReplicatedAttributes();
            if (AutoRegAtr != null && AutoRegAtr.RegisterType == MDAutoRegisterType.Debug)
            {
                Instance.RegisterCommandAttributes();
            }
        }
        private void DoCall(Node Target)
        {
            switch (Type)
            {
            case TypeOfCall.RPC:
                MethodInfo methodInfo = MDStatics.GetMethodInfo(Target, Convert.ToInt32(Name));
                if (methodInfo == null)
                {
                    MDLog.Fatal(LOG_CAT, $"Could not find method {Target.GetType().ToString()}#{Name}");
                    return;
                }
                object[] convertedParams = MDStatics.ConvertParametersBackToObject(methodInfo, Parameters);
                MDStatics.GetReplicator().RpcSenderId = SenderPeerId;
                methodInfo.Invoke(Target, convertedParams);
                MDStatics.GetReplicator().RpcSenderId = -1;
                break;

            case TypeOfCall.RSET:
                MemberInfo       memberInfo = MDStatics.GetMemberInfo(Target, Name);
                IMDDataConverter Converter  = MDStatics.GetConverterForType(memberInfo.GetUnderlyingType());
                object           value      = Converter.ConvertBackToObject(memberInfo.GetValue(Target), Parameters);
                MDLog.Trace(LOG_CAT, $"Setting {Name} on {Target.Name} to value {value}");
                memberInfo.SetValue(Target, value);
                break;
            }
        }
Exemple #15
0
        /// <summary>
        /// Opens a screen of the provided type with the specified name on the specified layer
        /// </summary>
        /// <param name="ScreenType">The C# Type of the screen to instantiate</param>
        /// <param name="ScreenName">The node name that will be given to the screen</param>
        /// <param name="ScreenLayer">The layer to open this screen on</param>
        /// <returns>The intance of the screen or null if it fails</returns>
        public MDScreen OpenScreen(Type ScreenType, string ScreenName, MDScreenLayer ScreenLayer)
        {
            MDScreen NewScreen = MDStatics.CreateTypeInstance <MDScreen>(ScreenType);

            AddScreenToStack(NewScreen, ScreenLayer);

            return(NewScreen);
        }
Exemple #16
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);
        }
 private void RequestPing(uint ServerTimeOfRequest, uint EstimateTime, uint EstimatedTick, int MaxPing)
 {
     // Set max player's ping received from server
     OnPlayerPingUpdatedEvent(MDStatics.GetServerId(), MaxPing);
     this.MaxPing = MaxPing;
     // Respond
     RequestPing(ServerTimeOfRequest);
     GameClock?.CheckSynch(EstimateTime, EstimatedTick);
 }
Exemple #19
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>
        /// 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));
        }
        private void ClientOnConnected()
        {
            MDLog.Info(LOG_CAT, "Client connected to server");
            int PeerId = MDStatics.GetPeerId();

            OnPlayerJoined_Internal(PeerId);
            OnSessionStartedEvent();
            IsSessionStarted = true;
        }
 private void UpdateSynchStatusOnAllClients(int PeerId, float SynchStatus)
 {
     foreach (int peerid in GameSession.GetAllPeerIds())
     {
         if (peerid != MDStatics.GetServerId() && peerid != PeerId)
         {
             RpcId(peerid, nameof(UpdateSynchStatus), PeerId, SynchStatus);
         }
     }
 }
        //
        /// <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);
        }
        /// <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);
        }
Exemple #25
0
 private void CreateGameClock()
 {
     if (GameClock == null)
     {
         GameClock                  = MDStatics.CreateTypeInstance <MDGameClock>(GetGameClockType());
         GameClock.Name             = "GameClock";
         GameSynchronizer.GameClock = GameClock;
         this.AddNodeToRoot(GameClock, true);
     }
 }
Exemple #26
0
 // Ensure GameSession is created
 private void CreateGameSession()
 {
     if (GameSession == null)
     {
         GameSession              = MDStatics.CreateTypeInstance <MDGameSession>(GetGameSessionType());
         GameSession.Name         = "GameSession";
         GameSession.GameInstance = this;
         this.AddNodeToRoot(GameSession, true);
     }
 }
Exemple #27
0
 // Ensure Replicator is created
 private void CreateConfiguration()
 {
     if (Configuration == null)
     {
         Configuration      = MDStatics.CreateTypeInstance <MDConfiguration>(GetConfigurationType());
         Configuration.Name = "MDConfiguration";
         Configuration.LoadConfiguration();
         this.AddNodeToRoot(Configuration, true);
     }
 }
Exemple #28
0
 /// <summary>
 /// Notifies the server that initialization for this player has completed
 /// </summary>
 protected void MarkPlayerInitializationCompleted()
 {
     if (MDStatics.IsServer())
     {
         ServerMarkPlayerInitializationCompleted();
     }
     else
     {
         RpcId(MDStatics.GetServerId(), nameof(ServerMarkPlayerInitializationCompleted));
     }
 }
Exemple #29
0
        public MDReplicatedCommandReplicator(MemberInfo Member, bool Reliable, MDReplicatedType ReplicatedType, WeakRef NodeRef, MDReplicatedSetting[] Settings)
            : base(Member, true, ReplicatedType, NodeRef, Settings)
        {
            GameSession = MDStatics.GetGameSession();
            Replicator  = GameSession.Replicator;
            GameClock   = GameSession.GetGameClock();
            Node node = NodeRef.GetRef() as Node;
            IMDCommandReplicator CommandReplicator = InitializeCommandReplicator(Member, node);

            CommandReplicator.MDSetSettings(Settings);
        }
Exemple #30
0
 // Ensure Replicator is created
 private void CreateReplicator()
 {
     if (Replicator == null)
     {
         Replicator      = MDStatics.CreateTypeInstance <MDReplicator>(GetReplicatorType());
         Replicator.Name = "Replicator";
         this.AddNodeToRoot(Replicator, true);
         Replicator.Initialize();
         GameSession.Replicator = Replicator;
     }
 }