From 326caccb0640b53754b5acdacb39d995ee9a14f3 Mon Sep 17 00:00:00 2001 From: mourningsickness Date: Sat, 12 Jul 2025 10:37:46 -0700 Subject: [PATCH 01/13] shavit-zones.sp - make it so slay/stoptimer zones don't apply if paused rip skronk's hour long bhop_thc time --- addons/sourcemod/scripting/shavit-zones.sp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/scripting/shavit-zones.sp b/addons/sourcemod/scripting/shavit-zones.sp index 9c5ce72f..170e555c 100644 --- a/addons/sourcemod/scripting/shavit-zones.sp +++ b/addons/sourcemod/scripting/shavit-zones.sp @@ -5347,7 +5347,7 @@ public void StartTouchPost(int entity, int other) case Zone_Slay: { - if (status != Timer_Stopped) + if (status == Timer_Running) { Shavit_StopTimer(other); ACTUALLY_ForcePlayerSuicide(other); @@ -5357,7 +5357,7 @@ public void StartTouchPost(int entity, int other) case Zone_Stop: { - if(status != Timer_Stopped) + if(status == Timer_Running) { Shavit_StopTimer(other); Shavit_PrintToChat(other, "%T", "ZoneStopEnter", other, gS_ChatStrings.sWarning, gS_ChatStrings.sVariable2, gS_ChatStrings.sWarning); From 0b0be25c78a1479e0dc16e855805607da31de948 Mon Sep 17 00:00:00 2001 From: mourningsickness Date: Mon, 14 Jul 2025 17:46:59 -0700 Subject: [PATCH 02/13] shavit-misc.sp - don't stop timer on tpto if paused with pause movement enabled (#1249) --- addons/sourcemod/scripting/shavit-misc.sp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/shavit-misc.sp b/addons/sourcemod/scripting/shavit-misc.sp index e37e8954..ccf26fb9 100644 --- a/addons/sourcemod/scripting/shavit-misc.sp +++ b/addons/sourcemod/scripting/shavit-misc.sp @@ -1929,7 +1929,16 @@ bool Teleport(int client, int targetserial) float vecPosition[3]; GetClientAbsOrigin(iTarget, vecPosition); - Shavit_StopTimer(client); + if(Shavit_GetTimerStatus(client) == Timer_Running || !gCV_PauseMovement.BoolValue) + { + if(ShouldDisplayStopWarning(client)) + { + gI_LastStopInfo[client] = targetserial; + OpenStopWarningMenu(client, DoTeleport); + return true; + } + Shavit_StopTimer(client); + } TeleportEntity(client, vecPosition, NULL_VECTOR, NULL_VECTOR); @@ -2040,6 +2049,12 @@ void DoStopTimer(int client) Shavit_StopTimer(client); } +void DoTeleport(int client) +{ + Shavit_StopTimer(client); + Teleport(client, gI_LastStopInfo[client]); +} + void OpenStopWarningMenu(int client, StopTimerCallback after) { gH_AfterWarningMenu[client] = after; From 378a2eae700c218e1d2568c4ae83241009ca49e4 Mon Sep 17 00:00:00 2001 From: rtldg Date: Tue, 15 Jul 2025 01:10:56 +0000 Subject: [PATCH 03/13] Include eventqueuefixfix in release zips --- .github/workflows/ci.yml | 3 +++ README.md | 1 + 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82ea637e..ca30d635 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,9 @@ jobs: wget https://github.com/hermansimensen/eventqueue-fix/archive/refs/heads/main.tar.gz tar --strip-components=1 -xvzf main.tar.gz -C addons/sourcemod rm -rf *.zip *.tar.gz addons/sourcemod/.git* addons/sourcemod/LICENSE + wget https://github.com/srcwr/eventqueuefixfix/releases/download/v1.0.1/eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip + unzip eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip "addons/sourcemod/extensions/*" + rm "addons/sourcemod/extensions/eventqueuefixfix.pdb" - name: Run compiler shell: bash diff --git a/README.md b/README.md index 18915a66..e05a5249 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Includes a records system, map zones (start/end marks etc), bonuses, HUD with us * [eventqueuefix](https://github.com/hermansimensen/eventqueue-fix) * Allows for timescaling boosters and is used to fix some exploits. (Use this instead of `boosterfix`) * (included in bhoptimer release zips) + * Along with using [eventqueuefixfix](https://github.com/srcwr/eventqueuefixfix) at the same time to fix eventqueuefix on Windows after the 2025-02-18 update. * [SteamWorks](https://forums.alliedmods.net/showthread.php?t=229556) * Used to grab `{serverip}` in advertisements. * [DynamicChannels](https://github.com/Vauff/DynamicChannels) From 70308f3d6ac765d6b437984b4144ba3806a9a34f Mon Sep 17 00:00:00 2001 From: rtldg Date: Tue, 15 Jul 2025 06:52:16 +0000 Subject: [PATCH 04/13] Clarify shavit_replay_timelimit 0.0/disabled not recording replays --- addons/sourcemod/scripting/shavit-replay-recorder.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/shavit-replay-recorder.sp b/addons/sourcemod/scripting/shavit-replay-recorder.sp index a5e19aa3..3f5d3ee7 100644 --- a/addons/sourcemod/scripting/shavit-replay-recorder.sp +++ b/addons/sourcemod/scripting/shavit-replay-recorder.sp @@ -142,7 +142,7 @@ public void OnPluginStart() gCV_PlaybackPostRunTime = new Convar("shavit_replay_postruntime", "1.5", "Time (in seconds) to record after a player enters the end zone.", 0, true, 0.0, true, 2.0); gCV_PreRunAlways = new Convar("shavit_replay_prerun_always", "1", "Record prerun frames outside the start zone?", 0, true, 0.0, true, 1.0); gCV_PlaybackPreRunTime = new Convar("shavit_replay_preruntime", "1.5", "Time (in seconds) to record before a player leaves start zone.", 0, true, 0.0, true, 2.0); - gCV_TimeLimit = new Convar("shavit_replay_timelimit", "7200.0", "Maximum amount of time (in seconds) to allow saving to disk.\nDefault is 7200 (2 hours)\n0 - Disabled", 0, true, 0.0); + gCV_TimeLimit = new Convar("shavit_replay_timelimit", "7200.0", "Maximum amount of time (in seconds) to allow saving to disk.\nDefault is 7200 (2 hours)\n0 - Disabled (no replays will be recorded)", 0, true, 0.0); Convar.AutoExecConfig(); From 16ccd0cc7ca2942baf3130b298c8ce57b5d236c5 Mon Sep 17 00:00:00 2001 From: rtldg <55846624+rtldg@users.noreply.github.com> Date: Thu, 17 Jul 2025 22:42:22 +0000 Subject: [PATCH 05/13] Use an extension to save replays asynchronously (#1253) --- .github/workflows/ci.yml | 3 + README.md | 3 + .../scripting/include/shavit/replay-file.inc | 29 ++++ .../include/shavit/replay-recorder.inc | 3 +- .../scripting/include/srcwr/floppy.inc | 50 +++++++ .../scripting/shavit-replay-recorder.sp | 129 +++++++++++++++--- 6 files changed, 199 insertions(+), 18 deletions(-) create mode 100644 addons/sourcemod/scripting/include/srcwr/floppy.inc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca30d635..5bc35056 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,9 @@ jobs: wget https://github.com/srcwr/eventqueuefixfix/releases/download/v1.0.1/eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip unzip eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip "addons/sourcemod/extensions/*" rm "addons/sourcemod/extensions/eventqueuefixfix.pdb" + wget https://github.com/srcwr/srcwrfloppy/releases/download/v2.0.0/srcwrfloppy-v2.0.0.zip + unzip -qO UTF-8 srcwrfloppy-v2.0.0.zip "addons/sourcemod/extensions/*" + rm "addons/sourcemod/extensions/srcwr💾.pdb" - name: Run compiler shell: bash diff --git a/README.md b/README.md index e05a5249..a488ad03 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ Includes a records system, map zones (start/end marks etc), bonuses, HUD with us * Allows for timescaling boosters and is used to fix some exploits. (Use this instead of `boosterfix`) * (included in bhoptimer release zips) * Along with using [eventqueuefixfix](https://github.com/srcwr/eventqueuefixfix) at the same time to fix eventqueuefix on Windows after the 2025-02-18 update. +* [srcwr💾](https://github.com/srcwr/srcwrfloppy) + * Saves replays asynchronously (read: doesn't lag the server when saving a replay). + * (included in bhoptimer release zips) * [SteamWorks](https://forums.alliedmods.net/showthread.php?t=229556) * Used to grab `{serverip}` in advertisements. * [DynamicChannels](https://github.com/Vauff/DynamicChannels) diff --git a/addons/sourcemod/scripting/include/shavit/replay-file.inc b/addons/sourcemod/scripting/include/shavit/replay-file.inc index 867b892f..085139a2 100644 --- a/addons/sourcemod/scripting/include/shavit/replay-file.inc +++ b/addons/sourcemod/scripting/include/shavit/replay-file.inc @@ -365,6 +365,35 @@ stock void WriteReplayHeader(File fFile, int style, int track, float time, int s fFile.WriteInt32(view_as(fZoneOffset[1])); } +stock void cell2buf(char[] buf, int& pos, int cell) +{ + buf[pos++] = cell & 0xFF; + buf[pos++] = (cell >> 8) & 0xFF; + buf[pos++] = (cell >> 16) & 0xFF; + buf[pos++] = (cell >> 24) & 0xFF; +} + +stock int WriteReplayHeaderToBuffer(char[] buf, int style, int track, float time, int steamid, int preframes, int postframes, float fZoneOffset[2], int totalframes, float tickrate, const char[] sMap) +{ + int pos = FormatEx(buf, 512, "%d:%s\n%s", REPLAY_FORMAT_SUBVERSION, REPLAY_FORMAT_FINAL, sMap); + pos += 1; // skip past NUL + buf[pos++] = style & 0xFF; + buf[pos++] = track & 0xFF; + cell2buf(buf, pos, preframes); + + cell2buf(buf, pos, totalframes - preframes - postframes); + cell2buf(buf, pos, view_as(time)); + cell2buf(buf, pos, steamid); + + cell2buf(buf, pos, postframes); + cell2buf(buf, pos, view_as(tickrate)); + + cell2buf(buf, pos, view_as(fZoneOffset[0])); + cell2buf(buf, pos, view_as(fZoneOffset[1])); + + return pos; +} + // file_a is usually used as the wr replay file. // file_b is usually used as the duplicate/backup replay file. stock void WriteReplayFrames(ArrayList playerrecording, int iSize, File file_a, File file_b) diff --git a/addons/sourcemod/scripting/include/shavit/replay-recorder.inc b/addons/sourcemod/scripting/include/shavit/replay-recorder.inc index 4ca5ad31..4dd19a7d 100644 --- a/addons/sourcemod/scripting/include/shavit/replay-recorder.inc +++ b/addons/sourcemod/scripting/include/shavit/replay-recorder.inc @@ -48,8 +48,9 @@ forward Action Shavit_ShouldSaveReplayCopy(int client, int style, float time, in /** * Called when either a WR replay or a copy of a replay has been saved. + * NOTE: Can be called with a delay after a run is finished due to asynchronous replay saving through extensions. * - * @param client Client index. + * @param client Client index. Can be 0 if the replay was saved asynchronously & the client disconnected super duper quick... * @param style Style the record was done on. * @param time Record time. * @param jumps Jumps amount. diff --git a/addons/sourcemod/scripting/include/srcwr/floppy.inc b/addons/sourcemod/scripting/include/srcwr/floppy.inc new file mode 100644 index 00000000..47962109 --- /dev/null +++ b/addons/sourcemod/scripting/include/srcwr/floppy.inc @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2025 rtldg + +#if defined _floppy_included + #endinput +#endif +#define _floppy_included +#pragma semicolon 1 + + +typeset ReplaySavedCallback { + function void(bool saved, any value, char[] sPath); +} + + +// Don't modify the `playerrecording` ArrayList until the ReplaySavedCallback is called... OR ELSE!!!! +native void SRCWRFloppy_AsyncSaveReplay( + ReplaySavedCallback callback // what to call when saved + , any value // what to pass along to the callback + , char[] wrpath + , char[] copypath + , char[] header + , int headersize + , ArrayList playerrecording + , int totalframes +); + + +public Extension __ext_srcwrfloppy = +{ + name = "srcwr💾", + file = "srcwr💾.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public void __ext_floppy_SetNTVOptional() +{ + MarkNativeAsOptional("SRCWRFloppy_AsyncSaveReplay"); +} +#endif diff --git a/addons/sourcemod/scripting/shavit-replay-recorder.sp b/addons/sourcemod/scripting/shavit-replay-recorder.sp index 3f5d3ee7..a8a0ac74 100644 --- a/addons/sourcemod/scripting/shavit-replay-recorder.sp +++ b/addons/sourcemod/scripting/shavit-replay-recorder.sp @@ -34,6 +34,10 @@ #include #include +#undef REQUIRE_EXTENSIONS +#include + + public Plugin myinfo = { name = "[shavit] Replay Recorder", @@ -94,6 +98,7 @@ float gF_HijackedAngles[MAXPLAYERS+1][2]; bool gB_HijackFramesKeepOnStart[MAXPLAYERS+1]; bool gB_ReplayPlayback = false; +bool gB_Floppy = false; //#include forward void TickRate_OnTickRateChanged(float fOld, float fNew); @@ -149,6 +154,7 @@ public void OnPluginStart() gF_Tickrate = (1.0 / GetTickInterval()); gB_ReplayPlayback = LibraryExists("shavit-replay-playback"); + gB_Floppy = LibraryExists("srcwr💾"); if (gB_Late) { @@ -170,6 +176,10 @@ public void OnLibraryAdded(const char[] name) { gB_ReplayPlayback = true; } + else if (StrEqual(name, "srcwr💾")) + { + gB_Floppy = true; + } } public void OnLibraryRemoved(const char[] name) @@ -178,6 +188,10 @@ public void OnLibraryRemoved(const char[] name) { gB_ReplayPlayback = false; } + else if (StrEqual(name, "srcwr💾")) + { + gB_Floppy = false; + } } public void OnMapStart() @@ -378,13 +392,94 @@ void DoReplaySaverCallbacks(int iSteamID, int client, int style, float time, int int postframes = gI_PlayerFrames[client] - gI_PlayerFinishFrame[client]; - char sPath[PLATFORM_MAX_PATH]; - bool saved = SaveReplay(style, track, time, iSteamID, gI_PlayerPrerunFrames[client], gA_PlayerFrames[client], gI_PlayerFrames[client], postframes, timestamp, fZoneOffset, makeCopy, makeReplay, sPath, sizeof(sPath)); + ArrayList playerrecording = view_as(CloneHandle(gA_PlayerFrames[client])); + + DataPack dp = new DataPack(); + dp.WriteCell(GetClientSerial(client)); + dp.WriteCell(style); + dp.WriteCell(time); + dp.WriteCell(jumps); + dp.WriteCell(strafes); + dp.WriteCell(sync); + dp.WriteCell(track); + dp.WriteCell(oldtime); + dp.WriteCell(perfs); + dp.WriteCell(avgvel); + dp.WriteCell(maxvel); + dp.WriteCell(timestamp); + dp.WriteCell(isBestReplay); + dp.WriteCell(isTooLong); + dp.WriteCell(makeCopy); + dp.WriteCell(playerrecording); + dp.WriteCell(gI_PlayerPrerunFrames[client]); + dp.WriteCell(postframes); + dp.WriteString(sName); + + if (gB_Floppy) + { + char buf[512]; + int headersize = WriteReplayHeaderToBuffer(buf, style, track, time, iSteamID, gI_PlayerPrerunFrames[client], postframes, fZoneOffset, gI_PlayerFrames[client], gF_Tickrate, gS_Map); + + char wrpath[PLATFORM_MAX_PATH], copypath[PLATFORM_MAX_PATH]; + if (makeReplay) + FormatEx(wrpath, sizeof(wrpath), + track>0?"%s/%d/%s%s_%d.replay" : "%s/%d/%s%s.replay", + gS_ReplayFolder, style, gS_Map, track + ); + if (makeCopy) + FormatEx(copypath, sizeof(copypath), "%s/copy/%d_%d_%s.replay", gS_ReplayFolder, timestamp, iSteamID, gS_Map); + + SRCWRFloppy_AsyncSaveReplay( + FloppyAsynchronouslySavedMyReplayWhichWasNiceOfThem + , dp + , wrpath + , copypath + , buf + , headersize + , playerrecording + , gI_PlayerFrames[client] + ); + } + else + { + char sPath[PLATFORM_MAX_PATH]; + bool saved = SaveReplay(style, track, time, iSteamID, gI_PlayerPrerunFrames[client], playerrecording, gI_PlayerFrames[client], postframes, timestamp, fZoneOffset, makeCopy, makeReplay, sPath, sizeof(sPath)); + FloppyAsynchronouslySavedMyReplayWhichWasNiceOfThem(saved, dp, sPath) + } + + ClearFrames(client); +} + +void FloppyAsynchronouslySavedMyReplayWhichWasNiceOfThem(bool saved, any value, char[] sPath) +{ + DataPack dp = value; + dp.Reset(); + + int client = GetClientFromSerial(dp.ReadCell()); + int style = dp.ReadCell(); + float time = dp.ReadCell(); + int jumps = dp.ReadCell(); + int strafes = dp.ReadCell(); + float sync = dp.ReadCell(); + int track = dp.ReadCell(); + float oldtime = dp.ReadCell(); + float perfs = dp.ReadCell(); + float avgvel = dp.ReadCell(); + float maxvel = dp.ReadCell(); + int timestamp = dp.ReadCell(); + bool isBestReplay = dp.ReadCell(); + bool isTooLong = dp.ReadCell(); + bool makeCopy = dp.ReadCell(); + ArrayList playerrecording = dp.ReadCell(); + int preframes = dp.ReadCell(); + int postframes = dp.ReadCell(); + char sName[MAX_NAME_LENGTH]; + dp.ReadString(sName, sizeof(sName)); if (!saved) { - LogError("SaveReplay() failed. Skipping OnReplaySaved") - ClearFrames(client); + LogError("Failed to save replay... Skipping OnReplaySaved"); + delete playerrecording; // importante! return; } @@ -405,13 +500,13 @@ void DoReplaySaverCallbacks(int iSteamID, int client, int style, float time, int Call_PushCell(isTooLong); Call_PushCell(makeCopy); Call_PushString(sPath); - Call_PushCell(gA_PlayerFrames[client]); - Call_PushCell(gI_PlayerPrerunFrames[client]); + Call_PushCell(playerrecording); + Call_PushCell(preframes); Call_PushCell(postframes); Call_PushString(sName); Call_Finish(); - ClearFrames(client); + delete playerrecording; } public void Shavit_OnFinish(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp) @@ -469,16 +564,6 @@ bool SaveReplay(int style, int track, float time, int steamid, int preframes, Ar File fWR = null; File fCopy = null; - if (saveWR) - { - FormatEx(sPath, sPathLen, "%s/%d/%s%s.replay", gS_ReplayFolder, style, gS_Map, (track > 0)? sTrack:""); - - if (!(fWR = OpenFile(sPath, "wb+"))) - { - LogError("Failed to open WR replay file for writing. ('%s')", sPath); - } - } - if (saveCopy) { FormatEx(sPath, sPathLen, "%s/copy/%d_%d_%s.replay", gS_ReplayFolder, timestamp, steamid, gS_Map); @@ -489,6 +574,16 @@ bool SaveReplay(int style, int track, float time, int steamid, int preframes, Ar } } + if (saveWR) + { + FormatEx(sPath, sPathLen, "%s/%d/%s%s.replay", gS_ReplayFolder, style, gS_Map, (track > 0)? sTrack:""); + + if (!(fWR = OpenFile(sPath, "wb+"))) + { + LogError("Failed to open WR replay file for writing. ('%s')", sPath); + } + } + if (!fWR && !fCopy) { // I want to try and salvage the replay file so let's write it out to a random From d08a3078e14abccd776e33f99351b54589c67427 Mon Sep 17 00:00:00 2001 From: rtldg Date: Fri, 18 Jul 2025 01:57:40 +0000 Subject: [PATCH 06/13] =?UTF-8?q?use=20srcwr=F0=9F=92=BE=20v2.0.1=20for=20?= =?UTF-8?q?working=20linux=20builds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bc35056..437fb22f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,8 +36,8 @@ jobs: wget https://github.com/srcwr/eventqueuefixfix/releases/download/v1.0.1/eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip unzip eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip "addons/sourcemod/extensions/*" rm "addons/sourcemod/extensions/eventqueuefixfix.pdb" - wget https://github.com/srcwr/srcwrfloppy/releases/download/v2.0.0/srcwrfloppy-v2.0.0.zip - unzip -qO UTF-8 srcwrfloppy-v2.0.0.zip "addons/sourcemod/extensions/*" + wget https://github.com/srcwr/srcwrfloppy/releases/download/v2.0.1/srcwrfloppy-v2.0.1.zip + unzip -qO UTF-8 srcwrfloppy-v2.0.1.zip "addons/sourcemod/extensions/*" rm "addons/sourcemod/extensions/srcwr💾.pdb" - name: Run compiler From f14cbf5aff899e1d012e760b8a6a39c4184d7653 Mon Sep 17 00:00:00 2001 From: rtldg Date: Fri, 18 Jul 2025 17:30:29 +0000 Subject: [PATCH 07/13] =?UTF-8?q?Fix=20replay=20path=20formatting=20when?= =?UTF-8?q?=20using=20srcwr=F0=9F=92=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/sourcemod/scripting/shavit-replay-recorder.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/shavit-replay-recorder.sp b/addons/sourcemod/scripting/shavit-replay-recorder.sp index a8a0ac74..d3cea3ec 100644 --- a/addons/sourcemod/scripting/shavit-replay-recorder.sp +++ b/addons/sourcemod/scripting/shavit-replay-recorder.sp @@ -423,7 +423,7 @@ void DoReplaySaverCallbacks(int iSteamID, int client, int style, float time, int char wrpath[PLATFORM_MAX_PATH], copypath[PLATFORM_MAX_PATH]; if (makeReplay) FormatEx(wrpath, sizeof(wrpath), - track>0?"%s/%d/%s%s_%d.replay" : "%s/%d/%s%s.replay", + track>0?"%s/%d/%s_%d.replay" : "%s/%d/%s.replay", gS_ReplayFolder, style, gS_Map, track ); if (makeCopy) From b1e8e1504346e9475b413b387b7ee06fb9414dc7 Mon Sep 17 00:00:00 2001 From: mourningsickness Date: Fri, 18 Jul 2025 20:41:39 -0700 Subject: [PATCH 08/13] shavit-replay-playback.sp - fix error in case where style == -1 L 07/18/2025 - 06:35:33: [SM] Exception reported: Array index out-of-bounds (index -1, limit 256) L 07/18/2025 - 06:35:33: [SM] Blaming: shavit/shavit-replay-playback.smx L 07/18/2025 - 06:35:33: [SM] Call stack trace: L 07/18/2025 - 06:35:33: [SM] [1] Line 3015, shavit-replay-playback.sp::DeleteConfirmation_Callback --- .../scripting/shavit-replay-playback.sp | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/addons/sourcemod/scripting/shavit-replay-playback.sp b/addons/sourcemod/scripting/shavit-replay-playback.sp index 22c229eb..bf43b3a6 100644 --- a/addons/sourcemod/scripting/shavit-replay-playback.sp +++ b/addons/sourcemod/scripting/shavit-replay-playback.sp @@ -3001,18 +3001,21 @@ public int DeleteConfirmation_Callback(Menu menu, MenuAction action, int param1, menu.GetItem(param2, sInfo, 4); int style = StringToInt(sInfo); - if (style != -1 && DeleteReplay(style, gI_MenuTrack[param1], 0, gS_Map)) + if (style != -1) { - char sTrack[32]; - GetTrackName(param1, gI_MenuTrack[param1], sTrack, 32); + if (DeleteReplay(style, gI_MenuTrack[param1], 0, gS_Map)) + { + char sTrack[32]; + GetTrackName(param1, gI_MenuTrack[param1], sTrack, 32); - LogAction(param1, param1, "Deleted replay for %s on map %s. (Track: %s)", gS_StyleStrings[style].sStyleName, gS_Map, sTrack); + LogAction(param1, param1, "Deleted replay for %s on map %s. (Track: %s)", gS_StyleStrings[style].sStyleName, gS_Map, sTrack); - Shavit_PrintToChat(param1, "%T (%s%s%s)", "ReplayDeleted", param1, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText, gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText); - } - else - { - Shavit_PrintToChat(param1, "%T", "ReplayDeleteFailure", param1, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText); + Shavit_PrintToChat(param1, "%T (%s%s%s)", "ReplayDeleted", param1, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText, gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText); + } + else + { + Shavit_PrintToChat(param1, "%T", "ReplayDeleteFailure", param1, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText); + } } Command_DeleteReplay(param1, 0); From 871c67deac43356194897979ab953cfd38906140 Mon Sep 17 00:00:00 2001 From: rtldg Date: Sat, 19 Jul 2025 19:26:10 +0000 Subject: [PATCH 09/13] =?UTF-8?q?bump=20srcwr=F0=9F=92=BE=20to=20v2.0.3=20?= =?UTF-8?q?to=20fix=20bad=20replay=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 ++-- addons/sourcemod/scripting/include/srcwr/floppy.inc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 437fb22f..feffdd3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,8 +36,8 @@ jobs: wget https://github.com/srcwr/eventqueuefixfix/releases/download/v1.0.1/eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip unzip eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip "addons/sourcemod/extensions/*" rm "addons/sourcemod/extensions/eventqueuefixfix.pdb" - wget https://github.com/srcwr/srcwrfloppy/releases/download/v2.0.1/srcwrfloppy-v2.0.1.zip - unzip -qO UTF-8 srcwrfloppy-v2.0.1.zip "addons/sourcemod/extensions/*" + wget https://github.com/srcwr/srcwrfloppy/releases/download/v2.0.3/srcwrfloppy-v2.0.3.zip + unzip -qO UTF-8 srcwrfloppy-v2.0.3.zip "addons/sourcemod/extensions/*" rm "addons/sourcemod/extensions/srcwr💾.pdb" - name: Run compiler diff --git a/addons/sourcemod/scripting/include/srcwr/floppy.inc b/addons/sourcemod/scripting/include/srcwr/floppy.inc index 47962109..ee2d989a 100644 --- a/addons/sourcemod/scripting/include/srcwr/floppy.inc +++ b/addons/sourcemod/scripting/include/srcwr/floppy.inc @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright 2025 rtldg -#if defined _floppy_included +#if defined _srcwrfloppy_included #endinput #endif -#define _floppy_included +#define _srcwrfloppy_included #pragma semicolon 1 @@ -43,7 +43,7 @@ public Extension __ext_srcwrfloppy = }; #if !defined REQUIRE_EXTENSIONS -public void __ext_floppy_SetNTVOptional() +public void __ext_srcwrfloppy_SetNTVOptional() { MarkNativeAsOptional("SRCWRFloppy_AsyncSaveReplay"); } From 309cdf3acf7dc91582370c7e13ca2f50c9fe910e Mon Sep 17 00:00:00 2001 From: rtldg Date: Sat, 19 Jul 2025 21:39:20 +0000 Subject: [PATCH 10/13] Update CSGO m_afButtonDisabled signature --- addons/sourcemod/gamedata/shavit.games.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/sourcemod/gamedata/shavit.games.txt b/addons/sourcemod/gamedata/shavit.games.txt index ac99c646..5d9b9fa5 100644 --- a/addons/sourcemod/gamedata/shavit.games.txt +++ b/addons/sourcemod/gamedata/shavit.games.txt @@ -162,10 +162,13 @@ } // search "sv_maxusrcmdprocessticks_warning at server" to find CPlayerMove::RunCommand // then sig the the `mov REG1,dword ptr [REG2 + OFFSET_TO_BUTTON_DISABLED_HERE]` + // looks like these in decompiled form: + // (windows) `param_3[0xc] = param_3[0xc] & ~param_2[0x32b];` + // (linux) `*(uint *)(param_3 + 0x30) = uVar12 & ~param_2[0x331];` "CBasePlayer->m_afButtonDisabled" { - "windows" "\x8B\x87\x2A\x2A\x2A\x2A\xF7\xD0\x21\x43" - "linux" "\x8B\x83\x2A\x2A\x2A\x2A\xF7\xD0" + "windows" "\x8B\x86\x2A\x2A\x2A\x2A\xF7\xD0\x21\x47" + "linux" "\x8b\x93\x2A\x2A\x2A\x2A\xF7\xD2\x21\xd0" } } } From 00feb624c2c78c94d2780ce694eff0226379f5d2 Mon Sep 17 00:00:00 2001 From: rtldg Date: Wed, 23 Jul 2025 23:06:24 +0000 Subject: [PATCH 11/13] Fix the on-ground 0.5s start-timer check from 89e97dfd3d5710ec5c25bafa78f0ded2c05d15a9 --- addons/sourcemod/scripting/shavit-core.sp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/addons/sourcemod/scripting/shavit-core.sp b/addons/sourcemod/scripting/shavit-core.sp index cd91bb1e..98aad5f1 100644 --- a/addons/sourcemod/scripting/shavit-core.sp +++ b/addons/sourcemod/scripting/shavit-core.sp @@ -2569,10 +2569,15 @@ bool CanStartTimer(int client, int track, bool skipGroundCheck) if (skipGroundTimer) return true; - int halfSecOfTicks = RoundFloat(0.5 / GetTickInterval()); - int onGroundTicks = gI_LastTickcount[client] - gI_FirstTouchedGround[client]; + if (gI_FirstTouchedGround[client] > 0) + { + int halfSecOfTicks = RoundFloat(0.5 / GetTickInterval()); + int onGroundTicks = gI_LastTickcount[client] - gI_FirstTouchedGround[client]; - return onGroundTicks >= halfSecOfTicks; + return onGroundTicks >= halfSecOfTicks; + } + + return false; } void StartTimer(int client, int track, bool skipGroundCheck) @@ -3672,6 +3677,11 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 } } + if (!bOnGround) + { + gI_FirstTouchedGround[client] = 0; + } + // This can be bypassed by spamming +duck on CSS which causes `iGroundEntity` to be `-1` here... // (e.g. an autobhop + velocity_limit style...) // m_hGroundEntity changes from 0 -> -1 same tick which causes problems and I'm not sure what the best way / place to handle that is... From 128940ff193e2b0ff22c6cc8eaa660e7b7883a6f Mon Sep 17 00:00:00 2001 From: rtldg Date: Fri, 25 Jul 2025 01:17:07 +0000 Subject: [PATCH 12/13] Fix the on-ground 0.5s start-timer even further dot dot dot --- addons/sourcemod/scripting/shavit-core.sp | 46 ++++++++++++++++++----- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/addons/sourcemod/scripting/shavit-core.sp b/addons/sourcemod/scripting/shavit-core.sp index 98aad5f1..6d3362bc 100644 --- a/addons/sourcemod/scripting/shavit-core.sp +++ b/addons/sourcemod/scripting/shavit-core.sp @@ -92,7 +92,8 @@ Handle gH_Forwards_OnProcessMovementPost = null; // player timer variables timer_snapshot_t gA_Timers[MAXPLAYERS+1]; bool gB_Auto[MAXPLAYERS+1]; -int gI_FirstTouchedGround[MAXPLAYERS+1]; +// 0 is in air, 1 or greater is on ground, -1 means client was on ground with zero...ish... velocity +int gI_FirstTouchedGroundForStartTimer[MAXPLAYERS+1]; int gI_LastTickcount[MAXPLAYERS+1]; // these are here until the compiler bug is fixed @@ -2569,10 +2570,15 @@ bool CanStartTimer(int client, int track, bool skipGroundCheck) if (skipGroundTimer) return true; - if (gI_FirstTouchedGround[client] > 0) + if (gI_FirstTouchedGroundForStartTimer[client] < 0) + { + // was on ground with zero...ish... velocity... + return true; + } + else if (gI_FirstTouchedGroundForStartTimer[client] > 0) { int halfSecOfTicks = RoundFloat(0.5 / GetTickInterval()); - int onGroundTicks = gI_LastTickcount[client] - gI_FirstTouchedGround[client]; + int onGroundTicks = gI_LastTickcount[client] - gI_FirstTouchedGroundForStartTimer[client]; return onGroundTicks >= halfSecOfTicks; } @@ -2769,7 +2775,7 @@ public void OnClientPutInServer(int client) gA_Timers[client].fNextFrameTime = 0.0; gA_Timers[client].fplayer_speedmod = 1.0; gS_DeleteMap[client][0] = 0; - gI_FirstTouchedGround[client] = 0; + gI_FirstTouchedGroundForStartTimer[client] = 0; gI_LastTickcount[client] = 0; gI_HijackFrames[client] = 0; gI_LastPrintedSteamID[client] = 0; @@ -3652,10 +3658,35 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 gI_LastTickcount[client] = tickcount; + if (bOnGround) + { + if (gI_FirstTouchedGroundForStartTimer[client] == 0) + { + // just landed (or teleported to the ground or whatever) + gI_FirstTouchedGroundForStartTimer[client] = tickcount; + } + + if (gI_FirstTouchedGroundForStartTimer[client] > 0) + { + float fSpeed[3]; + GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed); + + // zero...ish... velocity... (squared-ish (cubed?)) + if (GetVectorLength(fSpeed, true) <= 1000.0) + { + gI_FirstTouchedGroundForStartTimer[client] = -1; + } + } + } + else + { + gI_FirstTouchedGroundForStartTimer[client] = 0; + } + if(bOnGround && !gA_Timers[client].bOnGround) { gA_Timers[client].iLandingTick = tickcount; - gI_FirstTouchedGround[client] = tickcount; + gI_FirstTouchedGroundForStartTimer[client] = tickcount; if (gEV_Type != Engine_TF2 && GetStyleSettingBool(gA_Timers[client].bsStyle, "easybhop")) { @@ -3677,11 +3708,6 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 } } - if (!bOnGround) - { - gI_FirstTouchedGround[client] = 0; - } - // This can be bypassed by spamming +duck on CSS which causes `iGroundEntity` to be `-1` here... // (e.g. an autobhop + velocity_limit style...) // m_hGroundEntity changes from 0 -> -1 same tick which causes problems and I'm not sure what the best way / place to handle that is... From 0ebfa900b9468c6413ebf10b61dd35cc27d20e34 Mon Sep 17 00:00:00 2001 From: rtldg Date: Thu, 31 Jul 2025 02:11:42 +0000 Subject: [PATCH 13/13] =?UTF-8?q?bump=20to=20srcwr=F0=9F=92=BE=20v2.0.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index feffdd3b..82da0e2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,8 +36,8 @@ jobs: wget https://github.com/srcwr/eventqueuefixfix/releases/download/v1.0.1/eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip unzip eventqueuefixfix-v1.0.1-def5b0e-windows-x32.zip "addons/sourcemod/extensions/*" rm "addons/sourcemod/extensions/eventqueuefixfix.pdb" - wget https://github.com/srcwr/srcwrfloppy/releases/download/v2.0.3/srcwrfloppy-v2.0.3.zip - unzip -qO UTF-8 srcwrfloppy-v2.0.3.zip "addons/sourcemod/extensions/*" + wget https://github.com/srcwr/srcwrfloppy/releases/download/v2.0.4/srcwrfloppy-v2.0.4.zip + unzip -qO UTF-8 srcwrfloppy-v2.0.4.zip "addons/sourcemod/extensions/*" rm "addons/sourcemod/extensions/srcwr💾.pdb" - name: Run compiler