diff --git a/README.md b/README.md index aadc1ba..434b548 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Download all plugins in a zip file here: diff --git a/includes/match.inc b/includes/match.inc index 9f76731..c4ce71f 100644 --- a/includes/match.inc +++ b/includes/match.inc @@ -7,20 +7,24 @@ Remember to call: Then use these functions: -StartMatch() { +void StartMatch() { } -ResetMatch() { +void ResetMatch() { } -EndMatch(bool:endedMidgame) { +void EndMatch(bool endedMidgame) { } + +Optional: + +public void StartFirstRound() { +} + */ /* TODO: -- Dont call StartMatch before the 5-4-3-2-1 countdown reaches 0. -- Make a new callback, AllPlayersReady - Make a new callback, AboutToEnd (5 secs before MatchEnded) - g_fMatchEndTime - g_bCountdownStarted @@ -156,6 +160,12 @@ public void Match_Event_round_start(Event event, const char[] name, bool dontBro if (!g_bFirstRoundStarted) { g_bFirstRoundStarted = true; + + Function func = GetFunctionByName(null, "StartFirstRound"); + if (func != null) { + Call_StartFunction(null, func); + Call_Finish(); + } } } } diff --git a/logs-spec.md b/logs-spec.md index f6ba714..54f815d 100644 --- a/logs-spec.md +++ b/logs-spec.md @@ -16,7 +16,7 @@ When we write ``, `` or similar, they refer to this format: "F2<3><[U:1:1234]>" -# Supplemental logs (supstats2) +# Supplemental logs ## Pauses @@ -158,7 +158,7 @@ For Crusader's Crossbow, friendly hits will be logged as `shot_hit`. Destroying a sticky with a hitscan weapon will be logged as `shot_hit` (although it might not always be accurately detected). -# Medic Stats (medicstats) +# Medic Stats **A note on vaccinator:** These logs are very bugged for vaccinator. For example, "charge ready" is only logged at 100%, but with vaccinator you can use charge at 25%. And it does not make sense talking about "uber advantage lost" when one team has uber and the other vaccinator. So, if one team uses vaccinator, think carefully about how you use these logs. @@ -221,3 +221,47 @@ When one team gets uber significantly before the other team, but does not use it "F2<3><[U:1:1234]>" triggered "lost_uber_advantage" (time "14") Alternatively, this could also be computed by looking at the "chargeready" and "chargedeployed" logs from both teams. + +# Meta data + +Sometimes we want to write information to the logs that are not directly triggered by in-game events. We call this _meta data_. + +When it comes to these meta logs, **all properties are optional**. + +More properties can be added in the future, but the format of the ones listed should not be changed. + +## Player related + +> [!WARNING] +> Player related logs have not been finalized, and parsers should not implement this yet. + +To write meta data that is directly related to a player: + + triggered "meta_data" (position "%i %i %i") + + "F2<3><[U:1:1234]>" triggered "meta_data" (position "478 -334 603") + +It can have the following properties: + +- `position`: The current location of the player + +## Non-player related + +To write meta data that is not directly related to a player: + + World triggered "meta_data" (matchid "%s") (map "%s") (title "%s") + + World triggered "meta_data" (matchid "abc123") (map "cp_badlands") (title "serveme.tf #1337") + +It can have the following properties: + +- `matchid`: A randomly generated match id, hopefully unique (max 32 alphanumeric chars). + This can be used to detect duplicate logs from the same match. +- `map`: The current map +- `title`: A title for the match (similar to the one sent to logs.tf) + +There can be multiple of these log lines in a match, so for example you might see this: + + World triggered "meta_data" (matchid "abc123") + World triggered "meta_data" (map "cp_badlands") + World triggered "meta_data" (title "serveme.tf #1337") diff --git a/logstf/logstf.sp b/logstf/logstf.sp index cacba2b..da0fca7 100644 --- a/logstf/logstf.sp +++ b/logstf/logstf.sp @@ -125,6 +125,10 @@ Release notes: - Updated code to be compatible with SourceMod 1.12 +---- 2.7.0 (22/07/2025) ---- +- Changed match title trimming logic + + TODO: - Some people run multiple instances of the same server (located in the same directory). This is a problem, because they all write to the same logstf.log file. Make the logstf.log and -partial files have dynamic names, and don't forget to clean them up. - Sanitize names for < and >, since logs.tf doesn't like those @@ -146,7 +150,7 @@ TODO: #undef REQUIRE_PLUGIN #include -#define PLUGIN_VERSION "2.6.6" +#define PLUGIN_VERSION "2.7.0" #define UPDATE_URL "https://sourcemod.krus.dk/logstf/update.txt" #define LOG_PATH "logstf.log" @@ -364,6 +368,8 @@ void EndMatch(bool endedMidgame) { } void CacheMatchValues() { + // Note: If you are making changes related to title generation, also update supstats2. + g_hCvarHostname.GetString(g_sCachedHostname, sizeof(g_sCachedHostname)); g_hCvarBlueTeamName.GetString(g_sCachedBluTeamName, sizeof(g_sCachedBluTeamName)); g_hCvarRedTeamName.GetString(g_sCachedRedTeamName, sizeof(g_sCachedRedTeamName)); @@ -372,9 +378,10 @@ void CacheMatchValues() { String_Trim(g_sCachedRedTeamName, g_sCachedRedTeamName, sizeof(g_sCachedRedTeamName)); GetCurrentMap(g_sCachedMap, sizeof(g_sCachedMap)); - // Remove last word in hostname + // Trim the last words in hostname if it is a long hostname int spacepos = -1; - for (int i = strlen(g_sCachedHostname) - 1; i >= 17; i--) { + int hostnameLen = strlen(g_sCachedHostname); + for (int i = 25; i < hostnameLen; i++) { if (g_sCachedHostname[i] == ' ') { spacepos = i; break; @@ -690,6 +697,7 @@ void UploadLog(bool partial) { g_bIsUploading = true; g_bIsPartialUpload = partial; + // Note: If you are making changes related to title generation, also update supstats2. char title[128]; g_hCvarTitle.GetString(title, sizeof(title)); ReplaceString(title, sizeof(title), "{server}", g_sCachedHostname, false); diff --git a/logstf/update.txt b/logstf/update.txt index 030388d..6acda97 100644 --- a/logstf/update.txt +++ b/logstf/update.txt @@ -4,11 +4,11 @@ { "Version" { - "Latest" "2.6.6" + "Latest" "2.7.0" } - "Notes" "Changes in 2.6.6:" - "Notes" "- Updated code to be compatible with SourceMod 1.12" + "Notes" "Changes in 2.7.0:" + "Notes" "- Changed match title trimming logic" } "Files" diff --git a/supstats2/README.md b/supstats2/README.md index d5a5a8f..26d5449 100644 --- a/supstats2/README.md +++ b/supstats2/README.md @@ -15,7 +15,9 @@ Features: - Logs players spawning - Logs game pauses - Logs shots fired and shots hit (for certain weapons) +- Logs unique match id, map name and title at the start of the match -| CVAR | Default | Description | -| -------------------------- | ------- | -------------------- | -| `supstats_accuracy <0\|1>` | `1` | Enable accuracy logs | +| CVAR | Default | Description | +| -------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `supstats_accuracy <0\|1>` | `1` | Enable accuracy logs | +| `logstf_title ` | `{server}: {blu} vs {red}` | Sets the title of the log, which is logged at the start of the match.<br>Use `{server}` to insert the server title.<br>Use `{blu}` or `{red}` to insert team names. | diff --git a/supstats2/supstats2.sp b/supstats2/supstats2.sp index 0c133bd..80c693d 100644 --- a/supstats2/supstats2.sp +++ b/supstats2/supstats2.sp @@ -91,6 +91,10 @@ Release notes: - Updated code to be compatible with SourceMod 1.12 +---- 2.6.0 (19/07/2025) ---- +- Log meta data at the start of the match (matchid, map, title) + + TODO: - Use GetGameTime() instead of GetEngineTime()? - Write comments in code :D @@ -108,10 +112,11 @@ TODO: #include <sdkhooks> #include <smlib> #include <kvizzle> +#include <match> #undef REQUIRE_PLUGIN #include <updater> -#define PLUGIN_VERSION "2.5.4" +#define PLUGIN_VERSION "2.6.0" #define UPDATE_URL "https://sourcemod.krus.dk/supstats2/update.txt" #define NAMELEN 64 @@ -150,6 +155,12 @@ int medpackHealAmount[MAXPLAYERS+1]; float g_fPauseStartTime; char g_sTauntNames[][] = { "", "taunt_scout", "taunt_sniper", "taunt_soldier", "taunt_demoman", "taunt_medic", "taunt_heavy", "taunt_pyro", "taunt_spy", "taunt_engineer" }; +Handle g_hTimerMatchStarted = null; +ConVar g_hCvarLogsTitle = null; +ConVar g_hCvarHostname = null, + g_hCvarRedTeamName = null, + g_hCvarBlueTeamName = null; + // ---- ACCURACY ---- ConVar g_hCvarEnableAccuracy = null; @@ -195,6 +206,7 @@ public void OnPluginStart() { if (LibraryExists("updater")) Updater_AddPlugin(UPDATE_URL); + Match_OnPluginStart(); g_tShotTypes = CreateTrie(); g_tShotTypes.SetValue("tf_weapon_rocketlauncher", SHOT_ROCKET); @@ -228,8 +240,15 @@ public void OnPluginStart() { char cvarEnableAccuracy[16]; g_hCvarEnableAccuracy.GetString(cvarEnableAccuracy, sizeof(cvarEnableAccuracy)); CvarChange_EnableAccuracy(g_hCvarEnableAccuracy, cvarEnableAccuracy, cvarEnableAccuracy); + + // The "logstf_" prefix is used because we are reusing the cvar from the logstf plugin. + // This is done to avoid having all servers defining the title format twice. + g_hCvarLogsTitle = CreateConVar("logstf_title", "{server}: {blu} vs {red}", "Title for the log", FCVAR_NONE); + g_hCvarHostname = FindConVar("hostname"); + g_hCvarRedTeamName = FindConVar("mp_tournament_redteamname"); + g_hCvarBlueTeamName = FindConVar("mp_tournament_blueteamname"); - char map[64]; + char map[128]; GetCurrentMap(map, sizeof(map)); LogToGame("Loading map \"%s\"", map); @@ -271,6 +290,8 @@ public void OnLibraryAdded(const char[] name) { } public void OnMapStart() { + Match_OnMapStart(); + for (int i = 0; i < sizeof(g_iIgnoreDamageEnt); i++) g_iIgnoreDamageEnt[i] = 0; @@ -281,6 +302,10 @@ public void OnMapStart() { g_bIsPaused = false; // The game is automatically unpaused during a map change } +public void OnMapEnd() { + Match_OnMapEnd(); +} + public void OnClientPutInServer(int client) { if (!g_bWeaponDataInitialized) return; @@ -359,6 +384,108 @@ public Action CheckPause(Handle timer, int client) { return Plugin_Continue; } +void StartMatch() { +} + +public void StartFirstRound() { + if (g_hTimerMatchStarted != null) { + delete g_hTimerMatchStarted; + g_hTimerMatchStarted = null; + } + + // First log all the players spawning etc., and then we log the meta data. + g_hTimerMatchStarted = CreateTimer(0.5, LogMetaData); +} + +void ResetMatch() { + if (g_hTimerMatchStarted != null) { + delete g_hTimerMatchStarted; + g_hTimerMatchStarted = null; + } +} + +void EndMatch(bool endedMidgame) { + if (g_hTimerMatchStarted != null) { + delete g_hTimerMatchStarted; + g_hTimerMatchStarted = null; + } +} + +void LogMetaData(Handle timer) { + g_hTimerMatchStarted = null; + + LogMatchId(); + LogMapName(); + LogTitle(); +} + +void LogMatchId() { + char matchId[33]; + FormatTime(matchId, sizeof(matchId), "%y%m%d%H%M%S"); + + for (int i = 12; i < 32; i++) { + int randVal = GetRandomInt(0, 61); + if (randVal < 10) { + matchId[i] = '0' + randVal; + } else if (randVal < 36) { + matchId[i] = 'a' + (randVal - 10); + } else { + matchId[i] = 'A' + (randVal - 36); + } + } + matchId[32] = '\0'; + + LogToGame("World triggered \"meta_data\" (matchid \"%s\")", matchId); +} + +void LogMapName() { + char currentMap[128]; + GetCurrentMap(currentMap, sizeof(currentMap)); + LogToGame("World triggered \"meta_data\" (map \"%s\")", currentMap); +} + +void LogTitle() { + // Note: If you are making changes related to title generation, also update logstf. + + char hostname[64]; + char bluTeamName[32]; + char redTeamName[32]; + + g_hCvarHostname.GetString(hostname, sizeof(hostname)); + g_hCvarBlueTeamName.GetString(bluTeamName, sizeof(bluTeamName)); + g_hCvarRedTeamName.GetString(redTeamName, sizeof(redTeamName)); + String_Trim(hostname, hostname, sizeof(hostname)); + String_Trim(bluTeamName, bluTeamName, sizeof(bluTeamName)); + String_Trim(redTeamName, redTeamName, sizeof(redTeamName)); + + // Trim the last words in hostname if it is a long hostname + int spacepos = -1; + int hostnameLen = strlen(hostname); + for (int i = 25; i < hostnameLen; i++) { + if (hostname[i] == ' ') { + spacepos = i; + break; + } + } + + if (spacepos != -1) { + hostname[spacepos] = '\0'; + String_Trim(hostname, hostname, sizeof(hostname), " -:.!,;"); + } + + // Replace placeholders + char title[128]; + g_hCvarLogsTitle.GetString(title, sizeof(title)); + ReplaceString(title, sizeof(title), "{server}", hostname, false); + ReplaceString(title, sizeof(title), "{blu}", bluTeamName, false); + ReplaceString(title, sizeof(title), "{blue}", bluTeamName, false); + ReplaceString(title, sizeof(title), "{red}", redTeamName, false); + + ReplaceString(title, sizeof(title), "\\", "", false); + + LogToGame("World triggered \"meta_data\" (title \"%s\")", title); +} + public Action Event_PlayerHealed(Event event, const char[] name, bool dontBroadcast) { char patientName[NAMELEN]; char healerName[NAMELEN]; diff --git a/supstats2/update.txt b/supstats2/update.txt index f74518a..36a3087 100644 --- a/supstats2/update.txt +++ b/supstats2/update.txt @@ -4,11 +4,11 @@ { "Version" { - "Latest" "2.5.4" + "Latest" "2.6.0" } - "Notes" "Changes in 2.5.4:" - "Notes" "- Updated code to be compatible with SourceMod 1.12" + "Notes" "Changes in 2.6.0:" + "Notes" "- Log meta data at the start of the match (matchid, map, title)" } "Files"