csrd_uptime_limiter.sp 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /**
  2. * [CSRD] Uptime Limiter
  3. *
  4. * There seems to be a memory leak somewhere in the server. Definitely not a handle leak.
  5. * Supposedly, it's caused by SourceTV.
  6. *
  7. * Because I can't be bothered to figure out the full cause at the moment, I'll just let the
  8. * server restart itself every day to avoid it filling up swap and getting OOM killed.
  9. *
  10. * Disconnecting an actively connected user would be rude, so we'll wait until the server is
  11. * empty.
  12. */
  13. #pragma semicolon 1
  14. #include <sourcemod>
  15. #pragma newdecls required
  16. #define PLUGIN_VERSION "0.2.1"
  17. public Plugin myinfo = {
  18. name = "[CSRD] Uptime Limiter",
  19. author = "nosoop",
  20. description = "Forces a server to auto-restart after a certain uptime when empty.",
  21. version = PLUGIN_VERSION,
  22. url = "https://git.csrd.science/"
  23. }
  24. int g_nMaxUptimeLimit = 60 * 60 * 12;
  25. public void OnPluginStart() {
  26. RegAdminCmd("csrd_uptime", AdminCmd_GetUptime, ADMFLAG_ROOT,
  27. "Outputs the server's current uptime.");
  28. }
  29. /**
  30. * Command to output current uptime.
  31. */
  32. public Action AdminCmd_GetUptime(int client, int argc) {
  33. char uptime[64], limit[64];
  34. FormatDuration(uptime, sizeof(uptime), GetUptime());
  35. FormatDuration(limit, sizeof(limit), g_nMaxUptimeLimit);
  36. ReplyToCommand(client, "Uptime: %s (limit %s)", uptime, limit);
  37. return Plugin_Handled;
  38. }
  39. /**
  40. * Pretty-prints a duration of nSeconds as hours / minutes / seconds.
  41. */
  42. void FormatDuration(char[] buffer, int maxlen, int nSeconds) {
  43. Format(buffer, maxlen, "%d hours, %d minutes, %d seconds",
  44. nSeconds / 3600,
  45. (nSeconds % 3600) / 60,
  46. nSeconds % 60);
  47. }
  48. /**
  49. * Called when the Waiting for Players round state begins.
  50. * At least one client, human or bot, must be connected for this forward to be called.
  51. *
  52. * Considering the server runs bots, we can use this forward to check if any human players are
  53. * connected to the server, and if not, we can restart the server when desirable.
  54. */
  55. public void TF2_OnWaitingForPlayersStart() {
  56. if (GetUptime() > g_nMaxUptimeLimit) {
  57. if (!IsHumanConnected()) {
  58. LogMessage("Server has reached an uptime of %d seconds and is currently empty. "
  59. ... "Restarting...", GetUptime());
  60. ServerCommand("quit");
  61. }
  62. }
  63. }
  64. /**
  65. * Checks if a human player is connected to the server.
  66. * This always returns false between the OnMapEnd and OnMapStart / OnConfigsExecuted callbacks.
  67. */
  68. bool IsHumanConnected() {
  69. for (int i = 1; i < MaxClients; i++) {
  70. if (IsClientConnected(i) && !IsFakeClient(i)) {
  71. return true;
  72. }
  73. }
  74. return (GetClientCount(false) - GetClientCount(true)) != 0;
  75. }
  76. /**
  77. * Engine time is effectively the amount of time since server startup, as far as I'm
  78. * concerned.
  79. */
  80. int GetUptime() {
  81. float flEngineTime = GetEngineTime();
  82. return RoundToFloor(flEngineTime);
  83. }