BYTEARRAY CBNLSProtocol :: RECEIVE_BNLS_WARDEN( BYTEARRAY data ) { // 2 bytes -> Length // 1 byte -> ID // (BYTE) -> Usage // (DWORD) -> Cookie // (BYTE) -> Result // (WORD) -> Length of data // (VOID) -> Data if( ValidateLength( data ) && data.size( ) >= 11 ) { unsigned char Usage = data[3]; uint32_t Cookie = UTIL_ByteArrayToUInt32( data, false, 4 ); unsigned char Result = data[8]; uint16_t Length = UTIL_ByteArrayToUInt16( data, false, 10 ); if( Result == 0x00 ) return BYTEARRAY( data.begin( ) + 11, data.end( ) ); else CONSOLE_Print( "[BNLSPROTO] received error code " + UTIL_ToString( data[8] ) ); } return BYTEARRAY( ); }
void CBNLSClient :: ExtractPackets( ) { string *RecvBuffer = m_Socket->GetBytes( ); BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) ); while( Bytes.size( ) >= 3 ) { uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false ); if( Length >= 3 ) { if( Bytes.size( ) >= Length ) { m_Packets.push( new CCommandPacket( 0, Bytes[2], BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ) ); *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); } else return; } else { CONSOLE_Print( "[BNLSC: " + m_Server + ":" + UTIL_ToString( m_Port ) + ":C" + UTIL_ToString( m_WardenCookie ) + "] error - received invalid packet from BNLS server (bad length), disconnecting" ); m_Socket->Disconnect( ); return; } } }
bool CPotentialPlayer :: Update( void *fd ) { if( m_DeleteMe ) return true; if( !m_Socket ) return false; m_Socket->DoRecv( (fd_set *)fd ); // extract as many packets as possible from the socket's receive buffer and process them string *RecvBuffer = m_Socket->GetBytes( ); BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) ); // a packet is at least 4 bytes so loop as long as the buffer contains 4 bytes while( Bytes.size( ) >= 4 ) { if( Bytes[0] == W3GS_HEADER_CONSTANT || Bytes[0] == GPS_HEADER_CONSTANT ) { // bytes 2 and 3 contain the length of the packet uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 ); if( Length >= 4 ) { if( Bytes.size( ) >= Length ) { if( Bytes[0] == W3GS_HEADER_CONSTANT && Bytes[1] == CGameProtocol :: W3GS_REQJOIN ) { delete m_IncomingJoinPlayer; m_IncomingJoinPlayer = m_Protocol->RECEIVE_W3GS_REQJOIN( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ); if( m_IncomingJoinPlayer ) m_Game->EventPlayerJoined( this, m_IncomingJoinPlayer ); // this is the packet which interests us for now, the remaining is left for CGamePlayer *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); break; } *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); } else break; } } } // don't call DoSend here because some other players may not have updated yet and may generate a packet for this player // also m_Socket may have been set to NULL during ProcessPackets but we're banking on the fact that m_DeleteMe has been set to true as well so it'll short circuit before dereferencing return m_DeleteMe || !m_Socket->GetConnected( ) || m_Socket->HasError( ); }
void CGamePlayer :: ExtractPackets( ) { if( !m_Socket ) return; // extract as many packets as possible from the socket's receive buffer and put them in the m_Packets queue string *RecvBuffer = m_Socket->GetBytes( ); BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) ); // a packet is at least 4 bytes so loop as long as the buffer contains 4 bytes while( Bytes.size( ) >= 4 ) { if( Bytes[0] == W3GS_HEADER_CONSTANT || Bytes[0] == GPS_HEADER_CONSTANT || Bytes[0] == GCBI_HEADER_CONSTANT ) { // bytes 2 and 3 contain the length of the packet uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 ); if( Length >= 4 ) { if( Bytes.size( ) >= Length ) { m_Packets.push( new CCommandPacket( Bytes[0], Bytes[1], BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ) ); if( Bytes[0] == W3GS_HEADER_CONSTANT ) ++m_TotalPacketsReceived; *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); } else return; } else { m_Error = true; m_ErrorString = "received invalid packet from player (bad length)"; return; } } else { m_Error = true; m_ErrorString = "received invalid packet from player (bad header constant)"; return; } } }
bool CGCBIProtocol :: ValidateLength( BYTEARRAY &content ) { // verify that bytes 3 and 4 (indices 2 and 3) of the content array describe the length uint16_t Length; BYTEARRAY LengthBytes; if( content.size( ) >= 4 && content.size( ) <= 65535 ) { LengthBytes.push_back( content[2] ); LengthBytes.push_back( content[3] ); Length = UTIL_ByteArrayToUInt16( LengthBytes, false ); if( Length == content.size( ) ) return true; } return false; }
bool CBNLSProtocol :: ValidateLength( BYTEARRAY &content ) { // verify that bytes 1 and 2 (indices 0 and 1) of the content array describe the length uint16_t Length; BYTEARRAY LengthBytes; if( content.size( ) >= 2 && content.size( ) <= 65535 ) { LengthBytes.push_back( content[0] ); LengthBytes.push_back( content[1] ); Length = UTIL_ByteArrayToUInt16( LengthBytes, false ); if( Length == content.size( ) ) return true; } return false; }
CIncomingGameHost *CBNETProtocol :: RECEIVE_SID_GETADVLISTEX( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED SID_GETADVLISTEX" ); // DEBUG_Print( data ); // 2 bytes -> Header // 2 bytes -> Length // 4 bytes -> GamesFound // if( GamesFound > 0 ) // 10 bytes -> ??? // 2 bytes -> Port // 4 bytes -> IP // null term string -> GameName // 2 bytes -> ??? // 8 bytes -> HostCounter if( ValidateLength( data ) && data.size( ) >= 8 ) { BYTEARRAY GamesFound = BYTEARRAY( data.begin( ) + 4, data.begin( ) + 8 ); if( UTIL_ByteArrayToUInt32( GamesFound, false ) > 0 && data.size( ) >= 25 ) { BYTEARRAY Port = BYTEARRAY( data.begin( ) + 18, data.begin( ) + 20 ); BYTEARRAY IP = BYTEARRAY( data.begin( ) + 20, data.begin( ) + 24 ); BYTEARRAY GameName = UTIL_ExtractCString( data, 24 ); if( data.size( ) >= GameName.size( ) + 35 ) { BYTEARRAY HostCounter; HostCounter.push_back( UTIL_ExtractHex( data, GameName.size( ) + 27, true ) ); HostCounter.push_back( UTIL_ExtractHex( data, GameName.size( ) + 29, true ) ); HostCounter.push_back( UTIL_ExtractHex( data, GameName.size( ) + 31, true ) ); HostCounter.push_back( UTIL_ExtractHex( data, GameName.size( ) + 33, true ) ); return new CIncomingGameHost( IP, UTIL_ByteArrayToUInt16( Port, false ), string( GameName.begin( ), GameName.end( ) ), HostCounter ); } } } return NULL; }
CIncomingGameHost *CBNETProtocol :: RECEIVE_SID_GETADVLISTEX( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED SID_GETADVLISTEX" ); // DEBUG_Print( data ); // 2 bytes -> Header // 2 bytes -> Length // 4 bytes -> GamesFound // if( GamesFound > 0 ) // 10 bytes -> ??? // 2 bytes -> Port // 4 bytes -> IP // null term string -> GameName // 2 bytes -> ??? // 8 bytes -> HostCounter if( ValidateLength( data ) && data.size( ) >= 8 ) { BYTEARRAY GamesFound = BYTEARRAY( data.begin( ) + 4, data.begin( ) + 8 ); /* if (UTIL_ByteArrayToUInt32( GamesFound, false )>1) { string sIP =""; string GN =""; uint32_t PR; uint32_t GF = UTIL_ByteArrayToUInt32( GamesFound, false ); uint32_t Idx = 18; uint32_t Idx2 = 0; for (uint32_t i=0;i<GF;i++) { BYTEARRAY Port = BYTEARRAY( data.begin( ) + Idx, data.begin( ) + 2+Idx ); BYTEARRAY IP = BYTEARRAY( data.begin( ) + 2+Idx, data.begin( ) + 6+Idx ); BYTEARRAY GameName = UTIL_ExtractCString( data, 6+Idx ); Idx2 = GameName.size( ); Idx = Idx + Idx2+ 11; GN = string( GameName.begin( ), GameName.end( ) ); PR = UTIL_ByteArrayToUInt16( Port, false ); CONSOLE_Print("[GHOST] Game "+UTIL_ToString(i)+ " "+ GN + " "+sIP+" "+UTIL_ToString(PR)); } } else */ if( UTIL_ByteArrayToUInt32( GamesFound, false ) > 0 && data.size( ) >= 25 ) { BYTEARRAY Port = BYTEARRAY( data.begin( ) + 18, data.begin( ) + 20 ); BYTEARRAY IP = BYTEARRAY( data.begin( ) + 20, data.begin( ) + 24 ); BYTEARRAY GameName = UTIL_ExtractCString( data, 24 ); if( data.size( ) >= GameName.size( ) + 35 ) { BYTEARRAY HostCounter; HostCounter.push_back( UTIL_ExtractHex( data, GameName.size( ) + 27, true ) ); HostCounter.push_back( UTIL_ExtractHex( data, GameName.size( ) + 29, true ) ); HostCounter.push_back( UTIL_ExtractHex( data, GameName.size( ) + 31, true ) ); HostCounter.push_back( UTIL_ExtractHex( data, GameName.size( ) + 33, true ) ); return new CIncomingGameHost( IP, UTIL_ByteArrayToUInt16( Port, false ), string( GameName.begin( ), GameName.end( ) ), HostCounter ); } } } return NULL; }
void CReplay :: BuildReplay( string gameName, string statString ) { CONSOLE_Print( "[REPLAY] building replay" ); uint32_t LanguageID = 0x0012F8B0; BYTEARRAY Replay; Replay.push_back( 16 ); // Unknown (4.0) Replay.push_back( 1 ); // Unknown (4.0) Replay.push_back( 0 ); // Unknown (4.0) Replay.push_back( 0 ); // Unknown (4.0) Replay.push_back( 0 ); // Host RecordID (4.1) Replay.push_back( m_HostPID ); // Host PlayerID (4.1) UTIL_AppendByteArray( Replay, m_HostName ); // Host PlayerName (4.1) Replay.push_back( 1 ); // Host AdditionalSize (4.1) Replay.push_back( 0 ); // Host AdditionalData (4.1) UTIL_AppendByteArray( Replay, gameName ); // GameName (4.2) Replay.push_back( 0 ); // Null (4.0) UTIL_AppendByteArray( Replay, statString ); // StatString (4.3) UTIL_AppendByteArray( Replay, (uint32_t)m_Slots.size( ), false ); // PlayerCount (4.6) Replay.push_back( m_MapGameType ); // GameType (4.7) Replay.push_back( 32 ); // GameType (4.7) Replay.push_back( 73 ); // GameType (4.7) Replay.push_back( 0 ); // GameType (4.7) UTIL_AppendByteArray( Replay, LanguageID, false ); // LanguageID (4.8) // PlayerList (4.9) for( vector<ReplayPlayer> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ ) { if( (*i).first != m_HostPID ) { Replay.push_back( 22 ); // Player RecordID (4.1) Replay.push_back( (*i).first ); // Player PlayerID (4.1) UTIL_AppendByteArray( Replay, (*i).second ); // Player PlayerName (4.1) Replay.push_back( 1 ); // Player AdditionalSize (4.1) Replay.push_back( 0 ); // Player AdditionalData (4.1) UTIL_AppendByteArray( Replay, (uint32_t)0, false ); // Unknown } } // GameStartRecord (4.10) Replay.push_back( 25 ); // RecordID (4.10) UTIL_AppendByteArray( Replay, (uint16_t)( 7 + m_Slots.size( ) * 9 ), false ); // Size (4.10) Replay.push_back( m_Slots.size( ) ); // NumSlots (4.10) for( unsigned char i = 0; i < m_Slots.size( ); i++ ) UTIL_AppendByteArray( Replay, m_Slots[i].GetByteArray( ) ); UTIL_AppendByteArray( Replay, m_RandomSeed, false ); // RandomSeed (4.10) Replay.push_back( m_SelectMode ); // SelectMode (4.10) Replay.push_back( m_StartSpotCount ); // StartSpotCount (4.10) // ReplayData (5.0) Replay.push_back( REPLAY_FIRSTSTARTBLOCK ); UTIL_AppendByteArray( Replay, (uint32_t)1, false ); Replay.push_back( REPLAY_SECONDSTARTBLOCK ); UTIL_AppendByteArray( Replay, (uint32_t)1, false ); // leavers during loading need to be stored between the second and third start blocks while( !m_LoadingBlocks.empty( ) ) { UTIL_AppendByteArray( Replay, m_LoadingBlocks.front( ) ); m_LoadingBlocks.pop( ); } Replay.push_back( REPLAY_THIRDSTARTBLOCK ); UTIL_AppendByteArray( Replay, (uint32_t)1, false ); // initialize replay length to zero // we'll accumulate the replay length as we iterate through the timeslots // this is necessary because we might be discarding some timeslots due to not enough checksums and the replay length needs to be accurate m_ReplayLength = 0; uint32_t TimeSlotsDiscarded = 0; while( !m_Blocks.empty( ) ) { BYTEARRAY Block = m_Blocks.front( ); m_Blocks.pop( ); if( Block.size( ) >= 5 && Block[0] == REPLAY_TIMESLOT ) { if( m_CheckSums.empty( ) ) { TimeSlotsDiscarded++; continue; } // append timeslot UTIL_AppendByteArray( Replay, Block ); // append checksum BYTEARRAY CheckSum; CheckSum.push_back( REPLAY_CHECKSUM ); CheckSum.push_back( 4 ); UTIL_AppendByteArray( CheckSum, m_CheckSums.front( ), false ); m_CheckSums.pop( ); UTIL_AppendByteArray( Replay, CheckSum ); // accumulate replay length m_ReplayLength += UTIL_ByteArrayToUInt16( Block, false, 3 ); } else UTIL_AppendByteArray( Replay, Block ); } if( TimeSlotsDiscarded > 0 ) CONSOLE_Print( "[REPLAY] ran out of checksums, discarded " + UTIL_ToString( TimeSlotsDiscarded ) + " timeslots" ); // done m_Decompressed = string( Replay.begin( ), Replay.end( ) ); }
void CReplay :: BuildReplay( string gameName, string statString, uint32_t war3Version, uint16_t buildNumber ) { m_War3Version = war3Version; m_BuildNumber = buildNumber; m_Flags = 32768; CONSOLE_Print( "[REPLAY] building replay" ); uint32_t LanguageID = 0x0012F8B0; BYTEARRAY Replay; Replay.push_back( 16 ); // Unknown (4.0) Replay.push_back( 1 ); // Unknown (4.0) Replay.push_back( 0 ); // Unknown (4.0) Replay.push_back( 0 ); // Unknown (4.0) Replay.push_back( 0 ); // Host RecordID (4.1) Replay.push_back( m_HostPID ); // Host PlayerID (4.1) UTIL_AppendByteArrayFast( Replay, m_HostName ); // Host PlayerName (4.1) Replay.push_back( 1 ); // Host AdditionalSize (4.1) Replay.push_back( 0 ); // Host AdditionalData (4.1) UTIL_AppendByteArrayFast( Replay, gameName ); // GameName (4.2) Replay.push_back( 0 ); // Null (4.0) UTIL_AppendByteArrayFast( Replay, statString ); // StatString (4.3) UTIL_AppendByteArray( Replay, (uint32_t)m_Slots.size( ), false ); // PlayerCount (4.6) Replay.push_back( m_MapGameType ); // GameType (4.7) Replay.push_back( 32 ); // GameType (4.7) Replay.push_back( 73 ); // GameType (4.7) Replay.push_back( 0 ); // GameType (4.7) UTIL_AppendByteArray( Replay, LanguageID, false ); // LanguageID (4.8) // PlayerList (4.9) for( vector<PIDPlayer> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ ) { if( (*i).first != m_HostPID ) { Replay.push_back( 22 ); // Player RecordID (4.1) Replay.push_back( (*i).first ); // Player PlayerID (4.1) UTIL_AppendByteArrayFast( Replay, (*i).second ); // Player PlayerName (4.1) Replay.push_back( 1 ); // Player AdditionalSize (4.1) Replay.push_back( 0 ); // Player AdditionalData (4.1) UTIL_AppendByteArray( Replay, (uint32_t)0, false ); // Unknown } } // GameStartRecord (4.10) Replay.push_back( 25 ); // RecordID (4.10) UTIL_AppendByteArray( Replay, (uint16_t)( 7 + m_Slots.size( ) * 9 ), false ); // Size (4.10) Replay.push_back( m_Slots.size( ) ); // NumSlots (4.10) for( unsigned char i = 0; i < m_Slots.size( ); i++ ) UTIL_AppendByteArray( Replay, m_Slots[i].GetByteArray( ) ); UTIL_AppendByteArray( Replay, m_RandomSeed, false ); // RandomSeed (4.10) Replay.push_back( m_SelectMode ); // SelectMode (4.10) Replay.push_back( m_StartSpotCount ); // StartSpotCount (4.10) // ReplayData (5.0) Replay.push_back( REPLAY_FIRSTSTARTBLOCK ); UTIL_AppendByteArray( Replay, (uint32_t)1, false ); Replay.push_back( REPLAY_SECONDSTARTBLOCK ); UTIL_AppendByteArray( Replay, (uint32_t)1, false ); // leavers during loading need to be stored between the second and third start blocks while( !m_LoadingBlocks.empty( ) ) { UTIL_AppendByteArray( Replay, m_LoadingBlocks.front( ) ); m_LoadingBlocks.pop( ); } Replay.push_back( REPLAY_THIRDSTARTBLOCK ); UTIL_AppendByteArray( Replay, (uint32_t)1, false ); // initialize replay length to zero // we'll accumulate the replay length as we iterate through the timeslots // this is necessary because we might be discarding some timeslots due to not enough checksums and the replay length needs to be accurate m_ReplayLength = 0; uint32_t TimeSlotsDiscarded = 0; bool EndOfTimeSlots = false; while( !m_Blocks.empty( ) ) { BYTEARRAY Block = m_Blocks.front( ); m_Blocks.pop( ); if( Block.size( ) >= 5 && Block[0] == REPLAY_TIMESLOT ) { uint16_t TimeIncrement = UTIL_ByteArrayToUInt16( Block, false, 3 ); if( TimeIncrement != 0 && m_CheckSums.empty( ) ) EndOfTimeSlots = true; if( EndOfTimeSlots ) { TimeSlotsDiscarded++; continue; } // append timeslot UTIL_AppendByteArrayFast( Replay, Block ); // append checksum // todotodo: after experimenting, Strilanc discovered that checksums are NOT required in replays // we could optimize saving of replays by building a complete stream without waiting for checksums as the game progresses // alternatively, we could build that stream as the checksums were added if we wanted to keep them // rather than building it in one go right now, which might take a few hundred ms and cause a spike in other games if( TimeIncrement != 0 ) { BYTEARRAY CheckSum; CheckSum.reserve( 6 ); CheckSum.push_back( REPLAY_CHECKSUM ); CheckSum.push_back( 4 ); UTIL_AppendByteArray( CheckSum, m_CheckSums.front( ), false ); m_CheckSums.pop( ); UTIL_AppendByteArrayFast( Replay, CheckSum ); } // accumulate replay length m_ReplayLength += TimeIncrement; } else UTIL_AppendByteArrayFast( Replay, Block ); } if( TimeSlotsDiscarded > 0 ) CONSOLE_Print( "[REPLAY] ran out of checksums, discarded " + UTIL_ToString( TimeSlotsDiscarded ) + " timeslots" ); // done m_Decompressed = string( Replay.begin( ), Replay.end( ) ); }
bool CGamePlayer :: Update( void *fd ) { uint32_t Time = GetTime( ); // wait 4 seconds after joining before sending the /whois or /w // if we send the /whois too early battle.net may not have caught up with where the player is and return erroneous results if( m_WhoisShouldBeSent && !m_Spoofed && !m_WhoisSent && !m_JoinedRealm.empty( ) && Time - m_JoinTime >= 4 ) { // todotodo: we could get kicked from battle.net for sending a command with invalid characters, do some basic checking for( vector<CBNET *> :: iterator i = m_Game->m_Aura->m_BNETs.begin( ); i != m_Game->m_Aura->m_BNETs.end( ); ++i ) { if( (*i)->GetServer( ) == m_JoinedRealm ) { if( m_Game->GetGameState( ) == GAME_PUBLIC || (*i)->GetPasswordHashType( ) == "pvpgn" ) (*i)->QueueChatCommand( "/whois " + m_Name ); else if( m_Game->GetGameState( ) == GAME_PRIVATE ) (*i)->QueueChatCommand( m_Game->m_Aura->m_Language->SpoofCheckByReplying( ), m_Name, true, false ); } } m_WhoisSent = true; } // check for socket timeouts // if we don't receive anything from a player for 35 seconds we can assume they've dropped // this works because in the lobby we send pings every 5 seconds and expect a response to each one // and in the game the Warcraft 3 client sends keepalives frequently (at least once per second it looks like) if( m_Socket && Time - m_Socket->GetLastRecv( ) >= 35 ) m_Game->EventPlayerDisconnectTimedOut( this ); // GProxy++ acks if( m_GProxy && Time - m_LastGProxyAckTime >= 10 ) { if( m_Socket ) m_Socket->PutBytes( m_Game->m_Aura->m_GPSProtocol->SEND_GPSS_ACK( m_TotalPacketsReceived ) ); m_LastGProxyAckTime = Time; } // base class update if( m_Socket->GetConnected( ) ) m_Socket->DoRecv( (fd_set *)fd ); // extract as many packets as possible from the socket's receive buffer and process them string *RecvBuffer = m_Socket->GetBytes( ); BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) ); // a packet is at least 4 bytes so loop as long as the buffer contains 4 bytes CIncomingAction *Action = NULL; CIncomingChatPlayer *ChatPlayer = NULL; CIncomingMapSize *MapSize = NULL; uint32_t CheckSum = 0, Pong = 0; while( Bytes.size( ) >= 4 ) { if( Bytes[0] == W3GS_HEADER_CONSTANT ) { // bytes 2 and 3 contain the length of the packet uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 ); if( Length >= 4 ) { if( Bytes.size( ) >= Length ) { // m_Packets.push( new CCommandPacket( Bytes[0], Bytes[1], BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ) ); switch( Bytes[1] ) { case CGameProtocol :: W3GS_LEAVEGAME: m_Game->EventPlayerLeft( this, m_Protocol->RECEIVE_W3GS_LEAVEGAME( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ) ); break; case CGameProtocol :: W3GS_GAMELOADED_SELF: if( m_Protocol->RECEIVE_W3GS_GAMELOADED_SELF( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ) ) { if( !m_FinishedLoading ) { m_FinishedLoading = true; m_FinishedLoadingTicks = GetTicks( ); m_Game->EventPlayerLoaded( this ); } } break; case CGameProtocol :: W3GS_OUTGOING_ACTION: Action = m_Protocol->RECEIVE_W3GS_OUTGOING_ACTION( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ), m_PID ); if( Action ) m_Game->EventPlayerAction( this, Action ); // don't delete Action here because the game is going to store it in a queue and delete it later break; case CGameProtocol :: W3GS_OUTGOING_KEEPALIVE: CheckSum = m_Protocol->RECEIVE_W3GS_OUTGOING_KEEPALIVE( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ); m_CheckSums.push( CheckSum ); ++m_SyncCounter; m_Game->EventPlayerKeepAlive( ); break; case CGameProtocol :: W3GS_CHAT_TO_HOST: ChatPlayer = m_Protocol->RECEIVE_W3GS_CHAT_TO_HOST( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ); if( ChatPlayer ) m_Game->EventPlayerChatToHost( this, ChatPlayer ); delete ChatPlayer; ChatPlayer = NULL; break; case CGameProtocol :: W3GS_DROPREQ: if( !m_DropVote ) { m_DropVote = true; m_Game->EventPlayerDropRequest( this ); } break; case CGameProtocol :: W3GS_MAPSIZE: MapSize = m_Protocol->RECEIVE_W3GS_MAPSIZE( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ), m_Game->m_Aura->m_Map->GetMapSize( ) ); if( MapSize ) m_Game->EventPlayerMapSize( this, MapSize ); delete MapSize; MapSize = NULL; break; case CGameProtocol :: W3GS_PONG_TO_HOST: Pong = m_Protocol->RECEIVE_W3GS_PONG_TO_HOST( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ); // we discard pong values of 1 // the client sends one of these when connecting plus we return 1 on error to kill two birds with one stone if( Pong != 1 ) { // we also discard pong values when we're downloading because they're almost certainly inaccurate // this statement also gives the player a 5 second grace period after downloading the map to allow queued (i.e. delayed) ping packets to be ignored if( !m_DownloadStarted || ( m_DownloadFinished && GetTime( ) - m_FinishedDownloadingTime >= 5 ) ) { // we also discard pong values when anyone else is downloading if we're configured to if( !m_Game->IsDownloading( ) ) { m_Pings.push_back( GetTicks( ) - Pong ); if( m_Pings.size( ) > 20 ) m_Pings.erase( m_Pings.begin( ) ); } } } m_Game->EventPlayerPongToHost( this, Pong ); break; } *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); } else break; } } else if( Bytes[0] == GPS_HEADER_CONSTANT ) { uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 ); if( Length >= 4 ) { if( Bytes.size( ) >= Length ) { BYTEARRAY Data = BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ); if( Bytes[1] == CGPSProtocol :: GPS_ACK && Data.size( ) == 8 ) { uint32_t LastPacket = UTIL_ByteArrayToUInt32( Data, false, 4 ); uint32_t PacketsAlreadyUnqueued = m_TotalPacketsSent - m_GProxyBuffer.size( ); if( LastPacket > PacketsAlreadyUnqueued ) { uint32_t PacketsToUnqueue = LastPacket - PacketsAlreadyUnqueued; if( PacketsToUnqueue > m_GProxyBuffer.size( ) ) PacketsToUnqueue = m_GProxyBuffer.size( ); while( PacketsToUnqueue > 0 ) { m_GProxyBuffer.pop( ); --PacketsToUnqueue; } } } else if( Bytes[1] == CGPSProtocol :: GPS_INIT ) { if( m_Game->m_Aura->m_Reconnect ) { m_GProxy = true; m_Socket->PutBytes( m_Game->m_Aura->m_GPSProtocol->SEND_GPSS_INIT( m_Game->m_Aura->m_ReconnectPort, m_PID, m_GProxyReconnectKey, m_Game->GetGProxyEmptyActions( ) ) ); Print( "[GAME: " + m_Game->GetGameName( ) + "] player [" + m_Name + "] is using GProxy++" ); } } *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); } else break; } } } bool Deleting; if( m_GProxy && m_Game->GetGameLoaded( ) ) Deleting = m_DeleteMe; else Deleting = m_DeleteMe || m_Socket->HasError( ) || !m_Socket->GetConnected( ); // try to find out why we're requesting deletion // in cases other than the ones covered here m_LeftReason should have been set when m_DeleteMe was set if( m_Socket ) { if( m_Socket->HasError( ) ) { m_Game->EventPlayerDisconnectSocketError( this ); m_Socket->Reset( ); } else if( !m_Socket->GetConnected( ) ) { m_Game->EventPlayerDisconnectConnectionClosed( this ); m_Socket->Reset( ); } } return Deleting; }