Initial MSVC 2008 projects workspace

This commit is contained in:
alexey.min
2012-02-01 05:25:08 +00:00
commit 03de3bdc95
1446 changed files with 476853 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "ClientPacketHandler.h"
#include "ServerPackets.h"
#include "GS.h"
ClientPacketHandler::ClientPacketHandler( GameClient *cl )
{
m_cl = cl;
}
ClientPacketHandler::~ClientPacketHandler()
{
m_cl = NULL;
}
void ClientPacketHandler::handlePacket( L2GamePacket *pack )
{
L2GamePacket *reply = NULL;
unsigned char opcode = pack->getByteAt(2);
switch( m_cl->getState() )
{
case CLIENT_STATE_CONNECTED:
{
switch( opcode )
{
case 0x0E: reply = ProtocolVersion( pack ); break;
case 0x2B: reply = AuthLogin( pack ); break;
default: invalidPacket( pack, opcode, 0 ); break;
}
} break;
case CLIENT_STATE_AUTHED:
{
switch( opcode )
{
case 0x00: reply = Logout( pack ); break;
case 0x0C: reply = CharacterCreate( pack ); break;
case 0x12: reply = CharacterSelect( pack ); break;
case 0x13: reply = NewCharacter( pack ); break;
case 0xD0:
{
unsigned int opcode2 = pack->getByteAt(3) | (pack->getByteAt(4) << 8);
switch( opcode2 )
{
case 0x0036: reply = RequestGotoLobby( pack ); break;
default: invalidPacket( pack, opcode, opcode2 ); break;
}
} break;
default: invalidPacket( pack, opcode, 0 ); break;
}
} break;
case CLIENT_STATE_IN_GAME:
{
switch( opcode )
{
case 0x00: reply = Logout( pack ); break;
case 0xD0:
{
unsigned int opcode2 = pack->getByteAt(3) | (pack->getByteAt(4) << 8);
switch( opcode2 )
{
case 0x0001: break; // RequestManorList
default: invalidPacket( pack, opcode, opcode2 ); break;
}
} break;
default: invalidPacket( pack, opcode, 0 ); break;
}
} break;
}
if( reply )
{
m_cl->sendPacket( reply, true ); // true - delete reply immediately
}
}
void ClientPacketHandler::invalidPacket( L2GamePacket *p, unsigned int opcode, unsigned int opcode2 )
{
unsigned int plen = p->getPacketSize();
unsigned char *buf = (unsigned char *)p->getBytesPtr();
ClientState st = m_cl->getState();
wchar_t sn[32] = {0};
wcscpy( sn, L"Unknown" );
switch( st )
{
case CLIENT_STATE_OFFLINE: wcscpy( sn, L"OFFLINE" ); break;
case CLIENT_STATE_CONNECTED: wcscpy( sn, L"CONNECTED" ); break;
case CLIENT_STATE_AUTHED: wcscpy( sn, L"AUTHED" ); break;
case CLIENT_STATE_IN_GAME: wcscpy( sn, L"IN_GAME" ); break;
}
if( opcode2 == 0 )
LogWarning( L"%s: Invalid packet 0x%02X (len %u) in state: [%s]", m_cl->toString(), opcode, plen, sn );
else
LogWarning( L"%s: Invalid packet 0x%02X:%04X (len %u) in state: [%s]", m_cl->toString(), opcode, opcode2, plen, sn );
unsigned int i = 0;
while( i < plen )
{
wchar_t str[32] = {0};
swprintf( str, 32, L"%02X ", buf[i] );
LogFull( false, false, RGB(200,200,200), RGB(0,0,0), str );
if( i>0 && (i%16 == 0) ) LogFull( false, true, RGB(200,200,200), RGB(0,0,0), L"" );
i++;
}
LogFull( false, true, RGB(128,0,0), RGB(255,255,255), L"" );
LogFull( false, true, RGB(128,0,0), RGB(255,255,255), L"dump end" );
}

View File

@@ -0,0 +1,28 @@
#pragma once
class GameClient;
class ClientPacketHandler
{
public:
ClientPacketHandler( GameClient *cl );
~ClientPacketHandler();
public:
void handlePacket( L2GamePacket *pack );
protected:
L2GamePacket *ProtocolVersion( L2GamePacket *pack );
L2GamePacket *AuthLogin( L2GamePacket *pack );
L2GamePacket *NewCharacter( L2GamePacket *pack );
L2GamePacket *CharacterCreate( L2GamePacket *pack );
L2GamePacket *RequestGotoLobby( L2GamePacket *pack );
L2GamePacket *Logout( L2GamePacket *pack );
L2GamePacket *CharacterSelect( L2GamePacket *pack );
protected:
void invalidPacket( L2GamePacket *p, unsigned int opcode, unsigned int opcode2 );
protected:
GameClient *m_cl;
};

View File

