#pragma semicolon 1 #define DEBUG #define PLUGIN_AUTHOR "jenz" #define PLUGIN_VERSION "1.9" #define g_dLength 400 #define g_dIndex 65 #include #include #include #include #include #include #include #include #include #include #include #pragma newdecls required char g_cMapname[g_dLength]; char g_cSpecialMapStart[g_dLength]; char g_cSpecialMapEnd[g_dLength]; static char g_sConfigzones[PLATFORM_MAX_PATH]; float g_fStartTime[MAXPLAYERS + 1]; char g_csTime_record[MAXPLAYERS + 1][65]; char g_csSID[MAXPLAYERS + 1][65]; float g_fClientVectors[MAXPLAYERS + 1][3]; float g_fClient_End_time[MAXPLAYERS + 1]; int g_iClientFrames[MAXPLAYERS + 1]; int g_iClientSpeedInterval[MAXPLAYERS + 1]; int g_iClientChecking[MAXPLAYERS + 1]; bool g_bClientsIgnoring[MAXPLAYERS + 1]; bool g_bDisplaySpecial; bool g_bHumansAllowedTime[MAXPLAYERS + 1]; bool g_bhumans_finished[MAXPLAYERS + 1]; bool g_bHideTimer[MAXPLAYERS + 1]; bool g_bEventBool = false; bool g_bClient_allowed_to_leave_again[MAXPLAYERS + 1]; Handle g_hClientCookie = INVALID_HANDLE; Database g_dDatabase; Handle hText; int player_stage[MAXPLAYERS + 1]; Handle g_hAlterTableTimer = null; public Plugin myinfo = { name = "UNLOZE_racetimer_css", author = PLUGIN_AUTHOR, description = "tracks times on race maps on ze", version = PLUGIN_VERSION, url = "www.unloze.com" }; //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnPluginStart() { //cmds RegConsoleCmd("sm_toptime", cmd_timerCheckTop, "checking top 10"); RegConsoleCmd("sm_ignoretimer", cmd_timerIgnoreTime, "All racetimer will be ignored for you"); RegConsoleCmd("sm_ignoretime", cmd_timerIgnoreTime, "All racetimer will be ignored for you"); RegConsoleCmd("sm_mytime", cmd_timerCheckSelf, "checking your personal time"); RegConsoleCmd("sm_stages", cmd_timerCheckStage, "Checking race stages"); RegConsoleCmd("sm_hidetimer", cmd_hideTimerHUD, "Hides timer HUD"); RegAdminCmd("sm_cleantime", Cmd_timeReset, ADMFLAG_GENERIC); //hooks HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); HookEntityOutput("trigger_multiple", "OnTrigger", Trigger_Multiple); HookEntityOutput("trigger_multiple", "OnStartTouch", Trigger_Multiple); HookEntityOutput("trigger_teleport", "OnTrigger", trigger_teleport); HookEntityOutput("trigger_teleport", "OnStartTouch", trigger_teleport); //DB if (!g_dDatabase) { Database.Connect(SQL_OnDatabaseConnect, "racetimercss"); } //HUD hText = CreateHudSynchronizer(); //Just constantly reruns the alter table query to handle new zones, less lazy solution would just be adding a forward for when zones were renamed g_hAlterTableTimer = CreateTimer(10.00, Timer_alter_tables, _, TIMER_REPEAT); //cookies g_hClientCookie = RegClientCookie("hide_timer_cookie", "Hides the timer HUD", CookieAccess_Private); for (int i = MaxClients; i > 0; --i) { if (!AreClientCookiesCached(i)) continue; OnClientCookiesCached(i); } g_bDisplaySpecial = unloze_gBSpecialMapDisplay(); } public Action allow_leaving_again(Handle hTimer, int Serial) { int client; if ((client = GetClientFromSerial(Serial)) == 0) { return Plugin_Handled; } if (IsValidClient(client)) { g_bClient_allowed_to_leave_again[client] = true; } return Plugin_Handled; } public Action Timer_alter_tables(Handle hTimer) { if (g_dDatabase) { startTimer(); } return Plugin_Handled; } public void trigger_teleport(const char[] output, int entity_index, int client, float delay) { if (IsValidEdict(entity_index) && IsValidClient(client) && g_bHumansAllowedTime[client]) { //if its surf maps there most likely are needed teleports if (StrContains(g_cMapname, "surf", false) == -1) { g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; resetClientVectors(client); g_bClient_allowed_to_leave_again[client] = false; PrintToChat(client, "Disabled timer due to potential teleport abuse, type 1."); CreateTimer(1.0, allow_leaving_again, GetClientSerial(client)); } } } public void Trigger_Multiple(const char[] output, int entity_index, int client, float delay) { if (IsValidEdict(entity_index) && origin_command_check(entity_index) && IsValidClient(client) && g_bHumansAllowedTime[client]) { if (StrContains(g_cMapname, "surf", false) == -1) { g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; resetClientVectors(client); g_bClient_allowed_to_leave_again[client] = false; PrintToChat(client, "Disabled timer due to potential teleport abuse, type 1."); CreateTimer(1.0, allow_leaving_again, GetClientSerial(client)); } } } public bool origin_command_check(int entity_index) { int count_trigger = GetOutputCount(entity_index, "m_OnTrigger"); int count_starttouch = GetOutputCount(entity_index, "m_OnStartTouch"); for (int i = 0; i < count_trigger; i++) { char buffer[g_dLength]; GetOutputParameter(entity_index, "m_OnTrigger", i, buffer, sizeof(buffer)); if (StrContains(buffer, "origin", true) != -1) return true; } for(int i = 0; i < count_starttouch; i++) { char buffer[g_dLength]; GetOutputParameter(entity_index, "m_OnStartTouch", i, buffer, sizeof(buffer)); if (StrContains(buffer, "origin", true) != -1) return true; } return false; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void SQL_OnDatabaseConnect(Database db, const char[] error, any data) { if(!db || strlen(error)) { LogError("Database error: %s", error); return; } g_dDatabase = db; //create tables char sQuery[g_dLength]; Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `zetimer_table_new` (`steam_auth` VARCHAR(254) NOT NULL, `name` VARCHAR(254) NOT NULL, PRIMARY KEY (`steam_auth`))"); g_dDatabase.Query(SQL_OnConnectFinished, sQuery, _, DBPrio_Normal); } public void SQL_OnConnectFinished(Database db, DBResultSet results, const char[] error, any data) { if(!db || strlen(error)) { delete results; LogError("Database error: %s", error); return; } static Handle hHostName; if((hHostName = FindConVar("hostname")) == INVALID_HANDLE) { delete results; return; } char line[g_dLength]; GetConVarString(hHostName, line, sizeof(line)); if (StrContains(line, "EVENT", false) > -1) g_bEventBool = true; else g_bEventBool = false; g_bDisplaySpecial = unloze_gBSpecialMapDisplay(); GetCurrentMap(g_cMapname, sizeof(g_cMapname)); startTimer(); for(int i = 1; i <= MaxClients; i++) if (IsValidClient(i)) OnClientPostAdminCheck(i); AddBinarySearchIndex(); delete results; } public void MYSQLCheckMapEntry() { int l_iRaceCount; int l_iZoneCount = unloze_zoneCount(); char sQuery[g_dLength]; if (l_iZoneCount == 1) { Format(sQuery, sizeof(sQuery), "ALTER TABLE `zetimer_table_new` ADD COLUMN IF NOT EXISTS `%s` REAL DEFAULT 0.000 NOT NULL", g_cMapname); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); g_dDatabase.Query(SQL_FinishedQuery, sQuery, hDataPack, DBPrio_Normal); } else { for (int iterator = 0; iterator <= l_iZoneCount; iterator++) { char zoneIndexName[512]; ZoneNameBasedOnIndex(iterator, zoneIndexName); if (StrContains(zoneIndexName, "ZONE_PREFIX_RACE") > -1) { l_iRaceCount++; Format(sQuery, sizeof(sQuery), "ALTER TABLE `zetimer_table_new` ADD COLUMN IF NOT EXISTS `%sS%i` REAL DEFAULT 0.000 NOT NULL", g_cMapname, l_iRaceCount); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); g_dDatabase.Query(SQL_FinishedQuery, sQuery, hDataPack, DBPrio_Normal); } } } } public void SQL_FinishedQuery(Database db, DBResultSet results, const char[] error, DataPack data) { delete results; if (!db || strlen(error)) { char sQuery[g_dLength]; ResetPack(data); data.ReadString(sQuery, sizeof(sQuery)); LogError("Query error 3: %s", error); LogError("actual query: %s", sQuery); } delete data; } //a mysql table can max have 64 keys attached to it without recompiling. Therefore dropping and attaching binary tree index on the given map start //also check that racezones actually exist before making a binary tree index //2025: i eventually want to remake most of the race timer but i dont really have time. therefore i am changing this // with the simple assumption that the plugin only needs to index columns on main ze (port 27015) i will just skip deleting and creating indexes //for dev and ze2 server. public void AddBinarySearchIndex() { int i_port = GetConVarInt(FindConVar("hostport")); //only do stuff on ze if (i_port != 27015) { return; } int race_zone_count = GetTotalRaceZones(); int l_iZoneCount = unloze_zoneCount(); //need this to check if its one zone or actually two zones with a ZONE_PREFIX_RACE and ZONE_PREFIX_START char sQuery[g_dLength]; GetCurrentMap(g_cMapname, sizeof(g_cMapname)); //LogError("race_zone_count: %i. map: %s. l_iZoneCount: %i", race_zone_count, g_cMapname, l_iZoneCount); //if admins dont make dumb random zones without any meaning it works fine probably(?) if (race_zone_count == 1 && l_iZoneCount == 1) //there is a ZONE_PREFIX_RACE and its also the only zone on the map. { Format(sQuery, sizeof(sQuery), "ALTER TABLE `zetimer_table_new` add INDEX if not exists `%s` (`%s`)", g_cMapname, g_cMapname); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); //LogError("Create index query: %s", sQuery); g_dDatabase.Query(SQL_FinishedQuery, sQuery, hDataPack, DBPrio_High); } else if (race_zone_count > 0) //finally fixing the false created indexes. { for (int i = 1; i <= race_zone_count; i++) { Format(sQuery, sizeof(sQuery), "ALTER TABLE `zetimer_table_new` add INDEX if not exists `%sS%i` (`%sS%i`)", g_cMapname, i, g_cMapname, i); //LogError("Create index query iteration: %s", sQuery); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); g_dDatabase.Query(SQL_FinishedQuery, sQuery, hDataPack, DBPrio_High); } } } //2025 edit: as said above its not great, will be kept however until the eventual remake of the racetimer. public void RemoveBinarySearchIndex() { int i_port = GetConVarInt(FindConVar("hostport")); //only do stuff on ze if (i_port != 27015) { return; } //lazy change, just take all indexes from the table that are not the primary key. its what we care about. char sQuery[g_dLength]; Format(sQuery, sizeof(sQuery), "SELECT `INDEX_NAME` FROM `information_schema`.`STATISTICS` WHERE `TABLE_SCHEMA` = 'unloze_racetimer_css' AND `TABLE_NAME` = 'zetimer_table_new' AND `INDEX_NAME` != 'PRIMARY'"); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); g_dDatabase.Query(SQL_GetAllIndexes, sQuery, hDataPack, DBPrio_Normal); } public void SQL_GetAllIndexes(Database db, DBResultSet results, const char[] error, DataPack data) { ResetPack(data); if (!db || strlen(error)) { char sQuery[g_dLength]; data.ReadString(sQuery, sizeof(sQuery)); LogError("Query error in SQL_GetAllIndexes: %s", error); LogError("actual query: %s", sQuery); delete data; delete results; return; } delete data; while (results.RowCount && results.FetchRow()) { char index_name[256]; results.FetchString(0, index_name, sizeof(index_name)); //LogError("here mapname: %s", index_name); //just drop the indexes char sQuery[g_dLength]; Format(sQuery, sizeof(sQuery), "ALTER TABLE `zetimer_table_new` drop INDEX if exists `%s`", index_name); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); g_dDatabase.Query(SQL_FinishedQuery, sQuery, hDataPack, DBPrio_Normal); } delete results; } public void OnMapEnd() { if (g_dDatabase) RemoveBinarySearchIndex(); } public void OnMapStart() { if (!g_dDatabase) Database.Connect(SQL_OnDatabaseConnect, "racetimercss"); else { AddBinarySearchIndex(); static Handle hHostName; if((hHostName = FindConVar("hostname")) == INVALID_HANDLE) return; char line[g_dLength]; GetConVarString(hHostName, line, sizeof(line)); if (StrContains(line, "EVENT", false) > -1) g_bEventBool = true; else g_bEventBool = false; g_bDisplaySpecial = unloze_gBSpecialMapDisplay(); GetCurrentMap(g_cMapname, sizeof(g_cMapname)); } Format(g_cSpecialMapStart, sizeof(g_cSpecialMapStart), ""); Format(g_cSpecialMapEnd, sizeof(g_cSpecialMapEnd), ""); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnPluginEnd() { CloseHandle(hText); if (g_hAlterTableTimer != null) delete g_hAlterTableTimer; RemoveBinarySearchIndex(); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void startTimer() { char line[g_dLength]; Handle zonefile = INVALID_HANDLE; BuildPath(Path_SM, g_sConfigzones, sizeof(g_sConfigzones), "configs/unloze_zones/%s.zones.txt", g_cMapname); zonefile = OpenFile(g_sConfigzones, "r"); if (zonefile != INVALID_HANDLE) { while (!IsEndOfFile(zonefile) && ReadFileLine(zonefile, line, sizeof(line))) { if (StrContains(line, "ZONE_PREFIX_RACE", false) > -1 || g_bDisplaySpecial) { MYSQLCheckMapEntry(); break; } } } delete zonefile; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast) { Format(g_cSpecialMapStart, sizeof(g_cSpecialMapStart), ""); Format(g_cSpecialMapEnd, sizeof(g_cSpecialMapEnd), ""); int race_zone_count = GetTotalRaceZones(); int l_iZoneCount = unloze_zoneCount(); if (!race_zone_count) return; for (int i = 1; i <= MaxClients; i++) if (IsValidClient(i) && !IsFakeClient(i)) { g_bhumans_finished[i] = false; g_fClient_End_time[i] = 0.0; resetClientVectors(i); char first_zone[512]; ZoneNameBasedOnIndex(0, first_zone); g_bClient_allowed_to_leave_again[i] = true; g_bHumansAllowedTime[i] = false; if (l_iZoneCount == 1 && race_zone_count == 1) { g_bHumansAllowedTime[i] = true; PrintToChat(i, "Started timer: There is only a finish zone"); g_fStartTime[i] = GetEngineTime(); mysql_get_player_time(i, 0); } } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnClientPostAdminCheck(int client) { GetClientAuthId(client, AuthId_Steam2, g_csSID[client], sizeof(g_csSID[])); resetClient(client); insertPlayerMYSQL(client); client_ignoring_racetimer(client); } public void client_ignoring_racetimer(int client) { char query[g_dLength]; Format(query, sizeof(query), "SELECT is_ignoring FROM `zetimer_table_new_ignoring` where steam_auth = '%s'", g_csSID[client]); g_dDatabase.Query(SQL_OnQueryCompleted_ignoring, query, GetClientSerial(client)); } public void SQL_OnQueryCompleted_ignoring(Database db, DBResultSet results, const char[] error, int Serial) { if (!db) { delete results; return; } int client; if ((client = GetClientFromSerial(Serial)) == 0) { delete results; return; } if (!IsValidClient(client)) { delete results; return; } int val = 0; if (results.RowCount && results.FetchRow()) val = results.FetchInt(0); g_bClientsIgnoring[client] = false; if (val == 1) { g_bClientsIgnoring[client] = true; } delete results; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnClientCookiesCached(int client) { char sValue[8]; GetClientCookie(client, g_hClientCookie, sValue, sizeof(sValue)); g_bHideTimer[client] = (sValue[0] != '\0' && !!StringToInt(sValue)); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnClientDisconnect(int client) { Format(g_csSID[client], sizeof(g_csSID[]), ""); resetClient(client); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void resetClient(int client) { if (0 < client <= MaxClients) { g_iClientChecking[client] = 0; g_bClientsIgnoring[client] = false; g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; g_fClient_End_time[client] = 0.0; g_bClient_allowed_to_leave_again[client] = false; resetClientVectors(client); g_fStartTime[client] = 0.0; player_stage[client] = 0; Format(g_csTime_record[client], sizeof(g_csTime_record[]), "0.000"); } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float vel[3], const float angles[3], int weapon, int subtype, int cmdnum, int tickcount, int seed, const int mouse[2]) { if (!IsValidClient(client)) return; if (!g_bHumansAllowedTime[client]) return; //checks if event is going on if (g_bEventBool) { g_bHumansAllowedTime[client] = false; PrintToChat(client, "Disabling timer due to map being marked as event map."); return; } if (!IsPlayerAlive(client)) { g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; resetClientVectors(client); PrintToChat(client, "Disabled timer due to dying."); return; } if (GetClientTeam(client) == CS_TEAM_CT) { float clientVectors[3]; GetClientAbsOrigin(client, clientVectors); if (checkClientOrigin(g_fClientVectors[client], clientVectors, client)) { g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; resetClientVectors(client); g_bClient_allowed_to_leave_again[client] = false; PrintToChat(client, "Disabled timer due to potential teleport abuse, type 2."); CreateTimer(1.0, allow_leaving_again, GetClientSerial(client)); return; } int frameCap = 11; if (g_bhumans_finished[client]) g_iClientFrames[client] = 11; if (g_iClientFrames[client] >= frameCap) { g_iClientFrames[client] = 0; if (g_bhumans_finished[client]) { resetClientVectors(client); g_bhumans_finished[client] = false; g_bHumansAllowedTime[client] = false; FinishedStageRaceZone(client); return; } int speedCheckerCap = 10; if (g_iClientSpeedInterval[client] > speedCheckerCap) { g_iClientSpeedInterval[client] = 0; bool bNoclip = (GetEntityMoveType(client) == MOVETYPE_NOCLIP); if (bNoclip) { g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; resetClientVectors(client); PrintToChat(client, "Disabled timer due to Noclip"); return; } float speed = GetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue"); if (speed > 1.0) { if (StrContains(g_cMapname, "surf", false) == -1) { g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; resetClientVectors(client); PrintToChat(client, "Disabled timer due to modified run speed"); return; } } float client_gravity = GetEntityGravity(client); ConVar gravity = FindConVar("sv_gravity"); float gravityFloat = gravity.FloatValue; int minimalPermitedGravity = 610; //PrintToChat(client, "client_gravity: %f\ngravityFloat: %f", client_gravity, gravityFloat); if (((client_gravity > 1.3 || client_gravity < 0.6000) && client_gravity != 0.000000) || gravityFloat < minimalPermitedGravity) { //PrintToChat(client, "client_gravity: %f\ngravityFloat: %f", client_gravity, gravityFloat); g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; resetClientVectors(client); PrintToChat(client, "Disabled timer due to modified gravity"); return; } } g_fClientVectors[client] = clientVectors; if (hText != INVALID_HANDLE && !g_bHideTimer[client]) { SetHudTextParams(0.35, 0.85, 0.1, 125, 255, 255, 85); float total_time = client_current_race_time(client); if (total_time != -1.0) ShowSyncHudText(client, hText, "%N Time: %06.3f\nRecord: %s\nMap: %s\nCourse: %i", client, total_time, g_csTime_record[client], g_cMapname, player_stage[client]); } g_iClientSpeedInterval[client]++; } g_iClientFrames[client]++; } else { //is zm g_bHumansAllowedTime[client] = false; g_bhumans_finished[client] = false; //called if teleported to some place far away or teleported from inside start zone to end zone resetClientVectors(client); PrintToChat(client, "Disabled timer due to ZM infection."); return; } return; } public float client_current_race_time(int i_client) { if (g_fStartTime[i_client] == 0.0) return -1.0; return GetEngineTime() - g_fStartTime[i_client]; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void resetClientVectors(int client) { g_fClientVectors[client][0] = 0.000000; g_fClientVectors[client][1] = 0.000000; g_fClientVectors[client][2] = 0.000000; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public bool checkClientOrigin(float oldVals[3], float newVals[3], int client) { float zero = 0.000000; if ((oldVals[0] == zero && oldVals[1] == zero && oldVals[2] == zero) || (newVals[0] == zero && newVals[1] == zero && newVals[2] == zero)) { return false; } float teleport_range = 1090450.0; int velocityCap = 625; float distance = GetVectorDistance(oldVals, newVals, true); bool bInAir = (GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == -1); if (distance > teleport_range) { if (StrContains(g_cMapname, "surf", false) != -1) return false; float fVelocity[3]; GetEntPropVector(client, Prop_Data, "m_vecVelocity", fVelocity); float currentspeed = SquareRoot(Pow(fVelocity[0],2.0)+Pow(fVelocity[1],2.0)); //PrintToChat(client, "currentspeed: %f", currentspeed); if (bInAir && currentspeed > velocityCap) return false; return true; } return false; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void unloze_zoneEntry(int client, char[] zone) { if (g_bClientsIgnoring[client]) { return; } int zoneIndex = RetrieveZoneIndex(zone); int race_zone_count = GetTotalRaceZones(); if (!(StrEqual(zone, g_cSpecialMapEnd)) && !race_zone_count) return; int l_iZoneCount = unloze_zoneCount(); if (GetClientTeam(client) == CS_TEAM_CT && (g_bHumansAllowedTime[client] || StrEqual(zone, g_cSpecialMapEnd))) { if ((StrContains(zone, "ZONE_PREFIX_RACE") > -1) || StrEqual(zone, g_cSpecialMapEnd)) { if (l_iZoneCount < 2) player_stage[client] = 0; if (player_stage[client] == (zoneIndex / 2) || l_iZoneCount < 2) { g_bhumans_finished[client] = true; g_fClient_End_time[client] = client_current_race_time(client); } } } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void unloze_zoneLeave(int client, char[] zone) { //only maps with multiple zones need ZONE_PREFIX_START if (g_bClientsIgnoring[client]) { return; } int race_zone_count = GetTotalRaceZones(); if (!(StrEqual(zone, g_cSpecialMapStart)) && !race_zone_count) return; if (GetClientTeam(client) == CS_TEAM_CT) { if ((StrContains(zone, "ZONE_PREFIX_START") > -1) || StrEqual(zone, g_cSpecialMapStart)) { if (!SteamClientAuthenticated(g_csSID[client])) { PrintToChat(client, "Not starting timer due to being listed as nosteamer"); return; } if (!g_bClient_allowed_to_leave_again[client]) { PrintToChat(client, "Did not start timer due to teleport cooldown."); return; } g_fStartTime[client] = GetEngineTime(); float notRounded = float(RetrieveZoneIndex(zone)); player_stage[client] = RoundToCeil(notRounded / 2); CPrintToChat(client, "Timer started for Course: %i", player_stage[client]); g_bHumansAllowedTime[client] = true; mysql_get_player_time(client, player_stage[client]); } } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void CheckIfSpecialRoundZones(char[] resultstart, char[] resultend) { Format(g_cSpecialMapStart, sizeof(g_cSpecialMapStart), resultstart); Format(g_cSpecialMapEnd, sizeof(g_cSpecialMapEnd), resultend); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void CheckifAntiZones(int client, bool reset) { if (reset && g_bHumansAllowedTime[client]) { g_bHumansAllowedTime[client] = false; PrintToChat(client, "Disabled Timer due to using illegal shortcut"); } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public int RetrieveZoneIndex(char[] zone) { //if you leave zone_2 you want the corresponding racezone to be zone_3 int iterator = strlen(zone) - 1; if (iterator == -1) return 1; char l_sZone[g_dIndex]; Format(l_sZone, sizeof(l_sZone), zone); while (IsCharNumeric(l_sZone[iterator])) iterator--; iterator++; strcopy(l_sZone, sizeof(l_sZone), l_sZone[iterator]); return StringToInt(l_sZone[iterator]); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void FinishedStageRaceZone(int client) { char sName[MAX_NAME_LENGTH]; GetClientName(client, sName, sizeof(sName)); int size2 = 2 * strlen(sName) + 1; char[] sEscapedName = new char[size2 + 1]; g_dDatabase.Escape(sName, sEscapedName, size2 + 1); if (StrEqual(g_csSID[client], "STEAM_ID_STOP_IGNORING_RETVALS") || StrEqual(g_csSID[client], "STEAM_ID_PENDING")) { PrintToChat(client, "Your steam ID is not working, not updating timer"); return; } int l_iZoneCount = unloze_zoneCount(); float client_time = g_fClient_End_time[client]; if (client_time == -1.0) { //if client disconnected we dont want to cary timer over to other client return; } if (l_iZoneCount > 1) CPrintToChat(client, "{green}[UNLOZE] Stage: %i", player_stage[client]); CPrintToChat(client, "{green}[UNLOZE] Time: %06.3f", client_time); int stage = player_stage[client]; char sQuery[g_dLength]; if (StrEqual(g_csTime_record[client], "0.000")) { CPrintToChat(client, "{green}[UNLOZE] Your record: None yet\nCommand: !toptime !mytime !stages"); } else { CPrintToChat(client, "{green}[UNLOZE] Your record: %s\nCommand: !toptime !mytime !stages", g_csTime_record[client]); } if (l_iZoneCount < 2) Format(sQuery, sizeof(sQuery), "UPDATE `zetimer_table_new` SET `%s` = '%06.3f', name = '%s' WHERE steam_auth = '%s'", g_cMapname, client_time, sEscapedName, g_csSID[client]); else Format(sQuery, sizeof(sQuery), "UPDATE `zetimer_table_new` SET `%sS%i` = '%06.3f', name = '%s' WHERE steam_auth = '%s'", g_cMapname, stage, client_time, sEscapedName, g_csSID[client]); int generic_length = 32; char[][] sPart = new char[2][generic_length]; float old_client_time = 0.0; if (StrContains(g_csTime_record[client], ":") != -1) { ExplodeString(g_csTime_record[client], ":", sPart, 2, generic_length); old_client_time = (StringToFloat(sPart[0]) * 60) + StringToFloat(sPart[1]); } else { old_client_time = StringToFloat(g_csTime_record[client]); } if (client_time >= old_client_time && old_client_time > 0.0) { return; } DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); g_dDatabase.Query(SQL_FinishedQuery, sQuery, hDataPack, DBPrio_Normal); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void mysql_get_player_time(int client, int stage) { char query[g_dLength]; if (!stage) { Format(query, sizeof(query), "SELECT `%s` FROM `zetimer_table_new` where steam_auth = '%s'", g_cMapname, g_csSID[client]); DataPack hDataPack = new DataPack(); hDataPack.WriteCell(GetClientSerial(client)); g_dDatabase.Query(SQL_OnQueryCompleted_retry, query, hDataPack); } else { Format(query, sizeof(query), "SELECT `%sS%i` FROM `zetimer_table_new` where steam_auth = '%s'", g_cMapname, stage, g_csSID[client]); DataPack hDataPack = new DataPack(); hDataPack.WriteCell(GetClientSerial(client)); hDataPack.WriteString(query); g_dDatabase.Query(SQL_OnQueryCompleted, query, hDataPack); } } //we only enter here when stage is 0. that might however be incorrect as it instead might need to be S1. Simply a band aid instead of fixing actual problem. public void SQL_OnQueryCompleted_retry(Database db, DBResultSet results, const char[] error, DataPack data) { ResetPack(data); int client_serial = data.ReadCell(); delete data; if (!db) { delete results; return; } int client; if ((client = GetClientFromSerial(client_serial)) == 0) { delete results; return; } if (strlen(error)) { delete results; mysql_get_player_time(client, 1); return; } if (results.RowCount && results.FetchRow()) results.FetchString(0, g_csTime_record[client], sizeof(g_csTime_record[])); delete results; } public void SQL_OnQueryCompleted(Database db, DBResultSet results, const char[] error, DataPack data) { ResetPack(data); int client_serial = data.ReadCell(); if (!db || strlen(error)) { char sQuery[g_dLength]; data.ReadString(sQuery, sizeof(sQuery)); LogError("Query error 1: %s", error); LogError("actual query: %s", sQuery); delete data; delete results; return; } delete data; int client; if ((client = GetClientFromSerial(client_serial)) == 0) { delete results; return; } if (results.RowCount && results.FetchRow()) results.FetchString(0, g_csTime_record[client], sizeof(g_csTime_record[])); delete results; } //---------------------------------------------------------------------------------------------------- // Purpose: TODO implement if needed //---------------------------------------------------------------------------------------------------- public int GetTotalRaceZones() { int l_iZoneCount = unloze_zoneCount(); char l_cIndexName[g_dLength]; int l_iCountRace; for (int iterator = 0; iterator < l_iZoneCount; iterator++) { ZoneNameBasedOnIndex(iterator, l_cIndexName[iterator]); if (StrContains(l_cIndexName[iterator], "ZONE_PREFIX_RACE") > -1) l_iCountRace++; } return l_iCountRace; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void insertPlayerMYSQL(int client) { if (!IsValidClient(client)) return; char sQuery[g_dLength]; char sName[MAX_NAME_LENGTH]; GetClientName(client, sName, sizeof(sName)); int size2 = 2 * strlen(sName) + 1; char[] sEscapedName = new char[size2 + 1]; if (!g_dDatabase) { return; } g_dDatabase.Escape(sName, sEscapedName, size2 + 1); if (StrEqual(g_csSID[client], "STEAM_ID_STOP_IGNORING_RETVALS") || StrEqual(g_csSID[client], "STEAM_ID_PENDING")) { PrintToChat(client, "Your steam ID is not working, not updating timer"); return; } Format(sQuery, sizeof(sQuery), "INSERT INTO `zetimer_table_new` (`steam_auth`, `name`) VALUES ('%s', '%s') ON DUPLICATE KEY UPDATE `name` = '%s'", g_csSID[client], sEscapedName, sEscapedName); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); g_dDatabase.Query(SQL_FinishedQuery, sQuery, hDataPack, DBPrio_Normal); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Action cmd_timerIgnoreTime(int client, int args) { insert_client_ignoring_racetimer(client); return Plugin_Handled; } public void insert_client_ignoring_racetimer(int client) { if (!IsValidClient(client)) return; if (g_bClientsIgnoring[client]) { g_bClientsIgnoring[client] = false; g_bHideTimer[client] = false; SetClientCookie(client, g_hClientCookie, "0"); PrintToChat(client, "You are now not ignoring the racetimer"); } else { g_bClientsIgnoring[client] = true; g_bHideTimer[client] = true; SetClientCookie(client, g_hClientCookie, "1"); PrintToChat(client, "You are now ignoring the racetimer"); } char sQuery[g_dLength]; if (!g_dDatabase) { return; } if (StrEqual(g_csSID[client], "STEAM_ID_STOP_IGNORING_RETVALS") || StrEqual(g_csSID[client], "STEAM_ID_PENDING")) { PrintToChat(client, "Your steam ID is not working, not updating timer"); return; } Format(sQuery, sizeof(sQuery), "INSERT INTO `zetimer_table_new_ignoring` (`steam_auth`, `is_ignoring`) VALUES ('%s', '%i') ON DUPLICATE KEY UPDATE `is_ignoring` = '%i'", g_csSID[client], g_bClientsIgnoring[client], g_bClientsIgnoring[client]); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sQuery); g_dDatabase.Query(SQL_FinishedQuery, sQuery, hDataPack, DBPrio_Normal); } public Action cmd_timerCheckTop(int client, int args) { CheckTop(client, 0, 0); return Plugin_Handled; } public void mysql_select_lowest_ordered(int client, int index, int length_increase, Menu menu, int l_iPosition) { int l_iZoneCount = unloze_zoneCount(); char sQuery[g_dLength]; if (l_iZoneCount < 2) Format(sQuery, sizeof(sQuery), "select name, `%s` from `zetimer_table_new` where `%s` > 0 order by `%s` asc limit 10", g_cMapname, g_cMapname, g_cMapname); else Format(sQuery, sizeof(sQuery), "select name, `%sS%i` from `zetimer_table_new` where `%sS%i` > 0 order by `%sS%i` asc limit 10", g_cMapname, index, g_cMapname, index, g_cMapname, index); //LogMessage("sQuery: %s", sQuery); DataPack hDataPack = new DataPack(); hDataPack.WriteCell(GetClientSerial(client)); hDataPack.WriteString(sQuery); hDataPack.WriteCell(index); hDataPack.WriteCell(menu); hDataPack.WriteCell(l_iPosition); hDataPack.WriteCell(length_increase); g_dDatabase.Query(SQL_Select_Top_Callback, sQuery, hDataPack); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void CheckTop(int client, int index, int autismstate) { int l_iZoneCount = unloze_zoneCount(); if (l_iZoneCount < 1) { PrintToChat(client, "No zones active on this map"); return; } if (!autismstate) { CheckStagesOnMap(client, 0); return; } mysql_select_lowest_ordered(client, index, 0, null, 0); } public void SQL_Select_Top_Callback(Database db, DBResultSet results, const char[] error, DataPack data) { ResetPack(data); int client_serial = data.ReadCell(); char sQuery[g_dLength]; data.ReadString(sQuery, sizeof(sQuery)); if (!db || strlen(error)) { LogError("Query error 2: %s", error); LogError("actual query: %s", sQuery); delete data; delete results; return; } int index = data.ReadCell(); Menu menu = data.ReadCell(); int l_iPosition = data.ReadCell(); int length_increase = data.ReadCell(); length_increase++; delete data; int iclient; if ((iclient = GetClientFromSerial(client_serial)) == 0) { delete results; return; } //Player Name char[] g_cPlayerName = new char[MAX_NAME_LENGTH]; char g_cContent[g_dLength]; if (menu == null) { menu = new Menu(MenuHandler1); menu.SetTitle("Maptimer: %s", g_cMapname); } if (results != INVALID_HANDLE) { while (results.RowCount > 0 && results.FetchRow() && l_iPosition < 10) { l_iPosition++; results.FetchString(0, g_cPlayerName, MAX_NAME_LENGTH); float fTime = results.FetchFloat(1); Format(g_cContent, sizeof(g_cContent), "#%i: Time: %06.3f - %s", l_iPosition, fTime, g_cPlayerName); menu.AddItem("-1", g_cContent, ITEMDRAW_DISABLED); } delete results; if (!l_iPosition) { menu.AddItem("-1", "No results. Commands: !toptime !stages", ITEMDRAW_DISABLED); } else if (l_iPosition < 10) { //did 2 mins googling but found nothing about menus needing to be cleaned mysql_select_lowest_ordered(iclient, index, length_increase, menu, l_iPosition); return; } menu.ExitButton = true; menu.Display(iclient, 0); } delete results; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public int MenuHandler1(Menu menu, MenuAction action, int param1, int param2) { if (action == MenuAction_End) delete menu; return 0; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Action Cmd_timeReset(int client, int args) { if (!IsValidClient(client)) return Plugin_Handled; if (args != 2) { ReplyToCommand(client, "[SM] Usage cleantime "); return Plugin_Handled; } char sTarget[65]; GetCmdArg(1, sTarget, sizeof(sTarget)); int targetID = FindTarget(client, sTarget, false); if(targetID == -1) return Plugin_Handled; GetCmdArg(2, sTarget, sizeof(sTarget)); deleteClientTime(g_csSID[targetID], StringToInt(sTarget)); return Plugin_Handled; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Action cmd_hideTimerHUD(int client, int args) { if (!g_bHideTimer[client]) { g_bHideTimer[client] = true; SetClientCookie(client, g_hClientCookie, "1"); PrintToChat(client, "Disabled timer HUD"); } else { g_bHideTimer[client] = false; PrintToChat(client, "Enabled timer HUD"); SetClientCookie(client, g_hClientCookie, "0"); } return Plugin_Handled; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Action cmd_timerCheckStage(int client, int args) { CheckStagesOnMap(client, 0); return Plugin_Handled; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void CheckStagesOnMap(int client, int state) { int l_iCount; int l_iZoneCount = unloze_zoneCount(); Menu StageMenu = CreateMenu(Stage_menu); char l_cMenuContent[g_dLength]; if (!l_iZoneCount) { CPrintToChat(client, "[UNLOZE] Map does not support racestage timer"); return; } //state 0 == toptime, state 1 == own time g_iClientChecking[client] = state; StageMenu.SetTitle("Stages on: %s", g_cMapname); if (g_bDisplaySpecial) { l_iCount++; Format(l_cMenuContent, sizeof(l_cMenuContent), "Stage: %i", l_iCount); StageMenu.AddItem("", l_cMenuContent); } else { int zone_reached = 0; for (int Iterator = 0; Iterator <= l_iZoneCount; Iterator++) { char zoneIndexName[512]; ZoneNameBasedOnIndex(Iterator, zoneIndexName); if (StrContains(zoneIndexName, "ZONE_PREFIX_RACE") > -1) { char local_c[g_dLength]; Format(local_c, sizeof(local_c), "%s", zoneIndexName[strlen("ZONE_PREFIX_RACE") + 1]); int prev = zone_reached; zone_reached = StringToInt(local_c); if (zone_reached == prev) { continue; } l_iCount++; Format(l_cMenuContent, sizeof(l_cMenuContent), "Stage: %i", l_iCount); StageMenu.AddItem("", l_cMenuContent); } } } StageMenu.ExitButton = true; StageMenu.Display(client, 0); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public int Stage_menu(Menu menu, MenuAction action, int client, int selection) { if (action == MenuAction_Select && IsValidClient(client)) { selection++; if (!g_iClientChecking[client]) { CheckTop(client, selection, 1); } else { CheckStageSelf(client, selection); } } else if (action == MenuAction_End) { delete(menu); } return 0; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Action cmd_timerCheckSelf(int client, int args) { Checkself(client); return Plugin_Handled; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void deleteClientTime(char[] steam2, int stage) { char l_cQuery[g_dLength]; int l_iZoneCount = unloze_zoneCount(); if (l_iZoneCount > 1) Format(l_cQuery, sizeof(l_cQuery), "UPDATE `zetimer_table_new` SET `%sS%i` = 0.000 WHERE steam_auth = '%s'", g_cMapname, stage, steam2); else Format(l_cQuery, sizeof(l_cQuery), "UPDATE `zetimer_table_new` SET `%s` = 0.000 WHERE steam_auth = '%s'", g_cMapname, steam2); DataPack hDataPack = new DataPack(); hDataPack.WriteString(l_cQuery); g_dDatabase.Query(SQL_FinishedQuery, l_cQuery, hDataPack, DBPrio_Normal); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Checkself(int client) { int l_iZoneCount = unloze_zoneCount(); char l_cQuery[g_dLength]; if (l_iZoneCount < 1) { PrintToChat(client, "No zones active on this map"); return; } if (l_iZoneCount < 2) { Format(l_cQuery, sizeof(l_cQuery), "SELECT name, `%s` FROM `zetimer_table_new` WHERE steam_auth = '%s'", g_cMapname, g_csSID[client]); DataPack hDataPack = new DataPack(); hDataPack.WriteCell(GetClientSerial(client)); hDataPack.WriteString(l_cQuery); g_dDatabase.Query(SQL_CheckSelf, l_cQuery, hDataPack); } else CheckStagesOnMap(client, 1); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void CheckStageSelf(int client, int selection) { char l_cQuery[g_dLength]; Format(l_cQuery, sizeof(l_cQuery), "SELECT name, `%sS%i` FROM `zetimer_table_new` WHERE steam_auth = '%s'", g_cMapname, selection, g_csSID[client]); DataPack hDataPack = new DataPack(); hDataPack.WriteCell(GetClientSerial(client)); hDataPack.WriteString(l_cQuery); g_dDatabase.Query(SQL_CheckSelf, l_cQuery, hDataPack); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void SQL_CheckSelf(Database db, DBResultSet results, const char[] error, DataPack data) { ResetPack(data); int client_serial = data.ReadCell(); if (!db || strlen(error)) { char sQuery[g_dLength]; data.ReadString(sQuery, sizeof(sQuery)); LogError("Query error 4: %s", error); LogError("actual query: %s", sQuery); delete data; delete results; return; } delete data; char l_cMessageContent[g_dLength]; char[] l_cPlayerName = new char[MAX_NAME_LENGTH]; int iclient; if ((iclient = GetClientFromSerial(client_serial)) == 0) { delete results; return; } if (results.RowCount && results.FetchRow()) { results.FetchString(0, l_cPlayerName, MAX_NAME_LENGTH); float fTime = results.FetchFloat(1); if (fTime == 0.0) { CPrintToChat(iclient, "You have no time yet!"); delete results; return; } fTime += 0.001; Format(l_cMessageContent, sizeof(l_cMessageContent), "%06.3f - %s", fTime, l_cPlayerName); CPrintToChat(iclient, "Your best time: %s", l_cMessageContent); } else CPrintToChat(iclient, "You have no time yet!"); delete results; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock bool IsValidClient(int client) { if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client)) return true; return false; }