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

393
L2C_Server/GS.cpp Normal file
View File

@@ -0,0 +1,393 @@
#include "pch.h"
#include "Log.h"
#include "GS.h"
#include "utils/Debugging.h"
#include "utils/Exception.h"
#include "world/model/base/ClassIdTree.h"
#include "datatables/CharNameTable.h"
#include "datatables/CharTemplateTable.h"
#include "datatables/ItemTable.h"
// init class static vars
GameServer *GameServer::_self = NULL;
int GameServer::_referenceCount = 0;
GameServer *GameServer::getInstance()
{
if( !_self )
{
_self = new GameServer();
_referenceCount++;
}
return _self;
}
void GameServer::freeInstance()
{
if( _self )
{
_referenceCount--;
if( _referenceCount == 0 )
{
delete _self;
_self = NULL;
}
}
}
GameServer::GameServer()
{
m_serverStatus = SERVER_STATUS_DOWN;
m_cfg = new GameConfig();
m_mysql = new MysqlConnectionManager();
m_loginConnection = new LoginConnection();
m_isRunning = m_flagStop = false;
m_clientPool = new ClientPool();
m_idf = new IdFactory();
}
GameServer::~GameServer()
{
stop();
if( m_cfg ) { delete m_cfg; m_cfg = NULL; }
if( m_mysql ) { delete m_mysql; m_mysql = NULL; }
if( m_loginConnection ) { delete m_loginConnection; m_loginConnection = NULL; }
if( m_clientPool ) { delete m_clientPool; m_clientPool = NULL; }
if( m_idf ) { delete m_idf; m_idf = NULL; }
}
bool GameServer::start()
{
if( m_isRunning ) return false;
HANDLE hThread = NULL;
unsigned int threadId = 0;
m_gsLock.Lock();
{
m_flagStop = false;
m_isRunning = false;
hThread = (HANDLE)_beginthreadex( NULL, 0,
(unsigned int (__stdcall *)(void *))(GameServer::GS_Thread), (void *)this,
0, &threadId );
if( hThread )
{
m_isRunning = true;
CloseHandle( hThread );
}
else
{
m_isRunning = false;
}
}
m_gsLock.Unlock();
return (hThread != NULL);
}
bool GameServer::stop()
{
if( !m_isRunning ) return false;
m_gsLock.Lock();
{
this->m_flagStop = true;
while( this->m_isRunning ) Sleep( 20 );
this->m_flagStop = false;
}
m_gsLock.Unlock();
return true;
}
bool GameServer::isRunning() const
{
return m_isRunning;
}
GameConfig *GameServer::getConfig() const
{
return m_cfg;
}
MysqlConnection *GameServer::getDBConnection()
{
MysqlConnection *ret = m_mysql->getConnection();
if( ret ) return ret;
LogError( L"GameServer::getDBConnection(): not enough free connections, %d/%d busy",
m_mysql->getCountActiveConnections(), m_mysql->getCountMaxConnections() );
throw Exception( "GameServer::getDBConnection(): no free connections!" );
}
bool GameServer::releaseDBConnection( MysqlConnection *con )
{
if( !con ) return false;
return m_mysql->releaseConnection( con );
}
void GameServer::LogFile( const char *fn, const char *_Format, ... )
{
char ffn[256] = {0};
sprintf( ffn, ".\\log\\game_%s.log", fn );
FILE *f = fopen( ffn, "at" );
if( !f ) return;
SYSTEMTIME st;
GetLocalTime( &st );
fprintf( f, "%02d.%02d.%04d %02d:%02d:%03d: ", (int)st.wDay, (int)st.wMonth, (int)st.wYear,
(int)st.wHour, (int)st.wMinute, (int)st.wMilliseconds );
va_list _ArgList;
va_start( _ArgList, _Format );
vfprintf( f, _Format, _ArgList );
fclose( f );
}
const wchar_t *GameServer::getServerName() const
{
if( !m_loginConnection ) return L"NULL";
return m_loginConnection->getRegisteredServerName();
}
int GameServer::getServerId() const
{
if( !m_loginConnection ) return 0;
return m_loginConnection->getRegisteredServerId();
}
/* Server will be considered as Good when the number of online players
* is less than half the maximum. as Normal between half and 4/5
* and Full when there's more than 4/5 of the maximum number of players */
ServerStatus GameServer::getServerStatus()
{
// TODO: ServerStatus may also be GM_ONLY
// SERVER_STATUS_DOWN is set when server is shut down
if( m_serverStatus != SERVER_STATUS_DOWN )
{
int curOnline = getCurrentOnline();
int maxOnline = getMaxPlayers();
float ratio = 100.0f * ((float)curOnline) / ((float)maxOnline);
if( ratio <= 0.5f ) m_serverStatus = SERVER_STATUS_GOOD;
if( ratio > 0.5f && ratio <= 0.8f ) m_serverStatus = SERVER_STATUS_NORMAL;
if( ratio > 0.8f ) m_serverStatus = SERVER_STATUS_FULL;
}
return m_serverStatus;
}
int GameServer::getMaxPlayers() const
{
return m_cfg->max_players;
}
int GameServer::getCurrentOnline()
{
if( !m_clientPool ) return 0;
return m_clientPool->getCurrentOnline();
}
bool GameServer::checkFreePort( const wchar_t *bind_addr, int bind_port )
{
char a_addr[128] = {0};
l2c_unicode_to_ansi( bind_addr, a_addr, 128 );
unsigned short s_port = (unsigned short)( bind_port & 0xFFFF );
SOCKET s = L2PNet_TCPsocket_create( true );
if( s == INVALID_SOCKET ) return false;
int r = L2PNet_bind( s, a_addr, s_port );
L2PNet_shutdown( s );
L2PNet_closesocket( s );
return (r==0);
}
ClientPool *GameServer::getClientPool() const
{
return m_clientPool;
}
IdFactory *GameServer::getIdFactory()
{
return m_idf;
}
bool GameServer::kickPlayerByAccount( const wchar_t *accountName, bool reasonDualLogin )
{
if( !accountName ) return false;
if( !m_clientPool ) return false;
bool ret = m_clientPool->disconnectPlayerByAccount( accountName, reasonDualLogin );
return ret;
}
bool GameServer::logoutAccountFromLoginServer( const wchar_t *accountName )
{
if( !m_loginConnection || !accountName ) return false;
m_loginConnection->send_PlayerLogOut( accountName );
return true;
}
void GameServer::addWaitingClientAndSendPlayerAuthRequest( const wchar_t *accountName, unsigned char *loginKey, unsigned char *playKey )
{
if( !m_loginConnection ) return;
m_loginConnection->send_PlayerAuthRequest( accountName, loginKey, playKey );
}
void GameServer::notifyLoginPlayerAuth( const wchar_t *account, int ok )
{
if( !m_clientPool ) return;
m_clientPool->notifyClientAuthResponse( account, ok ? true : false );
}
DWORD WINAPI GameServer::GS_Thread( LPVOID lpvParam )
{
GameServer *gs = (GameServer *)lpvParam;
// remember load time start
DWORD startTick = GetTickCount();
DWORD endTick = 0;
int secs_load = 0;
LogFull( false, true, RGB(0,200,0), RGB(255,255,255), L"===================================" );
Log( L"Starting game server..." );
SOCKET gs_acceptor = INVALID_SOCKET;
try
{
///////////////////////////////////
// begin server initialization
///////////////////////////////////
// ensure log dir exists
if( CreateDirectory( TEXT(".\\log"), NULL ) ) Log( L"Created logs directory." );
// Load config
if( !gs->m_cfg->load() ) throw Exception( "GS_Thread: error loading config!" );
LogDebug( L"Config loaded." );
// check binding port is free
if( !checkFreePort( gs->m_cfg->game_server_bind_address, gs->m_cfg->game_server_bind_port ) )
{
LogError( L"Error: bind address %s:%d already in use (another GS is running?)",
gs->m_cfg->game_server_bind_address, gs->m_cfg->game_server_bind_port );
throw Exception( "Check free bind port failed" );
}
// Initialize MySQL
if( !gs->m_mysql->initialize( gs->m_cfg->mysql_max_connections, gs->m_cfg->mysql_host,
gs->m_cfg->mysql_user, gs->m_cfg->mysql_pass, gs->m_cfg->mysql_db ) )
throw Exception( "MySQL init error!" );
MysqlConnection *test_con = gs->m_mysql->getConnection();
if( !test_con ) throw Exception( "MySQL init error (no free connections???)" );
if( !test_con->isConnected() )
{
char comment[256] = {0};
sprintf( comment, "MySQL test connection error: %S", test_con->getErrorStr() );
gs->m_mysql->releaseConnection( test_con );
throw Exception( comment );
}
LogDebug( L"MySQL connection test OK" );
// init IdFactory
gs->m_idf->init();
Log( L"IdFactory: used %u object IDs, %I64u bytes", gs->m_idf->getUsedCount(), gs->m_idf->getUsedMemoryBytes() );
// init & load datatables
ItemTable::getInstance()->load();
ClassIdTree::getInstance();
CharTemplateTable::getInstance()->load();
CharNameTable::getInstance();
// TODO: load skills...
// TODO: load scripts/quests... TODO
// create listening socket
gs_acceptor = L2PNet_TCPsocket_create( true );
char a_bind_ip[128] = {0};
l2c_unicode_to_ansi( gs->m_cfg->game_server_bind_address, a_bind_ip, 128 );
if( L2PNet_bind( gs_acceptor, a_bind_ip, (unsigned short)gs->m_cfg->game_server_bind_port ) != 0 )
{
LogError( L"GS acceptor bind error at %s:%d!", gs->m_cfg->game_server_bind_address,
gs->m_cfg->game_server_bind_port );
throw Exception( "GS acceptor bind error!" );
}
L2PNet_listen( gs_acceptor );
// measure load time
endTick = GetTickCount();
secs_load = (endTick - startTick) / 1000;
Log( L"Game server loaded in %d seconds (%d ms).", secs_load, (int)(endTick - startTick) );
LogFull( false, true, RGB(0,200,0), RGB(255,255,255), L"===================================" );
// mark server as running
gs->m_serverStatus = SERVER_STATUS_AUTO;
// Initiaite login server connection thread
gs->m_loginConnection->start( gs->m_cfg->login_server_address,
gs->m_cfg->login_server_auth_port, gs->m_cfg->login_protocol_version );
////////////////////////////////////////////
// initiate main server loop
////////////////////////////////////////////
while( !gs->m_flagStop )
{
int r = 0, bReadyRead = 0, bReadyWrite = 0;
r = L2PNet_select( gs_acceptor, L2PNET_SELECT_READ, 200, &bReadyRead, &bReadyWrite );
if( r == 0 ) continue; // timeout
if( r == -1 ) // error
throw Exception( "GS acceptor select() error!" );
// accepted client?
if( bReadyRead )
{
char clientIp[32] = {0};
unsigned short clientPort = 0;
SOCKET s_cl = L2PNet_accept( gs_acceptor, clientIp, &clientPort );
if( s_cl != INVALID_SOCKET )
{
LogDebug( L"Accepted client %S:%d", clientIp, (int)clientPort );
if( !gs->m_clientPool->addClient( s_cl, clientIp, (int)clientPort ) )
{
LogError( L"Failed to add client %S:%d to clients list!", clientIp, (int)clientPort );
}
}
else LogWarning( L"gs acceptor: invalid_socket accepted :(" );
}
}
///////////////////////////////////////////
// end main server loop
///////////////////////////////////////////
}
catch( std::exception& e )
{
LogError( L"GS_Thread: std::exception: %S\n", e.what() );
}
catch( Exception& e )
{
LogError( L"GS_Thread: Exception: %S", e.what() );
e.logStackTrace();
}
////////////////////////////////////////////
// initiate shutdown procedures
////////////////////////////////////////////
// notify login server that server will now be offline
gs->m_serverStatus = SERVER_STATUS_DOWN;
gs->m_loginConnection->send_ServerStatus();
// disconnect all players
gs->m_clientPool->disconnectAllPlayers();
// TODO: save all data
// close GS acceptor socket
if( gs_acceptor != INVALID_SOCKET )
{
L2PNet_shutdown( gs_acceptor );
L2PNet_closesocket( gs_acceptor );
gs_acceptor = INVALID_SOCKET;
}
// cleanup datatables
CharNameTable::freeInstance();
CharTemplateTable::freeInstance();
ClassIdTree::freeInstance();
ItemTable::freeInstance();
// close id factory
gs->m_idf->clear();
// stop login conn thread
gs->m_loginConnection->stop();
// mark as not running
gs->m_isRunning = false;
return 0;
}

58
L2C_Server/GS.h Normal file
View File

@@ -0,0 +1,58 @@
#pragma once
#include "l2c_utils.h"
#include "enums.h"
#include "LS_Connection.h"
#include "GS_Cfg.h"
#include "net/ClientPool.h"
#include "utils/IdFactory.h"
class GameServer
{
protected:
GameServer(); // cannot be created - only by getInstance()
~GameServer(); // canot be deleted - only by freeInstnce()
public:
static GameServer *getInstance();
static void freeInstance();
protected:
static GameServer *_self;
static int _referenceCount;
public:
bool start();
bool stop();
bool isRunning() const;
public:
GameConfig *getConfig() const;
MysqlConnection *getDBConnection();
bool releaseDBConnection( MysqlConnection *con );
void LogFile( const char *fn, const char *_Format, ... );
const wchar_t *getServerName() const;
int getServerId() const;
ServerStatus getServerStatus();
int getMaxPlayers() const;
int getCurrentOnline();
ClientPool *getClientPool() const;
IdFactory *getIdFactory();
public:
bool kickPlayerByAccount( const wchar_t *accountName, bool reasonDualLogin );
bool logoutAccountFromLoginServer( const wchar_t *accountName );
void addWaitingClientAndSendPlayerAuthRequest( const wchar_t *accountName, unsigned char *loginKey, unsigned char *playKey );
void notifyLoginPlayerAuth( const wchar_t *account, int ok );
protected:
CriticalSection m_gsLock;
GameConfig *m_cfg;
MysqlConnectionManager *m_mysql;
LoginConnection *m_loginConnection;
ServerStatus m_serverStatus;
ClientPool *m_clientPool;
IdFactory *m_idf;
protected:
bool m_isRunning;
bool m_flagStop;
static DWORD WINAPI GS_Thread( LPVOID lpvParam );
static bool checkFreePort( const wchar_t *bind_addr, int bind_port );
};

144
L2C_Server/GS_Cfg.cpp Normal file
View File

@@ -0,0 +1,144 @@
#include "pch.h"
#include "Log.h"
#include "GS_Cfg.h"
GameConfig::GameConfig()
{
m_cfg_mysql = m_cfg_network = m_cfg_rates = m_cfg_hexid = m_cfg_dev = NULL;
clear();
}
GameConfig::~GameConfig()
{
clear();
if( m_cfg_mysql ) { delete m_cfg_mysql; m_cfg_mysql = NULL; }
if( m_cfg_network ) { delete m_cfg_network; m_cfg_network = NULL; }
if( m_cfg_rates ) { delete m_cfg_rates; m_cfg_rates = NULL; }
if( m_cfg_hexid ) { delete m_cfg_hexid; m_cfg_hexid = NULL; }
if( m_cfg_dev ) { delete m_cfg_dev; m_cfg_dev = NULL; }
}
void GameConfig::clear()
{
// clear MySQL settings
mysql_host = mysql_user = mysql_pass = mysql_db = NULL;
mysql_max_connections = 0;
// hexid
server_hexid = NULL;
server_id = 0;
accept_alternate_server_id = list_as_test_server = server_list_clock =
server_list_brackets = false;
// network
login_server_address = NULL;
login_server_auth_port = login_protocol_version = 0;
game_server_bind_address = NULL;
game_server_bind_port = 0;
report_internal_hostname = report_external_hostname = NULL;
min_game_protocol_version = max_game_protocol_version = 0;
max_players = 0;
// developer settings
EverybodyHasAdminRights = Debug = Assert = false;
}
bool GameConfig::load()
{
bool load_ok = true;
load_ok &= load_mysql();
load_ok &= load_hexid();
load_ok &= load_network();
load_ok &= load_rates();
load_ok &= load_developer();
return load_ok;
}
bool GameConfig::load_mysql()
{
if( m_cfg_mysql ) delete m_cfg_mysql;
m_cfg_mysql = new L2C_ConfigFile();
if( !m_cfg_mysql->load( L"./config_game/mysql.conf" ) )
{
LogError( L"Cannot open file: ./config_game/mysql.conf!" );
return false;
}
// init
mysql_host = (wchar_t *)m_cfg_mysql->getValueStrW( L"mysql_host", L"localhost" );
mysql_user = (wchar_t *)m_cfg_mysql->getValueStrW( L"mysql_user", L"root" );
mysql_pass = (wchar_t *)m_cfg_mysql->getValueStrW( L"mysql_pass", L"" );
mysql_db = (wchar_t *)m_cfg_mysql->getValueStrW( L"mysql_db", L"l2jdb" );
mysql_max_connections = m_cfg_mysql->getValueInt( L"mysql_max_connections", 10 );
//
return true;
}
bool GameConfig::load_hexid()
{
if( m_cfg_hexid ) delete m_cfg_hexid;
m_cfg_hexid = new L2C_ConfigFile();
if( !m_cfg_hexid->load( L"./config_game/hexid.txt" ) )
{
LogError( L"Cannot open file: ./config_game/hexid.txt!" );
return false;
}
// init
server_hexid = (wchar_t *)m_cfg_hexid->getValueStrW( L"HexID", L"" );
server_id = m_cfg_hexid->getValueInt( L"ServerID", 1 );
//
return true;
}
bool GameConfig::load_network()
{
if( m_cfg_network ) delete m_cfg_network;
m_cfg_network = new L2C_ConfigFile();
if( !m_cfg_network->load( L"./config_game/network.conf" ) )
{
LogError( L"Cannot open file: ./config_game/network.conf!" );
return false;
}
// init
login_server_address = (wchar_t *)m_cfg_network->getValueStrW( L"login_server_address", L"127.0.0.1" );
login_server_auth_port = m_cfg_network->getValueInt( L"login_server_auth_port", 9014 );
login_protocol_version = m_cfg_network->getValueInt( L"login_protocol_version", 258 );
game_server_bind_address = (wchar_t *)m_cfg_network->getValueStrW( L"game_server_bind_address", L"0.0.0.0" );
game_server_bind_port = m_cfg_network->getValueInt( L"game_server_bind_port", 7777 );
report_internal_hostname = (wchar_t *)m_cfg_network->getValueStrW( L"report_internal_hostname", L"127.0.0.1" );
report_external_hostname = (wchar_t *)m_cfg_network->getValueStrW( L"report_external_hostname", L"127.0.0.1" );
min_game_protocol_version = m_cfg_network->getValueInt( L"min_game_protocol_version", 12 );
max_game_protocol_version = m_cfg_network->getValueInt( L"max_game_protocol_version", 17 );
max_players = m_cfg_network->getValueInt( L"max_players", 10 );
// misc net
accept_alternate_server_id = m_cfg_network->getValueBool( L"accept_alternate_server_id", false );
list_as_test_server = m_cfg_network->getValueBool( L"list_as_test_server", false );
server_list_clock = m_cfg_network->getValueBool( L"server_list_clock", false );
server_list_brackets = m_cfg_network->getValueBool( L"server_list_brackets", false );
//
return true;
}
bool GameConfig::load_developer()
{
if( m_cfg_dev ) delete m_cfg_dev;
m_cfg_dev = new L2C_ConfigFile();
if( !m_cfg_dev->load( L"./config_game/developer.conf" ) )
{
LogError( L"Cannot open file: ./config_game/developer.conf!" );
return false;
}
// init
EverybodyHasAdminRights = m_cfg_dev->getValueBool( L"EverybodyHasAdminRights", false );
Assert = m_cfg_dev->getValueBool( L"Assert", false );
Debug = m_cfg_dev->getValueBool( L"Debug", false );
//
return true;
}
bool GameConfig::load_rates()
{
return true;
}
bool GameConfig::save()
{
return true;
}

57
L2C_Server/GS_Cfg.h Normal file
View File

@@ -0,0 +1,57 @@
#pragma once
#include "l2c_utils.h"
class GameConfig
{
public:
GameConfig();
~GameConfig();
public:
void clear();
bool load();
bool save();
public:
// MySQL settings
wchar_t *mysql_host;
wchar_t *mysql_user;
wchar_t *mysql_pass;
wchar_t *mysql_db;
int mysql_max_connections;
// Server auth settings
wchar_t *server_hexid;
int server_id;
bool accept_alternate_server_id;
bool list_as_test_server;
bool server_list_clock;
bool server_list_brackets;
// Network settings
wchar_t *login_server_address;
int login_server_auth_port;
int login_protocol_version;
wchar_t *game_server_bind_address;
int game_server_bind_port;
wchar_t *report_internal_hostname;
wchar_t *report_external_hostname;
int min_game_protocol_version;
int max_game_protocol_version;
int max_players;
// developer settings
bool EverybodyHasAdminRights;
bool Debug;
bool Assert;
protected:
L2C_ConfigFile *m_cfg_mysql;
L2C_ConfigFile *m_cfg_hexid;
L2C_ConfigFile *m_cfg_network;
L2C_ConfigFile *m_cfg_dev;
L2C_ConfigFile *m_cfg_rates;
protected:
bool load_mysql();
bool load_hexid();
bool load_network();
bool load_developer();
bool load_rates();
};

BIN
L2C_Server/L2C.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

213
L2C_Server/L2Serverd.cpp Normal file
View File

