diff --git a/addons/sourcemod/configs/matchmodes.txt b/addons/sourcemod/configs/matchmodes.txt index 03dd1de0f..a97a68049 100644 --- a/addons/sourcemod/configs/matchmodes.txt +++ b/addons/sourcemod/configs/matchmodes.txt @@ -27,11 +27,11 @@ { "zonemod" { - "name" "ZoneMod 2.8.9b" + "name" "ZoneMod 2.8.9c" } "zoneretro" { - "name" "ZoneMod Retro 2.8.9b" + "name" "ZoneMod Retro 2.8.9c" } "zm3v3" { @@ -132,7 +132,7 @@ { "zonehunters" { - "name" "ZoneHunters 2.8.9b" + "name" "ZoneHunters 2.8.9c" } "zh3v3" { diff --git a/addons/sourcemod/plugins/optional/autopause.smx b/addons/sourcemod/plugins/optional/autopause.smx index 24c434ef9..b87c755d8 100644 Binary files a/addons/sourcemod/plugins/optional/autopause.smx and b/addons/sourcemod/plugins/optional/autopause.smx differ diff --git a/addons/sourcemod/scripting/autopause.sp b/addons/sourcemod/scripting/autopause.sp index 18879db20..27c8bcff1 100644 --- a/addons/sourcemod/scripting/autopause.sp +++ b/addons/sourcemod/scripting/autopause.sp @@ -18,173 +18,300 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#pragma semicolon 1 + #pragma newdecls required -#include -#include #include +#include +#include #undef REQUIRE_PLUGIN -#include "readyup" +#include +#include + +#define DEBUG_SM 1 +#define DEBUG_CHAT 2 + +char sDebugMessage[256]; public Plugin myinfo = { - name = "L4D2 Auto-pause", - author = "Darkid, Griffin", - description = "When a player disconnects due to crash, automatically pause the game. When they rejoin, give them a correct spawn timer.", - version = "2.1", - url = "https://github.com/jbzdarkid/AutoPause" + name = "L4D2 Auto-pause", + author = "Darkid, Griffin, StarterX4", + description = "When a player disconnects due to crash, automatically pause the game. When they rejoin, give them a correct spawn timer.", + version = "2.2", + url = "https://github.com/SirPlease/L4D2-Competitive-Rework" } ConVar - g_hCvarEnabled, - g_hCvarForce, - g_hCvarApdebug; + convarEnabled, + convarForce, + convarForceUnpause, + convarDebug; + Handle - crashedPlayers, - infectedPlayers, - survivorPlayers; + crashedPlayers, + generalCrashers, + infectedPlayers, + survivorPlayers; + bool - readyUpIsAvailable, - RoundEnd; + bReadyUpIsAvailable, + bPauseIsAvailable, + bRoundEnd; -public void OnPluginStart() +public void OnPluginStart() { - LoadTranslations("autopause.phrases"); - // Suggestion by Nati: Disable for any 1v1 - g_hCvarEnabled = CreateConVar("autopause_enable", "1", "Whether or not to automatically pause when a player crashes."); - g_hCvarForce = CreateConVar("autopause_force", "0", "Whether or not to force pause when a player crashes."); - g_hCvarApdebug = CreateConVar("autopause_apdebug", "0", "Whether or not to debug information."); - - crashedPlayers = CreateTrie(); - infectedPlayers = CreateArray(64); - survivorPlayers = CreateArray(64); - - HookEvent("round_start", RoundStart_Event); - HookEvent("round_end", RoundEnd_Event); - HookEvent("player_team", PlayerTeam_Event); - HookEvent("player_disconnect", PlayerDisconnect_Event, EventHookMode_Pre); + convarEnabled = CreateConVar("autopause_enable", "1", "Whether or not to automatically pause when a player crashes."); + convarForce = CreateConVar("autopause_force", "0", "Whether or not to force pause when a player crashes."); + convarForceUnpause = CreateConVar("autopause_forceunpause", "0", "Whether or not we force unpause when the crashed players have loaded back in"); + convarDebug = CreateConVar("autopause_apdebug", "0", "0: No Debugging - 1: Sourcemod Logs - 2: PrintToChat - 3: Both", _, true, 0.0, true, 3.0); + + crashedPlayers = CreateTrie(); + generalCrashers = CreateArray(64); + infectedPlayers = CreateArray(64); + survivorPlayers = CreateArray(64); + + HookEvent("round_start", Event_RoundStart); + HookEvent("round_end", Event_RoundEnd); + HookEvent("player_team", Event_PlayerTeam); + HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre); } public void OnAllPluginsLoaded() { - readyUpIsAvailable = LibraryExists("readyup"); + bReadyUpIsAvailable = LibraryExists("readyup"); + bPauseIsAvailable = LibraryExists("pause"); } public void OnLibraryRemoved(const char[] name) { - if (StrEqual(name, "readyup")) readyUpIsAvailable = false; + if (strcmp(name, "readyup") == 0) + bReadyUpIsAvailable = false; + + if (strcmp(name, "pause") == 0) + bPauseIsAvailable = false; } public void OnLibraryAdded(const char[] name) { - if (StrEqual(name, "readyup")) readyUpIsAvailable = true; + if (strcmp(name, "readyup") == 0) + bReadyUpIsAvailable = true; + + if (strcmp(name, "pause") == 0) + bPauseIsAvailable = true; } -public void RoundStart_Event(Handle event, const char[] name, bool dontBroadcast) +public void OnClientPutInServer(int client) { - ClearTrie(crashedPlayers); - ClearArray(infectedPlayers); - ClearArray(survivorPlayers); - RoundEnd = false; + char sAuthId[64]; + GetClientAuthId(client, AuthId_Steam2, sAuthId, sizeof(sAuthId)); + + if (strcmp(sAuthId, "BOT") == 0) + return; + + int crasherIndex = FindStringInArray(generalCrashers, sAuthId); + + if (crasherIndex == -1) + return; + + RemoveFromArray(generalCrashers, crasherIndex); + int remainingCrashers = GetArraySize(generalCrashers); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[Autopause (OnClientPutInServer)] Crashed Player %s rejoined.", sAuthId); + DebugLog(sDebugMessage); + } + + if (convarForceUnpause.BoolValue && bPauseIsAvailable && IsInPause()) + { + if (!remainingCrashers) + { + CPrintToChatAll("{blue}[{default}AutoPause{blue}] {default}All {green}crashed {default}players have reconnected.. {blue}Unpausing{default}!"); + ServerCommand("sm_forceunpause"); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[Autopause (OnClientPutInServer)] All crashed players rejoined. Force Unpause was triggered."); + DebugLog(sDebugMessage); + } + } + else + CPrintToChatAll("{blue}[{default}AutoPause{blue}] {default}Waiting for {olive}%i {default}more {green}crashed {default}player%s {default}before automatic {blue}Unpause{default}!", remainingCrashers, remainingCrashers > 1 ? "s" : ""); + } } -public void RoundEnd_Event(Handle event, const char[] name, bool dontBroadcast) +public void Event_RoundStart(Event hEvent, char[] sEventName, bool dontBroadcast) { - RoundEnd = true; + ClearTrie(crashedPlayers); + ClearArray(generalCrashers); + ClearArray(infectedPlayers); + ClearArray(survivorPlayers); + bRoundEnd = false; +} + +public void Event_RoundEnd(Event hEvent, char[] sEventName, bool dontBroadcast) +{ + bRoundEnd = true; +} + +public void Event_PlayerTeam(Event hEvent, char[] sEventName, bool dontBroadcast) +{ + int client = GetClientOfUserId(hEvent.GetInt("userid")); + + if (client <= 0 || client > MaxClients) + return; + + char sAuthId[64]; + GetClientAuthId(client, AuthId_Steam2, sAuthId, sizeof(sAuthId)); + + if (strcmp(sAuthId, "BOT") == 0) + return; + + int survivorIndex = FindStringInArray(survivorPlayers, sAuthId); + int infectedIndex = FindStringInArray(infectedPlayers, sAuthId); + + if (survivorIndex != -1) + { + RemoveFromArray(survivorPlayers, survivorIndex); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[AutoPause (%s)] Removed player %s from the survivor team.", sEventName, sAuthId); + DebugLog(sDebugMessage); + } + } + + if (infectedIndex != -1) + { + RemoveFromArray(infectedPlayers, infectedIndex); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[AutoPause (%s)] Removed player %s from the infected team.", sEventName, sAuthId); + DebugLog(sDebugMessage); + } + } + + int newTeam = hEvent.GetInt("team"); + + if (newTeam == L4D_TEAM_SURVIVOR) + { + PushArrayString(survivorPlayers, sAuthId); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[AutoPause (%s)] Added player %s to the survivor team.", sEventName, sAuthId); + DebugLog(sDebugMessage); + } + } + else if (newTeam == L4D_TEAM_INFECTED) + { + float fSpawnTime; + + if (GetTrieValue(crashedPlayers, sAuthId, fSpawnTime)) + { + CountdownTimer CTimer_SpawnTimer = L4D2Direct_GetSpawnTimer(client); + CTimer_Start(CTimer_SpawnTimer, fSpawnTime); + RemoveFromTrie(crashedPlayers, sAuthId); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[AutoPause (%s)] Player %s rejoined the infected, set spawn timer to %f.", sEventName, sAuthId, fSpawnTime); + DebugLog(sDebugMessage); + } + } + + PushArrayString(infectedPlayers, sAuthId); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[AutoPause (%s)] Added player %s to the infected team.", sEventName, sAuthId); + DebugLog(sDebugMessage); + } + } } -// Handles players leaving and joining the infected team. -public void PlayerTeam_Event(Event event, const char[] name, bool dontBroadcast) +public void Event_PlayerDisconnect(Event hEvent, char[] sEventName, bool dontBroadcast) { - int client = GetClientOfUserId(GetEventInt(event, "userid")); - if (client <= 0 || client > MaxClients) return; - char steamId[64]; - GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId)); - if (strcmp(steamId, "BOT") == 0) return; - int oldTeam = GetEventInt(event, "oldteam"); - int newTeam = GetEventInt(event, "team"); - - int index = FindStringInArray(infectedPlayers, steamId); - int survindex = FindStringInArray(infectedPlayers, steamId); - if (oldTeam == 3) - { - if (index != -1) RemoveFromArray(infectedPlayers, index); - if (GetConVarBool(g_hCvarApdebug)) LogMessage("[AutoPause] Removed player %s from infected team.", steamId); - } - else if (oldTeam == 2) { - if (survindex != -1) RemoveFromArray(survivorPlayers, survindex); - if (GetConVarBool(g_hCvarApdebug)) LogMessage("[AutoPause] Removed player %s from survivor team.", steamId); - } - if (newTeam == 3) - { - float spawnTime; - if (GetTrieValue(crashedPlayers, steamId, spawnTime)) - { - CountdownTimer spawnTimer = L4D2Direct_GetSpawnTimer(client); - CTimer_Start(spawnTimer, spawnTime); - RemoveFromTrie(crashedPlayers, steamId); - LogMessage("[AutoPause] Player %s rejoined, set spawn timer to %f.", steamId, spawnTime); - } - else if (index == -1) { - PushArrayString(infectedPlayers, steamId); - if (GetConVarBool(g_hCvarApdebug)) LogMessage("[AutoPause] Added player %s to infected team.", steamId); - } - } - else if (newTeam == 2 && survindex == -1) { - PushArrayString(survivorPlayers, steamId); - if (GetConVarBool(g_hCvarApdebug)) LogMessage("[AutoPause] Added player %s to survivor team.", steamId); - } + int client = GetClientOfUserId(hEvent.GetInt("userid")); + + if (client <= 0 || client > MaxClients) + return; + + char sAuthId[64]; + GetClientAuthId(client, AuthId_Steam2, sAuthId, sizeof(sAuthId)); + + if (strcmp(sAuthId, "BOT") == 0) + return; + + if (FindStringInArray(infectedPlayers, sAuthId) == -1 && FindStringInArray(survivorPlayers, sAuthId) == -1) + return; + + if (GetClientTeam(client) == L4D_TEAM_SURVIVOR && !IsPlayerAlive(client)) + { + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[AutoPause (%s)] Player %N left the game but is a dead Survivor", sEventName, client); + DebugLog(sDebugMessage); + } + return; + } + + char sReason[128]; + hEvent.GetString("reason", sReason, sizeof(sReason)); + + char sTimedOut[64]; + Format(sTimedOut, sizeof(sTimedOut), "%N timed out", client); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[AutoPause (%s)] Player %N (%s) left the game: %s", sEventName, client, sAuthId, sReason); + DebugLog(sDebugMessage); + } + + if (strcmp(sReason, sTimedOut) == 0 || strcmp(sReason, "No Steam logon") == 0) + { + if (convarEnabled.BoolValue && (!bReadyUpIsAvailable || !IsInReady()) && !bRoundEnd) + { + if (convarForce.BoolValue) + ServerCommand("sm_forcepause"); + else + FakeClientCommand(client, "sm_pause"); + + if (FindStringInArray(generalCrashers, sAuthId) == -1) + PushArrayString(generalCrashers, sAuthId); + + CPrintToChatAll("{blue}[{default}AutoPause{blue}] {olive}%N {default}crashed.", client); + } + } + + if (FindStringInArray(infectedPlayers, sAuthId) != -1) + { + CountdownTimer CTimer_SpawnTimer = L4D2Direct_GetSpawnTimer(client); + if (CTimer_SpawnTimer != CTimer_Null) + { + float fTimeLeft = CTimer_GetRemainingTime(CTimer_SpawnTimer); + + if (convarDebug.BoolValue) + { + Format(sDebugMessage, sizeof(sDebugMessage), "[AutoPause (%s)] Player %s left the game with %f time until spawn.", sEventName, sAuthId, fTimeLeft); + DebugLog(sDebugMessage); + } + + SetTrieValue(crashedPlayers, sAuthId, fTimeLeft); + } + } } -public void PlayerDisconnect_Event(Event hEvent, const char[] name, bool dontBroadcast) +void DebugLog(char[] sMessage) { - int client = GetClientOfUserId(GetEventInt(hEvent, "userid")); - if (client <= 0 || client > MaxClients) return; - char steamId[64]; - GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId)); - if (strcmp(steamId, "BOT") == 0) return; - - // Player wasn't actually a gamer, ignore - if (FindStringInArray(infectedPlayers, steamId) == -1 && FindStringInArray(survivorPlayers, steamId) == -1) return; - - char reason[128]; - GetEventString(hEvent, "reason", reason, sizeof(reason)); - char playerName[128]; - GetEventString(hEvent, "name", playerName, sizeof(playerName)); - char timedOut[256]; - Format(timedOut, sizeof(timedOut), "%s timed out", playerName); - - if (GetConVarBool(g_hCvarApdebug)) LogMessage("[AutoPause] Player %s (%s) left the game: %s", playerName, steamId, reason); - - // If the leaving player crashed, pause. - if (strcmp(reason, timedOut) == 0 || strcmp(reason, "No Steam logon") == 0) - { - if ((!readyUpIsAvailable || !IsInReady()) && !RoundEnd && GetConVarBool(g_hCvarEnabled)) - { - if (GetConVarBool(g_hCvarForce)) - { - ServerCommand("sm_forcepause"); - } - else - { - FakeClientCommand(client, "sm_pause"); - } - CPrintToChatAll("%t %t", "Tag", "crashed", playerName); - } - } - - // If the leaving player was on infected, save their spawn timer. - if (FindStringInArray(infectedPlayers, steamId) != -1) - { - float timeLeft; - CountdownTimer spawnTimer = L4D2Direct_GetSpawnTimer(client); - if (spawnTimer != CTimer_Null) - { - timeLeft = CTimer_GetRemainingTime(spawnTimer); - LogMessage("[AutoPause] Player %s left the game with %f time until spawn.", steamId, timeLeft); - SetTrieValue(crashedPlayers, steamId, timeLeft); - } - } + int flags = convarDebug.IntValue; + + if (flags & DEBUG_SM) + LogMessage(sMessage); + + if (flags & DEBUG_CHAT) + PrintToChatAll(sMessage); } \ No newline at end of file diff --git a/cfg/cfgogl/zh1v1/confogl.cfg b/cfg/cfgogl/zh1v1/confogl.cfg index 6a12227c1..e07554963 100644 --- a/cfg/cfgogl/zh1v1/confogl.cfg +++ b/cfg/cfgogl/zh1v1/confogl.cfg @@ -7,7 +7,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "1v1 [Zone]Hunters v2.8.9b" +l4d_ready_cfg_name "1v1 [Zone]Hunters v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config. diff --git a/cfg/cfgogl/zh2v2/confogl.cfg b/cfg/cfgogl/zh2v2/confogl.cfg index b27d3c824..d11a8bde2 100644 --- a/cfg/cfgogl/zh2v2/confogl.cfg +++ b/cfg/cfgogl/zh2v2/confogl.cfg @@ -7,7 +7,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "2v2 [Zone]Hunters v2.8.9b" +l4d_ready_cfg_name "2v2 [Zone]Hunters v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config. diff --git a/cfg/cfgogl/zh3v3/confogl.cfg b/cfg/cfgogl/zh3v3/confogl.cfg index 925e38f96..dc28d7081 100644 --- a/cfg/cfgogl/zh3v3/confogl.cfg +++ b/cfg/cfgogl/zh3v3/confogl.cfg @@ -7,7 +7,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "3v3 [Zone]Hunters v2.8.9b" +l4d_ready_cfg_name "3v3 [Zone]Hunters v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config. diff --git a/cfg/cfgogl/zm1v1/confogl.cfg b/cfg/cfgogl/zm1v1/confogl.cfg index 1d1a3622c..f98828782 100644 --- a/cfg/cfgogl/zm1v1/confogl.cfg +++ b/cfg/cfgogl/zm1v1/confogl.cfg @@ -8,7 +8,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "1v1 Zonemod v2.8.9b" +l4d_ready_cfg_name "1v1 Zonemod v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config. diff --git a/cfg/cfgogl/zm2v2/confogl.cfg b/cfg/cfgogl/zm2v2/confogl.cfg index ff3ce7022..e4c48f4f6 100644 --- a/cfg/cfgogl/zm2v2/confogl.cfg +++ b/cfg/cfgogl/zm2v2/confogl.cfg @@ -8,7 +8,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "2v2 Zonemod v2.8.9b" +l4d_ready_cfg_name "2v2 Zonemod v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config. diff --git a/cfg/cfgogl/zm3v3/confogl.cfg b/cfg/cfgogl/zm3v3/confogl.cfg index 02ac843ce..079cda6dd 100644 --- a/cfg/cfgogl/zm3v3/confogl.cfg +++ b/cfg/cfgogl/zm3v3/confogl.cfg @@ -8,7 +8,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "3v3 Zonemod v2.8.9b" +l4d_ready_cfg_name "3v3 Zonemod v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config. diff --git a/cfg/cfgogl/zonehunters/confogl.cfg b/cfg/cfgogl/zonehunters/confogl.cfg index 6b03cd945..6e3b8f2d8 100644 --- a/cfg/cfgogl/zonehunters/confogl.cfg +++ b/cfg/cfgogl/zonehunters/confogl.cfg @@ -7,7 +7,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "[Zone]Hunters v2.8.9b" +l4d_ready_cfg_name "[Zone]Hunters v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config. diff --git a/cfg/cfgogl/zonemod/confogl.cfg b/cfg/cfgogl/zonemod/confogl.cfg index 4a9877ffc..6a9c0d2bb 100644 --- a/cfg/cfgogl/zonemod/confogl.cfg +++ b/cfg/cfgogl/zonemod/confogl.cfg @@ -8,7 +8,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "ZoneMod v2.8.9b" +l4d_ready_cfg_name "ZoneMod v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config. diff --git a/cfg/cfgogl/zoneretro/confogl.cfg b/cfg/cfgogl/zoneretro/confogl.cfg index 605bb8ab5..ec60c8fd8 100644 --- a/cfg/cfgogl/zoneretro/confogl.cfg +++ b/cfg/cfgogl/zoneretro/confogl.cfg @@ -8,7 +8,7 @@ // ======================================================================================= // ReadyUp Cvars -l4d_ready_cfg_name "ZoneMod Retro v2.8.9b" +l4d_ready_cfg_name "ZoneMod Retro v2.8.9c" // Confogl Cvars confogl_addcvar mp_gamemode "versus" // Force Versus for the config.