2009-08-11 12:17:38 +00:00
// *************************************************************************************************
// --------------------------------------
// Copyright (C) 2006-2010 Rajko Stojadinovic
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// *************************************************************************************************
# include "Common.h"
# include "GameClient.h"
# include "Timer.h"
# include "Util.h"
# include "MersenneTwister.h"
2009-08-12 19:55:32 +00:00
# include "EncryptedPacket.h"
# include "SequencedPacket.h"
2009-08-17 23:43:22 +00:00
# include "RsiData.h"
2010-02-08 14:51:16 +00:00
# include "MarginSocket.h"
# include "MarginServer.h"
# include "Database/Database.h"
# include "Log.h"
2010-02-13 14:32:04 +00:00
# include "GameServer.h"
2009-08-17 23:43:22 +00:00
# pragma pack(1)
2009-08-11 12:17:38 +00:00
GameClient : : GameClient ( struct sockaddr_in address , SOCKET * sock )
{
2010-02-13 14:32:04 +00:00
m_sock = sock ;
m_address = address ;
m_serverSequence = 0 ;
m_serverCommandsSent = 0 ;
m_clientCommandsReceived = 0 ;
m_numPackets = 0 ;
m_clientPSS = 0 ;
m_lastClientSequence = 0 ;
m_validClient = true ;
m_worldLoaded = false ;
m_characterSpawned = false ;
m_encryptionInitialized = false ;
m_lastSimTimeMS = 0 ;
m_lastOrderedFlush = 0 ;
m_playerGoId = 0 ;
2009-08-11 12:17:38 +00:00
}
GameClient : : ~ GameClient ( )
{
2010-02-13 14:32:04 +00:00
if ( m_playerGoId ! = 0 )
2010-02-08 14:51:16 +00:00
{
2010-02-13 14:32:04 +00:00
sObjMgr . deallocatePlayer ( m_playerGoId ) ;
2010-02-08 14:51:16 +00:00
}
2010-02-13 14:32:04 +00:00
sObjMgr . clientSigningOff ( this ) ;
MarginSocket * marginConn = sMargin . GetSocketByCharacterUID ( m_characterUID ) ;
if ( marginConn ! = NULL )
2010-02-08 14:51:16 +00:00
{
2010-02-13 14:32:04 +00:00
marginConn - > ForceDisconnect ( ) ;
2010-02-08 14:51:16 +00:00
}
2009-08-11 12:17:38 +00:00
}
void GameClient : : HandlePacket ( char * pData , uint16 nLength )
{
2010-02-13 14:32:04 +00:00
if ( nLength < 1 | | m_validClient = = false )
2010-02-08 14:51:16 +00:00
return ;
2010-02-13 14:32:04 +00:00
m_lastActivity = getTime ( ) ;
m_lastPacketReceivedMS = getMSTime ( ) ;
m_numPackets + + ;
2009-08-11 12:17:38 +00:00
2010-02-13 14:32:04 +00:00
if ( m_encryptionInitialized = = false & & pData [ 0 ] = = 0 & & nLength = = 43 )
2010-02-08 14:51:16 +00:00
{
ByteBuffer packetData ;
packetData . append ( pData , nLength ) ;
packetData . rpos ( 0x0B ) ;
2010-02-13 14:32:04 +00:00
if ( packetData . remaining ( ) < sizeof ( m_characterUID ) )
2010-02-08 14:51:16 +00:00
{
2010-02-13 14:32:04 +00:00
m_validClient = false ;
2010-02-08 14:51:16 +00:00
return ;
}
2010-02-13 14:32:04 +00:00
packetData > > m_characterUID ;
try
2010-02-08 14:51:16 +00:00
{
2010-02-13 14:32:04 +00:00
m_playerGoId = sObjMgr . allocatePlayer ( this , m_characterUID ) ;
}
catch ( ObjectMgr : : ObjectNotAvailable )
{
ERROR_LOG ( format ( " InitialUDPPacket(%1%): Character doesn't exist " ) % Address ( ) ) ;
m_validClient = false ;
return ;
2010-02-08 14:51:16 +00:00
}
2010-02-13 14:32:04 +00:00
MarginSocket * marginConn = sMargin . GetSocketByCharacterUID ( m_characterUID ) ;
2010-02-08 14:51:16 +00:00
if ( marginConn = = NULL )
{
2010-02-13 14:32:04 +00:00
ERROR_LOG ( format ( " InitialUDPPacket(%1%): Margin session not found " ) % Address ( ) ) ;
m_validClient = false ;
2010-02-08 14:51:16 +00:00
return ;
}
2010-02-13 14:32:04 +00:00
m_sessionId = marginConn - > GetSessionId ( ) ;
m_charWorldId = marginConn - > GetWorldCharId ( ) ;
2010-02-08 14:51:16 +00:00
//initialize encryptors with key from margin
vector < byte > twofishKey = marginConn - > GetTwofishKey ( ) ;
vector < byte > twofishIV ( CryptoPP : : Twofish : : BLOCKSIZE , 0 ) ;
2010-02-13 14:32:04 +00:00
m_TFDecrypt . reset ( new CryptoPP : : CBC_Mode < CryptoPP : : Twofish > : : Decryption ( & twofishKey [ 0 ] , twofishKey . size ( ) , & twofishIV [ 0 ] ) ) ;
m_TFEncrypt . reset ( new CryptoPP : : CBC_Mode < CryptoPP : : Twofish > : : Encryption ( & twofishKey [ 0 ] , twofishKey . size ( ) , & twofishIV [ 0 ] ) ) ;
2010-02-08 14:51:16 +00:00
//now we can verify if session key in this packet is correct
packetData . rpos ( packetData . size ( ) - CryptoPP : : Twofish : : BLOCKSIZE ) ;
if ( packetData . remaining ( ) < CryptoPP : : Twofish : : BLOCKSIZE )
{
//wat
2010-02-13 14:32:04 +00:00
m_validClient = false ;
2010-02-08 14:51:16 +00:00
marginConn - > ForceDisconnect ( ) ;
return ;
}
vector < byte > encryptedSessionId ( packetData . remaining ( ) ) ;
packetData . read ( & encryptedSessionId [ 0 ] , encryptedSessionId . size ( ) ) ;
string decryptedOutput ;
CryptoPP : : StringSource ( string ( ( const char * ) & encryptedSessionId [ 0 ] , encryptedSessionId . size ( ) ) , true ,
new CryptoPP : : StreamTransformationFilter (
2010-02-13 14:32:04 +00:00
* m_TFDecrypt , new CryptoPP : : StringSink ( decryptedOutput ) ,
2010-02-08 14:51:16 +00:00
CryptoPP : : BlockPaddingSchemeDef : : NO_PADDING ) ) ;
ByteBuffer decryptedData ;
decryptedData . append ( decryptedOutput . data ( ) , decryptedOutput . size ( ) ) ;
uint32 recoveredSessionId = 0 ;
decryptedData > > recoveredSessionId ;
2010-02-13 14:32:04 +00:00
if ( recoveredSessionId ! = m_sessionId )
2010-02-08 14:51:16 +00:00
{
2010-02-13 14:32:04 +00:00
ERROR_LOG ( format ( " InitialUDPPacket(%1%): Session Key Mismatch " ) % Address ( ) ) ;
m_validClient = false ;
2010-02-08 14:51:16 +00:00
marginConn - > ForceDisconnect ( ) ;
return ;
}
2010-02-13 14:32:04 +00:00
m_encryptionInitialized = true ;
2010-02-08 14:51:16 +00:00
//send latency/loss calibration heartbeats
const int numberOfBeats = 5 ;
for ( int i = 0 ; i < numberOfBeats ; i + + )
{
ByteBuffer beatPacket ;
for ( int j = 0 ; j < numberOfBeats ; j + + )
{
beatPacket < < uint8 ( 0 ) ;
}
beatPacket < < uint16 ( swap16 ( numberOfBeats ) ) ;
2010-02-13 14:32:04 +00:00
int clientlen = sizeof ( m_address ) ;
sendto ( * m_sock , beatPacket . contents ( ) , beatPacket . size ( ) , 0 , ( struct sockaddr * ) & m_address , clientlen ) ;
2010-02-08 14:51:16 +00:00
}
//notify margin that udp session is established
2010-02-13 14:32:04 +00:00
if ( marginConn - > UdpReady ( this ) = = false )
2010-02-08 14:51:16 +00:00
{
2010-02-13 14:32:04 +00:00
ERROR_LOG ( format ( " InitialUDPPacket(%1%): Margin not ready for UDP connection " ) % Address ( ) ) ;
m_encryptionInitialized = false ;
m_validClient = false ;
2010-02-08 14:51:16 +00:00
marginConn - > ForceDisconnect ( ) ;
return ;
}
2010-02-13 14:32:04 +00:00
sObjMgr . getGOPtr ( m_playerGoId ) - > InitializeWorld ( ) ;
FlushQueue ( ) ;
2010-02-08 14:51:16 +00:00
}
2010-02-13 14:32:04 +00:00
if ( m_worldLoaded = = true & & pData [ 0 ] ! = 0x01 ) // Ping...just reply with the same thing
2009-08-11 12:17:38 +00:00
{
2010-02-13 14:32:04 +00:00
int clientlen = sizeof ( m_address ) ;
sendto ( * m_sock , pData , nLength , 0 , ( struct sockaddr * ) & m_address , clientlen ) ;
2009-08-11 12:17:38 +00:00
}
else
{
2010-02-13 14:32:04 +00:00
if ( pData [ 0 ] = = 0x01 & & m_encryptionInitialized = = true )
2009-08-11 12:17:38 +00:00
{
2009-08-12 19:55:32 +00:00
SequencedPacket packetData = Decrypt ( & pData [ 1 ] , nLength - 1 ) ;
2010-02-13 14:32:04 +00:00
ByteBuffer dataToParse ;
2009-08-11 12:17:38 +00:00
2010-02-14 11:47:57 +00:00
uint32 pktsAcked = AcknowledgePacket ( packetData . getRemoteSeq ( ) ) ;
assert ( pktsAcked < = 1 ) ;
2009-08-11 12:17:38 +00:00
2010-02-14 11:47:57 +00:00
uint8 firstByte = packetData . contents ( ) [ 0 ] ;
//normal packet byte
if ( firstByte = = 0x02 )
2009-08-12 19:55:32 +00:00
{
2010-02-13 14:32:04 +00:00
dataToParse . append ( & packetData . contents ( ) [ 1 ] , packetData . size ( ) - 1 ) ;
2009-08-12 19:55:32 +00:00
}
2010-02-13 14:32:04 +00:00
else
2009-08-12 19:55:32 +00:00
{
2010-02-14 15:35:36 +00:00
WARNING_LOG ( format ( " (%1%) First byte of client packet isnt 0x02 ! " ) % Address ( ) ) ;
2010-02-13 14:32:04 +00:00
dataToParse . append ( & packetData . contents ( ) [ 0 ] , packetData . size ( ) ) ;
2009-08-17 23:43:22 +00:00
}
2010-02-14 15:35:36 +00:00
//DEBUG_LOG( format("(%s) Recv PSS: %x CSeq: %d SSeq: %d |%s|") % Address() % uint32(packetData.getPSS()) % packetData.getLocalSeq() % packetData.getRemoteSeq() % Bin2Hex(dataToParse) );
2010-02-14 11:47:57 +00:00
2010-02-13 14:32:04 +00:00
//add to need to ack list
PacketReceived ( packetData . getLocalSeq ( ) ) ;
if ( m_clientPSS ! = packetData . getPSS ( ) )
2009-08-17 23:43:22 +00:00
{
2010-02-13 14:32:04 +00:00
PSSChanged ( m_clientPSS , packetData . getPSS ( ) ) ;
2009-08-18 02:47:29 +00:00
}
2010-02-13 14:32:04 +00:00
if ( dataToParse . size ( ) > 0 )
2009-08-18 02:47:29 +00:00
{
2010-02-13 14:32:04 +00:00
HandleEncrypted ( dataToParse ) ;
2009-08-18 02:47:29 +00:00
}
2010-02-13 14:32:04 +00:00
FlushQueue ( ) ;
}
}
}
2009-08-20 08:45:05 +00:00
2010-02-13 14:32:04 +00:00
void GameClient : : HandleEncrypted ( ByteBuffer & srcData )
{
ByteBuffer dataCopy ( & srcData . contents ( ) [ srcData . rpos ( ) ] , srcData . remaining ( ) ) ;
int32 commandOffset = - 1 ;
ByteBuffer zeroFourBlock ;
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
//try to find 04 block
while ( dataCopy . remaining ( ) > 0 )
{
uint8 theByte ;
dataCopy > > theByte ;
if ( theByte = = 0x04 )
{
//reverse read of byte
dataCopy . rpos ( dataCopy . rpos ( ) - sizeof ( theByte ) ) ;
int32 thePos = dataCopy . rpos ( ) ;
//try and parse
OrderedPacket testPacket ;
bool parseSuccessfull = testPacket . FromBuffer ( dataCopy ) ;
if ( parseSuccessfull = = true & & dataCopy . remaining ( ) = = 0 )
2009-08-18 22:25:37 +00:00
{
2010-02-13 14:32:04 +00:00
dataCopy . rpos ( thePos ) ;
zeroFourBlock = ByteBuffer ( & dataCopy . contents ( ) [ dataCopy . rpos ( ) ] , dataCopy . remaining ( ) ) ;
commandOffset = thePos ;
break ;
2009-08-11 12:17:38 +00:00
}
2010-02-13 14:32:04 +00:00
}
}
2009-08-17 23:43:22 +00:00
2010-02-13 14:32:04 +00:00
ByteBuffer otherBlock ;
if ( commandOffset < 0 | | zeroFourBlock . size ( ) < 1 )
{
otherBlock = ByteBuffer ( dataCopy . contents ( ) , dataCopy . size ( ) ) ;
}
else if ( commandOffset > 0 )
{
otherBlock = ByteBuffer ( dataCopy . contents ( ) , commandOffset ) ;
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
if ( otherBlock . size ( ) > 0 )
{
HandleOther ( otherBlock ) ;
}
if ( zeroFourBlock . size ( ) > 0 )
{
HandleOrdered ( zeroFourBlock ) ;
}
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
void GameClient : : HandleOther ( ByteBuffer & otherData )
{
uint8 packetType ;
otherData > > packetType ;
otherData . rpos ( otherData . rpos ( ) - sizeof ( packetType ) ) ;
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
if ( packetType = = 0x03 )
{
if ( m_playerGoId = = 0 )
{
WARNING_LOG ( format ( " HandleOther(%1%): 03 received but no player object to handle it " ) % this - > Address ( ) ) ;
return ;
}
sObjMgr . getGOPtr ( m_playerGoId ) - > HandleStateUpdate ( otherData ) ;
}
else
{
WARNING_LOG ( format ( " HandleOther(%1%): Received unknown packet type %2% " ) % this - > Address ( ) % Bin2Hex ( otherData ) ) ;
}
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
bool msgblock_sequence_lessthan ( MsgBlock s2 , MsgBlock s1 )
{
return ( ( s1 . sequenceId > s2 . sequenceId ) & & ( s1 . sequenceId - s2 . sequenceId < = 65536 / 2 ) ) | |
( ( s2 . sequenceId > s1 . sequenceId ) & & ( s2 . sequenceId - s1 . sequenceId > 65536 / 2 ) ) ;
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
bool msgblock_sequence_greaterthan ( MsgBlock s1 , MsgBlock s2 )
{
return ( ( s1 . sequenceId > s2 . sequenceId ) & & ( s1 . sequenceId - s2 . sequenceId < = 65536 / 2 ) ) | |
( ( s2 . sequenceId > s1 . sequenceId ) & & ( s2 . sequenceId - s1 . sequenceId > 65536 / 2 ) ) ;
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
void GameClient : : HandleOrdered ( ByteBuffer & orderedData )
{
OrderedPacket bigPacket ;
if ( bigPacket . FromBuffer ( orderedData ) = = false )
{
2010-02-14 15:35:36 +00:00
ERROR_LOG ( format ( " (%1%) Error creating bigpacket from bytes %2% " ) % Address ( ) % Bin2Hex ( orderedData ) ) ;
2010-02-13 14:32:04 +00:00
return ;
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
if ( bigPacket . msgBlocks . size ( ) < 1 )
{
2010-02-14 15:35:36 +00:00
ERROR_LOG ( format ( " (%1%) Bigpacket has no msgblocks " ) % Address ( ) ) ;
2010-02-13 14:32:04 +00:00
return ;
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
if ( bigPacket . msgBlocks . size ( ) > 1 )
{
bigPacket . msgBlocks . sort ( msgblock_sequence_lessthan ) ;
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
for ( list < MsgBlock > : : iterator it1 = bigPacket . msgBlocks . begin ( ) ; it1 ! = bigPacket . msgBlocks . end ( ) ; + + it1 )
{
2010-02-14 15:35:36 +00:00
DEBUG_LOG ( format ( " (%s) 04 client order: %d, our order %d " ) % Address ( ) % it1 - > sequenceId % m_clientCommandsReceived ) ;
2010-02-13 14:32:04 +00:00
if ( isSequenceMoreRecent ( it1 - > sequenceId , m_clientCommandsReceived , 65536 ) = = true | | it1 - > sequenceId = = m_clientCommandsReceived )
{
for ( list < ByteBuffer > : : iterator it2 = it1 - > subPackets . begin ( ) ; it2 ! = it1 - > subPackets . end ( ) ; + + it2 )
2009-08-18 22:25:37 +00:00
{
2010-02-13 14:32:04 +00:00
if ( m_playerGoId ! = 0 )
2009-08-18 22:25:37 +00:00
{
2010-02-13 14:32:04 +00:00
sObjMgr . getGOPtr ( m_playerGoId ) - > HandleCommand ( * it2 ) ;
m_clientCommandsReceived + + ;
2009-08-18 22:25:37 +00:00
}
}
2010-02-13 14:32:04 +00:00
}
else
{
2010-02-14 15:35:36 +00:00
WARNING_LOG ( format ( " (%1%) Client order %2% smaller than server order %3% " ) % Address ( ) % it1 - > sequenceId % m_clientCommandsReceived ) ;
2010-02-13 14:32:04 +00:00
}
}
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
SequencedPacket GameClient : : Decrypt ( char * pData , uint16 nLength )
{
EncryptedPacket decryptedData ( ByteBuffer ( pData , nLength ) , m_TFDecrypt . get ( ) ) ;
return SequencedPacket ( decryptedData ) ;
}
2009-08-20 08:45:05 +00:00
2010-02-13 14:32:04 +00:00
void GameClient : : SendEncrypted ( SequencedPacket withSequences )
{
if ( ! m_TFEncrypt )
return ;
EncryptedPacket withEncryption ( withSequences . getDataWithHeader ( ) ) ;
ByteBuffer sendMe ;
sendMe < < uint8 ( 1 ) ;
sendMe . append ( withEncryption . toCipherText ( m_TFEncrypt . get ( ) ) ) ;
int clientlen = sizeof ( m_address ) ;
sendto ( * m_sock , sendMe . contents ( ) , ( int ) sendMe . size ( ) , 0 , ( struct sockaddr * ) & m_address , clientlen ) ;
}
void GameClient : : PSSChanged ( uint8 oldPSS , uint8 newPSS )
{
m_clientPSS = newPSS ;
if ( m_clientPSS = = 0x01 )
{
//skip straight to 0x07
2010-02-14 11:47:57 +00:00
m_clientPSS = 0x07 ;
if ( m_worldLoaded = = false )
{
sObjMgr . getGOPtr ( m_playerGoId ) - > SpawnSelf ( ) ;
m_worldLoaded = true ;
}
2010-02-13 14:32:04 +00:00
}
else if ( m_clientPSS = = 0x07 )
{
2010-02-14 11:47:57 +00:00
if ( m_worldLoaded = = false )
{
sObjMgr . getGOPtr ( m_playerGoId ) - > SpawnSelf ( ) ;
m_worldLoaded = true ;
}
2010-02-13 14:32:04 +00:00
}
else if ( m_clientPSS = = 0x7F )
{
if ( m_worldLoaded = = false )
{
sObjMgr . getGOPtr ( m_playerGoId ) - > SpawnSelf ( ) ;
m_worldLoaded = true ;
}
2010-02-15 12:52:58 +00:00
if ( m_characterSpawned = = false )
{
sObjMgr . getGOPtr ( m_playerGoId ) - > PopulateWorld ( ) ;
m_characterSpawned = true ;
}
2010-02-13 14:32:04 +00:00
}
}
2009-08-18 22:25:37 +00:00
2010-02-13 14:32:04 +00:00
void GameClient : : MoveMsgsToQueue ( )
{
//first we send out all the 03
2010-02-15 12:52:58 +00:00
for ( stateQueueType : : iterator it = m_queuedStates . begin ( ) ; it ! = m_queuedStates . end ( ) ; + + it )
2010-02-13 14:32:04 +00:00
{
//consume an ack if we can
if ( m_packetsToAck . size ( ) > 0 )
{
uint16 theClientSeq = m_packetsToAck . front ( ) ;
m_packetsToAck . pop_front ( ) ;
2010-02-15 12:52:58 +00:00
AddPacketToQueue ( theClientSeq , true , it - > stateData , it - > noResend ) ;
2010-02-13 14:32:04 +00:00
}
else
{
2010-02-15 12:52:58 +00:00
AddPacketToQueue ( it - > stateData , it - > noResend ) ;
2010-02-13 14:32:04 +00:00
}
}
//all queued 03s have been transferred to packet queue, clear the 03 queue
m_queuedStates . clear ( ) ;
2010-02-14 15:35:36 +00:00
if ( /*getMSTime() - m_lastOrderedFlush > 50*/ 1 ) //we will not send 04 packets more than 20 times a second
2010-02-13 14:32:04 +00:00
{
//send out subpackets, as much as we got, consuming as much acks as we can
while ( m_queuedCommands . size ( ) > 0 )
{
MsgBlock currBlock ;
currBlock . sequenceId = m_serverCommandsSent ;
while ( m_queuedCommands . size ( ) > 0 )
2009-08-18 22:25:37 +00:00
{
2010-02-13 14:32:04 +00:00
ByteBuffer packetStaticBuf ;
try
2009-08-18 22:25:37 +00:00
{
2010-02-13 14:32:04 +00:00
packetStaticBuf = m_queuedCommands . front ( ) - > toBuf ( ) ;
2009-08-18 22:25:37 +00:00
}
2010-02-13 14:32:04 +00:00
catch ( MsgBaseClass : : PacketNoLongerValid )
2009-08-18 22:25:37 +00:00
{
2010-02-13 14:32:04 +00:00
//just remove the command and go to the next one
m_queuedCommands . front ( ) . reset ( ) ;
m_queuedCommands . pop_front ( ) ;
2009-08-18 22:25:37 +00:00
}
2010-02-13 14:32:04 +00:00
currBlock . subPackets . push_back ( packetStaticBuf ) ;
m_serverCommandsSent + + ;
m_queuedCommands . front ( ) . reset ( ) ;
m_queuedCommands . pop_front ( ) ;
if ( m_queuedCommands . size ( ) < 1 | |
2010-02-14 15:35:36 +00:00
( currBlock . GetTotalSize ( ) + packetStaticBuf . size ( ) > = 1000 ) )
2009-08-18 22:25:37 +00:00
{
2010-02-13 14:32:04 +00:00
break ;
2009-08-18 22:25:37 +00:00
}
2009-08-18 16:39:50 +00:00
}
2010-02-13 14:32:04 +00:00
//consume an ack if we can
if ( m_packetsToAck . size ( ) > 0 )
2009-08-12 19:55:32 +00:00
{
2010-02-13 14:32:04 +00:00
uint16 theClientSeq = m_packetsToAck . front ( ) ;
m_packetsToAck . pop_front ( ) ;
2009-08-11 12:17:38 +00:00
2010-02-14 15:35:36 +00:00
AddPacketToQueue ( theClientSeq , true , make_shared < OrderedPacket > ( currBlock ) ) ;
2009-08-11 12:17:38 +00:00
}
2010-02-13 14:32:04 +00:00
else
2009-08-12 19:55:32 +00:00
{
2010-02-14 15:35:36 +00:00
AddPacketToQueue ( make_shared < OrderedPacket > ( currBlock ) ) ;
2009-08-12 19:55:32 +00:00
}
2009-08-11 12:17:38 +00:00
}
2010-02-13 14:32:04 +00:00
m_lastOrderedFlush = getMSTime ( ) ;
}
2009-08-11 12:17:38 +00:00
2010-02-13 14:32:04 +00:00
//we ran out of packets but there are still more acks to be sent ?
if ( m_packetsToAck . size ( ) > 0 )
{
for ( deque < uint16 > : : iterator it = m_packetsToAck . begin ( ) ; it ! = m_packetsToAck . end ( ) ; + + it )
2009-08-11 12:17:38 +00:00
{
2010-02-14 15:35:36 +00:00
AddPacketToQueue ( * it , true , make_shared < EmptyMsg > ( ) ) ;
2009-08-11 12:17:38 +00:00
}
2010-02-13 14:32:04 +00:00
m_packetsToAck . clear ( ) ;
2009-08-11 12:17:38 +00:00
}
}
2010-02-13 14:32:04 +00:00
void GameClient : : FlushQueue ( )
2009-08-11 12:17:38 +00:00
{
2010-02-13 14:32:04 +00:00
MoveMsgsToQueue ( ) ;
2009-08-11 12:17:38 +00:00
2010-02-13 14:32:04 +00:00
for ( sendQueueList : : iterator it = m_sendQueue . begin ( ) ; it ! = m_sendQueue . end ( ) ; )
{
//skip sent packets
if ( it - > sent = = true )
{
+ + it ;
continue ;
}
2009-08-13 10:58:38 +00:00
2010-02-13 14:32:04 +00:00
//serialize data from dynamic packet to a static one
ByteBuffer serializedData ;
try
{
serializedData = it - > theData - > toBuf ( ) ;
}
catch ( MsgBaseClass : : PacketNoLongerValid )
{
serializedData . clear ( ) ;
//remove data from packet in queue, this will leave it to still have an ack if needed
it - > theData . reset ( new EmptyMsg ( ) ) ;
}
2010-02-14 11:47:57 +00:00
ByteBuffer outputData ;
//we send simtime synchronization only in the first packet (anything else seems to break the game)
uint32 currTimeMS = getMSTime ( ) ;
if ( /*(*/ m_lastSimTimeMS = = 0 /*|| currTimeMS-m_lastSimTimeMS>1000)
& & serializedData . size ( ) > 0
& & dynamic_pointer_cast < OrderedPacket > ( it - > theData ) ! = NULL */ )
{
m_lastSimTimeMS = getMSTime ( ) ;
outputData < < uint8 ( 0x82 ) ;
//theirs ticks at 64hz, ours ticks at 1000
double preciseInterval = double ( m_lastSimTimeMS ) / double ( 1000 / 64 ) ;
uint32 simTimeToSend = uint32 ( preciseInterval ) ;
outputData < < uint32 ( simTimeToSend ) ;
}
else
{
//nosimtime byte
outputData < < uint8 ( 0x02 ) ;
}
//append that to our simtime or nosimtime byte
2010-02-13 14:32:04 +00:00
if ( serializedData . size ( ) > 0 )
{
outputData . append ( serializedData . contents ( ) , serializedData . size ( ) ) ;
}
else if ( it - > ack = = false )
{
//if no ack, and data is 0, no reason to send this packet, so remove from queue and carry on
it = m_sendQueue . erase ( it ) ;
continue ;
}
//prepend sequences
2010-02-14 11:47:57 +00:00
SequencedPacket prepended ( it - > server_sequence , it - > client_sequence , it - > clientPSS , outputData ) ;
2010-02-13 14:32:04 +00:00
SendEncrypted ( prepended ) ;
2010-02-14 15:35:36 +00:00
// DEBUG_LOG(format("(%s) Flush PSS: %x SSeq: %d CSeq: %d |%s|") % Address() % uint32(prepended.getPSS()) % prepended.getLocalSeq() % prepended.getRemoteSeq() % Bin2Hex(prepended));
2010-02-13 14:32:04 +00:00
//mark packet as sent
it - > sent = true ;
it - > msTimeSent = getMSTime ( ) ;
2010-02-15 12:52:58 +00:00
//if packet is non resendable, remove it
if ( it - > noResends = = true )
it = m_sendQueue . erase ( it ) ;
else
+ + it ;
2010-02-13 14:32:04 +00:00
}
2009-08-11 12:17:38 +00:00
}
2009-08-18 02:47:29 +00:00
2010-02-13 14:32:04 +00:00
void GameClient : : CheckAndResend ( )
2009-08-18 02:47:29 +00:00
{
2010-02-13 14:32:04 +00:00
bool modified = false ;
for ( sendQueueList : : iterator it = m_sendQueue . begin ( ) ; it ! = m_sendQueue . end ( ) ; )
2009-08-18 02:47:29 +00:00
{
2010-02-13 14:32:04 +00:00
//skip unsent packets
if ( it - > sent = = false )
2009-08-18 02:47:29 +00:00
{
2010-02-13 14:32:04 +00:00
+ + it ;
continue ;
}
bool deleted = false ;
uint32 currTime = getMSTime ( ) ;
2010-02-14 11:47:57 +00:00
if ( currTime - it - > msTimeSent > 500 ) //500 is timeout for resend
2010-02-13 14:32:04 +00:00
{
//client obviously doesnt want to ack this packet
2010-02-14 15:35:36 +00:00
if ( it - > resentCounter > 2 )
2009-08-18 02:47:29 +00:00
{
2010-02-13 14:32:04 +00:00
ByteBuffer resentPacketDumpedData ;
try
2009-08-18 02:47:29 +00:00
{
2010-02-13 14:32:04 +00:00
resentPacketDumpedData = it - > theData - > toBuf ( ) ;
2009-08-18 02:47:29 +00:00
}
2010-02-13 14:32:04 +00:00
catch ( MsgBaseClass : : PacketNoLongerValid )
2009-08-18 02:47:29 +00:00
{
2010-02-14 15:35:36 +00:00
INFO_LOG ( format ( " (%1%) Resent packet %2%'s data is no longer valid " ) % Address ( ) % it - > server_sequence ) ;
2009-08-18 22:25:37 +00:00
}
2010-02-14 15:35:36 +00:00
// INFO_LOG(format("(%1%) Doesn't want packet %2% of size %3%") % Address() % it->server_sequence % resentPacketDumpedData.size());
2009-08-20 08:45:05 +00:00
2010-02-13 14:32:04 +00:00
if ( resentPacketDumpedData . size ( ) > 1000 )
{
2010-02-14 15:35:36 +00:00
WARNING_LOG ( format ( " (%1%) WTF, WHY IS THIS SO BIG, %2% " ) % Address ( ) % Bin2Hex ( resentPacketDumpedData ) ) ;
2009-08-18 02:47:29 +00:00
}
2010-02-13 14:32:04 +00:00
it = m_sendQueue . erase ( it ) ;
deleted = true ;
}
else
{
2010-02-14 11:47:57 +00:00
bool subdivided = false ;
//is it a 04 packet ?
shared_ptr < OrderedPacket > daPacket = dynamic_pointer_cast < OrderedPacket > ( it - > theData ) ;
2010-02-14 15:35:36 +00:00
if ( daPacket ! = NULL & & daPacket - > msgBlocks . size ( ) = = 1 )
2010-02-14 11:47:57 +00:00
{
2010-02-14 15:35:36 +00:00
DEBUG_LOG ( format ( " (%1%) Packet %2% is a 04 packet, with %3% msgblocks %4% msgs(1) " ) % Address ( ) % it - > server_sequence % daPacket - > msgBlocks . size ( ) % daPacket - > msgBlocks . front ( ) . subPackets . size ( ) ) ;
2010-02-14 11:47:57 +00:00
shared_ptr < OrderedPacket > newPacket ( new OrderedPacket ( ) ) ;
for ( list < MsgBlock > : : iterator blockPtr = daPacket - > msgBlocks . begin ( ) ; blockPtr ! = daPacket - > msgBlocks . end ( ) ; + + blockPtr )
{
MsgBlock currBlock = * blockPtr ;
//send the same subpackets only in smaller chunks
uint16 altCommandsSent = currBlock . sequenceId ;
if ( currBlock . subPackets . size ( ) > 4 )
{
int numAtATime = 2 ;
if ( currBlock . subPackets . size ( ) > = 12 )
{
numAtATime = 4 ;
}
else if ( currBlock . subPackets . size ( ) > = 6 )
{
numAtATime = 3 ;
}
for ( ; ; )
{
MsgBlock newBlock ;
newBlock . sequenceId = altCommandsSent ;
for ( int i = 0 ; i < numAtATime ; i + + )
{
if ( currBlock . subPackets . size ( ) < 1 )
break ;
newBlock . subPackets . push_back ( currBlock . subPackets . front ( ) ) ;
altCommandsSent + + ;
currBlock . subPackets . pop_front ( ) ;
}
if ( newBlock . subPackets . size ( ) < 1 )
break ;
newPacket - > msgBlocks . push_back ( newBlock ) ;
}
}
}
if ( newPacket - > msgBlocks . size ( ) > 1 )
{
uint16 oldServerSeq = it - > server_sequence ;
uint16 oldClientSeq = it - > client_sequence ;
uint8 oldClientPSS = it - > clientPSS ;
bool oldAck = it - > ack ;
it = m_sendQueue . erase ( it ) ;
deleted = true ;
2010-02-14 15:35:36 +00:00
INFO_LOG ( format ( " (%1%) Subdivided packet sseq %2% from 1 msgblocks to %3% msgblocks " ) % Address ( ) % oldServerSeq % newPacket - > msgBlocks . size ( ) ) ;
2010-02-14 11:47:57 +00:00
//reverse iterator because push_front inserts into reverse anyway
for ( list < MsgBlock > : : reverse_iterator hurr = newPacket - > msgBlocks . rbegin ( ) ; hurr ! = newPacket - > msgBlocks . rend ( ) ; + + hurr )
{
increaseServerSequence ( ) ;
m_sendQueue . push_front ( PacketInQueue ( oldClientPSS , m_serverSequence , oldClientSeq , oldAck , shared_ptr < OrderedPacket > ( new OrderedPacket ( * hurr ) ) ) ) ;
}
subdivided = true ;
}
}
if ( subdivided = = false )
{
it - > resentCounter + + ;
it - > sent = false ;
it - > msTimeSent = 0 ;
uint16 oldSeq = it - > server_sequence ;
increaseServerSequence ( ) ;
it - > server_sequence = m_serverSequence ;
}
2010-02-14 15:35:36 +00:00
//DEBUG_LOG(format("(%1%) Resending packet sseq %2% under new sseq %3%") % Address() % oldSeq % it->server_sequence);
2010-02-13 14:32:04 +00:00
modified = true ;
2009-08-18 02:47:29 +00:00
}
}
2010-02-13 14:32:04 +00:00
if ( deleted = = false )
{
+ + it ;
}
}
if ( modified = = true )
{
FlushQueue ( ) ;
2009-08-18 02:47:29 +00:00
}
}