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(); } }
public ReplicatedNode(Node InInstance, List <MDReplicatedMember> InMembers) { Instance = Godot.Object.WeakRef(InInstance); Members = InMembers; NetworkMaster = InInstance.GetNetworkMaster(); CheckedPeerId = MDStatics.GetPeerId(); }
/// <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)); }
/// <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> /// 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); }
private void ClientOnConnected() { MDLog.Info(LOG_CAT, "Client connected to server"); int PeerId = MDStatics.GetPeerId(); OnPlayerJoined_Internal(PeerId); OnSessionStartedEvent(); IsSessionStarted = true; }
/// <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)); }
public void CheckForNetworkChanges(int CurrentMaster) { int CurrentPeerId = MDStatics.GetPeerId(); if (CurrentMaster != NetworkMaster || CheckedPeerId != CurrentPeerId) { Members.ForEach(member => member.CheckIfShouldReplicate()); NetworkMaster = CurrentMaster; CheckedPeerId = CurrentPeerId; } }
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)); } }
/// <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 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); } }
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)); } }
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); } }
/// <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)); }
/// <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()]); }
/// <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; } }
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); } }
/// <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(); } }
/// <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()); }