@@ -0,0 +1,237 @@
#include "pch.h"
#include "Log.h"
#include "ClientPool.h"
#include "GS.h"
#include "utils/Exception.h"
ClientPool::ClientPool()
{
m_clients = new std::vector<GameClient *>();
}
ClientPool::~ClientPool()
{
if( m_clients )
{
delete m_clients;
m_clients = NULL;
}
}
int ClientPool::getCurrentOnline()
{
int ret = 0;
m_lock.Lock();
if( m_clients ) ret = m_clients->size();
m_lock.Unlock();
return ret;
}
bool ClientPool::addClient( SOCKET s_cl, const char *ip, int port )
{
if( !m_clients || !ip || (s_cl == INVALID_SOCKET) || (port == 0) ) return false;
bool ret = false;
m_lock.Lock();
// check current online
int max_online = GameServer::getInstance()->getMaxPlayers();
int cur_online = m_clients->size();
if( cur_online < max_online )
{
// create client
GameClient *pl = new GameClient( s_cl, ip, port );
if( pl )
{
m_clients->push_back( pl );
ret = pl->startThread();
}
}
else // maximum online reached
{
LogWarning( L"Maximum online count (%d) is reached, cannot accept new clients", max_online );
ret = false;
// close client connection
L2PNet_shutdown( s_cl );
L2PNet_closesocket( s_cl );
}
m_lock.Unlock();
return ret;
}
void ClientPool::disconnectAllPlayers()
{
if( !m_clients ) return;
m_lock.Lock();
try
{
std::vector<GameClient *>::iterator iter;
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
{
GameClient *pl = (*iter);
if( pl )
{
pl->disconnectForceAndWaitThreadToStop( true, false );
delete pl;
}
}
m_clients->clear();
}
catch( std::exception& e )
{
LogError( L"ClientPool::disconnectAllPlayers(): excpt %S", e.what() );
}
catch( Exception& e )
{
LogError( L"ClientPool::disconnectAllPlayers(): Exception: %S", e.what() );
e.logStackTrace();
}
m_lock.Unlock();
}
bool ClientPool::disconnectPlayerByAccount( const wchar_t *accountName, bool reasonDualLogin )
{
if( !m_clients ) return false;
bool ret = false;
m_lock.Lock();
try
{
std::vector<GameClient *>::iterator iter;
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
{
GameClient *pl = (*iter);
if( pl )
{
if( wcscmp( accountName, pl->getAccount() ) == 0 )
{
pl->disconnectForceAndWaitThreadToStop( false, reasonDualLogin );
m_clients->erase( iter );
ret = true;
delete pl;
}
}
}
}
catch( std::exception& e )
{
LogError( L"ClientPool::disconnectPlayerByAccount( %s ): excpt %S", accountName, e.what() );
}
catch( Exception& e )
{
LogError( L"ClientPool::disconnectPlayerByAccount(): Exception: %S", e.what() );
e.logStackTrace();
}
m_lock.Unlock();
return ret;
}
bool ClientPool::notifyClientThreadEnded( GameClient *ptr )
{
if( !m_clients || !ptr ) return false;
bool ret = false;
m_lock.Lock();
try
{
std::vector<GameClient *>::iterator iter;
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
{
GameClient *pl = (*iter);
if( pl == ptr )
{
#ifdef _DEBUG
LogDebug( L"ClientPool: deleted player %s", pl->toString() );
#endif
m_clients->erase( iter );
ret = true;
break;
}
}
}
catch( std::exception& e )
{
LogError( L"ClientPool::notifyClientThreadEnded(): excpt %S", e.what() );
}
catch( Exception& e )
{
LogError( L"ClientPool::notifyClientThreadEnded(): Exception: %S", e.what() );
e.logStackTrace();
}
m_lock.Unlock();
if( !ret )
{
LogWarning( L"ClientPool: not found player to del %s", ptr->toString() );
}
return ret;
}
void ClientPool::notifyClientAuthResponse( const wchar_t *accountName, bool ok )
{
if( !m_clients || !accountName ) return;
m_lock.Lock();
try
{
std::vector<GameClient *>::iterator iter;
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
{
GameClient *pl = (*iter);
if( pl )
{
if( wcscmp( pl->getAccount(), accountName ) == 0 )
{
if( !ok ) // client auth failed
{
pl->disconnectForceAndWaitThreadToStop( false, false );
m_clients->erase( iter );
delete pl;
}
else // auth ok
{
pl->notifySessionKeysOK();
}
break;
}
}
}
}
catch( std::exception& e )
{
LogError( L"ClientPool::notifyClientAuthResponse(): excpt %S", e.what() );
}
catch( Exception& e )
{
LogError( L"ClientPool::notifyClientAuthResponse(): Exception: %S", e.what() );
e.logStackTrace();
}
m_lock.Unlock();
}
void ClientPool::dumpClientsToLog()
{
if( !m_clients ) return;
int n = 0;
Log( L"===== ClientPool clients dump =====" );
m_lock.Lock();
try
{
std::vector<GameClient *>::iterator iter;
for( iter = m_clients->begin(); iter != m_clients->end(); iter++ )
{
GameClient *pl = (*iter);
if( pl )
{
Log( L" %d: %s", n, pl->toString() );
n++;
}
}
m_clients->clear();
}
catch( std::exception& e )
{
LogError( L"ClientPool::dumpClientsToLog(): excpt %S", e.what() );
}
catch( Exception& e )
{
LogError( L"ClientPool::dumpClientsToLog(): Exception: %S", e.what() );
e.logStackTrace();
}
m_lock.Unlock();
Log( L"===== ClientPool clients dump end =====" );
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
class ClientPool
{
public:
ClientPool();
~ClientPool();
public:
int getCurrentOnline();
public:
bool addClient( SOCKET s_cl, const char *ip, int port );
public:
void disconnectAllPlayers();
bool disconnectPlayerByAccount( const wchar_t *accountName, bool reasonDualLogin );
bool notifyClientThreadEnded( GameClient *ptr );
void notifyClientAuthResponse( const wchar_t *accountName, bool ok );
public:
void dumpClientsToLog();
protected:
CriticalSection m_lock;
std::vector<GameClient *> *m_clients;
};

View File

@@ -0,0 +1,115 @@
#include "pch.h"
#include "l2c_utils.h"
#include "GameClient.h"
GameClient::GameClient( SOCKET s_cl, const char *ip, int port )
{
setUnused();
if( ip )
{
strncpy( m_ip, ip, 16 ); m_ip[16] = 0;
l2c_ansi_to_unicode( m_ip, m_wip, 16 );
}
m_port = port;
m_sock = s_cl;
m_was_force_disconnected = false;
m_flagStop = m_isRunning = false;
swprintf( m_tostring, 128, L"Client [IP: %s:%d]", m_wip, m_port );
m_ph = new ClientPacketHandler( this );
}
GameClient::~GameClient()
{
if( m_ph )
{
delete m_ph;
m_ph = NULL;
}
if( m_obf )
{
delete m_obf;
m_obf = NULL;
}
if( m_player )
{
delete m_player; // really? some notification?
m_player = NULL;
}
setUnused();
}
void GameClient::setUnused()
{
// zero our members
m_account[0] = 0;
memset( m_playKey, 0, sizeof(m_playKey) );
memset( m_loginKey, 0, sizeof(m_loginKey) );
m_ip[0] = 0;
m_wip[0] = 0;
m_port = 0;
m_sock = INVALID_SOCKET;
m_tostring[0] = 0;
m_state = CLIENT_STATE_OFFLINE;
m_xor_enabled = false;
memset( m_xor_key_recv, 0, sizeof(m_xor_key_recv) );
memset( m_xor_key_send, 0, sizeof(m_xor_key_send) );
m_protocolIsOk = false;
m_netStats.clear();
m_opcodeObfuscationSeed = 0;
m_obf = NULL;
m_player = NULL;
}
const wchar_t *GameClient::getAccount() const { return (const wchar_t *)m_account; }
void GameClient::getPlayKey( unsigned char *buffer ) const { memcpy( buffer, m_playKey, 8 ); }
void GameClient::getLoginKey( unsigned char *buffer ) const { memcpy( buffer, m_loginKey, 8 ); }
const char *GameClient::getIpA() const { return (const char *)m_ip; }
const wchar_t *GameClient::getIpW() const { return (const wchar_t *)m_wip; }
int GameClient::getPort() const { return m_port; }
const wchar_t *GameClient::toString() const { return (const wchar_t *)m_tostring; }
ClientState GameClient::getState() const { return m_state; }
bool GameClient::isProtocolOk() const { return m_protocolIsOk; }
const GameClientNetStats *GameClient::getNetStats() const { return &m_netStats; }
unsigned int GameClient::getOpcodeObfuscationSeed() const { return m_opcodeObfuscationSeed; }
L2PCodeObfuscator *GameClient::getOpcodeObfuscator() { return m_obf; }
GamePlayer *GameClient::getPlayer() { return m_player; }
void GameClient::setAccount( const wchar_t *acc )
{
if( acc )
{
wcsncpy( m_account, acc, sizeof(m_account)/sizeof(wchar_t) );
m_account[sizeof(m_account)/sizeof(wchar_t) - 1] = 0;
}
}
void GameClient::setSessionKeys( const unsigned char *playKey, const unsigned char *loginKey )
{
memcpy( m_playKey, playKey, 8 );
memcpy( m_loginKey, loginKey, 8 );
}
void GameClient::setProtocolOk( bool b ) { m_protocolIsOk = b; }
void GameClient::setOpcodeObfuscationSeed( unsigned int s )
{
m_opcodeObfuscationSeed = s;
if( m_obf )
{
delete m_obf;
m_obf = NULL;
}
if( s != 0 )
{
m_obf = new L2PCodeObfuscator();
m_obf->init_tables( m_opcodeObfuscationSeed );
}
}
void GameClient::setPlayer( GamePlayer *newPlayer, bool changeState, ClientState newState )
{
if( m_player ) delete m_player;
m_player = NULL;
if( newPlayer ) m_player = newPlayer;
if( changeState ) m_state = newState;
}

View File

@@ -0,0 +1,78 @@
#pragma once
#include "enums.h"
#include "net/ClientPacketHandler.h"
#include "GameClientNetStats.h"
#include "world/model/character/GamePlayer.h"
class GameClient
{
public:
GameClient( SOCKET s_cl, const char *ip, int port );
virtual ~GameClient();
virtual void setUnused();
public:
const wchar_t *getAccount() const;
void getPlayKey( unsigned char *buffer ) const;
void getLoginKey( unsigned char *buffer ) const;
const char *getIpA() const;
const wchar_t *getIpW() const;
int getPort() const;
const wchar_t *toString() const;
ClientState getState() const;
bool isProtocolOk() const;
const GameClientNetStats *getNetStats() const;
unsigned int getOpcodeObfuscationSeed() const;
L2PCodeObfuscator *getOpcodeObfuscator();
GamePlayer *getPlayer();
public:
void setAccount( const wchar_t *acc );
void setSessionKeys( const unsigned char *playKey, const unsigned char *loginKey );
void setProtocolOk( bool b ) ;
void setOpcodeObfuscationSeed( unsigned int s );
void setPlayer( GamePlayer *newPlayer, bool changeState = false, ClientState newState = CLIENT_STATE_OFFLINE );
public:
// begins client processing, starts thread
bool startThread();
// stops all client activity, disconnects socket, optionally sends packet before client close
void disconnectForceAndWaitThreadToStop( bool server_shutdown, bool reasonDualLogin );
// signal thread to stop (no wait to stop)
void signalThreadStop( bool setMarkToDeleteSelfAndCleanupClientPool );
bool sendPacket( L2GamePacket *pack, bool deleteAfterSend = false );
void enable_XOR_crypt( bool bEnable, unsigned char *initial_key );
void notifySessionKeysOK();
protected:
// signals to stop and waits for thread to stop
bool stopThread();
static DWORD WINAPI ClientThread( LPVOID lpvParam );
protected:
// informational stings
char m_ip[16];
wchar_t m_wip[16];
int m_port;
wchar_t m_account[128];
wchar_t m_tostring[128];
// network-related vars
SOCKET m_sock;
bool m_was_force_disconnected;
bool m_flagStop;
bool m_isRunning;
CriticalSection m_cs_send;
ClientState m_state;
bool m_xor_enabled;
unsigned char m_xor_key_send[16];
unsigned char m_xor_key_recv[16];
ClientPacketHandler *m_ph;
bool m_protocolIsOk;
GameClientNetStats m_netStats;
unsigned int m_opcodeObfuscationSeed;
L2PCodeObfuscator *m_obf;
unsigned char m_playKey[8];
unsigned char m_loginKey[8];
//
GamePlayer *m_player;
};

View File

@@ -0,0 +1,67 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "GameClient.h"
#include "GS.h"
#include "../ServerPackets.h"
#include "utils/Exception.h"
bool GameClient::sendPacket( L2GamePacket *pack, bool deleteAfterSend /*= false*/ )
{
if( !pack ) return false;
if( m_sock == INVALID_SOCKET ) return false;
//
int r = 0;
unsigned int sentLen = 0;
unsigned int pack_size = pack->getPacketSize();
bool ret = true;
// encode xor if xor enabled
if( m_xor_enabled )
{
// KeyPacket is first packet sent to client by server.
// it is never encrypted
if( m_netStats.ullPacketsSent > 0 )
pack->encodeXOR( m_xor_key_send );
}
// send packet (lock before send, release lock after send)
m_cs_send.Lock();
r = L2PacketSend( m_sock, pack, 2000, &sentLen );
m_cs_send.Unlock();
// delete pack, if set to do so
if( deleteAfterSend ) delete pack;
// validate send OK
if( r<=0 || (sentLen != pack_size) ) // some error
{
ret = false;
throw Exception( "%S: send error: sent %u, retval %d", toString(), sentLen, r );
}
// count stats
m_netStats.addSentPacket( sentLen );
//
return ret;
}
void GameClient::enable_XOR_crypt( bool bEnable, unsigned char *initial_key )
{
m_xor_enabled = bEnable;
if( bEnable )
{
memcpy( m_xor_key_recv, initial_key, sizeof(m_xor_key_recv) );
memcpy( m_xor_key_send, initial_key, sizeof(m_xor_key_send) );
}
else
{
memset( m_xor_key_recv, 0, sizeof(m_xor_key_recv) );
memset( m_xor_key_send, 0, sizeof(m_xor_key_send) );
}
}
void GameClient::notifySessionKeysOK()
{
#ifdef _DEBUG
LogDebug( L"GameClient::notifySessionKeysOK()" );
#endif
m_state = CLIENT_STATE_AUTHED;
swprintf( m_tostring, 128, L"Client Acc: %s [IP %s:%d]", m_account, m_wip, m_port );
sendPacket( ServerPackets::CharacterSelectionInfo( this ), true );
}

View File

@@ -0,0 +1,25 @@
#include "pch.h"
#include "GameClientNetStats.h"
GameClientNetStats::GameClientNetStats()
{
clear();
}
void GameClientNetStats::clear()
{
ullPacketsSent = ullBytesSent = ullPacketsRcvd = ullBytesRcvd = 0;
}
void GameClientNetStats::addSentPacket( unsigned long long len )
{
ullPacketsSent++;
ullBytesSent += len;
}
void GameClientNetStats::addRcvdPacket( unsigned long long len )
{
ullPacketsRcvd++;
ullBytesRcvd += len;
}

View File

@@ -0,0 +1,15 @@
#pragma once
class GameClientNetStats
{
public:
GameClientNetStats();
void clear();
void addSentPacket( unsigned long long len );
void addRcvdPacket( unsigned long long len );
public:
unsigned long long ullPacketsSent;
unsigned long long ullBytesSent;
unsigned long long ullPacketsRcvd;
unsigned long long ullBytesRcvd;
};

View File

@@ -0,0 +1,188 @@
#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;
}

