/// <summary> /// Shutsdown the ClientManager server and socket /// </summary> public void Shutdown() { // Stop accepting new connections base.IgnoreNewConnections = true; Exiting = true; // Unregister events so we dont get a shit ton of calls GpcmClient.OnSuccessfulLogin -= GpcmClient_OnSuccessfulLogin; GpcmClient.OnDisconnect -= GpcmClient_OnDisconnect; // Discard the poll timer PollTimer.Stop(); PollTimer.Dispose(); StatusTimer.Stop(); StatusTimer.Dispose(); // Disconnected all connected clients Console.WriteLine("Disconnecting all users..."); Parallel.ForEach(Clients.Values, client => client.Disconnect(DisconnectReason.ForcedServerShutdown)); Parallel.ForEach(Processing.Values, client => client.Disconnect(DisconnectReason.ForcedServerShutdown)); // Update the database try { // Set everyone's online session to 0 using (Database.GamespyDatabase db = new Database.GamespyDatabase()) db.Execute("UPDATE player SET online=0"); } catch (Exception e) { Program.ErrorLog.Write("WARNING: [GpcmServer.Shutdown] Failed to update client database: " + e.Message); } // Update Connected Clients in the Database Clients.Clear(); // Shutdown the listener socket base.ShutdownSocket(); // Tell the base to dispose all free objects base.Dispose(); }
/// <summary> /// Creates a new instance of <see cref="GpcmServer"/> /// </summary> /// <param name="bindTo"></param> public GpcmServer(IPEndPoint bindTo) : base(bindTo, MaxConnections) { // Register for events GpcmClient.OnSuccessfulLogin += GpcmClient_OnSuccessfulLogin; GpcmClient.OnDisconnect += GpcmClient_OnDisconnect; // Setup timer. Every 15 seconds should be sufficient if (PollTimer == null || !PollTimer.Enabled) { PollTimer = new System.Timers.Timer(15000); PollTimer.Elapsed += (s, e) => { // Send keep alive to all connected clients if (Clients.Count > 0) { Parallel.ForEach(Clients.Values, client => client.SendKeepAlive()); } // Disconnect hanging connections if (Processing.Count > 0) { Parallel.ForEach(Processing.Values, client => CheckTimeout(client)); } }; PollTimer.Start(); } // Setup timer. Every 5 seconds should be sufficient if (StatusTimer == null || !StatusTimer.Enabled) { StatusTimer = new System.Timers.Timer(5000); StatusTimer.Elapsed += (s, e) => { // Return if we are empty if (PlayerStatusQueue.IsEmpty) { return; } // Open database connection using (Database.GamespyDatabase db = new Database.GamespyDatabase()) using (var transaction = db.BeginTransaction()) { try { var timestamp = DateTime.UtcNow.ToUnixTimestamp(); PlayerStatusUpdate result; while (PlayerStatusQueue.TryDequeue(out result)) { // Skip if this player never finished logging in if (!result.Client.CompletedLoginProcess) { continue; } // Only update record under these two status' if (result.Status != LoginStatus.Completed && result.Status != LoginStatus.Disconnected) { continue; } // Update player record db.Execute( "UPDATE player SET online=@P0, lastip=@P1, lastonline=@P2 WHERE id=@P3", (result.Status == LoginStatus.Disconnected) ? 0 : 1, result.Client.RemoteEndPoint.Address, timestamp, result.Client.PlayerId ); } transaction.Commit(); } catch (Exception ex) { ServerManager.Log("[Gpcm..ctor] StatusTimer: " + ex.Message); transaction.Rollback(); } } }; StatusTimer.Start(); } // Set connection handling base.ConnectionEnforceMode = EnforceMode.DuringPrepare; base.FullErrorMessage = Config.GetValue("Settings", "LoginServerFullMessage").Replace("\"", ""); // Begin accepting connections base.StartAcceptAsync(); }