From 636b84e3850b49185e71a43f1073c00fd68d06aa Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Sat, 4 Oct 2025 00:01:34 +0700 Subject: [PATCH 1/3] [l4d2_static_shotgun_spread] Restore central bullet on unload - Added deletion of the `central pellet` patch when unloading the plugin. This patch is applied in-game, not in the ASM patch. The value is restored after unloading to prevent logic issues with other server settings. - Added a check for the `central pellet` patch offset. - Removed unnecessary type conversions and improved readability. - Minor plugin refactoring. --- .../scripting/l4d2_static_shotgun_spread.sp | 201 ++++++++++-------- 1 file changed, 107 insertions(+), 94 deletions(-) diff --git a/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp b/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp index 548395893..dbd21a5db 100644 --- a/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp +++ b/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp @@ -4,10 +4,9 @@ #include #include -#define GAMEDATA_FILE "code_patcher" -#define KEY_SGSPREAD "sgspread" -#define BULLET_MAX_SIZE 4 -#define DEBUG 0 +#define BULLET_MAX_SIZE 4 +#define GAMEDATA_FILE "code_patcher" +#define KEY_SGSPREAD "sgspread" // Original code & Notes: https://github.com/Jahze/l4d2_plugins/tree/master/spread_patch // Static Shotgun Spread leverages code_patcher (code_patcher.txt gamedata) @@ -22,11 +21,12 @@ enum eWindows = 0, eLinux, /* eMac, joke:) */ + ePlatform_Size -} +}; int - g_ePlatform; + g_ePlatform = eLinux; static const int g_BulletOffsets[ePlatform_Size][BULLET_MAX_SIZE] = { @@ -43,132 +43,145 @@ static const int }; MemoryPatch - g_hPatch_sgspread; + g_hPatchSgSpread = null; ConVar - hRing1BulletsCvar, - hRing1FactorCvar, - hCenterPelletCvar; + g_hCvarRing1Bullets = null, + g_hCvarRing1Factor = null, + g_hCvarhCenterPellet = null; -public Plugin myinfo = +public Plugin myinfo = { name = "L4D2 Static Shotgun Spread", author = "Jahze, Visor, A1m`, Rena", - version = "1.6.1", + version = "1.6.3", description = "Changes the values in the sgspread patch", url = "https://github.com/SirPlease/L4D2-Competitive-Rework" }; public void OnPluginStart() { - Handle conf = LoadGameConfigFile(GAMEDATA_FILE); - if (conf == null) { + InitGameData(); + + g_hCvarRing1Bullets = CreateConVar("sgspread_ring1_bullets", "3", "Number of bullets for the first ring, the remaining bullets will be in the second ring."); + g_hCvarRing1Factor = CreateConVar("sgspread_ring1_factor", "2", "Determines how far or closer the bullets will be from the center for the first ring."); + g_hCvarhCenterPellet = CreateConVar("sgspread_center_pellet", "1", "Center pellet: 0 - off, 1 - on.", _, true, 0.0, true, 1.0); + + InitPlugin(); +} + +void InitGameData() +{ + Handle hConf = LoadGameConfigFile(GAMEDATA_FILE); + if (hConf == null) { SetFailState("Missing gamedata \"" ... GAMEDATA_FILE ... "\""); } - - g_hPatch_sgspread = MemoryPatch.CreateFromConf(conf, KEY_SGSPREAD); - if (g_hPatch_sgspread == null || !g_hPatch_sgspread.Validate()) { + + g_ePlatform = GameConfGetOffset(hConf, "OS"); + if (g_ePlatform == -1) { + SetFailState("Failed to retrieve offset \"OS\""); + } + + g_hPatchSgSpread = MemoryPatch.CreateFromConf(hConf, KEY_SGSPREAD); + if (g_hPatchSgSpread == null || !g_hPatchSgSpread.Validate()) { SetFailState("Failed to validate MemoryPatch \"" ... KEY_SGSPREAD ... "\""); } - - if (!g_hPatch_sgspread.Enable()) { - SetFailState("Failed to enable MemoryPatch \"" ... KEY_SGSPREAD ... "\""); + + Address pFinalAddr = g_hPatchSgSpread.Address + view_as
(g_CenterPelletOffset[g_ePlatform]); + int iCurrentValue = LoadFromAddress(pFinalAddr, NumberType_Int8); + if (iCurrentValue != 1) { + SetFailState("Center pellet offset is uncorrect! CheckByte: %x", iCurrentValue); } - - g_ePlatform = GameConfGetOffset(conf, "OS"); - if (g_ePlatform == -1) { - SetFailState("Failed to retrieve offset \"OS\""); + + if (!g_hPatchSgSpread.Enable()) { + SetFailState("Failed to enable MemoryPatch \"" ... KEY_SGSPREAD ... "\""); } - - delete conf; - - hRing1BulletsCvar = CreateConVar("sgspread_ring1_bullets", "3", "Number of bullets for the first ring, the remaining bullets will be in the second ring."); - hRing1FactorCvar = CreateConVar("sgspread_ring1_factor", "2", "Determines how far or closer the bullets will be from the center for the first ring."); - hCenterPelletCvar = CreateConVar("sgspread_center_pellet", "1", "Center pellet: 0 - off, 1 - on.", _, true, 0.0, true, 1.0); - - hRing1BulletsCvar.AddChangeHook(OnRing1BulletsChange); - hRing1FactorCvar.AddChangeHook(OnRing1FactorChange); - hCenterPelletCvar.AddChangeHook(OnCenterPelletChange); - - HotPatchBullets(hRing1BulletsCvar.IntValue); - HotPatchFactor(hRing1FactorCvar.IntValue); - HotPatchCenterPellet(hCenterPelletCvar.BoolValue); + + delete hConf; } -static void HotPatchCenterPellet(bool newValue) +void InitPlugin() { - Address pAddr = g_hPatch_sgspread.Address; - - int currentValue = LoadFromAddress(pAddr + view_as
(g_CenterPelletOffset[g_ePlatform]), NumberType_Int8); - - int bullets = hRing1BulletsCvar.IntValue; - - #if DEBUG - static bool IsFirstPatch = false; - if (!IsFirstPatch) { - PrintToServer("Center pellet offset is %s! CheckByte: %x", (currentValue == 0x01) ? "correct ": "uncorrect", currentValue); - PrintToChatAll("Center pellet offset is %s! CheckByte: %x", (currentValue == 0x01) ? "correct ": "uncorrect", currentValue); - IsFirstPatch = true; - } - #endif - - if (!!currentValue == newValue) { - return; + HotPatchBullets(g_hCvarRing1Bullets.IntValue); + HotPatchFactor(g_hCvarRing1Factor.IntValue); + HotPatchCenterPellet(g_hCvarhCenterPellet.BoolValue); + + g_hCvarRing1Bullets.AddChangeHook(OnRing1BulletsChange); + g_hCvarRing1Factor.AddChangeHook(OnRing1FactorChange); + g_hCvarhCenterPellet.AddChangeHook(OnCenterPelletChange); +} + +public void OnPluginEnd() +{ + Address pFinalAddr = g_hPatchSgSpread.Address + view_as
(g_CenterPelletOffset[g_ePlatform]); + int iCurrentValue = LoadFromAddress(pFinalAddr, NumberType_Int8); + if (iCurrentValue != 1) { + StoreToAddress(pFinalAddr, 1, NumberType_Int8); } - - StoreToAddress(pAddr + view_as
(g_CenterPelletOffset[g_ePlatform]), view_as(newValue), NumberType_Int8); - - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][0]), bullets + (1 - view_as(!newValue)), NumberType_Int8); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][1]), bullets + (1 - view_as(!newValue)), NumberType_Int8); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][2]), bullets + (2 - view_as(!newValue)), NumberType_Int8); } -static void HotPatchBullets(int nBullets) +void OnRing1BulletsChange(ConVar hConVar, const char[] sOldValue, const char[] sNewValue) +{ + HotPatchBullets(hConVar.IntValue); +} + +void OnRing1FactorChange(ConVar hConVar, const char[] sOldValue, const char[] sNewValue) +{ + HotPatchFactor(hConVar.IntValue); +} + +void OnCenterPelletChange(ConVar hConVar, const char[] sOldValue, const char[] sNewValue) +{ + HotPatchCenterPellet(hConVar.BoolValue); +} + +void HotPatchBullets(int iBullets) { - bool centerpellet = !hCenterPelletCvar.BoolValue; - float degree = 0.0; - + bool bCenterpellet = !g_hCvarhCenterPellet.BoolValue; + float fDegree = 0.0; + if (g_ePlatform == eWindows) { - degree = 360.0 / float(nBullets); + fDegree = 360.0 / float(iBullets); } else { - degree = 360.0 / (2.0 * float(nBullets)); + fDegree = 360.0 / (2.0 * float(iBullets)); } - - Address pAddr = g_hPatch_sgspread.Address; - - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][0]), nBullets + (1 - view_as(centerpellet)), NumberType_Int8); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][1]), nBullets + (1 - view_as(centerpellet)), NumberType_Int8); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][2]), nBullets + (2 - view_as(centerpellet)), NumberType_Int8); - - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][3]), view_as(degree), NumberType_Int32); + + Address pAddr = g_hPatchSgSpread.Address; + + StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][0]), iBullets + (1 - view_as(bCenterpellet)), NumberType_Int8); + StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][1]), iBullets + (1 - view_as(bCenterpellet)), NumberType_Int8); + StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][2]), iBullets + (2 - view_as(bCenterpellet)), NumberType_Int8); + + StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][3]), view_as(fDegree), NumberType_Int32); } -static void HotPatchFactor(int factor) +void HotPatchFactor(int fFactor) { - Address pAddr = g_hPatch_sgspread.Address; + Address pAddr = g_hPatchSgSpread.Address; if (g_ePlatform == eWindows) { - StoreToAddress(pAddr + view_as
(g_FactorOffset[eWindows]), view_as(float(factor)), NumberType_Int32); //On windows need the float type !!! + // Asm patch on windows need the float type ! + StoreToAddress(pAddr + view_as
(g_FactorOffset[eWindows]), view_as(float(fFactor)), NumberType_Int32); return; } - - StoreToAddress(pAddr + view_as
(g_FactorOffset[eLinux]), factor, NumberType_Int32); -} -void OnRing1BulletsChange(ConVar hCvar, const char[] oldVal, const char[] newVal) -{ - int nBullets = StringToInt(newVal); - HotPatchBullets(nBullets); + StoreToAddress(pAddr + view_as
(g_FactorOffset[eLinux]), fFactor, NumberType_Int32); } -void OnRing1FactorChange(ConVar hCvar, const char[] oldVal, const char[] newVal) +void HotPatchCenterPellet(bool bNewValue) { - int factor = StringToInt(newVal); - HotPatchFactor(factor); -} + Address pAddr = g_hPatchSgSpread.Address; -void OnCenterPelletChange(ConVar hCvar, const char[] oldVal, const char[] newVal) -{ - bool value = !!StringToInt(newVal); - HotPatchCenterPellet(value); + bool iCurrentValue = LoadFromAddress(pAddr + view_as
(g_CenterPelletOffset[g_ePlatform]), NumberType_Int8); + if (iCurrentValue == bNewValue) { + return; + } + + int iBullets = g_hCvarRing1Bullets.IntValue; + + StoreToAddress(pAddr + view_as
(g_CenterPelletOffset[g_ePlatform]), view_as(bNewValue), NumberType_Int8); + + StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][0]), iBullets + (1 - view_as(!bNewValue)), NumberType_Int8); + StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][1]), iBullets + (1 - view_as(!bNewValue)), NumberType_Int8); + StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][2]), iBullets + (2 - view_as(!bNewValue)), NumberType_Int8); } From edcb0f31189cc5296a1aa2aa91b480ee649e3b5f Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Sat, 4 Oct 2025 17:42:10 +0700 Subject: [PATCH 2/3] Code refactoring --- .../scripting/l4d2_static_shotgun_spread.sp | 104 ++++++++++-------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp b/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp index dbd21a5db..28badacd3 100644 --- a/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp +++ b/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp @@ -4,7 +4,7 @@ #include #include -#define BULLET_MAX_SIZE 4 +#define BULLET_MAX_SIZE 3 #define GAMEDATA_FILE "code_patcher" #define KEY_SGSPREAD "sgspread" @@ -29,32 +29,42 @@ int g_ePlatform = eLinux; static const int - g_BulletOffsets[ePlatform_Size][BULLET_MAX_SIZE] = { - { 0xf, 0x21, 0x30, 0x3f }, // Windows - { 0x11, 0x22, 0x2f, 0x43 } // Linux + g_iBulletOffsets[ePlatform_Size][BULLET_MAX_SIZE] = { + { 0xf, 0x21, 0x30 }, // Windows + { 0x11, 0x22, 0x2f } // Linux }, - g_FactorOffset[ePlatform_Size] = { + g_iDegreeOffsets[ePlatform_Size] = { + 0x3f, // Windows + 0x43 // Linux + } + g_iFactorOffset[ePlatform_Size] = { 0x36, // Windows 0x34 // Linux }, - g_CenterPelletOffset[ePlatform_Size] = { + g_iCenterPelletOffset[ePlatform_Size] = { -0x36, // Windows -0x1c // Linux }; +Address + g_pBullets[BULLET_MAX_SIZE] = {Address_Null, ...}, + g_pDegree = Address_Null, + g_pFactor = Address_Null, + g_pCenterPellet = Address_Null; + MemoryPatch g_hPatchSgSpread = null; ConVar g_hCvarRing1Bullets = null, g_hCvarRing1Factor = null, - g_hCvarhCenterPellet = null; + g_hCvarCenterPellet = null; public Plugin myinfo = { name = "L4D2 Static Shotgun Spread", author = "Jahze, Visor, A1m`, Rena", - version = "1.6.3", + version = "1.6.5", description = "Changes the values in the sgspread patch", url = "https://github.com/SirPlease/L4D2-Competitive-Rework" }; @@ -65,7 +75,7 @@ public void OnPluginStart() g_hCvarRing1Bullets = CreateConVar("sgspread_ring1_bullets", "3", "Number of bullets for the first ring, the remaining bullets will be in the second ring."); g_hCvarRing1Factor = CreateConVar("sgspread_ring1_factor", "2", "Determines how far or closer the bullets will be from the center for the first ring."); - g_hCvarhCenterPellet = CreateConVar("sgspread_center_pellet", "1", "Center pellet: 0 - off, 1 - on.", _, true, 0.0, true, 1.0); + g_hCvarCenterPellet = CreateConVar("sgspread_center_pellet", "1", "Center pellet: 0 - off, 1 - on.", _, true, 0.0, true, 1.0); InitPlugin(); } @@ -87,7 +97,7 @@ void InitGameData() SetFailState("Failed to validate MemoryPatch \"" ... KEY_SGSPREAD ... "\""); } - Address pFinalAddr = g_hPatchSgSpread.Address + view_as
(g_CenterPelletOffset[g_ePlatform]); + Address pFinalAddr = g_hPatchSgSpread.Address + view_as
(g_iCenterPelletOffset[g_ePlatform]); int iCurrentValue = LoadFromAddress(pFinalAddr, NumberType_Int8); if (iCurrentValue != 1) { SetFailState("Center pellet offset is uncorrect! CheckByte: %x", iCurrentValue); @@ -102,21 +112,31 @@ void InitGameData() void InitPlugin() { + for (int i = 0; i < BULLET_MAX_SIZE; i++) { + g_pBullets[i] = g_hPatchSgSpread.Address + view_as
(g_iBulletOffsets[g_ePlatform][i]); + } + + g_pDegree = g_hPatchSgSpread.Address + view_as
(g_iDegreeOffsets[g_ePlatform]); + g_pFactor = g_hPatchSgSpread.Address + view_as
(g_iFactorOffset[g_ePlatform]); + g_pCenterPellet = g_hPatchSgSpread.Address + view_as
(g_iCenterPelletOffset[g_ePlatform]); + HotPatchBullets(g_hCvarRing1Bullets.IntValue); HotPatchFactor(g_hCvarRing1Factor.IntValue); - HotPatchCenterPellet(g_hCvarhCenterPellet.BoolValue); + HotPatchCenterPellet(g_hCvarCenterPellet.BoolValue); g_hCvarRing1Bullets.AddChangeHook(OnRing1BulletsChange); g_hCvarRing1Factor.AddChangeHook(OnRing1FactorChange); - g_hCvarhCenterPellet.AddChangeHook(OnCenterPelletChange); + g_hCvarCenterPellet.AddChangeHook(OnCenterPelletChange); } public void OnPluginEnd() { - Address pFinalAddr = g_hPatchSgSpread.Address + view_as
(g_CenterPelletOffset[g_ePlatform]); - int iCurrentValue = LoadFromAddress(pFinalAddr, NumberType_Int8); - if (iCurrentValue != 1) { - StoreToAddress(pFinalAddr, 1, NumberType_Int8); + if (g_pCenterPellet != Address_Null) { + int iCurrentValue = LoadFromAddress(g_pCenterPellet, NumberType_Int8); + + if (iCurrentValue != 1) { + StoreToAddress(g_pCenterPellet, 1, NumberType_Int8); + } } } @@ -137,51 +157,41 @@ void OnCenterPelletChange(ConVar hConVar, const char[] sOldValue, const char[] s void HotPatchBullets(int iBullets) { - bool bCenterpellet = !g_hCvarhCenterPellet.BoolValue; - float fDegree = 0.0; + PatchBullets(iBullets, g_hCvarCenterPellet.BoolValue); - if (g_ePlatform == eWindows) { - fDegree = 360.0 / float(iBullets); - } else { - fDegree = 360.0 / (2.0 * float(iBullets)); + float fBullets = float(iBullets); + if (g_ePlatform == eLinux) { + fBullets *= 2.0; } - Address pAddr = g_hPatchSgSpread.Address; - - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][0]), iBullets + (1 - view_as(bCenterpellet)), NumberType_Int8); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][1]), iBullets + (1 - view_as(bCenterpellet)), NumberType_Int8); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][2]), iBullets + (2 - view_as(bCenterpellet)), NumberType_Int8); - - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][3]), view_as(fDegree), NumberType_Int32); + StoreToAddress(g_pDegree, view_as(360.0 / fBullets), NumberType_Int32); } -void HotPatchFactor(int fFactor) +void HotPatchFactor(int iFactor) { - Address pAddr = g_hPatchSgSpread.Address; - - if (g_ePlatform == eWindows) { - // Asm patch on windows need the float type ! - StoreToAddress(pAddr + view_as
(g_FactorOffset[eWindows]), view_as(float(fFactor)), NumberType_Int32); - return; - } - - StoreToAddress(pAddr + view_as
(g_FactorOffset[eLinux]), fFactor, NumberType_Int32); + // Asm patch on windows need the float type ! + int iSetFactor = (g_ePlatform == eWindows) ? view_as(float(iFactor)) : iFactor; + StoreToAddress(g_pFactor, iSetFactor, NumberType_Int32); } void HotPatchCenterPellet(bool bNewValue) { - Address pAddr = g_hPatchSgSpread.Address; - - bool iCurrentValue = LoadFromAddress(pAddr + view_as
(g_CenterPelletOffset[g_ePlatform]), NumberType_Int8); + bool iCurrentValue = LoadFromAddress(g_pCenterPellet, NumberType_Int8); if (iCurrentValue == bNewValue) { return; } - int iBullets = g_hCvarRing1Bullets.IntValue; + StoreToAddress(g_pCenterPellet, view_as(bNewValue), NumberType_Int8); + + PatchBullets(g_hCvarRing1Bullets.IntValue, bNewValue); +} - StoreToAddress(pAddr + view_as
(g_CenterPelletOffset[g_ePlatform]), view_as(bNewValue), NumberType_Int8); +void PatchBullets(int iBullets, bool bCenterPellet) +{ + int iOffset1 = iBullets + (bCenterPellet ? 1 : 0); + int iOffset2 = iBullets + (bCenterPellet ? 2 : 1); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][0]), iBullets + (1 - view_as(!bNewValue)), NumberType_Int8); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][1]), iBullets + (1 - view_as(!bNewValue)), NumberType_Int8); - StoreToAddress(pAddr + view_as
(g_BulletOffsets[g_ePlatform][2]), iBullets + (2 - view_as(!bNewValue)), NumberType_Int8); + StoreToAddress(g_pBullets[0], iOffset1, NumberType_Int8); + StoreToAddress(g_pBullets[1], iOffset1, NumberType_Int8); + StoreToAddress(g_pBullets[2], iOffset2, NumberType_Int8); } From e882e2b97c5baba5e9e94f131c99ef1b5c60ebe1 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Sat, 4 Oct 2025 17:44:06 +0700 Subject: [PATCH 3/3] Fix mistake --- addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp b/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp index 28badacd3..6ef644f8a 100644 --- a/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp +++ b/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp @@ -36,7 +36,7 @@ static const int g_iDegreeOffsets[ePlatform_Size] = { 0x3f, // Windows 0x43 // Linux - } + }, g_iFactorOffset[ePlatform_Size] = { 0x36, // Windows 0x34 // Linux