Initial MSVC 2008 projects workspace
This commit is contained in:
393
L2C_Server/GS.cpp
Normal file
393
L2C_Server/GS.cpp
Normal 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
58
L2C_Server/GS.h
Normal 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
144
L2C_Server/GS_Cfg.cpp
Normal 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
57
L2C_Server/GS_Cfg.h
Normal 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
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
213
L2C_Server/L2Serverd.cpp
Normal 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
44
L2C_Server/L2Serverd.rc
Normal 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
|
||||
|
494
L2C_Server/LS_Connection.cpp
Normal file
494
L2C_Server/LS_Connection.cpp
Normal 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;
|
||||
}
|
55
L2C_Server/LS_Connection.h
Normal file
55
L2C_Server/LS_Connection.h
Normal 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
244
L2C_Server/Log.cpp
Normal 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
19
L2C_Server/Log.h
Normal 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 );
|
||||
|
23
L2C_Server/PostBuildEvent.cmd
Normal file
23
L2C_Server/PostBuildEvent.cmd
Normal 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
15
L2C_Server/Resource.h
Normal 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
|
||||
|
||||
|
94
L2C_Server/datatables/CharNameTable.cpp
Normal file
94
L2C_Server/datatables/CharNameTable.cpp
Normal 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;
|
||||
}
|
24
L2C_Server/datatables/CharNameTable.h
Normal file
24
L2C_Server/datatables/CharNameTable.h
Normal 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;
|
||||
};
|
198
L2C_Server/datatables/CharTemplateTable.cpp
Normal file
198
L2C_Server/datatables/CharTemplateTable.cpp
Normal 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++;
|
||||
}
|
||||
}
|
25
L2C_Server/datatables/CharTemplateTable.h
Normal file
25
L2C_Server/datatables/CharTemplateTable.h
Normal 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;
|
||||
};
|
525
L2C_Server/datatables/ItemTable.cpp
Normal file
525
L2C_Server/datatables/ItemTable.cpp
Normal 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];
|
||||
}
|
47
L2C_Server/datatables/ItemTable.h
Normal file
47
L2C_Server/datatables/ItemTable.h
Normal 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
39
L2C_Server/enums.h
Normal 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
|
||||
};
|
110
L2C_Server/net/ClientPacketHandler.cpp
Normal file
110
L2C_Server/net/ClientPacketHandler.cpp
Normal 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" );
|
||||
}
|
||||
|
28
L2C_Server/net/ClientPacketHandler.h
Normal file
28
L2C_Server/net/ClientPacketHandler.h
Normal 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;
|
||||
};
|
237
L2C_Server/net/ClientPool.cpp
Normal file
237
L2C_Server/net/ClientPool.cpp
Normal 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 =====" );
|
||||
}
|
29
L2C_Server/net/ClientPool.h
Normal file
29
L2C_Server/net/ClientPool.h
Normal 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;
|
||||
};
|
115
L2C_Server/net/GameClient/GameClient.cpp
Normal file
115
L2C_Server/net/GameClient/GameClient.cpp
Normal 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;
|
||||
}
|
78
L2C_Server/net/GameClient/GameClient.h
Normal file
78
L2C_Server/net/GameClient/GameClient.h
Normal 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;
|
||||
};
|
67
L2C_Server/net/GameClient/GameClientNet.cpp
Normal file
67
L2C_Server/net/GameClient/GameClientNet.cpp
Normal 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 );
|
||||
}
|
25
L2C_Server/net/GameClient/GameClientNetStats.cpp
Normal file
25
L2C_Server/net/GameClient/GameClientNetStats.cpp
Normal 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;
|
||||
}
|
||||
|
15
L2C_Server/net/GameClient/GameClientNetStats.h
Normal file
15
L2C_Server/net/GameClient/GameClientNetStats.h
Normal 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;
|
||||
};
|
188
L2C_Server/net/GameClient/GameClientThread.cpp
Normal file
188
L2C_Server/net/GameClient/GameClientThread.cpp
Normal 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;
|
||||
}
|
11
L2C_Server/net/ServerPackets.h
Normal file
11
L2C_Server/net/ServerPackets.h
Normal 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 );
|
||||
};
|
33
L2C_Server/net/clientpackets/c_AuthLogin.cpp
Normal file
33
L2C_Server/net/clientpackets/c_AuthLogin.cpp
Normal 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;
|
||||
}
|
||||
|
213
L2C_Server/net/clientpackets/c_CharacterCreate.cpp
Normal file
213
L2C_Server/net/clientpackets/c_CharacterCreate.cpp
Normal 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=?";
|
||||
*/
|
44
L2C_Server/net/clientpackets/c_CharacterSelect.cpp
Normal file
44
L2C_Server/net/clientpackets/c_CharacterSelect.cpp
Normal 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;
|
||||
}
|
||||
|
27
L2C_Server/net/clientpackets/c_Logout.cpp
Normal file
27
L2C_Server/net/clientpackets/c_Logout.cpp
Normal 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;
|
||||
}
|
13
L2C_Server/net/clientpackets/c_NewCharacter.cpp
Normal file
13
L2C_Server/net/clientpackets/c_NewCharacter.cpp
Normal 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 );
|
||||
}
|
37
L2C_Server/net/clientpackets/c_ProtocolVersion.cpp
Normal file
37
L2C_Server/net/clientpackets/c_ProtocolVersion.cpp
Normal 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 );
|
||||
}
|
15
L2C_Server/net/clientpackets/c_RequestGotoLobby.cpp
Normal file
15
L2C_Server/net/clientpackets/c_RequestGotoLobby.cpp
Normal 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 );
|
||||
}
|
||||
|
15
L2C_Server/net/serverpackets/s_CharCreateFail.cpp
Normal file
15
L2C_Server/net/serverpackets/s_CharCreateFail.cpp
Normal 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;
|
||||
}
|
14
L2C_Server/net/serverpackets/s_CharCreateOK.cpp
Normal file
14
L2C_Server/net/serverpackets/s_CharCreateOK.cpp
Normal 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;
|
||||
}
|
225
L2C_Server/net/serverpackets/s_CharacterSelectionInfo.cpp
Normal file
225
L2C_Server/net/serverpackets/s_CharacterSelectionInfo.cpp
Normal 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;
|
||||
}
|
27
L2C_Server/net/serverpackets/s_KeyPacket.cpp
Normal file
27
L2C_Server/net/serverpackets/s_KeyPacket.cpp
Normal 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;
|
||||
}
|
106
L2C_Server/net/serverpackets/s_NewCharacterSuccess.cpp
Normal file
106
L2C_Server/net/serverpackets/s_NewCharacterSuccess.cpp
Normal 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
1
L2C_Server/pch.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
44
L2C_Server/pch.h
Normal file
44
L2C_Server/pch.h
Normal 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>
|
304
L2C_Server/utils/Debugging.cpp
Normal file
304
L2C_Server/utils/Debugging.cpp
Normal 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++;
|
||||
}
|
||||
}
|
71
L2C_Server/utils/Debugging.h
Normal file
71
L2C_Server/utils/Debugging.h
Normal 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;
|
||||
};
|
98
L2C_Server/utils/Exception.cpp
Normal file
98
L2C_Server/utils/Exception.cpp
Normal 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();
|
||||
}
|
||||
|
22
L2C_Server/utils/Exception.h
Normal file
22
L2C_Server/utils/Exception.h
Normal 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;
|
||||
};
|
177
L2C_Server/utils/IdFactory.cpp
Normal file
177
L2C_Server/utils/IdFactory.cpp
Normal 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 );
|
||||
}
|
44
L2C_Server/utils/IdFactory.h
Normal file
44
L2C_Server/utils/IdFactory.h
Normal 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;
|
||||
};
|
16
L2C_Server/utils/Utils.cpp
Normal file
16
L2C_Server/utils/Utils.cpp
Normal 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
3
L2C_Server/utils/Utils.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
bool Utils_isValidCharName( const wchar_t *name );
|
59
L2C_Server/world/model/GameObject.cpp
Normal file
59
L2C_Server/world/model/GameObject.cpp
Normal 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;
|
||||
}
|
35
L2C_Server/world/model/GameObject.h
Normal file
35
L2C_Server/world/model/GameObject.h
Normal 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;
|
||||
};
|
43
L2C_Server/world/model/base/ClassId.cpp
Normal file
43
L2C_Server/world/model/base/ClassId.cpp
Normal 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 );
|
||||
}
|
112
L2C_Server/world/model/base/ClassId.h
Normal file
112
L2C_Server/world/model/base/ClassId.h
Normal 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
|
||||
};
|
196
L2C_Server/world/model/base/ClassIdTree.cpp
Normal file
196
L2C_Server/world/model/base/ClassIdTree.cpp
Normal 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;
|
||||
}
|
||||
|
27
L2C_Server/world/model/base/ClassIdTree.h
Normal file
27
L2C_Server/world/model/base/ClassIdTree.h
Normal 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
|
||||
};
|
11
L2C_Server/world/model/base/Race.h
Normal file
11
L2C_Server/world/model/base/Race.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
enum Race
|
||||
{
|
||||
RACE_Human,
|
||||
RACE_Elf,
|
||||
RACE_DarkElf,
|
||||
RACE_Orc,
|
||||
RACE_Dwarf,
|
||||
RACE_Kamael
|
||||
};
|
45
L2C_Server/world/model/character/GameCharacter.cpp
Normal file
45
L2C_Server/world/model/character/GameCharacter.cpp
Normal 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;
|
||||
}
|
24
L2C_Server/world/model/character/GameCharacter.h
Normal file
24
L2C_Server/world/model/character/GameCharacter.h
Normal 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;
|
||||
};
|
11
L2C_Server/world/model/character/GameNpc.cpp
Normal file
11
L2C_Server/world/model/character/GameNpc.cpp
Normal 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()
|
||||
{
|
||||
}
|
18
L2C_Server/world/model/character/GameNpc.h
Normal file
18
L2C_Server/world/model/character/GameNpc.h
Normal 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;
|
||||
};
|
19
L2C_Server/world/model/character/GamePlayer.cpp
Normal file
19
L2C_Server/world/model/character/GamePlayer.cpp
Normal 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;
|
||||
}
|
16
L2C_Server/world/model/character/GamePlayer.h
Normal file
16
L2C_Server/world/model/character/GamePlayer.h
Normal 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;
|
||||
};
|
385
L2C_Server/world/templates/StatsSet.cpp
Normal file
385
L2C_Server/world/templates/StatsSet.cpp
Normal 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 );
|
||||
}
|
44
L2C_Server/world/templates/StatsSet.h
Normal file
44
L2C_Server/world/templates/StatsSet.h
Normal 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;
|
||||
};
|
79
L2C_Server/world/templates/chars/L2CharTemplate.cpp
Normal file
79
L2C_Server/world/templates/chars/L2CharTemplate.cpp
Normal 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 );
|
||||
}
|
81
L2C_Server/world/templates/chars/L2CharTemplate.h
Normal file
81
L2C_Server/world/templates/chars/L2CharTemplate.h
Normal 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;
|
||||
};
|
83
L2C_Server/world/templates/chars/L2PlayerTemplate.cpp
Normal file
83
L2C_Server/world/templates/chars/L2PlayerTemplate.cpp
Normal 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;
|
||||
}
|
53
L2C_Server/world/templates/chars/L2PlayerTemplate.h
Normal file
53
L2C_Server/world/templates/chars/L2PlayerTemplate.h
Normal 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;
|
||||
};
|
23
L2C_Server/world/templates/item/L2ArmorTemplate.cpp
Normal file
23
L2C_Server/world/templates/item/L2ArmorTemplate.cpp
Normal 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()
|
||||
{
|
||||
}
|
25
L2C_Server/world/templates/item/L2ArmorTemplate.h
Normal file
25
L2C_Server/world/templates/item/L2ArmorTemplate.h
Normal 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
|
||||
};
|
13
L2C_Server/world/templates/item/L2EtcItemTemplate.cpp
Normal file
13
L2C_Server/world/templates/item/L2EtcItemTemplate.cpp
Normal 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()
|
||||
{
|
||||
}
|
16
L2C_Server/world/templates/item/L2EtcItemTemplate.h
Normal file
16
L2C_Server/world/templates/item/L2EtcItemTemplate.h
Normal 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?
|
||||
};
|
191
L2C_Server/world/templates/item/L2ItemTemplate.cpp
Normal file
191
L2C_Server/world/templates/item/L2ItemTemplate.cpp
Normal 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;
|
||||
}
|
98
L2C_Server/world/templates/item/L2ItemTemplate.h
Normal file
98
L2C_Server/world/templates/item/L2ItemTemplate.h
Normal 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];
|
||||
};
|
132
L2C_Server/world/templates/item/L2ItemType.h
Normal file
132
L2C_Server/world/templates/item/L2ItemType.h
Normal 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
|
||||
};
|
30
L2C_Server/world/templates/item/L2WeaponTemplate.cpp
Normal file
30
L2C_Server/world/templates/item/L2WeaponTemplate.cpp
Normal 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()
|
||||
{
|
||||
}
|
50
L2C_Server/world/templates/item/L2WeaponTemplate.h
Normal file
50
L2C_Server/world/templates/item/L2WeaponTemplate.h
Normal 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;
|
||||
};
|
Reference in New Issue
Block a user