View File

@@ -0,0 +1,11 @@
#pragma once
class ServerPackets
{
public:
static L2GamePacket *KeyPacket( GameClient *pl );
static L2GamePacket *CharacterSelectionInfo( GameClient *pl );
static L2GamePacket *NewCharacterSuccess( GameClient *pl );
static L2GamePacket *CharCreateFail( GameClient *pl, int reason );
static L2GamePacket *CharCreateOK( GameClient *pl );
};

View File

@@ -0,0 +1,33 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ClientPacketHandler.h"
#include "../ServerPackets.h"
#include "GS.h"
/* _loginName = readS().toLowerCase();
_playKey2 = readD();
_playKey1 = readD();
_loginKey1 = readD();
_loginKey2 = readD(); */
L2GamePacket *ClientPacketHandler::AuthLogin( L2GamePacket *pack )
{
GameServer *gs = GameServer::getInstance();
pack->getPacketType();
wchar_t login[128] = {0};
unsigned char loginKey[8] = {0,0,0,0,0,0,0,0};
unsigned char playKey[8] = {0,0,0,0,0,0,0,0};
wcsncpy( login, pack->readUnicodeStringPtr(), 128 ); login[127] = 0;
pack->readBytes( playKey+4, 4 );
pack->readBytes( playKey, 4 );
pack->readBytes( loginKey, 8 );
//
m_cl->setAccount( login );
m_cl->setSessionKeys( playKey, loginKey );
//
if( gs->getConfig()->Debug ) LogDebug( L"%s: AuthLogin %s", m_cl->toString(), login );
gs->addWaitingClientAndSendPlayerAuthRequest( login, loginKey, playKey );
return NULL;
}

