//========= Copyright Valve Corporation, All rights reserved. ============// // Parse Halloween 2011 server logs and mine Eyeball Boss (Monoculus) data #define _CRT_SECURE_NO_WARNINGS // STFU #include #include "stdio.h" #include #include #define MAX_BUFFER_SIZE 256 #define MAX_NAME_SIZE 64 enum EventType { EYEBALL_SPAWN, // eyeball_spawn (max_health 11200) (player_count 18) EYEBALL_ESCAPE, // eyeball_escaped (max_dps 220.00) (health 3071) EYEBALL_DEATH, // eyeball_death (max_dps 467.63) (max_health 12000) (player_count 23) EYEBALL_STUN, // "Aque0us<174>" eyeball_stunned with "NoWeapon" (attacker_position "-1033 872 143") PURGATORY_ENTER, // "I_DONT_KNOW<181>" purgatory_teleport "spawn_purgatory" PURGATORY_ESCAPE, // "I_DONT_KNOW<181>" purgatory_escaped LOOT_ISLAND_ENTER, // "I_DONT_KNOW<181>" purgatory_teleport "spawn_loot" EYEBALL_KILLER, // "Spaghetti Western<192>" eyeball_killer with "liberty_launcher" (attacker_position "-1629 -293 213") EVENT_COUNT }; #define TEAM_NONE 0 #define TEAM_RED 1 #define TEAM_BLUE 2 struct EventInfo { EventType m_type; char m_date[ MAX_NAME_SIZE ]; int m_timestamp; // seconds since log start int m_health; int m_maxHealth; int m_playerCount; int m_maxDPS; int m_attackerPosX; int m_attackerPosY; int m_attackerPosZ; char m_weaponName[ MAX_NAME_SIZE ]; char m_playerName[ MAX_NAME_SIZE ]; int m_playerID; int m_team; }; std::vector< EventInfo > TheData; //---------------------------------------------------------------------------------- bool ParseEvent( FILE *fp, EventInfo *event ) { return false; } //---------------------------------------------------------------------------------- char *ParsePlayer( EventInfo *event, char *data ) { // "TheSauce<17>" // ARGH! Player names can contain '<' and '>' // skip the " ++data; char *restOfData = NULL; char *c = &data[ strlen(data)-1 ]; // backup until we find '>' while( c != data ) { --c; if ( *c == '>' ) { restOfData = c+2; // terminate the team name *c = '\000'; break; } } // backup until we find '<' char *teamName = NULL; while( c != data ) { --c; if ( *c == '<' ) { teamName = c+1; // back up and terminate the Steam ID --c; *c = '\000'; break; } } if ( !teamName ) { return NULL; } if ( !strcmp( "Red", teamName ) ) { event->m_team = TEAM_RED; } else if ( !strcmp( "Blue", teamName ) ) { event->m_team = TEAM_BLUE; } else { return NULL; } // "TheSauce<17>" // backup until we find '<' char *steamID = NULL; while( c != data ) { --c; if ( *c == '<' ) { steamID = c+1; // back up and terminate the player ID --c; *c = '\000'; break; } } if ( !steamID ) { return NULL; } // backup until we find '<' char *playerID = NULL; while( c != data ) { --c; if ( *c == '<' ) { playerID = c+1; // terminate the player name *c = '\000'; break; } } if ( !playerID ) { return NULL; } event->m_playerID = atoi( playerID ); strcpy( event->m_playerName, data ); return restOfData; #ifdef OLDWAY // skip the " ++data; char *token = strtok( data, "<" ); if ( !token ) return NULL; strcpy( event->m_playerName, token ); token = strtok( NULL, ">" ); if ( !token ) return NULL; event->m_playerID = atoi( token ); // steam ID token = strtok( NULL, "<>" ); if ( !token ) return NULL; // team token = strtok( NULL, "<>" ); if ( !token ) return NULL; if ( !strcmp( "Red", token ) ) { event->m_team = TEAM_RED; } else if ( !strcmp( "Blue", token ) ) { event->m_team = TEAM_BLUE; } // get rest of line after closing quote token = strtok( NULL, "" ); token += 2; return token; #endif } //---------------------------------------------------------------------------------- // Parse and return int value from "(name value)" bool parse_strtok_int( const char *name, int *value ) { char *token = strtok( NULL, "( " ); if ( !token ) return false; if ( _stricmp( name, token ) ) return false; // get value token = strtok( NULL, ") " ); if ( !token ) return false; *value = atoi( token ); return true; } //---------------------------------------------------------------------------------- bool ProcessData( const char *filename ) { FILE *fp = fopen( filename, "r" ); if ( !fp ) { printf( "ERROR: Cannot access file '%s'\n", filename ); return false; } FILE *errorFP = fopen( "cooked_stats_error.txt", "w" ); if ( !errorFP ) { printf( "ERROR: Can't open output file\n" ); return false; } char buffer[ MAX_BUFFER_SIZE ]; bool isFirstLine = true; int initialTimestamp = 0; int line = 0; EventInfo info; while( true ) { memset( &info, 0, sizeof( EventInfo ) ); fgets( buffer, MAX_BUFFER_SIZE, fp ); ++line; // eat filename char *token = buffer; while( *token != 'L' ) { ++token; } if ( !token ) break; // read date token = strtok( token, "L " ); if ( !token ) break; strcpy( info.m_date, token ); // read time token = strtok( NULL, "- " ); if ( !token ) break; token[2] = '\000'; int hour = atoi( token ); token[5] = '\000'; int minute = atoi( &token[3] ); token[8] = '\000'; int second = atoi( &token[6] ); int timestamp = hour * 3600 + minute * 60 + second; if ( isFirstLine ) { isFirstLine = false; initialTimestamp = timestamp; } info.m_timestamp = timestamp - initialTimestamp; int fullDay = 24 * 3600; if ( info.m_timestamp > fullDay ) { // wrapped past midnight info.m_timestamp -= fullDay; } // eat "HALLOWEEN" token = strtok( NULL, ": " ); if ( !token ) break; // get the rest of the line token = strtok( NULL, "" ); if ( !token ) break; // skip trailing space ++token; if ( !strncmp( "eyeball_spawn", token, 13 ) ) { // eyeball_spawn (max_health 8000) (player_count 10) info.m_type = EYEBALL_SPAWN; // eat 'eyeball_spawn' token = strtok( token, " " ); if ( !token ) break; if ( !parse_strtok_int( "max_health", &info.m_maxHealth ) ) break; if ( !parse_strtok_int( "player_count", &info.m_playerCount ) ) break; } else if ( !strncmp( "eyeball_escaped", token, 15 ) ) { // eyeball_escaped (max_dps 143.64) (health 1525) info.m_type = EYEBALL_ESCAPE; // eat 'eyeball_escape' token = strtok( token, " " ); if ( !token ) break; if ( !parse_strtok_int( "max_dps", &info.m_maxDPS ) ) break; if ( !parse_strtok_int( "health", &info.m_health ) ) break; } else if ( !strncmp( "eyeball_death", token, 13 ) ) { // eyeball_death (max_dps 285.54) (max_health 13200) (player_count 24) info.m_type = EYEBALL_DEATH; // eat 'eyeball_death' token = strtok( token, " " ); if ( !token ) break; if ( !parse_strtok_int( "max_dps", &info.m_maxDPS ) ) break; if ( !parse_strtok_int( "max_health", &info.m_maxHealth ) ) break; if ( !parse_strtok_int( "player_count", &info.m_playerCount ) ) break; } else if ( token[0] == '"' ) { char *data = ParsePlayer( &info, token ); if ( data ) { token = strtok( data, " " ); if ( !token ) continue; if ( !strncmp( "purgatory_escaped", token, 17 ) ) { info.m_type = PURGATORY_ESCAPE; } else if ( !strcmp( "purgatory_teleport", token ) ) { token = strtok( NULL, "\" " ); if ( !token ) continue; if ( !strcmp( "spawn_purgatory", token ) ) { info.m_type = PURGATORY_ENTER; } else if ( !strcmp( "spawn_loot", token ) ) { info.m_type = LOOT_ISLAND_ENTER; } else { fprintf( errorFP, "ERROR @ Line %d: Unknown purgatory teleport '%s'\n", line, token ); continue; } } else if ( !strcmp( "eyeball_stunned", token ) ) { // eyeball_stunned with "short_stop" (attacker_position "-1951 -166 256") info.m_type = EYEBALL_STUN; // eat 'with' token = strtok( NULL, " " ); token = strtok( NULL, "\" " ); if ( !token ) continue; strcpy( info.m_weaponName, token ); // eat (attacker_position token = strtok( NULL, " " ); token = strtok( NULL, "\" " ); if ( !token ) continue; info.m_attackerPosX = atoi( token ); token = strtok( NULL, "\" " ); if ( !token ) continue; info.m_attackerPosY = atoi( token ); token = strtok( NULL, "\" " ); if ( !token ) continue; info.m_attackerPosZ = atoi( token ); } else if ( !strcmp( "eyeball_killer", token ) ) { // eyeball_killer with "short_stop" (attacker_position "-1213 271 236") info.m_type = EYEBALL_KILLER; // eat 'with' token = strtok( NULL, " " ); token = strtok( NULL, "\" " ); if ( !token ) continue; strcpy( info.m_weaponName, token ); // eat (attacker_position token = strtok( NULL, " " ); token = strtok( NULL, "\" " ); if ( !token ) continue; info.m_attackerPosX = atoi( token ); token = strtok( NULL, "\" " ); if ( !token ) continue; info.m_attackerPosY = atoi( token ); token = strtok( NULL, "\" " ); if ( !token ) continue; info.m_attackerPosZ = atoi( token ); } else { fprintf( errorFP, "ERROR at Line %d: Unknown player event '%s'\n", line, token ); } } } else { fprintf( errorFP, "ERROR at Line %d: Unknown data '%s'\n", line, token ); continue; } TheData.push_back( info ); } fclose( fp ); //---------------------------------------- unsigned int bossSpawnCount = 0; unsigned int bossEscapedCount = 0; unsigned int bossDeathCount = 0; unsigned int bossSpawnTimestamp = 0; unsigned int bossDroppedCount = 0; std::vector< int > bossLifetime; std::vector< int > bossEscapeLifetime; std::vector< int > bossDeathLifetime; std::vector< int > bossStunCount; bool isBossAlive = false; for( unsigned int i=0; i maxTime ) { maxTime = bossLifetime[i]; } avgTime += bossLifetime[i]; } fprintf( fp, "\n" ); #else fprintf( fp, "Lifetime, " ); for( unsigned int i=0; i maxTime ) { maxTime = bossLifetime[i]; } avgTime += bossLifetime[i]; } fprintf( fp, "\n" ); fprintf( fp, "EscapeLifetime, " ); for( unsigned int i=0; i 0 ) fprintf( fp, "%d, ", bossEscapeLifetime[i] ); } fprintf( fp, "\n" ); fprintf( fp, "DeathLifetime, " ); for( unsigned int i=0; i 0 ) fprintf( fp, "%d, ", bossDeathLifetime[i] ); } fprintf( fp, "\n" ); fprintf( fp, "StunCount, " ); for( unsigned int i=0; i\n", argv[0] ); return -1; } for( int i=1; i