CIncomingChatEvent *CBNETProtocol :: RECEIVE_SID_CHATEVENT( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED SID_CHATEVENT" ); // DEBUG_Print( data ); // 2 bytes -> Header // 2 bytes -> Length // 4 bytes -> EventID // 4 bytes -> UserFlags // 4 bytes -> Ping // 12 bytes -> ??? // null terminated string -> User // null terminated string -> Message if( ValidateLength( data ) && data.size( ) >= 29 ) { BYTEARRAY EventID = BYTEARRAY( data.begin( ) + 4, data.begin( ) + 8 ); BYTEARRAY UserFlags = BYTEARRAY( data.begin( ) + 8, data.begin( ) + 12 ); BYTEARRAY Ping = BYTEARRAY( data.begin( ) + 12, data.begin( ) + 16 ); BYTEARRAY User = UTIL_ExtractCString( data, 28 ); BYTEARRAY Message = UTIL_ExtractCString( data, User.size( ) + 29 ); switch( UTIL_ByteArrayToUInt32( EventID, false ) ) { case CBNETProtocol :: EID_SHOWUSER: case CBNETProtocol :: EID_JOIN: case CBNETProtocol :: EID_LEAVE: case CBNETProtocol :: EID_WHISPER: case CBNETProtocol :: EID_TALK: case CBNETProtocol :: EID_BROADCAST: case CBNETProtocol :: EID_CHANNEL: case CBNETProtocol :: EID_USERFLAGS: case CBNETProtocol :: EID_WHISPERSENT: case CBNETProtocol :: EID_CHANNELFULL: case CBNETProtocol :: EID_CHANNELDOESNOTEXIST: case CBNETProtocol :: EID_CHANNELRESTRICTED: case CBNETProtocol :: EID_INFO: case CBNETProtocol :: EID_ERROR: case CBNETProtocol :: EID_EMOTE: return new CIncomingChatEvent( (CBNETProtocol :: IncomingChatEvent)UTIL_ByteArrayToUInt32( EventID, false ), UTIL_ByteArrayToUInt32( UserFlags, false ), UTIL_ByteArrayToUInt32( Ping, false ), string( User.begin( ), User.end( ) ), string( Message.begin( ), Message.end( ) ) ); } } return NULL; }
vector<CIncomingFriendList *> CBNETProtocol :: RECEIVE_SID_FRIENDSLIST( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED SID_FRIENDSLIST" ); // DEBUG_Print( data ); // 2 bytes -> Header // 2 bytes -> Length // 1 byte -> Total // for( 1 .. Total ) // null term string -> Account // 1 byte -> Status // 1 byte -> Area // 4 bytes -> ??? // null term string -> Location vector<CIncomingFriendList *> Friends; if( ValidateLength( data ) && data.size( ) >= 5 ) { unsigned int i = 5; unsigned char Total = data[4]; while( Total > 0 ) { Total--; if( data.size( ) < i + 1 ) break; BYTEARRAY Account = UTIL_ExtractCString( data, i ); i += Account.size( ) + 1; if( data.size( ) < i + 7 ) break; unsigned char Status = data[i]; unsigned char Area = data[i + 1]; i += 6; BYTEARRAY Location = UTIL_ExtractCString( data, i ); i += Location.size( ) + 1; Friends.push_back( new CIncomingFriendList( string( Account.begin( ), Account.end( ) ), Status, Area, string( Location.begin( ), Location.end( ) ) ) ); } } return Friends; }
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( ); }
CIncomingGarenaUser *CGCBIProtocol :: RECEIVE_GCBI_INIT( BYTEARRAY data ) { // 2 bytes -> Header // 2 bytes -> Length // 4 bytes -> actual IP address (big endian) // 4 bytes -> Garena user ID (big endian) // 4 bytes -> Garena room ID (big endian) // 4 bytes -> Garena user experience (big endian) // 2 bytes -> country string from Garena if( ValidateLength( data ) && data.size( ) == 22 ) { BYTEARRAY IP = BYTEARRAY( data.begin( ) + 4, data.begin( ) + 8 ); BYTEARRAY UserID = BYTEARRAY( data.begin( ) + 8, data.begin( ) + 12 ); BYTEARRAY RoomID = BYTEARRAY( data.begin( ) + 12, data.begin( ) + 16 ); BYTEARRAY UserExp = BYTEARRAY( data.begin( ) + 16, data.begin( ) + 20 ); BYTEARRAY Country = BYTEARRAY( data.begin( ) + 20, data.begin( ) + 22 ); return new CIncomingGarenaUser(UTIL_ByteArrayToUInt32( IP, true ), UTIL_ByteArrayToUInt32( UserID, true ), UTIL_ByteArrayToUInt32( RoomID, true ), UTIL_ByteArrayToUInt32( UserExp, true ), string( Country.begin( ), Country.end( ) ) ); } return NULL; }
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; } } }
CIncomingClanList *CBNETProtocol :: RECEIVE_SID_CLANMEMBERSTATUSCHANGE( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED SID_CLANMEMBERSTATUSCHANGE" ); // DEBUG_Print( data ); // 2 bytes -> Header // 2 bytes -> Length // null terminated string -> Name // 1 byte -> Rank // 1 byte -> Status // null terminated string -> Location if( ValidateLength( data ) && data.size( ) >= 5 ) { BYTEARRAY Name = UTIL_ExtractCString( data, 4 ); if( data.size( ) >= Name.size( ) + 7 ) { unsigned char Rank = data[Name.size( ) + 5]; unsigned char Status = data[Name.size( ) + 6]; // in the original VB source the location string is read but discarded, so that's what I do here BYTEARRAY Location = UTIL_ExtractCString( data, Name.size( ) + 7 ); return new CIncomingClanList( string( Name.begin( ), Name.end( ) ), Rank, Status ); } } return NULL; }
CIncomingJoinPlayer *CGameProtocol :: RECEIVE_W3GS_REQJOIN( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED W3GS_REQJOIN" ); // DEBUG_Print( data ); // 2 bytes -> Header // 2 bytes -> Length // 4 bytes -> Host Counter (Game ID) // 4 bytes -> Entry Key (used in LAN) // 1 byte -> ??? // 2 bytes -> Listen Port // 4 bytes -> Peer Key // null terminated string -> Name // 4 bytes -> ??? // 2 bytes -> InternalPort (???) // 4 bytes -> InternalIP if( ValidateLength( data ) && data.size( ) >= 20 ) { uint32_t HostCounter = UTIL_ByteArrayToUInt32( data, false, 4 ); BYTEARRAY Name = UTIL_ExtractCString( data, 19 ); if( !Name.empty( ) && data.size( ) >= Name.size( ) + 30 ) { BYTEARRAY InternalIP = BYTEARRAY( data.begin( ) + Name.size( ) + 26, data.begin( ) + Name.size( ) + 30 ); return new CIncomingJoinPlayer( HostCounter, string( Name.begin( ), Name.end( ) ), InternalIP ); } } return NULL; }
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 CReplay :: AddLeaveGame( uint32_t reason, unsigned char PID, uint32_t result ) { BYTEARRAY Block; Block.push_back( REPLAY_LEAVEGAME ); UTIL_AppendByteArray( Block, reason, false ); Block.push_back( PID ); UTIL_AppendByteArray( Block, result, false ); UTIL_AppendByteArray( Block, (uint32_t)1, false ); m_CompiledBlocks += string( Block.begin( ), Block.end( ) ); }
bool CBNCSUtilInterface :: HELP_SID_AUTH_ACCOUNTLOGONPROOF( BYTEARRAY salt, BYTEARRAY serverKey ) { // set m_M1 char buf[20]; // nls_get_M1( (nls_t *)m_nls, buf, string( serverKey.begin( ), serverKey.end( ) ).c_str( ), string( salt.begin( ), salt.end( ) ).c_str( ) ); ( (NLS *)m_NLS )->getClientSessionKey( buf, string( salt.begin( ), salt.end( ) ).c_str( ), string( serverKey.begin( ), serverKey.end( ) ).c_str( ) ); m_M1 = UTIL_CreateByteArray( (unsigned char *)buf, 20 ); return true; }
bool CUDPSocket :: SendTo( struct sockaddr_in sin, BYTEARRAY message ) { if( m_Socket == INVALID_SOCKET || m_HasError ) return false; string MessageString = string( message.begin( ), message.end( ) ); if( sendto( m_Socket, MessageString.c_str( ), MessageString.size( ), 0, (struct sockaddr *)&sin, sizeof( sin ) ) == -1 ) return false; return true; }
vector<CIncomingClanList *> CBNETProtocol :: RECEIVE_SID_CLANMEMBERLIST( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED SID_CLANMEMBERLIST" ); // DEBUG_Print( data ); // 2 bytes -> Header // 2 bytes -> Length // 4 bytes -> ??? // 1 byte -> Total // for( 1 .. Total ) // null term string -> Name // 1 byte -> Rank // 1 byte -> Status // null term string -> Location vector<CIncomingClanList *> ClanList; if( ValidateLength( data ) && data.size( ) >= 9 ) { unsigned int i = 9; unsigned char Total = data[8]; while( Total > 0 ) { Total--; if( data.size( ) < i + 1 ) break; BYTEARRAY Name = UTIL_ExtractCString( data, i ); i += Name.size( ) + 1; if( data.size( ) < i + 3 ) break; unsigned char Rank = data[i]; unsigned char Status = data[i + 1]; i += 2; // in the original VB source the location string is read but discarded, so that's what I do here BYTEARRAY Location = UTIL_ExtractCString( data, i ); i += Location.size( ) + 1; ClanList.push_back( new CIncomingClanList( string( Name.begin( ), Name.end( ) ), Rank, Status ) ); } } return ClanList; }
BYTEARRAY CBNETProtocol :: RECEIVE_SID_WARDEN( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED SID_WARDEN" ); // DEBUG_PRINT( data ); // 2 bytes -> Header // 2 bytes -> Length // n bytes -> Data if( ValidateLength( data ) && data.size( ) >= 4 ) return BYTEARRAY( data.begin( ) + 4, data.end( ) ); return BYTEARRAY( ); }
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; } } }
void CSaveGame :: PrepareForSave() { // Custom W3Z file format type header // by freed BYTEARRAY packet; UTIL_AppendByteArray( packet, m_MapPath ); UTIL_AppendByteArray( packet, customMarkFileType, true ); UTIL_AppendByteArray( packet, m_GameName ); UTIL_AppendByteArray( packet, customMarkFileType, true ); UTIL_AppendByteArray( packet, customMarkFileType, true ); UTIL_AppendByteArray( packet, (uint32_t)0, false ); UTIL_AppendByteArray( packet, (uint32_t)0, false ); UTIL_AppendByteArray( packet, (uint16_t)0, false ); packet.push_back ( m_Slots.size() ); for( unsigned char i = 0; i < m_Slots.size(); i++ ) { packet.push_back( m_Slots[i].GetPID() ); packet.push_back( m_Slots[i].GetDownloadStatus() ); packet.push_back( m_Slots[i].GetSlotStatus() ); packet.push_back( m_Slots[i].GetComputer() ); packet.push_back( m_Slots[i].GetTeam() ); packet.push_back( m_Slots[i].GetColour() ); packet.push_back( m_Slots[i].GetRace() ); packet.push_back( m_Slots[i].GetComputerType() ); packet.push_back( m_Slots[i].GetHandicap() ); } UTIL_AppendByteArray( packet, (uint32_t)m_RandomSeed, false ); packet.push_back( 0 ); // GameType packet.push_back( m_PIDs.size() ); // number of player slots (non observer) UTIL_AppendByteArray( packet, m_MagicNumber ); packet.push_back( m_PIDs.size() ); for( vector<CGamePlayer*>::iterator i = m_PIDs.begin(); i != m_PIDs.end(); ++i ) { packet.push_back( (*i)->GetPID() ); UTIL_AppendByteArray( packet, (*i)->GetName(), true ); } m_Decompressed = string( packet.begin( ), packet.end( ) ); }
void CReplay :: AddChatMessage( unsigned char PID, unsigned char flags, uint32_t chatMode, string message ) { BYTEARRAY Block; Block.push_back( REPLAY_CHATMESSAGE ); Block.push_back( PID ); UTIL_AppendByteArray( Block, (uint16_t)0, false ); Block.push_back( flags ); UTIL_AppendByteArray( Block, chatMode, false ); UTIL_AppendByteArrayFast( Block, message ); // assign length BYTEARRAY LengthBytes = UTIL_CreateByteArray( (uint16_t)( Block.size( ) - 4 ), false ); Block[2] = LengthBytes[0]; Block[3] = LengthBytes[1]; m_CompiledBlocks += string( Block.begin( ), Block.end( ) ); }
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; }
void CBNET :: ExtractPackets( ) { // 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( reinterpret_cast<unsigned char*>( const_cast<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 ) { // byte 0 is always 255 if( Bytes[0] == BNET_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( BNET_HEADER_CONSTANT, Bytes[1], BYTEARRAY(Bytes.begin(), Bytes.begin() + Length) ) ); *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin() + Length, Bytes.end() ); } else return; } else { cout << "[BNET: " << m_ServerAlias << "] error - received invalid packet from battle.net (bad length), disconnecting\n"; m_Socket->Disconnect( ); return; } } else { cout << "[BNET: " << m_ServerAlias << "] error - received invalid packet from battle.net (bad header constant), disconnecting\n"; m_Socket->Disconnect( ); return; } } }
bool CUDPSocket :: Broadcast( uint16_t port, BYTEARRAY message ) { if( m_Socket == INVALID_SOCKET || m_HasError ) return false; struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_addr.s_addr = m_BroadcastTarget.s_addr; sin.sin_port = htons( port ); string MessageString = string( message.begin( ), message.end( ) ); if( sendto( m_Socket, MessageString.c_str( ), MessageString.size( ), 0, (struct sockaddr *)&sin, sizeof( sin ) ) == -1 ) { CONSOLE_Print( "[UDPSOCKET] failed to broadcast packet (port " + UTIL_ToString( port ) + ", size " + UTIL_ToString( MessageString.size( ) ) + " bytes)" ); return false; } return true; }
void CReplay :: AddTimeSlot2( queue<CIncomingAction *> actions ) { BYTEARRAY Block; Block.push_back( REPLAY_TIMESLOT2 ); UTIL_AppendByteArray( Block, (uint16_t)0, false ); UTIL_AppendByteArray( Block, (uint16_t)0, false ); while( !actions.empty( ) ) { CIncomingAction *Action = actions.front( ); actions.pop( ); Block.push_back( Action->GetPID( ) ); UTIL_AppendByteArray( Block, (uint16_t)Action->GetAction( )->size( ), false ); UTIL_AppendByteArrayFast( Block, *Action->GetAction( ) ); } // assign length BYTEARRAY LengthBytes = UTIL_CreateByteArray( (uint16_t)( Block.size( ) - 3 ), false ); Block[1] = LengthBytes[0]; Block[2] = LengthBytes[1]; m_CompiledBlocks += string( Block.begin( ), Block.end( ) ); }
void CGame::SetDetials ( BYTEARRAY b ) { m_details.assign( b.begin() , b.end() ); m_state = STATUS_ABOUTTOSTART; m_lastUpdateTime = GetTime( ); }
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; }
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( ) ); }
BYTEARRAY CGameProtocol :: SEND_W3GS_INCOMING_ACTION2( queue<CIncomingAction *> actions ) { BYTEARRAY packet; packet.push_back( W3GS_HEADER_CONSTANT ); // W3GS header constant packet.push_back( W3GS_INCOMING_ACTION2 ); // W3GS_INCOMING_ACTION2 packet.push_back( 0 ); // packet length will be assigned later packet.push_back( 0 ); // packet length will be assigned later packet.push_back( 0 ); // ??? (send interval?) packet.push_back( 0 ); // ??? (send interval?) // create subpacket if( !actions.empty( ) ) { BYTEARRAY subpacket; while( !actions.empty( ) ) { CIncomingAction *Action = actions.front( ); actions.pop( ); subpacket.push_back( Action->GetPID( ) ); UTIL_AppendByteArray( subpacket, (uint16_t)Action->GetAction( )->size( ), false ); UTIL_AppendByteArrayFast( subpacket, *Action->GetAction( ) ); } // calculate crc (we only care about the first 2 bytes though) BYTEARRAY crc32 = UTIL_CreateByteArray( m_GHost->m_CRC->FullCRC( (unsigned char *)string( subpacket.begin( ), subpacket.end( ) ).c_str( ), subpacket.size( ) ), false ); crc32.resize( 2 ); // finish subpacket UTIL_AppendByteArrayFast( packet, crc32 ); // crc UTIL_AppendByteArrayFast( packet, subpacket ); // subpacket } AssignLength( packet ); // DEBUG_Print( "SENT W3GS_INCOMING_ACTION2" ); // DEBUG_Print( packet ); return packet; }
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( ) ); }
CIncomingChatPlayer *CGameProtocol :: RECEIVE_W3GS_CHAT_TO_HOST( BYTEARRAY data ) { // DEBUG_Print( "RECEIVED W3GS_CHAT_TO_HOST" ); // DEBUG_Print( data ); // 2 bytes -> Header // 2 bytes -> Length // 1 byte -> Total // for( 1 .. Total ) // 1 byte -> ToPID // 1 byte -> FromPID // 1 byte -> Flag // if( Flag == 16 ) // null term string -> Message // elseif( Flag == 17 ) // 1 byte -> Team // elseif( Flag == 18 ) // 1 byte -> Colour // elseif( Flag == 19 ) // 1 byte -> Race // elseif( Flag == 20 ) // 1 byte -> Handicap // elseif( Flag == 32 ) // 4 bytes -> ExtraFlags // null term string -> Message if( ValidateLength( data ) ) { unsigned int i = 5; unsigned char Total = data[4]; if( Total > 0 && data.size( ) >= i + Total ) { BYTEARRAY ToPIDs = BYTEARRAY( data.begin( ) + i, data.begin( ) + i + Total ); i += Total; unsigned char FromPID = data[i]; unsigned char Flag = data[i + 1]; i += 2; if( Flag == 16 && data.size( ) >= i + 1 ) { // chat message BYTEARRAY Message = UTIL_ExtractCString( data, i ); return new CIncomingChatPlayer( FromPID, ToPIDs, Flag, string( Message.begin( ), Message.end( ) ) ); } else if( ( Flag >= 17 && Flag <= 20 ) && data.size( ) >= i + 1 ) { // team/colour/race/handicap change request unsigned char Byte = data[i]; return new CIncomingChatPlayer( FromPID, ToPIDs, Flag, Byte ); } else if( Flag == 32 && data.size( ) >= i + 5 ) { // chat message with extra flags BYTEARRAY ExtraFlags = BYTEARRAY( data.begin( ) + i, data.begin( ) + i + 4 ); BYTEARRAY Message = UTIL_ExtractCString( data, i + 4 ); return new CIncomingChatPlayer( FromPID, ToPIDs, Flag, string( Message.begin( ), Message.end( ) ), ExtraFlags ); } } } return NULL; }
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) UTIL_AppendByteArray( Replay, m_MapGameType, false ); // 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 ); // done m_Decompressed = string( Replay.begin( ), Replay.end( ) ); m_Decompressed += m_CompiledBlocks; }
void CTCPSocket :: PutBytes( BYTEARRAY bytes ) { m_SendBuffer += string( bytes.begin( ), bytes.end( ) ); }
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 CGame::SetStartHead ( BYTEARRAY b ) { m_startHead.assign( b.begin() , b.end() ); m_state = STATUS_STARTED; m_lastUpdateTime = GetTime( ); }