View File

@@ -0,0 +1,213 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ClientPacketHandler.h"
#include "../ServerPackets.h"
#include "GS.h"
#include "utils/Utils.h"
#include "datatables/CharNameTable.h"
#include "datatables/CharTemplateTable.h"
#include "datatables/ItemTable.h"
L2GamePacket *ClientPacketHandler::CharacterCreate( L2GamePacket *pack )
{
pack->getPacketType();
const wchar_t *name = pack->readUnicodeStringPtr(); // _name = readS();
int race = pack->readD(); // _race = readD();
int sex = pack->readD(); // _sex = readD();
int classId = pack->readD(); // _classId = readD();
pack->readD(); //_int = readD();
pack->readD(); //_str = readD();
pack->readD(); //_con = readD();
pack->readD(); //_men = readD();
pack->readD(); //_dex = readD();
pack->readD(); //_wit = readD();
int hairStyle = pack->readD(); //_hairStyle = readD();
int hairColor = pack->readD(); //_hairColor = readD();
int face = pack->readD(); //_face = readD();
// CharacterCreate
GameServer *gs = GameServer::getInstance();
int name_len = wcslen( name );
if( name_len < 1 || name_len > 16 )
{
if( gs->getConfig()->Debug )
LogWarning( L"%s: Character Creation Failure: Character name [%s] is invalid. "
L"Message generated: Your title cannot exceed 16 characters in length. Please try again.",
m_cl->toString(), name );
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_16_ENG_CHARS );
}
// alphanumeric name
if( !Utils_isValidCharName( name ) )
{
if( gs->getConfig()->Debug )
LogWarning( L"%s: Character Creation Failure: Character name [%s] is invalid. "
L"Message generated: Incorrect name. Please try again.", m_cl->toString(), name );
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_INCORRECT_NAME );
}
bool create_ok = false;
CharNameTable::getInstance()->LockDB();
if( !CharNameTable::getInstance()->doesCharNameExist( name ) )
{
// check maximum char count
int current_char_count = CharNameTable::getInstance()->getAccountCharCount( m_cl->getAccount() );
// TODO: CharacterCreate maximum char count may be in GS config?
if( current_char_count >= 7 )
{
if( gs->getConfig()->Debug ) LogWarning( L"%s: Max number of characters reached. Creation failed.", m_cl->toString() );
CharNameTable::getInstance()->UnlockDB();
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_TOO_MANY_CHARACTERS );
}
// get class template
const L2PlayerTemplate *tmpl = CharTemplateTable::getTemplate( classId );
if( !tmpl )
{
LogError( L"%s: CharacterCreate: template is NULL!", m_cl->toString() );
CharNameTable::getInstance()->UnlockDB();
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_CREATION_FAILED );
}
// base class must be 1 lvl
if( tmpl->classBaseLevel > 1 )
{
LogError( L"%s: CharacterCreate: template base level must be 1, but: %d!",
m_cl->toString(), tmpl->classBaseLevel );
CharNameTable::getInstance()->UnlockDB();
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_CREATION_FAILED );
}
// generate new objectId for char
unsigned int oid = gs->getIdFactory()->getNextObjectId();
// get race by class id
const ClassId *class_id = ClassIdTree::getInstance()->getClassId( classId );
assert( class_id != NULL );
int race_real = (int)class_id->getRace();
if( race_real != race )
LogWarning( L"%s: possible hacker: invalid race in CharacterCreate (%d)!=(%d)",
m_cl->toString(), race, race_real );
// insert new character into DB
MysqlQuery q;
q.create(
L"INSERT INTO characters ("
L"account_name,charId,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,"
L"x,y,z,heading,"
L"face,hairStyle,hairColor,sex,exp,sp,karma,fame,pvpkills,pkkills,clanid,race,classid,"
L"deletetime,cancraft,title,accesslevel,online,isin7sdungeon,clan_privs,wantspeace,base_class,"
L"newbie,nobless,power_grade,last_recom_date) values ("
L"'%s',%u,'%s',1, %d,%d,%d,%d,%d,%d, " // acc,oid,charName, ...
L"%d,%d,%d,0, " // x,y,z,heading
L"%d,%d,%d, %d,0,0,0,0,0,0,0,%d,%d, " // face,hs,hc, ..
L"0,0,'',0,0,0,0,0,%d, " // deltime, ...
L"1,0,0,%u)", /////////////////////////////////////////////////////////////////////////
m_cl->getAccount(), oid, name, (int)(tmpl->baseHpMax), (int)(tmpl->baseHpMax),
(int)(tmpl->baseCpMax), (int)(tmpl->baseCpMax), (int)(tmpl->baseMpMax), (int)(tmpl->baseMpMax),
tmpl->spawnX, tmpl->spawnY, tmpl->spawnZ,
face, hairStyle, hairColor, sex, race_real, classId, classId, (unsigned int)time( NULL )
);
MysqlConnection *con = gs->getDBConnection();
if( con->executeQuery( q ) )
{
create_ok = true;
}
else
{
LogError( L"%s: CharacterCreate: cannot create: MySQL error: %s\n", m_cl->toString(), con->getErrorStr() );
}
q.clear();
// add shortcuts
//(int slotId, int pageId, int shortcutType, int shortcutId, int shortcutLevel, int characterType)
// add attack shortcut L2ShortCut(0, 0, 3, 2, 0, 1);
q.create( L"INSERT INTO character_shortcuts (charId,slot,page,type,shortcut_id,level,class_index) VALUES ('%u',0,0,3,2,0,1)", oid );
if( !con->executeQuery( q ) ) LogError( L"CharacterCreate: shortcuts: MySQL: %s", con->getErrorStr() );
// add take shortcut L2ShortCut(3, 0, 3, 5, 0, 1);
q.create( L"INSERT INTO character_shortcuts (charId,slot,page,type,shortcut_id,level,class_index) VALUES ('%u',3,0,3,5,0,1)", oid );
if( !con->executeQuery( q ) ) LogError( L"CharacterCreate: shortcuts: MySQL: %s", con->getErrorStr() );
// add sit shortcut L2ShortCut(10, 0, 3, 0, 0, 1);
q.create( L"INSERT INTO character_shortcuts (charId,slot,page,type,shortcut_id,level,class_index) VALUES ('%u',10,0,3,0,0,1)", oid );
if( !con->executeQuery( q ) ) LogError( L"CharacterCreate: shortcuts: MySQL: %s", con->getErrorStr() );
// add start items
const std::list<L2PlayerTemplate::PcTemplateItem> itemsList = tmpl->getItems();
std::list<L2PlayerTemplate::PcTemplateItem>::const_iterator itemsListIter = itemsList.begin();
while( itemsListIter != itemsList.end() )
{
L2PlayerTemplate::PcTemplateItem item = (*itemsListIter);
const L2ItemTemplate *item_tmpl = ItemTable::getInstance()->getTemplate( item.getItemId() );
if( !item_tmpl )
{
LogError( L"CharacterCreate: cannot add starter item: template NULL (%d)", item.getItemId() );
continue;
}
//
q.clear();
unsigned int item_oid = gs->getIdFactory()->getNextObjectId();
if( item.isEquipped() )
{
// location set to paperdoll
q.create( L"INSERT INTO items (owner_id,item_id,count,loc,loc_data,enchant_level,"
L"object_id,custom_type1,custom_type2,mana_left,time) VALUES "
L"('%u','%d','%d','PAPERDOLL','%d',0,'%u','%d','%d','%d','%d')",
oid, item.getItemId(), item.getAmount(), (int)item_tmpl->getBodyPart(),
item_oid, item_tmpl->getType1(), item_tmpl->getType2(),
item_tmpl->getDuration(), item_tmpl->getTime() );
}
else
{
// location set to inventory
q.create( L"INSERT INTO items (owner_id,item_id,count,loc,loc_data,enchant_level,"
L"object_id,custom_type1,custom_type2,mana_left,time) VALUES "
L"('%u','%d','%d','INVENTORY','%d',0,'%u','%d','%d','%d','%d')",
oid, item.getItemId(), item.getAmount(), (int)item_tmpl->getBodyPart(),
item_oid, item_tmpl->getType1(), item_tmpl->getType2(),
item_tmpl->getDuration(), item_tmpl->getTime() );
}
if( !con->executeQuery( q ) )
LogError( L"CharacterCreate: cannot add starter item: MySQL error: %s", con->getErrorStr() );
// add tutbook shortcut
if( item.getItemId() == 5588 ) // CharacterCreate add Tutorial Guide...
{
// L2ShortCut(11, 0, 1, item.getObjectId(), 0, 1);
q.create( L"INSERT INTO character_shortcuts (charId,slot,page,type,shortcut_id,level,class_index) "
L"VALUES ('%u',11,0,1,'%u',0,1)", oid, item_oid );
if( !con->executeQuery( q ) )
LogError( L"CharacterCreate add Tutorial Guide: MySQL: %s", con->getErrorStr() );
}
//
itemsListIter++;
}
q.clear();
// TODO: CharacterCreate add starting skills (need SkillTable & SkillLearn)
gs->releaseDBConnection( con );
}
else
{
if( gs->getConfig()->Debug )
LogWarning( L"%s: Character Creation Failure: REASON_NAME_ALREADY_EXISTS.", m_cl->toString() );
CharNameTable::getInstance()->UnlockDB();
return ServerPackets::CharCreateFail( m_cl, L2Game_CharCreateFail::REASON_NAME_ALREADY_EXISTS );
}
CharNameTable::getInstance()->UnlockDB();
if( !create_ok )
{
return NULL;
}
return ServerPackets::CharCreateOK( m_cl );
}
/*
private static final String INSERT_CHARACTER = "INSERT INTO characters (account_name,charId,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,face,hairStyle,hairColor,sex,exp,sp,karma,fame,pvpkills,pkkills,clanid,race,classid,deletetime,cancraft,title,accesslevel,online,isin7sdungeon,clan_privs,wantspeace,base_class,newbie,nobless,power_grade,last_recom_date) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
private static final String UPDATE_CHARACTER = "UPDATE characters SET level=?,maxHp=?,curHp=?,maxCp=?,curCp=?,maxMp=?,curMp=?,face=?,hairStyle=?,hairColor=?,sex=?,heading=?,x=?,y=?,z=?,exp=?,expBeforeDeath=?,sp=?,karma=?,fame=?,pvpkills=?,pkkills=?,rec_have=?,rec_left=?,clanid=?,race=?,classid=?,deletetime=?,title=?,accesslevel=?,online=?,isin7sdungeon=?,clan_privs=?,wantspeace=?,base_class=?,onlinetime=?,punish_level=?,punish_timer=?,newbie=?,nobless=?,power_grade=?,subpledge=?,last_recom_date=?,lvl_joined_academy=?,apprentice=?,sponsor=?,varka_ketra_ally=?,clan_join_expiry_time=?,clan_create_expiry_time=?,char_name=?,death_penalty_level=?,bookmarkslot=?,vitality_points=? WHERE charId=?";
private static final String RESTORE_CHARACTER = "SELECT account_name, charId, char_name, level, maxHp, curHp, maxCp, curCp, maxMp, curMp, face, hairStyle, hairColor, sex, heading, x, y, z, exp, expBeforeDeath, sp, karma, fame, pvpkills, pkkills, clanid, race, classid, deletetime, cancraft, title, rec_have, rec_left, accesslevel, online, char_slot, lastAccess, clan_privs, wantspeace, base_class, onlinetime, isin7sdungeon, punish_level, punish_timer, newbie, nobless, power_grade, subpledge, last_recom_date, lvl_joined_academy, apprentice, sponsor, varka_ketra_ally,clan_join_expiry_time,clan_create_expiry_time,death_penalty_level,bookmarkslot,vitality_points FROM characters WHERE charId=?";
*/

