diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..12af3c0f5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [shavitush] +custom: https://paypal.me/shavitush diff --git a/addons/sourcemod/configs/shavit-styles.cfg b/addons/sourcemod/configs/shavit-styles.cfg index b1770c946..98308b30f 100644 --- a/addons/sourcemod/configs/shavit-styles.cfg +++ b/addons/sourcemod/configs/shavit-styles.cfg @@ -21,7 +21,7 @@ // Jumping settings "autobhop" "1" // Enable autobhopping and +ds? "easybhop" "1" // Enable easybhop (disable stamina reset)? - "prespeed" "0" // Allow prespeeding regardless of the prespeed server setting? + "prespeed" "0" // Allow prespeeding regardless of the prespeed server setting? If set to 2, the value of shavit_core_nozaxisspeed will be respected as well. "velocity_limit" "0.0" // Velocity limit: set to 0.0 for unlimited, 400.00 for 400vel styles etc. "bunnyhopping" "1" // Per-style sv_enablebunnyhopping. Leave as 1 if you want bunnyhopping to maintain player speed. This setting will override _strafe map settings. diff --git a/addons/sourcemod/configs/shavit-zones.cfg b/addons/sourcemod/configs/shavit-zones.cfg index 1eb9c9707..c22ca60a3 100644 --- a/addons/sourcemod/configs/shavit-zones.cfg +++ b/addons/sourcemod/configs/shavit-zones.cfg @@ -134,6 +134,18 @@ "width" "1.5" } + "Airaccelerate" + { + "visible" "1" + + "red" "118" + "green" "102" + "blue" "173" + + "alpha" "255" + "width" "1.5" + } + "Bonus Start" { "visible" "1" @@ -253,5 +265,17 @@ "alpha" "255" "width" "1.0" } + + "Bonus Airaccelerate" + { + "visible" "1" + + "red" "118" + "green" "102" + "blue" "173" + + "alpha" "255" + "width" "1.0" + } } } diff --git a/addons/sourcemod/scripting/include/shavit.inc b/addons/sourcemod/scripting/include/shavit.inc index 093f3f59b..c5c8cfd73 100644 --- a/addons/sourcemod/scripting/include/shavit.inc +++ b/addons/sourcemod/scripting/include/shavit.inc @@ -23,7 +23,7 @@ #endif #define _shavit_included -#define SHAVIT_VERSION "2.5.4" +#define SHAVIT_VERSION "2.5.5" #define STYLE_LIMIT 256 #define MAX_ZONES 64 #define MAX_NAME_LENGTH_SQL 32 @@ -74,6 +74,23 @@ enum CPR_NotOnGround = (1 << 3) }; +enum +{ + Migration_RemoveWorkshopMaptiers, + Migration_RemoveWorkshopMapzones, + Migration_RemoveWorkshopPlayertimes, + Migration_LastLoginIndex, + Migration_RemoveCountry, + Migration_ConvertIPAddresses, + Migration_ConvertSteamIDsUsers, + Migration_ConvertSteamIDsPlayertimes, + Migration_ConvertSteamIDsChat, + Migration_PlayertimesDateToInt, + Migration_AddZonesFlagsAndData, + Migration_AddPlayertimesCompletions, + MIGRATIONS_END +}; + enum { Zone_Start, @@ -82,11 +99,12 @@ enum Zone_Stop, Zone_Slay, Zone_Freestyle, - Zone_NoVelLimit, + Zone_CustomSpeedLimit, Zone_Teleport, Zone_CustomSpawn, Zone_Easybhop, Zone_Slide, + Zone_Airaccelerate, ZONETYPES_SIZE }; @@ -135,7 +153,7 @@ enum struct stylesettings_t { bool bAutobhop; bool bEasybhop; - bool bPrespeed; + int iPrespeed; float fVelocityLimit; bool bEnableBunnyhopping; float fAiraccelerate; @@ -249,6 +267,62 @@ char gS_CSGOColors[][] = }; #endif +// connects synchronously to the bhoptimer database +// calls errors if needed +stock Database GetTimerDatabaseHandle() +{ + Database db = null; + char sError[255]; + + if(SQL_CheckConfig("shavit")) + { + if((db = SQL_Connect("shavit", true, sError, 255)) == null) + { + SetFailState("Timer startup failed. Reason: %s", sError); + } + } + + else + { + db = SQLite_UseDatabase("shavit", sError, 255); + } + + return db +} + +// figures out if the database is a mysql database +stock bool IsMySQLDatabase(Database db) +{ + char sDriver[8]; + db.Driver.GetIdentifier(sDriver, 8); + + return StrEqual(sDriver, "mysql", false); +} + +// retrieves the table prefix defined in configs/shavit-prefix.txt +stock void GetTimerSQLPrefix(char[] buffer, int maxlen) +{ + char sFile[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); + + File fFile = OpenFile(sFile, "r"); + + if(fFile == null) + { + SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); + } + + char sLine[PLATFORM_MAX_PATH * 2]; + + if(fFile.ReadLine(sLine, PLATFORM_MAX_PATH * 2)) + { + TrimString(sLine); + strcopy(buffer, maxlen, sLine); + } + + delete fFile; +} + stock bool IsValidClient(int client, bool bAlive = false) { return (client >= 1 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client) && !IsClientSourceTV(client) && (!bAlive || IsPlayerAlive(client))); @@ -259,6 +333,25 @@ stock bool IsSource2013(EngineVersion ev) return (ev == Engine_CSS || ev == Engine_TF2); } +stock void IPAddressToString(int ip, char[] buffer, int maxlen) +{ + FormatEx(buffer, maxlen, "%d.%d.%d.%d", ((ip >> 24) & 0xFF), ((ip >> 16) & 0xFF), ((ip >> 8) & 0xFF), (ip & 0xFF)); +} + +stock int IPStringToAddress(const char[] ip) +{ + char sExplodedAddress[4][4]; + ExplodeString(ip, ".", sExplodedAddress, 4, 4, false); + + int iIPAddress = + (StringToInt(sExplodedAddress[0]) << 24) | + (StringToInt(sExplodedAddress[1]) << 16) | + (StringToInt(sExplodedAddress[2]) << 8) | + StringToInt(sExplodedAddress[3]); + + return iIPAddress; +} + // time formatting! stock void FormatSeconds(float time, char[] newtime, int newtimesize, bool precise = true) { @@ -503,7 +596,8 @@ forward void Shavit_OnStyleChanged(int client, int oldstyle, int newstyle, int t forward void Shavit_OnStyleConfigLoaded(int styles); /** - * Called when there's a successful connection to the database. + * Called when there's a successful connection to the database and it is ready to be used. + * Called through shavit-core after migrations have been applied, and after the attempt to create the default `users` table. * * @noreturn */ @@ -536,7 +630,7 @@ forward Action Shavit_OnSave(int client); * @param client Client index. * @param type Zone type. * @param track Zone track. - * @param id Zone ID. 0 for native zones. + * @param id Zone ID. * @param entity Zone trigger entity index. * @noreturn */ @@ -548,7 +642,7 @@ forward void Shavit_OnEnterZone(int client, int type, int track, int id, int ent * @param client Client index. * @param type Zone type. * @param track Zone track. - * @param id Zone ID. 0 for native zones. + * @param id Zone ID. * @param entity Zone trigger entity index. * @noreturn */ @@ -944,7 +1038,7 @@ native float Shavit_GetTimeForRank(int style, int rank, int track); native bool Shavit_ZoneExists(int type, int track); /** - * Checks if a player is inside a mapzone + * Checks if a player is inside a mapzone. * * @param client Client index. * @param type Mapzone type. @@ -953,6 +1047,33 @@ native bool Shavit_ZoneExists(int type, int track); */ native bool Shavit_InsideZone(int client, int type, int track); +/** + * Gets the specified zone's data. + * + * @param zoneid ID of the zone we query the data of. + * @return Zone data. 0 if none is specified. + */ +native int Shavit_GetZoneData(int zoneid); + +/** + * Gets the specified zone's flags. + * + * @param zoneid ID of the zone we query the flags of. + * @return Zone flags. 0 if none is specified. + */ +native int Shavit_GetZoneFlags(int zoneid); + +/** + * Checks if a player is inside a mapzone. + * + * @param client Client index. + * @param type Mapzone type. + * @param track Mapzone track, -1 to ignore track. + * @param zoneid Reference to variable that will hold the zone's ID. + * @return Boolean value. + */ +native bool Shavit_InsideZoneGetID(int client, int type, int track, int &zoneid); + /** * Checks if a player is in the process of creating a mapzone. * @@ -1132,10 +1253,10 @@ native int Shavit_ForceHUDUpdate(int client, bool spectators); * Opens the stats menu for a client. * * @param client Client index. - * @param authid Target SteamID3 to use. + * @param steamid Target Steam account ID to use. * @noreturn */ -native void Shavit_OpenStatsMenu(int client, const char[] authid); +native void Shavit_OpenStatsMenu(int client, int steamid); /** * Retrieves the amount of #1 records a player has. @@ -1450,9 +1571,12 @@ public void __pl_shavit_SetNTVOptional() MarkNativeAsOptional("Shavit_GetWRName"); MarkNativeAsOptional("Shavit_GetWRRecordID"); MarkNativeAsOptional("Shavit_GetWRTime"); + MarkNativeAsOptional("Shavit_GetZoneData"); + MarkNativeAsOptional("Shavit_GetZoneFlags"); MarkNativeAsOptional("Shavit_HasStyleAccess"); MarkNativeAsOptional("Shavit_HijackAngles"); MarkNativeAsOptional("Shavit_InsideZone"); + MarkNativeAsOptional("Shavit_InsideZoneGetID"); MarkNativeAsOptional("Shavit_IsClientCreatingZone"); MarkNativeAsOptional("Shavit_IsKZMap"); MarkNativeAsOptional("Shavit_IsPaused"); diff --git a/addons/sourcemod/scripting/shavit-chat.sp b/addons/sourcemod/scripting/shavit-chat.sp index 68d88b0eb..c48248381 100644 --- a/addons/sourcemod/scripting/shavit-chat.sp +++ b/addons/sourcemod/scripting/shavit-chat.sp @@ -86,6 +86,8 @@ Handle gH_ChatCookie = null; int gI_ChatSelection[MAXPLAYERS+1]; ArrayList gA_ChatRanks = null; +bool gB_ChangedSinceLogin[MAXPLAYERS+1]; + bool gB_NameEnabled[MAXPLAYERS+1]; char gS_CustomName[MAXPLAYERS+1][128]; @@ -110,16 +112,6 @@ public Plugin myinfo = url = "https://github.com/shavitush/bhoptimer" } -public void OnAllPluginsLoaded() -{ - gB_RTLer = LibraryExists("rtler"); - - if(gH_SQL == null) - { - Shavit_OnDatabaseLoaded(); - } -} - public void OnPluginStart() { gEV_Type = GetEngineVersion(); @@ -161,8 +153,10 @@ public void OnPluginStart() } } } - - SQL_SetPrefix(); + + gB_RTLer = LibraryExists("rtler"); + + SQL_DBConnect(); } public void OnMapStart() @@ -514,61 +508,6 @@ void Frame_SendText(DataPack pack) EndMessage(); } -public void Shavit_OnDatabaseLoaded() -{ - gH_SQL = Shavit_GetDatabase(); - SetSQLInfo(); -} - -public Action CheckForSQLInfo(Handle Timer) -{ - return SetSQLInfo(); -} - -Action SetSQLInfo() -{ - if(gH_SQL == null) - { - gH_SQL = Shavit_GetDatabase(); - - CreateTimer(0.5, CheckForSQLInfo); - } - - else - { - SQL_DBConnect(); - - return Plugin_Stop; - } - - return Plugin_Continue; -} - -void SQL_SetPrefix() -{ - char sFile[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); - - File fFile = OpenFile(sFile, "r"); - - if(fFile == null) - { - SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); - } - - char sLine[PLATFORM_MAX_PATH*2]; - - while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2)) - { - TrimString(sLine); - strcopy(gS_MySQLPrefix, 32, sLine); - - break; - } - - delete fFile; -} - public void OnLibraryAdded(const char[] name) { if(StrEqual(name, "rtler")) @@ -631,10 +570,7 @@ public void OnClientDisconnect(int client) public void OnClientPostAdminCheck(int client) { - if(gH_SQL != null) - { - LoadFromDatabase(client); - } + LoadFromDatabase(client); } public Action Command_CCHelp(int client, int args) @@ -707,6 +643,11 @@ public Action Command_CCName(int client, int args) Shavit_PrintToChat(client, "%T", "ChatUpdated", client); + if(!StrEqual(gS_CustomName[client], sArgs)) + { + gB_ChangedSinceLogin[client] = true; + } + gB_NameEnabled[client] = true; strcopy(gS_CustomName[client], 128, sArgs); @@ -752,6 +693,11 @@ public Action Command_CCMessage(int client, int args) Shavit_PrintToChat(client, "%T", "ChatUpdated", client); + if(!StrEqual(gS_CustomMessage[client], sArgs)) + { + gB_ChangedSinceLogin[client] = true; + } + gB_MessageEnabled[client] = true; strcopy(gS_CustomMessage[client], 16, sArgs); @@ -1321,30 +1267,26 @@ int RealRandomInt(int min, int max) void SQL_DBConnect() { - if(gH_SQL != null) - { - char sDriver[8]; - gH_SQL.Driver.GetIdentifier(sDriver, 8); - bool bMySQL = StrEqual(sDriver, "mysql", false); + GetTimerSQLPrefix(gS_MySQLPrefix, 32); + gH_SQL = GetTimerDatabaseHandle(); - char sQuery[512]; + char sQuery[512]; - if(bMySQL) - { - FormatEx(sQuery, 512, - "CREATE TABLE IF NOT EXISTS `%schat` (`auth` VARCHAR(32) NOT NULL, `name` INT NOT NULL DEFAULT 0, `ccname` VARCHAR(128) COLLATE 'utf8mb4_unicode_ci', `message` INT NOT NULL DEFAULT 0, `ccmessage` VARCHAR(16) COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`auth`), CONSTRAINT `ch_auth` FOREIGN KEY (`auth`) REFERENCES `users` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE) ENGINE=INNODB;", - gS_MySQLPrefix); - } + if(IsMySQLDatabase(gH_SQL)) + { + FormatEx(sQuery, 512, + "CREATE TABLE IF NOT EXISTS `%schat` (`auth` INT NOT NULL, `name` INT NOT NULL DEFAULT 0, `ccname` VARCHAR(128) COLLATE 'utf8mb4_unicode_ci', `message` INT NOT NULL DEFAULT 0, `ccmessage` VARCHAR(16) COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`auth`), CONSTRAINT `%sch_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE) ENGINE=INNODB;", + gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix); + } - else - { - FormatEx(sQuery, 512, - "CREATE TABLE IF NOT EXISTS `%schat` (`auth` VARCHAR(32) NOT NULL, `name` INT NOT NULL DEFAULT 0, `ccname` VARCHAR(128), `message` INT NOT NULL DEFAULT 0, `ccmessage` VARCHAR(16), PRIMARY KEY (`auth`), CONSTRAINT `ch_auth` FOREIGN KEY (`auth`) REFERENCES `users` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE);", - gS_MySQLPrefix); - } - - gH_SQL.Query(SQL_CreateTable_Callback, sQuery); + else + { + FormatEx(sQuery, 512, + "CREATE TABLE IF NOT EXISTS `%schat` (`auth` INT NOT NULL, `name` INT NOT NULL DEFAULT 0, `ccname` VARCHAR(128), `message` INT NOT NULL DEFAULT 0, `ccmessage` VARCHAR(16), PRIMARY KEY (`auth`), CONSTRAINT `%sch_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE);", + gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix); } + + gH_SQL.Query(SQL_CreateTable_Callback, sQuery); } public void SQL_CreateTable_Callback(Database db, DBResultSet results, const char[] error, any data) @@ -1372,9 +1314,14 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha void SaveToDatabase(int client) { - char sAuthID3[32]; + if(!gB_ChangedSinceLogin[client]) + { + return; + } - if(!GetClientAuthId(client, AuthId_Steam3, sAuthID3, 32)) + int iSteamID = GetSteamAccountID(client); + + if(iSteamID == 0) { return; } @@ -1389,8 +1336,8 @@ void SaveToDatabase(int client) char sQuery[512]; FormatEx(sQuery, 512, - "REPLACE INTO %schat (auth, name, ccname, message, ccmessage) VALUES ('%s', %d, '%s', %d, '%s');", - gS_MySQLPrefix, sAuthID3, gB_NameEnabled[client], sEscapedName, gB_MessageEnabled[client], sEscapedMessage); + "REPLACE INTO %schat (auth, name, ccname, message, ccmessage) VALUES (%d, %d, '%s', %d, '%s');", + gS_MySQLPrefix, iSteamID, gB_NameEnabled[client], sEscapedName, gB_MessageEnabled[client], sEscapedMessage); gH_SQL.Query(SQL_UpdateUser_Callback, sQuery, 0, DBPrio_Low); } @@ -1412,15 +1359,15 @@ void LoadFromDatabase(int client) return; } - char sAuthID3[32]; + int iSteamID = GetSteamAccountID(client); - if(!GetClientAuthId(client, AuthId_Steam3, sAuthID3, 32)) + if(iSteamID == 0) { return; } char sQuery[256]; - FormatEx(sQuery, 256, "SELECT name, ccname, message, ccmessage FROM %schat WHERE auth = '%s';", gS_MySQLPrefix, sAuthID3); + FormatEx(sQuery, 256, "SELECT name, ccname, message, ccmessage FROM %schat WHERE auth = %d;", gS_MySQLPrefix, iSteamID); gH_SQL.Query(SQL_GetChat_Callback, sQuery, GetClientSerial(client), DBPrio_Low); } @@ -1441,6 +1388,8 @@ public void SQL_GetChat_Callback(Database db, DBResultSet results, const char[] return; } + gB_ChangedSinceLogin[client] = false; + while(results.FetchRow()) { gB_NameEnabled[client] = view_as(results.FetchInt(0)); diff --git a/addons/sourcemod/scripting/shavit-core.sp b/addons/sourcemod/scripting/shavit-core.sp index bbc73d0bb..426dd6609 100644 --- a/addons/sourcemod/scripting/shavit-core.sp +++ b/addons/sourcemod/scripting/shavit-core.sp @@ -55,6 +55,7 @@ enum struct playertimer_t int iPerfectJumps; int iGroundTicks; MoveType iMoveType; + bool bCanUseAllKeys; } // game type (CS:S/CS:GO/TF2) @@ -116,6 +117,7 @@ ConVar gCV_NoZAxisSpeed = null; ConVar gCV_VelocityTeleport = null; ConVar gCV_DefaultStyle = null; ConVar gCV_NoChatSound = null; +ConVar gCV_SimplerLadders = null; // cached cvars int gI_DefaultStyle = 0; @@ -147,6 +149,8 @@ char gS_DeleteMap[MAXPLAYERS+1][160]; char gS_WipePlayerID[MAXPLAYERS+1][32]; char gS_Verification[MAXPLAYERS+1][16]; bool gB_CookiesRetrieved[MAXPLAYERS+1]; +float gF_ZoneAiraccelerate[MAXPLAYERS+1]; +float gF_ZoneSpeedLimit[MAXPLAYERS+1]; // flags int gI_StyleFlag[STYLE_LIMIT]; @@ -249,10 +253,6 @@ public void OnPluginStart() SetFailState("This plugin was meant to be used in CS:S, CS:GO and TF2 *only*."); } - // database connections - SQL_SetPrefix(); - SQL_DBConnect(); - // hooks gB_HookedJump = HookEventEx("player_jump", Player_Jump); HookEvent("player_death", Player_Death); @@ -324,6 +324,7 @@ public void OnPluginStart() gCV_VelocityTeleport = CreateConVar("shavit_core_velocityteleport", "0", "Teleport the client when changing its velocity? (for special styles)", 0, true, 0.0, true, 1.0); gCV_DefaultStyle = CreateConVar("shavit_core_defaultstyle", "0", "Default style ID.\nAdd the '!' prefix to disable style cookies - i.e. \"!3\" to *force* scroll to be the default style.", 0, true, 0.0); gCV_NoChatSound = CreateConVar("shavit_core_nochatsound", "0", "Disables click sound for chat messages.", 0, true, 0.0, true, 1.0); + gCV_SimplerLadders = CreateConVar("shavit_core_simplerladders", "1", "Allows using all keys on limited styles (such as sideways) after touching ladders\nTouching the ground enables the restriction again.", 0, true, 0.0, true, 1.0); gCV_DefaultStyle.AddChangeHook(OnConVarChanged); @@ -356,6 +357,9 @@ public void OnPluginStart() gB_Replay = LibraryExists("shavit-replay"); gB_Rankings = LibraryExists("shavit-rankings"); gB_HUD = LibraryExists("shavit-hud"); + + // database connections + SQL_DBConnect(); } public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) @@ -711,16 +715,23 @@ public Action Command_WipePlayer(int client, int args) void DeleteUserData(int client, const char[] sAuthID3) { + char sAuthID[32]; + strcopy(sAuthID, 32, sAuthID3); + ReplaceString(sAuthID, 32, "[U:1:", ""); + ReplaceString(sAuthID, 32, "]", ""); + + int iSteamID = StringToInt(sAuthID); + if(gB_Replay) { char sQueryGetWorldRecords[256]; FormatEx(sQueryGetWorldRecords, 256, - "SELECT map, id, style, track FROM %splayertimes WHERE auth = '%s';", - gS_MySQLPrefix, sAuthID3); + "SELECT map, id, style, track FROM %splayertimes WHERE auth = %d;", + gS_MySQLPrefix, iSteamID); DataPack hPack = new DataPack(); hPack.WriteCell(client); - hPack.WriteString(sAuthID3); + hPack.WriteCell(iSteamID); gH_SQL.Query(SQL_DeleteUserData_GetRecords_Callback, sQueryGetWorldRecords, hPack, DBPrio_High); } @@ -729,14 +740,14 @@ void DeleteUserData(int client, const char[] sAuthID3) { char sQueryDeleteUserTimes[256]; FormatEx(sQueryDeleteUserTimes, 256, - "DELETE FROM %splayertimes WHERE auth = '%s';", - gS_MySQLPrefix, sAuthID3); + "DELETE FROM %splayertimes WHERE auth = %d;", + gS_MySQLPrefix, iSteamID); - DataPack steamPack = new DataPack(); - steamPack.WriteString(sAuthID3); - steamPack.WriteCell(client); + DataPack hSteamPack = new DataPack(); + hSteamPack.WriteCell(iSteamID); + hSteamPack.WriteCell(client); - gH_SQL.Query(SQL_DeleteUserTimes_Callback, sQueryDeleteUserTimes, steamPack, DBPrio_High); + gH_SQL.Query(SQL_DeleteUserTimes_Callback, sQueryDeleteUserTimes, hSteamPack, DBPrio_High); } } @@ -745,9 +756,7 @@ public void SQL_DeleteUserData_GetRecords_Callback(Database db, DBResultSet resu DataPack hPack = view_as(data); hPack.Reset(); int client = hPack.ReadCell(); - - char sAuthID3[32]; - hPack.ReadString(sAuthID3, 32); + int iSteamID = hPack.ReadCell(); delete hPack; if(results == null) @@ -782,19 +791,18 @@ public void SQL_DeleteUserData_GetRecords_Callback(Database db, DBResultSet resu hTransaction.AddQuery(sQueryGetWorldRecordID, hTransPack); } - DataPack steamPack = new DataPack(); - steamPack.WriteString(sAuthID3); - steamPack.WriteCell(client); + DataPack hSteamPack = new DataPack(); + hSteamPack.WriteCell(iSteamID); + hSteamPack.WriteCell(client); - gH_SQL.Execute(hTransaction, Trans_OnRecordCompare, INVALID_FUNCTION, steamPack, DBPrio_High); + gH_SQL.Execute(hTransaction, Trans_OnRecordCompare, INVALID_FUNCTION, hSteamPack, DBPrio_High); } public void Trans_OnRecordCompare(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData) { - DataPack pack = view_as(data); - pack.Reset(); - char sAuthID3[32]; - pack.ReadString(sAuthID3, 32); + DataPack hPack = view_as(data); + hPack.Reset(); + int iSteamID = hPack.ReadCell(); for(int i = 0; i < numQueries; i++) { @@ -821,55 +829,53 @@ public void Trans_OnRecordCompare(Database db, any data, int numQueries, DBResul char sQueryDeleteUserTimes[256]; FormatEx(sQueryDeleteUserTimes, 256, - "DELETE FROM %splayertimes WHERE auth = '%s';", - gS_MySQLPrefix, sAuthID3); + "DELETE FROM %splayertimes WHERE auth = %d;", + gS_MySQLPrefix, iSteamID); - gH_SQL.Query(SQL_DeleteUserTimes_Callback, sQueryDeleteUserTimes, pack, DBPrio_High); + gH_SQL.Query(SQL_DeleteUserTimes_Callback, sQueryDeleteUserTimes, hPack, DBPrio_High); } public void SQL_DeleteUserTimes_Callback(Database db, DBResultSet results, const char[] error, any data) { - DataPack pack = view_as(data); - pack.Reset(); - char sAuthID3[32]; - pack.ReadString(sAuthID3, 32); + DataPack hPack = view_as(data); + hPack.Reset(); + int iSteamID = hPack.ReadCell(); if(results == null) { LogError("Timer error! Failed to wipe user data (wipe | delete user times). Reason: %s", error); - delete pack; + delete hPack; return; } char sQueryDeleteUsers[256]; - FormatEx(sQueryDeleteUsers, 256, "DELETE FROM %susers WHERE auth = '%s';", - gS_MySQLPrefix, sAuthID3); + FormatEx(sQueryDeleteUsers, 256, "DELETE FROM %susers WHERE auth = %d;", + gS_MySQLPrefix, iSteamID); - gH_SQL.Query(SQL_DeleteUserData_Callback, sQueryDeleteUsers, pack, DBPrio_High); + gH_SQL.Query(SQL_DeleteUserData_Callback, sQueryDeleteUsers, hPack, DBPrio_High); } public void SQL_DeleteUserData_Callback(Database db, DBResultSet results, const char[] error, any data) { - DataPack pack = view_as(data); - pack.Reset(); - char sAuthID3[32]; - pack.ReadString(sAuthID3, 32); - int client = pack.ReadCell(); - delete pack; + DataPack hPack = view_as(data); + hPack.Reset(); + int iSteamID = hPack.ReadCell(); + int client = hPack.ReadCell(); + delete hPack; if(results == null) { - LogError("Timer error! Failed to wipe user data (wipe | delete user data, id %s). Reason: %s", error, sAuthID3); + LogError("Timer error! Failed to wipe user data (wipe | delete user data, id [U:1:%d]). Reason: %s", error, iSteamID); return; } Shavit_ReloadLeaderboards(); - Shavit_PrintToChat(client, "Finished wiping timer data for user %s%s%s.", - gS_ChatStrings.sVariable, sAuthID3, gS_ChatStrings.sText); + Shavit_PrintToChat(client, "Finished wiping timer data for user %s[U:1:%d]%s.", + gS_ChatStrings.sVariable, iSteamID, gS_ChatStrings.sText); } public Action Command_AutoBhop(int client, int args) @@ -1708,7 +1714,9 @@ void StartTimer(int client, int track) float fSpeed[3]; GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed); - if(!gCV_NoZAxisSpeed.BoolValue || gA_StyleSettings[gA_Timers[client].iStyle].bPrespeed || (fSpeed[2] == 0.0 && SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)) <= 290.0)) + if(!gCV_NoZAxisSpeed.BoolValue || + gA_StyleSettings[gA_Timers[client].iStyle].iPrespeed == 1 || + (fSpeed[2] == 0.0 && (gA_StyleSettings[gA_Timers[client].iStyle].iPrespeed == 2 || SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)) <= 290.0))) { Action result = Plugin_Continue; Call_StartForward(gH_Forwards_Start); @@ -1730,6 +1738,7 @@ void StartTimer(int client, int track) gA_Timers[client].bPracticeMode = false; gA_Timers[client].iMeasuredJumps = 0; gA_Timers[client].iPerfectJumps = 0; + gA_Timers[client].bCanUseAllKeys = false; SetEntityGravity(client, view_as(gA_StyleSettings[gA_Timers[client].iStyle].fGravityMultiplier)); SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", view_as(gA_StyleSettings[gA_Timers[client].iStyle].fSpeedMultiplier)); @@ -1861,16 +1870,11 @@ public void OnClientPutInServer(int client) CallOnStyleChanged(client, 0, gI_DefaultStyle, false); } - if(gH_SQL == null) - { - return; - } - SDKHook(client, SDKHook_PreThinkPost, PreThinkPost); - char sAuthID3[32]; + int iSteamID = GetSteamAccountID(client); - if(!GetClientAuthId(client, AuthId_Steam3, sAuthID3, 32)) + if(iSteamID == 0) { KickClient(client, "%T", "VerificationFailed", client); @@ -1885,15 +1889,9 @@ public void OnClientPutInServer(int client) char[] sEscapedName = new char[iLength]; gH_SQL.Escape(sName, sEscapedName, iLength); - char sIP[64]; - GetClientIP(client, sIP, 64); - - char sCountry[128]; - - if(!GeoipCountry(sIP, sCountry, 128)) - { - strcopy(sCountry, 128, "Local Area Network"); - } + char sIPAddress[64]; + GetClientIP(client, sIPAddress, 64); + int iIPAddress = IPStringToAddress(sIPAddress); int iTime = GetTime(); @@ -1901,12 +1899,16 @@ public void OnClientPutInServer(int client) if(gB_MySQL) { - FormatEx(sQuery, 512, "INSERT INTO %susers (auth, name, country, ip, lastlogin) VALUES ('%s', '%s', '%s', '%s', %d) ON DUPLICATE KEY UPDATE name = '%s', country = '%s', ip = '%s', lastlogin = %d;", gS_MySQLPrefix, sAuthID3, sEscapedName, sCountry, sIP, iTime, sEscapedName, sCountry, sIP, iTime); + FormatEx(sQuery, 512, + "INSERT INTO %susers (auth, name, ip, lastlogin) VALUES (%d, '%s', %d, %d) ON DUPLICATE KEY UPDATE name = '%s', ip = %d, lastlogin = %d;", + gS_MySQLPrefix, iSteamID, sEscapedName, iIPAddress, iTime, sEscapedName, iIPAddress, iTime); } else { - FormatEx(sQuery, 512, "REPLACE INTO %susers (auth, name, country, ip, lastlogin) VALUES ('%s', '%s', '%s', '%s', %d);", gS_MySQLPrefix, sAuthID3, sEscapedName, sCountry, sIP, iTime); + FormatEx(sQuery, 512, + "REPLACE INTO %susers (auth, name, ip, lastlogin) VALUES (%d, '%s', %d, %d);", + gS_MySQLPrefix, iSteamID, sEscapedName, iIPAddress, iTime); } gH_SQL.Query(SQL_InsertUser_Callback, sQuery, GetClientSerial(client)); @@ -1960,7 +1962,7 @@ bool LoadStyles() gA_StyleSettings[i].bAutobhop = view_as(kv.GetNum("autobhop", 1)); gA_StyleSettings[i].bEasybhop = view_as(kv.GetNum("easybhop", 1)); - gA_StyleSettings[i].bPrespeed = view_as(kv.GetNum("prespeed", 0)); + gA_StyleSettings[i].iPrespeed = view_as(kv.GetNum("prespeed", 0)); gA_StyleSettings[i].fVelocityLimit = kv.GetFloat("velocity_limit", 0.0); gA_StyleSettings[i].fAiraccelerate = kv.GetFloat("airaccelerate", 1000.0); gA_StyleSettings[i].bEnableBunnyhopping = view_as(kv.GetNum("bunnyhopping", 1)); @@ -2151,123 +2153,303 @@ bool LoadMessages() return true; } -void SQL_SetPrefix() +void SQL_DBConnect() { - char sFile[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); + GetTimerSQLPrefix(gS_MySQLPrefix, 32); + gH_SQL = GetTimerDatabaseHandle(); + gB_MySQL = IsMySQLDatabase(gH_SQL); - File fFile = OpenFile(sFile, "r"); + // support unicode names + if(!gH_SQL.SetCharset("utf8mb4")) + { + gH_SQL.SetCharset("utf8"); + } - if(fFile == null) + // migrations will only exist for mysql. sorry sqlite users + if(gB_MySQL) { - SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); + char sQuery[128]; + FormatEx(sQuery, 128, "CREATE TABLE IF NOT EXISTS `%smigrations` (`code` TINYINT NOT NULL, UNIQUE INDEX `code` (`code`));", gS_MySQLPrefix); + + gH_SQL.Query(SQL_CreateMigrationsTable_Callback, sQuery, 0, DBPrio_High); } - - char sLine[PLATFORM_MAX_PATH*2]; - while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2)) + CreateUsersTable(); +} + +public void SQL_CreateMigrationsTable_Callback(Database db, DBResultSet results, const char[] error, any data) +{ + if(results == null) { - TrimString(sLine); - strcopy(gS_MySQLPrefix, 32, sLine); + LogError("Timer error! Migrations table creation failed. Reason: %s", error); - break; + return; } - delete fFile; + char sQuery[128]; + FormatEx(sQuery, 128, "SELECT code FROM %smigrations;", gS_MySQLPrefix); + + gH_SQL.Query(SQL_SelectMigrations_Callback, sQuery, 0, DBPrio_High); } -void SQL_DBConnect() +public void SQL_SelectMigrations_Callback(Database db, DBResultSet results, const char[] error, any data) { - if(gH_SQL != null) + if(results == null) { - delete gH_SQL; + LogError("Timer error! Migrations selection failed. Reason: %s", error); + + return; } - char sError[255]; + // this is ugly, i know. but it works and is more elegant than previous solutions so.. let it be =) + bool bMigrationApplied[255] = { false, ... }; - if(SQL_CheckConfig("shavit")) // can't be asynced as we have modules that require this database connection instantly + while(results.FetchRow()) { - gH_SQL = SQL_Connect("shavit", true, sError, 255); + bMigrationApplied[results.FetchInt(0)] = true; + } - if(gH_SQL == null) + for(int i = 0; i < MIGRATIONS_END; i++) + { + if(!bMigrationApplied[i]) { - SetFailState("Timer startup failed. Reason: %s", sError); + PrintToServer("--- Applying database migration %d ---", i); + ApplyMigration(i); } } +} - else +void ApplyMigration(int migration) +{ + switch(migration) { - gH_SQL = SQLite_UseDatabase("shavit", sError, 255); + case Migration_RemoveWorkshopMaptiers, Migration_RemoveWorkshopMapzones, Migration_RemoveWorkshopPlayertimes: ApplyMigration_RemoveWorkshopPath(migration); + case Migration_LastLoginIndex: ApplyMigration_LastLoginIndex(); + case Migration_RemoveCountry: ApplyMigration_RemoveCountry(); + case Migration_ConvertIPAddresses: ApplyMigration_ConvertIPAddresses(); + case Migration_ConvertSteamIDsUsers: ApplyMigration_ConvertSteamIDs(); + case Migration_ConvertSteamIDsPlayertimes, Migration_ConvertSteamIDsChat: return; // this is confusing, but the above case handles all of them + case Migration_PlayertimesDateToInt: ApplyMigration_PlayertimesDateToInt(); + case Migration_AddZonesFlagsAndData: ApplyMigration_AddZonesFlagsAndData(); + case Migration_AddPlayertimesCompletions: ApplyMigration_AddPlayertimesCompletions(); } +} - // support unicode names - if(!gH_SQL.SetCharset("utf8mb4")) +void ApplyMigration_LastLoginIndex() +{ + char sQuery[128]; + FormatEx(sQuery, 128, "ALTER TABLE `%susers` ADD INDEX `lastlogin` (`lastlogin`);", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_LastLoginIndex, DBPrio_High); +} + +void ApplyMigration_RemoveCountry() +{ + char sQuery[128]; + FormatEx(sQuery, 128, "ALTER TABLE `%susers` DROP COLUMN `country`;", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_RemoveCountry, DBPrio_High); +} + +void ApplyMigration_PlayertimesDateToInt() +{ + char sQuery[128]; + FormatEx(sQuery, 128, "ALTER TABLE `%splayertimes` CHANGE COLUMN `date` `date` INT;", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_PlayertimesDateToInt, DBPrio_High); +} + +void ApplyMigration_AddZonesFlagsAndData() +{ + char sQuery[192]; + FormatEx(sQuery, 192, "ALTER TABLE `%smapzones` ADD COLUMN `flags` INT NULL AFTER `track`, ADD COLUMN `data` INT NULL AFTER `flags`;", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddZonesFlagsAndData, DBPrio_High); +} + +void ApplyMigration_AddPlayertimesCompletions() +{ + char sQuery[192]; + FormatEx(sQuery, 192, "ALTER TABLE `%splayertimes` ADD COLUMN `completions` SMALLINT DEFAULT 1 AFTER `perfs`;", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddPlayertimesCompletions, DBPrio_High); +} + +public void SQL_TableMigrationSingleQuery_Callback(Database db, DBResultSet results, const char[] error, any data) +{ + InsertMigration(data); + + // i hate hardcoding REEEEEEEE + if(data == Migration_ConvertSteamIDsChat) { - gH_SQL.SetCharset("utf8"); + char sQuery[256]; + // deleting rows that cause data integrity issues + FormatEx(sQuery, 256, + "DELETE t1 FROM %splayertimes t1 LEFT JOIN %susers t2 ON t1.auth = t2.auth WHERE t2.auth IS NULL;", + gS_MySQLPrefix, gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIndexing_Callback, sQuery, 0, DBPrio_High); + + FormatEx(sQuery, 256, + "ALTER TABLE `%splayertimes` ADD CONSTRAINT `pt_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE;", + gS_MySQLPrefix, gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIndexing_Callback, sQuery); + + FormatEx(sQuery, 256, + "DELETE t1 FROM %schat t1 LEFT JOIN %susers t2 ON t1.auth = t2.auth WHERE t2.auth IS NULL;", + gS_MySQLPrefix, gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIndexing_Callback, sQuery, 0, DBPrio_High); + + FormatEx(sQuery, 256, + "ALTER TABLE `%schat` ADD CONSTRAINT `ch_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE;", + gS_MySQLPrefix, gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIndexing_Callback, sQuery); } +} - char sDriver[8]; - gH_SQL.Driver.GetIdentifier(sDriver, 8); - gB_MySQL = StrEqual(sDriver, "mysql", false); +void ApplyMigration_ConvertIPAddresses(bool index = true) +{ + char sQuery[128]; - char sQuery[512]; + if(index) + { + FormatEx(sQuery, 128, "ALTER TABLE `%susers` ADD INDEX `ip` (`ip`);", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIndexing_Callback, sQuery, 0, DBPrio_High); + } - if(gB_MySQL) + FormatEx(sQuery, 128, "SELECT DISTINCT ip FROM %susers WHERE ip LIKE '%%.%%';", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIPAddresses_Callback, sQuery); +} + +public void SQL_TableMigrationIPAddresses_Callback(Database db, DBResultSet results, const char[] error, DataPack data) +{ + if(results == null || results.RowCount == 0) { - FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%susers` (`auth` VARCHAR(32) NOT NULL, `name` VARCHAR(32) COLLATE 'utf8mb4_general_ci', `country` VARCHAR(32), `ip` VARCHAR(64), `lastlogin` INT NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0, PRIMARY KEY (`auth`), INDEX `points` (`points`)) ENGINE=INNODB;", gS_MySQLPrefix); + InsertMigration(Migration_ConvertIPAddresses); + + return; } - else + Transaction hTransaction = new Transaction(); + int iQueries = 0; + + while(results.FetchRow()) { - FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%susers` (`auth` VARCHAR(32) NOT NULL PRIMARY KEY, `name` VARCHAR(32), `country` VARCHAR(32), `ip` VARCHAR(64), `lastlogin` INTEGER NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0);", gS_MySQLPrefix); + char sIPAddress[32]; + results.FetchString(0, sIPAddress, 32); + + char sQuery[256]; + FormatEx(sQuery, 256, "UPDATE %susers SET ip = %d WHERE ip = '%s';", gS_MySQLPrefix, IPStringToAddress(sIPAddress), sIPAddress); + + hTransaction.AddQuery(sQuery); + + if(++iQueries >= 10000) + { + break; + } } - gH_SQL.Query(SQL_CreateTable_Callback, sQuery, 0, DBPrio_High); + gH_SQL.Execute(hTransaction, Trans_IPAddressMigrationSuccess, Trans_IPAddressMigrationFailed, iQueries); } -public void SQL_CreateTable_Callback(Database db, DBResultSet results, const char[] error, any data) +public void Trans_IPAddressMigrationSuccess(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData) { - if(results == null) + // too many queries, don't do all at once to avoid server crash due to too many queries in the transaction + if(data >= 10000) { - LogError("Timer error! Users' data table creation failed. Reason: %s", error); + ApplyMigration_ConvertIPAddresses(false); return; } + char sQuery[128]; + FormatEx(sQuery, 128, "ALTER TABLE `%susers` DROP INDEX `ip`;", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIndexing_Callback, sQuery, 0, DBPrio_High); + + FormatEx(sQuery, 128, "ALTER TABLE `%susers` CHANGE COLUMN `ip` `ip` INT;", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_ConvertIPAddresses, DBPrio_High); +} + +public void Trans_IPAddressMigrationFailed(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) +{ + LogError("Timer (core) error! IP address migration failed. Reason: %s", error); +} + +void ApplyMigration_ConvertSteamIDs() +{ char sTables[][] = { - "maptiers", - "mapzones", - "playertimes" + "users", + "playertimes", + "chat" }; + char sQuery[128]; + FormatEx(sQuery, 128, "ALTER TABLE `%splayertimes` DROP CONSTRAINT `pt_auth`;", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIndexing_Callback, sQuery, 0, DBPrio_High); + + FormatEx(sQuery, 128, "ALTER TABLE `%schat` DROP CONSTRAINT `ch_auth`;", gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationIndexing_Callback, sQuery, 0, DBPrio_High); + for(int i = 0; i < sizeof(sTables); i++) { - DataPack dp = new DataPack(); - dp.WriteString(sTables[i]); + DataPack hPack = new DataPack(); + hPack.WriteCell(Migration_ConvertSteamIDsUsers + i); + hPack.WriteString(sTables[i]); - char sQuery[192]; - FormatEx(sQuery, 192, "SELECT map FROM %s%s WHERE map LIKE 'workshop%%' GROUP BY map;", gS_MySQLPrefix, sTables[i]); - gH_SQL.Query(SQL_TableMigration_Callback, sQuery, dp, DBPrio_High); + FormatEx(sQuery, 128, "UPDATE %s%s SET auth = REPLACE(REPLACE(auth, \"[U:1:\", \"\"), \"]\", \"\") WHERE auth LIKE '[%%';", sTables[i], gS_MySQLPrefix); + gH_SQL.Query(SQL_TableMigrationSteamIDs_Callback, sQuery, hPack, DBPrio_High); } +} - Call_StartForward(gH_Forwards_OnDatabaseLoaded); - Call_Finish(); +public void SQL_TableMigrationIndexing_Callback(Database db, DBResultSet results, const char[] error, DataPack data) +{ + // nothing } -public void SQL_TableMigration_Callback(Database db, DBResultSet results, const char[] error, DataPack data) +public void SQL_TableMigrationSteamIDs_Callback(Database db, DBResultSet results, const char[] error, DataPack data) { + data.Reset(); + int iMigration = data.ReadCell(); char sTable[16]; + data.ReadString(sTable, 16); + delete data; + + char sQuery[128]; + FormatEx(sQuery, 128, "ALTER TABLE `%s%s` CHANGE COLUMN `auth` `auth` INT;", gS_MySQLPrefix, sTable); + gH_SQL.Query(SQL_TableMigrationSingleQuery_Callback, sQuery, iMigration, DBPrio_High); +} + +void ApplyMigration_RemoveWorkshopPath(int migration) +{ + char sTables[][] = + { + "maptiers", + "mapzones", + "playertimes" + }; + + DataPack hPack = new DataPack(); + hPack.WriteCell(migration); + hPack.WriteString(sTables[migration]); + + char sQuery[192]; + FormatEx(sQuery, 192, "SELECT map FROM %s%s WHERE map LIKE 'workshop%%' GROUP BY map;", gS_MySQLPrefix, sTables[migration]); + gH_SQL.Query(SQL_TableMigrationWorkshop_Callback, sQuery, hPack, DBPrio_High); +} + +public void SQL_TableMigrationWorkshop_Callback(Database db, DBResultSet results, const char[] error, DataPack data) +{ data.Reset(); + int iMigration = data.ReadCell(); + char sTable[16]; data.ReadString(sTable, 16); delete data; if(results == null || results.RowCount == 0) { // no error logging here because not everyone runs the rankings/wr modules + InsertMigration(iMigration); + return; } + Transaction hTransaction = new Transaction(); + while(results.FetchRow()) { char sMap[160]; @@ -2278,25 +2460,100 @@ public void SQL_TableMigration_Callback(Database db, DBResultSet results, const char sQuery[256]; FormatEx(sQuery, 256, "UPDATE %s%s SET map = '%s' WHERE map = '%s';", gS_MySQLPrefix, sTable, sDisplayMap, sMap); - gH_SQL.Query(SQL_AlterTable3_Callback, sQuery, 0, DBPrio_High); + + hTransaction.AddQuery(sQuery); } + + gH_SQL.Execute(hTransaction, Trans_WorkshopMigration, INVALID_FUNCTION, iMigration); +} + +public void Trans_WorkshopMigration(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData) +{ + InsertMigration(data); +} + +void InsertMigration(int migration) +{ + char sQuery[128]; + FormatEx(sQuery, 128, "INSERT INTO %smigrations (code) VALUES (%d);", gS_MySQLPrefix, migration); + gH_SQL.Query(SQL_MigrationApplied_Callback, sQuery, migration); +} + +public void SQL_MigrationApplied_Callback(Database db, DBResultSet results, const char[] error, any data) +{ + // nothing } -public void SQL_AlterTable3_Callback(Database db, DBResultSet results, const char[] error, any data) +void CreateUsersTable() +{ + char sQuery[512]; + + if(gB_MySQL) + { + FormatEx(sQuery, 512, + "CREATE TABLE IF NOT EXISTS `%susers` (`auth` INT NOT NULL, `name` VARCHAR(32) COLLATE 'utf8mb4_general_ci', `ip` INT, `lastlogin` INT NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0, PRIMARY KEY (`auth`), INDEX `points` (`points`), INDEX `lastlogin` (`lastlogin`)) ENGINE=INNODB;", + gS_MySQLPrefix); + } + + else + { + FormatEx(sQuery, 512, + "CREATE TABLE IF NOT EXISTS `%susers` (`auth` INT NOT NULL PRIMARY KEY, `name` VARCHAR(32), `ip` INT, `lastlogin` INTEGER NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0);", + gS_MySQLPrefix); + } + + gH_SQL.Query(SQL_CreateUsersTable_Callback, sQuery, 0, DBPrio_High); +} + +public void SQL_CreateUsersTable_Callback(Database db, DBResultSet results, const char[] error, any data) { if(results == null) { - LogError("Timer error! Table alteration 3 (core) failed. Reason: %s", error); + LogError("Timer error! Users' data table creation failed. Reason: %s", error); return; } + + Call_StartForward(gH_Forwards_OnDatabaseLoaded); + Call_Finish(); +} + +public void Shavit_OnEnterZone(int client, int type, int track, int id, int entity) +{ + if(type == Zone_Airaccelerate) + { + gF_ZoneAiraccelerate[client] = float(Shavit_GetZoneData(id)); + + UpdateAiraccelerate(client, gF_ZoneAiraccelerate[client]); + } + + else if(type == Zone_CustomSpeedLimit) + { + gF_ZoneSpeedLimit[client] = float(Shavit_GetZoneData(id)); + } +} + +public void Shavit_OnLeaveZone(int client, int type, int track, int id, int entity) +{ + if(type == Zone_Airaccelerate) + { + UpdateAiraccelerate(client, view_as(gA_StyleSettings[gA_Timers[client].iStyle].fAiraccelerate)); + } } public void PreThinkPost(int client) { if(IsPlayerAlive(client)) { - sv_airaccelerate.FloatValue = view_as(gA_StyleSettings[gA_Timers[client].iStyle].fAiraccelerate); + if(!gB_Zones || !Shavit_InsideZone(client, Zone_Airaccelerate, -1)) + { + sv_airaccelerate.FloatValue = view_as(gA_StyleSettings[gA_Timers[client].iStyle].fAiraccelerate); + } + + else + { + sv_airaccelerate.FloatValue = gF_ZoneAiraccelerate[client]; + } if(sv_enablebunnyhopping != null) { @@ -2481,8 +2738,18 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 MoveType mtMoveType = GetEntityMoveType(client); + if(mtMoveType == MOVETYPE_LADDER && gCV_SimplerLadders.BoolValue) + { + gA_Timers[client].bCanUseAllKeys = true; + } + + else if(iGroundEntity != -1) + { + gA_Timers[client].bCanUseAllKeys = false; + } + // key blocking - if(mtMoveType != MOVETYPE_NOCLIP && mtMoveType != MOVETYPE_LADDER && !Shavit_InsideZone(client, Zone_Freestyle, -1)) + if(!gA_Timers[client].bCanUseAllKeys && mtMoveType != MOVETYPE_NOCLIP && mtMoveType != MOVETYPE_LADDER && !Shavit_InsideZone(client, Zone_Freestyle, -1)) { // block E if(gA_StyleSettings[gA_Timers[client].iStyle].bBlockUse && (buttons & IN_USE) > 0) @@ -2641,24 +2908,30 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 gA_Timers[client].iGroundTicks = 0; } - if(bInStart && gCV_BlockPreJump.BoolValue && !gA_StyleSettings[gA_Timers[client].iStyle].bPrespeed && (vel[2] > 0 || (buttons & IN_JUMP) > 0)) + if(bInStart && gCV_BlockPreJump.BoolValue && gA_StyleSettings[gA_Timers[client].iStyle].iPrespeed == 0 && (vel[2] > 0 || (buttons & IN_JUMP) > 0)) { vel[2] = 0.0; buttons &= ~IN_JUMP; } // velocity limit - if(iGroundEntity != -1 && view_as(gA_StyleSettings[gA_Timers[client].iStyle].fVelocityLimit > 0.0) && - (!gB_Zones || !Shavit_InsideZone(client, Zone_NoVelLimit, -1))) + if(iGroundEntity != -1 && view_as(gA_StyleSettings[gA_Timers[client].iStyle].fVelocityLimit > 0.0)) { + float fSpeedLimit = view_as(gA_StyleSettings[gA_Timers[client].iStyle].fVelocityLimit); + + if(gB_Zones && Shavit_InsideZone(client, Zone_CustomSpeedLimit, -1)) + { + fSpeedLimit = gF_ZoneSpeedLimit[client]; + } + float fSpeed[3]; GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed); float fSpeed_New = (SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0))); - if(fSpeed_New > 0.0) + if(fSpeedLimit != 0.0 && fSpeed_New > 0.0) { - float fScale = view_as(gA_StyleSettings[gA_Timers[client].iStyle].fVelocityLimit) / fSpeed_New; + float fScale = fSpeedLimit / fSpeed_New; if(fScale < 1.0) { @@ -2697,39 +2970,55 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 fTempAngle += 360.0; } - float fDirectionAngle = (fTempAngle - fAngles[1]); + TestAngles(client, (fTempAngle - fAngles[1]), fAngle, vel); + } + } - if(fDirectionAngle < 0.0) - { - fDirectionAngle = -fDirectionAngle; - } + gA_Timers[client].iLastButtons = iPButtons; + gA_Timers[client].fLastAngle = angles[1]; - if(fDirectionAngle < 22.5 || fDirectionAngle > 337.5) - { - gA_Timers[client].iTotalMeasures++; + return Plugin_Continue; +} - if((fAngle > 0.0 && vel[1] <= -100.0) || (fAngle < 0.0 && vel[1] >= 100.0)) - { - gA_Timers[client].iGoodGains++; - } - } +void TestAngles(int client, float dirangle, float yawdelta, float vel[3]) +{ + if(dirangle < 0.0) + { + dirangle = -dirangle; + } - else if((fDirectionAngle > 67.5 && fDirectionAngle < 112.5) || (fDirectionAngle > 247.5 && fDirectionAngle < 292.5)) - { - gA_Timers[client].iTotalMeasures++; + // normal + if(dirangle < 22.5 || dirangle > 337.5) + { + gA_Timers[client].iTotalMeasures++; - if(vel[0] <= -100.0 || vel[0] >= 100.0) - { - gA_Timers[client].iGoodGains++; - } - } + if((yawdelta > 0.0 && vel[1] <= -100.0) || (yawdelta < 0.0 && vel[1] >= 100.0)) + { + gA_Timers[client].iGoodGains++; } } - gA_Timers[client].iLastButtons = iPButtons; - gA_Timers[client].fLastAngle = angles[1]; + // hsw (thanks nairda!) + else if((dirangle > 22.5 && dirangle < 67.5)) + { + gA_Timers[client].iTotalMeasures++; - return Plugin_Continue; + if((yawdelta != 0.0) && (vel[0] >= 100.0 || vel[1] >= 100.0) && (vel[0] >= -100.0 || vel[1] >= -100.0)) + { + gA_Timers[client].iGoodGains++; + } + } + + // sw + else if((dirangle > 67.5 && dirangle < 112.5) || (dirangle > 247.5 && dirangle < 292.5)) + { + gA_Timers[client].iTotalMeasures++; + + if(vel[0] <= -100.0 || vel[0] >= 100.0) + { + gA_Timers[client].iGoodGains++; + } + } } void StopTimer_Cheat(int client, const char[] message) @@ -2738,6 +3027,13 @@ void StopTimer_Cheat(int client, const char[] message) Shavit_PrintToChat(client, "%T", "CheatTimerStop", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText, message); } +void UpdateAiraccelerate(int client, float airaccelerate) +{ + char sAiraccelerate[8]; + FloatToString(airaccelerate, sAiraccelerate, 8); + sv_airaccelerate.ReplicateToClient(client, sAiraccelerate); +} + void UpdateStyleSettings(int client) { if(sv_autobunnyhopping != null) @@ -2750,9 +3046,7 @@ void UpdateStyleSettings(int client) sv_enablebunnyhopping.ReplicateToClient(client, (gA_StyleSettings[gA_Timers[client].iStyle].bEnableBunnyhopping)? "1":"0"); } - char sAiraccelerate[8]; - FloatToString(gA_StyleSettings[gA_Timers[client].iStyle].fAiraccelerate, sAiraccelerate, 8); - sv_airaccelerate.ReplicateToClient(client, sAiraccelerate); + UpdateAiraccelerate(client, view_as(gA_StyleSettings[gA_Timers[client].iStyle].fAiraccelerate)); SetEntityGravity(client, view_as(gA_StyleSettings[gA_Timers[client].iStyle].fGravityMultiplier)); } diff --git a/addons/sourcemod/scripting/shavit-hud.sp b/addons/sourcemod/scripting/shavit-hud.sp index b7adc4e10..512bef961 100644 --- a/addons/sourcemod/scripting/shavit-hud.sp +++ b/addons/sourcemod/scripting/shavit-hud.sp @@ -109,6 +109,7 @@ int gI_Buttons[MAXPLAYERS+1]; float gF_ConnectTime[MAXPLAYERS+1]; bool gB_FirstPrint[MAXPLAYERS+1]; int gI_PreviousSpeed[MAXPLAYERS+1]; +int gI_ZoneSpeedLimit[MAXPLAYERS+1]; bool gB_Late = false; @@ -222,7 +223,7 @@ public void OnPluginStart() // cookies gH_HUDCookie = RegClientCookie("shavit_hud_setting", "HUD settings", CookieAccess_Protected); - gH_HUDCookieMain = RegClientCookie("shavit_hud_settingmain", "HUD settings for main ", CookieAccess_Protected); + gH_HUDCookieMain = RegClientCookie("shavit_hud_settingmain", "HUD settings for hint text.", CookieAccess_Protected); if(gB_Late) { @@ -232,7 +233,7 @@ public void OnPluginStart() { OnClientPutInServer(i); - if(AreClientCookiesCached(i)) + if(AreClientCookiesCached(i) && !IsFakeClient(i)) { OnClientCookiesCached(i); } @@ -409,20 +410,19 @@ public void OnClientCookiesCached(int client) gI_HUDSettings[client] = StringToInt(sHUDSettings); } - char sHUDSettingsMain[8]; - GetClientCookie(client, gH_HUDCookieMain, sHUDSettingsMain, 8); + GetClientCookie(client, gH_HUDCookieMain, sHUDSettings, 8); - if(strlen(sHUDSettingsMain) == 0) + if(strlen(sHUDSettings) == 0) { - IntToString(HUD_DEFAULT2, sHUDSettingsMain, 8); + IntToString(HUD_DEFAULT2, sHUDSettings, 8); - SetClientCookie(client, gH_HUDCookieMain, sHUDSettingsMain); + SetClientCookie(client, gH_HUDCookieMain, sHUDSettings); gI_HUD2Settings[client] = HUD_DEFAULT2; } else { - gI_HUD2Settings[client] = StringToInt(sHUDSettingsMain); + gI_HUD2Settings[client] = StringToInt(sHUDSettings); } } @@ -940,6 +940,15 @@ int GetGradient(int start, int end, int steps) return GetHex(aColorGradient); } +public void Shavit_OnEnterZone(int client, int type, int track, int id, int entity) +{ + if(type == Zone_CustomSpeedLimit) + { + gI_ZoneSpeedLimit[client] = Shavit_GetZoneData(id); + } +} + + int AddHUDToBuffer_Source2013(int client, huddata_t data, char[] buffer, int maxlen) { int iLines = 0; @@ -1090,9 +1099,17 @@ int AddHUDToBuffer_Source2013(int client, huddata_t data, char[] buffer, int max AddHUDLine(buffer, maxlen, sLine, iLines); iLines++; - if(gA_StyleSettings[data.iStyle].fVelocityLimit > 0.0 && Shavit_InsideZone(data.iTarget, Zone_NoVelLimit, -1)) + if(gA_StyleSettings[data.iStyle].fVelocityLimit > 0.0 && Shavit_InsideZone(data.iTarget, Zone_CustomSpeedLimit, -1)) { - FormatEx(sLine, 128, "%T", "HudNoSpeedLimit", client); + if(gI_ZoneSpeedLimit[data.iTarget] == 0) + { + FormatEx(sLine, 128, "%T", "HudNoSpeedLimit", data.iTarget); + } + + else + { + FormatEx(sLine, 128, "%T", "HudCustomSpeedLimit", client, gI_ZoneSpeedLimit[data.iTarget]); + } AddHUDLine(buffer, maxlen, sLine, iLines); iLines++; diff --git a/addons/sourcemod/scripting/shavit-misc.sp b/addons/sourcemod/scripting/shavit-misc.sp index 81fd0e414..d2232258e 100644 --- a/addons/sourcemod/scripting/shavit-misc.sp +++ b/addons/sourcemod/scripting/shavit-misc.sp @@ -75,7 +75,7 @@ enum struct player_cpcache_t enum struct persistent_data_t { - char sAuthID[32]; + int iSteamID; float fDisconnectTime; float fPosition[3]; float fAngles[3]; @@ -97,7 +97,7 @@ int gI_Ammo = -1; char gS_RadioCommands[][] = { "coverme", "takepoint", "holdpos", "regroup", "followme", "takingfire", "go", "fallback", "sticktog", "getinpos", "stormfront", "report", "roger", "enemyspot", "needbackup", "sectorclear", "inposition", "reportingin", - "getout", "negative", "enemydown", "compliment", "thanks", "cheer" }; + "getout", "negative", "enemydown", "compliment", "thanks", "cheer", "go_a", "go_b", "sorry", "needrop" }; bool gB_Hide[MAXPLAYERS+1]; bool gB_Late = false; @@ -1014,7 +1014,7 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity"); // prespeed - if(!bNoclip && !gA_StyleSettings[gI_Style[client]].bPrespeed && Shavit_InsideZone(client, Zone_Start, track)) + if(!bNoclip && gA_StyleSettings[gI_Style[client]].iPrespeed == 0 && Shavit_InsideZone(client, Zone_Start, track)) { if((gCV_PreSpeed.IntValue == 2 || gCV_PreSpeed.IntValue == 3) && gI_GroundEntity[client] == -1 && iGroundEntity != -1 && (buttons & IN_JUMP) > 0) { @@ -1127,7 +1127,7 @@ void PersistData(int client) if(!IsClientInGame(client) || !IsPlayerAlive(client) || - !GetClientAuthId(client, AuthId_Steam3, aData.sAuthID, 32) || + (aData.iSteamID = GetSteamAccountID((client))) == 0 || Shavit_GetTimerStatus(client) == Timer_Stopped || gCV_PersistData.IntValue == 0) { @@ -1188,11 +1188,11 @@ void DeletePersistentData(int index, persistent_data_t data) public Action Timer_LoadPersistentData(Handle Timer, any data) { - char sAuthID[32]; + int iSteamID = 0; int client = GetClientFromSerial(data); if(client == 0 || - !GetClientAuthId(client, AuthId_Steam3, sAuthID, 32) || + (iSteamID = GetSteamAccountID(client)) == 0 || GetClientTeam(client) < 2 || !IsPlayerAlive(client)) { @@ -1207,7 +1207,7 @@ public Action Timer_LoadPersistentData(Handle Timer, any data) { gA_PersistentData.GetArray(i, aData); - if(StrEqual(sAuthID, aData.sAuthID)) + if(iSteamID == aData.iSteamID) { iIndex = i; @@ -1676,6 +1676,11 @@ public Action Command_Checkpoints(int client, int args) return Plugin_Handled; } + if(gA_StyleSettings[gI_Style[client]].bKZCheckpoints) + { + gB_ClosedKZCP[client] = false; + } + return OpenCheckpointsMenu(client); } @@ -2769,7 +2774,7 @@ public Action Command_Specs(int client, int args) public Action Shavit_OnStart(int client) { - if(!gA_StyleSettings[gI_Style[client]].bPrespeed && GetEntityMoveType(client) == MOVETYPE_NOCLIP) + if(gA_StyleSettings[gI_Style[client]].iPrespeed == 0 && GetEntityMoveType(client) == MOVETYPE_NOCLIP) { return Plugin_Stop; } @@ -3305,11 +3310,11 @@ public Action Command_Drop(int client, const char[] command, int argc) return Plugin_Continue; } - int weapon = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon"); + int iWeapon = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon"); - if(weapon != -1 && IsValidEntity(weapon)) + if(iWeapon != -1 && IsValidEntity(iWeapon) && GetEntPropEnt(iWeapon, Prop_Send, "m_hOwnerEntity") == client) { - CS_DropWeapon(client, weapon, true); + CS_DropWeapon(client, iWeapon, true); } return Plugin_Handled; @@ -3322,7 +3327,6 @@ void LoadState(int client) Shavit_LoadSnapshot(client, gA_SaveStates[client]); Shavit_SetPracticeMode(client, gB_SaveStatesSegmented[client], false); - Shavit_ResumeTimer(client); if(gB_Replay && gA_SaveFrames[client] != null) { diff --git a/addons/sourcemod/scripting/shavit-rankings.sp b/addons/sourcemod/scripting/shavit-rankings.sp index b15b5d04d..7c628083e 100644 --- a/addons/sourcemod/scripting/shavit-rankings.sp +++ b/addons/sourcemod/scripting/shavit-rankings.sp @@ -60,6 +60,7 @@ Database gH_SQL = null; bool gB_Stats = false; bool gB_Late = false; +bool gB_TierQueried = false; int gI_Tier = 1; // No floating numbers for tiers, sorry. @@ -71,6 +72,7 @@ StringMap gA_MapTiers = null; ConVar gCV_PointsPerTier = null; ConVar gCV_WeightingMultiplier = null; +ConVar gCV_LastLoginRecalculate = null; ranking_t gA_Rankings[MAXPLAYERS+1]; @@ -118,16 +120,6 @@ public void OnAllPluginsLoaded() { SetFailState("shavit-wr is required for the plugin to work."); } - - if(gH_SQL == null) - { - Shavit_OnDatabaseLoaded(); - } - - for(int i = 0; i < TRACKS_SIZE; i++) - { - GetTrackName(LANG_SERVER, i, gS_TrackNames[i], 32); - } } public void OnPluginStart() @@ -139,7 +131,7 @@ public void OnPluginStart() RegConsoleCmd("sm_maptier", Command_Tier, "Prints the map's tier to chat. (sm_tier alias)"); RegConsoleCmd("sm_rank", Command_Rank, "Show your or someone else's rank. Usage: sm_rank [name]"); - RegConsoleCmd("sm_top", Command_Top, "Show the top 100 players."); // The rewrite of rankings will not have the ability to show over 100 entries. Dynamic fetching can be exploited and overload the database. + RegConsoleCmd("sm_top", Command_Top, "Show the top 100 players."); RegAdminCmd("sm_settier", Command_SetTier, ADMFLAG_RCON, "Change the map's tier. Usage: sm_settier "); RegAdminCmd("sm_setmaptier", Command_SetTier, ADMFLAG_RCON, "Change the map's tier. Usage: sm_setmaptier (sm_settier alias)"); @@ -150,6 +142,7 @@ public void OnPluginStart() gCV_PointsPerTier = CreateConVar("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 = CreateConVar("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 = CreateConVar("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); AutoExecConfig(); @@ -160,13 +153,18 @@ public void OnPluginStart() // tier cache gA_ValidMaps = new ArrayList(128); gA_MapTiers = new StringMap(); - - SQL_SetPrefix(); if(gB_Late) { Shavit_OnChatConfigLoaded(); } + + for(int i = 0; i < TRACKS_SIZE; i++) + { + GetTrackName(LANG_SERVER, i, gS_TrackNames[i], 32); + } + + SQL_DBConnect(); } public void Shavit_OnChatConfigLoaded() @@ -209,78 +207,20 @@ public void OnLibraryRemoved(const char[] name) } } -public void Shavit_OnDatabaseLoaded() -{ - gH_SQL = Shavit_GetDatabase(); - SetSQLInfo(); -} - -public Action CheckForSQLInfo(Handle Timer) -{ - return SetSQLInfo(); -} - -Action SetSQLInfo() -{ - if(gH_SQL == null) - { - gH_SQL = Shavit_GetDatabase(); - - CreateTimer(0.5, CheckForSQLInfo); - } - - else - { - SQL_DBConnect(); - - return Plugin_Stop; - } - - return Plugin_Continue; -} - -void SQL_SetPrefix() +void SQL_DBConnect() { - char sFile[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); + GetTimerSQLPrefix(gS_MySQLPrefix, 32); + gH_SQL = GetTimerDatabaseHandle(); - File fFile = OpenFile(sFile, "r"); - - if(fFile == null) + if(!IsMySQLDatabase(gH_SQL)) { - SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); + SetFailState("MySQL is the only supported database engine for shavit-rankings."); } - - char sLine[PLATFORM_MAX_PATH*2]; - while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2)) - { - TrimString(sLine); - strcopy(gS_MySQLPrefix, 32, sLine); - - break; - } - - delete fFile; -} - -void SQL_DBConnect() -{ - if(gH_SQL != null) - { - char sDriver[8]; - gH_SQL.Driver.GetIdentifier(sDriver, 8); - - if(!StrEqual(sDriver, "mysql", false)) - { - SetFailState("MySQL is the only supported database engine for shavit-rankings."); - } - - char sQuery[256]; - FormatEx(sQuery, 256, "CREATE TABLE IF NOT EXISTS `%smaptiers` (`map` VARCHAR(128), `tier` INT NOT NULL DEFAULT 1, PRIMARY KEY (`map`)) ENGINE=INNODB;", gS_MySQLPrefix); + char sQuery[256]; + FormatEx(sQuery, 256, "CREATE TABLE IF NOT EXISTS `%smaptiers` (`map` VARCHAR(128), `tier` INT NOT NULL DEFAULT 1, PRIMARY KEY (`map`)) ENGINE=INNODB;", gS_MySQLPrefix); - gH_SQL.Query(SQL_CreateTable_Callback, sQuery, 0); - } + gH_SQL.Query(SQL_CreateTable_Callback, sQuery, 0); } public void SQL_CreateTable_Callback(Database db, DBResultSet results, const char[] error, any data) @@ -310,7 +250,7 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha bool bSuccess = true; RunLongFastQuery(bSuccess, "CREATE GetWeightedPoints", - "CREATE FUNCTION GetWeightedPoints(authid VARCHAR(32)) " ... + "CREATE FUNCTION GetWeightedPoints(steamid INT) " ... "RETURNS FLOAT " ... "READS SQL DATA " ... "BEGIN " ... @@ -318,7 +258,7 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha "DECLARE total FLOAT DEFAULT 0.0; " ... "DECLARE mult FLOAT DEFAULT 1.0; " ... "DECLARE done INT DEFAULT 0; " ... - "DECLARE cur CURSOR FOR SELECT points FROM %splayertimes WHERE auth = authid AND points > 0.0 ORDER BY points DESC; " ... + "DECLARE cur CURSOR FOR SELECT points FROM %splayertimes WHERE auth = steamid AND points > 0.0 ORDER BY points DESC; " ... "DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; " ... "OPEN cur; " ... "iter: LOOP " ... @@ -400,7 +340,8 @@ public void OnClientPostAdminCheck(int client) public void OnMapStart() { - if(gH_SQL == null) + // do NOT keep running this more than once per map, as UpdateAllPoints() is called after this eventually and locks up the database while it is running + if(gB_TierQueried) { return; } @@ -409,8 +350,6 @@ public void OnMapStart() PrintToServer("DEBUG: 1 (OnMapStart)"); #endif - UpdateRankedPlayers(); - GetCurrentMap(gS_Map, 160); GetMapDisplayName(gS_Map, gS_Map, 160); @@ -418,17 +357,11 @@ public void OnMapStart() // I won't repeat the same mistake blacky has done with tier 3 being default.. gI_Tier = 1; - char sDriver[8]; - gH_SQL.Driver.GetIdentifier(sDriver, 8); - - if(!StrEqual(sDriver, "mysql", false)) - { - SetFailState("Rankings will only support MySQL for the moment. Sorry."); - } - char sQuery[256]; FormatEx(sQuery, 256, "SELECT tier FROM %smaptiers WHERE map = '%s';", gS_MySQLPrefix, gS_Map); - gH_SQL.Query(SQL_GetMapTier_Callback, sQuery, 0, DBPrio_Low); + gH_SQL.Query(SQL_GetMapTier_Callback, sQuery); + + gB_TierQueried = true; } public void SQL_GetMapTier_Callback(Database db, DBResultSet results, const char[] error, any data) @@ -531,6 +464,7 @@ void GuessBestMapName(const char[] input, char[] output, int size) public void OnMapEnd() { RecalculateAll(gS_Map); + gB_TierQueried = false; } public Action Command_Tier(int client, int args) @@ -608,7 +542,7 @@ public int MenuHandler_Top(Menu menu, MenuAction action, int param1, int param2) if(gB_Stats && !StrEqual(sInfo, "-1")) { - Shavit_OpenStatsMenu(param1, sInfo); + Shavit_OpenStatsMenu(param1, StringToInt(sInfo)); } } @@ -708,7 +642,7 @@ public void Trans_OnRecalcSuccess(Database db, any data, int numQueries, DBResul ReplyToCommand(client, "- Finished recalculating all points. Recalculating user points, top 100 and user cache."); - UpdateAllPoints(); + UpdateAllPoints(true); UpdateTop100(); for(int i = 1; i <= MaxClients; i++) @@ -758,8 +692,8 @@ void RecalculateMap(const char[] map, const int track, const int style) PrintToServer("Recalculating points. (%s, %d, %d)", map, track, style); #endif - char sQuery[192]; - FormatEx(sQuery, 192, "UPDATE %splayertimes SET points = GetRecordPoints(%d, %d, time, '%s', %.1f, %.3f) WHERE style = %d AND track = %d AND map = '%s';", + char sQuery[256]; + FormatEx(sQuery, 256, "UPDATE %splayertimes SET points = GetRecordPoints(%d, %d, time, '%s', %.1f, %.3f) WHERE style = %d AND track = %d AND map = '%s';", gS_MySQLPrefix, style, track, map, gCV_PointsPerTier.FloatValue, gA_StyleSettings[style].fRankingMultiplier, style, track, map); gH_SQL.Query(SQL_Recalculate_Callback, sQuery, 0, DBPrio_High); @@ -783,15 +717,25 @@ public void SQL_Recalculate_Callback(Database db, DBResultSet results, const cha #endif } -void UpdateAllPoints() +void UpdateAllPoints(bool recalcall = false) { #if defined DEBUG LogError("DEBUG: 6 (UpdateAllPoints)"); #endif - char sQuery[128]; - FormatEx(sQuery, 128, "UPDATE %susers SET points = GetWeightedPoints(auth) WHERE auth IN (SELECT DISTINCT auth FROM %splayertimes);", - gS_MySQLPrefix, gS_MySQLPrefix); + char sQuery[256]; + + if(recalcall || gCV_LastLoginRecalculate.IntValue == 0) + { + FormatEx(sQuery, 256, "UPDATE %susers SET points = GetWeightedPoints(auth) WHERE auth IN (SELECT DISTINCT auth FROM %splayertimes);", + gS_MySQLPrefix, gS_MySQLPrefix); + } + + else + { + FormatEx(sQuery, 256, "UPDATE %susers SET points = GetWeightedPoints(auth) WHERE %d - lastlogin < %d AND auth IN (SELECT DISTINCT auth FROM %splayertimes);", + gS_MySQLPrefix, GetTime(), (gCV_LastLoginRecalculate.IntValue * 60), gS_MySQLPrefix); + } gH_SQL.Query(SQL_UpdateAllPoints_Callback, sQuery); } @@ -804,6 +748,8 @@ public void SQL_UpdateAllPoints_Callback(Database db, DBResultSet results, const return; } + + UpdateRankedPlayers(); } void UpdatePlayerRank(int client, bool first) @@ -811,15 +757,15 @@ void UpdatePlayerRank(int client, bool first) gA_Rankings[client].iRank = 0; gA_Rankings[client].fPoints = 0.0; - char sAuthID[32]; + int iSteamID = 0; - if(GetClientAuthId(client, AuthId_Steam3, sAuthID, 32)) + if((iSteamID = GetSteamAccountID(client)) != 0) { // if there's any issue with this query, // add "ORDER BY points DESC " before "LIMIT 1" char sQuery[512]; - FormatEx(sQuery, 512, "SELECT u2.points, COUNT(*) FROM %susers u1 JOIN (SELECT points FROM %susers WHERE auth = '%s') u2 WHERE u1.points >= u2.points;", - gS_MySQLPrefix, gS_MySQLPrefix, sAuthID); + FormatEx(sQuery, 512, "SELECT u2.points, COUNT(*) FROM %susers u1 JOIN (SELECT points FROM %susers WHERE auth = %d) u2 WHERE u1.points >= u2.points;", + gS_MySQLPrefix, gS_MySQLPrefix, iSteamID); DataPack hPack = new DataPack(); hPack.WriteCell(GetClientSerial(client)); @@ -924,8 +870,8 @@ public void SQL_UpdateTop100_Callback(Database db, DBResultSet results, const ch break; } - char sAuthID[32]; - results.FetchString(0, sAuthID, 32); + char sSteamID[32]; + results.FetchString(0, sSteamID, 32); char sName[MAX_NAME_LENGTH]; results.FetchString(1, sName, MAX_NAME_LENGTH); @@ -935,7 +881,7 @@ public void SQL_UpdateTop100_Callback(Database db, DBResultSet results, const ch char sDisplay[96]; FormatEx(sDisplay, 96, "#%d - %s (%s)", (++row), sName, sPoints); - gH_Top100Menu.AddItem(sAuthID, sDisplay); + gH_Top100Menu.AddItem(sSteamID, sDisplay); } if(gH_Top100Menu.ItemCount == 0) diff --git a/addons/sourcemod/scripting/shavit-replay.sp b/addons/sourcemod/scripting/shavit-replay.sp index a0ea08f01..6244d4d2d 100644 --- a/addons/sourcemod/scripting/shavit-replay.sp +++ b/addons/sourcemod/scripting/shavit-replay.sp @@ -33,7 +33,7 @@ #define REPLAY_FORMAT_V2 "{SHAVITREPLAYFORMAT}{V2}" #define REPLAY_FORMAT_FINAL "{SHAVITREPLAYFORMAT}{FINAL}" -#define REPLAY_FORMAT_SUBVERSION 0x03 +#define REPLAY_FORMAT_SUBVERSION 0x04 #define CELLS_PER_FRAME 8 // origin[3], angles[2], buttons, flags, movetype #define FRAMES_PER_WRITE 100 // amounts of frames to write per read/write call @@ -210,11 +210,6 @@ public void OnAllPluginsLoaded() { SetFailState("shavit-wr is required for the plugin to work."); } - - if(gH_SQL == null) - { - Shavit_OnDatabaseLoaded(); - } } public void OnPluginStart() @@ -299,7 +294,8 @@ public void OnPluginStart() RegConsoleCmd("sm_replay", Command_Replay, "Opens the central bot menu. For admins: 'sm_replay stop' to stop the playback."); // database - SQL_SetPrefix(); + GetTimerSQLPrefix(gS_MySQLPrefix, 32); + gH_SQL = GetTimerDatabaseHandle(); } public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) @@ -689,59 +685,6 @@ public int Native_Replay_DeleteMap(Handle handler, int numParams) } } -public void Shavit_OnDatabaseLoaded() -{ - gH_SQL = Shavit_GetDatabase(); - SetSQLInfo(); -} - -public Action CheckForSQLInfo(Handle Timer) -{ - return SetSQLInfo(); -} - -Action SetSQLInfo() -{ - if(gH_SQL == null) - { - gH_SQL = Shavit_GetDatabase(); - - CreateTimer(0.5, CheckForSQLInfo); - } - - else - { - return Plugin_Stop; - } - - return Plugin_Continue; -} - -void SQL_SetPrefix() -{ - char sFile[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); - - File fFile = OpenFile(sFile, "r"); - - if(fFile == null) - { - SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); - } - - char sLine[PLATFORM_MAX_PATH*2]; - - while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2)) - { - TrimString(sLine); - strcopy(gS_MySQLPrefix, 32, sLine); - - break; - } - - delete fFile; -} - public Action Cron(Handle Timer) { if(!gCV_Enabled.BoolValue) @@ -973,175 +916,213 @@ bool DefaultLoadReplay(int style, int track) return LoadReplay(style, track, sPath); } -bool LoadReplay(int style, int track, const char[] path) +bool LoadCurrentReplayFormat(File file, int version, int style, int track) { - if(FileExists(path)) + gA_FrameCache[style][track].iReplayVersion = version; + + // replay file integrity and preframes + if(gA_FrameCache[style][track].iReplayVersion >= 0x03) { - File fFile = OpenFile(path, "rb"); + char sMap[160]; + file.ReadString(sMap, 160); - char sHeader[64]; + int iStyle = 0; + file.ReadUint8(iStyle); - if(!fFile.ReadLine(sHeader, 64)) - { - delete fFile; + int iTrack = 0; + file.ReadUint8(iTrack); + if(!StrEqual(sMap, gS_Map, false) || iStyle != style || iTrack != track) + { + delete file; + return false; } - TrimString(sHeader); - char sExplodedHeader[2][64]; - ExplodeString(sHeader, ":", sExplodedHeader, 2, 64); + // whenever this is implemented + // file.ReadInt32(gA_FrameCache[style][track].iPreframes); + file.Seek(4, SEEK_CUR); + } - // TODO: split this piece of shit to different functions. cbf to keep it like this - if(StrEqual(sExplodedHeader[1], REPLAY_FORMAT_FINAL)) // hopefully, the last of them - { - gA_FrameCache[style][track].iReplayVersion = StringToInt(sExplodedHeader[0]); + int iTemp = 0; + file.ReadInt32(iTemp); + gA_FrameCache[style][track].iFrameCount = iTemp; - // replay file integrity and preframes - if(gA_FrameCache[style][track].iReplayVersion >= 0x03) - { - char sMap[160]; - fFile.ReadString(sMap, 160); + if(gA_Frames[style][track] == null) + { + gA_Frames[style][track] = new ArrayList(CELLS_PER_FRAME); + } - int iStyle = 0; - fFile.ReadUint8(iStyle); + gA_Frames[style][track].Resize(iTemp); - int iTrack = 0; - fFile.ReadUint8(iTrack); + file.ReadInt32(iTemp); + gA_FrameCache[style][track].fTime = view_as(iTemp); - if(!StrEqual(sMap, gS_Map, false) || iStyle != style || iTrack != track) - { - delete fFile; - - return false; - } + int iSteamID = 0; - // whenever this is implemented - // fFile.ReadInt32(gA_FrameCache[style][track].iPreframes); - fFile.Seek(4, SEEK_CUR); - } + if(gA_FrameCache[style][track].iReplayVersion >= 0x04) + { + file.ReadInt32(iSteamID); + } + + else + { + char sAuthID[32]; + file.ReadString(sAuthID, 32); + ReplaceString(sAuthID, 32, "[U:1:", ""); + ReplaceString(sAuthID, 32, "]", ""); + iSteamID = StringToInt(sAuthID); + } - int iTemp = 0; - fFile.ReadInt32(iTemp); - gA_FrameCache[style][track].iFrameCount = iTemp; + char sQuery[192]; + FormatEx(sQuery, 192, "SELECT name FROM %susers WHERE auth = %d;", gS_MySQLPrefix, iSteamID); - if(gA_Frames[style][track] == null) - { - gA_Frames[style][track] = new ArrayList(CELLS_PER_FRAME); - } + DataPack hPack = new DataPack(); + hPack.WriteCell(style); + hPack.WriteCell(track); - gA_Frames[style][track].Resize(iTemp); + gH_SQL.Query(SQL_GetUserName_Callback, sQuery, hPack, DBPrio_High); - fFile.ReadInt32(iTemp); - gA_FrameCache[style][track].fTime = view_as(iTemp); + int cells = CELLS_PER_FRAME; - char sAuthID[32]; - fFile.ReadString(sAuthID, 32); + if(gA_FrameCache[style][track].iReplayVersion == 0x01) + { + cells = 6; + } - if(gH_SQL != null) - { - char sQuery[192]; - FormatEx(sQuery, 192, "SELECT name FROM %susers WHERE auth = '%s';", gS_MySQLPrefix, sAuthID); + any[] aReplayData = new any[cells]; - DataPack pack = new DataPack(); - pack.WriteCell(style); - pack.WriteCell(track); + for(int i = 0; i < gA_FrameCache[style][track].iFrameCount; i++) + { + if(file.Read(aReplayData, cells, 4) >= 0) + { + gA_Frames[style][track].Set(i, view_as(aReplayData[0]), 0); + gA_Frames[style][track].Set(i, view_as(aReplayData[1]), 1); + gA_Frames[style][track].Set(i, view_as(aReplayData[2]), 2); + gA_Frames[style][track].Set(i, view_as(aReplayData[3]), 3); + gA_Frames[style][track].Set(i, view_as(aReplayData[4]), 4); + gA_Frames[style][track].Set(i, view_as(aReplayData[5]), 5); - gH_SQL.Query(SQL_GetUserName_Callback, sQuery, pack, DBPrio_High); + if(gA_FrameCache[style][track].iReplayVersion >= 0x02) + { + gA_Frames[style][track].Set(i, view_as(aReplayData[6]), 6); + gA_Frames[style][track].Set(i, view_as(aReplayData[7]), 7); } + } + } - int cells = CELLS_PER_FRAME; + gA_FrameCache[style][track].bNewFormat = true; // not wr-based - // backwards compatibility - if(gA_FrameCache[style][track].iReplayVersion == 0x01) - { - cells = 6; - } + delete file; - any[] aReplayData = new any[cells]; + return true; +} - for(int i = 0; i < gA_FrameCache[style][track].iFrameCount; i++) - { - if(fFile.Read(aReplayData, cells, 4) >= 0) - { - gA_Frames[style][track].Set(i, view_as(aReplayData[0]), 0); - gA_Frames[style][track].Set(i, view_as(aReplayData[1]), 1); - gA_Frames[style][track].Set(i, view_as(aReplayData[2]), 2); - gA_Frames[style][track].Set(i, view_as(aReplayData[3]), 3); - gA_Frames[style][track].Set(i, view_as(aReplayData[4]), 4); - gA_Frames[style][track].Set(i, view_as(aReplayData[5]), 5); - - if(gA_FrameCache[style][track].iReplayVersion >= 0x02) - { - gA_Frames[style][track].Set(i, view_as(aReplayData[6]), 6); - gA_Frames[style][track].Set(i, view_as(aReplayData[7]), 7); - } - } - } +bool LoadV2ReplayFormat(File file, int frames, int style, int track) +{ + int iReplaySize = gA_FrameCache[style][track].iFrameCount = frames; + gA_Frames[style][track].Resize(iReplaySize); - gA_FrameCache[style][track].bNewFormat = true; // not wr-based - } + gA_FrameCache[style][track].fTime = 0.0; // N/A at this version - else if(StrEqual(sExplodedHeader[1], REPLAY_FORMAT_V2)) + any[] aReplayData = new any[6]; + + for(int i = 0; i < iReplaySize; i++) + { + if(file.Read(aReplayData, 6, 4) >= 0) { - int iReplaySize = gA_FrameCache[style][track].iFrameCount = StringToInt(sExplodedHeader[0]); - gA_Frames[style][track].Resize(iReplaySize); + gA_Frames[style][track].Set(i, view_as(aReplayData[0]), 0); + gA_Frames[style][track].Set(i, view_as(aReplayData[1]), 1); + gA_Frames[style][track].Set(i, view_as(aReplayData[2]), 2); + gA_Frames[style][track].Set(i, view_as(aReplayData[3]), 3); + gA_Frames[style][track].Set(i, view_as(aReplayData[4]), 4); + gA_Frames[style][track].Set(i, view_as(aReplayData[5]), 5); + } + } - gA_FrameCache[style][track].fTime = 0.0; // N/A at this version + gA_FrameCache[style][track].bNewFormat = false; + strcopy(gA_FrameCache[style][track].sReplayName, MAX_NAME_LENGTH, "invalid"); - any[] aReplayData = new any[6]; + delete file; - for(int i = 0; i < iReplaySize; i++) - { - if(fFile.Read(aReplayData, 6, 4) >= 0) - { - gA_Frames[style][track].Set(i, view_as(aReplayData[0]), 0); - gA_Frames[style][track].Set(i, view_as(aReplayData[1]), 1); - gA_Frames[style][track].Set(i, view_as(aReplayData[2]), 2); - gA_Frames[style][track].Set(i, view_as(aReplayData[3]), 3); - gA_Frames[style][track].Set(i, view_as(aReplayData[4]), 4); - gA_Frames[style][track].Set(i, view_as(aReplayData[5]), 5); - } - } + return true; +} - gA_FrameCache[style][track].bNewFormat = false; - strcopy(gA_FrameCache[style][track].sReplayName, MAX_NAME_LENGTH, "invalid"); - } +bool LoadOldReplayFormat(File file, int style, int track) +{ + char sLine[320]; + char sExplodedLine[6][64]; - else // old, outdated and slow - only used for ancient replays + if(!file.Seek(0, SEEK_SET)) + { + delete file; + + return false; + } + + for(int i = 0; !file.EndOfFile(); i++) + { + file.ReadLine(sLine, 320); + int iStrings = ExplodeString(sLine, "|", sExplodedLine, 6, 64); + + gA_Frames[style][track].Resize(i + 1); + gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[0]), 0); + gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[1]), 1); + gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[2]), 2); + gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[3]), 3); + gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[4]), 4); + gA_Frames[style][track].Set(i, (iStrings == 6)? StringToInt(sExplodedLine[5]):0, 5); + } + + gA_FrameCache[style][track].iFrameCount = gA_Frames[style][track].Length; + gA_FrameCache[style][track].fTime = 0.0; // N/A at this version + gA_FrameCache[style][track].bNewFormat = false; // wr-based + strcopy(gA_FrameCache[style][track].sReplayName, MAX_NAME_LENGTH, "invalid"); + + delete file; + + return true; +} + +bool LoadReplay(int style, int track, const char[] path) +{ + if(FileExists(path)) + { + File fFile = OpenFile(path, "rb"); + + char sHeader[64]; + + if(!fFile.ReadLine(sHeader, 64)) { - char sLine[320]; - char sExplodedLine[6][64]; + delete fFile; - for(int i = 0; !fFile.EndOfFile(); i++) - { - fFile.ReadLine(sLine, 320); - int iStrings = ExplodeString(sLine, "|", sExplodedLine, 6, 64); - - gA_Frames[style][track].Resize(i + 1); - gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[0]), 0); - gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[1]), 1); - gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[2]), 2); - gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[3]), 3); - gA_Frames[style][track].Set(i, StringToFloat(sExplodedLine[4]), 4); - gA_Frames[style][track].Set(i, (iStrings == 6)? StringToInt(sExplodedLine[5]):0, 5); - } + return false; + } + + TrimString(sHeader); + char sExplodedHeader[2][64]; + ExplodeString(sHeader, ":", sExplodedHeader, 2, 64); - gA_FrameCache[style][track].iFrameCount = gA_Frames[style][track].Length; - gA_FrameCache[style][track].fTime = 0.0; // N/A at this version - gA_FrameCache[style][track].bNewFormat = false; // wr-based - strcopy(gA_FrameCache[style][track].sReplayName, MAX_NAME_LENGTH, "invalid"); + if(StrEqual(sExplodedHeader[1], REPLAY_FORMAT_FINAL)) // hopefully, the last of them + { + return LoadCurrentReplayFormat(fFile, StringToInt(sExplodedHeader[0]), style, track); } - delete fFile; + else if(StrEqual(sExplodedHeader[1], REPLAY_FORMAT_V2)) + { + return LoadV2ReplayFormat(fFile, StringToInt(sExplodedHeader[0]), style, track); + } - return true; + else // old, outdated and slow - only used for ancient replays + { + return LoadOldReplayFormat(fFile, style, track); + } } return false; } -bool SaveReplay(int style, int track, float time, char[] authid, char[] name, int preframes = 0) +bool SaveReplay(int style, int track, float time, int steamid, char[] name, int preframes = 0) { char sTrack[4]; FormatEx(sTrack, 4, "_%d", track); @@ -1165,7 +1146,7 @@ bool SaveReplay(int style, int track, float time, char[] authid, char[] name, in int iSize = gA_Frames[style][track].Length; fFile.WriteInt32(iSize); fFile.WriteInt32(view_as(time)); - fFile.WriteString(authid, true); + fFile.WriteInt32(steamid); any aFrameData[CELLS_PER_FRAME]; any aWriteData[CELLS_PER_FRAME * FRAMES_PER_WRITE]; @@ -1571,14 +1552,13 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st delete gA_Frames[style][track]; gA_Frames[style][track] = gA_PlayerFrames[client].Clone(); - char sAuthID[32]; - GetClientAuthId(client, AuthId_Steam3, sAuthID, 32); + int iSteamID = GetSteamAccountID(client); char sName[MAX_NAME_LENGTH]; GetClientName(client, sName, MAX_NAME_LENGTH); ReplaceString(sName, MAX_NAME_LENGTH, "#", "?"); - SaveReplay(style, track, time, sAuthID, sName); + SaveReplay(style, track, time, iSteamID, sName); if(ReplayEnabled(style)) { diff --git a/addons/sourcemod/scripting/shavit-sounds.sp b/addons/sourcemod/scripting/shavit-sounds.sp index a4caadb8b..f6fb75fcc 100644 --- a/addons/sourcemod/scripting/shavit-sounds.sp +++ b/addons/sourcemod/scripting/shavit-sounds.sp @@ -164,10 +164,7 @@ public void OnMapStart() else { - char sRank[8]; - IntToString(StringToInt(sExploded[0]), sRank, 8); - - gSM_RankSounds.SetString(sRank, sExploded[1]); + gSM_RankSounds.SetString(sExploded[0], sExploded[1]); } if(PrecacheSound(sExploded[1], true)) @@ -188,7 +185,7 @@ public void OnMapStart() public void Shavit_OnFinish(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs) { - if(oldtime != 0.0 && time > oldtime) + if(oldtime != 0.0 && time > oldtime && gA_NoImprovementSounds.Length != 0) { char sSound[PLATFORM_MAX_PATH]; gA_NoImprovementSounds.GetString(GetRandomInt(0, gA_NoImprovementSounds.Length - 1), sSound, PLATFORM_MAX_PATH); diff --git a/addons/sourcemod/scripting/shavit-stats.sp b/addons/sourcemod/scripting/shavit-stats.sp index 82c9bcbea..c205a9e0a 100644 --- a/addons/sourcemod/scripting/shavit-stats.sp +++ b/addons/sourcemod/scripting/shavit-stats.sp @@ -39,16 +39,14 @@ bool gB_Rankings = false; // database handle Database gH_SQL = null; - -// table prefix char gS_MySQLPrefix[32]; // cache -bool gB_AllowStats[MAXPLAYERS+1]; +bool gB_CanOpenMenu[MAXPLAYERS+1]; int gI_MapType[MAXPLAYERS+1]; int gI_Style[MAXPLAYERS+1]; int gI_Track[MAXPLAYERS+1]; -char gS_TargetAuth[MAXPLAYERS+1][32]; +int gI_TargetSteamID[MAXPLAYERS+1]; char gS_TargetName[MAXPLAYERS+1][MAX_NAME_LENGTH]; int gI_WRAmount[MAXPLAYERS+1]; EngineVersion gEV_Type = Engine_Unknown; @@ -95,11 +93,6 @@ public void OnAllPluginsLoaded() { SetFailState("shavit-wr is required for the plugin to work."); } - - if(gH_SQL == null) - { - Shavit_OnDatabaseLoaded(); - } } public void OnPluginStart() @@ -129,8 +122,6 @@ public void OnPluginStart() gB_Rankings = LibraryExists("shavit-rankings"); - SQL_SetPrefix(); - if(gB_Late) { for(int i = 1; i <= MaxClients; i++) @@ -141,6 +132,10 @@ public void OnPluginStart() } } } + + // database + GetTimerSQLPrefix(gS_MySQLPrefix, 32); + gH_SQL = GetTimerDatabaseHandle(); } public void OnMapStart() @@ -186,7 +181,7 @@ public void OnClientPutInServer(int client) return; } - gB_AllowStats[client] = true; + gB_CanOpenMenu[client] = true; gI_WRAmount[client] = 0; UpdateWRs(client); } @@ -207,59 +202,6 @@ public void OnLibraryRemoved(const char[] name) } } -public void Shavit_OnDatabaseLoaded() -{ - gH_SQL = Shavit_GetDatabase(); - SetSQLInfo(); -} - -public Action CheckForSQLInfo(Handle Timer) -{ - return SetSQLInfo(); -} - -Action SetSQLInfo() -{ - if(gH_SQL == null) - { - gH_SQL = Shavit_GetDatabase(); - - CreateTimer(0.5, CheckForSQLInfo); - } - - else - { - return Plugin_Stop; - } - - return Plugin_Continue; -} - -void SQL_SetPrefix() -{ - char sFile[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); - - File fFile = OpenFile(sFile, "r"); - - if(fFile == null) - { - SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); - } - - char sLine[PLATFORM_MAX_PATH*2]; - - while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2)) - { - TrimString(sLine); - strcopy(gS_MySQLPrefix, 32, sLine); - - break; - } - - delete fFile; -} - public void Player_Event(Event event, const char[] name, bool dontBroadcast) { if(gCV_MVPRankOnes.IntValue == 0) @@ -277,14 +219,9 @@ public void Player_Event(Event event, const char[] name, bool dontBroadcast) void UpdateWRs(int client) { - if(gH_SQL == null) - { - return; - } + int iSteamID = 0; - char sAuthID[32]; - - if(GetClientAuthId(client, AuthId_Steam3, sAuthID, 32)) + if((iSteamID = GetSteamAccountID(client)) != 0) { char sQuery[512]; @@ -292,16 +229,16 @@ void UpdateWRs(int client) if(gCV_MVPRankOnes.IntValue == 2) { FormatEx(sQuery, 512, - "SELECT COUNT(*) FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 %sGROUP by map, track) b ON a.time = b.time AND a.map = b.map AND style = 0 %sWHERE auth = '%s';", - gS_MySQLPrefix, gS_MySQLPrefix, (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", sAuthID); + "SELECT COUNT(*) FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 %sGROUP by map, track) b ON a.time = b.time AND a.map = b.map AND style = 0 %sWHERE auth = %d;", + gS_MySQLPrefix, gS_MySQLPrefix, (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", iSteamID); } // all styles else { FormatEx(sQuery, 512, - "SELECT COUNT(*) FROM %splayertimes a JOIN (SELECT MIN(time) time, map, style FROM %splayertimes %sGROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND a.style = b.style %sWHERE auth = '%s';", - gS_MySQLPrefix, gS_MySQLPrefix, (gCV_MVPRankOnes_Main.BoolValue)? "WHERE track = 0 ":"", (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", sAuthID); + "SELECT COUNT(*) FROM %splayertimes a JOIN (SELECT MIN(time) time, map, style FROM %splayertimes %sGROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND a.style = b.style %sWHERE auth = %d;", + gS_MySQLPrefix, gS_MySQLPrefix, (gCV_MVPRankOnes_Main.BoolValue)? "WHERE track = 0 ":"", (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", iSteamID); } gH_SQL.Query(SQL_GetWRs_Callback, sQuery, GetClientSerial(client)); @@ -356,7 +293,7 @@ public Action Command_MapsDoneLeft(int client, int args) } } - GetClientAuthId(target, AuthId_Steam3, gS_TargetAuth[client], 32); + gI_TargetSteamID[client] = GetSteamAccountID(target); char sCommand[16]; GetCmdArg(0, sCommand, 16); @@ -472,15 +409,15 @@ public Action Command_Profile(int client, int args) } } - GetClientAuthId(target, AuthId_Steam3, gS_TargetAuth[client], 32); + gI_TargetSteamID[client] = GetSteamAccountID(target); - return OpenStatsMenu(client, gS_TargetAuth[client]); + return OpenStatsMenu(client, gI_TargetSteamID[client]); } -Action OpenStatsMenu(int client, const char[] authid) +Action OpenStatsMenu(int client, int steamid) { // no spam please - if(!gB_AllowStats[client]) + if(!gB_CanOpenMenu[client]) { return Plugin_Handled; } @@ -490,26 +427,26 @@ Action OpenStatsMenu(int client, const char[] authid) if(gB_Rankings) { - FormatEx(sQuery, 2048, "SELECT a.clears, b.maps, c.wrs, d.name, d.country, d.lastlogin, d.points, e.rank FROM " ... - "(SELECT COUNT(*) clears FROM (SELECT map FROM %splayertimes WHERE auth = '%s' AND track = 0 GROUP BY map) s) a " ... + FormatEx(sQuery, 2048, "SELECT a.clears, b.maps, c.wrs, d.name, d.ip, d.lastlogin, d.points, e.rank FROM " ... + "(SELECT COUNT(*) clears FROM (SELECT map FROM %splayertimes WHERE auth = %d AND track = 0 GROUP BY map) s) a " ... "JOIN (SELECT COUNT(*) maps FROM (SELECT map FROM %smapzones WHERE track = 0 AND type = 0 GROUP BY map) s) b " ... - "JOIN (SELECT COUNT(*) wrs FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 AND track = 0 GROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND track = 0 AND style = 0 WHERE auth = '%s') c " ... - "JOIN (SELECT name, country, lastlogin, FORMAT(points, 2) points FROM %susers WHERE auth = '%s') d " ... - "JOIN (SELECT COUNT(*) rank FROM %susers u1 JOIN (SELECT points FROM %susers WHERE auth = '%s') u2 WHERE u1.points >= u2.points) e " ... - "LIMIT 1;", gS_MySQLPrefix, authid, gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, authid, gS_MySQLPrefix, authid, gS_MySQLPrefix, gS_MySQLPrefix, authid); + "JOIN (SELECT COUNT(*) wrs FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 AND track = 0 GROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND track = 0 AND style = 0 WHERE auth = %d) c " ... + "JOIN (SELECT name, ip, lastlogin, FORMAT(points, 2) points FROM %susers WHERE auth = %d) d " ... + "JOIN (SELECT COUNT(*) rank FROM %susers u1 JOIN (SELECT points FROM %susers WHERE auth = %d) u2 WHERE u1.points >= u2.points) e " ... + "LIMIT 1;", gS_MySQLPrefix, steamid, gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, steamid, gS_MySQLPrefix, steamid, gS_MySQLPrefix, gS_MySQLPrefix, steamid); } else { - FormatEx(sQuery, 2048, "SELECT a.clears, b.maps, c.wrs, d.name, d.country, d.lastlogin FROM " ... - "(SELECT COUNT(*) clears FROM (SELECT map FROM %splayertimes WHERE auth = '%s' AND track = 0 GROUP BY map) s) a " ... + FormatEx(sQuery, 2048, "SELECT a.clears, b.maps, c.wrs, d.name, d.ip, d.lastlogin FROM " ... + "(SELECT COUNT(*) clears FROM (SELECT map FROM %splayertimes WHERE auth = %d AND track = 0 GROUP BY map) s) a " ... "JOIN (SELECT COUNT(*) maps FROM (SELECT map FROM %smapzones WHERE track = 0 AND type = 0 GROUP BY map) s) b " ... - "JOIN (SELECT COUNT(*) wrs FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 AND track = 0 GROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND track = 0 AND style = 0 WHERE auth = '%s') c " ... - "JOIN (SELECT name, country, lastlogin FROM %susers WHERE auth = '%s') d " ... - "LIMIT 1;", gS_MySQLPrefix, authid, gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, authid, gS_MySQLPrefix, authid); + "JOIN (SELECT COUNT(*) wrs FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 AND track = 0 GROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND track = 0 AND style = 0 WHERE auth = %d) c " ... + "JOIN (SELECT name, ip, lastlogin FROM %susers WHERE auth = %d) d " ... + "LIMIT 1;", gS_MySQLPrefix, steamid, gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, steamid, gS_MySQLPrefix, steamid); } - gB_AllowStats[client] = false; + gB_CanOpenMenu[client] = false; gH_SQL.Query(OpenStatsMenuCallback, sQuery, GetClientSerial(client), DBPrio_Low); return Plugin_Handled; @@ -518,7 +455,7 @@ Action OpenStatsMenu(int client, const char[] authid) public void OpenStatsMenuCallback(Database db, DBResultSet results, const char[] error, any data) { int client = GetClientFromSerial(data); - gB_AllowStats[client] = true; + gB_CanOpenMenu[client] = true; if(results == null) { @@ -541,8 +478,16 @@ public void OpenStatsMenuCallback(Database db, DBResultSet results, const char[] results.FetchString(3, gS_TargetName[client], MAX_NAME_LENGTH); ReplaceString(gS_TargetName[client], MAX_NAME_LENGTH, "#", "?"); + int iIPAddress = results.FetchInt(4); + char sIPAddress[32]; + IPAddressToString(iIPAddress, sIPAddress, 32); + char sCountry[64]; - results.FetchString(4, sCountry, 64); + + if(!GeoipCountry(sIPAddress, sCountry, 64)) + { + strcopy(sCountry, 64, "Local Area Network"); + } int iLastLogin = results.FetchInt(5); char sLastLogin[32]; @@ -582,8 +527,8 @@ public void OpenStatsMenuCallback(Database db, DBResultSet results, const char[] FormatEx(sClearString, 128, "%T: %d/%d (%.01f%%)", "MapCompletions", client, iClears, iTotalMaps, ((float(iClears) / iTotalMaps) * 100.0)); Menu menu = new Menu(MenuHandler_ProfileHandler); - menu.SetTitle("%s's %T. %s\n%T: %s\n%s\n%s\n[%s] %T: %d%s\n", - gS_TargetName[client], "Profile", client, gS_TargetAuth[client], "Country", client, sCountry, sLastLogin, sClearString, + menu.SetTitle("%s's %T. [U:1:%d]\n%T: %s\n%s\n%s\n[%s] %T: %d%s\n", + gS_TargetName[client], "Profile", client, gI_TargetSteamID[client], "Country", client, sCountry, sLastLogin, sClearString, gS_StyleStrings[0].sStyleName, "WorldRecords", client, iWRs, sRankingString); int[] styles = new int[gI_Styles]; @@ -681,7 +626,7 @@ public int MenuHandler_TypeHandler(Menu menu, MenuAction action, int param1, int else if(action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) { - OpenStatsMenu(param1, gS_TargetAuth[param1]); + OpenStatsMenu(param1, gI_TargetSteamID[param1]); } else if(action == MenuAction_End) @@ -692,27 +637,10 @@ public int MenuHandler_TypeHandler(Menu menu, MenuAction action, int param1, int return 0; } -public Action Timer_DBFailure(Handle timer, any data) -{ - int client = GetClientFromSerial(data); - - if(client == 0) - { - return Plugin_Stop; - } - - ShowMaps(client); - - return Plugin_Stop; -} - void ShowMaps(int client) { - // database not found, display with a 3 seconds delay - if(gH_SQL == null) + if(!gB_CanOpenMenu[client]) { - CreateTimer(3.0, Timer_DBFailure, GetClientSerial(client)); - return; } @@ -721,8 +649,8 @@ void ShowMaps(int client) if(gI_MapType[client] == MAPSDONE) { FormatEx(sQuery, 512, - "SELECT a.map, a.time, a.jumps, a.id, COUNT(b.map) + 1 rank, a.points FROM %splayertimes a LEFT JOIN %splayertimes b ON a.time > b.time AND a.map = b.map AND a.style = b.style AND a.track = b.track WHERE a.auth = '%s' AND a.style = %d AND a.track = %d GROUP BY a.map, a.time, a.jumps, a.id, a.points ORDER BY a.%s;", - gS_MySQLPrefix, gS_MySQLPrefix, gS_TargetAuth[client], gI_Style[client], gI_Track[client], (gB_Rankings)? "points DESC":"map"); + "SELECT a.map, a.time, a.jumps, a.id, COUNT(b.map) + 1 rank, a.points FROM %splayertimes a LEFT JOIN %splayertimes b ON a.time > b.time AND a.map = b.map AND a.style = b.style AND a.track = b.track WHERE a.auth = %d AND a.style = %d AND a.track = %d GROUP BY a.map, a.time, a.jumps, a.id, a.points ORDER BY a.%s;", + gS_MySQLPrefix, gS_MySQLPrefix, gI_TargetSteamID[client], gI_Style[client], gI_Track[client], (gB_Rankings)? "points DESC":"map"); } else @@ -730,18 +658,20 @@ void ShowMaps(int client) if(gB_Rankings) { FormatEx(sQuery, 512, - "SELECT DISTINCT m.map, t.tier FROM %smapzones m LEFT JOIN %smaptiers t ON m.map = t.map WHERE m.type = 0 AND m.track = %d AND m.map NOT IN (SELECT DISTINCT map FROM %splayertimes WHERE auth = '%s' AND style = %d AND track = %d) ORDER BY m.map;", - gS_MySQLPrefix, gS_MySQLPrefix, gI_Track[client], gS_MySQLPrefix, gS_TargetAuth[client], gI_Style[client], gI_Track[client]); + "SELECT DISTINCT m.map, t.tier FROM %smapzones m LEFT JOIN %smaptiers t ON m.map = t.map WHERE m.type = 0 AND m.track = %d AND m.map NOT IN (SELECT DISTINCT map FROM %splayertimes WHERE auth = %d AND style = %d AND track = %d) ORDER BY m.map;", + gS_MySQLPrefix, gS_MySQLPrefix, gI_Track[client], gS_MySQLPrefix, gI_TargetSteamID[client], gI_Style[client], gI_Track[client]); } else { FormatEx(sQuery, 512, - "SELECT DISTINCT map FROM %smapzones WHERE type = 0 AND track = %d AND map NOT IN (SELECT DISTINCT map FROM %splayertimes WHERE auth = '%s' AND style = %d AND track = %d) ORDER BY map;", - gS_MySQLPrefix, gI_Track[client], gS_MySQLPrefix, gS_TargetAuth[client], gI_Style[client], gI_Track[client]); + "SELECT DISTINCT map FROM %smapzones WHERE type = 0 AND track = %d AND map NOT IN (SELECT DISTINCT map FROM %splayertimes WHERE auth = %d AND style = %d AND track = %d) ORDER BY map;", + gS_MySQLPrefix, gI_Track[client], gS_MySQLPrefix, gI_TargetSteamID[client], gI_Style[client], gI_Track[client]); } } + gB_CanOpenMenu[client] = false; + gH_SQL.Query(ShowMapsCallback, sQuery, GetClientSerial(client), DBPrio_High); } @@ -761,6 +691,8 @@ public void ShowMapsCallback(Database db, DBResultSet results, const char[] erro return; } + gB_CanOpenMenu[client] = true; + int rows = results.RowCount; char sTrack[32]; @@ -853,7 +785,7 @@ public int MenuHandler_ShowMaps(Menu menu, MenuAction action, int param1, int pa if(StrEqual(sInfo, "nope")) { - OpenStatsMenu(param1, gS_TargetAuth[param1]); + OpenStatsMenu(param1, gI_TargetSteamID[param1]); return 0; } @@ -866,7 +798,7 @@ public int MenuHandler_ShowMaps(Menu menu, MenuAction action, int param1, int pa else if(action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) { - OpenStatsMenu(param1, gS_TargetAuth[param1]); + OpenStatsMenu(param1, gI_TargetSteamID[param1]); } else if(action == MenuAction_End) @@ -893,10 +825,10 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] return; } - Menu menu = new Menu(SubMenu_Handler); + Menu hMenu = new Menu(SubMenu_Handler); char sName[MAX_NAME_LENGTH]; - char sAuthID[32]; + int iSteamID = 0; char sMap[192]; if(results.FetchRow()) @@ -911,20 +843,20 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] char sDisplay[128]; FormatEx(sDisplay, 128, "%T: %s", "Time", client, sTime); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); // 2 - jumps int jumps = results.FetchInt(2); FormatEx(sDisplay, 128, "%T: %d", "Jumps", client, jumps); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); // 3 - style int style = results.FetchInt(3); FormatEx(sDisplay, 128, "%T: %s", "Style", client, gS_StyleStrings[style].sStyleName); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); // 4 - steamid3 - results.FetchString(4, sAuthID, 32); + iSteamID = results.FetchInt(4); // 6 - map results.FetchString(6, sMap, 192); @@ -934,7 +866,7 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] if(gB_Rankings && points > 0.0) { FormatEx(sDisplay, 192, "%T: %.03f", "Points", client, points); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); } // 5 - date @@ -947,7 +879,7 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] } FormatEx(sDisplay, 128, "%T: %s", "Date", client, sDate); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); int strafes = results.FetchInt(7); float sync = results.FetchFloat(8); @@ -955,16 +887,16 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] if(jumps > 0 || strafes > 0) { FormatEx(sDisplay, 128, (sync > 0.0)? "%T: %d (%.02f%%)":"%T: %d", "Strafes", client, strafes, sync, "Strafes", client, strafes); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); } } char sFormattedTitle[256]; - FormatEx(sFormattedTitle, 256, "%s %s\n--- %s:", sName, sAuthID, sMap); + FormatEx(sFormattedTitle, 256, "%s [U:1:%d]\n--- %s:", sName, iSteamID, sMap); - menu.SetTitle(sFormattedTitle); - menu.ExitBackButton = true; - menu.Display(client, 20); + hMenu.SetTitle(sFormattedTitle); + hMenu.ExitBackButton = true; + hMenu.Display(client, 20); } public int SubMenu_Handler(Menu menu, MenuAction action, int param1, int param2) @@ -985,9 +917,9 @@ public int SubMenu_Handler(Menu menu, MenuAction action, int param1, int param2) public int Native_OpenStatsMenu(Handle handler, int numParams) { int client = GetNativeCell(1); - GetNativeString(2, gS_TargetAuth[client], 32); + gI_TargetSteamID[client] = GetNativeCell(2); - OpenStatsMenu(client, gS_TargetAuth[client]); + OpenStatsMenu(client, gI_TargetSteamID[client]); } public int Native_GetWRCount(Handle handler, int numParams) diff --git a/addons/sourcemod/scripting/shavit-timelimit.sp b/addons/sourcemod/scripting/shavit-timelimit.sp index a153efe93..918337356 100644 --- a/addons/sourcemod/scripting/shavit-timelimit.sp +++ b/addons/sourcemod/scripting/shavit-timelimit.sp @@ -74,11 +74,6 @@ public void OnAllPluginsLoaded() { SetFailState("shavit-wr is required for the plugin to work."); } - - if(gH_SQL == null) - { - Shavit_OnDatabaseLoaded(); - } } public void OnPluginStart() @@ -110,7 +105,8 @@ public void OnPluginStart() AutoExecConfig(); - SQL_SetPrefix(); + GetTimerSQLPrefix(gS_MySQLPrefix, 32); + gH_SQL = GetTimerDatabaseHandle(); } public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) @@ -149,11 +145,6 @@ public void OnConfigsExecuted() public void OnMapStart() { - if(gH_SQL == null) - { - return; - } - if(gCV_DynamicTimelimits.BoolValue) { StartCalculating(); @@ -170,78 +161,20 @@ public void OnMapStart() } } -public void Shavit_OnDatabaseLoaded() -{ - gH_SQL = Shavit_GetDatabase(); - SetSQLInfo(); -} - -public Action CheckForSQLInfo(Handle Timer) -{ - return SetSQLInfo(); -} - -Action SetSQLInfo() -{ - if(gH_SQL == null) - { - gH_SQL = Shavit_GetDatabase(); - - CreateTimer(0.5, CheckForSQLInfo); - } - - else - { - OnMapStart(); - - return Plugin_Stop; - } - - return Plugin_Continue; -} - -void SQL_SetPrefix() -{ - char sFile[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); - - File fFile = OpenFile(sFile, "r"); - - if(fFile == null) - { - SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); - } - - char sLine[PLATFORM_MAX_PATH*2]; - - while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2)) - { - TrimString(sLine); - strcopy(gS_MySQLPrefix, 32, sLine); - - break; - } - - delete fFile; -} - void StartCalculating() { - if(gH_SQL != null) - { - char sMap[160]; - GetCurrentMap(sMap, 160); - GetMapDisplayName(sMap, sMap, 160); + char sMap[160]; + GetCurrentMap(sMap, 160); + GetMapDisplayName(sMap, sMap, 160); - char sQuery[512]; - FormatEx(sQuery, 512, "SELECT COUNT(*), SUM(t.time) FROM (SELECT r.time, r.style FROM %splayertimes r WHERE r.map = '%s' AND r.track = 0 %sORDER BY r.time LIMIT %d) t;", gS_MySQLPrefix, sMap, (gCV_Style.BoolValue)? "AND style = 0 ":"", gCV_PlayerAmount.IntValue); + char sQuery[512]; + FormatEx(sQuery, 512, "SELECT COUNT(*), SUM(t.time) FROM (SELECT r.time, r.style FROM %splayertimes r WHERE r.map = '%s' AND r.track = 0 %sORDER BY r.time LIMIT %d) t;", gS_MySQLPrefix, sMap, (gCV_Style.BoolValue)? "AND style = 0 ":"", gCV_PlayerAmount.IntValue); - #if defined DEBUG - PrintToServer("%s", sQuery); - #endif + #if defined DEBUG + PrintToServer("%s", sQuery); + #endif - gH_SQL.Query(SQL_GetMapTimes, sQuery, 0, DBPrio_Low); - } + gH_SQL.Query(SQL_GetMapTimes, sQuery, 0, DBPrio_Low); } public void SQL_GetMapTimes(Database db, DBResultSet results, const char[] error, any data) diff --git a/addons/sourcemod/scripting/shavit-wr.sp b/addons/sourcemod/scripting/shavit-wr.sp index 967b15da9..b1ff73d19 100644 --- a/addons/sourcemod/scripting/shavit-wr.sp +++ b/addons/sourcemod/scripting/shavit-wr.sp @@ -63,7 +63,6 @@ int gI_ValidMaps = 1; float gF_WRTime[STYLE_LIMIT][TRACKS_SIZE]; int gI_WRRecordID[STYLE_LIMIT][TRACKS_SIZE]; char gS_WRName[STYLE_LIMIT][TRACKS_SIZE][MAX_NAME_LENGTH]; -int gI_RecordAmount[STYLE_LIMIT][TRACKS_SIZE]; ArrayList gA_Leaderboard[STYLE_LIMIT][TRACKS_SIZE]; float gF_PlayerRecord[MAXPLAYERS+1][STYLE_LIMIT][TRACKS_SIZE]; @@ -118,14 +117,6 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max return APLRes_Success; } -public void OnAllPluginsLoaded() -{ - if(gH_SQL == null) - { - Shavit_OnDatabaseLoaded(); - } -} - public void OnPluginStart() { LoadTranslations("shavit-common.phrases"); @@ -179,7 +170,8 @@ public void OnPluginStart() // cache gA_ValidMaps = new ArrayList(192); - SQL_SetPrefix(); + // database + SQL_DBConnect(); } public void OnAdminMenuCreated(Handle topmenu) @@ -283,7 +275,7 @@ public void OnLibraryRemoved(const char[] name) public void OnMapStart() { - if(gH_SQL == null || !gB_Connected) + if(!gB_Connected) { return; } @@ -381,7 +373,6 @@ public void Shavit_OnStyleConfigLoaded(int styles) } gA_Leaderboard[i][j].Clear(); - gI_RecordAmount[i][j] = 0; } else @@ -416,7 +407,7 @@ public void OnClientPutInServer(int client) } } - if(!IsClientConnected(client) || IsFakeClient(client) || gH_SQL == null) + if(!IsClientConnected(client) || IsFakeClient(client)) { return; } @@ -426,11 +417,15 @@ public void OnClientPutInServer(int client) void UpdateClientCache(int client) { - char sAuthID[32]; - GetClientAuthId(client, AuthId_Steam3, sAuthID, 32); + int iSteamID = GetSteamAccountID(client); + + if(iSteamID == 0) + { + return; + } char sQuery[256]; - FormatEx(sQuery, 256, "SELECT time, style, track FROM %splayertimes WHERE map = '%s' AND auth = '%s';", gS_MySQLPrefix, gS_Map, sAuthID); + FormatEx(sQuery, 256, "SELECT time, style, track FROM %splayertimes WHERE map = '%s' AND auth = %d;", gS_MySQLPrefix, gS_Map, iSteamID); gH_SQL.Query(SQL_UpdateCache_Callback, sQuery, GetClientSerial(client), DBPrio_High); } @@ -485,7 +480,7 @@ void UpdateWRCache() gS_MySQLPrefix, gS_MySQLPrefix, gS_Map, gS_MySQLPrefix); } - gH_SQL.Query(SQL_UpdateWRCache_Callback, sQuery, 0, DBPrio_Low); + gH_SQL.Query(SQL_UpdateWRCache_Callback, sQuery); } public void SQL_UpdateWRCache_Callback(Database db, DBResultSet results, const char[] error, any data) @@ -504,7 +499,6 @@ public void SQL_UpdateWRCache_Callback(Database db, DBResultSet results, const c { strcopy(gS_WRName[i][j], MAX_NAME_LENGTH, "invalid"); gF_WRTime[i][j] = 0.0; - gI_RecordAmount[i][j] = 0; } } @@ -579,7 +573,7 @@ public int Native_GetRankForTime(Handle handler, int numParams) public int Native_GetRecordAmount(Handle handler, int numParams) { - return gI_RecordAmount[GetNativeCell(1)][GetNativeCell(2)]; + return GetRecordAmount(GetNativeCell(1), GetNativeCell(2)); } public int Native_GetTimeForRank(Handle handler, int numParams) @@ -589,10 +583,10 @@ public int Native_GetTimeForRank(Handle handler, int numParams) int track = GetNativeCell(3); #if defined DEBUG - Shavit_PrintToChatAll("style %d | rank %d | track %d | amount %d", style, rank, track, gI_RecordAmount[style][track]); + Shavit_PrintToChatAll("style %d | rank %d | track %d | amount %d", style, rank, track, GetRecordAmount(style, track)); #endif - if(rank > gI_RecordAmount[style][track]) + if(rank > GetRecordAmount(style, track)) { return view_as(0.0); } @@ -635,10 +629,9 @@ public void SQL_DeleteMap_Callback(Database db, DBResultSet results, const char[ public Action Command_Junk(int client, int args) { char sQuery[256]; - - char sAuth[32]; - GetClientAuthId(client, AuthId_Steam3, sAuth, 32); - FormatEx(sQuery, 256, "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync) VALUES ('%s', '%s', %f, %d, %d, 0, %d, %.02f);", gS_MySQLPrefix, sAuth, gS_Map, GetRandomFloat(10.0, 20.0), GetRandomInt(5, 15), GetTime(), GetRandomInt(5, 15), GetRandomFloat(50.0, 99.99)); + FormatEx(sQuery, 256, + "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync) VALUES (%d, '%s', %f, %d, %d, 0, %d, %.02f);", + gS_MySQLPrefix, GetSteamAccountID(client), gS_Map, GetRandomFloat(10.0, 20.0), GetRandomInt(5, 15), GetTime(), GetRandomInt(5, 15), GetRandomFloat(50.0, 99.99)); SQL_LockDatabase(gH_SQL); SQL_FastQuery(gH_SQL, sQuery); @@ -649,14 +642,20 @@ public Action Command_Junk(int client, int args) public Action Command_PrintLeaderboards(int client, int args) { - ReplyToCommand(client, "Track: Main - Style: 0"); - ReplyToCommand(client, "Current PB: %f", gF_PlayerRecord[client][0][0]); - ReplyToCommand(client, "Count: %d", gI_RecordAmount[0][0]); - ReplyToCommand(client, "Rank: %d", Shavit_GetRankForTime(0, gF_PlayerRecord[client][0][0], 0)); + char sArg[8]; + GetCmdArg(1, sArg, 8); - for(int i = 0; i < gI_RecordAmount[0][0]; i++) + int iStyle = StringToInt(sArg); + int iRecords = GetRecordAmount(iStyle, Track_Main); + + ReplyToCommand(client, "Track: Main - Style: %d", iStyle); + ReplyToCommand(client, "Current PB: %f", gF_PlayerRecord[client][iStyle][0]); + ReplyToCommand(client, "Count: %d", iRecords); + ReplyToCommand(client, "Rank: %d", Shavit_GetRankForTime(iStyle, gF_PlayerRecord[client][iStyle][0], iStyle)); + + for(int i = 0; i < iRecords; i++) { - ReplyToCommand(client, "#%d: %f", i, gA_Leaderboard[0][0].Get(i)); + ReplyToCommand(client, "#%d: %f", i, gA_Leaderboard[iStyle][0].Get(i)); } return Plugin_Handled; @@ -669,7 +668,7 @@ int GetTrackRecordCount(int track) for(int i = 0; i < gI_Styles; i++) { - count += gI_RecordAmount[i][track]; + count += GetRecordAmount(i, track); } return count; @@ -749,9 +748,9 @@ void DeleteSubmenu(int client) IntToString(iStyle, sInfo, 8); char sDisplay[64]; - FormatEx(sDisplay, 64, "%s (%T: %d)", gS_StyleStrings[iStyle].sStyleName, "WRRecord", client, gI_RecordAmount[iStyle][gA_WRCache[client].iLastTrack]); + FormatEx(sDisplay, 64, "%s (%T: %d)", gS_StyleStrings[iStyle].sStyleName, "WRRecord", client, GetRecordAmount(iStyle, gA_WRCache[client].iLastTrack)); - menu.AddItem(sInfo, sDisplay, (gI_RecordAmount[iStyle][gA_WRCache[client].iLastTrack] > 0)? ITEMDRAW_DEFAULT:ITEMDRAW_DISABLED); + menu.AddItem(sInfo, sDisplay, (GetRecordAmount(iStyle, gA_WRCache[client].iLastTrack) > 0)? ITEMDRAW_DEFAULT:ITEMDRAW_DISABLED); } menu.ExitButton = true; @@ -823,7 +822,7 @@ public int MenuHandler_DeleteAll_First(Menu menu, MenuAction action, int param1, IntToString(iStyle, sInfo, 8); - int iRecords = gI_RecordAmount[iStyle][iTrack]; + int iRecords = GetRecordAmount(iStyle, iTrack); if(iRecords > 0) { @@ -1120,8 +1119,7 @@ public void GetRecordDetails_Callback(Database db, DBResultSet results, const ch if(results.FetchRow()) { - char sAuthID[32]; - results.FetchString(0, sAuthID, 32); + int iSteamID = results.FetchInt(0); char sName[MAX_NAME_LENGTH]; results.FetchString(1, sName, MAX_NAME_LENGTH); @@ -1145,7 +1143,7 @@ public void GetRecordDetails_Callback(Database db, DBResultSet results, const ch // that's a big datapack ya yeet DataPack hPack = new DataPack(); hPack.WriteCell(GetClientSerial(client)); - hPack.WriteString(sAuthID); + hPack.WriteCell(iSteamID); hPack.WriteString(sName); hPack.WriteString(sMap); hPack.WriteCell(fTime); @@ -1173,9 +1171,7 @@ public void DeleteConfirm_Callback(Database db, DBResultSet results, const char[ hPack.Reset(); int iSerial = hPack.ReadCell(); - - char sAuthID[32]; - hPack.ReadString(sAuthID, 32); + int iSteamID = hPack.ReadCell(); char sName[MAX_NAME_LENGTH]; hPack.ReadString(sName, MAX_NAME_LENGTH); @@ -1229,8 +1225,8 @@ public void DeleteConfirm_Callback(Database db, DBResultSet results, const char[ FormatTime(sDate, 32, "%Y-%m-%d %H:%M:%S", iTimestamp); // above the client == 0 so log doesn't get lost if admin disconnects between deleting record and query execution - Shavit_LogMessage("%L - deleted record. Runner: %s (%s) | Map: %s | Style: %s | Track: %s | Time: %.2f (%s) | Strafes: %d (%.1f%%) | Jumps: %d (%.1f%%) | Run date: %s | Record ID: %d", - client, sName, sAuthID, sMap, gS_StyleStrings[iStyle].sStyleName, sTrack, fTime, (bWRDeleted)? "WR":"not WR", iStrafes, fSync, iJumps, fPerfectJumps, sDate, iRecordID); + Shavit_LogMessage("%L - deleted record. Runner: %s ([U:1:%d]) | Map: %s | Style: %s | Track: %s | Time: %.2f (%s) | Strafes: %d (%.1f%%) | Jumps: %d (%.1f%%) | Run date: %s | Record ID: %d", + client, sName, iSteamID, sMap, gS_StyleStrings[iStyle].sStyleName, sTrack, fTime, (bWRDeleted)? "WR":"not WR", iStrafes, fSync, iJumps, fPerfectJumps, sDate, iRecordID); if(client == 0) { @@ -1351,7 +1347,7 @@ Action ShowWRStyleMenu(int client, int track) strcopy(sDisplay, 64, gS_StyleStrings[iStyle].sStyleName); } - menu.AddItem(sInfo, sDisplay, (gI_RecordAmount[iStyle][track] > 0 || !StrEqual(gA_WRCache[client].sClientMap, gS_Map))? ITEMDRAW_DEFAULT:ITEMDRAW_DISABLED); + menu.AddItem(sInfo, sDisplay, (GetRecordAmount(iStyle, track) > 0 || !StrEqual(gA_WRCache[client].sClientMap, gS_Map))? ITEMDRAW_DEFAULT:ITEMDRAW_DISABLED); } // should NEVER happen @@ -1443,10 +1439,9 @@ public void SQL_WR_Callback(Database db, DBResultSet results, const char[] error return; } - char sAuth[32]; - GetClientAuthId(client, AuthId_Steam3, sAuth, 32); + int iSteamID = GetSteamAccountID(client); - Menu menu = new Menu(WRMenu_Handler); + Menu hMenu = new Menu(WRMenu_Handler); int iCount = 0; int iMyRank = 0; @@ -1474,14 +1469,13 @@ public void SQL_WR_Callback(Database db, DBResultSet results, const char[] error char sDisplay[128]; FormatEx(sDisplay, 128, "#%d - %s - %s (%d %T)", iCount, sName, sTime, jumps, "WRJumps", client); - menu.AddItem(sID, sDisplay); + hMenu.AddItem(sID, sDisplay); } // check if record exists in the map's top X - char sQueryAuth[32]; - results.FetchString(4, sQueryAuth, 32); + int iQuerySteamID = results.FetchInt(4); - if(StrEqual(sQueryAuth, sAuth)) + if(iQuerySteamID == iSteamID) { iMyRank = iCount; } @@ -1489,13 +1483,13 @@ public void SQL_WR_Callback(Database db, DBResultSet results, const char[] error char sFormattedTitle[256]; - if(menu.ItemCount == 0) + if(hMenu.ItemCount == 0) { - menu.SetTitle("%T", "WRMap", client, sMap); + hMenu.SetTitle("%T", "WRMap", client, sMap); char sNoRecords[64]; FormatEx(sNoRecords, 64, "%T", "WRMapNoRecords", client); - menu.AddItem("-1", sNoRecords); + hMenu.AddItem("-1", sNoRecords); } else @@ -1520,11 +1514,11 @@ public void SQL_WR_Callback(Database db, DBResultSet results, const char[] error GetTrackName(client, track, sTrack, 32); FormatEx(sFormattedTitle, 192, "%T %s: [%s]\n%s", "WRRecordFor", client, sMap, sTrack, sRanks); - menu.SetTitle(sFormattedTitle); + hMenu.SetTitle(sFormattedTitle); } - menu.ExitBackButton = true; - menu.Display(client, 20); + hMenu.ExitBackButton = true; + hMenu.Display(client, 20); } public int WRMenu_Handler(Menu menu, MenuAction action, int param1, int param2) @@ -1689,7 +1683,9 @@ public int RRMenu_Handler(Menu menu, MenuAction action, int param1, int param2) void OpenSubMenu(int client, int id) { char sQuery[512]; - FormatEx(sQuery, 512, "SELECT u.name, p.time, p.jumps, p.style, u.auth, p.date, p.map, p.strafes, p.sync, p.perfs, p.points, p.track FROM %splayertimes p JOIN %susers u ON p.auth = u.auth WHERE p.id = %d LIMIT 1;", gS_MySQLPrefix, gS_MySQLPrefix, id); + FormatEx(sQuery, 512, + "SELECT u.name, p.time, p.jumps, p.style, u.auth, p.date, p.map, p.strafes, p.sync, p.perfs, p.points, p.track, p.completions FROM %splayertimes p JOIN %susers u ON p.auth = u.auth WHERE p.id = %d LIMIT 1;", + gS_MySQLPrefix, gS_MySQLPrefix, id); DataPack datapack = new DataPack(); datapack.WriteCell(GetClientSerial(client)); @@ -1717,50 +1713,48 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] return; } - Menu menu = new Menu(SubMenu_Handler); + Menu hMenu = new Menu(SubMenu_Handler); char sFormattedTitle[256]; char sName[MAX_NAME_LENGTH]; - char sAuthID[32]; + int iSteamID = 0; char sTrack[32]; char sMap[192]; if(results.FetchRow()) { - // 0 - name results.FetchString(0, sName, MAX_NAME_LENGTH); - // 1 - time - float time = results.FetchFloat(1); + float fTime = results.FetchFloat(1); char sTime[16]; - FormatSeconds(time, sTime, 16); + FormatSeconds(fTime, sTime, 16); char sDisplay[128]; FormatEx(sDisplay, 128, "%T: %s", "WRTime", client, sTime); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); - // 2 - jumps - int style = results.FetchInt(3); - int jumps = results.FetchInt(2); - float perfs = results.FetchFloat(9); + int iStyle = results.FetchInt(3); + int iJumps = results.FetchInt(2); + float fPerfs = results.FetchFloat(9); - if(gA_StyleSettings[style].bAutobhop) + if(gA_StyleSettings[iStyle].bAutobhop) { - FormatEx(sDisplay, 128, "%T: %d", "WRJumps", client, jumps); + FormatEx(sDisplay, 128, "%T: %d", "WRJumps", client, iJumps); } else { - FormatEx(sDisplay, 128, "%T: %d (%.2f%%)", "WRJumps", client, jumps, perfs); + FormatEx(sDisplay, 128, "%T: %d (%.2f%%)", "WRJumps", client, iJumps, fPerfs); } - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); + + FormatEx(sDisplay, 128, "%T: %d", "WRCompletions", client, results.FetchInt(12)); + hMenu.AddItem("-1", sDisplay); - // 3 - style - FormatEx(sDisplay, 128, "%T: %s", "WRStyle", client, gS_StyleStrings[style].sStyleName); - menu.AddItem("-1", sDisplay); + FormatEx(sDisplay, 128, "%T: %s", "WRStyle", client, gS_StyleStrings[iStyle].sStyleName); + hMenu.AddItem("-1", sDisplay); - // 6 - map results.FetchString(6, sMap, 192); float fPoints = results.FetchFloat(10); @@ -1768,13 +1762,11 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] if(gB_Rankings && fPoints > 0.0) { FormatEx(sDisplay, 128, "%T: %.03f", "WRPointsCap", client, fPoints); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); } - // 4 - steamid3 - results.FetchString(4, sAuthID, 32); + iSteamID = results.FetchInt(4); - // 5 - date char sDate[32]; results.FetchString(5, sDate, 32); @@ -1784,33 +1776,33 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] } FormatEx(sDisplay, 128, "%T: %s", "WRDate", client, sDate); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); int strafes = results.FetchInt(7); float sync = results.FetchFloat(8); - if(jumps > 0 || strafes > 0) + if(iJumps > 0 || strafes > 0) { FormatEx(sDisplay, 128, (sync != -1.0)? "%T: %d (%.02f%%)":"%T: %d", "WRStrafes", client, strafes, sync); - menu.AddItem("-1", sDisplay); + hMenu.AddItem("-1", sDisplay); } char sMenuItem[64]; FormatEx(sMenuItem, 64, "%T", "WRPlayerStats", client); char sInfo[32]; - FormatEx(sInfo, 32, "0;%s", sAuthID); + FormatEx(sInfo, 32, "0;%d", iSteamID); if(gB_Stats) { - menu.AddItem(sInfo, sMenuItem); + hMenu.AddItem(sInfo, sMenuItem); } if(CheckCommandAccess(client, "sm_delete", ADMFLAG_RCON)) { FormatEx(sMenuItem, 64, "%T", "WRDeleteRecord", client); FormatEx(sInfo, 32, "1;%d", id); - menu.AddItem(sInfo, sMenuItem); + hMenu.AddItem(sInfo, sMenuItem); } GetTrackName(client, results.FetchInt(11), sTrack, 32); @@ -1820,12 +1812,12 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] { char sMenuItem[64]; FormatEx(sMenuItem, 64, "%T", "DatabaseError", client); - menu.AddItem("-1", sMenuItem); + hMenu.AddItem("-1", sMenuItem); } if(strlen(sName) > 0) { - FormatEx(sFormattedTitle, 256, "%s %s\n--- %s: [%s]", sName, sAuthID, sMap, sTrack); + FormatEx(sFormattedTitle, 256, "%s [U:1:%d]\n--- %s: [%s]", sName, iSteamID, sMap, sTrack); } else @@ -1833,9 +1825,9 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[] FormatEx(sFormattedTitle, 256, "%T", "Error", client); } - menu.SetTitle(sFormattedTitle); - menu.ExitBackButton = true; - menu.Display(client, 20); + hMenu.SetTitle(sFormattedTitle); + hMenu.ExitBackButton = true; + hMenu.Display(client, 20); } public int SubMenu_Handler(Menu menu, MenuAction action, int param1, int param2) @@ -1856,7 +1848,7 @@ public int SubMenu_Handler(Menu menu, MenuAction action, int param1, int param2) { case 0: { - Shavit_OpenStatsMenu(param1, sExploded[1]); + Shavit_OpenStatsMenu(param1, StringToInt(sExploded[1])); } case 1: @@ -1885,81 +1877,26 @@ public int SubMenu_Handler(Menu menu, MenuAction action, int param1, int param2) return 0; } -public void Shavit_OnDatabaseLoaded() -{ - gH_SQL = Shavit_GetDatabase(); - SetSQLInfo(); -} - -public Action CheckForSQLInfo(Handle Timer) -{ - return SetSQLInfo(); -} - -Action SetSQLInfo() -{ - if(gH_SQL == null) - { - gH_SQL = Shavit_GetDatabase(); - - CreateTimer(0.5, CheckForSQLInfo); - } - - else - { - SQL_DBConnect(); - - return Plugin_Stop; - } - - return Plugin_Continue; -} - -void SQL_SetPrefix() -{ - char sFile[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); - - File fFile = OpenFile(sFile, "r"); - - if(fFile == null) - { - SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); - } - - char sLine[PLATFORM_MAX_PATH*2]; - - while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2)) - { - TrimString(sLine); - strcopy(gS_MySQLPrefix, 32, sLine); - - break; - } - - delete fFile; -} - void SQL_DBConnect() { - char sDriver[8]; - gH_SQL.Driver.GetIdentifier(sDriver, 8); - gB_MySQL = StrEqual(sDriver, "mysql", false); + GetTimerSQLPrefix(gS_MySQLPrefix, 32); + gH_SQL = GetTimerDatabaseHandle(); + gB_MySQL = IsMySQLDatabase(gH_SQL); char sQuery[1024]; if(gB_MySQL) { FormatEx(sQuery, 1024, - "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INT NOT NULL AUTO_INCREMENT, `auth` VARCHAR(32), `map` VARCHAR(128), `time` FLOAT, `jumps` INT, `style` TINYINT, `date` VARCHAR(16), `strafes` INT, `sync` FLOAT, `points` FLOAT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `perfs` FLOAT DEFAULT 0, PRIMARY KEY (`id`), INDEX `map` (`map`, `style`, `track`, `time`), INDEX `auth` (`auth`, `date`, `points`), INDEX `time` (`time`), CONSTRAINT `pt_auth` FOREIGN KEY (`auth`) REFERENCES `users` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE) ENGINE=INNODB;", - gS_MySQLPrefix); + "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INT NOT NULL AUTO_INCREMENT, `auth` INT, `map` VARCHAR(128), `time` FLOAT, `jumps` INT, `style` TINYINT, `date` INT, `strafes` INT, `sync` FLOAT, `points` FLOAT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `perfs` FLOAT DEFAULT 0, `completions` SMALLINT DEFAULT 1, PRIMARY KEY (`id`), INDEX `map` (`map`, `style`, `track`, `time`), INDEX `auth` (`auth`, `date`, `points`), INDEX `time` (`time`), CONSTRAINT `%spt_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE) ENGINE=INNODB;", + gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix); } else { FormatEx(sQuery, 1024, - "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INTEGER PRIMARY KEY, `auth` VARCHAR(32), `map` VARCHAR(128), `time` FLOAT, `jumps` INT, `style` TINYINT, `date` VARCHAR(16), `strafes` INT, `sync` FLOAT, `points` FLOAT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `perfs` FLOAT DEFAULT 0);", - gS_MySQLPrefix); + "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INTEGER PRIMARY KEY, `auth` INT, `map` VARCHAR(128), `time` FLOAT, `jumps` INT, `style` TINYINT, `date` INT, `strafes` INT, `sync` FLOAT, `points` FLOAT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `perfs` FLOAT DEFAULT 0, `completions` SMALLINT DEFAULT 1, CONSTRAINT `%spt_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE);", + gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix); } gH_SQL.Query(SQL_CreateTable_Callback, sQuery); @@ -2000,11 +1937,13 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st // 0 - no query // 1 - insert // 2 - update + bool bIncrementCompletions = true; int iOverwrite = 0; if(gA_StyleSettings[style].bUnranked || Shavit_IsPracticeMode(client)) { iOverwrite = 0; // ugly way of not writing to database + bIncrementCompletions = false; } else if(gF_PlayerRecord[client][style][track] == 0.0) @@ -2042,7 +1981,7 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st int iRank = GetRankForTime(style, time, track); - if(iRank >= gI_RecordAmount[style][track]) + if(iRank >= GetRecordAmount(style, track)) { Call_StartForward(gH_OnWorstRecord); Call_PushCell(client); @@ -2070,30 +2009,28 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st char sSync[32]; // 32 because colors FormatEx(sSync, 32, (sync != -1.0)? " @ %s%.02f%%":"", gS_ChatStrings.sVariable, sync); + int iSteamID = GetSteamAccountID(client); + if(iOverwrite > 0) { - char sAuthID[32]; - GetClientAuthId(client, AuthId_Steam3, sAuthID, 32); - char sQuery[512]; if(iOverwrite == 1) // insert { Shavit_PrintToChatAll("%s[%s]%s %T", gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText, "FirstCompletion", LANG_SERVER, gS_ChatStrings.sVariable2, client, gS_ChatStrings.sText, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, sTime, gS_ChatStrings.sText, gS_ChatStrings.sVariable, iRank, gS_ChatStrings.sText, jumps, strafes, sSync, gS_ChatStrings.sText); - if(gH_SQL == null) - { - return; - } - - FormatEx(sQuery, 512, "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync, points, track, perfs) VALUES ('%s', '%s', %f, %d, %d, %d, %d, %.2f, 0.0, %d, %.2f);", gS_MySQLPrefix, sAuthID, gS_Map, time, jumps, GetTime(), style, strafes, sync, track, perfs); + FormatEx(sQuery, 512, + "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync, points, track, perfs) VALUES (%d, '%s', %f, %d, %d, %d, %d, %.2f, 0.0, %d, %.2f);", + gS_MySQLPrefix, iSteamID, gS_Map, time, jumps, GetTime(), style, strafes, sync, track, perfs); } else // update { Shavit_PrintToChatAll("%s[%s]%s %T", gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText, "NotFirstCompletion", LANG_SERVER, gS_ChatStrings.sVariable2, client, gS_ChatStrings.sText, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, sTime, gS_ChatStrings.sText, gS_ChatStrings.sVariable, iRank, gS_ChatStrings.sText, jumps, strafes, sSync, gS_ChatStrings.sText, gS_ChatStrings.sWarning, sDifference); - FormatEx(sQuery, 512, "UPDATE %splayertimes SET time = %f, jumps = %d, date = %d, strafes = %d, sync = %.02f, points = 0.0, perfs = %.2f WHERE map = '%s' AND auth = '%s' AND style = %d AND track = %d;", gS_MySQLPrefix, time, jumps, GetTime(), strafes, sync, perfs, gS_Map, sAuthID, style, track); + FormatEx(sQuery, 512, + "UPDATE %splayertimes SET time = %f, jumps = %d, date = %d, strafes = %d, sync = %.02f, points = 0.0, perfs = %.2f WHERE map = '%s' AND auth = %d AND style = %d AND track = %d;", + gS_MySQLPrefix, time, jumps, GetTime(), strafes, sync, perfs, gS_Map, iSteamID, style, track); } gH_SQL.Query(SQL_OnFinish_Callback, sQuery, GetClientSerial(client), DBPrio_High); @@ -2115,6 +2052,16 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st gF_PlayerRecord[client][style][track] = time; } + if(bIncrementCompletions) + { + char sQuery[256]; + FormatEx(sQuery, 256, + "UPDATE %splayertimes SET completions = completions + 1 WHERE map = '%s' AND auth = %d AND style = %d AND track = %d;", + gS_MySQLPrefix, gS_Map, iSteamID, style, track); + + gH_SQL.Query(SQL_OnIncrementCompletions_Callback, sQuery, 0, DBPrio_Low); + } + else if(iOverwrite == 0 && !gA_StyleSettings[style].bUnranked) { Shavit_PrintToChat(client, "%s[%s]%s %T", gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText, "WorseTime", client, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, sTime, gS_ChatStrings.sText, jumps, strafes, sSync, gS_ChatStrings.sText, sDifference); @@ -2126,6 +2073,16 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st } } +public void SQL_OnIncrementCompletions_Callback(Database db, DBResultSet results, const char[] error, any data) +{ + if(results == null) + { + LogError("Timer (WR OnIncrementCompletions) SQL query failed. Reason: %s", error); + + return; + } +} + public void SQL_OnFinish_Callback(Database db, DBResultSet results, const char[] error, any data) { if(results == null) @@ -2149,8 +2106,8 @@ public void SQL_OnFinish_Callback(Database db, DBResultSet results, const char[] void UpdateLeaderboards() { char sQuery[192]; - FormatEx(sQuery, 192, "SELECT style, time, track FROM %splayertimes WHERE map = '%s' ORDER BY time ASC, date ASC LIMIT 1000;", gS_MySQLPrefix, gS_Map); - gH_SQL.Query(SQL_UpdateLeaderboards_Callback, sQuery, 0); + FormatEx(sQuery, 192, "SELECT style, track, time FROM %splayertimes WHERE map = '%s' ORDER BY time ASC, date ASC;", gS_MySQLPrefix, gS_Map); + gH_SQL.Query(SQL_UpdateLeaderboards_Callback, sQuery); } public void SQL_UpdateLeaderboards_Callback(Database db, DBResultSet results, const char[] error, any data) @@ -2166,7 +2123,6 @@ public void SQL_UpdateLeaderboards_Callback(Database db, DBResultSet results, co { for(int j = 0; j < TRACKS_SIZE; j++) { - gI_RecordAmount[i][j] = 0; gA_Leaderboard[i][j].Clear(); } } @@ -2174,14 +2130,14 @@ public void SQL_UpdateLeaderboards_Callback(Database db, DBResultSet results, co while(results.FetchRow()) { int style = results.FetchInt(0); - int track = results.FetchInt(2); + int track = results.FetchInt(1); if(style >= gI_Styles || gA_StyleSettings[style].bUnranked || track >= TRACKS_SIZE) { continue; } - gA_Leaderboard[style][track].Push(results.FetchFloat(1)); + gA_Leaderboard[style][track].Push(results.FetchFloat(2)); } for(int i = 0; i < gI_Styles; i++) @@ -2194,21 +2150,32 @@ public void SQL_UpdateLeaderboards_Callback(Database db, DBResultSet results, co for(int j = 0; j < TRACKS_SIZE; j++) { SortADTArray(gA_Leaderboard[i][j], Sort_Ascending, Sort_Float); - gI_RecordAmount[i][j] = gA_Leaderboard[i][j].Length; } } } +int GetRecordAmount(int style, int track) +{ + if(gA_Leaderboard[style][track] == null) + { + return 0; + } + + return gA_Leaderboard[style][track].Length; +} + int GetRankForTime(int style, float time, int track) { - if(time <= gF_WRTime[style][track] || gI_RecordAmount[style][track] <= 0) + int iRecords = GetRecordAmount(style, track); + + if(time <= gF_WRTime[style][track] || iRecords <= 0) { return 1; } if(gA_Leaderboard[style][track] != null && gA_Leaderboard[style][track].Length > 0) { - for(int i = 0; i < gI_RecordAmount[style][track]; i++) + for(int i = 0; i < iRecords; i++) { if(time <= gA_Leaderboard[style][track].Get(i)) { @@ -2217,7 +2184,7 @@ int GetRankForTime(int style, float time, int track) } } - return (gI_RecordAmount[style][track] + 1); + return (iRecords + 1); } void GuessBestMapName(const char[] input, char[] output, int size) diff --git a/addons/sourcemod/scripting/shavit-zones.sp b/addons/sourcemod/scripting/shavit-zones.sp index 9959c4cee..fcfc1abc7 100644 --- a/addons/sourcemod/scripting/shavit-zones.sp +++ b/addons/sourcemod/scripting/shavit-zones.sp @@ -50,20 +50,23 @@ char gS_ZoneNames[][] = "Glitch Zone (Stop Timer)", // stops the player's timer "Slay Player", // slays (kills) players which come to this zone "Freestyle Zone", // ignores style physics when at this zone. e.g. WASD when SWing - "No Speed Limit", // ignores velocity limit in that zone + "Custom Speed Limit", // overwrites velocity limit in the zone "Teleport Zone", // teleports to a defined point "SPAWN POINT", // << unused "Easybhop Zone", // forces easybhop whether if the player is in non-easy styles or if the server has different settings - "Slide Zone" // allows players to slide, in order to fix parts like the 5th stage of bhop_arcane + "Slide Zone", // allows players to slide, in order to fix parts like the 5th stage of bhop_arcane + "Custom Airaccelerate" // custom sv_airaccelerate inside this }; enum struct zone_cache_t { bool bZoneInitialized; int iZoneType; - int iZoneTrack; // 0 - main, 1 - bonus + int iZoneTrack; // 0 - main, 1 - bonus etc int iEntityID; int iDatabaseID; + int iZoneFlags; + int iZoneData; } enum struct zone_settings_t @@ -77,6 +80,11 @@ enum struct zone_settings_t bool bFlatZone; } +enum +{ + ZF_ForceRender = (1 << 0) +}; + int gI_ZoneType[MAXPLAYERS+1]; // 0 - nothing @@ -89,6 +97,9 @@ float gF_Modifier[MAXPLAYERS+1]; int gI_GridSnap[MAXPLAYERS+1]; bool gB_SnapToWall[MAXPLAYERS+1]; bool gB_CursorTracing[MAXPLAYERS+1]; +int gI_ZoneFlags[MAXPLAYERS+1]; +int gI_ZoneData[MAXPLAYERS+1]; +bool gB_WaitingForChatInput[MAXPLAYERS+1]; // cache float gV_Point1[MAXPLAYERS+1][3]; @@ -164,7 +175,10 @@ public Plugin myinfo = public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { // zone natives + CreateNative("Shavit_GetZoneData", Native_GetZoneData); + CreateNative("Shavit_GetZoneFlags", Native_GetZoneFlags); CreateNative("Shavit_InsideZone", Native_InsideZone); + CreateNative("Shavit_InsideZoneGetID", Native_InsideZoneGetID); CreateNative("Shavit_IsClientCreatingZone", Native_IsClientCreatingZone); CreateNative("Shavit_ZoneExists", Native_ZoneExists); CreateNative("Shavit_Zones_DeleteMap", Native_Zones_DeleteMap); @@ -177,14 +191,6 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max return APLRes_Success; } -public void OnAllPluginsLoaded() -{ - if(gH_SQL == null) - { - Shavit_OnDatabaseLoaded(); - } -} - public void OnPluginStart() { LoadTranslations("shavit-common.phrases"); @@ -274,7 +280,7 @@ public void OnPluginStart() } } - SQL_SetPrefix(); + SQL_DBConnect(); } public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) @@ -413,11 +419,42 @@ public int Native_ZoneExists(Handle handler, int numParams) return (GetZoneIndex(GetNativeCell(1), GetNativeCell(2)) != -1); } +public int Native_GetZoneData(Handle handler, int numParams) +{ + return gA_ZoneCache[GetNativeCell(1)].iZoneData; +} + +public int Native_GetZoneFlags(Handle handler, int numParams) +{ + return gA_ZoneCache[GetNativeCell(1)].iZoneFlags; +} + public int Native_InsideZone(Handle handler, int numParams) { return InsideZone(GetNativeCell(1), GetNativeCell(2), GetNativeCell(3)); } +public int Native_InsideZoneGetID(Handle handler, int numParams) +{ + int client = GetNativeCell(1); + int iType = GetNativeCell(2); + int iTrack = GetNativeCell(3); + + for(int i = 0; i < MAX_ZONES; i++) + { + if(gB_InsideZoneID[client][i] && + gA_ZoneCache[i].iZoneType == iType && + (gA_ZoneCache[i].iZoneTrack == iTrack || iTrack == -1)) + { + SetNativeCellRef(4, i); + + return true; + } + } + + return false; +} + public int Native_Zones_DeleteMap(Handle handler, int numParams) { char sMap[160]; @@ -581,7 +618,7 @@ void LoadZoneSettings() public void OnMapStart() { - if(gH_SQL == null || !gB_Connected) + if(!gB_Connected) { return; } @@ -767,6 +804,8 @@ void ClearZone(int index) gA_ZoneCache[index].iZoneTrack = -1; gA_ZoneCache[index].iEntityID = -1; gA_ZoneCache[index].iDatabaseID = -1; + gA_ZoneCache[index].iZoneFlags = 0; + gA_ZoneCache[index].iZoneData = 0; } void UnhookEntity(int entity) @@ -851,7 +890,9 @@ void UnloadZones(int zone) void RefreshZones() { char sQuery[512]; - FormatEx(sQuery, 512, "SELECT type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track, %s FROM %smapzones WHERE map = '%s';", (gB_MySQL)? "id":"rowid", gS_MySQLPrefix, gS_Map); + FormatEx(sQuery, 512, + "SELECT type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track, %s, flags, data FROM %smapzones WHERE map = '%s';", + (gB_MySQL)? "id":"rowid", gS_MySQLPrefix, gS_Map); gH_SQL.Query(SQL_RefreshZones_Callback, sQuery, 0, DBPrio_High); } @@ -906,6 +947,8 @@ public void SQL_RefreshZones_Callback(Database db, DBResultSet results, const ch gA_ZoneCache[gI_MapZones].iZoneType = type; gA_ZoneCache[gI_MapZones].iZoneTrack = results.FetchInt(10); gA_ZoneCache[gI_MapZones].iDatabaseID = results.FetchInt(11); + gA_ZoneCache[gI_MapZones].iZoneFlags = results.FetchInt(12); + gA_ZoneCache[gI_MapZones].iZoneData = results.FetchInt(13); gA_ZoneCache[gI_MapZones].iEntityID = -1; gI_MapZones++; @@ -1227,7 +1270,7 @@ public int MenuHandler_SelectZoneTrack(Menu menu, MenuAction action, int param1, GetTrackName(param1, gI_ZoneTrack[param1], sTrack, 16); Menu submenu = new Menu(MenuHandler_SelectZoneType); - submenu.SetTitle("%T", "ZoneMenuTitle", param1, sTrack); + submenu.SetTitle("%T\n ", "ZoneMenuTitle", param1, sTrack); for(int i = 0; i < sizeof(gS_ZoneNames); i++) { @@ -1327,6 +1370,8 @@ public int MenuHandler_ZoneEdit(Menu menu, MenuAction action, int param1, int pa gI_ZoneTrack[param1] = gA_ZoneCache[id].iZoneTrack; gV_Teleport[param1] = gV_Destinations[id]; gI_ZoneDatabaseID[param1] = gA_ZoneCache[id].iDatabaseID; + gI_ZoneFlags[param1] = gA_ZoneCache[id].iZoneFlags; + gI_ZoneData[param1] = gA_ZoneCache[id].iZoneData; // to stop the original zone from drawing gA_ZoneCache[id].bZoneInitialized = false; @@ -1581,7 +1626,10 @@ void Reset(int client) gI_GridSnap[client] = 16; gB_SnapToWall[client] = false; gB_CursorTracing[client] = true; + gI_ZoneFlags[client] = 0; + gI_ZoneData[client] = 0; gI_ZoneDatabaseID[client] = -1; + gB_WaitingForChatInput[client] = false; for(int i = 0; i < 3; i++) { @@ -1887,30 +1935,52 @@ public int CreateZoneConfirm_Handler(Menu menu, MenuAction action, int param1, i { if(action == MenuAction_Select) { - char info[8]; - menu.GetItem(param2, info, 8); + char sInfo[16]; + menu.GetItem(param2, sInfo, 16); - if(StrEqual(info, "yes")) + if(StrEqual(sInfo, "yes")) { InsertZone(param1); gI_MapStep[param1] = 0; + + return 0; } - else if(StrEqual(info, "no")) + else if(StrEqual(sInfo, "no")) { Reset(param1); + + return 0; } - else if(StrEqual(info, "adjust")) + else if(StrEqual(sInfo, "adjust")) { CreateAdjustMenu(param1, 0); + + return 0; } - else if(StrEqual(info, "tpzone")) + else if(StrEqual(sInfo, "tpzone")) { UpdateTeleportZone(param1); - CreateEditMenu(param1); } + + else if(StrEqual(sInfo, "datafromchat")) + { + gI_ZoneData[param1] = 0; + gB_WaitingForChatInput[param1] = true; + + Shavit_PrintToChat(param1, "%T", "ZoneEnterDataChat", param1); + + return 0; + } + + else if(StrEqual(sInfo, "forcerender")) + { + gI_ZoneFlags[param1] ^= ZF_ForceRender; + } + + CreateEditMenu(param1); } else if(action == MenuAction_End) @@ -1921,6 +1991,19 @@ public int CreateZoneConfirm_Handler(Menu menu, MenuAction action, int param1, i return 0; } +public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) +{ + if(gB_WaitingForChatInput[client] && gI_MapStep[client] == 3) + { + gI_ZoneData[client] = StringToInt(sArgs); + CreateEditMenu(client); + + return Plugin_Handled; + } + + return Plugin_Continue; +} + void GetTrackName(int client, int track, char[] output, int size) { if(track < 0 || track >= TRACKS_SIZE) @@ -1969,7 +2052,7 @@ void CreateEditMenu(int client) char sTrack[32]; GetTrackName(client, gI_ZoneTrack[client], sTrack, 32); - Menu menu = new Menu(CreateZoneConfirm_Handler, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem); + Menu menu = new Menu(CreateZoneConfirm_Handler); menu.SetTitle("%T\n%T\n ", "ZoneEditConfirm", client, "ZoneEditTrack", client, sTrack); char sMenuItem[64]; @@ -2004,6 +2087,30 @@ void CreateEditMenu(int client) FormatEx(sMenuItem, 64, "%T", "ZoneSetAdjust", client); menu.AddItem("adjust", sMenuItem); + FormatEx(sMenuItem, 64, "%T", "ZoneForceRender", client, ((gI_ZoneFlags[client] & ZF_ForceRender) > 0)? "+":"-"); + menu.AddItem("forcerender", sMenuItem); + + if(gI_ZoneType[client] == Zone_Airaccelerate) + { + FormatEx(sMenuItem, 64, "%T", "ZoneSetAiraccelerate", client, gI_ZoneData[client]); + menu.AddItem("datafromchat", sMenuItem); + } + + else if(gI_ZoneType[client] == Zone_CustomSpeedLimit) + { + if(gI_ZoneData[client] == 0) + { + FormatEx(sMenuItem, 64, "%T", "ZoneSetSpeedLimitUnlimited", client, gI_ZoneData[client]); + } + + else + { + FormatEx(sMenuItem, 64, "%T", "ZoneSetSpeedLimit", client, gI_ZoneData[client]); + } + + menu.AddItem("datafromchat", sMenuItem); + } + menu.ExitButton = true; menu.Display(client, 600); } @@ -2088,13 +2195,13 @@ public int ZoneAdjuster_Handler(Menu menu, MenuAction action, int param1, int pa void InsertZone(int client) { - int type = gI_ZoneType[client]; - int index = GetZoneIndex(type, gI_ZoneTrack[client]); - bool insert = (gI_ZoneDatabaseID[client] == -1 && (index == -1 || type >= Zone_Respawn)); + int iType = gI_ZoneType[client]; + int iIndex = GetZoneIndex(iType, gI_ZoneTrack[client]); + bool bInsert = (gI_ZoneDatabaseID[client] == -1 && (iIndex == -1 || iType >= Zone_Respawn)); char sQuery[512]; - if(type == Zone_CustomSpawn) + if(iType == Zone_CustomSpawn) { Shavit_LogMessage("%L - added custom spawn {%.2f, %.2f, %.2f} to map `%s`.", client, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gS_Map); @@ -2103,24 +2210,24 @@ void InsertZone(int client) gS_MySQLPrefix, gS_Map, Zone_CustomSpawn, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gI_ZoneTrack[client]); } - else if(insert) // insert + else if(bInsert) // insert { - Shavit_LogMessage("%L - added %s to map `%s`.", client, gS_ZoneNames[type], gS_Map); + Shavit_LogMessage("%L - added %s to map `%s`.", client, gS_ZoneNames[iType], gS_Map); FormatEx(sQuery, 512, - "INSERT INTO %smapzones (map, type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', %d);", - gS_MySQLPrefix, gS_Map, type, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gV_Teleport[client][0], gV_Teleport[client][1], gV_Teleport[client][2], gI_ZoneTrack[client]); + "INSERT INTO %smapzones (map, type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track, flags, data) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', %d, %d, %d);", + gS_MySQLPrefix, gS_Map, iType, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gV_Teleport[client][0], gV_Teleport[client][1], gV_Teleport[client][2], gI_ZoneTrack[client], gI_ZoneFlags[client], gI_ZoneData[client]); } else // update { - Shavit_LogMessage("%L - updated %s in map `%s`.", client, gS_ZoneNames[type], gS_Map); + Shavit_LogMessage("%L - updated %s in map `%s`.", client, gS_ZoneNames[iType], gS_Map); if(gI_ZoneDatabaseID[client] == -1) { for(int i = 0; i < gI_MapZones; i++) { - if(gA_ZoneCache[i].bZoneInitialized && gA_ZoneCache[i].iZoneType == type && gA_ZoneCache[i].iZoneTrack == gI_ZoneTrack[client]) + if(gA_ZoneCache[i].bZoneInitialized && gA_ZoneCache[i].iZoneType == iType && gA_ZoneCache[i].iZoneTrack == gI_ZoneTrack[client]) { gI_ZoneDatabaseID[client] = gA_ZoneCache[i].iDatabaseID; } @@ -2128,8 +2235,8 @@ void InsertZone(int client) } FormatEx(sQuery, 512, - "UPDATE %smapzones SET corner1_x = '%.03f', corner1_y = '%.03f', corner1_z = '%.03f', corner2_x = '%.03f', corner2_y = '%.03f', corner2_z = '%.03f', destination_x = '%.03f', destination_y = '%.03f', destination_z = '%.03f', track = %d WHERE %s = %d;", - gS_MySQLPrefix, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gV_Teleport[client][0], gV_Teleport[client][1], gV_Teleport[client][2], gI_ZoneTrack[client], (gB_MySQL)? "id":"rowid", gI_ZoneDatabaseID[client]); + "UPDATE %smapzones SET corner1_x = '%.03f', corner1_y = '%.03f', corner1_z = '%.03f', corner2_x = '%.03f', corner2_y = '%.03f', corner2_z = '%.03f', destination_x = '%.03f', destination_y = '%.03f', destination_z = '%.03f', track = %d, flags = %d, data = %d WHERE %s = %d;", + gS_MySQLPrefix, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gV_Teleport[client][0], gV_Teleport[client][1], gV_Teleport[client][2], gI_ZoneTrack[client], gI_ZoneFlags[client], gI_ZoneData[client], (gB_MySQL)? "id":"rowid", gI_ZoneDatabaseID[client]); } gH_SQL.Query(SQL_InsertZone_Callback, sQuery, GetClientSerial(client)); @@ -2183,7 +2290,7 @@ public Action Timer_DrawEverything(Handle Timer) int type = gA_ZoneCache[i].iZoneType; int track = gA_ZoneCache[i].iZoneTrack; - if(gA_ZoneSettings[type][track].bVisible) + if(gA_ZoneSettings[type][track].bVisible || (gA_ZoneCache[i].iZoneFlags & ZF_ForceRender) > 0) { DrawZone(gV_MapZones_Visual[i], GetZoneColors(type, track), @@ -2387,74 +2494,18 @@ void CreateZonePoints(float point[8][3], float offset = 0.0) } } -public void Shavit_OnDatabaseLoaded() -{ - gH_SQL = Shavit_GetDatabase(); - SetSQLInfo(); -} - -public Action CheckForSQLInfo(Handle Timer) -{ - return SetSQLInfo(); -} - -Action SetSQLInfo() -{ - if(gH_SQL == null) - { - gH_SQL = Shavit_GetDatabase(); - - CreateTimer(0.5, CheckForSQLInfo); - } - - else - { - SQL_DBConnect(); - - return Plugin_Stop; - } - - return Plugin_Continue; -} - -void SQL_SetPrefix() -{ - char sFile[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt"); - - File fFile = OpenFile(sFile, "r"); - - if(fFile == null) - { - SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it."); - } - - char sLine[PLATFORM_MAX_PATH*2]; - - while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2)) - { - TrimString(sLine); - strcopy(gS_MySQLPrefix, 32, sLine); - - break; - } - - delete fFile; -} - void SQL_DBConnect() { - if(gH_SQL != null) - { - char sDriver[8]; - gH_SQL.Driver.GetIdentifier(sDriver, 8); - gB_MySQL = StrEqual(sDriver, "mysql", false); + GetTimerSQLPrefix(gS_MySQLPrefix, 32); + gH_SQL = GetTimerDatabaseHandle(); + gB_MySQL = IsMySQLDatabase(gH_SQL); - char sQuery[1024]; - FormatEx(sQuery, 1024, "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INT AUTO_INCREMENT, `map` VARCHAR(128), `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, PRIMARY KEY (`id`))%s;", gS_MySQLPrefix, (gB_MySQL)? " ENGINE=INNODB":""); + char sQuery[1024]; + FormatEx(sQuery, 1024, + "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INT AUTO_INCREMENT, `map` VARCHAR(128), `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, `flags` INT NOT NULL DEFAULT 0, `data` INT NOT NULL DEFAULT 0, PRIMARY KEY (`id`))%s;", + gS_MySQLPrefix, (gB_MySQL)? " ENGINE=INNODB":""); - gH_SQL.Query(SQL_CreateTable_Callback, sQuery); - } + gH_SQL.Query(SQL_CreateTable_Callback, sQuery); } public void SQL_CreateTable_Callback(Database db, DBResultSet results, const char[] error, any data) diff --git a/addons/sourcemod/translations/shavit-hud.phrases.txt b/addons/sourcemod/translations/shavit-hud.phrases.txt index cc53345f8..4083e61f0 100644 --- a/addons/sourcemod/translations/shavit-hud.phrases.txt +++ b/addons/sourcemod/translations/shavit-hud.phrases.txt @@ -81,7 +81,12 @@ } "HudNoSpeedLimit" { - "en" "No Speed Limit" + "en" "(No Speed Limit)" + } + "HudCustomSpeedLimit" + { + "#format" "{1:d}" + "en" "(Speed Limit: {1})" } // ---------- Menus ---------- // "HUDMenuTitle" diff --git a/addons/sourcemod/translations/shavit-wr.phrases.txt b/addons/sourcemod/translations/shavit-wr.phrases.txt index 9739f3399..81945de9a 100644 --- a/addons/sourcemod/translations/shavit-wr.phrases.txt +++ b/addons/sourcemod/translations/shavit-wr.phrases.txt @@ -181,6 +181,10 @@ { "en" "Strafes" } + "WRCompletions" + { + "en" "Completions" + } "WRStyle" { "en" "Style" diff --git a/addons/sourcemod/translations/shavit-zones.phrases.txt b/addons/sourcemod/translations/shavit-zones.phrases.txt index 3c43ce515..dc4e291b5 100644 --- a/addons/sourcemod/translations/shavit-zones.phrases.txt +++ b/addons/sourcemod/translations/shavit-zones.phrases.txt @@ -179,6 +179,30 @@ { "en" "Adjust position" } + "ZoneForceRender" + { + "#format" "{1:s}" + "en" "[{1}] Force zone drawing" + } + "ZoneSetAiraccelerate" + { + "#format" "{1:d}" + "en" "Airaccelerate: {1}" + } + "ZoneSetSpeedLimit" + { + "#format" "{1:d}" + "en" "Custom speed limit: {1}" + } + "ZoneSetSpeedLimitUnlimited" + { + "#format" "{1:d}" + "en" "Custom speed limit: {1} (No Limit)" + } + "ZoneEnterDataChat" + { + "en" "Input your desired data in chat." + } "ZoneSetNo" { "en" "No"