/// <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]); }
public ReplicatedNode(Node InInstance, List <MDReplicatedMember> InMembers) { Instance = Godot.Object.WeakRef(InInstance); Members = InMembers; NetworkMaster = InInstance.GetNetworkMaster(); CheckedPeerId = MDStatics.GetPeerId(); }
/// <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]); }
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); }
/// <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; } }
/// <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); }
/// <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); }
/// <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); }
private void CreateGameClock() { if (GameClock == null) { GameClock = MDStatics.CreateTypeInstance <MDGameClock>(GetGameClockType()); GameClock.Name = "GameClock"; GameSynchronizer.GameClock = GameClock; this.AddNodeToRoot(GameClock, true); } }
// 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); } }
// Ensure Replicator is created private void CreateConfiguration() { if (Configuration == null) { Configuration = MDStatics.CreateTypeInstance <MDConfiguration>(GetConfigurationType()); Configuration.Name = "MDConfiguration"; Configuration.LoadConfiguration(); this.AddNodeToRoot(Configuration, true); } }
/// <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)); } }
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); }
// 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; } }