/// <summary> /// Handle a heartbeat timer elapse event, by requesting a heartbeat. /// </summary> /// <param name="sender">Unused.</param> /// <param name="e">Unused.</param> private void HeartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) { DateTime Now = DateTime.UtcNow; NWServerTracker Tracker = MasterServer.Tracker; bool Expired = false; // // First, check for server activity expiration. If the server has // exceeded the heartbeat cut off, mark it as offline. // lock (this) { if (InitialHeartbeat) { InitialHeartbeat = false; } else if ((Now >= LastHeartbeat) && (Now - LastHeartbeat) >= NWServerTracker.HeartbeatCutoffTimeSpan) { ActivePlayerCount = 0; Online = false; Expired = true; if (!Save()) { Logger.Log(LogLevel.Error, "NWGameServer.HeartbeatTimer_Elapsed(): Server {0} could not be saved as expired.", this); Expired = false; } } } if (Expired) { Logger.Log(LogLevel.Normal, "NWGameServer.HeartbeatTimer_Elapsed(): Server {0} expired from online server list due to heartbeat timeout.", this); return; } // // Request the tracker to initiate this heartbeat request. If the // server is not shutting down, continue on to queue the next // heartbeat expiration timer. // if (!MasterServer.Tracker.RequestHeartbeat(this)) { return; } HeartbeatTimer.Interval = (HEARTBEAT_INTERVAL + (Rng.Next() % HEARTBEAT_JITTER)); HeartbeatTimer.Start(); }
/// <summary> /// Synchronously execute the server. /// </summary> public void Run() { // // Set the log file first, if one existed. // if (!String.IsNullOrEmpty(ServerSettings.Default.LogFileName)) Logger.OpenLogFile(ServerSettings.Default.LogFileName); // // Set the log filter level. // Logger.LogFilterLevel = (LogLevel)Enum.Parse(typeof(LogLevel), ServerSettings.Default.LogLevel); try { bool IsShutdown = false; // // Bind the socket and prepare it for I/O. // SetupSockets(); // // Create the underlying server tracker. // ServerTracker = new NWServerTracker(this); // // Queue the initial block of receive buffers. // MasterServerNATDuplicateSocket.InitiateAllReceives(); GameSpySocket.InitiateAllReceives(); MasterServerSocket.InitiateAllReceives(); // // Inform the server tracker that it is clear to begin // heartbeat operations. // ServerTracker.QueueInitialHeartbeats(); Logger.Log(LogLevel.Normal, "NWMasterServer.Run(): Master server version {0} initialized (game build advertised: {1}).", Assembly.GetExecutingAssembly().GetName().Version, BuildNumber); // // Block waiting for a quit request. Once requested, initiate // shutdown on the socket and wait for outstanding receive // operations to drain. Once these have drained, close the // socket and terminate the server. // for (; ; ) { QuitEvent.WaitOne(); if (!IsShutdown) { // // Shut down future receives and inform the server // tracker that new heartbeat requests should be // drained out. // MasterServerSocket.Shutdown(); MasterServerNATDuplicateSocket.Shutdown(); GameSpySocket.Shutdown(); IsShutdown = true; ServerTracker.DrainHeartbeats(); } if (PendingBuffers == 0) break; } // // Finally, flush any lingering pending queries. // lock (QueryCombineBuffer) { if (QueryCombineBuffer.Length != 0) { ExecuteQueryNoReader(QueryCombineBuffer.ToString()); QueryCombineBuffer.Clear(); } } Logger.Log(LogLevel.Normal, "NWMasterServer.Run(): Main loop exiting."); if (ServiceObject != null) ServiceObject.RequestStop(); } catch (Exception e) { Logger.Log(LogLevel.Error, "NWMasterServer.Run(): Exception: {0}", e); } }