@@ -0,0 +1,213 @@
#include "pch.h"
#include "Resource.h"
#include "Log.h"
#include "GS_Cfg.h"
#include "LS_Connection.h"
#include "GS.h"
#include "utils/Debugging.h"
#include "utils/Exception.h"
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HINSTANCE g_hInst = NULL;
void Main_StartServer()
{
GameServer *gs = GameServer::getInstance();
if( !gs->start() ) LogError( L"GS Start failed!" );
}
void Main_StopServer()
{
GameServer *gs = GameServer::getInstance();
if( !gs->isRunning() ) return;
if( !gs->stop() ) LogError( L"GS Stop failed!" );
else Log( L"GS Stopped." );
}
void Main_OnTestExceptions()
{
LogFull( true, true, RGB(255,128,128), RGB(255,255,255), L"===== Throwing an exception... =====" );
try
{
throw Exception( "test exception: max players: %d", GameServer::getInstance()->getMaxPlayers() );
}
catch( Exception& e )
{
LogError( L"Got exception: %S", e.what() );
e.logStackTrace();
}
LogFull( true, true, RGB(255,128,128), RGB(255,255,255), L"===== Exceptions test complete =====" );
}
void Main_OnInitDialog( HWND hDlg )
{
#ifdef _DEBUG
SetWindowText( hDlg, TEXT("L2 Game Server (DEBUG)" ) );
#endif
// rect
RECT rc;
GetClientRect( hDlg, &rc );
HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE(IDI_MAIN) );
SendMessage( hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon );
SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon );
// create richedit
g_hWndLog = LogWnd_Create( hDlg );
// start server
Main_StartServer();
}
void Main_OnClose( HWND hDlg )
{
EndDialog( hDlg, IDOK );
}
void Main_OnButtonServer( HWND hDlg )
{
POINT pt;
GetCursorPos( &pt );
HMENU hMenu = CreatePopupMenu();
AppendMenu( hMenu, MF_STRING, 1, TEXT("Start") );
AppendMenu( hMenu, MF_STRING, 2, TEXT("Stop") );
int cmd = TrackPopupMenu( hMenu, TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, 0, hDlg, NULL );
DestroyMenu( hMenu );
switch( cmd )
{
case 1: Main_StartServer(); break;
case 2: Main_StopServer(); break;
}
}
void Main_OnButtonDebug( HWND hDlg )
{
POINT pt;
GetCursorPos( &pt );
HMENU hMenu = CreatePopupMenu();
AppendMenu( hMenu, MF_STRING, 1, TEXT("Dump ClientPool") );
AppendMenu( hMenu, MF_STRING, 2, TEXT("Test exceptions and stack trace") );
int cmd = TrackPopupMenu( hMenu, TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, 0, hDlg, NULL );
DestroyMenu( hMenu );
switch( cmd )
{
case 1:
{
if( GameServer::getInstance()->getClientPool() )
GameServer::getInstance()->getClientPool()->dumpClientsToLog();
} break;
case 2:
{
Main_OnTestExceptions();
} break;
}
}
void Main_OnSize( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
UNREFERENCED_PARAMETER(wParam);
int w = LOWORD(lParam);
int h = HIWORD(lParam);
const int controlswnd_h = 80;
HWND hWnd = GetDlgItem( hDlg, IDC_STATIC_CONTROLS );
MoveWindow( hWnd, 10, h-controlswnd_h-10, w-20, controlswnd_h, TRUE );
hWnd = GetDlgItem( hDlg, IDC_B_SERVER );
MoveWindow( hWnd, 20, h-controlswnd_h-10+20, 50, 25, TRUE );
hWnd = GetDlgItem( hDlg, IDC_B_DEBUG );
MoveWindow( hWnd, 80, h-controlswnd_h-10+20, 50, 25, TRUE );
// log wnd
MoveWindow( g_hWndLog, 10, 10, w-20, h-controlswnd_h-10-10-10, TRUE );
}
void Main_OnCommand( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
UNREFERENCED_PARAMETER(lParam);
switch( LOWORD(wParam) )
{
case IDC_B_SERVER: Main_OnButtonServer( hDlg ); break;
case IDC_B_DEBUG: Main_OnButtonDebug( hDlg ); break;
}
}
INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_INITDIALOG: Main_OnInitDialog( hDlg ); break;
case WM_CLOSE: Main_OnClose( hDlg ); break;
case WM_SIZE: Main_OnSize( hDlg, wParam, lParam ); break;
case WM_COMMAND: Main_OnCommand( hDlg, wParam, lParam ); break;
default: return FALSE; break;
}
return TRUE;
}
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpszCmdLine, int nShowCmd )
{
g_hInst = hInstance;
hPrevInstance = NULL;
lpszCmdLine = NULL; // cmdline ignored...
nShowCmd = 0; // ignored
// WinXP visual styles / common controls
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_TAB_CLASSES | ICC_LISTVIEW_CLASSES;
InitCommonControlsEx( &iccex );
// winsock
L2PNet_InitDefault();
// richedit
HINSTANCE hInstRiched = LoadLibrary( TEXT("Riched20") );
if( !hInstRiched )
{
MessageBox( NULL, TEXT("Cannot load Riched20.dll"), TEXT("Load error!"), MB_ICONSTOP );
return 1;
}
// setup logging
CreateDirectoryW( L"./log", NULL );
FILE *flog = _wfopen( L"log/gameserver.log", L"wt" );
Log_EnableLogToFile( true, flog );
// init OpenSSL
SSL_library_init();
SSL_load_error_strings();
void *rand_buf = malloc( 1024 );
if( rand_buf )
{
RAND_seed( rand_buf, 1024 );
free( rand_buf );
rand_buf = NULL;
}
srand( (unsigned)time( NULL ) );
// initialize debugging
Debug::getInstance();
// run dialog!
try
{
DialogBoxParam( hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc, 0 );
}
catch( std::exception& stde ) {
MessageBoxA( NULL, stde.what(), "WinMain: std::exception:", MB_ICONSTOP ); // warning C4702: unreachable code? WTF???
} catch( Exception& e ) {
MessageBoxA( NULL, e.what(), "WinMain: Exception:", MB_ICONSTOP );
} catch( ... ) {
MessageBox( NULL, L"catch( ... )", L"WinMain: unknown exception!", MB_ICONSTOP );
}
// close server
Main_StopServer();
GameServer::freeInstance();
// free debugging
Debug::freeInstance();
// stop logging to file (close file)
Log_EnableLogToFile( false, NULL );
// cleanup winsock
L2PNet_Cleanup();
return 0;
}

44
L2C_Server/L2Serverd.rc Normal file
View File

@@ -0,0 +1,44 @@
#include <windows.h>
#include "Resource.h"
#define IDC_STATIC -1
// icon
IDI_MAIN ICON "L2C.ico"
// version
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,0,1
PRODUCTVERSION 0,0,0,1
FILEOS 0x00000004
FILETYPE 0x00000001
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "041904B0"
BEGIN
VALUE "FileDescription", "L2Serverd\0"
VALUE "FileVersion", "0, 0, 0, 9\0"
VALUE "InternalName", "L2Serverd\0"
VALUE "LegalCopyright", "Public Domain (FreeForAll)\0"
VALUE "OriginalFilename", "L2Serverd.exe\0"
VALUE "ProductName", "L2Serverd\0"
VALUE "ProductVersion", "0, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0419, 0x04B0
END
END
// dialog
IDD_MAIN DIALOGEX 26,16,365,200
CAPTION "L2 Game Server"
FONT 8,"MS Shell Dlg",400,0,0
STYLE WS_VISIBLE|WS_OVERLAPPEDWINDOW
BEGIN
CONTROL "Controls",IDC_STATIC_CONTROLS,"Button",WS_CHILD|WS_VISIBLE|BS_GROUPBOX,4,151,354,43
CONTROL "Server",IDC_B_SERVER,"Button",WS_CHILD|WS_VISIBLE|WS_TABSTOP,14,166,60,19
CONTROL "Debug",IDC_B_DEBUG,"Button",WS_CHILD|WS_VISIBLE|WS_TABSTOP,80,166,60,19
END

View File

