Skip to content

Commit 9414e34

Browse files
authored
Auto-correct when the wrong build is installed (#33)
Co-authored-by: tommy <[email protected]>
1 parent 4a82b33 commit 9414e34

4 files changed

Lines changed: 211 additions & 19 deletions

File tree

addons/sourcemod/scripting/include/offstyledb.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ ConVar gCV_ReplayMode = null; // -1=never, 0=WRs only, 1=all times (d
6464
ConVar gCV_AutoUpdate = null; // 0=off, 1=check only, 2=check + download + auto-apply
6565
char gS_LatestTag[32];
6666
bool gB_UpdateInFlight = false;
67+
bool gB_BuildMismatch = false; // set in OnAllPluginsLoaded if running shavit differs from build
6768

6869
// Helper function for debug logging
6970
void DebugPrint(const char[] format, any ...)
@@ -171,6 +172,8 @@ public void OnAllPluginsLoaded()
171172
GetTimerSQLPrefix(gS_MySQLPrefix, sizeof(gS_MySQLPrefix));
172173
}
173174
}
175+
176+
Updater_CheckBuildMatch();
174177
}
175178

176179
public void OnConfigsExecuted()

addons/sourcemod/scripting/include/offstyledb_records.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ void Records_HandleFinish(int client, int style, float time, int jumps, int stra
4242

4343
DebugPrint("OnFinish: client=%d style=%d time=%f oldtime=%f track=%d", client, style, time, oldtime, track);
4444

45+
// Wrong-build guard: if the installed .smx doesn't match the running
46+
// shavit version, suppress submissions until the updater swaps builds.
47+
if (gB_BuildMismatch)
48+
{
49+
return;
50+
}
51+
4552
if (!IsSubmittableFinish(client, track))
4653
{
4754
return;

addons/sourcemod/scripting/include/offstyledb_shavit.inc

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,6 @@ bool IsTempReplayPath(const char[] path)
7979
}
8080

8181
#if !defined SHAVIT_V3
82-
int GetShavitMajorVersion()
83-
{
84-
int major = SHAVIT_VERSION_MAJOR;
85-
86-
return major;
87-
}
88-
8982
void GetTempReplayPath(int client, char[] buffer, int maxlen)
9083
{
9184
BuildPath(Path_SM, buffer, maxlen, "data/osdb_tmp/%d_%d.replay", client, GetTime());
@@ -155,22 +148,32 @@ void ShavitCompat_MarkOptionalNatives()
155148
void ShavitCompat_OnPluginStart()
156149
{
157150
#if !defined SHAVIT_V3
158-
int smv = GetShavitMajorVersion();
159-
if (smv < 4)
160-
{
161-
PrintToServer("[OSdb] smv = %d", smv);
162-
SetFailState("[OSdb] bhoptimer version <4 detected, use the v3 build (offstyledb_v3.smx)");
163-
}
164-
else if (smv >= 5)
165-
{
166-
// probably needless but future proofing is nice ig
167-
DebugPrint("bhoptimer version >4 detected, there may be compatibility issues, check for update here https://github.com/offstyles/offstyle-plugins/releases");
168-
}
169-
170151
EnsureTempReplayDir();
171152
#endif
172153
}
173154

155+
// Which shavit major version this .smx was compiled for. Compile-time constant,
156+
// not a runtime query.
157+
int ShavitCompat_BuildVersion()
158+
{
159+
#if defined SHAVIT_V3
160+
return 3;
161+
#else
162+
return 4;
163+
#endif
164+
}
165+
166+
// Runtime detection of the shavit major version actually loaded on the server.
167+
// Keyed on Shavit_AlsoSaveReplayTo, which is a v4-only native.
168+
int ShavitCompat_DetectRunningVersion()
169+
{
170+
if (GetFeatureStatus(FeatureType_Native, "Shavit_AlsoSaveReplayTo") == FeatureStatus_Available)
171+
{
172+
return 4;
173+
}
174+
return 3;
175+
}
176+
174177
// --- Shavit forwards: thin adapters delegating to the records layer ---
175178

176179
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)

addons/sourcemod/scripting/include/offstyledb_updater.inc

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,182 @@ public Action Command_ApplyUpdate(int client, int args)
249249
return Plugin_Handled;
250250
}
251251

