From 7b22190f87e49b3218b06ef2152a02a54b534af1 Mon Sep 17 00:00:00 2001 From: tommy Date: Sun, 19 Apr 2026 09:31:22 -0400 Subject: [PATCH] Centralise shavit v3/v4 compat behind an adapter --- .../scripting/include/offstyledb.inc | 35 +-- .../scripting/include/offstyledb_records.inc | 211 +++++------------- .../scripting/include/offstyledb_shavit.inc | 119 +++++++++- 3 files changed, 170 insertions(+), 195 deletions(-) diff --git a/addons/sourcemod/scripting/include/offstyledb.inc b/addons/sourcemod/scripting/include/offstyledb.inc index febc88a..fc8adf7 100644 --- a/addons/sourcemod/scripting/include/offstyledb.inc +++ b/addons/sourcemod/scripting/include/offstyledb.inc @@ -87,44 +87,19 @@ public Plugin myinfo = name = "Offstyle Database", author = "shavit (Modified by Jeft & Tommy)", description = "Provides Offstyles with a database of bhop records.", -#if defined SHAVIT_V3 - version = PLUGIN_VERSION ... " (shavit v3)", -#else - version = PLUGIN_VERSION ... " (shavit v4)", -#endif + version = PLUGIN_VERSION ... SHAVIT_BUILD_LABEL, url = "" }; public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { - MarkNativeAsOptional("Shavit_GetWorldRecord"); - MarkNativeAsOptional("Shavit_OnReplaySaved"); - MarkNativeAsOptional("OnTimerFinished_Post"); - MarkNativeAsOptional("Shavit_OnFinish"); - MarkNativeAsOptional("Shavit_IsPracticeMode"); - MarkNativeAsOptional("Shavit_IsPaused"); -#if defined SHAVIT_V3 - MarkNativeAsOptional("Shavit_ShouldSaveReplayCopy"); -#else - MarkNativeAsOptional("Shavit_AddAdditionalReplayPathsHere"); - MarkNativeAsOptional("Shavit_AlsoSaveReplayTo"); -#endif - + ShavitCompat_MarkOptionalNatives(); return APLRes_Success; } public void OnPluginStart() { -#if !defined SHAVIT_V3 - int smv = GetShavitMajorVersion(); - if (smv < 4) { - PrintToServer("[OSdb] smv = %d", smv); - SetFailState("[OSdb] bhoptimer version <4 detected, use the v3 build (offstyledb_v3.smx)"); - } else if (smv >= 5) { - // probably needless but future proofing is nice ig - DebugPrint("[OSdb] bhoptimer version >4 detected, there may be compatibility issues, check for update here https://github.com/offstyles/offstyle-plugins/releases"); - } -#endif + ShavitCompat_OnPluginStart(); RegConsoleCmd("osdb_get_all_wrs", Command_SendAllWRs); RegConsoleCmd("osdb_viewmapping", Command_ViewStyleMap); @@ -146,10 +121,6 @@ public void OnPluginStart() gM_StyleMapping = new StringMap(); -#if !defined SHAVIT_V3 - EnsureTempReplayDir(); -#endif - DebugPrint("[OSdb] OSdb plugin started, commands registered, ConVars created"); } diff --git a/addons/sourcemod/scripting/include/offstyledb_records.inc b/addons/sourcemod/scripting/include/offstyledb_records.inc index 6d280be..40778f8 100644 --- a/addons/sourcemod/scripting/include/offstyledb_records.inc +++ b/addons/sourcemod/scripting/include/offstyledb_records.inc @@ -16,86 +16,76 @@ bool IsSubmittableFinish(int client, int track) && !Shavit_IsPaused(client); } -#if defined SHAVIT_V3 -// v3: requests shavit to save a replay copy for non-WR PBs when replay_mode=1. -// Also submits the record directly when shavit refuses to save (istoolong), -// since OnFinish already deferred non-WR replay_mode=1 submissions to us. -public Action Shavit_ShouldSaveReplayCopy(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp, bool isbestreplay, bool istoolong) +// Called by the shavit compat layer from Shavit_OnFinish. +void Records_HandleFinish(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime) { - DebugPrint("[OSdb] ShouldSaveReplayCopy fired: client=%d style=%d time=%f oldtime=%f track=%d isbestreplay=%d istoolong=%d replay_mode=%d", - client, style, time, oldtime, track, isbestreplay, istoolong, gCV_ReplayMode.IntValue); - - if (gCV_ReplayMode.IntValue != 1 || isbestreplay) - { - return Plugin_Continue; - } + DebugPrint("[OSdb] oldtime = %f newtime = %f", oldtime, time); + // oldtime <= time is a filter to prevent non-pbs from being submitted + // also means times wont submit if they never beat ur pb, like in the case + // of a skip being removed, but thats up the to server to delete the time if (!IsSubmittableFinish(client, track) || oldtime <= time) { - return Plugin_Continue; + return; } - if (gCV_SubmitMode.IntValue == 0) + float fWR = Shavit_GetWorldRecord(style, track); + bool isWR = (fWR == 0.0 || time <= fWR); + + if (isWR && gCV_ReplayMode.IntValue != -1) { - return Plugin_Continue; + // WRs are handled by Shavit_OnReplaySaved with the correct replay file. + // When replays are disabled we submit here instead, since OnReplaySaved + // may not fire at all. + return; } - float fWR = Shavit_GetWorldRecord(style, track); - if (fWR == 0.0 || time <= fWR) + if (!isWR && gCV_SubmitMode.IntValue == 0) { - // Actually a WR - handled via shavit's canonical replay path - return Plugin_Continue; + DebugPrint("[OSdb] Skipping non-WR submission due to submit mode = 0 (WRs only)"); + return; } - if (istoolong) + if (!isWR && gCV_ReplayMode.IntValue == 1) { - // Shavit won't save a replay for this run, but OnFinish deferred the - // submission to us. Submit the record now without a replay attachment. - DebugPrint("[OSdb] Non-WR replay too long, submitting record without replay"); - - char sMap[64]; - GetCurrentMap(sMap, sizeof(sMap)); - GetMapDisplayName(sMap, sMap, sizeof(sMap)); - - char sSteamID[32]; - GetClientAuthId(client, AuthId_Steam3, sSteamID, sizeof(sSteamID)); - - char sName[MAX_NAME_LENGTH]; - GetClientName(client, sName, sizeof(sName)); - - SendRecord(sMap, sSteamID, sName, GetTime(), time, sync, strafes, jumps, style, false); - return Plugin_Continue; + // Non-WR with replay attachment: submitted from Shavit_OnReplaySaved so the replay is attached + DebugPrint("[OSdb] Deferring non-WR submit to OnReplaySaved (replay_mode=1)"); + return; } - DebugPrint("[OSdb] Requesting non-WR replay copy"); - return Plugin_Handled; + SubmitRecordNow(client, style, time, sync, strafes, jumps, isWR, ""); } -#else -// v4: registers a temp replay path for shavit to save to (normal case), or submits -// the record directly without a replay when shavit refuses to save (istoolong). -// OnFinish already deferred. Fires between Shavit_OnFinish and Shavit_OnReplaySaved. -public void Shavit_AddAdditionalReplayPathsHere(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp, bool isbestreplay, bool istoolong) + +// Called by the shavit compat layer from its pre-save hook +// (Shavit_ShouldSaveReplayCopy on v3, Shavit_AddAdditionalReplayPathsHere on v4). +// Returns true if shavit should save a replay we'll later pick up in +// Shavit_OnReplaySaved. When the replay cannot be saved (istoolong), submits the +// record directly without a replay and returns false. +bool Records_ShouldSaveReplay(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, bool isbestreplay, bool istoolong) { + DebugPrint("[OSdb] ShouldSaveReplay: client=%d style=%d time=%f oldtime=%f track=%d isbestreplay=%d istoolong=%d replay_mode=%d", + client, style, time, oldtime, track, isbestreplay, istoolong, gCV_ReplayMode.IntValue); + if (gCV_ReplayMode.IntValue != 1 || isbestreplay) { - return; + return false; } if (!IsSubmittableFinish(client, track) || oldtime <= time) { - return; + return false; } if (gCV_SubmitMode.IntValue == 0) { - return; + return false; } float fWR = Shavit_GetWorldRecord(style, track); if (fWR == 0.0 || time <= fWR) { // Actually a WR - handled via shavit's canonical replay path - return; + return false; } if (istoolong) @@ -103,35 +93,19 @@ public void Shavit_AddAdditionalReplayPathsHere(int client, int style, float tim // Shavit won't save a replay for this run, but OnFinish deferred the // submission to us. Submit the record now without a replay attachment. DebugPrint("[OSdb] Non-WR replay too long, submitting record without replay"); - - char sMap[64]; - GetCurrentMap(sMap, sizeof(sMap)); - GetMapDisplayName(sMap, sMap, sizeof(sMap)); - - char sSteamID[32]; - GetClientAuthId(client, AuthId_Steam3, sSteamID, sizeof(sSteamID)); - - char sName[MAX_NAME_LENGTH]; - GetClientName(client, sName, sizeof(sName)); - - SendRecord(sMap, sSteamID, sName, GetTime(), time, sync, strafes, jumps, style, false); - return; + SubmitRecordNow(client, style, time, sync, strafes, jumps, false, ""); + return false; } - char sTempPath[PLATFORM_MAX_PATH]; - GetTempReplayPath(client, sTempPath, sizeof(sTempPath)); - Shavit_AlsoSaveReplayTo(sTempPath); - DebugPrint("[OSdb] Requesting non-WR replay at %s", sTempPath); + DebugPrint("[OSdb] Requesting non-WR replay copy"); + return true; } -#endif -// Handles WR submissions, and non-WR submissions when replay_mode=1. -// Fires after the replay file has been written to disk. -#if defined SHAVIT_V3 -public void Shavit_OnReplaySaved(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp, bool isbestreplay, bool istoolong, bool iscopy, const char[] replaypath) -#else -public void Shavit_OnReplaySaved(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp, bool isbestreplay, bool istoolong, ArrayList replaypaths, ArrayList frames, int preframes, int postframes, const char[] name) -#endif +// Called by the shavit compat layer from Shavit_OnReplaySaved. +// isOurCopy indicates the replay was saved at a path we asked for (non-WR copy). +// Callers must have already filtered out foreign saves (e.g. another plugin's +// additional-path registrations). +void Records_HandleReplaySaved(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, bool isbestreplay, const char[] replayPath, bool isOurCopy) { if (client == 0 || !IsSubmittableFinish(client, track)) { @@ -146,106 +120,37 @@ public void Shavit_OnReplaySaved(int client, int style, float time, int jumps, i return; } - float fWR = Shavit_GetWorldRecord(style, track); + float fWR = Shavit_GetWorldRecord(style, track); bool isWR = (fWR == 0.0 || time <= fWR); -#if defined SHAVIT_V3 - if (iscopy) + if (isOurCopy) { - // Non-WR replay copy we requested via Shavit_ShouldSaveReplayCopy + // Non-WR replay copy we requested via the shavit compat layer. if (gCV_ReplayMode.IntValue != 1 || gCV_SubmitMode.IntValue == 0 || oldtime <= time || isWR) { - CleanupTempReplay(replaypath); + CleanupTempReplay(replayPath); return; } } else { - // Canonical (WR) save + // Canonical (WR) save. if (!isbestreplay || !isWR) { return; } } -#else - if (isWR != isbestreplay) - { - // Our registered temp path also triggers this forward with isbestreplay=false; - // only proceed when this invocation's WR status matches isbestreplay. - return; - } - - if (!isWR) - { - if (gCV_ReplayMode.IntValue != 1 || gCV_SubmitMode.IntValue == 0 || oldtime <= time) - { - return; - } - } -#endif DebugPrint("[OSdb] OnReplaySaved submit: isWR=%d submit_mode=%d", isWR, gCV_SubmitMode.IntValue); - char sMap[64]; - GetCurrentMap(sMap, sizeof(sMap)); - GetMapDisplayName(sMap, sMap, sizeof(sMap)); - - char sSteamID[32]; - GetClientAuthId(client, AuthId_Steam3, sSteamID, sizeof(sSteamID)); - - char sName[MAX_NAME_LENGTH]; - GetClientName(client, sName, sizeof(sName)); - - int sDate = GetTime(); - -#if defined SHAVIT_V3 - SendRecord(sMap, sSteamID, sName, sDate, time, sync, strafes, jumps, style, isWR, replaypath); -#else - char sReplayPath[PLATFORM_MAX_PATH]; - if (replaypaths != null && replaypaths.Length > 0) - { - replaypaths.GetString(0, sReplayPath, sizeof(sReplayPath)); - } - - SendRecord(sMap, sSteamID, sName, sDate, time, sync, strafes, jumps, style, isWR, sReplayPath); -#endif + SubmitRecordNow(client, style, time, sync, strafes, jumps, isWR, replayPath); } -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) +// Collects client context (map name, steamid, name, date) and dispatches the +// HTTP submission via SendRecord. Factored out so the OnFinish, OnReplaySaved, +// and istoolong code paths share one call shape. +void SubmitRecordNow(int client, int style, float time, float sync, int strafes, int jumps, bool isWR, const char[] replayPath) { - DebugPrint("[OSdb] oldtime = %f newtime = %f", oldtime, time); - // oldtime <= time is a filter to prevent non-pbs from being submitted - // also means times wont submit if they never beat ur pb, like in the case - // of a skip being removed, but thats up the to server to delete the time - if (!IsSubmittableFinish(client, track) || oldtime <= time) - { - return; - } - - float fWR = Shavit_GetWorldRecord(style, track); - bool isWR = (fWR == 0.0 || time <= fWR); - - if (isWR && gCV_ReplayMode.IntValue != -1) - { - // WRs are handled by Shavit_OnReplaySaved with the correct replay file. - // When replays are disabled we submit here instead, since OnReplaySaved - // may not fire at all. - return; - } - - if (!isWR && gCV_SubmitMode.IntValue == 0) - { - DebugPrint("[OSdb] Skipping non-WR submission due to submit mode = 0 (WRs only)"); - return; - } - - if (!isWR && gCV_ReplayMode.IntValue == 1) - { - // Non-WR with replay attachment: submitted from Shavit_OnReplaySaved so the replay is attached - DebugPrint("[OSdb] Deferring non-WR submit to OnReplaySaved (replay_mode=1)"); - return; - } - char sMap[64]; GetCurrentMap(sMap, sizeof(sMap)); GetMapDisplayName(sMap, sMap, sizeof(sMap)); @@ -256,9 +161,7 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st char sName[MAX_NAME_LENGTH]; GetClientName(client, sName, sizeof(sName)); - int sDate = GetTime(); - - SendRecord(sMap, sSteamID, sName, sDate, time, sync, strafes, jumps, style, isWR); + SendRecord(sMap, sSteamID, sName, GetTime(), time, sync, strafes, jumps, style, isWR, replayPath); } void SendRecord(char[] sMap, char[] sSteamID, char[] sName, int sDate, float time, float sync, int strafes, int jumps, int style, bool isWR, const char[] replayPath = "") diff --git a/addons/sourcemod/scripting/include/offstyledb_shavit.inc b/addons/sourcemod/scripting/include/offstyledb_shavit.inc index 1463f9e..73262c1 100644 --- a/addons/sourcemod/scripting/include/offstyledb_shavit.inc +++ b/addons/sourcemod/scripting/include/offstyledb_shavit.inc @@ -60,18 +60,12 @@ stock void GetTimerSQLPrefix(char[] buffer, int maxlen) delete fFile; } + + #define SHAVIT_BUILD_LABEL " (shavit v3)" #else #undef REQUIRE_PLUGIN #include -#endif - -#if !defined SHAVIT_V3 -int GetShavitMajorVersion() -{ - int major = SHAVIT_VERSION_MAJOR; - - return major; -} + #define SHAVIT_BUILD_LABEL " (shavit v4)" #endif // v4 writes its non-WR replay copies to a temp path we manage; v3 lets shavit @@ -86,6 +80,13 @@ bool IsTempReplayPath(const char[] path) } #if !defined SHAVIT_V3 +int GetShavitMajorVersion() +{ + int major = SHAVIT_VERSION_MAJOR; + + return major; +} + void GetTempReplayPath(int client, char[] buffer, int maxlen) { BuildPath(Path_SM, buffer, maxlen, "data/osdb_tmp/%d_%d.replay", client, GetTime()); @@ -134,3 +135,103 @@ void EnsureTempReplayDir() } } #endif + +// --- Compat helpers called from offstyledb.inc's plugin lifecycle hooks --- + +void ShavitCompat_MarkOptionalNatives() +{ + MarkNativeAsOptional("Shavit_GetWorldRecord"); + MarkNativeAsOptional("Shavit_OnReplaySaved"); + MarkNativeAsOptional("OnTimerFinished_Post"); + MarkNativeAsOptional("Shavit_OnFinish"); + MarkNativeAsOptional("Shavit_IsPracticeMode"); + MarkNativeAsOptional("Shavit_IsPaused"); +#if defined SHAVIT_V3 + MarkNativeAsOptional("Shavit_ShouldSaveReplayCopy"); +#else + MarkNativeAsOptional("Shavit_AddAdditionalReplayPathsHere"); + MarkNativeAsOptional("Shavit_AlsoSaveReplayTo"); +#endif +} + +void ShavitCompat_OnPluginStart() +{ +#if !defined SHAVIT_V3 + int smv = GetShavitMajorVersion(); + if (smv < 4) + { + PrintToServer("[OSdb] smv = %d", smv); + SetFailState("[OSdb] bhoptimer version <4 detected, use the v3 build (offstyledb_v3.smx)"); + } + else if (smv >= 5) + { + // probably needless but future proofing is nice ig + DebugPrint("[OSdb] bhoptimer version >4 detected, there may be compatibility issues, check for update here https://github.com/offstyles/offstyle-plugins/releases"); + } + + EnsureTempReplayDir(); +#endif +} + +// --- Shavit forwards: thin adapters delegating to the records layer --- + +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) +{ + Records_HandleFinish(client, style, time, jumps, strafes, sync, track, oldtime); +} + +#if defined SHAVIT_V3 +// v3: we return Plugin_Handled to ask shavit to save a /copy/ of the replay. +public Action Shavit_ShouldSaveReplayCopy(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp, bool isbestreplay, bool istoolong) +{ + return Records_ShouldSaveReplay(client, style, time, jumps, strafes, sync, track, oldtime, isbestreplay, istoolong) + ? Plugin_Handled + : Plugin_Continue; +} + +public void Shavit_OnReplaySaved(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp, bool isbestreplay, bool istoolong, bool iscopy, const char[] replaypath) +{ + // Ignore saves that are neither our requested copy nor shavit's canonical save. + if (!iscopy && !isbestreplay) + { + return; + } + + Records_HandleReplaySaved(client, style, time, jumps, strafes, sync, track, oldtime, isbestreplay, replaypath, iscopy); +} +#else +// v4: we register a temp path via Shavit_AlsoSaveReplayTo, and shavit calls us back +// with the saved file in Shavit_OnReplaySaved. +public void Shavit_AddAdditionalReplayPathsHere(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp, bool isbestreplay, bool istoolong) +{ + if (!Records_ShouldSaveReplay(client, style, time, jumps, strafes, sync, track, oldtime, isbestreplay, istoolong)) + { + return; + } + + char sTempPath[PLATFORM_MAX_PATH]; + GetTempReplayPath(client, sTempPath, sizeof(sTempPath)); + Shavit_AlsoSaveReplayTo(sTempPath); + DebugPrint("[OSdb] Requesting non-WR replay at %s", sTempPath); +} + +public void Shavit_OnReplaySaved(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp, bool isbestreplay, bool istoolong, ArrayList replaypaths, ArrayList frames, int preframes, int postframes, const char[] name) +{ + char replayPath[PLATFORM_MAX_PATH]; + if (replaypaths != null && replaypaths.Length > 0) + { + replaypaths.GetString(0, replayPath, sizeof(replayPath)); + } + + // A non-canonical save (isbestreplay=false) is ours only if the path lives in + // our temp dir - other plugins may register their own extra replay paths too. + bool isOurCopy = !isbestreplay && replayPath[0] != '\0' && IsTempReplayPath(replayPath); + + if (!isbestreplay && !isOurCopy) + { + return; + } + + Records_HandleReplaySaved(client, style, time, jumps, strafes, sync, track, oldtime, isbestreplay, replayPath, isOurCopy); +} +#endif