@@ -0,0 +1,494 @@
#include "pch.h"
#include "Log.h"
#include "l2c_utils.h"
#include "LS_Connection.h"
#include "GS.h"
#include "utils/Exception.h"
LoginConnection::LoginConnection()
{
m_ls_addr[0] = 0;
m_ls_port = m_ls_protover = 0;
m_isRunning = m_flagStop = m_isConnected = false;
m_sock = INVALID_SOCKET;
m_bfKey[0] = 0;
m_bfKeyLen = 0;
m_rsaKey = NULL;
m_registeredServerName[0] = 0;
m_registeredServerId = 0;
}
LoginConnection::~LoginConnection()
{
stop();
m_ls_addr[0] = 0;
m_ls_port = m_ls_protover = 0;
m_isRunning = m_flagStop = m_isConnected = false;
if( m_sock != INVALID_SOCKET )
{
L2PNet_shutdown( m_sock );
L2PNet_closesocket( m_sock );
}
m_sock = INVALID_SOCKET;
m_bfKey[0] = 0;
m_bfKeyLen = 0;
if( m_rsaKey )
{
RSA_free( m_rsaKey );
m_rsaKey = NULL;
}
m_registeredServerName[0] = 0;
m_registeredServerId = 0;
}
int LoginConnection::getRegisteredServerId() const
{
return m_registeredServerId;
}
const wchar_t *LoginConnection::getRegisteredServerName() const
{
return (const wchar_t *)m_registeredServerName;
}
void LoginConnection::start( const wchar_t *ls_addr, int ls_port, int ls_protover )
{
if( m_isRunning ) return;
HANDLE hThread = NULL;
unsigned int tid = 0;
m_flagStop = false;
m_lock.Lock();
{
hThread = (HANDLE)_beginthreadex( NULL, 0,
(unsigned int (__stdcall *)(void *))LoginConnection::LS_ConnThread, (void *)this,
0, &tid );
if( hThread )
{
wcsncpy( m_ls_addr, ls_addr, 127 ); m_ls_addr[127] = 0;
m_ls_port = ls_port;
m_ls_protover = ls_protover;
m_isRunning = true;
CloseHandle( hThread );
}
}
m_lock.Unlock();
}
void LoginConnection::stop()
{
if( !m_isRunning ) return;
m_lock.Lock();
m_flagStop = true;
while( m_isRunning ) Sleep(50);
m_flagStop = false;
m_registeredServerName[0] = 0;
m_registeredServerId = 0;
m_lock.Unlock();
//LogDebug( L"LS Connection stopped." ); // deadlock in SendMessage() :(
}
DWORD WINAPI LoginConnection::LS_ConnThread( LPVOID lpvParam )
{
LoginConnection *lc = (LoginConnection *)lpvParam;
//lc->m_isRunning = true; // not here, see line 107
unsigned char *packbuffer = NULL;
bool should_retry_connect = true;
int connect_tries = 0;
char a_ls_addr[128] = {0};
l2c_unicode_to_ansi( lc->m_ls_addr, a_ls_addr, 128 );
int bReadyRead = 0, bReadyWrite = 0, r = 0;
unsigned int rcvdLen = 0;
// initialize initial BF key
char initial_bfKey[] = "_;v.]05-31!|+-%xT!^[$\00";
unsigned int opcode = 0;
L2LoginPacket *pack = NULL;
retry_connect:
lc->m_isRunning = true; // only now we started work!
lc->m_isConnected = false; // not connected
connect_tries++;
if( connect_tries == 1 )
Log( L"Connecting to login at %s:%d...", lc->m_ls_addr, lc->m_ls_port );
else
Log( L"Connecting to login at %s:%d... (try #%d)", lc->m_ls_addr, lc->m_ls_port, connect_tries );
try
{
packbuffer = (unsigned char *)malloc( 64*1024 ); // 64 Kb enough
if( !packbuffer ) throw std::bad_alloc( "packbuffer (64 Kb)" );
lc->m_sock = L2PNet_TCPsocket_create( true );
L2PNet_connect( lc->m_sock, a_ls_addr, (unsigned short)lc->m_ls_port );
bReadyRead = 0, bReadyWrite = 0, r = 0;
r = L2PNet_select( lc->m_sock, L2PNET_SELECT_WRITE, 5000, &bReadyRead, &bReadyWrite );
if( !bReadyWrite ) throw Exception( "Connect to LS failed!" );
if( GameServer::getInstance()->getConfig()->Debug ) LogDebug( L"Connected to login." );
lc->m_isConnected = true; // we're connected!
// suddenly we're signaled to stop?
if( lc->m_flagStop ) goto lsconn_cleanup;
rcvdLen = 0;
memcpy( lc->m_bfKey, initial_bfKey, 23 );
lc->m_bfKey[22] = '0';
lc->m_bfKeyLen = 22;
while( !lc->m_flagStop )
{
r = L2PNet_select( lc->m_sock, L2PNET_SELECT_READ, 200, &bReadyRead, &bReadyWrite );
if( r == 0 ) continue; // timeout
if( r == -1 ) throw Exception( "Network select() error" );
r = L2PacketReceive_buffer( lc->m_sock, 2000, &rcvdLen, packbuffer );
if( r <= 0 ) throw Exception( "recv packet failed" );
// packet received OK
// decode blowfish
pack = new L2LoginPacket( packbuffer, rcvdLen );
pack->setDynamicBFKey( lc->m_bfKey, lc->m_bfKeyLen );
pack->decodeBlowfish( false );
// validate checksum
if( !pack->verifyChecksum() ) throw Exception( "Invalid packet checksum!" );
#ifdef _DEBUG
LogDebug( L"DBG: LoginConnection rcv: %u bytes (opcode %02X)", rcvdLen, (unsigned int)pack->getByteAt(2) );
#endif
// handle packet
opcode = (unsigned int)pack->getByteAt(2);
switch( opcode )
{
case 0x00: lc->ph_InitLS( pack ); break;
case 0x01: lc->ph_LoginServerFail( pack ); break;
case 0x02: lc->ph_LoginServerOK( pack ); break;
case 0x03: lc->ph_PlayerAuthResponse( pack ); break;
case 0x04: lc->ph_KickPlayer( pack ); break;
}
// free pack mem
if( pack ) delete pack;
pack = NULL;
}
}
catch( Exception& e )
{
if( !lc->m_flagStop ) // do not write log if signaled to stop (deadlock in SendMessage)
{
LogError( L"LoginConnection thread: Exception: %S", e.what() );
e.logStackTrace();
}
}
catch( std::bad_alloc& ba )
{
LogError( L"LoginConnection thread: bad memory allocation: %S", ba.what() );
}
catch( std::exception& e )
{
LogError( L"LoginConnection thread: error: %S", e.what() );
}
lsconn_cleanup:
// cleanup
if( packbuffer ) free( packbuffer );
packbuffer = NULL;
if( pack ) delete pack;
pack = NULL;
L2PNet_shutdown( lc->m_sock );
L2PNet_closesocket( lc->m_sock );
lc->m_sock = INVALID_SOCKET;
lc->m_bfKey[0] = 0;
lc->m_bfKeyLen = 0;
if( lc->m_rsaKey )
{
RSA_free( lc->m_rsaKey );
lc->m_rsaKey = NULL;
}
lc->m_registeredServerName[0] = 0;
lc->m_registeredServerId = 0;
lc->m_isRunning = false; // mark as stopped
lc->m_isConnected = false; // mark as not connected
// should we retry and connect again?
if( lc->m_flagStop ) should_retry_connect = false;
if( should_retry_connect )
{
if( GameServer::getInstance()->isRunning() )
should_retry_connect = true;
else
should_retry_connect = false;
}
if( should_retry_connect ) goto retry_connect;
return 0;
}
void LoginConnection::ph_InitLS( L2LoginPacket *pack )
{
// ddc[b64]
unsigned char rsa_modulus[128];
memset( rsa_modulus, 0, sizeof(rsa_modulus) );
//
pack->getPacketType(); // 0x00 - InitLS
int login_proto_rev = pack->readD(); // login protocol rev
unsigned int rsa_keylen = pack->readD() - 1; //
pack->readC(); // ?? wtf
pack->readBytes( rsa_modulus, rsa_keylen );
//
GameServer *gs = GameServer::getInstance();
if( login_proto_rev != gs->getConfig()->login_protocol_version )
LogWarning( L"Warning! login server sent protocol version %d (must be %d)",
login_proto_rev, gs->getConfig()->login_protocol_version );
if( gs->getConfig()->Debug )
LogDebug( L"LoginConnection: got InitLS, login protover = %u", login_proto_rev );
//
RSA *rsaKey = RSA_new();
BN_dec2bn( &(rsaKey->e), "65537" );
rsaKey->n = BN_bin2bn( rsa_modulus, rsa_keylen, NULL );
m_rsaKey = rsaKey;
send_BlowfishKey();
send_GameServerAuthRequest();
}
void LoginConnection::ph_LoginServerFail( L2LoginPacket *pack )
{
// c
pack->getPacketType();
unsigned int reason = (unsigned int)pack->readC();
bool mustThrow = true;
wchar_t strReason[32] = {0};
wcscpy( strReason, L"<unknown>" );
switch( reason )
{
case NOT_AUTHED: wcscpy( strReason, L"NOT_AUTHED" ); break;
case REASON_ALREADY_LOGGED_IN: wcscpy( strReason, L"REASON_ALREADY_LOGGED_IN" ); mustThrow = false; break;
case REASON_ID_RESERVED: wcscpy( strReason, L"REASON_ID_RESERVED" ); break;
case REASON_IP_BANNED: wcscpy( strReason, L"REASON_IP_BANNED" ); break;
case REASON_NO_FREE_ID: wcscpy( strReason, L"REASON_NO_FREE_ID" ); break;
case REASON_WRONG_HEXID: wcscpy( strReason, L"REASON_WRONG_HEXID" ); break;
}
if( mustThrow )
{
LogError( L"Failed to auth on login server! Error code %u (%s)", reason, strReason );
throw Exception( "LS auth failed!" );
}
LogWarning( L"Failed to auth on login server! Error code %u (%s)", reason, strReason );
}
void LoginConnection::ph_LoginServerOK( L2LoginPacket *pack )
{
// cS
pack->getPacketType();
m_registeredServerId = (int)pack->readUChar();
wcsncpy( m_registeredServerName, pack->readUnicodeStringPtr(), 255 );
m_registeredServerName[255] = 0;
Log( L"Registered on login as server #%d (%s)", m_registeredServerId, m_registeredServerName );
//
send_ServerStatus();
}
void LoginConnection::ph_PlayerAuthResponse( L2LoginPacket *pack )
{
// Sc
GameServer *gs = GameServer::getInstance();
pack->getPacketType();
wchar_t account[256] = {0};
wcsncpy( account, pack->readUnicodeStringPtr(), 255 ); account[255] = 0;
int ok = (int)pack->readUChar();
//
if( GameServer::getInstance()->getConfig()->Debug )
LogDebug( L"LS PlayerAuthResponse: account [%s] response: %d", account, ok );
//
gs->notifyLoginPlayerAuth( account, ok );
}
void LoginConnection::ph_KickPlayer( L2LoginPacket *pack )
{
// S
pack->getPacketType();
wchar_t account[256] = {0};
wcsncpy( account, pack->readUnicodeStringPtr(), 255 ); account[255] = 0;
//
GameServer *gs = GameServer::getInstance();
if( gs->getConfig()->Debug ) Log( L"LS KickPlayer acc [%s]", account );
gs->kickPlayerByAccount( account, true );
}
void LoginConnection::send_BlowfishKey()
{
// now reply with BlowfishKey
// construct array of 64 bytes, first 24 of them are NULLs, next 40 are new BF key.
unsigned char block[64];
unsigned char block_e[64];
memset( block, 0, sizeof(block) );
memset( block_e, 0, sizeof(block_e) );
srand( (unsigned int)time(NULL) );
int i;
for( i=0; i<40; i++ ) block[24+i] = (unsigned char)( (rand() % 255) & 0xFF );
// encode block with RSA key
int r = RSA_public_encrypt( 64, block, block_e, m_rsaKey, RSA_NO_PADDING );
if( r == -1 )
{
LogError( L"send BlowfishKey: RSA_public_encrypt() error!" );
m_flagStop = true;
return;
}
// construct packet BlowfishKey
L2LoginPacket p_bf;
p_bf.setPacketType( 0x00 );
p_bf.writeUInt( 64 ); // length of block
p_bf.writeBytes( block_e, 64 ); // rsa encrypted block with new BF key
// send it!
sendLSPacket( &p_bf );
// after send! save generated bf key as our new key
memcpy( m_bfKey, block+24, 40 );
m_bfKeyLen = 40;
// now we can free RSA key - we dont need it any more?
RSA_free( m_rsaKey );
m_rsaKey = NULL;
//
if( GameServer::getInstance()->getConfig()->Debug ) LogDebug( L"LoginConnection: sent BlowfishKey" );
}
void LoginConnection::send_GameServerAuthRequest()
{
GameServer *gs = GameServer::getInstance();
L2LoginPacket p;
p.setPacketType( 0x01 );
p.writeUChar( (unsigned char)(gs->getConfig()->server_id & 0xFF) );
p.writeUChar( gs->getConfig()->accept_alternate_server_id ? 0x01 : 0x00 );
p.writeUChar( 0x00 ); // host reserved = false
p.writeS( gs->getConfig()->report_external_hostname );
p.writeS( gs->getConfig()->report_internal_hostname );
p.writeH( (short)gs->getConfig()->game_server_bind_port );
p.writeD( gs->getConfig()->max_players );
// write hexid as byte array
wchar_t *server_hexid = gs->getConfig()->server_hexid;
int hexid_len = wcslen( server_hexid );
int i;
p.writeD( hexid_len/2 );
unsigned int val = 0;
wchar_t temp_ws[3] = {0,0,0};
for( i=0; i<hexid_len; i+=2 )
{
temp_ws[0] = server_hexid[i];
temp_ws[1] = server_hexid[i+1];
swscanf( temp_ws, L"%x", &val );
p.writeUChar( (unsigned char)(val & 0xFF) );
}
// send!
sendLSPacket( &p );
//
if( gs->getConfig()->Debug ) LogDebug( L"LoginConnection: sent GameServerAuthRequest" );
}
void LoginConnection::send_PlayerInGame( const wchar_t *accountName )
{
L2LoginPacket pack;
pack.setPacketType( 0x02 );
pack.writeH( 1 ); // 1 account name
pack.writeS( accountName );
sendLSPacket( &pack );
}
void LoginConnection::send_PlayerLogOut( const wchar_t *accountName )
{
L2LoginPacket pack;
pack.setPacketType( 0x03 );
pack.writeS( accountName );
sendLSPacket( &pack );
}
void LoginConnection::send_ChangeAccessLevel( const wchar_t *accountName, int new_access_level )
{
L2LoginPacket pack;
pack.setPacketType( 0x04 );
pack.writeD( new_access_level );
pack.writeS( accountName );
sendLSPacket( &pack );
}
void LoginConnection::send_PlayerAuthRequest( const wchar_t *accountName, unsigned char *loginKey, unsigned char *playKey )
{
L2LoginPacket pack;
pack.setPacketType( 0x05 );
pack.writeS( accountName );
pack.writeBytes( playKey, 8 );
pack.writeBytes( loginKey, 8 );
sendLSPacket( &pack );
}
void LoginConnection::send_ServerStatus()
{
GameServer *gs = GameServer::getInstance();
L2LoginPacket p;
p.setPacketType( 0x06 ); // opcode
//
p.writeD( 5 ); // parameters count
// first - server status
p.writeD( SERVER_LIST_STATUS );
p.writeD( gs->getServerStatus() );
// 2 - SERVER_LIST_CLOCK
p.writeD( SERVER_LIST_CLOCK );
p.writeD( gs->getConfig()->server_list_clock ? 0x01 : 0x00 );
// 3 - SERVER_LIST_SQUARE_BRACKET
p.writeD( SERVER_LIST_SQUARE_BRACKET );
p.writeD( gs->getConfig()->server_list_brackets ? 0x01 : 0x00 );
// 4 - max players
p.writeD( SERVER_LIST_MAX_PLAYERS );
p.writeD( gs->getMaxPlayers() );
// 5 - list as test server
p.writeD( SERVER_LIST_TEST_SERVER );
p.writeD( gs->getConfig()->list_as_test_server ? 0x01 : 0x00 );
//
sendLSPacket( &p );
}
void LoginConnection::sendLSPacket( L2LoginPacket *pack )
{
// asserts
if( !pack ) return;
if( m_sock == INVALID_SOCKET ) return; // socket down
if( !m_isRunning ) return; // login connection is stopped
if( !m_isConnected ) return; // maybe login connection thread is running, trying to connect, but not connected
//
unsigned int sentLen = 0;
L2LoginPacket *packet_copy = new L2LoginPacket( pack->getBytesPtr(), pack->getPacketSize() );
// pad packet to pad_step-byte border
const int pad_step = 8;
int dsize = packet_copy->getDataSize();
int rest = pad_step - (dsize % pad_step);
if( (rest > 0) && (rest < pad_step) ) // need alignment?
{
//LogDebug( L"Need padding %d bytes to %d byte-border", rest, pad_step );
int i;
for( i=0; i<rest; i++ ) packet_copy->writeC( 0x00 );
}
// now padded to 8-byte border; since we don't need to append more
// 4 bytes after checksum (appendChecksum( false )), write 4 bytes before chksum
// to keep padding to 8-byte border after checksum is appended
packet_copy->writeD( 0x00000000 );
// finally, checksum (4 bytes)
packet_copy->appendChecksum( false );
// now we can encode blowfish
packet_copy->setDynamicBFKey( m_bfKey, m_bfKeyLen );
packet_copy->encodeBlowfish( false );
//
m_lock.Lock();
int r = L2PacketSend( m_sock, packet_copy, 2000, &sentLen );
m_lock.Unlock();
if( r < 0 || sentLen != packet_copy->getPacketSize() )
{
//LogError( L"LoginConnection::sendLSPacket(): sent %u instead of %u bytes! (retval %d)",
// sentLen, packet_copy->getPacketSize(), r );
delete packet_copy;
throw Exception( "LoginConnection::sendLSPacket(): sent %u instead of %u bytes! (retval %d)",
sentLen, packet_copy->getPacketSize(), r );
}
delete packet_copy;
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include "enums.h"
class LoginConnection
{
public:
LoginConnection();
~LoginConnection();
public:
void start( const wchar_t *ls_addr, int ls_port, int ls_protover );
void stop();
public:
int getRegisteredServerId() const;
const wchar_t *getRegisteredServerName() const;
protected:
CriticalSection m_lock;
wchar_t m_ls_addr[128];
int m_ls_port;
int m_ls_protover;
int m_registeredServerId;
wchar_t m_registeredServerName[256];
protected:
static DWORD WINAPI LS_ConnThread( LPVOID lpvParam );
bool m_isRunning;
bool m_isConnected;
bool m_flagStop;
SOCKET m_sock;
unsigned char m_bfKey[64];
unsigned int m_bfKeyLen;
RSA *m_rsaKey;
protected:
// senders
void send_BlowfishKey(); // 0x00
void send_GameServerAuthRequest(); // 0x01
public: // these are public senders
void send_PlayerInGame( const wchar_t *accountName ); // 0x02
void send_PlayerLogOut( const wchar_t *accountName ); // 0x03
void send_ChangeAccessLevel( const wchar_t *accountName, int new_access_level ); // 0x04
void send_PlayerAuthRequest( const wchar_t *accountName, unsigned char *loginKey, unsigned char *playKey ); // 0x05
void send_ServerStatus(); // 0x06
protected:
// handlers
void ph_InitLS( L2LoginPacket *pack ); // 0x00
void ph_LoginServerFail( L2LoginPacket *pack ); // 0x01
void ph_LoginServerOK( L2LoginPacket *pack ); // 0x02
void ph_PlayerAuthResponse( L2LoginPacket *pack ); // 0x03
void ph_KickPlayer( L2LoginPacket *pack ); // 0x04
// internal
void sendLSPacket( L2LoginPacket *pack );
};

244
L2C_Server/Log.cpp Normal file
View File

@@ -0,0 +1,244 @@
#include "pch.h"
#include "Log.h"
HWND g_hWndLog = NULL;
extern HINSTANCE g_hInst;
CRITICAL_SECTION g_cs_log;
FILE *g_log_file = NULL;
bool g_log_is_sending = false;
HWND LogWnd_Create( HWND hWndParent )
{
g_log_is_sending = false;
InitializeCriticalSectionAndSpinCount( &g_cs_log, 10 );
RECT rc;
GetClientRect( hWndParent, &rc );
HWND hWndLog = CreateWindowEx( WS_EX_CLIENTEDGE, RICHEDIT_CLASS, TEXT(""),
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL |
// richedit styles
ES_LEFT | ES_NOHIDESEL | ES_READONLY | ES_MULTILINE,
10, 10, rc.right-rc.left-20, 230, hWndParent, NULL, g_hInst, NULL );
SendMessage( hWndLog, WM_SETTEXT, 0, (LPARAM)TEXT("") );
SendMessage( hWndLog, EM_SETTEXTMODE, TM_RICHTEXT, 0 );
SendMessage( hWndLog, EM_SETBKGNDCOLOR, FALSE, RGB(255,255,255) );
SendMessage( hWndLog, EM_AUTOURLDETECT, TRUE, 0 );
// setup default char format for control
CHARFORMAT2 cf2;
memset( &cf2, 0, sizeof(cf2) );
cf2.cbSize = sizeof(cf2);
cf2.dwMask = CFM_BACKCOLOR | CFM_COLOR | CFM_LINK | CFM_FACE | CFM_SIZE | CFM_WEIGHT;
cf2.crBackColor = RGB(255,255,255);
cf2.crTextColor = RGB(0,0,0);
cf2.dwEffects = 0; //CFE_LINK | CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
_tcscpy( cf2.szFaceName, _T("Tahoma") );
cf2.yHeight = 170;
cf2.wWeight = FW_NORMAL;
SendMessage( hWndLog, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2 );
SendMessage( hWndLog, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2 );
g_hWndLog = hWndLog;
return hWndLog;
}
void Log( const wchar_t *_Format, ... )
{
va_list _ArgList;
va_start( _ArgList, _Format );
// calc string len
int llen = _vscwprintf( _Format, _ArgList );
if( llen < 1 ) return;
// alloc string
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
if( !message ) return;
// sprintf
va_start( _ArgList, _Format );
_vsnwprintf( message, llen, _Format, _ArgList );
message[llen] = 0;
//
LogFull( true, true, 0, RGB(255,255,255), message );
// free string
free( message );
message = NULL;
}
void Log_np( const wchar_t *_Format, ... )
{
va_list _ArgList;
va_start( _ArgList, _Format );
// calc string len
int llen = _vscwprintf( _Format, _ArgList );
if( llen < 1 ) return;
// alloc string
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
if( !message ) return;
// sprintf
va_start( _ArgList, _Format );
_vsnwprintf( message, llen, _Format, _ArgList );
message[llen] = 0;
//
LogFull( false, false, 0, RGB(255,255,255), message );
// free string
free( message );
message = NULL;
}
void LogError( const wchar_t *_Format, ... )
{
va_list _ArgList;
va_start( _ArgList, _Format );
// calc string len
int llen = _vscwprintf( _Format, _ArgList );
if( llen < 1 ) return;
// alloc string
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
if( !message ) return;
// sprintf
va_start( _ArgList, _Format );
_vsnwprintf( message, llen, _Format, _ArgList );
message[llen] = 0;
//
LogFull( true, true, RGB(255,0,0), RGB(255,255,255), message );
// free string
free( message );
message = NULL;
}
void LogWarning( const wchar_t *_Format, ... )
{
va_list _ArgList;
va_start( _ArgList, _Format );
// calc string len
int llen = _vscwprintf( _Format, _ArgList );
if( llen < 1 ) return;
// alloc string
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
if( !message ) return;
// sprintf
va_start( _ArgList, _Format );
_vsnwprintf( message, llen, _Format, _ArgList );
message[llen] = 0;
//
LogFull( true, true, RGB(196,64,0), RGB(255,255,255), message );
// free string
free( message );
message = NULL;
}
void LogDebug( const wchar_t *_Format, ... )
{
//#ifdef _DEBUG
va_list _ArgList;
va_start( _ArgList, _Format );
// calc string len
int llen = _vscwprintf( _Format, _ArgList );
if( llen < 1 ) return;
// alloc string
wchar_t *message = (wchar_t *)malloc( llen*2 + 2 );
if( !message ) return;
// sprintf
va_start( _ArgList, _Format );
_vsnwprintf( message, llen, _Format, _ArgList );
message[llen] = 0;
//
LogFull( true, true, RGB(128,128,128), RGB(255,255,255), message );
// free string
free( message );
message = NULL;
//#else
// _Format = NULL; // unreferenced parameter
//#endif
}
void LogFull( bool prependTime, bool appendNL, COLORREF crTextColor, COLORREF crBackColor, const wchar_t *message )
{
//EnterCriticalSection( &g_cs_log ); // TODO: LogFull EnterCriticalSection
if( g_log_is_sending ) return; // already running here in other thread; cancel SendMessage
g_log_is_sending = true;
//
static wchar_t nl[2] = { L'\r', L'\0' }; // newline line-break
// move selection after last char
CHARRANGE cr;
cr.cpMin = -1;
cr.cpMax = -1;
SendMessage( g_hWndLog, EM_EXSETSEL, 0, (LPARAM)&cr );
// prepend time?
if( prependTime )
{
wchar_t tstr[128];
SYSTEMTIME st;
GetLocalTime( &st );
_snwprintf( tstr, 127, L"%02d:%02d:%02d.%03d ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds );
// log to file?
if( g_log_file )
fwprintf( g_log_file, L"%02d:%02d:%02d.%03d: ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds );
// out to window
CHARFORMAT2 tcf2;
memset( &tcf2, 0, sizeof(tcf2) );
tcf2.cbSize = sizeof(tcf2);
tcf2.dwMask = CFM_BACKCOLOR | CFM_COLOR | CFM_ITALIC | CFM_BOLD;
tcf2.crBackColor = RGB(255,255,255);
tcf2.crTextColor = RGB(100,100,100);
tcf2.dwEffects = 0;
SendMessage( g_hWndLog, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&tcf2 );
SendMessage( g_hWndLog, EM_REPLACESEL, FALSE, (LPARAM)tstr );
}
// log to file?
if( g_log_file ) fwprintf( g_log_file, L"%s", message );
// out to window
// set color
CHARFORMAT2 cf2;
memset( &cf2, 0, sizeof(cf2) );
cf2.cbSize = sizeof(cf2);
cf2.dwMask = CFM_BACKCOLOR | CFM_COLOR | CFM_ITALIC | CFM_BOLD;
cf2.crBackColor = crBackColor;
cf2.crTextColor = crTextColor;
cf2.dwEffects = CFE_BOLD;
SendMessage( g_hWndLog, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2 );
// add string
SendMessage( g_hWndLog, EM_REPLACESEL, FALSE, (LPARAM)message );
// add line-break
if( appendNL )
{
SendMessage( g_hWndLog, EM_REPLACESEL, FALSE, (LPARAM)nl );
if( g_log_file ) fwprintf( g_log_file, L"\n" );
}
//
//LeaveCriticalSection( &g_cs_log );
g_log_is_sending = false; // mark as not sending messages
}
void Log_EnableLogToFile( bool bEnable, FILE *f /*= NULL*/ )
{
if( bEnable )
{
if( g_log_file ) fclose( g_log_file );
g_log_file = f;
}
else
{
if( g_log_file ) fclose( g_log_file );
g_log_file = NULL;
}
}
void Log_FlushFile()
{
if( g_log_file ) fflush( g_log_file );
}
void Log_Win32Error( const wchar_t *comment, DWORD error_code )
{
wchar_t wbuffer[512] = {0};
FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error_code, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), wbuffer, 511, NULL );
wbuffer[511] = 0;
int ll = (int)wcslen( wbuffer );
if( ll >= 2 )
{
if( wbuffer[ll-1] == L'\r' || wbuffer[ll-1] == L'\n' ) wbuffer[ll-1] = 0;
if( wbuffer[ll-2] == L'\r' || wbuffer[ll-2] == L'\n' ) wbuffer[ll-2] = 0;
}
LogError( L"Win32 error: %s", wbuffer );
if( comment ) LogError( L"Error comment: %s", comment );
}

19
L2C_Server/Log.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
extern HWND g_hWndLog;
HWND LogWnd_Create( HWND hWndParent );
void Log( const wchar_t *_Format, ... );
void Log_np( const wchar_t *_Format, ... );
void LogError( const wchar_t *_Format, ... );
void LogWarning( const wchar_t *_Format, ... );
void LogDebug( const wchar_t *_Format, ... );
void LogFull( bool prependTime, bool appendNL, COLORREF crTextColor, COLORREF crBackColor, const wchar_t *message );
void Log_EnableLogToFile( bool bEnable, FILE *f = NULL );
void Log_FlushFile();
void Log_Win32Error( const wchar_t *comment, DWORD error_code );

View File

@@ -0,0 +1,23 @@
rem %1 = L2Serverd_d
rem %2 = output exe dir: m:\dev\l2\servers\l2c\_build\L2Serverd_Debug\
rem
rem ..\..\dist
rem echo 1 = %1
rem echo 2 = %2
set out=..\..\out\dist_l2c
set bin=..\..\bin
rem Copy EXE
copy /y %2\%1.exe %2\%out%
rem Copy PDB
copy /y %2\%1.pdb %2\%out%
rem Copy DLLs
copy /y %2\%bin%\dbghelp.dll %2\%out%
copy /y %2\%bin%\libeay32.dll %2\%out%
copy /y %2\%bin%\ssleay32.dll %2\%out%
copy /y %2\%bin%\libmysql.dll %2\%out%
rem copy /y %2\%bin%\*.dll %2\%out%

15
L2C_Server/Resource.h Normal file
View File

@@ -0,0 +1,15 @@
// icons
#define IDI_MAIN 100
// dialogs
#define IDD_MAIN 100
// buttons
#define IDC_B_SERVER 1000
#define IDC_B_DEBUG 10001
// static
#define IDC_STATIC_CONTROLS 2000

View File

@@ -0,0 +1,94 @@
#include "pch.h"
#include "CharNameTable.h"
#include "GS.h"
#include "Log.h"
CharNameTable *CharNameTable::s_instance = NULL;
int CharNameTable::s_refCount = 0;
CharNameTable *CharNameTable::getInstance()
{
if( !s_instance )
{
s_instance = new CharNameTable();
s_refCount++;
}
return s_instance;
}
void CharNameTable::freeInstance()
{
if( s_instance == NULL ) return;
s_refCount--;
if( s_refCount == 0 )
{
delete s_instance;
s_instance = NULL;
}
}
CharNameTable::CharNameTable()
{
//
}
CharNameTable::~CharNameTable()
{
//
}
void CharNameTable::LockDB()
{
m_lock.Lock();
}
void CharNameTable::UnlockDB()
{
m_lock.Unlock();
}
bool CharNameTable::doesCharNameExist( const wchar_t *charName )
{
bool ret = true;
m_lock.Lock();
{
GameServer *gs = GameServer::getInstance();
MysqlConnection *con = gs->getDBConnection();
MysqlQuery q;
q.create( L"SELECT account_name FROM characters WHERE char_name='%s'", charName );
if( con->executeQuery( q ) )
{
int num_chars = q.getNumRows();
assert( num_chars >= 0 );
if( num_chars == 0 ) ret = false;
}
else
LogError( L"CharNameTable::doesCharNameExist(): MySQL ERROR: %s\n", con->getErrorStr() );
gs->releaseDBConnection( con );
}
m_lock.Unlock();
return ret;
}
int CharNameTable::getAccountCharCount( const wchar_t *accountName )
{
if( !accountName ) return 0;
// SELECT COUNT(char_name) FROM characters WHERE account_name=?
int num_chars = 0;
m_lock.Lock();
{
GameServer *gs = GameServer::getInstance();
MysqlConnection *con = gs->getDBConnection();
MysqlQuery q;
q.create( L"SELECT COUNT(char_name) FROM characters WHERE account_name='%s'", accountName );
if( con->executeQuery( q ) )
{
if( q.getNextRow() ) num_chars = q.getFieldInt( 0 );
}
else
LogError( L"CharNameTable::getAccountCharCount(): MySQL ERROR: %s\n", con->getErrorStr() );
gs->releaseDBConnection( con );
}
m_lock.Unlock();
return num_chars;
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "l2c_utils.h"
class CharNameTable
{
protected:
CharNameTable();
~CharNameTable();
static CharNameTable *s_instance;
static int s_refCount;
public:
static CharNameTable *getInstance();
static void freeInstance();
public:
bool doesCharNameExist( const wchar_t *charName );
int getAccountCharCount( const wchar_t *accountName );
void LockDB();
void UnlockDB();
protected:
CriticalSection m_lock;
};

View File

@@ -0,0 +1,198 @@
#include "pch.h"
#include "Log.h"
#include "CharTemplateTable.h"
#include "world/model/base/ClassIdTree.h"
#include "ItemTable.h"
#include "GS.h"
CharTemplateTable *CharTemplateTable::s_instance = NULL;
int CharTemplateTable::s_refCount = 0;
CharTemplateTable *CharTemplateTable::getInstance()
{
if( s_instance == NULL )
{
s_instance = new CharTemplateTable();
s_refCount++;
}
return s_instance;
}
void CharTemplateTable::freeInstance()
{
if( s_instance )
{
s_refCount--;
if( s_refCount == 0 )
{
delete s_instance;
s_instance = NULL;
}
}
}
CharTemplateTable::CharTemplateTable()
{
m_templates.clear();
}
CharTemplateTable::~CharTemplateTable()
{
std::map<int, L2PlayerTemplate *>::const_iterator iter = m_templates.begin();
while( iter != m_templates.end() )
{
L2PlayerTemplate *tmpl = iter->second;
if( tmpl ) delete tmpl;
iter++;
}
m_templates.clear();
}
void CharTemplateTable::load()
{
GameServer *gs = GameServer::getInstance();
MysqlConnection *con = gs->getDBConnection();
//
MysqlQuery q;
q.create( L"SELECT * FROM class_list, char_templates, lvlupgain "
L"WHERE class_list.id = char_templates.classId AND class_list.id = lvlupgain.classId "
L"ORDER BY class_list.id" );
if( con->executeQuery( q ) )
{
while( q.getNextRow() )
{
StatsSet set;
int classId;
// l2 player template vars
classId = q.getFieldInt( "id" );
set.setInt( "classId", classId );
set.setInt( "raceId", q.getFieldInt( "raceId" ) );
set.setInt( "spawnX", q.getFieldInt( "x" ) );
set.setInt( "spawnY", q.getFieldInt( "y" ) );
set.setInt( "spawnZ", q.getFieldInt( "z" ) );
set.setInt( "classBaseLevel", q.getFieldInt( "class_lvl" ) );
set.setDouble( "lvlHpAdd", q.getFieldDouble( "defaultHpAdd" ) );
set.setDouble( "lvlHpMod", q.getFieldDouble( "defaultHpMod" ) );
set.setDouble( "lvlCpAdd", q.getFieldDouble( "defaultCpAdd" ) );
set.setDouble( "lvlCpMod", q.getFieldDouble( "defaultCpMod" ) );
set.setDouble( "lvlMpAdd", q.getFieldDouble( "defaultMpAdd" ) );
set.setDouble( "lvlMpMod", q.getFieldDouble( "defaultMpMod" ) );
set.setDouble( "baseHpMax", q.getFieldDouble( "defaultHpBase" ) );
set.setDouble( "baseMpMax", q.getFieldDouble( "defaultMpBase" ) );
set.setDouble( "baseCpMax", q.getFieldDouble( "defaultCpBase" ) );
set.setDouble( "baseHpReg", 1.5 ); // O_o constants...?
set.setDouble( "baseMpReg", 0.9 );
// l2 char template vars
set.setInt( "baseSTR", q.getFieldInt( "STR" ) );
set.setInt( "baseCON", q.getFieldInt( "CON" ) );
set.setInt( "baseDEX", q.getFieldInt( "DEX" ) );
set.setInt( "baseINT", q.getFieldInt( "_INT" ) );
set.setInt( "baseWIT", q.getFieldInt( "WIT" ) );
set.setInt( "baseMEN", q.getFieldInt( "MEN" ) );
//
set.setInt( "basePAtk", q.getFieldInt( "P_ATK" ) );
set.setInt( "basePDef", q.getFieldInt( "P_DEF" ) );
set.setInt( "baseMAtk", q.getFieldInt( "M_ATK" ) );
set.setInt( "baseMDef", q.getFieldInt( "M_DEF" ) );
set.setInt( "basePAtkSpd", q.getFieldInt( "P_SPD" ) );
set.setInt( "baseMAtkSpd", q.getFieldInt( "M_SPD" ) );
set.setInt( "baseCritRate", q.getFieldInt( "CRITICAL" ) );
set.setInt( "baseRunSpd", q.getFieldInt( "MOVE_SPD" ) );
set.setInt( "baseWalkSpd", 0 );
set.setInt( "baseShldDef", 0 );
set.setInt( "baseShldRate", 0 );
set.setInt( "baseAtkRange", 40 );
set.setInt( "collision_radius", (int)q.getFieldDouble( "M_COL_R" ) );
set.setInt( "collision_height", (int)q.getFieldDouble( "M_COL_H" ) );
// TODO: MISSED char_templates vars
set.setInt( "baseAccuracy", q.getFieldInt( "ACC" ) );
set.setInt( "baseEvasion", q.getFieldInt( "EVASION" ) );
set.setInt( "baseLoad", q.getFieldInt( "_LOAD" ) );
set.setBool( "canCraft", q.getFieldBool( "canCraft" ) );
//
L2PlayerTemplate *tmpl = new L2PlayerTemplate( set );
m_templates[classId] = tmpl;
}
Log( L"CharTemplateTable: loaded %u character templates.", m_templates.size() );
}
else
{
LogError( L"CharTemplateTable::load(): Mysql error: %s", con->getErrorStr() );
}
// load starting items
ItemTable *itemTable = ItemTable::getInstance();
q.create( L"SELECT classId, itemId, amount, equipped FROM char_creation_items" );
if( con->executeQuery( q ) )
{
int classId, itemId, amount;
bool equipped;
while( q.getNextRow() )
{
classId = q.getFieldInt( "classId" );
itemId = q.getFieldInt( "itemId" );
amount = q.getFieldInt( "amount" );
equipped = false;
if( strcmp( q.getFieldStr( "equipped" ), "true" ) == 0 ) equipped = true;
// check for item existance
if( itemTable->getTemplate( itemId ) != NULL )
{
if( classId == -1 )
{
this->addItemToAllTemplates( itemId, amount, equipped );
}
else
{
L2PlayerTemplate *pct = const_cast<L2PlayerTemplate *>(CharTemplateTable::getTemplate( classId ));
if( pct != NULL )
pct->addItem( itemId, amount, equipped );
else
LogWarning( L"CharTemplateTable: load: Entry for undefined class, classId: %d", classId );
}
}
else
{
const wchar_t * className = L"(all)";
if( classId >= 0 ) className = ClassIdTree::getInstance()->getClassId( classId )->getName();
LogWarning( L"CharTemplateTable: load: No data for itemId: %d defined for classId %d (%s)",
itemId, classId, className );
}
}
}
else
{
LogError( L"CharTemplateTable::load(): Mysql error: %s", con->getErrorStr() );
}
//
gs->releaseDBConnection( con );
}
const wchar_t *CharTemplateTable::getClassNameById( int classId )
{
ClassIdTree *cidtree = ClassIdTree::getInstance();
const ClassId *cls = cidtree->getClassId( classId );
if( !cls ) return NULL;
return cls->getName();
}
const L2PlayerTemplate *CharTemplateTable::getTemplate( int classId )
{
CharTemplateTable *ctt = CharTemplateTable::getInstance();
std::map<int, L2PlayerTemplate *>::const_iterator iter;
iter = ctt->m_templates.find( classId );
if( iter == ctt->m_templates.end() ) return NULL;
return iter->second;
}
void CharTemplateTable::addItemToAllTemplates( int itemId, int amount, bool equipped )
{
std::map<int, L2PlayerTemplate *>::const_iterator iter;
iter = m_templates.begin();
while( iter != m_templates.end() )
{
L2PlayerTemplate *tmpl = iter->second;
if( tmpl ) tmpl->addItem( itemId, amount, equipped );
iter++;
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include "world/templates/chars/L2PlayerTemplate.h"
class CharTemplateTable
{
protected:
static CharTemplateTable *s_instance;
static int s_refCount;
CharTemplateTable();
~CharTemplateTable();
public:
static CharTemplateTable *getInstance();
static void freeInstance();
public:
void load();
static const wchar_t *getClassNameById( int classId );
static const L2PlayerTemplate *getTemplate( int classId );
protected:
void addItemToAllTemplates( int itemId, int amount, bool equipped );
protected:
std::map<int, L2PlayerTemplate *> m_templates;
};

View File

@@ -0,0 +1,525 @@
#include "pch.h"
#include "ItemTable.h"
#include "Log.h"
#include "../GS.h"
#include "utils/Exception.h"
ItemTable *ItemTable::s_instance = NULL;
int ItemTable::s_refCount = 0;
ItemTable *ItemTable::getInstance()
{
if( s_instance == NULL )
{
s_instance = new ItemTable();
s_refCount++;
}
return s_instance;
}
void ItemTable::freeInstance()
{
if( !s_instance ) return;
s_refCount--;
if( s_refCount > 0 ) return;
delete s_instance;
s_instance = NULL;
}
ItemTable::ItemTable()
{
m_allTemplates = NULL;
//m_armorTemplates = NULL;
//m_weaponTemplates = NULL;
//m_etcItemTemplates = NULL;
m_cnt_all = 0;
//m_cnt_armors = m_cnt_weapons = m_cnt_etcItems = 0;
m_nTotalBytesAllocated = 0;
m_maxItemId = 0;
}
ItemTable::~ItemTable()
{
m_lock.Lock();
unsigned int i;
if( m_allTemplates )
{
if( m_maxItemId > 0 )
{
for( i=0; i<m_maxItemId; i++ )
{
if( m_allTemplates[i] ) delete m_allTemplates[i];
m_allTemplates[i] = NULL;
}
}
free( m_allTemplates );
m_allTemplates = NULL;
}
m_lock.Unlock();
m_nTotalBytesAllocated = 0;
m_maxItemId = 0;
m_cnt_all = 0;
}
unsigned int ItemTable::select_count( void *vcon, const wchar_t *table )
{
unsigned int ret = 0;
MysqlConnection *con = (MysqlConnection *)vcon;
MysqlQuery q;
q.create( L"SELECT COUNT(*) FROM `%s`", table );
if( con->executeQuery( q ) )
{
if( q.getNextRow() ) ret = q.getFieldUInt( 0 );
else LogError( L"ItemTable::select_count( %s ): q.getNextRow() failed!", table );
}
else LogError( L"ItemTable::select_count( %s ): Mysql error: %s", table, con->getErrorStr() );
return ret;
}
unsigned int ItemTable::select_max( void *vcon, const wchar_t *table )
{
unsigned int ret = 0;
MysqlConnection *con = (MysqlConnection *)vcon;
MysqlQuery q;
q.create( L"SELECT MAX(item_id) FROM `%s`", table );
if( con->executeQuery( q ) )
{
if( q.getNextRow() ) ret = q.getFieldUInt( 0 );
else LogError( L"ItemTable::select_max( %s ): q.getNextRow() failed!", table );
}
else LogError( L"ItemTable::select_max( %s ): Mysql error: %s", table, con->getErrorStr() );
return ret;
}
void ItemTable::load()
{
m_lock.Lock();
try
{
// get GS DB conn
GameServer *gs = GameServer::getInstance();
MysqlConnection *con = gs->getDBConnection();
// count items, armors, weapons in DB
unsigned int etc_count = select_count( con, L"etcitem" );
unsigned int arm_count = select_count( con, L"armor" );
unsigned int wea_count = select_count( con, L"weapon" );
Log( L"ItemTable: loading %u armors, %u weapons, %u etcitems", arm_count, wea_count, etc_count );
unsigned int max_itemId = 0;
unsigned int max_armor = select_max( con, L"armor" );
unsigned int max_etcItem = select_max( con, L"etcitem" );
unsigned int max_weapon = select_max( con, L"weapon" );
max_itemId = max_armor;
if( max_weapon > max_itemId ) max_itemId = max_weapon;
if( max_etcItem > max_itemId ) max_itemId = max_etcItem;
LogDebug( L"ItemTable: max itemId = %u (armor %u, etc %u, weapon %u)", max_itemId, max_armor, max_etcItem, max_weapon );
// allocate m_allTemplates
m_allTemplates = (L2ItemTemplate **)malloc( sizeof(void *) * (max_itemId+1) );
if( !m_allTemplates )
{
m_lock.Unlock();
LogError( L"ItemTable::load(): cannot allocate %u bytes\n", (unsigned int)(sizeof(void *) * (max_itemId+1)) );
return;
}
memset( m_allTemplates, 0, sizeof(void *) * (max_itemId+1) );
m_cnt_all = etc_count + arm_count + wea_count;
m_maxItemId = max_itemId;
m_nTotalBytesAllocated = sizeof(void *) * (max_itemId+1);
//
loadEtcItems( con );
loadArmors( con );
loadWeapons( con );
//
// release GS DB conn
gs->releaseDBConnection( con );
}
catch( Exception& e )
{
LogError( L"ItemTable load error: %S", e.what() );
e.logStackTrace();
}
m_lock.Unlock();
Log( L"ItemTable: loaded, used %I64u bytes", m_nTotalBytesAllocated );
}
void ItemTable::reload()
{
// TODO: maybe better way?
Log( L"ItemTable: reload starting..." );
m_lock.Lock();
this->~ItemTable();
this->load();
m_lock.Unlock();
Log( L"ItemTable: reload complete." );
}
void ItemTable::loadEtcItems( void *vcon )
{
MysqlConnection *con = (MysqlConnection *)vcon;
MysqlQuery q;
q.create( L"SELECT item_id, name, crystallizable, item_type, weight, consume_type, material, "
L" crystal_type, duration, time, price, crystal_count, sellable, dropable, destroyable, "
L" tradeable, handler, skill FROM etcitem" );
if( con->executeQuery( q ) )
{
while( q.getNextRow() )
{
StatsSet set;
L2ItemSubType etcItemType = SYBTYPE_INCORRECT;
int item_id = 0;
char *str = NULL;
L2ItemSlot bodypart = SLOT_NONE;
int type2 = L2ItemTemplate::TYPE2_OTHER;
wchar_t wname[256] = {0};
// get item ID
item_id = q.getFieldInt( "item_id" );
set.setInt( "item_id", item_id );
str = q.getFieldStr( "item_type" );
if( _stricmp( str, "none" ) == 0 )
etcItemType = ETCITEM_OTHER; // only for default
else if ( _stricmp( str, "castle_guard") == 0 )
etcItemType = ETCITEM_SCROLL; // dummy
else if( _stricmp( str, "material") == 0 )
etcItemType = ETCITEM_MATERIAL;
else if( _stricmp( str, "pet_collar") == 0 )
etcItemType = ETCITEM_PET_COLLAR;
else if( _stricmp( str, "potion") == 0 )
etcItemType = ETCITEM_POTION;
else if( _stricmp( str, "recipe") == 0 )
etcItemType = ETCITEM_RECEIPE;
else if( _stricmp( str, "scroll") == 0 )
etcItemType = ETCITEM_SCROLL;
else if( _stricmp( str, "seed") == 0 )
etcItemType = ETCITEM_SEED;
else if( _stricmp( str, "shot") == 0 )
etcItemType = ETCITEM_SHOT;
else if( _stricmp( str, "spellbook") == 0 )
etcItemType = ETCITEM_SPELLBOOK; // Spellbook, Amulet, Blueprint
else if( _stricmp( str, "herb") == 0 )
etcItemType = ETCITEM_HERB;
else if( _stricmp( str, "arrow") == 0 )
{
etcItemType = ETCITEM_ARROW;
bodypart = SLOT_L_HAND;
}
else if( _stricmp( str, "bolt") == 0 )
{
etcItemType = ETCITEM_BOLT;
bodypart = SLOT_L_HAND;
}
else if( _stricmp( str, "quest") == 0 )
{
etcItemType = ETCITEM_QUEST;
type2 = L2ItemTemplate::TYPE2_QUEST;
}
else if( _stricmp( str, "lure") == 0 )
{
etcItemType = ETCITEM_OTHER;
bodypart = SLOT_L_HAND;
}
else if( _stricmp( str, "dye") == 0 )
etcItemType = ETCITEM_DYE;
else if( _stricmp( str, "lotto") == 0 )
etcItemType = ETCITEM_OTHER;
else if( _stricmp( str, "race_ticket") == 0 )
etcItemType = ETCITEM_OTHER;
else if( _stricmp( str, "harvest") == 0 )
etcItemType = ETCITEM_OTHER;
else if( _stricmp( str, "ticket_of_lord") == 0 )
etcItemType = ETCITEM_OTHER;
else
{
q.getFieldStrW( "name", wname, 255 ); wname[255] = 0;
LogWarning( L"ItemTable::loadEtcItems(): unknown etcitem type: %S (item_id %d [%s])",
str, item_id, wname );
etcItemType = ETCITEM_OTHER;
}
set.setInt( "type1", L2ItemTemplate::TYPE1_ITEM_QUESTITEM_ADENA );
set.setInt( "type2", type2 );
set.setInt( "bodypart", (int)bodypart );
set.setBool( "crystallizable", q.getFieldBool( "crystallizable" ) );
set.setInt( "crystal_count", q.getFieldInt( "crystal_count" ) );
set.setBool( "sellable", q.getFieldBool( "sellable" ) );
set.setBool( "dropable", q.getFieldBool( "dropable" ) );
set.setBool( "destroyable", q.getFieldBool( "destroyable" ) );
set.setBool( "tradeable", q.getFieldBool( "tradeable" ) );
set.setString( "handler", q.getFieldStr( "handler" ) );
// item.set.set("skill", rset.getString("skill")); // TODO: etc item skills
str = q.getFieldStr( "consume_type" );
if( _stricmp( str, "asset" ) == 0 )
{
etcItemType = ETCITEM_MONEY;
set.setBool( "stackable", true );
set.setInt( "type2", L2ItemTemplate::TYPE2_MONEY );
}
else if ( _stricmp( str, "stackable" ) == 0 )
{
set.setBool( "stackable", true );
}
else
{
set.setBool( "stackable", false );
}
L2ItemMaterial material = L2ItemTemplate::getMaterialByName( q.getFieldStr( "material" ) );
set.setInt( "material", (int)material );
L2ItemCrystal crystal = L2ItemTemplate::getCrystalTypeByName( q.getFieldStr( "crystal_type" ) );
set.setInt( "crystal_type", (int)crystal );
set.setInt( "weight", q.getFieldInt( "weight" ) );
// name
q.getFieldStrW( "name", wname, 255 ); wname[255] = 0;
set.setWString( "name", wname );
//
set.setInt( "duration", q.getFieldInt( "duration" ) );
set.setInt( "time", q.getFieldInt( "time" ) );
set.setInt( "price", q.getFieldInt( "price" ) );
//
// create and insert template
L2EtcItemTemplate *tmpl = new L2EtcItemTemplate( etcItemType, set );
m_allTemplates[item_id] = (L2ItemTemplate *)tmpl;
// count bytes allocated
if( tmpl ) m_nTotalBytesAllocated += (sizeof(L2EtcItemTemplate) + 2*wcslen(tmpl->getName()));
} // next row
}
else
LogError( L"ItemTable::loadEtcItems(): MySQL error: %s", con->getErrorStr() );
}
void ItemTable::loadArmors( void *vcon )
{
MysqlConnection *con = (MysqlConnection *)vcon;
MysqlQuery q;
q.create( L"SELECT item_id, name, bodypart, crystallizable, armor_type, weight,"
L" material, crystal_type, avoid_modify, duration, time, p_def, m_def, mp_bonus,"
L" price, crystal_count, sellable, dropable, destroyable, tradeable, enchant4_skill, skill FROM armor" );
if( con->executeQuery( q ) )
{
while( q.getNextRow() )
{
StatsSet set;
int item_id;
int bodypart_i = 0;
L2ItemSlot bodypart = SLOT_NONE;
wchar_t wstr[256] = {0};
char *str = NULL;
//
L2ItemSubType armorType = L2ItemTemplate::getArmorTypeByName( q.getFieldStr( "armor_type" ) );
item_id = q.getFieldInt( 0 );
set.setInt( "item_id", item_id );
q.getFieldStrW( "name", wstr, 255 );
set.setWString( "name", wstr );
bodypart = L2ItemTemplate::getSlotByName( q.getFieldStr( "bodypart" ) );
bodypart_i = (int)bodypart;
set.setInt( "bodypart", bodypart_i );
set.setBool( "crystallizable", q.getFieldBool( "crystallizable" ) );
set.setInt( "crystal_count", q.getFieldInt( "crystal_count" ) );
set.setBool( "sellable", q.getFieldBool( "sellable" ) );
set.setBool( "dropable", q.getFieldBool( "dropable" ) );
set.setBool( "destroyable", q.getFieldBool( "destroyable" ) );
set.setBool( "tradeable", q.getFieldBool( "tradeable" ) );
set.setString( "enchant4_skill", q.getFieldStr( "enchant4_skill" ) );
set.setString( "skill", str = q.getFieldStr( "skill" ) );
// determine type1 & type2
if( bodypart == SLOT_NECK || bodypart == SLOT_HAIR
|| bodypart == SLOT_HAIR2 || bodypart == SLOT_HAIRALL
|| (bodypart & SLOT_L_EAR) != 0 || (bodypart & SLOT_L_FINGER) != 0)
{
set.setInt( "type1", L2ItemTemplate::TYPE1_WEAPON_RING_EARRING_NECKLACE );
set.setInt( "type2", L2ItemTemplate::TYPE2_ACCESSORY );
}
else
{
set.setInt( "type1", L2ItemTemplate::TYPE1_SHIELD_ARMOR );
set.setInt( "type2", L2ItemTemplate::TYPE2_SHIELD_ARMOR );
}
set.setInt( "weight", q.getFieldInt( "weight" ) );
set.setInt( "material", (int)L2ItemTemplate::getMaterialByName( q.getFieldStr( "material" ) ) );
set.setInt( "crystal_type", (int)L2ItemTemplate::getCrystalTypeByName( q.getFieldStr( "crystal_type" ) ) );
set.setInt( "avoid_modify", q.getFieldInt( "avoid_modify" ) );
set.setInt( "duration", q.getFieldInt( "duration" ) );
set.setInt( "time", q.getFieldInt( "time" ) );
set.setInt( "p_def", q.getFieldInt( "p_def" ) );
set.setInt( "m_def", q.getFieldInt( "m_def" ) );
set.setInt( "mp_bonus", q.getFieldInt( "mp_bonus" ) );
set.setInt( "price", q.getFieldInt( "price" ) );
// deal with pet items: type1, type2 & bodypart
if( armorType == ARMOR_PET )
{
if( bodypart == SLOT_NECK )
{
set.setInt( "type1", L2ItemTemplate::TYPE1_WEAPON_RING_EARRING_NECKLACE );
set.setInt( "type2", L2ItemTemplate::TYPE2_ACCESSORY );
set.setInt( "bodypart", (int)SLOT_NECK );
}
else
{
set.setInt( "type1", L2ItemTemplate::TYPE1_SHIELD_ARMOR );
L2ItemSlot temp_b = SLOT_NONE;
//int temp_b = 0;
set.getInt( "bodypart", (int *)&temp_b );
switch( temp_b )
{
case SLOT_WOLF:
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_WOLF );
break;
case SLOT_GREATWOLF:
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_EVOLVEDWOLF );
break;
case SLOT_HATCHLING:
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_HATCHLING );
break;
case SLOT_BABYPET:
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_BABY );
break;
default:
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_STRIDER );
break;
}
set.setInt( "bodypart", (int)SLOT_CHEST );
}
}
// create and insert template
L2ArmorTemplate *tmpl = new L2ArmorTemplate( armorType, set );
m_allTemplates[item_id] = (L2ItemTemplate *)tmpl;
// count bytes allocated
if( tmpl ) m_nTotalBytesAllocated += (sizeof(L2ArmorTemplate) + 2*wcslen(tmpl->getName()));
//else throw std::bad_alloc( "ItemTable:loadArmors: new L2ArmorTemplate" );
}
}
else
LogError( L"ItemTable::loadArmors(): MySQL error: %s", con->getErrorStr() );
}
void ItemTable::loadWeapons( void *vcon )
{
MysqlConnection *con = (MysqlConnection *)vcon;
MysqlQuery q;
q.create( L"SELECT item_id, name, bodypart, crystallizable, weight, soulshots, spiritshots,"
L" material, crystal_type, p_dam, rnd_dam, weaponType, critical, hit_modify, avoid_modify,"
L" shield_def, shield_def_rate, atk_speed, mp_consume, m_dam, duration, time, price, crystal_count,"
L" sellable, dropable, destroyable, tradeable, skill,enchant4_skill_id,enchant4_skill_lvl, "
L" onCast_skill_id, onCast_skill_lvl, onCast_skill_chance, onCrit_skill_id, "
L" onCrit_skill_lvl, onCrit_skill_chance, change_weaponId FROM weapon" );
if( con->executeQuery( q ) )
{
while( q.getNextRow() )
{
StatsSet set;
int item_id;
int bodypart_i = 0;
L2ItemSlot bodypart = SLOT_NONE;
wchar_t wstr[256] = {0};
//
L2ItemSubType weaponType = L2ItemTemplate::getWeaponTypeByName( q.getFieldStr( "weaponType" ) );
item_id = q.getFieldInt( 0 );
set.setInt( "item_id", item_id );
q.getFieldStrW( "name", wstr, 255 );
set.setWString( "name", wstr );
// lets see if this is a shield
if( weaponType == WEAPON_NONE )
{
set.setInt( "type1", L2ItemTemplate::TYPE1_SHIELD_ARMOR );
set.setInt( "type2", L2ItemTemplate::TYPE2_SHIELD_ARMOR );
}
else
{
set.setInt( "type1", L2ItemTemplate::TYPE1_WEAPON_RING_EARRING_NECKLACE );
set.setInt( "type2", L2ItemTemplate::TYPE2_WEAPON );
}
bodypart = L2ItemTemplate::getSlotByName( q.getFieldStr( "bodypart" ) );
bodypart_i = (int)bodypart;
set.setInt( "bodypart", bodypart_i );
set.setInt( "material", (int)L2ItemTemplate::getMaterialByName( q.getFieldStr( "material" ) ) );
set.setInt( "crystal_type", (int)L2ItemTemplate::getCrystalTypeByName( q.getFieldStr( "crystal_type" ) ) );
set.setBool( "crystallizable", q.getFieldBool( "crystallizable" ) );
set.setInt( "weight", q.getFieldInt( "weight" ) );
set.setInt( "soulshots", q.getFieldInt( "soulshots" ) );
set.setInt( "spiritshots", q.getFieldInt( "spiritshots" ) );
set.setInt( "p_dam", q.getFieldInt( "p_dam" ) );
set.setInt( "rnd_dam", q.getFieldInt( "rnd_dam" ) );
set.setInt( "critical", q.getFieldInt( "critical" ) );
set.setInt( "hit_modify", q.getFieldInt( "hit_modify" ) );
set.setInt( "avoid_modify", q.getFieldInt( "avoid_modify" ) );
set.setInt( "shield_def", q.getFieldInt( "shield_def" ) );
set.setInt( "shield_def_rate", q.getFieldInt( "shield_def_rate" ) );
set.setInt( "atk_speed", q.getFieldInt( "atk_speed" ) );
set.setInt( "mp_consume", q.getFieldInt( "mp_consume" ) );
set.setInt( "m_dam", q.getFieldInt( "m_dam" ) );
set.setInt( "duration", q.getFieldInt( "duration" ) );
set.setInt( "time", q.getFieldInt( "time" ) );
set.setInt( "price", q.getFieldInt( "price" ) );
set.setInt( "crystal_count", q.getFieldInt( "crystal_count" ) );
set.setBool( "sellable", q.getFieldBool( "sellable" ) );
set.setBool( "dropable", q.getFieldBool( "dropable" ) );
set.setBool( "destroyable", q.getFieldBool( "destroyable" ) );
set.setBool( "tradeable", q.getFieldBool( "tradeable" ) );
set.setString( "skill", q.getFieldStr( "skill" ) );
set.setInt( "enchant4_skill_id", q.getFieldInt( "enchant4_skill_id" ) );
set.setInt( "enchant4_skill_lvl", q.getFieldInt( "enchant4_skill_lvl" ) );
/*item.set.set("onCast_skill_id", rset.getInt("onCast_skill_id"));
item.set.set("onCast_skill_lvl", rset.getInt("onCast_skill_lvl"));
item.set.set("onCast_skill_chance", rset.getInt("onCast_skill_chance"));
item.set.set("onCrit_skill_id", rset.getInt("onCrit_skill_id"));
item.set.set("onCrit_skill_lvl", rset.getInt("onCrit_skill_lvl"));
item.set.set("onCrit_skill_chance", rset.getInt("onCrit_skill_chance"));*/
set.setInt( "change_weaponId", q.getFieldInt( "change_weaponId" ) );
if( weaponType == WEAPON_PET )
{
set.setInt( "type1", L2ItemTemplate::TYPE1_WEAPON_RING_EARRING_NECKLACE );
if( bodypart == SLOT_WOLF )
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_WOLF );
else if( bodypart == SLOT_GREATWOLF )
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_EVOLVEDWOLF );
else if( bodypart == SLOT_HATCHLING )
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_HATCHLING );
else if( bodypart == SLOT_BABYPET )
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_BABY );
else
set.setInt( "type2", L2ItemTemplate::TYPE2_PET_STRIDER );
bodypart = SLOT_R_HAND;
set.setInt( "bodypart", (int)SLOT_R_HAND);
}
// create and insert template
L2WeaponTemplate *tmpl = new L2WeaponTemplate( weaponType, set );
m_allTemplates[item_id] = (L2ItemTemplate *)tmpl;
// count bytes allocated
if( tmpl ) m_nTotalBytesAllocated += (sizeof(L2WeaponTemplate) + 2*wcslen(tmpl->getName()));
//else throw std::bad_alloc( "ItemTable:loadWeapons new L2WeaponTemplate" );
}
}
else
LogError( L"ItemTable::loadWeapons(): MySQL error: %s", con->getErrorStr() );
}
const L2ItemTemplate *ItemTable::getTemplate( unsigned int itemId )
{
if( !m_allTemplates ) return NULL;
if( itemId < 0 || itemId > m_maxItemId )
{
LogError( L"ItemTable::getTemplate( %u ): request to incorrect itemId (max id is %u)\n",
itemId, m_maxItemId );
return NULL;
}
return (const L2ItemTemplate *)m_allTemplates[itemId];
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include "l2c_utils.h"
#include "../world/templates/StatsSet.h"
#include "../world/templates/item/L2ItemTemplate.h"
#include "../world/templates/item/L2ArmorTemplate.h"
#include "../world/templates/item/L2EtcItemTemplate.h"
#include "../world/templates/item/L2WeaponTemplate.h"
class ItemTable
{
protected:
ItemTable();
~ItemTable();
static ItemTable *s_instance;
static int s_refCount;
public:
static ItemTable *getInstance();
static void freeInstance();
public:
void load();
void reload();
public:
const L2ItemTemplate *getTemplate( unsigned int itemId );
protected:
static unsigned int select_count( void *vcon, const wchar_t *table );
static unsigned int select_max( void *vcon, const wchar_t *table );
void loadEtcItems( void *vcon );
void loadArmors( void *vcon );
void loadWeapons( void *vcon );
protected:
CriticalSection m_lock;
L2ItemTemplate **m_allTemplates;
//L2ArmorTemplate *m_armorTemplates;
//L2WeaponTemplate *m_weaponTemplates;
//L2EtcItemTemplate *m_etcItemTemplates;
unsigned int m_cnt_all;
unsigned int m_maxItemId;
//unsigned int m_cnt_armors;
//unsigned int m_cnt_weapons;
//unsigned int m_cnt_etcItems;
unsigned long long int m_nTotalBytesAllocated;
};

39
L2C_Server/enums.h Normal file
View File

@@ -0,0 +1,39 @@
#pragma once
enum GS_LoginFailReason
{
REASON_IP_BANNED = 1,
REASON_IP_RESERVED = 2,
REASON_WRONG_HEXID = 3,
REASON_ID_RESERVED = 4,
REASON_NO_FREE_ID = 5,
NOT_AUTHED = 6,
REASON_ALREADY_LOGGED_IN = 7
};
enum GS_ServerListStatus
{
SERVER_LIST_STATUS = 0x01,
SERVER_LIST_CLOCK = 0x02,
SERVER_LIST_SQUARE_BRACKET = 0x03,
SERVER_LIST_MAX_PLAYERS = 0x04,
SERVER_LIST_TEST_SERVER = 0x05
};
enum ServerStatus
{
SERVER_STATUS_AUTO = 0x00,
SERVER_STATUS_GOOD = 0x01,
SERVER_STATUS_NORMAL = 0x02,
SERVER_STATUS_FULL = 0x03,
SERVER_STATUS_DOWN = 0x04,
SERVER_STATUS_GM_ONLY = 0x05
};
enum ClientState
{
CLIENT_STATE_OFFLINE = 0x00,
CLIENT_STATE_CONNECTED = 0x01,
CLIENT_STATE_AUTHED = 0x02,
CLIENT_STATE_IN_GAME = 0x03
};

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;
}

