diff --git a/addons/sourcemod/scripting/include/shavit.inc b/addons/sourcemod/scripting/include/shavit.inc index 1f520638..ed2abb5a 100644 --- a/addons/sourcemod/scripting/include/shavit.inc +++ b/addons/sourcemod/scripting/include/shavit.inc @@ -376,14 +376,14 @@ char gS_CSGOColors[][] = // connects synchronously to the bhoptimer database // calls errors if needed -stock Database GetTimerDatabaseHandle() +stock Database GetTimerDatabaseHandle(bool reuse_persistent_connection=true) { Database db = null; char sError[255]; if(SQL_CheckConfig("shavit")) { - if((db = SQL_Connect("shavit", true, sError, 255)) == null) + if((db = SQL_Connect("shavit", reuse_persistent_connection, sError, 255)) == null) { SetFailState("Timer startup failed. Reason: %s", sError); } @@ -396,9 +396,9 @@ stock Database GetTimerDatabaseHandle() return db; } -stock Database2 GetTimerDatabaseHandle2() +stock Database2 GetTimerDatabaseHandle2(bool reuse_persistent_connection=true) { - return view_as(GetTimerDatabaseHandle()); + return view_as(GetTimerDatabaseHandle(reuse_persistent_connection)); } // figures out if the database is a mysql database @@ -1337,6 +1337,13 @@ forward void Shavit_OnProcessMovement(int client); */ forward void Shavit_OnProcessMovementPost(int client); +/** + * Called after shavit-wr caches the current map's WRs. + * + * @noreturn + */ +forward void Shavit_OnWorldRecordsCached(); + /** * Called when a player RTV's. * Requires shavit-mapchooser. diff --git a/addons/sourcemod/scripting/shavit-rankings.sp b/addons/sourcemod/scripting/shavit-rankings.sp index 3bc6a3c1..d7dfeed0 100644 --- a/addons/sourcemod/scripting/shavit-rankings.sp +++ b/addons/sourcemod/scripting/shavit-rankings.sp @@ -84,6 +84,7 @@ StringMap gA_MapTiers = null; Convar gCV_PointsPerTier = null; Convar gCV_WeightingMultiplier = null; Convar gCV_LastLoginRecalculate = null; +Convar gCV_MVPRankOnes_Slow = null; Convar gCV_MVPRankOnes = null; Convar gCV_MVPRankOnes_Main = null; Convar gCV_DefaultTier = null; @@ -100,8 +101,9 @@ Handle gH_Forwards_OnRankAssigned = null; chatstrings_t gS_ChatStrings; int gI_Styles = 0; +bool gB_WorldRecordsCached = false; bool gB_WRHolderTablesMade = false; -bool gB_WRsRefreshed = false; +bool gB_WRHoldersRefreshed = false; int gI_WRHolders[2][STYLE_LIMIT]; int gI_WRHoldersAll; int gI_WRHoldersCvar; @@ -166,6 +168,7 @@ public void OnPluginStart() gCV_PointsPerTier = new Convar("shavit_rankings_pointspertier", "50.0", "Base points to use for per-tier scaling.\nRead the design idea to see how it works: https://github.com/shavitush/bhoptimer/issues/465", 0, true, 1.0); gCV_WeightingMultiplier = new Convar("shavit_rankings_weighting", "0.975", "Weighing multiplier. 1.0 to disable weighting.\nFormula: p[1] * this^0 + p[2] * this^1 + p[3] * this^2 + ... + p[n] * this^(n-1)\nRestart server to apply.", 0, true, 0.01, true, 1.0); gCV_LastLoginRecalculate = new Convar("shavit_rankings_llrecalc", "10080", "Maximum amount of time (in minutes) since last login to recalculate points for a player.\nsm_recalcall does not respect this setting.\n0 - disabled, don't filter anyone", 0, true, 0.0); + gCV_MVPRankOnes_Slow = new Convar("shavit_rankings_mvprankones_slow", "1", "Uses a slower but more featureful MVP counting system.\nEnables the WR Holder ranks & counts for every style & track.\nYou probably won't need to change this unless you have hundreds of thousands of player times in your database.", 0, true, 0.0, true, 1.0); gCV_MVPRankOnes = new Convar("shavit_rankings_mvprankones", "2", "Set the players' amount of MVPs to the amount of #1 times they have.\n0 - Disabled\n1 - Enabled, for all styles.\n2 - Enabled, for default style only.\n(CS:S/CS:GO only)", 0, true, 0.0, true, 2.0); gCV_MVPRankOnes_Main = new Convar("shavit_rankings_mvprankones_maintrack", "1", "If set to 0, all tracks will be counted for the MVP stars.\nOtherwise, only the main track will be checked.\n\nRequires \"shavit_stats_mvprankones\" set to 1 or above.\n(CS:S/CS:GO only)", 0, true, 0.0, true, 1.0); gCV_DefaultTier = new Convar("shavit_rankings_default_tier", "1", "Sets the default tier for new maps added.", 0, true, 0.0, true, 10.0); @@ -225,7 +228,7 @@ public void OnLibraryRemoved(const char[] name) public void Shavit_OnDatabaseLoaded() { GetTimerSQLPrefix(gS_MySQLPrefix, 32); - gH_SQL = view_as(Shavit_GetDatabase()); + gH_SQL = GetTimerDatabaseHandle2(false); if(!IsMySQLDatabase(gH_SQL)) { @@ -291,6 +294,7 @@ public void Shavit_OnDatabaseLoaded() hTrans.AddQuery(sQuery); +#if 0 FormatEx(sQuery, sizeof(sQuery), "CREATE FUNCTION GetRecordPoints(rtrack INT, rtime FLOAT, rmap VARCHAR(255), pointspertier FLOAT, stylemultiplier FLOAT, pwr FLOAT, xtier INT) " ... "RETURNS FLOAT " ... @@ -307,6 +311,7 @@ public void Shavit_OnDatabaseLoaded() "RETURN ppoints; " ... "END;;", gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix); hTrans.AddQuery(sQuery); +#endif gH_SQL.Execute(hTrans, Trans_RankingsSetupSuccess, Trans_RankingsSetupError, 0, DBPrio_High); } @@ -336,7 +341,7 @@ public void OnClientAuthorized(int client) { if (gH_SQL && !IsFakeClient(client)) { - if (gB_WRsRefreshed) + if (gB_WRHoldersRefreshed) { UpdateWRs(client); } @@ -354,7 +359,7 @@ public void OnMapStart() return; } - if (gB_WRHolderTablesMade && !gB_WRsRefreshed) + if (gB_WRHolderTablesMade && !gB_WRHoldersRefreshed) { RefreshWRHolders(); } @@ -397,7 +402,7 @@ public void SQL_GetMapTier_Callback(Database db, DBResultSet results, const char PrintToServer("DEBUG: 3 (tier: %d) (SQL_GetMapTier_Callback)", gI_Tier); #endif - RecalculateAll(); + RecalculateCurrentMap(); UpdateAllPoints(); #if defined DEBUG @@ -451,15 +456,21 @@ public void SQL_FillTierCache_Callback(Database db, DBResultSet results, const c public void OnMapEnd() { gB_TierQueried = false; - gB_WRsRefreshed = false; + gB_WRHoldersRefreshed = false; + gB_WorldRecordsCached = false; // might be null if Shavit_OnDatabaseLoaded hasn't been called yet if (gH_SQL != null) { - RecalculateAll(); + RecalculateCurrentMap(); } } +public void Shavit_OnWorldRecordsCached() +{ + gB_WorldRecordsCached = true; +} + void CS_SetMVPCount_Test(int client, int count) { CS_SetMVPCount(client, count); @@ -514,12 +525,26 @@ void UpdateWRs(int client) char sQuery[512]; - FormatEx(sQuery, sizeof(sQuery), - " SELECT *, 0 as track, 0 as type FROM %swrhrankmain WHERE auth = %d \ - UNION SELECT *, 1 as track, 0 as type FROM %swrhrankbonus WHERE auth = %d \ - UNION SELECT *, -1, 1 as type FROM %swrhrankall WHERE auth = %d \ - UNION SELECT *, -1, 2 as type FROM %swrhrankcvar WHERE auth = %d;", - gS_MySQLPrefix, iSteamID, gS_MySQLPrefix, iSteamID, gS_MySQLPrefix, iSteamID, gS_MySQLPrefix, iSteamID); + if (gCV_MVPRankOnes_Slow.BoolValue) + { + FormatEx(sQuery, sizeof(sQuery), + " SELECT *, 0 as track, 0 as type FROM %swrhrankmain WHERE auth = %d \ + UNION SELECT *, 1 as track, 0 as type FROM %swrhrankbonus WHERE auth = %d \ + UNION SELECT *, -1, 1 as type FROM %swrhrankall WHERE auth = %d \ + UNION SELECT *, -1, 2 as type FROM %swrhrankcvar WHERE auth = %d;", + gS_MySQLPrefix, iSteamID, gS_MySQLPrefix, iSteamID, gS_MySQLPrefix, iSteamID, gS_MySQLPrefix, iSteamID); + } + else + { + FormatEx(sQuery, sizeof(sQuery), + "SELECT 0 as wrrank, -1 as style, auth, COUNT(*), -1 as track, 2 as type FROM %swrs WHERE auth = %d %s %s;", + gS_MySQLPrefix, + iSteamID, + (gCV_MVPRankOnes.IntValue == 2) ? "AND style = 0" : "", + (gCV_MVPRankOnes_Main.BoolValue) ? "AND track = 0" : "" + ); + } + gH_SQL.Query(SQL_GetWRs_Callback, sQuery, GetClientSerial(client)); } @@ -698,12 +723,12 @@ public void SQL_SetMapTier_Callback(Database db, DBResultSet results, const char return; } - RecalculateAll(); + RecalculateCurrentMap(); } public Action Command_RecalcMap(int client, int args) { - RecalculateAll(); + RecalculateCurrentMap(); UpdateAllPoints(true); ReplyToCommand(client, "Done."); @@ -713,49 +738,107 @@ public Action Command_RecalcMap(int client, int args) void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery, int sQueryLen) { - char sTrack[30]; - - if (track != -1) - { - FormatEx(sTrack, sizeof(sTrack), "track %c 0", (track > 0) ? '>' : '='); - } - float fMultiplier = Shavit_GetStyleSettingFloat(style, "rankingmultiplier"); if (Shavit_GetStyleSettingBool(style, "unranked") || fMultiplier == 0.0) { FormatEx(sQuery, sQueryLen, - "UPDATE %splayertimes SET points = 0 WHERE style = %d %s %s%s%c %s %s;", - gS_MySQLPrefix, style, - (bUseCurrentMap || track != -1) ? "AND" : "", - (bUseCurrentMap) ? "map = '" : "", + "UPDATE %splayertimes SET points = 0 WHERE style = %d AND track %c 0 %s%s%s;", + gS_MySQLPrefix, + style, + (track > 0) ? '>' : '=', + (bUseCurrentMap) ? "AND map = '" : "", (bUseCurrentMap) ? gS_Map : "", - (bUseCurrentMap) ? '\'' : ' ', - (bUseCurrentMap && track != -1) ? "AND" : "", - sTrack + (bUseCurrentMap) ? "'" : "" ); + + return; + } + + if (bUseCurrentMap) + { + if (track == Track_Main) + { + if (gB_WorldRecordsCached) + { + float fWR = Shavit_GetWorldRecord(style, track); + + FormatEx(sQuery, sQueryLen, + "UPDATE %splayertimes PT " ... + "SET PT.points = ( "... + " ((%f * %d) * 1.5) + (%f / 15.0)) " ... + "* (%f / PT.time) " ... + "* %f " ... + "WHERE PT.style = %d AND PT.track = 0 AND PT.map = '%s';", + gS_MySQLPrefix, + gCV_PointsPerTier.FloatValue, + gI_Tier, + fWR, + fWR, + fMultiplier, + style, + gS_Map + ); + } + else + { + FormatEx(sQuery, sQueryLen, + "UPDATE %splayertimes PT " ... + "INNER JOIN %swrs WR ON " ... + " PT.track = WR.track AND PT.style = WR.style AND PT.map = WR.map " ... + "SET PT.points = ( "... + " ((%f * %d) * 1.5) + (WR.time / 15.0)) " ... + " * (WR.time / PT.time) " ... + " * %f " ... + "WHERE PT.style = %d AND PT.track = 0 AND PT.map = '%s';", + gS_MySQLPrefix, gS_MySQLPrefix, + gCV_PointsPerTier.FloatValue, + gI_Tier, + fMultiplier, + style, + gS_Map + ); + } + } + else + { + FormatEx(sQuery, sQueryLen, + "UPDATE %splayertimes PT " ... + "INNER JOIN %swrs WR ON " ... + " PT.track = WR.track AND PT.style = WR.style AND PT.map = WR.map " ... + "SET PT.points = ( "... + " ((%f * 1) * 1.5) + (WR.time / 15.0)) " ... + " * (WR.time / PT.time) " ... + " * %f " ... + " * 0.25 " ... + "WHERE PT.style = %d AND PT.track > 0 AND PT.map = '%s';", + gS_MySQLPrefix, gS_MySQLPrefix, + gCV_PointsPerTier.FloatValue, + fMultiplier, + style, + gS_Map + ); + } } else { FormatEx(sQuery, sQueryLen, - "UPDATE %splayertimes AS PT, "... - "( SELECT MIN(time) as time, map, track, style "... - " FROM %splayertimes "... - " WHERE style = %d %s %s%s%c %s %s"... - " GROUP BY map, track, style "... - ") as WR "... - "SET PT.points = GetRecordPoints(PT.track, PT.time, %s, %.1f, %.3f, WR.time, %d) "... - "WHERE PT.style = WR.style and PT.track = WR.track and PT.map = WR.map;", - gS_MySQLPrefix, gS_MySQLPrefix, style, - (bUseCurrentMap || track != -1) ? "AND" : "", - (bUseCurrentMap) ? "map = '" : "", - (bUseCurrentMap) ? gS_Map : "", - (bUseCurrentMap) ? '\'' : ' ', - (bUseCurrentMap && track != -1) ? "AND" : "", - sTrack, - (bUseCurrentMap) ? "''" : "PT.map", - gCV_PointsPerTier.FloatValue, fMultiplier, - gI_Tier + "UPDATE %splayertimes PT " ... + "INNER JOIN %swrs WR ON " ... + " PT.track = WR.track AND PT.style = WR.style AND PT.map = WR.map " ... + "INNER JOIN %smaptiers MT ON " ... + " PT.map = MT.map " ... + "SET PT.points = ( "... + " ((%f * MT.tier) * 1.5) + (WR.time / 15.0)) " ... + "* (WR.time / PT.time) " ... + "* %f %s " ... + "WHERE PT.style = %d AND PT.track %c 0;", + gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, + gCV_PointsPerTier.FloatValue, + fMultiplier, + (track > 0) ? "* 0.25" : "", + style, + (track > 0) ? '>' : '=' ); } } @@ -776,7 +859,9 @@ public Action Command_RecalcAll(int client, int args) { if (!Shavit_GetStyleSettingBool(i, "unranked") && Shavit_GetStyleSettingFloat(i, "rankingmultiplier") != 0.0) { - FormatRecalculate(false, -1, i, sQuery, sizeof(sQuery)); + FormatRecalculate(false, Track_Main, i, sQuery, sizeof(sQuery)); + trans.AddQuery(sQuery); + FormatRecalculate(false, Track_Bonus, i, sQuery, sizeof(sQuery)); trans.AddQuery(sQuery); } } @@ -816,18 +901,20 @@ public void Trans_OnRecalcFail(Database db, any data, int numQueries, const char LogError("Timer (rankings) error! Recalculation failed. Reason: %s", error); } -void RecalculateAll() +void RecalculateCurrentMap() { #if defined DEBUG - LogError("DEBUG: 5 (RecalculateAll)"); + LogError("DEBUG: 5 (RecalculateCurrentMap)"); #endif char sQuery[1024]; for(int i = 0; i < gI_Styles; i++) { - FormatRecalculate(true, -1, i, sQuery, sizeof(sQuery)); - gH_SQL.Query(SQL_Recalculate_Callback, sQuery, 0, DBPrio_High); + FormatRecalculate(true, Track_Main, i, sQuery, sizeof(sQuery)); + gH_SQL.Query(SQL_Recalculate_Callback, sQuery, (i << 8) | 0, DBPrio_High); + FormatRecalculate(true, Track_Bonus, i, sQuery, sizeof(sQuery)); + gH_SQL.Query(SQL_Recalculate_Callback, sQuery, (i << 8) | 1, DBPrio_High); } } @@ -845,20 +932,23 @@ public void Shavit_OnFinish_Post(int client, int style, float time, int jumps, i char sQuery[1024]; FormatRecalculate(true, track, style, sQuery, sizeof(sQuery)); - gH_SQL.Query(SQL_Recalculate_Callback, sQuery, 0, DBPrio_High); + gH_SQL.Query(SQL_Recalculate_Callback, sQuery, (style << 8) | track, DBPrio_High); } -public void SQL_Recalculate_Callback(Database db, DBResultSet results, const char[] error, DataPack data) +public void SQL_Recalculate_Callback(Database db, DBResultSet results, const char[] error, any data) { + int track = data & 0xFF; + int style = data >> 8; + if(results == null) { - LogError("Timer (rankings, recalculate map points) error! Reason: %s", error); + LogError("Timer (rankings, recalculate map points, %s, style=%d) error! Reason: %s", (track == Track_Main) ? "main" : "bonus", style, error); return; } #if defined DEBUG - PrintToServer("Recalculated."); + PrintToServer("Recalculated (%s, style=%d).", (track == Track_Main) ? "main_" : "bonus", style); #endif } @@ -1135,15 +1225,30 @@ public void Trans_WRHolderRankTablesSuccess(Database db, any data, int numQuerie void RefreshWRHolders() { char sQuery[1024]; - FormatEx(sQuery, sizeof(sQuery), - " SELECT 0 as type, 0 as track, style, COUNT(DISTINCT auth) FROM %swrhrankmain GROUP BY STYLE \ - UNION SELECT 0 as type, 1 as track, style, COUNT(DISTINCT auth) FROM %swrhrankbonus GROUP BY STYLE \ - UNION SELECT 1 as type, -1 as track, -1 as style, COUNT(DISTINCT auth) FROM %swrhrankall \ - UNION SELECT 2 as type, -1 as track, -1 as style, COUNT(DISTINCT auth) FROM %swrhrankcvar;", - gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix); + + if (gCV_MVPRankOnes_Slow.BoolValue) + { + FormatEx(sQuery, sizeof(sQuery), + " SELECT 0 as type, 0 as track, style, COUNT(DISTINCT auth) FROM %swrhrankmain GROUP BY STYLE \ + UNION SELECT 0 as type, 1 as track, style, COUNT(DISTINCT auth) FROM %swrhrankbonus GROUP BY STYLE \ + UNION SELECT 1 as type, -1 as track, -1 as style, COUNT(DISTINCT auth) FROM %swrhrankall \ + UNION SELECT 2 as type, -1 as track, -1 as style, COUNT(DISTINCT auth) FROM %swrhrankcvar;", + gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix); + } + else + { + FormatEx(sQuery, sizeof(sQuery), + "SELECT 2 as type, -1 as track, -1 as style, COUNT(DISTINC auth) FROM %swrs %s %s %s;", + gS_MySQLPrefix, + (gCV_MVPRankOnes.IntValue == 2 || gCV_MVPRankOnes_Main.BoolValue) ? "WHERE" : "", + (gCV_MVPRankOnes.IntValue == 2) ? "AND style = 0" : "", + (gCV_MVPRankOnes_Main.BoolValue) ? "AND track = 0" : "" + ); + } + gH_SQL.Query(SQL_GetWRHolders_Callback, sQuery); - gB_WRsRefreshed = true; + gB_WRHoldersRefreshed = true; } public void Trans_WRHolderRankTablesError(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) diff --git a/addons/sourcemod/scripting/shavit-stats.sp b/addons/sourcemod/scripting/shavit-stats.sp index 828ded6b..aa355048 100644 --- a/addons/sourcemod/scripting/shavit-stats.sp +++ b/addons/sourcemod/scripting/shavit-stats.sp @@ -145,7 +145,7 @@ public void OnPluginStart() public void Shavit_OnDatabaseLoaded() { GetTimerSQLPrefix(gS_MySQLPrefix, 32); - gH_SQL = view_as(Shavit_GetDatabase()); + gH_SQL = GetTimerDatabaseHandle2(false); char sQuery[512]; FormatEx(sQuery, sizeof(sQuery), diff --git a/addons/sourcemod/scripting/shavit-wr.sp b/addons/sourcemod/scripting/shavit-wr.sp index f4e38519..d715d2af 100644 --- a/addons/sourcemod/scripting/shavit-wr.sp +++ b/addons/sourcemod/scripting/shavit-wr.sp @@ -58,6 +58,7 @@ Handle gH_OnFinish_Post = null; Handle gH_OnWRDeleted = null; Handle gH_OnWorstRecord = null; Handle gH_OnFinishMessage = null; +Handle gH_OnWorldRecordsCached = null; // database handle Database2 gH_SQL = null; @@ -160,6 +161,7 @@ public void OnPluginStart() gH_OnWRDeleted = CreateGlobalForward("Shavit_OnWRDeleted", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_String); gH_OnWorstRecord = CreateGlobalForward("Shavit_OnWorstRecord", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell); gH_OnFinishMessage = CreateGlobalForward("Shavit_OnFinishMessage", ET_Event, Param_Cell, Param_CellByRef, Param_Array, Param_Cell, Param_Cell, Param_String, Param_Cell, Param_String, Param_Cell); + gH_OnWorldRecordsCached = CreateGlobalForward("Shavit_OnWorldRecordsCached", ET_Event); // player commands RegConsoleCmd("sm_wr", Command_WorldRecord, "View the leaderboard of a map. Usage: sm_wr [map]"); @@ -641,6 +643,9 @@ public void SQL_UpdateWRCache_Callback(Database db, DBResultSet results, const c ReplaceString(sName, MAX_NAME_LENGTH, "#", "?"); gSM_WRNames.SetString(sSteamID, sName, false); } + + Call_StartForward(gH_OnWorldRecordsCached); + Call_Finish(); } public void SQL_UpdateWRStageTimes_Callback(Database db, DBResultSet results, const char[] error, any data) @@ -2275,7 +2280,7 @@ public int SubMenu_Handler(Menu menu, MenuAction action, int param1, int param2) public void Shavit_OnDatabaseLoaded() { GetTimerSQLPrefix(gS_MySQLPrefix, 32); - gH_SQL = view_as(Shavit_GetDatabase()); + gH_SQL = GetTimerDatabaseHandle2(false); gB_MySQL = IsMySQLDatabase(gH_SQL); char sQuery[1024];