l2-unlegits/L2C_Server/net/GameClient/GameClientThread.cpp
2012-02-01 05:25:08 +00:00

189 lines
4.9 KiB
C++

#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "GameClient.h"
#include "GS.h"
#include "utils/Exception.h"
bool GameClient::startThread()
{
if( m_isRunning ) return false;
m_flagStop = false;
unsigned int tid = 0;
HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0,
(unsigned int (__stdcall *)(void *))GameClient::ClientThread, (void *)this,
0, &tid );
bool ret = false;
if( hThread != NULL )
{
ret = true;
CloseHandle( hThread );
}
return ret;
}
bool GameClient::stopThread()
{
if( !m_isRunning ) return false;
m_flagStop = true;
while( m_isRunning ) Sleep(50);
m_flagStop = false;
return true;
}
void GameClient::disconnectForceAndWaitThreadToStop( bool server_shutdown, bool reasonDualLogin )
{
m_was_force_disconnected = true;
// TODO: send SystemMessage about account in use if dual login
if( reasonDualLogin )
{
}
// TODO: send ServerClose / ExRestartClient? if server shutdown
if( server_shutdown )
{
}
stopThread(); // stops thread; thread cleanups socket himself
}
void GameClient::signalThreadStop( bool setMarkToDeleteSelfAndCleanupClientPool )
{
m_flagStop = true;
m_was_force_disconnected = !setMarkToDeleteSelfAndCleanupClientPool;
}
DWORD WINAPI GameClient::ClientThread( LPVOID lpvParam )
{
GameClient *pl = (GameClient *)lpvParam;
pl->m_was_force_disconnected = false;
GameServer *gs = GameServer::getInstance();
pl->m_isRunning = true;
pl->m_state = CLIENT_STATE_CONNECTED;
pl->m_netStats.clear(); // reset nework statistics
int r, bReadyRead, bReadyWrite;
unsigned char *packbuffer = NULL;
unsigned int rcvdLen = 0;
L2GamePacket *pack = NULL;
try
{
// allocate packets recv buffer
packbuffer = (unsigned char *)malloc( 65536 );
if( !packbuffer ) throw std::bad_alloc( "allocate packbuffer (64 Kb) failed" );
// packets recv loop
while( !pl->m_flagStop )
{
// wait for socket ready to receive data
r = L2PNet_select( pl->m_sock, L2PNET_SELECT_READ, 100, &bReadyRead, &bReadyWrite );
if( r == 0 )
{
// select timout
// update world state? move moving objects?...
continue;
}
if( r == -1 ) // select error
{
LogError( L"%s: L2PNet_select() error!", pl->toString() );
throw Exception( "select error" );
}
// recv packet
r = L2PacketReceive_buffer( pl->m_sock, 1000, &rcvdLen, packbuffer );
if( r <= 0 )
{
if( r == 0 )
{
LogWarning( L"%s: closed connection? (received %u, retval %d)", pl->toString(), rcvdLen, r );
pl->m_flagStop = true;
break;
}
if( r < 0 )
{
LogError( L"%s: failed to receive packet (received %u, retval %d)", pl->toString(), rcvdLen, r );
throw Exception( "network packet receive error" );
}
}
// count stats
pl->m_netStats.addRcvdPacket( rcvdLen );
// create packet object
pack = new L2GamePacket( packbuffer, rcvdLen );
// decode XOR if enabled
if( pl->m_xor_enabled ) pack->decodeXOR( pl->m_xor_key_recv );
// deobfuscate opcode if enabled
if( pl->m_opcodeObfuscationSeed > 0 )
{
// HACK: here buffer that should be constant, is modified (but length is not changed thoug)
pl->m_obf->decodeIDs( (unsigned char *)pack->getBytesPtr() );
}
#ifdef _DEBUG
int opcode = pack->getByteAt(2);
LogDebug( L"%s: opcode %02X", pl->toString(), opcode );
#endif
// handle game client packet...
pl->m_ph->handlePacket( pack );
// free packet object
delete pack; pack = NULL;
}
}
catch( L2P_Exception& e )
{
LogError( L"ClientThread: %s: L2Packets exception: %S", pl->toString(), e.what() );
}
catch( std::bad_alloc& e )
{
LogError( L"ClientThread: %s: bad_alloc %S", pl->toString(), e.what() );
}
catch( std::exception& e )
{
LogError( L"ClientThread: %s: std::excpt %S", pl->toString(), e.what() );
}
catch( Exception& e )
{
LogError( L"ClientThread: Exception: %S", e.what() );
e.logStackTrace();
}
// player disconnected - remove player account from login server list of authed accounts
if( pl->m_account[0] ) // has authed??
gs->logoutAccountFromLoginServer( pl->m_account );
// cleanup socket
L2PNet_shutdown( pl->m_sock );
L2PNet_closesocket( pl->m_sock );
pl->m_sock = INVALID_SOCKET;
pl->m_state = CLIENT_STATE_OFFLINE;
// cleanup opcode obfuscator
if( pl->m_obf )
{
delete pl->m_obf;
pl->m_obf = NULL;
pl->m_opcodeObfuscationSeed = 0;
}
// free packets buffer
if( packbuffer ) free( packbuffer ); packbuffer = NULL;
// mark self as finished
pl->m_isRunning = false;
// notify GS ClientPool about client stop, if needed
if( !pl->m_was_force_disconnected )
{
#ifdef _DEBUG
LogDebug( L"ClientThread s: Calling ClientPool to cleanup & deleting self", pl->toString() );
#endif
gs->getClientPool()->notifyClientThreadEnded( pl );
delete pl;
}
return 0;
}