1
L2C_Server/pch.cpp Normal file
View File

@@ -0,0 +1 @@
#include "pch.h"

44
L2C_Server/pch.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_WARNINGS
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0600
// CRT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <stdarg.h>
#include <assert.h>
// STL
#include <string>
#include <list>
#include <map>
#include <set>
#include <vector>
#include <bitset>
// Win32 API
#include <winsock2.h>
#include <windows.h>
#include <windowsx.h>
#include <wchar.h>
#include <tchar.h>
#include <process.h>
#include <commctrl.h>
#include <dbghelp.h>
#include <richedit.h>
// extern libs
#include <L2Packets.h>
#include <mysqlclient/mysql.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/blowfish.h>
#include <openssl/sha.h>

View File

@@ -0,0 +1,304 @@
#include "pch.h"
#include "Log.h"
#include "Debugging.h"
#define MAX_SYM_NAME_LEN 256
Debug *Debug::s_instance = NULL;
Debug *Debug::getInstance()
{
if( !s_instance )
s_instance = new Debug();
return s_instance;
}
void Debug::freeInstance()
{
if( s_instance )
{
delete s_instance;
s_instance = NULL;
}
}
/*BOOL CALLBACK Debug_EnumerateModulesProc64( PCSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext )
{
//LogDebug( L" EnumerateModulesProc [%S] 0x%08X", ModuleName, (DWORD)BaseOfDll );
wchar_t w[1024];
wsprintfW( w, L" EnumerateModulesProc [%S] 0x%08X", ModuleName, (DWORD)BaseOfDll );
MessageBox( NULL, w, L"Enum mod", 0 );
return TRUE;
}*/
Debug::Debug()
{
InitializeCriticalSectionAndSpinCount( &m_lock, 10 );
EnterCriticalSection( &m_lock );
m_initOK = false;
m_symHandle = GetCurrentProcess();
// set symbol handler options
SymSetOptions( SYMOPT_UNDNAME | /*SYMOPT_DEFERRED_LOADS |*/ SYMOPT_LOAD_LINES );
// create symbol search path
WCHAR windir[256] = {0};
WCHAR szSearchPath[256] = {0};
GetWindowsDirectoryW( windir, 255 );
wsprintfW( szSearchPath, L".;.\\Symbols;%s\\Symbols;%s\\Symbols\\dll", windir, windir );
//MessageBoxW( NULL, szSearchPath, NULL, 0 );
// initialize symbol handler
BOOL ok = SymInitializeW( m_symHandle, szSearchPath/*NULL*/, TRUE );
if( !ok )
{
DWORD le = GetLastError();
wchar_t msg[512];
wsprintfW( msg, L"SymInitialize() failed: %u", le );
MessageBox( NULL, msg, L"Error", MB_ICONSTOP );
}
else
{
m_initOK = true;
//
// load PDBs for all modules possible
//SymEnumerateModules64( m_symHandle, Debug_EnumerateModulesProc64, NULL );
//
}
LeaveCriticalSection( &m_lock );
}
Debug::~Debug()
{
EnterCriticalSection( &m_lock );
if( m_initOK )
{
SymCleanup( m_symHandle );
m_initOK = false;
}
LeaveCriticalSection( &m_lock );
DeleteCriticalSection( &m_lock );
}
void Debug::createStackTrace( DebugStackTrace& traceObject )
{
traceObject.clear();
EnterCriticalSection( &m_lock );
//
// fill stackframe struct
STACKFRAME64 stackFrame;
memset( &stackFrame, 0, sizeof(stackFrame) );
stackFrame.AddrPC.Mode = AddrModeFlat;
stackFrame.AddrFrame.Mode = AddrModeFlat;
stackFrame.AddrStack.Mode = AddrModeFlat;
unsigned int i;
__asm mov i, esp
stackFrame.AddrStack.Offset = i;
__asm mov i, ebp
stackFrame.AddrFrame.Offset = i;
//stackFrame.AddrPC.Offset = (DWORD64)(void *)(Debug::createStackTrace);
stackFrame.AddrPC.Offset = (DWORD64)(void *)&stackFrame;
BOOL bRet = TRUE;
//
bRet = StackWalk64( IMAGE_FILE_MACHINE_I386, this->m_symHandle, GetCurrentThread(), &stackFrame,
NULL, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL );
while( bRet )
{
DebugOneStackFrameInfo frameInfo;
DWORD64 addr_offset = 0; // address offset
SYMBOL_INFO *psymInfo = (SYMBOL_INFO *)malloc( sizeof(SYMBOL_INFO) + MAX_SYM_NAME_LEN + 1 );
memset( psymInfo, 0, sizeof(SYMBOL_INFO) + MAX_SYM_NAME_LEN + 1 );
psymInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
psymInfo->MaxNameLen = MAX_SYM_NAME_LEN;
if( stackFrame.AddrReturn.Offset > 0 )
{
bRet = SymFromAddr( this->m_symHandle, stackFrame.AddrReturn.Offset, &addr_offset, psymInfo );
if( bRet )
{
// get module info
char *moduleName = NULL;
IMAGEHLP_MODULE64 moduleInfo;
memset( &moduleInfo, 0, sizeof(moduleInfo) );
moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); // 584?
if( !SymGetModuleInfo64( this->m_symHandle, psymInfo->Address, &moduleInfo ) )
{
Log_Win32Error( L"SymGetModuleInfo64() failed", GetLastError() );
LogError( L"For addr 0x%08X name %S", (DWORD)psymInfo->Address, psymInfo->Name );
}
else moduleName = moduleInfo.ModuleName;
//
// get file name and line number
IMAGEHLP_LINE64 lineinfo;
memset( &lineinfo, 0, sizeof(lineinfo) );
lineinfo.SizeOfStruct = sizeof(lineinfo);
DWORD disp = 0;
if( SymGetLineFromAddr64( GetCurrentProcess(), psymInfo->Address, &disp, &lineinfo ) )
{
frameInfo.set( psymInfo->Name, psymInfo->Address, addr_offset,
lineinfo.FileName, lineinfo.LineNumber,
moduleName );
}
else
{
DWORD le = GetLastError();
if( le != ERROR_INVALID_ADDRESS ) // Attempt to access invalid address.
{
Log_Win32Error( L"dbghelp!SymGetLineFromAddr64() failed", le );
LogError( L"For name: [%S]", psymInfo->Name );
}
frameInfo.set( psymInfo->Name, psymInfo->Address, addr_offset, NULL, 0, moduleName );
}
}
else
{
DWORD le = GetLastError();
Log_Win32Error( L"dbghelp!SymFromAddr() failed", le );
LogError( L"For addr: 0x%08X", (DWORD)stackFrame.AddrReturn.Offset );
}
// add frame info
traceObject.addStackFrame( frameInfo );
}
free( psymInfo );
//
bRet = StackWalk64( IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &stackFrame, NULL,
NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL );
}
DWORD le = GetLastError();
if( le != ERROR_INVALID_ADDRESS && le != NO_ERROR )
Log_Win32Error( L"StackWalk64() ended", le );
//
// unlock
LeaveCriticalSection( &m_lock );
}
DebugOneStackFrameInfo::~DebugOneStackFrameInfo()
{
if( m_name ) free( m_name );
m_name = NULL;
if( m_fileName ) free( m_fileName );
m_fileName = NULL;
if( m_moduleName ) free( m_moduleName );
m_moduleName = NULL;
m_addr = 0;
m_addr_offset = 0;
m_line = 0;
}
DebugOneStackFrameInfo::DebugOneStackFrameInfo( const DebugOneStackFrameInfo& other )
{
m_name = NULL;
m_fileName = NULL;
m_addr = 0;
m_addr_offset = 0;
m_line = 0;
m_moduleName = NULL;
this->operator=( other );
}
const DebugOneStackFrameInfo& DebugOneStackFrameInfo::operator=( const DebugOneStackFrameInfo& other )
{
if( this == &other ) return (*this);
set( other.m_name, other.m_addr, other.m_addr_offset, other.m_fileName, other.m_line, other.m_moduleName );
return (*this);
}
// compares only address & addr offset
bool DebugOneStackFrameInfo::operator==( const DebugOneStackFrameInfo& other )
{
if( this->m_addr != other.m_addr ) return false; // different addr
if( this->m_addr_offset != other.m_addr_offset ) return false; // different offset
return true; // addresses equal
}
void DebugOneStackFrameInfo::set(
const char *name,
unsigned long long addr,
unsigned long long addr_offset,
const char *fileName,
unsigned int line,
const char *moduleName )
{
m_addr = addr;
m_addr_offset = addr_offset;
m_line = line;
if( m_name ) free( m_name );
m_name = NULL;
if( name ) m_name = _strdup( name );
if( m_fileName ) free( m_fileName );
m_fileName = NULL;
if( fileName ) m_fileName = _strdup( fileName );
if( m_moduleName ) free( m_moduleName );
m_moduleName = NULL;
if( moduleName ) m_moduleName = _strdup( moduleName );
}
DebugStackTrace::DebugStackTrace()
{
m_list.clear();
}
DebugStackTrace::~DebugStackTrace()
{
m_list.clear();
}
void DebugStackTrace::clear()
{
m_list.clear();
}
void DebugStackTrace::addStackFrame( DebugOneStackFrameInfo& ref )
{
m_list.push_back( ref );
}
void DebugStackTrace::logStackTrace()
{
std::list<DebugOneStackFrameInfo>::const_iterator iter = m_list.begin();
int nFrame = 0;
while( iter != m_list.end() )
{
DebugOneStackFrameInfo frm = (*iter);
if( frm.getModuleName() == NULL )
{
if( frm.getFileName() )
{
Log( L" %2d: %S() (0x%08X + %I64u) [%S line %u]", nFrame,
frm.getName(), (unsigned int)frm.getAddr(), frm.getAddrOffset(),
frm.getFileName(), frm.getLine() );
}
else
{
Log( L" %2d: %S() (0x%08X + %I64u)", nFrame,
frm.getName(), (unsigned int)frm.getAddr(), frm.getAddrOffset() );
}
}
else
{
if( frm.getFileName() )
{
Log( L" %2d: %S!%S() (0x%08X + %I64u) [%S line %u]", nFrame,
frm.getModuleName(), frm.getName(), (unsigned int)frm.getAddr(), frm.getAddrOffset(),
frm.getFileName(), frm.getLine() );
}
else
{
Log( L" %2d: %S!%S() (0x%08X + %I64u)", nFrame,
frm.getModuleName(), frm.getName(), (unsigned int)frm.getAddr(), frm.getAddrOffset() );
}
}
nFrame++;
iter++;
}
}

