From dbee42fa245723a230e024efdd19a57538ffb740 Mon Sep 17 00:00:00 2001 From: Ferks-FK Date: Wed, 7 Oct 2026 09:00:00 -0400 Subject: [PATCH 1/2] Optimize transmit hook and stop hiding equipped weapons. --- .../scripting/l4d2_blind_infected.sp | 190 ++++++++++++------ 1 file changed, 132 insertions(+), 58 deletions(-) diff --git a/addons/sourcemod/scripting/l4d2_blind_infected.sp b/addons/sourcemod/scripting/l4d2_blind_infected.sp index a5e7a761bf..5acbf185a1 100644 --- a/addons/sourcemod/scripting/l4d2_blind_infected.sp +++ b/addons/sourcemod/scripting/l4d2_blind_infected.sp @@ -7,16 +7,12 @@ #define L4D2UTIL_STOCKS_ONLY 1 #include -#define ENT_CHECK_INTERVAL 1.0 -#define TRACE_TOLERANCE 75.0 +// The whole pending list is checked once every ENT_CHECK_SLICE * ENT_CHECK_SLICES seconds. +#define ENT_CHECK_SLICE 0.1 +#define ENT_CHECK_SLICES 10 -enum -{ - eiEntRef = 0, - ebHasBeenSeen, - - eArray_Size -}; +#define TRACE_TOLERANCE 75.0 +#define MAX_ENTS 2048 static const int g_iIdsToBlock[] = { @@ -52,74 +48,118 @@ static const int g_iIdsToBlock[] = WEPID_SNIPER_AWP }; -ArrayList - g_hBlockedEntities = null; +bool g_bBlocked[MAX_ENTS + 1]; + +// Entity references not yet seen by survivors. +ArrayList g_hPending = null; + +int g_iCursor = 0; public Plugin myinfo = { name = "Blind Infected", - author = "CanadaRox, ProdigySim, A1m`", + author = "CanadaRox, ProdigySim, A1m`, Ferks-FK", description = "Hides specified weapons from the infected team until they are (possibly) visible to one of the survivors to prevent SI scouting the map", - version = "1.2.2", + version = "1.2.3", url = "https://github.com/SirPlease/L4D2-Competitive-Rework" }; public void OnPluginStart() { L4D2Weapons_Init(); - g_hBlockedEntities = new ArrayList(eArray_Size); - + + g_hPending = new ArrayList(); + HookEvent("round_start", RoundStart_Event, EventHookMode_PostNoCopy); + CreateTimer(ENT_CHECK_SLICE, Timer_EntCheck, _, TIMER_REPEAT); - CreateTimer(ENT_CHECK_INTERVAL, Timer_EntCheck, _, TIMER_REPEAT); + // Late load. + for (int i = 1; i <= MaxClients; i++) { + if (IsClientInGame(i)) { + OnClientPutInServer(i); + } + } } -Action Timer_EntCheck(Handle hTimer) +public void OnClientPutInServer(int iClient) { - char sTmp[PLATFORM_MAX_PATH]; - int iCurrentEnt[eArray_Size], iEntity, iSize = g_hBlockedEntities.Length; - - for (int i = 0; i < iSize; i++) { - g_hBlockedEntities.GetArray(i, iCurrentEnt[0], sizeof(iCurrentEnt)); - iEntity = EntRefToEntIndex(iCurrentEnt[eiEntRef]); - - if (iEntity != INVALID_ENT_REFERENCE && !iCurrentEnt[ebHasBeenSeen] && IsVisibleToSurvivors(iEntity)) { - GetEntPropString(iEntity, Prop_Data, "m_ModelName", sTmp, sizeof(sTmp)); - iCurrentEnt[ebHasBeenSeen] = true; - - g_hBlockedEntities.SetArray(i, iCurrentEnt[0], sizeof(iCurrentEnt)); - } + SDKHook(iClient, SDKHook_WeaponEquipPost, OnWeaponEquipPost); +} + +// Once equipped, a weapon is parented to the player and its m_vecOrigin becomes +// relative, so the visibility trace would never reach it and it would stay hidden. +void OnWeaponEquipPost(int iClient, int iWeapon) +{ + if (iWeapon > MaxClients && iWeapon <= MAX_ENTS && g_bBlocked[iWeapon]) { + UnblockEntity(iWeapon); } +} - return Plugin_Continue; +void UnblockEntity(int iEntity) +{ + SDKUnhook(iEntity, SDKHook_SetTransmit, OnTransmit); + g_bBlocked[iEntity] = false; +} + +public void OnMapStart() +{ + ClearAll(); +} + +// Prevents a recycled entity index from inheriting the blocked state. +public void OnEntityDestroyed(int iEntity) +{ + if (iEntity > 0 && iEntity <= MAX_ENTS) { + g_bBlocked[iEntity] = false; + } +} + +void ClearAll() +{ + for (int i = 0; i <= MAX_ENTS; i++) { + g_bBlocked[i] = false; + } + + if (g_hPending != null) { + g_hPending.Clear(); + } + + g_iCursor = 0; } void RoundStart_Event(Event hEvent, const char[] sEventName, bool bDontBroadcast) { - g_hBlockedEntities.Clear(); - + ClearAll(); CreateTimer(1.2, RoundStartDelay_Timer, _, TIMER_FLAG_NO_MAPCHANGE); } Action RoundStartDelay_Timer(Handle hTimer) { - int iWeapon; - int iBhTemp[eArray_Size], iEntityCount = GetEntityCount(); - - for (int i = (MaxClients + 1); i < iEntityCount; i++) { - iWeapon = IdentifyWeapon(i); - if (iWeapon) { - for (int j = 0; j < sizeof(g_iIdsToBlock); j++) { - if (iWeapon == g_iIdsToBlock[j]) { - SDKHook(i, SDKHook_SetTransmit, OnTransmit); - - iBhTemp[eiEntRef] = EntIndexToEntRef(i); - iBhTemp[ebHasBeenSeen] = false; - - g_hBlockedEntities.PushArray(iBhTemp[0], sizeof(iBhTemp)); - - break; - } + // Entity indices can have gaps, so GetEntityCount() is not a valid upper bound. + int iMax = GetMaxEntities(); + + for (int i = (MaxClients + 1); i < iMax; i++) { + if (!IsValidEntity(i)) { + continue; + } + + int iWeapon = IdentifyWeapon(i); + if (!iWeapon) { + continue; + } + + // Already equipped, e.g. the survivors' starting pistols. + int iOwner = GetEntPropEnt(i, Prop_Send, "m_hOwnerEntity"); + if (iOwner > 0 && iOwner <= MaxClients) { + continue; + } + + for (int j = 0; j < sizeof(g_iIdsToBlock); j++) { + if (iWeapon == g_iIdsToBlock[j]) { + SDKHook(i, SDKHook_SetTransmit, OnTransmit); + g_bBlocked[i] = true; + g_hPending.Push(EntIndexToEntRef(i)); + break; } } } @@ -129,19 +169,53 @@ Action RoundStartDelay_Timer(Handle hTimer) Action OnTransmit(int iEntity, int iClient) { + if (iEntity < 0 || iEntity > MAX_ENTS || !g_bBlocked[iEntity]) { + return Plugin_Continue; + } + if (GetClientTeam(iClient) != L4D2Team_Infected) { return Plugin_Continue; } - - int iCurrentEnt[eArray_Size], iSize = g_hBlockedEntities.Length; - for (int i = 0; i < iSize; i++) { - g_hBlockedEntities.GetArray(i, iCurrentEnt[0], sizeof(iCurrentEnt)); - - if (iEntity == EntRefToEntIndex(iCurrentEnt[eiEntRef])) { - return (iCurrentEnt[ebHasBeenSeen]) ? Plugin_Continue : Plugin_Handled; + + return Plugin_Handled; +} + +Action Timer_EntCheck(Handle hTimer) +{ + int iSize = g_hPending.Length; + if (iSize == 0) { + return Plugin_Continue; + } + + int iBatch = (iSize + ENT_CHECK_SLICES - 1) / ENT_CHECK_SLICES; + + if (g_iCursor >= iSize) { + g_iCursor = 0; + } + + int iProcessed = 0; + + while (iProcessed < iBatch && g_iCursor < g_hPending.Length) { + int iRef = g_hPending.Get(g_iCursor); + int iEntity = EntRefToEntIndex(iRef); + + if (iEntity == INVALID_ENT_REFERENCE || !g_bBlocked[iEntity]) { + g_hPending.Erase(g_iCursor); + iProcessed++; + continue; + } + + if (IsVisibleToSurvivors(iEntity)) { + UnblockEntity(iEntity); + g_hPending.Erase(g_iCursor); + iProcessed++; + continue; } + + g_iCursor++; + iProcessed++; } - + return Plugin_Continue; } From 9437d80418ff1f592cef1550ed983449328a2f4a Mon Sep 17 00:00:00 2001 From: Ferks-FK Date: Wed, 7 Oct 2026 09:02:37 -0400 Subject: [PATCH 2/2] Add a compiled version. --- .../plugins/optional/l4d2_blind_infected.smx | Bin 9386 -> 9938 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/addons/sourcemod/plugins/optional/l4d2_blind_infected.smx b/addons/sourcemod/plugins/optional/l4d2_blind_infected.smx index aeb0b5aa3c12ce3027177b61d3c73bd43ea0d42c..59f0a67d97df0bc021468b18975615bb253743cd 100644 GIT binary patch literal 9938 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 delta 9262 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 HtNQ-{j&Bfn