View File

@@ -0,0 +1,44 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ClientPacketHandler.h"
#include "../ServerPackets.h"
#include "GS.h"
L2GamePacket *ClientPacketHandler::CharacterSelect( L2GamePacket *pack )
{
L2GamePacket *ret = NULL;
pack->getPacketType();
int charSlot = pack->readD();
LogDebug( L"CharacterSelect: charSlot : %d", charSlot );
// create &load player for game client if not exists
if( m_cl->getPlayer() == NULL )
{
GamePlayer *pl = new GamePlayer( m_cl, 0 );
// TODO: CharacterSelect: load char from base
m_cl->setPlayer( pl, true, CLIENT_STATE_IN_GAME ); // sets state to IN_GAME
//
L2Game_CharSelected *p = new L2Game_CharSelected();
ret = p;
p->p_char_name[0] = 0;
p->p_opcodeObfuscatorSeed = rand();
p->p_title[0] = 0;
p->p_classId = 0;
p->p_level = 1;
unsigned char playKey[8];
m_cl->getPlayKey( playKey );
p->p_sessionId = playKey[0] | (playKey[1] << 8) | (playKey[2] << 16) | (playKey[3] << 24); // playOkID1
//
p->create( L2_VERSION_T23 );
//
m_cl->setOpcodeObfuscationSeed( p->p_opcodeObfuscatorSeed );
}
else
LogError( L"Net: %s: CharacterSelect: already has player attached to GameClient?? Bad Bad Error or Cheater!",
m_cl->toString() );
//
return ret;
}