View File

@@ -0,0 +1,71 @@
#pragma once
class DebugOneStackFrameInfo
{
public:
DebugOneStackFrameInfo():
m_name(NULL), m_fileName(NULL), m_moduleName(NULL), m_addr(0), m_addr_offset(0), m_line(0) {}
DebugOneStackFrameInfo( const DebugOneStackFrameInfo& other );
~DebugOneStackFrameInfo();
const DebugOneStackFrameInfo& operator=( const DebugOneStackFrameInfo& other );
bool operator==( const DebugOneStackFrameInfo& other );
public:
void set(
const char *name,
unsigned long long addr,
unsigned long long addr_offset,
const char *fileName,
unsigned int line,
const char *moduleName
);
char *getName() const { return m_name; }
unsigned long long getAddr() const { return m_addr; }
unsigned long long getAddrOffset() const { return m_addr_offset; }
char *getFileName() const { return m_fileName; }
unsigned int getLine() const { return m_line; }
char *getModuleName() const { return m_moduleName; }
public:
char *m_name;
unsigned long long m_addr;
unsigned long long m_addr_offset;
char *m_fileName;
unsigned int m_line;
char *m_moduleName;
};
class DebugStackTrace
{
public:
DebugStackTrace();
~DebugStackTrace();
public:
void clear();
void addStackFrame( DebugOneStackFrameInfo& ref );
void logStackTrace();
protected:
std::list<DebugOneStackFrameInfo> m_list;
};
class Debug
{
protected:
Debug();
~Debug();
static Debug *s_instance;
public:
static Debug *getInstance();
static void freeInstance();
public:
void createStackTrace( DebugStackTrace& traceObject );
protected:
CRITICAL_SECTION m_lock;
HANDLE m_symHandle;
bool m_initOK;
};

View File

