Compare commits

...
3 Commits
2 changed files with 251 additions and 58 deletions
@@ -18,7 +18,7 @@ public Plugin myinfo =
name = "LagCompensation",
author = "BotoX, Vauff, .Rushaway, maxime1907",
description = "",
version = "1.0.6",
version = "1.1.0",
url = ""
};
@@ -28,7 +28,7 @@ bool g_bHasOnEntitySpawned = false;
bool g_bCheckPing = false;
int g_iCompenseEntityClass;
// Dont change this.
// Don't change this.
#define MAX_EDICTS 2048
#define FSOLID_FORCE_WORLD_ALIGNED 0x0040
#define FSOLID_ROOT_PARENT_ALIGNED 0x0100
@@ -104,6 +104,7 @@ enum struct EntityLagData
int iNotMoving;
bool bRestore;
bool bLateKill;
char sClassname[64];
}
LagRecord g_aaLagRecords[MAX_ENTITIES][MAX_RECORDS];
@@ -159,10 +160,11 @@ int g_aaFilterClientSolidTouch[((MAXPLAYERS + 1) * MAX_EDICTS) / 32];
int g_aBlockTriggerMoved[MAX_EDICTS / 32];
int g_aBlacklisted[MAX_EDICTS / 32];
Handle g_hCookie_EnableLagComp;
Handle g_hCookie_LagCompMessages;
bool g_bEnableLagComp[MAXPLAYERS + 1];
Handle g_hCookie_LagCompSettings;
bool g_bDisableLagComp[MAXPLAYERS + 1];
bool g_bPlayerLaserCompensated[MAXPLAYERS + 1];
bool g_bLagCompMessages[MAXPLAYERS + 1];
bool g_bSettingsTouched[MAXPLAYERS + 1];
int g_iDisableLagComp[MAXPLAYERS + 1];
ConVar g_cvCompenseEntityClass;
ConVar g_cvCheckUserPing;
@@ -173,8 +175,8 @@ public void OnPluginStart()
LoadTranslations("common.phrases");
g_cvCompenseEntityClass = CreateConVar("sm_lagcomp_entityclass", "0", "Only lagcompensate: [0 = All | 1 = func_physbox only]", _, true, 0.0, true, 1.0);
g_cvCheckUserPing = CreateConVar("sm_lagcomp_checkuserping", "0", "Check users ping before lagcompensating them [0 = Disabled | 1 = Enabled]", _, true, 0.0, true, 1.0);
g_cvMinimumPing = CreateConVar("sm_lagcomp_ping", "150", "Minimum ping threshold to apply lagcomp on a client", _, true, 0.0);
g_cvCheckUserPing = CreateConVar("sm_lagcomp_checkuserping", "0", "Check user's ping before lagcompensating them [0 = Disabled | 1 = Enabled]", _, true, 0.0, true, 1.0);
g_cvMinimumPing = CreateConVar("sm_lagcomp_ping", "180", "Minimum ping threshold to apply lagcomp on a client", _, true, 0.0);
AutoExecConfig(true);
@@ -347,16 +349,16 @@ public void OnPluginStart()
HookConVarChange(g_cvMinimumPing, OnConVarChanged);
g_hCookie_EnableLagComp = RegClientCookie("enable_lagcomp", "Client LagComp Status", CookieAccess_Private);
g_hCookie_LagCompMessages = RegClientCookie("lagcomp_messages", "Print LagComp messages in chat", CookieAccess_Private);
RegConsoleCmd("sm_lagcomp", OnToggleLagCompSettings);
g_hCookie_LagCompSettings = RegClientCookie("lagcomp_settings", "LagCompensation Settings", CookieAccess_Private);
RegConsoleCmd("sm_lagcomp", OnLagCompSettings);
RegConsoleCmd("sm_0ping", OnToggleLagCompSettings);
RegConsoleCmd("sm_0pinglasers", OnToggleLagCompLasersSettings);
RegConsoleCmd("sm_checklag", CheckLagComp);
SetCookieMenuItem(MenuHandler_CookieMenu, 0, "LagCompensation");
CreateTimer(0.1, DisableLagCompTimer, _, TIMER_REPEAT);
RegAdminCmd("sm_unlag", Command_AddLagCompensation, ADMFLAG_ROOT, "sm_unlag <entidx>");
RegAdminCmd("sm_unlag", Command_AddLagCompensation, ADMFLAG_ROOT, "sm_unlag <entidx> [late]");
RegAdminCmd("sm_lagged", Command_CheckLagCompensated, ADMFLAG_ROOT, "sm_lagged");
FilterClientSolidTouch(g_aaFilterClientSolidTouch, true);
@@ -486,18 +488,27 @@ public void OnMapEnd()
public void OnClientConnected(int client)
{
g_bEnableLagComp[client] = false;
// Must match the defaults in ParseClientCookie() for clients whose cookies never cache.
g_bDisableLagComp[client] = false;
g_bPlayerLaserCompensated[client] = true;
g_bLagCompMessages[client] = false;
g_bSettingsTouched[client] = false;
g_iDisableLagComp[client] = 0;
g_aLerpTicks[client] = 0;
}
public void OnClientCookiesCached(int client)
{
char sBuffer[16];
GetClientCookie(client, g_hCookie_EnableLagComp, sBuffer, sizeof(sBuffer));
g_bEnableLagComp[client] = StringToInt(sBuffer) != 0; //this wording should make more sense, if the cookie exists its true to enable it.
GetClientCookie(client, g_hCookie_LagCompMessages, sBuffer, sizeof(sBuffer));
g_bLagCompMessages[client] = StringToInt(sBuffer) != 0;
// A toggle made before the cookies arrived wins, so flush it instead of
// overwriting the client's choice with the stored value.
if(g_bSettingsTouched[client])
{
SaveLagCompSettings(client);
return;
}
// Load all settings from single cookie
ParseClientCookie(client, g_bDisableLagComp[client], g_bPlayerLaserCompensated[client], g_bLagCompMessages[client]);
}
public void OnClientSettingsChanged(int client)
@@ -511,7 +522,10 @@ public void OnClientSettingsChanged(int client)
public void OnClientDisconnect(int client)
{
g_bEnableLagComp[client] = false;
g_bDisableLagComp[client] = false;
g_bPlayerLaserCompensated[client] = true;
g_bLagCompMessages[client] = false;
g_bSettingsTouched[client] = false;
g_iDisableLagComp[client] = 0;
}
@@ -529,8 +543,9 @@ public void OnEntityCreated(int entity, const char[] classname)
DHookEntity(g_hAcceptInput, true, entity);
}
if (!g_bHasOnEntitySpawned && (!strncmp(classname, "func_physbox", 12, false) || (g_iCompenseEntityClass == 0 && strcmp(classname, "trigger_hurt", false) == 0 ||
strcmp(classname, "trigger_push", false) == 0 || strcmp(classname, "trigger_teleport", false) == 0)))
if (!g_bHasOnEntitySpawned && (!strncmp(classname, "func_physbox", 12, false) ||
(g_iCompenseEntityClass == 0 && (strcmp(classname, "trigger_hurt", false) == 0 ||
strcmp(classname, "trigger_push", false) == 0 || strcmp(classname, "trigger_teleport", false) == 0))))
{
SDKHook(entity, SDKHook_SpawnPost, OnSDKHookEntitySpawnPost);
}
@@ -570,7 +585,7 @@ public MRESReturn Hook_AcceptInput(int entity, Handle hReturn, Handle hParams)
if(iCaller <= 0 || iCaller >= MAX_EDICTS)
return MRES_Ignored;
// Dont lagcompensate anything that has a game_ui button in their hierarchy.
// Don't lagcompensate anything that has a game_ui button in their hierarchy.
BlacklistFamily(iCaller);
return MRES_Ignored;
@@ -623,7 +638,7 @@ void BlacklistChildren(int parent)
bool CheckEntityForLagComp(int entity, const char[] classname, bool bRecursive=false, bool bGoodParents=false)
{
if(entity < 0 || entity > MAX_EDICTS)
if(entity < 0 || entity >= MAX_EDICTS)
return false;
if(!IsValidEntity(entity))
@@ -668,7 +683,7 @@ bool CheckEntityForLagComp(int entity, const char[] classname, bool bRecursive=f
return false;
}
if(CheckBit(g_aBlacklisted, entity))
if(CheckBit(g_aBlacklisted, iParent))
{
return false;
}
@@ -736,7 +751,7 @@ public void OnEntityDestroyed(int entity)
if(g_bCleaningUp)
return;
if(entity < 0 || entity > MAX_EDICTS)
if(entity < 0 || entity >= MAX_EDICTS)
return;
ClearBit(g_aBlacklisted, entity);
@@ -758,7 +773,7 @@ public MRESReturn Detour_OnUTIL_Remove(Handle hParams)
return MRES_Ignored;
int entity = DHookGetParamObjectPtrVar(hParams, 1, g_iNetworkableOuter, ObjectValueType_CBaseEntityPtr);
if(entity < 0 || entity > MAX_EDICTS)
if(entity < 0 || entity >= MAX_EDICTS)
return MRES_Ignored;
int iIndex = g_aLagCompensated[entity];
@@ -858,8 +873,8 @@ public MRESReturn Detour_OnFrameUpdatePostEntityThink()
for(int i = 0; i < g_iNumEntities; i++)
{
// Dont make the entity check untouch in FrameUpdatePostEntityThink.
// If the player didnt get simulated in the current frame then
// they didnt have a chance to touch this entity.
// If the player didn't get simulated in the current frame then
// they didn't have a chance to touch this entity.
// Hence the touchlink could be broken and we only let the player check untouch.
int EFlags = GetEntData(g_aEntityLagData[i].iEntity, g_iEFlags);
EFlags &= ~EFL_CHECK_UNTOUCH;
@@ -877,7 +892,7 @@ public void OnRunThinkFunctions(bool simulating)
public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2])
{
if(!IsPlayerAlive(client) || IsFakeClient(client))
if(!IsClientInGame(client) || !IsPlayerAlive(client) || IsFakeClient(client))
return Plugin_Continue;
int iTargetTick = tickcount - g_aLerpTicks[client];
@@ -928,6 +943,13 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
continue;
int iRecord = iDelta;
if(!g_bPlayerLaserCompensated[client] &&
(strcmp(g_aEntityLagData[i].sClassname, "trigger_hurt", false) == 0 ||
strcmp(g_aEntityLagData[i].sClassname, "trigger_push", false) == 0 ||
strcmp(g_aEntityLagData[i].sClassname, "trigger_teleport", false) == 0))
iRecord = 0;
if(iRecord >= g_aEntityLagData[i].iNumRecords)
iRecord = g_aEntityLagData[i].iNumRecords - 1;
@@ -1112,6 +1134,10 @@ bool AddEntityForLagCompensation(int iEntity, bool bLateKill)
if(g_bCleaningUp)
return false;
// sm_unlag takes an arbitrary index from the console, so validate before indexing anything.
if(iEntity < 0 || iEntity >= MAX_EDICTS || !IsValidEntity(iEntity))
return false;
if(g_aLagCompensated[iEntity] != -1)
return false;
@@ -1144,6 +1170,7 @@ bool AddEntityForLagCompensation(int iEntity, bool bLateKill)
g_aEntityLagData[i].iNotMoving = MAX_RECORDS;
g_aEntityLagData[i].bRestore = false;
g_aEntityLagData[i].bLateKill = bLateKill;
g_aEntityLagData[i].sClassname = sClassname;
if(bLateKill)
{
@@ -1226,6 +1253,7 @@ void EntityLagData_Copy(EntityLagData obj, const EntityLagData other)
obj.iNotMoving = other.iNotMoving;
obj.bRestore = other.bRestore;
obj.bLateKill = other.bLateKill;
obj.sClassname = other.sClassname;
}
void LagRecord_Copy(LagRecord obj, const LagRecord other)
@@ -1253,6 +1281,59 @@ bool CompareVectors(const float vec1[3], const float vec2[3])
return vec1[0] == vec2[0] && vec1[1] == vec2[1] && vec1[2] == vec2[2];
}
void SaveLagCompSettings(int client)
{
// Remember that this client chose something, so OnClientCookiesCached
// flushes it rather than loading over the top of it.
g_bSettingsTouched[client] = true;
// Cookie reads/writes are meaningless until the cookies arrive; we get called back then.
if(!AreClientCookiesCached(client))
return;
// Read current cookie values
bool currentDisableLagComp, currentPlayerLaserCompensated, currentLagCompMessages;
ParseClientCookie(client, currentDisableLagComp, currentPlayerLaserCompensated, currentLagCompMessages);
// Check if values have changed
bool disableChanged = g_bDisableLagComp[client] != currentDisableLagComp;
bool laserChanged = g_bPlayerLaserCompensated[client] != currentPlayerLaserCompensated;
bool messagesChanged = g_bLagCompMessages[client] != currentLagCompMessages;
// If no values have changed, no need to save
if (!disableChanged && !laserChanged && !messagesChanged) {
return;
}
// Otherwise, save the chain format
char sBuffer[8];
Format(sBuffer, sizeof(sBuffer), "%d%d%d",
g_bDisableLagComp[client] ? 1 : 0,
g_bPlayerLaserCompensated[client] ? 1 : 0,
g_bLagCompMessages[client] ? 1 : 0);
SetClientCookie(client, g_hCookie_LagCompSettings, sBuffer);
}
void ParseClientCookie(int client, bool &disableLagComp, bool &playerLaserCompensated, bool &lagCompMessages)
{
char buffer[16];
GetClientCookie(client, g_hCookie_LagCompSettings, buffer, sizeof(buffer));
// Parse chain format: disable|laser|messages
if (strlen(buffer) >= 3)
{
disableLagComp = (buffer[0] == '1');
playerLaserCompensated = (buffer[1] == '1');
lagCompMessages = (buffer[2] == '1');
}
else
{
// Default values
disableLagComp = false;
playerLaserCompensated = true;
lagCompMessages = false;
}
}
public Action Command_AddLagCompensation(int client, int argc)
{
@@ -1355,9 +1436,9 @@ stock void PrintToBoth(const char[] format, any ...)
for(int client = 1; client <= MaxClients; client++)
{
if (IsClientInGame(client) && g_bLagCompMessages[client])
if (IsClientInGame(client) && g_bLagCompMessages[client] && CheckCommandAccess(client, "sm_lagged", ADMFLAG_ROOT))
{
PrintToChat(client, "%s", buffer);
PrintToConsole(client, "%s", buffer);
}
}
}
@@ -1370,22 +1451,25 @@ public Action DisableLagCompTimer(Handle timer)
continue;
// https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/game/server/util.cpp#L747
// Lag compensation only kicks in above the ping threshold, so ramp it off below it.
if (g_bCheckPing)
{
int iAvgPing = RoundFloat(GetClientAvgLatency(client, NetFlow_Outgoing) * 1000);
if (iAvgPing > g_iMinPing)
if (iAvgPing <= g_iMinPing)
{
g_iDisableLagComp[client]++;
return Plugin_Continue;
if(g_iDisableLagComp[client] < MAX_RECORDS)
g_iDisableLagComp[client]++;
continue;
}
}
if(!g_bEnableLagComp[client] && g_iDisableLagComp[client] < MAX_RECORDS)
if(g_bDisableLagComp[client] && g_iDisableLagComp[client] < MAX_RECORDS)
{
g_iDisableLagComp[client]++;
}
else if(g_bEnableLagComp[client] && g_iDisableLagComp[client] > 0)
else if(!g_bDisableLagComp[client] && g_iDisableLagComp[client] > 0)
{
g_iDisableLagComp[client]--;
}
@@ -1406,33 +1490,52 @@ public Action OnToggleLagCompSettings(int client, int args)
return Plugin_Handled;
}
public Action OnToggleLagCompLasersSettings(int client, int args)
{
ToggleLagCompLasers(client);
return Plugin_Handled;
}
public void ToggleLagCompSettings(int client)
{
if(!client)
if(!client || !IsClientInGame(client) || IsFakeClient(client))
return;
g_bEnableLagComp[client] = !g_bEnableLagComp[client];
SetClientCookie(client, g_hCookie_EnableLagComp, g_bEnableLagComp[client] ? "1" : "");
g_bDisableLagComp[client] = !g_bDisableLagComp[client];
SaveLagCompSettings(client);
CPrintToChat(client, "%s LagCompensation has been %s.", PREFIX, g_bEnableLagComp[client] ? "enabled" : "disabled");
CPrintToChat(client, "%s LagCompensation has been %s.", PREFIX, g_bDisableLagComp[client] ? "disabled" : "enabled");
}
public void ToggleLagCompLasers(int client)
{
if(!client || !IsClientInGame(client) || IsFakeClient(client))
return;
g_bPlayerLaserCompensated[client] = !g_bPlayerLaserCompensated[client];
SaveLagCompSettings(client);
CPrintToChat(client, "%s LagCompensation for Lasers has been %s.", PREFIX, g_bPlayerLaserCompensated[client] ? "enabled" : "disabled");
if (g_bCheckPing && g_bPlayerLaserCompensated[client])
CPrintToChat(client, "%s It will take effect if your real ping is higher than %dms.", PREFIX, g_iMinPing);
}
public void ToggleLagCompMessages(int client)
{
if(!client)
if(!client || !IsClientInGame(client) || IsFakeClient(client))
return;
g_bLagCompMessages[client] = !g_bLagCompMessages[client];
SetClientCookie(client, g_hCookie_LagCompMessages, g_bLagCompMessages[client] ? "1" : "");
SaveLagCompSettings(client);
CPrintToChat(client, "%s LagCompensation messages have been %s.", PREFIX, g_bLagCompMessages[client] ? "enabled" : "disabled");
}
public Action CheckLagComp(int client, int args)
{
if (args > 1)
if (args > 1 || (args == 0 && !client))
{
CPrintToChat(client, "%s Usage sm_checklag <client>", PREFIX);
CReplyToCommand(client, "%s Usage: sm_checklag <client>", PREFIX);
return Plugin_Handled;
}
@@ -1455,7 +1558,14 @@ public Action CheckLagComp(int client, int args)
return Plugin_Handled;
}
CPrintToChat(client, "%s LagCompensation is %s for {olive}%N", PREFIX, g_bEnableLagComp[target] ? sEnable : "disabled", target);
CReplyToCommand(client, "%s LagCompensation is %s for {olive}%N", PREFIX, g_bDisableLagComp[target] ? "disabled" : sEnable, target);
CReplyToCommand(client, "%s Laser compensation: %s", PREFIX, g_bPlayerLaserCompensated[target] ? "enabled" : "disabled");
// g_iDisableLagComp also ramps from the ping threshold, so report it when they haven't opted out.
if(!g_bDisableLagComp[target] && g_iDisableLagComp[target] > 0)
CReplyToCommand(client, "%s Currently reduced: %d of %d ticks skipped%s.", PREFIX,
g_iDisableLagComp[target], MAX_RECORDS,
g_bCheckPing ? " (ping at or below the threshold ramps this up)" : "");
return Plugin_Handled;
}
@@ -1463,15 +1573,17 @@ public Action CheckLagComp(int client, int args)
public void ShowSettingsMenu(int client)
{
Menu menu = new Menu(MenuHandler_MainMenu);
menu.SetTitle("LagCompensation Settings", client);
menu.SetTitle("LagCompensation Settings");
menu.ExitBackButton = true;
char buffer[128], msg_buffer[128];
Format(buffer, sizeof(buffer), "LagCompensation: %s", g_bEnableLagComp[client] ? "Enabled" : "Disabled");
char buffer[128], buffer_lasers[128], msg_buffer[128];
Format(buffer, sizeof(buffer), "LagCompensation: %s", g_bDisableLagComp[client] ? "Disabled" : "Enabled");
Format(buffer_lasers, sizeof(buffer_lasers), "LagCompensation for Lasers: %s", g_bPlayerLaserCompensated[client] ? "Enabled" : "Disabled");
Format(msg_buffer, sizeof(msg_buffer), "LagCompensation Messages: %s", g_bLagCompMessages[client] ? "Enabled" : "Disabled");
menu.AddItem("0", buffer);
menu.AddItem("1", msg_buffer);
menu.AddItem("1", buffer_lasers);
menu.AddItem("2", msg_buffer, CheckCommandAccess(client, "sm_lagged", ADMFLAG_ROOT) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
menu.Display(client, MENU_TIME_FOREVER);
}
@@ -1482,7 +1594,7 @@ public void MenuHandler_CookieMenu(int client, CookieMenuAction action, any info
{
case(CookieMenuAction_DisplayOption):
{
Format(buffer, maxlen, "LagCompensation", client);
strcopy(buffer, maxlen, "LagCompensation");
}
case(CookieMenuAction_SelectOption):
{
@@ -1500,7 +1612,8 @@ public int MenuHandler_MainMenu(Menu menu, MenuAction action, int client, int se
switch(selection)
{
case(0): ToggleLagCompSettings(client);
case(1): ToggleLagCompMessages(client);
case(1): ToggleLagCompLasers(client);
case(2): ToggleLagCompMessages(client);
}
ShowSettingsMenu(client);
+88 -8
View File
@@ -21,9 +21,16 @@ Menu g_NpcMenu = null;
KeyValues g_Models;
ConVar g_cvNotifyShot;
ConVar g_cvNotifyTimer;
ArrayList g_aSpawnedProps; // Tracks entity indices of props spawned by this plugin so we can unhook them
ArrayList g_aNotifyList;
enum struct NotifyListData
{
int client;
Handle timer;
}
public void OnPluginStart()
{
@@ -49,9 +56,11 @@ public void OnPluginStart()
RegAdminCmd("sm_propspawner", Command_MainMenu, ADMFLAG_CHANGEMAP); //g
g_cvNotifyShot = CreateConVar("sm_propspawner_notify_shot", "0", "If set to 1, announces in chat when a plugin-spawned prop is shot.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
g_cvNotifyTimer = CreateConVar("sm_propspawner_notify_shot_timer", "0.5", "How long a player stays in the \"shooting couch\" list.");
AutoExecConfig(true, "PropSpawner");
g_aSpawnedProps = new ArrayList();
g_aNotifyList = new ArrayList();
HookEvent("round_end", Event_RoundEnd);
}
@@ -226,7 +235,7 @@ public int MainMenuHandler(Menu menu, MenuAction action, int client, int param2)
}
}
}
return 0;
return 0;
}
public RotationHandler(Menu menu, MenuAction action, int client, int param2)
@@ -547,7 +556,7 @@ void prop_any_create(int client)
// Hook the prop so we can detect when it gets shot/damaged, and track it so we can unhook later
if (g_cvNotifyShot.BoolValue)
{
SetEntityFlags(prop, FL_CLIENT); //Now youre thinking with portals
SetEntityFlags(prop, FL_CLIENT); //Now youre thinking with portals
SDKHook(prop, SDKHook_OnTakeDamage, OnPropTakeDamage);
g_aSpawnedProps.Push(prop);
}
@@ -566,14 +575,85 @@ public Action OnPropTakeDamage(int victim, int &attacker, int &inflictor, float
if (attacker < 1 || attacker > MaxClients || !IsClientInGame(attacker))
return Plugin_Continue;
char sPropName[64];
GetEntPropString(victim, Prop_Data, "m_ModelName", sPropName, sizeof(sPropName));
Handle timer = CreateTimer(g_cvNotifyTimer.FloatValue, OnNotifyTimer, attacker);
int idx = FindInNotifyList(attacker);
if (idx == -1)
{
NotifyListData data;
data.client = attacker;
data.timer = timer;
g_aNotifyList.PushArray(data, sizeof(NotifyListData));
}
else
{
NotifyListData buf;
g_aNotifyList.GetArray(idx, buf, sizeof(NotifyListData));
if (buf.timer != INVALID_HANDLE)
KillTimer(buf.timer);
char sAttackerName[MAX_NAME_LENGTH];
GetClientName(attacker, sAttackerName, sizeof(sAttackerName));
PrintToChatAll("\x01[SM] \x04%s\x01 is shooting the couch.", sAttackerName);
buf.timer = timer;
g_aNotifyList.SetArray(idx, buf, sizeof(NotifyListData));
}
DisplayNotifyList();
return Plugin_Continue;
}
void DisplayNotifyList()
{
static int channel = -1;
char text[255];
strcopy(text, sizeof(text), "Shooting Couch:\n");
for (int i = g_aNotifyList.Length - 1; i >= 0; i--)
{
NotifyListData buf;
g_aNotifyList.GetArray(i, buf, sizeof(NotifyListData));
if (!IsClientInGame(buf.client))
{
if (buf.timer != INVALID_HANDLE)
KillTimer(buf.timer);
g_aNotifyList.Erase(i);
continue;
}
char clientName[32];
GetClientName(buf.client, clientName, sizeof(clientName));
StrCat(text, sizeof(text), clientName);
StrCat(text, sizeof(text), "\n");
}
SetHudTextParams(0.8, -1.0, g_cvNotifyTimer.FloatValue, 255, 255, 255, 255, 0, 0.0, 0.0, 0.0);
for (int i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i))
channel = ShowHudText(i, channel, text);
}
}
void OnNotifyTimer(Handle timer, any data)
{
int client = view_as<int>(data);
int idx = FindInNotifyList(client);
if (idx != -1)
g_aNotifyList.Erase(idx);
if (g_aNotifyList.Length)
DisplayNotifyList();
}
int FindInNotifyList(int client)
{
for (int i = 0; i < g_aNotifyList.Length; i++)
{
NotifyListData buf;
g_aNotifyList.GetArray(i, buf, sizeof(NotifyListData));
if (buf.client == client)
return i;
}
return -1;
}