Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 1st, 2012  |  syntax: None  |  size: 19.39 KB  |  hits: 18  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*
  2.  
  3.    Copyright [2008] [Trevor Hogan]
  4.  
  5.    Licensed under the Apache License, Version 2.0 (the "License");
  6.    you may not use this file except in compliance with the License.
  7.    You may obtain a copy of the License at
  8.  
  9.        http://www.apache.org/licenses/LICENSE-2.0
  10.  
  11.    Unless required by applicable law or agreed to in writing, software
  12.    distributed under the License is distributed on an "AS IS" BASIS,
  13.    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.    See the License for the specific language governing permissions and
  15.    limitations under the License.
  16.  
  17.    CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/
  18.  
  19. */
  20.  
  21. #include "ghost.h"
  22. #include "util.h"
  23. #include "ghostdb.h"
  24. #include "gameplayer.h"
  25. #include "gameprotocol.h"
  26. #include "game_base.h"
  27. #include "stats.h"
  28. #include "statsdota.h"
  29.  
  30. //
  31. // CStatsDOTA
  32. //
  33.  
  34. CStatsDOTA :: CStatsDOTA( CBaseGame *nGame ) : CStats( nGame )
  35. {                      
  36.         CONSOLE_Print( "[STATSDOTA] using dota stats" );
  37.  
  38.         for( unsigned int i = 0; i < 12; i++ )
  39.                 m_Players[i] = NULL;
  40.  
  41.         m_Winner = 0;
  42.         m_Min = 0;
  43.         m_Sec = 0;
  44. }
  45.  
  46. CStatsDOTA :: ~CStatsDOTA( )
  47. {
  48.         for( unsigned int i = 0; i < 12; i++ )
  49.         {
  50.                 if( m_Players[i] )
  51.                         delete m_Players[i];
  52.         }
  53. }
  54.  
  55. bool CStatsDOTA :: ProcessAction( CIncomingAction *Action )
  56. {
  57.         unsigned int i = 0;
  58.         BYTEARRAY *ActionData = Action->GetAction( );
  59.         BYTEARRAY Data;
  60.         BYTEARRAY Key;
  61.         BYTEARRAY Value;
  62.  
  63.         // dota actions with real time replay data start with 0x6b then the null terminated string "dr.x"
  64.         // unfortunately more than one action can be sent in a single packet and the length of each action isn't explicitly represented in the packet
  65.         // so we have to either parse all the actions and calculate the length based on the type or we can search for an identifying sequence
  66.         // parsing the actions would be more correct but would be a lot more difficult to write for relatively little gain
  67.         // so we take the easy route (which isn't always guaranteed to work) and search the data for the sequence "6b 64 72 2e 78 00" and hope it identifies an action
  68.  
  69.         while( ActionData->size( ) >= i + 6 )
  70.         {
  71.                 if( (*ActionData)[i] == 0x6b && (*ActionData)[i + 1] == 0x64 && (*ActionData)[i + 2] == 0x72 && (*ActionData)[i + 3] == 0x2e && (*ActionData)[i + 4] == 0x78 && (*ActionData)[i + 5] == 0x00 )
  72.                 {
  73.                         // we think we've found an action with real time replay data (but we can't be 100% sure)
  74.                         // next we parse out two null terminated strings and a 4 byte integer
  75.  
  76.                         if( ActionData->size( ) >= i + 7 )
  77.                         {
  78.                                 // the first null terminated string should either be the strings "Data" or "Global" or a player id in ASCII representation, e.g. "1" or "2"
  79.  
  80.                                 Data = UTIL_ExtractCString( *ActionData, i + 6 );
  81.  
  82.                                 if( ActionData->size( ) >= i + 8 + Data.size( ) )
  83.                                 {
  84.                                         // the second null terminated string should be the key
  85.  
  86.                                         Key = UTIL_ExtractCString( *ActionData, i + 7 + Data.size( ) );
  87.  
  88.                                         if( ActionData->size( ) >= i + 12 + Data.size( ) + Key.size( ) )
  89.                                         {
  90.                                                 // the 4 byte integer should be the value
  91.  
  92.                                                 Value = BYTEARRAY( ActionData->begin( ) + i + 8 + Data.size( ) + Key.size( ), ActionData->begin( ) + i + 12 + Data.size( ) + Key.size( ) );
  93.                                                 string DataString = string( Data.begin( ), Data.end( ) );
  94.                                                 string KeyString = string( Key.begin( ), Key.end( ) );
  95.                                                 uint32_t ValueInt = UTIL_ByteArrayToUInt32( Value, false );
  96.  
  97.                                                 // CONSOLE_Print( "[STATS] " + DataString + ", " + KeyString + ", " + UTIL_ToString( ValueInt ) );
  98.  
  99.                                                 if( DataString == "Data" )
  100.                                                 {
  101.                                                         // these are received during the game
  102.                                                         // you could use these to calculate killing sprees and double or triple kills (you'd have to make up your own time restrictions though)
  103.                                                         // you could also build a table of "who killed who" data
  104.  
  105.                                                         if( KeyString.size( ) >= 5 && KeyString.substr( 0, 4 ) == "Hero" )
  106.                                                         {
  107.                                                                 // a hero died
  108.  
  109.                                                                 string VictimColourString = KeyString.substr( 4 );
  110.                                                                 uint32_t VictimColour = UTIL_ToUInt32( VictimColourString );
  111.                                                                 CGamePlayer *Killer = m_Game->GetPlayerFromColour( ValueInt );
  112.                                                                 CGamePlayer *Victim = m_Game->GetPlayerFromColour( VictimColour );
  113.                                                                 if( Killer && Victim )
  114.                                                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] player [" + Killer->GetName( ) + "] killed player [" + Victim->GetName( ) + "]" );
  115.                                                                 else if( Victim )
  116.                                                                 {
  117.                                                                         if( ValueInt == 0 )
  118.                                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Sentinel killed player [" + Victim->GetName( ) + "]" );
  119.                                                                         else if( ValueInt == 6 )
  120.                                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Scourge killed player [" + Victim->GetName( ) + "]" );
  121.                                                                
  122.                                                                 }
  123.                                                                 else if ( Killer && ! Victim )
  124.                                                                 {
  125.                                                                         // a leaver was killed
  126.                                                                         CDBDotAPlayer *LeftPlayerWhoWasKilled = GetDBPlayerFromColour( VictimColour );
  127.                                                                         if( LeftPlayerWhoWasKilled )
  128.                                                                                 LeftPlayerWhoWasKilled->AddLeaverDeath( );
  129.                                                                
  130.                                                                 }
  131.                                                                 else if ( Victim && ! Killer )
  132.                                                                 {
  133.                                                                         // someone used a leaver to finish off a player ("zeus ulti")
  134.                                                                         CDBDotAPlayer *LeftPlayerWhoWasUsedToKill = GetDBPlayerFromColour( VictimColour );
  135.  
  136.                                                                                        
  137.                                                                 }
  138.                                                         }
  139.                                                         else if( KeyString.size( ) >= 8 && KeyString.substr( 0, 7 ) == "Courier" )
  140.                                                         {
  141.                                                                 // a courier died
  142.  
  143.                                                                 if( ( ValueInt >= 1 && ValueInt <= 5 ) || ( ValueInt >= 7 && ValueInt <= 11 ) )
  144.                                                                 {
  145.                                                                         if( !m_Players[ValueInt] )
  146.                                                                                 m_Players[ValueInt] = new CDBDotAPlayer( );
  147.  
  148.                                                                         m_Players[ValueInt]->SetCourierKills( m_Players[ValueInt]->GetCourierKills( ) + 1 );
  149.                                                                 }
  150.  
  151.                                                                 string VictimColourString = KeyString.substr( 7 );
  152.                                                                 uint32_t VictimColour = UTIL_ToUInt32( VictimColourString );
  153.                                                                 CGamePlayer *Killer = m_Game->GetPlayerFromColour( ValueInt );
  154.                                                                 CGamePlayer *Victim = m_Game->GetPlayerFromColour( VictimColour );
  155.  
  156.                                                                 if( Killer && Victim )
  157.                                                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] player [" + Killer->GetName( ) + "] killed a courier owned by player [" + Victim->GetName( ) + "]" );
  158.                                                                 else if( Victim )
  159.                                                                 {
  160.                                                                         if( ValueInt == 0 )
  161.                                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Sentinel killed a courier owned by player [" + Victim->GetName( ) + "]" );
  162.                                                                         else if( ValueInt == 6 )
  163.                                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Scourge killed a courier owned by player [" + Victim->GetName( ) + "]" );
  164.                                                                 }
  165.                                                         }
  166.                                                         else if( KeyString.size( ) >= 8 && KeyString.substr( 0, 5 ) == "Tower" )
  167.                                                         {
  168.                                                                 // a tower died
  169.  
  170.                                                                 if( ( ValueInt >= 1 && ValueInt <= 5 ) || ( ValueInt >= 7 && ValueInt <= 11 ) )
  171.                                                                 {
  172.                                                                         if( !m_Players[ValueInt] )
  173.                                                                                 m_Players[ValueInt] = new CDBDotAPlayer( );
  174.  
  175.                                                                         m_Players[ValueInt]->SetTowerKills( m_Players[ValueInt]->GetTowerKills( ) + 1 );
  176.                                                                 }
  177.  
  178.                                                                 string Alliance = KeyString.substr( 5, 1 );
  179.                                                                 string Level = KeyString.substr( 6, 1 );
  180.                                                                 string Side = KeyString.substr( 7, 1 );
  181.                                                                 CGamePlayer *Killer = m_Game->GetPlayerFromColour( ValueInt );
  182.                                                                 string AllianceString;
  183.                                                                 string SideString;
  184.  
  185.                                                                 if( Alliance == "0" )
  186.                                                                         AllianceString = "Sentinel";
  187.                                                                 else if( Alliance == "1" )
  188.                                                                         AllianceString = "Scourge";
  189.                                                                 else
  190.                                                                         AllianceString = "unknown";
  191.  
  192.                                                                 if( Side == "0" )
  193.                                                                         SideString = "top";
  194.                                                                 else if( Side == "1" )
  195.                                                                         SideString = "mid";
  196.                                                                 else if( Side == "2" )
  197.                                                                         SideString = "bottom";
  198.                                                                 else
  199.                                                                         SideString = "unknown";
  200.  
  201.                                                                 if( Killer )
  202.                                                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] player [" + Killer->GetName( ) + "] destroyed a level [" + Level + "] " + AllianceString + " tower (" + SideString + ")" );
  203.                                                                 else
  204.                                                                 {
  205.                                                                         if( ValueInt == 0 )
  206.                                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Sentinel destroyed a level [" + Level + "] " + AllianceString + " tower (" + SideString + ")" );
  207.                                                                         else if( ValueInt == 6 )
  208.                                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Scourge destroyed a level [" + Level + "] " + AllianceString + " tower (" + SideString + ")" );
  209.                                                                 }
  210.                                                         }
  211.                                                         else if( KeyString.size( ) >= 6 && KeyString.substr( 0, 3 ) == "Rax" )
  212.                                                         {
  213.                                                                 // a rax died
  214.  
  215.                                                                 if( ( ValueInt >= 1 && ValueInt <= 5 ) || ( ValueInt >= 7 && ValueInt <= 11 ) )
  216.                                                                 {
  217.                                                                         if( !m_Players[ValueInt] )
  218.                                                                                 m_Players[ValueInt] = new CDBDotAPlayer( );
  219.  
  220.                                                                         m_Players[ValueInt]->SetRaxKills( m_Players[ValueInt]->GetRaxKills( ) + 1 );
  221.                                                                 }
  222.  
  223.                                                                 string Alliance = KeyString.substr( 3, 1 );
  224.                                                                 string Side = KeyString.substr( 4, 1 );
  225.                                                                 string Type = KeyString.substr( 5, 1 );
  226.                                                                 CGamePlayer *Killer = m_Game->GetPlayerFromColour( ValueInt );
  227.                                                                 string AllianceString;
  228.                                                                 string SideString;
  229.                                                                 string TypeString;
  230.  
  231.                                                                 if( Alliance == "0" )
  232.                                                                         AllianceString = "Sentinel";
  233.                                                                 else if( Alliance == "1" )
  234.                                                                         AllianceString = "Scourge";
  235.                                                                 else
  236.                                                                         AllianceString = "unknown";
  237.  
  238.                                                                 if( Side == "0" )
  239.                                                                         SideString = "top";
  240.                                                                 else if( Side == "1" )
  241.                                                                         SideString = "mid";
  242.                                                                 else if( Side == "2" )
  243.                                                                         SideString = "bottom";
  244.                                                                 else
  245.                                                                         SideString = "unknown";
  246.  
  247.                                                                 if( Type == "0" )
  248.                                                                         TypeString = "melee";
  249.                                                                 else if( Type == "1" )
  250.                                                                         TypeString = "ranged";
  251.                                                                 else
  252.                                                                         TypeString = "unknown";
  253.  
  254.                                                                 if( Killer )
  255.                                                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] player [" + Killer->GetName( ) + "] destroyed a " + TypeString + " " + AllianceString + " rax (" + SideString + ")" );
  256.                                                                 else
  257.                                                                 {
  258.                                                                         if( ValueInt == 0 )
  259.                                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Sentinel destroyed a " + TypeString + " " + AllianceString + " rax (" + SideString + ")" );
  260.                                                                         else if( ValueInt == 6 )
  261.                                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Scourge destroyed a " + TypeString + " " + AllianceString + " rax (" + SideString + ")" );
  262.                                                                 }
  263.                                                         }
  264.                                                         else if( KeyString.size( ) >= 6 && KeyString.substr( 0, 6 ) == "Throne" )
  265.                                                         {
  266.                                                                 // the frozen throne got hurt
  267.  
  268.                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the Frozen Throne is now at " + UTIL_ToString( ValueInt ) + "% HP" );
  269.                                                         }
  270.                                                         else if( KeyString.size( ) >= 4 && KeyString.substr( 0, 4 ) == "Tree" )
  271.                                                         {
  272.                                                                 // the world tree got hurt
  273.  
  274.                                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] the World Tree is now at " + UTIL_ToString( ValueInt ) + "% HP" );
  275.                                                         }
  276.                                                         else if( KeyString.size( ) >= 2 && KeyString.substr( 0, 2 ) == "CK" )
  277.                                                         {
  278.                                                                 // a player disconnected
  279.                                                         }
  280.                                                 }
  281.                                                 else if( DataString == "Global" )
  282.                                                 {
  283.                                                         // these are only received at the end of the game
  284.  
  285.                                                         if( KeyString == "Winner" )
  286.                                                         {
  287.                                                                 // Value 1 -> sentinel
  288.                                                                 // Value 2 -> scourge
  289.  
  290.                                                                 m_Winner = ValueInt;
  291.  
  292.                                                                 if( m_Winner == 1 )
  293.                                                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] detected winner: Sentinel" );
  294.                                                                 else if( m_Winner == 2 )
  295.                                                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] detected winner: Scourge" );
  296.                                                                 else
  297.                                                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] detected winner: " + UTIL_ToString( ValueInt ) );
  298.                                                         }
  299.                                                         else if( KeyString == "m" )
  300.                                                                 m_Min = ValueInt;
  301.                                                         else if( KeyString == "s" )
  302.                                                                 m_Sec = ValueInt;
  303.                                                 }
  304.                                                 else if( DataString.size( ) <= 2 && DataString.find_first_not_of( "1234567890" ) == string :: npos )
  305.                                                 {
  306.                                                         // these are only received at the start and end of the game, and after a -switch or -swap (colour change)
  307.  
  308.                                                         uint32_t ID = UTIL_ToUInt32( DataString );
  309.  
  310.                                                         if( ( ID >= 0 && ID <= 5 ) || ( ID >= 6 && ID <= 12 ) )
  311.                                                         {
  312.                                                                 if( !m_Players[ID] )
  313.                                                                 {
  314.                                                                         m_Players[ID] = new CDBDotAPlayer( );
  315.                                                                         m_Players[ID]->SetColour( ID );
  316.                                                                 }
  317.  
  318.                                                                 // Key "1"              -> Kills
  319.                                                                 // Key "2"              -> Deaths
  320.                                                                 // Key "3"              -> Creep Kills
  321.                                                                 // Key "4"              -> Creep Denies
  322.                                                                 // Key "5"              -> Assists
  323.                                                                 // Key "6"              -> Current Gold
  324.                                                                 // Key "7"              -> Neutral Kills
  325.                                                                 // Key "8_0"    -> Item 1
  326.                                                                 // Key "8_1"    -> Item 2
  327.                                                                 // Key "8_2"    -> Item 3
  328.                                                                 // Key "8_3"    -> Item 4
  329.                                                                 // Key "8_4"    -> Item 5
  330.                                                                 // Key "8_5"    -> Item 6
  331.                                                                 // Key "id"             -> ID (1-5 for sentinel, 6-10 for scourge, accurate after using -sp and/or -switch)
  332.  
  333.                                                                 if( KeyString == "1" )
  334.                                                                         m_Players[ID]->SetKills( ValueInt );
  335.                                                                 else if( KeyString == "2" )
  336.                                                                         m_Players[ID]->SetDeaths( ValueInt );
  337.                                                                 else if( KeyString == "3" )
  338.                                                                         m_Players[ID]->SetCreepKills( ValueInt );
  339.                                                                 else if( KeyString == "4" )
  340.                                                                         m_Players[ID]->SetCreepDenies( ValueInt );
  341.                                                                 else if( KeyString == "5" )
  342.                                                                         m_Players[ID]->SetAssists( ValueInt );
  343.                                                                 else if( KeyString == "6" )
  344.                                                                         m_Players[ID]->SetGold( ValueInt );
  345.                                                                 else if( KeyString == "7" )
  346.                                                                         m_Players[ID]->SetNeutralKills( ValueInt );
  347.                                                                 else if( KeyString == "8_0" )
  348.                                                                         m_Players[ID]->SetItem( 0, string( Value.rbegin( ), Value.rend( ) ) );
  349.                                                                 else if( KeyString == "8_1" )
  350.                                                                         m_Players[ID]->SetItem( 1, string( Value.rbegin( ), Value.rend( ) ) );
  351.                                                                 else if( KeyString == "8_2" )
  352.                                                                         m_Players[ID]->SetItem( 2, string( Value.rbegin( ), Value.rend( ) ) );
  353.                                                                 else if( KeyString == "8_3" )
  354.                                                                         m_Players[ID]->SetItem( 3, string( Value.rbegin( ), Value.rend( ) ) );
  355.                                                                 else if( KeyString == "8_4" )
  356.                                                                         m_Players[ID]->SetItem( 4, string( Value.rbegin( ), Value.rend( ) ) );
  357.                                                                 else if( KeyString == "8_5" )
  358.                                                                         m_Players[ID]->SetItem( 5, string( Value.rbegin( ), Value.rend( ) ) );
  359.                                                                 else if( KeyString == "9" )
  360.                                                                 {
  361.                                                                         m_Players[ID]->SetHero( string( Value.rbegin( ), Value.rend( ) ) );
  362.                                                                         CONSOLE_Print("[STATSDOTA: " + m_Game->GetGameName( ) + "] Updated hero on player #" + DataString + ", colour " + UTIL_ToString(m_Players[ID]->GetColour( )) + ": " + m_Players[ID]->GetHero( ) );
  363.                                                                 }
  364.                                                                 else if( KeyString == "id" )
  365.                                                                 {
  366.                                                                         // DotA sends id values from 1-10 with 1-5 being sentinel players and 6-10 being scourge players
  367.                                                                         // unfortunately the actual player colours are from 1-5 and from 7-11 so we need to deal with this case here
  368.  
  369.                                                                         if (ID == 0 && ValueInt == 6)
  370.                                                                                 m_Players[ID]->SetNewColour(0);                         // red sentinel (1st in 6v6, replacing the traditional blue)
  371.                                                                         else if (ID == 6 && ValueInt == 12)
  372.                                                                                 m_Players[ID]->SetNewColour(6);                         // green scourge (1st in 6v6, replacing the traditional pink)
  373.                                                                        
  374.                                                                         else if( ValueInt >= 6 )
  375.                                                                                 m_Players[ID]->SetNewColour( ValueInt + 1 );
  376.                                                                         else
  377.                                                                                 m_Players[ID]->SetNewColour( ValueInt );
  378.  
  379.  
  380.                                                                         CONSOLE_Print("[STATSDOTA: " + m_Game->GetGameName( ) + "] Updated colour on player #" + DataString + ", colour " + UTIL_ToString(m_Players[ID]->GetColour( )) + ": " + UTIL_ToString(m_Players[ID]->GetNewColour( )) );
  381.                                                                 }
  382.                                                         }
  383.                                                 }
  384.  
  385.                                                 i += 12 + Data.size( ) + Key.size( );
  386.                                         }
  387.                                         else
  388.                                                 i++;
  389.                                 }
  390.                                 else
  391.                                         i++;
  392.                         }
  393.                         else
  394.                                 i++;
  395.                 }
  396.                 else
  397.                         i++;
  398.         }
  399.  
  400.         return m_Winner != 0;
  401. }
  402.  
  403. void CStatsDOTA :: Save( CGHost *GHost, CGHostDB *DB, uint32_t GameID )
  404. {
  405.         if( DB->Begin( ) )
  406.         {
  407.                 // since we only record the end game information it's possible we haven't recorded anything yet if the game didn't end with a tree/throne death
  408.                 // this will happen if all the players leave before properly finishing the game
  409.                 // the dotagame stats are always saved (with winner = 0 if the game didn't properly finish)
  410.                 // the dotaplayer stats are only saved if the game is properly finished
  411.  
  412.                 unsigned int Players = 0;
  413.  
  414.                 // save the dotagame
  415.  
  416.                 GHost->m_Callables.push_back( DB->ThreadedDotAGameAdd( GameID, m_Winner, m_Min, m_Sec ) );
  417.  
  418.                 // check for invalid colours and duplicates
  419.                 // this can only happen if DotA sends us garbage in the "id" value but we should check anyway
  420.  
  421.                 for( unsigned int i = 0; i < 12; i++ )
  422.                 {
  423.                         if( m_Players[i] )
  424.                         {
  425.                                 uint32_t Colour = m_Players[i]->GetNewColour( );
  426.                                 if( !( ( Colour >= 0 && Colour <= 5 ) || ( Colour >= 6 && Colour <= 12 ) ) )
  427.                                 {
  428.                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] warn: invalid new colour found for player #" + UTIL_ToString( (int) i) + ": " + UTIL_ToString( Colour ) + " (old colour: " + UTIL_ToString( m_Players[i]->GetColour( ) ) + ")" );
  429. //                                      DB->Commit( );
  430. //                                      return;
  431.                                 }
  432.  
  433.                                 for( unsigned int j = i + 1; j < 12; j++ )
  434.                                 {
  435.                                         if( m_Players[j] && Colour == m_Players[j]->GetNewColour( ) )
  436.                                         {
  437.                                                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] discarding player data, duplicate colour found" );
  438.                                                 DB->Commit( );
  439.                                                 return;
  440.                                         }
  441.                                 }
  442.                         }
  443.                 }
  444.  
  445.                 // save the dotaplayers
  446.  
  447.                 unsigned int Leavers = 0;
  448.                 unsigned int SentLeavers = 0, ScourgeLeavers = 0;
  449.  
  450.                 for( unsigned int i = 0; i < 12; i++ )
  451.                 {
  452.                         if( m_Players[i] )
  453.                         {
  454.                                 string Name = "UNKNOWN";
  455.  
  456.                                 CGamePlayer *Player = m_Game->GetPlayerFromColour( m_Players[i]->GetColour( ) );
  457.                                 if( Player == NULL )
  458.                                 {
  459.                                         Leavers++;                             
  460.                                         if( m_Players[i]->GetColour( ) < 6 )
  461.                                                 SentLeavers++;
  462.                                         else
  463.                                                 ScourgeLeavers++;
  464.  
  465.                                         // try to find him in the leavers vector
  466.                                         for( vector<CDBGamePlayer *> :: iterator i = m_DBGamePlayers.begin( ); i != m_DBGamePlayers.end( ); i++ )
  467.                                         {
  468.                                                 if( (*i)->GetColour( ) == m_Players[i]->GetColour( ) )
  469.                                                 {
  470.                                                         Name = (*i)->GetName( );
  471.                                                         break;
  472.                                                 }
  473.                                         }
  474.                                 } else {
  475.                                         Name = Player->GetName( );
  476.                                 }
  477.  
  478.                                 if( Name == "UNKNOWN" )
  479.                                 {
  480.                                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] Could not get player name from m_Game->m_Players or " );
  481.                                 }
  482.  
  483.                                
  484.  
  485.  
  486.                                 GHost->m_Callables.push_back( DB->ThreadedDotAPlayerAdd( GameID, m_Players[i]->GetColour( ), m_Players[i]->GetKills( ), m_Players[i]->GetDeaths( ), m_Players[i]->GetCreepKills( ), m_Players[i]->GetCreepDenies( ), m_Players[i]->GetAssists( ), m_Players[i]->GetGold( ), m_Players[i]->GetNeutralKills( ), m_Players[i]->GetItem( 0 ), m_Players[i]->GetItem( 1 ), m_Players[i]->GetItem( 2 ), m_Players[i]->GetItem( 3 ), m_Players[i]->GetItem( 4 ), m_Players[i]->GetItem( 5 ), m_Players[i]->GetHero( ), m_Players[i]->GetNewColour( ), m_Players[i]->GetTowerKills( ), m_Players[i]->GetRaxKills( ), m_Players[i]->GetCourierKills( ) ) );
  487.                                 Players++;
  488.                         }
  489.                 }
  490.  
  491.                 if( DB->Commit( ) )
  492.                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] saving " + UTIL_ToString( Players ) + " players" );
  493.                 else
  494.                         CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] unable to commit database transaction, data not saved" );
  495.         }
  496.         else
  497.                 CONSOLE_Print( "[STATSDOTA: " + m_Game->GetGameName( ) + "] unable to begin database transaction, data not saved" );
  498. }
  499.  
  500. CDBDotAPlayer *CStatsDOTA :: GetDBPlayerFromColour( unsigned char colour )
  501. {
  502.         for( unsigned int i = 0; i < 12; i++ )
  503.         {
  504.                 if( m_Players[i] )
  505.                 {
  506.                         uint32_t newcolour = m_Players[i]->GetNewColour( );
  507.                         if( newcolour == colour )
  508.                                 return m_Players[i];
  509.                 }
  510.  
  511.         }
  512.  
  513.         return NULL;
  514. }