@@ -0,0 +1,98 @@
#include "pch.h"
#include "Log.h"
#include "Exception.h"
Exception::Exception()
{
m_what = NULL;
m_trace = new DebugStackTrace();
Debug::getInstance()->createStackTrace( (*m_trace) );
}
Exception::Exception( const char *_Format, ... )
{
m_what = NULL;
m_trace = new DebugStackTrace();
Debug::getInstance()->createStackTrace( (*m_trace) );
//
va_list _ArgPtr;
va_start( _ArgPtr, _Format );
// _vscprintf(): Returns the number of characters in the formatted string using a pointer to a list of arguments.
size_t what_len = _vscprintf( _Format, _ArgPtr );
m_what = (char *)malloc( what_len + 16 );
if( m_what )
{
va_start( _ArgPtr, _Format );
vsprintf( m_what, _Format, _ArgPtr );
}
}
Exception::Exception( const Exception& other )
{
m_what = NULL;
m_trace = new DebugStackTrace();
Debug::getInstance()->createStackTrace( (*m_trace) );
this->operator=( other );
}
Exception::~Exception()
{
if( m_what )
{
free( m_what );
m_what = NULL;
}
if( m_trace )
{
delete m_trace;
m_trace = NULL;
}
}
const Exception& Exception::operator=( const Exception& other )
{
if( this == &other ) return (*this);
if( m_what )
{
free( m_what );
m_what = NULL;
}
if( other.m_what ) m_what = _strdup( other.m_what );
return (*this);
}
const char *Exception::what() const
{
if( !m_what ) return "(no comment)";
return m_what;
}
void Exception::setWhat( const char *_Format, ... )
{
if( m_what )
{
free( m_what );
m_what = NULL;
}
va_list _ArgPtr;
va_start( _ArgPtr, _Format );
// _vscprintf(): Returns the number of characters in the formatted string using a pointer to a list of arguments.
size_t what_len = _vscprintf( _Format, _ArgPtr );
m_what = (char *)malloc( what_len + 16 );
if( m_what )
{
va_start( _ArgPtr, _Format );
vsprintf( m_what, _Format, _ArgPtr );
}
}
void Exception::logStackTrace()
{
if( !m_trace )
{
Log( L"Exception: no stack trace!" );
return;
}
m_trace->logStackTrace();
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Debugging.h"
class Exception
{
public:
Exception();
Exception( const char *_Format, ... );
Exception( const Exception& other );
virtual ~Exception();
const Exception& operator=( const Exception& other );
public:
virtual void setWhat( const char *_Format, ... );
virtual const char *what() const;
virtual void logStackTrace();
protected:
char *m_what;
DebugStackTrace *m_trace;
};

View File

@@ -0,0 +1,177 @@
#include "pch.h"
#include "IdFactory.h"
#include "GS.h"
#include "Log.h"
//#include "Exception.h"
IdFactory::IdFactory()
{
m_bitset = NULL;
m_usedMemory = 0;
m_lastOid = MIN_OBJECT_ID;
}
IdFactory::~IdFactory()
{
clear();
}
bool IdFactory::init()
{
if( m_bitset ) return false;
m_lock.Lock();
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx( &ms );
unsigned long long mem_before = ms.ullAvailVirtual;
unsigned long long mem_after = 0;
m_bitset = new std::bitset<OBJECT_ID_COUNT>();
if( m_bitset )
{
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx( &ms );
mem_after = ms.ullAvailVirtual;
m_usedMemory = mem_before - mem_after;
m_lastOid = MIN_OBJECT_ID;
// restore all currently used objectId's
fillObjectIdsFromDB();
m_lock.Unlock();
return true;
}
m_lock.Unlock();
return false;
}
void IdFactory::clear()
{
m_lock.Lock();
if( m_bitset )
{
delete m_bitset;
m_bitset = NULL;
m_usedMemory = 0;
}
m_lastOid = MIN_OBJECT_ID;
m_lock.Unlock();
}
unsigned int IdFactory::getUsedCount()
{
if( !m_bitset ) return 0;
unsigned int r = 0;
m_lock.Lock();
r = m_bitset->count();
m_lock.Unlock();
return r;
}
unsigned int IdFactory::getNextObjectId() throw(NoFreeObjectIdException)
{
if( !m_bitset ) return 0;
m_lock.Lock();
unsigned int o = findUnusedOid();
if( o == 0 )
{
m_lock.Unlock();
throw NoFreeObjectIdException();
}
m_bitset->set( o-MIN_OBJECT_ID );
m_lastOid = o+1; // next time try to give next objectId
if( m_lastOid > MAX_OBJECT_ID ) m_lastOid = MIN_OBJECT_ID;
m_lock.Unlock();
return o;
}
bool IdFactory::releaseObjectId( unsigned int o )
{
if( !m_bitset ) return false;
m_lock.Lock();
bool ret = false;
if( m_bitset->test( o ) )
{
ret = true;
m_bitset->reset( o );
}
m_lock.Unlock();
return ret;
}
bool IdFactory::isUsedObjectId( unsigned int o )
{
if( !m_bitset ) return false;
bool ret = false;
m_lock.Lock();
ret = m_bitset->test( o );
m_lock.Unlock();
return ret;
}
unsigned long long int IdFactory::getUsedMemoryBytes() const
{
return m_usedMemory;
}
unsigned int IdFactory::findUnusedOid() const
{
if( !m_bitset ) return 0;
// lock should be locked!
unsigned int o = m_lastOid;
unsigned int start_o = o;
while( o >= MIN_OBJECT_ID )
{
#ifdef _DEBUG_IDFACTORY_INT
try
{
#endif /*_DEBUG_IDFACTORY_INT*/
if( !m_bitset->test( o-MIN_OBJECT_ID ) ) return o;
#ifdef _DEBUG_IDFACTORY_INT
}
catch( std::exception& e )
{
printf( "std::exception: %s\n", e.what() );
printf( "o = %u (m_lastOid = %u, start_o = %u)\n", o, m_lastOid, start_o );
}
#endif /*_DEBUG_IDFACTORY_INT*/
o++;
if( o > MAX_OBJECT_ID ) o = MIN_OBJECT_ID;
if( o == start_o ) break; // search reached start point
}
return 0;
}
void IdFactory::fillObjectIdsFromDB()
{
MysqlConnection *con = GameServer::getInstance()->getDBConnection();
MysqlQuery q;
//"SELECT charId FROM characters"
//"SELECT object_id FROM items"
//"SELECT object_id FROM itemsonground
//"SELECT clan_id FROM clan_data"
q.create( L"SELECT charId FROM characters UNION SELECT object_id FROM items "
L"UNION SELECT object_id FROM itemsonground UNION SELECT clan_id FROM clan_data" );
if( con->executeQuery( q ) )
{
while( q.getNextRow() )
{
unsigned int oid = q.getFieldUInt( 0 );
fillObjectIdsFromDB_insertOne( oid );
}
}
else
{
LogError( L"IdFactory::fillObjectIdsFromDB(): MySQL ERROR: %s\n", con->getErrorStr() );
}
GameServer::getInstance()->releaseDBConnection( con );
}
void IdFactory::fillObjectIdsFromDB_insertOne( unsigned int oid )
{
if( oid >= IdFactory::MIN_OBJECT_ID && oid <= IdFactory::MAX_OBJECT_ID )
{
m_bitset->set( oid - IdFactory::MIN_OBJECT_ID );
}
else
LogError( L"IdFactory: RESTORE: oid [%u] is not between MIN_OBJECT_ID[%u] and MAX_OBJECT_ID[%u]!\n",
oid, IdFactory::MIN_OBJECT_ID, IdFactory::MAX_OBJECT_ID );
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include "l2c_utils.h"
// warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)
#pragma warning (disable: 4290)
class Exception;
class IdFactory
{
public:
static const unsigned int MIN_OBJECT_ID = 0x10000000;
static const unsigned int MAX_OBJECT_ID = 0x7FFFFFFF;
static const unsigned int OBJECT_ID_COUNT = MAX_OBJECT_ID - MIN_OBJECT_ID + 1;
public:
class NoFreeObjectIdException: public std::exception {};
public:
IdFactory();
~IdFactory();
bool init();
void clear();
public:
unsigned int getUsedCount();
unsigned int getNextObjectId() throw(NoFreeObjectIdException);
bool releaseObjectId( unsigned int o );
bool isUsedObjectId( unsigned int o );
public:
unsigned long long int getUsedMemoryBytes() const;
protected:
unsigned int findUnusedOid() const;
void fillObjectIdsFromDB();
void fillObjectIdsFromDB_insertOne( unsigned int oid );
protected:
std::bitset<OBJECT_ID_COUNT> *m_bitset;
unsigned long long int m_usedMemory;
CriticalSection m_lock;
unsigned int m_lastOid;
};

View File

@@ -0,0 +1,16 @@
#include "pch.h"
#include "Utils.h"
bool Utils_isValidCharName( const wchar_t *name )
{
if( !name ) return false;
size_t nl = wcslen( name );
if( nl < 3 ) return false;
size_t i = 0;
for( i=0; i<nl; i++ )
{
wchar_t c = name[i];
if( !iswalnum( c ) ) return false;
}
return true;
}

3
L2C_Server/utils/Utils.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
bool Utils_isValidCharName( const wchar_t *name );

View File

@@ -0,0 +1,59 @@
#include "pch.h"
#include "Log.h"
#include "GameObject.h"
GameObject::GameObject( unsigned int objectId )
{
m_objectId = objectId;
m_x = m_y = m_z = m_reflection = 0;
m_toString_buffer = NULL;
}
GameObject::GameObject( int x, int y, int z, int reflection, unsigned int objectId )
{
m_x = x; m_y = y; m_z = z;
m_reflection = reflection;
m_objectId = objectId;
m_toString_buffer = NULL;
}
GameObject::~GameObject()
{
if( m_toString_buffer )
{
free( m_toString_buffer );
m_toString_buffer = NULL;
}
}
void GameObject::LockObject()
{
m_lock.Lock();
}
void GameObject::LockRelease()
{
m_lock.Unlock();
}
void GameObject::setXYZ( int x, int y, int z )
{
m_x = x;
m_y = y;
m_z = z;
}
void GameObject::setReflection( int rId )
{
m_reflection = rId;
}
wchar_t *GameObject::toString() const
{
GameObject *non_const_this = const_cast<GameObject *>(this);
if( non_const_this->m_toString_buffer == NULL )
non_const_this->m_toString_buffer = (wchar_t *)malloc( 512 );
if( non_const_this->m_toString_buffer )
swprintf( non_const_this->m_toString_buffer, 255, L"ObjectID[%u] (%d,%d,%d,%d)", m_objectId, m_x, m_y, m_z, m_reflection );
return non_const_this->m_toString_buffer;
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include "l2c_utils.h"
class GameObject
{
public:
GameObject( unsigned int objectId );
GameObject( int x, int y, int z, int reflection, unsigned int objectId );
virtual ~GameObject();
public:
unsigned int getObjectId() const { return m_objectId; }
int getX() const { return m_x; }
int getY() const { return m_y; }
int getZ() const { return m_z; }
int getReflection() const { return m_reflection; }
public:
void setXYZ( int x, int y, int z );
void setReflection( int rId );
public:
virtual void LockObject();
virtual void LockRelease();
virtual wchar_t *toString() const;
protected:
unsigned int m_objectId;
int m_x;
int m_y;
int m_z;
int m_reflection;
wchar_t *m_toString_buffer;
CriticalSection m_lock;
};

View File

@@ -0,0 +1,43 @@
#include "pch.h"
#include "ClassId.h"
#include "ClassIdTree.h"
ClassId::ClassId( int id, const wchar_t *name, bool isMage, bool isSummoner, Race race, int parentId )
{
m_id = id; m_isMage = isMage; m_isSummoner = isSummoner;
m_race = race; m_parentId = parentId;
m_name[0] = 0;
if( name )
{
wcsncpy( m_name, name, sizeof(m_name)/sizeof(m_name[0]) );
m_name[sizeof(m_name)/sizeof(m_name[0])-1] = 0;
}
}
int ClassId::getId() const { return m_id; }
const wchar_t *ClassId::getName() const { return m_name; }
bool ClassId::isMage() const { return m_isMage; }
bool ClassId::isSummoner() const { return m_isSummoner; }
Race ClassId::getRace() const { return m_race; }
int ClassId::getParentId() const { return m_parentId; }
int ClassId::level() const
{
if( m_id != 0x87 && m_parentId == -1 ) return 0; // every non-Inspector without a parent is 0th prof
if( m_id == 0x87 ) return 2; // Inspector, has no parent, but is 2nd class
if( m_id == 0x88 ) return 3; // Judicator
return 1 + ClassIdTree::getInstance()->getParentClassId( m_id )->level();
}
bool ClassId::isChildOf( int classId ) const
{
if( m_parentId == -1 ) return false; // no parents...
if( m_parentId == classId ) return true; // direct child of
// maybe is parent's child?
return ClassIdTree::getInstance()->getClassId( m_parentId )->isChildOf( classId );
}
bool ClassId::isEqualOrChildOf( int classId ) const
{
return (m_id == classId) || isChildOf( classId );
}

View File

@@ -0,0 +1,112 @@
#pragma once
#include "Race.h"
class ClassId
{
public:
static const int CID_NONE = -1;
static const int CID_HUMAN_FIGHTER = 0x00;
static const int CID_WARRIOR = 0x01;
static const int CID_GLADIATOR = 0x02;
static const int CID_WARLORD = 0x03;
static const int CID_KNIGHT = 0x04;
static const int CID_PALLADIN = 0x05;
static const int CID_DARK_AVENGER = 0x06;
static const int CID_ROGUE = 0x07;
static const int CID_TREASURE_HUNTER = 0x08;
static const int CID_HAWKEYE = 0x09;
static const int CID_HUMAN_MAGE = 0x0A;
static const int CID_HUMAN_WIZARD = 0x0B;
static const int CID_SORCEROR = 0x0C;
static const int CID_NECROMANCER = 0x0D;
static const int CID_WARLOCK = 0x0E;
static const int CID_CLERIC = 0x0F;
static const int CID_BISHOP = 0x10;
static const int CID_PROPHET = 0x11;
static const int CID_ELVEN_FIGHTER = 0x12;
static const int CID_ELVEN_KNIGHT = 0x13;
static const int CID_TEMPLE_KNIGHT = 0x14;
static const int CID_SWORDSINGER = 0x15;
static const int CID_ELVEN_SCOUT = 0x16;
static const int CID_PLAINSWALKER = 0x17;
static const int CID_SILVER_RANGER = 0x18;
static const int CID_ELVEN_MAGE = 0x19;
static const int CID_ELVEN_WIZARD = 0x1A;
static const int CID_SPELLSINGER = 0x1B;
static const int CID_ELEMENTAL_SUMMONER = 0x1C;
static const int CID_ELVEN_ORACLE = 0x1D;
static const int CID_ELVEN_ELDER = 0x1E;
static const int CID_DARK_FIGHTER = 0x1F;
static const int CID_PALUS_KNIGHT = 0x20;
static const int CID_SHILLEN_KNIGHT = 0x21;
static const int CID_BLADEDANCER = 0x22;
static const int CID_ASSASIN = 0x23;
static const int CID_ABYSS_WALKER = 0x24;
static const int CID_PHANTOM_RANGER = 0x25;
static const int CID_DARK_MAGE = 0x26;
static const int CID_DARK_WIZARD = 0x27;
static const int CID_SPELLHOWLER = 0x28;
static const int CID_PHANTOM_SUMMONER = 0x29;
static const int CID_SHILLEN_ORACLE = 0x2A;
static const int CID_SHILLEN_ELDER = 0x2B;
static const int CID_ORC_FIGHTER = 0x2C;
static const int CID_ORC_RIDER = 0x2D;
static const int CID_DESTROYER = 0x2E;
static const int CID_ORC_MONK = 0x2F;
static const int CID_TYRANT = 0x30;
static const int CID_ORC_MAGE = 0x31;
static const int CID_ORC_SHAMAN = 0x32;
static const int CID_OVERLORD = 0x33;
static const int CID_WARCRYER = 0x34;
static const int CID_DWARVEN_FIGHTER = 0x35;
static const int CID_SCAVENGER = 0x36;
static const int CID_BOUNTY_HUNTER = 0x37;
static const int CID_ARTISAN = 0x38;
static const int CID_WARSMITH = 0x39;
static const int CID_DUELIST = 0x58;
static const int CID_KAMAEL_MALE_SOLDIER = 0x7B;
static const int CID_KAMAEL_FEMALE_SOLDIER = 0x7C;
static const int CID_TROOPER = 0x7D;
static const int CID_WARDER = 0x7E;
static const int CID_BERSERKER = 0x7F;
static const int CID_MALE_SOULBREAKER = 0x80;
static const int CID_FEMALE_SOULBREAKER = 0x81;
static const int CID_ARBALESTER = 0x82;
static const int CID_DOOMBRINGER = 0x83;
static const int CID_MALE_SOULHOUND = 0x84;
static const int CID_FEMALE_SOULHOUND = 0x85;
static const int CID_TRICKSTER = 0x86;
static const int CID_INSPECTOR = 0x87;
static const int CID_JUDICATOR = 0x88;
public:
ClassId( int id, const wchar_t *name, bool isMage, bool isSummoner, Race race, int parentId );
int getId() const;
const wchar_t *getName() const;
bool isMage() const;
bool isSummoner() const;
Race getRace() const;
int getParentId() const;
public:
// 0 - 0 profession, 1 - 1st profession, 2 - 2nd prof, 3 - 3rd prof
int level() const;
bool isChildOf( int classId ) const;
bool isEqualOrChildOf( int classId ) const;
protected:
int m_id;
wchar_t m_name[32];
bool m_isMage;
bool m_isSummoner;
Race m_race;
int m_parentId; // -1 - no parent class
};

View File

@@ -0,0 +1,196 @@
#include "pch.h"
#include "ClassIdTree.h"
// mem for static vars
ClassIdTree *ClassIdTree::_instance = NULL;
int ClassIdTree::_refCount = 0;
ClassIdTree *ClassIdTree::getInstance()
{
if( !_instance )
{
_instance = new ClassIdTree();
_refCount++;
}
return _instance;
}
void ClassIdTree::freeInstance()
{
if( _instance )
{
_refCount--;
if( _refCount == 0 )
{
delete _instance;
_instance = NULL;
}
}
}
ClassIdTree::ClassIdTree()
{
int i;
for( i=0; i<NUM_CLASS_IDS; i++ ) m_class[i] = NULL;
// add info about all classes
addClassId( 0x00, L"Fighter", false, false, RACE_Human, ClassId::CID_NONE );
addClassId( 0x01, L"Warrior", false, false, RACE_Human, ClassId::CID_HUMAN_FIGHTER );
addClassId( 0x02, L"Gladiator", false, false, RACE_Human, ClassId::CID_WARRIOR );
addClassId( 0x03, L"Warlord", false, false, RACE_Human, ClassId::CID_WARRIOR );
addClassId( 0x04, L"Knight", false, false, RACE_Human, ClassId::CID_HUMAN_FIGHTER );
addClassId( 0x05, L"Palladin", false, false, RACE_Human, ClassId::CID_KNIGHT );
addClassId( 0x06, L"Dark Avenger", false, false, RACE_Human, ClassId::CID_KNIGHT );
addClassId( 0x07, L"Rogue", false, false, RACE_Human, ClassId::CID_HUMAN_FIGHTER );
addClassId( 0x08, L"Treasure Hunter", false, false, RACE_Human, ClassId::CID_ROGUE );
addClassId( 0x09, L"Hawkeye", false, false, RACE_Human, ClassId::CID_ROGUE );
addClassId( 0x0A, L"Mage", true, false, RACE_Human, ClassId::CID_NONE );
addClassId( 0x0B, L"Wizard", true, false, RACE_Human, ClassId::CID_HUMAN_MAGE );
addClassId( 0x0C, L"Sorceror", true, false, RACE_Human, ClassId::CID_HUMAN_WIZARD );
addClassId( 0x0D, L"Necromancer", true, false, RACE_Human, ClassId::CID_HUMAN_WIZARD );
addClassId( 0x0E, L"Warlock", true, true, RACE_Human, ClassId::CID_HUMAN_WIZARD );
addClassId( 0x0F, L"Cleric", true, false, RACE_Human, ClassId::CID_HUMAN_MAGE );
addClassId( 0x10, L"Bishop", true, false, RACE_Human, ClassId::CID_CLERIC );
addClassId( 0x11, L"Prophet", true, false, RACE_Human, ClassId::CID_CLERIC );
addClassId( 0x12, L"Elven fighter", false, false, RACE_Elf, ClassId::CID_NONE );
addClassId( 0x13, L"Elven Knight", false, false, RACE_Elf, ClassId::CID_ELVEN_FIGHTER );
addClassId( 0x14, L"Temple Knight", false, false, RACE_Elf, ClassId::CID_ELVEN_KNIGHT );
addClassId( 0x15, L"Swordsinger", false, false, RACE_Elf, ClassId::CID_ELVEN_KNIGHT );
addClassId( 0x16, L"Elven Scout", false, false, RACE_Elf, ClassId::CID_ELVEN_FIGHTER );
addClassId( 0x17, L"Plains Walker", false, false, RACE_Elf, ClassId::CID_ELVEN_SCOUT );
addClassId( 0x18, L"Silver Ranger", false, false, RACE_Elf, ClassId::CID_ELVEN_SCOUT );
addClassId( 0x19, L"Elven Mage", true, false, RACE_Elf, ClassId::CID_NONE );
addClassId( 0x1A, L"Elven Wizard", true, false, RACE_Elf, ClassId::CID_ELVEN_MAGE );
addClassId( 0x1B, L"Spellsinger", true, false, RACE_Elf, ClassId::CID_ELVEN_WIZARD );
addClassId( 0x1C, L"Elemenal Summoner", true, true, RACE_Elf, ClassId::CID_ELVEN_WIZARD );
addClassId( 0x1D, L"Elven Oracle", true, false, RACE_Elf, ClassId::CID_ELVEN_MAGE );
addClassId( 0x1E, L"Elven Elder", true, false, RACE_Elf, ClassId::CID_ELVEN_ORACLE );
addClassId( 0x1F, L"Dark Fighter", false, false, RACE_DarkElf, ClassId::CID_NONE );
addClassId( 0x20, L"Palus Knight", false, false, RACE_DarkElf, ClassId::CID_DARK_FIGHTER );
addClassId( 0x21, L"Shillen Knight", false, false, RACE_DarkElf, ClassId::CID_PALUS_KNIGHT );
addClassId( 0x22, L"Bladedancer", false, false, RACE_DarkElf, ClassId::CID_PALUS_KNIGHT );
addClassId( 0x23, L"Assasin", false, false, RACE_DarkElf, ClassId::CID_DARK_FIGHTER );
addClassId( 0x24, L"Abyss Walker", false, false, RACE_DarkElf, ClassId::CID_ASSASIN );
addClassId( 0x25, L"Phantom Ranger", false, false, RACE_DarkElf, ClassId::CID_ASSASIN );
addClassId( 0x26, L"Dark Mage", true, false, RACE_DarkElf, ClassId::CID_NONE );
addClassId( 0x27, L"Dark Wizard", true, false, RACE_DarkElf, ClassId::CID_DARK_MAGE );
addClassId( 0x28, L"Spellhowler", true, false, RACE_DarkElf, ClassId::CID_DARK_WIZARD );
addClassId( 0x29, L"Phantom Summoner", true, true, RACE_DarkElf, ClassId::CID_DARK_WIZARD );
addClassId( 0x2A, L"Shillen Oracle", true, false, RACE_DarkElf, ClassId::CID_DARK_MAGE );
addClassId( 0x2B, L"Shillen ELder", true, false, RACE_DarkElf, ClassId::CID_SHILLEN_ORACLE );
addClassId( 0x2C, L"Orc Fighter", false, false, RACE_Orc, ClassId::CID_NONE );
addClassId( 0x2D, L"Orc Rider", false, false, RACE_Orc, ClassId::CID_ORC_FIGHTER );
addClassId( 0x2E, L"Destroyer", false, false, RACE_Orc, ClassId::CID_ORC_RIDER );
addClassId( 0x2F, L"Orc Monk", false, false, RACE_Orc, ClassId::CID_ORC_FIGHTER );
addClassId( 0x30, L"Tyrant", false, false, RACE_Orc, ClassId::CID_ORC_MONK );
addClassId( 0x31, L"Orc Mage", true, false, RACE_Orc, ClassId::CID_NONE );
addClassId( 0x32, L"Orc Shaman", true, false, RACE_Orc, ClassId::CID_ORC_MAGE );
addClassId( 0x33, L"Overlord", true, false, RACE_Orc, ClassId::CID_ORC_SHAMAN );
addClassId( 0x34, L"Warcryer", true, false, RACE_Orc, ClassId::CID_ORC_SHAMAN );
addClassId( 0x35, L"Dwarven Fighter", false, false, RACE_Dwarf, ClassId::CID_NONE );
addClassId( 0x36, L"Scavenger", false, false, RACE_Dwarf, ClassId::CID_DWARVEN_FIGHTER );
addClassId( 0x37, L"Bounty Hunter", false, false, RACE_Dwarf, ClassId::CID_SCAVENGER );
addClassId( 0x38, L"Artisan", false, false, RACE_Dwarf, ClassId::CID_DWARVEN_FIGHTER );
addClassId( 0x39, L"Warsmith", false, false, RACE_Dwarf, ClassId::CID_ARTISAN );
// 3rd prof
addClassId( 0x58, L"Duelist", false, false, RACE_Human, ClassId::CID_GLADIATOR );
addClassId( 0x59, L"Dreadnought", false, false, RACE_Human, ClassId::CID_WARLORD );
addClassId( 0x5A, L"Phoenix Knight", false, false, RACE_Human, ClassId::CID_PALLADIN );
addClassId( 0x5B, L"Hell Knight", false, false, RACE_Human, ClassId::CID_DARK_AVENGER );
addClassId( 0x5C, L"Sagittarius", false, false, RACE_Human, ClassId::CID_HAWKEYE );
addClassId( 0x5D, L"Adventurer", false, false, RACE_Human, ClassId::CID_TREASURE_HUNTER );
addClassId( 0x5E, L"Archmage", true, false, RACE_Human, ClassId::CID_SORCEROR );
addClassId( 0x5F, L"Soultaker", true, false, RACE_Human, ClassId::CID_NECROMANCER );
addClassId( 0x60, L"Arcana Lord", true, true, RACE_Human, ClassId::CID_WARLOCK );
addClassId( 0x61, L"Cardinal", true, false, RACE_Human, ClassId::CID_BISHOP );
addClassId( 0x62, L"Hierophant", true, false, RACE_Human, ClassId::CID_PROPHET );
addClassId( 0x63, L"Eva's Templar", false, false, RACE_Elf, ClassId::CID_TEMPLE_KNIGHT );
addClassId( 0x64, L"Sword Muse", false, false, RACE_Elf, ClassId::CID_SWORDSINGER );
addClassId( 0x65, L"Wind Rider", false, false, RACE_Elf, ClassId::CID_PLAINSWALKER );
addClassId( 0x66, L"Moonlight Sentinel", false, false, RACE_Elf, ClassId::CID_SILVER_RANGER );
addClassId( 0x67, L"Mustic Muse", true, false, RACE_Elf, ClassId::CID_SPELLSINGER );
addClassId( 0x68, L"Elemental Master", true, true, RACE_Elf, ClassId::CID_ELEMENTAL_SUMMONER );
addClassId( 0x69, L"Eva's Saint", true, false, RACE_Elf, ClassId::CID_ELVEN_ELDER );
addClassId( 0x6A, L"Shillen Templar", false, false, RACE_DarkElf, ClassId::CID_SHILLEN_KNIGHT );
addClassId( 0x6B, L"Spectral Dancer", false, false, RACE_DarkElf, ClassId::CID_BLADEDANCER );
addClassId( 0x6C, L"Ghost Hunter", false, false, RACE_DarkElf, ClassId::CID_ABYSS_WALKER );
addClassId( 0x6D, L"Ghost Sentinel", false, false, RACE_DarkElf, ClassId::CID_PHANTOM_RANGER );
addClassId( 0x6E, L"Storm Screamer", true, false, RACE_DarkElf, ClassId::CID_SPELLHOWLER );
addClassId( 0x6F, L"Spectral Master", true, true, RACE_DarkElf, ClassId::CID_PHANTOM_SUMMONER );
addClassId( 0x70, L"Shillen Saint", true, false, RACE_DarkElf, ClassId::CID_SHILLEN_ELDER );
addClassId( 0x71, L"Titan", false, false, RACE_Orc, ClassId::CID_DESTROYER );
addClassId( 0x72, L"Grand Khavatari", false, false, RACE_Orc, ClassId::CID_TYRANT );
addClassId( 0x73, L"Dominator", true, false, RACE_Orc, ClassId::CID_OVERLORD );
addClassId( 0x74, L"Doomcryer", true, false, RACE_Orc, ClassId::CID_WARCRYER );
addClassId( 0x75, L"Fortune Seeker", false, false, RACE_Dwarf, ClassId::CID_BOUNTY_HUNTER );
addClassId( 0x76, L"Maestro", false, false, RACE_Dwarf, ClassId::CID_WARSMITH );
addClassId( 0x7B, L"Kamael Male Soldier", false, false, RACE_Kamael, ClassId::CID_NONE );
addClassId( 0x7C, L"Kamael Female Soldier", false, false, RACE_Kamael, ClassId::CID_NONE );
addClassId( 0x7D, L"Trooper", false, false, RACE_Kamael, ClassId::CID_KAMAEL_MALE_SOLDIER );
addClassId( 0x7E, L"Warder", false, false, RACE_Kamael, ClassId::CID_KAMAEL_FEMALE_SOLDIER );
addClassId( 0x7F, L"Berserker", false, false, RACE_Kamael, ClassId::CID_TROOPER );
addClassId( 0x80, L"Male Soulbreaker", false, false, RACE_Kamael, ClassId::CID_TROOPER );
addClassId( 0x81, L"Female Soulbreaker", false, false, RACE_Kamael, ClassId::CID_WARDER );
addClassId( 0x82, L"Arbalester", false, false, RACE_Kamael, ClassId::CID_WARDER );
addClassId( 0x83, L"Doombringer", false, false, RACE_Kamael, ClassId::CID_BERSERKER );
addClassId( 0x84, L"Male Soulhound", false, false, RACE_Kamael, ClassId::CID_MALE_SOULBREAKER );
addClassId( 0x85, L"Female Soulhound", false, false, RACE_Kamael, ClassId::CID_FEMALE_SOULBREAKER );
addClassId( 0x86, L"Trickster", false, false, RACE_Kamael, ClassId::CID_ARBALESTER );
addClassId( 0x87, L"Inspector", false, false, RACE_Kamael, ClassId::CID_NONE ); // Hidden subclass, no first class which it comes from
addClassId( 0x88, L"Judicator", false, false, RACE_Kamael, ClassId::CID_INSPECTOR );
}
ClassIdTree::~ClassIdTree()
{
int i;
for( i=0; i<NUM_CLASS_IDS; i++ )
{
if( m_class[i] )
{
delete m_class[i];
m_class[i] = NULL;
}
}
}
void ClassIdTree::addClassId( int id, const wchar_t *name, bool isMage, bool isSummoner, Race race, int parentId )
{
if( (id>=0) && (id<NUM_CLASS_IDS) )
{
assert( m_class[id] == NULL );
if( m_class[id] == NULL )
m_class[id] = new ClassId( id, name, isMage, isSummoner, race, parentId );
}
}
const ClassId *ClassIdTree::getClassId( int class_id ) const
{
if( (class_id>=0) && (class_id<NUM_CLASS_IDS) ) return m_class[class_id];
return NULL;
}
const ClassId *ClassIdTree::getParentClassId( int class_id ) const
{
const ClassId *c = getClassId( class_id );
if( c ) return getClassId( c->getParentId() );
return NULL;
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Race.h"
#include "ClassId.h"
class ClassIdTree
{
protected:
static ClassIdTree *_instance;
static int _refCount;
public:
static ClassIdTree *getInstance();
static void freeInstance();
enum NumClassIds { NUM_CLASS_IDS = 137 };
protected:
ClassIdTree();
~ClassIdTree();
void addClassId( int id, const wchar_t *name, bool isMage, bool isSummoner, Race race, int parentId );
public:
const ClassId *getClassId( int class_id ) const;
const ClassId *getParentClassId( int class_id ) const;
protected:
// array to hold class tree info
ClassId *m_class[NUM_CLASS_IDS]; // 136 classes
};

View File

@@ -0,0 +1,11 @@
#pragma once
enum Race
{
RACE_Human,
RACE_Elf,
RACE_DarkElf,
RACE_Orc,
RACE_Dwarf,
RACE_Kamael
};

View File

@@ -0,0 +1,45 @@
#include "pch.h"
#include "GameCharacter.h"
GameCharacter::GameCharacter( unsigned int objectId ): GameObject( objectId )
{
m_name = NULL;
}
GameCharacter::~GameCharacter()
{
if( m_name )
{
free( m_name );
m_name = NULL;
}
GameObject::~GameObject();
}
void GameCharacter::setName( const wchar_t *name )
{
if( m_name ) free( m_name );
m_name = NULL;
if( name ) m_name = _wcsdup( name );
}
void GameCharacter::setLevel( int level )
{
m_level = level;
if( m_level < 0 ) m_level = 0;
}
const wchar_t *GameCharacter::getName() const
{
return (const wchar_t *)m_name;
}
wchar_t *GameCharacter::toString() const
{
GameCharacter *nc_this = const_cast<GameCharacter *>(this);
if( !nc_this->m_toString_buffer )
nc_this->m_toString_buffer = (wchar_t *)malloc( 512 );
if( nc_this->m_toString_buffer )
swprintf( nc_this->m_toString_buffer, 255, L"%s lvl %d [oid %u]", m_name, m_level, m_objectId );
return m_toString_buffer;
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../GameObject.h"
class GameCharacter: public GameObject
{
public:
GameCharacter( unsigned int objectId );
virtual ~GameCharacter();
public:
void setName( const wchar_t *name );
void setLevel( int level );
public:
const wchar_t *getName() const;
int getLevel() const { return m_level; }
public:
virtual wchar_t *toString() const;
protected:
wchar_t *m_name;
int m_level;
};

View File

@@ -0,0 +1,11 @@
#include "pch.h"
#include "GameNpc.h"
GameNpc::GameNpc( unsigned int objectId, int npcTemplateId ): GameCharacter( objectId )
{
m_npcTemplateId = npcTemplateId;
}
GameNpc::~GameNpc()
{
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "GameCharacter.h"
class GameNpc: public GameCharacter
{
public:
GameNpc( unsigned int objectId, int npcTemplateId );
virtual ~GameNpc();
public:
int getNpcTemplateId() const { return m_npcTemplateId; }
public:
void setNpcTemplateId( int tmplId ) { m_npcTemplateId = tmplId; }
protected:
int m_npcTemplateId;
};

View File

@@ -0,0 +1,19 @@
#include "pch.h"
#include "Log.h"
#include "../../../net/GameClient/GameClient.h"
#include "GamePlayer.h"
GamePlayer::GamePlayer( GameClient *clnt, unsigned int objectId ): GameCharacter( objectId )
{
m_gameClient = clnt;
}
GamePlayer::~GamePlayer()
{
m_gameClient = NULL;
}
GameClient *GamePlayer::getGameClient()
{
return m_gameClient;
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "GameCharacter.h"
class GameClient; // forward decl
class GamePlayer: public GameCharacter
{
public:
GamePlayer( GameClient *clnt, unsigned int objectId );
virtual ~GamePlayer();
public:
GameClient *getGameClient();
protected:
GameClient *m_gameClient;
};

View File

@@ -0,0 +1,385 @@
#include "pch.h"
#include "StatsSet.h"
#include "utils/Debugging.h"
#include "utils/Exception.h"
#include "l2c_utils.h"
StatsSet::StatsSet()
{
m_map.clear();
}
StatsSet::~StatsSet()
{
m_map.clear();
}
StatsSet::StatsSet( const StatsSet& other )
{
this->operator=( other );
}
const StatsSet& StatsSet::operator=( const StatsSet& other )
{
if( this == &other ) return (*this);
m_map = other.m_map;
return (*this);
}
bool StatsSet::getInt( const char *name, int *val )
{
std::wstring value;
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
throw Exception( "StatsSet: trying to get non-existent Int var [%s] with no default value!", name );
//return false; // warning C4702: unreachable code
}
value = iter->second;
int i = 0;
int r = swscanf( value.c_str(), L"%d", &i );
if( r == 1 )
{
(*val) = i;
return true;
}
throw Exception( "StatsSet.getInt: failed to scanf %%d from [%s]=[%S]", name, value.c_str() );
}
bool StatsSet::getInt( const char *name, int *val, int defVal )
{
std::wstring ws;
std::wstring ws_def(L"0");
getWString( name, ws, ws_def );
int v = 0;
int r = swscanf( ws.c_str(), L"%d", &v );
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
else (*val) = v;
return true;
}
bool StatsSet::getUInt( const char *name, unsigned int *val )
{
std::wstring value;
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
throw Exception( "StatsSet: trying to get non-existent UInt var [%s] with no default value!", name );
//return false; // warning C4702: unreachable code
}
value = iter->second;
unsigned int ui = 0;
int r = swscanf( value.c_str(), L"%u", &ui );
if( r == 1 )
{
(*val) = ui;
return true;
}
throw Exception( "StatsSet.getInt: failed to scanf %%u from [%s]=[%S]", name, value.c_str() );
}
bool StatsSet::getUInt( const char *name, unsigned int *val, unsigned int defVal )
{
std::wstring ws;
std::wstring ws_def(L"0");
getWString( name, ws, ws_def );
unsigned int v = 0;
int r = swscanf( ws.c_str(), L"%u", &v );
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
else (*val) = v;
return true;
}
bool StatsSet::getInt64( const char *name, long long int *val )
{
std::wstring value;
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
throw Exception( "StatsSet: trying to get non-existent Int64 var [%s] with no default value!", name );
//return false; // warning C4702: unreachable code
}
value = iter->second;
long long int i64 = 0;
int r = swscanf( value.c_str(), L"%I64d", &i64 );
if( r == 1 )
{
(*val) = i64;
return true;
}
throw Exception( "StatsSet.getInt: failed to scanf %%I64d from [%s]=[%S]", name, value.c_str() );
}
bool StatsSet::getInt64( const char *name, long long int *val, long long int defVal )
{
std::wstring ws;
std::wstring ws_def(L"0");
getWString( name, ws, ws_def );
long long int v = 0;
int r = swscanf( ws.c_str(), L"%I64d", &v );
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
else (*val) = v;
return true;
}
bool StatsSet::getUInt64( const char *name, unsigned long long int *val )
{
std::wstring value;
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
throw Exception( "StatsSet: trying to get non-existent UInt64 var [%s] with no default value!", name );
//return false; // warning C4702: unreachable code
}
value = iter->second;
unsigned long long int ui64 = 0;
int r = swscanf( value.c_str(), L"%I64u", &ui64 );
if( r == 1 )
{
(*val) = ui64;
return true;
}
throw Exception( "StatsSet.getInt: failed to scanf %%I64u from [%s]=[%S]", name, value.c_str() );
}
bool StatsSet::getUInt64( const char *name, unsigned long long int *val, unsigned long long int defVal )
{
std::wstring ws;
std::wstring ws_def(L"0");
getWString( name, ws, ws_def );
unsigned long long int v = 0;
int r = swscanf( ws.c_str(), L"%I64u", &v );
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
else (*val) = v;
return true;
}
bool StatsSet::getDouble( const char *name, double *val )
{
std::wstring value;
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
throw Exception( "StatsSet: trying to get non-existent Double var [%s] with no default value!", name );
//return false; // warning C4702: unreachable code
}
value = iter->second;
double lf = 0;
int r = swscanf( value.c_str(), L"%lf", &lf );
if( r == 1 )
{
(*val) = lf;
return true;
}
throw Exception( "StatsSet.getInt: failed to scanf %%lf from [%s]=[%S]", name, value.c_str() );
}
bool StatsSet::getDouble( const char *name, double *val, double defVal )
{
std::wstring ws;
std::wstring ws_def(L"0.0");
getWString( name, ws, ws_def );
double v = 0.0;
int r = swscanf( ws.c_str(), L"%lf", &v );
if( r <= 0 ) (*val) = defVal; // 0 tokens were read by swscanf or EOL reached unexpectedly
else (*val) = v;
return true;
}
bool StatsSet::getString( const char *name, std::string& val )
{
std::wstring ws;
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
throw Exception( "StatsSet: trying to get non-existent String var [%s] with no default value!", name );
//return false; // warning C4702: unreachable code
}
ws = iter->second;
const wchar_t *cws = ws.c_str(); // get wchar_t *
char *c_tmp = (char *)malloc( ws.size() + 16 ); // allocate space for Unicode -> ANSI convert
if( c_tmp )
{
l2c_unicode_to_ansi( cws, c_tmp, ws.size()+1 );
c_tmp[ws.size()] = 0;
val.assign( c_tmp, ws.size() );
free( c_tmp );
}
else val.assign( "malloc() failed" );
return true;
}
bool StatsSet::getString( const char *name, std::string& val, std::string& defVal )
{
std::wstring ws;
std::wstring ws_def( L"" );
getWString( name, ws, ws_def ); // always returns true
const wchar_t *cws = ws.c_str();
char *c_tmp = (char *)malloc( ws.size() + 16 );
if( c_tmp )
{
l2c_unicode_to_ansi( cws, c_tmp, ws.size()+1 );
c_tmp[ws.size()] = 0;
val.assign( c_tmp, ws.size() );
free( c_tmp );
}
else val = defVal;
return true;
}
bool StatsSet::getWString( const char *name, std::wstring& val )
{
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
throw Exception( "StatsSet: trying to get non-existent WString var [%s] with no default value!", name );
//return false; // warning C4702: unreachable code
}
val = iter->second;
return true;
}
bool StatsSet::getWString( const char *name, std::wstring& val, std::wstring& defVal )
{
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
val = defVal;
return true;
}
val = iter->second;
return true;
}
bool StatsSet::getBool( const char *name, bool *val )
{
std::wstring value;
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
throw Exception( "StatsSet: trying to get non-existent Bool var [%s] with no default value!", name );
//return false; // warning C4702: unreachable code
}
value = iter->second;
int i = 0;
swscanf( value.c_str(), L"%d", &i );
(*val) = (bool)(i != 0);
return true;
}
bool StatsSet::getBool( const char *name, bool *val, bool defVal )
{
std::wstring value;
std::string sname( name );
std::map<std::string, std::wstring>::const_iterator iter = m_map.find( sname );
if( iter == m_map.end() )
{
(*val) = defVal;
return true;
}
value = iter->second;
int i = 0;
swscanf( value.c_str(), L"%d", &i );
(*val) = (bool)(i != 0);
return true; // defVal funcs never return false and never throw exceptions
}
bool StatsSet::setInt( const char *name, int val )
{
std::string sname( name );
wchar_t wval[32];
swprintf( wval, 31, L"%d", val );
std::wstring v( wval );
m_map[name] = v;
return true;
}
bool StatsSet::setUInt( const char *name, unsigned int val )
{
std::string sname( name );
wchar_t wval[32];
swprintf( wval, 31, L"%u", val );
std::wstring v( wval );
m_map[name] = v;
return true;
}
bool StatsSet::setInt64( const char *name, long long int val )
{
std::string sname( name );
wchar_t wval[32];
swprintf( wval, 31, L"%I64d", val );
std::wstring v( wval );
m_map[name] = v;
return true;
}
bool StatsSet::setUInt64( const char *name, unsigned long long int val )
{
std::string sname( name );
wchar_t wval[32];
swprintf( wval, 31, L"%I64u", val );
std::wstring v( wval );
m_map[name] = v;
return true;
}
bool StatsSet::setDouble( const char *name, double val )
{
std::string sname( name );
wchar_t wval[32];
swprintf( wval, 31, L"%0.20f", val );
std::wstring v( wval );
m_map[name] = v;
return true;
}
bool StatsSet::setString( const char *name, std::string& val )
{
std::string sname( name );
wchar_t *wval = (wchar_t *)malloc( (val.size()+16) * sizeof(wchar_t) );
if( wval )
{
swprintf( wval, val.size()+1, L"%S", val.c_str() );
std::wstring v( wval );
m_map[sname] = v;
free( wval );
return true;
}
return false;
}
bool StatsSet::setString( const char *name, const char *val )
{
std::string sval( val );
return setString( name, sval );
}
bool StatsSet::setWString( const char *name, const wchar_t *val )
{
std::wstring sval( val );
return setWString( name, sval );
}
bool StatsSet::setWString( const char *name, std::wstring& val )
{
std::string sname( name );
m_map[sname] = val;
return true;
}
bool StatsSet::setBool( const char *name, bool val )
{
return setInt( name, (int)val );
}

