Skip to content

Instantly share code, notes, and snippets.

@StarterX4
Last active January 22, 2024 20:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StarterX4/3a58bf95dc45d80bb479d2b55a7371fd to your computer and use it in GitHub Desktop.
Save StarterX4/3a58bf95dc45d80bb479d2b55a7371fd to your computer and use it in GitHub Desktop.
/*
SourcePawn is Copyright (C) 2006-2015 AlliedModders LLC. All rights reserved.
SourceMod is Copyright (C) 2006-2015 AlliedModders LLC. All rights reserved.
Pawn and SMALL are Copyright (C) 1997-2015 ITB CompuPhase.
Source is Copyright (C) Valve Corporation.
All trademarks are property of their respective owners.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sourcemod>
#include <left4dhooks>
#include <colors>
#undef REQUIRE_PLUGIN
#include "readyup"
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"
}
new Handle:g_hCvarEnabled;
new Handle:g_hCvarForce;
new Handle:g_hCvarApdebug;
new Handle:g_hCvarForceUnPause;
new Handle:g_hCvarAutoReady;
new Handle:crashedPlayers;
new Handle:crashedPlayers2;
new Handle:infectedPlayers;
new Handle:survivorPlayers;
new bool:readyUpIsAvailable;
new bool:RoundEnd;
public OnPluginStart() {
// 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.");
g_hCvarForceUnPause = CreateConVar("autopause_forceunpause", "0", "Whether or not to force unpause when a crashed player rejoins.");
g_hCvarAutoReady = CreateConVar("autopause_autoready", "1", "Whether or not to automatically mark crashed player ready for an unpause on rejoin.");
crashedPlayers = CreateTrie();
crashedPlayers2 = CreateArray(64);
infectedPlayers = CreateArray(64);
survivorPlayers = CreateArray(64);
HookEvent("round_start", round_start);
HookEvent("round_end", round_end);
HookEvent("player_team", playerTeam);
HookEvent("player_disconnect", playerDisconnect, EventHookMode_Pre);
}
public OnAllPluginsLoaded()
{
readyUpIsAvailable = LibraryExists("readyup");
}
public OnLibraryRemoved(const String:name[])
{
if (StrEqual(name, "readyup")) readyUpIsAvailable = false;
}
public OnLibraryAdded(const String:name[])
{
if (StrEqual(name, "readyup")) readyUpIsAvailable = true;
}
public round_start(Handle:event, const String:name[], bool:dontBroadcast) {
ClearTrie(crashedPlayers);
ClearArray(crashedPlayers2);
ClearArray(infectedPlayers);
ClearArray(survivorPlayers);
RoundEnd = false;
}
public round_end(Handle:event, const String:name[], bool:dontBroadcast) {
RoundEnd = true;
}
// Handles players leaving and joining the infected team.
public playerTeam(Handle:event, const String:name[], bool:dontBroadcast) {
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client <= 0 || client > MaxClients) return;
decl String:steamId[64];
GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId));
if (strcmp(steamId, "BOT") == 0) return;
new oldTeam = GetEventInt(event, "oldteam");
new newTeam = GetEventInt(event, "team");
new index = FindStringInArray(infectedPlayers, steamId);
new survindex = FindStringInArray(survivorPlayers, 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) {
decl Float:spawnTime;
if (GetTrieValue(crashedPlayers, steamId, spawnTime)) {
new CountdownTimer:spawnTimer = L4D2Direct_GetSpawnTimer(client);
CTimer_Start(spawnTimer, spawnTime);
RemoveFromTrie(crashedPlayers, steamId);
if (GetConVarBool(g_hCvarForceUnPause) || GetConVarBool(g_hCvarForce))
{
ServerCommand("sm_forceunpause");
}
else if (GetConVarBool(g_hCvarAutoReady))
{
FakeClientCommand(client, "sm_unpause");
}
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) {
if (FindStringInArray(crashedPlayers2, steamId)) {
RemoveFromArray(crashedPlayers2, FindStringInArray(crashedPlayers2, steamId));
if (GetConVarBool(g_hCvarForceUnPause) || GetConVarBool(g_hCvarForce))
{
ServerCommand("sm_forceunpause");
}
else if (GetConVarBool(g_hCvarAutoReady))
{
FakeClientCommand(client, "sm_unpause");
}
LogMessage("[AutoPause] Player %s rejoined to survivors.", steamId);
} else if (survindex == -1) {
PushArrayString(survivorPlayers, steamId);
if (GetConVarBool(g_hCvarApdebug)) LogMessage("[AutoPause] Added player %s to survivor team.", steamId);
}
}
}
public playerDisconnect(Handle:event, const String:name[], bool:dontBroadcast) {
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if (client <= 0 || client > MaxClients) return;
decl String: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;
decl String:reason[128];
GetEventString(event, "reason", reason, sizeof(reason));
decl String:playerName[128];
GetEventString(event, "name", playerName, sizeof(playerName));
decl String: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("{blue}[{default}AutoPause{blue}] {olive}%s {default}crashed.", playerName);
}
}
// If the leaving player was on infected, save their spawn timer.
if (FindStringInArray(infectedPlayers, steamId) != -1) {
decl Float:timeLeft;
new 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);
}
}
// If the leaving player was on survivors, save them as well.
if (FindStringInArray(survivorPlayers, steamId) != -1) {
LogMessage("[AutoPause] Player %s left the game.", steamId);
PushArrayString(crashedPlayers2, steamId);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment