├── .vscode └── tasks.json ├── README.md └── scripting └── map_restart.sp /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "options": 4 | { 5 | "env": 6 | { 7 | "sm_path": "/home/dev/sm-10", 8 | "server_path": "/home/christian/test-servers/css/cstrike/addons/sourcemod/plugins" 9 | } 10 | }, 11 | "tasks": 12 | [ 13 | { 14 | "label": "sourcepawn", 15 | "type": "shell", 16 | "command": "$sm_path/spcomp64 ${file} -D ${workspaceFolder}/build -i ${workspaceFolder}/scripting/include && sudo cp -f ${workspaceFolder}/build/${fileBasenameNoExtension}.smx ${server_path}", 17 | "problemMatcher": [] 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Map/Server Restart 2 | ## Description 3 | A simple plugin for SourceMod that restarts the map or server (if configured properly) after the time limit is reached and the server is reported as empty (not including bots). 4 | 5 | From the testing I've performed, the plugin will still trigger even when the server is hibernating. 6 | 7 | ``` 8 | x.x.x.x:27005: password failed. 9 | Client "[LBG] Christian" connected (x.x.x.x:27005). 10 | Server waking up from hibernation 11 | Game will not start until both teams have players. 12 | Game will not start until both teams have players. 13 | Going to intermission... 14 | Dropped [LBG] Christian from server (Disconnect by user.) 15 | Server is hibernating 16 | L 06/13/2022 - 00:01:41: [map_restart.smx] [MR] Found time to trigger map/server restart. 17 | ---- Host_Changelevel ---- 18 | find L 06/13/2022 - 00:01:42: -------- Mapchange to de_dust2 -------- 19 | ConVarRef room_type doesn't point to an existing ConVar 20 | ``` 21 | 22 | ## ConVars 23 | Here is a list of the plugin's ConVars along with their descriptions and default values. 24 | 25 | ``` 26 | // This file was auto-generated by SourceMod (v1.10.0.6537) 27 | // ConVars for plugin "map_restart.smx" 28 | 29 | 30 | // How often to check if we need to restart the map in seconds. 31 | // - 32 | // Default: "10.0" 33 | sm_mr_check_interval "10.0" 34 | 35 | // Whether to log when a map/server restart is triggered. 36 | // - 37 | // Default: "1" 38 | // Minimum: "0.000000" 39 | // Maximum: "1.000000" 40 | sm_mr_log "1" 41 | 42 | // Whether to restart map when the server is reported as empty. 43 | // - 44 | // Default: "1" 45 | // Minimum: "0.000000" 46 | // Maximum: "1.000000" 47 | sm_mr_restart_on_empty "1" 48 | 49 | // 0 = uses 'changelevel' command. 1 = executes 'quit' command. 50 | // - 51 | // Default: "0" 52 | // Minimum: "0.000000" 53 | // Maximum: "1.000000" 54 | sm_mr_restart_type "0" 55 | ``` 56 | 57 | ## Credits 58 | * [Christian Deacon](https://github.com/gamemann) -------------------------------------------------------------------------------- /scripting/map_restart.sp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define PLUGIN_TAG "[MR]" 4 | #define PLUGIN_VERSION "1.0.0" 5 | 6 | public Plugin myinfo = 7 | { 8 | name = "Map Restart", 9 | author = "Christian Deacon", 10 | description = "A map restart plugin that executes when the server empties out.", 11 | version = PLUGIN_VERSION, 12 | url = "Deaconn.net & LBGaming.co" 13 | }; 14 | 15 | ConVar g_cvRestartOnEmpty = null; 16 | ConVar g_cvRestartType = null; 17 | ConVar g_cvCheckInterval = null; 18 | ConVar g_cvLog = null; 19 | 20 | Handle g_hCheckTimer = null; 21 | 22 | public void OnPluginStart() 23 | { 24 | g_cvRestartOnEmpty = CreateConVar("sm_mr_restart_on_empty", "1", "Whether to restart map when the server is reported as empty.", _, true, 0.0, true, 1.0); 25 | g_cvRestartType = CreateConVar("sm_mr_restart_type", "0", "0 = uses 'changelevel' command. 1 = executes 'quit' command.", _, true, 0.0, true, 1.0); 26 | g_cvCheckInterval = CreateConVar("sm_mr_check_interval", "10.0", "How often to check if we need to restart the map in seconds."); 27 | g_cvLog = CreateConVar("sm_mr_log", "1", "Whether to log when a map/server restart is triggered.", _, true, 0.0, true, 1.0); 28 | 29 | HookConVarChange(g_cvCheckInterval, CVarChange); 30 | 31 | AutoExecConfig(true, "plugin.maprestart"); 32 | } 33 | 34 | public void CVarChange(Handle hCVar, const char[] oldv, const char[] newv) 35 | { 36 | ResetupTimer(); 37 | } 38 | 39 | public void OnConfigsExecuted() 40 | { 41 | ResetupTimer(); 42 | } 43 | 44 | public void OnMapEnd() 45 | { 46 | if (g_hCheckTimer != null) 47 | { 48 | delete g_hCheckTimer; 49 | } 50 | } 51 | 52 | stock void ResetupTimer() 53 | { 54 | if (g_hCheckTimer != null) 55 | { 56 | delete g_hCheckTimer; 57 | } 58 | 59 | g_hCheckTimer = CreateTimer(g_cvCheckInterval.FloatValue, Timer_Check, _, TIMER_REPEAT); 60 | } 61 | 62 | public Action Timer_Check(Handle hTimer, any data) 63 | { 64 | // Check if we need to restart the map/server. 65 | if (!NeedsRestart()) 66 | { 67 | return Plugin_Continue; 68 | } 69 | 70 | // Retrieve real client count. 71 | int cl_cnt = GetClientCountCustom(); 72 | 73 | // If restart when empty is on, check client count. 74 | if (g_cvRestartOnEmpty.BoolValue && cl_cnt > 0) 75 | { 76 | return Plugin_Continue; 77 | } 78 | 79 | // If client count is below 1, restart map or server. 80 | if (cl_cnt < 1) 81 | { 82 | if (g_cvLog.BoolValue) 83 | { 84 | LogMessage("%s Found time to trigger map/server restart.", PLUGIN_TAG); 85 | } 86 | 87 | if (g_cvRestartType.IntValue == 0) 88 | { 89 | char map_name[MAX_NAME_LENGTH]; 90 | GetCurrentMap(map_name, sizeof(map_name)); 91 | 92 | ServerCommand("changelevel %s", map_name); 93 | } 94 | else 95 | { 96 | ServerCommand("quit"); 97 | } 98 | } 99 | 100 | return Plugin_Continue; 101 | } 102 | 103 | stock bool NeedsRestart() 104 | { 105 | int time_left = 0; 106 | 107 | // If GetMapTimeLeft() isn't supported, just return false and log error. 108 | if (!GetMapTimeLeft(time_left)) 109 | { 110 | LogError("%s GetMapTimeLeft() not supported. Does this engine/game support mp_timelimit?", PLUGIN_TAG); 111 | 112 | return false; 113 | } 114 | 115 | // -1 = map timelimit is infinite. 116 | if (time_left == -1) 117 | { 118 | return false; 119 | } 120 | 121 | return time_left < 1; 122 | } 123 | 124 | stock int GetClientCountCustom() 125 | { 126 | int ret = 0, i; 127 | 128 | for (i = 1; i <= MaxClients; i++) 129 | { 130 | if (!IsClientInGame(i)) 131 | { 132 | continue; 133 | } 134 | 135 | if (IsFakeClient(i)) 136 | { 137 | continue; 138 | } 139 | 140 | ret++; 141 | } 142 | 143 | return ret; 144 | } --------------------------------------------------------------------------------