View File

@@ -0,0 +1,44 @@
#pragma once
class StatsSet
{
public:
StatsSet();
~StatsSet();
StatsSet( const StatsSet& other );
const StatsSet& operator=( const StatsSet& other );
public:
bool getInt( const char *name, int *val );
bool getInt( const char *name, int *val, int defVal );
bool getUInt( const char *name, unsigned int *val );
bool getUInt( const char *name, unsigned int *val, unsigned int defVal );
bool getInt64( const char *name, long long int *val );
bool getInt64( const char *name, long long int *val, long long int defVal );
bool getUInt64( const char *name, unsigned long long int *val );
bool getUInt64( const char *name, unsigned long long int *val, unsigned long long int defVal );
bool getDouble( const char *name, double *val );
bool getDouble( const char *name, double *val, double defVal );
bool getString( const char *name, std::string& val );
bool getString( const char *name, std::string& val, std::string& defVal );
bool getWString( const char *name, std::wstring& val );
bool getWString( const char *name, std::wstring& val, std::wstring& defVal );
bool getBool( const char *name, bool *val );
bool getBool( const char *name, bool *val, bool defVal );
public:
bool setInt( const char *name, int val );
bool setUInt( const char *name, unsigned int val );
bool setInt64( const char *name, long long int val );
bool setUInt64( const char *name, unsigned long long int val );
bool setDouble( const char *name, double val );
bool setString( const char *name, std::string& val );
bool setString( const char *name, const char *val );
bool setWString( const char *name, std::wstring& val );
bool setWString( const char *name, const wchar_t *val );
bool setBool( const char *name, bool val );
protected:
// map of pair: <"name", L"value">
std::map<std::string, std::wstring> m_map;
};

View File

@@ -0,0 +1,79 @@
#include "pch.h"
#include "L2CharTemplate.h"
L2CharTemplate::L2CharTemplate( StatsSet& set )
{
// Base stats
set.getInt( "baseSTR", &baseSTR );
set.getInt( "baseCON", &baseCON );
set.getInt( "baseDEX", &baseDEX );
set.getInt( "baseINT", &baseINT );
set.getInt( "baseWIT", &baseWIT );
set.getInt( "baseMEN", &baseMEN );
set.getDouble( "baseHpMax", &baseHpMax );
set.getDouble( "baseCpMax", &baseCpMax );
set.getDouble( "baseMpMax", &baseMpMax );
set.getDouble( "baseHpReg", &baseHpReg );
set.getDouble( "baseMpReg", &baseMpReg );
set.getInt( "basePAtk", &basePAtk );
set.getInt( "baseMAtk", &baseMAtk );
set.getInt( "basePDef", &basePDef );
set.getInt( "baseMDef", &baseMDef );
set.getInt( "basePAtkSpd", &basePAtkSpd );
set.getInt( "baseMAtkSpd", &baseMAtkSpd );
set.getDouble( "baseMReuseDelay", &baseMReuseRate, 1.0 );
set.getInt( "baseShldDef", &baseShldDef );
set.getInt( "baseAtkRange", &baseAtkRange );
set.getInt( "baseShldRate", &baseShldRate );
set.getInt( "baseCritRate", &baseCritRate );
set.getInt( "baseMCritRate", &baseMCritRate, 80 ); // CT2: The magic critical rate has been increased to 10 times.
set.getInt( "baseWalkSpd", &baseWalkSpd );
set.getInt( "baseRunSpd", &baseRunSpd );
// missed base stats
set.getInt( "baseAccuracy", &baseAccuracy );
set.getInt( "baseEvasion", &baseEvasion );
// SpecialStats
set.getInt( "baseBreath", &baseBreath, 100 );
set.getInt( "baseAggression", &baseAggression, 0 );
set.getInt( "baseBleed", &baseBleed, 0 );
set.getInt( "basePoison", &basePoison, 0 );
set.getInt( "baseStun", &baseStun, 0 );
set.getInt( "baseRoot", &baseRoot, 0 );
set.getInt( "baseMovement", &baseMovement, 0 );
set.getInt( "baseConfusion", &baseConfusion, 0 );
set.getInt( "baseSleep", &baseSleep, 0 );
set.getInt( "baseFire", &baseFire, 0 );
set.getInt( "baseWind", &baseWind, 0 );
set.getInt( "baseWater", &baseWater, 0 );
set.getInt( "baseEarth", &baseEarth, 0 );
set.getInt( "baseHoly", &baseHoly, 0 );
set.getInt( "baseDark", &baseDark, 0 );
set.getDouble( "baseAaggressionVuln", &baseAggressionVuln, 1.0 );
set.getDouble( "baseBleedVuln", &baseBleedVuln, 1.0 );
set.getDouble( "basePoisonVuln", &basePoisonVuln, 1.0 );
set.getDouble( "baseStunVuln", &baseStunVuln, 1.0 );
set.getDouble( "baseRootVuln", &baseRootVuln, 1.0 );
set.getDouble( "baseMovementVuln", &baseMovementVuln, 1.0 );
set.getDouble( "baseConfusionVuln", &baseConfusionVuln, 1.0 );
set.getDouble( "baseSleepVuln", &baseSleepVuln, 1.0 );
set.getDouble( "baseFireRes", &baseFireRes, 0.0 );
set.getDouble( "baseWindRes", &baseWindRes, 0.0 );
set.getDouble( "baseWaterRes", &baseWaterRes, 0.0 );
set.getDouble( "baseEarthRes", &baseEarthRes, 0.0 );
set.getDouble( "baseHolyRes", &baseHolyRes, 0.0 );
set.getDouble( "baseDarkRes", &baseDarkRes, 0.0 );
set.getDouble( "baseCritVuln", &baseCritVuln, 1.0 );
// undead? default NO
set.getBool( "isUndead", &isUndead, false );
//C4 Stats
set.getInt( "baseMpConsumeRate", &baseMpConsumeRate, 0 );
set.getInt( "baseHpConsumeRate", &baseHpConsumeRate, 0 );
// Geometry
set.getInt( "collision_radius", &collisionRadius, 10 );
set.getInt( "collision_height", &collisionHeight, 10 );
}

View File

@@ -0,0 +1,81 @@
#pragma once
#include "world/templates/StatsSet.h"
class L2CharTemplate
{
public:
L2CharTemplate( StatsSet& set );
public:
// BaseStats
int baseSTR;
int baseCON;
int baseDEX;
int baseINT;
int baseWIT;
int baseMEN;
double baseHpMax;
double baseCpMax;
double baseMpMax;
double baseHpReg;
double baseMpReg;
int basePAtk;
int baseMAtk;
int basePDef;
int baseMDef;
int basePAtkSpd;
int baseMAtkSpd;
double baseMReuseRate;
int baseShldDef;
int baseAtkRange;
int baseShldRate;
int baseCritRate;
int baseMCritRate;
int baseWalkSpd;
int baseRunSpd;
// missed base stats
int baseAccuracy;
int baseEvasion;
// SpecialStats
int baseBreath;
int baseAggression;
int baseBleed;
int basePoison;
int baseStun;
int baseRoot;
int baseMovement;
int baseConfusion;
int baseSleep;
int baseFire;
int baseWind;
int baseWater;
int baseEarth;
int baseHoly;
int baseDark;
double baseAggressionVuln;
double baseBleedVuln;
double basePoisonVuln;
double baseStunVuln;
double baseRootVuln;
double baseMovementVuln;
double baseConfusionVuln;
double baseSleepVuln;
double baseFireRes;
double baseWindRes;
double baseWaterRes;
double baseEarthRes;
double baseHolyRes;
double baseDarkRes;
double baseCritVuln;
bool isUndead;
//C4 Stats
int baseMpConsumeRate;
int baseHpConsumeRate;
int collisionRadius;
int collisionHeight;
};

View File

@@ -0,0 +1,83 @@
#include "pch.h"
#include "L2PlayerTemplate.h"
L2PlayerTemplate::L2PlayerTemplate( StatsSet& set ): L2CharTemplate( set )
{
set.getInt( "classId", &iClassId );
classId = ClassIdTree::getInstance()->getClassId( iClassId );
set.getInt( "raceId", (int *)&race );
set.getInt( "spawnX", &spawnX );
set.getInt( "spawnY", &spawnY );
set.getInt( "spawnZ", &spawnZ );
set.getInt( "classBaseLevel", &classBaseLevel );
set.getDouble( "lvlHpAdd", &lvlHpAdd );
set.getDouble( "lvlHpMod", &lvlHpMod );
set.getDouble( "lvlCpAdd", &lvlCpAdd );
set.getDouble( "lvlCpMod", &lvlCpMod );
set.getDouble( "lvlMpAdd", &lvlMpAdd );
set.getDouble( "lvlMpMod", &lvlMpMod );
// missed loaded vars
set.getInt( "baseLoad", &baseLoad, 62500 );
set.getBool( "canCraft", &canCraft, false );
}
void L2PlayerTemplate::addItem( int itemId, int amount, bool equipped )
{
PcTemplateItem it( itemId, amount, equipped );
m_items.push_back( it );
}
const std::list<L2PlayerTemplate::PcTemplateItem>& L2PlayerTemplate::getItems() const
{
return m_items;
}
L2PlayerTemplate::PcTemplateItem::PcTemplateItem()
{
m_itemId = 0;
m_amount = 0;
m_equipped = false;
}
L2PlayerTemplate::PcTemplateItem::PcTemplateItem( int itemId, int amount, bool equipped )
{
m_itemId = itemId;
m_amount = amount;
m_equipped = equipped;
}
L2PlayerTemplate::PcTemplateItem::PcTemplateItem( const L2PlayerTemplate::PcTemplateItem& other )
{
this->operator=( other );
}
const L2PlayerTemplate::PcTemplateItem& L2PlayerTemplate::PcTemplateItem::operator=( const L2PlayerTemplate::PcTemplateItem& other )
{
if( this == &other ) return (*this);
m_itemId = other.m_itemId;
m_amount = other.m_amount;
m_equipped = other.m_equipped;
return (*this);
}
int L2PlayerTemplate::PcTemplateItem::getItemId() const
{
return m_itemId;
}
int L2PlayerTemplate::PcTemplateItem::getAmount() const
{
return m_amount;
}
bool L2PlayerTemplate::PcTemplateItem::isEquipped() const
{
return m_equipped;
}

View File

@@ -0,0 +1,53 @@
#pragma once
#include "L2CharTemplate.h"
#include "world/model/base/ClassId.h"
#include "world/model/base/ClassIdTree.h"
#include "world/model/base/Race.h"
class L2PlayerTemplate: public L2CharTemplate
{
public: // class for template item
class PcTemplateItem
{
public:
PcTemplateItem();
PcTemplateItem( int itemId, int amount, bool equipped );
PcTemplateItem( const PcTemplateItem& other );
const PcTemplateItem& operator=( const PcTemplateItem& other );
int getItemId() const;
int getAmount() const;
bool isEquipped() const;
protected:
int m_itemId;
int m_amount;
bool m_equipped;
};
public:
L2PlayerTemplate( StatsSet& set );
void addItem( int itemId, int amount, bool equipped );
const std::list<L2PlayerTemplate::PcTemplateItem>& getItems() const;
public:
int iClassId;
const ClassId *classId;
Race race;
int spawnX;
int spawnY;
int spawnZ;
int classBaseLevel;
double lvlHpAdd;
double lvlHpMod;
double lvlCpAdd;
double lvlCpMod;
double lvlMpAdd;
double lvlMpMod;
int baseLoad;
bool canCraft;
protected:
std::list<L2PlayerTemplate::PcTemplateItem> m_items;
};

View File