View File

@@ -0,0 +1,27 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ClientPacketHandler.h"
#include "../ServerPackets.h"
#include "GS.h"
L2GamePacket *ClientPacketHandler::Logout( L2GamePacket *pack )
{
pack->getPacketType();
L2GamePacket *ret = NULL;
switch( m_cl->getState() )
{
case CLIENT_STATE_OFFLINE:
case CLIENT_STATE_CONNECTED:
case CLIENT_STATE_AUTHED:
ret = NULL;
m_cl->signalThreadStop( true ); // we can just disconnect player now
break;
case CLIENT_STATE_IN_GAME:
// TODO: check if player can logout here and now
// answer with RestartResponse (LogoutOK) or (LogoutFailed)
break;
}
return ret;
}

View File

@@ -0,0 +1,13 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ClientPacketHandler.h"
#include "../ServerPackets.h"
#include "GS.h"
L2GamePacket *ClientPacketHandler::NewCharacter( L2GamePacket *pack )
{
pack = NULL; // just trigger
return ServerPackets::NewCharacterSuccess( m_cl );
}

View File

@@ -0,0 +1,37 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ClientPacketHandler.h"
#include "../ServerPackets.h"
#include "GS.h"
L2GamePacket *ClientPacketHandler::ProtocolVersion( L2GamePacket *pack )
{
GameServer *gs = GameServer::getInstance();
pack->getPacketType();
int pv = pack->readD();
// just server ping?
if( pv == -2 )
{
m_cl->setProtocolOk( false );
m_cl->signalThreadStop( true );
if( gs->getConfig()->Debug ) LogDebug( L"%s: received server ping from client (%d)", m_cl->toString(), pv );
return NULL;
}
// protocol version validation
if( gs->getConfig()->Debug ) LogDebug( L"%s: protocol version %d", m_cl->toString(), pv );
if( pv < gs->getConfig()->min_game_protocol_version ||
pv > gs->getConfig()->max_game_protocol_version )
{
m_cl->setProtocolOk( false );
m_cl->signalThreadStop( true );
LogWarning( L"%s: invalid protocol version %d (must be [%d..%d])",
m_cl->toString(), pv,
gs->getConfig()->min_game_protocol_version,
gs->getConfig()->max_game_protocol_version );
}
else m_cl->setProtocolOk( true );
// reply KeyPacket
return ServerPackets::KeyPacket( m_cl );
}

View File

@@ -0,0 +1,15 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ClientPacketHandler.h"
#include "../ServerPackets.h"
#include "GS.h"
L2GamePacket *ClientPacketHandler::RequestGotoLobby( L2GamePacket *pack )
{
pack = NULL; // unused here
return ServerPackets::CharacterSelectionInfo( m_cl );
}

View File

@@ -0,0 +1,15 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ServerPackets.h"
#include "GS.h"
L2GamePacket *ServerPackets::CharCreateFail( GameClient *pl, int reason )
{
pl = NULL; // reference to player not needed
L2Game_CharCreateFail *p = new L2Game_CharCreateFail();
p->p_reasonCode = (unsigned int)reason;
p->create( L2_VERSION_T23 );
return p;
}

View File

@@ -0,0 +1,14 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ServerPackets.h"
#include "GS.h"
L2GamePacket *ServerPackets::CharCreateOK( GameClient *pl )
{
pl = NULL; // reference to player not needed
L2Game_CharCreateSuccess *p = new L2Game_CharCreateSuccess();
p->create( L2_VERSION_T23 );
return p;
}

View File

