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(); }
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); }
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> /// Notifies the server that initialization for this player has completed /// </summary> protected void MarkPlayerInitializationCompleted() { if (MDStatics.IsServer()) { ServerMarkPlayerInitializationCompleted(); } else { RpcId(MDStatics.GetServerId(), nameof(ServerMarkPlayerInitializationCompleted)); } }
protected virtual void OnServerRequestedInitialization() { MDLog.Debug(LOG_CAT, $"Initializing PeerId [{PeerId}] from owner"); string PlaceholderName = "Player_" + PeerId; if (MDStatics.IsServer()) { OnClientSentPlayerName(PlaceholderName); } else { RpcId(MDStatics.GetServerId(), nameof(OnClientSentPlayerName), PlaceholderName); } }
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); } }
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); } }
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 RpcReceiveNodeCount(int NodeCount) { MDLog.Debug(LOG_CAT, $"Total nodes that need synch are {NodeCount}"); this.NodeCount = NodeCount; NodeSynchCompleted = false; // Start synch timer Timer timer = this.CreateUnpausableTimer("SynchTimer", false, SYNCH_TIMER_CHECK_INTERVAL, true, this, nameof(CheckSynchStatus)); timer.Start(); // Send out synch started event OnSynchStartedEvent(IsPauseOnJoin()); // Set all peers to 0% except server foreach (int peerid in GameSession.GetAllPeerIds().Where(peerid => peerid != MDStatics.GetServerId())) { OnPlayerSynchStatusUpdateEvent(peerid, 0f); } }
/// <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; } }
/// <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; } }