@@ -0,0 +1,23 @@
#include "pch.h"
#include "L2ArmorTemplate.h"
#include "l2c_utils.h"
L2ArmorTemplate::L2ArmorTemplate( L2ItemSubType subType, StatsSet& set ):
L2ItemTemplate( TYPE_ARMOR, subType, set )
{
set.getInt( "avoid_modify", &m_avoidModifier );
set.getInt( "p_def", &m_pDef, 0 );
set.getInt( "m_def", &m_mDef, 0 );
set.getInt( "mp_bonus", &m_mpBonus, 0 );
set.getInt( "hp_bonus", &m_hpBonus, 0 );
m_enchant4Skill = 0;
std::string stds;
set.getString( "enchant4_skill", stds );
//String[] skill = set.getString("enchant4_skill").split("-");
//_skill = set.getString("skill").split(";");
set.getString( "skill", stds );
}
L2ArmorTemplate::~L2ArmorTemplate()
{
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include "L2ItemTemplate.h"
class L2ArmorTemplate: public L2ItemTemplate
{
public:
L2ArmorTemplate( L2ItemSubType subType, StatsSet& set );
virtual ~L2ArmorTemplate();
public:
int getAvoidModifier() const { return m_avoidModifier; }
int getPDef() const { return m_pDef; }
int getMDef() const { return m_mDef; }
int getMPBonus() const { return m_mpBonus; }
int getHPBonus() const { return m_hpBonus; }
int getEnchant4Skill() const { return m_enchant4Skill; }
protected:
int m_avoidModifier;
int m_pDef;
int m_mDef;
int m_mpBonus;
int m_hpBonus;
unsigned int m_enchant4Skill; // skill that activates when armor is enchanted +4
};

View File

@@ -0,0 +1,13 @@
#include "pch.h"
#include "L2EtcItemTemplate.h"
#include "l2c_utils.h"
L2EtcItemTemplate::L2EtcItemTemplate( L2ItemSubType subType, StatsSet& set ):
L2ItemTemplate( TYPE_ETCITEM, subType, set )
{
set.getString( "handler", m_handler );
}
L2EtcItemTemplate::~L2EtcItemTemplate()
{
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "L2ItemTemplate.h"
class L2EtcItemTemplate: public L2ItemTemplate
{
public:
L2EtcItemTemplate( L2ItemSubType subType, StatsSet& set );
virtual ~L2EtcItemTemplate();
public:
const char *getHandler() const { return m_handler.c_str(); }
protected:
std::string m_handler;
// TODO: skills?
};

View File

@@ -0,0 +1,191 @@
#include "pch.h"
#include "L2ItemTemplate.h"
#include "l2c_utils.h"
#include "Log.h"
L2ItemTemplate::L2ItemTemplate( L2ItemType itemType, L2ItemSubType subType, StatsSet& set )
{
m_type = itemType;
m_subType = subType;
int temp_i;
// id
set.getInt( "item_id", &m_itemId );
// name
std::wstring wstr_name;
set.getWString( "name", wstr_name );
m_name = NULL;
if( wstr_name.length() > 0 )
m_name = _wcsdup( wstr_name.c_str() );
set.getInt( "type1", &m_type1, 0 );
set.getInt( "type2", &m_type2, 0 );
set.getInt( "weight", &m_weight, 0 );
set.getBool( "crystallizable", &m_crystallizable, false );
set.getBool( "stackable", &m_stackable, false );
set.getInt( "material", (int *)&m_materialType );
set.getInt( "crystal_type", (int *)&m_crystalType, (int)CRYSTAL_NONE ); // default to none-grade
set.getInt( "duration", &m_duration ); // default 0 or -1?
set.getInt( "time", &m_time ); // default seems to be -1
set.getInt( "bodypart", &temp_i );
m_bodyPart = (L2ItemSlot)temp_i;
set.getInt( "price", &m_referencePrice );
set.getInt( "crystal_count", &m_crystalCount, 0 );
set.getBool( "sellable", &m_sellable, true );
set.getBool( "dropable", &m_dropable, true );
set.getBool( "destroyable", &m_destroyable, true );
set.getBool( "tradeable", &m_tradeable, true );
// checks by item id
m_common = (m_itemId >= 12006 && m_itemId <= 12361) || (m_itemId >= 11605 && m_itemId <= 12308);
m_heroItem = (m_itemId >= 6611 && m_itemId <= 6621) || (m_itemId >= 9388 && m_itemId <= 9390) || m_itemId == 6842;
m_pvpItem = (m_itemId >= 10667 && m_itemId <= 10792) || (m_itemId >= 10793 && m_itemId <= 10835) || (m_itemId >= 12852 && m_itemId <= 12977) || (m_itemId >= 14363 && m_itemId <= 14519) || (m_itemId >= 14520 && m_itemId <= 14525) || m_itemId == 14528 || m_itemId == 14529 || m_itemId == 14558;
}
L2ItemTemplate::~L2ItemTemplate()
{
if( m_name )
{
free( m_name );
m_name = NULL;
}
}
L2ItemSubType L2ItemTemplate::getArmorTypeByName( const char *name )
{
L2ItemSubType ret = SYBTYPE_INCORRECT;
if( !name ) return ret;
if( _stricmp( name, "none" ) == 0 ) ret = ARMOR_NONE;
else if( _stricmp( name, "light" ) == 0 ) ret = ARMOR_LIGHT;
else if( _stricmp( name, "heavy" ) == 0 ) ret = ARMOR_HEAVY;
else if( _stricmp( name, "magic" ) == 0 ) ret = ARMOR_MAGIC;
else if( _stricmp( name, "pet" ) == 0 ) ret = ARMOR_PET;
else if( _stricmp( name, "sigil" ) == 0 ) ret = ARMOR_SIGIL;
if( ret == SYBTYPE_INCORRECT )
LogWarning( L"L2ItemTemplate::getArmorTypeByName(): unknown name [%S]", name );
return ret;
}
L2ItemSubType L2ItemTemplate::getWeaponTypeByName( const char *name )
{
L2ItemSubType ret = SYBTYPE_INCORRECT;
if( !name ) return ret;
if( _stricmp( name, "none" ) == 0 ) ret = WEAPON_NONE; // shield
else if( _stricmp( name, "sword" ) == 0 ) ret = WEAPON_SWORD;
else if( _stricmp( name, "blunt" ) == 0 ) ret = WEAPON_BLUNT;
else if( _stricmp( name, "dagger" ) == 0 ) ret = WEAPON_DAGGER;
else if( _stricmp( name, "bow" ) == 0 ) ret = WEAPON_BOW;
else if( _stricmp( name, "pole" ) == 0 ) ret = WEAPON_POLE;
else if( _stricmp( name, "etc" ) == 0 ) ret = WEAPON_ETC;
else if( _stricmp( name, "fist" ) == 0 ) ret = WEAPON_FIST;
else if( _stricmp( name, "dual" ) == 0 ) ret = WEAPON_DUAL;
else if( _stricmp( name, "dualfist" ) == 0 ) ret = WEAPON_DUALFIST;
else if( _stricmp( name, "bigsword" ) == 0 ) ret = WEAPON_BIGSWORD;
else if( _stricmp( name, "pet" ) == 0 ) ret = WEAPON_PET;
else if( _stricmp( name, "rod" ) == 0 ) ret = WEAPON_ROD;
else if( _stricmp( name, "bigblunt" ) == 0 ) ret = WEAPON_BIGBLUNT;
else if( _stricmp( name, "ancient" ) == 0 ) ret = WEAPON_ANCIENT_SWORD;
else if( _stricmp( name, "crossbow" ) == 0 ) ret = WEAPON_CROSSBOW;
else if( _stricmp( name, "rapier" ) == 0 ) ret = WEAPON_RAPIER;
else if( _stricmp( name, "dualdagger" ) == 0 ) ret = WEAPON_DUAL_DAGGER;
if( ret == SYBTYPE_INCORRECT )
LogWarning( L"L2ItemTemplate::getWeaponTypeByName(): unknown name [%S]", name );
return ret;
}
L2ItemSlot L2ItemTemplate::getSlotByName( const char *name )
{
if( !name ) return SLOT_NONE;
L2ItemSlot ret = SLOT_NONE;
//
if( _stricmp( name, "shirt" ) == 0 ) ret = SLOT_UNDERWEAR;
else if( _stricmp( name, "lbracelet" ) == 0 ) ret = SLOT_L_BRACELET;
else if( _stricmp( name, "rbracelet" ) == 0 ) ret = SLOT_R_BRACELET;
else if( _stricmp( name, "talisman" ) == 0 ) ret = SLOT_DECO;
else if( _stricmp( name, "chest" ) == 0 ) ret = SLOT_CHEST;
else if( _stricmp( name, "fullarmor" ) == 0 ) ret = SLOT_FULL_ARMOR;
else if( _stricmp( name, "head" ) == 0 ) ret = SLOT_HEAD;
else if( _stricmp( name, "hair" ) == 0 ) ret = SLOT_HAIR;
else if( _stricmp( name, "face" ) == 0 ) ret = SLOT_HAIR2;
else if( _stricmp( name, "hair2" ) == 0 ) ret = SLOT_HAIR2;
else if( _stricmp( name, "dhair" ) == 0 ) ret = SLOT_HAIRALL;
else if( _stricmp( name, "hairall" ) == 0 ) ret = SLOT_HAIRALL;
else if( _stricmp( name, "underwear" ) == 0 ) ret = SLOT_UNDERWEAR;
else if( _stricmp( name, "back" ) == 0 ) ret = SLOT_BACK;
else if( _stricmp( name, "neck" ) == 0 ) ret = SLOT_NECK;
else if( _stricmp( name, "legs" ) == 0 ) ret = SLOT_LEGS;
else if( _stricmp( name, "feet" ) == 0 ) ret = SLOT_FEET;
else if( _stricmp( name, "gloves" ) == 0 ) ret = SLOT_GLOVES;
//else if( _stricmp( name, "chest,legs" ) == 0 ) ret = SLOT_CHEST | SLOT_LEGS;
else if( _stricmp( name, "belt" ) == 0 ) ret = SLOT_BELT;
else if( _stricmp( name, "rhand" ) == 0 ) ret = SLOT_R_HAND;
else if( _stricmp( name, "lhand" ) == 0 ) ret = SLOT_L_HAND;
else if( _stricmp( name, "lrhand" ) == 0 ) ret = SLOT_LR_HAND;
//else if( _stricmp( name, "rear,lear" ) == 0 ) ret = SLOT_R_EAR | SLOT_L_EAR;
//else if( _stricmp( name, "rfinger,lfinger" ) == 0 ) ret = SLOT_R_FINGER | SLOT_L_FINGER;
else if( _stricmp( name, "wolf" ) == 0 ) ret = SLOT_WOLF;
else if( _stricmp( name, "greatwolf" ) == 0 ) ret = SLOT_GREATWOLF;
else if( _stricmp( name, "hatchling" ) == 0 ) ret = SLOT_HATCHLING;
else if( _stricmp( name, "strider" ) == 0 ) ret = SLOT_STRIDER;
else if( _stricmp( name, "babypet" ) == 0 ) ret = SLOT_BABYPET;
else if( _stricmp( name, "none" ) == 0 ) ret = SLOT_NONE;
//
if( ret == -1 )
{
LogError( L"L2ItemTemplate::getSlotIdByName(): unknown bodypart slot name [%S]", name );
return SLOT_NONE;
}
return ret;
}
L2ItemMaterial L2ItemTemplate::getMaterialByName( const char *name )
{
if( !name ) return MATERIAL_NONE;
L2ItemMaterial ret = MATERIAL_NONE;
if( _stricmp( name, "steel" ) == 0 ) ret = MATERIAL_STEEL;
else if( _stricmp( name, "fine_steel" ) == 0 ) ret = MATERIAL_FINE_STEEL;
else if( _stricmp( name, "blood_steel" ) == 0 ) ret = MATERIAL_BLOOD_STEEL;
else if( _stricmp( name, "bronze" ) == 0 ) ret = MATERIAL_BRONZE;
else if( _stricmp( name, "silver" ) == 0 ) ret = MATERIAL_SILVER;
else if( _stricmp( name, "gold" ) == 0 ) ret = MATERIAL_GOLD;
else if( _stricmp( name, "mithril" ) == 0 ) ret = MATERIAL_MITHRIL;
else if( _stricmp( name, "oriharukon" ) == 0 ) ret = MATERIAL_ORIHARUKON;
else if( _stricmp( name, "paper" ) == 0 ) ret = MATERIAL_PAPER;
else if( _stricmp( name, "wood" ) == 0 ) ret = MATERIAL_WOOD;
else if( _stricmp( name, "cloth" ) == 0 ) ret = MATERIAL_CLOTH;
else if( _stricmp( name, "leather" ) == 0 ) ret = MATERIAL_LEATHER;
else if( _stricmp( name, "bone" ) == 0 ) ret = MATERIAL_BONE;
else if( _stricmp( name, "horn" ) == 0 ) ret = MATERIAL_HORN;
else if( _stricmp( name, "damascus" ) == 0 ) ret = MATERIAL_DAMASCUS;
else if( _stricmp( name, "adamantaite" ) == 0 ) ret = MATERIAL_ADAMANTAITE;
else if( _stricmp( name, "chrysolite" ) == 0 ) ret = MATERIAL_CHRYSOLITE;
else if( _stricmp( name, "crystal" ) == 0 ) ret = MATERIAL_CRYSTAL;
else if( _stricmp( name, "liquid" ) == 0 ) ret = MATERIAL_LIQUID;
else if( _stricmp( name, "scale_of_dragon" ) == 0 ) ret = MATERIAL_SCALE_OF_DRAGON;
else if( _stricmp( name, "dyestuff" ) == 0 ) ret = MATERIAL_DYESTUFF;
else if( _stricmp( name, "cobweb" ) == 0 ) ret = MATERIAL_COBWEB;
else if( _stricmp( name, "seed" ) == 0 ) ret = MATERIAL_SEED;
else if( _stricmp( name, "cotton" ) == 0 ) ret = MATERIAL_COTTON;
else
{
LogError( L"L2ItemTemplate::getMaterialIdByName(): unknown material name [%S]", name );
}
return ret;
}
L2ItemCrystal L2ItemTemplate::getCrystalTypeByName( const char *name )
{
if( !name ) return CRYSTAL_NONE;
L2ItemCrystal ret = CRYSTAL_NONE;
if( _stricmp( name, "none" ) == 0 ) ret = CRYSTAL_NONE;
else if( _stricmp( name, "a" ) == 0 ) ret = CRYSTAL_A;
else if( _stricmp( name, "b" ) == 0 ) ret = CRYSTAL_B;
else if( _stricmp( name, "c" ) == 0 ) ret = CRYSTAL_C;
else if( _stricmp( name, "d" ) == 0 ) ret = CRYSTAL_D;
else if( _stricmp( name, "s" ) == 0 ) ret = CRYSTAL_S;
else if( _stricmp( name, "s80" ) == 0 ) ret = CRYSTAL_S80;
else if( _stricmp( name, "s84" ) == 0 ) ret = CRYSTAL_S84;
else
{
LogError( L"L2ItemTemplate::getCrystalTypeByName(): unknown crystal name [%S]", name );
}
return ret;
}

View File

@@ -0,0 +1,98 @@
#pragma once
#include "L2ItemType.h"
#include "../StatsSet.h"
class L2ItemTemplate
{
public:
static const int TYPE1_WEAPON_RING_EARRING_NECKLACE = 0;
static const int TYPE1_SHIELD_ARMOR = 1;
static const int TYPE1_ITEM_QUESTITEM_ADENA = 4;
static const int TYPE2_WEAPON = 0;
static const int TYPE2_SHIELD_ARMOR = 1;
static const int TYPE2_ACCESSORY = 2;
static const int TYPE2_QUEST = 3;
static const int TYPE2_MONEY = 4;
static const int TYPE2_OTHER = 5;
static const int TYPE2_PET_WOLF = 6;
static const int TYPE2_PET_HATCHLING = 7;
static const int TYPE2_PET_STRIDER = 8;
static const int TYPE2_PET_BABY = 9;
static const int TYPE2_PET_EVOLVEDWOLF = 10;
protected:
//static const int crystalItemId[] = { 0, 1458, 1459, 1460, 1461, 1462, 1462, 1462 };
//static const int crystalEnchantBonusArmor[] = { 0, 11, 6, 11, 19, 25, 25, 25 };
//static const int crystalEnchantBonusWeapon[] = { 0, 90, 45, 67, 144, 250, 250, 250 };
public:
L2ItemTemplate( L2ItemType itemType, L2ItemSubType subType, StatsSet& set );
virtual ~L2ItemTemplate();
public:
static L2ItemSubType getArmorTypeByName( const char *name );
static L2ItemSubType getWeaponTypeByName( const char *name );
static L2ItemSlot getSlotByName( const char *name );
static L2ItemMaterial getMaterialByName( const char *name );
static L2ItemCrystal getCrystalTypeByName( const char *name );
public:
L2ItemType getType() const { return m_type; }
L2ItemSubType getSubType() const { return m_subType; }
bool isArmor() const { return (m_subType>=ARMOR_NONE) && (m_subType<=ARMOR_SIGIL); }
bool isWeapon() const { return (m_subType>=WEAPON_NONE) && (m_subType<=WEAPON_DUAL_DAGGER); }
bool isEtcItem() const { return (m_subType>=ETCITEM_ARROW) && (m_subType<=ETCITEM_HERB); }
int getId() const { return m_itemId; }
const wchar_t *getName() const { return (const wchar_t *)m_name; }
int getType1() const { return m_type1; }
int getType2() const { return m_type2; }
int getWeight() const { return m_weight; }
L2ItemMaterial getMaterialType() const { return m_materialType; }
L2ItemCrystal getCrystalType() const { return m_crystalType; }
int getDuration() const { return m_duration; }
int getTime() const { return m_time; }
L2ItemSlot getBodyPart() const { return m_bodyPart; }
int getPrice() const { return m_referencePrice; }
int getCrystalCount() const { return m_crystalCount; }
bool isStackable() const { return m_stackable; }
bool isCrystallizable() const { return m_crystallizable; }
bool isSellable() const { return m_sellable; }
bool isDropable() const { return m_dropable; }
bool isDestroyable() const { return m_destroyable; }
bool isTradeable() const { return m_tradeable; }
bool isCommon() const { return m_common; }
bool isHeroItem() const { return m_heroItem; }
bool isPvpItem() const { return m_pvpItem; }
protected:
L2ItemType m_type; // weapon/armor/etcitem
L2ItemSubType m_subType; // type of weapon/type of armor/type of etcitem
int m_itemId;
wchar_t *m_name;
int m_type1;
int m_type2;
int m_weight;
L2ItemMaterial m_materialType;
L2ItemCrystal m_crystalType; // default to none-grade
int m_duration;
int m_time;
L2ItemSlot m_bodyPart;
int m_referencePrice;
int m_crystalCount;
bool m_crystallizable;
bool m_stackable;
bool m_sellable;
bool m_dropable;
bool m_destroyable;
bool m_tradeable;
bool m_common;
bool m_heroItem;
bool m_pvpItem;
//protected FuncTemplate[] _funcTemplates;
//protected EffectTemplate[] _effectTemplates;
//protected L2Skill[] _skills;
//protected List <Condition> _preConditions = new FastList<Condition>();
//private static final Func[] _emptyFunctionSet = new Func[0];
//protected static final L2Effect[] _emptyEffectSet = new L2Effect[0];
};

View File

@@ -0,0 +1,132 @@
#pragma once
enum L2ItemType
{
TYPE_WEAPON,
TYPE_ARMOR,
TYPE_ETCITEM
};
enum L2ItemSubType
{
SYBTYPE_INCORRECT = 0,
// weapons
WEAPON_NONE = 1, // "Shield" // mask 2 (2^1)
WEAPON_SWORD = 2, // "Sword" // mask 4 (2^2)
WEAPON_BLUNT = 3, // "Blunt" // mask 8 (2^3)
WEAPON_DAGGER = 4, // "Dagger" // mask 16 (2^4)
WEAPON_BOW = 5, // "Bow" // mask 32 (2^5)
WEAPON_POLE = 6, // "Pole" // mask 64 (2^6)
WEAPON_ETC = 7, // "Etc" // mask 128 (2^7)
WEAPON_FIST = 8, // "Fist" // mask 256 (2^8)
WEAPON_DUAL = 9, // "Dual Sword" // mask 512 (2^9)
WEAPON_DUALFIST = 10, // "Dual Fist" // mask 1024 (2^10)
WEAPON_BIGSWORD = 11, // "Big Sword" // Two Handed Swords // mask 2048 (2^11)
WEAPON_PET = 12, // "Pet" // mask 4096 (2^12)
WEAPON_ROD = 13, // "Rod" // mask 8192 (2^13)
WEAPON_BIGBLUNT = 14, // "Big Blunt" // mask 16384 (2^14)
WEAPON_ANCIENT_SWORD = 15, // "Ancient Sword" // mask 32768 (2^15)
WEAPON_CROSSBOW = 16, // "Crossbow" // mask 65536 (2^16)
WEAPON_RAPIER = 17, // "Rapier" // mask 131072 (2^17)
WEAPON_DUAL_DAGGER = 18, // "Dual Dagger" // mask 262144 (2^18)
// armors
ARMOR_NONE = 19, // "None"
ARMOR_LIGHT = 20, // "Light"
ARMOR_HEAVY = 21, // "Heavy"
ARMOR_MAGIC = 22, // "Magic"
ARMOR_PET = 23, // "Pet"
ARMOR_SIGIL = 24, // "Sigil"
// etc items
ETCITEM_ARROW = 25, // "Arrow"
ETCITEM_BOLT = 26, // "Bolt"
ETCITEM_MATERIAL = 27, // "Material"
ETCITEM_PET_COLLAR = 28, // "PetCollar"
ETCITEM_POTION = 29, // "Potion"
ETCITEM_RECEIPE = 30, // "Receipe"
ETCITEM_SCROLL = 31, // "Scroll"
ETCITEM_QUEST = 32, // "Quest"
ETCITEM_MONEY = 33, // "Money"
ETCITEM_OTHER = 34, // "Other"
ETCITEM_SPELLBOOK = 35, // "Spellbook"
ETCITEM_SEED = 36, // "Seed"
ETCITEM_SHOT = 37, // "Shot"
ETCITEM_HERB = 38, // "Herb"
ETCITEM_DYE = 39 // "dye"
};
enum L2ItemSlot
{
SLOT_NONE = 0x00000000,
SLOT_UNDERWEAR = 0x00000001,
SLOT_R_EAR = 0x00000002,
SLOT_L_EAR = 0x00000004,
SLOT_LR_EAR = 0x00000006,
SLOT_NECK = 0x00000008,
SLOT_R_FINGER = 0x00000010,
SLOT_L_FINGER = 0x00000020,
SLOT_LR_FINGER = 0x00000030,
SLOT_HEAD = 0x00000040,
SLOT_R_HAND = 0x00000080,
SLOT_L_HAND = 0x00000100,
SLOT_GLOVES = 0x00000200,
SLOT_CHEST = 0x00000400,
SLOT_LEGS = 0x00000800,
SLOT_FEET = 0x00001000,
SLOT_BACK = 0x00002000,
SLOT_LR_HAND = 0x00004000,
SLOT_FULL_ARMOR = 0x00008000,
SLOT_HAIR = 0x00010000,
SLOT_ALLDRESS = 0x00020000,
SLOT_HAIR2 = 0x00040000,
SLOT_HAIRALL = 0x00080000,
SLOT_R_BRACELET = 0x00100000,
SLOT_L_BRACELET = 0x00200000,
SLOT_DECO = 0x00400000,
SLOT_BELT = 0x00800000, //0x10000000,
SLOT_WOLF = 0x01000000, //-100,
SLOT_HATCHLING = 0x02000000, //-101,
SLOT_STRIDER = 0x04000000, //-102,
SLOT_BABYPET = 0x08000000, //-103,
SLOT_GREATWOLF = 0x10000000 //-104
};
enum L2ItemMaterial
{
MATERIAL_NONE = -1,
MATERIAL_STEEL = 0x00,
MATERIAL_FINE_STEEL = 0x01,
MATERIAL_BLOOD_STEEL = 0x02,
MATERIAL_BRONZE = 0x03,
MATERIAL_SILVER = 0x04,
MATERIAL_GOLD = 0x05,
MATERIAL_MITHRIL = 0x06,
MATERIAL_ORIHARUKON = 0x07,
MATERIAL_PAPER = 0x08,
MATERIAL_WOOD = 0x09,
MATERIAL_CLOTH = 0x0a,
MATERIAL_LEATHER = 0x0b,
MATERIAL_BONE = 0x0c,
MATERIAL_HORN = 0x0d,
MATERIAL_DAMASCUS = 0x0e,
MATERIAL_ADAMANTAITE = 0x0f,
MATERIAL_CHRYSOLITE = 0x10,
MATERIAL_CRYSTAL = 0x11,
MATERIAL_LIQUID = 0x12,
MATERIAL_SCALE_OF_DRAGON = 0x13,
MATERIAL_DYESTUFF = 0x14,
MATERIAL_COBWEB = 0x15,
MATERIAL_SEED = 0x16,
MATERIAL_COTTON = 0x17
};
enum L2ItemCrystal
{
CRYSTAL_NONE = 0x00,
CRYSTAL_D = 0x01,
CRYSTAL_C = 0x02,
CRYSTAL_B = 0x03,
CRYSTAL_A = 0x04,
CRYSTAL_S = 0x05,
CRYSTAL_S80 = 0x06,
CRYSTAL_S84 = 0x07
};

View File

@@ -0,0 +1,30 @@
#include "pch.h"
#include "L2WeaponTemplate.h"
#include "l2c_utils.h"
L2WeaponTemplate::L2WeaponTemplate( L2ItemSubType subType, StatsSet& set ):
L2ItemTemplate( TYPE_WEAPON, subType, set )
{
set.getInt( "soulshots", &m_soulShotCount );
set.getInt( "soulshots", &m_soulShotCount );
set.getInt( "spiritshots", &m_spiritShotCount );
set.getInt( "p_dam", &m_pDam );
set.getInt( "rnd_dam", &m_rndDam );
set.getInt( "critical", &m_critical );
set.getDouble( "hit_modify", &m_hitModifier );
set.getInt( "avoid_modify", &m_avoidModifier );
set.getInt( "shield_def", &m_shieldDef );
set.getDouble( "shield_def_rate", &m_shieldDefRate );
set.getInt( "atk_speed", &m_atkSpeed );
int default_atkReuse = 0;
if( m_subType == WEAPON_BOW ) default_atkReuse = 1500;
else if( m_subType == WEAPON_CROSSBOW ) default_atkReuse = 1200;
set.getInt( "atk_reuse", &m_atkReuse, default_atkReuse );
set.getInt( "mp_consume", &m_mpConsume );
set.getInt( "m_dam", &m_mDam );
set.getInt( "change_weaponId", &m_changeWeaponId );
}
L2WeaponTemplate::~L2WeaponTemplate()
{
}

View File

@@ -0,0 +1,50 @@
#pragma once
#include "L2ItemTemplate.h"
class L2WeaponTemplate: public L2ItemTemplate
{
public:
L2WeaponTemplate( L2ItemSubType subType, StatsSet& set );
virtual ~L2WeaponTemplate();
public:
int getSoulShotCount() const { return m_soulShotCount; }
int getSpiritShotCount() const { return m_spiritShotCount; }
int getPDam() const { return m_pDam; }
int getRndDam() const { return m_rndDam; }
int getCritical() const { return m_critical; }
double getHitModifier() const { return m_hitModifier; }
int getAvoidModifier() const { return m_avoidModifier; }
int getShieldDef() const { return m_shieldDef; }
double getShieldDefRate() const { return m_shieldDefRate; }
int getAtkSpeed() const { return m_atkSpeed; }
int getAtkReuse() const { return m_atkReuse; }
int getMpConsume() const { return m_mpConsume; }
int getMDam() const { return m_mDam; }
int getChangeWeaponId() const { return m_changeWeaponId; }
protected:
int m_soulShotCount;
int m_spiritShotCount;
int m_pDam;
int m_rndDam;
int m_critical;
double m_hitModifier;
int m_avoidModifier;
int m_shieldDef;
double m_shieldDefRate;
int m_atkSpeed;
int m_atkReuse;
int m_mpConsume;
int m_mDam;
int m_changeWeaponId;
// TODO: skills
//private L2Skill _enchant4Skill = null; // skill that activates when item is enchanted +4 (for duals)
//String[] _skill;
// Attached skills for Special Abilities
//protected L2Skill _skillsOnCast;
//protected Condition _skillsOnCastCondition;
//protected L2Skill _skillsOnCrit;
//protected Condition _skillsOnCritCondition;
};