@@ -0,0 +1,225 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ServerPackets.h"
#include "GS.h"
#include "world/templates/item/L2ItemTemplate.h"
L2GamePacket *ServerPackets::CharacterSelectionInfo( GameClient *pl )
{
GameServer *gs = GameServer::getInstance();
MysqlConnection *con = gs->getDBConnection();
L2Game_CharSelectionInfoBlock csb[7];
memset( &csb, 0, sizeof(csb) );
int i = 0;
int num_chars = 0;
unsigned int max_lastAccess = 0;
int activeCharIndex = -1;
MysqlQuery q;
q.create( L"SELECT account_name, charId, char_name, level, maxHp, curHp, maxMp, curMp, face, hairStyle, "
L"hairColor, sex, heading, x, y, z, exp, sp, karma, pvpkills, pkkills, clanid, race, classid, "
L"deletetime, cancraft, title, rec_have, rec_left, accesslevel, online, char_slot, lastAccess, "
L"base_class, transform_id FROM characters WHERE account_name='%s' LIMIT 7", pl->getAccount() );
if( con->executeQuery( q ) )
{
i = 0;
while( q.getNextRow() )
{
unsigned int lastAccess = q.getFieldUInt( "lastAccess" );
if( lastAccess > max_lastAccess )
{
max_lastAccess = lastAccess;
activeCharIndex = i;
}
//
q.getFieldStrW( 0, csb[i].accountName, 32 );
csb[i].charID = q.getFieldUInt( 1 );
q.getFieldStrW( 2, csb[i].charName, 32 );
csb[i].level = q.getFieldUInt( 3 );
csb[i].HP_max = q.getFieldDouble( 4 );
csb[i].HP_cur = q.getFieldDouble( 5 );
csb[i].MP_max = q.getFieldDouble( 6 );
csb[i].MP_cur = q.getFieldDouble( 7 );
csb[i].face = q.getFieldUInt( 8 );
csb[i].hairStyle = q.getFieldUInt( 9 );
csb[i].hairColor = q.getFieldUInt( 10 );
csb[i].sex = q.getFieldUInt( 11 );
//q.getFieldUInt( 12, ???? ); // no heading
csb[i].x = q.getFieldInt( 13 );
csb[i].y = q.getFieldInt( 14 );
csb[i].z = q.getFieldInt( 15 );
csb[i].Exp = q.getFieldUInt64( 16 );
csb[i].SP = q.getFieldUInt( 17 );
csb[i].karma = q.getFieldUInt( 18 );
csb[i].PVP_kills = q.getFieldUInt( 19 );
csb[i].PK_kills = q.getFieldUInt( 20 );
csb[i].clanID = q.getFieldUInt( 21 );
csb[i].race = q.getFieldUInt( 22 );
csb[i].classID = q.getFieldUInt( 23 );
csb[i].deleteSeconds = q.getFieldUInt( 24 );
//q.getFieldUInt( 25, can craft??? );
//q.getFieldStrW( 26, title?? );
//q.getFieldUInt( 27, rec_have );
csb[i].baseClassID = q.getFieldUInt( "base_class" );
csb[i].isActive = 1;
// fake
//csb[i].iid_chest = 0x0967; // DC Robe
//csb[i].iid_R_hand = 0x2ec2; // Common Branch of Mother tree
//
i++;
}
num_chars = i;
}
else
{
LogError( L"CharacterSelectionInfo: MySQL error: %s", con->getErrorStr() );
}
// TODO: load also equipped inventory items
for( i=0; i<num_chars; i++ )
{
q.clear();
q.create( L"SELECT object_id,item_id,loc_data,enchant_level FROM items WHERE owner_id=%u AND loc='PAPERDOLL'", csb[i].charID );
if( con->executeQuery( q ) )
{
while( q.getNextRow() )
{
int slot = q.getFieldInt( "loc_data" );
int item_id = q.getFieldInt( "item_id" );
int enchant_level = q.getFieldInt( "enchant_level" );
//
switch( slot )
{
case 1: csb[i].iid_R_ear = item_id; break;
case 2: csb[i].iid_L_ear = item_id; break;
//case 3: LREAR
case 4: csb[i].iid_neck = item_id; break;
case 5: csb[i].iid_L_finger = item_id; break;
case 6: csb[i].iid_R_finger = item_id; break;
//case 7: LRFINGER
case 8: csb[i].iid_head = item_id; break;
case 9: csb[i].iid_R_hand = item_id; break;
case 10: csb[i].iid_L_hand = item_id; break;
case 11: csb[i].iid_gloves = item_id; break;
case 12: csb[i].iid_chest = item_id; break;
case 13: csb[i].iid_legs = item_id; break;
case 14: csb[i].iid_feet = item_id; break;
case 15: csb[i].iid_back = item_id; break;
case 16: csb[i].iid_R_hand = csb[i].iid_L_hand = item_id; break; // LRHAND
case 17: csb[i].iid_chest = item_id; break; // fullarmor?
case 18: csb[i].iid_hair = item_id; break;
//case 19: ALLDRESS
case 20: csb[i].iid_hair_2 = item_id; break;
case 21: csb[i].iid_hair_all = item_id; break;
case 22: csb[i].iid_R_bracelet = item_id; break;
case 23: csb[i].iid_L_bracelet = item_id; break;
// 24..29 DECOs
case 30: csb[i].iid_belt = item_id; break;
}
if( slot == SLOT_R_HAND && enchant_level > 0 ) // weapon
csb[i].enchantEffect = (unsigned char)(enchant_level & 0x000000FF);
}
}
else
{
LogError( L"CharacterSelectionInfo: MySQL error: %s", con->getErrorStr() );
}
}
q.clear();
gs->releaseDBConnection( con );
// retreive playOk1
unsigned char playKey[8];
memset( playKey, 0, 8 );
pl->getPlayKey( playKey );
unsigned int playOk1 = *(unsigned int *)&playKey[0];
//
L2GamePacket *p = new L2GamePacket();
p->setPacketType( 0x09 );
p->writeD( num_chars ); // number of chars
p->writeD( 0x07 ); // server max_chars
p->writeC( 0x00 ); // 0x00
//
for( i=0; i<num_chars; i++ )
{
p->writeS( csb[i].charName );
p->writeUInt( csb[i].charID );
p->writeS( pl->getAccount() );
p->writeUInt( playOk1 ); // TODO: playOK id 1
p->writeUInt( csb[i].clanID );
p->writeD( 0x00 );
//
p->writeUInt( csb[i].sex );
p->writeUInt( csb[i].race );
p->writeUInt( csb[i].baseClassID );
p->writeUInt( (i == activeCharIndex) ? 0x01 : 0x00 ); // is active
//
p->writeInt( csb[i].x );
p->writeInt( csb[i].y );
p->writeInt( csb[i].z );
p->writeDouble( csb[i].HP_cur );
p->writeDouble( csb[i].MP_cur );
//
p->writeUInt( csb[i].SP );
p->writeUInt64( csb[i].Exp );
p->writeUInt( csb[i].level );
//
p->writeUInt( csb[i].karma );
p->writeUInt( csb[i].PK_kills );
p->writeUInt( csb[i].PVP_kills );
//
p->writeD( 0x00 ); p->writeD( 0x00 ); p->writeD( 0x00 ); p->writeD( 0x00 );
p->writeD( 0x00 ); p->writeD( 0x00 ); p->writeD( 0x00 ); // 7 0x00
//
// 26 inventory item ids // TODO: equipment ids
p->writeD( csb[i].iid_hair_all ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_HAIRALL));
p->writeD( csb[i].iid_R_ear ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_REAR));
p->writeD( csb[i].iid_L_ear ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LEAR));
p->writeD( csb[i].iid_neck ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_NECK));
p->writeD( csb[i].iid_R_finger ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_RFINGER));
p->writeD( csb[i].iid_L_finger ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LFINGER));
p->writeD( csb[i].iid_head ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_HEAD));
p->writeD( csb[i].iid_R_hand ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_RHAND));
p->writeD( csb[i].iid_L_hand ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LHAND));
p->writeD( csb[i].iid_gloves ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_GLOVES));
p->writeD( csb[i].iid_chest ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_CHEST));
p->writeD( csb[i].iid_legs ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LEGS));
p->writeD( csb[i].iid_feet ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_FEET));
p->writeD( csb[i].iid_back ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_BACK));
p->writeD( csb[i].iid_LR_hand ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LRHAND));
p->writeD( csb[i].iid_hair ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_HAIR));
p->writeD( csb[i].iid_hair_2 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_HAIR2));
p->writeD( csb[i].iid_R_bracelet ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_RBRACELET));
p->writeD( csb[i].iid_L_bracelet ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_LBRACELET));
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO1));
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO2));
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO3));
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO4));
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO5));
p->writeD( 0x00 ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_DECO6));
p->writeD( csb[i].iid_belt ); //writeD(charInfoPackage.getPaperdollItemId(Inventory.PAPERDOLL_BELT));
//
p->writeUInt( csb[i].hairStyle );
p->writeUInt( csb[i].hairColor );
p->writeUInt( csb[i].face );
//LogDebug( L"Writing hs,hc,face = %u,%u,%u", csb[i].hairStyle, csb[i].hairColor, csb[i].face );
//
p->writeDouble( csb[i].HP_max );
p->writeDouble( csb[i].MP_max );
//
p->writeUInt( csb[i].deleteSeconds );
p->writeUInt( csb[i].classID ); // not base classid
p->writeUInt( 0x00 ); // TODO: is last used char
//
p->writeC( 0x00 ); // TODO: enchant Effect
p->writeD( 0x00 ); // TODO: aug. ID
p->writeD( 0x00 ); // transformID is always 0 here
}
//
//FILE *_f = fopen( "CharacterSelectionInfo.log", "wt" );
//p->dumpToFile( _f );
//fclose( _f );
//
return p;
}