252+
// Called from OnAllPluginsLoaded once shavit natives have been resolved.
253+
// Detects mismatch between this build and the running shavit version, and
254+
// triggers a build switch when OSdb_autoupdate is 2.
255+
void Updater_CheckBuildMatch()
256+
{
257+
int running = ShavitCompat_DetectRunningVersion();
258+
int built = ShavitCompat_BuildVersion();
259+
260+
if (running == built)
261+
{
262+
return;
263+
}
264+
265+
gB_BuildMismatch = true;
266+
LogError("[OSdb] This is the shavit v%d build but the server is running shavit v%d. Records will not submit until the correct build is installed.", built, running);
267+
268+
int mode = gCV_AutoUpdate.IntValue;
269+
if (mode == 0)
270+
{
271+
PrintToServer("[OSdb] OSdb_autoupdate is 0 - install offstyledb%s.smx manually, or set OSdb_autoupdate 2 to auto-correct.", running == 3 ? "_v3" : "");
272+
return;
273+
}
274+
if (mode < 2)
275+
{
276+
PrintToServer("[OSdb] Set OSdb_autoupdate 2 to auto-download the correct build, or manually install offstyledb%s.smx.", running == 3 ? "_v3" : "");
277+
return;
278+
}
279+
280+
PrintToServer("[OSdb] Auto-switching to offstyledb%s.smx...", running == 3 ? "_v3" : "");
281+
Updater_SwitchToBuild(running);
282+
}
283+
284+
// Fetches the latest release and downloads the OTHER build's .smx asset, then
285+
// loads it and unloads self.
286+
void Updater_SwitchToBuild(int targetVersion)
287+
{
288+
if (gB_UpdateInFlight)
289+
{
290+
return;
291+
}
292+
gB_UpdateInFlight = true;
293+
294+
HTTPRequest req = new HTTPRequest(UPDATER_API_URL);
295+
req.SetHeader("Accept", "application/vnd.github+json");
296+
req.SetHeader("User-Agent", "offstyledb-updater");
297+
req.Get(Callback_OnSwitchReleaseInfo, targetVersion);
298+
}
299+
300+
public void Callback_OnSwitchReleaseInfo(HTTPResponse resp, any value)
301+
{
302+
int targetVersion = value;
303+
304+
if (resp.Status != HTTPStatus_OK || resp.Data == null)
305+
{
306+
LogError("[OSdb] Build-switch: GitHub API failed (status=%d)", resp.Status);
307+
gB_UpdateInFlight = false;
308+
return;
309+
}
310+
311+
char targetSmx[64];
312+
BuildSmxNameForVersion(targetVersion, targetSmx, sizeof(targetSmx));
313+
314+
JSONObject data = view_as<JSONObject>(resp.Data);
315+
JSONArray assets = view_as<JSONArray>(data.Get("assets"));
316+
char downloadUrl[256];
317+
downloadUrl[0] = '\0';
318+
319+
if (assets != null)
320+
{
321+
for (int i = 0; i < assets.Length; i++)
322+
{
323+
JSONObject asset = view_as<JSONObject>(assets.Get(i));
324+
char name[64];
325+
asset.GetString("name", name, sizeof(name));
326+
327+
if (StrEqual(name, targetSmx))
328+
{
329+
asset.GetString("browser_download_url", downloadUrl, sizeof(downloadUrl));
330+
delete asset;
331+
break;
332+
}
333+
334+
delete asset;
335+
}
336+
}
337+
delete assets;
338+
delete data;
339+
340+
if (downloadUrl[0] == '\0')
341+
{
342+
LogError("[OSdb] Build-switch: no %s asset in the latest release.", targetSmx);
343+
gB_UpdateInFlight = false;
344+
return;
345+
}
346+
347+
char stagePath[PLATFORM_MAX_PATH];
348+
BuildPath(Path_SM, stagePath, sizeof(stagePath), "plugins/%s.new", targetSmx);
349+
350+
HTTPRequest dl = new HTTPRequest(downloadUrl);
351+
dl.SetHeader("User-Agent", "offstyledb-updater");
352+
dl.DownloadFile(stagePath, Callback_OnSwitchDownloaded, targetVersion);
353+
}
354+
355+
public void Callback_OnSwitchDownloaded(HTTPStatus status, any value)
356+
{
357+
int targetVersion = value;
358+
gB_UpdateInFlight = false;
359+
360+
if (status != HTTPStatus_OK)
361+
{
362+
LogError("[OSdb] Build-switch download failed (status=%d)", status);
363+
return;
364+
}
365+
366+
char targetSmx[64];
367+
char targetSmName[32];
368+
BuildSmxNameForVersion(targetVersion, targetSmx, sizeof(targetSmx));
369+
BuildSmNameForVersion(targetVersion, targetSmName, sizeof(targetSmName));
370+
371+
char livePath[PLATFORM_MAX_PATH];
372+
char stagePath[PLATFORM_MAX_PATH];
373+
BuildPath(Path_SM, livePath, sizeof(livePath), "plugins/%s", targetSmx);
374+
BuildPath(Path_SM, stagePath, sizeof(stagePath), "plugins/%s.new", targetSmx);
375+
376+
if (FileExists(livePath) && !DeleteFile(livePath))
377+
{
378+
LogError("[OSdb] Build-switch: could not delete %s - install manually.", livePath);
379+
return;
380+
}
381+
382+
if (!RenameFile(livePath, stagePath))
383+
{
384+
LogError("[OSdb] Build-switch: rename %s -> %s failed.", stagePath, livePath);
385+
return;
386+
}
387+
388+
LogMessage("[OSdb] Build-switch: installed %s for shavit v%d.", targetSmx, targetVersion);
389+
PrintToServer("[OSdb] Installed correct build %s, loading and unloading self.", targetSmx);
390+
391+
// Remove our own .smx so the wrong build doesn't re-load on next restart.
392+
// On Windows this may fail while the plugin is loaded; admin will see the
393+
// log and can clean up manually.
394+
char ownPath[PLATFORM_MAX_PATH];
395+
BuildPath(Path_SM, ownPath, sizeof(ownPath), "plugins/" ... UPDATER_SMX_NAME);
396+
if (FileExists(ownPath) && !DeleteFile(ownPath))
397+
{
398+
LogError("[OSdb] Build-switch: could not delete own .smx at %s - please remove manually.", ownPath);
399+
}
400+
401+
char cmd[128];
402+
FormatEx(cmd, sizeof(cmd), "sm plugins load %s", targetSmName);
403+
ServerCommand(cmd);
404+
ServerCommand("sm plugins unload " ... UPDATER_SM_NAME);
405+
}
406+
407+
void BuildSmxNameForVersion(int version, char[] buf, int maxlen)
408+
{
409+
if (version == 3)
410+
{
411+
strcopy(buf, maxlen, "offstyledb_v3.smx");
412+
}
413+
else
414+
{
415+
strcopy(buf, maxlen, "offstyledb.smx");
416+
}
417+
}
418+
419+
void BuildSmNameForVersion(int version, char[] buf, int maxlen)
420+
{
421+
if (version == 3)
422+
{
423+
strcopy(buf, maxlen, "offstyledb_v3");
424+
}
425+
else
426+
{
427+
strcopy(buf, maxlen, "offstyledb");
428+
}
429+
}
430+

0 commit comments

Comments
 (0)