View File

@@ -0,0 +1,27 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ServerPackets.h"
#include "GS.h"
L2GamePacket *ServerPackets::KeyPacket( GameClient *pl )
{
#ifdef ENABLE_OBF
unsigned int obf_seed = rand();
#else
unsigned int obf_seed = 0;
#endif
pl->setOpcodeObfuscationSeed( obf_seed );
//
L2Game_KeyPacket *p = new L2Game_KeyPacket();
p->p_protocolIsOK = pl->isProtocolOk();
p->p_obfuscatorSeed = obf_seed;
p->p_serverId = (unsigned char)GameServer::getInstance()->getServerId();
// initial key is random bytes...
L2Game_KeyPacket::createInitialHellboundKey( p->p_initialKey, p->p_initialKey );
p->create( L2_VERSION_T23 );
// enable crypt on client
pl->enable_XOR_crypt( true, p->p_initialKey );
return p;
}

View File

@@ -0,0 +1,106 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "net/GameClient/GameClient.h"
#include "../ServerPackets.h"
#include "GS.h"
//
#include "world/model/base/ClassIdTree.h"
#include "datatables/CharTemplateTable.h"
/*
Server: Len 967 [NewCharacterSuccess]
C7 03
0D // opcode
0C 00 00 00 // number of templates
// [ for each template ]
00 00 00 00 // race
00 00 00 00 // class id
46 00 00 00 // 0x46
28 00 00 00 // base STR
0A 00 00 00 // 0x0A
46 00 00 00 // 0x46
1E 00 00 00 // base DEX
0A 00 00 00
46 00 00 00
2B 00 00 00 // base CON
0A 00 00 00
46 00 00 00
15 00 00 00 // base INT
0A 00 00 00
46 00 00 00
0B 00 00 00 // base WIT
0A 00 00 00
46 00 00 00
19 00 00 00 // base MEN
0A 00 00 00
*/
L2GamePacket *ServerPackets::NewCharacterSuccess( GameClient *pl )
{
pl = NULL; // reference to player is not needed here
//
unsigned int numTemplates = 0;
L2Game_NewCharacterTemplate tmpl[32];
int ic = 0;
ClassIdTree *cidtree = ClassIdTree::getInstance();
for( ic = 0; ic < ClassIdTree::NUM_CLASS_IDS; ic++ )
{
// no Inspector, though it's base class
if( ic == ClassId::CID_INSPECTOR ) continue;
// get ClassId if class
const ClassId *cid = cidtree->getClassId( ic );
if( cid )
{
// no parents, base classes
if( cid->getParentId() == ClassId::CID_NONE )
{
// get template base stats for class
const L2PlayerTemplate *plt = CharTemplateTable::getTemplate( ic );
if( plt )
{
tmpl[numTemplates].classID = ic;
tmpl[numTemplates].base_CON = plt->baseCON;
tmpl[numTemplates].base_DEX = plt->baseDEX;
tmpl[numTemplates].base_INT = plt->baseINT;
tmpl[numTemplates].base_MEN = plt->baseMEN;
tmpl[numTemplates].base_STR = plt->baseSTR;
tmpl[numTemplates].base_WIT = plt->baseWIT;
tmpl[numTemplates].race = (int)plt->race;
numTemplates++;
}
}
}
if( numTemplates >= 32 ) break;
}
//
L2GamePacket *p = new L2GamePacket();
p->setPacketType( 0x0D ); // NewCharacterSuccess
p->writeUInt( numTemplates );
for( ic = 0; ic < (int)numTemplates; ic++ )
{
p->writeD( tmpl[ic].race );
p->writeD( tmpl[ic].classID );
p->writeD( 0x46 );
p->writeD( tmpl[ic].base_STR );
p->writeD( 0x0A );
p->writeD( 0x46 );
p->writeD( tmpl[ic].base_DEX );
p->writeD( 0x0A );
p->writeD( 0x46 );
p->writeD( tmpl[ic].base_CON );
p->writeD( 0x0A );
p->writeD( 0x46 );
p->writeD( tmpl[ic].base_INT );
p->writeD( 0x0A );
p->writeD( 0x46 );
p->writeD( tmpl[ic].base_WIT );
p->writeD( 0x0A );
p->writeD( 0x46 );
p->writeD( tmpl[ic].base_MEN );
p->writeD( 0x0A );
}
return p;
}